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