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