1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/spdy/core/spdy_frame_builder.h"
6
7 #include <algorithm>
8 #include <cstddef>
9 #include <cstdint>
10 #include <cstring>
11
12 #include "absl/strings/string_view.h"
13 #include "quiche/common/platform/api/quiche_bug_tracker.h"
14 #include "quiche/common/platform/api/quiche_logging.h"
15 #include "quiche/spdy/core/spdy_bitmasks.h"
16 #include "quiche/spdy/core/spdy_protocol.h"
17 #include "quiche/spdy/core/zero_copy_output_buffer.h"
18
19 namespace spdy {
20
SpdyFrameBuilder(size_t size)21 SpdyFrameBuilder::SpdyFrameBuilder(size_t size)
22 : buffer_(new char[size]), capacity_(size), length_(0), offset_(0) {}
23
SpdyFrameBuilder(size_t size,ZeroCopyOutputBuffer * output)24 SpdyFrameBuilder::SpdyFrameBuilder(size_t size, ZeroCopyOutputBuffer* output)
25 : buffer_(output == nullptr ? new char[size] : nullptr),
26 output_(output),
27 capacity_(size),
28 length_(0),
29 offset_(0) {}
30
31 SpdyFrameBuilder::~SpdyFrameBuilder() = default;
32
GetWritableBuffer(size_t length)33 char* SpdyFrameBuilder::GetWritableBuffer(size_t length) {
34 if (!CanWrite(length)) {
35 return nullptr;
36 }
37 return buffer_.get() + offset_ + length_;
38 }
39
GetWritableOutput(size_t length,size_t * actual_length)40 char* SpdyFrameBuilder::GetWritableOutput(size_t length,
41 size_t* actual_length) {
42 char* dest = nullptr;
43 int size = 0;
44
45 if (!CanWrite(length)) {
46 return nullptr;
47 }
48 output_->Next(&dest, &size);
49 *actual_length = std::min<size_t>(length, size);
50 return dest;
51 }
52
Seek(size_t length)53 bool SpdyFrameBuilder::Seek(size_t length) {
54 if (!CanWrite(length)) {
55 return false;
56 }
57 if (output_ == nullptr) {
58 length_ += length;
59 } else {
60 output_->AdvanceWritePtr(length);
61 length_ += length;
62 }
63 return true;
64 }
65
BeginNewFrame(SpdyFrameType type,uint8_t flags,SpdyStreamId stream_id)66 bool SpdyFrameBuilder::BeginNewFrame(SpdyFrameType type, uint8_t flags,
67 SpdyStreamId stream_id) {
68 uint8_t raw_frame_type = SerializeFrameType(type);
69 QUICHE_DCHECK(IsDefinedFrameType(raw_frame_type));
70 QUICHE_DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
71 bool success = true;
72 if (length_ > 0) {
73 QUICHE_BUG(spdy_bug_73_1)
74 << "SpdyFrameBuilder doesn't have a clean state when BeginNewFrame"
75 << "is called. Leftover length_ is " << length_;
76 offset_ += length_;
77 length_ = 0;
78 }
79
80 success &= WriteUInt24(capacity_ - offset_ - kFrameHeaderSize);
81 success &= WriteUInt8(raw_frame_type);
82 success &= WriteUInt8(flags);
83 success &= WriteUInt32(stream_id);
84 QUICHE_DCHECK_EQ(kDataFrameMinimumSize, length_);
85 return success;
86 }
87
BeginNewFrame(SpdyFrameType type,uint8_t flags,SpdyStreamId stream_id,size_t length)88 bool SpdyFrameBuilder::BeginNewFrame(SpdyFrameType type, uint8_t flags,
89 SpdyStreamId stream_id, size_t length) {
90 uint8_t raw_frame_type = SerializeFrameType(type);
91 QUICHE_DCHECK(IsDefinedFrameType(raw_frame_type));
92 QUICHE_DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
93 QUICHE_BUG_IF(spdy_bug_73_2, length > kSpdyMaxFrameSizeLimit)
94 << "Frame length " << length << " is longer than frame size limit.";
95 return BeginNewFrameInternal(raw_frame_type, flags, stream_id, length);
96 }
97
BeginNewUncheckedFrame(uint8_t raw_frame_type,uint8_t flags,SpdyStreamId stream_id,size_t length)98 bool SpdyFrameBuilder::BeginNewUncheckedFrame(uint8_t raw_frame_type,
99 uint8_t flags,
100 SpdyStreamId stream_id,
101 size_t length) {
102 return BeginNewFrameInternal(raw_frame_type, flags, stream_id, length);
103 }
104
BeginNewFrameInternal(uint8_t raw_frame_type,uint8_t flags,SpdyStreamId stream_id,size_t length)105 bool SpdyFrameBuilder::BeginNewFrameInternal(uint8_t raw_frame_type,
106 uint8_t flags,
107 SpdyStreamId stream_id,
108 size_t length) {
109 QUICHE_DCHECK_EQ(length, length & kLengthMask);
110 bool success = true;
111
112 offset_ += length_;
113 length_ = 0;
114
115 success &= WriteUInt24(length);
116 success &= WriteUInt8(raw_frame_type);
117 success &= WriteUInt8(flags);
118 success &= WriteUInt32(stream_id);
119 QUICHE_DCHECK_EQ(kDataFrameMinimumSize, length_);
120 return success;
121 }
122
WriteStringPiece32(const absl::string_view value)123 bool SpdyFrameBuilder::WriteStringPiece32(const absl::string_view value) {
124 if (!WriteUInt32(value.size())) {
125 return false;
126 }
127
128 return WriteBytes(value.data(), value.size());
129 }
130
WriteBytes(const void * data,uint32_t data_len)131 bool SpdyFrameBuilder::WriteBytes(const void* data, uint32_t data_len) {
132 if (!CanWrite(data_len)) {
133 return false;
134 }
135
136 if (output_ == nullptr) {
137 char* dest = GetWritableBuffer(data_len);
138 memcpy(dest, data, data_len);
139 Seek(data_len);
140 } else {
141 char* dest = nullptr;
142 size_t size = 0;
143 size_t total_written = 0;
144 const char* data_ptr = reinterpret_cast<const char*>(data);
145 while (data_len > 0) {
146 dest = GetWritableOutput(data_len, &size);
147 if (dest == nullptr || size == 0) {
148 // Unable to make progress.
149 return false;
150 }
151 uint32_t to_copy = std::min<uint32_t>(data_len, size);
152 const char* src = data_ptr + total_written;
153 memcpy(dest, src, to_copy);
154 Seek(to_copy);
155 data_len -= to_copy;
156 total_written += to_copy;
157 }
158 }
159 return true;
160 }
161
CanWrite(size_t length) const162 bool SpdyFrameBuilder::CanWrite(size_t length) const {
163 if (length > kLengthMask) {
164 QUICHE_DCHECK(false);
165 return false;
166 }
167
168 if (output_ == nullptr) {
169 if (offset_ + length_ + length > capacity_) {
170 QUICHE_DLOG(FATAL) << "Requested: " << length
171 << " capacity: " << capacity_
172 << " used: " << offset_ + length_;
173 return false;
174 }
175 } else {
176 if (length > output_->BytesFree()) {
177 return false;
178 }
179 }
180
181 return true;
182 }
183
184 } // namespace spdy
185