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