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 <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 | |
14 namespace spdy { | |
15 | |
16 namespace test { | |
17 | |
18 std::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 const int kSizeLimit = 1024; | |
24 if (length > kSizeLimit || mark_length > kSizeLimit) { | |
25 LOG(ERROR) << "Only dumping first " << kSizeLimit << " bytes."; | |
26 length = std::min(length, kSizeLimit); | |
27 mark_length = std::min(mark_length, kSizeLimit); | |
28 } | |
29 | |
30 std::string hex; | |
31 for (const unsigned char* row = data; length > 0; | |
32 row += kColumns, length -= kColumns) { | |
33 for (const unsigned char *p = row; p < row + 4; ++p) { | |
34 if (p < row + length) { | |
35 const bool mark = | |
36 (marks && (p - data) < mark_length && marks[p - data]); | |
37 hex += mark ? '*' : ' '; | |
38 hex += kHexChars[(*p & 0xf0) >> 4]; | |
39 hex += kHexChars[*p & 0x0f]; | |
40 hex += mark ? '*' : ' '; | |
41 } else { | |
42 hex += " "; | |
43 } | |
44 } | |
45 hex = hex + " "; | |
46 | |
47 for (const unsigned char *p = row; p < row + 4 && p < row + length; ++p) | |
48 hex += (*p >= 0x20 && *p <= 0x7f) ? (*p) : '.'; | |
49 | |
50 hex = hex + '\n'; | |
51 } | |
52 return hex; | |
53 } | |
54 | |
55 void CompareCharArraysWithHexError( | |
56 const std::string& description, | |
57 const unsigned char* actual, | |
58 const int actual_len, | |
59 const unsigned char* expected, | |
60 const int expected_len) { | |
61 const int min_len = actual_len > expected_len ? expected_len : actual_len; | |
62 const int max_len = actual_len > expected_len ? actual_len : expected_len; | |
63 scoped_array<bool> marks(new bool[max_len]); | |
64 bool identical = (actual_len == expected_len); | |
65 for (int i = 0; i < min_len; ++i) { | |
66 if (actual[i] != expected[i]) { | |
67 marks[i] = true; | |
68 identical = false; | |
69 } else { | |
70 marks[i] = false; | |
71 } | |
72 } | |
73 for (int i = min_len; i < max_len; ++i) { | |
74 marks[i] = true; | |
75 } | |
76 if (identical) return; | |
77 ADD_FAILURE() | |
78 << "Description:\n" | |
79 << description | |
80 << "\n\nExpected:\n" | |
81 << HexDumpWithMarks(expected, expected_len, marks.get(), max_len) | |
82 << "\nActual:\n" | |
83 << HexDumpWithMarks(actual, actual_len, marks.get(), max_len); | |
84 } | |
85 | |
86 class TestSpdyVisitor : public SpdyFramerVisitorInterface { | |
87 public: | |
88 static const size_t kDefaultHeaderBufferSize = 64 * 1024; | |
89 static const size_t kDefaultCredentialBufferSize = 16 * 1024; | |
90 | |
91 TestSpdyVisitor() | |
92 : use_compression_(false), | |
93 error_count_(0), | |
94 syn_frame_count_(0), | |
95 syn_reply_frame_count_(0), | |
96 headers_frame_count_(0), | |
97 goaway_count_(0), | |
98 credential_count_(0), | |
99 data_bytes_(0), | |
100 fin_frame_count_(0), | |
101 fin_flag_count_(0), | |
102 zero_length_data_frame_count_(0), | |
103 header_blocks_count_(0), | |
104 control_frame_header_data_count_(0), | |
105 zero_length_control_frame_header_data_count_(0), | |
106 data_frame_count_(0), | |
107 header_buffer_(new char[kDefaultHeaderBufferSize]), | |
108 header_buffer_length_(0), | |
109 header_buffer_size_(kDefaultHeaderBufferSize), | |
110 header_stream_id_(-1), | |
111 header_control_type_(NUM_CONTROL_FRAME_TYPES), | |
112 header_buffer_valid_(false), | |
113 credential_buffer_(new char[kDefaultCredentialBufferSize]), | |
114 credential_buffer_length_(0), | |
115 credential_buffer_size_(kDefaultCredentialBufferSize) { | |
116 } | |
117 | |
118 void OnError(SpdyFramer* f) { | |
119 LOG(INFO) << "SpdyFramer Error: " | |
120 << SpdyFramer::ErrorCodeToString(f->error_code()); | |
121 error_count_++; | |
122 } | |
123 | |
124 void OnDataFrameHeader(const SpdyDataFrame* frame) { | |
125 data_frame_count_++; | |
126 header_stream_id_ = frame->stream_id(); | |
127 } | |
128 | |
129 void OnStreamFrameData(SpdyStreamId stream_id, | |
130 const char* data, | |
131 size_t len) { | |
132 EXPECT_EQ(header_stream_id_, stream_id); | |
133 if (len == 0) | |
134 ++zero_length_data_frame_count_; | |
135 | |
136 data_bytes_ += len; | |
137 std::cerr << "OnStreamFrameData(" << stream_id << ", \""; | |
138 if (len > 0) { | |
139 for (size_t i = 0 ; i < len; ++i) { | |
140 std::cerr << std::hex << (0xFF & (unsigned int)data[i]) << std::dec; | |
141 } | |
142 } | |
143 std::cerr << "\", " << len << ")\n"; | |
144 } | |
145 | |
146 void OnControl(const SpdyControlFrame* frame) { | |
147 switch (frame->type()) { | |
148 case SYN_STREAM: | |
149 syn_frame_count_++; | |
150 InitHeaderStreaming(frame); | |
151 break; | |
152 case SYN_REPLY: | |
153 syn_reply_frame_count_++; | |
154 InitHeaderStreaming(frame); | |
155 break; | |
156 case RST_STREAM: | |
157 fin_frame_count_++; | |
158 break; | |
159 case HEADERS: | |
160 headers_frame_count_++; | |
161 InitHeaderStreaming(frame); | |
162 break; | |
163 case GOAWAY: | |
164 goaway_count_++; | |
165 break; | |
166 case CREDENTIAL: | |
167 credential_count_++; | |
168 break; | |
169 default: | |
170 DLOG(FATAL); // Error! | |
171 } | |
172 if (frame->flags() & CONTROL_FLAG_FIN) | |
173 ++fin_flag_count_; | |
174 } | |
175 | |
176 bool OnControlFrameHeaderData(SpdyStreamId stream_id, | |
177 const char* header_data, | |
178 size_t len) { | |
179 ++control_frame_header_data_count_; | |
180 CHECK_EQ(header_stream_id_, stream_id); | |
181 if (len == 0) { | |
182 ++zero_length_control_frame_header_data_count_; | |
183 // Indicates end-of-header-block. | |
184 CHECK(header_buffer_valid_); | |
185 bool parsed_headers = SpdyFramer::ParseHeaderBlockInBuffer( | |
186 header_buffer_.get(), header_buffer_length_, &headers_); | |
187 DCHECK(parsed_headers); | |
188 return true; | |
189 } | |
190 const size_t available = header_buffer_size_ - header_buffer_length_; | |
191 if (len > available) { | |
192 header_buffer_valid_ = false; | |
193 return false; | |
194 } | |
195 memcpy(header_buffer_.get() + header_buffer_length_, header_data, len); | |
196 header_buffer_length_ += len; | |
197 return true; | |
198 } | |
199 | |
200 bool OnCredentialFrameData(const char* credential_data, size_t len) { | |
201 if (len == 0) { | |
202 if (!framer_.ParseCredentialData(credential_buffer_.get(), | |
203 credential_buffer_length_, | |
204 &credential_)) { | |
205 ++error_count_; | |
206 } | |
207 return true; | |
208 } | |
209 const size_t available = | |
210 credential_buffer_size_ - credential_buffer_length_; | |
211 if (len > available) { | |
212 return false; | |
213 } | |
214 memcpy(credential_buffer_.get() + credential_buffer_length_, | |
215 credential_data, len); | |
216 credential_buffer_length_ += len; | |
217 return true; | |
218 } | |
219 | |
220 // Convenience function which runs a framer simulation with particular input. | |
221 void SimulateInFramer(const unsigned char* input, size_t size) { | |
222 framer_.set_enable_compression(use_compression_); | |
223 framer_.set_visitor(this); | |
224 size_t input_remaining = size; | |
225 const char* input_ptr = reinterpret_cast<const char*>(input); | |
226 while (input_remaining > 0 && | |
227 framer_.error_code() == SpdyFramer::SPDY_NO_ERROR) { | |
228 // To make the tests more interesting, we feed random (amd small) chunks | |
229 // into the framer. This simulates getting strange-sized reads from | |
230 // the socket. | |
231 const size_t kMaxReadSize = 32; | |
232 size_t bytes_read = | |
233 (rand() % std::min(input_remaining, kMaxReadSize)) + 1; | |
234 size_t bytes_processed = framer_.ProcessInput(input_ptr, bytes_read); | |
235 input_remaining -= bytes_processed; | |
236 input_ptr += bytes_processed; | |
237 if (framer_.state() == SpdyFramer::SPDY_DONE) | |
238 framer_.Reset(); | |
239 } | |
240 } | |
241 | |
242 void InitHeaderStreaming(const SpdyControlFrame* frame) { | |
243 memset(header_buffer_.get(), 0, header_buffer_size_); | |
244 header_buffer_length_ = 0; | |
245 header_stream_id_ = SpdyFramer::GetControlFrameStreamId(frame); | |
246 header_control_type_ = frame->type(); | |
247 header_buffer_valid_ = true; | |
248 DCHECK_NE(header_stream_id_, SpdyFramer::kInvalidStream); | |
249 } | |
250 | |
251 // Override the default buffer size (16K). Call before using the framer! | |
252 void set_header_buffer_size(size_t header_buffer_size) { | |
253 header_buffer_size_ = header_buffer_size; | |
254 header_buffer_.reset(new char[header_buffer_size]); | |
255 } | |
256 | |
257 static size_t control_frame_buffer_max_size() { | |
258 return SpdyFramer::kControlFrameBufferMaxSize; | |
259 } | |
260 | |
261 static size_t header_data_chunk_max_size() { | |
262 return SpdyFramer::kHeaderDataChunkMaxSize; | |
263 } | |
264 | |
265 SpdyFramer framer_; | |
266 bool use_compression_; | |
267 | |
268 // Counters from the visitor callbacks. | |
269 int error_count_; | |
270 int syn_frame_count_; | |
271 int syn_reply_frame_count_; | |
272 int headers_frame_count_; | |
273 int goaway_count_; | |
274 int credential_count_; | |
275 int data_bytes_; | |
276 int fin_frame_count_; // The count of RST_STREAM type frames received. | |
277 int fin_flag_count_; // The count of frames with the FIN flag set. | |
278 int zero_length_data_frame_count_; // The count of zero-length data frames. | |
279 int header_blocks_count_; | |
280 int control_frame_header_data_count_; // The count of chunks received. | |
281 // The count of zero-length control frame header data chunks received. | |
282 int zero_length_control_frame_header_data_count_; | |
283 int data_frame_count_; | |
284 | |
285 // Header block streaming state: | |
286 scoped_array<char> header_buffer_; | |
287 size_t header_buffer_length_; | |
288 size_t header_buffer_size_; | |
289 SpdyStreamId header_stream_id_; | |
290 SpdyControlType header_control_type_; | |
291 bool header_buffer_valid_; | |
292 SpdyHeaderBlock headers_; | |
293 | |
294 scoped_array<char> credential_buffer_; | |
295 size_t credential_buffer_length_; | |
296 size_t credential_buffer_size_; | |
297 SpdyCredential credential_; | |
298 }; | |
299 | |
300 } // namespace test | |
301 | |
302 } // namespace spdy | |
303 | |
304 using spdy::SpdyControlFlags; | |
305 using spdy::SpdyControlFrame; | |
306 using spdy::SpdyDataFrame; | |
307 using spdy::SpdyFrame; | |
308 using spdy::SpdyFrameBuilder; | |
309 using spdy::SpdyFramer; | |
310 using spdy::SpdyHeaderBlock; | |
311 using spdy::SpdySynStreamControlFrame; | |
312 using spdy::kControlFlagMask; | |
313 using spdy::kLengthMask; | |
314 using spdy::CONTROL_FLAG_NONE; | |
315 using spdy::DATA_FLAG_COMPRESSED; | |
316 using spdy::DATA_FLAG_FIN; | |
317 using spdy::SYN_STREAM; | |
318 using spdy::test::CompareCharArraysWithHexError; | |
319 using spdy::test::TestSpdyVisitor; | |
320 | |
321 namespace spdy { | |
322 | |
323 TEST(SpdyFrameBuilderTest, WriteLimits) { | |
324 SpdyFrameBuilder builder(kLengthMask + 4); | |
325 // length field should fail. | |
326 EXPECT_FALSE(builder.WriteBytes(reinterpret_cast<const void*>(0x1), | |
327 kLengthMask + 1)); | |
328 EXPECT_EQ(0, builder.length()); | |
329 | |
330 // Writing a block of the maximum allowed size should succeed. | |
331 const std::string kLargeData(kLengthMask, 'A'); | |
332 builder.WriteUInt32(kLengthMask); | |
333 EXPECT_EQ(4, builder.length()); | |
334 EXPECT_TRUE(builder.WriteBytes(kLargeData.data(), kLengthMask)); | |
335 EXPECT_EQ(4 + kLengthMask, static_cast<unsigned>(builder.length())); | |
336 } | |
337 | |
338 class SpdyFramerTest : public PlatformTest { | |
339 public: | |
340 virtual void TearDown() {} | |
341 | |
342 protected: | |
343 void CompareFrame(const std::string& description, | |
344 const SpdyFrame& actual_frame, | |
345 const unsigned char* expected, | |
346 const int expected_len) { | |
347 const unsigned char* actual = | |
348 reinterpret_cast<const unsigned char*>(actual_frame.data()); | |
349 int actual_len = actual_frame.length() + SpdyFrame::kHeaderSize; | |
350 CompareCharArraysWithHexError( | |
351 description, actual, actual_len, expected, expected_len); | |
352 } | |
353 | |
354 // Returns true if the two header blocks have equivalent content. | |
355 bool CompareHeaderBlocks(const SpdyHeaderBlock* expected, | |
356 const SpdyHeaderBlock* actual) { | |
357 if (expected->size() != actual->size()) { | |
358 LOG(ERROR) << "Expected " << expected->size() << " headers; actually got " | |
359 << actual->size() << "." << std::endl; | |
360 return false; | |
361 } | |
362 for (SpdyHeaderBlock::const_iterator it = expected->begin(); | |
363 it != expected->end(); | |
364 ++it) { | |
365 SpdyHeaderBlock::const_iterator it2 = actual->find(it->first); | |
366 if (it2 == actual->end()) { | |
367 LOG(ERROR) << "Expected header name '" << it->first << "'." | |
368 << std::endl; | |
369 return false; | |
370 } | |
371 if (it->second.compare(it2->second) != 0) { | |
372 LOG(ERROR) << "Expected header named '" << it->first | |
373 << "' to have a value of '" << it->second | |
374 << "'. The actual value received was '" << it2->second | |
375 << "'." << std::endl; | |
376 return false; | |
377 } | |
378 } | |
379 return true; | |
380 } | |
381 }; | |
382 | |
383 | |
384 // Test that we can encode and decode a SpdyHeaderBlock in serialized form. | |
385 TEST_F(SpdyFramerTest, HeaderBlockInBuffer) { | |
386 SpdyHeaderBlock headers; | |
387 headers["alpha"] = "beta"; | |
388 headers["gamma"] = "charlie"; | |
389 SpdyFramer framer; | |
390 | |
391 // Encode the header block into a SynStream frame. | |
392 scoped_ptr<SpdySynStreamControlFrame> frame( | |
393 framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, false, &headers)); | |
394 EXPECT_TRUE(frame.get() != NULL); | |
395 std::string serialized_headers(frame->header_block(), | |
396 frame->header_block_len()); | |
397 SpdyHeaderBlock new_headers; | |
398 EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.c_str(), | |
399 serialized_headers.size(), | |
400 &new_headers)); | |
401 | |
402 EXPECT_EQ(headers.size(), new_headers.size()); | |
403 EXPECT_EQ(headers["alpha"], new_headers["alpha"]); | |
404 EXPECT_EQ(headers["gamma"], new_headers["gamma"]); | |
405 } | |
406 | |
407 // Test that if there's not a full frame, we fail to parse it. | |
408 TEST_F(SpdyFramerTest, UndersizedHeaderBlockInBuffer) { | |
409 SpdyHeaderBlock headers; | |
410 headers["alpha"] = "beta"; | |
411 headers["gamma"] = "charlie"; | |
412 SpdyFramer framer; | |
413 | |
414 // Encode the header block into a SynStream frame. | |
415 scoped_ptr<SpdySynStreamControlFrame> frame( | |
416 framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, false, &headers)); | |
417 EXPECT_TRUE(frame.get() != NULL); | |
418 | |
419 std::string serialized_headers(frame->header_block(), | |
420 frame->header_block_len()); | |
421 SpdyHeaderBlock new_headers; | |
422 EXPECT_FALSE(framer.ParseHeaderBlockInBuffer(serialized_headers.c_str(), | |
423 serialized_headers.size() - 2, | |
424 &new_headers)); | |
425 } | |
426 | |
427 TEST_F(SpdyFramerTest, OutOfOrderHeaders) { | |
428 // Frame builder with plentiful buffer size. | |
429 SpdyFrameBuilder frame(1024); | |
430 | |
431 frame.WriteUInt16(kControlFlagMask | 1); | |
432 frame.WriteUInt16(SYN_STREAM); | |
433 frame.WriteUInt32(0); // Placeholder for the length. | |
434 frame.WriteUInt32(3); // stream_id | |
435 frame.WriteUInt32(0); // Associated stream id | |
436 frame.WriteUInt16(0); // Priority. | |
437 | |
438 frame.WriteUInt16(2); // Number of headers. | |
439 SpdyHeaderBlock::iterator it; | |
440 frame.WriteString("gamma"); | |
441 frame.WriteString("gamma"); | |
442 frame.WriteString("alpha"); | |
443 frame.WriteString("alpha"); | |
444 // write the length | |
445 frame.WriteUInt32ToOffset(4, frame.length() - SpdyFrame::kHeaderSize); | |
446 | |
447 SpdyHeaderBlock new_headers; | |
448 scoped_ptr<SpdyFrame> control_frame(frame.take()); | |
449 SpdySynStreamControlFrame syn_frame(control_frame->data(), false); | |
450 std::string serialized_headers(syn_frame.header_block(), | |
451 syn_frame.header_block_len()); | |
452 SpdyFramer framer; | |
453 framer.set_enable_compression(false); | |
454 EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.c_str(), | |
455 serialized_headers.size(), | |
456 &new_headers)); | |
457 } | |
458 | |
459 TEST_F(SpdyFramerTest, DuplicateHeader) { | |
460 // Frame builder with plentiful buffer size. | |
461 SpdyFrameBuilder frame(1024); | |
462 | |
463 frame.WriteUInt16(kControlFlagMask | 1); | |
464 frame.WriteUInt16(SYN_STREAM); | |
465 frame.WriteUInt32(0); // Placeholder for the length. | |
466 frame.WriteUInt32(3); // stream_id | |
467 frame.WriteUInt32(0); // associated stream id | |
468 frame.WriteUInt16(0); // Priority. | |
469 | |
470 frame.WriteUInt16(2); // Number of headers. | |
471 SpdyHeaderBlock::iterator it; | |
472 frame.WriteString("name"); | |
473 frame.WriteString("value1"); | |
474 frame.WriteString("name"); | |
475 frame.WriteString("value2"); | |
476 // write the length | |
477 frame.WriteUInt32ToOffset(4, frame.length() - SpdyFrame::kHeaderSize); | |
478 | |
479 SpdyHeaderBlock new_headers; | |
480 scoped_ptr<SpdyFrame> control_frame(frame.take()); | |
481 SpdySynStreamControlFrame syn_frame(control_frame->data(), false); | |
482 std::string serialized_headers(syn_frame.header_block(), | |
483 syn_frame.header_block_len()); | |
484 SpdyFramer framer; | |
485 framer.set_enable_compression(false); | |
486 // This should fail because duplicate headers are verboten by the spec. | |
487 EXPECT_FALSE(framer.ParseHeaderBlockInBuffer(serialized_headers.c_str(), | |
488 serialized_headers.size(), | |
489 &new_headers)); | |
490 } | |
491 | |
492 TEST_F(SpdyFramerTest, MultiValueHeader) { | |
493 // Frame builder with plentiful buffer size. | |
494 SpdyFrameBuilder frame(1024); | |
495 | |
496 frame.WriteUInt16(kControlFlagMask | 1); | |
497 frame.WriteUInt16(SYN_STREAM); | |
498 frame.WriteUInt32(0); // Placeholder for the length. | |
499 frame.WriteUInt32(3); // stream_id | |
500 frame.WriteUInt32(0); // associated stream id | |
501 frame.WriteUInt16(0); // Priority. | |
502 | |
503 frame.WriteUInt16(1); // Number of headers. | |
504 SpdyHeaderBlock::iterator it; | |
505 frame.WriteString("name"); | |
506 std::string value("value1\0value2"); | |
507 frame.WriteString(value); | |
508 // write the length | |
509 frame.WriteUInt32ToOffset(4, frame.length() - SpdyFrame::kHeaderSize); | |
510 | |
511 SpdyHeaderBlock new_headers; | |
512 scoped_ptr<SpdyFrame> control_frame(frame.take()); | |
513 SpdySynStreamControlFrame syn_frame(control_frame->data(), false); | |
514 std::string serialized_headers(syn_frame.header_block(), | |
515 syn_frame.header_block_len()); | |
516 SpdyFramer framer; | |
517 framer.set_enable_compression(false); | |
518 EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.c_str(), | |
519 serialized_headers.size(), | |
520 &new_headers)); | |
521 EXPECT_TRUE(new_headers.find("name") != new_headers.end()); | |
522 EXPECT_EQ(value, new_headers.find("name")->second); | |
523 } | |
524 | |
525 TEST_F(SpdyFramerTest, BasicCompression) { | |
526 SpdyHeaderBlock headers; | |
527 headers["server"] = "SpdyServer 1.0"; | |
528 headers["date"] = "Mon 12 Jan 2009 12:12:12 PST"; | |
529 headers["status"] = "200"; | |
530 headers["version"] = "HTTP/1.1"; | |
531 headers["content-type"] = "text/html"; | |
532 headers["content-length"] = "12"; | |
533 | |
534 SpdyFramer framer; | |
535 framer.set_enable_compression(true); | |
536 scoped_ptr<SpdySynStreamControlFrame> | |
537 frame1(framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, true, | |
538 &headers)); | |
539 scoped_ptr<SpdySynStreamControlFrame> | |
540 frame2(framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, true, | |
541 &headers)); | |
542 | |
543 // Expect the second frame to be more compact than the first. | |
544 EXPECT_LE(frame2->length(), frame1->length()); | |
545 | |
546 // Decompress the first frame | |
547 scoped_ptr<SpdyFrame> frame3(framer.DecompressFrame(*frame1.get())); | |
548 | |
549 // Decompress the second frame | |
550 scoped_ptr<SpdyFrame> frame4(framer.DecompressFrame(*frame2.get())); | |
551 | |
552 // Expect frames 3 & 4 to be the same. | |
553 EXPECT_EQ(0, | |
554 memcmp(frame3->data(), frame4->data(), | |
555 SpdyFrame::kHeaderSize + frame3->length())); | |
556 | |
557 | |
558 // Expect frames 3 to be the same as a uncompressed frame created | |
559 // from scratch. | |
560 scoped_ptr<SpdySynStreamControlFrame> | |
561 uncompressed_frame(framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, | |
562 false, &headers)); | |
563 EXPECT_EQ(frame3->length(), uncompressed_frame->length()); | |
564 EXPECT_EQ(0, | |
565 memcmp(frame3->data(), uncompressed_frame->data(), | |
566 SpdyFrame::kHeaderSize + uncompressed_frame->length())); | |
567 } | |
568 | |
569 TEST_F(SpdyFramerTest, DecompressUncompressedFrame) { | |
570 SpdyHeaderBlock headers; | |
571 headers["server"] = "SpdyServer 1.0"; | |
572 headers["date"] = "Mon 12 Jan 2009 12:12:12 PST"; | |
573 headers["status"] = "200"; | |
574 headers["version"] = "HTTP/1.1"; | |
575 headers["content-type"] = "text/html"; | |
576 headers["content-length"] = "12"; | |
577 | |
578 SpdyFramer framer; | |
579 framer.set_enable_compression(true); | |
580 scoped_ptr<SpdySynStreamControlFrame> | |
581 frame1(framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, false, | |
582 &headers)); | |
583 | |
584 // Decompress the frame | |
585 scoped_ptr<SpdyFrame> frame2(framer.DecompressFrame(*frame1.get())); | |
586 | |
587 EXPECT_EQ(NULL, frame2.get()); | |
588 } | |
589 | |
590 TEST_F(SpdyFramerTest, Basic) { | |
591 const unsigned char input[] = { | |
592 0x80, 0x02, 0x00, 0x01, // SYN Stream #1 | |
593 0x00, 0x00, 0x00, 0x14, | |
594 0x00, 0x00, 0x00, 0x01, | |
595 0x00, 0x00, 0x00, 0x00, | |
596 0x00, 0x00, 0x00, 0x01, | |
597 0x00, 0x02, 'h', 'h', | |
598 0x00, 0x02, 'v', 'v', | |
599 | |
600 0x80, 0x02, 0x00, 0x08, // HEADERS on Stream #1 | |
601 0x00, 0x00, 0x00, 0x18, | |
602 0x00, 0x00, 0x00, 0x01, | |
603 0x00, 0x00, 0x00, 0x02, | |
604 0x00, 0x02, 'h', '2', | |
605 0x00, 0x02, 'v', '2', | |
606 0x00, 0x02, 'h', '3', | |
607 0x00, 0x02, 'v', '3', | |
608 | |
609 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1 | |
610 0x00, 0x00, 0x00, 0x0c, | |
611 0xde, 0xad, 0xbe, 0xef, | |
612 0xde, 0xad, 0xbe, 0xef, | |
613 0xde, 0xad, 0xbe, 0xef, | |
614 | |
615 0x80, 0x02, 0x00, 0x01, // SYN Stream #3 | |
616 0x00, 0x00, 0x00, 0x0c, | |
617 0x00, 0x00, 0x00, 0x03, | |
618 0x00, 0x00, 0x00, 0x00, | |
619 0x00, 0x00, 0x00, 0x00, | |
620 | |
621 0x00, 0x00, 0x00, 0x03, // DATA on Stream #3 | |
622 0x00, 0x00, 0x00, 0x08, | |
623 0xde, 0xad, 0xbe, 0xef, | |
624 0xde, 0xad, 0xbe, 0xef, | |
625 | |
626 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1 | |
627 0x00, 0x00, 0x00, 0x04, | |
628 0xde, 0xad, 0xbe, 0xef, | |
629 | |
630 0x80, 0x02, 0x00, 0x03, // RST_STREAM on Stream #1 | |
631 0x00, 0x00, 0x00, 0x08, | |
632 0x00, 0x00, 0x00, 0x01, | |
633 0x00, 0x00, 0x00, 0x00, | |
634 | |
635 0x00, 0x00, 0x00, 0x03, // DATA on Stream #3 | |
636 0x00, 0x00, 0x00, 0x00, | |
637 | |
638 0x80, 0x02, 0x00, 0x03, // RST_STREAM on Stream #3 | |
639 0x00, 0x00, 0x00, 0x08, | |
640 0x00, 0x00, 0x00, 0x03, | |
641 0x00, 0x00, 0x00, 0x00, | |
642 }; | |
643 | |
644 TestSpdyVisitor visitor; | |
645 visitor.SimulateInFramer(input, sizeof(input)); | |
646 | |
647 EXPECT_EQ(0, visitor.error_count_); | |
648 EXPECT_EQ(2, visitor.syn_frame_count_); | |
649 EXPECT_EQ(0, visitor.syn_reply_frame_count_); | |
650 EXPECT_EQ(1, visitor.headers_frame_count_); | |
651 EXPECT_EQ(24, visitor.data_bytes_); | |
652 EXPECT_EQ(2, visitor.fin_frame_count_); | |
653 EXPECT_EQ(0, visitor.fin_flag_count_); | |
654 EXPECT_EQ(0, visitor.zero_length_data_frame_count_); | |
655 EXPECT_EQ(4, visitor.data_frame_count_); | |
656 } | |
657 | |
658 // Test that the FIN flag on a data frame signifies EOF. | |
659 TEST_F(SpdyFramerTest, FinOnDataFrame) { | |
660 const unsigned char input[] = { | |
661 0x80, 0x02, 0x00, 0x01, // SYN Stream #1 | |
662 0x00, 0x00, 0x00, 0x14, | |
663 0x00, 0x00, 0x00, 0x01, | |
664 0x00, 0x00, 0x00, 0x00, | |
665 0x00, 0x00, 0x00, 0x01, | |
666 0x00, 0x02, 'h', 'h', | |
667 0x00, 0x02, 'v', 'v', | |
668 | |
669 0x80, 0x02, 0x00, 0x02, // SYN REPLY Stream #1 | |
670 0x00, 0x00, 0x00, 0x10, | |
671 0x00, 0x00, 0x00, 0x01, | |
672 0x00, 0x00, 0x00, 0x01, | |
673 0x00, 0x02, 'a', 'a', | |
674 0x00, 0x02, 'b', 'b', | |
675 | |
676 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1 | |
677 0x00, 0x00, 0x00, 0x0c, | |
678 0xde, 0xad, 0xbe, 0xef, | |
679 0xde, 0xad, 0xbe, 0xef, | |
680 0xde, 0xad, 0xbe, 0xef, | |
681 | |
682 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1, with EOF | |
683 0x01, 0x00, 0x00, 0x04, | |
684 0xde, 0xad, 0xbe, 0xef, | |
685 }; | |
686 | |
687 TestSpdyVisitor visitor; | |
688 visitor.SimulateInFramer(input, sizeof(input)); | |
689 | |
690 EXPECT_EQ(0, visitor.error_count_); | |
691 EXPECT_EQ(1, visitor.syn_frame_count_); | |
692 EXPECT_EQ(1, visitor.syn_reply_frame_count_); | |
693 EXPECT_EQ(0, visitor.headers_frame_count_); | |
694 EXPECT_EQ(16, visitor.data_bytes_); | |
695 EXPECT_EQ(0, visitor.fin_frame_count_); | |
696 EXPECT_EQ(0, visitor.fin_flag_count_); | |
697 EXPECT_EQ(1, visitor.zero_length_data_frame_count_); | |
698 EXPECT_EQ(2, visitor.data_frame_count_); | |
699 } | |
700 | |
701 // Test that the FIN flag on a SYN reply frame signifies EOF. | |
702 TEST_F(SpdyFramerTest, FinOnSynReplyFrame) { | |
703 const unsigned char input[] = { | |
704 0x80, 0x02, 0x00, 0x01, // SYN Stream #1 | |
705 0x00, 0x00, 0x00, 0x14, | |
706 0x00, 0x00, 0x00, 0x01, | |
707 0x00, 0x00, 0x00, 0x00, | |
708 0x00, 0x00, 0x00, 0x01, | |
709 0x00, 0x02, 'h', 'h', | |
710 0x00, 0x02, 'v', 'v', | |
711 | |
712 0x80, 0x02, 0x00, 0x02, // SYN REPLY Stream #1 | |
713 0x01, 0x00, 0x00, 0x14, | |
714 0x00, 0x00, 0x00, 0x01, | |
715 0x00, 0x00, 0x00, 0x00, | |
716 0x00, 0x00, 0x00, 0x01, | |
717 0x00, 0x02, 'a', 'a', | |
718 0x00, 0x02, 'b', 'b', | |
719 }; | |
720 | |
721 TestSpdyVisitor visitor; | |
722 visitor.SimulateInFramer(input, sizeof(input)); | |
723 | |
724 EXPECT_EQ(0, visitor.error_count_); | |
725 EXPECT_EQ(1, visitor.syn_frame_count_); | |
726 EXPECT_EQ(1, visitor.syn_reply_frame_count_); | |
727 EXPECT_EQ(0, visitor.headers_frame_count_); | |
728 EXPECT_EQ(0, visitor.data_bytes_); | |
729 EXPECT_EQ(0, visitor.fin_frame_count_); | |
730 EXPECT_EQ(1, visitor.fin_flag_count_); | |
731 EXPECT_EQ(1, visitor.zero_length_data_frame_count_); | |
732 EXPECT_EQ(0, visitor.data_frame_count_); | |
733 } | |
734 | |
735 TEST_F(SpdyFramerTest, HeaderCompression) { | |
736 SpdyFramer send_framer; | |
737 SpdyFramer recv_framer; | |
738 | |
739 send_framer.set_enable_compression(true); | |
740 recv_framer.set_enable_compression(true); | |
741 | |
742 const char kHeader1[] = "header1"; | |
743 const char kHeader2[] = "header2"; | |
744 const char kHeader3[] = "header3"; | |
745 const char kValue1[] = "value1"; | |
746 const char kValue2[] = "value2"; | |
747 const char kValue3[] = "value3"; | |
748 | |
749 // SYN_STREAM #1 | |
750 SpdyHeaderBlock block; | |
751 block[kHeader1] = kValue1; | |
752 block[kHeader2] = kValue2; | |
753 SpdyControlFlags flags(CONTROL_FLAG_NONE); | |
754 scoped_ptr<spdy::SpdyFrame> syn_frame_1( | |
755 send_framer.CreateSynStream(1, 0, 0, flags, true, &block)); | |
756 EXPECT_TRUE(syn_frame_1.get() != NULL); | |
757 | |
758 // SYN_STREAM #2 | |
759 block[kHeader3] = kValue3; | |
760 scoped_ptr<spdy::SpdyFrame> syn_frame_2( | |
761 send_framer.CreateSynStream(3, 0, 0, flags, true, &block)); | |
762 EXPECT_TRUE(syn_frame_2.get() != NULL); | |
763 | |
764 // Now start decompressing | |
765 scoped_ptr<SpdyFrame> decompressed; | |
766 scoped_ptr<SpdyFrame> decompressed_syn_frame; | |
767 SpdySynStreamControlFrame* syn_frame; | |
768 scoped_ptr<std::string> serialized_headers; | |
769 SpdyHeaderBlock decompressed_headers; | |
770 | |
771 // Decompress SYN_STREAM #1 | |
772 decompressed.reset(recv_framer.DecompressFrame(*syn_frame_1.get())); | |
773 EXPECT_TRUE(decompressed.get() != NULL); | |
774 EXPECT_TRUE(decompressed->is_control_frame()); | |
775 EXPECT_EQ(SYN_STREAM, | |
776 reinterpret_cast<SpdyControlFrame*>(decompressed.get())->type()); | |
777 decompressed_syn_frame.reset( | |
778 new SpdySynStreamControlFrame(decompressed->data(), false)); | |
779 syn_frame = reinterpret_cast<SpdySynStreamControlFrame*>( | |
780 decompressed_syn_frame.get()); | |
781 serialized_headers.reset(new std::string(syn_frame->header_block(), | |
782 syn_frame->header_block_len())); | |
783 EXPECT_TRUE(recv_framer.ParseHeaderBlockInBuffer(serialized_headers->c_str(), | |
784 serialized_headers->size(), | |
785 &decompressed_headers)); | |
786 EXPECT_EQ(2u, decompressed_headers.size()); | |
787 EXPECT_EQ(kValue1, decompressed_headers[kHeader1]); | |
788 EXPECT_EQ(kValue2, decompressed_headers[kHeader2]); | |
789 | |
790 // Decompress SYN_STREAM #2 | |
791 decompressed.reset(recv_framer.DecompressFrame(*syn_frame_2.get())); | |
792 EXPECT_TRUE(decompressed.get() != NULL); | |
793 EXPECT_TRUE(decompressed->is_control_frame()); | |
794 EXPECT_EQ(SYN_STREAM, | |
795 reinterpret_cast<SpdyControlFrame*>(decompressed.get())->type()); | |
796 decompressed_syn_frame.reset( | |
797 new SpdySynStreamControlFrame(decompressed->data(), false)); | |
798 syn_frame = reinterpret_cast<SpdySynStreamControlFrame*>( | |
799 decompressed_syn_frame.get()); | |
800 serialized_headers.reset(new std::string(syn_frame->header_block(), | |
801 syn_frame->header_block_len())); | |
802 decompressed_headers.clear(); | |
803 EXPECT_TRUE(recv_framer.ParseHeaderBlockInBuffer(serialized_headers->c_str(), | |
804 serialized_headers->size(), | |
805 &decompressed_headers)); | |
806 EXPECT_EQ(3u, decompressed_headers.size()); | |
807 EXPECT_EQ(kValue1, decompressed_headers[kHeader1]); | |
808 EXPECT_EQ(kValue2, decompressed_headers[kHeader2]); | |
809 EXPECT_EQ(kValue3, decompressed_headers[kHeader3]); | |
810 | |
811 // We didn't have data streams, so we shouldn't have (de)compressors. | |
812 EXPECT_EQ(0, send_framer.num_stream_compressors()); | |
813 EXPECT_EQ(0, send_framer.num_stream_decompressors()); | |
814 EXPECT_EQ(0, recv_framer.num_stream_compressors()); | |
815 EXPECT_EQ(0, recv_framer.num_stream_decompressors()); | |
816 } | |
817 | |
818 // Verify we don't leak when we leave streams unclosed | |
819 TEST_F(SpdyFramerTest, UnclosedStreamDataCompressors) { | |
820 SpdyFramer send_framer; | |
821 | |
822 send_framer.set_enable_compression(true); | |
823 | |
824 const char kHeader1[] = "header1"; | |
825 const char kHeader2[] = "header2"; | |
826 const char kValue1[] = "value1"; | |
827 const char kValue2[] = "value2"; | |
828 | |
829 SpdyHeaderBlock block; | |
830 block[kHeader1] = kValue1; | |
831 block[kHeader2] = kValue2; | |
832 SpdyControlFlags flags(CONTROL_FLAG_NONE); | |
833 scoped_ptr<spdy::SpdyFrame> syn_frame( | |
834 send_framer.CreateSynStream(1, 0, 0, flags, true, &block)); | |
835 EXPECT_TRUE(syn_frame.get() != NULL); | |
836 | |
837 const char bytes[] = "this is a test test test test test!"; | |
838 scoped_ptr<SpdyFrame> send_frame( | |
839 send_framer.CreateDataFrame( | |
840 1, bytes, arraysize(bytes), | |
841 DATA_FLAG_FIN)); | |
842 EXPECT_TRUE(send_frame.get() != NULL); | |
843 | |
844 // Run the inputs through the framer. | |
845 TestSpdyVisitor visitor; | |
846 visitor.use_compression_ = true; | |
847 const unsigned char* data; | |
848 data = reinterpret_cast<const unsigned char*>(syn_frame->data()); | |
849 visitor.SimulateInFramer(data, syn_frame->length() + SpdyFrame::kHeaderSize); | |
850 data = reinterpret_cast<const unsigned char*>(send_frame->data()); | |
851 visitor.SimulateInFramer(data, send_frame->length() + SpdyFrame::kHeaderSize); | |
852 | |
853 EXPECT_EQ(0, visitor.error_count_); | |
854 EXPECT_EQ(1, visitor.syn_frame_count_); | |
855 EXPECT_EQ(0, visitor.syn_reply_frame_count_); | |
856 EXPECT_EQ(0, visitor.headers_frame_count_); | |
857 EXPECT_EQ(arraysize(bytes), static_cast<unsigned>(visitor.data_bytes_)); | |
858 EXPECT_EQ(0, visitor.fin_frame_count_); | |
859 EXPECT_EQ(0, visitor.fin_flag_count_); | |
860 EXPECT_EQ(1, visitor.zero_length_data_frame_count_); | |
861 EXPECT_EQ(1, visitor.data_frame_count_); | |
862 | |
863 // We closed the streams, so all compressors should be down. | |
864 EXPECT_EQ(0, visitor.framer_.num_stream_compressors()); | |
865 EXPECT_EQ(0, visitor.framer_.num_stream_decompressors()); | |
866 EXPECT_EQ(0, send_framer.num_stream_compressors()); | |
867 EXPECT_EQ(0, send_framer.num_stream_decompressors()); | |
868 } | |
869 | |
870 TEST_F(SpdyFramerTest, WindowUpdateFrame) { | |
871 scoped_ptr<SpdyWindowUpdateControlFrame> window_update_frame( | |
872 SpdyFramer::CreateWindowUpdate(1, 0x12345678)); | |
873 | |
874 const unsigned char expected_data_frame[] = { | |
875 0x80, 0x02, 0x00, 0x09, | |
876 0x00, 0x00, 0x00, 0x08, | |
877 0x00, 0x00, 0x00, 0x01, | |
878 0x12, 0x34, 0x56, 0x78 | |
879 }; | |
880 | |
881 EXPECT_EQ(16u, window_update_frame->size()); | |
882 EXPECT_EQ(0, | |
883 memcmp(window_update_frame->data(), expected_data_frame, 16)); | |
884 } | |
885 | |
886 TEST_F(SpdyFramerTest, CreateDataFrame) { | |
887 SpdyFramer framer; | |
888 | |
889 { | |
890 const char kDescription[] = "'hello' data frame, no FIN"; | |
891 const unsigned char kFrameData[] = { | |
892 0x00, 0x00, 0x00, 0x01, | |
893 0x00, 0x00, 0x00, 0x05, | |
894 'h', 'e', 'l', 'l', | |
895 'o' | |
896 }; | |
897 scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( | |
898 1, "hello", 5, DATA_FLAG_NONE)); | |
899 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
900 } | |
901 | |
902 { | |
903 const char kDescription[] = "Data frame with negative data byte, no FIN"; | |
904 const unsigned char kFrameData[] = { | |
905 0x00, 0x00, 0x00, 0x01, | |
906 0x00, 0x00, 0x00, 0x01, | |
907 0xff | |
908 }; | |
909 scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( | |
910 1, "\xff", 1, DATA_FLAG_NONE)); | |
911 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
912 } | |
913 | |
914 { | |
915 const char kDescription[] = "'hello' data frame, with FIN"; | |
916 const unsigned char kFrameData[] = { | |
917 0x00, 0x00, 0x00, 0x01, | |
918 0x01, 0x00, 0x00, 0x05, | |
919 'h', 'e', 'l', 'l', | |
920 'o' | |
921 }; | |
922 scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( | |
923 1, "hello", 5, DATA_FLAG_FIN)); | |
924 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
925 } | |
926 | |
927 { | |
928 const char kDescription[] = "Empty data frame"; | |
929 const unsigned char kFrameData[] = { | |
930 0x00, 0x00, 0x00, 0x01, | |
931 0x00, 0x00, 0x00, 0x00, | |
932 }; | |
933 scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( | |
934 1, "", 0, DATA_FLAG_NONE)); | |
935 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
936 } | |
937 | |
938 { | |
939 const char kDescription[] = "Data frame with max stream ID"; | |
940 const unsigned char kFrameData[] = { | |
941 0x7f, 0xff, 0xff, 0xff, | |
942 0x01, 0x00, 0x00, 0x05, | |
943 'h', 'e', 'l', 'l', | |
944 'o' | |
945 }; | |
946 scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( | |
947 0x7fffffff, "hello", 5, DATA_FLAG_FIN)); | |
948 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
949 } | |
950 | |
951 { | |
952 const char kDescription[] = "Large data frame"; | |
953 const int kDataSize = 4 * 1024 * 1024; // 4 MB | |
954 const std::string kData(kDataSize, 'A'); | |
955 const unsigned char kFrameHeader[] = { | |
956 0x00, 0x00, 0x00, 0x01, | |
957 0x01, 0x40, 0x00, 0x00, | |
958 }; | |
959 | |
960 const int kFrameSize = arraysize(kFrameHeader) + kDataSize; | |
961 scoped_array<unsigned char> expected_frame_data( | |
962 new unsigned char[kFrameSize]); | |
963 memcpy(expected_frame_data.get(), kFrameHeader, arraysize(kFrameHeader)); | |
964 memset(expected_frame_data.get() + arraysize(kFrameHeader), 'A', kDataSize); | |
965 | |
966 scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame( | |
967 1, kData.data(), kData.size(), DATA_FLAG_FIN)); | |
968 CompareFrame(kDescription, *frame, expected_frame_data.get(), kFrameSize); | |
969 } | |
970 } | |
971 | |
972 TEST_F(SpdyFramerTest, CreateSynStreamUncompressed) { | |
973 SpdyFramer framer; | |
974 framer.set_enable_compression(false); | |
975 | |
976 { | |
977 const char kDescription[] = "SYN_STREAM frame, lowest pri, no FIN"; | |
978 | |
979 SpdyHeaderBlock headers; | |
980 headers["bar"] = "foo"; | |
981 headers["foo"] = "bar"; | |
982 | |
983 const unsigned char kFrameData[] = { | |
984 0x80, 0x02, 0x00, 0x01, | |
985 0x00, 0x00, 0x00, 0x20, | |
986 0x00, 0x00, 0x00, 0x01, | |
987 0x00, 0x00, 0x00, 0x00, | |
988 0xC0, 0x00, 0x00, 0x02, | |
989 0x00, 0x03, 'b', 'a', | |
990 'r', 0x00, 0x03, 'f', | |
991 'o', 'o', 0x00, 0x03, | |
992 'f', 'o', 'o', 0x00, | |
993 0x03, 'b', 'a', 'r' | |
994 }; | |
995 scoped_ptr<SpdyFrame> frame(framer.CreateSynStream( | |
996 1, 0, SPDY_PRIORITY_LOWEST, CONTROL_FLAG_NONE, | |
997 false, &headers)); | |
998 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
999 EXPECT_EQ(1u, SpdyFramer::GetControlFrameStreamId( | |
1000 reinterpret_cast<const SpdyControlFrame*>(frame.get()))); | |
1001 } | |
1002 | |
1003 { | |
1004 const char kDescription[] = | |
1005 "SYN_STREAM frame with a 0-length header name, highest pri, FIN, " | |
1006 "max stream ID"; | |
1007 | |
1008 SpdyHeaderBlock headers; | |
1009 headers[""] = "foo"; | |
1010 headers["foo"] = "bar"; | |
1011 | |
1012 const unsigned char kFrameData[] = { | |
1013 0x80, 0x02, 0x00, 0x01, | |
1014 0x01, 0x00, 0x00, 0x1D, | |
1015 0x7f, 0xff, 0xff, 0xff, | |
1016 0x7f, 0xff, 0xff, 0xff, | |
1017 0x00, 0x00, 0x00, 0x02, | |
1018 0x00, 0x00, 0x00, 0x03, | |
1019 'f', 'o', 'o', 0x00, | |
1020 0x03, 'f', 'o', 'o', | |
1021 0x00, 0x03, 'b', 'a', | |
1022 'r' | |
1023 }; | |
1024 scoped_ptr<SpdyFrame> frame(framer.CreateSynStream( | |
1025 0x7fffffff, 0x7fffffff, SPDY_PRIORITY_HIGHEST, CONTROL_FLAG_FIN, | |
1026 false, &headers)); | |
1027 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1028 } | |
1029 | |
1030 { | |
1031 const char kDescription[] = | |
1032 "SYN_STREAM frame with a 0-length header val, highest pri, FIN, " | |
1033 "max stream ID"; | |
1034 | |
1035 SpdyHeaderBlock headers; | |
1036 headers["bar"] = "foo"; | |
1037 headers["foo"] = ""; | |
1038 | |
1039 const unsigned char kFrameData[] = { | |
1040 0x80, 0x02, 0x00, 0x01, | |
1041 0x01, 0x00, 0x00, 0x1D, | |
1042 0x7f, 0xff, 0xff, 0xff, | |
1043 0x7f, 0xff, 0xff, 0xff, | |
1044 0x00, 0x00, 0x00, 0x02, | |
1045 0x00, 0x03, 'b', 'a', | |
1046 'r', 0x00, 0x03, 'f', | |
1047 'o', 'o', 0x00, 0x03, | |
1048 'f', 'o', 'o', 0x00, | |
1049 0x00 | |
1050 }; | |
1051 scoped_ptr<SpdyFrame> frame(framer.CreateSynStream( | |
1052 0x7fffffff, 0x7fffffff, SPDY_PRIORITY_HIGHEST, CONTROL_FLAG_FIN, | |
1053 false, &headers)); | |
1054 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1055 } | |
1056 } | |
1057 | |
1058 TEST_F(SpdyFramerTest, CreateSynStreamCompressed) { | |
1059 SpdyFramer framer; | |
1060 framer.set_enable_compression(true); | |
1061 | |
1062 { | |
1063 const char kDescription[] = | |
1064 "SYN_STREAM frame, lowest pri, no FIN"; | |
1065 | |
1066 SpdyHeaderBlock headers; | |
1067 headers["bar"] = "foo"; | |
1068 headers["foo"] = "bar"; | |
1069 | |
1070 const unsigned char kFrameData[] = { | |
1071 0x80, 0x02, 0x00, 0x01, | |
1072 0x00, 0x00, 0x00, 0x25, | |
1073 0x00, 0x00, 0x00, 0x01, | |
1074 0x00, 0x00, 0x00, 0x00, | |
1075 0xC0, 0x00, 0x38, 0xea, | |
1076 0xdf, 0xa2, 0x51, 0xb2, | |
1077 0x62, 0x60, 0x62, 0x60, | |
1078 0x4e, 0x4a, 0x2c, 0x62, | |
1079 0x60, 0x4e, 0xcb, 0xcf, | |
1080 0x87, 0x12, 0x40, 0x2e, | |
1081 0x00, 0x00, 0x00, 0xff, | |
1082 0xff | |
1083 }; | |
1084 scoped_ptr<SpdyFrame> frame(framer.CreateSynStream( | |
1085 1, 0, SPDY_PRIORITY_LOWEST, CONTROL_FLAG_NONE, | |
1086 true, &headers)); | |
1087 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1088 } | |
1089 } | |
1090 | |
1091 TEST_F(SpdyFramerTest, CreateSynReplyUncompressed) { | |
1092 SpdyFramer framer; | |
1093 framer.set_enable_compression(false); | |
1094 | |
1095 { | |
1096 const char kDescription[] = "SYN_REPLY frame, no FIN"; | |
1097 | |
1098 SpdyHeaderBlock headers; | |
1099 headers["bar"] = "foo"; | |
1100 headers["foo"] = "bar"; | |
1101 | |
1102 const unsigned char kFrameData[] = { | |
1103 0x80, 0x02, 0x00, 0x02, | |
1104 0x00, 0x00, 0x00, 0x1C, | |
1105 0x00, 0x00, 0x00, 0x01, | |
1106 0x00, 0x00, 0x00, 0x02, | |
1107 0x00, 0x03, 'b', 'a', | |
1108 'r', 0x00, 0x03, 'f', | |
1109 'o', 'o', 0x00, 0x03, | |
1110 'f', 'o', 'o', 0x00, | |
1111 0x03, 'b', 'a', 'r' | |
1112 }; | |
1113 scoped_ptr<SpdyFrame> frame(framer.CreateSynReply( | |
1114 1, CONTROL_FLAG_NONE, false, &headers)); | |
1115 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1116 } | |
1117 | |
1118 { | |
1119 const char kDescription[] = | |
1120 "SYN_REPLY frame with a 0-length header name, FIN, max stream ID"; | |
1121 | |
1122 SpdyHeaderBlock headers; | |
1123 headers[""] = "foo"; | |
1124 headers["foo"] = "bar"; | |
1125 | |
1126 const unsigned char kFrameData[] = { | |
1127 0x80, 0x02, 0x00, 0x02, | |
1128 0x01, 0x00, 0x00, 0x19, | |
1129 0x7f, 0xff, 0xff, 0xff, | |
1130 0x00, 0x00, 0x00, 0x02, | |
1131 0x00, 0x00, 0x00, 0x03, | |
1132 'f', 'o', 'o', 0x00, | |
1133 0x03, 'f', 'o', 'o', | |
1134 0x00, 0x03, 'b', 'a', | |
1135 'r' | |
1136 }; | |
1137 scoped_ptr<SpdyFrame> frame(framer.CreateSynReply( | |
1138 0x7fffffff, CONTROL_FLAG_FIN, false, &headers)); | |
1139 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1140 } | |
1141 | |
1142 { | |
1143 const char kDescription[] = | |
1144 "SYN_REPLY frame with a 0-length header val, FIN, max stream ID"; | |
1145 | |
1146 SpdyHeaderBlock headers; | |
1147 headers["bar"] = "foo"; | |
1148 headers["foo"] = ""; | |
1149 | |
1150 const unsigned char kFrameData[] = { | |
1151 0x80, 0x02, 0x00, 0x02, | |
1152 0x01, 0x00, 0x00, 0x19, | |
1153 0x7f, 0xff, 0xff, 0xff, | |
1154 0x00, 0x00, 0x00, 0x02, | |
1155 0x00, 0x03, 'b', 'a', | |
1156 'r', 0x00, 0x03, 'f', | |
1157 'o', 'o', 0x00, 0x03, | |
1158 'f', 'o', 'o', 0x00, | |
1159 0x00 | |
1160 }; | |
1161 scoped_ptr<SpdyFrame> frame(framer.CreateSynReply( | |
1162 0x7fffffff, CONTROL_FLAG_FIN, false, &headers)); | |
1163 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1164 } | |
1165 } | |
1166 | |
1167 TEST_F(SpdyFramerTest, CreateSynReplyCompressed) { | |
1168 SpdyFramer framer; | |
1169 framer.set_enable_compression(true); | |
1170 | |
1171 { | |
1172 const char kDescription[] = "SYN_REPLY frame, no FIN"; | |
1173 | |
1174 SpdyHeaderBlock headers; | |
1175 headers["bar"] = "foo"; | |
1176 headers["foo"] = "bar"; | |
1177 | |
1178 const unsigned char kFrameData[] = { | |
1179 0x80, 0x02, 0x00, 0x02, | |
1180 0x00, 0x00, 0x00, 0x21, | |
1181 0x00, 0x00, 0x00, 0x01, | |
1182 0x00, 0x00, 0x38, 0xea, | |
1183 0xdf, 0xa2, 0x51, 0xb2, | |
1184 0x62, 0x60, 0x62, 0x60, | |
1185 0x4e, 0x4a, 0x2c, 0x62, | |
1186 0x60, 0x4e, 0xcb, 0xcf, | |
1187 0x87, 0x12, 0x40, 0x2e, | |
1188 0x00, 0x00, 0x00, 0xff, | |
1189 0xff | |
1190 }; | |
1191 scoped_ptr<SpdyFrame> frame(framer.CreateSynReply( | |
1192 1, CONTROL_FLAG_NONE, true, &headers)); | |
1193 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1194 } | |
1195 } | |
1196 | |
1197 TEST_F(SpdyFramerTest, CreateRstStream) { | |
1198 SpdyFramer framer; | |
1199 | |
1200 { | |
1201 const char kDescription[] = "RST_STREAM frame"; | |
1202 const unsigned char kFrameData[] = { | |
1203 0x80, 0x02, 0x00, 0x03, | |
1204 0x00, 0x00, 0x00, 0x08, | |
1205 0x00, 0x00, 0x00, 0x01, | |
1206 0x00, 0x00, 0x00, 0x01, | |
1207 }; | |
1208 scoped_ptr<SpdyFrame> frame(framer.CreateRstStream(1, PROTOCOL_ERROR)); | |
1209 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1210 EXPECT_EQ(1u, SpdyFramer::GetControlFrameStreamId( | |
1211 reinterpret_cast<const SpdyControlFrame*>(frame.get()))); | |
1212 } | |
1213 | |
1214 { | |
1215 const char kDescription[] = "RST_STREAM frame with max stream ID"; | |
1216 const unsigned char kFrameData[] = { | |
1217 0x80, 0x02, 0x00, 0x03, | |
1218 0x00, 0x00, 0x00, 0x08, | |
1219 0x7f, 0xff, 0xff, 0xff, | |
1220 0x00, 0x00, 0x00, 0x01, | |
1221 }; | |
1222 scoped_ptr<SpdyFrame> frame(framer.CreateRstStream(0x7FFFFFFF, | |
1223 PROTOCOL_ERROR)); | |
1224 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1225 } | |
1226 | |
1227 { | |
1228 const char kDescription[] = "RST_STREAM frame with max status code"; | |
1229 const unsigned char kFrameData[] = { | |
1230 0x80, 0x02, 0x00, 0x03, | |
1231 0x00, 0x00, 0x00, 0x08, | |
1232 0x7f, 0xff, 0xff, 0xff, | |
1233 0x00, 0x00, 0x00, 0x06, | |
1234 }; | |
1235 scoped_ptr<SpdyFrame> frame(framer.CreateRstStream(0x7FFFFFFF, | |
1236 INTERNAL_ERROR)); | |
1237 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1238 } | |
1239 } | |
1240 | |
1241 TEST_F(SpdyFramerTest, CreateSettings) { | |
1242 SpdyFramer framer; | |
1243 | |
1244 { | |
1245 const char kDescription[] = "Basic SETTINGS frame"; | |
1246 | |
1247 SpdySettings settings; | |
1248 settings.push_back(SpdySetting(0x00000000, 0x00000000)); | |
1249 settings.push_back(SpdySetting(0xffffffff, 0x00000001)); | |
1250 settings.push_back(SpdySetting(0xff000001, 0x00000002)); | |
1251 | |
1252 // Duplicates allowed | |
1253 settings.push_back(SpdySetting(0x01000002, 0x00000003)); | |
1254 settings.push_back(SpdySetting(0x01000002, 0x00000003)); | |
1255 | |
1256 settings.push_back(SpdySetting(0x01000003, 0x000000ff)); | |
1257 settings.push_back(SpdySetting(0x01000004, 0xff000001)); | |
1258 settings.push_back(SpdySetting(0x01000004, 0xffffffff)); | |
1259 | |
1260 const unsigned char kFrameData[] = { | |
1261 0x80, 0x02, 0x00, 0x04, | |
1262 0x00, 0x00, 0x00, 0x44, | |
1263 0x00, 0x00, 0x00, 0x08, | |
1264 0x00, 0x00, 0x00, 0x00, | |
1265 0x00, 0x00, 0x00, 0x00, | |
1266 0xff, 0xff, 0xff, 0xff, | |
1267 0x00, 0x00, 0x00, 0x01, | |
1268 0xff, 0x00, 0x00, 0x01, | |
1269 0x00, 0x00, 0x00, 0x02, | |
1270 0x01, 0x00, 0x00, 0x02, | |
1271 0x00, 0x00, 0x00, 0x03, | |
1272 0x01, 0x00, 0x00, 0x02, | |
1273 0x00, 0x00, 0x00, 0x03, | |
1274 0x01, 0x00, 0x00, 0x03, | |
1275 0x00, 0x00, 0x00, 0xff, | |
1276 0x01, 0x00, 0x00, 0x04, | |
1277 0xff, 0x00, 0x00, 0x01, | |
1278 0x01, 0x00, 0x00, 0x04, | |
1279 0xff, 0xff, 0xff, 0xff, | |
1280 }; | |
1281 scoped_ptr<SpdyFrame> frame(framer.CreateSettings(settings)); | |
1282 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1283 EXPECT_EQ(SpdyFramer::kInvalidStream, | |
1284 SpdyFramer::GetControlFrameStreamId( | |
1285 reinterpret_cast<const SpdyControlFrame*>(frame.get()))); | |
1286 } | |
1287 | |
1288 { | |
1289 const char kDescription[] = "Empty SETTINGS frame"; | |
1290 | |
1291 SpdySettings settings; | |
1292 | |
1293 const unsigned char kFrameData[] = { | |
1294 0x80, 0x02, 0x00, 0x04, | |
1295 0x00, 0x00, 0x00, 0x04, | |
1296 0x00, 0x00, 0x00, 0x00, | |
1297 }; | |
1298 scoped_ptr<SpdyFrame> frame(framer.CreateSettings(settings)); | |
1299 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1300 } | |
1301 } | |
1302 | |
1303 TEST_F(SpdyFramerTest, CreateNopFrame) { | |
1304 SpdyFramer framer; | |
1305 | |
1306 { | |
1307 const char kDescription[] = "NOOP frame"; | |
1308 const unsigned char kFrameData[] = { | |
1309 0x80, 0x02, 0x00, 0x05, | |
1310 0x00, 0x00, 0x00, 0x00, | |
1311 }; | |
1312 scoped_ptr<SpdyFrame> frame(framer.CreateNopFrame()); | |
1313 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1314 EXPECT_EQ(SpdyFramer::kInvalidStream, | |
1315 SpdyFramer::GetControlFrameStreamId( | |
1316 reinterpret_cast<const SpdyControlFrame*>(frame.get()))); | |
1317 } | |
1318 } | |
1319 | |
1320 TEST_F(SpdyFramerTest, CreatePingFrame) { | |
1321 SpdyFramer framer; | |
1322 | |
1323 { | |
1324 const char kDescription[] = "PING frame"; | |
1325 const unsigned char kFrameData[] = { | |
1326 0x80, 0x02, 0x00, 0x06, | |
1327 0x00, 0x00, 0x00, 0x04, | |
1328 0x12, 0x34, 0x56, 0x78, | |
1329 }; | |
1330 scoped_ptr<SpdyFrame> frame(framer.CreatePingFrame(0x12345678u)); | |
1331 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1332 EXPECT_EQ(SpdyFramer::kInvalidStream, | |
1333 SpdyFramer::GetControlFrameStreamId( | |
1334 reinterpret_cast<const SpdyControlFrame*>(frame.get()))); | |
1335 } | |
1336 } | |
1337 | |
1338 TEST_F(SpdyFramerTest, CreateGoAway) { | |
1339 SpdyFramer framer; | |
1340 | |
1341 { | |
1342 const char kDescription[] = "GOAWAY frame"; | |
1343 const unsigned char kFrameData[] = { | |
1344 0x80, 0x02, 0x00, 0x07, | |
1345 0x00, 0x00, 0x00, 0x04, | |
1346 0x00, 0x00, 0x00, 0x00, | |
1347 }; | |
1348 scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(0)); | |
1349 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1350 EXPECT_EQ(SpdyFramer::kInvalidStream, | |
1351 SpdyFramer::GetControlFrameStreamId( | |
1352 reinterpret_cast<const SpdyControlFrame*>(frame.get()))); | |
1353 } | |
1354 | |
1355 { | |
1356 const char kDescription[] = "GOAWAY frame with max stream ID"; | |
1357 const unsigned char kFrameData[] = { | |
1358 0x80, 0x02, 0x00, 0x07, | |
1359 0x00, 0x00, 0x00, 0x04, | |
1360 0x7f, 0xff, 0xff, 0xff, | |
1361 }; | |
1362 scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(0x7FFFFFFF)); | |
1363 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1364 } | |
1365 } | |
1366 | |
1367 TEST_F(SpdyFramerTest, CreateHeadersUncompressed) { | |
1368 SpdyFramer framer; | |
1369 framer.set_enable_compression(false); | |
1370 | |
1371 { | |
1372 const char kDescription[] = "HEADERS frame, no FIN"; | |
1373 | |
1374 SpdyHeaderBlock headers; | |
1375 headers["bar"] = "foo"; | |
1376 headers["foo"] = "bar"; | |
1377 | |
1378 const unsigned char kFrameData[] = { | |
1379 0x80, 0x02, 0x00, 0x08, | |
1380 0x00, 0x00, 0x00, 0x1C, | |
1381 0x00, 0x00, 0x00, 0x01, | |
1382 0x00, 0x00, 0x00, 0x02, | |
1383 0x00, 0x03, 'b', 'a', | |
1384 'r', 0x00, 0x03, 'f', | |
1385 'o', 'o', 0x00, 0x03, | |
1386 'f', 'o', 'o', 0x00, | |
1387 0x03, 'b', 'a', 'r' | |
1388 }; | |
1389 scoped_ptr<SpdyFrame> frame(framer.CreateHeaders( | |
1390 1, CONTROL_FLAG_NONE, false, &headers)); | |
1391 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1392 } | |
1393 | |
1394 { | |
1395 const char kDescription[] = | |
1396 "HEADERS frame with a 0-length header name, FIN, max stream ID"; | |
1397 | |
1398 SpdyHeaderBlock headers; | |
1399 headers[""] = "foo"; | |
1400 headers["foo"] = "bar"; | |
1401 | |
1402 const unsigned char kFrameData[] = { | |
1403 0x80, 0x02, 0x00, 0x08, | |
1404 0x01, 0x00, 0x00, 0x19, | |
1405 0x7f, 0xff, 0xff, 0xff, | |
1406 0x00, 0x00, 0x00, 0x02, | |
1407 0x00, 0x00, 0x00, 0x03, | |
1408 'f', 'o', 'o', 0x00, | |
1409 0x03, 'f', 'o', 'o', | |
1410 0x00, 0x03, 'b', 'a', | |
1411 'r' | |
1412 }; | |
1413 scoped_ptr<SpdyFrame> frame(framer.CreateHeaders( | |
1414 0x7fffffff, CONTROL_FLAG_FIN, false, &headers)); | |
1415 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1416 } | |
1417 | |
1418 { | |
1419 const char kDescription[] = | |
1420 "HEADERS frame with a 0-length header val, FIN, max stream ID"; | |
1421 | |
1422 SpdyHeaderBlock headers; | |
1423 headers["bar"] = "foo"; | |
1424 headers["foo"] = ""; | |
1425 | |
1426 const unsigned char kFrameData[] = { | |
1427 0x80, 0x02, 0x00, 0x08, | |
1428 0x01, 0x00, 0x00, 0x19, | |
1429 0x7f, 0xff, 0xff, 0xff, | |
1430 0x00, 0x00, 0x00, 0x02, | |
1431 0x00, 0x03, 'b', 'a', | |
1432 'r', 0x00, 0x03, 'f', | |
1433 'o', 'o', 0x00, 0x03, | |
1434 'f', 'o', 'o', 0x00, | |
1435 0x00 | |
1436 }; | |
1437 scoped_ptr<SpdyFrame> frame(framer.CreateHeaders( | |
1438 0x7fffffff, CONTROL_FLAG_FIN, false, &headers)); | |
1439 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1440 } | |
1441 } | |
1442 | |
1443 TEST_F(SpdyFramerTest, CreateHeadersCompressed) { | |
1444 SpdyFramer framer; | |
1445 framer.set_enable_compression(true); | |
1446 | |
1447 { | |
1448 const char kDescription[] = "HEADERS frame, no FIN"; | |
1449 | |
1450 SpdyHeaderBlock headers; | |
1451 headers["bar"] = "foo"; | |
1452 headers["foo"] = "bar"; | |
1453 | |
1454 const unsigned char kFrameData[] = { | |
1455 0x80, 0x02, 0x00, 0x08, | |
1456 0x00, 0x00, 0x00, 0x21, | |
1457 0x00, 0x00, 0x00, 0x01, | |
1458 0x00, 0x00, 0x38, 0xea, | |
1459 0xdf, 0xa2, 0x51, 0xb2, | |
1460 0x62, 0x60, 0x62, 0x60, | |
1461 0x4e, 0x4a, 0x2c, 0x62, | |
1462 0x60, 0x4e, 0xcb, 0xcf, | |
1463 0x87, 0x12, 0x40, 0x2e, | |
1464 0x00, 0x00, 0x00, 0xff, | |
1465 0xff | |
1466 }; | |
1467 scoped_ptr<SpdyFrame> frame(framer.CreateHeaders( | |
1468 1, CONTROL_FLAG_NONE, true, &headers)); | |
1469 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1470 } | |
1471 } | |
1472 | |
1473 TEST_F(SpdyFramerTest, CreateWindowUpdate) { | |
1474 SpdyFramer framer; | |
1475 | |
1476 { | |
1477 const char kDescription[] = "WINDOW_UPDATE frame"; | |
1478 const unsigned char kFrameData[] = { | |
1479 0x80, 0x02, 0x00, 0x09, | |
1480 0x00, 0x00, 0x00, 0x08, | |
1481 0x00, 0x00, 0x00, 0x01, | |
1482 0x00, 0x00, 0x00, 0x01, | |
1483 }; | |
1484 scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(1, 1)); | |
1485 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1486 EXPECT_EQ(1u, SpdyFramer::GetControlFrameStreamId( | |
1487 reinterpret_cast<const SpdyControlFrame*>(frame.get()))); | |
1488 } | |
1489 | |
1490 { | |
1491 const char kDescription[] = "WINDOW_UPDATE frame with max stream ID"; | |
1492 const unsigned char kFrameData[] = { | |
1493 0x80, 0x02, 0x00, 0x09, | |
1494 0x00, 0x00, 0x00, 0x08, | |
1495 0x7f, 0xff, 0xff, 0xff, | |
1496 0x00, 0x00, 0x00, 0x01, | |
1497 }; | |
1498 scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(0x7FFFFFFF, 1)); | |
1499 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1500 } | |
1501 | |
1502 { | |
1503 const char kDescription[] = "WINDOW_UPDATE frame with max window delta"; | |
1504 const unsigned char kFrameData[] = { | |
1505 0x80, 0x02, 0x00, 0x09, | |
1506 0x00, 0x00, 0x00, 0x08, | |
1507 0x00, 0x00, 0x00, 0x01, | |
1508 0x7f, 0xff, 0xff, 0xff, | |
1509 }; | |
1510 scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(1, 0x7FFFFFFF)); | |
1511 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1512 } | |
1513 } | |
1514 | |
1515 TEST_F(SpdyFramerTest, CreateCredential) { | |
1516 SpdyFramer framer; | |
1517 | |
1518 { | |
1519 const char kDescription[] = "CREDENTIAL frame"; | |
1520 const unsigned char kFrameData[] = { | |
1521 0x80, 0x02, 0x00, 0x0A, | |
1522 0x00, 0x00, 0x00, 0x33, | |
1523 0x00, 0x03, 0x00, 0x00, | |
1524 0x00, 0x05, 'p', 'r', | |
1525 'o', 'o', 'f', 0x00, | |
1526 0x00, 0x00, 0x06, 'a', | |
1527 ' ', 'c', 'e', 'r', | |
1528 't', 0x00, 0x00, 0x00, | |
1529 0x0C, 'a', 'n', 'o', | |
1530 't', 'h', 'e', 'r', | |
1531 ' ', 'c', 'e', 'r', | |
1532 't', 0x00, 0x00, 0x00, | |
1533 0x0A, 'f', 'i', 'n', | |
1534 'a', 'l', ' ', 'c', | |
1535 'e', 'r', 't', | |
1536 }; | |
1537 SpdyCredential credential; | |
1538 credential.slot = 3; | |
1539 credential.proof = "proof"; | |
1540 credential.certs.push_back("a cert"); | |
1541 credential.certs.push_back("another cert"); | |
1542 credential.certs.push_back("final cert"); | |
1543 scoped_ptr<SpdyFrame> frame(framer.CreateCredentialFrame(credential)); | |
1544 CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData)); | |
1545 } | |
1546 } | |
1547 | |
1548 TEST_F(SpdyFramerTest, ParseCredentialFrame) { | |
1549 SpdyFramer framer; | |
1550 | |
1551 { | |
1552 unsigned char kFrameData[] = { | |
1553 0x80, 0x02, 0x00, 0x0A, | |
1554 0x00, 0x00, 0x00, 0x33, | |
1555 0x00, 0x03, 0x00, 0x00, | |
1556 0x00, 0x05, 'p', 'r', | |
1557 'o', 'o', 'f', 0x00, | |
1558 0x00, 0x00, 0x06, 'a', | |
1559 ' ', 'c', 'e', 'r', | |
1560 't', 0x00, 0x00, 0x00, | |
1561 0x0C, 'a', 'n', 'o', | |
1562 't', 'h', 'e', 'r', | |
1563 ' ', 'c', 'e', 'r', | |
1564 't', 0x00, 0x00, 0x00, | |
1565 0x0A, 'f', 'i', 'n', | |
1566 'a', 'l', ' ', 'c', | |
1567 'e', 'r', 't', | |
1568 }; | |
1569 SpdyCredentialControlFrame frame(reinterpret_cast<char*>(kFrameData), | |
1570 false); | |
1571 SpdyCredential credential; | |
1572 EXPECT_TRUE(SpdyFramer::ParseCredentialData(frame.payload(), | |
1573 frame.length(), | |
1574 &credential)); | |
1575 EXPECT_EQ(3u, credential.slot); | |
1576 EXPECT_EQ("proof", credential.proof); | |
1577 EXPECT_EQ("a cert", credential.certs.front()); | |
1578 credential.certs.erase(credential.certs.begin()); | |
1579 EXPECT_EQ("another cert", credential.certs.front()); | |
1580 credential.certs.erase(credential.certs.begin()); | |
1581 EXPECT_EQ("final cert", credential.certs.front()); | |
1582 credential.certs.erase(credential.certs.begin()); | |
1583 EXPECT_TRUE(credential.certs.empty()); | |
1584 } | |
1585 } | |
1586 | |
1587 TEST_F(SpdyFramerTest, DuplicateFrame) { | |
1588 SpdyFramer framer; | |
1589 | |
1590 { | |
1591 const char kDescription[] = "PING frame"; | |
1592 const unsigned char kFrameData[] = { | |
1593 0x80, 0x02, 0x00, 0x06, | |
1594 0x00, 0x00, 0x00, 0x04, | |
1595 0x12, 0x34, 0x56, 0x78, | |
1596 }; | |
1597 scoped_ptr<SpdyFrame> frame1(framer.CreatePingFrame(0x12345678u)); | |
1598 CompareFrame(kDescription, *frame1, kFrameData, arraysize(kFrameData)); | |
1599 | |
1600 scoped_ptr<SpdyFrame> frame2(framer.DuplicateFrame(*frame1)); | |
1601 CompareFrame(kDescription, *frame2, kFrameData, arraysize(kFrameData)); | |
1602 } | |
1603 } | |
1604 | |
1605 // This test case reproduces conditions that caused ExpandControlFrameBuffer to | |
1606 // fail to expand the buffer control frame buffer when it should have, allowing | |
1607 // the framer to overrun the buffer, and smash other heap contents. This test | |
1608 // relies on the debug version of the heap manager, which checks for buffer | |
1609 // overrun errors during delete processing. Regression test for b/2974814. | |
1610 TEST_F(SpdyFramerTest, ExpandBuffer_HeapSmash) { | |
1611 // Sweep through the area of problematic values, to make sure we always cover | |
1612 // the danger zone, even if it moves around at bit due to SPDY changes. | |
1613 for (uint16 val2_len = SpdyFramer::kControlFrameBufferInitialSize - 50; | |
1614 val2_len < SpdyFramer::kControlFrameBufferInitialSize; | |
1615 val2_len++) { | |
1616 std::string val2 = std::string(val2_len, 'a'); | |
1617 SpdyHeaderBlock headers; | |
1618 headers["bar"] = "foo"; | |
1619 headers["foo"] = "baz"; | |
1620 headers["grue"] = val2.c_str(); | |
1621 SpdyFramer framer; | |
1622 scoped_ptr<SpdySynStreamControlFrame> template_frame( | |
1623 framer.CreateSynStream(1, // stream_id | |
1624 0, // associated_stream_id | |
1625 1, // priority | |
1626 CONTROL_FLAG_NONE, | |
1627 false, // compress | |
1628 &headers)); | |
1629 EXPECT_TRUE(template_frame.get() != NULL); | |
1630 TestSpdyVisitor visitor; | |
1631 visitor.SimulateInFramer( | |
1632 reinterpret_cast<unsigned char*>(template_frame.get()->data()), | |
1633 template_frame.get()->length() + SpdyControlFrame::kHeaderSize); | |
1634 EXPECT_EQ(1, visitor.syn_frame_count_); | |
1635 } | |
1636 } | |
1637 | |
1638 TEST_F(SpdyFramerTest, ReadCredentialFrame) { | |
1639 SpdyCredential credential; | |
1640 credential.slot = 3; | |
1641 credential.proof = "proof"; | |
1642 credential.certs.push_back("a cert"); | |
1643 credential.certs.push_back("another cert"); | |
1644 credential.certs.push_back("final cert"); | |
1645 SpdyFramer framer; | |
1646 scoped_ptr<SpdyFrame> control_frame( | |
1647 framer.CreateCredentialFrame(credential)); | |
1648 EXPECT_TRUE(control_frame.get() != NULL); | |
1649 TestSpdyVisitor visitor; | |
1650 visitor.use_compression_ = false; | |
1651 visitor.SimulateInFramer( | |
1652 reinterpret_cast<unsigned char*>(control_frame.get()->data()), | |
1653 control_frame.get()->length() + SpdyControlFrame::kHeaderSize); | |
1654 EXPECT_EQ(0, visitor.error_count_); | |
1655 EXPECT_EQ(1, visitor.credential_count_); | |
1656 EXPECT_EQ(control_frame->length(), visitor.credential_buffer_length_); | |
1657 EXPECT_EQ(credential.slot, visitor.credential_.slot); | |
1658 EXPECT_EQ(credential.proof, visitor.credential_.proof); | |
1659 EXPECT_EQ(credential.certs.size(), visitor.credential_.certs.size()); | |
1660 for (size_t i = 0; i < credential.certs.size(); i++) { | |
1661 EXPECT_EQ(credential.certs[i], visitor.credential_.certs[i]); | |
1662 } | |
1663 } | |
1664 | |
1665 TEST_F(SpdyFramerTest, ReadCredentialFrameWithCorruptProof) { | |
1666 SpdyCredential credential; | |
1667 credential.slot = 3; | |
1668 credential.proof = "proof"; | |
1669 credential.certs.push_back("a cert"); | |
1670 credential.certs.push_back("another cert"); | |
1671 credential.certs.push_back("final cert"); | |
1672 SpdyFramer framer; | |
1673 scoped_ptr<SpdyFrame> control_frame( | |
1674 framer.CreateCredentialFrame(credential)); | |
1675 EXPECT_TRUE(control_frame.get() != NULL); | |
1676 TestSpdyVisitor visitor; | |
1677 visitor.use_compression_ = false; | |
1678 unsigned char* data = | |
1679 reinterpret_cast<unsigned char*>(control_frame.get()->data()); | |
1680 size_t offset = SpdyControlFrame::kHeaderSize + 4; | |
1681 data[offset] = 0xFF; // Proof length is past the end of the frame | |
1682 visitor.SimulateInFramer( | |
1683 data, control_frame.get()->length() + SpdyControlFrame::kHeaderSize); | |
1684 EXPECT_EQ(1, visitor.error_count_); | |
1685 } | |
1686 | |
1687 TEST_F(SpdyFramerTest, ReadCredentialFrameWithCorruptCertificate) { | |
1688 SpdyCredential credential; | |
1689 credential.slot = 3; | |
1690 credential.proof = "proof"; | |
1691 credential.certs.push_back("a cert"); | |
1692 credential.certs.push_back("another cert"); | |
1693 credential.certs.push_back("final cert"); | |
1694 SpdyFramer framer; | |
1695 scoped_ptr<SpdyFrame> control_frame( | |
1696 framer.CreateCredentialFrame(credential)); | |
1697 EXPECT_TRUE(control_frame.get() != NULL); | |
1698 TestSpdyVisitor visitor; | |
1699 visitor.use_compression_ = false; | |
1700 unsigned char* data = | |
1701 reinterpret_cast<unsigned char*>(control_frame.get()->data()); | |
1702 size_t offset = SpdyControlFrame::kHeaderSize + credential.proof.length(); | |
1703 data[offset] = 0xFF; // Certificate length is past the end of the frame | |
1704 visitor.SimulateInFramer( | |
1705 data, control_frame.get()->length() + SpdyControlFrame::kHeaderSize); | |
1706 EXPECT_EQ(1, visitor.error_count_); | |
1707 } | |
1708 | |
1709 TEST_F(SpdyFramerTest, ReadGarbage) { | |
1710 SpdyFramer framer; | |
1711 unsigned char garbage_frame[256]; | |
1712 memset(garbage_frame, ~0, sizeof(garbage_frame)); | |
1713 TestSpdyVisitor visitor; | |
1714 visitor.use_compression_ = false; | |
1715 visitor.SimulateInFramer(garbage_frame, sizeof(garbage_frame)); | |
1716 EXPECT_EQ(1, visitor.error_count_); | |
1717 } | |
1718 | |
1719 TEST_F(SpdyFramerTest, ReadGarbageWithValidVersion) { | |
1720 SpdyFramer framer; | |
1721 char garbage_frame[256]; | |
1722 memset(garbage_frame, ~0, sizeof(garbage_frame)); | |
1723 SpdyControlFrame control_frame(&garbage_frame[0], false); | |
1724 control_frame.set_version(kSpdyProtocolVersion); | |
1725 TestSpdyVisitor visitor; | |
1726 visitor.use_compression_ = false; | |
1727 visitor.SimulateInFramer( | |
1728 reinterpret_cast<unsigned char*>(control_frame.data()), | |
1729 sizeof(garbage_frame)); | |
1730 EXPECT_EQ(1, visitor.error_count_); | |
1731 } | |
1732 | |
1733 TEST(SpdyFramer, StateToStringTest) { | |
1734 EXPECT_STREQ("ERROR", | |
1735 SpdyFramer::StateToString(SpdyFramer::SPDY_ERROR)); | |
1736 EXPECT_STREQ("DONE", | |
1737 SpdyFramer::StateToString(SpdyFramer::SPDY_DONE)); | |
1738 EXPECT_STREQ("AUTO_RESET", | |
1739 SpdyFramer::StateToString(SpdyFramer::SPDY_AUTO_RESET)); | |
1740 EXPECT_STREQ("RESET", | |
1741 SpdyFramer::StateToString(SpdyFramer::SPDY_RESET)); | |
1742 EXPECT_STREQ("READING_COMMON_HEADER", | |
1743 SpdyFramer::StateToString( | |
1744 SpdyFramer::SPDY_READING_COMMON_HEADER)); | |
1745 EXPECT_STREQ("CONTROL_FRAME_PAYLOAD", | |
1746 SpdyFramer::StateToString( | |
1747 SpdyFramer::SPDY_CONTROL_FRAME_PAYLOAD)); | |
1748 EXPECT_STREQ("IGNORE_REMAINING_PAYLOAD", | |
1749 SpdyFramer::StateToString( | |
1750 SpdyFramer::SPDY_IGNORE_REMAINING_PAYLOAD)); | |
1751 EXPECT_STREQ("FORWARD_STREAM_FRAME", | |
1752 SpdyFramer::StateToString( | |
1753 SpdyFramer::SPDY_FORWARD_STREAM_FRAME)); | |
1754 EXPECT_STREQ("SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK", | |
1755 SpdyFramer::StateToString( | |
1756 SpdyFramer::SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK)); | |
1757 EXPECT_STREQ("SPDY_CONTROL_FRAME_HEADER_BLOCK", | |
1758 SpdyFramer::StateToString( | |
1759 SpdyFramer::SPDY_CONTROL_FRAME_HEADER_BLOCK)); | |
1760 EXPECT_STREQ("SPDY_CREDENTIAL_FRAME_PAYLOAD", | |
1761 SpdyFramer::StateToString( | |
1762 SpdyFramer::SPDY_CREDENTIAL_FRAME_PAYLOAD)); | |
1763 EXPECT_STREQ("UNKNOWN_STATE", | |
1764 SpdyFramer::StateToString( | |
1765 SpdyFramer::SPDY_CREDENTIAL_FRAME_PAYLOAD + 1)); | |
1766 } | |
1767 | |
1768 TEST(SpdyFramer, ErrorCodeToStringTest) { | |
1769 EXPECT_STREQ("NO_ERROR", | |
1770 SpdyFramer::ErrorCodeToString(SpdyFramer::SPDY_NO_ERROR)); | |
1771 EXPECT_STREQ("INVALID_CONTROL_FRAME", | |
1772 SpdyFramer::ErrorCodeToString( | |
1773 SpdyFramer::SPDY_INVALID_CONTROL_FRAME)); | |
1774 EXPECT_STREQ("CONTROL_PAYLOAD_TOO_LARGE", | |
1775 SpdyFramer::ErrorCodeToString( | |
1776 SpdyFramer::SPDY_CONTROL_PAYLOAD_TOO_LARGE)); | |
1777 EXPECT_STREQ("ZLIB_INIT_FAILURE", | |
1778 SpdyFramer::ErrorCodeToString( | |
1779 SpdyFramer::SPDY_ZLIB_INIT_FAILURE)); | |
1780 EXPECT_STREQ("UNSUPPORTED_VERSION", | |
1781 SpdyFramer::ErrorCodeToString( | |
1782 SpdyFramer::SPDY_UNSUPPORTED_VERSION)); | |
1783 EXPECT_STREQ("DECOMPRESS_FAILURE", | |
1784 SpdyFramer::ErrorCodeToString( | |
1785 SpdyFramer::SPDY_DECOMPRESS_FAILURE)); | |
1786 EXPECT_STREQ("COMPRESS_FAILURE", | |
1787 SpdyFramer::ErrorCodeToString( | |
1788 SpdyFramer::SPDY_COMPRESS_FAILURE)); | |
1789 EXPECT_STREQ("UNKNOWN_ERROR", | |
1790 SpdyFramer::ErrorCodeToString(SpdyFramer::LAST_ERROR)); | |
1791 } | |
1792 | |
1793 TEST(SpdyFramer, StatusCodeToStringTest) { | |
1794 EXPECT_STREQ("INVALID", | |
1795 SpdyFramer::StatusCodeToString(INVALID)); | |
1796 EXPECT_STREQ("PROTOCOL_ERROR", | |
1797 SpdyFramer::StatusCodeToString(PROTOCOL_ERROR)); | |
1798 EXPECT_STREQ("INVALID_STREAM", | |
1799 SpdyFramer::StatusCodeToString(INVALID_STREAM)); | |
1800 EXPECT_STREQ("REFUSED_STREAM", | |
1801 SpdyFramer::StatusCodeToString(REFUSED_STREAM)); | |
1802 EXPECT_STREQ("UNSUPPORTED_VERSION", | |
1803 SpdyFramer::StatusCodeToString(UNSUPPORTED_VERSION)); | |
1804 EXPECT_STREQ("CANCEL", | |
1805 SpdyFramer::StatusCodeToString(CANCEL)); | |
1806 EXPECT_STREQ("INTERNAL_ERROR", | |
1807 SpdyFramer::StatusCodeToString(INTERNAL_ERROR)); | |
1808 EXPECT_STREQ("FLOW_CONTROL_ERROR", | |
1809 SpdyFramer::StatusCodeToString(FLOW_CONTROL_ERROR)); | |
1810 EXPECT_STREQ("UNKNOWN_STATUS", | |
1811 SpdyFramer::StatusCodeToString(NUM_STATUS_CODES)); | |
1812 } | |
1813 | |
1814 TEST(SpdyFramer, ControlTypeToStringTest) { | |
1815 EXPECT_STREQ("SYN_STREAM", | |
1816 SpdyFramer::ControlTypeToString(SYN_STREAM)); | |
1817 EXPECT_STREQ("SYN_REPLY", | |
1818 SpdyFramer::ControlTypeToString(SYN_REPLY)); | |
1819 EXPECT_STREQ("RST_STREAM", | |
1820 SpdyFramer::ControlTypeToString(RST_STREAM)); | |
1821 EXPECT_STREQ("SETTINGS", | |
1822 SpdyFramer::ControlTypeToString(SETTINGS)); | |
1823 EXPECT_STREQ("NOOP", | |
1824 SpdyFramer::ControlTypeToString(NOOP)); | |
1825 EXPECT_STREQ("PING", | |
1826 SpdyFramer::ControlTypeToString(PING)); | |
1827 EXPECT_STREQ("GOAWAY", | |
1828 SpdyFramer::ControlTypeToString(GOAWAY)); | |
1829 EXPECT_STREQ("HEADERS", | |
1830 SpdyFramer::ControlTypeToString(HEADERS)); | |
1831 EXPECT_STREQ("WINDOW_UPDATE", | |
1832 SpdyFramer::ControlTypeToString(WINDOW_UPDATE)); | |
1833 EXPECT_STREQ("SETTINGS", | |
1834 SpdyFramer::ControlTypeToString(SETTINGS)); | |
1835 EXPECT_STREQ("UNKNOWN_CONTROL_TYPE", | |
1836 SpdyFramer::ControlTypeToString(NUM_CONTROL_FRAME_TYPES)); | |
1837 } | |
1838 | |
1839 TEST(SpdyFramer, GetMinimumControlFrameSizeTest) { | |
1840 EXPECT_EQ(SpdySynStreamControlFrame::size(), | |
1841 SpdyFramer::GetMinimumControlFrameSize(SYN_STREAM)); | |
1842 EXPECT_EQ(SpdySynReplyControlFrame::size(), | |
1843 SpdyFramer::GetMinimumControlFrameSize(SYN_REPLY)); | |
1844 EXPECT_EQ(SpdyRstStreamControlFrame::size(), | |
1845 SpdyFramer::GetMinimumControlFrameSize(RST_STREAM)); | |
1846 EXPECT_EQ(SpdySettingsControlFrame::size(), | |
1847 SpdyFramer::GetMinimumControlFrameSize(SETTINGS)); | |
1848 EXPECT_EQ(SpdyNoOpControlFrame::size(), | |
1849 SpdyFramer::GetMinimumControlFrameSize(NOOP)); | |
1850 EXPECT_EQ(SpdyPingControlFrame::size(), | |
1851 SpdyFramer::GetMinimumControlFrameSize(PING)); | |
1852 EXPECT_EQ(SpdyGoAwayControlFrame::size(), | |
1853 SpdyFramer::GetMinimumControlFrameSize(GOAWAY)); | |
1854 EXPECT_EQ(SpdyHeadersControlFrame::size(), | |
1855 SpdyFramer::GetMinimumControlFrameSize(HEADERS)); | |
1856 EXPECT_EQ(SpdyWindowUpdateControlFrame::size(), | |
1857 SpdyFramer::GetMinimumControlFrameSize(WINDOW_UPDATE)); | |
1858 EXPECT_EQ(SpdyCredentialControlFrame::size(), | |
1859 SpdyFramer::GetMinimumControlFrameSize(CREDENTIAL)); | |
1860 EXPECT_EQ(static_cast<size_t>(0x7FFFFFFF), | |
1861 SpdyFramer::GetMinimumControlFrameSize(NUM_CONTROL_FRAME_TYPES)); | |
1862 } | |
1863 | |
1864 std::string RandomString(int length) { | |
1865 std::string rv; | |
1866 for (int index = 0; index < length; index++) | |
1867 rv += static_cast<char>('a' + (rand() % 26)); | |
1868 return rv; | |
1869 } | |
1870 | |
1871 } // namespace | |
OLD | NEW |