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.
5 #include "net/tools/flip_server/spdy_interface.h"
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"
22 using ::base::StringPiece
;
24 using ::testing::InSequence
;
25 using ::testing::InvokeWithoutArgs
;
26 using ::testing::Return
;
27 using ::testing::SaveArg
;
28 using ::testing::Values
;
34 StringSaver() : data(NULL
), size(0) {}
35 void Save() { string
= std::string(data
, size
); }
42 class SpdyFramerVisitor
: public BufferedSpdyFramerVisitorInterface
{
44 virtual ~SpdyFramerVisitor() {}
45 MOCK_METHOD1(OnError
, void(SpdyFramer::SpdyError
));
46 MOCK_METHOD2(OnStreamError
, void(SpdyStreamId
, const std::string
&));
47 MOCK_METHOD6(OnSynStream
,
53 const SpdyHeaderBlock
&));
54 MOCK_METHOD3(OnSynReply
, void(SpdyStreamId
, bool, const SpdyHeaderBlock
&));
55 MOCK_METHOD7(OnHeaders
,
56 void(SpdyStreamId stream_id
,
58 SpdyPriority priority
,
59 SpdyStreamId parent_stream_id
,
62 const SpdyHeaderBlock
& headers
));
63 MOCK_METHOD3(OnDataFrameHeader
, void(SpdyStreamId
, size_t, bool));
64 MOCK_METHOD4(OnStreamFrameData
, void(SpdyStreamId
,
68 MOCK_METHOD2(OnStreamPadding
, void(SpdyStreamId
, size_t));
69 MOCK_METHOD1(OnSettings
, void(bool clear_persisted
));
70 MOCK_METHOD3(OnSetting
, void(SpdySettingsIds
, uint8
, uint32
));
71 MOCK_METHOD2(OnPing
, void(SpdyPingId unique_id
, bool is_ack
));
72 MOCK_METHOD2(OnRstStream
, void(SpdyStreamId
, SpdyRstStreamStatus
));
73 MOCK_METHOD2(OnGoAway
, void(SpdyStreamId
, SpdyGoAwayStatus
));
74 MOCK_METHOD2(OnWindowUpdate
, void(SpdyStreamId
, int));
75 MOCK_METHOD3(OnPushPromise
,
76 void(SpdyStreamId
, SpdyStreamId
, const SpdyHeaderBlock
&));
77 MOCK_METHOD2(OnUnknownFrame
, bool(SpdyStreamId stream_id
, int frame_type
));
80 class FakeSMConnection
: public SMConnection
{
82 FakeSMConnection(EpollServer
* epoll_server
,
84 MemoryCache
* memory_cache
,
85 FlipAcceptor
* acceptor
,
86 std::string log_prefix
)
87 : SMConnection(epoll_server
,
93 MOCK_METHOD0(Cleanup
, void());
94 MOCK_METHOD8(InitSMConnection
,
95 void(SMConnectionPoolInterface
*,
105 // This class is almost SpdySM, except one function.
106 // This class is the test target of tests in this file.
107 class TestSpdySM
: public SpdySM
{
109 virtual ~TestSpdySM() {}
110 TestSpdySM(SMConnection
* connection
,
111 SMInterface
* sm_http_interface
,
112 EpollServer
* epoll_server
,
113 MemoryCache
* memory_cache
,
114 FlipAcceptor
* acceptor
,
115 SpdyMajorVersion version
)
123 MOCK_METHOD2(FindOrMakeNewSMConnectionInterface
,
124 SMInterface
*(const std::string
&, const std::string
&));
127 class SpdySMTestBase
: public ::testing::TestWithParam
<SpdyMajorVersion
> {
129 explicit SpdySMTestBase(FlipHandlerType type
) {
130 SSLState
* ssl_state
= NULL
;
131 mock_another_interface_
.reset(new MockSMInterface
);
132 memory_cache_
.reset(new MemoryCache
);
133 acceptor_
.reset(new FlipAcceptor(type
,
149 epoll_server_
.reset(new EpollServer
);
150 connection_
.reset(new FakeSMConnection(epoll_server_
.get(),
156 interface_
.reset(new TestSpdySM(connection_
.get(),
157 mock_another_interface_
.get(),
163 spdy_framer_
.reset(new BufferedSpdyFramer(GetParam(), true));
164 spdy_framer_visitor_
.reset(new SpdyFramerVisitor
);
165 spdy_framer_
->set_visitor(spdy_framer_visitor_
.get());
168 virtual ~SpdySMTestBase() {
169 if (acceptor_
->listen_fd_
>= 0) {
170 epoll_server_
->UnregisterFD(acceptor_
->listen_fd_
);
171 close(acceptor_
->listen_fd_
);
172 acceptor_
->listen_fd_
= -1;
174 OutputList
& output_list
= *connection_
->output_list();
175 for (OutputList::const_iterator i
= output_list
.begin();
176 i
!= output_list
.end();
183 bool HasStream(uint32 stream_id
) {
184 return interface_
->output_ordering().ExistsInPriorityMaps(stream_id
);
188 scoped_ptr
<MockSMInterface
> mock_another_interface_
;
189 scoped_ptr
<MemoryCache
> memory_cache_
;
190 scoped_ptr
<FlipAcceptor
> acceptor_
;
191 scoped_ptr
<EpollServer
> epoll_server_
;
192 scoped_ptr
<FakeSMConnection
> connection_
;
193 scoped_ptr
<TestSpdySM
> interface_
;
194 scoped_ptr
<BufferedSpdyFramer
> spdy_framer_
;
195 scoped_ptr
<SpdyFramerVisitor
> spdy_framer_visitor_
;
198 class SpdySMProxyTest
: public SpdySMTestBase
{
200 SpdySMProxyTest() : SpdySMTestBase(FLIP_HANDLER_PROXY
) {}
201 virtual ~SpdySMProxyTest() {}
204 class SpdySMServerTest
: public SpdySMTestBase
{
206 SpdySMServerTest() : SpdySMTestBase(FLIP_HANDLER_SPDY_SERVER
) {}
207 virtual ~SpdySMServerTest() {}
210 INSTANTIATE_TEST_CASE_P(SpdySMProxyTest
, SpdySMProxyTest
, Values(SPDY3
, HTTP2
));
211 INSTANTIATE_TEST_CASE_P(SpdySMServerTest
, SpdySMServerTest
, Values(HTTP2
));
213 TEST_P(SpdySMProxyTest
, InitSMConnection
) {
216 EXPECT_CALL(*connection_
, InitSMConnection(_
, _
, _
, _
, _
, _
, _
, _
));
218 interface_
->InitSMConnection(
219 NULL
, NULL
, epoll_server_
.get(), -1, "", "", "", false);
222 TEST_P(SpdySMProxyTest
, OnStreamFrameData
) {
223 BufferedSpdyFramerVisitorInterface
* visitor
= interface_
.get();
224 scoped_ptr
<MockSMInterface
> mock_interface(new MockSMInterface
);
225 uint32 stream_id
= 92;
226 uint32 associated_id
= 43;
227 SpdyHeaderBlock block
;
228 testing::MockFunction
<void(int)> checkpoint
; // NOLINT
230 scoped_ptr
<SpdyFrame
> frame(spdy_framer_
->CreatePingFrame(12, false));
231 block
[":method"] = "GET";
232 block
[":host"] = "www.example.com";
233 block
[":path"] = "/path";
234 block
[":scheme"] = "http";
235 block
["foo"] = "bar";
238 EXPECT_CALL(*interface_
,
239 FindOrMakeNewSMConnectionInterface(_
, _
))
240 .WillOnce(Return(mock_interface
.get()));
241 EXPECT_CALL(*mock_interface
, SetStreamID(stream_id
));
242 EXPECT_CALL(*mock_interface
, ProcessWriteInput(_
, _
)).Times(1);
243 EXPECT_CALL(checkpoint
, Call(0));
244 EXPECT_CALL(*mock_interface
,
245 ProcessWriteInput(frame
->data(), frame
->size())).Times(1);
248 visitor
->OnSynStream(stream_id
, associated_id
, 0, false, false, block
);
250 visitor
->OnStreamFrameData(stream_id
, frame
->data(), frame
->size(), true);
253 TEST_P(SpdySMProxyTest
, OnRstStream
) {
254 BufferedSpdyFramerVisitorInterface
* visitor
= interface_
.get();
255 uint32 stream_id
= 82;
257 mci
.stream_id
= stream_id
;
260 BalsaHeaders headers
;
261 std::string filename
= "foobar";
262 memory_cache_
->InsertFile(&headers
, filename
, "");
263 mci
.file_data
= memory_cache_
->GetFileData(filename
);
266 interface_
->AddToOutputOrder(mci
);
267 ASSERT_TRUE(HasStream(stream_id
));
268 visitor
->OnRstStream(stream_id
, RST_STREAM_INVALID
);
269 ASSERT_FALSE(HasStream(stream_id
));
272 TEST_P(SpdySMProxyTest
, ProcessReadInput
) {
273 ASSERT_EQ(SpdyFramer::SPDY_READY_FOR_FRAME
,
274 interface_
->spdy_framer()->state());
275 interface_
->ProcessReadInput("", 1);
276 ASSERT_EQ(SpdyFramer::SPDY_READING_COMMON_HEADER
,
277 interface_
->spdy_framer()->state());
280 TEST_P(SpdySMProxyTest
, ResetForNewConnection
) {
281 uint32 stream_id
= 13;
283 mci
.stream_id
= stream_id
;
285 const char input
[] = {'\0', '\0', '\0'};
288 BalsaHeaders headers
;
289 std::string filename
= "foobar";
290 memory_cache_
->InsertFile(&headers
, filename
, "");
291 mci
.file_data
= memory_cache_
->GetFileData(filename
);
294 interface_
->AddToOutputOrder(mci
);
295 ASSERT_TRUE(HasStream(stream_id
));
296 interface_
->ProcessReadInput(input
, sizeof(input
));
297 ASSERT_NE(SpdyFramer::SPDY_READY_FOR_FRAME
,
298 interface_
->spdy_framer()->state());
300 interface_
->ResetForNewConnection();
301 ASSERT_FALSE(HasStream(stream_id
));
302 ASSERT_TRUE(interface_
->spdy_framer() == NULL
);
305 TEST_P(SpdySMProxyTest
, CreateFramer
) {
306 interface_
->ResetForNewConnection();
307 interface_
->CreateFramer(SPDY3
);
308 ASSERT_TRUE(interface_
->spdy_framer() != NULL
);
309 ASSERT_EQ(interface_
->spdy_version(), SPDY3
);
311 interface_
->ResetForNewConnection();
312 interface_
->CreateFramer(HTTP2
);
313 ASSERT_TRUE(interface_
->spdy_framer() != NULL
);
314 ASSERT_EQ(interface_
->spdy_version(), HTTP2
);
317 TEST_P(SpdySMProxyTest
, PostAcceptHook
) {
318 interface_
->PostAcceptHook();
320 ASSERT_EQ(1u, connection_
->output_list()->size());
321 std::list
<DataFrame
*>::const_iterator i
= connection_
->output_list()->begin();
322 DataFrame
* df
= *i
++;
326 EXPECT_CALL(*spdy_framer_visitor_
, OnSettings(false));
327 EXPECT_CALL(*spdy_framer_visitor_
,
328 OnSetting(SETTINGS_MAX_CONCURRENT_STREAMS
, 0u, 100u));
330 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
333 TEST_P(SpdySMProxyTest
, NewStream
) {
334 // TODO(yhirano): SpdySM::NewStream leads to crash when
335 // acceptor_->flip_handler_type_ != FLIP_HANDLER_SPDY_SERVER.
336 // It should be fixed though I don't know the solution now.
339 TEST_P(SpdySMProxyTest
, AddToOutputOrder
) {
340 uint32 stream_id
= 13;
342 mci
.stream_id
= stream_id
;
345 BalsaHeaders headers
;
346 std::string filename
= "foobar";
347 memory_cache_
->InsertFile(&headers
, filename
, "");
348 mci
.file_data
= memory_cache_
->GetFileData(filename
);
351 interface_
->AddToOutputOrder(mci
);
352 ASSERT_TRUE(HasStream(stream_id
));
355 TEST_P(SpdySMProxyTest
, SendErrorNotFound
) {
356 uint32 stream_id
= 82;
357 SpdyHeaderBlock actual_header_block
;
358 const char* actual_data
;
360 testing::MockFunction
<void(int)> checkpoint
; // NOLINT
362 interface_
->SendErrorNotFound(stream_id
);
364 ASSERT_EQ(2u, connection_
->output_list()->size());
368 if (GetParam() < HTTP2
) {
369 EXPECT_CALL(*spdy_framer_visitor_
,
370 OnSynReply(stream_id
, false, _
))
371 .WillOnce(SaveArg
<2>(&actual_header_block
));
373 EXPECT_CALL(*spdy_framer_visitor_
,
374 OnHeaders(stream_id
, /*has_priority=*/false, _
, _
, _
,
376 .WillOnce(SaveArg
<6>(&actual_header_block
));
378 EXPECT_CALL(checkpoint
, Call(0));
379 EXPECT_CALL(*spdy_framer_visitor_
,
380 OnDataFrameHeader(stream_id
, _
, true));
381 EXPECT_CALL(*spdy_framer_visitor_
,
382 OnStreamFrameData(stream_id
, _
, _
, false)).Times(1)
383 .WillOnce(DoAll(SaveArg
<1>(&actual_data
),
384 SaveArg
<2>(&actual_size
)));
385 EXPECT_CALL(*spdy_framer_visitor_
,
386 OnStreamFrameData(stream_id
, NULL
, 0, true)).Times(1);
389 std::list
<DataFrame
*>::const_iterator i
= connection_
->output_list()->begin();
390 DataFrame
* df
= *i
++;
391 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
394 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
396 ASSERT_EQ(2, spdy_framer_
->frames_received());
397 ASSERT_EQ(2u, actual_header_block
.size());
398 ASSERT_EQ("404 Not Found", actual_header_block
[":status"]);
399 ASSERT_EQ("HTTP/1.1", actual_header_block
[":version"]);
400 ASSERT_EQ("wtf?", StringPiece(actual_data
, actual_size
));
403 TEST_P(SpdySMProxyTest
, SendSynStream
) {
404 uint32 stream_id
= 82;
405 BalsaHeaders headers
;
406 SpdyHeaderBlock actual_header_block
;
407 headers
.AppendHeader("key1", "value1");
408 headers
.AppendHeader("Host", "www.example.com");
409 headers
.SetRequestFirstlineFromStringPieces("GET", "/path", "HTTP/1.1");
411 interface_
->SendSynStream(stream_id
, headers
);
413 ASSERT_EQ(1u, connection_
->output_list()->size());
414 std::list
<DataFrame
*>::const_iterator i
= connection_
->output_list()->begin();
415 DataFrame
* df
= *i
++;
419 EXPECT_CALL(*spdy_framer_visitor_
,
420 OnSynStream(stream_id
, 0, _
, false, false, _
))
421 .WillOnce(SaveArg
<5>(&actual_header_block
));
424 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
425 ASSERT_EQ(1, spdy_framer_
->frames_received());
426 ASSERT_EQ(5u, actual_header_block
.size());
427 ASSERT_EQ("GET", actual_header_block
[":method"]);
428 ASSERT_EQ("HTTP/1.1", actual_header_block
[":version"]);
429 ASSERT_EQ("/path", actual_header_block
[":path"]);
430 ASSERT_EQ("www.example.com", actual_header_block
[":host"]);
431 ASSERT_EQ("value1", actual_header_block
["key1"]);
434 TEST_P(SpdySMProxyTest
, SendSynReply
) {
435 uint32 stream_id
= 82;
436 BalsaHeaders headers
;
437 SpdyHeaderBlock actual_header_block
;
438 headers
.AppendHeader("key1", "value1");
439 headers
.SetResponseFirstlineFromStringPieces("HTTP/1.1", "200", "OK");
441 interface_
->SendSynReply(stream_id
, headers
);
443 ASSERT_EQ(1u, connection_
->output_list()->size());
444 std::list
<DataFrame
*>::const_iterator i
= connection_
->output_list()->begin();
445 DataFrame
* df
= *i
++;
449 if (GetParam() < HTTP2
) {
450 EXPECT_CALL(*spdy_framer_visitor_
, OnSynReply(stream_id
, false, _
))
451 .WillOnce(SaveArg
<2>(&actual_header_block
));
453 EXPECT_CALL(*spdy_framer_visitor_
,
454 OnHeaders(stream_id
, /*has_priority=*/false, _
, _
, _
,
456 .WillOnce(SaveArg
<6>(&actual_header_block
));
460 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
461 ASSERT_EQ(1, spdy_framer_
->frames_received());
462 ASSERT_EQ(3u, actual_header_block
.size());
463 ASSERT_EQ("200 OK", actual_header_block
[":status"]);
464 ASSERT_EQ("HTTP/1.1", actual_header_block
[":version"]);
465 ASSERT_EQ("value1", actual_header_block
["key1"]);
468 TEST_P(SpdySMProxyTest
, SendDataFrame
) {
469 uint32 stream_id
= 133;
470 SpdyDataFlags flags
= DATA_FLAG_NONE
;
471 const char* actual_data
;
474 interface_
->SendDataFrame(stream_id
, "hello", 5, flags
, true);
476 ASSERT_EQ(1u, connection_
->output_list()->size());
477 std::list
<DataFrame
*>::const_iterator i
= connection_
->output_list()->begin();
478 DataFrame
* df
= *i
++;
482 EXPECT_CALL(*spdy_framer_visitor_
,
483 OnDataFrameHeader(stream_id
, _
, false));
484 EXPECT_CALL(*spdy_framer_visitor_
,
485 OnStreamFrameData(stream_id
, _
, _
, false))
486 .WillOnce(DoAll(SaveArg
<1>(&actual_data
), SaveArg
<2>(&actual_size
)));
489 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
490 ASSERT_EQ(1, spdy_framer_
->frames_received());
491 ASSERT_EQ("hello", StringPiece(actual_data
, actual_size
));
494 TEST_P(SpdySMProxyTest
, SendLongDataFrame
) {
495 uint32 stream_id
= 133;
496 SpdyDataFlags flags
= DATA_FLAG_NONE
;
497 const char* actual_data
;
500 std::string data
= std::string(kSpdySegmentSize
, 'a') +
501 std::string(kSpdySegmentSize
, 'b') + "c";
502 interface_
->SendDataFrame(stream_id
, data
.data(), data
.size(), flags
, true);
506 for (int i
= 0; i
< 3; ++i
) {
507 EXPECT_CALL(*spdy_framer_visitor_
,
508 OnDataFrameHeader(stream_id
, _
, false));
509 EXPECT_CALL(*spdy_framer_visitor_
,
510 OnStreamFrameData(stream_id
, _
, _
, false))
511 .WillOnce(DoAll(SaveArg
<1>(&actual_data
),
512 SaveArg
<2>(&actual_size
)));
516 ASSERT_EQ(3u, connection_
->output_list()->size());
517 std::list
<DataFrame
*>::const_iterator i
= connection_
->output_list()->begin();
518 DataFrame
* df
= *i
++;
519 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
520 ASSERT_EQ(std::string(kSpdySegmentSize
, 'a'),
521 StringPiece(actual_data
, actual_size
));
524 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
525 ASSERT_EQ(std::string(kSpdySegmentSize
, 'b'),
526 StringPiece(actual_data
, actual_size
));
529 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
530 ASSERT_EQ("c", StringPiece(actual_data
, actual_size
));
533 TEST_P(SpdySMServerTest
, OnSynStream
) {
534 BufferedSpdyFramerVisitorInterface
* visitor
= interface_
.get();
535 uint32 stream_id
= 82;
536 SpdyHeaderBlock spdy_headers
;
537 spdy_headers
["url"] = "http://www.example.com/path";
538 spdy_headers
["method"] = "GET";
539 spdy_headers
["scheme"] = "http";
540 spdy_headers
["version"] = "HTTP/1.1";
543 BalsaHeaders headers
;
544 memory_cache_
->InsertFile(&headers
, "GET_/path", "");
546 visitor
->OnSynStream(stream_id
, 0, 0, true, true, spdy_headers
);
547 ASSERT_TRUE(HasStream(stream_id
));
550 TEST_P(SpdySMServerTest
, NewStream
) {
551 uint32 stream_id
= 13;
552 std::string filename
= "foobar";
555 BalsaHeaders headers
;
556 memory_cache_
->InsertFile(&headers
, filename
, "");
559 interface_
->NewStream(stream_id
, 0, filename
);
560 ASSERT_TRUE(HasStream(stream_id
));
563 TEST_P(SpdySMServerTest
, NewStreamError
) {
564 uint32 stream_id
= 82;
565 SpdyHeaderBlock actual_header_block
;
566 const char* actual_data
;
568 testing::MockFunction
<void(int)> checkpoint
; // NOLINT
570 interface_
->NewStream(stream_id
, 0, "nonexistingfile");
572 ASSERT_EQ(2u, connection_
->output_list()->size());
576 if (GetParam() < HTTP2
) {
577 EXPECT_CALL(*spdy_framer_visitor_
, OnSynReply(stream_id
, false, _
))
578 .WillOnce(SaveArg
<2>(&actual_header_block
));
580 EXPECT_CALL(*spdy_framer_visitor_
,
581 OnHeaders(stream_id
, /*has_priority=*/false, _
, _
, _
,
583 .WillOnce(SaveArg
<6>(&actual_header_block
));
585 EXPECT_CALL(checkpoint
, Call(0));
586 EXPECT_CALL(*spdy_framer_visitor_
,
587 OnDataFrameHeader(stream_id
, _
, true));
588 EXPECT_CALL(*spdy_framer_visitor_
,
589 OnStreamFrameData(stream_id
, _
, _
, false)).Times(1)
590 .WillOnce(DoAll(SaveArg
<1>(&actual_data
),
591 SaveArg
<2>(&actual_size
)));
592 EXPECT_CALL(*spdy_framer_visitor_
,
593 OnStreamFrameData(stream_id
, NULL
, 0, true)).Times(1);
596 std::list
<DataFrame
*>::const_iterator i
= connection_
->output_list()->begin();
597 DataFrame
* df
= *i
++;
598 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
601 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
603 ASSERT_EQ(2, spdy_framer_
->frames_received());
604 ASSERT_EQ(2u, actual_header_block
.size());
605 ASSERT_EQ("404 Not Found", actual_header_block
["status"]);
606 ASSERT_EQ("HTTP/1.1", actual_header_block
["version"]);
607 ASSERT_EQ("wtf?", StringPiece(actual_data
, actual_size
));