| OLD | NEW |
| (Empty) |
| 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 "net/spdy/spdy_framer.h" | |
| 6 | |
| 7 #include <stdlib.h> | |
| 8 #include <string.h> | |
| 9 | |
| 10 #include <algorithm> | |
| 11 #include <cstdint> | |
| 12 #include <limits> | |
| 13 #include <memory> | |
| 14 #include <tuple> | |
| 15 #include <utility> | |
| 16 #include <vector> | |
| 17 | |
| 18 #include "base/compiler_specific.h" | |
| 19 #include "base/logging.h" | |
| 20 #include "base/macros.h" | |
| 21 #include "base/memory/ptr_util.h" | |
| 22 #include "base/strings/string_number_conversions.h" | |
| 23 #include "net/quic/platform/api/quic_flags.h" | |
| 24 #include "net/spdy/array_output_buffer.h" | |
| 25 #include "net/spdy/hpack/hpack_constants.h" | |
| 26 #include "net/spdy/mock_spdy_framer_visitor.h" | |
| 27 #include "net/spdy/spdy_flags.h" | |
| 28 #include "net/spdy/spdy_frame_builder.h" | |
| 29 #include "net/spdy/spdy_frame_reader.h" | |
| 30 #include "net/spdy/spdy_protocol.h" | |
| 31 #include "net/spdy/spdy_test_utils.h" | |
| 32 #include "testing/gmock/include/gmock/gmock.h" | |
| 33 #include "testing/gtest/include/gtest/gtest.h" | |
| 34 #include "testing/platform_test.h" | |
| 35 | |
| 36 using testing::_; | |
| 37 | |
| 38 namespace net { | |
| 39 | |
| 40 namespace test { | |
| 41 | |
| 42 namespace { | |
| 43 | |
| 44 const int64_t kSize = 64 * 1024; | |
| 45 char output_buffer[kSize] = ""; | |
| 46 | |
| 47 // frame_list_char is used to hold frames to be compared with output_buffer. | |
| 48 const int64_t buffer_size = 64 * 1024; | |
| 49 char frame_list_char[buffer_size] = ""; | |
| 50 } | |
| 51 | |
| 52 class MockDebugVisitor : public SpdyFramerDebugVisitorInterface { | |
| 53 public: | |
| 54 MOCK_METHOD4(OnSendCompressedFrame, | |
| 55 void(SpdyStreamId stream_id, | |
| 56 SpdyFrameType type, | |
| 57 size_t payload_len, | |
| 58 size_t frame_len)); | |
| 59 | |
| 60 MOCK_METHOD3(OnReceiveCompressedFrame, | |
| 61 void(SpdyStreamId stream_id, | |
| 62 SpdyFrameType type, | |
| 63 size_t frame_len)); | |
| 64 }; | |
| 65 | |
| 66 class SpdyFramerTestUtil { | |
| 67 public: | |
| 68 // Decompress a single frame using the decompression context held by | |
| 69 // the SpdyFramer. The implemention is meant for use only in tests | |
| 70 // and will CHECK fail if the input is anything other than a single, | |
| 71 // well-formed compressed frame. | |
| 72 // | |
| 73 // Returns a new decompressed SpdySerializedFrame. | |
| 74 template <class SpdyFrameType> | |
| 75 static SpdySerializedFrame DecompressFrame(SpdyFramer* framer, | |
| 76 const SpdyFrameType& frame) { | |
| 77 DecompressionVisitor visitor; | |
| 78 framer->set_visitor(&visitor); | |
| 79 CHECK_EQ(frame.size(), framer->ProcessInput(frame.data(), frame.size())); | |
| 80 CHECK_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer->state()); | |
| 81 framer->set_visitor(nullptr); | |
| 82 SpdyFramer serializer(SpdyFramer::DISABLE_COMPRESSION); | |
| 83 return serializer.SerializeFrame(visitor.GetFrame()); | |
| 84 } | |
| 85 | |
| 86 class DecompressionVisitor : public SpdyFramerVisitorInterface { | |
| 87 public: | |
| 88 DecompressionVisitor() : finished_(false) {} | |
| 89 | |
| 90 const SpdyFrameIR& GetFrame() const { | |
| 91 CHECK(finished_); | |
| 92 return *frame_; | |
| 93 } | |
| 94 | |
| 95 SpdyHeadersHandlerInterface* OnHeaderFrameStart( | |
| 96 SpdyStreamId stream_id) override { | |
| 97 if (headers_handler_ == nullptr) { | |
| 98 headers_handler_ = base::MakeUnique<TestHeadersHandler>(); | |
| 99 } | |
| 100 return headers_handler_.get(); | |
| 101 } | |
| 102 | |
| 103 void OnHeaderFrameEnd(SpdyStreamId stream_id, bool end_headers) override { | |
| 104 CHECK(!finished_); | |
| 105 frame_->set_header_block(headers_handler_->decoded_block().Clone()); | |
| 106 finished_ = true; | |
| 107 if (end_headers) { | |
| 108 headers_handler_.reset(); | |
| 109 } | |
| 110 } | |
| 111 | |
| 112 void OnHeaders(SpdyStreamId stream_id, | |
| 113 bool has_priority, | |
| 114 int weight, | |
| 115 SpdyStreamId parent_stream_id, | |
| 116 bool exclusive, | |
| 117 bool fin, | |
| 118 bool end) override { | |
| 119 auto headers = base::MakeUnique<SpdyHeadersIR>(stream_id); | |
| 120 headers->set_has_priority(has_priority); | |
| 121 headers->set_weight(weight); | |
| 122 headers->set_parent_stream_id(parent_stream_id); | |
| 123 headers->set_exclusive(exclusive); | |
| 124 headers->set_fin(fin); | |
| 125 frame_ = std::move(headers); | |
| 126 } | |
| 127 | |
| 128 void OnPushPromise(SpdyStreamId stream_id, | |
| 129 SpdyStreamId promised_stream_id, | |
| 130 bool end) override { | |
| 131 frame_ = | |
| 132 base::MakeUnique<SpdyPushPromiseIR>(stream_id, promised_stream_id); | |
| 133 } | |
| 134 | |
| 135 // TODO(birenroy): Add support for CONTINUATION. | |
| 136 void OnContinuation(SpdyStreamId stream_id, bool end) override { | |
| 137 LOG(FATAL); | |
| 138 } | |
| 139 | |
| 140 // All other methods just LOG(FATAL). | |
| 141 void OnError(SpdyFramer* framer) override { LOG(FATAL); } | |
| 142 void OnDataFrameHeader(SpdyStreamId stream_id, | |
| 143 size_t length, | |
| 144 bool fin) override { | |
| 145 LOG(FATAL) << "Unexpected data frame header"; | |
| 146 } | |
| 147 void OnStreamFrameData(SpdyStreamId stream_id, | |
| 148 const char* data, | |
| 149 size_t len) override { | |
| 150 LOG(FATAL); | |
| 151 } | |
| 152 | |
| 153 void OnStreamEnd(SpdyStreamId stream_id) override { LOG(FATAL); } | |
| 154 | |
| 155 void OnStreamPadding(SpdyStreamId stream_id, size_t len) override { | |
| 156 LOG(FATAL); | |
| 157 } | |
| 158 | |
| 159 void OnRstStream(SpdyStreamId stream_id, | |
| 160 SpdyErrorCode error_code) override { | |
| 161 LOG(FATAL); | |
| 162 } | |
| 163 void OnSetting(SpdySettingsIds id, uint32_t value) override { LOG(FATAL); } | |
| 164 void OnPing(SpdyPingId unique_id, bool is_ack) override { LOG(FATAL); } | |
| 165 void OnSettingsEnd() override { LOG(FATAL); } | |
| 166 void OnGoAway(SpdyStreamId last_accepted_stream_id, | |
| 167 SpdyErrorCode error_code) override { | |
| 168 LOG(FATAL); | |
| 169 } | |
| 170 | |
| 171 void OnWindowUpdate(SpdyStreamId stream_id, | |
| 172 int delta_window_size) override { | |
| 173 LOG(FATAL); | |
| 174 } | |
| 175 | |
| 176 void OnPriority(SpdyStreamId stream_id, | |
| 177 SpdyStreamId parent_stream_id, | |
| 178 int weight, | |
| 179 bool exclusive) override { | |
| 180 // Do nothing. | |
| 181 } | |
| 182 | |
| 183 bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) override { | |
| 184 LOG(FATAL); | |
| 185 return false; | |
| 186 } | |
| 187 | |
| 188 private: | |
| 189 std::unique_ptr<TestHeadersHandler> headers_handler_; | |
| 190 std::unique_ptr<SpdyFrameWithHeaderBlockIR> frame_; | |
| 191 bool finished_; | |
| 192 | |
| 193 DISALLOW_COPY_AND_ASSIGN(DecompressionVisitor); | |
| 194 }; | |
| 195 | |
| 196 private: | |
| 197 DISALLOW_COPY_AND_ASSIGN(SpdyFramerTestUtil); | |
| 198 }; | |
| 199 | |
| 200 MATCHER_P(IsFrameUnionOf, frame_list, "") { | |
| 201 size_t size_verified = 0; | |
| 202 for (const auto& frame : *frame_list) { | |
| 203 if (arg.size() < size_verified + frame.size()) { | |
| 204 LOG(FATAL) << "Incremental header serialization should not lead to a " | |
| 205 << "higher total frame length than non-incremental method."; | |
| 206 return false; | |
| 207 } | |
| 208 if (memcmp(arg.data() + size_verified, frame.data(), frame.size())) { | |
| 209 CompareCharArraysWithHexError( | |
| 210 "Header serialization methods should be equivalent: ", | |
| 211 reinterpret_cast<unsigned char*>(arg.data() + size_verified), | |
| 212 frame.size(), reinterpret_cast<unsigned char*>(frame.data()), | |
| 213 frame.size()); | |
| 214 return false; | |
| 215 } | |
| 216 size_verified += frame.size(); | |
| 217 } | |
| 218 return size_verified == arg.size(); | |
| 219 } | |
| 220 | |
| 221 class SpdyFramerPeer { | |
| 222 public: | |
| 223 static size_t ControlFrameBufferSize() { | |
| 224 return SpdyFramer::kControlFrameBufferSize; | |
| 225 } | |
| 226 static size_t GetNumberRequiredContinuationFrames(SpdyFramer* framer, | |
| 227 size_t size) { | |
| 228 return framer->GetNumberRequiredContinuationFrames(size); | |
| 229 } | |
| 230 static void SetError(SpdyFramer* framer, SpdyFramer::SpdyFramerError error) { | |
| 231 framer->set_error(error); | |
| 232 } | |
| 233 | |
| 234 // TODO(dahollings): Remove these methods when deprecating non-incremental | |
| 235 // header serialization path. | |
| 236 static std::unique_ptr<SpdyHeadersIR> CloneSpdyHeadersIR( | |
| 237 const SpdyHeadersIR& headers) { | |
| 238 auto newHeaders = base::MakeUnique<SpdyHeadersIR>( | |
| 239 headers.stream_id(), headers.header_block().Clone()); | |
| 240 newHeaders->set_fin(headers.fin()); | |
| 241 newHeaders->set_has_priority(headers.has_priority()); | |
| 242 newHeaders->set_weight(headers.weight()); | |
| 243 newHeaders->set_parent_stream_id(headers.parent_stream_id()); | |
| 244 newHeaders->set_exclusive(headers.exclusive()); | |
| 245 if (headers.padded()) { | |
| 246 newHeaders->set_padding_len(headers.padding_payload_len() + 1); | |
| 247 } | |
| 248 newHeaders->set_end_headers(headers.end_headers()); | |
| 249 return newHeaders; | |
| 250 } | |
| 251 | |
| 252 static SpdySerializedFrame SerializeHeaders(SpdyFramer* framer, | |
| 253 const SpdyHeadersIR& headers) { | |
| 254 SpdySerializedFrame serialized_headers_old_version( | |
| 255 framer->SerializeHeaders(headers)); | |
| 256 framer->hpack_encoder_.reset(nullptr); | |
| 257 auto* saved_debug_visitor = framer->debug_visitor_; | |
| 258 framer->debug_visitor_ = nullptr; | |
| 259 | |
| 260 std::vector<SpdySerializedFrame> frame_list; | |
| 261 ArrayOutputBuffer frame_list_buffer(frame_list_char, buffer_size); | |
| 262 SpdyFramer::SpdyHeaderFrameIterator it(framer, CloneSpdyHeadersIR(headers)); | |
| 263 while (it.HasNextFrame()) { | |
| 264 size_t size_before = frame_list_buffer.Size(); | |
| 265 it.NextFrame(&frame_list_buffer); | |
| 266 frame_list.emplace_back( | |
| 267 SpdySerializedFrame(frame_list_buffer.Begin() + size_before, | |
| 268 frame_list_buffer.Size() - size_before, false)); | |
| 269 } | |
| 270 framer->debug_visitor_ = saved_debug_visitor; | |
| 271 | |
| 272 EXPECT_THAT(serialized_headers_old_version, IsFrameUnionOf(&frame_list)); | |
| 273 return serialized_headers_old_version; | |
| 274 } | |
| 275 | |
| 276 static SpdySerializedFrame SerializeHeaders(SpdyFramer* framer, | |
| 277 const SpdyHeadersIR& headers, | |
| 278 ArrayOutputBuffer* output) { | |
| 279 if (output == nullptr) { | |
| 280 return SerializeHeaders(framer, headers); | |
| 281 } | |
| 282 output->Reset(); | |
| 283 EXPECT_TRUE(framer->SerializeHeaders(headers, output)); | |
| 284 SpdySerializedFrame serialized_headers_old_version(output->Begin(), | |
| 285 output->Size(), false); | |
| 286 framer->hpack_encoder_.reset(nullptr); | |
| 287 auto* saved_debug_visitor = framer->debug_visitor_; | |
| 288 framer->debug_visitor_ = nullptr; | |
| 289 | |
| 290 std::vector<SpdySerializedFrame> frame_list; | |
| 291 ArrayOutputBuffer frame_list_buffer(frame_list_char, buffer_size); | |
| 292 SpdyFramer::SpdyHeaderFrameIterator it(framer, CloneSpdyHeadersIR(headers)); | |
| 293 while (it.HasNextFrame()) { | |
| 294 size_t size_before = frame_list_buffer.Size(); | |
| 295 it.NextFrame(&frame_list_buffer); | |
| 296 frame_list.emplace_back( | |
| 297 SpdySerializedFrame(frame_list_buffer.Begin() + size_before, | |
| 298 frame_list_buffer.Size() - size_before, false)); | |
| 299 } | |
| 300 framer->debug_visitor_ = saved_debug_visitor; | |
| 301 | |
| 302 EXPECT_THAT(serialized_headers_old_version, IsFrameUnionOf(&frame_list)); | |
| 303 return serialized_headers_old_version; | |
| 304 } | |
| 305 | |
| 306 static std::unique_ptr<SpdyPushPromiseIR> CloneSpdyPushPromiseIR( | |
| 307 const SpdyPushPromiseIR& push_promise) { | |
| 308 auto new_push_promise = base::MakeUnique<SpdyPushPromiseIR>( | |
| 309 push_promise.stream_id(), push_promise.promised_stream_id(), | |
| 310 push_promise.header_block().Clone()); | |
| 311 new_push_promise->set_fin(push_promise.fin()); | |
| 312 if (push_promise.padded()) { | |
| 313 new_push_promise->set_padding_len(push_promise.padding_payload_len() + 1); | |
| 314 } | |
| 315 new_push_promise->set_end_headers(push_promise.end_headers()); | |
| 316 return new_push_promise; | |
| 317 } | |
| 318 | |
| 319 static SpdySerializedFrame SerializePushPromise( | |
| 320 SpdyFramer* framer, | |
| 321 const SpdyPushPromiseIR& push_promise) { | |
| 322 SpdySerializedFrame serialized_headers_old_version = | |
| 323 framer->SerializePushPromise(push_promise); | |
| 324 framer->hpack_encoder_.reset(nullptr); | |
| 325 auto* saved_debug_visitor = framer->debug_visitor_; | |
| 326 framer->debug_visitor_ = nullptr; | |
| 327 | |
| 328 std::vector<SpdySerializedFrame> frame_list; | |
| 329 ArrayOutputBuffer frame_list_buffer(frame_list_char, buffer_size); | |
| 330 frame_list_buffer.Reset(); | |
| 331 SpdyFramer::SpdyPushPromiseFrameIterator it( | |
| 332 framer, CloneSpdyPushPromiseIR(push_promise)); | |
| 333 while (it.HasNextFrame()) { | |
| 334 size_t size_before = frame_list_buffer.Size(); | |
| 335 it.NextFrame(&frame_list_buffer); | |
| 336 frame_list.emplace_back( | |
| 337 SpdySerializedFrame(frame_list_buffer.Begin() + size_before, | |
| 338 frame_list_buffer.Size() - size_before, false)); | |
| 339 } | |
| 340 framer->debug_visitor_ = saved_debug_visitor; | |
| 341 | |
| 342 EXPECT_THAT(serialized_headers_old_version, IsFrameUnionOf(&frame_list)); | |
| 343 return serialized_headers_old_version; | |
| 344 } | |
| 345 | |
| 346 static SpdySerializedFrame SerializePushPromise( | |
| 347 SpdyFramer* framer, | |
| 348 const SpdyPushPromiseIR& push_promise, | |
| 349 ArrayOutputBuffer* output) { | |
| 350 if (output == nullptr) { | |
| 351 return SerializePushPromise(framer, push_promise); | |
| 352 } | |
| 353 output->Reset(); | |
| 354 EXPECT_TRUE(framer->SerializePushPromise(push_promise, output)); | |
| 355 SpdySerializedFrame serialized_headers_old_version(output->Begin(), | |
| 356 output->Size(), false); | |
| 357 framer->hpack_encoder_.reset(nullptr); | |
| 358 auto* saved_debug_visitor = framer->debug_visitor_; | |
| 359 framer->debug_visitor_ = nullptr; | |
| 360 | |
| 361 std::vector<SpdySerializedFrame> frame_list; | |
| 362 ArrayOutputBuffer frame_list_buffer(frame_list_char, buffer_size); | |
| 363 frame_list_buffer.Reset(); | |
| 364 SpdyFramer::SpdyPushPromiseFrameIterator it( | |
| 365 framer, CloneSpdyPushPromiseIR(push_promise)); | |
| 366 while (it.HasNextFrame()) { | |
| 367 size_t size_before = frame_list_buffer.Size(); | |
| 368 it.NextFrame(&frame_list_buffer); | |
| 369 frame_list.emplace_back( | |
| 370 SpdySerializedFrame(frame_list_buffer.Begin() + size_before, | |
| 371 frame_list_buffer.Size() - size_before, false)); | |
| 372 } | |
| 373 framer->debug_visitor_ = saved_debug_visitor; | |
| 374 | |
| 375 EXPECT_THAT(serialized_headers_old_version, IsFrameUnionOf(&frame_list)); | |
| 376 return serialized_headers_old_version; | |
| 377 } | |
| 378 }; | |
| 379 | |
| 380 class TestSpdyVisitor : public SpdyFramerVisitorInterface, | |
| 381 public SpdyFramerDebugVisitorInterface { | |
| 382 public: | |
| 383 // This is larger than our max frame size because header blocks that | |
| 384 // are too long can spill over into CONTINUATION frames. | |
| 385 static const size_t kDefaultHeaderBufferSize = 16 * 1024 * 1024; | |
| 386 | |
| 387 explicit TestSpdyVisitor(SpdyFramer::CompressionOption option) | |
| 388 : framer_(option), | |
| 389 error_count_(0), | |
| 390 headers_frame_count_(0), | |
| 391 push_promise_frame_count_(0), | |
| 392 goaway_count_(0), | |
| 393 setting_count_(0), | |
| 394 settings_ack_sent_(0), | |
| 395 settings_ack_received_(0), | |
| 396 continuation_count_(0), | |
| 397 altsvc_count_(0), | |
| 398 priority_count_(0), | |
| 399 test_altsvc_ir_(0), | |
| 400 on_unknown_frame_result_(false), | |
| 401 last_window_update_stream_(0), | |
| 402 last_window_update_delta_(0), | |
| 403 last_push_promise_stream_(0), | |
| 404 last_push_promise_promised_stream_(0), | |
| 405 data_bytes_(0), | |
| 406 fin_frame_count_(0), | |
| 407 fin_flag_count_(0), | |
| 408 end_of_stream_count_(0), | |
| 409 control_frame_header_data_count_(0), | |
| 410 zero_length_control_frame_header_data_count_(0), | |
| 411 data_frame_count_(0), | |
| 412 last_payload_len_(0), | |
| 413 last_frame_len_(0), | |
| 414 header_buffer_(kDefaultHeaderBufferSize), | |
| 415 header_buffer_length_(0), | |
| 416 header_stream_id_(static_cast<SpdyStreamId>(-1)), | |
| 417 header_control_type_(SpdyFrameType::DATA), | |
| 418 header_buffer_valid_(false) {} | |
| 419 | |
| 420 void OnError(SpdyFramer* f) override { | |
| 421 VLOG(1) << "SpdyFramer Error: " | |
| 422 << SpdyFramer::SpdyFramerErrorToString(f->spdy_framer_error()); | |
| 423 ++error_count_; | |
| 424 } | |
| 425 | |
| 426 void OnDataFrameHeader(SpdyStreamId stream_id, | |
| 427 size_t length, | |
| 428 bool fin) override { | |
| 429 VLOG(1) << "OnDataFrameHeader(" << stream_id << ", " << length << ", " | |
| 430 << fin << ")"; | |
| 431 ++data_frame_count_; | |
| 432 header_stream_id_ = stream_id; | |
| 433 } | |
| 434 | |
| 435 void OnStreamFrameData(SpdyStreamId stream_id, | |
| 436 const char* data, | |
| 437 size_t len) override { | |
| 438 VLOG(1) << "OnStreamFrameData(" << stream_id << ", data, " << len << ", " | |
| 439 << ") data:\n" | |
| 440 << base::HexEncode(data, len); | |
| 441 EXPECT_EQ(header_stream_id_, stream_id); | |
| 442 data_bytes_ += len; | |
| 443 } | |
| 444 | |
| 445 void OnStreamEnd(SpdyStreamId stream_id) override { | |
| 446 VLOG(1) << "OnStreamEnd(" << stream_id << ")"; | |
| 447 EXPECT_EQ(header_stream_id_, stream_id); | |
| 448 ++end_of_stream_count_; | |
| 449 } | |
| 450 | |
| 451 void OnStreamPadding(SpdyStreamId stream_id, size_t len) override { | |
| 452 VLOG(1) << "OnStreamPadding(" << stream_id << ", " << len << ")\n"; | |
| 453 EXPECT_EQ(header_stream_id_, stream_id); | |
| 454 data_bytes_ += len; | |
| 455 } | |
| 456 | |
| 457 SpdyHeadersHandlerInterface* OnHeaderFrameStart( | |
| 458 SpdyStreamId stream_id) override { | |
| 459 if (headers_handler_ == nullptr) { | |
| 460 headers_handler_ = base::MakeUnique<TestHeadersHandler>(); | |
| 461 } | |
| 462 return headers_handler_.get(); | |
| 463 } | |
| 464 | |
| 465 void OnHeaderFrameEnd(SpdyStreamId stream_id, bool end_headers) override { | |
| 466 CHECK(headers_handler_ != nullptr); | |
| 467 headers_ = headers_handler_->decoded_block().Clone(); | |
| 468 header_bytes_received_ = headers_handler_->header_bytes_parsed(); | |
| 469 if (end_headers) { | |
| 470 headers_handler_.reset(); | |
| 471 } | |
| 472 } | |
| 473 | |
| 474 void OnRstStream(SpdyStreamId stream_id, SpdyErrorCode error_code) override { | |
| 475 VLOG(1) << "OnRstStream(" << stream_id << ", " << error_code << ")"; | |
| 476 ++fin_frame_count_; | |
| 477 } | |
| 478 | |
| 479 void OnSetting(SpdySettingsIds id, uint32_t value) override { | |
| 480 VLOG(1) << "OnSetting(" << id << ", " << std::hex << ", " << value << ")"; | |
| 481 ++setting_count_; | |
| 482 } | |
| 483 | |
| 484 void OnSettingsAck() override { | |
| 485 VLOG(1) << "OnSettingsAck"; | |
| 486 ++settings_ack_received_; | |
| 487 } | |
| 488 | |
| 489 void OnSettingsEnd() override { | |
| 490 VLOG(1) << "OnSettingsEnd"; | |
| 491 ++settings_ack_sent_; | |
| 492 } | |
| 493 | |
| 494 void OnPing(SpdyPingId unique_id, bool is_ack) override { | |
| 495 LOG(DFATAL) << "OnPing(" << unique_id << ", " << (is_ack ? 1 : 0) << ")"; | |
| 496 } | |
| 497 | |
| 498 void OnGoAway(SpdyStreamId last_accepted_stream_id, | |
| 499 SpdyErrorCode error_code) override { | |
| 500 VLOG(1) << "OnGoAway(" << last_accepted_stream_id << ", " << error_code | |
| 501 << ")"; | |
| 502 ++goaway_count_; | |
| 503 } | |
| 504 | |
| 505 void OnHeaders(SpdyStreamId stream_id, | |
| 506 bool has_priority, | |
| 507 int weight, | |
| 508 SpdyStreamId parent_stream_id, | |
| 509 bool exclusive, | |
| 510 bool fin, | |
| 511 bool end) override { | |
| 512 VLOG(1) << "OnHeaders(" << stream_id << ", " << has_priority << ", " | |
| 513 << weight << ", " << parent_stream_id << ", " << exclusive << ", " | |
| 514 << fin << ", " << end << ")"; | |
| 515 ++headers_frame_count_; | |
| 516 InitHeaderStreaming(SpdyFrameType::HEADERS, stream_id); | |
| 517 if (fin) { | |
| 518 ++fin_flag_count_; | |
| 519 } | |
| 520 header_has_priority_ = has_priority; | |
| 521 header_parent_stream_id_ = parent_stream_id; | |
| 522 header_exclusive_ = exclusive; | |
| 523 } | |
| 524 | |
| 525 void OnWindowUpdate(SpdyStreamId stream_id, int delta_window_size) override { | |
| 526 VLOG(1) << "OnWindowUpdate(" << stream_id << ", " << delta_window_size | |
| 527 << ")"; | |
| 528 last_window_update_stream_ = stream_id; | |
| 529 last_window_update_delta_ = delta_window_size; | |
| 530 } | |
| 531 | |
| 532 void OnPushPromise(SpdyStreamId stream_id, | |
| 533 SpdyStreamId promised_stream_id, | |
| 534 bool end) override { | |
| 535 VLOG(1) << "OnPushPromise(" << stream_id << ", " << promised_stream_id | |
| 536 << ", " << end << ")"; | |
| 537 ++push_promise_frame_count_; | |
| 538 InitHeaderStreaming(SpdyFrameType::PUSH_PROMISE, stream_id); | |
| 539 last_push_promise_stream_ = stream_id; | |
| 540 last_push_promise_promised_stream_ = promised_stream_id; | |
| 541 } | |
| 542 | |
| 543 void OnContinuation(SpdyStreamId stream_id, bool end) override { | |
| 544 VLOG(1) << "OnContinuation(" << stream_id << ", " << end << ")"; | |
| 545 ++continuation_count_; | |
| 546 } | |
| 547 | |
| 548 void OnAltSvc(SpdyStreamId stream_id, | |
| 549 SpdyStringPiece origin, | |
| 550 const SpdyAltSvcWireFormat::AlternativeServiceVector& | |
| 551 altsvc_vector) override { | |
| 552 VLOG(1) << "OnAltSvc(" << stream_id << ", \"" << origin | |
| 553 << "\", altsvc_vector)"; | |
| 554 test_altsvc_ir_.set_stream_id(stream_id); | |
| 555 if (origin.length() > 0) { | |
| 556 test_altsvc_ir_.set_origin(SpdyString(origin)); | |
| 557 } | |
| 558 for (const auto& altsvc : altsvc_vector) { | |
| 559 test_altsvc_ir_.add_altsvc(altsvc); | |
| 560 } | |
| 561 ++altsvc_count_; | |
| 562 } | |
| 563 | |
| 564 void OnPriority(SpdyStreamId stream_id, | |
| 565 SpdyStreamId parent_stream_id, | |
| 566 int weight, | |
| 567 bool exclusive) override { | |
| 568 VLOG(1) << "OnPriority(" << stream_id << ", " << parent_stream_id << ", " | |
| 569 << weight << ", " << (exclusive ? 1 : 0) << ")"; | |
| 570 ++priority_count_; | |
| 571 } | |
| 572 | |
| 573 bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) override { | |
| 574 VLOG(1) << "OnUnknownFrame(" << stream_id << ", " << frame_type << ")"; | |
| 575 return on_unknown_frame_result_; | |
| 576 } | |
| 577 | |
| 578 void OnSendCompressedFrame(SpdyStreamId stream_id, | |
| 579 SpdyFrameType type, | |
| 580 size_t payload_len, | |
| 581 size_t frame_len) override { | |
| 582 VLOG(1) << "OnSendCompressedFrame(" << stream_id << ", " << type << ", " | |
| 583 << payload_len << ", " << frame_len << ")"; | |
| 584 last_payload_len_ = payload_len; | |
| 585 last_frame_len_ = frame_len; | |
| 586 } | |
| 587 | |
| 588 void OnReceiveCompressedFrame(SpdyStreamId stream_id, | |
| 589 SpdyFrameType type, | |
| 590 size_t frame_len) override { | |
| 591 VLOG(1) << "OnReceiveCompressedFrame(" << stream_id << ", " << type << ", " | |
| 592 << frame_len << ")"; | |
| 593 last_frame_len_ = frame_len; | |
| 594 } | |
| 595 | |
| 596 // Convenience function which runs a framer simulation with particular input. | |
| 597 void SimulateInFramer(const unsigned char* input, size_t size) { | |
| 598 framer_.set_visitor(this); | |
| 599 size_t input_remaining = size; | |
| 600 const char* input_ptr = reinterpret_cast<const char*>(input); | |
| 601 while (input_remaining > 0 && | |
| 602 framer_.spdy_framer_error() == SpdyFramer::SPDY_NO_ERROR) { | |
| 603 // To make the tests more interesting, we feed random (and small) chunks | |
| 604 // into the framer. This simulates getting strange-sized reads from | |
| 605 // the socket. | |
| 606 const size_t kMaxReadSize = 32; | |
| 607 size_t bytes_read = | |
| 608 (rand() % std::min(input_remaining, kMaxReadSize)) + 1; | |
| 609 size_t bytes_processed = framer_.ProcessInput(input_ptr, bytes_read); | |
| 610 input_remaining -= bytes_processed; | |
| 611 input_ptr += bytes_processed; | |
| 612 } | |
| 613 } | |
| 614 | |
| 615 void InitHeaderStreaming(SpdyFrameType header_control_type, | |
| 616 SpdyStreamId stream_id) { | |
| 617 if (!IsDefinedFrameType(SerializeFrameType(header_control_type))) { | |
| 618 DLOG(FATAL) << "Attempted to init header streaming with " | |
| 619 << "invalid control frame type: " << header_control_type; | |
| 620 } | |
| 621 std::fill(header_buffer_.begin(), header_buffer_.end(), 0); | |
| 622 header_buffer_length_ = 0; | |
| 623 header_stream_id_ = stream_id; | |
| 624 header_control_type_ = header_control_type; | |
| 625 header_buffer_valid_ = true; | |
| 626 DCHECK_NE(header_stream_id_, SpdyFramer::kInvalidStream); | |
| 627 } | |
| 628 | |
| 629 void set_extension_visitor(ExtensionVisitorInterface* extension) { | |
| 630 framer_.set_extension_visitor(extension); | |
| 631 } | |
| 632 | |
| 633 // Override the default buffer size (16K). Call before using the framer! | |
| 634 void set_header_buffer_size(size_t header_buffer_size) { | |
| 635 header_buffer_.resize(header_buffer_size); | |
| 636 } | |
| 637 | |
| 638 // Largest control frame that the SPDY implementation sends, including the | |
| 639 // size of the header. | |
| 640 static size_t sent_control_frame_max_size() { | |
| 641 return SpdyFramer::kMaxControlFrameSize; | |
| 642 } | |
| 643 | |
| 644 // Largest control frame that the SPDY implementation is willing to receive, | |
| 645 // excluding the size of the header. | |
| 646 static size_t received_control_frame_max_size() { | |
| 647 return kSpdyInitialFrameSizeLimit; | |
| 648 } | |
| 649 | |
| 650 static size_t header_data_chunk_max_size() { | |
| 651 return SpdyFramer::kHeaderDataChunkMaxSize; | |
| 652 } | |
| 653 | |
| 654 SpdyFramer framer_; | |
| 655 | |
| 656 // Counters from the visitor callbacks. | |
| 657 int error_count_; | |
| 658 int headers_frame_count_; | |
| 659 int push_promise_frame_count_; | |
| 660 int goaway_count_; | |
| 661 int setting_count_; | |
| 662 int settings_ack_sent_; | |
| 663 int settings_ack_received_; | |
| 664 int continuation_count_; | |
| 665 int altsvc_count_; | |
| 666 int priority_count_; | |
| 667 SpdyAltSvcIR test_altsvc_ir_; | |
| 668 bool on_unknown_frame_result_; | |
| 669 SpdyStreamId last_window_update_stream_; | |
| 670 int last_window_update_delta_; | |
| 671 SpdyStreamId last_push_promise_stream_; | |
| 672 SpdyStreamId last_push_promise_promised_stream_; | |
| 673 int data_bytes_; | |
| 674 int fin_frame_count_; // The count of RST_STREAM type frames received. | |
| 675 int fin_flag_count_; // The count of frames with the FIN flag set. | |
| 676 int end_of_stream_count_; // The count of zero-length data frames. | |
| 677 int control_frame_header_data_count_; // The count of chunks received. | |
| 678 // The count of zero-length control frame header data chunks received. | |
| 679 int zero_length_control_frame_header_data_count_; | |
| 680 int data_frame_count_; | |
| 681 size_t last_payload_len_; | |
| 682 size_t last_frame_len_; | |
| 683 | |
| 684 // Header block streaming state: | |
| 685 std::vector<char> header_buffer_; | |
| 686 size_t header_buffer_length_; | |
| 687 size_t header_bytes_received_; | |
| 688 SpdyStreamId header_stream_id_; | |
| 689 SpdyFrameType header_control_type_; | |
| 690 bool header_buffer_valid_; | |
| 691 std::unique_ptr<TestHeadersHandler> headers_handler_; | |
| 692 SpdyHeaderBlock headers_; | |
| 693 bool header_has_priority_; | |
| 694 SpdyStreamId header_parent_stream_id_; | |
| 695 bool header_exclusive_; | |
| 696 }; | |
| 697 | |
| 698 class TestExtension : public ExtensionVisitorInterface { | |
| 699 public: | |
| 700 void OnSetting(uint16_t id, uint32_t value) override { | |
| 701 settings_received_.push_back({id, value}); | |
| 702 } | |
| 703 | |
| 704 // Called when non-standard frames are received. | |
| 705 bool OnFrameHeader(SpdyStreamId stream_id, | |
| 706 size_t length, | |
| 707 uint8_t type, | |
| 708 uint8_t flags) override { | |
| 709 stream_id_ = stream_id; | |
| 710 length_ = length; | |
| 711 type_ = type; | |
| 712 flags_ = flags; | |
| 713 return true; | |
| 714 } | |
| 715 | |
| 716 // The payload for a single frame may be delivered as multiple calls to | |
| 717 // OnFramePayload. | |
| 718 void OnFramePayload(const char* data, size_t len) override { | |
| 719 payload_.append(data, len); | |
| 720 } | |
| 721 | |
| 722 std::vector<std::pair<uint16_t, uint32_t>> settings_received_; | |
| 723 SpdyStreamId stream_id_ = 0; | |
| 724 size_t length_ = 0; | |
| 725 uint8_t type_ = 0; | |
| 726 uint8_t flags_ = 0; | |
| 727 SpdyString payload_; | |
| 728 }; | |
| 729 | |
| 730 // Retrieves serialized headers from a HEADERS frame. | |
| 731 SpdyStringPiece GetSerializedHeaders(const SpdySerializedFrame& frame, | |
| 732 const SpdyFramer& framer) { | |
| 733 SpdyFrameReader reader(frame.data(), frame.size()); | |
| 734 reader.Seek(3); // Seek past the frame length. | |
| 735 | |
| 736 uint8_t serialized_type; | |
| 737 reader.ReadUInt8(&serialized_type); | |
| 738 | |
| 739 SpdyFrameType type = ParseFrameType(serialized_type); | |
| 740 DCHECK_EQ(SpdyFrameType::HEADERS, type); | |
| 741 uint8_t flags; | |
| 742 reader.ReadUInt8(&flags); | |
| 743 | |
| 744 return SpdyStringPiece(frame.data() + framer.GetHeadersMinimumSize(), | |
| 745 frame.size() - framer.GetHeadersMinimumSize()); | |
| 746 } | |
| 747 | |
| 748 enum DecoderChoice { DECODER_SELF, DECODER_NESTED, DECODER_HTTP2 }; | |
| 749 enum HpackChoice { HPACK_DECODER_1, HPACK_DECODER_3 }; | |
| 750 enum Output { USE, NOT_USE }; | |
| 751 | |
| 752 class SpdyFramerTest : public ::testing::TestWithParam< | |
| 753 std::tuple<DecoderChoice, HpackChoice, Output>> { | |
| 754 public: | |
| 755 SpdyFramerTest() : output_(output_buffer, kSize) {} | |
| 756 | |
| 757 protected: | |
| 758 void SetUp() override { | |
| 759 auto param = GetParam(); | |
| 760 switch (std::get<0>(param)) { | |
| 761 case DECODER_SELF: | |
| 762 FLAGS_use_nested_spdy_framer_decoder = false; | |
| 763 FLAGS_chromium_http2_flag_spdy_use_http2_frame_decoder_adapter = false; | |
| 764 break; | |
| 765 case DECODER_NESTED: | |
| 766 FLAGS_use_nested_spdy_framer_decoder = true; | |
| 767 FLAGS_chromium_http2_flag_spdy_use_http2_frame_decoder_adapter = false; | |
| 768 break; | |
| 769 case DECODER_HTTP2: | |
| 770 FLAGS_use_nested_spdy_framer_decoder = false; | |
| 771 FLAGS_chromium_http2_flag_spdy_use_http2_frame_decoder_adapter = true; | |
| 772 break; | |
| 773 } | |
| 774 switch (std::get<1>(param)) { | |
| 775 case HPACK_DECODER_1: | |
| 776 FLAGS_chromium_http2_flag_spdy_use_hpack_decoder3 = false; | |
| 777 break; | |
| 778 case HPACK_DECODER_3: | |
| 779 FLAGS_chromium_http2_flag_spdy_use_hpack_decoder3 = true; | |
| 780 break; | |
| 781 } | |
| 782 switch (std::get<2>(param)) { | |
| 783 case USE: | |
| 784 use_output_ = true; | |
| 785 break; | |
| 786 case NOT_USE: | |
| 787 // TODO(yasong): remove this case after | |
| 788 // FLAGS_chromium_http2_flag_remove_rewritelength deprecates. | |
| 789 use_output_ = false; | |
| 790 break; | |
| 791 } | |
| 792 } | |
| 793 | |
| 794 void CompareFrame(const SpdyString& description, | |
| 795 const SpdySerializedFrame& actual_frame, | |
| 796 const unsigned char* expected, | |
| 797 const int expected_len) { | |
| 798 const unsigned char* actual = | |
| 799 reinterpret_cast<const unsigned char*>(actual_frame.data()); | |
| 800 CompareCharArraysWithHexError(description, actual, actual_frame.size(), | |
| 801 expected, expected_len); | |
| 802 } | |
| 803 | |
| 804 void CompareFrames(const SpdyString& description, | |
| 805 const SpdySerializedFrame& expected_frame, | |
| 806 const SpdySerializedFrame& actual_frame) { | |
| 807 CompareCharArraysWithHexError( | |
| 808 description, | |
| 809 reinterpret_cast<const unsigned char*>(expected_frame.data()), | |
| 810 expected_frame.size(), | |
| 811 reinterpret_cast<const unsigned char*>(actual_frame.data()), | |
| 812 actual_frame.size()); | |
| 813 } | |
| 814 | |
| 815 bool use_output_ = false; | |
| 816 ArrayOutputBuffer output_; | |
| 817 }; | |
| 818 | |
| 819 INSTANTIATE_TEST_CASE_P(SpdyFramerTests, | |
| 820 SpdyFramerTest, | |
| 821 ::testing::Combine(::testing::Values(DECODER_SELF, | |
| 822 DECODER_NESTED, | |
| 823 DECODER_HTTP2), | |
| 824 ::testing::Values(HPACK_DECODER_1, | |
| 825 HPACK_DECODER_3), | |
| 826 ::testing::Values(USE, NOT_USE))); | |
| 827 | |
| 828 // Test that we can encode and decode a SpdyHeaderBlock in serialized form. | |
| 829 TEST_P(SpdyFramerTest, HeaderBlockInBuffer) { | |
| 830 SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION); | |
| 831 | |
| 832 // Encode the header block into a Headers frame. | |
| 833 SpdyHeadersIR headers(1); | |
| 834 headers.SetHeader("alpha", "beta"); | |
| 835 headers.SetHeader("gamma", "charlie"); | |
| 836 headers.SetHeader("cookie", "key1=value1; key2=value2"); | |
| 837 SpdySerializedFrame frame( | |
| 838 SpdyFramerPeer::SerializeHeaders(&framer, headers, &output_)); | |
| 839 | |
| 840 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 841 visitor.SimulateInFramer(reinterpret_cast<unsigned char*>(frame.data()), | |
| 842 frame.size()); | |
| 843 | |
| 844 EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_); | |
| 845 EXPECT_EQ(headers.header_block(), visitor.headers_); | |
| 846 } | |
| 847 | |
| 848 // Test that if there's not a full frame, we fail to parse it. | |
| 849 TEST_P(SpdyFramerTest, UndersizedHeaderBlockInBuffer) { | |
| 850 SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION); | |
| 851 | |
| 852 // Encode the header block into a Headers frame. | |
| 853 SpdyHeadersIR headers(1); | |
| 854 headers.SetHeader("alpha", "beta"); | |
| 855 headers.SetHeader("gamma", "charlie"); | |
| 856 SpdySerializedFrame frame( | |
| 857 SpdyFramerPeer::SerializeHeaders(&framer, headers, &output_)); | |
| 858 | |
| 859 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 860 visitor.SimulateInFramer(reinterpret_cast<unsigned char*>(frame.data()), | |
| 861 frame.size() - 2); | |
| 862 | |
| 863 EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_); | |
| 864 EXPECT_EQ(0u, visitor.headers_.size()); | |
| 865 } | |
| 866 | |
| 867 // Test that we treat incoming upper-case or mixed-case header values as | |
| 868 // malformed. | |
| 869 TEST_P(SpdyFramerTest, RejectUpperCaseHeaderBlockValue) { | |
| 870 SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION); | |
| 871 | |
| 872 SpdyFrameBuilder frame(1024); | |
| 873 frame.BeginNewFrame(framer, SpdyFrameType::HEADERS, 0, 1); | |
| 874 frame.WriteUInt32(1); | |
| 875 frame.WriteStringPiece32("Name1"); | |
| 876 frame.WriteStringPiece32("value1"); | |
| 877 frame.OverwriteLength(framer, frame.length() - framer.GetFrameHeaderSize()); | |
| 878 | |
| 879 SpdyFrameBuilder frame2(1024); | |
| 880 frame2.BeginNewFrame(framer, SpdyFrameType::HEADERS, 0, 1); | |
| 881 frame2.WriteUInt32(2); | |
| 882 frame2.WriteStringPiece32("name1"); | |
| 883 frame2.WriteStringPiece32("value1"); | |
| 884 frame2.WriteStringPiece32("nAmE2"); | |
| 885 frame2.WriteStringPiece32("value2"); | |
| 886 frame.OverwriteLength(framer, frame2.length() - framer.GetFrameHeaderSize()); | |
| 887 | |
| 888 SpdySerializedFrame control_frame(frame.take()); | |
| 889 SpdyStringPiece serialized_headers = | |
| 890 GetSerializedHeaders(control_frame, framer); | |
| 891 SpdySerializedFrame control_frame2(frame2.take()); | |
| 892 SpdyStringPiece serialized_headers2 = | |
| 893 GetSerializedHeaders(control_frame2, framer); | |
| 894 | |
| 895 SpdyHeaderBlock new_headers; | |
| 896 EXPECT_FALSE(framer.ParseHeaderBlockInBuffer( | |
| 897 serialized_headers.data(), serialized_headers.size(), &new_headers)); | |
| 898 EXPECT_FALSE(framer.ParseHeaderBlockInBuffer( | |
| 899 serialized_headers2.data(), serialized_headers2.size(), &new_headers)); | |
| 900 } | |
| 901 | |
| 902 // Test that we can encode and decode stream dependency values in a header | |
| 903 // frame. | |
| 904 TEST_P(SpdyFramerTest, HeaderStreamDependencyValues) { | |
| 905 SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION); | |
| 906 | |
| 907 const SpdyStreamId parent_stream_id_test_array[] = {0, 3}; | |
| 908 for (SpdyStreamId parent_stream_id : parent_stream_id_test_array) { | |
| 909 const bool exclusive_test_array[] = {true, false}; | |
| 910 for (bool exclusive : exclusive_test_array) { | |
| 911 SpdyHeadersIR headers(1); | |
| 912 headers.set_has_priority(true); | |
| 913 headers.set_parent_stream_id(parent_stream_id); | |
| 914 headers.set_exclusive(exclusive); | |
| 915 SpdySerializedFrame frame( | |
| 916 SpdyFramerPeer::SerializeHeaders(&framer, headers, &output_)); | |
| 917 | |
| 918 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 919 visitor.SimulateInFramer(reinterpret_cast<unsigned char*>(frame.data()), | |
| 920 frame.size()); | |
| 921 | |
| 922 EXPECT_TRUE(visitor.header_has_priority_); | |
| 923 EXPECT_EQ(parent_stream_id, visitor.header_parent_stream_id_); | |
| 924 EXPECT_EQ(exclusive, visitor.header_exclusive_); | |
| 925 } | |
| 926 } | |
| 927 } | |
| 928 | |
| 929 // Test that if we receive a frame with payload length field at the | |
| 930 // advertised max size, we do not set an error in ProcessInput. | |
| 931 TEST_P(SpdyFramerTest, AcceptMaxFrameSizeSetting) { | |
| 932 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 933 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 934 framer.set_visitor(&visitor); | |
| 935 | |
| 936 // DATA frame with maximum allowed payload length. | |
| 937 unsigned char kH2FrameData[] = { | |
| 938 0x00, 0x40, 0x00, // Length: 2^14 | |
| 939 0x00, // Type: HEADERS | |
| 940 0x00, // Flags: None | |
| 941 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 942 0x00, 0x00, 0x00, 0x00, // Junk payload | |
| 943 }; | |
| 944 | |
| 945 SpdySerializedFrame frame(reinterpret_cast<char*>(kH2FrameData), | |
| 946 sizeof(kH2FrameData), false); | |
| 947 | |
| 948 EXPECT_CALL(visitor, OnDataFrameHeader(1, 1 << 14, false)); | |
| 949 EXPECT_CALL(visitor, OnStreamFrameData(1, _, 4)); | |
| 950 framer.ProcessInput(frame.data(), frame.size()); | |
| 951 EXPECT_FALSE(framer.HasError()); | |
| 952 } | |
| 953 | |
| 954 // Test that if we receive a frame with payload length larger than the | |
| 955 // advertised max size, we set an error of SPDY_INVALID_CONTROL_FRAME_SIZE. | |
| 956 TEST_P(SpdyFramerTest, ExceedMaxFrameSizeSetting) { | |
| 957 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 958 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 959 framer.set_visitor(&visitor); | |
| 960 | |
| 961 // DATA frame with too large payload length. | |
| 962 unsigned char kH2FrameData[] = { | |
| 963 0x00, 0x40, 0x01, // Length: 2^14 + 1 | |
| 964 0x00, // Type: HEADERS | |
| 965 0x00, // Flags: None | |
| 966 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 967 0x00, 0x00, 0x00, 0x00, // Junk payload | |
| 968 }; | |
| 969 | |
| 970 SpdySerializedFrame frame(reinterpret_cast<char*>(kH2FrameData), | |
| 971 sizeof(kH2FrameData), false); | |
| 972 | |
| 973 EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); | |
| 974 framer.ProcessInput(frame.data(), frame.size()); | |
| 975 EXPECT_TRUE(framer.HasError()); | |
| 976 EXPECT_EQ(SpdyFramer::SPDY_OVERSIZED_PAYLOAD, framer.spdy_framer_error()) | |
| 977 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 978 } | |
| 979 | |
| 980 // Test that if we receive a DATA frame with padding length larger than the | |
| 981 // payload length, we set an error of SPDY_INVALID_PADDING | |
| 982 TEST_P(SpdyFramerTest, OversizedDataPaddingError) { | |
| 983 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 984 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 985 framer.set_visitor(&visitor); | |
| 986 | |
| 987 // DATA frame with invalid padding length. | |
| 988 // |kH2FrameData| has to be |unsigned char|, because Chromium on Windows uses | |
| 989 // MSVC, where |char| is signed by default, which would not compile because of | |
| 990 // the element exceeding 127. | |
| 991 unsigned char kH2FrameData[] = { | |
| 992 0x00, 0x00, 0x05, // Length: 5 | |
| 993 0x00, // Type: DATA | |
| 994 0x09, // Flags: END_STREAM|PADDED | |
| 995 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 996 0xff, // PadLen: 255 trailing bytes (Too Long) | |
| 997 0x00, 0x00, 0x00, 0x00, // Padding | |
| 998 }; | |
| 999 | |
| 1000 SpdySerializedFrame frame(reinterpret_cast<char*>(kH2FrameData), | |
| 1001 sizeof(kH2FrameData), false); | |
| 1002 | |
| 1003 { | |
| 1004 testing::InSequence seq; | |
| 1005 EXPECT_CALL(visitor, OnDataFrameHeader(1, 5, 1)); | |
| 1006 EXPECT_CALL(visitor, OnStreamPadding(1, 1)); | |
| 1007 EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); | |
| 1008 } | |
| 1009 EXPECT_GT(frame.size(), framer.ProcessInput(frame.data(), frame.size())); | |
| 1010 EXPECT_TRUE(framer.HasError()); | |
| 1011 EXPECT_EQ(SpdyFramer::SPDY_INVALID_PADDING, framer.spdy_framer_error()) | |
| 1012 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 1013 } | |
| 1014 | |
| 1015 // Test that if we receive a DATA frame with padding length not larger than the | |
| 1016 // payload length, we do not set an error of SPDY_INVALID_PADDING | |
| 1017 TEST_P(SpdyFramerTest, CorrectlySizedDataPaddingNoError) { | |
| 1018 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 1019 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 1020 framer.set_visitor(&visitor); | |
| 1021 | |
| 1022 // DATA frame with valid Padding length | |
| 1023 char kH2FrameData[] = { | |
| 1024 0x00, 0x00, 0x05, // Length: 5 | |
| 1025 0x00, // Type: DATA | |
| 1026 0x08, // Flags: PADDED | |
| 1027 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1028 0x04, // PadLen: 4 trailing bytes | |
| 1029 0x00, 0x00, 0x00, 0x00, // Padding | |
| 1030 }; | |
| 1031 | |
| 1032 SpdySerializedFrame frame(kH2FrameData, sizeof(kH2FrameData), false); | |
| 1033 | |
| 1034 { | |
| 1035 testing::InSequence seq; | |
| 1036 EXPECT_CALL(visitor, OnDataFrameHeader(1, 5, false)); | |
| 1037 EXPECT_CALL(visitor, OnStreamPadding(1, 1)); | |
| 1038 EXPECT_CALL(visitor, OnError(testing::Eq(&framer))).Times(0); | |
| 1039 // Note that OnStreamFrameData(1, _, 1)) is never called | |
| 1040 // since there is no data, only padding | |
| 1041 EXPECT_CALL(visitor, OnStreamPadding(1, 4)); | |
| 1042 } | |
| 1043 | |
| 1044 EXPECT_EQ(frame.size(), framer.ProcessInput(frame.data(), frame.size())); | |
| 1045 EXPECT_FALSE(framer.HasError()); | |
| 1046 EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) | |
| 1047 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 1048 } | |
| 1049 | |
| 1050 // Test that if we receive a HEADERS frame with padding length larger than the | |
| 1051 // payload length, we set an error of SPDY_INVALID_PADDING | |
| 1052 TEST_P(SpdyFramerTest, OversizedHeadersPaddingError) { | |
| 1053 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 1054 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 1055 framer.set_visitor(&visitor); | |
| 1056 | |
| 1057 // HEADERS frame with invalid padding length. | |
| 1058 // |kH2FrameData| has to be |unsigned char|, because Chromium on Windows uses | |
| 1059 // MSVC, where |char| is signed by default, which would not compile because of | |
| 1060 // the element exceeding 127. | |
| 1061 unsigned char kH2FrameData[] = { | |
| 1062 0x00, 0x00, 0x05, // Length: 5 | |
| 1063 0x01, // Type: HEADERS | |
| 1064 0x08, // Flags: PADDED | |
| 1065 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1066 0xff, // PadLen: 255 trailing bytes (Too Long) | |
| 1067 0x00, 0x00, 0x00, 0x00, // Padding | |
| 1068 }; | |
| 1069 | |
| 1070 SpdySerializedFrame frame(reinterpret_cast<char*>(kH2FrameData), | |
| 1071 sizeof(kH2FrameData), false); | |
| 1072 | |
| 1073 EXPECT_CALL(visitor, OnHeaders(1, false, 0, 0, false, false, false)); | |
| 1074 EXPECT_CALL(visitor, OnHeaderFrameStart(1)).Times(1); | |
| 1075 EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); | |
| 1076 EXPECT_EQ(frame.size(), framer.ProcessInput(frame.data(), frame.size())); | |
| 1077 EXPECT_TRUE(framer.HasError()); | |
| 1078 EXPECT_EQ(SpdyFramer::SPDY_INVALID_PADDING, framer.spdy_framer_error()) | |
| 1079 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 1080 } | |
| 1081 | |
| 1082 // Test that if we receive a HEADERS frame with padding length not larger | |
| 1083 // than the payload length, we do not set an error of SPDY_INVALID_PADDING | |
| 1084 TEST_P(SpdyFramerTest, CorrectlySizedHeadersPaddingNoError) { | |
| 1085 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 1086 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 1087 framer.set_visitor(&visitor); | |
| 1088 | |
| 1089 // HEADERS frame with invalid Padding length | |
| 1090 char kH2FrameData[] = { | |
| 1091 0x00, 0x00, 0x05, // Length: 5 | |
| 1092 0x01, // Type: HEADERS | |
| 1093 0x08, // Flags: PADDED | |
| 1094 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1095 0x04, // PadLen: 4 trailing bytes | |
| 1096 0x00, 0x00, 0x00, 0x00, // Padding | |
| 1097 }; | |
| 1098 | |
| 1099 SpdySerializedFrame frame(kH2FrameData, sizeof(kH2FrameData), false); | |
| 1100 | |
| 1101 EXPECT_CALL(visitor, OnHeaders(1, false, 0, 0, false, false, false)); | |
| 1102 EXPECT_CALL(visitor, OnHeaderFrameStart(1)).Times(1); | |
| 1103 EXPECT_EQ(frame.size(), framer.ProcessInput(frame.data(), frame.size())); | |
| 1104 EXPECT_FALSE(framer.HasError()); | |
| 1105 EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) | |
| 1106 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 1107 } | |
| 1108 | |
| 1109 // Test that if we receive a DATA with stream ID zero, we signal an error | |
| 1110 // (but don't crash). | |
| 1111 TEST_P(SpdyFramerTest, DataWithStreamIdZero) { | |
| 1112 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 1113 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 1114 framer.set_visitor(&visitor); | |
| 1115 | |
| 1116 const char bytes[] = "hello"; | |
| 1117 SpdyDataIR data_ir(0, bytes); | |
| 1118 SpdySerializedFrame frame(framer.SerializeData(data_ir)); | |
| 1119 | |
| 1120 // We shouldn't have to read the whole frame before we signal an error. | |
| 1121 EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); | |
| 1122 EXPECT_GT(frame.size(), framer.ProcessInput(frame.data(), frame.size())); | |
| 1123 EXPECT_TRUE(framer.HasError()); | |
| 1124 EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.spdy_framer_error()) | |
| 1125 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 1126 } | |
| 1127 | |
| 1128 // Test that if we receive a HEADERS with stream ID zero, we signal an error | |
| 1129 // (but don't crash). | |
| 1130 TEST_P(SpdyFramerTest, HeadersWithStreamIdZero) { | |
| 1131 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 1132 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 1133 framer.set_visitor(&visitor); | |
| 1134 | |
| 1135 SpdyHeadersIR headers(0); | |
| 1136 headers.SetHeader("alpha", "beta"); | |
| 1137 SpdySerializedFrame frame( | |
| 1138 SpdyFramerPeer::SerializeHeaders(&framer, headers, &output_)); | |
| 1139 | |
| 1140 // We shouldn't have to read the whole frame before we signal an error. | |
| 1141 EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); | |
| 1142 EXPECT_GT(frame.size(), framer.ProcessInput(frame.data(), frame.size())); | |
| 1143 EXPECT_TRUE(framer.HasError()); | |
| 1144 EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.spdy_framer_error()) | |
| 1145 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 1146 } | |
| 1147 | |
| 1148 // Test that if we receive a PRIORITY with stream ID zero, we signal an error | |
| 1149 // (but don't crash). | |
| 1150 TEST_P(SpdyFramerTest, PriorityWithStreamIdZero) { | |
| 1151 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 1152 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 1153 framer.set_visitor(&visitor); | |
| 1154 | |
| 1155 SpdyPriorityIR priority_ir(0, 1, 16, true); | |
| 1156 SpdySerializedFrame frame(framer.SerializeFrame(priority_ir)); | |
| 1157 if (use_output_) { | |
| 1158 ASSERT_TRUE(framer.SerializeFrame(priority_ir, &output_)); | |
| 1159 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 1160 } | |
| 1161 | |
| 1162 // We shouldn't have to read the whole frame before we signal an error. | |
| 1163 EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); | |
| 1164 EXPECT_GT(frame.size(), framer.ProcessInput(frame.data(), frame.size())); | |
| 1165 EXPECT_TRUE(framer.HasError()); | |
| 1166 EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.spdy_framer_error()) | |
| 1167 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 1168 } | |
| 1169 | |
| 1170 // Test that if we receive a RST_STREAM with stream ID zero, we signal an error | |
| 1171 // (but don't crash). | |
| 1172 TEST_P(SpdyFramerTest, RstStreamWithStreamIdZero) { | |
| 1173 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 1174 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 1175 framer.set_visitor(&visitor); | |
| 1176 | |
| 1177 SpdyRstStreamIR rst_stream_ir(0, ERROR_CODE_PROTOCOL_ERROR); | |
| 1178 SpdySerializedFrame frame(framer.SerializeRstStream(rst_stream_ir)); | |
| 1179 if (use_output_) { | |
| 1180 ASSERT_TRUE(framer.SerializeRstStream(rst_stream_ir, &output_)); | |
| 1181 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 1182 } | |
| 1183 | |
| 1184 // We shouldn't have to read the whole frame before we signal an error. | |
| 1185 EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); | |
| 1186 EXPECT_GT(frame.size(), framer.ProcessInput(frame.data(), frame.size())); | |
| 1187 EXPECT_TRUE(framer.HasError()); | |
| 1188 EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.spdy_framer_error()) | |
| 1189 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 1190 } | |
| 1191 | |
| 1192 // Test that if we receive a SETTINGS with stream ID other than zero, | |
| 1193 // we signal an error (but don't crash). | |
| 1194 TEST_P(SpdyFramerTest, SettingsWithStreamIdNotZero) { | |
| 1195 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 1196 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 1197 framer.set_visitor(&visitor); | |
| 1198 | |
| 1199 // Settings frame with invalid StreamID of 0x01 | |
| 1200 char kH2FrameData[] = { | |
| 1201 0x00, 0x00, 0x06, // Length: 6 | |
| 1202 0x04, // Type: SETTINGS | |
| 1203 0x00, // Flags: none | |
| 1204 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1205 0x00, 0x04, // Param: INITIAL_WINDOW_SIZE | |
| 1206 0x0a, 0x0b, 0x0c, 0x0d, // Value: 168496141 | |
| 1207 }; | |
| 1208 | |
| 1209 SpdySerializedFrame frame(kH2FrameData, sizeof(kH2FrameData), false); | |
| 1210 | |
| 1211 // We shouldn't have to read the whole frame before we signal an error. | |
| 1212 EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); | |
| 1213 EXPECT_GT(frame.size(), framer.ProcessInput(frame.data(), frame.size())); | |
| 1214 EXPECT_TRUE(framer.HasError()); | |
| 1215 EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.spdy_framer_error()) | |
| 1216 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 1217 } | |
| 1218 | |
| 1219 // Test that if we receive a GOAWAY with stream ID other than zero, | |
| 1220 // we signal an error (but don't crash). | |
| 1221 TEST_P(SpdyFramerTest, GoawayWithStreamIdNotZero) { | |
| 1222 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 1223 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 1224 framer.set_visitor(&visitor); | |
| 1225 | |
| 1226 // GOAWAY frame with invalid StreamID of 0x01 | |
| 1227 char kH2FrameData[] = { | |
| 1228 0x00, 0x00, 0x0a, // Length: 10 | |
| 1229 0x07, // Type: GOAWAY | |
| 1230 0x00, // Flags: none | |
| 1231 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1232 0x00, 0x00, 0x00, 0x00, // Last: 0 | |
| 1233 0x00, 0x00, 0x00, 0x00, // Error: NO_ERROR | |
| 1234 0x47, 0x41, // Description | |
| 1235 }; | |
| 1236 | |
| 1237 SpdySerializedFrame frame(kH2FrameData, sizeof(kH2FrameData), false); | |
| 1238 | |
| 1239 // We shouldn't have to read the whole frame before we signal an error. | |
| 1240 EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); | |
| 1241 EXPECT_GT(frame.size(), framer.ProcessInput(frame.data(), frame.size())); | |
| 1242 EXPECT_TRUE(framer.HasError()); | |
| 1243 EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.spdy_framer_error()) | |
| 1244 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 1245 } | |
| 1246 | |
| 1247 // Test that if we receive a CONTINUATION with stream ID zero, we signal an | |
| 1248 // SPDY_INVALID_STREAM_ID. | |
| 1249 TEST_P(SpdyFramerTest, ContinuationWithStreamIdZero) { | |
| 1250 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 1251 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 1252 framer.set_visitor(&visitor); | |
| 1253 | |
| 1254 SpdyContinuationIR continuation(0); | |
| 1255 auto some_nonsense_encoding = | |
| 1256 base::MakeUnique<SpdyString>("some nonsense encoding"); | |
| 1257 continuation.take_encoding(std::move(some_nonsense_encoding)); | |
| 1258 continuation.set_end_headers(true); | |
| 1259 SpdySerializedFrame frame(framer.SerializeContinuation(continuation)); | |
| 1260 if (use_output_) { | |
| 1261 ASSERT_TRUE(framer.SerializeContinuation(continuation, &output_)); | |
| 1262 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 1263 } | |
| 1264 | |
| 1265 // We shouldn't have to read the whole frame before we signal an error. | |
| 1266 EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); | |
| 1267 EXPECT_GT(frame.size(), framer.ProcessInput(frame.data(), frame.size())); | |
| 1268 EXPECT_TRUE(framer.HasError()); | |
| 1269 EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.spdy_framer_error()) | |
| 1270 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 1271 } | |
| 1272 | |
| 1273 // Test that if we receive a PUSH_PROMISE with stream ID zero, we signal an | |
| 1274 // SPDY_INVALID_STREAM_ID. | |
| 1275 TEST_P(SpdyFramerTest, PushPromiseWithStreamIdZero) { | |
| 1276 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 1277 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 1278 framer.set_visitor(&visitor); | |
| 1279 | |
| 1280 SpdyPushPromiseIR push_promise(0, 4); | |
| 1281 push_promise.SetHeader("alpha", "beta"); | |
| 1282 SpdySerializedFrame frame(SpdyFramerPeer::SerializePushPromise( | |
| 1283 &framer, push_promise, use_output_ ? &output_ : nullptr)); | |
| 1284 | |
| 1285 // We shouldn't have to read the whole frame before we signal an error. | |
| 1286 EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); | |
| 1287 EXPECT_GT(frame.size(), framer.ProcessInput(frame.data(), frame.size())); | |
| 1288 EXPECT_TRUE(framer.HasError()); | |
| 1289 EXPECT_EQ(SpdyFramer::SPDY_INVALID_STREAM_ID, framer.spdy_framer_error()) | |
| 1290 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 1291 } | |
| 1292 | |
| 1293 // Test that if we receive a PUSH_PROMISE with promised stream ID zero, we | |
| 1294 // signal SPDY_INVALID_STREAM_ID. | |
| 1295 TEST_P(SpdyFramerTest, PushPromiseWithPromisedStreamIdZero) { | |
| 1296 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 1297 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 1298 framer.set_visitor(&visitor); | |
| 1299 | |
| 1300 SpdyPushPromiseIR push_promise(3, 0); | |
| 1301 push_promise.SetHeader("alpha", "beta"); | |
| 1302 SpdySerializedFrame frame(SpdyFramerPeer::SerializePushPromise( | |
| 1303 &framer, push_promise, use_output_ ? &output_ : nullptr)); | |
| 1304 | |
| 1305 EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); | |
| 1306 framer.ProcessInput(frame.data(), frame.size()); | |
| 1307 EXPECT_TRUE(framer.HasError()); | |
| 1308 EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, framer.spdy_framer_error()) | |
| 1309 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 1310 } | |
| 1311 | |
| 1312 TEST_P(SpdyFramerTest, DuplicateHeader) { | |
| 1313 SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION); | |
| 1314 // Frame builder with plentiful buffer size. | |
| 1315 SpdyFrameBuilder frame(1024); | |
| 1316 frame.BeginNewFrame(framer, SpdyFrameType::HEADERS, 0, 3); | |
| 1317 | |
| 1318 frame.WriteUInt32(2); // Number of headers. | |
| 1319 frame.WriteStringPiece32("name"); | |
| 1320 frame.WriteStringPiece32("value1"); | |
| 1321 frame.WriteStringPiece32("name"); | |
| 1322 frame.WriteStringPiece32("value2"); | |
| 1323 // write the length | |
| 1324 frame.OverwriteLength(framer, frame.length() - framer.GetFrameHeaderSize()); | |
| 1325 | |
| 1326 SpdyHeaderBlock new_headers; | |
| 1327 SpdySerializedFrame control_frame(frame.take()); | |
| 1328 SpdyStringPiece serialized_headers = | |
| 1329 GetSerializedHeaders(control_frame, framer); | |
| 1330 // This should fail because duplicate headers are verboten by the spec. | |
| 1331 EXPECT_FALSE(framer.ParseHeaderBlockInBuffer( | |
| 1332 serialized_headers.data(), serialized_headers.size(), &new_headers)); | |
| 1333 } | |
| 1334 | |
| 1335 TEST_P(SpdyFramerTest, MultiValueHeader) { | |
| 1336 SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION); | |
| 1337 // Frame builder with plentiful buffer size. | |
| 1338 SpdyFrameBuilder frame(1024); | |
| 1339 frame.BeginNewFrame(framer, SpdyFrameType::HEADERS, | |
| 1340 HEADERS_FLAG_PRIORITY | HEADERS_FLAG_END_HEADERS, 3); | |
| 1341 frame.WriteUInt32(0); // Priority exclusivity and dependent stream. | |
| 1342 frame.WriteUInt8(255); // Priority weight. | |
| 1343 | |
| 1344 SpdyString value("value1\0value2", 13); | |
| 1345 // TODO(jgraettinger): If this pattern appears again, move to test class. | |
| 1346 SpdyHeaderBlock header_set; | |
| 1347 header_set["name"] = value; | |
| 1348 SpdyString buffer; | |
| 1349 HpackEncoder encoder(ObtainHpackHuffmanTable()); | |
| 1350 encoder.DisableCompression(); | |
| 1351 encoder.EncodeHeaderSet(header_set, &buffer); | |
| 1352 frame.WriteBytes(&buffer[0], buffer.size()); | |
| 1353 // write the length | |
| 1354 frame.OverwriteLength(framer, frame.length() - framer.GetFrameHeaderSize()); | |
| 1355 | |
| 1356 SpdySerializedFrame control_frame(frame.take()); | |
| 1357 | |
| 1358 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 1359 visitor.SimulateInFramer( | |
| 1360 reinterpret_cast<unsigned char*>(control_frame.data()), | |
| 1361 control_frame.size()); | |
| 1362 | |
| 1363 EXPECT_THAT(visitor.headers_, testing::ElementsAre(testing::Pair( | |
| 1364 "name", SpdyStringPiece(value)))); | |
| 1365 } | |
| 1366 | |
| 1367 TEST_P(SpdyFramerTest, CompressEmptyHeaders) { | |
| 1368 // See crbug.com/172383 | |
| 1369 SpdyHeadersIR headers(1); | |
| 1370 headers.SetHeader("server", "SpdyServer 1.0"); | |
| 1371 headers.SetHeader("date", "Mon 12 Jan 2009 12:12:12 PST"); | |
| 1372 headers.SetHeader("status", "200"); | |
| 1373 headers.SetHeader("version", "HTTP/1.1"); | |
| 1374 headers.SetHeader("content-type", "text/html"); | |
| 1375 headers.SetHeader("content-length", "12"); | |
| 1376 headers.SetHeader("x-empty-header", ""); | |
| 1377 | |
| 1378 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 1379 SpdySerializedFrame frame1( | |
| 1380 SpdyFramerPeer::SerializeHeaders(&framer, headers, &output_)); | |
| 1381 } | |
| 1382 | |
| 1383 TEST_P(SpdyFramerTest, Basic) { | |
| 1384 // Send HEADERS frames with PRIORITY and END_HEADERS set. | |
| 1385 // frame-format off | |
| 1386 const unsigned char kH2Input[] = { | |
| 1387 0x00, 0x00, 0x05, // Length: 5 | |
| 1388 0x01, // Type: HEADERS | |
| 1389 0x24, // Flags: END_HEADERS|PRIORITY | |
| 1390 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1391 0x00, 0x00, 0x00, 0x00, // Parent: 0 | |
| 1392 0x82, // Weight: 131 | |
| 1393 | |
| 1394 0x00, 0x00, 0x01, // Length: 1 | |
| 1395 0x01, // Type: HEADERS | |
| 1396 0x04, // Flags: END_HEADERS | |
| 1397 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1398 0x8c, // :status: 200 | |
| 1399 | |
| 1400 0x00, 0x00, 0x0c, // Length: 12 | |
| 1401 0x00, // Type: DATA | |
| 1402 0x00, // Flags: none | |
| 1403 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1404 0xde, 0xad, 0xbe, 0xef, // Payload | |
| 1405 0xde, 0xad, 0xbe, 0xef, // | |
| 1406 0xde, 0xad, 0xbe, 0xef, // | |
| 1407 | |
| 1408 0x00, 0x00, 0x05, // Length: 5 | |
| 1409 0x01, // Type: HEADERS | |
| 1410 0x24, // Flags: END_HEADERS|PRIORITY | |
| 1411 0x00, 0x00, 0x00, 0x03, // Stream: 3 | |
| 1412 0x00, 0x00, 0x00, 0x00, // Parent: 0 | |
| 1413 0x82, // Weight: 131 | |
| 1414 | |
| 1415 0x00, 0x00, 0x08, // Length: 8 | |
| 1416 0x00, // Type: DATA | |
| 1417 0x00, // Flags: none | |
| 1418 0x00, 0x00, 0x00, 0x03, // Stream: 3 | |
| 1419 0xde, 0xad, 0xbe, 0xef, // Payload | |
| 1420 0xde, 0xad, 0xbe, 0xef, // | |
| 1421 | |
| 1422 0x00, 0x00, 0x04, // Length: 4 | |
| 1423 0x00, // Type: DATA | |
| 1424 0x00, // Flags: none | |
| 1425 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1426 0xde, 0xad, 0xbe, 0xef, // Payload | |
| 1427 | |
| 1428 0x00, 0x00, 0x04, // Length: 4 | |
| 1429 0x03, // Type: RST_STREAM | |
| 1430 0x00, // Flags: none | |
| 1431 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1432 0x00, 0x00, 0x00, 0x08, // Error: CANCEL | |
| 1433 | |
| 1434 0x00, 0x00, 0x00, // Length: 0 | |
| 1435 0x00, // Type: DATA | |
| 1436 0x00, // Flags: none | |
| 1437 0x00, 0x00, 0x00, 0x03, // Stream: 3 | |
| 1438 | |
| 1439 0x00, 0x00, 0x04, // Length: 4 | |
| 1440 0x03, // Type: RST_STREAM | |
| 1441 0x00, // Flags: none | |
| 1442 0x00, 0x00, 0x00, 0x03, // Stream: 3 | |
| 1443 0x00, 0x00, 0x00, 0x08, // Error: CANCEL | |
| 1444 }; | |
| 1445 // frame-format on | |
| 1446 | |
| 1447 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 1448 visitor.SimulateInFramer(kH2Input, sizeof(kH2Input)); | |
| 1449 | |
| 1450 EXPECT_EQ(24, visitor.data_bytes_); | |
| 1451 EXPECT_EQ(0, visitor.error_count_); | |
| 1452 EXPECT_EQ(2, visitor.fin_frame_count_); | |
| 1453 | |
| 1454 EXPECT_EQ(3, visitor.headers_frame_count_); | |
| 1455 | |
| 1456 EXPECT_EQ(0, visitor.fin_flag_count_); | |
| 1457 EXPECT_EQ(0, visitor.end_of_stream_count_); | |
| 1458 EXPECT_EQ(4, visitor.data_frame_count_); | |
| 1459 } | |
| 1460 | |
| 1461 // Test that the FIN flag on a data frame signifies EOF. | |
| 1462 TEST_P(SpdyFramerTest, FinOnDataFrame) { | |
| 1463 // Send HEADERS frames with END_HEADERS set. | |
| 1464 // frame-format off | |
| 1465 const unsigned char kH2Input[] = { | |
| 1466 0x00, 0x00, 0x05, // Length: 5 | |
| 1467 0x01, // Type: HEADERS | |
| 1468 0x24, // Flags: END_HEADERS|PRIORITY | |
| 1469 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1470 0x00, 0x00, 0x00, 0x00, // Parent: 0 | |
| 1471 0x82, // Weight: 131 | |
| 1472 | |
| 1473 0x00, 0x00, 0x01, // Length: 1 | |
| 1474 0x01, // Type: HEADERS | |
| 1475 0x04, // Flags: END_HEADERS | |
| 1476 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1477 0x8c, // :status: 200 | |
| 1478 | |
| 1479 0x00, 0x00, 0x0c, // Length: 12 | |
| 1480 0x00, // Type: DATA | |
| 1481 0x00, // Flags: none | |
| 1482 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1483 0xde, 0xad, 0xbe, 0xef, // Payload | |
| 1484 0xde, 0xad, 0xbe, 0xef, // | |
| 1485 0xde, 0xad, 0xbe, 0xef, // | |
| 1486 | |
| 1487 0x00, 0x00, 0x04, // Length: 4 | |
| 1488 0x00, // Type: DATA | |
| 1489 0x01, // Flags: END_STREAM | |
| 1490 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1491 0xde, 0xad, 0xbe, 0xef, // Payload | |
| 1492 }; | |
| 1493 // frame-format on | |
| 1494 | |
| 1495 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 1496 visitor.SimulateInFramer(kH2Input, sizeof(kH2Input)); | |
| 1497 | |
| 1498 EXPECT_EQ(0, visitor.error_count_); | |
| 1499 EXPECT_EQ(2, visitor.headers_frame_count_); | |
| 1500 EXPECT_EQ(16, visitor.data_bytes_); | |
| 1501 EXPECT_EQ(0, visitor.fin_frame_count_); | |
| 1502 EXPECT_EQ(0, visitor.fin_flag_count_); | |
| 1503 EXPECT_EQ(1, visitor.end_of_stream_count_); | |
| 1504 EXPECT_EQ(2, visitor.data_frame_count_); | |
| 1505 } | |
| 1506 | |
| 1507 TEST_P(SpdyFramerTest, FinOnHeadersFrame) { | |
| 1508 // Send HEADERS frames with END_HEADERS set. | |
| 1509 // frame-format off | |
| 1510 const unsigned char kH2Input[] = { | |
| 1511 0x00, 0x00, 0x05, // Length: 5 | |
| 1512 0x01, // Type: HEADERS | |
| 1513 0x24, // Flags: END_HEADERS|PRIORITY | |
| 1514 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1515 0x00, 0x00, 0x00, 0x00, // Parent: 0 | |
| 1516 0x82, // Weight: 131 | |
| 1517 | |
| 1518 0x00, 0x00, 0x01, // Length: 1 | |
| 1519 0x01, // Type: HEADERS | |
| 1520 0x05, // Flags: END_STREAM|END_HEADERS | |
| 1521 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1522 0x8c, // :status: 200 | |
| 1523 }; | |
| 1524 // frame-format on | |
| 1525 | |
| 1526 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 1527 visitor.SimulateInFramer(kH2Input, sizeof(kH2Input)); | |
| 1528 | |
| 1529 EXPECT_EQ(0, visitor.error_count_); | |
| 1530 EXPECT_EQ(2, visitor.headers_frame_count_); | |
| 1531 EXPECT_EQ(0, visitor.data_bytes_); | |
| 1532 EXPECT_EQ(0, visitor.fin_frame_count_); | |
| 1533 EXPECT_EQ(1, visitor.fin_flag_count_); | |
| 1534 EXPECT_EQ(1, visitor.end_of_stream_count_); | |
| 1535 EXPECT_EQ(0, visitor.data_frame_count_); | |
| 1536 } | |
| 1537 | |
| 1538 // Verify we can decompress the stream even if handed over to the | |
| 1539 // framer 1 byte at a time. | |
| 1540 TEST_P(SpdyFramerTest, UnclosedStreamDataCompressorsOneByteAtATime) { | |
| 1541 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 1542 | |
| 1543 const char kHeader1[] = "header1"; | |
| 1544 const char kHeader2[] = "header2"; | |
| 1545 const char kValue1[] = "value1"; | |
| 1546 const char kValue2[] = "value2"; | |
| 1547 | |
| 1548 SpdyHeadersIR headers(1); | |
| 1549 headers.SetHeader(kHeader1, kValue1); | |
| 1550 headers.SetHeader(kHeader2, kValue2); | |
| 1551 SpdySerializedFrame headers_frame(SpdyFramerPeer::SerializeHeaders( | |
| 1552 &framer, headers, use_output_ ? &output_ : nullptr)); | |
| 1553 | |
| 1554 const char bytes[] = "this is a test test test test test!"; | |
| 1555 SpdyDataIR data_ir(1, SpdyStringPiece(bytes, arraysize(bytes))); | |
| 1556 data_ir.set_fin(true); | |
| 1557 SpdySerializedFrame send_frame(framer.SerializeData(data_ir)); | |
| 1558 | |
| 1559 // Run the inputs through the framer. | |
| 1560 TestSpdyVisitor visitor(SpdyFramer::ENABLE_COMPRESSION); | |
| 1561 const unsigned char* data; | |
| 1562 data = reinterpret_cast<const unsigned char*>(headers_frame.data()); | |
| 1563 for (size_t idx = 0; idx < headers_frame.size(); ++idx) { | |
| 1564 visitor.SimulateInFramer(data + idx, 1); | |
| 1565 ASSERT_EQ(0, visitor.error_count_); | |
| 1566 } | |
| 1567 data = reinterpret_cast<const unsigned char*>(send_frame.data()); | |
| 1568 for (size_t idx = 0; idx < send_frame.size(); ++idx) { | |
| 1569 visitor.SimulateInFramer(data + idx, 1); | |
| 1570 ASSERT_EQ(0, visitor.error_count_); | |
| 1571 } | |
| 1572 | |
| 1573 EXPECT_EQ(0, visitor.error_count_); | |
| 1574 EXPECT_EQ(1, visitor.headers_frame_count_); | |
| 1575 EXPECT_EQ(arraysize(bytes), static_cast<unsigned>(visitor.data_bytes_)); | |
| 1576 EXPECT_EQ(0, visitor.fin_frame_count_); | |
| 1577 EXPECT_EQ(0, visitor.fin_flag_count_); | |
| 1578 EXPECT_EQ(1, visitor.end_of_stream_count_); | |
| 1579 EXPECT_EQ(1, visitor.data_frame_count_); | |
| 1580 } | |
| 1581 | |
| 1582 TEST_P(SpdyFramerTest, WindowUpdateFrame) { | |
| 1583 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 1584 SpdyWindowUpdateIR window_update(1, 0x12345678); | |
| 1585 SpdySerializedFrame frame(framer.SerializeWindowUpdate(window_update)); | |
| 1586 if (use_output_) { | |
| 1587 ASSERT_TRUE(framer.SerializeWindowUpdate(window_update, &output_)); | |
| 1588 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 1589 } | |
| 1590 | |
| 1591 const char kDescription[] = "WINDOW_UPDATE frame, stream 1, delta 0x12345678"; | |
| 1592 const unsigned char kH2FrameData[] = { | |
| 1593 0x00, 0x00, 0x04, // Length: 4 | |
| 1594 0x08, // Type: WINDOW_UPDATE | |
| 1595 0x00, // Flags: none | |
| 1596 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1597 0x12, 0x34, 0x56, 0x78, // Increment: 305419896 | |
| 1598 }; | |
| 1599 | |
| 1600 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 1601 } | |
| 1602 | |
| 1603 TEST_P(SpdyFramerTest, CreateDataFrame) { | |
| 1604 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 1605 | |
| 1606 { | |
| 1607 const char kDescription[] = "'hello' data frame, no FIN"; | |
| 1608 // frame-format off | |
| 1609 const unsigned char kH2FrameData[] = { | |
| 1610 0x00, 0x00, 0x05, // Length: 5 | |
| 1611 0x00, // Type: DATA | |
| 1612 0x00, // Flags: none | |
| 1613 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1614 'h', 'e', 'l', 'l', // Payload | |
| 1615 'o', // | |
| 1616 }; | |
| 1617 // frame-format on | |
| 1618 const char bytes[] = "hello"; | |
| 1619 | |
| 1620 SpdyDataIR data_ir(1, bytes); | |
| 1621 SpdySerializedFrame frame(framer.SerializeData(data_ir)); | |
| 1622 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 1623 | |
| 1624 SpdyDataIR data_header_ir(1); | |
| 1625 data_header_ir.SetDataShallow(bytes); | |
| 1626 frame = | |
| 1627 framer.SerializeDataFrameHeaderWithPaddingLengthField(data_header_ir); | |
| 1628 CompareCharArraysWithHexError( | |
| 1629 kDescription, reinterpret_cast<const unsigned char*>(frame.data()), | |
| 1630 framer.GetDataFrameMinimumSize(), kH2FrameData, | |
| 1631 framer.GetDataFrameMinimumSize()); | |
| 1632 } | |
| 1633 | |
| 1634 { | |
| 1635 const char kDescription[] = "'hello' data frame with more padding, no FIN"; | |
| 1636 const unsigned char kH2FrameData[] = { | |
| 1637 0x00, 0x00, 0xfd, // Length: 253 | |
| 1638 0x00, // Type: DATA | |
| 1639 0x08, // Flags: PADDED | |
| 1640 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1641 0xf7, // PadLen: 247 trailing bytes | |
| 1642 'h', 'e', 'l', 'l', // Payload | |
| 1643 'o', // | |
| 1644 // Padding of 247 0x00(s). | |
| 1645 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 1646 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 1647 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 1648 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 1649 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 1650 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 1651 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 1652 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 1653 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 1654 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 1655 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 1656 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 1657 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 1658 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 1659 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 1660 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 1661 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 1662 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 1663 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 1664 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 1665 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 1666 }; | |
| 1667 // frame-format on | |
| 1668 // clang-format on | |
| 1669 const char bytes[] = "hello"; | |
| 1670 | |
| 1671 SpdyDataIR data_ir(1, bytes); | |
| 1672 // 247 zeros and the pad length field make the overall padding to be 248 | |
| 1673 // bytes. | |
| 1674 data_ir.set_padding_len(248); | |
| 1675 SpdySerializedFrame frame(framer.SerializeData(data_ir)); | |
| 1676 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 1677 | |
| 1678 frame = framer.SerializeDataFrameHeaderWithPaddingLengthField(data_ir); | |
| 1679 CompareCharArraysWithHexError( | |
| 1680 kDescription, reinterpret_cast<const unsigned char*>(frame.data()), | |
| 1681 framer.GetDataFrameMinimumSize(), kH2FrameData, | |
| 1682 framer.GetDataFrameMinimumSize()); | |
| 1683 } | |
| 1684 | |
| 1685 { | |
| 1686 const char kDescription[] = "'hello' data frame with few padding, no FIN"; | |
| 1687 // frame-format off | |
| 1688 const unsigned char kH2FrameData[] = { | |
| 1689 0x00, 0x00, 0x0d, // Length: 13 | |
| 1690 0x00, // Type: DATA | |
| 1691 0x08, // Flags: PADDED | |
| 1692 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1693 0x07, // PadLen: 7 trailing bytes | |
| 1694 'h', 'e', 'l', 'l', // Payload | |
| 1695 'o', // | |
| 1696 0x00, 0x00, 0x00, 0x00, // Padding | |
| 1697 0x00, 0x00, 0x00, // Padding | |
| 1698 }; | |
| 1699 // frame-format on | |
| 1700 const char bytes[] = "hello"; | |
| 1701 | |
| 1702 SpdyDataIR data_ir(1, bytes); | |
| 1703 // 7 zeros and the pad length field make the overall padding to be 8 bytes. | |
| 1704 data_ir.set_padding_len(8); | |
| 1705 SpdySerializedFrame frame(framer.SerializeData(data_ir)); | |
| 1706 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 1707 } | |
| 1708 | |
| 1709 { | |
| 1710 const char kDescription[] = | |
| 1711 "'hello' data frame with 1 byte padding, no FIN"; | |
| 1712 // frame-format off | |
| 1713 const unsigned char kH2FrameData[] = { | |
| 1714 0x00, 0x00, 0x06, // Length: 6 | |
| 1715 0x00, // Type: DATA | |
| 1716 0x08, // Flags: PADDED | |
| 1717 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1718 0x00, // PadLen: 0 trailing bytes | |
| 1719 'h', 'e', 'l', 'l', // Payload | |
| 1720 'o', // | |
| 1721 }; | |
| 1722 // frame-format on | |
| 1723 const char bytes[] = "hello"; | |
| 1724 | |
| 1725 SpdyDataIR data_ir(1, bytes); | |
| 1726 // The pad length field itself is used for the 1-byte padding and no padding | |
| 1727 // payload is needed. | |
| 1728 data_ir.set_padding_len(1); | |
| 1729 SpdySerializedFrame frame(framer.SerializeData(data_ir)); | |
| 1730 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 1731 | |
| 1732 frame = framer.SerializeDataFrameHeaderWithPaddingLengthField(data_ir); | |
| 1733 CompareCharArraysWithHexError( | |
| 1734 kDescription, reinterpret_cast<const unsigned char*>(frame.data()), | |
| 1735 framer.GetDataFrameMinimumSize(), kH2FrameData, | |
| 1736 framer.GetDataFrameMinimumSize()); | |
| 1737 } | |
| 1738 | |
| 1739 { | |
| 1740 const char kDescription[] = "Data frame with negative data byte, no FIN"; | |
| 1741 const unsigned char kH2FrameData[] = { | |
| 1742 0x00, 0x00, 0x01, // Length: 1 | |
| 1743 0x00, // Type: DATA | |
| 1744 0x00, // Flags: none | |
| 1745 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1746 0xff, // Payload | |
| 1747 }; | |
| 1748 SpdyDataIR data_ir(1, "\xff"); | |
| 1749 SpdySerializedFrame frame(framer.SerializeData(data_ir)); | |
| 1750 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 1751 } | |
| 1752 | |
| 1753 { | |
| 1754 const char kDescription[] = "'hello' data frame, with FIN"; | |
| 1755 const unsigned char kH2FrameData[] = { | |
| 1756 0x00, 0x00, 0x05, // Length: 5 | |
| 1757 0x00, // Type: DATA | |
| 1758 0x01, // Flags: END_STREAM | |
| 1759 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1760 0x68, 0x65, 0x6c, 0x6c, // Payload | |
| 1761 0x6f, // | |
| 1762 }; | |
| 1763 SpdyDataIR data_ir(1, "hello"); | |
| 1764 data_ir.set_fin(true); | |
| 1765 SpdySerializedFrame frame(framer.SerializeData(data_ir)); | |
| 1766 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 1767 } | |
| 1768 | |
| 1769 { | |
| 1770 const char kDescription[] = "Empty data frame"; | |
| 1771 const unsigned char kH2FrameData[] = { | |
| 1772 0x00, 0x00, 0x00, // Length: 0 | |
| 1773 0x00, // Type: DATA | |
| 1774 0x00, // Flags: none | |
| 1775 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1776 }; | |
| 1777 SpdyDataIR data_ir(1, ""); | |
| 1778 SpdySerializedFrame frame(framer.SerializeData(data_ir)); | |
| 1779 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 1780 | |
| 1781 frame = framer.SerializeDataFrameHeaderWithPaddingLengthField(data_ir); | |
| 1782 CompareCharArraysWithHexError( | |
| 1783 kDescription, reinterpret_cast<const unsigned char*>(frame.data()), | |
| 1784 framer.GetDataFrameMinimumSize(), kH2FrameData, | |
| 1785 framer.GetDataFrameMinimumSize()); | |
| 1786 } | |
| 1787 | |
| 1788 { | |
| 1789 const char kDescription[] = "Data frame with max stream ID"; | |
| 1790 const unsigned char kH2FrameData[] = { | |
| 1791 0x00, 0x00, 0x05, // Length: 5 | |
| 1792 0x00, // Type: DATA | |
| 1793 0x01, // Flags: END_STREAM | |
| 1794 0x7f, 0xff, 0xff, 0xff, // Stream: 0x7fffffff | |
| 1795 0x68, 0x65, 0x6c, 0x6c, // Payload | |
| 1796 0x6f, // | |
| 1797 }; | |
| 1798 SpdyDataIR data_ir(0x7fffffff, "hello"); | |
| 1799 data_ir.set_fin(true); | |
| 1800 SpdySerializedFrame frame(framer.SerializeData(data_ir)); | |
| 1801 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 1802 } | |
| 1803 } | |
| 1804 | |
| 1805 TEST_P(SpdyFramerTest, CreateRstStream) { | |
| 1806 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 1807 | |
| 1808 { | |
| 1809 const char kDescription[] = "RST_STREAM frame"; | |
| 1810 const unsigned char kH2FrameData[] = { | |
| 1811 0x00, 0x00, 0x04, // Length: 4 | |
| 1812 0x03, // Type: RST_STREAM | |
| 1813 0x00, // Flags: none | |
| 1814 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 1815 0x00, 0x00, 0x00, 0x01, // Error: PROTOCOL_ERROR | |
| 1816 }; | |
| 1817 SpdyRstStreamIR rst_stream(1, ERROR_CODE_PROTOCOL_ERROR); | |
| 1818 SpdySerializedFrame frame(framer.SerializeRstStream(rst_stream)); | |
| 1819 if (use_output_) { | |
| 1820 ASSERT_TRUE(framer.SerializeRstStream(rst_stream, &output_)); | |
| 1821 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 1822 } | |
| 1823 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 1824 } | |
| 1825 | |
| 1826 { | |
| 1827 const char kDescription[] = "RST_STREAM frame with max stream ID"; | |
| 1828 const unsigned char kH2FrameData[] = { | |
| 1829 0x00, 0x00, 0x04, // Length: 4 | |
| 1830 0x03, // Type: RST_STREAM | |
| 1831 0x00, // Flags: none | |
| 1832 0x7f, 0xff, 0xff, 0xff, // Stream: 0x7fffffff | |
| 1833 0x00, 0x00, 0x00, 0x01, // Error: PROTOCOL_ERROR | |
| 1834 }; | |
| 1835 SpdyRstStreamIR rst_stream(0x7FFFFFFF, ERROR_CODE_PROTOCOL_ERROR); | |
| 1836 SpdySerializedFrame frame(framer.SerializeRstStream(rst_stream)); | |
| 1837 if (use_output_) { | |
| 1838 output_.Reset(); | |
| 1839 ASSERT_TRUE(framer.SerializeRstStream(rst_stream, &output_)); | |
| 1840 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 1841 } | |
| 1842 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 1843 } | |
| 1844 | |
| 1845 { | |
| 1846 const char kDescription[] = "RST_STREAM frame with max status code"; | |
| 1847 const unsigned char kH2FrameData[] = { | |
| 1848 0x00, 0x00, 0x04, // Length: 4 | |
| 1849 0x03, // Type: RST_STREAM | |
| 1850 0x00, // Flags: none | |
| 1851 0x7f, 0xff, 0xff, 0xff, // Stream: 0x7fffffff | |
| 1852 0x00, 0x00, 0x00, 0x02, // Error: INTERNAL_ERROR | |
| 1853 }; | |
| 1854 SpdyRstStreamIR rst_stream(0x7FFFFFFF, ERROR_CODE_INTERNAL_ERROR); | |
| 1855 SpdySerializedFrame frame(framer.SerializeRstStream(rst_stream)); | |
| 1856 if (use_output_) { | |
| 1857 output_.Reset(); | |
| 1858 ASSERT_TRUE(framer.SerializeRstStream(rst_stream, &output_)); | |
| 1859 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 1860 } | |
| 1861 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 1862 } | |
| 1863 } | |
| 1864 | |
| 1865 TEST_P(SpdyFramerTest, CreateSettings) { | |
| 1866 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 1867 | |
| 1868 { | |
| 1869 const char kDescription[] = "Network byte order SETTINGS frame"; | |
| 1870 | |
| 1871 const unsigned char kH2FrameData[] = { | |
| 1872 0x00, 0x00, 0x06, // Length: 6 | |
| 1873 0x04, // Type: SETTINGS | |
| 1874 0x00, // Flags: none | |
| 1875 0x00, 0x00, 0x00, 0x00, // Stream: 0 | |
| 1876 0x00, 0x04, // Param: INITIAL_WINDOW_SIZE | |
| 1877 0x0a, 0x0b, 0x0c, 0x0d, // Value: 168496141 | |
| 1878 }; | |
| 1879 | |
| 1880 uint32_t kValue = 0x0a0b0c0d; | |
| 1881 SpdySettingsIR settings_ir; | |
| 1882 | |
| 1883 SpdySettingsIds kId = SETTINGS_INITIAL_WINDOW_SIZE; | |
| 1884 settings_ir.AddSetting(kId, kValue); | |
| 1885 | |
| 1886 SpdySerializedFrame frame(framer.SerializeSettings(settings_ir)); | |
| 1887 if (use_output_) { | |
| 1888 ASSERT_TRUE(framer.SerializeSettings(settings_ir, &output_)); | |
| 1889 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 1890 } | |
| 1891 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 1892 } | |
| 1893 | |
| 1894 { | |
| 1895 const char kDescription[] = "Basic SETTINGS frame"; | |
| 1896 // These end up seemingly out of order because of the way that our internal | |
| 1897 // ordering for settings_ir works. HTTP2 has no requirement on ordering on | |
| 1898 // the wire. | |
| 1899 const unsigned char kH2FrameData[] = { | |
| 1900 0x00, 0x00, 0x18, // Length: 24 | |
| 1901 0x04, // Type: SETTINGS | |
| 1902 0x00, // Flags: none | |
| 1903 0x00, 0x00, 0x00, 0x00, // Stream: 0 | |
| 1904 0x00, 0x01, // Param: HEADER_TABLE_SIZE | |
| 1905 0x00, 0x00, 0x00, 0x05, // Value: 5 | |
| 1906 0x00, 0x02, // Param: ENABLE_PUSH | |
| 1907 0x00, 0x00, 0x00, 0x06, // Value: 6 | |
| 1908 0x00, 0x03, // Param: MAX_CONCURRENT_STREAMS | |
| 1909 0x00, 0x00, 0x00, 0x07, // Value: 7 | |
| 1910 0x00, 0x04, // Param: INITIAL_WINDOW_SIZE | |
| 1911 0x00, 0x00, 0x00, 0x08, // Value: 8 | |
| 1912 }; | |
| 1913 | |
| 1914 SpdySettingsIR settings_ir; | |
| 1915 settings_ir.AddSetting(SETTINGS_HEADER_TABLE_SIZE, 5); | |
| 1916 settings_ir.AddSetting(SETTINGS_ENABLE_PUSH, 6); | |
| 1917 settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, 7); | |
| 1918 settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 8); | |
| 1919 SpdySerializedFrame frame(framer.SerializeSettings(settings_ir)); | |
| 1920 if (use_output_) { | |
| 1921 output_.Reset(); | |
| 1922 ASSERT_TRUE(framer.SerializeSettings(settings_ir, &output_)); | |
| 1923 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 1924 } | |
| 1925 | |
| 1926 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 1927 } | |
| 1928 | |
| 1929 { | |
| 1930 const char kDescription[] = "Empty SETTINGS frame"; | |
| 1931 | |
| 1932 const unsigned char kH2FrameData[] = { | |
| 1933 0x00, 0x00, 0x00, // Length: 0 | |
| 1934 0x04, // Type: SETTINGS | |
| 1935 0x00, // Flags: none | |
| 1936 0x00, 0x00, 0x00, 0x00, // Stream: 0 | |
| 1937 }; | |
| 1938 SpdySettingsIR settings_ir; | |
| 1939 SpdySerializedFrame frame(framer.SerializeSettings(settings_ir)); | |
| 1940 if (use_output_) { | |
| 1941 output_.Reset(); | |
| 1942 ASSERT_TRUE(framer.SerializeSettings(settings_ir, &output_)); | |
| 1943 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 1944 } | |
| 1945 | |
| 1946 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 1947 } | |
| 1948 } | |
| 1949 | |
| 1950 TEST_P(SpdyFramerTest, CreatePingFrame) { | |
| 1951 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 1952 | |
| 1953 { | |
| 1954 const char kDescription[] = "PING frame"; | |
| 1955 const unsigned char kH2FrameData[] = { | |
| 1956 0x00, 0x00, 0x08, // Length: 8 | |
| 1957 0x06, // Type: PING | |
| 1958 0x00, // Flags: none | |
| 1959 0x00, 0x00, 0x00, 0x00, // Stream: 0 | |
| 1960 0x12, 0x34, 0x56, 0x78, // Opaque | |
| 1961 0x9a, 0xbc, 0xde, 0xff, // Data | |
| 1962 }; | |
| 1963 const unsigned char kH2FrameDataWithAck[] = { | |
| 1964 0x00, 0x00, 0x08, // Length: 8 | |
| 1965 0x06, // Type: PING | |
| 1966 0x01, // Flags: ACK | |
| 1967 0x00, 0x00, 0x00, 0x00, // Stream: 0 | |
| 1968 0x12, 0x34, 0x56, 0x78, // Opaque | |
| 1969 0x9a, 0xbc, 0xde, 0xff, // Data | |
| 1970 }; | |
| 1971 SpdySerializedFrame frame; | |
| 1972 const SpdyPingId kPingId = 0x123456789abcdeffULL; | |
| 1973 SpdyPingIR ping_ir(kPingId); | |
| 1974 // Tests SpdyPingIR when the ping is not an ack. | |
| 1975 ASSERT_FALSE(ping_ir.is_ack()); | |
| 1976 frame = framer.SerializePing(ping_ir); | |
| 1977 if (use_output_) { | |
| 1978 ASSERT_TRUE(framer.SerializePing(ping_ir, &output_)); | |
| 1979 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 1980 } | |
| 1981 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 1982 | |
| 1983 // Tests SpdyPingIR when the ping is an ack. | |
| 1984 ping_ir.set_is_ack(true); | |
| 1985 frame = framer.SerializePing(ping_ir); | |
| 1986 if (use_output_) { | |
| 1987 output_.Reset(); | |
| 1988 ASSERT_TRUE(framer.SerializePing(ping_ir, &output_)); | |
| 1989 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 1990 } | |
| 1991 CompareFrame(kDescription, frame, kH2FrameDataWithAck, | |
| 1992 arraysize(kH2FrameDataWithAck)); | |
| 1993 } | |
| 1994 } | |
| 1995 | |
| 1996 TEST_P(SpdyFramerTest, CreateGoAway) { | |
| 1997 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 1998 | |
| 1999 { | |
| 2000 const char kDescription[] = "GOAWAY frame"; | |
| 2001 const unsigned char kH2FrameData[] = { | |
| 2002 0x00, 0x00, 0x0a, // Length: 10 | |
| 2003 0x07, // Type: GOAWAY | |
| 2004 0x00, // Flags: none | |
| 2005 0x00, 0x00, 0x00, 0x00, // Stream: 0 | |
| 2006 0x00, 0x00, 0x00, 0x00, // Last: 0 | |
| 2007 0x00, 0x00, 0x00, 0x00, // Error: NO_ERROR | |
| 2008 0x47, 0x41, // Description | |
| 2009 }; | |
| 2010 SpdyGoAwayIR goaway_ir(0, ERROR_CODE_NO_ERROR, "GA"); | |
| 2011 SpdySerializedFrame frame(framer.SerializeGoAway(goaway_ir)); | |
| 2012 if (use_output_) { | |
| 2013 ASSERT_TRUE(framer.SerializeGoAway(goaway_ir, &output_)); | |
| 2014 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 2015 } | |
| 2016 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 2017 } | |
| 2018 | |
| 2019 { | |
| 2020 const char kDescription[] = "GOAWAY frame with max stream ID, status"; | |
| 2021 const unsigned char kH2FrameData[] = { | |
| 2022 0x00, 0x00, 0x0a, // Length: 10 | |
| 2023 0x07, // Type: GOAWAY | |
| 2024 0x00, // Flags: none | |
| 2025 0x00, 0x00, 0x00, 0x00, // Stream: 0 | |
| 2026 0x7f, 0xff, 0xff, 0xff, // Last: 0x7fffffff | |
| 2027 0x00, 0x00, 0x00, 0x02, // Error: INTERNAL_ERROR | |
| 2028 0x47, 0x41, // Description | |
| 2029 }; | |
| 2030 SpdyGoAwayIR goaway_ir(0x7FFFFFFF, ERROR_CODE_INTERNAL_ERROR, "GA"); | |
| 2031 SpdySerializedFrame frame(framer.SerializeGoAway(goaway_ir)); | |
| 2032 if (use_output_) { | |
| 2033 output_.Reset(); | |
| 2034 ASSERT_TRUE(framer.SerializeGoAway(goaway_ir, &output_)); | |
| 2035 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 2036 } | |
| 2037 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 2038 } | |
| 2039 } | |
| 2040 | |
| 2041 TEST_P(SpdyFramerTest, CreateHeadersUncompressed) { | |
| 2042 SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION); | |
| 2043 | |
| 2044 { | |
| 2045 const char kDescription[] = "HEADERS frame, no FIN"; | |
| 2046 // frame-format off | |
| 2047 const unsigned char kH2FrameData[] = { | |
| 2048 0x00, 0x00, 0x12, // Length: 18 | |
| 2049 0x01, // Type: HEADERS | |
| 2050 0x04, // Flags: END_HEADERS | |
| 2051 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 2052 | |
| 2053 0x00, // Unindexed Entry | |
| 2054 0x03, // Name Len: 3 | |
| 2055 0x62, 0x61, 0x72, // bar | |
| 2056 0x03, // Value Len: 3 | |
| 2057 0x66, 0x6f, 0x6f, // foo | |
| 2058 | |
| 2059 0x00, // Unindexed Entry | |
| 2060 0x03, // Name Len: 3 | |
| 2061 0x66, 0x6f, 0x6f, // foo | |
| 2062 0x03, // Value Len: 3 | |
| 2063 0x62, 0x61, 0x72, // bar | |
| 2064 }; | |
| 2065 // frame-format on | |
| 2066 | |
| 2067 SpdyHeadersIR headers(1); | |
| 2068 headers.SetHeader("bar", "foo"); | |
| 2069 headers.SetHeader("foo", "bar"); | |
| 2070 SpdySerializedFrame frame(SpdyFramerPeer::SerializeHeaders( | |
| 2071 &framer, headers, use_output_ ? &output_ : nullptr)); | |
| 2072 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 2073 } | |
| 2074 | |
| 2075 { | |
| 2076 const char kDescription[] = | |
| 2077 "HEADERS frame with a 0-length header name, FIN, max stream ID"; | |
| 2078 // frame-format off | |
| 2079 const unsigned char kH2FrameData[] = { | |
| 2080 0x00, 0x00, 0x0f, // Length: 15 | |
| 2081 0x01, // Type: HEADERS | |
| 2082 0x05, // Flags: END_STREAM|END_HEADERS | |
| 2083 0x7f, 0xff, 0xff, 0xff, // Stream: 2147483647 | |
| 2084 | |
| 2085 0x00, // Unindexed Entry | |
| 2086 0x00, // Name Len: 0 | |
| 2087 0x03, // Value Len: 3 | |
| 2088 0x66, 0x6f, 0x6f, // foo | |
| 2089 | |
| 2090 0x00, // Unindexed Entry | |
| 2091 0x03, // Name Len: 3 | |
| 2092 0x66, 0x6f, 0x6f, // foo | |
| 2093 0x03, // Value Len: 3 | |
| 2094 0x62, 0x61, 0x72, // bar | |
| 2095 }; | |
| 2096 // frame-format on | |
| 2097 SpdyHeadersIR headers(0x7fffffff); | |
| 2098 headers.set_fin(true); | |
| 2099 headers.SetHeader("", "foo"); | |
| 2100 headers.SetHeader("foo", "bar"); | |
| 2101 SpdySerializedFrame frame(SpdyFramerPeer::SerializeHeaders( | |
| 2102 &framer, headers, use_output_ ? &output_ : nullptr)); | |
| 2103 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 2104 } | |
| 2105 | |
| 2106 { | |
| 2107 const char kDescription[] = | |
| 2108 "HEADERS frame with a 0-length header val, FIN, max stream ID"; | |
| 2109 // frame-format off | |
| 2110 const unsigned char kH2FrameData[] = { | |
| 2111 0x00, 0x00, 0x0f, // Length: 15 | |
| 2112 0x01, // Type: HEADERS | |
| 2113 0x05, // Flags: END_STREAM|END_HEADERS | |
| 2114 0x7f, 0xff, 0xff, 0xff, // Stream: 2147483647 | |
| 2115 | |
| 2116 0x00, // Unindexed Entry | |
| 2117 0x03, // Name Len: 3 | |
| 2118 0x62, 0x61, 0x72, // bar | |
| 2119 0x03, // Value Len: 3 | |
| 2120 0x66, 0x6f, 0x6f, // foo | |
| 2121 | |
| 2122 0x00, // Unindexed Entry | |
| 2123 0x03, // Name Len: 3 | |
| 2124 0x66, 0x6f, 0x6f, // foo | |
| 2125 0x00, // Value Len: 0 | |
| 2126 }; | |
| 2127 // frame-format on | |
| 2128 SpdyHeadersIR headers_ir(0x7fffffff); | |
| 2129 headers_ir.set_fin(true); | |
| 2130 headers_ir.SetHeader("bar", "foo"); | |
| 2131 headers_ir.SetHeader("foo", ""); | |
| 2132 SpdySerializedFrame frame(SpdyFramerPeer::SerializeHeaders( | |
| 2133 &framer, headers_ir, use_output_ ? &output_ : nullptr)); | |
| 2134 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 2135 } | |
| 2136 | |
| 2137 { | |
| 2138 const char kDescription[] = | |
| 2139 "HEADERS frame with a 0-length header val, FIN, max stream ID, pri"; | |
| 2140 | |
| 2141 // frame-format off | |
| 2142 const unsigned char kH2FrameData[] = { | |
| 2143 0x00, 0x00, 0x14, // Length: 20 | |
| 2144 0x01, // Type: HEADERS | |
| 2145 0x25, // Flags: END_STREAM|END_HEADERS|PRIORITY | |
| 2146 0x7f, 0xff, 0xff, 0xff, // Stream: 2147483647 | |
| 2147 0x00, 0x00, 0x00, 0x00, // Parent: 0 | |
| 2148 0xdb, // Weight: 220 | |
| 2149 | |
| 2150 0x00, // Unindexed Entry | |
| 2151 0x03, // Name Len: 3 | |
| 2152 0x62, 0x61, 0x72, // bar | |
| 2153 0x03, // Value Len: 3 | |
| 2154 0x66, 0x6f, 0x6f, // foo | |
| 2155 | |
| 2156 0x00, // Unindexed Entry | |
| 2157 0x03, // Name Len: 3 | |
| 2158 0x66, 0x6f, 0x6f, // foo | |
| 2159 0x00, // Value Len: 0 | |
| 2160 }; | |
| 2161 // frame-format on | |
| 2162 SpdyHeadersIR headers_ir(0x7fffffff); | |
| 2163 headers_ir.set_fin(true); | |
| 2164 headers_ir.set_has_priority(true); | |
| 2165 headers_ir.set_weight(220); | |
| 2166 headers_ir.SetHeader("bar", "foo"); | |
| 2167 headers_ir.SetHeader("foo", ""); | |
| 2168 SpdySerializedFrame frame(SpdyFramerPeer::SerializeHeaders( | |
| 2169 &framer, headers_ir, use_output_ ? &output_ : nullptr)); | |
| 2170 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 2171 } | |
| 2172 | |
| 2173 { | |
| 2174 const char kDescription[] = | |
| 2175 "HEADERS frame with a 0-length header val, FIN, max stream ID, pri, " | |
| 2176 "exclusive=true, parent_stream=0"; | |
| 2177 | |
| 2178 // frame-format off | |
| 2179 const unsigned char kV4FrameData[] = { | |
| 2180 0x00, 0x00, 0x14, // Length: 20 | |
| 2181 0x01, // Type: HEADERS | |
| 2182 0x25, // Flags: END_STREAM|END_HEADERS|PRIORITY | |
| 2183 0x7f, 0xff, 0xff, 0xff, // Stream: 2147483647 | |
| 2184 0x80, 0x00, 0x00, 0x00, // Parent: 0 (Exclusive) | |
| 2185 0xdb, // Weight: 220 | |
| 2186 | |
| 2187 0x00, // Unindexed Entry | |
| 2188 0x03, // Name Len: 3 | |
| 2189 0x62, 0x61, 0x72, // bar | |
| 2190 0x03, // Value Len: 3 | |
| 2191 0x66, 0x6f, 0x6f, // foo | |
| 2192 | |
| 2193 0x00, // Unindexed Entry | |
| 2194 0x03, // Name Len: 3 | |
| 2195 0x66, 0x6f, 0x6f, // foo | |
| 2196 0x00, // Value Len: 0 | |
| 2197 }; | |
| 2198 // frame-format on | |
| 2199 SpdyHeadersIR headers_ir(0x7fffffff); | |
| 2200 headers_ir.set_fin(true); | |
| 2201 headers_ir.set_has_priority(true); | |
| 2202 headers_ir.set_weight(220); | |
| 2203 headers_ir.set_exclusive(true); | |
| 2204 headers_ir.set_parent_stream_id(0); | |
| 2205 headers_ir.SetHeader("bar", "foo"); | |
| 2206 headers_ir.SetHeader("foo", ""); | |
| 2207 SpdySerializedFrame frame(SpdyFramerPeer::SerializeHeaders( | |
| 2208 &framer, headers_ir, use_output_ ? &output_ : nullptr)); | |
| 2209 CompareFrame(kDescription, frame, kV4FrameData, arraysize(kV4FrameData)); | |
| 2210 } | |
| 2211 | |
| 2212 { | |
| 2213 const char kDescription[] = | |
| 2214 "HEADERS frame with a 0-length header val, FIN, max stream ID, pri, " | |
| 2215 "exclusive=false, parent_stream=max stream ID"; | |
| 2216 | |
| 2217 // frame-format off | |
| 2218 const unsigned char kV4FrameData[] = { | |
| 2219 0x00, 0x00, 0x14, // Length: 20 | |
| 2220 0x01, // Type: HEADERS | |
| 2221 0x25, // Flags: END_STREAM|END_HEADERS|PRIORITY | |
| 2222 0x7f, 0xff, 0xff, 0xff, // Stream: 2147483647 | |
| 2223 0x7f, 0xff, 0xff, 0xff, // Parent: 2147483647 | |
| 2224 0xdb, // Weight: 220 | |
| 2225 | |
| 2226 0x00, // Unindexed Entry | |
| 2227 0x03, // Name Len: 3 | |
| 2228 0x62, 0x61, 0x72, // bar | |
| 2229 0x03, // Value Len: 3 | |
| 2230 0x66, 0x6f, 0x6f, // foo | |
| 2231 | |
| 2232 0x00, // Unindexed Entry | |
| 2233 0x03, // Name Len: 3 | |
| 2234 0x66, 0x6f, 0x6f, // foo | |
| 2235 0x00, // Value Len: 0 | |
| 2236 }; | |
| 2237 // frame-format on | |
| 2238 SpdyHeadersIR headers_ir(0x7fffffff); | |
| 2239 headers_ir.set_fin(true); | |
| 2240 headers_ir.set_has_priority(true); | |
| 2241 headers_ir.set_weight(220); | |
| 2242 headers_ir.set_exclusive(false); | |
| 2243 headers_ir.set_parent_stream_id(0x7fffffff); | |
| 2244 headers_ir.SetHeader("bar", "foo"); | |
| 2245 headers_ir.SetHeader("foo", ""); | |
| 2246 SpdySerializedFrame frame(SpdyFramerPeer::SerializeHeaders( | |
| 2247 &framer, headers_ir, use_output_ ? &output_ : nullptr)); | |
| 2248 CompareFrame(kDescription, frame, kV4FrameData, arraysize(kV4FrameData)); | |
| 2249 } | |
| 2250 | |
| 2251 { | |
| 2252 const char kDescription[] = | |
| 2253 "HEADERS frame with a 0-length header name, FIN, max stream ID, padded"; | |
| 2254 | |
| 2255 // frame-format off | |
| 2256 const unsigned char kH2FrameData[] = { | |
| 2257 0x00, 0x00, 0x15, // Length: 21 | |
| 2258 0x01, // Type: HEADERS | |
| 2259 0x0d, // Flags: END_STREAM|END_HEADERS|PADDED | |
| 2260 0x7f, 0xff, 0xff, 0xff, // Stream: 2147483647 | |
| 2261 0x05, // PadLen: 5 trailing bytes | |
| 2262 | |
| 2263 0x00, // Unindexed Entry | |
| 2264 0x00, // Name Len: 0 | |
| 2265 0x03, // Value Len: 3 | |
| 2266 0x66, 0x6f, 0x6f, // foo | |
| 2267 | |
| 2268 0x00, // Unindexed Entry | |
| 2269 0x03, // Name Len: 3 | |
| 2270 0x66, 0x6f, 0x6f, // foo | |
| 2271 0x03, // Value Len: 3 | |
| 2272 0x62, 0x61, 0x72, // bar | |
| 2273 | |
| 2274 0x00, 0x00, 0x00, 0x00, // Padding | |
| 2275 0x00, // Padding | |
| 2276 }; | |
| 2277 // frame-format on | |
| 2278 SpdyHeadersIR headers_ir(0x7fffffff); | |
| 2279 headers_ir.set_fin(true); | |
| 2280 headers_ir.SetHeader("", "foo"); | |
| 2281 headers_ir.SetHeader("foo", "bar"); | |
| 2282 headers_ir.set_padding_len(6); | |
| 2283 SpdySerializedFrame frame(SpdyFramerPeer::SerializeHeaders( | |
| 2284 &framer, headers_ir, use_output_ ? &output_ : nullptr)); | |
| 2285 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 2286 } | |
| 2287 } | |
| 2288 | |
| 2289 TEST_P(SpdyFramerTest, CreateWindowUpdate) { | |
| 2290 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 2291 | |
| 2292 { | |
| 2293 const char kDescription[] = "WINDOW_UPDATE frame"; | |
| 2294 const unsigned char kH2FrameData[] = { | |
| 2295 0x00, 0x00, 0x04, // Length: 4 | |
| 2296 0x08, // Type: WINDOW_UPDATE | |
| 2297 0x00, // Flags: none | |
| 2298 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 2299 0x00, 0x00, 0x00, 0x01, // Increment: 1 | |
| 2300 }; | |
| 2301 SpdySerializedFrame frame( | |
| 2302 framer.SerializeWindowUpdate(SpdyWindowUpdateIR(1, 1))); | |
| 2303 if (use_output_) { | |
| 2304 output_.Reset(); | |
| 2305 ASSERT_TRUE( | |
| 2306 framer.SerializeWindowUpdate(SpdyWindowUpdateIR(1, 1), &output_)); | |
| 2307 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 2308 } | |
| 2309 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 2310 } | |
| 2311 | |
| 2312 { | |
| 2313 const char kDescription[] = "WINDOW_UPDATE frame with max stream ID"; | |
| 2314 const unsigned char kH2FrameData[] = { | |
| 2315 0x00, 0x00, 0x04, // Length: 4 | |
| 2316 0x08, // Type: WINDOW_UPDATE | |
| 2317 0x00, // Flags: none | |
| 2318 0x7f, 0xff, 0xff, 0xff, // Stream: 0x7fffffff | |
| 2319 0x00, 0x00, 0x00, 0x01, // Increment: 1 | |
| 2320 }; | |
| 2321 SpdySerializedFrame frame( | |
| 2322 framer.SerializeWindowUpdate(SpdyWindowUpdateIR(0x7FFFFFFF, 1))); | |
| 2323 if (use_output_) { | |
| 2324 output_.Reset(); | |
| 2325 ASSERT_TRUE(framer.SerializeWindowUpdate( | |
| 2326 SpdyWindowUpdateIR(0x7FFFFFFF, 1), &output_)); | |
| 2327 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 2328 } | |
| 2329 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 2330 } | |
| 2331 | |
| 2332 { | |
| 2333 const char kDescription[] = "WINDOW_UPDATE frame with max window delta"; | |
| 2334 const unsigned char kH2FrameData[] = { | |
| 2335 0x00, 0x00, 0x04, // Length: 4 | |
| 2336 0x08, // Type: WINDOW_UPDATE | |
| 2337 0x00, // Flags: none | |
| 2338 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 2339 0x7f, 0xff, 0xff, 0xff, // Increment: 0x7fffffff | |
| 2340 }; | |
| 2341 SpdySerializedFrame frame( | |
| 2342 framer.SerializeWindowUpdate(SpdyWindowUpdateIR(1, 0x7FFFFFFF))); | |
| 2343 if (use_output_) { | |
| 2344 output_.Reset(); | |
| 2345 ASSERT_TRUE(framer.SerializeWindowUpdate( | |
| 2346 SpdyWindowUpdateIR(1, 0x7FFFFFFF), &output_)); | |
| 2347 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 2348 } | |
| 2349 CompareFrame(kDescription, frame, kH2FrameData, arraysize(kH2FrameData)); | |
| 2350 } | |
| 2351 } | |
| 2352 | |
| 2353 TEST_P(SpdyFramerTest, CreatePushPromiseUncompressed) { | |
| 2354 { | |
| 2355 // Test framing PUSH_PROMISE without padding. | |
| 2356 SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION); | |
| 2357 const char kDescription[] = "PUSH_PROMISE frame without padding"; | |
| 2358 | |
| 2359 // frame-format off | |
| 2360 const unsigned char kFrameData[] = { | |
| 2361 0x00, 0x00, 0x16, // Length: 22 | |
| 2362 0x05, // Type: PUSH_PROMISE | |
| 2363 0x04, // Flags: END_HEADERS | |
| 2364 0x00, 0x00, 0x00, 0x29, // Stream: 41 | |
| 2365 0x00, 0x00, 0x00, 0x3a, // Promise: 58 | |
| 2366 | |
| 2367 0x00, // Unindexed Entry | |
| 2368 0x03, // Name Len: 3 | |
| 2369 0x62, 0x61, 0x72, // bar | |
| 2370 0x03, // Value Len: 3 | |
| 2371 0x66, 0x6f, 0x6f, // foo | |
| 2372 | |
| 2373 0x00, // Unindexed Entry | |
| 2374 0x03, // Name Len: 3 | |
| 2375 0x66, 0x6f, 0x6f, // foo | |
| 2376 0x03, // Value Len: 3 | |
| 2377 0x62, 0x61, 0x72, // bar | |
| 2378 }; | |
| 2379 // frame-format on | |
| 2380 | |
| 2381 SpdyPushPromiseIR push_promise(41, 58); | |
| 2382 push_promise.SetHeader("bar", "foo"); | |
| 2383 push_promise.SetHeader("foo", "bar"); | |
| 2384 SpdySerializedFrame frame(SpdyFramerPeer::SerializePushPromise( | |
| 2385 &framer, push_promise, use_output_ ? &output_ : nullptr)); | |
| 2386 CompareFrame(kDescription, frame, kFrameData, arraysize(kFrameData)); | |
| 2387 } | |
| 2388 | |
| 2389 { | |
| 2390 // Test framing PUSH_PROMISE with one byte of padding. | |
| 2391 SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION); | |
| 2392 const char kDescription[] = "PUSH_PROMISE frame with one byte of padding"; | |
| 2393 | |
| 2394 // frame-format off | |
| 2395 const unsigned char kFrameData[] = { | |
| 2396 0x00, 0x00, 0x17, // Length: 23 | |
| 2397 0x05, // Type: PUSH_PROMISE | |
| 2398 0x0c, // Flags: END_HEADERS|PADDED | |
| 2399 0x00, 0x00, 0x00, 0x29, // Stream: 41 | |
| 2400 0x00, // PadLen: 0 trailing bytes | |
| 2401 0x00, 0x00, 0x00, 0x3a, // Promise: 58 | |
| 2402 | |
| 2403 0x00, // Unindexed Entry | |
| 2404 0x03, // Name Len: 3 | |
| 2405 0x62, 0x61, 0x72, // bar | |
| 2406 0x03, // Value Len: 3 | |
| 2407 0x66, 0x6f, 0x6f, // foo | |
| 2408 | |
| 2409 0x00, // Unindexed Entry | |
| 2410 0x03, // Name Len: 3 | |
| 2411 0x66, 0x6f, 0x6f, // foo | |
| 2412 0x03, // Value Len: 3 | |
| 2413 0x62, 0x61, 0x72, // bar | |
| 2414 }; | |
| 2415 // frame-format on | |
| 2416 | |
| 2417 SpdyPushPromiseIR push_promise(41, 58); | |
| 2418 push_promise.set_padding_len(1); | |
| 2419 push_promise.SetHeader("bar", "foo"); | |
| 2420 push_promise.SetHeader("foo", "bar"); | |
| 2421 output_.Reset(); | |
| 2422 SpdySerializedFrame frame(SpdyFramerPeer::SerializePushPromise( | |
| 2423 &framer, push_promise, use_output_ ? &output_ : nullptr)); | |
| 2424 | |
| 2425 CompareFrame(kDescription, frame, kFrameData, arraysize(kFrameData)); | |
| 2426 } | |
| 2427 | |
| 2428 { | |
| 2429 // Test framing PUSH_PROMISE with 177 bytes of padding. | |
| 2430 SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION); | |
| 2431 const char kDescription[] = "PUSH_PROMISE frame with 177 bytes of padding"; | |
| 2432 | |
| 2433 // frame-format off | |
| 2434 // clang-format off | |
| 2435 const unsigned char kFrameData[] = { | |
| 2436 0x00, 0x00, 0xc7, // Length: 199 | |
| 2437 0x05, // Type: PUSH_PROMISE | |
| 2438 0x0c, // Flags: END_HEADERS|PADDED | |
| 2439 0x00, 0x00, 0x00, 0x2a, // Stream: 42 | |
| 2440 0xb0, // PadLen: 176 trailing bytes | |
| 2441 0x00, 0x00, 0x00, 0x39, // Promise: 57 | |
| 2442 | |
| 2443 0x00, // Unindexed Entry | |
| 2444 0x03, // Name Len: 3 | |
| 2445 0x62, 0x61, 0x72, // bar | |
| 2446 0x03, // Value Len: 3 | |
| 2447 0x66, 0x6f, 0x6f, // foo | |
| 2448 | |
| 2449 0x00, // Unindexed Entry | |
| 2450 0x03, // Name Len: 3 | |
| 2451 0x66, 0x6f, 0x6f, // foo | |
| 2452 0x03, // Value Len: 3 | |
| 2453 0x62, 0x61, 0x72, // bar | |
| 2454 | |
| 2455 // Padding of 176 0x00(s). | |
| 2456 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 2457 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 2458 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 2459 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 2460 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 2461 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 2462 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 2463 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 2464 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 2465 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 2466 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 2467 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 2468 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 2469 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 2470 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 2471 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 2472 }; | |
| 2473 // clang-format on | |
| 2474 // frame-format on | |
| 2475 | |
| 2476 SpdyPushPromiseIR push_promise(42, 57); | |
| 2477 push_promise.set_padding_len(177); | |
| 2478 push_promise.SetHeader("bar", "foo"); | |
| 2479 push_promise.SetHeader("foo", "bar"); | |
| 2480 output_.Reset(); | |
| 2481 SpdySerializedFrame frame(SpdyFramerPeer::SerializePushPromise( | |
| 2482 &framer, push_promise, use_output_ ? &output_ : nullptr)); | |
| 2483 | |
| 2484 CompareFrame(kDescription, frame, kFrameData, arraysize(kFrameData)); | |
| 2485 } | |
| 2486 } | |
| 2487 | |
| 2488 // Regression test for https://crbug.com/464748. | |
| 2489 TEST_P(SpdyFramerTest, GetNumberRequiredContinuationFrames) { | |
| 2490 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 2491 EXPECT_EQ(1u, SpdyFramerPeer::GetNumberRequiredContinuationFrames( | |
| 2492 &framer, 16383 + 16374)); | |
| 2493 EXPECT_EQ(2u, SpdyFramerPeer::GetNumberRequiredContinuationFrames( | |
| 2494 &framer, 16383 + 16374 + 1)); | |
| 2495 EXPECT_EQ(2u, SpdyFramerPeer::GetNumberRequiredContinuationFrames( | |
| 2496 &framer, 16383 + 2 * 16374)); | |
| 2497 EXPECT_EQ(3u, SpdyFramerPeer::GetNumberRequiredContinuationFrames( | |
| 2498 &framer, 16383 + 2 * 16374 + 1)); | |
| 2499 } | |
| 2500 | |
| 2501 TEST_P(SpdyFramerTest, CreateContinuationUncompressed) { | |
| 2502 SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION); | |
| 2503 const char kDescription[] = "CONTINUATION frame"; | |
| 2504 | |
| 2505 // frame-format off | |
| 2506 const unsigned char kFrameData[] = { | |
| 2507 0x00, 0x00, 0x12, // Length: 18 | |
| 2508 0x09, // Type: CONTINUATION | |
| 2509 0x04, // Flags: END_HEADERS | |
| 2510 0x00, 0x00, 0x00, 0x2a, // Stream: 42 | |
| 2511 | |
| 2512 0x00, // Unindexed Entry | |
| 2513 0x03, // Name Len: 3 | |
| 2514 0x62, 0x61, 0x72, // bar | |
| 2515 0x03, // Value Len: 3 | |
| 2516 0x66, 0x6f, 0x6f, // foo | |
| 2517 | |
| 2518 0x00, // Unindexed Entry | |
| 2519 0x03, // Name Len: 3 | |
| 2520 0x66, 0x6f, 0x6f, // foo | |
| 2521 0x03, // Value Len: 3 | |
| 2522 0x62, 0x61, 0x72, // bar | |
| 2523 }; | |
| 2524 // frame-format on | |
| 2525 | |
| 2526 SpdyHeaderBlock header_block; | |
| 2527 header_block["bar"] = "foo"; | |
| 2528 header_block["foo"] = "bar"; | |
| 2529 auto buffer = base::MakeUnique<SpdyString>(); | |
| 2530 HpackEncoder encoder(ObtainHpackHuffmanTable()); | |
| 2531 encoder.DisableCompression(); | |
| 2532 encoder.EncodeHeaderSet(header_block, buffer.get()); | |
| 2533 | |
| 2534 SpdyContinuationIR continuation(42); | |
| 2535 continuation.take_encoding(std::move(buffer)); | |
| 2536 continuation.set_end_headers(true); | |
| 2537 | |
| 2538 SpdySerializedFrame frame(framer.SerializeContinuation(continuation)); | |
| 2539 if (use_output_) { | |
| 2540 ASSERT_TRUE(framer.SerializeContinuation(continuation, &output_)); | |
| 2541 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 2542 } | |
| 2543 CompareFrame(kDescription, frame, kFrameData, arraysize(kFrameData)); | |
| 2544 } | |
| 2545 | |
| 2546 // Test that if we send an unexpected CONTINUATION | |
| 2547 // we signal an error (but don't crash). | |
| 2548 TEST_P(SpdyFramerTest, SendUnexpectedContinuation) { | |
| 2549 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 2550 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 2551 framer.set_visitor(&visitor); | |
| 2552 | |
| 2553 // frame-format off | |
| 2554 char kH2FrameData[] = { | |
| 2555 0x00, 0x00, 0x12, // Length: 18 | |
| 2556 0x09, // Type: CONTINUATION | |
| 2557 0x04, // Flags: END_HEADERS | |
| 2558 0x00, 0x00, 0x00, 0x2a, // Stream: 42 | |
| 2559 | |
| 2560 0x00, // Unindexed Entry | |
| 2561 0x03, // Name Len: 3 | |
| 2562 0x62, 0x61, 0x72, // bar | |
| 2563 0x03, // Value Len: 3 | |
| 2564 0x66, 0x6f, 0x6f, // foo | |
| 2565 | |
| 2566 0x00, // Unindexed Entry | |
| 2567 0x03, // Name Len: 3 | |
| 2568 0x66, 0x6f, 0x6f, // foo | |
| 2569 0x03, // Value Len: 3 | |
| 2570 0x62, 0x61, 0x72, // bar | |
| 2571 }; | |
| 2572 // frame-format on | |
| 2573 | |
| 2574 SpdySerializedFrame frame(kH2FrameData, sizeof(kH2FrameData), false); | |
| 2575 | |
| 2576 // We shouldn't have to read the whole frame before we signal an error. | |
| 2577 EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); | |
| 2578 EXPECT_GT(frame.size(), framer.ProcessInput(frame.data(), frame.size())); | |
| 2579 EXPECT_TRUE(framer.HasError()); | |
| 2580 EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME, framer.spdy_framer_error()) | |
| 2581 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 2582 } | |
| 2583 | |
| 2584 TEST_P(SpdyFramerTest, CreatePushPromiseThenContinuationUncompressed) { | |
| 2585 { | |
| 2586 // Test framing in a case such that a PUSH_PROMISE frame, with one byte of | |
| 2587 // padding, cannot hold all the data payload, which is overflowed to the | |
| 2588 // consecutive CONTINUATION frame. | |
| 2589 SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION); | |
| 2590 const char kDescription[] = | |
| 2591 "PUSH_PROMISE and CONTINUATION frames with one byte of padding"; | |
| 2592 | |
| 2593 // frame-format off | |
| 2594 const unsigned char kPartialPushPromiseFrameData[] = { | |
| 2595 0x00, 0x3f, 0xf6, // Length: 16374 | |
| 2596 0x05, // Type: PUSH_PROMISE | |
| 2597 0x08, // Flags: PADDED | |
| 2598 0x00, 0x00, 0x00, 0x2a, // Stream: 42 | |
| 2599 0x00, // PadLen: 0 trailing bytes | |
| 2600 0x00, 0x00, 0x00, 0x39, // Promise: 57 | |
| 2601 | |
| 2602 0x00, // Unindexed Entry | |
| 2603 0x03, // Name Len: 3 | |
| 2604 0x78, 0x78, 0x78, // xxx | |
| 2605 0x7f, 0x80, 0x7f, // Value Len: 16361 | |
| 2606 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2607 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2608 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2609 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2610 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2611 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2612 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2613 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2614 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2615 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2616 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2617 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2618 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2619 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2620 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2621 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2622 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2623 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2624 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2625 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2626 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2627 }; | |
| 2628 const unsigned char kContinuationFrameData[] = { | |
| 2629 0x00, 0x00, 0x16, // Length: 22 | |
| 2630 0x09, // Type: CONTINUATION | |
| 2631 0x04, // Flags: END_HEADERS | |
| 2632 0x00, 0x00, 0x00, 0x2a, // Stream: 42 | |
| 2633 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2634 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2635 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2636 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2637 0x78, 0x78, 0x78, 0x78, // xxxx | |
| 2638 0x78, // x | |
| 2639 }; | |
| 2640 // frame-format on | |
| 2641 | |
| 2642 SpdyPushPromiseIR push_promise(42, 57); | |
| 2643 push_promise.set_padding_len(1); | |
| 2644 SpdyString big_value(TestSpdyVisitor::sent_control_frame_max_size(), 'x'); | |
| 2645 push_promise.SetHeader("xxx", big_value); | |
| 2646 SpdySerializedFrame frame(SpdyFramerPeer::SerializePushPromise( | |
| 2647 &framer, push_promise, use_output_ ? &output_ : nullptr)); | |
| 2648 | |
| 2649 // The entire frame should look like below: | |
| 2650 // Name Length in Byte | |
| 2651 // ------------------------------------------- Begin of PUSH_PROMISE frame | |
| 2652 // PUSH_PROMISE header 9 | |
| 2653 // Pad length field 1 | |
| 2654 // Promised stream 4 | |
| 2655 // Length field of key 2 | |
| 2656 // Content of key 3 | |
| 2657 // Length field of value 3 | |
| 2658 // Part of big_value 16361 | |
| 2659 // ------------------------------------------- Begin of CONTINUATION frame | |
| 2660 // CONTINUATION header 9 | |
| 2661 // Remaining of big_value 22 | |
| 2662 // ------------------------------------------- End | |
| 2663 | |
| 2664 // Length of everything listed above except big_value. | |
| 2665 int len_non_data_payload = 31; | |
| 2666 EXPECT_EQ( | |
| 2667 TestSpdyVisitor::sent_control_frame_max_size() + len_non_data_payload, | |
| 2668 frame.size()); | |
| 2669 | |
| 2670 // Partially compare the PUSH_PROMISE frame against the template. | |
| 2671 const unsigned char* frame_data = | |
| 2672 reinterpret_cast<const unsigned char*>(frame.data()); | |
| 2673 CompareCharArraysWithHexError( | |
| 2674 kDescription, frame_data, arraysize(kPartialPushPromiseFrameData), | |
| 2675 kPartialPushPromiseFrameData, arraysize(kPartialPushPromiseFrameData)); | |
| 2676 | |
| 2677 // Compare the CONTINUATION frame against the template. | |
| 2678 frame_data += TestSpdyVisitor::sent_control_frame_max_size(); | |
| 2679 CompareCharArraysWithHexError( | |
| 2680 kDescription, frame_data, arraysize(kContinuationFrameData), | |
| 2681 kContinuationFrameData, arraysize(kContinuationFrameData)); | |
| 2682 } | |
| 2683 } | |
| 2684 | |
| 2685 TEST_P(SpdyFramerTest, CreateAltSvc) { | |
| 2686 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 2687 | |
| 2688 const char kDescription[] = "ALTSVC frame"; | |
| 2689 const unsigned char kType = SerializeFrameType(SpdyFrameType::ALTSVC); | |
| 2690 const unsigned char kFrameData[] = { | |
| 2691 0x00, 0x00, 0x49, kType, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 'o', | |
| 2692 'r', 'i', 'g', 'i', 'n', 'p', 'i', 'd', '1', '=', '"', 'h', | |
| 2693 'o', 's', 't', ':', '4', '4', '3', '"', ';', ' ', 'm', 'a', | |
| 2694 '=', '5', ',', 'p', '%', '2', '2', '%', '3', 'D', 'i', '%', | |
| 2695 '3', 'A', 'd', '=', '"', 'h', '_', '\\', '\\', 'o', '\\', '"', | |
| 2696 's', 't', ':', '1', '2', '3', '"', ';', ' ', 'm', 'a', '=', | |
| 2697 '4', '2', ';', ' ', 'v', '=', '"', '2', '4', '"'}; | |
| 2698 SpdyAltSvcIR altsvc_ir(3); | |
| 2699 altsvc_ir.set_origin("origin"); | |
| 2700 altsvc_ir.add_altsvc(SpdyAltSvcWireFormat::AlternativeService( | |
| 2701 "pid1", "host", 443, 5, SpdyAltSvcWireFormat::VersionVector())); | |
| 2702 altsvc_ir.add_altsvc(SpdyAltSvcWireFormat::AlternativeService( | |
| 2703 "p\"=i:d", "h_\\o\"st", 123, 42, | |
| 2704 SpdyAltSvcWireFormat::VersionVector{24})); | |
| 2705 SpdySerializedFrame frame(framer.SerializeFrame(altsvc_ir)); | |
| 2706 if (use_output_) { | |
| 2707 ASSERT_TRUE(framer.SerializeFrame(altsvc_ir, &output_)); | |
| 2708 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 2709 } | |
| 2710 CompareFrame(kDescription, frame, kFrameData, arraysize(kFrameData)); | |
| 2711 } | |
| 2712 | |
| 2713 TEST_P(SpdyFramerTest, CreatePriority) { | |
| 2714 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 2715 | |
| 2716 const char kDescription[] = "PRIORITY frame"; | |
| 2717 const unsigned char kFrameData[] = { | |
| 2718 0x00, 0x00, 0x05, // Length: 5 | |
| 2719 0x02, // Type: PRIORITY | |
| 2720 0x00, // Flags: none | |
| 2721 0x00, 0x00, 0x00, 0x02, // Stream: 2 | |
| 2722 0x80, 0x00, 0x00, 0x01, // Parent: 1 (Exclusive) | |
| 2723 0x10, // Weight: 17 | |
| 2724 }; | |
| 2725 SpdyPriorityIR priority_ir(2, 1, 17, true); | |
| 2726 SpdySerializedFrame frame(framer.SerializeFrame(priority_ir)); | |
| 2727 if (use_output_) { | |
| 2728 ASSERT_TRUE(framer.SerializeFrame(priority_ir, &output_)); | |
| 2729 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 2730 } | |
| 2731 CompareFrame(kDescription, frame, kFrameData, arraysize(kFrameData)); | |
| 2732 SpdyPriorityIR priority2(2); | |
| 2733 priority2.set_parent_stream_id(1); | |
| 2734 priority2.set_weight(17); | |
| 2735 priority2.set_exclusive(true); | |
| 2736 if (use_output_) { | |
| 2737 output_.Reset(); | |
| 2738 ASSERT_TRUE(framer.SerializeFrame(priority2, &output_)); | |
| 2739 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 2740 } else { | |
| 2741 frame = framer.SerializeFrame(priority2); | |
| 2742 } | |
| 2743 CompareFrame(kDescription, frame, kFrameData, arraysize(kFrameData)); | |
| 2744 } | |
| 2745 | |
| 2746 TEST_P(SpdyFramerTest, ReadCompressedHeadersHeaderBlock) { | |
| 2747 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 2748 SpdyHeadersIR headers_ir(1); | |
| 2749 headers_ir.SetHeader("alpha", "beta"); | |
| 2750 headers_ir.SetHeader("gamma", "delta"); | |
| 2751 SpdySerializedFrame control_frame(SpdyFramerPeer::SerializeHeaders( | |
| 2752 &framer, headers_ir, use_output_ ? &output_ : nullptr)); | |
| 2753 TestSpdyVisitor visitor(SpdyFramer::ENABLE_COMPRESSION); | |
| 2754 visitor.SimulateInFramer( | |
| 2755 reinterpret_cast<unsigned char*>(control_frame.data()), | |
| 2756 control_frame.size()); | |
| 2757 EXPECT_EQ(1, visitor.headers_frame_count_); | |
| 2758 EXPECT_EQ(0, visitor.control_frame_header_data_count_); | |
| 2759 EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_); | |
| 2760 EXPECT_EQ(0, visitor.end_of_stream_count_); | |
| 2761 EXPECT_EQ(headers_ir.header_block(), visitor.headers_); | |
| 2762 } | |
| 2763 | |
| 2764 TEST_P(SpdyFramerTest, ReadCompressedHeadersHeaderBlockWithHalfClose) { | |
| 2765 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 2766 SpdyHeadersIR headers_ir(1); | |
| 2767 headers_ir.set_fin(true); | |
| 2768 headers_ir.SetHeader("alpha", "beta"); | |
| 2769 headers_ir.SetHeader("gamma", "delta"); | |
| 2770 SpdySerializedFrame control_frame(SpdyFramerPeer::SerializeHeaders( | |
| 2771 &framer, headers_ir, use_output_ ? &output_ : nullptr)); | |
| 2772 TestSpdyVisitor visitor(SpdyFramer::ENABLE_COMPRESSION); | |
| 2773 visitor.SimulateInFramer( | |
| 2774 reinterpret_cast<unsigned char*>(control_frame.data()), | |
| 2775 control_frame.size()); | |
| 2776 EXPECT_EQ(1, visitor.headers_frame_count_); | |
| 2777 EXPECT_EQ(0, visitor.control_frame_header_data_count_); | |
| 2778 EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_); | |
| 2779 EXPECT_EQ(1, visitor.end_of_stream_count_); | |
| 2780 EXPECT_EQ(headers_ir.header_block(), visitor.headers_); | |
| 2781 } | |
| 2782 | |
| 2783 TEST_P(SpdyFramerTest, TooLargeHeadersFrameUsesContinuation) { | |
| 2784 SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION); | |
| 2785 SpdyHeadersIR headers(1); | |
| 2786 headers.set_padding_len(256); | |
| 2787 | |
| 2788 // Exact payload length will change with HPACK, but this should be long | |
| 2789 // enough to cause an overflow. | |
| 2790 const size_t kBigValueSize = TestSpdyVisitor::sent_control_frame_max_size(); | |
| 2791 SpdyString big_value(kBigValueSize, 'x'); | |
| 2792 headers.SetHeader("aa", big_value); | |
| 2793 SpdySerializedFrame control_frame(SpdyFramerPeer::SerializeHeaders( | |
| 2794 &framer, headers, use_output_ ? &output_ : nullptr)); | |
| 2795 EXPECT_GT(control_frame.size(), | |
| 2796 TestSpdyVisitor::sent_control_frame_max_size()); | |
| 2797 | |
| 2798 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 2799 visitor.SimulateInFramer( | |
| 2800 reinterpret_cast<unsigned char*>(control_frame.data()), | |
| 2801 control_frame.size()); | |
| 2802 EXPECT_TRUE(visitor.header_buffer_valid_); | |
| 2803 EXPECT_EQ(0, visitor.error_count_); | |
| 2804 EXPECT_EQ(1, visitor.headers_frame_count_); | |
| 2805 EXPECT_EQ(1, visitor.continuation_count_); | |
| 2806 EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_); | |
| 2807 } | |
| 2808 | |
| 2809 TEST_P(SpdyFramerTest, MultipleContinuationFramesWithIterator) { | |
| 2810 SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION); | |
| 2811 auto headers = base::MakeUnique<SpdyHeadersIR>(1); | |
| 2812 headers->set_padding_len(256); | |
| 2813 | |
| 2814 // Exact payload length will change with HPACK, but this should be long | |
| 2815 // enough to cause an overflow. | |
| 2816 const size_t kBigValueSize = TestSpdyVisitor::sent_control_frame_max_size(); | |
| 2817 SpdyString big_valuex(kBigValueSize, 'x'); | |
| 2818 headers->SetHeader("aa", big_valuex); | |
| 2819 SpdyString big_valuez(kBigValueSize, 'z'); | |
| 2820 headers->SetHeader("bb", big_valuez); | |
| 2821 | |
| 2822 SpdyFramer::SpdyHeaderFrameIterator frame_it(&framer, std::move(headers)); | |
| 2823 | |
| 2824 EXPECT_TRUE(frame_it.HasNextFrame()); | |
| 2825 EXPECT_TRUE(frame_it.NextFrame(&output_)); | |
| 2826 SpdySerializedFrame headers_frame(output_.Begin(), output_.Size(), false); | |
| 2827 EXPECT_EQ(headers_frame.size(), | |
| 2828 TestSpdyVisitor::sent_control_frame_max_size()); | |
| 2829 | |
| 2830 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 2831 visitor.SimulateInFramer( | |
| 2832 reinterpret_cast<unsigned char*>(headers_frame.data()), | |
| 2833 headers_frame.size()); | |
| 2834 EXPECT_TRUE(visitor.header_buffer_valid_); | |
| 2835 EXPECT_EQ(0, visitor.error_count_); | |
| 2836 EXPECT_EQ(1, visitor.headers_frame_count_); | |
| 2837 EXPECT_EQ(0, visitor.continuation_count_); | |
| 2838 EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_); | |
| 2839 | |
| 2840 output_.Reset(); | |
| 2841 EXPECT_TRUE(frame_it.HasNextFrame()); | |
| 2842 EXPECT_TRUE(frame_it.NextFrame(&output_)); | |
| 2843 SpdySerializedFrame first_cont_frame(output_.Begin(), output_.Size(), false); | |
| 2844 EXPECT_EQ(first_cont_frame.size(), | |
| 2845 TestSpdyVisitor::sent_control_frame_max_size()); | |
| 2846 | |
| 2847 visitor.SimulateInFramer( | |
| 2848 reinterpret_cast<unsigned char*>(first_cont_frame.data()), | |
| 2849 first_cont_frame.size()); | |
| 2850 EXPECT_TRUE(visitor.header_buffer_valid_); | |
| 2851 EXPECT_EQ(0, visitor.error_count_); | |
| 2852 EXPECT_EQ(1, visitor.headers_frame_count_); | |
| 2853 EXPECT_EQ(1, visitor.continuation_count_); | |
| 2854 EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_); | |
| 2855 | |
| 2856 output_.Reset(); | |
| 2857 EXPECT_TRUE(frame_it.HasNextFrame()); | |
| 2858 EXPECT_TRUE(frame_it.NextFrame(&output_)); | |
| 2859 SpdySerializedFrame second_cont_frame(output_.Begin(), output_.Size(), false); | |
| 2860 EXPECT_LT(second_cont_frame.size(), | |
| 2861 TestSpdyVisitor::sent_control_frame_max_size()); | |
| 2862 | |
| 2863 visitor.SimulateInFramer( | |
| 2864 reinterpret_cast<unsigned char*>(second_cont_frame.data()), | |
| 2865 second_cont_frame.size()); | |
| 2866 EXPECT_TRUE(visitor.header_buffer_valid_); | |
| 2867 EXPECT_EQ(0, visitor.error_count_); | |
| 2868 EXPECT_EQ(1, visitor.headers_frame_count_); | |
| 2869 EXPECT_EQ(2, visitor.continuation_count_); | |
| 2870 EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_); | |
| 2871 | |
| 2872 EXPECT_FALSE(frame_it.HasNextFrame()); | |
| 2873 } | |
| 2874 | |
| 2875 TEST_P(SpdyFramerTest, PushPromiseFramesWithIterator) { | |
| 2876 SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION); | |
| 2877 auto push_promise = base::MakeUnique<SpdyPushPromiseIR>(1, 2); | |
| 2878 push_promise->set_padding_len(256); | |
| 2879 | |
| 2880 // Exact payload length will change with HPACK, but this should be long | |
| 2881 // enough to cause an overflow. | |
| 2882 const size_t kBigValueSize = TestSpdyVisitor::sent_control_frame_max_size(); | |
| 2883 SpdyString big_valuex(kBigValueSize, 'x'); | |
| 2884 push_promise->SetHeader("aa", big_valuex); | |
| 2885 SpdyString big_valuez(kBigValueSize, 'z'); | |
| 2886 push_promise->SetHeader("bb", big_valuez); | |
| 2887 | |
| 2888 SpdyFramer::SpdyPushPromiseFrameIterator frame_it(&framer, | |
| 2889 std::move(push_promise)); | |
| 2890 | |
| 2891 EXPECT_TRUE(frame_it.HasNextFrame()); | |
| 2892 EXPECT_TRUE(frame_it.NextFrame(&output_)); | |
| 2893 SpdySerializedFrame push_promise_frame(output_.Begin(), output_.Size(), | |
| 2894 false); | |
| 2895 EXPECT_EQ(push_promise_frame.size(), | |
| 2896 TestSpdyVisitor::sent_control_frame_max_size()); | |
| 2897 | |
| 2898 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 2899 visitor.SimulateInFramer( | |
| 2900 reinterpret_cast<unsigned char*>(push_promise_frame.data()), | |
| 2901 push_promise_frame.size()); | |
| 2902 EXPECT_TRUE(visitor.header_buffer_valid_); | |
| 2903 EXPECT_EQ(0, visitor.error_count_); | |
| 2904 EXPECT_EQ(1, visitor.push_promise_frame_count_); | |
| 2905 EXPECT_EQ(0, visitor.continuation_count_); | |
| 2906 EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_); | |
| 2907 | |
| 2908 EXPECT_TRUE(frame_it.HasNextFrame()); | |
| 2909 output_.Reset(); | |
| 2910 EXPECT_TRUE(frame_it.NextFrame(&output_)); | |
| 2911 SpdySerializedFrame first_cont_frame(output_.Begin(), output_.Size(), false); | |
| 2912 | |
| 2913 EXPECT_EQ(first_cont_frame.size(), | |
| 2914 TestSpdyVisitor::sent_control_frame_max_size()); | |
| 2915 visitor.SimulateInFramer( | |
| 2916 reinterpret_cast<unsigned char*>(first_cont_frame.data()), | |
| 2917 first_cont_frame.size()); | |
| 2918 EXPECT_TRUE(visitor.header_buffer_valid_); | |
| 2919 EXPECT_EQ(0, visitor.error_count_); | |
| 2920 EXPECT_EQ(1, visitor.push_promise_frame_count_); | |
| 2921 EXPECT_EQ(1, visitor.continuation_count_); | |
| 2922 EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_); | |
| 2923 | |
| 2924 EXPECT_TRUE(frame_it.HasNextFrame()); | |
| 2925 output_.Reset(); | |
| 2926 EXPECT_TRUE(frame_it.NextFrame(&output_)); | |
| 2927 SpdySerializedFrame second_cont_frame(output_.Begin(), output_.Size(), false); | |
| 2928 EXPECT_LT(second_cont_frame.size(), | |
| 2929 TestSpdyVisitor::sent_control_frame_max_size()); | |
| 2930 | |
| 2931 visitor.SimulateInFramer( | |
| 2932 reinterpret_cast<unsigned char*>(second_cont_frame.data()), | |
| 2933 second_cont_frame.size()); | |
| 2934 EXPECT_TRUE(visitor.header_buffer_valid_); | |
| 2935 EXPECT_EQ(0, visitor.error_count_); | |
| 2936 EXPECT_EQ(1, visitor.push_promise_frame_count_); | |
| 2937 EXPECT_EQ(2, visitor.continuation_count_); | |
| 2938 EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_); | |
| 2939 | |
| 2940 EXPECT_FALSE(frame_it.HasNextFrame()); | |
| 2941 } | |
| 2942 | |
| 2943 TEST_P(SpdyFramerTest, TooLargePushPromiseFrameUsesContinuation) { | |
| 2944 SpdyFramer framer(SpdyFramer::DISABLE_COMPRESSION); | |
| 2945 SpdyPushPromiseIR push_promise(1, 2); | |
| 2946 push_promise.set_padding_len(256); | |
| 2947 | |
| 2948 // Exact payload length will change with HPACK, but this should be long | |
| 2949 // enough to cause an overflow. | |
| 2950 const size_t kBigValueSize = TestSpdyVisitor::sent_control_frame_max_size(); | |
| 2951 SpdyString big_value(kBigValueSize, 'x'); | |
| 2952 push_promise.SetHeader("aa", big_value); | |
| 2953 SpdySerializedFrame control_frame(SpdyFramerPeer::SerializePushPromise( | |
| 2954 &framer, push_promise, use_output_ ? &output_ : nullptr)); | |
| 2955 EXPECT_GT(control_frame.size(), | |
| 2956 TestSpdyVisitor::sent_control_frame_max_size()); | |
| 2957 | |
| 2958 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 2959 visitor.SimulateInFramer( | |
| 2960 reinterpret_cast<unsigned char*>(control_frame.data()), | |
| 2961 control_frame.size()); | |
| 2962 EXPECT_TRUE(visitor.header_buffer_valid_); | |
| 2963 EXPECT_EQ(0, visitor.error_count_); | |
| 2964 EXPECT_EQ(1, visitor.push_promise_frame_count_); | |
| 2965 EXPECT_EQ(1, visitor.continuation_count_); | |
| 2966 EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_); | |
| 2967 } | |
| 2968 | |
| 2969 // Check that the framer stops delivering header data chunks once the visitor | |
| 2970 // declares it doesn't want any more. This is important to guard against | |
| 2971 // "zip bomb" types of attacks. | |
| 2972 TEST_P(SpdyFramerTest, ControlFrameMuchTooLarge) { | |
| 2973 const size_t kHeaderBufferChunks = 4; | |
| 2974 const size_t kHeaderBufferSize = | |
| 2975 TestSpdyVisitor::header_data_chunk_max_size() * kHeaderBufferChunks; | |
| 2976 const size_t kBigValueSize = kHeaderBufferSize * 2; | |
| 2977 SpdyString big_value(kBigValueSize, 'x'); | |
| 2978 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 2979 SpdyHeadersIR headers(1); | |
| 2980 headers.set_fin(true); | |
| 2981 headers.SetHeader("aa", big_value); | |
| 2982 SpdySerializedFrame control_frame(SpdyFramerPeer::SerializeHeaders( | |
| 2983 &framer, headers, use_output_ ? &output_ : nullptr)); | |
| 2984 TestSpdyVisitor visitor(SpdyFramer::ENABLE_COMPRESSION); | |
| 2985 visitor.set_header_buffer_size(kHeaderBufferSize); | |
| 2986 visitor.SimulateInFramer( | |
| 2987 reinterpret_cast<unsigned char*>(control_frame.data()), | |
| 2988 control_frame.size()); | |
| 2989 // It's up to the visitor to ignore extraneous header data; the framer | |
| 2990 // won't throw an error. | |
| 2991 EXPECT_GT(visitor.header_bytes_received_, visitor.header_buffer_.size()); | |
| 2992 EXPECT_EQ(1, visitor.end_of_stream_count_); | |
| 2993 } | |
| 2994 | |
| 2995 TEST_P(SpdyFramerTest, ControlFrameSizesAreValidated) { | |
| 2996 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 2997 // Create a GoAway frame that has a few extra bytes at the end. | |
| 2998 // We create enough overhead to overflow the framer's control frame buffer. | |
| 2999 ASSERT_LE(SpdyFramerPeer::ControlFrameBufferSize(), 250u); | |
| 3000 const size_t length = SpdyFramerPeer::ControlFrameBufferSize() + 1; | |
| 3001 | |
| 3002 // HTTP/2 GOAWAY frames are only bound by a minimal length, since they may | |
| 3003 // carry opaque data. Verify that minimal length is tested. | |
| 3004 ASSERT_GT(framer.GetGoAwayMinimumSize(), kFrameHeaderSize); | |
| 3005 const size_t less_than_min_length = | |
| 3006 framer.GetGoAwayMinimumSize() - kFrameHeaderSize - 1; | |
| 3007 ASSERT_LE(less_than_min_length, std::numeric_limits<unsigned char>::max()); | |
| 3008 const unsigned char kH2Len = static_cast<unsigned char>(less_than_min_length); | |
| 3009 const unsigned char kH2FrameData[] = { | |
| 3010 0x00, 0x00, kH2Len, // Length: min length - 1 | |
| 3011 0x07, // Type: GOAWAY | |
| 3012 0x00, // Flags: none | |
| 3013 0x00, 0x00, 0x00, 0x00, // Stream: 0 | |
| 3014 0x00, 0x00, 0x00, 0x00, // Last: 0 | |
| 3015 0x00, 0x00, 0x00, // Truncated Status Field | |
| 3016 }; | |
| 3017 const size_t pad_length = length + kFrameHeaderSize - sizeof(kH2FrameData); | |
| 3018 SpdyString pad(pad_length, 'A'); | |
| 3019 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3020 | |
| 3021 visitor.SimulateInFramer(kH2FrameData, sizeof(kH2FrameData)); | |
| 3022 visitor.SimulateInFramer(reinterpret_cast<const unsigned char*>(pad.c_str()), | |
| 3023 pad.length()); | |
| 3024 | |
| 3025 EXPECT_EQ(1, visitor.error_count_); // This generated an error. | |
| 3026 EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, | |
| 3027 visitor.framer_.spdy_framer_error()) | |
| 3028 << SpdyFramer::SpdyFramerErrorToString( | |
| 3029 visitor.framer_.spdy_framer_error()); | |
| 3030 EXPECT_EQ(0, visitor.goaway_count_); // Frame not parsed. | |
| 3031 } | |
| 3032 | |
| 3033 TEST_P(SpdyFramerTest, ReadZeroLenSettingsFrame) { | |
| 3034 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3035 SpdySettingsIR settings_ir; | |
| 3036 SpdySerializedFrame control_frame(framer.SerializeSettings(settings_ir)); | |
| 3037 if (use_output_) { | |
| 3038 ASSERT_TRUE(framer.SerializeSettings(settings_ir, &output_)); | |
| 3039 control_frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 3040 } | |
| 3041 SetFrameLength(&control_frame, 0); | |
| 3042 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3043 visitor.SimulateInFramer( | |
| 3044 reinterpret_cast<unsigned char*>(control_frame.data()), | |
| 3045 framer.GetFrameHeaderSize()); | |
| 3046 // Zero-len settings frames are permitted as of HTTP/2. | |
| 3047 EXPECT_EQ(0, visitor.error_count_); | |
| 3048 } | |
| 3049 | |
| 3050 // Tests handling of SETTINGS frames with invalid length. | |
| 3051 TEST_P(SpdyFramerTest, ReadBogusLenSettingsFrame) { | |
| 3052 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3053 SpdySettingsIR settings_ir; | |
| 3054 | |
| 3055 // Add settings to more than fill the frame so that we don't get a buffer | |
| 3056 // overflow when calling SimulateInFramer() below. These settings must be | |
| 3057 // distinct parameters because SpdySettingsIR has a map for settings, and will | |
| 3058 // collapse multiple copies of the same parameter. | |
| 3059 settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 0x00000002); | |
| 3060 settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, 0x00000002); | |
| 3061 SpdySerializedFrame control_frame(framer.SerializeSettings(settings_ir)); | |
| 3062 if (use_output_) { | |
| 3063 ASSERT_TRUE(framer.SerializeSettings(settings_ir, &output_)); | |
| 3064 control_frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 3065 } | |
| 3066 const size_t kNewLength = 8; | |
| 3067 SetFrameLength(&control_frame, kNewLength); | |
| 3068 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3069 visitor.SimulateInFramer( | |
| 3070 reinterpret_cast<unsigned char*>(control_frame.data()), | |
| 3071 framer.GetFrameHeaderSize() + kNewLength); | |
| 3072 // Should generate an error, since its not possible to have a | |
| 3073 // settings frame of length kNewLength. | |
| 3074 EXPECT_EQ(1, visitor.error_count_); | |
| 3075 EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_SIZE, | |
| 3076 visitor.framer_.spdy_framer_error()) | |
| 3077 << SpdyFramer::SpdyFramerErrorToString( | |
| 3078 visitor.framer_.spdy_framer_error()); | |
| 3079 } | |
| 3080 | |
| 3081 // Tests handling of SETTINGS frames larger than the frame buffer size. | |
| 3082 TEST_P(SpdyFramerTest, ReadLargeSettingsFrame) { | |
| 3083 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3084 SpdySettingsIR settings_ir; | |
| 3085 settings_ir.AddSetting(SETTINGS_HEADER_TABLE_SIZE, 5); | |
| 3086 settings_ir.AddSetting(SETTINGS_ENABLE_PUSH, 6); | |
| 3087 settings_ir.AddSetting(SETTINGS_MAX_CONCURRENT_STREAMS, 7); | |
| 3088 | |
| 3089 SpdySerializedFrame control_frame(framer.SerializeSettings(settings_ir)); | |
| 3090 if (use_output_) { | |
| 3091 ASSERT_TRUE(framer.SerializeSettings(settings_ir, &output_)); | |
| 3092 control_frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 3093 } | |
| 3094 | |
| 3095 EXPECT_LT(SpdyFramerPeer::ControlFrameBufferSize(), control_frame.size()); | |
| 3096 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3097 | |
| 3098 // Read all at once. | |
| 3099 visitor.SimulateInFramer( | |
| 3100 reinterpret_cast<unsigned char*>(control_frame.data()), | |
| 3101 control_frame.size()); | |
| 3102 EXPECT_EQ(0, visitor.error_count_); | |
| 3103 EXPECT_EQ(3, visitor.setting_count_); | |
| 3104 EXPECT_EQ(1, visitor.settings_ack_sent_); | |
| 3105 | |
| 3106 // Read data in small chunks. | |
| 3107 size_t framed_data = 0; | |
| 3108 size_t unframed_data = control_frame.size(); | |
| 3109 size_t kReadChunkSize = 5; // Read five bytes at a time. | |
| 3110 while (unframed_data > 0) { | |
| 3111 size_t to_read = std::min(kReadChunkSize, unframed_data); | |
| 3112 visitor.SimulateInFramer( | |
| 3113 reinterpret_cast<unsigned char*>(control_frame.data() + framed_data), | |
| 3114 to_read); | |
| 3115 unframed_data -= to_read; | |
| 3116 framed_data += to_read; | |
| 3117 } | |
| 3118 EXPECT_EQ(0, visitor.error_count_); | |
| 3119 EXPECT_EQ(3 * 2, visitor.setting_count_); | |
| 3120 EXPECT_EQ(2, visitor.settings_ack_sent_); | |
| 3121 } | |
| 3122 | |
| 3123 // Tests handling of SETTINGS frame with duplicate entries. | |
| 3124 TEST_P(SpdyFramerTest, ReadDuplicateSettings) { | |
| 3125 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3126 | |
| 3127 const unsigned char kH2FrameData[] = { | |
| 3128 0x00, 0x00, 0x12, // Length: 18 | |
| 3129 0x04, // Type: SETTINGS | |
| 3130 0x00, // Flags: none | |
| 3131 0x00, 0x00, 0x00, 0x00, // Stream: 0 | |
| 3132 0x00, 0x01, // Param: HEADER_TABLE_SIZE | |
| 3133 0x00, 0x00, 0x00, 0x02, // Value: 2 | |
| 3134 0x00, 0x01, // Param: HEADER_TABLE_SIZE | |
| 3135 0x00, 0x00, 0x00, 0x03, // Value: 3 | |
| 3136 0x00, 0x03, // Param: MAX_CONCURRENT_STREAMS | |
| 3137 0x00, 0x00, 0x00, 0x03, // Value: 3 | |
| 3138 }; | |
| 3139 | |
| 3140 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3141 visitor.SimulateInFramer(kH2FrameData, sizeof(kH2FrameData)); | |
| 3142 | |
| 3143 // In HTTP/2, duplicate settings are allowed; | |
| 3144 // each setting replaces the previous value for that setting. | |
| 3145 EXPECT_EQ(3, visitor.setting_count_); | |
| 3146 EXPECT_EQ(0, visitor.error_count_); | |
| 3147 EXPECT_EQ(1, visitor.settings_ack_sent_); | |
| 3148 } | |
| 3149 | |
| 3150 // Tests handling of SETTINGS frame with a setting we don't recognize. | |
| 3151 TEST_P(SpdyFramerTest, ReadUnknownSettingsId) { | |
| 3152 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3153 const unsigned char kH2FrameData[] = { | |
| 3154 0x00, 0x00, 0x06, // Length: 6 | |
| 3155 0x04, // Type: SETTINGS | |
| 3156 0x00, // Flags: none | |
| 3157 0x00, 0x00, 0x00, 0x00, // Stream: 0 | |
| 3158 0x00, 0x10, // Param: 16 | |
| 3159 0x00, 0x00, 0x00, 0x02, // Value: 2 | |
| 3160 }; | |
| 3161 | |
| 3162 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3163 visitor.SimulateInFramer(kH2FrameData, sizeof(kH2FrameData)); | |
| 3164 | |
| 3165 // In HTTP/2, we ignore unknown settings because of extensions. | |
| 3166 EXPECT_EQ(0, visitor.setting_count_); | |
| 3167 EXPECT_EQ(0, visitor.error_count_); | |
| 3168 } | |
| 3169 | |
| 3170 TEST_P(SpdyFramerTest, ReadUnknownSettingsWithExtension) { | |
| 3171 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3172 const unsigned char kH2FrameData[] = { | |
| 3173 0x00, 0x00, 0x0c, // Length: 12 | |
| 3174 0x04, // Type: SETTINGS | |
| 3175 0x00, // Flags: none | |
| 3176 0x00, 0x00, 0x00, 0x00, // Stream: 0 | |
| 3177 0x00, 0x10, // Param: 16 | |
| 3178 0x00, 0x00, 0x00, 0x02, // Value: 2 | |
| 3179 0x00, 0x5f, // Param: 95 | |
| 3180 0x00, 0x01, 0x00, 0x02, // Value: 65538 | |
| 3181 }; | |
| 3182 | |
| 3183 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3184 TestExtension extension; | |
| 3185 visitor.set_extension_visitor(&extension); | |
| 3186 visitor.SimulateInFramer(kH2FrameData, sizeof(kH2FrameData)); | |
| 3187 | |
| 3188 // In HTTP/2, we ignore unknown settings because of extensions. | |
| 3189 EXPECT_EQ(0, visitor.setting_count_); | |
| 3190 EXPECT_EQ(0, visitor.error_count_); | |
| 3191 | |
| 3192 EXPECT_THAT( | |
| 3193 extension.settings_received_, | |
| 3194 testing::ElementsAre(testing::Pair(16, 2), testing::Pair(95, 65538))); | |
| 3195 } | |
| 3196 | |
| 3197 // Tests handling of SETTINGS frame with entries out of order. | |
| 3198 TEST_P(SpdyFramerTest, ReadOutOfOrderSettings) { | |
| 3199 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3200 const unsigned char kH2FrameData[] = { | |
| 3201 0x00, 0x00, 0x12, // Length: 18 | |
| 3202 0x04, // Type: SETTINGS | |
| 3203 0x00, // Flags: none | |
| 3204 0x00, 0x00, 0x00, 0x00, // Stream: 0 | |
| 3205 0x00, 0x02, // Param: ENABLE_PUSH | |
| 3206 0x00, 0x00, 0x00, 0x02, // Value: 2 | |
| 3207 0x00, 0x01, // Param: HEADER_TABLE_SIZE | |
| 3208 0x00, 0x00, 0x00, 0x03, // Value: 3 | |
| 3209 0x00, 0x03, // Param: MAX_CONCURRENT_STREAMS | |
| 3210 0x00, 0x00, 0x00, 0x03, // Value: 3 | |
| 3211 }; | |
| 3212 | |
| 3213 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3214 visitor.SimulateInFramer(kH2FrameData, sizeof(kH2FrameData)); | |
| 3215 | |
| 3216 // In HTTP/2, settings are allowed in any order. | |
| 3217 EXPECT_EQ(3, visitor.setting_count_); | |
| 3218 EXPECT_EQ(0, visitor.error_count_); | |
| 3219 } | |
| 3220 | |
| 3221 TEST_P(SpdyFramerTest, ProcessSettingsAckFrame) { | |
| 3222 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3223 | |
| 3224 const unsigned char kFrameData[] = { | |
| 3225 0x00, 0x00, 0x00, // Length: 0 | |
| 3226 0x04, // Type: SETTINGS | |
| 3227 0x01, // Flags: ACK | |
| 3228 0x00, 0x00, 0x00, 0x00, // Stream: 0 | |
| 3229 }; | |
| 3230 | |
| 3231 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3232 visitor.SimulateInFramer(kFrameData, sizeof(kFrameData)); | |
| 3233 | |
| 3234 EXPECT_EQ(0, visitor.error_count_); | |
| 3235 EXPECT_EQ(0, visitor.setting_count_); | |
| 3236 EXPECT_EQ(1, visitor.settings_ack_received_); | |
| 3237 } | |
| 3238 | |
| 3239 TEST_P(SpdyFramerTest, ProcessDataFrameWithPadding) { | |
| 3240 const int kPaddingLen = 119; | |
| 3241 const char data_payload[] = "hello"; | |
| 3242 | |
| 3243 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 3244 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3245 framer.set_visitor(&visitor); | |
| 3246 | |
| 3247 SpdyDataIR data_ir(1, data_payload); | |
| 3248 data_ir.set_padding_len(kPaddingLen); | |
| 3249 SpdySerializedFrame frame(framer.SerializeData(data_ir)); | |
| 3250 | |
| 3251 int bytes_consumed = 0; | |
| 3252 | |
| 3253 // Send the frame header. | |
| 3254 EXPECT_CALL(visitor, | |
| 3255 OnDataFrameHeader(1, kPaddingLen + strlen(data_payload), false)); | |
| 3256 CHECK_EQ(framer.GetDataFrameMinimumSize(), | |
| 3257 framer.ProcessInput(frame.data(), framer.GetDataFrameMinimumSize())); | |
| 3258 CHECK_EQ(framer.state(), SpdyFramer::SPDY_READ_DATA_FRAME_PADDING_LENGTH); | |
| 3259 CHECK_EQ(framer.spdy_framer_error(), SpdyFramer::SPDY_NO_ERROR); | |
| 3260 bytes_consumed += framer.GetDataFrameMinimumSize(); | |
| 3261 | |
| 3262 // Send the padding length field. | |
| 3263 EXPECT_CALL(visitor, OnStreamPadding(1, 1)); | |
| 3264 CHECK_EQ(1u, framer.ProcessInput(frame.data() + bytes_consumed, 1)); | |
| 3265 CHECK_EQ(framer.state(), SpdyFramer::SPDY_FORWARD_STREAM_FRAME); | |
| 3266 CHECK_EQ(framer.spdy_framer_error(), SpdyFramer::SPDY_NO_ERROR); | |
| 3267 bytes_consumed += 1; | |
| 3268 | |
| 3269 // Send the first two bytes of the data payload, i.e., "he". | |
| 3270 EXPECT_CALL(visitor, OnStreamFrameData(1, _, 2)); | |
| 3271 CHECK_EQ(2u, framer.ProcessInput(frame.data() + bytes_consumed, 2)); | |
| 3272 CHECK_EQ(framer.state(), SpdyFramer::SPDY_FORWARD_STREAM_FRAME); | |
| 3273 CHECK_EQ(framer.spdy_framer_error(), SpdyFramer::SPDY_NO_ERROR); | |
| 3274 bytes_consumed += 2; | |
| 3275 | |
| 3276 // Send the rest three bytes of the data payload, i.e., "llo". | |
| 3277 EXPECT_CALL(visitor, OnStreamFrameData(1, _, 3)); | |
| 3278 CHECK_EQ(3u, framer.ProcessInput(frame.data() + bytes_consumed, 3)); | |
| 3279 CHECK_EQ(framer.state(), SpdyFramer::SPDY_CONSUME_PADDING); | |
| 3280 CHECK_EQ(framer.spdy_framer_error(), SpdyFramer::SPDY_NO_ERROR); | |
| 3281 bytes_consumed += 3; | |
| 3282 | |
| 3283 // Send the first 100 bytes of the padding payload. | |
| 3284 EXPECT_CALL(visitor, OnStreamPadding(1, 100)); | |
| 3285 CHECK_EQ(100u, framer.ProcessInput(frame.data() + bytes_consumed, 100)); | |
| 3286 CHECK_EQ(framer.state(), SpdyFramer::SPDY_CONSUME_PADDING); | |
| 3287 CHECK_EQ(framer.spdy_framer_error(), SpdyFramer::SPDY_NO_ERROR); | |
| 3288 bytes_consumed += 100; | |
| 3289 | |
| 3290 // Send rest of the padding payload. | |
| 3291 EXPECT_CALL(visitor, OnStreamPadding(1, 18)); | |
| 3292 CHECK_EQ(18u, framer.ProcessInput(frame.data() + bytes_consumed, 18)); | |
| 3293 CHECK_EQ(framer.state(), SpdyFramer::SPDY_READY_FOR_FRAME); | |
| 3294 CHECK_EQ(framer.spdy_framer_error(), SpdyFramer::SPDY_NO_ERROR); | |
| 3295 } | |
| 3296 | |
| 3297 TEST_P(SpdyFramerTest, ReadWindowUpdate) { | |
| 3298 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3299 SpdySerializedFrame control_frame( | |
| 3300 framer.SerializeWindowUpdate(SpdyWindowUpdateIR(1, 2))); | |
| 3301 if (use_output_) { | |
| 3302 ASSERT_TRUE( | |
| 3303 framer.SerializeWindowUpdate(SpdyWindowUpdateIR(1, 2), &output_)); | |
| 3304 control_frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 3305 } | |
| 3306 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3307 visitor.SimulateInFramer( | |
| 3308 reinterpret_cast<unsigned char*>(control_frame.data()), | |
| 3309 control_frame.size()); | |
| 3310 EXPECT_EQ(1u, visitor.last_window_update_stream_); | |
| 3311 EXPECT_EQ(2, visitor.last_window_update_delta_); | |
| 3312 } | |
| 3313 | |
| 3314 TEST_P(SpdyFramerTest, ReadCompressedPushPromise) { | |
| 3315 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3316 SpdyPushPromiseIR push_promise(42, 57); | |
| 3317 push_promise.SetHeader("foo", "bar"); | |
| 3318 push_promise.SetHeader("bar", "foofoo"); | |
| 3319 SpdySerializedFrame frame(SpdyFramerPeer::SerializePushPromise( | |
| 3320 &framer, push_promise, use_output_ ? &output_ : nullptr)); | |
| 3321 TestSpdyVisitor visitor(SpdyFramer::ENABLE_COMPRESSION); | |
| 3322 visitor.SimulateInFramer(reinterpret_cast<unsigned char*>(frame.data()), | |
| 3323 frame.size()); | |
| 3324 EXPECT_EQ(42u, visitor.last_push_promise_stream_); | |
| 3325 EXPECT_EQ(57u, visitor.last_push_promise_promised_stream_); | |
| 3326 EXPECT_EQ(push_promise.header_block(), visitor.headers_); | |
| 3327 } | |
| 3328 | |
| 3329 TEST_P(SpdyFramerTest, ReadHeadersWithContinuation) { | |
| 3330 // frame-format off | |
| 3331 const unsigned char kInput[] = { | |
| 3332 0x00, 0x00, 0x14, // Length: 20 | |
| 3333 0x01, // Type: HEADERS | |
| 3334 0x08, // Flags: PADDED | |
| 3335 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 3336 0x03, // PadLen: 3 trailing bytes | |
| 3337 0x00, // Unindexed Entry | |
| 3338 0x06, // Name Len: 6 | |
| 3339 'c', 'o', 'o', 'k', 'i', 'e', // Name | |
| 3340 0x07, // Value Len: 7 | |
| 3341 'f', 'o', 'o', '=', 'b', 'a', 'r', // Value | |
| 3342 0x00, 0x00, 0x00, // Padding | |
| 3343 | |
| 3344 0x00, 0x00, 0x14, // Length: 20 | |
| 3345 0x09, // Type: CONTINUATION | |
| 3346 0x00, // Flags: none | |
| 3347 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 3348 0x00, // Unindexed Entry | |
| 3349 0x06, // Name Len: 6 | |
| 3350 'c', 'o', 'o', 'k', 'i', 'e', // Name | |
| 3351 0x08, // Value Len: 7 | |
| 3352 'b', 'a', 'z', '=', 'b', 'i', 'n', 'g', // Value | |
| 3353 0x00, // Unindexed Entry | |
| 3354 0x06, // Name Len: 6 | |
| 3355 'c', // Name (split) | |
| 3356 | |
| 3357 0x00, 0x00, 0x12, // Length: 18 | |
| 3358 0x09, // Type: CONTINUATION | |
| 3359 0x04, // Flags: END_HEADERS | |
| 3360 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 3361 'o', 'o', 'k', 'i', 'e', // Name (continued) | |
| 3362 0x00, // Value Len: 0 | |
| 3363 0x00, // Unindexed Entry | |
| 3364 0x04, // Name Len: 4 | |
| 3365 'n', 'a', 'm', 'e', // Name | |
| 3366 0x05, // Value Len: 5 | |
| 3367 'v', 'a', 'l', 'u', 'e', // Value | |
| 3368 }; | |
| 3369 // frame-format on | |
| 3370 | |
| 3371 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3372 visitor.SimulateInFramer(kInput, sizeof(kInput)); | |
| 3373 | |
| 3374 EXPECT_EQ(0, visitor.error_count_); | |
| 3375 EXPECT_EQ(1, visitor.headers_frame_count_); | |
| 3376 EXPECT_EQ(2, visitor.continuation_count_); | |
| 3377 EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_); | |
| 3378 EXPECT_EQ(0, visitor.end_of_stream_count_); | |
| 3379 | |
| 3380 EXPECT_THAT( | |
| 3381 visitor.headers_, | |
| 3382 testing::ElementsAre(testing::Pair("cookie", "foo=bar; baz=bing; "), | |
| 3383 testing::Pair("name", "value"))); | |
| 3384 } | |
| 3385 | |
| 3386 TEST_P(SpdyFramerTest, ReadHeadersWithContinuationAndFin) { | |
| 3387 // frame-format off | |
| 3388 const unsigned char kInput[] = { | |
| 3389 0x00, 0x00, 0x10, // Length: 20 | |
| 3390 0x01, // Type: HEADERS | |
| 3391 0x01, // Flags: END_STREAM | |
| 3392 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 3393 0x00, // Unindexed Entry | |
| 3394 0x06, // Name Len: 6 | |
| 3395 'c', 'o', 'o', 'k', 'i', 'e', // Name | |
| 3396 0x07, // Value Len: 7 | |
| 3397 'f', 'o', 'o', '=', 'b', 'a', 'r', // Value | |
| 3398 | |
| 3399 0x00, 0x00, 0x14, // Length: 20 | |
| 3400 0x09, // Type: CONTINUATION | |
| 3401 0x00, // Flags: none | |
| 3402 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 3403 0x00, // Unindexed Entry | |
| 3404 0x06, // Name Len: 6 | |
| 3405 'c', 'o', 'o', 'k', 'i', 'e', // Name | |
| 3406 0x08, // Value Len: 7 | |
| 3407 'b', 'a', 'z', '=', 'b', 'i', 'n', 'g', // Value | |
| 3408 0x00, // Unindexed Entry | |
| 3409 0x06, // Name Len: 6 | |
| 3410 'c', // Name (split) | |
| 3411 | |
| 3412 0x00, 0x00, 0x12, // Length: 18 | |
| 3413 0x09, // Type: CONTINUATION | |
| 3414 0x04, // Flags: END_HEADERS | |
| 3415 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 3416 'o', 'o', 'k', 'i', 'e', // Name (continued) | |
| 3417 0x00, // Value Len: 0 | |
| 3418 0x00, // Unindexed Entry | |
| 3419 0x04, // Name Len: 4 | |
| 3420 'n', 'a', 'm', 'e', // Name | |
| 3421 0x05, // Value Len: 5 | |
| 3422 'v', 'a', 'l', 'u', 'e', // Value | |
| 3423 }; | |
| 3424 // frame-format on | |
| 3425 | |
| 3426 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3427 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3428 visitor.SimulateInFramer(kInput, sizeof(kInput)); | |
| 3429 | |
| 3430 EXPECT_EQ(0, visitor.error_count_); | |
| 3431 EXPECT_EQ(1, visitor.headers_frame_count_); | |
| 3432 EXPECT_EQ(2, visitor.continuation_count_); | |
| 3433 EXPECT_EQ(1, visitor.fin_flag_count_); | |
| 3434 EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_); | |
| 3435 EXPECT_EQ(1, visitor.end_of_stream_count_); | |
| 3436 | |
| 3437 EXPECT_THAT( | |
| 3438 visitor.headers_, | |
| 3439 testing::ElementsAre(testing::Pair("cookie", "foo=bar; baz=bing; "), | |
| 3440 testing::Pair("name", "value"))); | |
| 3441 } | |
| 3442 | |
| 3443 TEST_P(SpdyFramerTest, ReadPushPromiseWithContinuation) { | |
| 3444 // frame-format off | |
| 3445 const unsigned char kInput[] = { | |
| 3446 0x00, 0x00, 0x17, 0x05, // PUSH_PROMISE | |
| 3447 0x08, 0x00, 0x00, 0x00, // PADDED | |
| 3448 0x01, 0x02, 0x00, 0x00, // Stream 1, Pad length field | |
| 3449 0x00, 0x2A, 0x00, 0x06, // Promised stream 42 | |
| 3450 'c', 'o', 'o', 'k', | |
| 3451 'i', 'e', 0x07, 'f', | |
| 3452 'o', 'o', '=', 'b', | |
| 3453 'a', 'r', 0x00, 0x00, | |
| 3454 | |
| 3455 0x00, 0x00, 0x14, 0x09, // CONTINUATION | |
| 3456 0x00, 0x00, 0x00, 0x00, | |
| 3457 0x01, 0x00, 0x06, 'c', // Stream 1 | |
| 3458 'o', 'o', 'k', 'i', | |
| 3459 'e', 0x08, 'b', 'a', | |
| 3460 'z', '=', 'b', 'i', | |
| 3461 'n', 'g', 0x00, 0x06, | |
| 3462 'c', | |
| 3463 | |
| 3464 0x00, 0x00, 0x12, 0x09, // CONTINUATION | |
| 3465 0x04, 0x00, 0x00, 0x00, // END_HEADERS | |
| 3466 0x01, 'o', 'o', 'k', // Stream 1 | |
| 3467 'i', 'e', 0x00, 0x00, | |
| 3468 0x04, 'n', 'a', 'm', | |
| 3469 'e', 0x05, 'v', 'a', | |
| 3470 'l', 'u', 'e', | |
| 3471 }; | |
| 3472 // frame-format on | |
| 3473 | |
| 3474 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3475 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3476 visitor.SimulateInFramer(kInput, sizeof(kInput)); | |
| 3477 | |
| 3478 EXPECT_EQ(0, visitor.error_count_); | |
| 3479 EXPECT_EQ(1u, visitor.last_push_promise_stream_); | |
| 3480 EXPECT_EQ(42u, visitor.last_push_promise_promised_stream_); | |
| 3481 EXPECT_EQ(2, visitor.continuation_count_); | |
| 3482 EXPECT_EQ(0, visitor.zero_length_control_frame_header_data_count_); | |
| 3483 EXPECT_EQ(0, visitor.end_of_stream_count_); | |
| 3484 | |
| 3485 EXPECT_THAT( | |
| 3486 visitor.headers_, | |
| 3487 testing::ElementsAre(testing::Pair("cookie", "foo=bar; baz=bing; "), | |
| 3488 testing::Pair("name", "value"))); | |
| 3489 } | |
| 3490 | |
| 3491 // Receiving an unknown frame when a continuation is expected should | |
| 3492 // result in a SPDY_UNEXPECTED_FRAME error | |
| 3493 TEST_P(SpdyFramerTest, ReceiveUnknownMidContinuation) { | |
| 3494 const unsigned char kInput[] = { | |
| 3495 0x00, 0x00, 0x10, // Length: 16 | |
| 3496 0x01, // Type: HEADERS | |
| 3497 0x00, // Flags: none | |
| 3498 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 3499 0x00, 0x06, 0x63, 0x6f, // HPACK | |
| 3500 0x6f, 0x6b, 0x69, 0x65, // | |
| 3501 0x07, 0x66, 0x6f, 0x6f, // | |
| 3502 0x3d, 0x62, 0x61, 0x72, // | |
| 3503 | |
| 3504 0x00, 0x00, 0x14, // Length: 20 | |
| 3505 0xa9, // Type: UnknownFrameType(169) | |
| 3506 0x00, // Flags: none | |
| 3507 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 3508 0x00, 0x06, 0x63, 0x6f, // Payload | |
| 3509 0x6f, 0x6b, 0x69, 0x65, // | |
| 3510 0x08, 0x62, 0x61, 0x7a, // | |
| 3511 0x3d, 0x62, 0x69, 0x6e, // | |
| 3512 0x67, 0x00, 0x06, 0x63, // | |
| 3513 }; | |
| 3514 | |
| 3515 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3516 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3517 // Assume the unknown frame is allowed | |
| 3518 visitor.on_unknown_frame_result_ = true; | |
| 3519 framer.set_visitor(&visitor); | |
| 3520 visitor.SimulateInFramer(kInput, sizeof(kInput)); | |
| 3521 | |
| 3522 EXPECT_EQ(1, visitor.error_count_); | |
| 3523 EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME, | |
| 3524 visitor.framer_.spdy_framer_error()) | |
| 3525 << SpdyFramer::SpdyFramerErrorToString( | |
| 3526 visitor.framer_.spdy_framer_error()); | |
| 3527 EXPECT_EQ(1, visitor.headers_frame_count_); | |
| 3528 EXPECT_EQ(0, visitor.continuation_count_); | |
| 3529 EXPECT_EQ(0u, visitor.header_buffer_length_); | |
| 3530 } | |
| 3531 | |
| 3532 // Receiving an unknown frame when a continuation is expected should | |
| 3533 // result in a SPDY_UNEXPECTED_FRAME error | |
| 3534 TEST_P(SpdyFramerTest, ReceiveUnknownMidContinuationWithExtension) { | |
| 3535 const unsigned char kInput[] = { | |
| 3536 0x00, 0x00, 0x10, // Length: 16 | |
| 3537 0x01, // Type: HEADERS | |
| 3538 0x00, // Flags: none | |
| 3539 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 3540 0x00, 0x06, 0x63, 0x6f, // HPACK | |
| 3541 0x6f, 0x6b, 0x69, 0x65, // | |
| 3542 0x07, 0x66, 0x6f, 0x6f, // | |
| 3543 0x3d, 0x62, 0x61, 0x72, // | |
| 3544 | |
| 3545 0x00, 0x00, 0x14, // Length: 20 | |
| 3546 0xa9, // Type: UnknownFrameType(169) | |
| 3547 0x00, // Flags: none | |
| 3548 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 3549 0x00, 0x06, 0x63, 0x6f, // Payload | |
| 3550 0x6f, 0x6b, 0x69, 0x65, // | |
| 3551 0x08, 0x62, 0x61, 0x7a, // | |
| 3552 0x3d, 0x62, 0x69, 0x6e, // | |
| 3553 0x67, 0x00, 0x06, 0x63, // | |
| 3554 }; | |
| 3555 | |
| 3556 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3557 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3558 TestExtension extension; | |
| 3559 visitor.set_extension_visitor(&extension); | |
| 3560 framer.set_visitor(&visitor); | |
| 3561 visitor.SimulateInFramer(kInput, sizeof(kInput)); | |
| 3562 | |
| 3563 EXPECT_EQ(1, visitor.error_count_); | |
| 3564 EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME, | |
| 3565 visitor.framer_.spdy_framer_error()) | |
| 3566 << SpdyFramer::SpdyFramerErrorToString( | |
| 3567 visitor.framer_.spdy_framer_error()); | |
| 3568 EXPECT_EQ(1, visitor.headers_frame_count_); | |
| 3569 EXPECT_EQ(0, visitor.continuation_count_); | |
| 3570 EXPECT_EQ(0u, visitor.header_buffer_length_); | |
| 3571 } | |
| 3572 | |
| 3573 TEST_P(SpdyFramerTest, ReceiveContinuationOnWrongStream) { | |
| 3574 const unsigned char kInput[] = { | |
| 3575 0x00, 0x00, 0x10, // Length: 16 | |
| 3576 0x01, // Type: HEADERS | |
| 3577 0x00, // Flags: none | |
| 3578 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 3579 0x00, 0x06, 0x63, 0x6f, // HPACK | |
| 3580 0x6f, 0x6b, 0x69, 0x65, // | |
| 3581 0x07, 0x66, 0x6f, 0x6f, // | |
| 3582 0x3d, 0x62, 0x61, 0x72, // | |
| 3583 | |
| 3584 0x00, 0x00, 0x14, // Length: 20 | |
| 3585 0x09, // Type: CONTINUATION | |
| 3586 0x00, // Flags: none | |
| 3587 0x00, 0x00, 0x00, 0x02, // Stream: 2 | |
| 3588 0x00, 0x06, 0x63, 0x6f, // HPACK | |
| 3589 0x6f, 0x6b, 0x69, 0x65, // | |
| 3590 0x08, 0x62, 0x61, 0x7a, // | |
| 3591 0x3d, 0x62, 0x69, 0x6e, // | |
| 3592 0x67, 0x00, 0x06, 0x63, // | |
| 3593 }; | |
| 3594 | |
| 3595 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3596 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3597 framer.set_visitor(&visitor); | |
| 3598 visitor.SimulateInFramer(kInput, sizeof(kInput)); | |
| 3599 | |
| 3600 EXPECT_EQ(1, visitor.error_count_); | |
| 3601 EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME, | |
| 3602 visitor.framer_.spdy_framer_error()) | |
| 3603 << SpdyFramer::SpdyFramerErrorToString( | |
| 3604 visitor.framer_.spdy_framer_error()); | |
| 3605 EXPECT_EQ(1, visitor.headers_frame_count_); | |
| 3606 EXPECT_EQ(0, visitor.continuation_count_); | |
| 3607 EXPECT_EQ(0u, visitor.header_buffer_length_); | |
| 3608 } | |
| 3609 | |
| 3610 TEST_P(SpdyFramerTest, ReadContinuationOutOfOrder) { | |
| 3611 const unsigned char kInput[] = { | |
| 3612 0x00, 0x00, 0x18, // Length: 24 | |
| 3613 0x09, // Type: CONTINUATION | |
| 3614 0x00, // Flags: none | |
| 3615 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 3616 0x00, 0x06, 0x63, 0x6f, // HPACK | |
| 3617 0x6f, 0x6b, 0x69, 0x65, // | |
| 3618 0x07, 0x66, 0x6f, 0x6f, // | |
| 3619 0x3d, 0x62, 0x61, 0x72, // | |
| 3620 }; | |
| 3621 | |
| 3622 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3623 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3624 framer.set_visitor(&visitor); | |
| 3625 visitor.SimulateInFramer(kInput, sizeof(kInput)); | |
| 3626 | |
| 3627 EXPECT_EQ(1, visitor.error_count_); | |
| 3628 EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME, | |
| 3629 visitor.framer_.spdy_framer_error()) | |
| 3630 << SpdyFramer::SpdyFramerErrorToString( | |
| 3631 visitor.framer_.spdy_framer_error()); | |
| 3632 EXPECT_EQ(0, visitor.continuation_count_); | |
| 3633 EXPECT_EQ(0u, visitor.header_buffer_length_); | |
| 3634 } | |
| 3635 | |
| 3636 TEST_P(SpdyFramerTest, ExpectContinuationReceiveData) { | |
| 3637 const unsigned char kInput[] = { | |
| 3638 0x00, 0x00, 0x10, // Length: 16 | |
| 3639 0x01, // Type: HEADERS | |
| 3640 0x00, // Flags: none | |
| 3641 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 3642 0x00, 0x06, 0x63, 0x6f, // HPACK | |
| 3643 0x6f, 0x6b, 0x69, 0x65, // | |
| 3644 0x07, 0x66, 0x6f, 0x6f, // | |
| 3645 0x3d, 0x62, 0x61, 0x72, // | |
| 3646 | |
| 3647 0x00, 0x00, 0x00, // Length: 0 | |
| 3648 0x00, // Type: DATA | |
| 3649 0x01, // Flags: END_STREAM | |
| 3650 0x00, 0x00, 0x00, 0x04, // Stream: 4 | |
| 3651 | |
| 3652 0xde, 0xad, 0xbe, 0xef, // Truncated Frame Header | |
| 3653 }; | |
| 3654 | |
| 3655 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3656 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3657 framer.set_visitor(&visitor); | |
| 3658 visitor.SimulateInFramer(kInput, sizeof(kInput)); | |
| 3659 | |
| 3660 EXPECT_EQ(1, visitor.error_count_); | |
| 3661 EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME, | |
| 3662 visitor.framer_.spdy_framer_error()) | |
| 3663 << SpdyFramer::SpdyFramerErrorToString( | |
| 3664 visitor.framer_.spdy_framer_error()); | |
| 3665 EXPECT_EQ(1, visitor.headers_frame_count_); | |
| 3666 EXPECT_EQ(0, visitor.continuation_count_); | |
| 3667 EXPECT_EQ(0u, visitor.header_buffer_length_); | |
| 3668 EXPECT_EQ(0, visitor.data_frame_count_); | |
| 3669 } | |
| 3670 | |
| 3671 TEST_P(SpdyFramerTest, ExpectContinuationReceiveControlFrame) { | |
| 3672 const unsigned char kInput[] = { | |
| 3673 0x00, 0x00, 0x10, // Length: 16 | |
| 3674 0x01, // Type: HEADERS | |
| 3675 0x00, // Flags: none | |
| 3676 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 3677 0x00, 0x06, 0x63, 0x6f, // HPACK | |
| 3678 0x6f, 0x6b, 0x69, 0x65, // | |
| 3679 0x07, 0x66, 0x6f, 0x6f, // | |
| 3680 0x3d, 0x62, 0x61, 0x72, // | |
| 3681 | |
| 3682 0x00, 0x00, 0x10, // Length: 16 | |
| 3683 0x01, // Type: HEADERS | |
| 3684 0x00, // Flags: none | |
| 3685 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 3686 0x00, 0x06, 0x63, 0x6f, // HPACK | |
| 3687 0x6f, 0x6b, 0x69, 0x65, // | |
| 3688 0x07, 0x66, 0x6f, 0x6f, // | |
| 3689 0x3d, 0x62, 0x61, 0x72, // | |
| 3690 }; | |
| 3691 | |
| 3692 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3693 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3694 framer.set_visitor(&visitor); | |
| 3695 visitor.SimulateInFramer(kInput, sizeof(kInput)); | |
| 3696 | |
| 3697 EXPECT_EQ(1, visitor.error_count_); | |
| 3698 EXPECT_EQ(SpdyFramer::SPDY_UNEXPECTED_FRAME, | |
| 3699 visitor.framer_.spdy_framer_error()) | |
| 3700 << SpdyFramer::SpdyFramerErrorToString( | |
| 3701 visitor.framer_.spdy_framer_error()); | |
| 3702 EXPECT_EQ(1, visitor.headers_frame_count_); | |
| 3703 EXPECT_EQ(0, visitor.continuation_count_); | |
| 3704 EXPECT_EQ(0u, visitor.header_buffer_length_); | |
| 3705 EXPECT_EQ(0, visitor.data_frame_count_); | |
| 3706 } | |
| 3707 | |
| 3708 TEST_P(SpdyFramerTest, ReadGarbage) { | |
| 3709 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3710 unsigned char garbage_frame[256]; | |
| 3711 memset(garbage_frame, ~0, sizeof(garbage_frame)); | |
| 3712 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3713 visitor.SimulateInFramer(garbage_frame, sizeof(garbage_frame)); | |
| 3714 EXPECT_EQ(1, visitor.error_count_); | |
| 3715 } | |
| 3716 | |
| 3717 TEST_P(SpdyFramerTest, ReadUnknownExtensionFrame) { | |
| 3718 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3719 | |
| 3720 // The unrecognized frame type should still have a valid length. | |
| 3721 const unsigned char unknown_frame[] = { | |
| 3722 0x00, 0x00, 0x08, // Length: 8 | |
| 3723 0xff, // Type: UnknownFrameType(255) | |
| 3724 0xff, // Flags: 0xff | |
| 3725 0xff, 0xff, 0xff, 0xff, // Stream: 0x7fffffff (R-bit set) | |
| 3726 0xff, 0xff, 0xff, 0xff, // Payload | |
| 3727 0xff, 0xff, 0xff, 0xff, // | |
| 3728 }; | |
| 3729 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3730 | |
| 3731 // Simulate the case where the stream id validation checks out. | |
| 3732 visitor.on_unknown_frame_result_ = true; | |
| 3733 visitor.SimulateInFramer(unknown_frame, arraysize(unknown_frame)); | |
| 3734 EXPECT_EQ(0, visitor.error_count_); | |
| 3735 | |
| 3736 // Follow it up with a valid control frame to make sure we handle | |
| 3737 // subsequent frames correctly. | |
| 3738 SpdySettingsIR settings_ir; | |
| 3739 settings_ir.AddSetting(SETTINGS_HEADER_TABLE_SIZE, 10); | |
| 3740 SpdySerializedFrame control_frame(framer.SerializeSettings(settings_ir)); | |
| 3741 if (use_output_) { | |
| 3742 ASSERT_TRUE(framer.SerializeSettings(settings_ir, &output_)); | |
| 3743 control_frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 3744 } | |
| 3745 visitor.SimulateInFramer( | |
| 3746 reinterpret_cast<unsigned char*>(control_frame.data()), | |
| 3747 control_frame.size()); | |
| 3748 EXPECT_EQ(0, visitor.error_count_); | |
| 3749 EXPECT_EQ(1u, static_cast<unsigned>(visitor.setting_count_)); | |
| 3750 EXPECT_EQ(1u, static_cast<unsigned>(visitor.settings_ack_sent_)); | |
| 3751 } | |
| 3752 | |
| 3753 TEST_P(SpdyFramerTest, ReadUnknownExtensionFrameWithExtension) { | |
| 3754 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3755 | |
| 3756 // The unrecognized frame type should still have a valid length. | |
| 3757 const unsigned char unknown_frame[] = { | |
| 3758 0x00, 0x00, 0x14, // Length: 20 | |
| 3759 0xff, // Type: UnknownFrameType(255) | |
| 3760 0xff, // Flags: 0xff | |
| 3761 0xff, 0xff, 0xff, 0xff, // Stream: 0x7fffffff (R-bit set) | |
| 3762 0xff, 0xff, 0xff, 0xff, // Payload | |
| 3763 0xff, 0xff, 0xff, 0xff, // | |
| 3764 0xff, 0xff, 0xff, 0xff, // | |
| 3765 0xff, 0xff, 0xff, 0xff, // | |
| 3766 0xff, 0xff, 0xff, 0xff, // | |
| 3767 }; | |
| 3768 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3769 TestExtension extension; | |
| 3770 visitor.set_extension_visitor(&extension); | |
| 3771 visitor.SimulateInFramer(unknown_frame, arraysize(unknown_frame)); | |
| 3772 EXPECT_EQ(0, visitor.error_count_); | |
| 3773 EXPECT_EQ(0x7fffffffu, extension.stream_id_); | |
| 3774 EXPECT_EQ(20u, extension.length_); | |
| 3775 EXPECT_EQ(255, extension.type_); | |
| 3776 EXPECT_EQ(0xff, extension.flags_); | |
| 3777 EXPECT_EQ(SpdyString(20, '\xff'), extension.payload_); | |
| 3778 | |
| 3779 // Follow it up with a valid control frame to make sure we handle | |
| 3780 // subsequent frames correctly. | |
| 3781 SpdySettingsIR settings_ir; | |
| 3782 settings_ir.AddSetting(SETTINGS_HEADER_TABLE_SIZE, 10); | |
| 3783 SpdySerializedFrame control_frame(framer.SerializeSettings(settings_ir)); | |
| 3784 visitor.SimulateInFramer( | |
| 3785 reinterpret_cast<unsigned char*>(control_frame.data()), | |
| 3786 control_frame.size()); | |
| 3787 EXPECT_EQ(0, visitor.error_count_); | |
| 3788 EXPECT_EQ(1u, static_cast<unsigned>(visitor.setting_count_)); | |
| 3789 EXPECT_EQ(1u, static_cast<unsigned>(visitor.settings_ack_sent_)); | |
| 3790 } | |
| 3791 | |
| 3792 TEST_P(SpdyFramerTest, ReadGarbageWithValidLength) { | |
| 3793 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3794 const unsigned char kFrameData[] = { | |
| 3795 0x00, 0x00, 0x08, // Length: 8 | |
| 3796 0xff, // Type: UnknownFrameType(255) | |
| 3797 0xff, // Flags: 0xff | |
| 3798 0xff, 0xff, 0xff, 0xff, // Stream: 0x7fffffff (R-bit set) | |
| 3799 0xff, 0xff, 0xff, 0xff, // Payload | |
| 3800 0xff, 0xff, 0xff, 0xff, // | |
| 3801 }; | |
| 3802 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3803 visitor.SimulateInFramer(kFrameData, arraysize(kFrameData)); | |
| 3804 EXPECT_EQ(1, visitor.error_count_); | |
| 3805 } | |
| 3806 | |
| 3807 TEST_P(SpdyFramerTest, ReadGarbageHPACKEncoding) { | |
| 3808 const unsigned char kInput[] = { | |
| 3809 0x00, 0x12, 0x01, // Length: 4609 | |
| 3810 0x04, // Type: SETTINGS | |
| 3811 0x00, // Flags: none | |
| 3812 0x00, 0x00, 0x01, 0xef, // Stream: 495 | |
| 3813 0xef, 0xff, // Param: 61439 | |
| 3814 0xff, 0xff, 0xff, 0xff, // Value: 4294967295 | |
| 3815 0xff, 0xff, // Param: 0xffff | |
| 3816 0xff, 0xff, 0xff, 0xff, // Value: 4294967295 | |
| 3817 0xff, 0xff, 0xff, 0xff, // Settings (Truncated) | |
| 3818 0xff, // | |
| 3819 }; | |
| 3820 | |
| 3821 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 3822 visitor.SimulateInFramer(kInput, arraysize(kInput)); | |
| 3823 EXPECT_EQ(1, visitor.error_count_); | |
| 3824 } | |
| 3825 | |
| 3826 TEST_P(SpdyFramerTest, SizesTest) { | |
| 3827 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3828 EXPECT_EQ(9u, framer.GetDataFrameMinimumSize()); | |
| 3829 EXPECT_EQ(9u, framer.GetFrameHeaderSize()); | |
| 3830 EXPECT_EQ(13u, framer.GetRstStreamSize()); | |
| 3831 EXPECT_EQ(9u, framer.GetSettingsMinimumSize()); | |
| 3832 EXPECT_EQ(17u, framer.GetPingSize()); | |
| 3833 EXPECT_EQ(17u, framer.GetGoAwayMinimumSize()); | |
| 3834 EXPECT_EQ(9u, framer.GetHeadersMinimumSize()); | |
| 3835 EXPECT_EQ(13u, framer.GetWindowUpdateSize()); | |
| 3836 EXPECT_EQ(13u, framer.GetPushPromiseMinimumSize()); | |
| 3837 EXPECT_EQ(11u, framer.GetAltSvcMinimumSize()); | |
| 3838 EXPECT_EQ(9u, framer.GetFrameMinimumSize()); | |
| 3839 EXPECT_EQ(16393u, framer.GetFrameMaximumSize()); | |
| 3840 EXPECT_EQ(16384u, framer.GetDataFrameMaximumPayload()); | |
| 3841 } | |
| 3842 | |
| 3843 TEST_P(SpdyFramerTest, StateToStringTest) { | |
| 3844 EXPECT_STREQ("ERROR", SpdyFramer::StateToString(SpdyFramer::SPDY_ERROR)); | |
| 3845 EXPECT_STREQ("FRAME_COMPLETE", | |
| 3846 SpdyFramer::StateToString(SpdyFramer::SPDY_FRAME_COMPLETE)); | |
| 3847 EXPECT_STREQ("READY_FOR_FRAME", | |
| 3848 SpdyFramer::StateToString(SpdyFramer::SPDY_READY_FOR_FRAME)); | |
| 3849 EXPECT_STREQ( | |
| 3850 "READING_COMMON_HEADER", | |
| 3851 SpdyFramer::StateToString(SpdyFramer::SPDY_READING_COMMON_HEADER)); | |
| 3852 EXPECT_STREQ( | |
| 3853 "CONTROL_FRAME_PAYLOAD", | |
| 3854 SpdyFramer::StateToString(SpdyFramer::SPDY_CONTROL_FRAME_PAYLOAD)); | |
| 3855 EXPECT_STREQ( | |
| 3856 "IGNORE_REMAINING_PAYLOAD", | |
| 3857 SpdyFramer::StateToString(SpdyFramer::SPDY_IGNORE_REMAINING_PAYLOAD)); | |
| 3858 EXPECT_STREQ( | |
| 3859 "FORWARD_STREAM_FRAME", | |
| 3860 SpdyFramer::StateToString(SpdyFramer::SPDY_FORWARD_STREAM_FRAME)); | |
| 3861 EXPECT_STREQ("SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK", | |
| 3862 SpdyFramer::StateToString( | |
| 3863 SpdyFramer::SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK)); | |
| 3864 EXPECT_STREQ( | |
| 3865 "SPDY_CONTROL_FRAME_HEADER_BLOCK", | |
| 3866 SpdyFramer::StateToString(SpdyFramer::SPDY_CONTROL_FRAME_HEADER_BLOCK)); | |
| 3867 EXPECT_STREQ( | |
| 3868 "SPDY_SETTINGS_FRAME_PAYLOAD", | |
| 3869 SpdyFramer::StateToString(SpdyFramer::SPDY_SETTINGS_FRAME_PAYLOAD)); | |
| 3870 EXPECT_STREQ( | |
| 3871 "SPDY_ALTSVC_FRAME_PAYLOAD", | |
| 3872 SpdyFramer::StateToString(SpdyFramer::SPDY_ALTSVC_FRAME_PAYLOAD)); | |
| 3873 EXPECT_STREQ("UNKNOWN_STATE", SpdyFramer::StateToString( | |
| 3874 SpdyFramer::SPDY_ALTSVC_FRAME_PAYLOAD + 1)); | |
| 3875 } | |
| 3876 | |
| 3877 TEST_P(SpdyFramerTest, SpdyFramerErrorToStringTest) { | |
| 3878 EXPECT_STREQ("NO_ERROR", | |
| 3879 SpdyFramer::SpdyFramerErrorToString(SpdyFramer::SPDY_NO_ERROR)); | |
| 3880 EXPECT_STREQ("INVALID_STREAM_ID", SpdyFramer::SpdyFramerErrorToString( | |
| 3881 SpdyFramer::SPDY_INVALID_STREAM_ID)); | |
| 3882 EXPECT_STREQ("INVALID_CONTROL_FRAME", | |
| 3883 SpdyFramer::SpdyFramerErrorToString( | |
| 3884 SpdyFramer::SPDY_INVALID_CONTROL_FRAME)); | |
| 3885 EXPECT_STREQ("CONTROL_PAYLOAD_TOO_LARGE", | |
| 3886 SpdyFramer::SpdyFramerErrorToString( | |
| 3887 SpdyFramer::SPDY_CONTROL_PAYLOAD_TOO_LARGE)); | |
| 3888 EXPECT_STREQ("ZLIB_INIT_FAILURE", SpdyFramer::SpdyFramerErrorToString( | |
| 3889 SpdyFramer::SPDY_ZLIB_INIT_FAILURE)); | |
| 3890 EXPECT_STREQ("UNSUPPORTED_VERSION", | |
| 3891 SpdyFramer::SpdyFramerErrorToString( | |
| 3892 SpdyFramer::SPDY_UNSUPPORTED_VERSION)); | |
| 3893 EXPECT_STREQ("DECOMPRESS_FAILURE", SpdyFramer::SpdyFramerErrorToString( | |
| 3894 SpdyFramer::SPDY_DECOMPRESS_FAILURE)); | |
| 3895 EXPECT_STREQ("COMPRESS_FAILURE", SpdyFramer::SpdyFramerErrorToString( | |
| 3896 SpdyFramer::SPDY_COMPRESS_FAILURE)); | |
| 3897 EXPECT_STREQ("GOAWAY_FRAME_CORRUPT", | |
| 3898 SpdyFramer::SpdyFramerErrorToString( | |
| 3899 SpdyFramer::SPDY_GOAWAY_FRAME_CORRUPT)); | |
| 3900 EXPECT_STREQ("RST_STREAM_FRAME_CORRUPT", | |
| 3901 SpdyFramer::SpdyFramerErrorToString( | |
| 3902 SpdyFramer::SPDY_RST_STREAM_FRAME_CORRUPT)); | |
| 3903 EXPECT_STREQ("INVALID_PADDING", SpdyFramer::SpdyFramerErrorToString( | |
| 3904 SpdyFramer::SPDY_INVALID_PADDING)); | |
| 3905 EXPECT_STREQ("INVALID_DATA_FRAME_FLAGS", | |
| 3906 SpdyFramer::SpdyFramerErrorToString( | |
| 3907 SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS)); | |
| 3908 EXPECT_STREQ("INVALID_CONTROL_FRAME_FLAGS", | |
| 3909 SpdyFramer::SpdyFramerErrorToString( | |
| 3910 SpdyFramer::SPDY_INVALID_CONTROL_FRAME_FLAGS)); | |
| 3911 EXPECT_STREQ("UNEXPECTED_FRAME", SpdyFramer::SpdyFramerErrorToString( | |
| 3912 SpdyFramer::SPDY_UNEXPECTED_FRAME)); | |
| 3913 EXPECT_STREQ("INTERNAL_FRAMER_ERROR", | |
| 3914 SpdyFramer::SpdyFramerErrorToString( | |
| 3915 SpdyFramer::SPDY_INTERNAL_FRAMER_ERROR)); | |
| 3916 EXPECT_STREQ("INVALID_CONTROL_FRAME_SIZE", | |
| 3917 SpdyFramer::SpdyFramerErrorToString( | |
| 3918 SpdyFramer::SPDY_INVALID_CONTROL_FRAME_SIZE)); | |
| 3919 EXPECT_STREQ("OVERSIZED_PAYLOAD", SpdyFramer::SpdyFramerErrorToString( | |
| 3920 SpdyFramer::SPDY_OVERSIZED_PAYLOAD)); | |
| 3921 EXPECT_STREQ("UNKNOWN_ERROR", | |
| 3922 SpdyFramer::SpdyFramerErrorToString(SpdyFramer::LAST_ERROR)); | |
| 3923 EXPECT_STREQ("UNKNOWN_ERROR", SpdyFramer::SpdyFramerErrorToString( | |
| 3924 static_cast<SpdyFramer::SpdyFramerError>( | |
| 3925 SpdyFramer::LAST_ERROR + 1))); | |
| 3926 } | |
| 3927 | |
| 3928 TEST_P(SpdyFramerTest, DataFrameFlagsV4) { | |
| 3929 uint8_t valid_data_flags = DATA_FLAG_FIN | DATA_FLAG_PADDED; | |
| 3930 | |
| 3931 uint8_t flags = 0; | |
| 3932 do { | |
| 3933 SCOPED_TRACE(testing::Message() << "Flags " << flags << std::hex | |
| 3934 << static_cast<int>(flags)); | |
| 3935 | |
| 3936 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 3937 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3938 framer.set_visitor(&visitor); | |
| 3939 | |
| 3940 SpdyDataIR data_ir(1, "hello"); | |
| 3941 SpdySerializedFrame frame(framer.SerializeData(data_ir)); | |
| 3942 SetFrameFlags(&frame, flags); | |
| 3943 | |
| 3944 if (flags & ~valid_data_flags) { | |
| 3945 EXPECT_CALL(visitor, OnError(_)); | |
| 3946 } else { | |
| 3947 EXPECT_CALL(visitor, OnDataFrameHeader(1, 5, flags & DATA_FLAG_FIN)); | |
| 3948 if (flags & DATA_FLAG_PADDED) { | |
| 3949 // The first byte of payload is parsed as padding length, but 'h' | |
| 3950 // (0x68) is too large a padding length for a 5 byte payload. | |
| 3951 EXPECT_CALL(visitor, OnStreamPadding(_, 1)); | |
| 3952 // Expect Error since the frame ends prematurely. | |
| 3953 EXPECT_CALL(visitor, OnError(_)); | |
| 3954 } else { | |
| 3955 EXPECT_CALL(visitor, OnStreamFrameData(_, _, 5)); | |
| 3956 if (flags & DATA_FLAG_FIN) { | |
| 3957 EXPECT_CALL(visitor, OnStreamEnd(_)); | |
| 3958 } | |
| 3959 } | |
| 3960 } | |
| 3961 | |
| 3962 framer.ProcessInput(frame.data(), frame.size()); | |
| 3963 if (flags & ~valid_data_flags) { | |
| 3964 EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state()); | |
| 3965 EXPECT_EQ(SpdyFramer::SPDY_INVALID_DATA_FRAME_FLAGS, | |
| 3966 framer.spdy_framer_error()) | |
| 3967 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 3968 } else if (flags & DATA_FLAG_PADDED) { | |
| 3969 EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state()); | |
| 3970 EXPECT_EQ(SpdyFramer::SPDY_INVALID_PADDING, framer.spdy_framer_error()) | |
| 3971 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 3972 } else { | |
| 3973 EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 3974 EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) | |
| 3975 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 3976 } | |
| 3977 } while (++flags != 0); | |
| 3978 } | |
| 3979 | |
| 3980 TEST_P(SpdyFramerTest, RstStreamFrameFlags) { | |
| 3981 uint8_t flags = 0; | |
| 3982 do { | |
| 3983 SCOPED_TRACE(testing::Message() << "Flags " << flags << std::hex | |
| 3984 << static_cast<int>(flags)); | |
| 3985 | |
| 3986 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 3987 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 3988 framer.set_visitor(&visitor); | |
| 3989 | |
| 3990 SpdyRstStreamIR rst_stream(13, ERROR_CODE_CANCEL); | |
| 3991 SpdySerializedFrame frame(framer.SerializeRstStream(rst_stream)); | |
| 3992 if (use_output_) { | |
| 3993 output_.Reset(); | |
| 3994 ASSERT_TRUE(framer.SerializeRstStream(rst_stream, &output_)); | |
| 3995 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 3996 } | |
| 3997 SetFrameFlags(&frame, flags); | |
| 3998 | |
| 3999 EXPECT_CALL(visitor, OnRstStream(13, ERROR_CODE_CANCEL)); | |
| 4000 | |
| 4001 framer.ProcessInput(frame.data(), frame.size()); | |
| 4002 | |
| 4003 EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 4004 EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) | |
| 4005 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 4006 } while (++flags != 0); | |
| 4007 } | |
| 4008 | |
| 4009 TEST_P(SpdyFramerTest, SettingsFrameFlags) { | |
| 4010 uint8_t flags = 0; | |
| 4011 do { | |
| 4012 SCOPED_TRACE(testing::Message() << "Flags " << flags << std::hex | |
| 4013 << static_cast<int>(flags)); | |
| 4014 | |
| 4015 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 4016 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 4017 framer.set_visitor(&visitor); | |
| 4018 | |
| 4019 SpdySettingsIR settings_ir; | |
| 4020 settings_ir.AddSetting(SETTINGS_INITIAL_WINDOW_SIZE, 16); | |
| 4021 SpdySerializedFrame frame(framer.SerializeSettings(settings_ir)); | |
| 4022 if (use_output_) { | |
| 4023 output_.Reset(); | |
| 4024 ASSERT_TRUE(framer.SerializeSettings(settings_ir, &output_)); | |
| 4025 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 4026 } | |
| 4027 SetFrameFlags(&frame, flags); | |
| 4028 | |
| 4029 if (flags & SETTINGS_FLAG_ACK) { | |
| 4030 EXPECT_CALL(visitor, OnError(_)); | |
| 4031 } else { | |
| 4032 EXPECT_CALL(visitor, OnSettings(flags & SETTINGS_FLAG_ACK)); | |
| 4033 EXPECT_CALL(visitor, OnSetting(SETTINGS_INITIAL_WINDOW_SIZE, 16)); | |
| 4034 EXPECT_CALL(visitor, OnSettingsEnd()); | |
| 4035 } | |
| 4036 | |
| 4037 framer.ProcessInput(frame.data(), frame.size()); | |
| 4038 if (flags & SETTINGS_FLAG_ACK) { | |
| 4039 // The frame is invalid because ACK frames should have no payload. | |
| 4040 EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state()); | |
| 4041 EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_SIZE, | |
| 4042 framer.spdy_framer_error()) | |
| 4043 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 4044 } else { | |
| 4045 EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 4046 EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) | |
| 4047 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 4048 } | |
| 4049 } while (++flags != 0); | |
| 4050 } | |
| 4051 | |
| 4052 TEST_P(SpdyFramerTest, GoawayFrameFlags) { | |
| 4053 uint8_t flags = 0; | |
| 4054 do { | |
| 4055 SCOPED_TRACE(testing::Message() << "Flags " << flags << std::hex | |
| 4056 << static_cast<int>(flags)); | |
| 4057 | |
| 4058 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 4059 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 4060 framer.set_visitor(&visitor); | |
| 4061 | |
| 4062 SpdyGoAwayIR goaway_ir(97, ERROR_CODE_NO_ERROR, "test"); | |
| 4063 SpdySerializedFrame frame(framer.SerializeGoAway(goaway_ir)); | |
| 4064 if (use_output_) { | |
| 4065 output_.Reset(); | |
| 4066 ASSERT_TRUE(framer.SerializeGoAway(goaway_ir, &output_)); | |
| 4067 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 4068 } | |
| 4069 SetFrameFlags(&frame, flags); | |
| 4070 | |
| 4071 EXPECT_CALL(visitor, OnGoAway(97, ERROR_CODE_NO_ERROR)); | |
| 4072 | |
| 4073 framer.ProcessInput(frame.data(), frame.size()); | |
| 4074 EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 4075 EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) | |
| 4076 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 4077 } while (++flags != 0); | |
| 4078 } | |
| 4079 | |
| 4080 TEST_P(SpdyFramerTest, HeadersFrameFlags) { | |
| 4081 uint8_t flags = 0; | |
| 4082 do { | |
| 4083 SCOPED_TRACE(testing::Message() << "Flags " << flags << std::hex | |
| 4084 << static_cast<int>(flags)); | |
| 4085 | |
| 4086 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 4087 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 4088 framer.set_visitor(&visitor); | |
| 4089 | |
| 4090 SpdyHeadersIR headers_ir(57); | |
| 4091 if (flags & HEADERS_FLAG_PRIORITY) { | |
| 4092 headers_ir.set_weight(3); | |
| 4093 headers_ir.set_has_priority(true); | |
| 4094 headers_ir.set_parent_stream_id(5); | |
| 4095 headers_ir.set_exclusive(true); | |
| 4096 } | |
| 4097 headers_ir.SetHeader("foo", "bar"); | |
| 4098 SpdySerializedFrame frame(SpdyFramerPeer::SerializeHeaders( | |
| 4099 &framer, headers_ir, use_output_ ? &output_ : nullptr)); | |
| 4100 uint8_t set_flags = flags & ~HEADERS_FLAG_PADDED; | |
| 4101 SetFrameFlags(&frame, set_flags); | |
| 4102 | |
| 4103 // Expected callback values | |
| 4104 SpdyStreamId stream_id = 57; | |
| 4105 bool has_priority = false; | |
| 4106 SpdyPriority priority = 0; | |
| 4107 SpdyStreamId parent_stream_id = 0; | |
| 4108 bool exclusive = false; | |
| 4109 bool fin = flags & CONTROL_FLAG_FIN; | |
| 4110 bool end = flags & HEADERS_FLAG_END_HEADERS; | |
| 4111 if (flags & HEADERS_FLAG_PRIORITY) { | |
| 4112 has_priority = true; | |
| 4113 priority = 3; | |
| 4114 parent_stream_id = 5; | |
| 4115 exclusive = true; | |
| 4116 } | |
| 4117 EXPECT_CALL(visitor, OnHeaders(stream_id, has_priority, priority, | |
| 4118 parent_stream_id, exclusive, fin, end)); | |
| 4119 EXPECT_CALL(visitor, OnHeaderFrameStart(57)).Times(1); | |
| 4120 if (end) { | |
| 4121 EXPECT_CALL(visitor, OnHeaderFrameEnd(57, _)).Times(1); | |
| 4122 } | |
| 4123 if (flags & DATA_FLAG_FIN && end) { | |
| 4124 EXPECT_CALL(visitor, OnStreamEnd(_)); | |
| 4125 } else { | |
| 4126 // Do not close the stream if we are expecting a CONTINUATION frame. | |
| 4127 EXPECT_CALL(visitor, OnStreamEnd(_)).Times(0); | |
| 4128 } | |
| 4129 | |
| 4130 framer.ProcessInput(frame.data(), frame.size()); | |
| 4131 EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 4132 EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) | |
| 4133 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 4134 } while (++flags != 0); | |
| 4135 } | |
| 4136 | |
| 4137 TEST_P(SpdyFramerTest, PingFrameFlags) { | |
| 4138 uint8_t flags = 0; | |
| 4139 do { | |
| 4140 SCOPED_TRACE(testing::Message() << "Flags " << flags << std::hex | |
| 4141 << static_cast<int>(flags)); | |
| 4142 | |
| 4143 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 4144 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 4145 framer.set_visitor(&visitor); | |
| 4146 | |
| 4147 SpdySerializedFrame frame(framer.SerializePing(SpdyPingIR(42))); | |
| 4148 SetFrameFlags(&frame, flags); | |
| 4149 | |
| 4150 EXPECT_CALL(visitor, OnPing(42, flags & PING_FLAG_ACK)); | |
| 4151 | |
| 4152 framer.ProcessInput(frame.data(), frame.size()); | |
| 4153 EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 4154 EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) | |
| 4155 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 4156 } while (++flags != 0); | |
| 4157 } | |
| 4158 | |
| 4159 TEST_P(SpdyFramerTest, WindowUpdateFrameFlags) { | |
| 4160 uint8_t flags = 0; | |
| 4161 do { | |
| 4162 SCOPED_TRACE(testing::Message() << "Flags " << flags << std::hex | |
| 4163 << static_cast<int>(flags)); | |
| 4164 | |
| 4165 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 4166 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 4167 framer.set_visitor(&visitor); | |
| 4168 | |
| 4169 SpdySerializedFrame frame( | |
| 4170 framer.SerializeWindowUpdate(SpdyWindowUpdateIR(4, 1024))); | |
| 4171 SetFrameFlags(&frame, flags); | |
| 4172 | |
| 4173 EXPECT_CALL(visitor, OnWindowUpdate(4, 1024)); | |
| 4174 | |
| 4175 framer.ProcessInput(frame.data(), frame.size()); | |
| 4176 EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 4177 EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) | |
| 4178 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 4179 } while (++flags != 0); | |
| 4180 } | |
| 4181 | |
| 4182 TEST_P(SpdyFramerTest, PushPromiseFrameFlags) { | |
| 4183 const SpdyStreamId client_id = 123; // Must be odd. | |
| 4184 const SpdyStreamId promised_id = 22; // Must be even. | |
| 4185 uint8_t flags = 0; | |
| 4186 do { | |
| 4187 SCOPED_TRACE(testing::Message() << "Flags " << flags << std::hex | |
| 4188 << static_cast<int>(flags)); | |
| 4189 | |
| 4190 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 4191 testing::StrictMock<test::MockDebugVisitor> debug_visitor; | |
| 4192 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 4193 framer.set_visitor(&visitor); | |
| 4194 framer.set_debug_visitor(&debug_visitor); | |
| 4195 | |
| 4196 EXPECT_CALL( | |
| 4197 debug_visitor, | |
| 4198 OnSendCompressedFrame(client_id, SpdyFrameType::PUSH_PROMISE, _, _)); | |
| 4199 | |
| 4200 SpdyPushPromiseIR push_promise(client_id, promised_id); | |
| 4201 push_promise.SetHeader("foo", "bar"); | |
| 4202 SpdySerializedFrame frame(SpdyFramerPeer::SerializePushPromise( | |
| 4203 &framer, push_promise, use_output_ ? &output_ : nullptr)); | |
| 4204 // TODO(jgraettinger): Add padding to SpdyPushPromiseIR, | |
| 4205 // and implement framing. | |
| 4206 SetFrameFlags(&frame, flags & ~HEADERS_FLAG_PADDED); | |
| 4207 | |
| 4208 bool end = flags & PUSH_PROMISE_FLAG_END_PUSH_PROMISE; | |
| 4209 EXPECT_CALL(debug_visitor, OnReceiveCompressedFrame( | |
| 4210 client_id, SpdyFrameType::PUSH_PROMISE, _)); | |
| 4211 EXPECT_CALL(visitor, OnPushPromise(client_id, promised_id, end)); | |
| 4212 EXPECT_CALL(visitor, OnHeaderFrameStart(client_id)).Times(1); | |
| 4213 if (end) { | |
| 4214 EXPECT_CALL(visitor, OnHeaderFrameEnd(client_id, _)).Times(1); | |
| 4215 } | |
| 4216 | |
| 4217 framer.ProcessInput(frame.data(), frame.size()); | |
| 4218 EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 4219 EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) | |
| 4220 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 4221 } while (++flags != 0); | |
| 4222 } | |
| 4223 | |
| 4224 TEST_P(SpdyFramerTest, ContinuationFrameFlags) { | |
| 4225 uint8_t flags = 0; | |
| 4226 do { | |
| 4227 if (use_output_) { | |
| 4228 output_.Reset(); | |
| 4229 } | |
| 4230 SCOPED_TRACE(testing::Message() << "Flags " << flags << std::hex | |
| 4231 << static_cast<int>(flags)); | |
| 4232 | |
| 4233 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 4234 testing::StrictMock<test::MockDebugVisitor> debug_visitor; | |
| 4235 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 4236 framer.set_visitor(&visitor); | |
| 4237 framer.set_debug_visitor(&debug_visitor); | |
| 4238 | |
| 4239 EXPECT_CALL(debug_visitor, | |
| 4240 OnSendCompressedFrame(42, SpdyFrameType::HEADERS, _, _)); | |
| 4241 EXPECT_CALL(debug_visitor, | |
| 4242 OnReceiveCompressedFrame(42, SpdyFrameType::HEADERS, _)); | |
| 4243 EXPECT_CALL(visitor, OnHeaders(42, false, 0, 0, false, false, false)); | |
| 4244 EXPECT_CALL(visitor, OnHeaderFrameStart(42)).Times(1); | |
| 4245 | |
| 4246 SpdyHeadersIR headers_ir(42); | |
| 4247 headers_ir.SetHeader("foo", "bar"); | |
| 4248 SpdySerializedFrame frame0; | |
| 4249 if (use_output_) { | |
| 4250 EXPECT_TRUE(framer.SerializeHeaders(headers_ir, &output_)); | |
| 4251 frame0 = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 4252 } else { | |
| 4253 frame0 = framer.SerializeHeaders(headers_ir); | |
| 4254 } | |
| 4255 SetFrameFlags(&frame0, 0); | |
| 4256 | |
| 4257 SpdyContinuationIR continuation(42); | |
| 4258 SpdySerializedFrame frame1; | |
| 4259 if (use_output_) { | |
| 4260 char* begin = output_.Begin() + output_.Size(); | |
| 4261 ASSERT_TRUE(framer.SerializeContinuation(continuation, &output_)); | |
| 4262 frame1 = | |
| 4263 SpdySerializedFrame(begin, output_.Size() - frame0.size(), false); | |
| 4264 } else { | |
| 4265 frame1 = framer.SerializeContinuation(continuation); | |
| 4266 } | |
| 4267 SetFrameFlags(&frame1, flags); | |
| 4268 | |
| 4269 EXPECT_CALL(debug_visitor, | |
| 4270 OnReceiveCompressedFrame(42, SpdyFrameType::CONTINUATION, _)); | |
| 4271 EXPECT_CALL(visitor, OnContinuation(42, flags & HEADERS_FLAG_END_HEADERS)); | |
| 4272 bool end = flags & HEADERS_FLAG_END_HEADERS; | |
| 4273 if (end) { | |
| 4274 EXPECT_CALL(visitor, OnHeaderFrameEnd(42, _)).Times(1); | |
| 4275 } | |
| 4276 | |
| 4277 framer.ProcessInput(frame0.data(), frame0.size()); | |
| 4278 framer.ProcessInput(frame1.data(), frame1.size()); | |
| 4279 EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 4280 EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) | |
| 4281 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 4282 } while (++flags != 0); | |
| 4283 } | |
| 4284 | |
| 4285 // TODO(mlavan): Add TEST_F(SpdyFramerTest, AltSvcFrameFlags) | |
| 4286 | |
| 4287 // Test handling of a RST_STREAM with out-of-bounds status codes. | |
| 4288 TEST_P(SpdyFramerTest, RstStreamStatusBounds) { | |
| 4289 const unsigned char kH2RstStreamInvalid[] = { | |
| 4290 0x00, 0x00, 0x04, // Length: 4 | |
| 4291 0x03, // Type: RST_STREAM | |
| 4292 0x00, // Flags: none | |
| 4293 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 4294 0x00, 0x00, 0x00, 0x00, // Error: NO_ERROR | |
| 4295 }; | |
| 4296 const unsigned char kH2RstStreamNumStatusCodes[] = { | |
| 4297 0x00, 0x00, 0x04, // Length: 4 | |
| 4298 0x03, // Type: RST_STREAM | |
| 4299 0x00, // Flags: none | |
| 4300 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 4301 0x00, 0x00, 0x00, 0xff, // Error: 255 | |
| 4302 }; | |
| 4303 | |
| 4304 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 4305 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 4306 framer.set_visitor(&visitor); | |
| 4307 | |
| 4308 EXPECT_CALL(visitor, OnRstStream(1, ERROR_CODE_NO_ERROR)); | |
| 4309 framer.ProcessInput(reinterpret_cast<const char*>(kH2RstStreamInvalid), | |
| 4310 arraysize(kH2RstStreamInvalid)); | |
| 4311 EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 4312 EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) | |
| 4313 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 4314 | |
| 4315 framer.Reset(); | |
| 4316 | |
| 4317 EXPECT_CALL(visitor, OnRstStream(1, ERROR_CODE_INTERNAL_ERROR)); | |
| 4318 framer.ProcessInput(reinterpret_cast<const char*>(kH2RstStreamNumStatusCodes), | |
| 4319 arraysize(kH2RstStreamNumStatusCodes)); | |
| 4320 EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 4321 EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) | |
| 4322 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 4323 } | |
| 4324 | |
| 4325 // Test handling of GOAWAY frames with out-of-bounds status code. | |
| 4326 TEST_P(SpdyFramerTest, GoAwayStatusBounds) { | |
| 4327 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 4328 const unsigned char kH2FrameData[] = { | |
| 4329 0x00, 0x00, 0x0a, // Length: 10 | |
| 4330 0x07, // Type: GOAWAY | |
| 4331 0x00, // Flags: none | |
| 4332 0x00, 0x00, 0x00, 0x00, // Stream: 0 | |
| 4333 0x00, 0x00, 0x00, 0x01, // Last: 1 | |
| 4334 0xff, 0xff, 0xff, 0xff, // Error: 0xffffffff | |
| 4335 0x47, 0x41, // Description | |
| 4336 }; | |
| 4337 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 4338 framer.set_visitor(&visitor); | |
| 4339 | |
| 4340 EXPECT_CALL(visitor, OnGoAway(1, ERROR_CODE_INTERNAL_ERROR)); | |
| 4341 framer.ProcessInput(reinterpret_cast<const char*>(kH2FrameData), | |
| 4342 arraysize(kH2FrameData)); | |
| 4343 EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 4344 EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) | |
| 4345 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 4346 } | |
| 4347 | |
| 4348 // Tests handling of a GOAWAY frame with out-of-bounds stream ID. | |
| 4349 TEST_P(SpdyFramerTest, GoAwayStreamIdBounds) { | |
| 4350 const unsigned char kH2FrameData[] = { | |
| 4351 0x00, 0x00, 0x08, // Length: 8 | |
| 4352 0x07, // Type: GOAWAY | |
| 4353 0x00, // Flags: none | |
| 4354 0x00, 0x00, 0x00, 0x00, // Stream: 0 | |
| 4355 0xff, 0xff, 0xff, 0xff, // Last: 0x7fffffff (R-bit set) | |
| 4356 0x00, 0x00, 0x00, 0x00, // Error: NO_ERROR | |
| 4357 }; | |
| 4358 | |
| 4359 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 4360 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 4361 framer.set_visitor(&visitor); | |
| 4362 | |
| 4363 EXPECT_CALL(visitor, OnGoAway(0x7fffffff, ERROR_CODE_NO_ERROR)); | |
| 4364 framer.ProcessInput(reinterpret_cast<const char*>(kH2FrameData), | |
| 4365 arraysize(kH2FrameData)); | |
| 4366 EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 4367 EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) | |
| 4368 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 4369 } | |
| 4370 | |
| 4371 TEST_P(SpdyFramerTest, OnAltSvcWithOrigin) { | |
| 4372 const SpdyStreamId kStreamId = 0; // Stream id must be zero if origin given. | |
| 4373 | |
| 4374 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 4375 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 4376 framer.set_visitor(&visitor); | |
| 4377 | |
| 4378 SpdyAltSvcWireFormat::AlternativeService altsvc1( | |
| 4379 "pid1", "host", 443, 5, SpdyAltSvcWireFormat::VersionVector()); | |
| 4380 SpdyAltSvcWireFormat::AlternativeService altsvc2( | |
| 4381 "p\"=i:d", "h_\\o\"st", 123, 42, SpdyAltSvcWireFormat::VersionVector{24}); | |
| 4382 SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector; | |
| 4383 altsvc_vector.push_back(altsvc1); | |
| 4384 altsvc_vector.push_back(altsvc2); | |
| 4385 EXPECT_CALL(visitor, | |
| 4386 OnAltSvc(kStreamId, SpdyStringPiece("o_r|g!n"), altsvc_vector)); | |
| 4387 | |
| 4388 SpdyAltSvcIR altsvc_ir(kStreamId); | |
| 4389 altsvc_ir.set_origin("o_r|g!n"); | |
| 4390 altsvc_ir.add_altsvc(altsvc1); | |
| 4391 altsvc_ir.add_altsvc(altsvc2); | |
| 4392 SpdySerializedFrame frame(framer.SerializeFrame(altsvc_ir)); | |
| 4393 if (use_output_) { | |
| 4394 output_.Reset(); | |
| 4395 ASSERT_TRUE(framer.SerializeFrame(altsvc_ir, &output_)); | |
| 4396 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 4397 } | |
| 4398 framer.ProcessInput(frame.data(), frame.size()); | |
| 4399 | |
| 4400 EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 4401 EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) | |
| 4402 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 4403 } | |
| 4404 | |
| 4405 TEST_P(SpdyFramerTest, OnAltSvcNoOrigin) { | |
| 4406 const SpdyStreamId kStreamId = 1; | |
| 4407 | |
| 4408 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 4409 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 4410 framer.set_visitor(&visitor); | |
| 4411 | |
| 4412 SpdyAltSvcWireFormat::AlternativeService altsvc1( | |
| 4413 "pid1", "host", 443, 5, SpdyAltSvcWireFormat::VersionVector()); | |
| 4414 SpdyAltSvcWireFormat::AlternativeService altsvc2( | |
| 4415 "p\"=i:d", "h_\\o\"st", 123, 42, SpdyAltSvcWireFormat::VersionVector{24}); | |
| 4416 SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector; | |
| 4417 altsvc_vector.push_back(altsvc1); | |
| 4418 altsvc_vector.push_back(altsvc2); | |
| 4419 EXPECT_CALL(visitor, OnAltSvc(kStreamId, SpdyStringPiece(""), altsvc_vector)); | |
| 4420 | |
| 4421 SpdyAltSvcIR altsvc_ir(kStreamId); | |
| 4422 altsvc_ir.add_altsvc(altsvc1); | |
| 4423 altsvc_ir.add_altsvc(altsvc2); | |
| 4424 SpdySerializedFrame frame(framer.SerializeFrame(altsvc_ir)); | |
| 4425 framer.ProcessInput(frame.data(), frame.size()); | |
| 4426 | |
| 4427 EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 4428 EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) | |
| 4429 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 4430 } | |
| 4431 | |
| 4432 TEST_P(SpdyFramerTest, OnAltSvcEmptyProtocolId) { | |
| 4433 const SpdyStreamId kStreamId = 0; // Stream id must be zero if origin given. | |
| 4434 | |
| 4435 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 4436 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 4437 framer.set_visitor(&visitor); | |
| 4438 | |
| 4439 EXPECT_CALL(visitor, OnError(testing::Eq(&framer))); | |
| 4440 | |
| 4441 SpdyAltSvcIR altsvc_ir(kStreamId); | |
| 4442 altsvc_ir.set_origin("o1"); | |
| 4443 altsvc_ir.add_altsvc(SpdyAltSvcWireFormat::AlternativeService( | |
| 4444 "pid1", "host", 443, 5, SpdyAltSvcWireFormat::VersionVector())); | |
| 4445 altsvc_ir.add_altsvc(SpdyAltSvcWireFormat::AlternativeService( | |
| 4446 "", "h1", 443, 10, SpdyAltSvcWireFormat::VersionVector())); | |
| 4447 SpdySerializedFrame frame(framer.SerializeFrame(altsvc_ir)); | |
| 4448 if (use_output_) { | |
| 4449 output_.Reset(); | |
| 4450 ASSERT_TRUE(framer.SerializeFrame(altsvc_ir, &output_)); | |
| 4451 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 4452 } | |
| 4453 framer.ProcessInput(frame.data(), frame.size()); | |
| 4454 | |
| 4455 EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state()); | |
| 4456 EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, framer.spdy_framer_error()) | |
| 4457 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 4458 } | |
| 4459 | |
| 4460 TEST_P(SpdyFramerTest, OnAltSvcBadLengths) { | |
| 4461 const unsigned char kType = SerializeFrameType(SpdyFrameType::ALTSVC); | |
| 4462 const unsigned char kFrameDataOriginLenLargerThanFrame[] = { | |
| 4463 0x00, 0x00, 0x05, kType, 0x00, 0x00, 0x00, | |
| 4464 0x00, 0x03, 0x42, 0x42, 'f', 'o', 'o', | |
| 4465 }; | |
| 4466 | |
| 4467 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 4468 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 4469 framer.set_visitor(&visitor); | |
| 4470 visitor.SimulateInFramer(kFrameDataOriginLenLargerThanFrame, | |
| 4471 sizeof(kFrameDataOriginLenLargerThanFrame)); | |
| 4472 | |
| 4473 EXPECT_EQ(1, visitor.error_count_); | |
| 4474 EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, | |
| 4475 visitor.framer_.spdy_framer_error()); | |
| 4476 } | |
| 4477 | |
| 4478 // Tests handling of ALTSVC frames delivered in small chunks. | |
| 4479 TEST_P(SpdyFramerTest, ReadChunkedAltSvcFrame) { | |
| 4480 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 4481 SpdyAltSvcIR altsvc_ir(1); | |
| 4482 SpdyAltSvcWireFormat::AlternativeService altsvc1( | |
| 4483 "pid1", "host", 443, 5, SpdyAltSvcWireFormat::VersionVector()); | |
| 4484 SpdyAltSvcWireFormat::AlternativeService altsvc2( | |
| 4485 "p\"=i:d", "h_\\o\"st", 123, 42, SpdyAltSvcWireFormat::VersionVector{24}); | |
| 4486 altsvc_ir.add_altsvc(altsvc1); | |
| 4487 altsvc_ir.add_altsvc(altsvc2); | |
| 4488 | |
| 4489 SpdySerializedFrame control_frame(framer.SerializeAltSvc(altsvc_ir)); | |
| 4490 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 4491 | |
| 4492 // Read data in small chunks. | |
| 4493 size_t framed_data = 0; | |
| 4494 size_t unframed_data = control_frame.size(); | |
| 4495 size_t kReadChunkSize = 5; // Read five bytes at a time. | |
| 4496 while (unframed_data > 0) { | |
| 4497 size_t to_read = std::min(kReadChunkSize, unframed_data); | |
| 4498 visitor.SimulateInFramer( | |
| 4499 reinterpret_cast<unsigned char*>(control_frame.data() + framed_data), | |
| 4500 to_read); | |
| 4501 unframed_data -= to_read; | |
| 4502 framed_data += to_read; | |
| 4503 } | |
| 4504 EXPECT_EQ(0, visitor.error_count_); | |
| 4505 EXPECT_EQ(1, visitor.altsvc_count_); | |
| 4506 ASSERT_EQ(2u, visitor.test_altsvc_ir_.altsvc_vector().size()); | |
| 4507 EXPECT_TRUE(visitor.test_altsvc_ir_.altsvc_vector()[0] == altsvc1); | |
| 4508 EXPECT_TRUE(visitor.test_altsvc_ir_.altsvc_vector()[1] == altsvc2); | |
| 4509 } | |
| 4510 | |
| 4511 // While RFC7838 Section 4 says that an ALTSVC frame on stream 0 with empty | |
| 4512 // origin MUST be ignored, it is not implemented at the framer level: instead, | |
| 4513 // such frames are passed on to the consumer. | |
| 4514 TEST_P(SpdyFramerTest, ReadAltSvcFrame) { | |
| 4515 constexpr struct { | |
| 4516 uint32_t stream_id; | |
| 4517 const char* origin; | |
| 4518 } test_cases[] = {{0, ""}, | |
| 4519 {1, ""}, | |
| 4520 {0, "https://www.example.com"}, | |
| 4521 {1, "https://www.example.com"}}; | |
| 4522 for (const auto& test_case : test_cases) { | |
| 4523 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 4524 SpdyAltSvcIR altsvc_ir(test_case.stream_id); | |
| 4525 SpdyAltSvcWireFormat::AlternativeService altsvc( | |
| 4526 "pid1", "host", 443, 5, SpdyAltSvcWireFormat::VersionVector()); | |
| 4527 altsvc_ir.add_altsvc(altsvc); | |
| 4528 altsvc_ir.set_origin(test_case.origin); | |
| 4529 SpdySerializedFrame frame(framer.SerializeAltSvc(altsvc_ir)); | |
| 4530 | |
| 4531 TestSpdyVisitor visitor(SpdyFramer::ENABLE_COMPRESSION); | |
| 4532 framer.set_visitor(&visitor); | |
| 4533 framer.ProcessInput(frame.data(), frame.size()); | |
| 4534 | |
| 4535 EXPECT_EQ(0, visitor.error_count_); | |
| 4536 EXPECT_EQ(1, visitor.altsvc_count_); | |
| 4537 EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 4538 EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) | |
| 4539 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 4540 } | |
| 4541 } | |
| 4542 | |
| 4543 // An ALTSVC frame with invalid Alt-Svc-Field-Value results in an error. | |
| 4544 TEST_P(SpdyFramerTest, ErrorOnAltSvcFrameWithInvalidValue) { | |
| 4545 // Alt-Svc-Field-Value must be "clear" or must contain an "=" character | |
| 4546 // per RFC7838 Section 3. | |
| 4547 const char kFrameData[] = { | |
| 4548 0x00, 0x00, 0x16, // Length: 22 | |
| 4549 0x0a, // Type: ALTSVC | |
| 4550 0x00, // Flags: none | |
| 4551 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 4552 0x00, 0x00, // Origin-Len: 0 | |
| 4553 0x74, 0x68, 0x69, 0x73, // thisisnotavalidvalue | |
| 4554 0x69, 0x73, 0x6e, 0x6f, 0x74, 0x61, 0x76, 0x61, | |
| 4555 0x6c, 0x69, 0x64, 0x76, 0x61, 0x6c, 0x75, 0x65, | |
| 4556 }; | |
| 4557 | |
| 4558 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 4559 TestSpdyVisitor visitor(SpdyFramer::ENABLE_COMPRESSION); | |
| 4560 framer.set_visitor(&visitor); | |
| 4561 framer.ProcessInput(kFrameData, sizeof(kFrameData)); | |
| 4562 | |
| 4563 EXPECT_EQ(1, visitor.error_count_); | |
| 4564 EXPECT_EQ(0, visitor.altsvc_count_); | |
| 4565 EXPECT_EQ(SpdyFramer::SPDY_ERROR, framer.state()); | |
| 4566 EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME, framer.spdy_framer_error()) | |
| 4567 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 4568 } | |
| 4569 | |
| 4570 // Tests handling of PRIORITY frames. | |
| 4571 TEST_P(SpdyFramerTest, ReadPriority) { | |
| 4572 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 4573 SpdyPriorityIR priority(3, 1, 256, false); | |
| 4574 SpdySerializedFrame frame(framer.SerializePriority(priority)); | |
| 4575 if (use_output_) { | |
| 4576 output_.Reset(); | |
| 4577 ASSERT_TRUE(framer.SerializePriority(priority, &output_)); | |
| 4578 frame = SpdySerializedFrame(output_.Begin(), output_.Size(), false); | |
| 4579 } | |
| 4580 testing::StrictMock<test::MockSpdyFramerVisitor> visitor; | |
| 4581 framer.set_visitor(&visitor); | |
| 4582 EXPECT_CALL(visitor, OnPriority(3, 1, 256, false)); | |
| 4583 framer.ProcessInput(frame.data(), frame.size()); | |
| 4584 | |
| 4585 EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 4586 EXPECT_EQ(SpdyFramer::SPDY_NO_ERROR, framer.spdy_framer_error()) | |
| 4587 << SpdyFramer::SpdyFramerErrorToString(framer.spdy_framer_error()); | |
| 4588 // TODO(mlavan): once we actually maintain a priority tree, | |
| 4589 // check that state is adjusted correctly. | |
| 4590 } | |
| 4591 | |
| 4592 // Tests handling of PRIORITY frame with incorrect size. | |
| 4593 TEST_P(SpdyFramerTest, ReadIncorrectlySizedPriority) { | |
| 4594 // PRIORITY frame of size 4, which isn't correct. | |
| 4595 const unsigned char kFrameData[] = { | |
| 4596 0x00, 0x00, 0x04, // Length: 4 | |
| 4597 0x02, // Type: PRIORITY | |
| 4598 0x00, // Flags: none | |
| 4599 0x00, 0x00, 0x00, 0x03, // Stream: 3 | |
| 4600 0x00, 0x00, 0x00, 0x01, // Priority (Truncated) | |
| 4601 }; | |
| 4602 | |
| 4603 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 4604 visitor.SimulateInFramer(kFrameData, sizeof(kFrameData)); | |
| 4605 | |
| 4606 EXPECT_EQ(SpdyFramer::SPDY_ERROR, visitor.framer_.state()); | |
| 4607 EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_SIZE, | |
| 4608 visitor.framer_.spdy_framer_error()) | |
| 4609 << SpdyFramer::SpdyFramerErrorToString( | |
| 4610 visitor.framer_.spdy_framer_error()); | |
| 4611 } | |
| 4612 | |
| 4613 // Tests handling of PING frame with incorrect size. | |
| 4614 TEST_P(SpdyFramerTest, ReadIncorrectlySizedPing) { | |
| 4615 // PING frame of size 4, which isn't correct. | |
| 4616 const unsigned char kFrameData[] = { | |
| 4617 0x00, 0x00, 0x04, // Length: 4 | |
| 4618 0x06, // Type: PING | |
| 4619 0x00, // Flags: none | |
| 4620 0x00, 0x00, 0x00, 0x00, // Stream: 0 | |
| 4621 0x00, 0x00, 0x00, 0x01, // Ping (Truncated) | |
| 4622 }; | |
| 4623 | |
| 4624 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 4625 visitor.SimulateInFramer(kFrameData, sizeof(kFrameData)); | |
| 4626 | |
| 4627 EXPECT_EQ(SpdyFramer::SPDY_ERROR, visitor.framer_.state()); | |
| 4628 EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_SIZE, | |
| 4629 visitor.framer_.spdy_framer_error()) | |
| 4630 << SpdyFramer::SpdyFramerErrorToString( | |
| 4631 visitor.framer_.spdy_framer_error()); | |
| 4632 } | |
| 4633 | |
| 4634 // Tests handling of WINDOW_UPDATE frame with incorrect size. | |
| 4635 TEST_P(SpdyFramerTest, ReadIncorrectlySizedWindowUpdate) { | |
| 4636 // WINDOW_UPDATE frame of size 3, which isn't correct. | |
| 4637 const unsigned char kFrameData[] = { | |
| 4638 0x00, 0x00, 0x03, // Length: 3 | |
| 4639 0x08, // Type: WINDOW_UPDATE | |
| 4640 0x00, // Flags: none | |
| 4641 0x00, 0x00, 0x00, 0x03, // Stream: 3 | |
| 4642 0x00, 0x00, 0x01, // WindowUpdate (Truncated) | |
| 4643 }; | |
| 4644 | |
| 4645 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 4646 visitor.SimulateInFramer(kFrameData, sizeof(kFrameData)); | |
| 4647 | |
| 4648 EXPECT_EQ(SpdyFramer::SPDY_ERROR, visitor.framer_.state()); | |
| 4649 EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_SIZE, | |
| 4650 visitor.framer_.spdy_framer_error()) | |
| 4651 << SpdyFramer::SpdyFramerErrorToString( | |
| 4652 visitor.framer_.spdy_framer_error()); | |
| 4653 } | |
| 4654 | |
| 4655 // Tests handling of RST_STREAM frame with incorrect size. | |
| 4656 TEST_P(SpdyFramerTest, ReadIncorrectlySizedRstStream) { | |
| 4657 // RST_STREAM frame of size 3, which isn't correct. | |
| 4658 const unsigned char kFrameData[] = { | |
| 4659 0x00, 0x00, 0x03, // Length: 3 | |
| 4660 0x03, // Type: RST_STREAM | |
| 4661 0x00, // Flags: none | |
| 4662 0x00, 0x00, 0x00, 0x03, // Stream: 3 | |
| 4663 0x00, 0x00, 0x01, // RstStream (Truncated) | |
| 4664 }; | |
| 4665 | |
| 4666 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 4667 visitor.SimulateInFramer(kFrameData, sizeof(kFrameData)); | |
| 4668 | |
| 4669 EXPECT_EQ(SpdyFramer::SPDY_ERROR, visitor.framer_.state()); | |
| 4670 EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_SIZE, | |
| 4671 visitor.framer_.spdy_framer_error()) | |
| 4672 << SpdyFramer::SpdyFramerErrorToString( | |
| 4673 visitor.framer_.spdy_framer_error()); | |
| 4674 } | |
| 4675 | |
| 4676 // Regression test for https://crbug.com/548674: | |
| 4677 // RST_STREAM with payload must not be accepted. | |
| 4678 TEST_P(SpdyFramerTest, ReadInvalidRstStreamWithPayload) { | |
| 4679 const unsigned char kFrameData[] = { | |
| 4680 0x00, 0x00, 0x07, // Length: 7 | |
| 4681 0x03, // Type: RST_STREAM | |
| 4682 0x00, // Flags: none | |
| 4683 0x00, 0x00, 0x00, 0x01, // Stream: 1 | |
| 4684 0x00, 0x00, 0x00, 0x00, // Error: NO_ERROR | |
| 4685 'f', 'o', 'o' // Payload: "foo" | |
| 4686 }; | |
| 4687 | |
| 4688 TestSpdyVisitor visitor(SpdyFramer::DISABLE_COMPRESSION); | |
| 4689 visitor.SimulateInFramer(kFrameData, sizeof(kFrameData)); | |
| 4690 | |
| 4691 EXPECT_EQ(SpdyFramer::SPDY_ERROR, visitor.framer_.state()); | |
| 4692 EXPECT_EQ(SpdyFramer::SPDY_INVALID_CONTROL_FRAME_SIZE, | |
| 4693 visitor.framer_.spdy_framer_error()) | |
| 4694 << SpdyFramer::SpdyFramerErrorToString( | |
| 4695 visitor.framer_.spdy_framer_error()); | |
| 4696 } | |
| 4697 | |
| 4698 // Test that SpdyFramer processes, by default, all passed input in one call | |
| 4699 // to ProcessInput (i.e. will not be calling set_process_single_input_frame()). | |
| 4700 TEST_P(SpdyFramerTest, ProcessAllInput) { | |
| 4701 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 4702 auto visitor = | |
| 4703 base::MakeUnique<TestSpdyVisitor>(SpdyFramer::DISABLE_COMPRESSION); | |
| 4704 framer.set_visitor(visitor.get()); | |
| 4705 | |
| 4706 // Create two input frames. | |
| 4707 SpdyHeadersIR headers(1); | |
| 4708 headers.SetHeader("alpha", "beta"); | |
| 4709 headers.SetHeader("gamma", "charlie"); | |
| 4710 headers.SetHeader("cookie", "key1=value1; key2=value2"); | |
| 4711 SpdySerializedFrame headers_frame(SpdyFramerPeer::SerializeHeaders( | |
| 4712 &framer, headers, use_output_ ? &output_ : nullptr)); | |
| 4713 | |
| 4714 const char four_score[] = "Four score and seven years ago"; | |
| 4715 SpdyDataIR four_score_ir(1, four_score); | |
| 4716 SpdySerializedFrame four_score_frame(framer.SerializeData(four_score_ir)); | |
| 4717 | |
| 4718 // Put them in a single buffer (new variables here to make it easy to | |
| 4719 // change the order and type of frames). | |
| 4720 SpdySerializedFrame frame1 = std::move(headers_frame); | |
| 4721 SpdySerializedFrame frame2 = std::move(four_score_frame); | |
| 4722 | |
| 4723 const size_t frame1_size = frame1.size(); | |
| 4724 const size_t frame2_size = frame2.size(); | |
| 4725 | |
| 4726 VLOG(1) << "frame1_size = " << frame1_size; | |
| 4727 VLOG(1) << "frame2_size = " << frame2_size; | |
| 4728 | |
| 4729 SpdyString input_buffer; | |
| 4730 input_buffer.append(frame1.data(), frame1_size); | |
| 4731 input_buffer.append(frame2.data(), frame2_size); | |
| 4732 | |
| 4733 const char* buf = input_buffer.data(); | |
| 4734 const size_t buf_size = input_buffer.size(); | |
| 4735 | |
| 4736 VLOG(1) << "buf_size = " << buf_size; | |
| 4737 | |
| 4738 size_t processed = framer.ProcessInput(buf, buf_size); | |
| 4739 EXPECT_EQ(buf_size, processed); | |
| 4740 EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 4741 EXPECT_EQ(1, visitor->headers_frame_count_); | |
| 4742 EXPECT_EQ(1, visitor->data_frame_count_); | |
| 4743 EXPECT_EQ(strlen(four_score), static_cast<unsigned>(visitor->data_bytes_)); | |
| 4744 } | |
| 4745 | |
| 4746 // Test that SpdyFramer stops after processing a full frame if | |
| 4747 // process_single_input_frame is set. Input to ProcessInput has two frames, but | |
| 4748 // only processes the first when we give it the first frame split at any point, | |
| 4749 // or give it more than one frame in the input buffer. | |
| 4750 TEST_P(SpdyFramerTest, ProcessAtMostOneFrame) { | |
| 4751 SpdyFramer framer(SpdyFramer::ENABLE_COMPRESSION); | |
| 4752 framer.set_process_single_input_frame(true); | |
| 4753 | |
| 4754 // Create two input frames. | |
| 4755 const char four_score[] = "Four score and ..."; | |
| 4756 SpdyDataIR four_score_ir(1, four_score); | |
| 4757 SpdySerializedFrame four_score_frame(framer.SerializeData(four_score_ir)); | |
| 4758 | |
| 4759 SpdyHeadersIR headers(2); | |
| 4760 headers.SetHeader("alpha", "beta"); | |
| 4761 headers.SetHeader("gamma", "charlie"); | |
| 4762 headers.SetHeader("cookie", "key1=value1; key2=value2"); | |
| 4763 SpdySerializedFrame headers_frame(SpdyFramerPeer::SerializeHeaders( | |
| 4764 &framer, headers, use_output_ ? &output_ : nullptr)); | |
| 4765 | |
| 4766 // Put them in a single buffer (new variables here to make it easy to | |
| 4767 // change the order and type of frames). | |
| 4768 SpdySerializedFrame frame1 = std::move(four_score_frame); | |
| 4769 SpdySerializedFrame frame2 = std::move(headers_frame); | |
| 4770 | |
| 4771 const size_t frame1_size = frame1.size(); | |
| 4772 const size_t frame2_size = frame2.size(); | |
| 4773 | |
| 4774 VLOG(1) << "frame1_size = " << frame1_size; | |
| 4775 VLOG(1) << "frame2_size = " << frame2_size; | |
| 4776 | |
| 4777 SpdyString input_buffer; | |
| 4778 input_buffer.append(frame1.data(), frame1_size); | |
| 4779 input_buffer.append(frame2.data(), frame2_size); | |
| 4780 | |
| 4781 const char* buf = input_buffer.data(); | |
| 4782 const size_t buf_size = input_buffer.size(); | |
| 4783 | |
| 4784 VLOG(1) << "buf_size = " << buf_size; | |
| 4785 | |
| 4786 for (size_t first_size = 0; first_size <= buf_size; ++first_size) { | |
| 4787 VLOG(1) << "first_size = " << first_size; | |
| 4788 auto visitor = | |
| 4789 base::MakeUnique<TestSpdyVisitor>(SpdyFramer::DISABLE_COMPRESSION); | |
| 4790 framer.set_visitor(visitor.get()); | |
| 4791 | |
| 4792 EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 4793 | |
| 4794 size_t processed_first = framer.ProcessInput(buf, first_size); | |
| 4795 if (first_size < frame1_size) { | |
| 4796 EXPECT_EQ(first_size, processed_first); | |
| 4797 | |
| 4798 if (first_size == 0) { | |
| 4799 EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 4800 } else { | |
| 4801 EXPECT_NE(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 4802 } | |
| 4803 | |
| 4804 const char* rest = buf + processed_first; | |
| 4805 const size_t remaining = buf_size - processed_first; | |
| 4806 VLOG(1) << "remaining = " << remaining; | |
| 4807 | |
| 4808 size_t processed_second = framer.ProcessInput(rest, remaining); | |
| 4809 | |
| 4810 // Redundant tests just to make it easier to think about. | |
| 4811 EXPECT_EQ(frame1_size - processed_first, processed_second); | |
| 4812 size_t processed_total = processed_first + processed_second; | |
| 4813 EXPECT_EQ(frame1_size, processed_total); | |
| 4814 } else { | |
| 4815 EXPECT_EQ(frame1_size, processed_first); | |
| 4816 } | |
| 4817 | |
| 4818 EXPECT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME, framer.state()); | |
| 4819 | |
| 4820 // At this point should have processed the entirety of the first frame, | |
| 4821 // and none of the second frame. | |
| 4822 | |
| 4823 EXPECT_EQ(1, visitor->data_frame_count_); | |
| 4824 EXPECT_EQ(strlen(four_score), static_cast<unsigned>(visitor->data_bytes_)); | |
| 4825 EXPECT_EQ(0, visitor->headers_frame_count_); | |
| 4826 } | |
| 4827 } | |
| 4828 | |
| 4829 } // namespace test | |
| 4830 | |
| 4831 } // namespace net | |
| OLD | NEW |