OLD | NEW |
| (Empty) |
1 // Copyright 2016 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 "net/spdy/spdy_deframer_visitor.h" | |
6 | |
7 #include <stdlib.h> | |
8 | |
9 #include <algorithm> | |
10 #include <cstdint> | |
11 #include <limits> | |
12 | |
13 #include "base/logging.h" | |
14 #include "base/memory/ptr_util.h" | |
15 #include "net/spdy/hpack/hpack_constants.h" | |
16 #include "net/spdy/mock_spdy_framer_visitor.h" | |
17 #include "net/spdy/spdy_frame_builder.h" | |
18 #include "net/spdy/spdy_frame_reader.h" | |
19 #include "net/spdy/spdy_protocol.h" | |
20 #include "net/spdy/spdy_test_utils.h" | |
21 | |
22 using ::base::MakeUnique; | |
23 using ::testing::AssertionFailure; | |
24 using ::testing::AssertionResult; | |
25 using ::testing::AssertionSuccess; | |
26 | |
27 namespace net { | |
28 namespace test { | |
29 | |
30 // Specify whether to process headers as request or response in visitor-related | |
31 // params. | |
32 enum class HeaderDirection { REQUEST, RESPONSE }; | |
33 | |
34 // Types of HTTP/2 frames, per RFC 7540. | |
35 // TODO(jamessynge): Switch to using //net/http2/http2_constants.h when ready. | |
36 enum Http2FrameType { | |
37 DATA = 0, | |
38 HEADERS = 1, | |
39 PRIORITY = 2, | |
40 RST_STREAM = 3, | |
41 SETTINGS = 4, | |
42 PUSH_PROMISE = 5, | |
43 PING = 6, | |
44 GOAWAY = 7, | |
45 WINDOW_UPDATE = 8, | |
46 CONTINUATION = 9, | |
47 ALTSVC = 10, | |
48 | |
49 // Not a frame type. | |
50 UNSET = -1, | |
51 UNKNOWN = -2, | |
52 }; | |
53 | |
54 // TODO(jamessynge): Switch to using //net/http2/http2_constants.h when ready. | |
55 const char* Http2FrameTypeToString(Http2FrameType v) { | |
56 switch (v) { | |
57 case DATA: | |
58 return "DATA"; | |
59 case HEADERS: | |
60 return "HEADERS"; | |
61 case PRIORITY: | |
62 return "PRIORITY"; | |
63 case RST_STREAM: | |
64 return "RST_STREAM"; | |
65 case SETTINGS: | |
66 return "SETTINGS"; | |
67 case PUSH_PROMISE: | |
68 return "PUSH_PROMISE"; | |
69 case PING: | |
70 return "PING"; | |
71 case GOAWAY: | |
72 return "GOAWAY"; | |
73 case WINDOW_UPDATE: | |
74 return "WINDOW_UPDATE"; | |
75 case CONTINUATION: | |
76 return "CONTINUATION"; | |
77 case ALTSVC: | |
78 return "ALTSVC"; | |
79 case UNSET: | |
80 return "UNSET"; | |
81 case UNKNOWN: | |
82 return "UNKNOWN"; | |
83 default: | |
84 return "Invalid Http2FrameType"; | |
85 } | |
86 } | |
87 | |
88 // TODO(jamessynge): Switch to using //net/http2/http2_constants.h when ready. | |
89 inline std::ostream& operator<<(std::ostream& out, Http2FrameType v) { | |
90 return out << Http2FrameTypeToString(v); | |
91 } | |
92 | |
93 // Flag bits in the flag field of the common header of HTTP/2 frames | |
94 // (see https://httpwg.github.io/specs/rfc7540.html#FrameHeader for details on | |
95 // the fixed 9-octet header structure shared by all frames). | |
96 // Flag bits are only valid for specified frame types. | |
97 // TODO(jamessynge): Switch to using //net/http2/http2_constants.h when ready. | |
98 enum Http2HeaderFlag { | |
99 NO_FLAGS = 0, | |
100 | |
101 END_STREAM_FLAG = 0x1, | |
102 ACK_FLAG = 0x1, | |
103 END_HEADERS_FLAG = 0x4, | |
104 PADDED_FLAG = 0x8, | |
105 PRIORITY_FLAG = 0x20, | |
106 }; | |
107 | |
108 // Returns name of frame type. | |
109 // TODO(jamessynge): Switch to using //net/http2/http2_constants.h when ready. | |
110 const char* Http2FrameTypeToString(Http2FrameType v); | |
111 | |
112 void SpdyDeframerVisitorInterface::OnPingAck( | |
113 std::unique_ptr<SpdyPingIR> frame) { | |
114 OnPing(std::move(frame)); | |
115 } | |
116 | |
117 void SpdyDeframerVisitorInterface::OnSettingsAck( | |
118 std::unique_ptr<SpdySettingsIR> frame) { | |
119 OnSettings(std::move(frame), nullptr); | |
120 } | |
121 | |
122 class SpdyTestDeframerImpl : public SpdyTestDeframer, | |
123 public SpdyHeadersHandlerInterface { | |
124 public: | |
125 explicit SpdyTestDeframerImpl( | |
126 std::unique_ptr<SpdyDeframerVisitorInterface> listener) | |
127 : listener_(std::move(listener)) { | |
128 CHECK(listener_); | |
129 } | |
130 ~SpdyTestDeframerImpl() override {} | |
131 | |
132 bool AtFrameEnd() override; | |
133 | |
134 // Callbacks defined in SpdyFramerVisitorInterface. These are in the | |
135 // alphabetical order for ease of navigation, and are not in same order | |
136 // as in SpdyFramerVisitorInterface. | |
137 void OnAltSvc(SpdyStreamId stream_id, | |
138 SpdyStringPiece origin, | |
139 const SpdyAltSvcWireFormat::AlternativeServiceVector& | |
140 altsvc_vector) override; | |
141 void OnContinuation(SpdyStreamId stream_id, bool end) override; | |
142 SpdyHeadersHandlerInterface* OnHeaderFrameStart( | |
143 SpdyStreamId stream_id) override; | |
144 void OnHeaderFrameEnd(SpdyStreamId stream_id, bool end_headers) override; | |
145 void OnDataFrameHeader(SpdyStreamId stream_id, | |
146 size_t length, | |
147 bool fin) override; | |
148 void OnError(SpdyFramer* framer) override; | |
149 void OnGoAway(SpdyStreamId last_accepted_stream_id, | |
150 SpdyErrorCode error_code) override; | |
151 bool OnGoAwayFrameData(const char* goaway_data, size_t len) override; | |
152 void OnHeaders(SpdyStreamId stream_id, | |
153 bool has_priority, | |
154 int weight, | |
155 SpdyStreamId parent_stream_id, | |
156 bool exclusive, | |
157 bool fin, | |
158 bool end) override; | |
159 void OnPing(SpdyPingId unique_id, bool is_ack) override; | |
160 void OnPriority(SpdyStreamId stream_id, | |
161 SpdyStreamId parent_stream_id, | |
162 int weight, | |
163 bool exclusive) override; | |
164 void OnPushPromise(SpdyStreamId stream_id, | |
165 SpdyStreamId promised_stream_id, | |
166 bool end) override; | |
167 void OnRstStream(SpdyStreamId stream_id, SpdyErrorCode error_code) override; | |
168 void OnSetting(SpdySettingsIds id, uint32_t value) override; | |
169 void OnSettings(bool clear_persisted) override; | |
170 void OnSettingsAck() override; | |
171 void OnSettingsEnd() override; | |
172 void OnStreamFrameData(SpdyStreamId stream_id, | |
173 const char* data, | |
174 size_t len) override; | |
175 void OnStreamEnd(SpdyStreamId stream_id) override; | |
176 void OnStreamPadding(SpdyStreamId stream_id, size_t len) override; | |
177 bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) override; | |
178 void OnWindowUpdate(SpdyStreamId stream_id, int delta_window_size) override; | |
179 | |
180 // Callbacks defined in SpdyHeadersHandlerInterface. | |
181 | |
182 void OnHeaderBlockStart() override; | |
183 void OnHeader(SpdyStringPiece key, SpdyStringPiece value) override; | |
184 void OnHeaderBlockEnd(size_t header_bytes_parsed) override; | |
185 void OnHeaderBlockEnd(size_t header_bytes_parsed, | |
186 size_t compressed_header_bytes_parsed) override; | |
187 | |
188 protected: | |
189 void AtDataEnd(); | |
190 void AtGoAwayEnd(); | |
191 void AtHeadersEnd(); | |
192 void AtPushPromiseEnd(); | |
193 | |
194 // Per-physical frame state. | |
195 // Frame type of the frame currently being processed. | |
196 Http2FrameType frame_type_ = UNSET; | |
197 // Stream id of the frame currently being processed. | |
198 SpdyStreamId stream_id_; | |
199 // Did the most recent frame header include the END_HEADERS flag? | |
200 bool end_ = false; | |
201 // Did the most recent frame header include the ack flag? | |
202 bool ack_ = false; | |
203 | |
204 // Per-HPACK block state. Only valid while processing a HEADERS or | |
205 // PUSH_PROMISE frame, and its CONTINUATION frames. | |
206 // Did the most recent HEADERS or PUSH_PROMISE include the END_STREAM flag? | |
207 // Note that this does not necessarily indicate that the current frame is | |
208 // the last frame for the stream (may be followed by CONTINUATION frames, | |
209 // may only half close). | |
210 bool fin_ = false; | |
211 bool got_hpack_end_ = false; | |
212 | |
213 std::unique_ptr<SpdyString> data_; | |
214 | |
215 // Total length of the data frame. | |
216 size_t data_len_ = 0; | |
217 | |
218 // Amount of skipped padding (i.e. total length of padding, including Pad | |
219 // Length field). | |
220 size_t padding_len_ = 0; | |
221 | |
222 std::unique_ptr<SpdyString> goaway_description_; | |
223 std::unique_ptr<StringPairVector> headers_; | |
224 std::unique_ptr<SettingVector> settings_; | |
225 std::unique_ptr<TestHeadersHandler> headers_handler_; | |
226 | |
227 std::unique_ptr<SpdyGoAwayIR> goaway_ir_; | |
228 std::unique_ptr<SpdyHeadersIR> headers_ir_; | |
229 std::unique_ptr<SpdyPushPromiseIR> push_promise_ir_; | |
230 std::unique_ptr<SpdySettingsIR> settings_ir_; | |
231 | |
232 private: | |
233 std::unique_ptr<SpdyDeframerVisitorInterface> listener_; | |
234 | |
235 DISALLOW_COPY_AND_ASSIGN(SpdyTestDeframerImpl); | |
236 }; | |
237 | |
238 // static | |
239 std::unique_ptr<SpdyTestDeframer> SpdyTestDeframer::CreateConverter( | |
240 std::unique_ptr<SpdyDeframerVisitorInterface> listener) { | |
241 return MakeUnique<SpdyTestDeframerImpl>(std::move(listener)); | |
242 } | |
243 | |
244 void SpdyTestDeframerImpl::AtDataEnd() { | |
245 DVLOG(1) << "AtDataEnd"; | |
246 CHECK_EQ(data_len_, padding_len_ + data_->size()); | |
247 auto ptr = MakeUnique<SpdyDataIR>(stream_id_, std::move(*data_)); | |
248 CHECK_EQ(0u, data_->size()); | |
249 data_.reset(); | |
250 | |
251 CHECK_LE(0u, padding_len_); | |
252 CHECK_LE(padding_len_, 256u); | |
253 if (padding_len_ > 0) { | |
254 ptr->set_padding_len(padding_len_); | |
255 } | |
256 padding_len_ = 0; | |
257 | |
258 ptr->set_fin(fin_); | |
259 listener_->OnData(std::move(ptr)); | |
260 frame_type_ = UNSET; | |
261 fin_ = false; | |
262 data_len_ = 0; | |
263 } | |
264 | |
265 void SpdyTestDeframerImpl::AtGoAwayEnd() { | |
266 DVLOG(1) << "AtDataEnd"; | |
267 CHECK_EQ(frame_type_, GOAWAY); | |
268 CHECK(goaway_description_); | |
269 if (goaway_description_->empty()) { | |
270 listener_->OnGoAway(std::move(goaway_ir_)); | |
271 } else { | |
272 listener_->OnGoAway(MakeUnique<SpdyGoAwayIR>( | |
273 goaway_ir_->last_good_stream_id(), goaway_ir_->error_code(), | |
274 std::move(*goaway_description_))); | |
275 CHECK_EQ(0u, goaway_description_->size()); | |
276 } | |
277 goaway_description_.reset(); | |
278 goaway_ir_.reset(); | |
279 frame_type_ = UNSET; | |
280 } | |
281 | |
282 void SpdyTestDeframerImpl::AtHeadersEnd() { | |
283 DVLOG(1) << "AtDataEnd"; | |
284 CHECK(frame_type_ == HEADERS || frame_type_ == CONTINUATION) | |
285 << " frame_type_=" << Http2FrameTypeToString(frame_type_); | |
286 CHECK(end_) << " frame_type_=" << Http2FrameTypeToString(frame_type_); | |
287 CHECK(got_hpack_end_); | |
288 | |
289 CHECK(headers_ir_); | |
290 CHECK(headers_); | |
291 CHECK(headers_handler_); | |
292 | |
293 CHECK_LE(0u, padding_len_); | |
294 CHECK_LE(padding_len_, 256u); | |
295 if (padding_len_ > 0) { | |
296 headers_ir_->set_padding_len(padding_len_); | |
297 } | |
298 padding_len_ = 0; | |
299 | |
300 headers_ir_->set_header_block(headers_handler_->decoded_block().Clone()); | |
301 headers_handler_.reset(); | |
302 listener_->OnHeaders(std::move(headers_ir_), std::move(headers_)); | |
303 | |
304 frame_type_ = UNSET; | |
305 fin_ = false; | |
306 end_ = false; | |
307 got_hpack_end_ = false; | |
308 } | |
309 | |
310 void SpdyTestDeframerImpl::AtPushPromiseEnd() { | |
311 DVLOG(1) << "AtDataEnd"; | |
312 CHECK(frame_type_ == PUSH_PROMISE || frame_type_ == CONTINUATION) | |
313 << " frame_type_=" << Http2FrameTypeToString(frame_type_); | |
314 CHECK(end_) << " frame_type_=" << Http2FrameTypeToString(frame_type_); | |
315 | |
316 CHECK(push_promise_ir_); | |
317 CHECK(headers_); | |
318 CHECK(headers_handler_); | |
319 | |
320 CHECK_EQ(headers_ir_.get(), nullptr); | |
321 | |
322 CHECK_LE(0u, padding_len_); | |
323 CHECK_LE(padding_len_, 256u); | |
324 if (padding_len_ > 0) { | |
325 push_promise_ir_->set_padding_len(padding_len_); | |
326 } | |
327 padding_len_ = 0; | |
328 | |
329 push_promise_ir_->set_header_block(headers_handler_->decoded_block().Clone()); | |
330 headers_handler_.reset(); | |
331 listener_->OnPushPromise(std::move(push_promise_ir_), std::move(headers_)); | |
332 | |
333 frame_type_ = UNSET; | |
334 end_ = false; | |
335 } | |
336 | |
337 bool SpdyTestDeframerImpl::AtFrameEnd() { | |
338 bool incomplete_logical_header = false; | |
339 // The caller says that the SpdyFrame has reached the end of the frame, | |
340 // so if we have any accumulated data, flush it. | |
341 switch (frame_type_) { | |
342 case DATA: | |
343 AtDataEnd(); | |
344 break; | |
345 | |
346 case GOAWAY: | |
347 AtGoAwayEnd(); | |
348 break; | |
349 | |
350 case HEADERS: | |
351 if (end_) { | |
352 AtHeadersEnd(); | |
353 } else { | |
354 incomplete_logical_header = true; | |
355 } | |
356 break; | |
357 | |
358 case PUSH_PROMISE: | |
359 if (end_) { | |
360 AtPushPromiseEnd(); | |
361 } else { | |
362 incomplete_logical_header = true; | |
363 } | |
364 break; | |
365 | |
366 case CONTINUATION: | |
367 if (end_) { | |
368 if (headers_ir_) { | |
369 AtHeadersEnd(); | |
370 } else if (push_promise_ir_) { | |
371 AtPushPromiseEnd(); | |
372 } else { | |
373 LOG(FATAL) << "Where is the SpdyFrameIR for the headers!"; | |
374 } | |
375 } else { | |
376 incomplete_logical_header = true; | |
377 } | |
378 break; | |
379 | |
380 case UNSET: | |
381 // Except for the frame types above, the others don't leave any record | |
382 // in the state of this object. Make sure nothing got left by accident. | |
383 CHECK_EQ(data_.get(), nullptr); | |
384 CHECK_EQ(goaway_description_.get(), nullptr); | |
385 CHECK_EQ(goaway_ir_.get(), nullptr); | |
386 CHECK_EQ(headers_.get(), nullptr); | |
387 CHECK_EQ(headers_handler_.get(), nullptr); | |
388 CHECK_EQ(headers_ir_.get(), nullptr); | |
389 CHECK_EQ(push_promise_ir_.get(), nullptr); | |
390 CHECK_EQ(settings_.get(), nullptr); | |
391 CHECK_EQ(settings_ir_.get(), nullptr); | |
392 break; | |
393 | |
394 default: | |
395 SPDY_BUG << "Expected UNSET, instead frame_type_==" << frame_type_; | |
396 return false; | |
397 } | |
398 frame_type_ = UNSET; | |
399 stream_id_ = 0; | |
400 end_ = false; | |
401 ack_ = false; | |
402 if (!incomplete_logical_header) { | |
403 fin_ = false; | |
404 } | |
405 return true; | |
406 } | |
407 | |
408 // Overridden methods from SpdyFramerVisitorInterface in alpha order... | |
409 | |
410 void SpdyTestDeframerImpl::OnAltSvc( | |
411 SpdyStreamId stream_id, | |
412 SpdyStringPiece origin, | |
413 const SpdyAltSvcWireFormat::AlternativeServiceVector& altsvc_vector) { | |
414 DVLOG(1) << "OnAltSvc stream_id: " << stream_id; | |
415 CHECK_EQ(frame_type_, UNSET) << " frame_type_=" | |
416 << Http2FrameTypeToString(frame_type_); | |
417 CHECK_GT(stream_id, 0u); | |
418 auto ptr = MakeUnique<SpdyAltSvcIR>(stream_id); | |
419 ptr->set_origin(SpdyString(origin)); | |
420 for (auto& altsvc : altsvc_vector) { | |
421 ptr->add_altsvc(altsvc); | |
422 } | |
423 listener_->OnAltSvc(std::move(ptr)); | |
424 } | |
425 | |
426 // A CONTINUATION frame contains a Header Block Fragment, and immediately | |
427 // follows another frame that contains a Header Block Fragment (HEADERS, | |
428 // PUSH_PROMISE or CONTINUATION). The last such frame has the END flag set. | |
429 // SpdyFramer ensures that the behavior is correct before calling the visitor. | |
430 void SpdyTestDeframerImpl::OnContinuation(SpdyStreamId stream_id, bool end) { | |
431 DVLOG(1) << "OnContinuation stream_id: " << stream_id; | |
432 CHECK_EQ(frame_type_, UNSET) << " frame_type_=" | |
433 << Http2FrameTypeToString(frame_type_); | |
434 CHECK_GT(stream_id, 0u); | |
435 CHECK_NE(nullptr, headers_.get()); | |
436 frame_type_ = CONTINUATION; | |
437 | |
438 stream_id_ = stream_id; | |
439 end_ = end; | |
440 } | |
441 | |
442 // Note that length includes the padding length (0 to 256, when the optional | |
443 // padding length field is counted). Padding comes after the payload, both | |
444 // for DATA frames and for control frames. | |
445 void SpdyTestDeframerImpl::OnDataFrameHeader(SpdyStreamId stream_id, | |
446 size_t length, | |
447 bool fin) { | |
448 DVLOG(1) << "OnDataFrameHeader stream_id: " << stream_id; | |
449 CHECK_EQ(frame_type_, UNSET) << " frame_type_=" | |
450 << Http2FrameTypeToString(frame_type_); | |
451 CHECK_GT(stream_id, 0u); | |
452 CHECK_EQ(data_.get(), nullptr); | |
453 frame_type_ = DATA; | |
454 | |
455 stream_id_ = stream_id; | |
456 fin_ = fin; | |
457 data_len_ = length; | |
458 data_.reset(new SpdyString()); | |
459 } | |
460 | |
461 // The SpdyFramer will not process any more data at this point. | |
462 void SpdyTestDeframerImpl::OnError(SpdyFramer* framer) { | |
463 DVLOG(1) << "SpdyFramer detected an error in the stream: " | |
464 << SpdyFramer::SpdyFramerErrorToString(framer->spdy_framer_error()) | |
465 << " frame_type_: " << Http2FrameTypeToString(frame_type_); | |
466 listener_->OnError(framer, this); | |
467 } | |
468 | |
469 // Received a GOAWAY frame from the peer. The last stream id it accepted from us | |
470 // is |last_accepted_stream_id|. |status| is a protocol defined error code. | |
471 // The frame may also contain data. After this OnGoAwayFrameData will be called | |
472 // for any non-zero amount of data, and after that it will be called with len==0 | |
473 // to indicate the end of the GOAWAY frame. | |
474 void SpdyTestDeframerImpl::OnGoAway(SpdyStreamId last_good_stream_id, | |
475 SpdyErrorCode error_code) { | |
476 DVLOG(1) << "OnGoAway last_good_stream_id: " << last_good_stream_id | |
477 << " error code: " << error_code; | |
478 CHECK_EQ(frame_type_, UNSET) << " frame_type_=" | |
479 << Http2FrameTypeToString(frame_type_); | |
480 frame_type_ = GOAWAY; | |
481 goaway_ir_ = MakeUnique<SpdyGoAwayIR>(last_good_stream_id, error_code, ""); | |
482 goaway_description_.reset(new SpdyString()); | |
483 } | |
484 | |
485 // If len==0 then we've reached the end of the GOAWAY frame. | |
486 bool SpdyTestDeframerImpl::OnGoAwayFrameData(const char* goaway_data, | |
487 size_t len) { | |
488 DVLOG(1) << "OnGoAwayFrameData"; | |
489 CHECK_EQ(frame_type_, GOAWAY) << " frame_type_=" | |
490 << Http2FrameTypeToString(frame_type_); | |
491 CHECK(goaway_description_); | |
492 goaway_description_->append(goaway_data, len); | |
493 return true; | |
494 } | |
495 | |
496 SpdyHeadersHandlerInterface* SpdyTestDeframerImpl::OnHeaderFrameStart( | |
497 SpdyStreamId stream_id) { | |
498 return this; | |
499 } | |
500 | |
501 void SpdyTestDeframerImpl::OnHeaderFrameEnd(SpdyStreamId stream_id, | |
502 bool end_headers) { | |
503 DVLOG(1) << "OnHeaderFrameEnd stream_id: " << stream_id | |
504 << " end_headers: " << (end_headers ? "true" : "false"); | |
505 } | |
506 | |
507 // Received the fixed portion of a HEADERS frame. Called before the variable | |
508 // length (including zero length) Header Block Fragment is processed. If fin | |
509 // is true then there will be no DATA or trailing HEADERS after this HEADERS | |
510 // frame. | |
511 // If end is true, then there will be no CONTINUATION frame(s) following this | |
512 // frame; else if true then there will be CONTINATION frames(s) immediately | |
513 // following this frame, terminated by a CONTINUATION frame with end==true. | |
514 void SpdyTestDeframerImpl::OnHeaders(SpdyStreamId stream_id, | |
515 bool has_priority, | |
516 int weight, | |
517 SpdyStreamId parent_stream_id, | |
518 bool exclusive, | |
519 bool fin, | |
520 bool end) { | |
521 DVLOG(1) << "OnHeaders stream_id: " << stream_id; | |
522 CHECK_EQ(frame_type_, UNSET) << " frame_type_=" | |
523 << Http2FrameTypeToString(frame_type_); | |
524 CHECK_GT(stream_id, 0u); | |
525 frame_type_ = HEADERS; | |
526 | |
527 stream_id_ = stream_id; | |
528 fin_ = fin; | |
529 end_ = end; | |
530 | |
531 headers_.reset(new StringPairVector()); | |
532 headers_handler_.reset(new TestHeadersHandler()); | |
533 headers_ir_ = MakeUnique<SpdyHeadersIR>(stream_id); | |
534 headers_ir_->set_fin(fin); | |
535 if (has_priority) { | |
536 headers_ir_->set_has_priority(true); | |
537 headers_ir_->set_weight(weight); | |
538 headers_ir_->set_parent_stream_id(parent_stream_id); | |
539 headers_ir_->set_exclusive(exclusive); | |
540 } | |
541 } | |
542 | |
543 // The HTTP/2 protocol refers to the payload, |unique_id| here, as 8 octets of | |
544 // opaque data that is to be echoed back to the sender, with the ACK bit added. | |
545 // It isn't defined as a counter, | |
546 // or frame id, as the SpdyPingId naming might imply. | |
547 // Responding to a PING is supposed to be at the highest priority. | |
548 void SpdyTestDeframerImpl::OnPing(uint64_t unique_id, bool is_ack) { | |
549 DVLOG(1) << "OnPing unique_id: " << unique_id | |
550 << " is_ack: " << (is_ack ? "true" : "false"); | |
551 CHECK_EQ(frame_type_, UNSET) << " frame_type_=" | |
552 << Http2FrameTypeToString(frame_type_); | |
553 auto ptr = MakeUnique<SpdyPingIR>(unique_id); | |
554 if (is_ack) { | |
555 ptr->set_is_ack(is_ack); | |
556 listener_->OnPingAck(std::move(ptr)); | |
557 } else { | |
558 listener_->OnPing(std::move(ptr)); | |
559 } | |
560 } | |
561 | |
562 void SpdyTestDeframerImpl::OnPriority(SpdyStreamId stream_id, | |
563 SpdyStreamId parent_stream_id, | |
564 int weight, | |
565 bool exclusive) { | |
566 DVLOG(1) << "OnPriority stream_id: " << stream_id; | |
567 CHECK_EQ(frame_type_, UNSET) << " frame_type_=" | |
568 << Http2FrameTypeToString(frame_type_); | |
569 CHECK_GT(stream_id, 0u); | |
570 | |
571 listener_->OnPriority(MakeUnique<SpdyPriorityIR>(stream_id, parent_stream_id, | |
572 weight, exclusive)); | |
573 } | |
574 | |
575 void SpdyTestDeframerImpl::OnPushPromise(SpdyStreamId stream_id, | |
576 SpdyStreamId promised_stream_id, | |
577 bool end) { | |
578 DVLOG(1) << "OnPushPromise stream_id: " << stream_id; | |
579 CHECK_EQ(frame_type_, UNSET) << " frame_type_=" | |
580 << Http2FrameTypeToString(frame_type_); | |
581 CHECK_GT(stream_id, 0u); | |
582 | |
583 frame_type_ = PUSH_PROMISE; | |
584 stream_id_ = stream_id; | |
585 end_ = end; | |
586 | |
587 headers_.reset(new StringPairVector()); | |
588 headers_handler_.reset(new TestHeadersHandler()); | |
589 push_promise_ir_ = | |
590 MakeUnique<SpdyPushPromiseIR>(stream_id, promised_stream_id); | |
591 } | |
592 | |
593 // Closes the specified stream. After this the sender may still send PRIORITY | |
594 // frames for this stream, which we can ignore. | |
595 void SpdyTestDeframerImpl::OnRstStream(SpdyStreamId stream_id, | |
596 SpdyErrorCode error_code) { | |
597 DVLOG(1) << "OnRstStream stream_id: " << stream_id | |
598 << " error code: " << error_code; | |
599 CHECK_EQ(frame_type_, UNSET) << " frame_type_=" | |
600 << Http2FrameTypeToString(frame_type_); | |
601 CHECK_GT(stream_id, 0u); | |
602 | |
603 listener_->OnRstStream(MakeUnique<SpdyRstStreamIR>(stream_id, error_code)); | |
604 } | |
605 | |
606 // Called for an individual setting. There is no negotiation, the sender is | |
607 // stating the value that the sender is using. | |
608 void SpdyTestDeframerImpl::OnSetting(SpdySettingsIds id, uint32_t value) { | |
609 DVLOG(1) << "OnSetting id: " << id << std::hex << " value: " << value; | |
610 CHECK_EQ(frame_type_, SETTINGS) << " frame_type_=" | |
611 << Http2FrameTypeToString(frame_type_); | |
612 CHECK(settings_); | |
613 settings_->push_back(std::make_pair(id, value)); | |
614 settings_ir_->AddSetting(id, value); | |
615 } | |
616 | |
617 // Called at the start of a SETTINGS frame with setting entries, but not the | |
618 // (required) ACK of a SETTINGS frame. There is no stream_id because | |
619 // the settings apply to the entire connection, not to an individual stream. | |
620 // The |clear_persisted| flag is a pre-HTTP/2 remnant. | |
621 void SpdyTestDeframerImpl::OnSettings(bool /*clear_persisted*/) { | |
622 DVLOG(1) << "OnSettings"; | |
623 CHECK_EQ(frame_type_, UNSET) << " frame_type_=" | |
624 << Http2FrameTypeToString(frame_type_); | |
625 CHECK_EQ(nullptr, settings_ir_.get()); | |
626 CHECK_EQ(nullptr, settings_.get()); | |
627 frame_type_ = SETTINGS; | |
628 ack_ = false; | |
629 | |
630 settings_.reset(new SettingVector()); | |
631 settings_ir_.reset(new SpdySettingsIR()); | |
632 } | |
633 | |
634 void SpdyTestDeframerImpl::OnSettingsAck() { | |
635 DVLOG(1) << "OnSettingsAck"; | |
636 CHECK_EQ(frame_type_, UNSET) << " frame_type_=" | |
637 << Http2FrameTypeToString(frame_type_); | |
638 auto ptr = MakeUnique<SpdySettingsIR>(); | |
639 ptr->set_is_ack(true); | |
640 listener_->OnSettingsAck(std::move(ptr)); | |
641 } | |
642 | |
643 void SpdyTestDeframerImpl::OnSettingsEnd() { | |
644 DVLOG(1) << "OnSettingsEnd"; | |
645 CHECK_EQ(frame_type_, SETTINGS) << " frame_type_=" | |
646 << Http2FrameTypeToString(frame_type_); | |
647 CHECK(!ack_); | |
648 CHECK_NE(nullptr, settings_ir_.get()); | |
649 CHECK_NE(nullptr, settings_.get()); | |
650 listener_->OnSettings(std::move(settings_ir_), std::move(settings_)); | |
651 frame_type_ = UNSET; | |
652 } | |
653 | |
654 // Called for a zero length DATA frame with the END_STREAM flag set, or at the | |
655 // end a complete HPACK block (and its padding) that started with a HEADERS | |
656 // frame with the END_STREAM flag set. Doesn't apply to PUSH_PROMISE frames | |
657 // because they don't have END_STREAM flags. | |
658 void SpdyTestDeframerImpl::OnStreamEnd(SpdyStreamId stream_id) { | |
659 DVLOG(1) << "OnStreamEnd stream_id: " << stream_id; | |
660 CHECK_EQ(stream_id_, stream_id); | |
661 CHECK(frame_type_ == DATA || frame_type_ == HEADERS || | |
662 frame_type_ == CONTINUATION) | |
663 << " frame_type_=" << Http2FrameTypeToString(frame_type_); | |
664 CHECK(fin_); | |
665 } | |
666 | |
667 // The data arg points into the non-padding payload of a DATA frame. | |
668 // This must be a DATA frame (i.e. this method will not be | |
669 // called for HEADERS or CONTINUATION frames). | |
670 // This method may be called multiple times for a single DATA frame, depending | |
671 // upon buffer boundaries. | |
672 void SpdyTestDeframerImpl::OnStreamFrameData(SpdyStreamId stream_id, | |
673 const char* data, | |
674 size_t len) { | |
675 DVLOG(1) << "OnStreamFrameData stream_id: " << stream_id | |
676 << " len: " << len; | |
677 CHECK_EQ(stream_id_, stream_id); | |
678 CHECK_EQ(frame_type_, DATA); | |
679 data_->append(data, len); | |
680 } | |
681 | |
682 // Called when padding is skipped over, including the padding length field at | |
683 // the start of the frame payload, and the actual padding at the end. len will | |
684 // be in the range 1 to 255. | |
685 void SpdyTestDeframerImpl::OnStreamPadding(SpdyStreamId stream_id, size_t len) { | |
686 DVLOG(1) << "OnStreamPadding stream_id: " << stream_id << " len: " << len; | |
687 CHECK(frame_type_ == DATA || frame_type_ == HEADERS || | |
688 frame_type_ == PUSH_PROMISE) | |
689 << " frame_type_=" << Http2FrameTypeToString(frame_type_); | |
690 CHECK_EQ(stream_id_, stream_id); | |
691 CHECK_LE(1u, len); | |
692 CHECK_GE(255u, len); | |
693 padding_len_ += len; | |
694 CHECK_LE(padding_len_, 256u) << "len=" << len; | |
695 } | |
696 | |
697 // WINDOW_UPDATE is supposed to be hop-by-hop, according to the spec. | |
698 // stream_id is 0 if the update applies to the connection, else stream_id | |
699 // will be the id of a stream previously seen, which maybe half or fully | |
700 // closed. | |
701 void SpdyTestDeframerImpl::OnWindowUpdate(SpdyStreamId stream_id, | |
702 int delta_window_size) { | |
703 DVLOG(1) << "OnWindowUpdate stream_id: " << stream_id | |
704 << " delta_window_size: " << delta_window_size; | |
705 CHECK_EQ(frame_type_, UNSET) << " frame_type_=" | |
706 << Http2FrameTypeToString(frame_type_); | |
707 CHECK_NE(0, delta_window_size); | |
708 | |
709 listener_->OnWindowUpdate( | |
710 MakeUnique<SpdyWindowUpdateIR>(stream_id, delta_window_size)); | |
711 } | |
712 | |
713 // Return true to indicate that the stream_id is valid; if not valid then | |
714 // SpdyFramer considers the connection corrupted. Requires keeping track | |
715 // of the set of currently open streams. For now we'll assume that unknown | |
716 // frame types are unsupported. | |
717 bool SpdyTestDeframerImpl::OnUnknownFrame(SpdyStreamId stream_id, | |
718 uint8_t frame_type) { | |
719 DVLOG(1) << "OnAltSvc stream_id: " << stream_id; | |
720 CHECK_EQ(frame_type_, UNSET) << " frame_type_=" | |
721 << Http2FrameTypeToString(frame_type_); | |
722 frame_type_ = UNKNOWN; | |
723 | |
724 stream_id_ = stream_id; | |
725 return false; | |
726 } | |
727 | |
728 // Callbacks defined in SpdyHeadersHandlerInterface. | |
729 | |
730 void SpdyTestDeframerImpl::OnHeaderBlockStart() { | |
731 CHECK(frame_type_ == HEADERS || frame_type_ == PUSH_PROMISE) | |
732 << " frame_type_=" << Http2FrameTypeToString(frame_type_); | |
733 CHECK(headers_); | |
734 CHECK_EQ(0u, headers_->size()); | |
735 got_hpack_end_ = false; | |
736 } | |
737 | |
738 void SpdyTestDeframerImpl::OnHeader(SpdyStringPiece key, | |
739 SpdyStringPiece value) { | |
740 CHECK(frame_type_ == HEADERS || frame_type_ == CONTINUATION || | |
741 frame_type_ == PUSH_PROMISE) | |
742 << " frame_type_=" << Http2FrameTypeToString(frame_type_); | |
743 CHECK(!got_hpack_end_); | |
744 CHECK(headers_); | |
745 headers_->emplace_back(SpdyString(key), SpdyString(value)); | |
746 CHECK(headers_handler_); | |
747 headers_handler_->OnHeader(key, value); | |
748 } | |
749 | |
750 void SpdyTestDeframerImpl::OnHeaderBlockEnd(size_t header_bytes_parsed) { | |
751 CHECK(headers_); | |
752 CHECK(frame_type_ == HEADERS || frame_type_ == CONTINUATION || | |
753 frame_type_ == PUSH_PROMISE) | |
754 << " frame_type_=" << Http2FrameTypeToString(frame_type_); | |
755 CHECK(end_); | |
756 CHECK(!got_hpack_end_); | |
757 got_hpack_end_ = true; | |
758 } | |
759 | |
760 void SpdyTestDeframerImpl::OnHeaderBlockEnd( | |
761 size_t /* header_bytes_parsed */, | |
762 size_t /* compressed_header_bytes_parsed */) { | |
763 CHECK(headers_); | |
764 CHECK(frame_type_ == HEADERS || frame_type_ == CONTINUATION || | |
765 frame_type_ == PUSH_PROMISE) | |
766 << " frame_type_=" << Http2FrameTypeToString(frame_type_); | |
767 CHECK(end_); | |
768 CHECK(!got_hpack_end_); | |
769 got_hpack_end_ = true; | |
770 } | |
771 | |
772 class LoggingSpdyDeframerDelegate : public SpdyDeframerVisitorInterface { | |
773 public: | |
774 explicit LoggingSpdyDeframerDelegate( | |
775 std::unique_ptr<SpdyDeframerVisitorInterface> wrapped) | |
776 : wrapped_(std::move(wrapped)) { | |
777 if (!wrapped_) { | |
778 wrapped_ = MakeUnique<SpdyDeframerVisitorInterface>(); | |
779 } | |
780 } | |
781 ~LoggingSpdyDeframerDelegate() override {} | |
782 | |
783 void OnAltSvc(std::unique_ptr<SpdyAltSvcIR> frame) override { | |
784 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnAltSvc"; | |
785 wrapped_->OnAltSvc(std::move(frame)); | |
786 } | |
787 void OnData(std::unique_ptr<SpdyDataIR> frame) override { | |
788 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnData"; | |
789 wrapped_->OnData(std::move(frame)); | |
790 } | |
791 void OnGoAway(std::unique_ptr<SpdyGoAwayIR> frame) override { | |
792 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnGoAway"; | |
793 wrapped_->OnGoAway(std::move(frame)); | |
794 } | |
795 | |
796 // SpdyHeadersIR and SpdyPushPromiseIR each has a SpdyHeaderBlock which | |
797 // significantly modifies the headers, so the actual header entries (name | |
798 // and value strings) are provided in a vector. | |
799 void OnHeaders(std::unique_ptr<SpdyHeadersIR> frame, | |
800 std::unique_ptr<StringPairVector> headers) override { | |
801 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnHeaders"; | |
802 wrapped_->OnHeaders(std::move(frame), std::move(headers)); | |
803 } | |
804 | |
805 void OnPing(std::unique_ptr<SpdyPingIR> frame) override { | |
806 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPing"; | |
807 wrapped_->OnPing(std::move(frame)); | |
808 } | |
809 void OnPingAck(std::unique_ptr<SpdyPingIR> frame) override { | |
810 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPingAck"; | |
811 wrapped_->OnPingAck(std::move(frame)); | |
812 } | |
813 | |
814 void OnPriority(std::unique_ptr<SpdyPriorityIR> frame) override { | |
815 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPriority"; | |
816 wrapped_->OnPriority(std::move(frame)); | |
817 } | |
818 | |
819 // SpdyHeadersIR and SpdyPushPromiseIR each has a SpdyHeaderBlock which | |
820 // significantly modifies the headers, so the actual header entries (name | |
821 // and value strings) are provided in a vector. | |
822 void OnPushPromise(std::unique_ptr<SpdyPushPromiseIR> frame, | |
823 std::unique_ptr<StringPairVector> headers) override { | |
824 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnPushPromise"; | |
825 wrapped_->OnPushPromise(std::move(frame), std::move(headers)); | |
826 } | |
827 | |
828 void OnRstStream(std::unique_ptr<SpdyRstStreamIR> frame) override { | |
829 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnRstStream"; | |
830 wrapped_->OnRstStream(std::move(frame)); | |
831 } | |
832 | |
833 // SpdySettingsIR has a map for settings, so loses info about the order of | |
834 // settings, and whether the same setting appeared more than once, so the | |
835 // the actual settings (parameter and value) are provided in a vector. | |
836 void OnSettings(std::unique_ptr<SpdySettingsIR> frame, | |
837 std::unique_ptr<SettingVector> settings) override { | |
838 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnSettings"; | |
839 wrapped_->OnSettings(std::move(frame), std::move(settings)); | |
840 } | |
841 | |
842 // A settings frame with an ACK has no content, but for uniformity passing | |
843 // a frame with the ACK flag set. | |
844 void OnSettingsAck(std::unique_ptr<SpdySettingsIR> frame) override { | |
845 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnSettingsAck"; | |
846 wrapped_->OnSettingsAck(std::move(frame)); | |
847 } | |
848 | |
849 void OnWindowUpdate(std::unique_ptr<SpdyWindowUpdateIR> frame) override { | |
850 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnWindowUpdate"; | |
851 wrapped_->OnWindowUpdate(std::move(frame)); | |
852 } | |
853 | |
854 // The SpdyFramer will not process any more data at this point. | |
855 void OnError(SpdyFramer* framer, SpdyTestDeframer* deframer) override { | |
856 DVLOG(1) << "LoggingSpdyDeframerDelegate::OnError"; | |
857 wrapped_->OnError(framer, deframer); | |
858 } | |
859 | |
860 private: | |
861 std::unique_ptr<SpdyDeframerVisitorInterface> wrapped_; | |
862 }; | |
863 | |
864 // static | |
865 std::unique_ptr<SpdyDeframerVisitorInterface> | |
866 SpdyDeframerVisitorInterface::LogBeforeVisiting( | |
867 std::unique_ptr<SpdyDeframerVisitorInterface> wrapped_listener) { | |
868 return MakeUnique<LoggingSpdyDeframerDelegate>(std::move(wrapped_listener)); | |
869 } | |
870 | |
871 CollectedFrame::CollectedFrame() {} | |
872 | |
873 CollectedFrame::CollectedFrame(CollectedFrame&& other) | |
874 : frame_ir(std::move(other.frame_ir)), | |
875 headers(std::move(other.headers)), | |
876 settings(std::move(other.settings)), | |
877 error_reported(other.error_reported) {} | |
878 | |
879 CollectedFrame::~CollectedFrame() {} | |
880 | |
881 CollectedFrame& CollectedFrame::operator=(CollectedFrame&& other) { | |
882 frame_ir = std::move(other.frame_ir); | |
883 headers = std::move(other.headers); | |
884 settings = std::move(other.settings); | |
885 error_reported = other.error_reported; | |
886 return *this; | |
887 } | |
888 | |
889 AssertionResult CollectedFrame::VerifyHasHeaders( | |
890 const StringPairVector& expected_headers) const { | |
891 if (headers.get() == nullptr) | |
892 return AssertionFailure(); | |
893 if (*headers != expected_headers) | |
894 return AssertionFailure(); | |
895 | |
896 return AssertionSuccess(); | |
897 } | |
898 | |
899 AssertionResult CollectedFrame::VerifyHasSettings( | |
900 const SettingVector& expected_settings) const { | |
901 if (settings.get() == nullptr) | |
902 return AssertionFailure(); | |
903 if (*settings != expected_settings) | |
904 return AssertionFailure(); | |
905 | |
906 return AssertionSuccess(); | |
907 } | |
908 | |
909 DeframerCallbackCollector::DeframerCallbackCollector( | |
910 std::vector<CollectedFrame>* collected_frames) | |
911 : collected_frames_(collected_frames) { | |
912 CHECK(collected_frames); | |
913 } | |
914 | |
915 void DeframerCallbackCollector::OnAltSvc( | |
916 std::unique_ptr<SpdyAltSvcIR> frame_ir) { | |
917 CollectedFrame cf; | |
918 cf.frame_ir = std::move(frame_ir); | |
919 collected_frames_->push_back(std::move(cf)); | |
920 } | |
921 void DeframerCallbackCollector::OnData(std::unique_ptr<SpdyDataIR> frame_ir) { | |
922 CollectedFrame cf; | |
923 cf.frame_ir = std::move(frame_ir); | |
924 collected_frames_->push_back(std::move(cf)); | |
925 } | |
926 void DeframerCallbackCollector::OnGoAway( | |
927 std::unique_ptr<SpdyGoAwayIR> frame_ir) { | |
928 CollectedFrame cf; | |
929 cf.frame_ir = std::move(frame_ir); | |
930 collected_frames_->push_back(std::move(cf)); | |
931 } | |
932 | |
933 // SpdyHeadersIR and SpdyPushPromiseIR each has a SpdyHeaderBlock which | |
934 // significantly modifies the headers, so the actual header entries (name | |
935 // and value strings) are provided in a vector. | |
936 void DeframerCallbackCollector::OnHeaders( | |
937 std::unique_ptr<SpdyHeadersIR> frame_ir, | |
938 std::unique_ptr<StringPairVector> headers) { | |
939 CollectedFrame cf; | |
940 cf.frame_ir = std::move(frame_ir); | |
941 cf.headers = std::move(headers); | |
942 collected_frames_->push_back(std::move(cf)); | |
943 } | |
944 | |
945 void DeframerCallbackCollector::OnPing(std::unique_ptr<SpdyPingIR> frame_ir) { | |
946 EXPECT_TRUE(frame_ir && !frame_ir->is_ack()); | |
947 CollectedFrame cf; | |
948 cf.frame_ir = std::move(frame_ir); | |
949 collected_frames_->push_back(std::move(cf)); | |
950 } | |
951 | |
952 void DeframerCallbackCollector::OnPingAck( | |
953 std::unique_ptr<SpdyPingIR> frame_ir) { | |
954 EXPECT_TRUE(frame_ir && frame_ir->is_ack()); | |
955 CollectedFrame cf; | |
956 cf.frame_ir = std::move(frame_ir); | |
957 collected_frames_->push_back(std::move(cf)); | |
958 } | |
959 | |
960 void DeframerCallbackCollector::OnPriority( | |
961 std::unique_ptr<SpdyPriorityIR> frame_ir) { | |
962 CollectedFrame cf; | |
963 cf.frame_ir = std::move(frame_ir); | |
964 collected_frames_->push_back(std::move(cf)); | |
965 } | |
966 | |
967 // SpdyHeadersIR and SpdyPushPromiseIR each has a SpdyHeaderBlock which | |
968 // significantly modifies the headers, so the actual header entries (name | |
969 // and value strings) are provided in a vector. | |
970 void DeframerCallbackCollector::OnPushPromise( | |
971 std::unique_ptr<SpdyPushPromiseIR> frame_ir, | |
972 std::unique_ptr<StringPairVector> headers) { | |
973 CollectedFrame cf; | |
974 cf.frame_ir = std::move(frame_ir); | |
975 cf.headers = std::move(headers); | |
976 collected_frames_->push_back(std::move(cf)); | |
977 } | |
978 | |
979 void DeframerCallbackCollector::OnRstStream( | |
980 std::unique_ptr<SpdyRstStreamIR> frame_ir) { | |
981 CollectedFrame cf; | |
982 cf.frame_ir = std::move(frame_ir); | |
983 collected_frames_->push_back(std::move(cf)); | |
984 } | |
985 | |
986 // SpdySettingsIR has a map for settings, so loses info about the order of | |
987 // settings, and whether the same setting appeared more than once, so the | |
988 // the actual settings (parameter and value) are provided in a vector. | |
989 void DeframerCallbackCollector::OnSettings( | |
990 std::unique_ptr<SpdySettingsIR> frame_ir, | |
991 std::unique_ptr<SettingVector> settings) { | |
992 EXPECT_TRUE(frame_ir && !frame_ir->is_ack()); | |
993 CollectedFrame cf; | |
994 cf.frame_ir = std::move(frame_ir); | |
995 cf.settings = std::move(settings); | |
996 collected_frames_->push_back(std::move(cf)); | |
997 } | |
998 | |
999 // A settings frame_ir with an ACK has no content, but for uniformity passing | |
1000 // a frame_ir with the ACK flag set. | |
1001 void DeframerCallbackCollector::OnSettingsAck( | |
1002 std::unique_ptr<SpdySettingsIR> frame_ir) { | |
1003 EXPECT_TRUE(frame_ir && frame_ir->is_ack()); | |
1004 CollectedFrame cf; | |
1005 cf.frame_ir = std::move(frame_ir); | |
1006 collected_frames_->push_back(std::move(cf)); | |
1007 } | |
1008 | |
1009 void DeframerCallbackCollector::OnWindowUpdate( | |
1010 std::unique_ptr<SpdyWindowUpdateIR> frame_ir) { | |
1011 CollectedFrame cf; | |
1012 cf.frame_ir = std::move(frame_ir); | |
1013 collected_frames_->push_back(std::move(cf)); | |
1014 } | |
1015 | |
1016 // The SpdyFramer will not process any more data at this point. | |
1017 void DeframerCallbackCollector::OnError(SpdyFramer* framer, | |
1018 SpdyTestDeframer* deframer) { | |
1019 CollectedFrame cf; | |
1020 cf.error_reported = true; | |
1021 collected_frames_->push_back(std::move(cf)); | |
1022 } | |
1023 | |
1024 } // namespace test | |
1025 } // namespace net | |
OLD | NEW |