• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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