blob: 492cd438d4c14a44711894efc4cd95e87e7de44a [file] [log] [blame]
QUICHE team82dee2f2019-01-18 12:35:12 -05001// 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 "net/third_party/quiche/src/spdy/core/spdy_frame_builder.h"
6
7#include <algorithm>
8#include <cstdint>
9#include <limits>
10#include <new>
11
QUICHE team82dee2f2019-01-18 12:35:12 -050012#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
13#include "net/third_party/quiche/src/spdy/core/zero_copy_output_buffer.h"
14#include "net/third_party/quiche/src/spdy/platform/api/spdy_bug_tracker.h"
QUICHE teamded03512019-03-07 14:45:11 -080015#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
QUICHE team82dee2f2019-01-18 12:35:12 -050016
17namespace spdy {
18
19SpdyFrameBuilder::SpdyFrameBuilder(size_t size)
20 : buffer_(new char[size]), capacity_(size), length_(0), offset_(0) {}
21
22SpdyFrameBuilder::SpdyFrameBuilder(size_t size, ZeroCopyOutputBuffer* output)
23 : buffer_(output == nullptr ? new char[size] : nullptr),
24 output_(output),
25 capacity_(size),
26 length_(0),
27 offset_(0) {}
28
29SpdyFrameBuilder::~SpdyFrameBuilder() = default;
30
31char* SpdyFrameBuilder::GetWritableBuffer(size_t length) {
32 if (!CanWrite(length)) {
33 return nullptr;
34 }
35 return buffer_.get() + offset_ + length_;
36}
37
38char* SpdyFrameBuilder::GetWritableOutput(size_t length,
39 size_t* actual_length) {
40 char* dest = nullptr;
41 int size = 0;
42
43 if (!CanWrite(length)) {
44 return nullptr;
45 }
46 output_->Next(&dest, &size);
47 *actual_length = std::min<size_t>(length, size);
48 return dest;
49}
50
51bool SpdyFrameBuilder::Seek(size_t length) {
52 if (!CanWrite(length)) {
53 return false;
54 }
55 if (output_ == nullptr) {
56 length_ += length;
57 } else {
58 output_->AdvanceWritePtr(length);
59 length_ += length;
60 }
61 return true;
62}
63
64bool SpdyFrameBuilder::BeginNewFrame(SpdyFrameType type,
65 uint8_t flags,
66 SpdyStreamId stream_id) {
67 uint8_t raw_frame_type = SerializeFrameType(type);
68 DCHECK(IsDefinedFrameType(raw_frame_type));
69 DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
70 bool success = true;
71 if (length_ > 0) {
72 SPDY_BUG << "SpdyFrameBuilder doesn't have a clean state when BeginNewFrame"
73 << "is called. Leftover length_ is " << length_;
74 offset_ += length_;
75 length_ = 0;
76 }
77
78 success &= WriteUInt24(capacity_ - offset_ - kFrameHeaderSize);
79 success &= WriteUInt8(raw_frame_type);
80 success &= WriteUInt8(flags);
81 success &= WriteUInt32(stream_id);
82 DCHECK_EQ(kDataFrameMinimumSize, length_);
83 return success;
84}
85
86bool SpdyFrameBuilder::BeginNewFrame(SpdyFrameType type,
87 uint8_t flags,
88 SpdyStreamId stream_id,
89 size_t length) {
90 uint8_t raw_frame_type = SerializeFrameType(type);
91 DCHECK(IsDefinedFrameType(raw_frame_type));
92 DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
93 SPDY_BUG_IF(length > kHttp2DefaultFramePayloadLimit)
94 << "Frame length " << length_ << " is longer than frame size limit.";
95 return BeginNewFrameInternal(raw_frame_type, flags, stream_id, length);
96}
97
98bool 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
105bool SpdyFrameBuilder::BeginNewFrameInternal(uint8_t raw_frame_type,
106 uint8_t flags,
107 SpdyStreamId stream_id,
108 size_t length) {
109 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 DCHECK_EQ(kDataFrameMinimumSize, length_);
120 return success;
121}
122
bnc7f82d042020-01-03 12:18:53 -0800123bool SpdyFrameBuilder::WriteStringPiece32(
124 const quiche::QuicheStringPiece value) {
QUICHE team82dee2f2019-01-18 12:35:12 -0500125 if (!WriteUInt32(value.size())) {
126 return false;
127 }
128
129 return WriteBytes(value.data(), value.size());
130}
131
132bool SpdyFrameBuilder::WriteBytes(const void* data, uint32_t data_len) {
133 if (!CanWrite(data_len)) {
134 return false;
135 }
136
137 if (output_ == nullptr) {
138 char* dest = GetWritableBuffer(data_len);
139 memcpy(dest, data, data_len);
140 Seek(data_len);
141 } else {
142 char* dest = nullptr;
143 size_t size = 0;
144 size_t total_written = 0;
145 const char* data_ptr = reinterpret_cast<const char*>(data);
146 while (data_len > 0) {
147 dest = GetWritableOutput(data_len, &size);
148 if (dest == nullptr || size == 0) {
149 // Unable to make progress.
150 return false;
151 }
152 uint32_t to_copy = std::min<uint32_t>(data_len, size);
153 const char* src = data_ptr + total_written;
154 memcpy(dest, src, to_copy);
155 Seek(to_copy);
156 data_len -= to_copy;
157 total_written += to_copy;
158 }
159 }
160 return true;
161}
162
163bool SpdyFrameBuilder::CanWrite(size_t length) const {
164 if (length > kLengthMask) {
165 DCHECK(false);
166 return false;
167 }
168
169 if (output_ == nullptr) {
170 if (offset_ + length_ + length > capacity_) {
QUICHE teamded03512019-03-07 14:45:11 -0800171 SPDY_DLOG(FATAL) << "Requested: " << length << " capacity: " << capacity_
172 << " used: " << offset_ + length_;
QUICHE team82dee2f2019-01-18 12:35:12 -0500173 return false;
174 }
175 } else {
176 if (length > output_->BytesFree()) {
177 return false;
178 }
179 }
180
181 return true;
182}
183
184} // namespace spdy