1// Copyright (c) 2011 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 <algorithm> 6#include <iostream> 7 8#include "base/memory/scoped_ptr.h" 9#include "net/spdy/spdy_framer.h" 10#include "net/spdy/spdy_protocol.h" 11#include "net/spdy/spdy_frame_builder.h" 12#include "testing/platform_test.h" 13 14namespace spdy { 15 16namespace test { 17 18std::string HexDumpWithMarks(const unsigned char* data, int length, 19 const bool* marks, int mark_length) { 20 static const char kHexChars[] = "0123456789ABCDEF"; 21 static const int kColumns = 4; 22 23 std::string hex; 24 for (const unsigned char* row = data; length > 0; 25 row += kColumns, length -= kColumns) { 26 for (const unsigned char *p = row; p < row + 4; ++p) { 27 if (p < row + length) { 28 const bool mark = 29 (marks && (p - data) < mark_length && marks[p - data]); 30 hex += mark ? '*' : ' '; 31 hex += kHexChars[(*p & 0xf0) >> 4]; 32 hex += kHexChars[*p & 0x0f]; 33 hex += mark ? '*' : ' '; 34 } else { 35 hex += " "; 36 } 37 } 38 hex = hex + " "; 39 40 for (const unsigned char *p = row; p < row + 4 && p < row + length; ++p) 41 hex += (*p >= 0x20 && *p <= 0x7f) ? (*p) : '.'; 42 43 hex = hex + '\n'; 44 } 45 return hex; 46} 47 48void CompareCharArraysWithHexError( 49 const std::string& description, 50 const unsigned char* actual, 51 const int actual_len, 52 const unsigned char* expected, 53 const int expected_len) { 54 const int min_len = actual_len > expected_len ? expected_len : actual_len; 55 const int max_len = actual_len > expected_len ? actual_len : expected_len; 56 scoped_array<bool> marks(new bool[max_len]); 57 bool identical = (actual_len == expected_len); 58 for (int i = 0; i < min_len; ++i) { 59 if (actual[i] != expected[i]) { 60 marks[i] = true; 61 identical = false; 62 } else { 63 marks[i] = false; 64 } 65 } 66 for (int i = min_len; i < max_len; ++i) { 67 marks[i] = true; 68 } 69 if (identical) return; 70 ADD_FAILURE() 71 << "Description:\n" 72 << description 73 << "\n\nExpected:\n" 74 << HexDumpWithMarks(expected, expected_len, marks.get(), max_len) 75 << "\nActual:\n" 76 << HexDumpWithMarks(actual, actual_len, marks.get(), max_len); 77} 78 79void FramerSetEnableCompressionHelper(SpdyFramer* framer, bool compress) { 80 framer->set_enable_compression(compress); 81} 82 83class TestSpdyVisitor : public SpdyFramerVisitorInterface { 84 public: 85 TestSpdyVisitor() 86 : error_count_(0), 87 syn_frame_count_(0), 88 syn_reply_frame_count_(0), 89 headers_frame_count_(0), 90 data_bytes_(0), 91 fin_frame_count_(0), 92 fin_flag_count_(0), 93 zero_length_data_frame_count_(0) { 94 } 95 96 void OnError(SpdyFramer* f) { 97 error_count_++; 98 } 99 100 void OnStreamFrameData(SpdyStreamId stream_id, 101 const char* data, 102 size_t len) { 103 if (len == 0) 104 ++zero_length_data_frame_count_; 105 106 data_bytes_ += len; 107 std::cerr << "OnStreamFrameData(" << stream_id << ", \""; 108 if (len > 0) { 109 for (size_t i = 0 ; i < len; ++i) { 110 std::cerr << std::hex << (0xFF & (unsigned int)data[i]) << std::dec; 111 } 112 } 113 std::cerr << "\", " << len << ")\n"; 114 } 115 116 void OnControl(const SpdyControlFrame* frame) { 117 SpdyHeaderBlock headers; 118 bool parsed_headers = false; 119 switch (frame->type()) { 120 case SYN_STREAM: 121 parsed_headers = framer_.ParseHeaderBlock(frame, &headers); 122 DCHECK(parsed_headers); 123 syn_frame_count_++; 124 break; 125 case SYN_REPLY: 126 parsed_headers = framer_.ParseHeaderBlock(frame, &headers); 127 DCHECK(parsed_headers); 128 syn_reply_frame_count_++; 129 break; 130 case RST_STREAM: 131 fin_frame_count_++; 132 break; 133 case HEADERS: 134 parsed_headers = framer_.ParseHeaderBlock(frame, &headers); 135 DCHECK(parsed_headers); 136 headers_frame_count_++; 137 break; 138 default: 139 DCHECK(false); // Error! 140 } 141 if (frame->flags() & CONTROL_FLAG_FIN) 142 ++fin_flag_count_; 143 } 144 145 bool OnControlFrameHeaderData(SpdyStreamId stream_id, 146 const char* header_data, 147 size_t len) { 148 DCHECK(false); 149 return false; 150 } 151 152 void OnDataFrameHeader(const SpdyDataFrame* frame) { 153 DCHECK(false); 154 } 155 156 // Convenience function which runs a framer simulation with particular input. 157 void SimulateInFramer(const unsigned char* input, size_t size) { 158 framer_.set_enable_compression(false); 159 framer_.set_visitor(this); 160 size_t input_remaining = size; 161 const char* input_ptr = reinterpret_cast<const char*>(input); 162 while (input_remaining > 0 && 163 framer_.error_code() == SpdyFramer::SPDY_NO_ERROR) { 164 // To make the tests more interesting, we feed random (amd small) chunks 165 // into the framer. This simulates getting strange-sized reads from 166 // the socket. 167 const size_t kMaxReadSize = 32; 168 size_t bytes_read = 169 (rand() % std::min(input_remaining, kMaxReadSize)) + 1; 170 size_t bytes_processed = framer_.ProcessInput(input_ptr, bytes_read); 171 input_remaining -= bytes_processed; 172 input_ptr += bytes_processed; 173 if (framer_.state() == SpdyFramer::SPDY_DONE) 174 framer_.Reset(); 175 } 176 } 177 178 SpdyFramer framer_; 179 // Counters from the visitor callbacks. 180 int error_count_; 181 int syn_frame_count_; 182 int syn_reply_frame_count_; 183 int headers_frame_count_; 184 int data_bytes_; 185 int fin_frame_count_; // The count of RST_STREAM type frames received. 186 int fin_flag_count_; // The count of frames with the FIN flag set. 187 int zero_length_data_frame_count_; // The count of zero-length data frames. 188}; 189 190} // namespace test 191 192} // namespace spdy 193 194using spdy::SpdyControlFlags; 195using spdy::SpdyControlFrame; 196using spdy::SpdyDataFrame; 197using spdy::SpdyFrame; 198using spdy::SpdyFrameBuilder; 199using spdy::SpdyFramer; 200using spdy::SpdyHeaderBlock; 201using spdy::SpdySynStreamControlFrame; 202using spdy::kControlFlagMask; 203using spdy::CONTROL_FLAG_NONE; 204using spdy::DATA_FLAG_COMPRESSED; 205using spdy::DATA_FLAG_FIN; 206using spdy::SYN_STREAM; 207using spdy::test::CompareCharArraysWithHexError; 208using spdy::test::FramerSetEnableCompressionHelper; 209using spdy::test::TestSpdyVisitor; 210 211namespace spdy { 212 213class SpdyFramerTest : public PlatformTest { 214 public: 215 virtual void TearDown() {} 216 217 protected: 218 void CompareFrame(const std::string& description, 219 const SpdyFrame& actual_frame, 220 const unsigned char* expected, 221 const int expected_len) { 222 const unsigned char* actual = 223 reinterpret_cast<const unsigned char*>(actual_frame.data()); 224 int actual_len = actual_frame.length() + SpdyFrame::size(); 225 CompareCharArraysWithHexError( 226 description, actual, actual_len, expected, expected_len); 227 } 228}; 229 230// Test that we can encode and decode a SpdyHeaderBlock. 231TEST_F(SpdyFramerTest, HeaderBlock) { 232 SpdyHeaderBlock headers; 233 headers["alpha"] = "beta"; 234 headers["gamma"] = "charlie"; 235 SpdyFramer framer; 236 237 // Encode the header block into a SynStream frame. 238 scoped_ptr<SpdySynStreamControlFrame> frame( 239 framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, true, &headers)); 240 EXPECT_TRUE(frame.get() != NULL); 241 242 SpdyHeaderBlock new_headers; 243 EXPECT_TRUE(framer.ParseHeaderBlock(frame.get(), &new_headers)); 244 245 EXPECT_EQ(headers.size(), new_headers.size()); 246 EXPECT_EQ(headers["alpha"], new_headers["alpha"]); 247 EXPECT_EQ(headers["gamma"], new_headers["gamma"]); 248} 249 250TEST_F(SpdyFramerTest, OutOfOrderHeaders) { 251 SpdyFrameBuilder frame; 252 253 frame.WriteUInt16(kControlFlagMask | 1); 254 frame.WriteUInt16(SYN_STREAM); 255 frame.WriteUInt32(0); // Placeholder for the length. 256 frame.WriteUInt32(3); // stream_id 257 frame.WriteUInt32(0); // associated stream id 258 frame.WriteUInt16(0); // Priority. 259 260 frame.WriteUInt16(2); // Number of headers. 261 SpdyHeaderBlock::iterator it; 262 frame.WriteString("gamma"); 263 frame.WriteString("gamma"); 264 frame.WriteString("alpha"); 265 frame.WriteString("alpha"); 266 // write the length 267 frame.WriteUInt32ToOffset(4, frame.length() - SpdyFrame::size()); 268 269 SpdyHeaderBlock new_headers; 270 scoped_ptr<SpdyFrame> control_frame(frame.take()); 271 SpdyFramer framer; 272 FramerSetEnableCompressionHelper(&framer, false); 273 EXPECT_TRUE(framer.ParseHeaderBlock(control_frame.get(), &new_headers)); 274} 275 276TEST_F(SpdyFramerTest, WrongNumberOfHeaders) { 277 SpdyFrameBuilder frame1; 278 SpdyFrameBuilder frame2; 279 280 // a frame with smaller number of actual headers 281 frame1.WriteUInt16(kControlFlagMask | 1); 282 frame1.WriteUInt16(SYN_STREAM); 283 frame1.WriteUInt32(0); // Placeholder for the length. 284 frame1.WriteUInt32(3); // stream_id 285 frame1.WriteUInt16(0); // Priority. 286 287 frame1.WriteUInt16(1); // Wrong number of headers (underflow) 288 frame1.WriteString("gamma"); 289 frame1.WriteString("gamma"); 290 frame1.WriteString("alpha"); 291 frame1.WriteString("alpha"); 292 // write the length 293 frame1.WriteUInt32ToOffset(4, frame1.length() - SpdyFrame::size()); 294 295 // a frame with larger number of actual headers 296 frame2.WriteUInt16(kControlFlagMask | 1); 297 frame2.WriteUInt16(SYN_STREAM); 298 frame2.WriteUInt32(0); // Placeholder for the length. 299 frame2.WriteUInt32(3); // stream_id 300 frame2.WriteUInt16(0); // Priority. 301 302 frame2.WriteUInt16(100); // Wrong number of headers (overflow) 303 frame2.WriteString("gamma"); 304 frame2.WriteString("gamma"); 305 frame2.WriteString("alpha"); 306 frame2.WriteString("alpha"); 307 // write the length 308 frame2.WriteUInt32ToOffset(4, frame2.length() - SpdyFrame::size()); 309 310 SpdyHeaderBlock new_headers; 311 scoped_ptr<SpdyFrame> syn_frame1(frame1.take()); 312 scoped_ptr<SpdyFrame> syn_frame2(frame2.take()); 313 SpdyFramer framer; 314 FramerSetEnableCompressionHelper(&framer, false); 315 EXPECT_FALSE(framer.ParseHeaderBlock(syn_frame1.get(), &new_headers)); 316 EXPECT_FALSE(framer.ParseHeaderBlock(syn_frame2.get(), &new_headers)); 317} 318 319TEST_F(SpdyFramerTest, DuplicateHeader) { 320 SpdyFrameBuilder frame; 321 322 frame.WriteUInt16(kControlFlagMask | 1); 323 frame.WriteUInt16(SYN_STREAM); 324 frame.WriteUInt32(0); // Placeholder for the length. 325 frame.WriteUInt32(3); // stream_id 326 frame.WriteUInt32(0); // associated stream id 327 frame.WriteUInt16(0); // Priority. 328 329 frame.WriteUInt16(2); // Number of headers. 330 SpdyHeaderBlock::iterator it; 331 frame.WriteString("name"); 332 frame.WriteString("value1"); 333 frame.WriteString("name"); 334 frame.WriteString("value2"); 335 // write the length 336 frame.WriteUInt32ToOffset(4, frame.length() - SpdyFrame::size()); 337 338 SpdyHeaderBlock new_headers; 339 scoped_ptr<SpdyFrame> control_frame(frame.take()); 340 SpdyFramer framer; 341 FramerSetEnableCompressionHelper(&framer, false); 342 // This should fail because duplicate headers are verboten by the spec. 343 EXPECT_FALSE(framer.ParseHeaderBlock(control_frame.get(), &new_headers)); 344} 345 346TEST_F(SpdyFramerTest, MultiValueHeader) { 347 SpdyFrameBuilder frame; 348 349 frame.WriteUInt16(kControlFlagMask | 1); 350 frame.WriteUInt16(SYN_STREAM); 351 frame.WriteUInt32(0); // Placeholder for the length. 352 frame.WriteUInt32(3); // stream_id 353 frame.WriteUInt32(0); // associated stream id 354 frame.WriteUInt16(0); // Priority. 355 356 frame.WriteUInt16(1); // Number of headers. 357 SpdyHeaderBlock::iterator it; 358 frame.WriteString("name"); 359 std::string value("value1\0value2"); 360 frame.WriteString(value); 361 // write the length 362 frame.WriteUInt32ToOffset(4, frame.length() - SpdyFrame::size()); 363 364 SpdyHeaderBlock new_headers; 365 scoped_ptr<SpdyFrame> control_frame(frame.take()); 366 SpdyFramer framer; 367 FramerSetEnableCompressionHelper(&framer, false); 368 EXPECT_TRUE(framer.ParseHeaderBlock(control_frame.get(), &new_headers)); 369 EXPECT_TRUE(new_headers.find("name") != new_headers.end()); 370 EXPECT_EQ(value, new_headers.find("name")->second); 371} 372 373TEST_F(SpdyFramerTest, ZeroLengthHeader) { 374 SpdyHeaderBlock header1; 375 SpdyHeaderBlock header2; 376 SpdyHeaderBlock header3; 377 378 header1[""] = "value2"; 379 header2["name3"] = ""; 380 header3[""] = ""; 381 382 SpdyFramer framer; 383 SpdyHeaderBlock parsed_headers; 384 385 scoped_ptr<SpdySynStreamControlFrame> frame1( 386 framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, true, &header1)); 387 EXPECT_TRUE(frame1.get() != NULL); 388 EXPECT_FALSE(framer.ParseHeaderBlock(frame1.get(), &parsed_headers)); 389 390 scoped_ptr<SpdySynStreamControlFrame> frame2( 391 framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, true, &header2)); 392 EXPECT_TRUE(frame2.get() != NULL); 393 EXPECT_FALSE(framer.ParseHeaderBlock(frame2.get(), &parsed_headers)); 394 395 scoped_ptr<SpdySynStreamControlFrame> frame3( 396 framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, true, &header3)); 397 EXPECT_TRUE(frame3.get() != NULL); 398 EXPECT_FALSE(framer.ParseHeaderBlock(frame3.get(), &parsed_headers)); 399} 400 401TEST_F(SpdyFramerTest, BasicCompression) { 402 SpdyHeaderBlock headers; 403 headers["server"] = "SpdyServer 1.0"; 404 headers["date"] = "Mon 12 Jan 2009 12:12:12 PST"; 405 headers["status"] = "200"; 406 headers["version"] = "HTTP/1.1"; 407 headers["content-type"] = "text/html"; 408 headers["content-length"] = "12"; 409 410 SpdyFramer framer; 411 FramerSetEnableCompressionHelper(&framer, true); 412 scoped_ptr<SpdySynStreamControlFrame> 413 frame1(framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, true, 414 &headers)); 415 scoped_ptr<SpdySynStreamControlFrame> 416 frame2(framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, true, 417 &headers)); 418 419 // Expect the second frame to be more compact than the first. 420 EXPECT_LE(frame2->length(), frame1->length()); 421 422 // Decompress the first frame 423 scoped_ptr<SpdyFrame> frame3(framer.DecompressFrame(*frame1.get())); 424 425 // Decompress the second frame 426 scoped_ptr<SpdyFrame> frame4(framer.DecompressFrame(*frame2.get())); 427 428 // Expect frames 3 & 4 to be the same. 429 EXPECT_EQ(0, 430 memcmp(frame3->data(), frame4->data(), 431 SpdyFrame::size() + frame3->length())); 432} 433 434TEST_F(SpdyFramerTest, DecompressUncompressedFrame) { 435 SpdyHeaderBlock headers; 436 headers["server"] = "SpdyServer 1.0"; 437 headers["date"] = "Mon 12 Jan 2009 12:12:12 PST"; 438 headers["status"] = "200"; 439 headers["version"] = "HTTP/1.1"; 440 headers["content-type"] = "text/html"; 441 headers["content-length"] = "12"; 442 443 SpdyFramer framer; 444 FramerSetEnableCompressionHelper(&framer, true); 445 scoped_ptr<SpdySynStreamControlFrame> 446 frame1(framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, false, 447 &headers)); 448 449 // Decompress the frame 450 scoped_ptr<SpdyFrame> frame2(framer.DecompressFrame(*frame1.get())); 451 452 EXPECT_EQ(NULL, frame2.get()); 453} 454 455TEST_F(SpdyFramerTest, Basic) { 456 const unsigned char input[] = { 457 0x80, 0x02, 0x00, 0x01, // SYN Stream #1 458 0x00, 0x00, 0x00, 0x14, 459 0x00, 0x00, 0x00, 0x01, 460 0x00, 0x00, 0x00, 0x00, 461 0x00, 0x00, 0x00, 0x01, 462 0x00, 0x02, 'h', 'h', 463 0x00, 0x02, 'v', 'v', 464 465 0x80, 0x02, 0x00, 0x08, // HEADERS on Stream #1 466 0x00, 0x00, 0x00, 0x18, 467 0x00, 0x00, 0x00, 0x01, 468 0x00, 0x00, 0x00, 0x02, 469 0x00, 0x02, 'h', '2', 470 0x00, 0x02, 'v', '2', 471 0x00, 0x02, 'h', '3', 472 0x00, 0x02, 'v', '3', 473 474 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1 475 0x00, 0x00, 0x00, 0x0c, 476 0xde, 0xad, 0xbe, 0xef, 477 0xde, 0xad, 0xbe, 0xef, 478 0xde, 0xad, 0xbe, 0xef, 479 480 0x80, 0x02, 0x00, 0x01, // SYN Stream #3 481 0x00, 0x00, 0x00, 0x0c, 482 0x00, 0x00, 0x00, 0x03, 483 0x00, 0x00, 0x00, 0x00, 484 0x00, 0x00, 0x00, 0x00, 485 486 0x00, 0x00, 0x00, 0x03, // DATA on Stream #3 487 0x00, 0x00, 0x00, 0x08, 488 0xde, 0xad, 0xbe, 0xef, 489 0xde, 0xad, 0xbe, 0xef, 490 491 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1 492 0x00, 0x00, 0x00, 0x04, 493 0xde, 0xad, 0xbe, 0xef, 494 495 0x80, 0x02, 0x00, 0x03, // RST_STREAM on Stream #1 496 0x00, 0x00, 0x00, 0x08, 497 0x00, 0x00, 0x00, 0x01, 498 0x00, 0x00, 0x00, 0x00, 499 500 0x00, 0x00, 0x00, 0x03, // DATA on Stream #3 501 0x00, 0x00, 0x00, 0x00, 502 503 0x80, 0x02, 0x00, 0x03, // RST_STREAM on Stream #3 504 0x00, 0x00, 0x00, 0x08, 505 0x00, 0x00, 0x00, 0x03, 506 0x00, 0x00, 0x00, 0x00, 507 }; 508 509 TestSpdyVisitor visitor; 510 visitor.SimulateInFramer(input, sizeof(input)); 511 512 EXPECT_EQ(0, visitor.error_count_); 513 EXPECT_EQ(2, visitor.syn_frame_count_); 514 EXPECT_EQ(0, visitor.syn_reply_frame_count_); 515 EXPECT_EQ(1, visitor.headers_frame_count_); 516 EXPECT_EQ(24, visitor.data_bytes_); 517 EXPECT_EQ(2, visitor.fin_frame_count_); 518 EXPECT_EQ(0, visitor.fin_flag_count_); 519 EXPECT_EQ(0, visitor.zero_length_data_frame_count_); 520} 521 522// Test that the FIN flag on a data frame signifies EOF. 523TEST_F(SpdyFramerTest, FinOnDataFrame) { 524 const unsigned char input[] = { 525 0x80, 0x02, 0x00, 0x01, // SYN Stream #1 526 0x00, 0x00, 0x00, 0x14, 527 0x00, 0x00, 0x00, 0x01, 528 0x00, 0x00, 0x00, 0x00, 529 0x00, 0x00, 0x00, 0x01, 530 0x00, 0x02, 'h', 'h', 531 0x00, 0x02, 'v', 'v', 532 533 0x80, 0x02, 0x00, 0x02, // SYN REPLY Stream #1 534 0x00, 0x00, 0x00, 0x10, 535 0x00, 0x00, 0x00, 0x01, 536 0x00, 0x00, 0x00, 0x01, 537 0x00, 0x02, 'a', 'a', 538 0x00, 0x02, 'b', 'b', 539 540 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1 541 0x00, 0x00, 0x00, 0x0c, 542 0xde, 0xad, 0xbe, 0xef, 543 0xde, 0xad, 0xbe, 0xef, 544 0xde, 0xad, 0xbe, 0xef, 545 546 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1, with EOF 547 0x01, 0x00, 0x00, 0x04, 548 0xde, 0xad, 0xbe, 0xef, 549 }; 550 551 TestSpdyVisitor visitor; 552 visitor.SimulateInFramer(input, sizeof(input)); 553 554 EXPECT_EQ(0, visitor.error_count_); 555 EXPECT_EQ(1, visitor.syn_frame_count_); 556 EXPECT_EQ(1, visitor.syn_reply_frame_count_); 557 EXPECT_EQ(0, visitor.headers_frame_count_); 558 EXPECT_EQ(16, visitor.data_bytes_); 559 EXPECT_EQ(0, visitor.fin_frame_count_); 560 EXPECT_EQ(0, visitor.fin_flag_count_); 561 EXPECT_EQ(1, visitor.zero_length_data_frame_count_); 562} 563 564// Test that the FIN flag on a SYN reply frame signifies EOF. 565TEST_F(SpdyFramerTest, FinOnSynReplyFrame) { 566 const unsigned char input[] = { 567 0x80, 0x02, 0x00, 0x01, // SYN Stream #1 568 0x00, 0x00, 0x00, 0x14, 569 0x00, 0x00, 0x00, 0x01, 570 0x00, 0x00, 0x00, 0x00, 571 0x00, 0x00, 0x00, 0x01, 572 0x00, 0x02, 'h', 'h', 573 0x00, 0x02, 'v', 'v', 574 575 0x80, 0x02, 0x00, 0x02, // SYN REPLY Stream #1 576 0x01, 0x00, 0x00, 0x10, 577 0x00, 0x00, 0x00, 0x01, 578 0x00, 0x00, 0x00, 0x01, 579 0x00, 0x02, 'a', 'a', 580 0x00, 0x02, 'b', 'b', 581 }; 582 583 TestSpdyVisitor visitor; 584 visitor.SimulateInFramer(input, sizeof(input)); 585 586 EXPECT_EQ(0, visitor.error_count_); 587 EXPECT_EQ(1, visitor.syn_frame_count_); 588 EXPECT_EQ(1, visitor.syn_reply_frame_count_); 589 EXPECT_EQ(0, visitor.headers_frame_count_); 590 EXPECT_EQ(0, visitor.data_bytes_); 591 EXPECT_EQ(0, visitor.fin_frame_count_); 592 EXPECT_EQ(1, visitor.fin_flag_count_); 593 EXPECT_EQ(1, visitor.zero_length_data_frame_count_); 594} 595 596// Basic compression & decompression 597TEST_F(SpdyFramerTest, DataCompression) { 598 SpdyFramer send_framer; 599 SpdyFramer recv_framer; 600 601 FramerSetEnableCompressionHelper(&send_framer, true); 602 FramerSetEnableCompressionHelper(&recv_framer, true); 603 604 // Mix up some SYNs and DATA frames since they use different compressors. 605 const char kHeader1[] = "header1"; 606 const char kHeader2[] = "header2"; 607 const char kHeader3[] = "header3"; 608 const char kValue1[] = "value1"; 609 const char kValue2[] = "value2"; 610 const char kValue3[] = "value3"; 611 612 // SYN_STREAM #1 613 SpdyHeaderBlock block; 614 block[kHeader1] = kValue1; 615 block[kHeader2] = kValue2; 616 SpdyControlFlags flags(CONTROL_FLAG_NONE); 617 scoped_ptr<spdy::SpdyFrame> syn_frame_1( 618 send_framer.CreateSynStream(1, 0, 0, flags, true, &block)); 619 EXPECT_TRUE(syn_frame_1.get() != NULL); 620 621 // DATA #1 622 const char bytes[] = "this is a test test test test test!"; 623 scoped_ptr<SpdyFrame> data_frame_1( 624 send_framer.CreateDataFrame(1, bytes, arraysize(bytes), 625 DATA_FLAG_COMPRESSED)); 626 EXPECT_TRUE(data_frame_1.get() != NULL); 627 628 // SYN_STREAM #2 629 block[kHeader3] = kValue3; 630 scoped_ptr<SpdyFrame> syn_frame_2( 631 send_framer.CreateSynStream(3, 0, 0, flags, true, &block)); 632 EXPECT_TRUE(syn_frame_2.get() != NULL); 633 634 // DATA #2 635 scoped_ptr<SpdyFrame> data_frame_2( 636 send_framer.CreateDataFrame(3, bytes, arraysize(bytes), 637 DATA_FLAG_COMPRESSED)); 638 EXPECT_TRUE(data_frame_2.get() != NULL); 639 640 // Now start decompressing 641 scoped_ptr<SpdyFrame> decompressed; 642 SpdyControlFrame* control_frame; 643 SpdyDataFrame* data_frame; 644 SpdyHeaderBlock decompressed_headers; 645 646 decompressed.reset(recv_framer.DuplicateFrame(*syn_frame_1.get())); 647 EXPECT_TRUE(decompressed.get() != NULL); 648 EXPECT_TRUE(decompressed->is_control_frame()); 649 control_frame = reinterpret_cast<SpdyControlFrame*>(decompressed.get()); 650 EXPECT_EQ(SYN_STREAM, control_frame->type()); 651 EXPECT_TRUE(recv_framer.ParseHeaderBlock( 652 control_frame, &decompressed_headers)); 653 EXPECT_EQ(2u, decompressed_headers.size()); 654 EXPECT_EQ(SYN_STREAM, control_frame->type()); 655 EXPECT_EQ(kValue1, decompressed_headers[kHeader1]); 656 EXPECT_EQ(kValue2, decompressed_headers[kHeader2]); 657 658 decompressed.reset(recv_framer.DecompressFrame(*data_frame_1.get())); 659 EXPECT_TRUE(decompressed.get() != NULL); 660 EXPECT_FALSE(decompressed->is_control_frame()); 661 data_frame = reinterpret_cast<SpdyDataFrame*>(decompressed.get()); 662 EXPECT_EQ(arraysize(bytes), data_frame->length()); 663 EXPECT_EQ(0, memcmp(data_frame->payload(), bytes, data_frame->length())); 664 665 decompressed.reset(recv_framer.DuplicateFrame(*syn_frame_2.get())); 666 EXPECT_TRUE(decompressed.get() != NULL); 667 EXPECT_TRUE(decompressed->is_control_frame()); 668 control_frame = reinterpret_cast<SpdyControlFrame*>(decompressed.get()); 669 EXPECT_EQ(control_frame->type(), SYN_STREAM); 670 decompressed_headers.clear(); 671 EXPECT_TRUE(recv_framer.ParseHeaderBlock( 672 control_frame, &decompressed_headers)); 673 EXPECT_EQ(3u, decompressed_headers.size()); 674 EXPECT_EQ(SYN_STREAM, control_frame->type()); 675 EXPECT_EQ(kValue1, decompressed_headers[kHeader1]); 676 EXPECT_EQ(kValue2, decompressed_headers[kHeader2]); 677 EXPECT_EQ(kValue3, decompressed_headers[kHeader3]); 678 679 decompressed.reset(recv_framer.DecompressFrame(*data_frame_2.get())); 680 EXPECT_TRUE(decompressed.get() != NULL); 681 EXPECT_FALSE(decompressed->is_control_frame()); 682 data_frame = reinterpret_cast<SpdyDataFrame*>(decompressed.get()); 683 EXPECT_EQ(arraysize(bytes), data_frame->length()); 684 EXPECT_EQ(0, memcmp(data_frame->payload(), bytes, data_frame->length())); 685 686 // We didn't close these streams, so the compressors should be active. 687 EXPECT_EQ(2, send_framer.num_stream_compressors()); 688 EXPECT_EQ(0, send_framer.num_stream_decompressors()); 689 EXPECT_EQ(0, recv_framer.num_stream_compressors()); 690 EXPECT_EQ(2, recv_framer.num_stream_decompressors()); 691} 692 693// Verify we don't leak when we leave streams unclosed 694TEST_F(SpdyFramerTest, UnclosedStreamDataCompressors) { 695 SpdyFramer send_framer; 696 697 FramerSetEnableCompressionHelper(&send_framer, false); 698 699 const char kHeader1[] = "header1"; 700 const char kHeader2[] = "header2"; 701 const char kValue1[] = "value1"; 702 const char kValue2[] = "value2"; 703 704 SpdyHeaderBlock block; 705 block[kHeader1] = kValue1; 706 block[kHeader2] = kValue2; 707 SpdyControlFlags flags(CONTROL_FLAG_NONE); 708 scoped_ptr<spdy::SpdyFrame> syn_frame( 709 send_framer.CreateSynStream(1, 0, 0, flags, true, &block)); 710 EXPECT_TRUE(syn_frame.get() != NULL); 711 712 const char bytes[] = "this is a test test test test test!"; 713 scoped_ptr<SpdyFrame> send_frame( 714 send_framer.CreateDataFrame(1, 715 bytes, 716 arraysize(bytes), 717 DATA_FLAG_FIN)); 718 EXPECT_TRUE(send_frame.get() != NULL); 719 720 // Run the inputs through the framer. 721 TestSpdyVisitor visitor; 722 const unsigned char* data; 723 data = reinterpret_cast<const unsigned char*>(syn_frame->data()); 724 visitor.SimulateInFramer(data, syn_frame->length() + SpdyFrame::size()); 725 data = reinterpret_cast<const unsigned char*>(send_frame->data()); 726 visitor.SimulateInFramer(data, send_frame->length() + SpdyFrame::size()); 727 728 EXPECT_EQ(0, visitor.error_count_); 729 EXPECT_EQ(1, visitor.syn_frame_count_); 730 EXPECT_EQ(0, visitor.syn_reply_frame_count_); 731 EXPECT_EQ(0, visitor.headers_frame_count_); 732 EXPECT_EQ(arraysize(bytes), static_cast<unsigned>(visitor.data_bytes_)); 733 EXPECT_EQ(0, visitor.fin_frame_count_); 734 EXPECT_EQ(0, visitor.fin_flag_count_); 735 EXPECT_EQ(1, visitor.zero_length_data_frame_count_); 736 737 // We closed the streams, so all compressors should be down. 738 EXPECT_EQ(0, visitor.framer_.num_stream_compressors()); 739 EXPECT_EQ(0, visitor.framer_.num_stream_decompressors()); 740 EXPECT_EQ(0, send_framer.num_stream_compressors()); 741 EXPECT_EQ(0, send_framer.num_stream_decompressors()); 742} 743 744TEST_F(SpdyFramerTest, CreateDataFrame) { 745 SpdyFramer framer; 746 747 { 748 const char kDescription[] = "'hello' data frame, no FIN"; 749 const unsigned char kFrameData[] = { 750 0x00, 0x00, 0x00, 0x01, 751 0x00, 0x00, 0x00, 0x05, 752 'h', 'e', 'l', 'l', 753 'o' 754 }; 755 scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( 756 1, "hello", 5, DATA_FLAG_NONE)); 757 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 758 } 759 760 { 761 const char kDescription[] = "Data frame with negative data byte, no FIN"; 762 const unsigned char kFrameData[] = { 763 0x00, 0x00, 0x00, 0x01, 764 0x00, 0x00, 0x00, 0x01, 765 0xff 766 }; 767 scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( 768 1, "\xff", 1, DATA_FLAG_NONE)); 769 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 770 } 771 772 { 773 const char kDescription[] = "'hello' data frame, with FIN"; 774 const unsigned char kFrameData[] = { 775 0x00, 0x00, 0x00, 0x01, 776 0x01, 0x00, 0x00, 0x05, 777 'h', 'e', 'l', 'l', 778 'o' 779 }; 780 scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( 781 1, "hello", 5, DATA_FLAG_FIN)); 782 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 783 } 784 785 { 786 const char kDescription[] = "Empty data frame"; 787 const unsigned char kFrameData[] = { 788 0x00, 0x00, 0x00, 0x01, 789 0x00, 0x00, 0x00, 0x00, 790 }; 791 scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( 792 1, "", 0, DATA_FLAG_NONE)); 793 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 794 } 795 796 { 797 const char kDescription[] = "Data frame with max stream ID"; 798 const unsigned char kFrameData[] = { 799 0x7f, 0xff, 0xff, 0xff, 800 0x01, 0x00, 0x00, 0x05, 801 'h', 'e', 'l', 'l', 802 'o' 803 }; 804 scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( 805 0x7fffffff, "hello", 5, DATA_FLAG_FIN)); 806 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 807 } 808} 809 810TEST_F(SpdyFramerTest, CreateSynStreamUncompressed) { 811 SpdyFramer framer; 812 FramerSetEnableCompressionHelper(&framer, false); 813 814 { 815 const char kDescription[] = "SYN_STREAM frame, lowest pri, no FIN"; 816 817 SpdyHeaderBlock headers; 818 headers["bar"] = "foo"; 819 headers["foo"] = "bar"; 820 821 const unsigned char kFrameData[] = { 822 0x80, 0x02, 0x00, 0x01, 823 0x00, 0x00, 0x00, 0x20, 824 0x00, 0x00, 0x00, 0x01, 825 0x00, 0x00, 0x00, 0x00, 826 0xC0, 0x00, 0x00, 0x02, 827 0x00, 0x03, 'b', 'a', 828 'r', 0x00, 0x03, 'f', 829 'o', 'o', 0x00, 0x03, 830 'f', 'o', 'o', 0x00, 831 0x03, 'b', 'a', 'r' 832 }; 833 scoped_ptr<SpdyFrame> frame(framer.CreateSynStream( 834 1, 0, SPDY_PRIORITY_LOWEST, CONTROL_FLAG_NONE, 835 false, &headers)); 836 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 837 } 838 839 { 840 const char kDescription[] = 841 "SYN_STREAM frame with a 0-length header name, highest pri, FIN, " 842 "max stream ID"; 843 844 SpdyHeaderBlock headers; 845 headers[""] = "foo"; 846 headers["foo"] = "bar"; 847 848 const unsigned char kFrameData[] = { 849 0x80, 0x02, 0x00, 0x01, 850 0x01, 0x00, 0x00, 0x1D, 851 0x7f, 0xff, 0xff, 0xff, 852 0x7f, 0xff, 0xff, 0xff, 853 0x00, 0x00, 0x00, 0x02, 854 0x00, 0x00, 0x00, 0x03, 855 'f', 'o', 'o', 0x00, 856 0x03, 'f', 'o', 'o', 857 0x00, 0x03, 'b', 'a', 858 'r' 859 }; 860 scoped_ptr<SpdyFrame> frame(framer.CreateSynStream( 861 0x7fffffff, 0x7fffffff, SPDY_PRIORITY_HIGHEST, CONTROL_FLAG_FIN, 862 false, &headers)); 863 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 864 } 865 866 { 867 const char kDescription[] = 868 "SYN_STREAM frame with a 0-length header val, highest pri, FIN, " 869 "max stream ID"; 870 871 SpdyHeaderBlock headers; 872 headers["bar"] = "foo"; 873 headers["foo"] = ""; 874 875 const unsigned char kFrameData[] = { 876 0x80, 0x02, 0x00, 0x01, 877 0x01, 0x00, 0x00, 0x1D, 878 0x7f, 0xff, 0xff, 0xff, 879 0x7f, 0xff, 0xff, 0xff, 880 0x00, 0x00, 0x00, 0x02, 881 0x00, 0x03, 'b', 'a', 882 'r', 0x00, 0x03, 'f', 883 'o', 'o', 0x00, 0x03, 884 'f', 'o', 'o', 0x00, 885 0x00 886 }; 887 scoped_ptr<SpdyFrame> frame(framer.CreateSynStream( 888 0x7fffffff, 0x7fffffff, SPDY_PRIORITY_HIGHEST, CONTROL_FLAG_FIN, 889 false, &headers)); 890 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 891 } 892} 893 894TEST_F(SpdyFramerTest, CreateSynStreamCompressed) { 895 SpdyFramer framer; 896 FramerSetEnableCompressionHelper(&framer, true); 897 898 { 899 const char kDescription[] = 900 "SYN_STREAM frame, lowest pri, no FIN"; 901 902 SpdyHeaderBlock headers; 903 headers["bar"] = "foo"; 904 headers["foo"] = "bar"; 905 906 const unsigned char kFrameData[] = { 907 0x80, 0x02, 0x00, 0x01, 908 0x00, 0x00, 0x00, 0x25, 909 0x00, 0x00, 0x00, 0x01, 910 0x00, 0x00, 0x00, 0x00, 911 0xC0, 0x00, 0x38, 0xea, 912 0xdf, 0xa2, 0x51, 0xb2, 913 0x62, 0x60, 0x62, 0x60, 914 0x4e, 0x4a, 0x2c, 0x62, 915 0x60, 0x4e, 0xcb, 0xcf, 916 0x87, 0x12, 0x40, 0x2e, 917 0x00, 0x00, 0x00, 0xff, 918 0xff 919 }; 920 scoped_ptr<SpdyFrame> frame(framer.CreateSynStream( 921 1, 0, SPDY_PRIORITY_LOWEST, CONTROL_FLAG_NONE, 922 true, &headers)); 923 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 924 } 925} 926 927TEST_F(SpdyFramerTest, CreateSynReplyUncompressed) { 928 SpdyFramer framer; 929 FramerSetEnableCompressionHelper(&framer, false); 930 931 { 932 const char kDescription[] = "SYN_REPLY frame, no FIN"; 933 934 SpdyHeaderBlock headers; 935 headers["bar"] = "foo"; 936 headers["foo"] = "bar"; 937 938 const unsigned char kFrameData[] = { 939 0x80, 0x02, 0x00, 0x02, 940 0x00, 0x00, 0x00, 0x1C, 941 0x00, 0x00, 0x00, 0x01, 942 0x00, 0x00, 0x00, 0x02, 943 0x00, 0x03, 'b', 'a', 944 'r', 0x00, 0x03, 'f', 945 'o', 'o', 0x00, 0x03, 946 'f', 'o', 'o', 0x00, 947 0x03, 'b', 'a', 'r' 948 }; 949 scoped_ptr<SpdyFrame> frame(framer.CreateSynReply( 950 1, CONTROL_FLAG_NONE, false, &headers)); 951 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 952 } 953 954 { 955 const char kDescription[] = 956 "SYN_REPLY frame with a 0-length header name, FIN, max stream ID"; 957 958 SpdyHeaderBlock headers; 959 headers[""] = "foo"; 960 headers["foo"] = "bar"; 961 962 const unsigned char kFrameData[] = { 963 0x80, 0x02, 0x00, 0x02, 964 0x01, 0x00, 0x00, 0x19, 965 0x7f, 0xff, 0xff, 0xff, 966 0x00, 0x00, 0x00, 0x02, 967 0x00, 0x00, 0x00, 0x03, 968 'f', 'o', 'o', 0x00, 969 0x03, 'f', 'o', 'o', 970 0x00, 0x03, 'b', 'a', 971 'r' 972 }; 973 scoped_ptr<SpdyFrame> frame(framer.CreateSynReply( 974 0x7fffffff, CONTROL_FLAG_FIN, false, &headers)); 975 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 976 } 977 978 { 979 const char kDescription[] = 980 "SYN_REPLY frame with a 0-length header val, FIN, max stream ID"; 981 982 SpdyHeaderBlock headers; 983 headers["bar"] = "foo"; 984 headers["foo"] = ""; 985 986 const unsigned char kFrameData[] = { 987 0x80, 0x02, 0x00, 0x02, 988 0x01, 0x00, 0x00, 0x19, 989 0x7f, 0xff, 0xff, 0xff, 990 0x00, 0x00, 0x00, 0x02, 991 0x00, 0x03, 'b', 'a', 992 'r', 0x00, 0x03, 'f', 993 'o', 'o', 0x00, 0x03, 994 'f', 'o', 'o', 0x00, 995 0x00 996 }; 997 scoped_ptr<SpdyFrame> frame(framer.CreateSynReply( 998 0x7fffffff, CONTROL_FLAG_FIN, false, &headers)); 999 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 1000 } 1001} 1002 1003TEST_F(SpdyFramerTest, CreateSynReplyCompressed) { 1004 SpdyFramer framer; 1005 FramerSetEnableCompressionHelper(&framer, true); 1006 1007 { 1008 const char kDescription[] = "SYN_REPLY frame, no FIN"; 1009 1010 SpdyHeaderBlock headers; 1011 headers["bar"] = "foo"; 1012 headers["foo"] = "bar"; 1013 1014 const unsigned char kFrameData[] = { 1015 0x80, 0x02, 0x00, 0x02, 1016 0x00, 0x00, 0x00, 0x21, 1017 0x00, 0x00, 0x00, 0x01, 1018 0x00, 0x00, 0x38, 0xea, 1019 0xdf, 0xa2, 0x51, 0xb2, 1020 0x62, 0x60, 0x62, 0x60, 1021 0x4e, 0x4a, 0x2c, 0x62, 1022 0x60, 0x4e, 0xcb, 0xcf, 1023 0x87, 0x12, 0x40, 0x2e, 1024 0x00, 0x00, 0x00, 0xff, 1025 0xff 1026 }; 1027 scoped_ptr<SpdyFrame> frame(framer.CreateSynReply( 1028 1, CONTROL_FLAG_NONE, true, &headers)); 1029 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 1030 } 1031} 1032 1033TEST_F(SpdyFramerTest, CreateRstStream) { 1034 SpdyFramer framer; 1035 1036 { 1037 const char kDescription[] = "RST_STREAM frame"; 1038 const unsigned char kFrameData[] = { 1039 0x80, 0x02, 0x00, 0x03, 1040 0x00, 0x00, 0x00, 0x08, 1041 0x00, 0x00, 0x00, 0x01, 1042 0x00, 0x00, 0x00, 0x01, 1043 }; 1044 scoped_ptr<SpdyFrame> frame(framer.CreateRstStream(1, PROTOCOL_ERROR)); 1045 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 1046 } 1047 1048 { 1049 const char kDescription[] = "RST_STREAM frame with max stream ID"; 1050 const unsigned char kFrameData[] = { 1051 0x80, 0x02, 0x00, 0x03, 1052 0x00, 0x00, 0x00, 0x08, 1053 0x7f, 0xff, 0xff, 0xff, 1054 0x00, 0x00, 0x00, 0x01, 1055 }; 1056 scoped_ptr<SpdyFrame> frame(framer.CreateRstStream(0x7FFFFFFF, 1057 PROTOCOL_ERROR)); 1058 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 1059 } 1060 1061 { 1062 const char kDescription[] = "RST_STREAM frame with max status code"; 1063 const unsigned char kFrameData[] = { 1064 0x80, 0x02, 0x00, 0x03, 1065 0x00, 0x00, 0x00, 0x08, 1066 0x7f, 0xff, 0xff, 0xff, 1067 0x00, 0x00, 0x00, 0x06, 1068 }; 1069 scoped_ptr<SpdyFrame> frame(framer.CreateRstStream(0x7FFFFFFF, 1070 INTERNAL_ERROR)); 1071 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 1072 } 1073} 1074 1075TEST_F(SpdyFramerTest, CreateSettings) { 1076 SpdyFramer framer; 1077 1078 { 1079 const char kDescription[] = "Basic SETTINGS frame"; 1080 1081 SpdySettings settings; 1082 settings.push_back(SpdySetting(0x00000000, 0x00000000)); 1083 settings.push_back(SpdySetting(0xffffffff, 0x00000001)); 1084 settings.push_back(SpdySetting(0xff000001, 0x00000002)); 1085 1086 // Duplicates allowed 1087 settings.push_back(SpdySetting(0x01000002, 0x00000003)); 1088 settings.push_back(SpdySetting(0x01000002, 0x00000003)); 1089 1090 settings.push_back(SpdySetting(0x01000003, 0x000000ff)); 1091 settings.push_back(SpdySetting(0x01000004, 0xff000001)); 1092 settings.push_back(SpdySetting(0x01000004, 0xffffffff)); 1093 1094 const unsigned char kFrameData[] = { 1095 0x80, 0x02, 0x00, 0x04, 1096 0x00, 0x00, 0x00, 0x44, 1097 0x00, 0x00, 0x00, 0x08, 1098 0x00, 0x00, 0x00, 0x00, 1099 0x00, 0x00, 0x00, 0x00, 1100 0xff, 0xff, 0xff, 0xff, 1101 0x00, 0x00, 0x00, 0x01, 1102 0xff, 0x00, 0x00, 0x01, 1103 0x00, 0x00, 0x00, 0x02, 1104 0x01, 0x00, 0x00, 0x02, 1105 0x00, 0x00, 0x00, 0x03, 1106 0x01, 0x00, 0x00, 0x02, 1107 0x00, 0x00, 0x00, 0x03, 1108 0x01, 0x00, 0x00, 0x03, 1109 0x00, 0x00, 0x00, 0xff, 1110 0x01, 0x00, 0x00, 0x04, 1111 0xff, 0x00, 0x00, 0x01, 1112 0x01, 0x00, 0x00, 0x04, 1113 0xff, 0xff, 0xff, 0xff, 1114 }; 1115 scoped_ptr<SpdyFrame> frame(framer.CreateSettings(settings)); 1116 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 1117 } 1118 1119 { 1120 const char kDescription[] = "Empty SETTINGS frame"; 1121 1122 SpdySettings settings; 1123 1124 const unsigned char kFrameData[] = { 1125 0x80, 0x02, 0x00, 0x04, 1126 0x00, 0x00, 0x00, 0x04, 1127 0x00, 0x00, 0x00, 0x00, 1128 }; 1129 scoped_ptr<SpdyFrame> frame(framer.CreateSettings(settings)); 1130 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 1131 } 1132} 1133 1134TEST_F(SpdyFramerTest, CreateNopFrame) { 1135 SpdyFramer framer; 1136 1137 { 1138 const char kDescription[] = "NOOP frame"; 1139 const unsigned char kFrameData[] = { 1140 0x80, 0x02, 0x00, 0x05, 1141 0x00, 0x00, 0x00, 0x00, 1142 }; 1143 scoped_ptr<SpdyFrame> frame(framer.CreateNopFrame()); 1144 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 1145 } 1146} 1147 1148TEST_F(SpdyFramerTest, CreateGoAway) { 1149 SpdyFramer framer; 1150 1151 { 1152 const char kDescription[] = "GOAWAY frame"; 1153 const unsigned char kFrameData[] = { 1154 0x80, 0x02, 0x00, 0x07, 1155 0x00, 0x00, 0x00, 0x04, 1156 0x00, 0x00, 0x00, 0x00, 1157 }; 1158 scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(0)); 1159 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 1160 } 1161 1162 { 1163 const char kDescription[] = "GOAWAY frame with max stream ID"; 1164 const unsigned char kFrameData[] = { 1165 0x80, 0x02, 0x00, 0x07, 1166 0x00, 0x00, 0x00, 0x04, 1167 0x7f, 0xff, 0xff, 0xff, 1168 }; 1169 scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(0x7FFFFFFF)); 1170 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 1171 } 1172} 1173 1174TEST_F(SpdyFramerTest, CreateHeadersUncompressed) { 1175 SpdyFramer framer; 1176 FramerSetEnableCompressionHelper(&framer, false); 1177 1178 { 1179 const char kDescription[] = "HEADERS frame, no FIN"; 1180 1181 SpdyHeaderBlock headers; 1182 headers["bar"] = "foo"; 1183 headers["foo"] = "bar"; 1184 1185 const unsigned char kFrameData[] = { 1186 0x80, 0x02, 0x00, 0x08, 1187 0x00, 0x00, 0x00, 0x1C, 1188 0x00, 0x00, 0x00, 0x01, 1189 0x00, 0x00, 0x00, 0x02, 1190 0x00, 0x03, 'b', 'a', 1191 'r', 0x00, 0x03, 'f', 1192 'o', 'o', 0x00, 0x03, 1193 'f', 'o', 'o', 0x00, 1194 0x03, 'b', 'a', 'r' 1195 }; 1196 scoped_ptr<SpdyFrame> frame(framer.CreateHeaders( 1197 1, CONTROL_FLAG_NONE, false, &headers)); 1198 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 1199 } 1200 1201 { 1202 const char kDescription[] = 1203 "HEADERS frame with a 0-length header name, FIN, max stream ID"; 1204 1205 SpdyHeaderBlock headers; 1206 headers[""] = "foo"; 1207 headers["foo"] = "bar"; 1208 1209 const unsigned char kFrameData[] = { 1210 0x80, 0x02, 0x00, 0x08, 1211 0x01, 0x00, 0x00, 0x19, 1212 0x7f, 0xff, 0xff, 0xff, 1213 0x00, 0x00, 0x00, 0x02, 1214 0x00, 0x00, 0x00, 0x03, 1215 'f', 'o', 'o', 0x00, 1216 0x03, 'f', 'o', 'o', 1217 0x00, 0x03, 'b', 'a', 1218 'r' 1219 }; 1220 scoped_ptr<SpdyFrame> frame(framer.CreateHeaders( 1221 0x7fffffff, CONTROL_FLAG_FIN, false, &headers)); 1222 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 1223 } 1224 1225 { 1226 const char kDescription[] = 1227 "HEADERS frame with a 0-length header val, FIN, max stream ID"; 1228 1229 SpdyHeaderBlock headers; 1230 headers["bar"] = "foo"; 1231 headers["foo"] = ""; 1232 1233 const unsigned char kFrameData[] = { 1234 0x80, 0x02, 0x00, 0x08, 1235 0x01, 0x00, 0x00, 0x19, 1236 0x7f, 0xff, 0xff, 0xff, 1237 0x00, 0x00, 0x00, 0x02, 1238 0x00, 0x03, 'b', 'a', 1239 'r', 0x00, 0x03, 'f', 1240 'o', 'o', 0x00, 0x03, 1241 'f', 'o', 'o', 0x00, 1242 0x00 1243 }; 1244 scoped_ptr<SpdyFrame> frame(framer.CreateHeaders( 1245 0x7fffffff, CONTROL_FLAG_FIN, false, &headers)); 1246 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 1247 } 1248} 1249 1250TEST_F(SpdyFramerTest, CreateHeadersCompressed) { 1251 SpdyFramer framer; 1252 FramerSetEnableCompressionHelper(&framer, true); 1253 1254 { 1255 const char kDescription[] = "HEADERS frame, no FIN"; 1256 1257 SpdyHeaderBlock headers; 1258 headers["bar"] = "foo"; 1259 headers["foo"] = "bar"; 1260 1261 const unsigned char kFrameData[] = { 1262 0x80, 0x02, 0x00, 0x08, 1263 0x00, 0x00, 0x00, 0x21, 1264 0x00, 0x00, 0x00, 0x01, 1265 0x00, 0x00, 0x38, 0xea, 1266 0xdf, 0xa2, 0x51, 0xb2, 1267 0x62, 0x60, 0x62, 0x60, 1268 0x4e, 0x4a, 0x2c, 0x62, 1269 0x60, 0x4e, 0xcb, 0xcf, 1270 0x87, 0x12, 0x40, 0x2e, 1271 0x00, 0x00, 0x00, 0xff, 1272 0xff 1273 }; 1274 scoped_ptr<SpdyFrame> frame(framer.CreateHeaders( 1275 1, CONTROL_FLAG_NONE, true, &headers)); 1276 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 1277 } 1278} 1279 1280TEST_F(SpdyFramerTest, CreateWindowUpdate) { 1281 SpdyFramer framer; 1282 1283 { 1284 const char kDescription[] = "WINDOW_UPDATE frame"; 1285 const unsigned char kFrameData[] = { 1286 0x80, 0x02, 0x00, 0x09, 1287 0x00, 0x00, 0x00, 0x08, 1288 0x00, 0x00, 0x00, 0x01, 1289 0x00, 0x00, 0x00, 0x01, 1290 }; 1291 scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(1, 1)); 1292 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 1293 } 1294 1295 { 1296 const char kDescription[] = "WINDOW_UPDATE frame with max stream ID"; 1297 const unsigned char kFrameData[] = { 1298 0x80, 0x02, 0x00, 0x09, 1299 0x00, 0x00, 0x00, 0x08, 1300 0x7f, 0xff, 0xff, 0xff, 1301 0x00, 0x00, 0x00, 0x01, 1302 }; 1303 scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(0x7FFFFFFF, 1)); 1304 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 1305 } 1306 1307 { 1308 const char kDescription[] = "WINDOW_UPDATE frame with max window delta"; 1309 const unsigned char kFrameData[] = { 1310 0x80, 0x02, 0x00, 0x09, 1311 0x00, 0x00, 0x00, 0x08, 1312 0x00, 0x00, 0x00, 0x01, 1313 0x7f, 0xff, 0xff, 0xff, 1314 }; 1315 scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(1, 0x7FFFFFFF)); 1316 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); 1317 } 1318} 1319 1320// This test case reproduces conditions that caused ExpandControlFrameBuffer to 1321// fail to expand the buffer control frame buffer when it should have, allowing 1322// the framer to overrun the buffer, and smash other heap contents. This test 1323// relies on the debug version of the heap manager, which checks for buffer 1324// overrun errors during delete processing. Regression test for b/2974814. 1325TEST_F(SpdyFramerTest, ExpandBuffer_HeapSmash) { 1326 // Sweep through the area of problematic values, to make sure we always cover 1327 // the danger zone, even if it moves around at bit due to SPDY changes. 1328 for (uint16 val2_len = SpdyFramer::kControlFrameBufferInitialSize - 50; 1329 val2_len < SpdyFramer::kControlFrameBufferInitialSize; 1330 val2_len++) { 1331 std::string val2 = std::string(val2_len, 'a'); 1332 SpdyHeaderBlock headers; 1333 headers["bar"] = "foo"; 1334 headers["foo"] = "baz"; 1335 headers["grue"] = val2.c_str(); 1336 SpdyFramer framer; 1337 scoped_ptr<SpdySynStreamControlFrame> template_frame( 1338 framer.CreateSynStream(1, // stream_id 1339 0, // associated_stream_id 1340 1, // priority 1341 CONTROL_FLAG_NONE, 1342 false, // compress 1343 &headers)); 1344 EXPECT_TRUE(template_frame.get() != NULL); 1345 TestSpdyVisitor visitor; 1346 visitor.SimulateInFramer( 1347 reinterpret_cast<unsigned char*>(template_frame.get()->data()), 1348 template_frame.get()->length() + SpdyControlFrame::size()); 1349 EXPECT_EQ(1, visitor.syn_frame_count_); 1350 } 1351} 1352 1353std::string RandomString(int length) { 1354 std::string rv; 1355 for (int index = 0; index < length; index++) 1356 rv += static_cast<char>('a' + (rand() % 26)); 1357 return rv; 1358} 1359 1360// Stress that we can handle a really large header block compression and 1361// decompression. 1362TEST_F(SpdyFramerTest, HugeHeaderBlock) { 1363 // Loop targetting various sizes which will potentially jam up the 1364 // frame compressor/decompressor. 1365 SpdyFramer compress_framer; 1366 SpdyFramer decompress_framer; 1367 for (size_t target_size = 1024; 1368 target_size < SpdyFramer::kControlFrameBufferInitialSize; 1369 target_size += 1024) { 1370 SpdyHeaderBlock headers; 1371 for (size_t index = 0; index < target_size; ++index) { 1372 std::string name = RandomString(4); 1373 std::string value = RandomString(8); 1374 headers[name] = value; 1375 } 1376 1377 // Encode the header block into a SynStream frame. 1378 scoped_ptr<SpdySynStreamControlFrame> frame( 1379 compress_framer.CreateSynStream(1, 1380 0, 1381 1, 1382 CONTROL_FLAG_NONE, 1383 true, 1384 &headers)); 1385 // The point of this test is to exercise the limits. So, it is ok if the 1386 // frame was too large to encode, or if the decompress fails. We just want 1387 // to make sure we don't crash. 1388 if (frame.get() != NULL) { 1389 // Now that same header block should decompress just fine. 1390 SpdyHeaderBlock new_headers; 1391 decompress_framer.ParseHeaderBlock(frame.get(), &new_headers); 1392 } 1393 } 1394} 1395 1396} // namespace 1397