blob: f4b71d323ad2cea7cf2c213441c39acbd078c4c8 [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
123bool SpdyFrameBuilder::WriteStringPiece32(const SpdyStringPiece value) {
124 if (!WriteUInt32(value.size())) {
125 return false;
126 }
127
128 return WriteBytes(value.data(), value.size());
129}
130
131bool 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
162bool SpdyFrameBuilder::CanWrite(size_t length) const {
163 if (length > kLengthMask) {
164 DCHECK(false);
165 return false;
166 }
167
168 if (output_ == nullptr) {
169 if (offset_ + length_ + length > capacity_) {
QUICHE teamded03512019-03-07 14:45:11 -0800170 SPDY_DLOG(FATAL) << "Requested: " << length << " capacity: " << capacity_
171 << " used: " << offset_ + length_;
QUICHE team82dee2f2019-01-18 12:35:12 -0500172 return false;
173 }
174 } else {
175 if (length > output_->BytesFree()) {
176 return false;
177 }
178 }
179
180 return true;
181}
182
183} // namespace spdy