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/http_interface.h"
6
7#include <list>
8
9#include "base/memory/scoped_ptr.h"
10#include "base/stl_util.h"
11#include "base/strings/string_piece.h"
12#include "net/tools/balsa/balsa_enums.h"
13#include "net/tools/balsa/balsa_frame.h"
14#include "net/tools/balsa/balsa_headers.h"
15#include "net/tools/flip_server/flip_config.h"
16#include "net/tools/flip_server/flip_test_utils.h"
17#include "net/tools/flip_server/mem_cache.h"
18#include "testing/gmock/include/gmock/gmock.h"
19#include "testing/gtest/include/gtest/gtest.h"
20
21namespace net {
22
23using ::base::StringPiece;
24using ::testing::_;
25using ::testing::InSequence;
26
27namespace {
28
29class MockSMConnection : public SMConnection {
30 public:
31  MockSMConnection(EpollServer* epoll_server,
32                   SSLState* ssl_state,
33                   MemoryCache* memory_cache,
34                   FlipAcceptor* acceptor,
35                   std::string log_prefix)
36      : SMConnection(epoll_server,
37                     ssl_state,
38                     memory_cache,
39                     acceptor,
40                     log_prefix) {}
41
42  MOCK_METHOD0(Cleanup, void());
43  MOCK_METHOD8(InitSMConnection,
44               void(SMConnectionPoolInterface*,
45                    SMInterface*,
46                    EpollServer*,
47                    int,
48                    std::string,
49                    std::string,
50                    std::string,
51                    bool));
52};
53
54class FlipHttpSMTest : public ::testing::Test {
55 public:
56  explicit FlipHttpSMTest(FlipHandlerType type = FLIP_HANDLER_PROXY) {
57    SSLState* ssl_state = NULL;
58    mock_another_interface_.reset(new MockSMInterface);
59    memory_cache_.reset(new MemoryCache);
60    acceptor_.reset(new FlipAcceptor(type,
61                                     "127.0.0.1",
62                                     "8941",
63                                     "ssl_cert_filename",
64                                     "ssl_key_filename",
65                                     "127.0.0.1",
66                                     "8942",
67                                     "127.0.0.1",
68                                     "8943",
69                                     1,
70                                     0,
71                                     true,
72                                     1,
73                                     false,
74                                     true,
75                                     NULL));
76    epoll_server_.reset(new EpollServer);
77    connection_.reset(new MockSMConnection(epoll_server_.get(),
78                                           ssl_state,
79                                           memory_cache_.get(),
80                                           acceptor_.get(),
81                                           "log_prefix"));
82
83    interface_.reset(new HttpSM(connection_.get(),
84                                mock_another_interface_.get(),
85                                memory_cache_.get(),
86                                acceptor_.get()));
87  }
88
89  virtual void TearDown() OVERRIDE {
90    if (acceptor_->listen_fd_ >= 0) {
91      epoll_server_->UnregisterFD(acceptor_->listen_fd_);
92      close(acceptor_->listen_fd_);
93      acceptor_->listen_fd_ = -1;
94    }
95    STLDeleteElements(connection_->output_list());
96  }
97
98  bool HasStream(uint32 stream_id) {
99    return interface_->output_ordering().ExistsInPriorityMaps(stream_id);
100  }
101
102 protected:
103  scoped_ptr<MockSMInterface> mock_another_interface_;
104  scoped_ptr<MemoryCache> memory_cache_;
105  scoped_ptr<FlipAcceptor> acceptor_;
106  scoped_ptr<EpollServer> epoll_server_;
107  scoped_ptr<MockSMConnection> connection_;
108  scoped_ptr<HttpSM> interface_;
109};
110
111class FlipHttpSMProxyTest : public FlipHttpSMTest {
112 public:
113  FlipHttpSMProxyTest() : FlipHttpSMTest(FLIP_HANDLER_PROXY) {}
114  virtual ~FlipHttpSMProxyTest() {}
115};
116
117class FlipHttpSMHttpTest : public FlipHttpSMTest {
118 public:
119  FlipHttpSMHttpTest() : FlipHttpSMTest(FLIP_HANDLER_HTTP_SERVER) {}
120  virtual ~FlipHttpSMHttpTest() {}
121};
122
123class FlipHttpSMSpdyTest : public FlipHttpSMTest {
124 public:
125  FlipHttpSMSpdyTest() : FlipHttpSMTest(FLIP_HANDLER_SPDY_SERVER) {}
126  virtual ~FlipHttpSMSpdyTest() {}
127};
128
129TEST_F(FlipHttpSMTest, Construct) {
130  ASSERT_FALSE(interface_->spdy_framer()->is_request());
131}
132
133TEST_F(FlipHttpSMTest, AddToOutputOrder) {
134  uint32 stream_id = 13;
135  MemCacheIter mci;
136  mci.stream_id = stream_id;
137
138  {
139    BalsaHeaders headers;
140    std::string filename = "foobar";
141    memory_cache_->InsertFile(&headers, filename, "");
142    mci.file_data = memory_cache_->GetFileData(filename);
143  }
144
145  interface_->AddToOutputOrder(mci);
146  ASSERT_TRUE(HasStream(stream_id));
147}
148
149TEST_F(FlipHttpSMTest, InitSMInterface) {
150  scoped_ptr<MockSMInterface> mock(new MockSMInterface);
151  {
152    InSequence s;
153    EXPECT_CALL(*mock_another_interface_, SendEOF(_));
154    EXPECT_CALL(*mock_another_interface_, ResetForNewInterface(_));
155    EXPECT_CALL(*mock, SendEOF(_));
156    EXPECT_CALL(*mock, ResetForNewInterface(_));
157  }
158
159  interface_->ResetForNewConnection();
160  interface_->InitSMInterface(mock.get(), 0);
161  interface_->ResetForNewConnection();
162}
163
164TEST_F(FlipHttpSMTest, InitSMConnection) {
165  EXPECT_CALL(*connection_, InitSMConnection(_, _, _, _, _, _, _, _));
166
167  interface_->InitSMConnection(NULL, NULL, NULL, 0, "", "", "", false);
168}
169
170TEST_F(FlipHttpSMTest, ProcessReadInput) {
171  std::string data =
172      "HTTP/1.1 200 OK\r\n"
173      "Content-Length: 14\r\n\r\n"
174      "hello, world\r\n";
175  testing::MockFunction<void(int)> checkpoint;  // NOLINT
176  {
177    InSequence s;
178    EXPECT_CALL(*mock_another_interface_, SendSynReply(_, _));
179    EXPECT_CALL(checkpoint, Call(0));
180    EXPECT_CALL(*mock_another_interface_, SendDataFrame(_, _, _, _, _));
181    EXPECT_CALL(*mock_another_interface_, SendEOF(_));
182  }
183
184  ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE,
185            interface_->spdy_framer()->ParseState());
186
187  size_t read = interface_->ProcessReadInput(data.data(), data.size());
188  ASSERT_EQ(39u, read);
189  checkpoint.Call(0);
190  read += interface_->ProcessReadInput(&data.data()[read], data.size() - read);
191  ASSERT_EQ(data.size(), read);
192  ASSERT_EQ(BalsaFrameEnums::MESSAGE_FULLY_READ,
193            interface_->spdy_framer()->ParseState());
194  ASSERT_TRUE(interface_->MessageFullyRead());
195}
196
197TEST_F(FlipHttpSMTest, ProcessWriteInput) {
198  std::string data = "hello, world";
199  interface_->ProcessWriteInput(data.data(), data.size());
200
201  ASSERT_EQ(1u, connection_->output_list()->size());
202  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
203  DataFrame* df = *i++;
204  ASSERT_EQ(data, StringPiece(df->data, df->size));
205  ASSERT_EQ(connection_->output_list()->end(), i);
206}
207
208TEST_F(FlipHttpSMTest, Reset) {
209  std::string data = "HTTP/1.1 200 OK\r\n\r\n";
210  testing::MockFunction<void(int)> checkpoint;  // NOLINT
211  {
212    InSequence s;
213    EXPECT_CALL(*mock_another_interface_, SendSynReply(_, _));
214    EXPECT_CALL(checkpoint, Call(0));
215  }
216
217  ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE,
218            interface_->spdy_framer()->ParseState());
219
220  interface_->ProcessReadInput(data.data(), data.size());
221  checkpoint.Call(0);
222  ASSERT_FALSE(interface_->MessageFullyRead());
223  ASSERT_EQ(BalsaFrameEnums::READING_UNTIL_CLOSE,
224            interface_->spdy_framer()->ParseState());
225
226  interface_->Reset();
227  ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE,
228            interface_->spdy_framer()->ParseState());
229}
230
231TEST_F(FlipHttpSMTest, ResetForNewConnection) {
232  std::string data = "HTTP/1.1 200 OK\r\n\r\n";
233  testing::MockFunction<void(int)> checkpoint;  // NOLINT
234  {
235    InSequence s;
236    EXPECT_CALL(*mock_another_interface_, SendSynReply(_, _));
237    EXPECT_CALL(checkpoint, Call(0));
238    EXPECT_CALL(*mock_another_interface_, SendEOF(_));
239    EXPECT_CALL(*mock_another_interface_, ResetForNewInterface(_));
240  }
241
242  ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE,
243            interface_->spdy_framer()->ParseState());
244
245  interface_->ProcessReadInput(data.data(), data.size());
246  checkpoint.Call(0);
247  ASSERT_FALSE(interface_->MessageFullyRead());
248  ASSERT_EQ(BalsaFrameEnums::READING_UNTIL_CLOSE,
249            interface_->spdy_framer()->ParseState());
250
251  interface_->ResetForNewConnection();
252  ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE,
253            interface_->spdy_framer()->ParseState());
254}
255
256TEST_F(FlipHttpSMTest, NewStream) {
257  uint32 stream_id = 4;
258  {
259    BalsaHeaders headers;
260    std::string filename = "foobar";
261    memory_cache_->InsertFile(&headers, filename, "");
262  }
263
264  interface_->NewStream(stream_id, 1, "foobar");
265  ASSERT_TRUE(HasStream(stream_id));
266}
267
268TEST_F(FlipHttpSMTest, NewStreamError) {
269  std::string syn_reply =
270      "HTTP/1.1 404 Not Found\r\n"
271      "transfer-encoding: chunked\r\n\r\n";
272  std::string body = "e\r\npage not found\r\n";
273  uint32 stream_id = 4;
274
275  ASSERT_FALSE(HasStream(stream_id));
276  interface_->NewStream(stream_id, 1, "foobar");
277
278  ASSERT_EQ(3u, connection_->output_list()->size());
279  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
280  DataFrame* df = *i++;
281  ASSERT_EQ(syn_reply, StringPiece(df->data, df->size));
282  df = *i++;
283  ASSERT_EQ(body, StringPiece(df->data, df->size));
284  df = *i++;
285  ASSERT_EQ("0\r\n\r\n", StringPiece(df->data, df->size));
286  ASSERT_FALSE(HasStream(stream_id));
287}
288
289TEST_F(FlipHttpSMTest, SendErrorNotFound) {
290  std::string syn_reply =
291      "HTTP/1.1 404 Not Found\r\n"
292      "transfer-encoding: chunked\r\n\r\n";
293  std::string body = "e\r\npage not found\r\n";
294  uint32 stream_id = 13;
295  MemCacheIter mci;
296  mci.stream_id = stream_id;
297
298  {
299    BalsaHeaders headers;
300    std::string filename = "foobar";
301    memory_cache_->InsertFile(&headers, filename, "");
302    mci.file_data = memory_cache_->GetFileData(filename);
303  }
304
305  interface_->AddToOutputOrder(mci);
306  ASSERT_TRUE(HasStream(stream_id));
307  interface_->SendErrorNotFound(stream_id);
308
309  ASSERT_EQ(3u, connection_->output_list()->size());
310  std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin();
311  DataFrame* df = *i++;
312  ASSERT_EQ(syn_reply, StringPiece(df->data, df->size));
313  df = *i++;
314  ASSERT_EQ(body, StringPiece(df->data, df->size));
315  df = *i++;
316  ASSERT_EQ("0\r\n\r\n", StringPiece(df->data, df->size));
317  ASSERT_FALSE(HasStream(stream_id));
318}
319
320TEST_F(FlipHttpSMTest, SendSynStream) {
321  std::string expected =
322      "GET / HTTP/1.0\r\n"
323      "key1: value1\r\n\r\n";
324  BalsaHeaders headers;
325  headers.SetResponseFirstlineFromStringPieces("GET", "/path", "HTTP/1.0");
326  headers.AppendHeader("key1", "value1");
327  interface_->SendSynStream(18, headers);
328
329  // TODO(yhirano): Is this behavior correct?
330  ASSERT_EQ(0u, connection_->output_list()->size());
331}
332
333TEST_F(FlipHttpSMTest, SendSynReply) {
334  std::string expected =
335      "HTTP/1.1 200 OK\r\n"
336      "key1: value1\r\n\r\n";
337  BalsaHeaders headers;
338  headers.SetResponseFirstlineFromStringPieces("HTTP/1.1", "200", "OK");
339  headers.AppendHeader("key1", "value1");
340  interface_->SendSynReply(18, headers);
341
342  ASSERT_EQ(1u, connection_->output_list()->size());
343  DataFrame* df = connection_->output_list()->front();
344  ASSERT_EQ(expected, StringPiece(df->data, df->size));
345}
346
347TEST_F(FlipHttpSMTest, SendDataFrame) {
348  std::string data = "foo bar baz";
349  interface_->SendDataFrame(12, data.data(), data.size(), 0, false);
350
351  ASSERT_EQ(1u, connection_->output_list()->size());
352  DataFrame* df = connection_->output_list()->front();
353  ASSERT_EQ("b\r\nfoo bar baz\r\n", StringPiece(df->data, df->size));
354}
355
356TEST_F(FlipHttpSMProxyTest, ProcessBodyData) {
357  BalsaVisitorInterface* visitor = interface_.get();
358  std::string data = "hello, world";
359  {
360    InSequence s;
361    EXPECT_CALL(*mock_another_interface_,
362                SendDataFrame(0, data.data(), data.size(), 0, false));
363  }
364  visitor->ProcessBodyData(data.data(), data.size());
365}
366
367// --
368// FlipHttpSMProxyTest
369
370TEST_F(FlipHttpSMProxyTest, ProcessHeaders) {
371  BalsaVisitorInterface* visitor = interface_.get();
372  {
373    InSequence s;
374    EXPECT_CALL(*mock_another_interface_, SendSynReply(0, _));
375  }
376  BalsaHeaders headers;
377  visitor->ProcessHeaders(headers);
378}
379
380TEST_F(FlipHttpSMProxyTest, MessageDone) {
381  BalsaVisitorInterface* visitor = interface_.get();
382  {
383    InSequence s;
384    EXPECT_CALL(*mock_another_interface_, SendEOF(0));
385  }
386  visitor->MessageDone();
387}
388
389TEST_F(FlipHttpSMProxyTest, Cleanup) {
390  EXPECT_CALL(*connection_, Cleanup()).Times(0);
391  interface_->Cleanup();
392}
393
394TEST_F(FlipHttpSMProxyTest, SendEOF) {
395  {
396    InSequence s;
397    EXPECT_CALL(*mock_another_interface_, ResetForNewInterface(_));
398  }
399  interface_->SendEOF(32);
400  ASSERT_EQ(1u, connection_->output_list()->size());
401  DataFrame* df = connection_->output_list()->front();
402  ASSERT_EQ("0\r\n\r\n", StringPiece(df->data, df->size));
403}
404
405// --
406// FlipHttpSMHttpTest
407
408TEST_F(FlipHttpSMHttpTest, ProcessHeaders) {
409  BalsaVisitorInterface* visitor = interface_.get();
410  {
411    BalsaHeaders headers;
412    std::string filename = "GET_/path/file";
413    memory_cache_->InsertFile(&headers, filename, "");
414  }
415
416  BalsaHeaders headers;
417  headers.AppendHeader("Host", "example.com");
418  headers.SetRequestFirstlineFromStringPieces("GET", "/path/file", "HTTP/1.0");
419  uint32 stream_id = 133;
420  interface_->SetStreamID(stream_id);
421  ASSERT_FALSE(HasStream(stream_id));
422  visitor->ProcessHeaders(headers);
423  ASSERT_TRUE(HasStream(stream_id));
424}
425
426TEST_F(FlipHttpSMHttpTest, MessageDone) {
427  BalsaVisitorInterface* visitor = interface_.get();
428  {
429    InSequence s;
430    EXPECT_CALL(*mock_another_interface_, SendEOF(0)).Times(0);
431  }
432  visitor->MessageDone();
433}
434
435TEST_F(FlipHttpSMHttpTest, Cleanup) {
436  EXPECT_CALL(*connection_, Cleanup()).Times(0);
437  interface_->Cleanup();
438}
439
440TEST_F(FlipHttpSMHttpTest, SendEOF) {
441  {
442    InSequence s;
443    EXPECT_CALL(*mock_another_interface_, ResetForNewInterface(_)).Times(0);
444  }
445  interface_->SendEOF(32);
446  ASSERT_EQ(1u, connection_->output_list()->size());
447  DataFrame* df = connection_->output_list()->front();
448  ASSERT_EQ("0\r\n\r\n", StringPiece(df->data, df->size));
449}
450
451// --
452// FlipHttpSMSpdyTest
453
454TEST_F(FlipHttpSMSpdyTest, ProcessHeaders) {
455  BalsaVisitorInterface* visitor = interface_.get();
456  {
457    InSequence s;
458    EXPECT_CALL(*mock_another_interface_, SendSynReply(0, _));
459  }
460  BalsaHeaders headers;
461  visitor->ProcessHeaders(headers);
462}
463
464TEST_F(FlipHttpSMSpdyTest, MessageDone) {
465  BalsaVisitorInterface* visitor = interface_.get();
466  {
467    InSequence s;
468    EXPECT_CALL(*mock_another_interface_, SendEOF(0)).Times(0);
469  }
470  visitor->MessageDone();
471}
472
473TEST_F(FlipHttpSMSpdyTest, Cleanup) {
474  EXPECT_CALL(*connection_, Cleanup()).Times(0);
475  interface_->Cleanup();
476}
477
478TEST_F(FlipHttpSMSpdyTest, SendEOF) {
479  {
480    InSequence s;
481    EXPECT_CALL(*mock_another_interface_, ResetForNewInterface(_)).Times(0);
482  }
483  interface_->SendEOF(32);
484  ASSERT_EQ(1u, connection_->output_list()->size());
485  DataFrame* df = connection_->output_list()->front();
486  ASSERT_EQ("0\r\n\r\n", StringPiece(df->data, df->size));
487}
488
489}  // namespace
490
491}  // namespace net
492