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_METHOD5(OnHeaders
,
60 const SpdyHeaderBlock
&));
61 MOCK_METHOD3(OnDataFrameHeader
, void(SpdyStreamId
, size_t, bool));
62 MOCK_METHOD4(OnStreamFrameData
, void(SpdyStreamId
,
66 MOCK_METHOD2(OnStreamPadding
, void(SpdyStreamId
, size_t));
67 MOCK_METHOD1(OnSettings
, void(bool clear_persisted
));
68 MOCK_METHOD3(OnSetting
, void(SpdySettingsIds
, uint8
, uint32
));
69 MOCK_METHOD2(OnPing
, void(SpdyPingId unique_id
, bool is_ack
));
70 MOCK_METHOD2(OnRstStream
, void(SpdyStreamId
, SpdyRstStreamStatus
));
71 MOCK_METHOD2(OnGoAway
, void(SpdyStreamId
, SpdyGoAwayStatus
));
72 MOCK_METHOD2(OnWindowUpdate
, void(SpdyStreamId
, uint32
));
73 MOCK_METHOD3(OnPushPromise
,
74 void(SpdyStreamId
, SpdyStreamId
, const SpdyHeaderBlock
&));
75 MOCK_METHOD2(OnUnknownFrame
, bool(SpdyStreamId stream_id
, int frame_type
));
78 class FakeSMConnection
: public SMConnection
{
80 FakeSMConnection(EpollServer
* epoll_server
,
82 MemoryCache
* memory_cache
,
83 FlipAcceptor
* acceptor
,
84 std::string log_prefix
)
85 : SMConnection(epoll_server
,
91 MOCK_METHOD0(Cleanup
, void());
92 MOCK_METHOD8(InitSMConnection
,
93 void(SMConnectionPoolInterface
*,
103 // This class is almost SpdySM, except one function.
104 // This class is the test target of tests in this file.
105 class TestSpdySM
: public SpdySM
{
107 virtual ~TestSpdySM() {}
108 TestSpdySM(SMConnection
* connection
,
109 SMInterface
* sm_http_interface
,
110 EpollServer
* epoll_server
,
111 MemoryCache
* memory_cache
,
112 FlipAcceptor
* acceptor
,
113 SpdyMajorVersion version
)
121 MOCK_METHOD2(FindOrMakeNewSMConnectionInterface
,
122 SMInterface
*(const std::string
&, const std::string
&));
125 class SpdySMTestBase
: public ::testing::TestWithParam
<SpdyMajorVersion
> {
127 explicit SpdySMTestBase(FlipHandlerType type
) {
128 SSLState
* ssl_state
= NULL
;
129 mock_another_interface_
.reset(new MockSMInterface
);
130 memory_cache_
.reset(new MemoryCache
);
131 acceptor_
.reset(new FlipAcceptor(type
,
147 epoll_server_
.reset(new EpollServer
);
148 connection_
.reset(new FakeSMConnection(epoll_server_
.get(),
154 interface_
.reset(new TestSpdySM(connection_
.get(),
155 mock_another_interface_
.get(),
161 spdy_framer_
.reset(new BufferedSpdyFramer(GetParam(), true));
162 spdy_framer_visitor_
.reset(new SpdyFramerVisitor
);
163 spdy_framer_
->set_visitor(spdy_framer_visitor_
.get());
166 virtual ~SpdySMTestBase() {
167 if (acceptor_
->listen_fd_
>= 0) {
168 epoll_server_
->UnregisterFD(acceptor_
->listen_fd_
);
169 close(acceptor_
->listen_fd_
);
170 acceptor_
->listen_fd_
= -1;
172 OutputList
& output_list
= *connection_
->output_list();
173 for (OutputList::const_iterator i
= output_list
.begin();
174 i
!= output_list
.end();
181 bool HasStream(uint32 stream_id
) {
182 return interface_
->output_ordering().ExistsInPriorityMaps(stream_id
);
186 scoped_ptr
<MockSMInterface
> mock_another_interface_
;
187 scoped_ptr
<MemoryCache
> memory_cache_
;
188 scoped_ptr
<FlipAcceptor
> acceptor_
;
189 scoped_ptr
<EpollServer
> epoll_server_
;
190 scoped_ptr
<FakeSMConnection
> connection_
;
191 scoped_ptr
<TestSpdySM
> interface_
;
192 scoped_ptr
<BufferedSpdyFramer
> spdy_framer_
;
193 scoped_ptr
<SpdyFramerVisitor
> spdy_framer_visitor_
;
196 class SpdySMProxyTest
: public SpdySMTestBase
{
198 SpdySMProxyTest() : SpdySMTestBase(FLIP_HANDLER_PROXY
) {}
199 virtual ~SpdySMProxyTest() {}
202 class SpdySMServerTest
: public SpdySMTestBase
{
204 SpdySMServerTest() : SpdySMTestBase(FLIP_HANDLER_SPDY_SERVER
) {}
205 virtual ~SpdySMServerTest() {}
208 INSTANTIATE_TEST_CASE_P(SpdySMProxyTest
,
210 Values(SPDY3
, SPDY4
));
211 INSTANTIATE_TEST_CASE_P(SpdySMServerTest
, SpdySMServerTest
, Values(SPDY4
));
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_RESET
, interface_
->spdy_framer()->state());
274 interface_
->ProcessReadInput("", 1);
275 ASSERT_EQ(SpdyFramer::SPDY_READING_COMMON_HEADER
,
276 interface_
->spdy_framer()->state());
279 TEST_P(SpdySMProxyTest
, ResetForNewConnection
) {
280 uint32 stream_id
= 13;
282 mci
.stream_id
= stream_id
;
284 const char input
[] = {'\0', '\0', '\0'};
287 BalsaHeaders headers
;
288 std::string filename
= "foobar";
289 memory_cache_
->InsertFile(&headers
, filename
, "");
290 mci
.file_data
= memory_cache_
->GetFileData(filename
);
293 interface_
->AddToOutputOrder(mci
);
294 ASSERT_TRUE(HasStream(stream_id
));
295 interface_
->ProcessReadInput(input
, sizeof(input
));
296 ASSERT_NE(SpdyFramer::SPDY_RESET
, interface_
->spdy_framer()->state());
298 interface_
->ResetForNewConnection();
299 ASSERT_FALSE(HasStream(stream_id
));
300 ASSERT_TRUE(interface_
->spdy_framer() == NULL
);
303 TEST_P(SpdySMProxyTest
, CreateFramer
) {
304 interface_
->ResetForNewConnection();
305 interface_
->CreateFramer(SPDY3
);
306 ASSERT_TRUE(interface_
->spdy_framer() != NULL
);
307 ASSERT_EQ(interface_
->spdy_version(), SPDY3
);
309 interface_
->ResetForNewConnection();
310 interface_
->CreateFramer(SPDY4
);
311 ASSERT_TRUE(interface_
->spdy_framer() != NULL
);
312 ASSERT_EQ(interface_
->spdy_version(), SPDY4
);
315 TEST_P(SpdySMProxyTest
, PostAcceptHook
) {
316 interface_
->PostAcceptHook();
318 ASSERT_EQ(1u, connection_
->output_list()->size());
319 std::list
<DataFrame
*>::const_iterator i
= connection_
->output_list()->begin();
320 DataFrame
* df
= *i
++;
324 EXPECT_CALL(*spdy_framer_visitor_
, OnSettings(false));
325 EXPECT_CALL(*spdy_framer_visitor_
,
326 OnSetting(SETTINGS_MAX_CONCURRENT_STREAMS
, 0u, 100u));
328 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
331 TEST_P(SpdySMProxyTest
, NewStream
) {
332 // TODO(yhirano): SpdySM::NewStream leads to crash when
333 // acceptor_->flip_handler_type_ != FLIP_HANDLER_SPDY_SERVER.
334 // It should be fixed though I don't know the solution now.
337 TEST_P(SpdySMProxyTest
, AddToOutputOrder
) {
338 uint32 stream_id
= 13;
340 mci
.stream_id
= stream_id
;
343 BalsaHeaders headers
;
344 std::string filename
= "foobar";
345 memory_cache_
->InsertFile(&headers
, filename
, "");
346 mci
.file_data
= memory_cache_
->GetFileData(filename
);
349 interface_
->AddToOutputOrder(mci
);
350 ASSERT_TRUE(HasStream(stream_id
));
353 TEST_P(SpdySMProxyTest
, SendErrorNotFound
) {
354 uint32 stream_id
= 82;
355 SpdyHeaderBlock actual_header_block
;
356 const char* actual_data
;
358 testing::MockFunction
<void(int)> checkpoint
; // NOLINT
360 interface_
->SendErrorNotFound(stream_id
);
362 ASSERT_EQ(2u, connection_
->output_list()->size());
366 if (GetParam() < SPDY4
) {
367 EXPECT_CALL(*spdy_framer_visitor_
,
368 OnSynReply(stream_id
, false, _
))
369 .WillOnce(SaveArg
<2>(&actual_header_block
));
371 EXPECT_CALL(*spdy_framer_visitor_
,
372 OnHeaders(stream_id
, false, 0, false, _
))
373 .WillOnce(SaveArg
<4>(&actual_header_block
));
375 EXPECT_CALL(checkpoint
, Call(0));
376 EXPECT_CALL(*spdy_framer_visitor_
,
377 OnDataFrameHeader(stream_id
, _
, true));
378 EXPECT_CALL(*spdy_framer_visitor_
,
379 OnStreamFrameData(stream_id
, _
, _
, false)).Times(1)
380 .WillOnce(DoAll(SaveArg
<1>(&actual_data
),
381 SaveArg
<2>(&actual_size
)));
382 EXPECT_CALL(*spdy_framer_visitor_
,
383 OnStreamFrameData(stream_id
, NULL
, 0, true)).Times(1);
386 std::list
<DataFrame
*>::const_iterator i
= connection_
->output_list()->begin();
387 DataFrame
* df
= *i
++;
388 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
391 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
393 ASSERT_EQ(2, spdy_framer_
->frames_received());
394 ASSERT_EQ(2u, actual_header_block
.size());
395 ASSERT_EQ("404 Not Found", actual_header_block
[":status"]);
396 ASSERT_EQ("HTTP/1.1", actual_header_block
[":version"]);
397 ASSERT_EQ("wtf?", StringPiece(actual_data
, actual_size
));
400 TEST_P(SpdySMProxyTest
, SendSynStream
) {
401 uint32 stream_id
= 82;
402 BalsaHeaders headers
;
403 SpdyHeaderBlock actual_header_block
;
404 headers
.AppendHeader("key1", "value1");
405 headers
.AppendHeader("Host", "www.example.com");
406 headers
.SetRequestFirstlineFromStringPieces("GET", "/path", "HTTP/1.1");
408 interface_
->SendSynStream(stream_id
, headers
);
410 ASSERT_EQ(1u, connection_
->output_list()->size());
411 std::list
<DataFrame
*>::const_iterator i
= connection_
->output_list()->begin();
412 DataFrame
* df
= *i
++;
416 EXPECT_CALL(*spdy_framer_visitor_
,
417 OnSynStream(stream_id
, 0, _
, false, false, _
))
418 .WillOnce(SaveArg
<5>(&actual_header_block
));
421 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
422 ASSERT_EQ(1, spdy_framer_
->frames_received());
423 ASSERT_EQ(5u, actual_header_block
.size());
424 ASSERT_EQ("GET", actual_header_block
[":method"]);
425 ASSERT_EQ("HTTP/1.1", actual_header_block
[":version"]);
426 ASSERT_EQ("/path", actual_header_block
[":path"]);
427 ASSERT_EQ("www.example.com", actual_header_block
[":host"]);
428 ASSERT_EQ("value1", actual_header_block
["key1"]);
431 TEST_P(SpdySMProxyTest
, SendSynReply
) {
432 uint32 stream_id
= 82;
433 BalsaHeaders headers
;
434 SpdyHeaderBlock actual_header_block
;
435 headers
.AppendHeader("key1", "value1");
436 headers
.SetResponseFirstlineFromStringPieces("HTTP/1.1", "200", "OK");
438 interface_
->SendSynReply(stream_id
, headers
);
440 ASSERT_EQ(1u, connection_
->output_list()->size());
441 std::list
<DataFrame
*>::const_iterator i
= connection_
->output_list()->begin();
442 DataFrame
* df
= *i
++;
446 if (GetParam() < SPDY4
) {
447 EXPECT_CALL(*spdy_framer_visitor_
, OnSynReply(stream_id
, false, _
))
448 .WillOnce(SaveArg
<2>(&actual_header_block
));
450 EXPECT_CALL(*spdy_framer_visitor_
,
451 OnHeaders(stream_id
, false, 0, false, _
))
452 .WillOnce(SaveArg
<4>(&actual_header_block
));
456 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
457 ASSERT_EQ(1, spdy_framer_
->frames_received());
458 ASSERT_EQ(3u, actual_header_block
.size());
459 ASSERT_EQ("200 OK", actual_header_block
[":status"]);
460 ASSERT_EQ("HTTP/1.1", actual_header_block
[":version"]);
461 ASSERT_EQ("value1", actual_header_block
["key1"]);
464 TEST_P(SpdySMProxyTest
, SendDataFrame
) {
465 uint32 stream_id
= 133;
466 SpdyDataFlags flags
= DATA_FLAG_NONE
;
467 const char* actual_data
;
470 interface_
->SendDataFrame(stream_id
, "hello", 5, flags
, true);
472 ASSERT_EQ(1u, connection_
->output_list()->size());
473 std::list
<DataFrame
*>::const_iterator i
= connection_
->output_list()->begin();
474 DataFrame
* df
= *i
++;
478 EXPECT_CALL(*spdy_framer_visitor_
,
479 OnDataFrameHeader(stream_id
, _
, false));
480 EXPECT_CALL(*spdy_framer_visitor_
,
481 OnStreamFrameData(stream_id
, _
, _
, false))
482 .WillOnce(DoAll(SaveArg
<1>(&actual_data
), SaveArg
<2>(&actual_size
)));
485 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
486 ASSERT_EQ(1, spdy_framer_
->frames_received());
487 ASSERT_EQ("hello", StringPiece(actual_data
, actual_size
));
490 TEST_P(SpdySMProxyTest
, SendLongDataFrame
) {
491 uint32 stream_id
= 133;
492 SpdyDataFlags flags
= DATA_FLAG_NONE
;
493 const char* actual_data
;
496 std::string data
= std::string(kSpdySegmentSize
, 'a') +
497 std::string(kSpdySegmentSize
, 'b') + "c";
498 interface_
->SendDataFrame(stream_id
, data
.data(), data
.size(), flags
, true);
502 for (int i
= 0; i
< 3; ++i
) {
503 EXPECT_CALL(*spdy_framer_visitor_
,
504 OnDataFrameHeader(stream_id
, _
, false));
505 EXPECT_CALL(*spdy_framer_visitor_
,
506 OnStreamFrameData(stream_id
, _
, _
, false))
507 .WillOnce(DoAll(SaveArg
<1>(&actual_data
),
508 SaveArg
<2>(&actual_size
)));
512 ASSERT_EQ(3u, connection_
->output_list()->size());
513 std::list
<DataFrame
*>::const_iterator i
= connection_
->output_list()->begin();
514 DataFrame
* df
= *i
++;
515 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
516 ASSERT_EQ(std::string(kSpdySegmentSize
, 'a'),
517 StringPiece(actual_data
, actual_size
));
520 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
521 ASSERT_EQ(std::string(kSpdySegmentSize
, 'b'),
522 StringPiece(actual_data
, actual_size
));
525 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
526 ASSERT_EQ("c", StringPiece(actual_data
, actual_size
));
529 TEST_P(SpdySMServerTest
, OnSynStream
) {
530 BufferedSpdyFramerVisitorInterface
* visitor
= interface_
.get();
531 uint32 stream_id
= 82;
532 SpdyHeaderBlock spdy_headers
;
533 spdy_headers
["url"] = "http://www.example.com/path";
534 spdy_headers
["method"] = "GET";
535 spdy_headers
["scheme"] = "http";
536 spdy_headers
["version"] = "HTTP/1.1";
539 BalsaHeaders headers
;
540 memory_cache_
->InsertFile(&headers
, "GET_/path", "");
542 visitor
->OnSynStream(stream_id
, 0, 0, true, true, spdy_headers
);
543 ASSERT_TRUE(HasStream(stream_id
));
546 TEST_P(SpdySMServerTest
, NewStream
) {
547 uint32 stream_id
= 13;
548 std::string filename
= "foobar";
551 BalsaHeaders headers
;
552 memory_cache_
->InsertFile(&headers
, filename
, "");
555 interface_
->NewStream(stream_id
, 0, filename
);
556 ASSERT_TRUE(HasStream(stream_id
));
559 TEST_P(SpdySMServerTest
, NewStreamError
) {
560 uint32 stream_id
= 82;
561 SpdyHeaderBlock actual_header_block
;
562 const char* actual_data
;
564 testing::MockFunction
<void(int)> checkpoint
; // NOLINT
566 interface_
->NewStream(stream_id
, 0, "nonexistingfile");
568 ASSERT_EQ(2u, connection_
->output_list()->size());
572 if (GetParam() < SPDY4
) {
573 EXPECT_CALL(*spdy_framer_visitor_
, OnSynReply(stream_id
, false, _
))
574 .WillOnce(SaveArg
<2>(&actual_header_block
));
576 EXPECT_CALL(*spdy_framer_visitor_
,
577 OnHeaders(stream_id
, false, 0, false, _
))
578 .WillOnce(SaveArg
<4>(&actual_header_block
));
580 EXPECT_CALL(checkpoint
, Call(0));
581 EXPECT_CALL(*spdy_framer_visitor_
,
582 OnDataFrameHeader(stream_id
, _
, true));
583 EXPECT_CALL(*spdy_framer_visitor_
,
584 OnStreamFrameData(stream_id
, _
, _
, false)).Times(1)
585 .WillOnce(DoAll(SaveArg
<1>(&actual_data
),
586 SaveArg
<2>(&actual_size
)));
587 EXPECT_CALL(*spdy_framer_visitor_
,
588 OnStreamFrameData(stream_id
, NULL
, 0, true)).Times(1);
591 std::list
<DataFrame
*>::const_iterator i
= connection_
->output_list()->begin();
592 DataFrame
* df
= *i
++;
593 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
596 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
598 ASSERT_EQ(2, spdy_framer_
->frames_received());
599 ASSERT_EQ(2u, actual_header_block
.size());
600 ASSERT_EQ("404 Not Found", actual_header_block
["status"]);
601 ASSERT_EQ("HTTP/1.1", actual_header_block
["version"]);
602 ASSERT_EQ("wtf?", StringPiece(actual_data
, actual_size
));