spdy_interface_test.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/tools/flip_server/spdy_interface.h"
6
7#include <list>
8
9#include "base/memory/scoped_ptr.h"
10#include "base/strings/string_piece.h"
11#include "net/spdy/buffered_spdy_framer.h"
12#include "net/tools/balsa/balsa_enums.h"
13#include "net/tools/balsa/balsa_headers.h"
14#include "net/tools/flip_server/flip_config.h"
15#include "net/tools/flip_server/flip_test_utils.h"
16#include "net/tools/flip_server/mem_cache.h"
17#include "testing/gmock/include/gmock/gmock.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20namespace net {
21
22using ::base::StringPiece;
23using ::testing::_;
24using ::testing::InSequence;
25using ::testing::InvokeWithoutArgs;
26using ::testing::Return;
27using ::testing::SaveArg;
28using ::testing::Values;
29
30namespace {
31
32struct StringSaver {
33 public:
34  StringSaver() : data(NULL), size(0) {}
35  void Save() { string = std::string(data, size); }
36
37  const char* data;
38  size_t size;
39  std::string string;
40};
41
42class SpdyFramerVisitor : public BufferedSpdyFramerVisitorInterface {
43 public:
44  virtual ~SpdyFramerVisitor() {}
45  MOCK_METHOD1(OnError, void(SpdyFramer::SpdyError));
46  MOCK_METHOD2(OnStreamError, void(SpdyStreamId, const std::string&));
47  MOCK_METHOD7(OnSynStream,
48               void(SpdyStreamId,
49                    SpdyStreamId,
50                    SpdyPriority,
51                    uint8,
52                    bool,
53                    bool,
54                    const SpdyHeaderBlock&));
55  MOCK_METHOD3(OnSynStream, void(SpdyStreamId, bool, const SpdyHeaderBlock&));
56  MOCK_METHOD3(OnSynReply, void(SpdyStreamId, bool, const SpdyHeaderBlock&));
57  MOCK_METHOD3(OnHeaders, void(SpdyStreamId, bool, const SpdyHeaderBlock&));
58  MOCK_METHOD3(OnDataFrameHeader, void(SpdyStreamId, size_t, bool));
59  MOCK_METHOD4(OnStreamFrameData, void(SpdyStreamId,
60                                       const char*,
61                                       size_t,
62                                       bool));
63  MOCK_METHOD1(OnSettings, void(bool clear_persisted));
64  MOCK_METHOD3(OnSetting, void(SpdySettingsIds, uint8, uint32));
65  MOCK_METHOD1(OnPing, void(uint32 unique_id));
66  MOCK_METHOD2(OnRstStream, void(SpdyStreamId, SpdyRstStreamStatus));
67  MOCK_METHOD2(OnGoAway, void(SpdyStreamId, SpdyGoAwayStatus));
68  MOCK_METHOD2(OnWindowUpdate, void(SpdyStreamId, uint32));
69  MOCK_METHOD2(OnPushPromise, void(SpdyStreamId, SpdyStreamId));
70};
71
72class FakeSMConnection : public SMConnection {
73 public:
74  FakeSMConnection(EpollServer* epoll_server,
75                   SSLState* ssl_state,
76                   MemoryCache* memory_cache,
77                   FlipAcceptor* acceptor,
78                   std::string log_prefix)
79      : SMConnection(epoll_server,
80                     ssl_state,
81                     memory_cache,
82                     acceptor,
83                     log_prefix) {}
84
85  MOCK_METHOD0(Cleanup, void());
86  MOCK_METHOD8(InitSMConnection,
87               void(SMConnectionPoolInterface*,
88                    SMInterface*,
89                    EpollServer*,
90                    int,
91                    std::string,
92                    std::string,
93                    std::string,
94                    bool));
95};
96
97// This class is almost SpdySM, except one function.
98// This class is the test target of tests in this file.
99class TestSpdySM : public SpdySM {
100 public:
101  virtual ~TestSpdySM() {}
102  TestSpdySM(SMConnection* connection,
103             SMInterface* sm_http_interface,
104             EpollServer* epoll_server,
105             MemoryCache* memory_cache,
106             FlipAcceptor* acceptor,
107             SpdyMajorVersion version)
108      : SpdySM(connection,
109               sm_http_interface,
110               epoll_server,
111               memory_cache,
112               acceptor,
113               version) {}
114
115  MOCK_METHOD2(FindOrMakeNewSMConnectionInterface,
116               SMInterface*(const std::string&, const std::string&));
117};
118
119class SpdySMTestBase : public ::testing::TestWithParam<SpdyMajorVersion> {
120 public:
121  explicit SpdySMTestBase(FlipHandlerType type) {
122    SSLState* ssl_state = NULL;
123    mock_another_interface_.reset(new MockSMInterface);
124    memory_cache_.reset(new MemoryCache);
125    acceptor_.reset(new FlipAcceptor(type,
126                                     "127.0.0.1",
127                                     "8941",
128                                     "ssl_cert_filename",
129                                     "ssl_key_filename",
130                                     "127.0.0.1",
131                                     "8942",
132                                     "127.0.0.1",
133                                     "8943",
134                                     1,
135                                     0,
136                                     true,
137                                     1,
138                                     false,
139                                     true,
140                                     NULL));
141    epoll_server_.reset(new EpollServer);
142    connection_.reset(new FakeSMConnection(epoll_server_.get(),
143                                           ssl_state,
144                                           memory_cache_.get(),
145                                           acceptor_.get(),
146                                           "log_prefix"));
147
148    interface_.reset(new TestSpdySM(connection_.get(),
149                                    mock_another_interface_.get(),
150                                    epoll_server_.get(),
151                                    memory_cache_.get(),
152                                    acceptor_.get(),
153                                    GetParam()));
154
155    spdy_framer_.reset(new BufferedSpdyFramer(GetParam(), true));
156    spdy_framer_visitor_.reset(new SpdyFramerVisitor);
157    spdy_framer_->set_visitor(spdy_framer_visitor_.get());
158  }
159
160  virtual ~SpdySMTestBase() {
161    if (acceptor_->listen_fd_ >= 0) {
162      epoll_server_->UnregisterFD(acceptor_->listen_fd_);
163      close(acceptor_->listen_fd_);
164      acceptor_->listen_fd_ = -1;
165    }
166    OutputList& output_list = *connection_->output_list();
167    for (OutputList::const_iterator i = output_list.begin();
168         i != output_list.end();
169         ++i) {
170      delete *i;
171    }
172    output_list.clear();
173  }
174
175  bool HasStream(uint32 stream_id) {
176    return interface_->output_ordering().ExistsInPriorityMaps(stream_id);
177  }
178
179 protected:
180  scoped_ptr<MockSMInterface> mock_another_interface_;
181  scoped_ptr<MemoryCache> memory_cache_;
182  scoped_ptr<FlipAcceptor> acceptor_;
183  scoped_ptr<EpollServer> epoll_server_;
184  scoped_ptr<FakeSMConnection> connection_;
185  scoped_ptr<TestSpdySM> interface_;
186  scoped_ptr<BufferedSpdyFramer> spdy_framer_;
187  scoped_ptr<SpdyFramerVisitor> spdy_framer_visitor_;
188};
189
190class SpdySMProxyTest : public SpdySMTestBase {
191 public:
192  SpdySMProxyTest() : SpdySMTestBase(FLIP_HANDLER_PROXY) {}
193  virtual ~SpdySMProxyTest() {}
194};
195
196class SpdySMServerTest : public SpdySMTestBase {
197 public:
198  SpdySMServerTest() : SpdySMTestBase(FLIP_HANDLER_SPDY_SERVER) {}
199  virtual ~SpdySMServerTest() {}
200};
201
202INSTANTIATE_TEST_CASE_P(SpdySMProxyTest,
203                        SpdySMProxyTest,
204                        Values(SPDY2, SPDY3, SPDY4));
205INSTANTIATE_TEST_CASE_P(SpdySMServerTest, SpdySMServerTest, Values(SPDY2));
206
207TEST_P(SpdySMProxyTest, InitSMConnection) {
208  {
209    InSequence s;
210    EXPECT_CALL(*connection_, InitSMConnection(_, _, _, _, _, _, _, _));
211  }
212  interface_->InitSMConnection(
213      NULL, NULL, epoll_server_.get(), -1, "", "", "", false);
214}
215
216TEST_P(SpdySMProxyTest, OnSynStream_SPDY2) {
217  if (GetParam() != SPDY2) {
218    // This test case is for SPDY2.
219    return;
220  }
221  BufferedSpdyFramerVisitorInterface* visitor = interface_.get();
222  scoped_ptr<MockSMInterface> mock_interface(new MockSMInterface);
223  uint32 stream_id = 92;
224  uint32 associated_id = 43;
225  std::string expected = "GET /path HTTP/1.0\r\n"
226      "Host: 127.0.0.1\r\n"
227      "hoge: fuga\r\n\r\n";
228  SpdyHeaderBlock block;
229  block["method"] = "GET";
230  block["url"] = "/path";
231  block["scheme"] = "http";
232  block["version"] = "HTTP/1.0";
233  block["hoge"] = "fuga";
234  StringSaver saver;
235  {
236    InSequence s;
237    EXPECT_CALL(*interface_, FindOrMakeNewSMConnectionInterface(_, _))
238        .WillOnce(Return(mock_interface.get()));
239    EXPECT_CALL(*mock_interface, SetStreamID(stream_id));
240    EXPECT_CALL(*mock_interface, ProcessWriteInput(_, _))
241        .WillOnce(DoAll(SaveArg<0>(&saver.data),
242                        SaveArg<1>(&saver.size),
243                        InvokeWithoutArgs(&saver, &StringSaver::Save),
244                        Return(0)));
245  }
246  visitor->OnSynStream(stream_id, associated_id, 0, 0, false, false, block);
247  ASSERT_EQ(expected, saver.string);
248}
249
250TEST_P(SpdySMProxyTest, OnSynStream) {
251  if (GetParam() == SPDY2) {
252    // This test case is not for SPDY2.
253    return;
254  }
255  BufferedSpdyFramerVisitorInterface* visitor = interface_.get();
256  scoped_ptr<MockSMInterface> mock_interface(new MockSMInterface);
257  uint32 stream_id = 92;
258  uint32 associated_id = 43;
259  std::string expected = "GET /path HTTP/1.1\r\n"
260      "Host: 127.0.0.1\r\n"
261      "foo: bar\r\n\r\n";
262  SpdyHeaderBlock block;
263  block[":method"] = "GET";
264  block[":host"] = "www.example.com";
265  block[":path"] = "/path";
266  block[":scheme"] = "http";
267  block["foo"] = "bar";
268  StringSaver saver;
269  {
270    InSequence s;
271    EXPECT_CALL(*interface_,
272                FindOrMakeNewSMConnectionInterface(_, _))
273        .WillOnce(Return(mock_interface.get()));
274    EXPECT_CALL(*mock_interface, SetStreamID(stream_id));
275    EXPECT_CALL(*mock_interface, ProcessWriteInput(_, _))
276        .WillOnce(DoAll(SaveArg<0>(&saver.data),
277                        SaveArg<1>(&saver.size),
278                        InvokeWithoutArgs(&saver, &StringSaver::Save),
279                        Return(0)));
280  }
281  visitor->OnSynStream(stream_id, associated_id, 0, 0, false, false, block);
282  ASSERT_EQ(expected, saver.string);
283}
284
285TEST_P(SpdySMProxyTest, OnStreamFrameData_SPDY2) {
286  if (GetParam() != SPDY2) {
287    // This test case is for SPDY2.
288    return;
289  }
290  BufferedSpdyFramerVisitorInterface* visitor = interface_.get();
291  scoped_ptr<MockSMInterface> mock_interface(new MockSMInterface);
292  uint32 stream_id = 92;
293  uint32 associated_id = 43;
294  SpdyHeaderBlock block;
295  testing::MockFunction<void(int)> checkpoint;  // NOLINT
296
297  scoped_ptr<SpdyFrame> frame(spdy_framer_->CreatePingFrame(12));
298  block["method"] = "GET";
299  block["url"] = "http://www.example.com/path";
300  block["scheme"] = "http";
301  block["version"] = "HTTP/1.0";
302  {
303    InSequence s;
304    EXPECT_CALL(*interface_, FindOrMakeNewSMConnectionInterface(_, _))
305        .WillOnce(Return(mock_interface.get()));
306    EXPECT_CALL(*mock_interface, SetStreamID(stream_id));
307    EXPECT_CALL(*mock_interface, ProcessWriteInput(_, _)).Times(1);
308    EXPECT_CALL(checkpoint, Call(0));
309    EXPECT_CALL(*mock_interface,
310                ProcessWriteInput(frame->data(), frame->size())).Times(1);
311  }
312
313  visitor->OnSynStream(stream_id, associated_id, 0, 0, false, false, block);
314  checkpoint.Call(0);
315  visitor->OnStreamFrameData(stream_id, frame->data(), frame->size(), true);
316}
317
318TEST_P(SpdySMProxyTest, OnStreamFrameData) {
319  if (GetParam() == SPDY2) {
320    // This test case is not for SPDY2.
321    return;
322  }
323  BufferedSpdyFramerVisitorInterface* visitor = interface_.get();
324  scoped_ptr<MockSMInterface> mock_interface(new MockSMInterface);
325  uint32 stream_id = 92;
326  uint32 associated_id = 43;
327  SpdyHeaderBlock block;
328  testing::MockFunction<void(int)> checkpoint;  // NOLINT
329
330  scoped_ptr<SpdyFrame> frame(spdy_framer_->CreatePingFrame(12));
331  block[":method"] = "GET";
332  block[":host"] = "www.example.com";
333  block[":path"] = "/path";
334  block[":scheme"] = "http";
335  block["foo"] = "bar";
336  {
337    InSequence s;
338    EXPECT_CALL(*interface_,
339                FindOrMakeNewSMConnectionInterface(_, _))
340        .WillOnce(Return(mock_interface.get()));
341    EXPECT_CALL(*mock_interface, SetStreamID(stream_id));
342    EXPECT_CALL(*mock_interface, ProcessWriteInput(_, _)).Times(1);
343    EXPECT_CALL(checkpoint, Call(0));
344    EXPECT_CALL(*mock_interface,
345                ProcessWriteInput(frame->data(), frame->size())).Times(1);
346  }
347
348  visitor->OnSynStream(stream_id, associated_id, 0, 0, false, false, block);
349  checkpoint.Call(0);
350  visitor->OnStreamFrameData(stream_id, frame->data(), frame->size(), true);
351}
352
353TEST_P(SpdySMProxyTest, OnRstStream) {
354  BufferedSpdyFramerVisitorInterface* visitor = interface_.get();
355  uint32 stream_id = 82;
356  MemCacheIter mci;
357  mci.stream_id = stream_id;
358
359  {
360    BalsaHeaders headers;
361    std::string filename = "foobar";
362    memory_cache_->InsertFile(&headers, filename, "");
363    mci.file_data = memory_cache_->GetFileData(filename);
364  }
365
366  interface_->AddToOutputOrder(mci);
367  ASSERT_TRUE(HasStream(stream_id));
368  visitor->OnRstStream(stream_id, RST_STREAM_INVALID);
369  ASSERT_FALSE(HasStream(stream_id));
370}
371
372TEST_P(SpdySMProxyTest, ProcessReadInput) {
373  ASSERT_EQ(SpdyFramer::SPDY_RESET, interface_->spdy_framer()->state());
374  interface_->ProcessReadInput("", 1);
375  ASSERT_EQ(SpdyFramer::SPDY_READING_COMMON_HEADER,
376            interface_->spdy_framer()->state());
377}
378
379TEST_P(SpdySMProxyTest, ResetForNewConnection) {
380  uint32 stream_id = 13;
381  MemCacheIter mci;
382  mci.stream_id = stream_id;
383  // incomplete input
384  const char input[] = {'\0', '\0', '\0'};
385
386  {
387    BalsaHeaders headers;
388    std::string filename = "foobar";
389    memory_cache_->InsertFile(&headers, filename, "");
390    mci.file_data = memory_cache_->GetFileData(filename);
391  }
392
393  interface_->AddToOutputOrder(mci);
394  ASSERT_TRUE(HasStream(stream_id));
395  interface_->ProcessReadInput(input, sizeof(input));
396  ASSERT_NE(SpdyFramer::SPDY_RESET, interface_->spdy_framer()->state());
397
398  interface_->ResetForNewConnection();
399  ASSERT_FALSE(HasStream(stream_id));
400  ASSERT_EQ(SpdyFramer::SPDY_RESET, interface_->spdy_framer()->state());
401}
402
403TEST_P(SpdySMProxyTest, PostAcceptHook) {
404  interface_->PostAcceptHook();
405
406  ASSERT_EQ(1u, connection_->output_list()->size());
407  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
408  DataFrame* df = *i++;
409
410  {
411    InSequence s;
412    EXPECT_CALL(*spdy_framer_visitor_, OnSettings(false));
413    EXPECT_CALL(*spdy_framer_visitor_,
414                OnSetting(SETTINGS_MAX_CONCURRENT_STREAMS, 0u, 100u));
415  }
416  spdy_framer_->ProcessInput(df->data, df->size);
417}
418
419TEST_P(SpdySMProxyTest, NewStream) {
420  // TODO(yhirano): SpdySM::NewStream leads to crash when
421  // acceptor_->flip_handler_type_ != FLIP_HANDLER_SPDY_SERVER.
422  // It should be fixed though I don't know the solution now.
423}
424
425TEST_P(SpdySMProxyTest, AddToOutputOrder) {
426  uint32 stream_id = 13;
427  MemCacheIter mci;
428  mci.stream_id = stream_id;
429
430  {
431    BalsaHeaders headers;
432    std::string filename = "foobar";
433    memory_cache_->InsertFile(&headers, filename, "");
434    mci.file_data = memory_cache_->GetFileData(filename);
435  }
436
437  interface_->AddToOutputOrder(mci);
438  ASSERT_TRUE(HasStream(stream_id));
439}
440
441TEST_P(SpdySMProxyTest, SendErrorNotFound_SPDY2) {
442  if (GetParam() != SPDY2) {
443    // This test is for SPDY2.
444    return;
445  }
446  uint32 stream_id = 82;
447  SpdyHeaderBlock actual_header_block;
448  const char* actual_data;
449  size_t actual_size;
450  testing::MockFunction<void(int)> checkpoint;  // NOLINT
451
452  interface_->SendErrorNotFound(stream_id);
453
454  ASSERT_EQ(2u, connection_->output_list()->size());
455
456  {
457    InSequence s;
458    EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
459        .WillOnce(SaveArg<2>(&actual_header_block));
460    EXPECT_CALL(checkpoint, Call(0));
461    EXPECT_CALL(*spdy_framer_visitor_,
462                OnDataFrameHeader(stream_id, _, true));
463    EXPECT_CALL(*spdy_framer_visitor_,
464                OnStreamFrameData(stream_id, _, _, false)).Times(1)
465        .WillOnce(DoAll(SaveArg<1>(&actual_data),
466                        SaveArg<2>(&actual_size)));
467    EXPECT_CALL(*spdy_framer_visitor_,
468                OnStreamFrameData(stream_id, NULL, 0, true)).Times(1);
469  }
470
471  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
472  DataFrame* df = *i++;
473  spdy_framer_->ProcessInput(df->data, df->size);
474  checkpoint.Call(0);
475  df = *i++;
476  spdy_framer_->ProcessInput(df->data, df->size);
477
478  ASSERT_EQ(2, spdy_framer_->frames_received());
479  ASSERT_EQ(2u, actual_header_block.size());
480  ASSERT_EQ("404 Not Found", actual_header_block["status"]);
481  ASSERT_EQ("HTTP/1.1", actual_header_block["version"]);
482  ASSERT_EQ("wtf?", StringPiece(actual_data, actual_size));
483}
484
485TEST_P(SpdySMProxyTest, SendErrorNotFound) {
486  if (GetParam() == SPDY2) {
487    // This test is not for SPDY2.
488    return;
489  }
490  uint32 stream_id = 82;
491  SpdyHeaderBlock actual_header_block;
492  const char* actual_data;
493  size_t actual_size;
494  testing::MockFunction<void(int)> checkpoint;  // NOLINT
495
496  interface_->SendErrorNotFound(stream_id);
497
498  ASSERT_EQ(2u, connection_->output_list()->size());
499
500  {
501    InSequence s;
502    EXPECT_CALL(*spdy_framer_visitor_,
503                OnSynReply(stream_id, false, _))
504        .WillOnce(SaveArg<2>(&actual_header_block));
505    EXPECT_CALL(checkpoint, Call(0));
506    EXPECT_CALL(*spdy_framer_visitor_,
507                OnDataFrameHeader(stream_id, _, true));
508    EXPECT_CALL(*spdy_framer_visitor_,
509                OnStreamFrameData(stream_id, _, _, false)).Times(1)
510        .WillOnce(DoAll(SaveArg<1>(&actual_data),
511                        SaveArg<2>(&actual_size)));
512    EXPECT_CALL(*spdy_framer_visitor_,
513                OnStreamFrameData(stream_id, NULL, 0, true)).Times(1);
514  }
515
516  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
517  DataFrame* df = *i++;
518  spdy_framer_->ProcessInput(df->data, df->size);
519  checkpoint.Call(0);
520  df = *i++;
521  spdy_framer_->ProcessInput(df->data, df->size);
522
523  ASSERT_EQ(2, spdy_framer_->frames_received());
524  ASSERT_EQ(2u, actual_header_block.size());
525  ASSERT_EQ("404 Not Found", actual_header_block[":status"]);
526  ASSERT_EQ("HTTP/1.1", actual_header_block[":version"]);
527  ASSERT_EQ("wtf?", StringPiece(actual_data, actual_size));
528}
529
530TEST_P(SpdySMProxyTest, SendSynStream_SPDY2) {
531  if (GetParam() != SPDY2) {
532    // This test is for SPDY2.
533    return;
534  }
535  uint32 stream_id = 82;
536  BalsaHeaders headers;
537  SpdyHeaderBlock actual_header_block;
538  headers.AppendHeader("key1", "value1");
539  headers.SetRequestFirstlineFromStringPieces("GET", "/path", "HTTP/1.0");
540
541  interface_->SendSynStream(stream_id, headers);
542
543  ASSERT_EQ(1u, connection_->output_list()->size());
544  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
545  DataFrame* df = *i++;
546
547  {
548    InSequence s;
549    EXPECT_CALL(*spdy_framer_visitor_,
550                OnSynStream(stream_id, 0, _, _, false, false, _))
551        .WillOnce(SaveArg<6>(&actual_header_block));
552  }
553
554  spdy_framer_->ProcessInput(df->data, df->size);
555  ASSERT_EQ(1, spdy_framer_->frames_received());
556  ASSERT_EQ(4u, actual_header_block.size());
557  ASSERT_EQ("GET", actual_header_block["method"]);
558  ASSERT_EQ("HTTP/1.0", actual_header_block["version"]);
559  ASSERT_EQ("/path", actual_header_block["url"]);
560  ASSERT_EQ("value1", actual_header_block["key1"]);
561}
562
563TEST_P(SpdySMProxyTest, SendSynStream) {
564  if (GetParam() == SPDY2) {
565    // This test is not for SPDY2.
566    return;
567  }
568  uint32 stream_id = 82;
569  BalsaHeaders headers;
570  SpdyHeaderBlock actual_header_block;
571  headers.AppendHeader("key1", "value1");
572  headers.AppendHeader("Host", "www.example.com");
573  headers.SetRequestFirstlineFromStringPieces("GET", "/path", "HTTP/1.1");
574
575  interface_->SendSynStream(stream_id, headers);
576
577  ASSERT_EQ(1u, connection_->output_list()->size());
578  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
579  DataFrame* df = *i++;
580
581  {
582    InSequence s;
583    EXPECT_CALL(*spdy_framer_visitor_,
584                OnSynStream(stream_id, 0, _, _, false, false, _))
585        .WillOnce(SaveArg<6>(&actual_header_block));
586  }
587
588  spdy_framer_->ProcessInput(df->data, df->size);
589  ASSERT_EQ(1, spdy_framer_->frames_received());
590  ASSERT_EQ(5u, actual_header_block.size());
591  ASSERT_EQ("GET", actual_header_block[":method"]);
592  ASSERT_EQ("HTTP/1.1", actual_header_block[":version"]);
593  ASSERT_EQ("/path", actual_header_block[":path"]);
594  ASSERT_EQ("www.example.com", actual_header_block[":host"]);
595  ASSERT_EQ("value1", actual_header_block["key1"]);
596}
597
598TEST_P(SpdySMProxyTest, SendSynReply_SPDY2) {
599  if (GetParam() != SPDY2) {
600    // This test is for SPDY2.
601    return;
602  }
603  uint32 stream_id = 82;
604  BalsaHeaders headers;
605  SpdyHeaderBlock actual_header_block;
606  headers.AppendHeader("key1", "value1");
607  headers.SetResponseFirstlineFromStringPieces("HTTP/1.1", "200", "OK");
608
609  interface_->SendSynReply(stream_id, headers);
610
611  ASSERT_EQ(1u, connection_->output_list()->size());
612  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
613  DataFrame* df = *i++;
614
615  {
616    InSequence s;
617    EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
618        .WillOnce(SaveArg<2>(&actual_header_block));
619  }
620
621  spdy_framer_->ProcessInput(df->data, df->size);
622  ASSERT_EQ(1, spdy_framer_->frames_received());
623  ASSERT_EQ(3u, actual_header_block.size());
624  ASSERT_EQ("200 OK", actual_header_block["status"]);
625  ASSERT_EQ("HTTP/1.1", actual_header_block["version"]);
626  ASSERT_EQ("value1", actual_header_block["key1"]);
627}
628
629TEST_P(SpdySMProxyTest, SendSynReply) {
630  if (GetParam() == SPDY2) {
631    // This test is not for SPDY2.
632    return;
633  }
634  uint32 stream_id = 82;
635  BalsaHeaders headers;
636  SpdyHeaderBlock actual_header_block;
637  headers.AppendHeader("key1", "value1");
638  headers.SetResponseFirstlineFromStringPieces("HTTP/1.1", "200", "OK");
639
640  interface_->SendSynReply(stream_id, headers);
641
642  ASSERT_EQ(1u, connection_->output_list()->size());
643  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
644  DataFrame* df = *i++;
645
646  {
647    InSequence s;
648    EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
649        .WillOnce(SaveArg<2>(&actual_header_block));
650  }
651
652  spdy_framer_->ProcessInput(df->data, df->size);
653  ASSERT_EQ(1, spdy_framer_->frames_received());
654  ASSERT_EQ(3u, actual_header_block.size());
655  ASSERT_EQ("200 OK", actual_header_block[":status"]);
656  ASSERT_EQ("HTTP/1.1", actual_header_block[":version"]);
657  ASSERT_EQ("value1", actual_header_block["key1"]);
658}
659
660TEST_P(SpdySMProxyTest, SendDataFrame) {
661  uint32 stream_id = 133;
662  SpdyDataFlags flags = DATA_FLAG_NONE;
663  const char* actual_data;
664  size_t actual_size;
665
666  interface_->SendDataFrame(stream_id, "hello", 5, flags, true);
667
668  ASSERT_EQ(1u, connection_->output_list()->size());
669  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
670  DataFrame* df = *i++;
671
672  {
673    InSequence s;
674    EXPECT_CALL(*spdy_framer_visitor_,
675                OnDataFrameHeader(stream_id, _, false));
676    EXPECT_CALL(*spdy_framer_visitor_,
677                OnStreamFrameData(stream_id, _, _, false))
678        .WillOnce(DoAll(SaveArg<1>(&actual_data), SaveArg<2>(&actual_size)));
679  }
680
681  spdy_framer_->ProcessInput(df->data, df->size);
682  ASSERT_EQ(1, spdy_framer_->frames_received());
683  ASSERT_EQ("hello", StringPiece(actual_data, actual_size));
684}
685
686TEST_P(SpdySMProxyTest, SendLongDataFrame) {
687  uint32 stream_id = 133;
688  SpdyDataFlags flags = DATA_FLAG_NONE;
689  const char* actual_data;
690  size_t actual_size;
691
692  std::string data = std::string(kSpdySegmentSize, 'a') +
693                     std::string(kSpdySegmentSize, 'b') + "c";
694  interface_->SendDataFrame(stream_id, data.data(), data.size(), flags, true);
695
696  {
697    InSequence s;
698    for (int i = 0; i < 3; ++i) {
699        EXPECT_CALL(*spdy_framer_visitor_,
700                    OnDataFrameHeader(stream_id, _, false));
701        EXPECT_CALL(*spdy_framer_visitor_,
702                    OnStreamFrameData(stream_id, _, _, false))
703            .WillOnce(DoAll(SaveArg<1>(&actual_data),
704                            SaveArg<2>(&actual_size)));
705    }
706  }
707
708  ASSERT_EQ(3u, connection_->output_list()->size());
709  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
710  DataFrame* df = *i++;
711  spdy_framer_->ProcessInput(df->data, df->size);
712  ASSERT_EQ(std::string(kSpdySegmentSize, 'a'),
713            StringPiece(actual_data, actual_size));
714
715  df = *i++;
716  spdy_framer_->ProcessInput(df->data, df->size);
717  ASSERT_EQ(std::string(kSpdySegmentSize, 'b'),
718            StringPiece(actual_data, actual_size));
719
720  df = *i++;
721  spdy_framer_->ProcessInput(df->data, df->size);
722  ASSERT_EQ("c", StringPiece(actual_data, actual_size));
723}
724
725TEST_P(SpdySMProxyTest, SendEOF_SPDY2) {
726  // This test is for SPDY2.
727  if (GetParam() != SPDY2) {
728    return;
729  }
730
731  uint32 stream_id = 82;
732  // SPDY2 data frame
733  char empty_data_frame[] = {'\0', '\0', '\0', '\x52', '\x1', '\0', '\0', '\0'};
734  MemCacheIter mci;
735  mci.stream_id = stream_id;
736
737  {
738    BalsaHeaders headers;
739    std::string filename = "foobar";
740    memory_cache_->InsertFile(&headers, filename, "");
741    mci.file_data = memory_cache_->GetFileData(filename);
742  }
743
744  interface_->AddToOutputOrder(mci);
745  ASSERT_TRUE(HasStream(stream_id));
746  interface_->SendEOF(stream_id);
747  ASSERT_FALSE(HasStream(stream_id));
748
749  ASSERT_EQ(1u, connection_->output_list()->size());
750  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
751  DataFrame* df = *i++;
752  ASSERT_EQ(StringPiece(empty_data_frame, sizeof(empty_data_frame)),
753            StringPiece(df->data, df->size));
754}
755
756TEST_P(SpdySMProxyTest, SendEmptyDataFrame_SPDY2) {
757  // This test is for SPDY2.
758  if (GetParam() != SPDY2) {
759    return;
760  }
761
762  uint32 stream_id = 133;
763  SpdyDataFlags flags = DATA_FLAG_NONE;
764  // SPDY2 data frame
765  char expected[] = {'\0', '\0', '\0', '\x85', '\0', '\0', '\0', '\0'};
766
767  interface_->SendDataFrame(stream_id, "hello", 0, flags, true);
768
769  ASSERT_EQ(1u, connection_->output_list()->size());
770  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
771  DataFrame* df = *i++;
772
773  ASSERT_EQ(StringPiece(expected, sizeof(expected)),
774            StringPiece(df->data, df->size));
775}
776
777TEST_P(SpdySMServerTest, OnSynStream) {
778  BufferedSpdyFramerVisitorInterface* visitor = interface_.get();
779  uint32 stream_id = 82;
780  SpdyHeaderBlock spdy_headers;
781  spdy_headers["url"] = "http://www.example.com/path";
782  spdy_headers["method"] = "GET";
783  spdy_headers["scheme"] = "http";
784  spdy_headers["version"] = "HTTP/1.1";
785
786  {
787    BalsaHeaders headers;
788    memory_cache_->InsertFile(&headers, "GET_/path", "");
789  }
790  visitor->OnSynStream(stream_id, 0, 0, 0, true, true, spdy_headers);
791  ASSERT_TRUE(HasStream(stream_id));
792}
793
794TEST_P(SpdySMServerTest, NewStream) {
795  uint32 stream_id = 13;
796  std::string filename = "foobar";
797
798  {
799    BalsaHeaders headers;
800    memory_cache_->InsertFile(&headers, filename, "");
801  }
802
803  interface_->NewStream(stream_id, 0, filename);
804  ASSERT_TRUE(HasStream(stream_id));
805}
806
807TEST_P(SpdySMServerTest, NewStreamError) {
808  uint32 stream_id = 82;
809  SpdyHeaderBlock actual_header_block;
810  const char* actual_data;
811  size_t actual_size;
812  testing::MockFunction<void(int)> checkpoint;  // NOLINT
813
814  interface_->NewStream(stream_id, 0, "nonexistingfile");
815
816  ASSERT_EQ(2u, connection_->output_list()->size());
817
818  {
819    InSequence s;
820    EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _))
821        .WillOnce(SaveArg<2>(&actual_header_block));
822    EXPECT_CALL(checkpoint, Call(0));
823    EXPECT_CALL(*spdy_framer_visitor_,
824                OnDataFrameHeader(stream_id, _, true));
825    EXPECT_CALL(*spdy_framer_visitor_,
826                OnStreamFrameData(stream_id, _, _, false)).Times(1)
827        .WillOnce(DoAll(SaveArg<1>(&actual_data),
828                        SaveArg<2>(&actual_size)));
829    EXPECT_CALL(*spdy_framer_visitor_,
830                OnStreamFrameData(stream_id, NULL, 0, true)).Times(1);
831  }
832
833  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
834  DataFrame* df = *i++;
835  spdy_framer_->ProcessInput(df->data, df->size);
836  checkpoint.Call(0);
837  df = *i++;
838  spdy_framer_->ProcessInput(df->data, df->size);
839
840  ASSERT_EQ(2, spdy_framer_->frames_received());
841  ASSERT_EQ(2u, actual_header_block.size());
842  ASSERT_EQ("404 Not Found", actual_header_block["status"]);
843  ASSERT_EQ("HTTP/1.1", actual_header_block["version"]);
844  ASSERT_EQ("wtf?", StringPiece(actual_data, actual_size));
845}
846
847}  // namespace
848
849}  // namespace net
850