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_METHOD1(OnSettings
, void(bool clear_persisted
));
67 MOCK_METHOD3(OnSetting
, void(SpdySettingsIds
, uint8
, uint32
));
68 MOCK_METHOD2(OnPing
, void(SpdyPingId unique_id
, bool is_ack
));
69 MOCK_METHOD2(OnRstStream
, void(SpdyStreamId
, SpdyRstStreamStatus
));
70 MOCK_METHOD2(OnGoAway
, void(SpdyStreamId
, SpdyGoAwayStatus
));
71 MOCK_METHOD2(OnWindowUpdate
, void(SpdyStreamId
, uint32
));
72 MOCK_METHOD3(OnPushPromise
,
73 void(SpdyStreamId
, SpdyStreamId
, const SpdyHeaderBlock
&));
74 MOCK_METHOD2(OnUnknownFrame
, bool(SpdyStreamId stream_id
, int frame_type
));
77 class FakeSMConnection
: public SMConnection
{
79 FakeSMConnection(EpollServer
* epoll_server
,
81 MemoryCache
* memory_cache
,
82 FlipAcceptor
* acceptor
,
83 std::string log_prefix
)
84 : SMConnection(epoll_server
,
90 MOCK_METHOD0(Cleanup
, void());
91 MOCK_METHOD8(InitSMConnection
,
92 void(SMConnectionPoolInterface
*,
102 // This class is almost SpdySM, except one function.
103 // This class is the test target of tests in this file.
104 class TestSpdySM
: public SpdySM
{
106 virtual ~TestSpdySM() {}
107 TestSpdySM(SMConnection
* connection
,
108 SMInterface
* sm_http_interface
,
109 EpollServer
* epoll_server
,
110 MemoryCache
* memory_cache
,
111 FlipAcceptor
* acceptor
,
112 SpdyMajorVersion version
)
120 MOCK_METHOD2(FindOrMakeNewSMConnectionInterface
,
121 SMInterface
*(const std::string
&, const std::string
&));
124 class SpdySMTestBase
: public ::testing::TestWithParam
<SpdyMajorVersion
> {
126 explicit SpdySMTestBase(FlipHandlerType type
) {
127 SSLState
* ssl_state
= NULL
;
128 mock_another_interface_
.reset(new MockSMInterface
);
129 memory_cache_
.reset(new MemoryCache
);
130 acceptor_
.reset(new FlipAcceptor(type
,
146 epoll_server_
.reset(new EpollServer
);
147 connection_
.reset(new FakeSMConnection(epoll_server_
.get(),
153 interface_
.reset(new TestSpdySM(connection_
.get(),
154 mock_another_interface_
.get(),
160 spdy_framer_
.reset(new BufferedSpdyFramer(GetParam(), true));
161 spdy_framer_visitor_
.reset(new SpdyFramerVisitor
);
162 spdy_framer_
->set_visitor(spdy_framer_visitor_
.get());
165 virtual ~SpdySMTestBase() {
166 if (acceptor_
->listen_fd_
>= 0) {
167 epoll_server_
->UnregisterFD(acceptor_
->listen_fd_
);
168 close(acceptor_
->listen_fd_
);
169 acceptor_
->listen_fd_
= -1;
171 OutputList
& output_list
= *connection_
->output_list();
172 for (OutputList::const_iterator i
= output_list
.begin();
173 i
!= output_list
.end();
180 bool HasStream(uint32 stream_id
) {
181 return interface_
->output_ordering().ExistsInPriorityMaps(stream_id
);
185 scoped_ptr
<MockSMInterface
> mock_another_interface_
;
186 scoped_ptr
<MemoryCache
> memory_cache_
;
187 scoped_ptr
<FlipAcceptor
> acceptor_
;
188 scoped_ptr
<EpollServer
> epoll_server_
;
189 scoped_ptr
<FakeSMConnection
> connection_
;
190 scoped_ptr
<TestSpdySM
> interface_
;
191 scoped_ptr
<BufferedSpdyFramer
> spdy_framer_
;
192 scoped_ptr
<SpdyFramerVisitor
> spdy_framer_visitor_
;
195 class SpdySMProxyTest
: public SpdySMTestBase
{
197 SpdySMProxyTest() : SpdySMTestBase(FLIP_HANDLER_PROXY
) {}
198 virtual ~SpdySMProxyTest() {}
201 class SpdySMServerTest
: public SpdySMTestBase
{
203 SpdySMServerTest() : SpdySMTestBase(FLIP_HANDLER_SPDY_SERVER
) {}
204 virtual ~SpdySMServerTest() {}
207 INSTANTIATE_TEST_CASE_P(SpdySMProxyTest
,
209 Values(SPDY3
, SPDY4
));
210 INSTANTIATE_TEST_CASE_P(SpdySMServerTest
, SpdySMServerTest
, Values(SPDY4
));
212 TEST_P(SpdySMProxyTest
, InitSMConnection
) {
215 EXPECT_CALL(*connection_
, InitSMConnection(_
, _
, _
, _
, _
, _
, _
, _
));
217 interface_
->InitSMConnection(
218 NULL
, NULL
, epoll_server_
.get(), -1, "", "", "", false);
221 TEST_P(SpdySMProxyTest
, OnStreamFrameData
) {
222 BufferedSpdyFramerVisitorInterface
* visitor
= interface_
.get();
223 scoped_ptr
<MockSMInterface
> mock_interface(new MockSMInterface
);
224 uint32 stream_id
= 92;
225 uint32 associated_id
= 43;
226 SpdyHeaderBlock block
;
227 testing::MockFunction
<void(int)> checkpoint
; // NOLINT
229 scoped_ptr
<SpdyFrame
> frame(spdy_framer_
->CreatePingFrame(12, false));
230 block
[":method"] = "GET";
231 block
[":host"] = "www.example.com";
232 block
[":path"] = "/path";
233 block
[":scheme"] = "http";
234 block
["foo"] = "bar";
237 EXPECT_CALL(*interface_
,
238 FindOrMakeNewSMConnectionInterface(_
, _
))
239 .WillOnce(Return(mock_interface
.get()));
240 EXPECT_CALL(*mock_interface
, SetStreamID(stream_id
));
241 EXPECT_CALL(*mock_interface
, ProcessWriteInput(_
, _
)).Times(1);
242 EXPECT_CALL(checkpoint
, Call(0));
243 EXPECT_CALL(*mock_interface
,
244 ProcessWriteInput(frame
->data(), frame
->size())).Times(1);
247 visitor
->OnSynStream(stream_id
, associated_id
, 0, false, false, block
);
249 visitor
->OnStreamFrameData(stream_id
, frame
->data(), frame
->size(), true);
252 TEST_P(SpdySMProxyTest
, OnRstStream
) {
253 BufferedSpdyFramerVisitorInterface
* visitor
= interface_
.get();
254 uint32 stream_id
= 82;
256 mci
.stream_id
= stream_id
;
259 BalsaHeaders headers
;
260 std::string filename
= "foobar";
261 memory_cache_
->InsertFile(&headers
, filename
, "");
262 mci
.file_data
= memory_cache_
->GetFileData(filename
);
265 interface_
->AddToOutputOrder(mci
);
266 ASSERT_TRUE(HasStream(stream_id
));
267 visitor
->OnRstStream(stream_id
, RST_STREAM_INVALID
);
268 ASSERT_FALSE(HasStream(stream_id
));
271 TEST_P(SpdySMProxyTest
, ProcessReadInput
) {
272 ASSERT_EQ(SpdyFramer::SPDY_RESET
, interface_
->spdy_framer()->state());
273 interface_
->ProcessReadInput("", 1);
274 ASSERT_EQ(SpdyFramer::SPDY_READING_COMMON_HEADER
,
275 interface_
->spdy_framer()->state());
278 TEST_P(SpdySMProxyTest
, ResetForNewConnection
) {
279 uint32 stream_id
= 13;
281 mci
.stream_id
= stream_id
;
283 const char input
[] = {'\0', '\0', '\0'};
286 BalsaHeaders headers
;
287 std::string filename
= "foobar";
288 memory_cache_
->InsertFile(&headers
, filename
, "");
289 mci
.file_data
= memory_cache_
->GetFileData(filename
);
292 interface_
->AddToOutputOrder(mci
);
293 ASSERT_TRUE(HasStream(stream_id
));
294 interface_
->ProcessReadInput(input
, sizeof(input
));
295 ASSERT_NE(SpdyFramer::SPDY_RESET
, interface_
->spdy_framer()->state());
297 interface_
->ResetForNewConnection();
298 ASSERT_FALSE(HasStream(stream_id
));
299 ASSERT_TRUE(interface_
->spdy_framer() == NULL
);
302 TEST_P(SpdySMProxyTest
, CreateFramer
) {
303 interface_
->ResetForNewConnection();
304 interface_
->CreateFramer(SPDY3
);
305 ASSERT_TRUE(interface_
->spdy_framer() != NULL
);
306 ASSERT_EQ(interface_
->spdy_version(), SPDY3
);
308 interface_
->ResetForNewConnection();
309 interface_
->CreateFramer(SPDY4
);
310 ASSERT_TRUE(interface_
->spdy_framer() != NULL
);
311 ASSERT_EQ(interface_
->spdy_version(), SPDY4
);
314 TEST_P(SpdySMProxyTest
, PostAcceptHook
) {
315 interface_
->PostAcceptHook();
317 ASSERT_EQ(1u, connection_
->output_list()->size());
318 std::list
<DataFrame
*>::const_iterator i
= connection_
->output_list()->begin();
319 DataFrame
* df
= *i
++;
323 EXPECT_CALL(*spdy_framer_visitor_
, OnSettings(false));
324 EXPECT_CALL(*spdy_framer_visitor_
,
325 OnSetting(SETTINGS_MAX_CONCURRENT_STREAMS
, 0u, 100u));
327 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
330 TEST_P(SpdySMProxyTest
, NewStream
) {
331 // TODO(yhirano): SpdySM::NewStream leads to crash when
332 // acceptor_->flip_handler_type_ != FLIP_HANDLER_SPDY_SERVER.
333 // It should be fixed though I don't know the solution now.
336 TEST_P(SpdySMProxyTest
, AddToOutputOrder
) {
337 uint32 stream_id
= 13;
339 mci
.stream_id
= stream_id
;
342 BalsaHeaders headers
;
343 std::string filename
= "foobar";
344 memory_cache_
->InsertFile(&headers
, filename
, "");
345 mci
.file_data
= memory_cache_
->GetFileData(filename
);
348 interface_
->AddToOutputOrder(mci
);
349 ASSERT_TRUE(HasStream(stream_id
));
352 TEST_P(SpdySMProxyTest
, SendErrorNotFound
) {
353 uint32 stream_id
= 82;
354 SpdyHeaderBlock actual_header_block
;
355 const char* actual_data
;
357 testing::MockFunction
<void(int)> checkpoint
; // NOLINT
359 interface_
->SendErrorNotFound(stream_id
);
361 ASSERT_EQ(2u, connection_
->output_list()->size());
365 if (GetParam() < SPDY4
) {
366 EXPECT_CALL(*spdy_framer_visitor_
,
367 OnSynReply(stream_id
, false, _
))
368 .WillOnce(SaveArg
<2>(&actual_header_block
));
370 EXPECT_CALL(*spdy_framer_visitor_
,
371 OnHeaders(stream_id
, false, 0, false, _
))
372 .WillOnce(SaveArg
<4>(&actual_header_block
));
374 EXPECT_CALL(checkpoint
, Call(0));
375 EXPECT_CALL(*spdy_framer_visitor_
,
376 OnDataFrameHeader(stream_id
, _
, true));
377 EXPECT_CALL(*spdy_framer_visitor_
,
378 OnStreamFrameData(stream_id
, _
, _
, false)).Times(1)
379 .WillOnce(DoAll(SaveArg
<1>(&actual_data
),
380 SaveArg
<2>(&actual_size
)));
381 EXPECT_CALL(*spdy_framer_visitor_
,
382 OnStreamFrameData(stream_id
, NULL
, 0, true)).Times(1);
385 std::list
<DataFrame
*>::const_iterator i
= connection_
->output_list()->begin();
386 DataFrame
* df
= *i
++;
387 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
390 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
392 ASSERT_EQ(2, spdy_framer_
->frames_received());
393 ASSERT_EQ(2u, actual_header_block
.size());
394 ASSERT_EQ("404 Not Found", actual_header_block
[":status"]);
395 ASSERT_EQ("HTTP/1.1", actual_header_block
[":version"]);
396 ASSERT_EQ("wtf?", StringPiece(actual_data
, actual_size
));
399 TEST_P(SpdySMProxyTest
, SendSynStream
) {
400 uint32 stream_id
= 82;
401 BalsaHeaders headers
;
402 SpdyHeaderBlock actual_header_block
;
403 headers
.AppendHeader("key1", "value1");
404 headers
.AppendHeader("Host", "www.example.com");
405 headers
.SetRequestFirstlineFromStringPieces("GET", "/path", "HTTP/1.1");
407 interface_
->SendSynStream(stream_id
, headers
);
409 ASSERT_EQ(1u, connection_
->output_list()->size());
410 std::list
<DataFrame
*>::const_iterator i
= connection_
->output_list()->begin();
411 DataFrame
* df
= *i
++;
415 EXPECT_CALL(*spdy_framer_visitor_
,
416 OnSynStream(stream_id
, 0, _
, false, false, _
))
417 .WillOnce(SaveArg
<5>(&actual_header_block
));
420 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
421 ASSERT_EQ(1, spdy_framer_
->frames_received());
422 ASSERT_EQ(5u, actual_header_block
.size());
423 ASSERT_EQ("GET", actual_header_block
[":method"]);
424 ASSERT_EQ("HTTP/1.1", actual_header_block
[":version"]);
425 ASSERT_EQ("/path", actual_header_block
[":path"]);
426 ASSERT_EQ("www.example.com", actual_header_block
[":host"]);
427 ASSERT_EQ("value1", actual_header_block
["key1"]);
430 TEST_P(SpdySMProxyTest
, SendSynReply
) {
431 uint32 stream_id
= 82;
432 BalsaHeaders headers
;
433 SpdyHeaderBlock actual_header_block
;
434 headers
.AppendHeader("key1", "value1");
435 headers
.SetResponseFirstlineFromStringPieces("HTTP/1.1", "200", "OK");
437 interface_
->SendSynReply(stream_id
, headers
);
439 ASSERT_EQ(1u, connection_
->output_list()->size());
440 std::list
<DataFrame
*>::const_iterator i
= connection_
->output_list()->begin();
441 DataFrame
* df
= *i
++;
445 if (GetParam() < SPDY4
) {
446 EXPECT_CALL(*spdy_framer_visitor_
, OnSynReply(stream_id
, false, _
))
447 .WillOnce(SaveArg
<2>(&actual_header_block
));
449 EXPECT_CALL(*spdy_framer_visitor_
,
450 OnHeaders(stream_id
, false, 0, false, _
))
451 .WillOnce(SaveArg
<4>(&actual_header_block
));
455 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
456 ASSERT_EQ(1, spdy_framer_
->frames_received());
457 ASSERT_EQ(3u, actual_header_block
.size());
458 ASSERT_EQ("200 OK", actual_header_block
[":status"]);
459 ASSERT_EQ("HTTP/1.1", actual_header_block
[":version"]);
460 ASSERT_EQ("value1", actual_header_block
["key1"]);
463 TEST_P(SpdySMProxyTest
, SendDataFrame
) {
464 uint32 stream_id
= 133;
465 SpdyDataFlags flags
= DATA_FLAG_NONE
;
466 const char* actual_data
;
469 interface_
->SendDataFrame(stream_id
, "hello", 5, flags
, true);
471 ASSERT_EQ(1u, connection_
->output_list()->size());
472 std::list
<DataFrame
*>::const_iterator i
= connection_
->output_list()->begin();
473 DataFrame
* df
= *i
++;
477 EXPECT_CALL(*spdy_framer_visitor_
,
478 OnDataFrameHeader(stream_id
, _
, false));
479 EXPECT_CALL(*spdy_framer_visitor_
,
480 OnStreamFrameData(stream_id
, _
, _
, false))
481 .WillOnce(DoAll(SaveArg
<1>(&actual_data
), SaveArg
<2>(&actual_size
)));
484 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
485 ASSERT_EQ(1, spdy_framer_
->frames_received());
486 ASSERT_EQ("hello", StringPiece(actual_data
, actual_size
));
489 TEST_P(SpdySMProxyTest
, SendLongDataFrame
) {
490 uint32 stream_id
= 133;
491 SpdyDataFlags flags
= DATA_FLAG_NONE
;
492 const char* actual_data
;
495 std::string data
= std::string(kSpdySegmentSize
, 'a') +
496 std::string(kSpdySegmentSize
, 'b') + "c";
497 interface_
->SendDataFrame(stream_id
, data
.data(), data
.size(), flags
, true);
501 for (int i
= 0; i
< 3; ++i
) {
502 EXPECT_CALL(*spdy_framer_visitor_
,
503 OnDataFrameHeader(stream_id
, _
, false));
504 EXPECT_CALL(*spdy_framer_visitor_
,
505 OnStreamFrameData(stream_id
, _
, _
, false))
506 .WillOnce(DoAll(SaveArg
<1>(&actual_data
),
507 SaveArg
<2>(&actual_size
)));
511 ASSERT_EQ(3u, connection_
->output_list()->size());
512 std::list
<DataFrame
*>::const_iterator i
= connection_
->output_list()->begin();
513 DataFrame
* df
= *i
++;
514 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
515 ASSERT_EQ(std::string(kSpdySegmentSize
, 'a'),
516 StringPiece(actual_data
, actual_size
));
519 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
520 ASSERT_EQ(std::string(kSpdySegmentSize
, 'b'),
521 StringPiece(actual_data
, actual_size
));
524 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
525 ASSERT_EQ("c", StringPiece(actual_data
, actual_size
));
528 TEST_P(SpdySMServerTest
, OnSynStream
) {
529 BufferedSpdyFramerVisitorInterface
* visitor
= interface_
.get();
530 uint32 stream_id
= 82;
531 SpdyHeaderBlock spdy_headers
;
532 spdy_headers
["url"] = "http://www.example.com/path";
533 spdy_headers
["method"] = "GET";
534 spdy_headers
["scheme"] = "http";
535 spdy_headers
["version"] = "HTTP/1.1";
538 BalsaHeaders headers
;
539 memory_cache_
->InsertFile(&headers
, "GET_/path", "");
541 visitor
->OnSynStream(stream_id
, 0, 0, true, true, spdy_headers
);
542 ASSERT_TRUE(HasStream(stream_id
));
545 TEST_P(SpdySMServerTest
, NewStream
) {
546 uint32 stream_id
= 13;
547 std::string filename
= "foobar";
550 BalsaHeaders headers
;
551 memory_cache_
->InsertFile(&headers
, filename
, "");
554 interface_
->NewStream(stream_id
, 0, filename
);
555 ASSERT_TRUE(HasStream(stream_id
));
558 TEST_P(SpdySMServerTest
, NewStreamError
) {
559 uint32 stream_id
= 82;
560 SpdyHeaderBlock actual_header_block
;
561 const char* actual_data
;
563 testing::MockFunction
<void(int)> checkpoint
; // NOLINT
565 interface_
->NewStream(stream_id
, 0, "nonexistingfile");
567 ASSERT_EQ(2u, connection_
->output_list()->size());
571 if (GetParam() < SPDY4
) {
572 EXPECT_CALL(*spdy_framer_visitor_
, OnSynReply(stream_id
, false, _
))
573 .WillOnce(SaveArg
<2>(&actual_header_block
));
575 EXPECT_CALL(*spdy_framer_visitor_
,
576 OnHeaders(stream_id
, false, 0, false, _
))
577 .WillOnce(SaveArg
<4>(&actual_header_block
));
579 EXPECT_CALL(checkpoint
, Call(0));
580 EXPECT_CALL(*spdy_framer_visitor_
,
581 OnDataFrameHeader(stream_id
, _
, true));
582 EXPECT_CALL(*spdy_framer_visitor_
,
583 OnStreamFrameData(stream_id
, _
, _
, false)).Times(1)
584 .WillOnce(DoAll(SaveArg
<1>(&actual_data
),
585 SaveArg
<2>(&actual_size
)));
586 EXPECT_CALL(*spdy_framer_visitor_
,
587 OnStreamFrameData(stream_id
, NULL
, 0, true)).Times(1);
590 std::list
<DataFrame
*>::const_iterator i
= connection_
->output_list()->begin();
591 DataFrame
* df
= *i
++;
592 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
595 spdy_framer_
->ProcessInput(df
->data
, df
->size
);
597 ASSERT_EQ(2, spdy_framer_
->frames_received());
598 ASSERT_EQ(2u, actual_header_block
.size());
599 ASSERT_EQ("404 Not Found", actual_header_block
["status"]);
600 ASSERT_EQ("HTTP/1.1", actual_header_block
["version"]);
601 ASSERT_EQ("wtf?", StringPiece(actual_data
, actual_size
));