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_protocol.h"
6
7 #include <cstddef>
8 #include <cstdint>
9 #include <limits>
10 #include <memory>
11 #include <ostream>
12 #include <string>
13 #include <utility>
14
15 #include "absl/strings/str_cat.h"
16 #include "absl/strings/string_view.h"
17 #include "quiche/common/platform/api/quiche_bug_tracker.h"
18 #include "quiche/common/platform/api/quiche_flag_utils.h"
19 #include "quiche/common/platform/api/quiche_logging.h"
20 #include "quiche/spdy/core/http2_header_block.h"
21 #include "quiche/spdy/core/spdy_alt_svc_wire_format.h"
22
23 namespace spdy {
24
25 const char* const kHttp2ConnectionHeaderPrefix =
26 "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
27
operator <<(std::ostream & out,SpdyKnownSettingsId id)28 std::ostream& operator<<(std::ostream& out, SpdyKnownSettingsId id) {
29 return out << static_cast<SpdySettingsId>(id);
30 }
31
operator <<(std::ostream & out,SpdyFrameType frame_type)32 std::ostream& operator<<(std::ostream& out, SpdyFrameType frame_type) {
33 return out << SerializeFrameType(frame_type);
34 }
35
ClampSpdy3Priority(SpdyPriority priority)36 SpdyPriority ClampSpdy3Priority(SpdyPriority priority) {
37 static_assert(std::numeric_limits<SpdyPriority>::min() == kV3HighestPriority,
38 "The value of given priority shouldn't be smaller than highest "
39 "priority. Check this invariant explicitly.");
40 if (priority > kV3LowestPriority) {
41 QUICHE_BUG(spdy_bug_22_1)
42 << "Invalid priority: " << static_cast<int>(priority);
43 return kV3LowestPriority;
44 }
45 return priority;
46 }
47
ClampHttp2Weight(int weight)48 int ClampHttp2Weight(int weight) {
49 if (weight < kHttp2MinStreamWeight) {
50 QUICHE_BUG(spdy_bug_22_2) << "Invalid weight: " << weight;
51 return kHttp2MinStreamWeight;
52 }
53 if (weight > kHttp2MaxStreamWeight) {
54 QUICHE_BUG(spdy_bug_22_3) << "Invalid weight: " << weight;
55 return kHttp2MaxStreamWeight;
56 }
57 return weight;
58 }
59
Spdy3PriorityToHttp2Weight(SpdyPriority priority)60 int Spdy3PriorityToHttp2Weight(SpdyPriority priority) {
61 priority = ClampSpdy3Priority(priority);
62 const float kSteps = 255.9f / 7.f;
63 return static_cast<int>(kSteps * (7.f - priority)) + 1;
64 }
65
Http2WeightToSpdy3Priority(int weight)66 SpdyPriority Http2WeightToSpdy3Priority(int weight) {
67 weight = ClampHttp2Weight(weight);
68 const float kSteps = 255.9f / 7.f;
69 return static_cast<SpdyPriority>(7.f - (weight - 1) / kSteps);
70 }
71
IsDefinedFrameType(uint8_t frame_type_field)72 bool IsDefinedFrameType(uint8_t frame_type_field) {
73 switch (static_cast<SpdyFrameType>(frame_type_field)) {
74 case SpdyFrameType::DATA:
75 return true;
76 case SpdyFrameType::HEADERS:
77 return true;
78 case SpdyFrameType::PRIORITY:
79 return true;
80 case SpdyFrameType::RST_STREAM:
81 return true;
82 case SpdyFrameType::SETTINGS:
83 return true;
84 case SpdyFrameType::PUSH_PROMISE:
85 return true;
86 case SpdyFrameType::PING:
87 return true;
88 case SpdyFrameType::GOAWAY:
89 return true;
90 case SpdyFrameType::WINDOW_UPDATE:
91 return true;
92 case SpdyFrameType::CONTINUATION:
93 return true;
94 case SpdyFrameType::ALTSVC:
95 return true;
96 case SpdyFrameType::PRIORITY_UPDATE:
97 return true;
98 case SpdyFrameType::ACCEPT_CH:
99 return true;
100 }
101 return false;
102 }
103
ParseFrameType(uint8_t frame_type_field)104 SpdyFrameType ParseFrameType(uint8_t frame_type_field) {
105 QUICHE_BUG_IF(spdy_bug_22_4, !IsDefinedFrameType(frame_type_field))
106 << "Frame type not defined: " << static_cast<int>(frame_type_field);
107 return static_cast<SpdyFrameType>(frame_type_field);
108 }
109
SerializeFrameType(SpdyFrameType frame_type)110 uint8_t SerializeFrameType(SpdyFrameType frame_type) {
111 return static_cast<uint8_t>(frame_type);
112 }
113
IsValidHTTP2FrameStreamId(SpdyStreamId current_frame_stream_id,SpdyFrameType frame_type_field)114 bool IsValidHTTP2FrameStreamId(SpdyStreamId current_frame_stream_id,
115 SpdyFrameType frame_type_field) {
116 if (current_frame_stream_id == 0) {
117 switch (frame_type_field) {
118 case SpdyFrameType::DATA:
119 case SpdyFrameType::HEADERS:
120 case SpdyFrameType::PRIORITY:
121 case SpdyFrameType::RST_STREAM:
122 case SpdyFrameType::CONTINUATION:
123 case SpdyFrameType::PUSH_PROMISE:
124 // These frame types must specify a stream
125 return false;
126 default:
127 return true;
128 }
129 } else {
130 switch (frame_type_field) {
131 case SpdyFrameType::GOAWAY:
132 case SpdyFrameType::SETTINGS:
133 case SpdyFrameType::PING:
134 // These frame types must not specify a stream
135 return false;
136 default:
137 return true;
138 }
139 }
140 }
141
FrameTypeToString(SpdyFrameType frame_type)142 const char* FrameTypeToString(SpdyFrameType frame_type) {
143 switch (frame_type) {
144 case SpdyFrameType::DATA:
145 return "DATA";
146 case SpdyFrameType::RST_STREAM:
147 return "RST_STREAM";
148 case SpdyFrameType::SETTINGS:
149 return "SETTINGS";
150 case SpdyFrameType::PING:
151 return "PING";
152 case SpdyFrameType::GOAWAY:
153 return "GOAWAY";
154 case SpdyFrameType::HEADERS:
155 return "HEADERS";
156 case SpdyFrameType::WINDOW_UPDATE:
157 return "WINDOW_UPDATE";
158 case SpdyFrameType::PUSH_PROMISE:
159 return "PUSH_PROMISE";
160 case SpdyFrameType::CONTINUATION:
161 return "CONTINUATION";
162 case SpdyFrameType::PRIORITY:
163 return "PRIORITY";
164 case SpdyFrameType::ALTSVC:
165 return "ALTSVC";
166 case SpdyFrameType::PRIORITY_UPDATE:
167 return "PRIORITY_UPDATE";
168 case SpdyFrameType::ACCEPT_CH:
169 return "ACCEPT_CH";
170 }
171 return "UNKNOWN_FRAME_TYPE";
172 }
173
ParseSettingsId(SpdySettingsId wire_setting_id,SpdyKnownSettingsId * setting_id)174 bool ParseSettingsId(SpdySettingsId wire_setting_id,
175 SpdyKnownSettingsId* setting_id) {
176 if (wire_setting_id != SETTINGS_EXPERIMENT_SCHEDULER &&
177 (wire_setting_id < SETTINGS_MIN || wire_setting_id > SETTINGS_MAX)) {
178 return false;
179 }
180
181 *setting_id = static_cast<SpdyKnownSettingsId>(wire_setting_id);
182 // This switch ensures that the casted value is valid. The default case is
183 // explicitly omitted to have compile-time guarantees that new additions to
184 // |SpdyKnownSettingsId| must also be handled here.
185 switch (*setting_id) {
186 case SETTINGS_HEADER_TABLE_SIZE:
187 case SETTINGS_ENABLE_PUSH:
188 case SETTINGS_MAX_CONCURRENT_STREAMS:
189 case SETTINGS_INITIAL_WINDOW_SIZE:
190 case SETTINGS_MAX_FRAME_SIZE:
191 case SETTINGS_MAX_HEADER_LIST_SIZE:
192 case SETTINGS_ENABLE_CONNECT_PROTOCOL:
193 case SETTINGS_DEPRECATE_HTTP2_PRIORITIES:
194 case SETTINGS_EXPERIMENT_SCHEDULER:
195 return true;
196 }
197 return false;
198 }
199
SettingsIdToString(SpdySettingsId id)200 std::string SettingsIdToString(SpdySettingsId id) {
201 SpdyKnownSettingsId known_id;
202 if (!ParseSettingsId(id, &known_id)) {
203 return absl::StrCat("SETTINGS_UNKNOWN_", absl::Hex(uint32_t{id}));
204 }
205
206 switch (known_id) {
207 case SETTINGS_HEADER_TABLE_SIZE:
208 return "SETTINGS_HEADER_TABLE_SIZE";
209 case SETTINGS_ENABLE_PUSH:
210 return "SETTINGS_ENABLE_PUSH";
211 case SETTINGS_MAX_CONCURRENT_STREAMS:
212 return "SETTINGS_MAX_CONCURRENT_STREAMS";
213 case SETTINGS_INITIAL_WINDOW_SIZE:
214 return "SETTINGS_INITIAL_WINDOW_SIZE";
215 case SETTINGS_MAX_FRAME_SIZE:
216 return "SETTINGS_MAX_FRAME_SIZE";
217 case SETTINGS_MAX_HEADER_LIST_SIZE:
218 return "SETTINGS_MAX_HEADER_LIST_SIZE";
219 case SETTINGS_ENABLE_CONNECT_PROTOCOL:
220 return "SETTINGS_ENABLE_CONNECT_PROTOCOL";
221 case SETTINGS_DEPRECATE_HTTP2_PRIORITIES:
222 return "SETTINGS_DEPRECATE_HTTP2_PRIORITIES";
223 case SETTINGS_EXPERIMENT_SCHEDULER:
224 return "SETTINGS_EXPERIMENT_SCHEDULER";
225 }
226
227 return absl::StrCat("SETTINGS_UNKNOWN_", absl::Hex(uint32_t{id}));
228 }
229
ParseErrorCode(uint32_t wire_error_code)230 SpdyErrorCode ParseErrorCode(uint32_t wire_error_code) {
231 if (wire_error_code > ERROR_CODE_MAX) {
232 return ERROR_CODE_INTERNAL_ERROR;
233 }
234
235 return static_cast<SpdyErrorCode>(wire_error_code);
236 }
237
ErrorCodeToString(SpdyErrorCode error_code)238 const char* ErrorCodeToString(SpdyErrorCode error_code) {
239 switch (error_code) {
240 case ERROR_CODE_NO_ERROR:
241 return "NO_ERROR";
242 case ERROR_CODE_PROTOCOL_ERROR:
243 return "PROTOCOL_ERROR";
244 case ERROR_CODE_INTERNAL_ERROR:
245 return "INTERNAL_ERROR";
246 case ERROR_CODE_FLOW_CONTROL_ERROR:
247 return "FLOW_CONTROL_ERROR";
248 case ERROR_CODE_SETTINGS_TIMEOUT:
249 return "SETTINGS_TIMEOUT";
250 case ERROR_CODE_STREAM_CLOSED:
251 return "STREAM_CLOSED";
252 case ERROR_CODE_FRAME_SIZE_ERROR:
253 return "FRAME_SIZE_ERROR";
254 case ERROR_CODE_REFUSED_STREAM:
255 return "REFUSED_STREAM";
256 case ERROR_CODE_CANCEL:
257 return "CANCEL";
258 case ERROR_CODE_COMPRESSION_ERROR:
259 return "COMPRESSION_ERROR";
260 case ERROR_CODE_CONNECT_ERROR:
261 return "CONNECT_ERROR";
262 case ERROR_CODE_ENHANCE_YOUR_CALM:
263 return "ENHANCE_YOUR_CALM";
264 case ERROR_CODE_INADEQUATE_SECURITY:
265 return "INADEQUATE_SECURITY";
266 case ERROR_CODE_HTTP_1_1_REQUIRED:
267 return "HTTP_1_1_REQUIRED";
268 }
269 return "UNKNOWN_ERROR_CODE";
270 }
271
WriteSchedulerTypeToString(WriteSchedulerType type)272 const char* WriteSchedulerTypeToString(WriteSchedulerType type) {
273 switch (type) {
274 case WriteSchedulerType::LIFO:
275 return "LIFO";
276 case WriteSchedulerType::SPDY:
277 return "SPDY";
278 case WriteSchedulerType::HTTP2:
279 return "HTTP2";
280 case WriteSchedulerType::FIFO:
281 return "FIFO";
282 }
283 return "UNKNOWN";
284 }
285
GetNumberRequiredContinuationFrames(size_t size)286 size_t GetNumberRequiredContinuationFrames(size_t size) {
287 QUICHE_DCHECK_GT(size, kHttp2MaxControlFrameSendSize);
288 size_t overflow = size - kHttp2MaxControlFrameSendSize;
289 int payload_size =
290 kHttp2MaxControlFrameSendSize - kContinuationFrameMinimumSize;
291 // This is ceiling(overflow/payload_size) using integer arithmetics.
292 return (overflow - 1) / payload_size + 1;
293 }
294
295 const char* const kHttp2Npn = "h2";
296
297 const char* const kHttp2AuthorityHeader = ":authority";
298 const char* const kHttp2MethodHeader = ":method";
299 const char* const kHttp2PathHeader = ":path";
300 const char* const kHttp2SchemeHeader = ":scheme";
301 const char* const kHttp2ProtocolHeader = ":protocol";
302
303 const char* const kHttp2StatusHeader = ":status";
304
fin() const305 bool SpdyFrameIR::fin() const { return false; }
306
flow_control_window_consumed() const307 int SpdyFrameIR::flow_control_window_consumed() const { return 0; }
308
fin() const309 bool SpdyFrameWithFinIR::fin() const { return fin_; }
310
SpdyFrameWithHeaderBlockIR(SpdyStreamId stream_id,Http2HeaderBlock header_block)311 SpdyFrameWithHeaderBlockIR::SpdyFrameWithHeaderBlockIR(
312 SpdyStreamId stream_id, Http2HeaderBlock header_block)
313 : SpdyFrameWithFinIR(stream_id), header_block_(std::move(header_block)) {}
314
315 SpdyFrameWithHeaderBlockIR::~SpdyFrameWithHeaderBlockIR() = default;
316
SpdyDataIR(SpdyStreamId stream_id,absl::string_view data)317 SpdyDataIR::SpdyDataIR(SpdyStreamId stream_id, absl::string_view data)
318 : SpdyFrameWithFinIR(stream_id),
319 data_(nullptr),
320 data_len_(0),
321 padded_(false),
322 padding_payload_len_(0) {
323 SetDataDeep(data);
324 }
325
SpdyDataIR(SpdyStreamId stream_id,const char * data)326 SpdyDataIR::SpdyDataIR(SpdyStreamId stream_id, const char* data)
327 : SpdyDataIR(stream_id, absl::string_view(data)) {}
328
SpdyDataIR(SpdyStreamId stream_id,std::string data)329 SpdyDataIR::SpdyDataIR(SpdyStreamId stream_id, std::string data)
330 : SpdyFrameWithFinIR(stream_id),
331 data_store_(std::make_unique<std::string>(std::move(data))),
332 data_(data_store_->data()),
333 data_len_(data_store_->size()),
334 padded_(false),
335 padding_payload_len_(0) {}
336
SpdyDataIR(SpdyStreamId stream_id)337 SpdyDataIR::SpdyDataIR(SpdyStreamId stream_id)
338 : SpdyFrameWithFinIR(stream_id),
339 data_(nullptr),
340 data_len_(0),
341 padded_(false),
342 padding_payload_len_(0) {}
343
344 SpdyDataIR::~SpdyDataIR() = default;
345
Visit(SpdyFrameVisitor * visitor) const346 void SpdyDataIR::Visit(SpdyFrameVisitor* visitor) const {
347 return visitor->VisitData(*this);
348 }
349
frame_type() const350 SpdyFrameType SpdyDataIR::frame_type() const { return SpdyFrameType::DATA; }
351
flow_control_window_consumed() const352 int SpdyDataIR::flow_control_window_consumed() const {
353 return padded_ ? 1 + padding_payload_len_ + data_len_ : data_len_;
354 }
355
size() const356 size_t SpdyDataIR::size() const {
357 return kFrameHeaderSize +
358 (padded() ? 1 + padding_payload_len() + data_len() : data_len());
359 }
360
SpdyRstStreamIR(SpdyStreamId stream_id,SpdyErrorCode error_code)361 SpdyRstStreamIR::SpdyRstStreamIR(SpdyStreamId stream_id,
362 SpdyErrorCode error_code)
363 : SpdyFrameIR(stream_id) {
364 set_error_code(error_code);
365 }
366
367 SpdyRstStreamIR::~SpdyRstStreamIR() = default;
368
Visit(SpdyFrameVisitor * visitor) const369 void SpdyRstStreamIR::Visit(SpdyFrameVisitor* visitor) const {
370 return visitor->VisitRstStream(*this);
371 }
372
frame_type() const373 SpdyFrameType SpdyRstStreamIR::frame_type() const {
374 return SpdyFrameType::RST_STREAM;
375 }
376
size() const377 size_t SpdyRstStreamIR::size() const { return kRstStreamFrameSize; }
378
SpdySettingsIR()379 SpdySettingsIR::SpdySettingsIR() : is_ack_(false) {}
380
381 SpdySettingsIR::~SpdySettingsIR() = default;
382
Visit(SpdyFrameVisitor * visitor) const383 void SpdySettingsIR::Visit(SpdyFrameVisitor* visitor) const {
384 return visitor->VisitSettings(*this);
385 }
386
frame_type() const387 SpdyFrameType SpdySettingsIR::frame_type() const {
388 return SpdyFrameType::SETTINGS;
389 }
390
size() const391 size_t SpdySettingsIR::size() const {
392 return kFrameHeaderSize + values_.size() * kSettingsOneSettingSize;
393 }
394
Visit(SpdyFrameVisitor * visitor) const395 void SpdyPingIR::Visit(SpdyFrameVisitor* visitor) const {
396 return visitor->VisitPing(*this);
397 }
398
frame_type() const399 SpdyFrameType SpdyPingIR::frame_type() const { return SpdyFrameType::PING; }
400
size() const401 size_t SpdyPingIR::size() const { return kPingFrameSize; }
402
SpdyGoAwayIR(SpdyStreamId last_good_stream_id,SpdyErrorCode error_code,absl::string_view description)403 SpdyGoAwayIR::SpdyGoAwayIR(SpdyStreamId last_good_stream_id,
404 SpdyErrorCode error_code,
405 absl::string_view description)
406 : description_(description) {
407 set_last_good_stream_id(last_good_stream_id);
408 set_error_code(error_code);
409 }
410
SpdyGoAwayIR(SpdyStreamId last_good_stream_id,SpdyErrorCode error_code,const char * description)411 SpdyGoAwayIR::SpdyGoAwayIR(SpdyStreamId last_good_stream_id,
412 SpdyErrorCode error_code, const char* description)
413 : SpdyGoAwayIR(last_good_stream_id, error_code,
414 absl::string_view(description)) {}
415
SpdyGoAwayIR(SpdyStreamId last_good_stream_id,SpdyErrorCode error_code,std::string description)416 SpdyGoAwayIR::SpdyGoAwayIR(SpdyStreamId last_good_stream_id,
417 SpdyErrorCode error_code, std::string description)
418 : description_store_(std::move(description)),
419 description_(description_store_) {
420 set_last_good_stream_id(last_good_stream_id);
421 set_error_code(error_code);
422 }
423
424 SpdyGoAwayIR::~SpdyGoAwayIR() = default;
425
Visit(SpdyFrameVisitor * visitor) const426 void SpdyGoAwayIR::Visit(SpdyFrameVisitor* visitor) const {
427 return visitor->VisitGoAway(*this);
428 }
429
frame_type() const430 SpdyFrameType SpdyGoAwayIR::frame_type() const { return SpdyFrameType::GOAWAY; }
431
size() const432 size_t SpdyGoAwayIR::size() const {
433 return kGoawayFrameMinimumSize + description_.size();
434 }
435
SpdyContinuationIR(SpdyStreamId stream_id)436 SpdyContinuationIR::SpdyContinuationIR(SpdyStreamId stream_id)
437 : SpdyFrameIR(stream_id), end_headers_(false) {}
438
439 SpdyContinuationIR::~SpdyContinuationIR() = default;
440
Visit(SpdyFrameVisitor * visitor) const441 void SpdyContinuationIR::Visit(SpdyFrameVisitor* visitor) const {
442 return visitor->VisitContinuation(*this);
443 }
444
frame_type() const445 SpdyFrameType SpdyContinuationIR::frame_type() const {
446 return SpdyFrameType::CONTINUATION;
447 }
448
size() const449 size_t SpdyContinuationIR::size() const {
450 // We don't need to get the size of CONTINUATION frame directly. It is
451 // calculated in HEADERS or PUSH_PROMISE frame.
452 QUICHE_DLOG(WARNING) << "Shouldn't not call size() for CONTINUATION frame.";
453 return 0;
454 }
455
Visit(SpdyFrameVisitor * visitor) const456 void SpdyHeadersIR::Visit(SpdyFrameVisitor* visitor) const {
457 return visitor->VisitHeaders(*this);
458 }
459
frame_type() const460 SpdyFrameType SpdyHeadersIR::frame_type() const {
461 return SpdyFrameType::HEADERS;
462 }
463
size() const464 size_t SpdyHeadersIR::size() const {
465 size_t size = kHeadersFrameMinimumSize;
466
467 if (padded_) {
468 // Padding field length.
469 size += 1;
470 size += padding_payload_len_;
471 }
472
473 if (has_priority_) {
474 size += 5;
475 }
476
477 // TODO(b/322146543): Remove `hpack_overhead` with deprecation of
478 // --gfe2_reloadable_flag_http2_add_hpack_overhead_bytes2.
479 size_t hpack_overhead = kPerHeaderHpackOverheadOld;
480 if (add_hpack_overhead_bytes_) {
481 QUICHE_RELOADABLE_FLAG_COUNT(http2_add_hpack_overhead_bytes2);
482 hpack_overhead = kPerHeaderHpackOverheadNew;
483 }
484 // Assume no hpack encoding is applied.
485 size +=
486 header_block().TotalBytesUsed() + header_block().size() * hpack_overhead;
487 if (size > kHttp2MaxControlFrameSendSize) {
488 size += GetNumberRequiredContinuationFrames(size) *
489 kContinuationFrameMinimumSize;
490 }
491 return size;
492 }
493
Visit(SpdyFrameVisitor * visitor) const494 void SpdyWindowUpdateIR::Visit(SpdyFrameVisitor* visitor) const {
495 return visitor->VisitWindowUpdate(*this);
496 }
497
frame_type() const498 SpdyFrameType SpdyWindowUpdateIR::frame_type() const {
499 return SpdyFrameType::WINDOW_UPDATE;
500 }
501
size() const502 size_t SpdyWindowUpdateIR::size() const { return kWindowUpdateFrameSize; }
503
Visit(SpdyFrameVisitor * visitor) const504 void SpdyPushPromiseIR::Visit(SpdyFrameVisitor* visitor) const {
505 return visitor->VisitPushPromise(*this);
506 }
507
frame_type() const508 SpdyFrameType SpdyPushPromiseIR::frame_type() const {
509 return SpdyFrameType::PUSH_PROMISE;
510 }
511
size() const512 size_t SpdyPushPromiseIR::size() const {
513 size_t size = kPushPromiseFrameMinimumSize;
514
515 if (padded_) {
516 // Padding length field.
517 size += 1;
518 size += padding_payload_len_;
519 }
520
521 size += header_block().TotalBytesUsed();
522 if (size > kHttp2MaxControlFrameSendSize) {
523 size += GetNumberRequiredContinuationFrames(size) *
524 kContinuationFrameMinimumSize;
525 }
526 return size;
527 }
528
SpdyAltSvcIR(SpdyStreamId stream_id)529 SpdyAltSvcIR::SpdyAltSvcIR(SpdyStreamId stream_id) : SpdyFrameIR(stream_id) {}
530
531 SpdyAltSvcIR::~SpdyAltSvcIR() = default;
532
Visit(SpdyFrameVisitor * visitor) const533 void SpdyAltSvcIR::Visit(SpdyFrameVisitor* visitor) const {
534 return visitor->VisitAltSvc(*this);
535 }
536
frame_type() const537 SpdyFrameType SpdyAltSvcIR::frame_type() const { return SpdyFrameType::ALTSVC; }
538
size() const539 size_t SpdyAltSvcIR::size() const {
540 size_t size = kGetAltSvcFrameMinimumSize;
541 size += origin_.length();
542 // TODO(yasong): estimates the size without serializing the vector.
543 std::string str =
544 SpdyAltSvcWireFormat::SerializeHeaderFieldValue(altsvc_vector_);
545 size += str.size();
546 return size;
547 }
548
Visit(SpdyFrameVisitor * visitor) const549 void SpdyPriorityIR::Visit(SpdyFrameVisitor* visitor) const {
550 return visitor->VisitPriority(*this);
551 }
552
frame_type() const553 SpdyFrameType SpdyPriorityIR::frame_type() const {
554 return SpdyFrameType::PRIORITY;
555 }
556
size() const557 size_t SpdyPriorityIR::size() const { return kPriorityFrameSize; }
558
Visit(SpdyFrameVisitor * visitor) const559 void SpdyPriorityUpdateIR::Visit(SpdyFrameVisitor* visitor) const {
560 return visitor->VisitPriorityUpdate(*this);
561 }
562
frame_type() const563 SpdyFrameType SpdyPriorityUpdateIR::frame_type() const {
564 return SpdyFrameType::PRIORITY_UPDATE;
565 }
566
size() const567 size_t SpdyPriorityUpdateIR::size() const {
568 return kPriorityUpdateFrameMinimumSize + priority_field_value_.size();
569 }
570
Visit(SpdyFrameVisitor * visitor) const571 void SpdyAcceptChIR::Visit(SpdyFrameVisitor* visitor) const {
572 return visitor->VisitAcceptCh(*this);
573 }
574
frame_type() const575 SpdyFrameType SpdyAcceptChIR::frame_type() const {
576 return SpdyFrameType::ACCEPT_CH;
577 }
578
size() const579 size_t SpdyAcceptChIR::size() const {
580 size_t total_size = kAcceptChFrameMinimumSize;
581 for (const AcceptChOriginValuePair& entry : entries_) {
582 total_size += entry.origin.size() + entry.value.size() +
583 kAcceptChFramePerEntryOverhead;
584 }
585 return total_size;
586 }
587
Visit(SpdyFrameVisitor * visitor) const588 void SpdyUnknownIR::Visit(SpdyFrameVisitor* visitor) const {
589 return visitor->VisitUnknown(*this);
590 }
591
frame_type() const592 SpdyFrameType SpdyUnknownIR::frame_type() const {
593 return static_cast<SpdyFrameType>(type());
594 }
595
size() const596 size_t SpdyUnknownIR::size() const {
597 return kFrameHeaderSize + payload_.size();
598 }
599
flow_control_window_consumed() const600 int SpdyUnknownIR::flow_control_window_consumed() const {
601 if (frame_type() == SpdyFrameType::DATA) {
602 return payload_.size();
603 } else {
604 return 0;
605 }
606 }
607
608 // Wire size of pad length field.
609 const size_t kPadLengthFieldSize = 1;
610
GetHeaderFrameSizeSansBlock(const SpdyHeadersIR & header_ir)611 size_t GetHeaderFrameSizeSansBlock(const SpdyHeadersIR& header_ir) {
612 size_t min_size = kFrameHeaderSize;
613 if (header_ir.padded()) {
614 min_size += kPadLengthFieldSize;
615 min_size += header_ir.padding_payload_len();
616 }
617 if (header_ir.has_priority()) {
618 min_size += 5;
619 }
620 return min_size;
621 }
622
GetPushPromiseFrameSizeSansBlock(const SpdyPushPromiseIR & push_promise_ir)623 size_t GetPushPromiseFrameSizeSansBlock(
624 const SpdyPushPromiseIR& push_promise_ir) {
625 size_t min_size = kPushPromiseFrameMinimumSize;
626 if (push_promise_ir.padded()) {
627 min_size += kPadLengthFieldSize;
628 min_size += push_promise_ir.padding_payload_len();
629 }
630 return min_size;
631 }
632
633 } // namespace spdy
634