QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 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 | |
QUICHE team | 5be974e | 2020-12-29 18:35:24 -0500 | [diff] [blame] | 5 | #include "spdy/core/spdy_framer.h" |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 6 | |
| 7 | #include <algorithm> |
| 8 | #include <cstdint> |
| 9 | #include <iterator> |
| 10 | #include <list> |
| 11 | #include <new> |
bnc | 463f235 | 2019-10-10 04:49:34 -0700 | [diff] [blame] | 12 | #include <utility> |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 13 | |
vasilvv | add8ac3 | 2020-10-20 10:32:13 -0700 | [diff] [blame] | 14 | #include "absl/memory/memory.h" |
QUICHE team | 5be974e | 2020-12-29 18:35:24 -0500 | [diff] [blame] | 15 | #include "http2/platform/api/http2_macros.h" |
QUICHE team | 33a4aa6 | 2021-04-08 11:10:59 -0700 | [diff] [blame] | 16 | #include "common/platform/api/quiche_bug_tracker.h" |
QUICHE team | 1b5d09e | 2021-04-08 13:36:42 -0700 | [diff] [blame] | 17 | #include "common/platform/api/quiche_logging.h" |
QUICHE team | 5be974e | 2020-12-29 18:35:24 -0500 | [diff] [blame] | 18 | #include "spdy/core/spdy_bitmasks.h" |
| 19 | #include "spdy/core/spdy_frame_builder.h" |
| 20 | #include "spdy/core/spdy_frame_reader.h" |
QUICHE team | 5be974e | 2020-12-29 18:35:24 -0500 | [diff] [blame] | 21 | #include "spdy/platform/api/spdy_estimate_memory_usage.h" |
QUICHE team | 5be974e | 2020-12-29 18:35:24 -0500 | [diff] [blame] | 22 | #include "spdy/platform/api/spdy_string_utils.h" |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 23 | |
| 24 | namespace spdy { |
| 25 | |
| 26 | namespace { |
| 27 | |
| 28 | // Pack parent stream ID and exclusive flag into the format used by HTTP/2 |
| 29 | // headers and priority frames. |
| 30 | uint32_t PackStreamDependencyValues(bool exclusive, |
| 31 | SpdyStreamId parent_stream_id) { |
| 32 | // Make sure the highest-order bit in the parent stream id is zeroed out. |
| 33 | uint32_t parent = parent_stream_id & 0x7fffffff; |
| 34 | // Set the one-bit exclusivity flag. |
| 35 | uint32_t e_bit = exclusive ? 0x80000000 : 0; |
| 36 | return parent | e_bit; |
| 37 | } |
| 38 | |
| 39 | // Used to indicate no flags in a HTTP2 flags field. |
| 40 | const uint8_t kNoFlags = 0; |
| 41 | |
| 42 | // Wire size of pad length field. |
| 43 | const size_t kPadLengthFieldSize = 1; |
| 44 | |
| 45 | // The size of one parameter in SETTINGS frame. |
| 46 | const size_t kOneSettingParameterSize = 6; |
| 47 | |
QUICHE team | 232cf49 | 2020-10-27 08:21:26 -0700 | [diff] [blame] | 48 | size_t GetUncompressedSerializedLength(const Http2HeaderBlock& headers) { |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 49 | const size_t num_name_value_pairs_size = sizeof(uint32_t); |
| 50 | const size_t length_of_name_size = num_name_value_pairs_size; |
| 51 | const size_t length_of_value_size = num_name_value_pairs_size; |
| 52 | |
| 53 | size_t total_length = num_name_value_pairs_size; |
| 54 | for (const auto& header : headers) { |
| 55 | // We add space for the length of the name and the length of the value as |
| 56 | // well as the length of the name and the length of the value. |
| 57 | total_length += length_of_name_size + header.first.size() + |
| 58 | length_of_value_size + header.second.size(); |
| 59 | } |
| 60 | return total_length; |
| 61 | } |
| 62 | |
| 63 | // Serializes the flags octet for a given SpdyHeadersIR. |
| 64 | uint8_t SerializeHeaderFrameFlags(const SpdyHeadersIR& header_ir, |
| 65 | const bool end_headers) { |
| 66 | uint8_t flags = 0; |
| 67 | if (header_ir.fin()) { |
| 68 | flags |= CONTROL_FLAG_FIN; |
| 69 | } |
| 70 | if (end_headers) { |
| 71 | flags |= HEADERS_FLAG_END_HEADERS; |
| 72 | } |
| 73 | if (header_ir.padded()) { |
| 74 | flags |= HEADERS_FLAG_PADDED; |
| 75 | } |
| 76 | if (header_ir.has_priority()) { |
| 77 | flags |= HEADERS_FLAG_PRIORITY; |
| 78 | } |
| 79 | return flags; |
| 80 | } |
| 81 | |
| 82 | // Serializes the flags octet for a given SpdyPushPromiseIR. |
| 83 | uint8_t SerializePushPromiseFrameFlags(const SpdyPushPromiseIR& push_promise_ir, |
| 84 | const bool end_headers) { |
| 85 | uint8_t flags = 0; |
| 86 | if (push_promise_ir.padded()) { |
| 87 | flags = flags | PUSH_PROMISE_FLAG_PADDED; |
| 88 | } |
| 89 | if (end_headers) { |
| 90 | flags |= PUSH_PROMISE_FLAG_END_PUSH_PROMISE; |
| 91 | } |
| 92 | return flags; |
| 93 | } |
| 94 | |
| 95 | // Serializes a HEADERS frame from the given SpdyHeadersIR and encoded header |
| 96 | // block. Does not need or use the SpdyHeaderBlock inside SpdyHeadersIR. |
| 97 | // Return false if the serialization fails. |encoding| should not be empty. |
| 98 | bool SerializeHeadersGivenEncoding(const SpdyHeadersIR& headers, |
bnc | 4471291 | 2019-08-15 18:58:14 -0700 | [diff] [blame] | 99 | const std::string& encoding, |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 100 | const bool end_headers, |
| 101 | ZeroCopyOutputBuffer* output) { |
| 102 | const size_t frame_size = |
| 103 | GetHeaderFrameSizeSansBlock(headers) + encoding.size(); |
| 104 | SpdyFrameBuilder builder(frame_size, output); |
| 105 | bool ret = builder.BeginNewFrame( |
| 106 | SpdyFrameType::HEADERS, SerializeHeaderFrameFlags(headers, end_headers), |
| 107 | headers.stream_id(), frame_size - kFrameHeaderSize); |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 108 | QUICHE_DCHECK_EQ(kFrameHeaderSize, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 109 | |
| 110 | if (ret && headers.padded()) { |
| 111 | ret &= builder.WriteUInt8(headers.padding_payload_len()); |
| 112 | } |
| 113 | |
| 114 | if (ret && headers.has_priority()) { |
| 115 | int weight = ClampHttp2Weight(headers.weight()); |
| 116 | ret &= builder.WriteUInt32(PackStreamDependencyValues( |
| 117 | headers.exclusive(), headers.parent_stream_id())); |
| 118 | // Per RFC 7540 section 6.3, serialized weight value is actual value - 1. |
| 119 | ret &= builder.WriteUInt8(weight - 1); |
| 120 | } |
| 121 | |
| 122 | if (ret) { |
| 123 | ret &= builder.WriteBytes(encoding.data(), encoding.size()); |
| 124 | } |
| 125 | |
| 126 | if (ret && headers.padding_payload_len() > 0) { |
bnc | 4471291 | 2019-08-15 18:58:14 -0700 | [diff] [blame] | 127 | std::string padding(headers.padding_payload_len(), 0); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 128 | ret &= builder.WriteBytes(padding.data(), padding.length()); |
| 129 | } |
| 130 | |
| 131 | if (!ret) { |
QUICHE team | 1b5d09e | 2021-04-08 13:36:42 -0700 | [diff] [blame] | 132 | QUICHE_DLOG(WARNING) |
| 133 | << "Failed to build HEADERS. Not enough space in output"; |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 134 | } |
| 135 | return ret; |
| 136 | } |
| 137 | |
| 138 | // Serializes a PUSH_PROMISE frame from the given SpdyPushPromiseIR and |
| 139 | // encoded header block. Does not need or use the SpdyHeaderBlock inside |
| 140 | // SpdyPushPromiseIR. |
| 141 | bool SerializePushPromiseGivenEncoding(const SpdyPushPromiseIR& push_promise, |
bnc | 4471291 | 2019-08-15 18:58:14 -0700 | [diff] [blame] | 142 | const std::string& encoding, |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 143 | const bool end_headers, |
| 144 | ZeroCopyOutputBuffer* output) { |
| 145 | const size_t frame_size = |
| 146 | GetPushPromiseFrameSizeSansBlock(push_promise) + encoding.size(); |
| 147 | SpdyFrameBuilder builder(frame_size, output); |
| 148 | bool ok = builder.BeginNewFrame( |
| 149 | SpdyFrameType::PUSH_PROMISE, |
| 150 | SerializePushPromiseFrameFlags(push_promise, end_headers), |
| 151 | push_promise.stream_id(), frame_size - kFrameHeaderSize); |
| 152 | |
| 153 | if (push_promise.padded()) { |
| 154 | ok = ok && builder.WriteUInt8(push_promise.padding_payload_len()); |
| 155 | } |
| 156 | ok = ok && builder.WriteUInt32(push_promise.promised_stream_id()) && |
| 157 | builder.WriteBytes(encoding.data(), encoding.size()); |
| 158 | if (ok && push_promise.padding_payload_len() > 0) { |
bnc | 4471291 | 2019-08-15 18:58:14 -0700 | [diff] [blame] | 159 | std::string padding(push_promise.padding_payload_len(), 0); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 160 | ok = builder.WriteBytes(padding.data(), padding.length()); |
| 161 | } |
| 162 | |
QUICHE team | 1b5d09e | 2021-04-08 13:36:42 -0700 | [diff] [blame] | 163 | QUICHE_DLOG_IF(ERROR, !ok) |
QUICHE team | ded0351 | 2019-03-07 14:45:11 -0800 | [diff] [blame] | 164 | << "Failed to write PUSH_PROMISE encoding, not enough " |
| 165 | << "space in output"; |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 166 | return ok; |
| 167 | } |
| 168 | |
| 169 | bool WritePayloadWithContinuation(SpdyFrameBuilder* builder, |
bnc | 4471291 | 2019-08-15 18:58:14 -0700 | [diff] [blame] | 170 | const std::string& hpack_encoding, |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 171 | SpdyStreamId stream_id, |
| 172 | SpdyFrameType type, |
| 173 | int padding_payload_len) { |
| 174 | uint8_t end_flag = 0; |
| 175 | uint8_t flags = 0; |
| 176 | if (type == SpdyFrameType::HEADERS) { |
| 177 | end_flag = HEADERS_FLAG_END_HEADERS; |
| 178 | } else if (type == SpdyFrameType::PUSH_PROMISE) { |
| 179 | end_flag = PUSH_PROMISE_FLAG_END_PUSH_PROMISE; |
| 180 | } else { |
QUICHE team | 1b5d09e | 2021-04-08 13:36:42 -0700 | [diff] [blame] | 181 | QUICHE_DLOG(FATAL) << "CONTINUATION frames cannot be used with frame type " |
| 182 | << FrameTypeToString(type); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 183 | } |
| 184 | |
| 185 | // Write all the padding payload and as much of the data payload as possible |
| 186 | // into the initial frame. |
| 187 | size_t bytes_remaining = 0; |
| 188 | bytes_remaining = hpack_encoding.size() - |
| 189 | std::min(hpack_encoding.size(), |
| 190 | kHttp2MaxControlFrameSendSize - builder->length() - |
| 191 | padding_payload_len); |
| 192 | bool ret = builder->WriteBytes(&hpack_encoding[0], |
| 193 | hpack_encoding.size() - bytes_remaining); |
| 194 | if (padding_payload_len > 0) { |
bnc | 4471291 | 2019-08-15 18:58:14 -0700 | [diff] [blame] | 195 | std::string padding = std::string(padding_payload_len, 0); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 196 | ret &= builder->WriteBytes(padding.data(), padding.length()); |
| 197 | } |
| 198 | |
| 199 | // Tack on CONTINUATION frames for the overflow. |
| 200 | while (bytes_remaining > 0 && ret) { |
| 201 | size_t bytes_to_write = |
| 202 | std::min(bytes_remaining, |
| 203 | kHttp2MaxControlFrameSendSize - kContinuationFrameMinimumSize); |
| 204 | // Write CONTINUATION frame prefix. |
| 205 | if (bytes_remaining == bytes_to_write) { |
| 206 | flags |= end_flag; |
| 207 | } |
| 208 | ret &= builder->BeginNewFrame(SpdyFrameType::CONTINUATION, flags, stream_id, |
| 209 | bytes_to_write); |
| 210 | // Write payload fragment. |
| 211 | ret &= builder->WriteBytes( |
| 212 | &hpack_encoding[hpack_encoding.size() - bytes_remaining], |
| 213 | bytes_to_write); |
| 214 | bytes_remaining -= bytes_to_write; |
| 215 | } |
| 216 | return ret; |
| 217 | } |
| 218 | |
| 219 | void SerializeDataBuilderHelper(const SpdyDataIR& data_ir, |
| 220 | uint8_t* flags, |
| 221 | int* num_padding_fields, |
| 222 | size_t* size_with_padding) { |
| 223 | if (data_ir.fin()) { |
| 224 | *flags = DATA_FLAG_FIN; |
| 225 | } |
| 226 | |
| 227 | if (data_ir.padded()) { |
| 228 | *flags = *flags | DATA_FLAG_PADDED; |
| 229 | ++*num_padding_fields; |
| 230 | } |
| 231 | |
| 232 | *size_with_padding = *num_padding_fields + data_ir.data_len() + |
| 233 | data_ir.padding_payload_len() + kDataFrameMinimumSize; |
| 234 | } |
| 235 | |
| 236 | void SerializeDataFrameHeaderWithPaddingLengthFieldBuilderHelper( |
| 237 | const SpdyDataIR& data_ir, |
| 238 | uint8_t* flags, |
| 239 | size_t* frame_size, |
| 240 | size_t* num_padding_fields) { |
| 241 | *flags = DATA_FLAG_NONE; |
| 242 | if (data_ir.fin()) { |
| 243 | *flags = DATA_FLAG_FIN; |
| 244 | } |
| 245 | |
| 246 | *frame_size = kDataFrameMinimumSize; |
| 247 | if (data_ir.padded()) { |
| 248 | *flags = *flags | DATA_FLAG_PADDED; |
| 249 | ++(*num_padding_fields); |
| 250 | *frame_size = *frame_size + *num_padding_fields; |
| 251 | } |
| 252 | } |
| 253 | |
| 254 | void SerializeSettingsBuilderHelper(const SpdySettingsIR& settings, |
| 255 | uint8_t* flags, |
| 256 | const SettingsMap* values, |
| 257 | size_t* size) { |
| 258 | if (settings.is_ack()) { |
| 259 | *flags = *flags | SETTINGS_FLAG_ACK; |
| 260 | } |
| 261 | *size = |
| 262 | kSettingsFrameMinimumSize + (values->size() * kOneSettingParameterSize); |
| 263 | } |
| 264 | |
| 265 | void SerializeAltSvcBuilderHelper(const SpdyAltSvcIR& altsvc_ir, |
bnc | 4471291 | 2019-08-15 18:58:14 -0700 | [diff] [blame] | 266 | std::string* value, |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 267 | size_t* size) { |
| 268 | *size = kGetAltSvcFrameMinimumSize; |
| 269 | *size = *size + altsvc_ir.origin().length(); |
| 270 | *value = SpdyAltSvcWireFormat::SerializeHeaderFieldValue( |
| 271 | altsvc_ir.altsvc_vector()); |
| 272 | *size = *size + value->length(); |
| 273 | } |
| 274 | |
| 275 | } // namespace |
| 276 | |
| 277 | SpdyFramer::SpdyFramer(CompressionOption option) |
| 278 | : debug_visitor_(nullptr), compression_option_(option) { |
| 279 | static_assert(kHttp2MaxControlFrameSendSize <= kHttp2DefaultFrameSizeLimit, |
| 280 | "Our send limit should be at most our receive limit."); |
| 281 | } |
| 282 | |
| 283 | SpdyFramer::~SpdyFramer() = default; |
| 284 | |
| 285 | void SpdyFramer::set_debug_visitor( |
| 286 | SpdyFramerDebugVisitorInterface* debug_visitor) { |
| 287 | debug_visitor_ = debug_visitor; |
| 288 | } |
| 289 | |
| 290 | SpdyFramer::SpdyFrameIterator::SpdyFrameIterator(SpdyFramer* framer) |
| 291 | : framer_(framer), is_first_frame_(true), has_next_frame_(true) {} |
| 292 | |
| 293 | SpdyFramer::SpdyFrameIterator::~SpdyFrameIterator() = default; |
| 294 | |
| 295 | size_t SpdyFramer::SpdyFrameIterator::NextFrame(ZeroCopyOutputBuffer* output) { |
| 296 | const SpdyFrameIR& frame_ir = GetIR(); |
| 297 | if (!has_next_frame_) { |
QUICHE team | 33a4aa6 | 2021-04-08 11:10:59 -0700 | [diff] [blame] | 298 | QUICHE_BUG(spdy_bug_75_1) |
QUICHE team | 1650120 | 2021-03-11 09:32:16 -0800 | [diff] [blame] | 299 | << "SpdyFramer::SpdyFrameIterator::NextFrame called without " |
| 300 | << "a next frame."; |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 301 | return false; |
| 302 | } |
| 303 | |
| 304 | const size_t size_without_block = |
| 305 | is_first_frame_ ? GetFrameSizeSansBlock() : kContinuationFrameMinimumSize; |
bnc | 463f235 | 2019-10-10 04:49:34 -0700 | [diff] [blame] | 306 | auto encoding = std::make_unique<std::string>(); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 307 | encoder_->Next(kHttp2MaxControlFrameSendSize - size_without_block, |
| 308 | encoding.get()); |
| 309 | has_next_frame_ = encoder_->HasNext(); |
| 310 | |
| 311 | if (framer_->debug_visitor_ != nullptr) { |
| 312 | const auto& header_block_frame_ir = |
QUICHE team | 1605d8b | 2019-01-18 17:10:34 -0500 | [diff] [blame] | 313 | static_cast<const SpdyFrameWithHeaderBlockIR&>(frame_ir); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 314 | const size_t header_list_size = |
| 315 | GetUncompressedSerializedLength(header_block_frame_ir.header_block()); |
| 316 | framer_->debug_visitor_->OnSendCompressedFrame( |
| 317 | frame_ir.stream_id(), |
| 318 | is_first_frame_ ? frame_ir.frame_type() : SpdyFrameType::CONTINUATION, |
| 319 | header_list_size, size_without_block + encoding->size()); |
| 320 | } |
| 321 | |
| 322 | const size_t free_bytes_before = output->BytesFree(); |
| 323 | bool ok = false; |
| 324 | if (is_first_frame_) { |
| 325 | is_first_frame_ = false; |
| 326 | ok = SerializeGivenEncoding(*encoding, output); |
| 327 | } else { |
| 328 | SpdyContinuationIR continuation_ir(frame_ir.stream_id()); |
| 329 | continuation_ir.take_encoding(std::move(encoding)); |
| 330 | continuation_ir.set_end_headers(!has_next_frame_); |
| 331 | ok = framer_->SerializeContinuation(continuation_ir, output); |
| 332 | } |
| 333 | return ok ? free_bytes_before - output->BytesFree() : 0; |
| 334 | } |
| 335 | |
| 336 | bool SpdyFramer::SpdyFrameIterator::HasNextFrame() const { |
| 337 | return has_next_frame_; |
| 338 | } |
| 339 | |
| 340 | SpdyFramer::SpdyHeaderFrameIterator::SpdyHeaderFrameIterator( |
| 341 | SpdyFramer* framer, |
| 342 | std::unique_ptr<const SpdyHeadersIR> headers_ir) |
| 343 | : SpdyFrameIterator(framer), headers_ir_(std::move(headers_ir)) { |
| 344 | SetEncoder(headers_ir_.get()); |
| 345 | } |
| 346 | |
| 347 | SpdyFramer::SpdyHeaderFrameIterator::~SpdyHeaderFrameIterator() = default; |
| 348 | |
| 349 | const SpdyFrameIR& SpdyFramer::SpdyHeaderFrameIterator::GetIR() const { |
| 350 | return *(headers_ir_.get()); |
| 351 | } |
| 352 | |
| 353 | size_t SpdyFramer::SpdyHeaderFrameIterator::GetFrameSizeSansBlock() const { |
| 354 | return GetHeaderFrameSizeSansBlock(*headers_ir_); |
| 355 | } |
| 356 | |
| 357 | bool SpdyFramer::SpdyHeaderFrameIterator::SerializeGivenEncoding( |
bnc | 4471291 | 2019-08-15 18:58:14 -0700 | [diff] [blame] | 358 | const std::string& encoding, |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 359 | ZeroCopyOutputBuffer* output) const { |
| 360 | return SerializeHeadersGivenEncoding(*headers_ir_, encoding, |
| 361 | !has_next_frame(), output); |
| 362 | } |
| 363 | |
| 364 | SpdyFramer::SpdyPushPromiseFrameIterator::SpdyPushPromiseFrameIterator( |
| 365 | SpdyFramer* framer, |
| 366 | std::unique_ptr<const SpdyPushPromiseIR> push_promise_ir) |
| 367 | : SpdyFrameIterator(framer), push_promise_ir_(std::move(push_promise_ir)) { |
| 368 | SetEncoder(push_promise_ir_.get()); |
| 369 | } |
| 370 | |
| 371 | SpdyFramer::SpdyPushPromiseFrameIterator::~SpdyPushPromiseFrameIterator() = |
| 372 | default; |
| 373 | |
| 374 | const SpdyFrameIR& SpdyFramer::SpdyPushPromiseFrameIterator::GetIR() const { |
| 375 | return *(push_promise_ir_.get()); |
| 376 | } |
| 377 | |
| 378 | size_t SpdyFramer::SpdyPushPromiseFrameIterator::GetFrameSizeSansBlock() const { |
| 379 | return GetPushPromiseFrameSizeSansBlock(*push_promise_ir_); |
| 380 | } |
| 381 | |
| 382 | bool SpdyFramer::SpdyPushPromiseFrameIterator::SerializeGivenEncoding( |
bnc | 4471291 | 2019-08-15 18:58:14 -0700 | [diff] [blame] | 383 | const std::string& encoding, |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 384 | ZeroCopyOutputBuffer* output) const { |
| 385 | return SerializePushPromiseGivenEncoding(*push_promise_ir_, encoding, |
| 386 | !has_next_frame(), output); |
| 387 | } |
| 388 | |
| 389 | SpdyFramer::SpdyControlFrameIterator::SpdyControlFrameIterator( |
| 390 | SpdyFramer* framer, |
| 391 | std::unique_ptr<const SpdyFrameIR> frame_ir) |
| 392 | : framer_(framer), frame_ir_(std::move(frame_ir)) {} |
| 393 | |
| 394 | SpdyFramer::SpdyControlFrameIterator::~SpdyControlFrameIterator() = default; |
| 395 | |
| 396 | size_t SpdyFramer::SpdyControlFrameIterator::NextFrame( |
| 397 | ZeroCopyOutputBuffer* output) { |
| 398 | size_t size_written = framer_->SerializeFrame(*frame_ir_, output); |
| 399 | has_next_frame_ = false; |
| 400 | return size_written; |
| 401 | } |
| 402 | |
| 403 | bool SpdyFramer::SpdyControlFrameIterator::HasNextFrame() const { |
| 404 | return has_next_frame_; |
| 405 | } |
| 406 | |
| 407 | const SpdyFrameIR& SpdyFramer::SpdyControlFrameIterator::GetIR() const { |
| 408 | return *(frame_ir_.get()); |
| 409 | } |
| 410 | |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 411 | std::unique_ptr<SpdyFrameSequence> SpdyFramer::CreateIterator( |
| 412 | SpdyFramer* framer, |
| 413 | std::unique_ptr<const SpdyFrameIR> frame_ir) { |
| 414 | switch (frame_ir->frame_type()) { |
| 415 | case SpdyFrameType::HEADERS: { |
bnc | 463f235 | 2019-10-10 04:49:34 -0700 | [diff] [blame] | 416 | return std::make_unique<SpdyHeaderFrameIterator>( |
vasilvv | add8ac3 | 2020-10-20 10:32:13 -0700 | [diff] [blame] | 417 | framer, absl::WrapUnique( |
bnc | 24194c8 | 2020-01-14 17:39:51 -0800 | [diff] [blame] | 418 | static_cast<const SpdyHeadersIR*>(frame_ir.release()))); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 419 | } |
| 420 | case SpdyFrameType::PUSH_PROMISE: { |
bnc | 463f235 | 2019-10-10 04:49:34 -0700 | [diff] [blame] | 421 | return std::make_unique<SpdyPushPromiseFrameIterator>( |
QUICHE team | 5159c75 | 2021-03-31 10:51:05 -0700 | [diff] [blame] | 422 | framer, absl::WrapUnique(static_cast<const SpdyPushPromiseIR*>( |
| 423 | frame_ir.release()))); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 424 | } |
| 425 | case SpdyFrameType::DATA: { |
QUICHE team | 1b5d09e | 2021-04-08 13:36:42 -0700 | [diff] [blame] | 426 | QUICHE_DVLOG(1) << "Serialize a stream end DATA frame for VTL"; |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 427 | HTTP2_FALLTHROUGH; |
| 428 | } |
| 429 | default: { |
bnc | 463f235 | 2019-10-10 04:49:34 -0700 | [diff] [blame] | 430 | return std::make_unique<SpdyControlFrameIterator>(framer, |
| 431 | std::move(frame_ir)); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 432 | } |
| 433 | } |
| 434 | } |
| 435 | |
| 436 | SpdySerializedFrame SpdyFramer::SerializeData(const SpdyDataIR& data_ir) { |
| 437 | uint8_t flags = DATA_FLAG_NONE; |
| 438 | int num_padding_fields = 0; |
| 439 | size_t size_with_padding = 0; |
| 440 | SerializeDataBuilderHelper(data_ir, &flags, &num_padding_fields, |
| 441 | &size_with_padding); |
| 442 | |
| 443 | SpdyFrameBuilder builder(size_with_padding); |
| 444 | builder.BeginNewFrame(SpdyFrameType::DATA, flags, data_ir.stream_id()); |
| 445 | if (data_ir.padded()) { |
| 446 | builder.WriteUInt8(data_ir.padding_payload_len() & 0xff); |
| 447 | } |
| 448 | builder.WriteBytes(data_ir.data(), data_ir.data_len()); |
| 449 | if (data_ir.padding_payload_len() > 0) { |
bnc | 4471291 | 2019-08-15 18:58:14 -0700 | [diff] [blame] | 450 | std::string padding(data_ir.padding_payload_len(), 0); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 451 | builder.WriteBytes(padding.data(), padding.length()); |
| 452 | } |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 453 | QUICHE_DCHECK_EQ(size_with_padding, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 454 | return builder.take(); |
| 455 | } |
| 456 | |
| 457 | SpdySerializedFrame SpdyFramer::SerializeDataFrameHeaderWithPaddingLengthField( |
| 458 | const SpdyDataIR& data_ir) { |
| 459 | uint8_t flags = DATA_FLAG_NONE; |
| 460 | size_t frame_size = 0; |
| 461 | size_t num_padding_fields = 0; |
| 462 | SerializeDataFrameHeaderWithPaddingLengthFieldBuilderHelper( |
| 463 | data_ir, &flags, &frame_size, &num_padding_fields); |
| 464 | |
| 465 | SpdyFrameBuilder builder(frame_size); |
| 466 | builder.BeginNewFrame( |
| 467 | SpdyFrameType::DATA, flags, data_ir.stream_id(), |
| 468 | num_padding_fields + data_ir.data_len() + data_ir.padding_payload_len()); |
| 469 | if (data_ir.padded()) { |
| 470 | builder.WriteUInt8(data_ir.padding_payload_len() & 0xff); |
| 471 | } |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 472 | QUICHE_DCHECK_EQ(frame_size, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 473 | return builder.take(); |
| 474 | } |
| 475 | |
| 476 | SpdySerializedFrame SpdyFramer::SerializeRstStream( |
| 477 | const SpdyRstStreamIR& rst_stream) const { |
| 478 | size_t expected_length = kRstStreamFrameSize; |
| 479 | SpdyFrameBuilder builder(expected_length); |
| 480 | |
| 481 | builder.BeginNewFrame(SpdyFrameType::RST_STREAM, 0, rst_stream.stream_id()); |
| 482 | |
| 483 | builder.WriteUInt32(rst_stream.error_code()); |
| 484 | |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 485 | QUICHE_DCHECK_EQ(expected_length, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 486 | return builder.take(); |
| 487 | } |
| 488 | |
| 489 | SpdySerializedFrame SpdyFramer::SerializeSettings( |
| 490 | const SpdySettingsIR& settings) const { |
| 491 | uint8_t flags = 0; |
| 492 | // Size, in bytes, of this SETTINGS frame. |
| 493 | size_t size = 0; |
| 494 | const SettingsMap* values = &(settings.values()); |
| 495 | SerializeSettingsBuilderHelper(settings, &flags, values, &size); |
| 496 | SpdyFrameBuilder builder(size); |
| 497 | builder.BeginNewFrame(SpdyFrameType::SETTINGS, flags, 0); |
| 498 | |
| 499 | // If this is an ACK, payload should be empty. |
| 500 | if (settings.is_ack()) { |
| 501 | return builder.take(); |
| 502 | } |
| 503 | |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 504 | QUICHE_DCHECK_EQ(kSettingsFrameMinimumSize, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 505 | for (auto it = values->begin(); it != values->end(); ++it) { |
| 506 | int setting_id = it->first; |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 507 | QUICHE_DCHECK_GE(setting_id, 0); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 508 | builder.WriteUInt16(static_cast<SpdySettingsId>(setting_id)); |
| 509 | builder.WriteUInt32(it->second); |
| 510 | } |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 511 | QUICHE_DCHECK_EQ(size, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 512 | return builder.take(); |
| 513 | } |
| 514 | |
| 515 | SpdySerializedFrame SpdyFramer::SerializePing(const SpdyPingIR& ping) const { |
| 516 | SpdyFrameBuilder builder(kPingFrameSize); |
| 517 | uint8_t flags = 0; |
| 518 | if (ping.is_ack()) { |
| 519 | flags |= PING_FLAG_ACK; |
| 520 | } |
| 521 | builder.BeginNewFrame(SpdyFrameType::PING, flags, 0); |
| 522 | builder.WriteUInt64(ping.id()); |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 523 | QUICHE_DCHECK_EQ(kPingFrameSize, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 524 | return builder.take(); |
| 525 | } |
| 526 | |
| 527 | SpdySerializedFrame SpdyFramer::SerializeGoAway( |
| 528 | const SpdyGoAwayIR& goaway) const { |
| 529 | // Compute the output buffer size, take opaque data into account. |
| 530 | size_t expected_length = kGoawayFrameMinimumSize; |
| 531 | expected_length += goaway.description().size(); |
| 532 | SpdyFrameBuilder builder(expected_length); |
| 533 | |
| 534 | // Serialize the GOAWAY frame. |
| 535 | builder.BeginNewFrame(SpdyFrameType::GOAWAY, 0, 0); |
| 536 | |
| 537 | // GOAWAY frames specify the last good stream id. |
| 538 | builder.WriteUInt32(goaway.last_good_stream_id()); |
| 539 | |
| 540 | // GOAWAY frames also specify the error code. |
| 541 | builder.WriteUInt32(goaway.error_code()); |
| 542 | |
| 543 | // GOAWAY frames may also specify opaque data. |
| 544 | if (!goaway.description().empty()) { |
| 545 | builder.WriteBytes(goaway.description().data(), |
| 546 | goaway.description().size()); |
| 547 | } |
| 548 | |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 549 | QUICHE_DCHECK_EQ(expected_length, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 550 | return builder.take(); |
| 551 | } |
| 552 | |
| 553 | void SpdyFramer::SerializeHeadersBuilderHelper(const SpdyHeadersIR& headers, |
| 554 | uint8_t* flags, |
| 555 | size_t* size, |
bnc | 4471291 | 2019-08-15 18:58:14 -0700 | [diff] [blame] | 556 | std::string* hpack_encoding, |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 557 | int* weight, |
| 558 | size_t* length_field) { |
| 559 | if (headers.fin()) { |
| 560 | *flags = *flags | CONTROL_FLAG_FIN; |
| 561 | } |
| 562 | // This will get overwritten if we overflow into a CONTINUATION frame. |
| 563 | *flags = *flags | HEADERS_FLAG_END_HEADERS; |
| 564 | if (headers.has_priority()) { |
| 565 | *flags = *flags | HEADERS_FLAG_PRIORITY; |
| 566 | } |
| 567 | if (headers.padded()) { |
| 568 | *flags = *flags | HEADERS_FLAG_PADDED; |
| 569 | } |
| 570 | |
| 571 | *size = kHeadersFrameMinimumSize; |
| 572 | |
| 573 | if (headers.padded()) { |
| 574 | *size = *size + kPadLengthFieldSize; |
| 575 | *size = *size + headers.padding_payload_len(); |
| 576 | } |
| 577 | |
| 578 | if (headers.has_priority()) { |
| 579 | *weight = ClampHttp2Weight(headers.weight()); |
| 580 | *size = *size + 5; |
| 581 | } |
| 582 | |
| 583 | GetHpackEncoder()->EncodeHeaderSet(headers.header_block(), hpack_encoding); |
| 584 | *size = *size + hpack_encoding->size(); |
| 585 | if (*size > kHttp2MaxControlFrameSendSize) { |
| 586 | *size = *size + GetNumberRequiredContinuationFrames(*size) * |
| 587 | kContinuationFrameMinimumSize; |
| 588 | *flags = *flags & ~HEADERS_FLAG_END_HEADERS; |
| 589 | } |
| 590 | // Compute frame length field. |
| 591 | if (headers.padded()) { |
| 592 | *length_field = *length_field + kPadLengthFieldSize; |
| 593 | } |
| 594 | if (headers.has_priority()) { |
| 595 | *length_field = *length_field + 4; // Dependency field. |
| 596 | *length_field = *length_field + 1; // Weight field. |
| 597 | } |
| 598 | *length_field = *length_field + headers.padding_payload_len(); |
| 599 | *length_field = *length_field + hpack_encoding->size(); |
| 600 | // If the HEADERS frame with payload would exceed the max frame size, then |
| 601 | // WritePayloadWithContinuation() will serialize CONTINUATION frames as |
| 602 | // necessary. |
| 603 | *length_field = |
| 604 | std::min(*length_field, kHttp2MaxControlFrameSendSize - kFrameHeaderSize); |
| 605 | } |
| 606 | |
| 607 | SpdySerializedFrame SpdyFramer::SerializeHeaders(const SpdyHeadersIR& headers) { |
| 608 | uint8_t flags = 0; |
| 609 | // The size of this frame, including padding (if there is any) and |
| 610 | // variable-length header block. |
| 611 | size_t size = 0; |
bnc | 4471291 | 2019-08-15 18:58:14 -0700 | [diff] [blame] | 612 | std::string hpack_encoding; |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 613 | int weight = 0; |
| 614 | size_t length_field = 0; |
| 615 | SerializeHeadersBuilderHelper(headers, &flags, &size, &hpack_encoding, |
| 616 | &weight, &length_field); |
| 617 | |
| 618 | SpdyFrameBuilder builder(size); |
| 619 | builder.BeginNewFrame(SpdyFrameType::HEADERS, flags, headers.stream_id(), |
| 620 | length_field); |
| 621 | |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 622 | QUICHE_DCHECK_EQ(kHeadersFrameMinimumSize, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 623 | |
| 624 | int padding_payload_len = 0; |
| 625 | if (headers.padded()) { |
| 626 | builder.WriteUInt8(headers.padding_payload_len()); |
| 627 | padding_payload_len = headers.padding_payload_len(); |
| 628 | } |
| 629 | if (headers.has_priority()) { |
| 630 | builder.WriteUInt32(PackStreamDependencyValues(headers.exclusive(), |
| 631 | headers.parent_stream_id())); |
| 632 | // Per RFC 7540 section 6.3, serialized weight value is actual value - 1. |
| 633 | builder.WriteUInt8(weight - 1); |
| 634 | } |
| 635 | WritePayloadWithContinuation(&builder, hpack_encoding, headers.stream_id(), |
| 636 | SpdyFrameType::HEADERS, padding_payload_len); |
| 637 | |
| 638 | if (debug_visitor_) { |
| 639 | const size_t header_list_size = |
| 640 | GetUncompressedSerializedLength(headers.header_block()); |
| 641 | debug_visitor_->OnSendCompressedFrame(headers.stream_id(), |
| 642 | SpdyFrameType::HEADERS, |
| 643 | header_list_size, builder.length()); |
| 644 | } |
| 645 | |
| 646 | return builder.take(); |
| 647 | } |
| 648 | |
| 649 | SpdySerializedFrame SpdyFramer::SerializeWindowUpdate( |
| 650 | const SpdyWindowUpdateIR& window_update) { |
| 651 | SpdyFrameBuilder builder(kWindowUpdateFrameSize); |
| 652 | builder.BeginNewFrame(SpdyFrameType::WINDOW_UPDATE, kNoFlags, |
| 653 | window_update.stream_id()); |
| 654 | builder.WriteUInt32(window_update.delta()); |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 655 | QUICHE_DCHECK_EQ(kWindowUpdateFrameSize, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 656 | return builder.take(); |
| 657 | } |
| 658 | |
| 659 | void SpdyFramer::SerializePushPromiseBuilderHelper( |
| 660 | const SpdyPushPromiseIR& push_promise, |
| 661 | uint8_t* flags, |
bnc | 4471291 | 2019-08-15 18:58:14 -0700 | [diff] [blame] | 662 | std::string* hpack_encoding, |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 663 | size_t* size) { |
| 664 | *flags = 0; |
| 665 | // This will get overwritten if we overflow into a CONTINUATION frame. |
| 666 | *flags = *flags | PUSH_PROMISE_FLAG_END_PUSH_PROMISE; |
| 667 | // The size of this frame, including variable-length name-value block. |
| 668 | *size = kPushPromiseFrameMinimumSize; |
| 669 | |
| 670 | if (push_promise.padded()) { |
| 671 | *flags = *flags | PUSH_PROMISE_FLAG_PADDED; |
| 672 | *size = *size + kPadLengthFieldSize; |
| 673 | *size = *size + push_promise.padding_payload_len(); |
| 674 | } |
| 675 | |
| 676 | GetHpackEncoder()->EncodeHeaderSet(push_promise.header_block(), |
| 677 | hpack_encoding); |
| 678 | *size = *size + hpack_encoding->size(); |
| 679 | if (*size > kHttp2MaxControlFrameSendSize) { |
| 680 | *size = *size + GetNumberRequiredContinuationFrames(*size) * |
| 681 | kContinuationFrameMinimumSize; |
| 682 | *flags = *flags & ~PUSH_PROMISE_FLAG_END_PUSH_PROMISE; |
| 683 | } |
| 684 | } |
| 685 | |
| 686 | SpdySerializedFrame SpdyFramer::SerializePushPromise( |
| 687 | const SpdyPushPromiseIR& push_promise) { |
| 688 | uint8_t flags = 0; |
| 689 | size_t size = 0; |
bnc | 4471291 | 2019-08-15 18:58:14 -0700 | [diff] [blame] | 690 | std::string hpack_encoding; |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 691 | SerializePushPromiseBuilderHelper(push_promise, &flags, &hpack_encoding, |
| 692 | &size); |
| 693 | |
| 694 | SpdyFrameBuilder builder(size); |
| 695 | size_t length = |
| 696 | std::min(size, kHttp2MaxControlFrameSendSize) - kFrameHeaderSize; |
| 697 | builder.BeginNewFrame(SpdyFrameType::PUSH_PROMISE, flags, |
| 698 | push_promise.stream_id(), length); |
| 699 | int padding_payload_len = 0; |
| 700 | if (push_promise.padded()) { |
| 701 | builder.WriteUInt8(push_promise.padding_payload_len()); |
| 702 | builder.WriteUInt32(push_promise.promised_stream_id()); |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 703 | QUICHE_DCHECK_EQ(kPushPromiseFrameMinimumSize + kPadLengthFieldSize, |
| 704 | builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 705 | |
| 706 | padding_payload_len = push_promise.padding_payload_len(); |
| 707 | } else { |
| 708 | builder.WriteUInt32(push_promise.promised_stream_id()); |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 709 | QUICHE_DCHECK_EQ(kPushPromiseFrameMinimumSize, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 710 | } |
| 711 | |
| 712 | WritePayloadWithContinuation( |
| 713 | &builder, hpack_encoding, push_promise.stream_id(), |
| 714 | SpdyFrameType::PUSH_PROMISE, padding_payload_len); |
| 715 | |
| 716 | if (debug_visitor_) { |
| 717 | const size_t header_list_size = |
| 718 | GetUncompressedSerializedLength(push_promise.header_block()); |
| 719 | debug_visitor_->OnSendCompressedFrame(push_promise.stream_id(), |
| 720 | SpdyFrameType::PUSH_PROMISE, |
| 721 | header_list_size, builder.length()); |
| 722 | } |
| 723 | |
| 724 | return builder.take(); |
| 725 | } |
| 726 | |
| 727 | SpdySerializedFrame SpdyFramer::SerializeContinuation( |
| 728 | const SpdyContinuationIR& continuation) const { |
bnc | 4471291 | 2019-08-15 18:58:14 -0700 | [diff] [blame] | 729 | const std::string& encoding = continuation.encoding(); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 730 | size_t frame_size = kContinuationFrameMinimumSize + encoding.size(); |
| 731 | SpdyFrameBuilder builder(frame_size); |
| 732 | uint8_t flags = continuation.end_headers() ? HEADERS_FLAG_END_HEADERS : 0; |
| 733 | builder.BeginNewFrame(SpdyFrameType::CONTINUATION, flags, |
| 734 | continuation.stream_id()); |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 735 | QUICHE_DCHECK_EQ(kFrameHeaderSize, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 736 | |
| 737 | builder.WriteBytes(encoding.data(), encoding.size()); |
| 738 | return builder.take(); |
| 739 | } |
| 740 | |
| 741 | SpdySerializedFrame SpdyFramer::SerializeAltSvc(const SpdyAltSvcIR& altsvc_ir) { |
bnc | 4471291 | 2019-08-15 18:58:14 -0700 | [diff] [blame] | 742 | std::string value; |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 743 | size_t size = 0; |
| 744 | SerializeAltSvcBuilderHelper(altsvc_ir, &value, &size); |
| 745 | SpdyFrameBuilder builder(size); |
| 746 | builder.BeginNewFrame(SpdyFrameType::ALTSVC, kNoFlags, altsvc_ir.stream_id()); |
| 747 | |
| 748 | builder.WriteUInt16(altsvc_ir.origin().length()); |
| 749 | builder.WriteBytes(altsvc_ir.origin().data(), altsvc_ir.origin().length()); |
| 750 | builder.WriteBytes(value.data(), value.length()); |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 751 | QUICHE_DCHECK_LT(kGetAltSvcFrameMinimumSize, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 752 | return builder.take(); |
| 753 | } |
| 754 | |
| 755 | SpdySerializedFrame SpdyFramer::SerializePriority( |
| 756 | const SpdyPriorityIR& priority) const { |
| 757 | SpdyFrameBuilder builder(kPriorityFrameSize); |
| 758 | builder.BeginNewFrame(SpdyFrameType::PRIORITY, kNoFlags, |
| 759 | priority.stream_id()); |
| 760 | |
| 761 | builder.WriteUInt32(PackStreamDependencyValues(priority.exclusive(), |
| 762 | priority.parent_stream_id())); |
| 763 | // Per RFC 7540 section 6.3, serialized weight value is actual value - 1. |
| 764 | builder.WriteUInt8(priority.weight() - 1); |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 765 | QUICHE_DCHECK_EQ(kPriorityFrameSize, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 766 | return builder.take(); |
| 767 | } |
| 768 | |
bnc | 19423c7 | 2020-12-15 07:51:45 -0800 | [diff] [blame] | 769 | SpdySerializedFrame SpdyFramer::SerializePriorityUpdate( |
| 770 | const SpdyPriorityUpdateIR& priority_update) const { |
| 771 | const size_t total_size = kPriorityUpdateFrameMinimumSize + |
| 772 | priority_update.priority_field_value().size(); |
| 773 | SpdyFrameBuilder builder(total_size); |
| 774 | builder.BeginNewFrame(SpdyFrameType::PRIORITY_UPDATE, kNoFlags, |
| 775 | priority_update.stream_id()); |
| 776 | |
| 777 | builder.WriteUInt32(priority_update.prioritized_stream_id()); |
| 778 | builder.WriteBytes(priority_update.priority_field_value().data(), |
| 779 | priority_update.priority_field_value().size()); |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 780 | QUICHE_DCHECK_EQ(total_size, builder.length()); |
bnc | 19423c7 | 2020-12-15 07:51:45 -0800 | [diff] [blame] | 781 | return builder.take(); |
| 782 | } |
| 783 | |
bnc | 5c194f3 | 2021-02-05 08:15:05 -0800 | [diff] [blame] | 784 | SpdySerializedFrame SpdyFramer::SerializeAcceptCh( |
| 785 | const SpdyAcceptChIR& accept_ch) const { |
| 786 | const size_t total_size = accept_ch.size(); |
| 787 | SpdyFrameBuilder builder(total_size); |
| 788 | builder.BeginNewFrame(SpdyFrameType::ACCEPT_CH, kNoFlags, |
| 789 | accept_ch.stream_id()); |
| 790 | |
bnc | 8cfb52b | 2021-02-05 13:49:00 -0800 | [diff] [blame] | 791 | for (const AcceptChOriginValuePair& entry : accept_ch.entries()) { |
bnc | 5c194f3 | 2021-02-05 08:15:05 -0800 | [diff] [blame] | 792 | builder.WriteUInt16(entry.origin.size()); |
| 793 | builder.WriteBytes(entry.origin.data(), entry.origin.size()); |
| 794 | builder.WriteUInt16(entry.value.size()); |
| 795 | builder.WriteBytes(entry.value.data(), entry.value.size()); |
| 796 | } |
| 797 | |
| 798 | QUICHE_DCHECK_EQ(total_size, builder.length()); |
| 799 | return builder.take(); |
| 800 | } |
| 801 | |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 802 | SpdySerializedFrame SpdyFramer::SerializeUnknown( |
| 803 | const SpdyUnknownIR& unknown) const { |
| 804 | const size_t total_size = kFrameHeaderSize + unknown.payload().size(); |
| 805 | SpdyFrameBuilder builder(total_size); |
| 806 | builder.BeginNewUncheckedFrame(unknown.type(), unknown.flags(), |
| 807 | unknown.stream_id(), unknown.length()); |
| 808 | builder.WriteBytes(unknown.payload().data(), unknown.payload().size()); |
| 809 | return builder.take(); |
| 810 | } |
| 811 | |
| 812 | namespace { |
| 813 | |
| 814 | class FrameSerializationVisitor : public SpdyFrameVisitor { |
| 815 | public: |
| 816 | explicit FrameSerializationVisitor(SpdyFramer* framer) |
| 817 | : framer_(framer), frame_() {} |
| 818 | ~FrameSerializationVisitor() override = default; |
| 819 | |
| 820 | SpdySerializedFrame ReleaseSerializedFrame() { return std::move(frame_); } |
| 821 | |
| 822 | void VisitData(const SpdyDataIR& data) override { |
| 823 | frame_ = framer_->SerializeData(data); |
| 824 | } |
| 825 | void VisitRstStream(const SpdyRstStreamIR& rst_stream) override { |
| 826 | frame_ = framer_->SerializeRstStream(rst_stream); |
| 827 | } |
| 828 | void VisitSettings(const SpdySettingsIR& settings) override { |
| 829 | frame_ = framer_->SerializeSettings(settings); |
| 830 | } |
| 831 | void VisitPing(const SpdyPingIR& ping) override { |
| 832 | frame_ = framer_->SerializePing(ping); |
| 833 | } |
| 834 | void VisitGoAway(const SpdyGoAwayIR& goaway) override { |
| 835 | frame_ = framer_->SerializeGoAway(goaway); |
| 836 | } |
| 837 | void VisitHeaders(const SpdyHeadersIR& headers) override { |
| 838 | frame_ = framer_->SerializeHeaders(headers); |
| 839 | } |
| 840 | void VisitWindowUpdate(const SpdyWindowUpdateIR& window_update) override { |
| 841 | frame_ = framer_->SerializeWindowUpdate(window_update); |
| 842 | } |
| 843 | void VisitPushPromise(const SpdyPushPromiseIR& push_promise) override { |
| 844 | frame_ = framer_->SerializePushPromise(push_promise); |
| 845 | } |
| 846 | void VisitContinuation(const SpdyContinuationIR& continuation) override { |
| 847 | frame_ = framer_->SerializeContinuation(continuation); |
| 848 | } |
| 849 | void VisitAltSvc(const SpdyAltSvcIR& altsvc) override { |
| 850 | frame_ = framer_->SerializeAltSvc(altsvc); |
| 851 | } |
| 852 | void VisitPriority(const SpdyPriorityIR& priority) override { |
| 853 | frame_ = framer_->SerializePriority(priority); |
| 854 | } |
bnc | 19423c7 | 2020-12-15 07:51:45 -0800 | [diff] [blame] | 855 | void VisitPriorityUpdate( |
| 856 | const SpdyPriorityUpdateIR& priority_update) override { |
| 857 | frame_ = framer_->SerializePriorityUpdate(priority_update); |
| 858 | } |
bnc | 5c194f3 | 2021-02-05 08:15:05 -0800 | [diff] [blame] | 859 | void VisitAcceptCh(const SpdyAcceptChIR& accept_ch) override { |
| 860 | frame_ = framer_->SerializeAcceptCh(accept_ch); |
| 861 | } |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 862 | void VisitUnknown(const SpdyUnknownIR& unknown) override { |
| 863 | frame_ = framer_->SerializeUnknown(unknown); |
| 864 | } |
| 865 | |
| 866 | private: |
| 867 | SpdyFramer* framer_; |
| 868 | SpdySerializedFrame frame_; |
| 869 | }; |
| 870 | |
| 871 | // TODO(diannahu): Use also in frame serialization. |
| 872 | class FlagsSerializationVisitor : public SpdyFrameVisitor { |
| 873 | public: |
| 874 | void VisitData(const SpdyDataIR& data) override { |
| 875 | flags_ = DATA_FLAG_NONE; |
| 876 | if (data.fin()) { |
| 877 | flags_ |= DATA_FLAG_FIN; |
| 878 | } |
| 879 | if (data.padded()) { |
| 880 | flags_ |= DATA_FLAG_PADDED; |
| 881 | } |
| 882 | } |
| 883 | |
danzh | 8f3a576 | 2019-06-25 13:43:51 -0700 | [diff] [blame] | 884 | void VisitRstStream(const SpdyRstStreamIR& /*rst_stream*/) override { |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 885 | flags_ = kNoFlags; |
| 886 | } |
| 887 | |
| 888 | void VisitSettings(const SpdySettingsIR& settings) override { |
| 889 | flags_ = kNoFlags; |
| 890 | if (settings.is_ack()) { |
| 891 | flags_ |= SETTINGS_FLAG_ACK; |
| 892 | } |
| 893 | } |
| 894 | |
| 895 | void VisitPing(const SpdyPingIR& ping) override { |
| 896 | flags_ = kNoFlags; |
| 897 | if (ping.is_ack()) { |
| 898 | flags_ |= PING_FLAG_ACK; |
| 899 | } |
| 900 | } |
| 901 | |
danzh | 8f3a576 | 2019-06-25 13:43:51 -0700 | [diff] [blame] | 902 | void VisitGoAway(const SpdyGoAwayIR& /*goaway*/) override { |
| 903 | flags_ = kNoFlags; |
| 904 | } |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 905 | |
| 906 | // TODO(diannahu): The END_HEADERS flag is incorrect for HEADERS that require |
| 907 | // CONTINUATION frames. |
| 908 | void VisitHeaders(const SpdyHeadersIR& headers) override { |
| 909 | flags_ = HEADERS_FLAG_END_HEADERS; |
| 910 | if (headers.fin()) { |
| 911 | flags_ |= CONTROL_FLAG_FIN; |
| 912 | } |
| 913 | if (headers.padded()) { |
| 914 | flags_ |= HEADERS_FLAG_PADDED; |
| 915 | } |
| 916 | if (headers.has_priority()) { |
| 917 | flags_ |= HEADERS_FLAG_PRIORITY; |
| 918 | } |
| 919 | } |
| 920 | |
danzh | 8f3a576 | 2019-06-25 13:43:51 -0700 | [diff] [blame] | 921 | void VisitWindowUpdate(const SpdyWindowUpdateIR& /*window_update*/) override { |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 922 | flags_ = kNoFlags; |
| 923 | } |
| 924 | |
| 925 | // TODO(diannahu): The END_PUSH_PROMISE flag is incorrect for PUSH_PROMISEs |
| 926 | // that require CONTINUATION frames. |
| 927 | void VisitPushPromise(const SpdyPushPromiseIR& push_promise) override { |
| 928 | flags_ = PUSH_PROMISE_FLAG_END_PUSH_PROMISE; |
| 929 | if (push_promise.padded()) { |
| 930 | flags_ |= PUSH_PROMISE_FLAG_PADDED; |
| 931 | } |
| 932 | } |
| 933 | |
| 934 | // TODO(diannahu): The END_HEADERS flag is incorrect for CONTINUATIONs that |
| 935 | // require CONTINUATION frames. |
danzh | 8f3a576 | 2019-06-25 13:43:51 -0700 | [diff] [blame] | 936 | void VisitContinuation(const SpdyContinuationIR& /*continuation*/) override { |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 937 | flags_ = HEADERS_FLAG_END_HEADERS; |
| 938 | } |
| 939 | |
danzh | 8f3a576 | 2019-06-25 13:43:51 -0700 | [diff] [blame] | 940 | void VisitAltSvc(const SpdyAltSvcIR& /*altsvc*/) override { |
| 941 | flags_ = kNoFlags; |
| 942 | } |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 943 | |
danzh | 8f3a576 | 2019-06-25 13:43:51 -0700 | [diff] [blame] | 944 | void VisitPriority(const SpdyPriorityIR& /*priority*/) override { |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 945 | flags_ = kNoFlags; |
| 946 | } |
| 947 | |
bnc | 19423c7 | 2020-12-15 07:51:45 -0800 | [diff] [blame] | 948 | void VisitPriorityUpdate( |
| 949 | const SpdyPriorityUpdateIR& /*priority_update*/) override { |
| 950 | flags_ = kNoFlags; |
| 951 | } |
| 952 | |
bnc | 5c194f3 | 2021-02-05 08:15:05 -0800 | [diff] [blame] | 953 | void VisitAcceptCh(const SpdyAcceptChIR& /*accept_ch*/) override { |
| 954 | flags_ = kNoFlags; |
| 955 | } |
| 956 | |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 957 | uint8_t flags() const { return flags_; } |
| 958 | |
| 959 | private: |
| 960 | uint8_t flags_ = kNoFlags; |
| 961 | }; |
| 962 | |
| 963 | } // namespace |
| 964 | |
| 965 | SpdySerializedFrame SpdyFramer::SerializeFrame(const SpdyFrameIR& frame) { |
| 966 | FrameSerializationVisitor visitor(this); |
| 967 | frame.Visit(&visitor); |
| 968 | return visitor.ReleaseSerializedFrame(); |
| 969 | } |
| 970 | |
| 971 | uint8_t SpdyFramer::GetSerializedFlags(const SpdyFrameIR& frame) { |
| 972 | FlagsSerializationVisitor visitor; |
| 973 | frame.Visit(&visitor); |
| 974 | return visitor.flags(); |
| 975 | } |
| 976 | |
| 977 | bool SpdyFramer::SerializeData(const SpdyDataIR& data_ir, |
| 978 | ZeroCopyOutputBuffer* output) const { |
| 979 | uint8_t flags = DATA_FLAG_NONE; |
| 980 | int num_padding_fields = 0; |
| 981 | size_t size_with_padding = 0; |
| 982 | SerializeDataBuilderHelper(data_ir, &flags, &num_padding_fields, |
| 983 | &size_with_padding); |
| 984 | SpdyFrameBuilder builder(size_with_padding, output); |
| 985 | |
| 986 | bool ok = |
| 987 | builder.BeginNewFrame(SpdyFrameType::DATA, flags, data_ir.stream_id()); |
| 988 | |
| 989 | if (data_ir.padded()) { |
| 990 | ok = ok && builder.WriteUInt8(data_ir.padding_payload_len() & 0xff); |
| 991 | } |
| 992 | |
| 993 | ok = ok && builder.WriteBytes(data_ir.data(), data_ir.data_len()); |
| 994 | if (data_ir.padding_payload_len() > 0) { |
bnc | 4471291 | 2019-08-15 18:58:14 -0700 | [diff] [blame] | 995 | std::string padding; |
| 996 | padding = std::string(data_ir.padding_payload_len(), 0); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 997 | ok = ok && builder.WriteBytes(padding.data(), padding.length()); |
| 998 | } |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 999 | QUICHE_DCHECK_EQ(size_with_padding, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1000 | return ok; |
| 1001 | } |
| 1002 | |
| 1003 | bool SpdyFramer::SerializeDataFrameHeaderWithPaddingLengthField( |
| 1004 | const SpdyDataIR& data_ir, |
| 1005 | ZeroCopyOutputBuffer* output) const { |
| 1006 | uint8_t flags = DATA_FLAG_NONE; |
| 1007 | size_t frame_size = 0; |
| 1008 | size_t num_padding_fields = 0; |
| 1009 | SerializeDataFrameHeaderWithPaddingLengthFieldBuilderHelper( |
| 1010 | data_ir, &flags, &frame_size, &num_padding_fields); |
| 1011 | |
| 1012 | SpdyFrameBuilder builder(frame_size, output); |
| 1013 | bool ok = true; |
| 1014 | ok = ok && |
| 1015 | builder.BeginNewFrame(SpdyFrameType::DATA, flags, data_ir.stream_id(), |
| 1016 | num_padding_fields + data_ir.data_len() + |
| 1017 | data_ir.padding_payload_len()); |
| 1018 | if (data_ir.padded()) { |
| 1019 | ok = ok && builder.WriteUInt8(data_ir.padding_payload_len() & 0xff); |
| 1020 | } |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 1021 | QUICHE_DCHECK_EQ(frame_size, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1022 | return ok; |
| 1023 | } |
| 1024 | |
| 1025 | bool SpdyFramer::SerializeRstStream(const SpdyRstStreamIR& rst_stream, |
| 1026 | ZeroCopyOutputBuffer* output) const { |
| 1027 | size_t expected_length = kRstStreamFrameSize; |
| 1028 | SpdyFrameBuilder builder(expected_length, output); |
| 1029 | bool ok = builder.BeginNewFrame(SpdyFrameType::RST_STREAM, 0, |
| 1030 | rst_stream.stream_id()); |
| 1031 | ok = ok && builder.WriteUInt32(rst_stream.error_code()); |
| 1032 | |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 1033 | QUICHE_DCHECK_EQ(expected_length, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1034 | return ok; |
| 1035 | } |
| 1036 | |
| 1037 | bool SpdyFramer::SerializeSettings(const SpdySettingsIR& settings, |
| 1038 | ZeroCopyOutputBuffer* output) const { |
| 1039 | uint8_t flags = 0; |
| 1040 | // Size, in bytes, of this SETTINGS frame. |
| 1041 | size_t size = 0; |
| 1042 | const SettingsMap* values = &(settings.values()); |
| 1043 | SerializeSettingsBuilderHelper(settings, &flags, values, &size); |
| 1044 | SpdyFrameBuilder builder(size, output); |
| 1045 | bool ok = builder.BeginNewFrame(SpdyFrameType::SETTINGS, flags, 0); |
| 1046 | |
| 1047 | // If this is an ACK, payload should be empty. |
| 1048 | if (settings.is_ack()) { |
| 1049 | return ok; |
| 1050 | } |
| 1051 | |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 1052 | QUICHE_DCHECK_EQ(kSettingsFrameMinimumSize, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1053 | for (auto it = values->begin(); it != values->end(); ++it) { |
| 1054 | int setting_id = it->first; |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 1055 | QUICHE_DCHECK_GE(setting_id, 0); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1056 | ok = ok && builder.WriteUInt16(static_cast<SpdySettingsId>(setting_id)) && |
| 1057 | builder.WriteUInt32(it->second); |
| 1058 | } |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 1059 | QUICHE_DCHECK_EQ(size, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1060 | return ok; |
| 1061 | } |
| 1062 | |
| 1063 | bool SpdyFramer::SerializePing(const SpdyPingIR& ping, |
| 1064 | ZeroCopyOutputBuffer* output) const { |
| 1065 | SpdyFrameBuilder builder(kPingFrameSize, output); |
| 1066 | uint8_t flags = 0; |
| 1067 | if (ping.is_ack()) { |
| 1068 | flags |= PING_FLAG_ACK; |
| 1069 | } |
| 1070 | bool ok = builder.BeginNewFrame(SpdyFrameType::PING, flags, 0); |
| 1071 | ok = ok && builder.WriteUInt64(ping.id()); |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 1072 | QUICHE_DCHECK_EQ(kPingFrameSize, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1073 | return ok; |
| 1074 | } |
| 1075 | |
| 1076 | bool SpdyFramer::SerializeGoAway(const SpdyGoAwayIR& goaway, |
| 1077 | ZeroCopyOutputBuffer* output) const { |
| 1078 | // Compute the output buffer size, take opaque data into account. |
| 1079 | size_t expected_length = kGoawayFrameMinimumSize; |
| 1080 | expected_length += goaway.description().size(); |
| 1081 | SpdyFrameBuilder builder(expected_length, output); |
| 1082 | |
| 1083 | // Serialize the GOAWAY frame. |
| 1084 | bool ok = builder.BeginNewFrame(SpdyFrameType::GOAWAY, 0, 0); |
| 1085 | |
| 1086 | // GOAWAY frames specify the last good stream id. |
| 1087 | ok = ok && builder.WriteUInt32(goaway.last_good_stream_id()) && |
| 1088 | // GOAWAY frames also specify the error status code. |
| 1089 | builder.WriteUInt32(goaway.error_code()); |
| 1090 | |
| 1091 | // GOAWAY frames may also specify opaque data. |
| 1092 | if (!goaway.description().empty()) { |
| 1093 | ok = ok && builder.WriteBytes(goaway.description().data(), |
| 1094 | goaway.description().size()); |
| 1095 | } |
| 1096 | |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 1097 | QUICHE_DCHECK_EQ(expected_length, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1098 | return ok; |
| 1099 | } |
| 1100 | |
| 1101 | bool SpdyFramer::SerializeHeaders(const SpdyHeadersIR& headers, |
| 1102 | ZeroCopyOutputBuffer* output) { |
| 1103 | uint8_t flags = 0; |
| 1104 | // The size of this frame, including padding (if there is any) and |
| 1105 | // variable-length header block. |
| 1106 | size_t size = 0; |
bnc | 4471291 | 2019-08-15 18:58:14 -0700 | [diff] [blame] | 1107 | std::string hpack_encoding; |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1108 | int weight = 0; |
| 1109 | size_t length_field = 0; |
| 1110 | SerializeHeadersBuilderHelper(headers, &flags, &size, &hpack_encoding, |
| 1111 | &weight, &length_field); |
| 1112 | |
| 1113 | bool ok = true; |
| 1114 | SpdyFrameBuilder builder(size, output); |
| 1115 | ok = ok && builder.BeginNewFrame(SpdyFrameType::HEADERS, flags, |
| 1116 | headers.stream_id(), length_field); |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 1117 | QUICHE_DCHECK_EQ(kHeadersFrameMinimumSize, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1118 | |
| 1119 | int padding_payload_len = 0; |
| 1120 | if (headers.padded()) { |
| 1121 | ok = ok && builder.WriteUInt8(headers.padding_payload_len()); |
| 1122 | padding_payload_len = headers.padding_payload_len(); |
| 1123 | } |
| 1124 | if (headers.has_priority()) { |
| 1125 | ok = ok && |
| 1126 | builder.WriteUInt32(PackStreamDependencyValues( |
| 1127 | headers.exclusive(), headers.parent_stream_id())) && |
| 1128 | // Per RFC 7540 section 6.3, serialized weight value is weight - 1. |
| 1129 | builder.WriteUInt8(weight - 1); |
| 1130 | } |
| 1131 | ok = ok && WritePayloadWithContinuation( |
| 1132 | &builder, hpack_encoding, headers.stream_id(), |
| 1133 | SpdyFrameType::HEADERS, padding_payload_len); |
| 1134 | |
| 1135 | if (debug_visitor_) { |
| 1136 | const size_t header_list_size = |
| 1137 | GetUncompressedSerializedLength(headers.header_block()); |
| 1138 | debug_visitor_->OnSendCompressedFrame(headers.stream_id(), |
| 1139 | SpdyFrameType::HEADERS, |
| 1140 | header_list_size, builder.length()); |
| 1141 | } |
| 1142 | |
| 1143 | return ok; |
| 1144 | } |
| 1145 | |
| 1146 | bool SpdyFramer::SerializeWindowUpdate(const SpdyWindowUpdateIR& window_update, |
| 1147 | ZeroCopyOutputBuffer* output) const { |
| 1148 | SpdyFrameBuilder builder(kWindowUpdateFrameSize, output); |
| 1149 | bool ok = builder.BeginNewFrame(SpdyFrameType::WINDOW_UPDATE, kNoFlags, |
| 1150 | window_update.stream_id()); |
| 1151 | ok = ok && builder.WriteUInt32(window_update.delta()); |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 1152 | QUICHE_DCHECK_EQ(kWindowUpdateFrameSize, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1153 | return ok; |
| 1154 | } |
| 1155 | |
| 1156 | bool SpdyFramer::SerializePushPromise(const SpdyPushPromiseIR& push_promise, |
| 1157 | ZeroCopyOutputBuffer* output) { |
| 1158 | uint8_t flags = 0; |
| 1159 | size_t size = 0; |
bnc | 4471291 | 2019-08-15 18:58:14 -0700 | [diff] [blame] | 1160 | std::string hpack_encoding; |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1161 | SerializePushPromiseBuilderHelper(push_promise, &flags, &hpack_encoding, |
| 1162 | &size); |
| 1163 | |
| 1164 | bool ok = true; |
| 1165 | SpdyFrameBuilder builder(size, output); |
| 1166 | size_t length = |
| 1167 | std::min(size, kHttp2MaxControlFrameSendSize) - kFrameHeaderSize; |
| 1168 | ok = builder.BeginNewFrame(SpdyFrameType::PUSH_PROMISE, flags, |
| 1169 | push_promise.stream_id(), length); |
| 1170 | |
| 1171 | int padding_payload_len = 0; |
| 1172 | if (push_promise.padded()) { |
| 1173 | ok = ok && builder.WriteUInt8(push_promise.padding_payload_len()) && |
| 1174 | builder.WriteUInt32(push_promise.promised_stream_id()); |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 1175 | QUICHE_DCHECK_EQ(kPushPromiseFrameMinimumSize + kPadLengthFieldSize, |
| 1176 | builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1177 | |
| 1178 | padding_payload_len = push_promise.padding_payload_len(); |
| 1179 | } else { |
| 1180 | ok = ok && builder.WriteUInt32(push_promise.promised_stream_id()); |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 1181 | QUICHE_DCHECK_EQ(kPushPromiseFrameMinimumSize, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1182 | } |
| 1183 | |
| 1184 | ok = ok && WritePayloadWithContinuation( |
| 1185 | &builder, hpack_encoding, push_promise.stream_id(), |
| 1186 | SpdyFrameType::PUSH_PROMISE, padding_payload_len); |
| 1187 | |
| 1188 | if (debug_visitor_) { |
| 1189 | const size_t header_list_size = |
| 1190 | GetUncompressedSerializedLength(push_promise.header_block()); |
| 1191 | debug_visitor_->OnSendCompressedFrame(push_promise.stream_id(), |
| 1192 | SpdyFrameType::PUSH_PROMISE, |
| 1193 | header_list_size, builder.length()); |
| 1194 | } |
| 1195 | |
| 1196 | return ok; |
| 1197 | } |
| 1198 | |
| 1199 | bool SpdyFramer::SerializeContinuation(const SpdyContinuationIR& continuation, |
| 1200 | ZeroCopyOutputBuffer* output) const { |
bnc | 4471291 | 2019-08-15 18:58:14 -0700 | [diff] [blame] | 1201 | const std::string& encoding = continuation.encoding(); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1202 | size_t frame_size = kContinuationFrameMinimumSize + encoding.size(); |
| 1203 | SpdyFrameBuilder builder(frame_size, output); |
| 1204 | uint8_t flags = continuation.end_headers() ? HEADERS_FLAG_END_HEADERS : 0; |
| 1205 | bool ok = builder.BeginNewFrame(SpdyFrameType::CONTINUATION, flags, |
| 1206 | continuation.stream_id(), |
| 1207 | frame_size - kFrameHeaderSize); |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 1208 | QUICHE_DCHECK_EQ(kFrameHeaderSize, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1209 | |
| 1210 | ok = ok && builder.WriteBytes(encoding.data(), encoding.size()); |
| 1211 | return ok; |
| 1212 | } |
| 1213 | |
| 1214 | bool SpdyFramer::SerializeAltSvc(const SpdyAltSvcIR& altsvc_ir, |
| 1215 | ZeroCopyOutputBuffer* output) { |
bnc | 4471291 | 2019-08-15 18:58:14 -0700 | [diff] [blame] | 1216 | std::string value; |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1217 | size_t size = 0; |
| 1218 | SerializeAltSvcBuilderHelper(altsvc_ir, &value, &size); |
| 1219 | SpdyFrameBuilder builder(size, output); |
| 1220 | bool ok = builder.BeginNewFrame(SpdyFrameType::ALTSVC, kNoFlags, |
| 1221 | altsvc_ir.stream_id()) && |
| 1222 | builder.WriteUInt16(altsvc_ir.origin().length()) && |
| 1223 | builder.WriteBytes(altsvc_ir.origin().data(), |
| 1224 | altsvc_ir.origin().length()) && |
| 1225 | builder.WriteBytes(value.data(), value.length()); |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 1226 | QUICHE_DCHECK_LT(kGetAltSvcFrameMinimumSize, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1227 | return ok; |
| 1228 | } |
| 1229 | |
| 1230 | bool SpdyFramer::SerializePriority(const SpdyPriorityIR& priority, |
| 1231 | ZeroCopyOutputBuffer* output) const { |
| 1232 | SpdyFrameBuilder builder(kPriorityFrameSize, output); |
| 1233 | bool ok = builder.BeginNewFrame(SpdyFrameType::PRIORITY, kNoFlags, |
| 1234 | priority.stream_id()); |
| 1235 | ok = ok && |
| 1236 | builder.WriteUInt32(PackStreamDependencyValues( |
| 1237 | priority.exclusive(), priority.parent_stream_id())) && |
| 1238 | // Per RFC 7540 section 6.3, serialized weight value is actual value - 1. |
| 1239 | builder.WriteUInt8(priority.weight() - 1); |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 1240 | QUICHE_DCHECK_EQ(kPriorityFrameSize, builder.length()); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1241 | return ok; |
| 1242 | } |
| 1243 | |
bnc | 19423c7 | 2020-12-15 07:51:45 -0800 | [diff] [blame] | 1244 | bool SpdyFramer::SerializePriorityUpdate( |
| 1245 | const SpdyPriorityUpdateIR& priority_update, |
| 1246 | ZeroCopyOutputBuffer* output) const { |
| 1247 | const size_t total_size = kPriorityUpdateFrameMinimumSize + |
| 1248 | priority_update.priority_field_value().size(); |
| 1249 | SpdyFrameBuilder builder(total_size, output); |
| 1250 | bool ok = builder.BeginNewFrame(SpdyFrameType::PRIORITY_UPDATE, kNoFlags, |
| 1251 | priority_update.stream_id()); |
| 1252 | |
| 1253 | ok = ok && builder.WriteUInt32(priority_update.prioritized_stream_id()); |
| 1254 | ok = ok && builder.WriteBytes(priority_update.priority_field_value().data(), |
| 1255 | priority_update.priority_field_value().size()); |
vasilvv | ed4f308 | 2021-02-01 14:29:40 -0800 | [diff] [blame] | 1256 | QUICHE_DCHECK_EQ(total_size, builder.length()); |
bnc | 19423c7 | 2020-12-15 07:51:45 -0800 | [diff] [blame] | 1257 | return ok; |
| 1258 | } |
| 1259 | |
bnc | 5c194f3 | 2021-02-05 08:15:05 -0800 | [diff] [blame] | 1260 | bool SpdyFramer::SerializeAcceptCh(const SpdyAcceptChIR& accept_ch, |
| 1261 | ZeroCopyOutputBuffer* output) const { |
| 1262 | const size_t total_size = accept_ch.size(); |
| 1263 | SpdyFrameBuilder builder(total_size, output); |
| 1264 | bool ok = builder.BeginNewFrame(SpdyFrameType::ACCEPT_CH, kNoFlags, |
| 1265 | accept_ch.stream_id()); |
| 1266 | |
bnc | 8cfb52b | 2021-02-05 13:49:00 -0800 | [diff] [blame] | 1267 | for (const AcceptChOriginValuePair& entry : accept_ch.entries()) { |
bnc | 5c194f3 | 2021-02-05 08:15:05 -0800 | [diff] [blame] | 1268 | ok = ok && builder.WriteUInt16(entry.origin.size()); |
| 1269 | ok = ok && builder.WriteBytes(entry.origin.data(), entry.origin.size()); |
| 1270 | ok = ok && builder.WriteUInt16(entry.value.size()); |
| 1271 | ok = ok && builder.WriteBytes(entry.value.data(), entry.value.size()); |
| 1272 | } |
| 1273 | |
| 1274 | QUICHE_DCHECK_EQ(total_size, builder.length()); |
| 1275 | return ok; |
| 1276 | } |
| 1277 | |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1278 | bool SpdyFramer::SerializeUnknown(const SpdyUnknownIR& unknown, |
| 1279 | ZeroCopyOutputBuffer* output) const { |
| 1280 | const size_t total_size = kFrameHeaderSize + unknown.payload().size(); |
| 1281 | SpdyFrameBuilder builder(total_size, output); |
| 1282 | bool ok = builder.BeginNewUncheckedFrame( |
| 1283 | unknown.type(), unknown.flags(), unknown.stream_id(), unknown.length()); |
| 1284 | ok = ok && |
| 1285 | builder.WriteBytes(unknown.payload().data(), unknown.payload().size()); |
| 1286 | return ok; |
| 1287 | } |
| 1288 | |
| 1289 | namespace { |
| 1290 | |
| 1291 | class FrameSerializationVisitorWithOutput : public SpdyFrameVisitor { |
| 1292 | public: |
| 1293 | explicit FrameSerializationVisitorWithOutput(SpdyFramer* framer, |
| 1294 | ZeroCopyOutputBuffer* output) |
| 1295 | : framer_(framer), output_(output), result_(false) {} |
| 1296 | ~FrameSerializationVisitorWithOutput() override = default; |
| 1297 | |
| 1298 | size_t Result() { return result_; } |
| 1299 | |
| 1300 | void VisitData(const SpdyDataIR& data) override { |
| 1301 | result_ = framer_->SerializeData(data, output_); |
| 1302 | } |
| 1303 | void VisitRstStream(const SpdyRstStreamIR& rst_stream) override { |
| 1304 | result_ = framer_->SerializeRstStream(rst_stream, output_); |
| 1305 | } |
| 1306 | void VisitSettings(const SpdySettingsIR& settings) override { |
| 1307 | result_ = framer_->SerializeSettings(settings, output_); |
| 1308 | } |
| 1309 | void VisitPing(const SpdyPingIR& ping) override { |
| 1310 | result_ = framer_->SerializePing(ping, output_); |
| 1311 | } |
| 1312 | void VisitGoAway(const SpdyGoAwayIR& goaway) override { |
| 1313 | result_ = framer_->SerializeGoAway(goaway, output_); |
| 1314 | } |
| 1315 | void VisitHeaders(const SpdyHeadersIR& headers) override { |
| 1316 | result_ = framer_->SerializeHeaders(headers, output_); |
| 1317 | } |
| 1318 | void VisitWindowUpdate(const SpdyWindowUpdateIR& window_update) override { |
| 1319 | result_ = framer_->SerializeWindowUpdate(window_update, output_); |
| 1320 | } |
| 1321 | void VisitPushPromise(const SpdyPushPromiseIR& push_promise) override { |
| 1322 | result_ = framer_->SerializePushPromise(push_promise, output_); |
| 1323 | } |
| 1324 | void VisitContinuation(const SpdyContinuationIR& continuation) override { |
| 1325 | result_ = framer_->SerializeContinuation(continuation, output_); |
| 1326 | } |
| 1327 | void VisitAltSvc(const SpdyAltSvcIR& altsvc) override { |
| 1328 | result_ = framer_->SerializeAltSvc(altsvc, output_); |
| 1329 | } |
| 1330 | void VisitPriority(const SpdyPriorityIR& priority) override { |
| 1331 | result_ = framer_->SerializePriority(priority, output_); |
| 1332 | } |
bnc | 19423c7 | 2020-12-15 07:51:45 -0800 | [diff] [blame] | 1333 | void VisitPriorityUpdate( |
| 1334 | const SpdyPriorityUpdateIR& priority_update) override { |
| 1335 | result_ = framer_->SerializePriorityUpdate(priority_update, output_); |
| 1336 | } |
bnc | 5c194f3 | 2021-02-05 08:15:05 -0800 | [diff] [blame] | 1337 | void VisitAcceptCh(const SpdyAcceptChIR& accept_ch) override { |
| 1338 | result_ = framer_->SerializeAcceptCh(accept_ch, output_); |
| 1339 | } |
| 1340 | |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1341 | void VisitUnknown(const SpdyUnknownIR& unknown) override { |
| 1342 | result_ = framer_->SerializeUnknown(unknown, output_); |
| 1343 | } |
| 1344 | |
| 1345 | private: |
| 1346 | SpdyFramer* framer_; |
| 1347 | ZeroCopyOutputBuffer* output_; |
| 1348 | bool result_; |
| 1349 | }; |
| 1350 | |
| 1351 | } // namespace |
| 1352 | |
| 1353 | size_t SpdyFramer::SerializeFrame(const SpdyFrameIR& frame, |
| 1354 | ZeroCopyOutputBuffer* output) { |
| 1355 | FrameSerializationVisitorWithOutput visitor(this, output); |
| 1356 | size_t free_bytes_before = output->BytesFree(); |
| 1357 | frame.Visit(&visitor); |
| 1358 | return visitor.Result() ? free_bytes_before - output->BytesFree() : 0; |
| 1359 | } |
| 1360 | |
| 1361 | HpackEncoder* SpdyFramer::GetHpackEncoder() { |
| 1362 | if (hpack_encoder_ == nullptr) { |
bnc | e3fd686 | 2020-10-02 12:46:15 -0700 | [diff] [blame] | 1363 | hpack_encoder_ = std::make_unique<HpackEncoder>(); |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1364 | if (!compression_enabled()) { |
| 1365 | hpack_encoder_->DisableCompression(); |
| 1366 | } |
| 1367 | } |
| 1368 | return hpack_encoder_.get(); |
| 1369 | } |
| 1370 | |
| 1371 | void SpdyFramer::UpdateHeaderEncoderTableSize(uint32_t value) { |
| 1372 | GetHpackEncoder()->ApplyHeaderTableSizeSetting(value); |
| 1373 | } |
| 1374 | |
| 1375 | size_t SpdyFramer::header_encoder_table_size() const { |
| 1376 | if (hpack_encoder_ == nullptr) { |
| 1377 | return kDefaultHeaderTableSizeSetting; |
| 1378 | } else { |
| 1379 | return hpack_encoder_->CurrentHeaderTableSizeSetting(); |
| 1380 | } |
| 1381 | } |
| 1382 | |
QUICHE team | 82dee2f | 2019-01-18 12:35:12 -0500 | [diff] [blame] | 1383 | size_t SpdyFramer::EstimateMemoryUsage() const { |
| 1384 | return SpdyEstimateMemoryUsage(hpack_encoder_); |
| 1385 | } |
| 1386 | |
| 1387 | } // namespace spdy |