diff options
author | yhirano@chromium.org <yhirano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-19 06:44:45 +0000 |
---|---|---|
committer | yhirano@chromium.org <yhirano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-19 06:44:45 +0000 |
commit | 139fc2f431e10b4cc52813607f00fb134ebf4cde (patch) | |
tree | 80744c04f048c87f2273ec8d33c42389899bec56 /net/tools/flip_server | |
parent | 1ca42a4e4ad10553e31cb4e9dce443b270b3ae64 (diff) | |
download | chromium_src-139fc2f431e10b4cc52813607f00fb134ebf4cde.zip chromium_src-139fc2f431e10b4cc52813607f00fb134ebf4cde.tar.gz chromium_src-139fc2f431e10b4cc52813607f00fb134ebf4cde.tar.bz2 |
Add unittests for HttpSM and SpdySM on FlipServer.
Add unittests for HttpSM and SpdySM.
Fix some defects and many style issues.
BUG=267354
R=rch@chromium.org
Review URL: https://chromiumcodereview.appspot.com/22859020
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@218237 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/tools/flip_server')
-rw-r--r-- | net/tools/flip_server/create_listener.cc | 5 | ||||
-rw-r--r-- | net/tools/flip_server/create_listener.h | 3 | ||||
-rw-r--r-- | net/tools/flip_server/epoll_server.cc | 8 | ||||
-rw-r--r-- | net/tools/flip_server/flip_config.cc | 2 | ||||
-rw-r--r-- | net/tools/flip_server/flip_test_utils.cc | 15 | ||||
-rw-r--r-- | net/tools/flip_server/flip_test_utils.h | 53 | ||||
-rw-r--r-- | net/tools/flip_server/http_interface.cc | 26 | ||||
-rw-r--r-- | net/tools/flip_server/http_interface.h | 15 | ||||
-rw-r--r-- | net/tools/flip_server/http_interface_test.cc | 487 | ||||
-rw-r--r-- | net/tools/flip_server/mem_cache.cc | 30 | ||||
-rw-r--r-- | net/tools/flip_server/mem_cache.h | 6 | ||||
-rw-r--r-- | net/tools/flip_server/output_ordering.cc | 11 | ||||
-rw-r--r-- | net/tools/flip_server/output_ordering.h | 2 | ||||
-rw-r--r-- | net/tools/flip_server/sm_connection.cc | 2 | ||||
-rw-r--r-- | net/tools/flip_server/sm_connection.h | 18 | ||||
-rw-r--r-- | net/tools/flip_server/spdy_interface.cc | 32 | ||||
-rw-r--r-- | net/tools/flip_server/spdy_interface.h | 17 | ||||
-rw-r--r-- | net/tools/flip_server/spdy_interface_test.cc | 634 |
18 files changed, 1270 insertions, 96 deletions
diff --git a/net/tools/flip_server/create_listener.cc b/net/tools/flip_server/create_listener.cc index 4676912..21867a9 100644 --- a/net/tools/flip_server/create_listener.cc +++ b/net/tools/flip_server/create_listener.cc @@ -67,7 +67,7 @@ bool CloseSocket(int *fd, int tries) { //////////////////////////////////////////////////////////////////////////////// // Sets an FD to be nonblocking. -void SetNonBlocking(int fd) { +void FlipSetNonBlocking(int fd) { DCHECK_GE(fd, 0); int fcntl_return = fcntl(fd, F_GETFL, 0); @@ -270,7 +270,7 @@ int CreateConnectedSocket( int *connect_fd, return -1; } - SetNonBlocking( sock ); + FlipSetNonBlocking( sock ); if (disable_nagle) { if (!SetDisableNagle(sock)) { @@ -297,4 +297,3 @@ int CreateConnectedSocket( int *connect_fd, } } // namespace net - diff --git a/net/tools/flip_server/create_listener.h b/net/tools/flip_server/create_listener.h index 4a4a6e1..a3f5a87 100644 --- a/net/tools/flip_server/create_listener.h +++ b/net/tools/flip_server/create_listener.h @@ -10,7 +10,7 @@ namespace net { -void SetNonBlocking(int fd); +void FlipSetNonBlocking(int fd); // Summary: // creates a socket for listening, and bind()s and listen()s it. @@ -54,4 +54,3 @@ int CreateConnectedSocket(int *connect_fd, } // namespace net #endif // NET_TOOLS_FLIP_SERVER_CREATE_LISTENER_H__ - diff --git a/net/tools/flip_server/epoll_server.cc b/net/tools/flip_server/epoll_server.cc index af09c14..0e09a6d 100644 --- a/net/tools/flip_server/epoll_server.cc +++ b/net/tools/flip_server/epoll_server.cc @@ -4,11 +4,10 @@ #include "net/tools/flip_server/epoll_server.h" +#include <unistd.h> // For read, pipe, close and write. #include <stdlib.h> // for abort #include <errno.h> // for errno and strerror_r #include <algorithm> -#include <ostream> -#include <unistd.h> // For read, pipe, close and write. #include <utility> #include <vector> @@ -482,7 +481,7 @@ void EpollServer::UnregisterAlarm(const AlarmRegToken& iterator_token) { } int EpollServer::NumFDsRegistered() const { - DCHECK(cb_map_.size() >= 1); + DCHECK_GE(cb_map_.size(), 1u); // Omit the internal FD (read_fd_) return cb_map_.size() - 1; } @@ -490,7 +489,7 @@ int EpollServer::NumFDsRegistered() const { void EpollServer::Wake() { char data = 'd'; // 'd' is for data. It's good enough for me. int rv = write(write_fd_, &data, 1); - DCHECK(rv == 1); + DCHECK_EQ(rv, 1); } int64 EpollServer::NowInUsec() const { @@ -819,4 +818,3 @@ void EpollAlarm::UnregisterIfRegistered() { } } // namespace net - diff --git a/net/tools/flip_server/flip_config.cc b/net/tools/flip_server/flip_config.cc index eb5c3ca..3de3028 100644 --- a/net/tools/flip_server/flip_config.cc +++ b/net/tools/flip_server/flip_config.cc @@ -72,7 +72,7 @@ FlipAcceptor::FlipAcceptor(enum FlipHandlerType flip_handler_type, } } - SetNonBlocking(listen_fd_); + FlipSetNonBlocking(listen_fd_); VLOG(1) << "Listening on socket: "; if (flip_handler_type == FLIP_HANDLER_PROXY) VLOG(1) << "\tType : Proxy"; diff --git a/net/tools/flip_server/flip_test_utils.cc b/net/tools/flip_server/flip_test_utils.cc new file mode 100644 index 0000000..d9846cf --- /dev/null +++ b/net/tools/flip_server/flip_test_utils.cc @@ -0,0 +1,15 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/tools/flip_server/flip_test_utils.h" + +namespace net { + +MockSMInterface::MockSMInterface() { +} + +MockSMInterface::~MockSMInterface() { +} + +} // namespace net diff --git a/net/tools/flip_server/flip_test_utils.h b/net/tools/flip_server/flip_test_utils.h new file mode 100644 index 0000000..8a10b95 --- /dev/null +++ b/net/tools/flip_server/flip_test_utils.h @@ -0,0 +1,53 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_TOOLS_FLIP_SERVER_FLIP_TEST_UTILS_H_ +#define NET_TOOLS_FLIP_SERVER_FLIP_TEST_UTILS_H_ + +#include <string> + +#include "net/tools/flip_server/sm_interface.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +class MockSMInterface : public SMInterface { + public: + MockSMInterface(); + virtual ~MockSMInterface(); + + MOCK_METHOD2(InitSMInterface, void(SMInterface*, int32)); + MOCK_METHOD8(InitSMConnection, void(SMConnectionPoolInterface*, + SMInterface*, + EpollServer*, + int, + std::string, + std::string, + std::string, + bool)); + MOCK_METHOD2(ProcessReadInput, size_t(const char*, size_t)); + MOCK_METHOD2(ProcessWriteInput, size_t(const char*, size_t)); + MOCK_METHOD1(SetStreamID, void(uint32 stream_id)); + MOCK_CONST_METHOD0(MessageFullyRead, bool()); + MOCK_CONST_METHOD0(Error, bool()); + MOCK_CONST_METHOD0(ErrorAsString, const char*()); + MOCK_METHOD0(Reset, void()); + MOCK_METHOD1(ResetForNewInterface, void(int32 server_idx)); + MOCK_METHOD0(ResetForNewConnection, void()); + MOCK_METHOD0(Cleanup, void()); + MOCK_METHOD0(PostAcceptHook, int()); + MOCK_METHOD3(NewStream, void(uint32, uint32, const std::string&)); + MOCK_METHOD1(SendEOF, void(uint32 stream_id)); + MOCK_METHOD1(SendErrorNotFound, void(uint32 stream_id)); + MOCK_METHOD2(SendSynStream, size_t(uint32, const BalsaHeaders&)); + MOCK_METHOD2(SendSynReply, size_t(uint32, const BalsaHeaders&)); + MOCK_METHOD5(SendDataFrame, void(uint32, const char*, int64, uint32, bool)); + MOCK_METHOD0(GetOutput, void()); + MOCK_METHOD0(set_is_request, void()); +}; + +} // namespace net + +#endif // NET_TOOLS_FLIP_SERVER_FLIP_TEST_UTILS_H_ diff --git a/net/tools/flip_server/http_interface.cc b/net/tools/flip_server/http_interface.cc index 7a44c03..916ba51 100644 --- a/net/tools/flip_server/http_interface.cc +++ b/net/tools/flip_server/http_interface.cc @@ -14,11 +14,9 @@ namespace net { HttpSM::HttpSM(SMConnection* connection, SMInterface* sm_spdy_interface, - EpollServer* epoll_server, MemoryCache* memory_cache, FlipAcceptor* acceptor) - : seq_num_(0), - http_framer_(new BalsaFrame), + : http_framer_(new BalsaFrame), stream_id_(0), server_idx_(-1), connection_(connection), @@ -37,7 +35,7 @@ HttpSM::~HttpSM() { delete http_framer_; } -void HttpSM::ProcessBodyData(const char *input, size_t size) { +void HttpSM::ProcessBodyData(const char* input, size_t size) { if (acceptor_->flip_handler_type_ == FLIP_HANDLER_PROXY) { VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Process Body Data: stream " << stream_id_ << ": size " << size; @@ -94,10 +92,6 @@ void HttpSM::AddToOutputOrder(const MemCacheIter& mci) { output_ordering_.AddToOutputOrder(mci); } -void HttpSM::SendOKResponse(uint32 stream_id, std::string* output) { - SendOKResponseImpl(stream_id, output); -} - void HttpSM::InitSMInterface(SMInterface* sm_spdy_interface, int32 server_idx) { sm_spdy_interface_ = sm_spdy_interface; @@ -133,10 +127,10 @@ size_t HttpSM::ProcessReadInput(const char* data, size_t len) { size_t HttpSM::ProcessWriteInput(const char* data, size_t len) { VLOG(2) << ACCEPTOR_CLIENT_IDENT << "HttpSM: Process write input: size " << len << ": stream " << stream_id_; - char * dataPtr = new char[len]; + char* dataPtr = new char[len]; memcpy(dataPtr, data, len); DataFrame* data_frame = new DataFrame; - data_frame->data = (const char *)dataPtr; + data_frame->data = dataPtr; data_frame->size = len; data_frame->delete_when_done = true; connection_->EnqueueDataFrame(data_frame); @@ -178,7 +172,6 @@ void HttpSM::ResetForNewConnection() { << "Sending EOF to spdy."; sm_spdy_interface_->SendEOF(stream_id_); } - seq_num_ = 0; output_ordering_.Reset(); http_framer_->Reset(); if (sm_spdy_interface_) { @@ -257,17 +250,6 @@ void HttpSM::SendErrorNotFoundImpl(uint32 stream_id) { output_ordering_.RemoveStreamId(stream_id); } -void HttpSM::SendOKResponseImpl(uint32 stream_id, std::string* output) { - BalsaHeaders my_headers; - my_headers.SetFirstlineFromStringPieces("HTTP/1.1", "200", "OK"); - my_headers.RemoveAllOfHeader("content-length"); - my_headers.AppendHeader("transfer-encoding", "chunked"); - SendSynReplyImpl(stream_id, my_headers); - SendDataFrame(stream_id, output->c_str(), output->size(), 0, false); - SendEOFImpl(stream_id); - output_ordering_.RemoveStreamId(stream_id); -} - size_t HttpSM::SendSynReplyImpl(uint32 stream_id, const BalsaHeaders& headers) { SimpleBuffer sb; headers.WriteHeaderAndEndingToBuffer(&sb); diff --git a/net/tools/flip_server/http_interface.h b/net/tools/flip_server/http_interface.h index e311881..18e616d 100644 --- a/net/tools/flip_server/http_interface.h +++ b/net/tools/flip_server/http_interface.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef NET_TOOLS_FLIP_SERVER_HTTP_INTERFACE_ -#define NET_TOOLS_FLIP_SERVER_HTTP_INTERFACE_ +#ifndef NET_TOOLS_FLIP_SERVER_HTTP_INTERFACE_H_ +#define NET_TOOLS_FLIP_SERVER_HTTP_INTERFACE_H_ #include <string> @@ -27,7 +27,6 @@ class HttpSM : public BalsaVisitorInterface, public: HttpSM(SMConnection* connection, SMInterface* sm_spdy_interface, - EpollServer* epoll_server, MemoryCache* memory_cache, FlipAcceptor* acceptor); virtual ~HttpSM(); @@ -69,9 +68,9 @@ class HttpSM : public BalsaVisitorInterface, public: void AddToOutputOrder(const MemCacheIter& mci); - void SendOKResponse(uint32 stream_id, std::string* output); BalsaFrame* spdy_framer() { return http_framer_; } virtual void set_is_request() OVERRIDE {} + const OutputOrdering& output_ordering() const { return output_ordering_; } // SMInterface: virtual void InitSMInterface(SMInterface* sm_spdy_interface, @@ -110,7 +109,7 @@ class HttpSM : public BalsaVisitorInterface, private: void SendEOFImpl(uint32 stream_id); void SendErrorNotFoundImpl(uint32 stream_id); - void SendOKResponseImpl(uint32 stream_id, std::string* output); + void SendOKResponseImpl(uint32 stream_id, const std::string& output); size_t SendSynReplyImpl(uint32 stream_id, const BalsaHeaders& headers); size_t SendSynStreamImpl(uint32 stream_id, const BalsaHeaders& headers); void SendDataFrameImpl(uint32 stream_id, const char* data, int64 len, @@ -119,7 +118,6 @@ class HttpSM : public BalsaVisitorInterface, virtual void GetOutput() OVERRIDE; private: - uint64 seq_num_; BalsaFrame* http_framer_; BalsaHeaders headers_; uint32 stream_id_; @@ -133,7 +131,6 @@ class HttpSM : public BalsaVisitorInterface, FlipAcceptor* acceptor_; }; -} // namespace - -#endif // NET_TOOLS_FLIP_SERVER_HTTP_INTERFACE_ +} // namespace net +#endif // NET_TOOLS_FLIP_SERVER_HTTP_INTERFACE_H_ diff --git a/net/tools/flip_server/http_interface_test.cc b/net/tools/flip_server/http_interface_test.cc new file mode 100644 index 0000000..ba9b3aa --- /dev/null +++ b/net/tools/flip_server/http_interface_test.cc @@ -0,0 +1,487 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/tools/flip_server/http_interface.h" + +#include <list> + +#include "base/memory/scoped_ptr.h" +#include "base/stl_util.h" +#include "base/strings/string_piece.h" +#include "net/tools/flip_server/balsa_enums.h" +#include "net/tools/flip_server/balsa_frame.h" +#include "net/tools/flip_server/balsa_headers.h" +#include "net/tools/flip_server/flip_config.h" +#include "net/tools/flip_server/flip_test_utils.h" +#include "net/tools/flip_server/mem_cache.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +using ::base::StringPiece; +using ::testing::_; +using ::testing::InSequence; + +namespace { + +class MockSMConnection : public SMConnection { + public: + MockSMConnection(EpollServer* epoll_server, + SSLState* ssl_state, + MemoryCache* memory_cache, + FlipAcceptor* acceptor, + std::string log_prefix) + : SMConnection(epoll_server, + ssl_state, + memory_cache, + acceptor, + log_prefix) {} + + MOCK_METHOD0(Cleanup, void()); + MOCK_METHOD8(InitSMConnection, void(SMConnectionPoolInterface*, + SMInterface*, + EpollServer*, + int, + std::string, + std::string, + std::string, + bool)); +}; + +class FlipHttpSMTest : public ::testing::Test { + public: + explicit FlipHttpSMTest(FlipHandlerType type = FLIP_HANDLER_PROXY) { + SSLState* ssl_state = NULL; + mock_another_interface_.reset(new MockSMInterface); + memory_cache_.reset(new MemoryCache); + acceptor_.reset(new FlipAcceptor(type, + "127.0.0.1", + "8941", + "ssl_cert_filename", + "ssl_key_filename", + "127.0.0.1", + "8942", + "127.0.0.1", + "8943", + 1, + 0, + true, + 1, + false, + true, + NULL)); + epoll_server_.reset(new EpollServer); + connection_.reset(new MockSMConnection(epoll_server_.get(), + ssl_state, + memory_cache_.get(), + acceptor_.get(), + "log_prefix")); + + interface_.reset(new HttpSM(connection_.get(), + mock_another_interface_.get(), + memory_cache_.get(), + acceptor_.get())); + } + + virtual void TearDown() OVERRIDE { + if (acceptor_->listen_fd_ >= 0) { + epoll_server_->UnregisterFD(acceptor_->listen_fd_); + close(acceptor_->listen_fd_); + acceptor_->listen_fd_ = -1; + } + STLDeleteElements(connection_->output_list()); + } + + bool HasStream(uint32 stream_id) { + return interface_->output_ordering().ExistsInPriorityMaps(stream_id); + } + + protected: + scoped_ptr<MockSMInterface> mock_another_interface_; + scoped_ptr<MemoryCache> memory_cache_; + scoped_ptr<FlipAcceptor> acceptor_; + scoped_ptr<EpollServer> epoll_server_; + scoped_ptr<MockSMConnection> connection_; + scoped_ptr<HttpSM> interface_; +}; + +class FlipHttpSMProxyTest : public FlipHttpSMTest { + public: + FlipHttpSMProxyTest() : FlipHttpSMTest(FLIP_HANDLER_PROXY) {} + virtual ~FlipHttpSMProxyTest() {} +}; + +class FlipHttpSMHttpTest : public FlipHttpSMTest { + public: + FlipHttpSMHttpTest() : FlipHttpSMTest(FLIP_HANDLER_HTTP_SERVER) {} + virtual ~FlipHttpSMHttpTest() {} +}; + +class FlipHttpSMSpdyTest : public FlipHttpSMTest { + public: + FlipHttpSMSpdyTest() : FlipHttpSMTest(FLIP_HANDLER_SPDY_SERVER) {} + virtual ~FlipHttpSMSpdyTest() {} +}; + +TEST_F(FlipHttpSMTest, Construct) { + ASSERT_FALSE(interface_->spdy_framer()->is_request()); +} + +TEST_F(FlipHttpSMTest, AddToOutputOrder) { + uint32 stream_id = 13; + MemCacheIter mci; + mci.stream_id = stream_id; + + { + BalsaHeaders headers; + std::string filename = "foobar"; + memory_cache_->InsertFile(&headers, filename, ""); + mci.file_data = memory_cache_->GetFileData(filename); + } + + interface_->AddToOutputOrder(mci); + ASSERT_TRUE(HasStream(stream_id)); +} + +TEST_F(FlipHttpSMTest, InitSMInterface) { + scoped_ptr<MockSMInterface> mock(new MockSMInterface); + { + InSequence s; + EXPECT_CALL(*mock_another_interface_, SendEOF(_)); + EXPECT_CALL(*mock_another_interface_, ResetForNewInterface(_)); + EXPECT_CALL(*mock, SendEOF(_)); + EXPECT_CALL(*mock, ResetForNewInterface(_)); + } + + interface_->ResetForNewConnection(); + interface_->InitSMInterface(mock.get(), 0); + interface_->ResetForNewConnection(); +} + +TEST_F(FlipHttpSMTest, InitSMConnection) { + EXPECT_CALL(*connection_, InitSMConnection(_, _, _, _, _, _, _, _)); + + interface_->InitSMConnection(NULL, NULL, NULL, 0, "", "", "", false); +} + +TEST_F(FlipHttpSMTest, ProcessReadInput) { + std::string data = "HTTP/1.1 200 OK\r\n" + "Content-Length: 14\r\n\r\n" + "hello, world\r\n"; + testing::MockFunction<void(int)> checkpoint; + { + InSequence s; + EXPECT_CALL(*mock_another_interface_, SendSynReply(_, _)); + EXPECT_CALL(checkpoint, Call(0)); + EXPECT_CALL(*mock_another_interface_, SendDataFrame(_, _, _, _, _)); + EXPECT_CALL(*mock_another_interface_, SendEOF(_)); + } + + ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE, + interface_->spdy_framer()->ParseState()); + + size_t read = interface_->ProcessReadInput(data.data(), data.size()); + ASSERT_EQ(39u, read); + checkpoint.Call(0); + read += interface_->ProcessReadInput(&data.data()[read], data.size() - read); + ASSERT_EQ(data.size(), read); + ASSERT_EQ(BalsaFrameEnums::MESSAGE_FULLY_READ, + interface_->spdy_framer()->ParseState()); + ASSERT_TRUE(interface_->MessageFullyRead()); +} + +TEST_F(FlipHttpSMTest, ProcessWriteInput) { + std::string data = "hello, world"; + interface_->ProcessWriteInput(data.data(), data.size()); + + ASSERT_EQ(1u, connection_->output_list()->size()); + std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin(); + DataFrame* df = *i++; + ASSERT_EQ(data, StringPiece(df->data, df->size)); + ASSERT_EQ(connection_->output_list()->end(), i); +} + +TEST_F(FlipHttpSMTest, Reset) { + std::string data = "HTTP/1.1 200 OK\r\n\r\n"; + testing::MockFunction<void(int)> checkpoint; + { + InSequence s; + EXPECT_CALL(*mock_another_interface_, SendSynReply(_, _)); + EXPECT_CALL(checkpoint, Call(0)); + } + + ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE, + interface_->spdy_framer()->ParseState()); + + interface_->ProcessReadInput(data.data(), data.size()); + checkpoint.Call(0); + ASSERT_FALSE(interface_->MessageFullyRead()); + ASSERT_EQ(BalsaFrameEnums::READING_UNTIL_CLOSE, + interface_->spdy_framer()->ParseState()); + + interface_->Reset(); + ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE, + interface_->spdy_framer()->ParseState()); +} + +TEST_F(FlipHttpSMTest, ResetForNewConnection) { + std::string data = "HTTP/1.1 200 OK\r\n\r\n"; + testing::MockFunction<void(int)> checkpoint; + { + InSequence s; + EXPECT_CALL(*mock_another_interface_, SendSynReply(_, _)); + EXPECT_CALL(checkpoint, Call(0)); + EXPECT_CALL(*mock_another_interface_, SendEOF(_)); + EXPECT_CALL(*mock_another_interface_, ResetForNewInterface(_)); + } + + ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE, + interface_->spdy_framer()->ParseState()); + + interface_->ProcessReadInput(data.data(), data.size()); + checkpoint.Call(0); + ASSERT_FALSE(interface_->MessageFullyRead()); + ASSERT_EQ(BalsaFrameEnums::READING_UNTIL_CLOSE, + interface_->spdy_framer()->ParseState()); + + interface_->ResetForNewConnection(); + ASSERT_EQ(BalsaFrameEnums::READING_HEADER_AND_FIRSTLINE, + interface_->spdy_framer()->ParseState()); +} + +TEST_F(FlipHttpSMTest, NewStream) { + uint32 stream_id = 4; + { + BalsaHeaders headers; + std::string filename = "foobar"; + memory_cache_->InsertFile(&headers, filename, ""); + } + + interface_->NewStream(stream_id, 1, "foobar"); + ASSERT_TRUE(HasStream(stream_id)); +} + +TEST_F(FlipHttpSMTest, NewStreamError) { + std::string syn_reply = "HTTP/1.1 404 Not Found\r\n" + "transfer-encoding: chunked\r\n\r\n"; + std::string body = "e\r\npage not found\r\n"; + uint32 stream_id = 4; + + ASSERT_FALSE(HasStream(stream_id)); + interface_->NewStream(stream_id, 1, "foobar"); + + ASSERT_EQ(3u, connection_->output_list()->size()); + std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin(); + DataFrame* df = *i++; + ASSERT_EQ(syn_reply, StringPiece(df->data, df->size)); + df = *i++; + ASSERT_EQ(body, StringPiece(df->data, df->size)); + df = *i++; + ASSERT_EQ("0\r\n\r\n", StringPiece(df->data, df->size)); + ASSERT_FALSE(HasStream(stream_id)); +} + +TEST_F(FlipHttpSMTest, SendErrorNotFound) { + std::string syn_reply = "HTTP/1.1 404 Not Found\r\n" + "transfer-encoding: chunked\r\n\r\n"; + std::string body = "e\r\npage not found\r\n"; + uint32 stream_id = 13; + MemCacheIter mci; + mci.stream_id = stream_id; + + { + BalsaHeaders headers; + std::string filename = "foobar"; + memory_cache_->InsertFile(&headers, filename, ""); + mci.file_data = memory_cache_->GetFileData(filename); + } + + interface_->AddToOutputOrder(mci); + ASSERT_TRUE(HasStream(stream_id)); + interface_->SendErrorNotFound(stream_id); + + ASSERT_EQ(3u, connection_->output_list()->size()); + std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin(); + DataFrame* df = *i++; + ASSERT_EQ(syn_reply, StringPiece(df->data, df->size)); + df = *i++; + ASSERT_EQ(body, StringPiece(df->data, df->size)); + df = *i++; + ASSERT_EQ("0\r\n\r\n", StringPiece(df->data, df->size)); + ASSERT_FALSE(HasStream(stream_id)); +} + +TEST_F(FlipHttpSMTest, SendSynStream) { + std::string expected = "GET / HTTP/1.0\r\n" + "key1: value1\r\n\r\n"; + BalsaHeaders headers; + headers.SetResponseFirstlineFromStringPieces("GET", "/path", "HTTP/1.0"); + headers.AppendHeader("key1", "value1"); + interface_->SendSynStream(18, headers); + + // TODO(yhirano): Is this behavior correct? + ASSERT_EQ(0u, connection_->output_list()->size()); +} + +TEST_F(FlipHttpSMTest, SendSynReply) { + std::string expected = "HTTP/1.1 200 OK\r\n" + "key1: value1\r\n\r\n"; + BalsaHeaders headers; + headers.SetResponseFirstlineFromStringPieces("HTTP/1.1", "200", "OK"); + headers.AppendHeader("key1", "value1"); + interface_->SendSynReply(18, headers); + + ASSERT_EQ(1u, connection_->output_list()->size()); + DataFrame* df = connection_->output_list()->front(); + ASSERT_EQ(expected, StringPiece(df->data, df->size)); +} + +TEST_F(FlipHttpSMTest, SendDataFrame) { + std::string data = "foo bar baz"; + interface_->SendDataFrame(12, data.data(), data.size(), 0, false); + + ASSERT_EQ(1u, connection_->output_list()->size()); + DataFrame* df = connection_->output_list()->front(); + ASSERT_EQ("b\r\nfoo bar baz\r\n", StringPiece(df->data, df->size)); +} + +TEST_F(FlipHttpSMProxyTest, ProcessBodyData) { + BalsaVisitorInterface* visitor = interface_.get(); + std::string data = "hello, world"; + { + InSequence s; + EXPECT_CALL(*mock_another_interface_, + SendDataFrame(0, data.data(), data.size(), 0, false)); + } + visitor->ProcessBodyData(data.data(), data.size()); +} + +// -- +// FlipHttpSMProxyTest + +TEST_F(FlipHttpSMProxyTest, ProcessHeaders) { + BalsaVisitorInterface* visitor = interface_.get(); + { + InSequence s; + EXPECT_CALL(*mock_another_interface_, SendSynReply(0, _)); + } + BalsaHeaders headers; + visitor->ProcessHeaders(headers); +} + +TEST_F(FlipHttpSMProxyTest, MessageDone) { + BalsaVisitorInterface* visitor = interface_.get(); + { + InSequence s; + EXPECT_CALL(*mock_another_interface_, SendEOF(0)); + } + visitor->MessageDone(); +} + +TEST_F(FlipHttpSMProxyTest, Cleanup) { + EXPECT_CALL(*connection_, Cleanup()).Times(0); + interface_->Cleanup(); +} + +TEST_F(FlipHttpSMProxyTest, SendEOF) { + { + InSequence s; + EXPECT_CALL(*mock_another_interface_, ResetForNewInterface(_)); + } + interface_->SendEOF(32); + ASSERT_EQ(1u, connection_->output_list()->size()); + DataFrame* df = connection_->output_list()->front(); + ASSERT_EQ("0\r\n\r\n", StringPiece(df->data, df->size)); +} + +// -- +// FlipHttpSMHttpTest + +TEST_F(FlipHttpSMHttpTest, ProcessHeaders) { + BalsaVisitorInterface* visitor = interface_.get(); + { + BalsaHeaders headers; + std::string filename = "GET_/path/file"; + memory_cache_->InsertFile(&headers, filename, ""); + } + + BalsaHeaders headers; + headers.AppendHeader("Host", "example.com"); + headers.SetRequestFirstlineFromStringPieces("GET", + "/path/file", + "HTTP/1.0"); + uint32 stream_id = 133; + interface_->SetStreamID(stream_id); + ASSERT_FALSE(HasStream(stream_id)); + visitor->ProcessHeaders(headers); + ASSERT_TRUE(HasStream(stream_id)); +} + +TEST_F(FlipHttpSMHttpTest, MessageDone) { + BalsaVisitorInterface* visitor = interface_.get(); + { + InSequence s; + EXPECT_CALL(*mock_another_interface_, SendEOF(0)).Times(0); + } + visitor->MessageDone(); +} + +TEST_F(FlipHttpSMHttpTest, Cleanup) { + EXPECT_CALL(*connection_, Cleanup()).Times(0); + interface_->Cleanup(); +} + +TEST_F(FlipHttpSMHttpTest, SendEOF) { + { + InSequence s; + EXPECT_CALL(*mock_another_interface_, ResetForNewInterface(_)).Times(0); + } + interface_->SendEOF(32); + ASSERT_EQ(1u, connection_->output_list()->size()); + DataFrame* df = connection_->output_list()->front(); + ASSERT_EQ("0\r\n\r\n", StringPiece(df->data, df->size)); +} + +// -- +// FlipHttpSMSpdyTest + +TEST_F(FlipHttpSMSpdyTest, ProcessHeaders) { + BalsaVisitorInterface* visitor = interface_.get(); + { + InSequence s; + EXPECT_CALL(*mock_another_interface_, SendSynReply(0, _)); + } + BalsaHeaders headers; + visitor->ProcessHeaders(headers); +} + +TEST_F(FlipHttpSMSpdyTest, MessageDone) { + BalsaVisitorInterface* visitor = interface_.get(); + { + InSequence s; + EXPECT_CALL(*mock_another_interface_, SendEOF(0)).Times(0); + } + visitor->MessageDone(); +} + +TEST_F(FlipHttpSMSpdyTest, Cleanup) { + EXPECT_CALL(*connection_, Cleanup()).Times(0); + interface_->Cleanup(); +} + +TEST_F(FlipHttpSMSpdyTest, SendEOF) { + { + InSequence s; + EXPECT_CALL(*mock_another_interface_, ResetForNewInterface(_)).Times(0); + } + interface_->SendEOF(32); + ASSERT_EQ(1u, connection_->output_list()->size()); + DataFrame* df = connection_->output_list()->front(); + ASSERT_EQ("0\r\n\r\n", StringPiece(df->data, df->size)); +} + +} // namespace + +} // namespace net diff --git a/net/tools/flip_server/mem_cache.cc b/net/tools/flip_server/mem_cache.cc index 9249208..d1e0e58 100644 --- a/net/tools/flip_server/mem_cache.cc +++ b/net/tools/flip_server/mem_cache.cc @@ -202,17 +202,9 @@ void MemoryCache::ReadAndStoreFileContents(const char* filename) { if (slash_pos == std::string::npos) { slash_pos = filename_stripped.size(); } - FileData* data = - new FileData(&visitor.headers, - filename_stripped.substr(0, slash_pos), - visitor.body); - Files::iterator it = files_.find(filename_stripped); - if (it != files_.end()) { - delete it->second; - it->second = data; - } else { - files_.insert(std::make_pair(filename_stripped, data)); - } + InsertFile(&visitor.headers, + filename_stripped.substr(0, slash_pos), + visitor.body); } FileData* MemoryCache::GetFileData(const std::string& filename) { @@ -239,6 +231,22 @@ bool MemoryCache::AssignFileData(const std::string& filename, return true; } +void MemoryCache::InsertFile(const BalsaHeaders* headers, + const std::string& filename, + const std::string& body) { + InsertFile(new FileData(headers, filename, body)); +} + +void MemoryCache::InsertFile(FileData* file_data) { + Files::iterator it = files_.find(file_data->filename()); + if (it != files_.end()) { + delete it->second; + it->second = file_data; + } else { + files_.insert(std::make_pair(file_data->filename(), file_data)); + } +} + void MemoryCache::ClearFiles() { for (Files::const_iterator i = files_.begin(); i != files_.end(); ++i) { delete i->second; diff --git a/net/tools/flip_server/mem_cache.h b/net/tools/flip_server/mem_cache.h index 806ae53..300c84a 100644 --- a/net/tools/flip_server/mem_cache.h +++ b/net/tools/flip_server/mem_cache.h @@ -135,7 +135,13 @@ class MemoryCache { bool AssignFileData(const std::string& filename, MemCacheIter* mci); + // For unittests + void InsertFile(const BalsaHeaders* headers, + const std::string& filename, + const std::string& body); + private: + void InsertFile(FileData* file_data); void ClearFiles(); Files files_; diff --git a/net/tools/flip_server/output_ordering.cc b/net/tools/flip_server/output_ordering.cc index 22fd08ac..6a42869 100644 --- a/net/tools/flip_server/output_ordering.cc +++ b/net/tools/flip_server/output_ordering.cc @@ -4,6 +4,8 @@ #include "net/tools/flip_server/output_ordering.h" +#include <utility> + #include "net/tools/flip_server/flip_config.h" #include "net/tools/flip_server/sm_connection.h" @@ -25,7 +27,9 @@ OutputOrdering::OutputOrdering(SMConnectionInterface* connection) epoll_server_ = connection->epoll_server(); } -OutputOrdering::~OutputOrdering() {} +OutputOrdering::~OutputOrdering() { + Reset(); +} void OutputOrdering::Reset() { while (!stream_ids_.empty()) { @@ -40,8 +44,8 @@ void OutputOrdering::Reset() { first_data_senders_.clear(); } -bool OutputOrdering::ExistsInPriorityMaps(uint32 stream_id) { - StreamIdToPriorityMap::iterator sitpmi = stream_ids_.find(stream_id); +bool OutputOrdering::ExistsInPriorityMaps(uint32 stream_id) const { + StreamIdToPriorityMap::const_iterator sitpmi = stream_ids_.find(stream_id); return sitpmi != stream_ids_.end(); } @@ -78,6 +82,7 @@ void OutputOrdering::BeginOutputtingAlarm::OnRegistration( void OutputOrdering::BeginOutputtingAlarm::OnUnregistration() { pmp_->alarm_enabled = false; + delete this; } void OutputOrdering::BeginOutputtingAlarm::OnShutdown(EpollServer* eps) { diff --git a/net/tools/flip_server/output_ordering.h b/net/tools/flip_server/output_ordering.h index 0558e3e4..922d03f 100644 --- a/net/tools/flip_server/output_ordering.h +++ b/net/tools/flip_server/output_ordering.h @@ -45,7 +45,7 @@ class OutputOrdering { explicit OutputOrdering(SMConnectionInterface* connection); ~OutputOrdering(); void Reset(); - bool ExistsInPriorityMaps(uint32 stream_id); + bool ExistsInPriorityMaps(uint32 stream_id) const; struct BeginOutputtingAlarm : public EpollAlarmCallbackInterface { public: diff --git a/net/tools/flip_server/sm_connection.cc b/net/tools/flip_server/sm_connection.cc index 71e81a0..375bc2e 100644 --- a/net/tools/flip_server/sm_connection.cc +++ b/net/tools/flip_server/sm_connection.cc @@ -9,6 +9,7 @@ #include <sys/socket.h> #include <unistd.h> +#include <algorithm> #include <list> #include <string> @@ -357,7 +358,6 @@ bool SMConnection::SetupProtocolInterfaces() { if (!sm_http_interface_) sm_http_interface_ = new HttpSM(this, NULL, - epoll_server_, memory_cache_, acceptor_); sm_interface_ = sm_http_interface_; diff --git a/net/tools/flip_server/sm_connection.h b/net/tools/flip_server/sm_connection.h index e7f1e99..3e21772 100644 --- a/net/tools/flip_server/sm_connection.h +++ b/net/tools/flip_server/sm_connection.h @@ -65,14 +65,14 @@ class SMConnection : public SMConnectionInterface, bool initialized() const { return initialized_; } std::string client_ip() const { return client_ip_; } - void InitSMConnection(SMConnectionPoolInterface* connection_pool, - SMInterface* sm_interface, - EpollServer* epoll_server, - int fd, - std::string server_ip, - std::string server_port, - std::string remote_ip, - bool use_ssl); + virtual void InitSMConnection(SMConnectionPoolInterface* connection_pool, + SMInterface* sm_interface, + EpollServer* epoll_server, + int fd, + std::string server_ip, + std::string server_port, + std::string remote_ip, + bool use_ssl); void CorkSocket(); void UncorkSocket(); @@ -119,12 +119,12 @@ class SMConnection : public SMConnectionInterface, return os; } - private: SMConnection(EpollServer* epoll_server, SSLState* ssl_state, MemoryCache* memory_cache, FlipAcceptor* acceptor, std::string log_prefix); + private: int fd_; int events_; diff --git a/net/tools/flip_server/spdy_interface.cc b/net/tools/flip_server/spdy_interface.cc index b4e36be..359eab8 100644 --- a/net/tools/flip_server/spdy_interface.cc +++ b/net/tools/flip_server/spdy_interface.cc @@ -22,7 +22,7 @@ std::string SpdySM::forward_ip_header_; class SpdyFrameDataFrame : public DataFrame { public: - SpdyFrameDataFrame(SpdyFrame* spdy_frame) + explicit SpdyFrameDataFrame(SpdyFrame* spdy_frame) : frame(spdy_frame) { data = spdy_frame->data(); size = spdy_frame->size(); @@ -86,14 +86,14 @@ SMInterface* SpdySM::NewConnectionInterface() { VLOG(2) << ACCEPTOR_CLIENT_IDENT << "SpdySM: Creating new HTTP interface"; SMInterface *sm_http_interface = new HttpSM(server_connection, this, - epoll_server_, memory_cache_, acceptor_); return sm_http_interface; } SMInterface* SpdySM::FindOrMakeNewSMConnectionInterface( - std::string server_ip, std::string server_port) { + const std::string& server_ip, + const std::string& server_port) { SMInterface *sm_http_interface; int32 server_idx; if (unused_server_interface_list.empty()) { @@ -154,7 +154,7 @@ int SpdySM::SpdyHandleNewStream( // UrlUtilities::GetUrlPath will fail and always return a / breaking // the request. GetUrlPath assumes the absolute URL is being passed in. std::string uri; - if (url->second.compare(0,4,"http") == 0) + if (url->second.compare(0, 4, "http") == 0) uri = UrlUtilities::GetUrlPath(url->second); else uri = std::string(url->second); @@ -323,6 +323,9 @@ void SpdySM::NewStream(uint32 stream_id, MemCacheIter mci; mci.stream_id = stream_id; mci.priority = priority; + // TODO(yhirano): The program will crash when + // acceptor_->flip_handler_type_ != FLIP_HANDLER_SPDY_SERVER. + // It should be fixed or an assertion should be placed. if (acceptor_->flip_handler_type_ == FLIP_HANDLER_SPDY_SERVER) { if (!memory_cache_->AssignFileData(filename, &mci)) { // error creating new stream. @@ -348,10 +351,6 @@ void SpdySM::SendErrorNotFound(uint32 stream_id) { SendErrorNotFoundImpl(stream_id); } -void SpdySM::SendOKResponse(uint32 stream_id, std::string* output) { - SendOKResponseImpl(stream_id, output); -} - size_t SpdySM::SendSynStream(uint32 stream_id, const BalsaHeaders& headers) { return SendSynStreamImpl(stream_id, headers); } @@ -381,15 +380,6 @@ void SpdySM::SendErrorNotFoundImpl(uint32 stream_id) { client_output_ordering_.RemoveStreamId(stream_id); } -void SpdySM::SendOKResponseImpl(uint32 stream_id, std::string* output) { - BalsaHeaders my_headers; - my_headers.SetFirstlineFromStringPieces("HTTP/1.1", "200", "OK"); - SendSynReplyImpl(stream_id, my_headers); - SendDataFrame( - stream_id, output->c_str(), output->size(), DATA_FLAG_FIN, false); - client_output_ordering_.RemoveStreamId(stream_id); -} - void SpdySM::KillStream(uint32 stream_id) { client_output_ordering_.RemoveStreamId(stream_id); } @@ -426,15 +416,13 @@ size_t SpdySM::SendSynStreamImpl(uint32 stream_id, const BalsaHeaders& headers) { SpdyHeaderBlock block; block["method"] = headers.request_method().as_string(); - if (!headers.HasHeader("status")) - block["status"] = headers.response_code().as_string(); if (!headers.HasHeader("version")) - block["version"] =headers.response_version().as_string(); + block["version"] =headers.request_version().as_string(); if (headers.HasHeader("X-Original-Url")) { std::string original_url = headers.GetHeader("X-Original-Url").as_string(); - block["path"] = UrlUtilities::GetUrlPath(original_url); + block["url"] = UrlUtilities::GetUrlPath(original_url); } else { - block["path"] = headers.request_uri().as_string(); + block["url"] = headers.request_uri().as_string(); } CopyHeaders(block, headers); diff --git a/net/tools/flip_server/spdy_interface.h b/net/tools/flip_server/spdy_interface.h index a83dab7..3ce1f3b 100644 --- a/net/tools/flip_server/spdy_interface.h +++ b/net/tools/flip_server/spdy_interface.h @@ -48,8 +48,10 @@ class SpdySM : public BufferedSpdyFramerVisitorInterface, private: virtual void set_is_request() OVERRIDE {} SMInterface* NewConnectionInterface(); - SMInterface* FindOrMakeNewSMConnectionInterface(std::string server_ip, - std::string server_port); + // virtual for tests + virtual SMInterface* FindOrMakeNewSMConnectionInterface( + const std::string& server_ip, + const std::string& server_port); int SpdyHandleNewStream(SpdyStreamId stream_id, SpdyPriority priority, const SpdyHeaderBlock& headers, @@ -143,26 +145,26 @@ class SpdySM : public BufferedSpdyFramerVisitorInterface, void AddToOutputOrder(const MemCacheIter& mci); virtual void SendEOF(uint32 stream_id) OVERRIDE; virtual void SendErrorNotFound(uint32 stream_id) OVERRIDE; - void SendOKResponse(uint32 stream_id, std::string* output); virtual size_t SendSynStream(uint32 stream_id, const BalsaHeaders& headers) OVERRIDE; virtual size_t SendSynReply(uint32 stream_id, const BalsaHeaders& headers) OVERRIDE; virtual void SendDataFrame(uint32 stream_id, const char* data, int64 len, uint32 flags, bool compress) OVERRIDE; - BufferedSpdyFramer* spdy_framer() { - return buffered_spdy_framer_; + BufferedSpdyFramer* spdy_framer() { return buffered_spdy_framer_; } + + const OutputOrdering& output_ordering() const { + return client_output_ordering_; } static std::string forward_ip_header() { return forward_ip_header_; } - static void set_forward_ip_header(std::string value) { + static void set_forward_ip_header(const std::string& value) { forward_ip_header_ = value; } private: void SendEOFImpl(uint32 stream_id); void SendErrorNotFoundImpl(uint32 stream_id); - void SendOKResponseImpl(uint32 stream_id, std::string* output); void KillStream(uint32 stream_id); void CopyHeaders(SpdyHeaderBlock& dest, const BalsaHeaders& headers); size_t SendSynStreamImpl(uint32 stream_id, const BalsaHeaders& headers); @@ -171,6 +173,7 @@ class SpdySM : public BufferedSpdyFramerVisitorInterface, SpdyDataFlags flags, bool compress); void EnqueueDataFrame(DataFrame* df); virtual void GetOutput() OVERRIDE; + private: BufferedSpdyFramer* buffered_spdy_framer_; bool valid_spdy_session_; // True if we have seen valid data on this session. diff --git a/net/tools/flip_server/spdy_interface_test.cc b/net/tools/flip_server/spdy_interface_test.cc new file mode 100644 index 0000000..7a1c6e9 --- /dev/null +++ b/net/tools/flip_server/spdy_interface_test.cc @@ -0,0 +1,634 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/tools/flip_server/spdy_interface.h" + +#include <list> + +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" +#include "net/spdy/buffered_spdy_framer.h" +#include "net/tools/flip_server/balsa_enums.h" +#include "net/tools/flip_server/balsa_headers.h" +#include "net/tools/flip_server/flip_config.h" +#include "net/tools/flip_server/flip_test_utils.h" +#include "net/tools/flip_server/mem_cache.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +using ::base::StringPiece; +using ::testing::_; +using ::testing::InSequence; +using ::testing::InvokeWithoutArgs; +using ::testing::Return; +using ::testing::SaveArg; + +namespace { + +struct StringSaver { + public: + StringSaver() : data(NULL), size(0) {} + void Save() { + string = std::string(data, size); + } + + const char* data; + size_t size; + std::string string; +}; + +class SpdyFramerVisitor : public BufferedSpdyFramerVisitorInterface { + public: + virtual ~SpdyFramerVisitor() {} + MOCK_METHOD1(OnError, void(SpdyFramer::SpdyError)); + MOCK_METHOD2(OnStreamError, void(SpdyStreamId, const std::string&)); + MOCK_METHOD7(OnSynStream, void(SpdyStreamId, + SpdyStreamId, + SpdyPriority, + uint8, + bool, + bool, + const SpdyHeaderBlock&)); + MOCK_METHOD3(OnSynStream, void(SpdyStreamId, bool, const SpdyHeaderBlock&)); + MOCK_METHOD3(OnSynReply, void(SpdyStreamId, bool, const SpdyHeaderBlock&)); + MOCK_METHOD3(OnHeaders, void(SpdyStreamId, bool, const SpdyHeaderBlock&)); + MOCK_METHOD4(OnStreamFrameData, void(SpdyStreamId, + const char*, + size_t, + bool)); + MOCK_METHOD1(OnStreamFrameData, void(bool)); + MOCK_METHOD1(OnSettings, void(bool)); + MOCK_METHOD3(OnSetting, void(SpdySettingsIds, uint8, uint32)); + MOCK_METHOD1(OnPing, void(uint32)); + MOCK_METHOD2(OnRstStream, void(SpdyStreamId, SpdyRstStreamStatus)); + MOCK_METHOD2(OnGoAway, void(SpdyStreamId, SpdyGoAwayStatus)); + MOCK_METHOD2(OnWindowUpdate, void(SpdyStreamId, uint32)); + MOCK_METHOD2(OnPushPromise, void(SpdyStreamId, SpdyStreamId)); +}; + +class FakeSMConnection : public SMConnection { + public: + FakeSMConnection(EpollServer* epoll_server, + SSLState* ssl_state, + MemoryCache* memory_cache, + FlipAcceptor* acceptor, + std::string log_prefix) + : SMConnection(epoll_server, + ssl_state, + memory_cache, + acceptor, + log_prefix) {} + + MOCK_METHOD0(Cleanup, void()); + MOCK_METHOD8(InitSMConnection, void(SMConnectionPoolInterface*, + SMInterface*, + EpollServer*, + int, + std::string, + std::string, + std::string, + bool)); +}; + +class SpdySMWithMockSMInterfaceFactory : public SpdySM { + public: + virtual ~SpdySMWithMockSMInterfaceFactory() {} + SpdySMWithMockSMInterfaceFactory(SMConnection* connection, + SMInterface* sm_http_interface, + EpollServer* epoll_server, + MemoryCache* memory_cache, + FlipAcceptor* acceptor) + : SpdySM(connection, + sm_http_interface, + epoll_server, + memory_cache, + acceptor) {} + + MOCK_METHOD2(FindOrMakeNewSMConnectionInterface, + SMInterface*(const std::string&, const std::string&)); +}; + +class FlipSpdySMTest : public ::testing::Test { + public: + FlipSpdySMTest(SpdyMajorVersion version = SPDY2, + FlipHandlerType type = FLIP_HANDLER_PROXY) + : spdy_version_(version) { + SSLState* ssl_state = NULL; + mock_another_interface_.reset(new MockSMInterface); + memory_cache_.reset(new MemoryCache); + acceptor_.reset(new FlipAcceptor(type, + "127.0.0.1", + "8941", + "ssl_cert_filename", + "ssl_key_filename", + "127.0.0.1", + "8942", + "127.0.0.1", + "8943", + 1, + 0, + true, + 1, + false, + true, + NULL)); + epoll_server_.reset(new EpollServer); + connection_.reset(new FakeSMConnection(epoll_server_.get(), + ssl_state, + memory_cache_.get(), + acceptor_.get(), + "log_prefix")); + + interface_.reset(new SpdySMWithMockSMInterfaceFactory( + connection_.get(), + mock_another_interface_.get(), + epoll_server_.get(), + memory_cache_.get(), + acceptor_.get())); + + spdy_framer_.reset(new BufferedSpdyFramer(spdy_version_, true)); + spdy_framer_visitor_.reset(new SpdyFramerVisitor); + spdy_framer_->set_visitor(spdy_framer_visitor_.get()); + } + + virtual void TearDown() OVERRIDE { + if (acceptor_->listen_fd_ >= 0) { + epoll_server_->UnregisterFD(acceptor_->listen_fd_); + close(acceptor_->listen_fd_); + acceptor_->listen_fd_ = -1; + } + OutputList& output_list = *connection_->output_list(); + for (OutputList::const_iterator i = output_list.begin(); + i != output_list.end(); + ++i) { + delete *i; + } + output_list.clear(); + } + + bool HasStream(uint32 stream_id) { + return interface_->output_ordering().ExistsInPriorityMaps(stream_id); + } + + protected: + SpdyMajorVersion spdy_version_; + scoped_ptr<MockSMInterface> mock_another_interface_; + scoped_ptr<MemoryCache> memory_cache_; + scoped_ptr<FlipAcceptor> acceptor_; + scoped_ptr<EpollServer> epoll_server_; + scoped_ptr<FakeSMConnection> connection_; + scoped_ptr<SpdySMWithMockSMInterfaceFactory> interface_; + scoped_ptr<BufferedSpdyFramer> spdy_framer_; + scoped_ptr<SpdyFramerVisitor> spdy_framer_visitor_; +}; + +class FlipSpdy2SMTest : public FlipSpdySMTest { + public: + FlipSpdy2SMTest(): FlipSpdySMTest(SPDY2) {} + virtual ~FlipSpdy2SMTest() {} +}; + +class FlipSpdy2SMTestNonProxy : public FlipSpdySMTest { + public: + FlipSpdy2SMTestNonProxy(): FlipSpdySMTest(SPDY2, FLIP_HANDLER_SPDY_SERVER) {} + virtual ~FlipSpdy2SMTestNonProxy() {} +}; + +TEST_F(FlipSpdySMTest, InitSMConnection) { + { + InSequence s; + EXPECT_CALL(*connection_, InitSMConnection(_, _, _, _, _, _, _, _)); + } + interface_->InitSMConnection(NULL, + NULL, + epoll_server_.get(), + -1, + "", + "", + "", + false); +} + +TEST_F(FlipSpdySMTest, OnSynStream) { + BufferedSpdyFramerVisitorInterface* visitor = interface_.get(); + scoped_ptr<MockSMInterface> mock_interface(new MockSMInterface); + uint32 stream_id = 92; + uint32 associated_id = 43; + std::string expected = "GET /path HTTP/1.0\r\n" + "method: GET\r\n" + "scheme: http\r\n" + "url: http://www.example.com/path\r\n" + "version: HTTP/1.0\r\n\r\n"; + SpdyHeaderBlock block; + block["method"] = "GET"; + block["url"] = "http://www.example.com/path"; + block["scheme"] = "http"; + block["version"] = "HTTP/1.0"; + StringSaver saver; + { + InSequence s; + EXPECT_CALL(*interface_, + FindOrMakeNewSMConnectionInterface(_, _)) + .WillOnce(Return(mock_interface.get())); + EXPECT_CALL(*mock_interface, SetStreamID(stream_id)); + EXPECT_CALL(*mock_interface, ProcessWriteInput(_, _)) + .WillOnce(DoAll(SaveArg<0>(&saver.data), + SaveArg<1>(&saver.size), + InvokeWithoutArgs(&saver, &StringSaver::Save), + Return(0))); + } + visitor->OnSynStream(stream_id, associated_id, 0, 0, false, false, block); + ASSERT_EQ(expected, saver.string); +} + +TEST_F(FlipSpdySMTest, OnStreamFrameData) { + BufferedSpdyFramerVisitorInterface* visitor = interface_.get(); + scoped_ptr<MockSMInterface> mock_interface(new MockSMInterface); + uint32 stream_id = 92; + uint32 associated_id = 43; + SpdyHeaderBlock block; + testing::MockFunction<void(int)> checkpoint; + + scoped_ptr<SpdyFrame> frame(spdy_framer_->CreatePingFrame(12)); + block["method"] = "GET"; + block["url"] = "http://www.example.com/path"; + block["scheme"] = "http"; + block["version"] = "HTTP/1.0"; + { + InSequence s; + EXPECT_CALL(*interface_, + FindOrMakeNewSMConnectionInterface(_, _)) + .WillOnce(Return(mock_interface.get())); + EXPECT_CALL(*mock_interface, SetStreamID(stream_id)); + EXPECT_CALL(*mock_interface, ProcessWriteInput(_, _)).Times(1); + EXPECT_CALL(checkpoint, Call(0)); + EXPECT_CALL(*mock_interface, + ProcessWriteInput(frame->data(), frame->size())).Times(1); + } + + visitor->OnSynStream(stream_id, associated_id, 0, 0, false, false, block); + checkpoint.Call(0); + visitor->OnStreamFrameData(stream_id, frame->data(), frame->size(), true); +} + +TEST_F(FlipSpdySMTest, OnRstStream) { + BufferedSpdyFramerVisitorInterface* visitor = interface_.get(); + uint32 stream_id = 82; + MemCacheIter mci; + mci.stream_id = stream_id; + + { + BalsaHeaders headers; + std::string filename = "foobar"; + memory_cache_->InsertFile(&headers, filename, ""); + mci.file_data = memory_cache_->GetFileData(filename); + } + + interface_->AddToOutputOrder(mci); + ASSERT_TRUE(HasStream(stream_id)); + visitor->OnRstStream(stream_id, RST_STREAM_INVALID); + ASSERT_FALSE(HasStream(stream_id)); +} + +TEST_F(FlipSpdySMTest, ProcessReadInput) { + ASSERT_EQ(SpdyFramer::SPDY_RESET, interface_->spdy_framer()->state()); + interface_->ProcessReadInput("", 1); + ASSERT_EQ(SpdyFramer::SPDY_READING_COMMON_HEADER, + interface_->spdy_framer()->state()); +} + +TEST_F(FlipSpdySMTest, ResetForNewConnection) { + uint32 stream_id = 13; + MemCacheIter mci; + mci.stream_id = stream_id; + // incomplete input + const char input[] = {'\0', '\0', '\0'}; + + { + BalsaHeaders headers; + std::string filename = "foobar"; + memory_cache_->InsertFile(&headers, filename, ""); + mci.file_data = memory_cache_->GetFileData(filename); + } + + interface_->AddToOutputOrder(mci); + ASSERT_TRUE(HasStream(stream_id)); + interface_->ProcessReadInput(input, sizeof(input)); + ASSERT_NE(SpdyFramer::SPDY_RESET, interface_->spdy_framer()->state()); + + interface_->ResetForNewConnection(); + ASSERT_FALSE(HasStream(stream_id)); + ASSERT_EQ(SpdyFramer::SPDY_RESET, interface_->spdy_framer()->state()); +} + +TEST_F(FlipSpdySMTest, PostAcceptHook) { + interface_->PostAcceptHook(); + + ASSERT_EQ(1u, connection_->output_list()->size()); + std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin(); + DataFrame* df = *i++; + + { + InSequence s; + EXPECT_CALL(*spdy_framer_visitor_, OnSettings(false)); + EXPECT_CALL(*spdy_framer_visitor_, OnSetting( + SETTINGS_MAX_CONCURRENT_STREAMS, 0u, 100u)); + } + spdy_framer_->ProcessInput(df->data, df->size); +} + +TEST_F(FlipSpdySMTest, NewStream) { + // TODO(yhirano): SpdySM::NewStream leads to crash when + // acceptor_->flip_handler_type_ != FLIP_HANDLER_SPDY_SERVER. + // It should be fixed though I don't know the solution now. +} + +TEST_F(FlipSpdySMTest, AddToOutputOrder) { + uint32 stream_id = 13; + MemCacheIter mci; + mci.stream_id = stream_id; + + { + BalsaHeaders headers; + std::string filename = "foobar"; + memory_cache_->InsertFile(&headers, filename, ""); + mci.file_data = memory_cache_->GetFileData(filename); + } + + interface_->AddToOutputOrder(mci); + ASSERT_TRUE(HasStream(stream_id)); +} + +TEST_F(FlipSpdySMTest, SendErrorNotFound) { + uint32 stream_id = 82; + SpdyHeaderBlock actual_header_block; + const char* actual_data; + size_t actual_size; + testing::MockFunction<void(int)> checkpoint; + + interface_->SendErrorNotFound(stream_id); + + ASSERT_EQ(2u, connection_->output_list()->size()); + + { + InSequence s; + EXPECT_CALL(*spdy_framer_visitor_, + OnSynReply(stream_id, false, _)) + .WillOnce(SaveArg<2>(&actual_header_block)); + EXPECT_CALL(checkpoint, Call(0)); + EXPECT_CALL(*spdy_framer_visitor_, + OnStreamFrameData(stream_id, _, _, false)).Times(1) + .WillOnce(DoAll(SaveArg<1>(&actual_data), + SaveArg<2>(&actual_size))); + EXPECT_CALL(*spdy_framer_visitor_, + OnStreamFrameData(stream_id, NULL, 0, true)).Times(1); + } + + std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin(); + DataFrame* df = *i++; + spdy_framer_->ProcessInput(df->data, df->size); + checkpoint.Call(0); + df = *i++; + spdy_framer_->ProcessInput(df->data, df->size); + + ASSERT_EQ(2, spdy_framer_->frames_received()); + ASSERT_EQ(2u, actual_header_block.size()); + ASSERT_EQ("404 Not Found", actual_header_block["status"]); + ASSERT_EQ("HTTP/1.1", actual_header_block["version"]); + ASSERT_EQ("wtf?", StringPiece(actual_data, actual_size)); +} + +TEST_F(FlipSpdySMTest, SendSynStream) { + uint32 stream_id = 82; + BalsaHeaders headers; + SpdyHeaderBlock actual_header_block; + headers.AppendHeader("key1", "value1"); + headers.SetRequestFirstlineFromStringPieces("GET", "/path", "HTTP/1.0"); + + interface_->SendSynStream(stream_id, headers); + + ASSERT_EQ(1u, connection_->output_list()->size()); + std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin(); + DataFrame* df = *i++; + + { + InSequence s; + EXPECT_CALL(*spdy_framer_visitor_, + OnSynStream(stream_id, 0, _, _, false, false, _)) + .WillOnce(SaveArg<6>(&actual_header_block)); + } + + spdy_framer_->ProcessInput(df->data, df->size); + ASSERT_EQ(1, spdy_framer_->frames_received()); + ASSERT_EQ(4u, actual_header_block.size()); + ASSERT_EQ("GET", actual_header_block["method"]); + ASSERT_EQ("HTTP/1.0", actual_header_block["version"]); + ASSERT_EQ("/path", actual_header_block["url"]); + ASSERT_EQ("value1", actual_header_block["key1"]); +} + +TEST_F(FlipSpdySMTest, SendSynReply) { + uint32 stream_id = 82; + BalsaHeaders headers; + SpdyHeaderBlock actual_header_block; + headers.AppendHeader("key1", "value1"); + headers.SetResponseFirstlineFromStringPieces("HTTP/1.1", "200", "OK"); + + interface_->SendSynReply(stream_id, headers); + + ASSERT_EQ(1u, connection_->output_list()->size()); + std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin(); + DataFrame* df = *i++; + + { + InSequence s; + EXPECT_CALL(*spdy_framer_visitor_, OnSynReply(stream_id, false, _)) + .WillOnce(SaveArg<2>(&actual_header_block)); + } + + spdy_framer_->ProcessInput(df->data, df->size); + ASSERT_EQ(1, spdy_framer_->frames_received()); + ASSERT_EQ(3u, actual_header_block.size()); + ASSERT_EQ("200 OK", actual_header_block["status"]); + ASSERT_EQ("HTTP/1.1", actual_header_block["version"]); + ASSERT_EQ("value1", actual_header_block["key1"]); +} + +TEST_F(FlipSpdySMTest, SendDataFrame) { + uint32 stream_id = 133; + SpdyDataFlags flags = DATA_FLAG_NONE; + const char* actual_data; + size_t actual_size; + + interface_->SendDataFrame(stream_id, "hello", 5, flags, true); + + ASSERT_EQ(1u, connection_->output_list()->size()); + std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin(); + DataFrame* df = *i++; + + { + InSequence s; + EXPECT_CALL(*spdy_framer_visitor_, + OnStreamFrameData(stream_id, _, _, false)) + .WillOnce(DoAll(SaveArg<1>(&actual_data), + SaveArg<2>(&actual_size))); + } + + spdy_framer_->ProcessInput(df->data, df->size); + ASSERT_EQ(1, spdy_framer_->frames_received()); + ASSERT_EQ("hello", StringPiece(actual_data, actual_size)); +} + +TEST_F(FlipSpdySMTest, SendLongDataFrame) { + uint32 stream_id = 133; + SpdyDataFlags flags = DATA_FLAG_NONE; + const char* actual_data; + size_t actual_size; + + std::string data = + std::string(kSpdySegmentSize, 'a') + + std::string(kSpdySegmentSize, 'b') + + "c"; + interface_->SendDataFrame(stream_id, data.data(), data.size(), flags, true); + + { + InSequence s; + EXPECT_CALL(*spdy_framer_visitor_, + OnStreamFrameData(stream_id, _, _, false)).Times(3) + .WillRepeatedly(DoAll(SaveArg<1>(&actual_data), + SaveArg<2>(&actual_size))); + } + + ASSERT_EQ(3u, connection_->output_list()->size()); + std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin(); + DataFrame* df = *i++; + spdy_framer_->ProcessInput(df->data, df->size); + ASSERT_EQ(std::string(kSpdySegmentSize, 'a'), + StringPiece(actual_data, actual_size)); + + df = *i++; + spdy_framer_->ProcessInput(df->data, df->size); + ASSERT_EQ(std::string(kSpdySegmentSize, 'b'), + StringPiece(actual_data, actual_size)); + + df = *i++; + spdy_framer_->ProcessInput(df->data, df->size); + ASSERT_EQ("c", StringPiece(actual_data, actual_size)); +} + +TEST_F(FlipSpdy2SMTest, SendEOF) { + uint32 stream_id = 82; + // SPDY2 data frame + char empty_data_frame[] = {'\0', '\0', '\0', '\x52', '\x1', '\0', '\0', '\0'}; + MemCacheIter mci; + mci.stream_id = stream_id; + + { + BalsaHeaders headers; + std::string filename = "foobar"; + memory_cache_->InsertFile(&headers, filename, ""); + mci.file_data = memory_cache_->GetFileData(filename); + } + + interface_->AddToOutputOrder(mci); + ASSERT_TRUE(HasStream(stream_id)); + interface_->SendEOF(stream_id); + ASSERT_FALSE(HasStream(stream_id)); + + ASSERT_EQ(1u, connection_->output_list()->size()); + std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin(); + DataFrame* df = *i++; + ASSERT_EQ(StringPiece(empty_data_frame, sizeof(empty_data_frame)), + StringPiece(df->data, df->size)); +} + +TEST_F(FlipSpdy2SMTest, SendEmptyDataFrame) { + uint32 stream_id = 133; + SpdyDataFlags flags = DATA_FLAG_NONE; + // SPDY2 data frame + char expected[] = {'\0', '\0', '\0', '\x85', '\0', '\0', '\0', '\0'}; + + interface_->SendDataFrame(stream_id, "hello", 0, flags, true); + + ASSERT_EQ(1u, connection_->output_list()->size()); + std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin(); + DataFrame* df = *i++; + + ASSERT_EQ(StringPiece(expected, sizeof(expected)), + StringPiece(df->data, df->size)); +} + +TEST_F(FlipSpdy2SMTestNonProxy, OnSynStream) { + BufferedSpdyFramerVisitorInterface* visitor = interface_.get(); + uint32 stream_id = 82; + SpdyHeaderBlock spdy_headers; + spdy_headers["url"] = "http://www.example.com/path"; + spdy_headers["method"] = "GET"; + spdy_headers["scheme"] = "http"; + + { + BalsaHeaders headers; + memory_cache_->InsertFile(&headers, "GET_/path", ""); + } + visitor->OnSynStream(stream_id, 0, 0, 0, true, true, spdy_headers); + ASSERT_TRUE(HasStream(stream_id)); +} + +TEST_F(FlipSpdy2SMTestNonProxy, NewStream) { + uint32 stream_id = 13; + std::string filename = "foobar"; + + { + BalsaHeaders headers; + memory_cache_->InsertFile(&headers, filename, ""); + } + + interface_->NewStream(stream_id, 0, filename); + ASSERT_TRUE(HasStream(stream_id)); +} + +TEST_F(FlipSpdy2SMTestNonProxy, NewStreamError) { + uint32 stream_id = 82; + SpdyHeaderBlock actual_header_block; + const char* actual_data; + size_t actual_size; + testing::MockFunction<void(int)> checkpoint; + + interface_->NewStream(stream_id, 0, "nonexistingfile"); + + ASSERT_EQ(2u, connection_->output_list()->size()); + + { + InSequence s; + EXPECT_CALL(*spdy_framer_visitor_, + OnSynReply(stream_id, false, _)) + .WillOnce(SaveArg<2>(&actual_header_block)); + EXPECT_CALL(checkpoint, Call(0)); + EXPECT_CALL(*spdy_framer_visitor_, + OnStreamFrameData(stream_id, _, _, false)).Times(1) + .WillOnce(DoAll(SaveArg<1>(&actual_data), + SaveArg<2>(&actual_size))); + EXPECT_CALL(*spdy_framer_visitor_, + OnStreamFrameData(stream_id, NULL, 0, true)).Times(1); + } + + std::list<DataFrame*>::const_iterator i = connection_->output_list()->begin(); + DataFrame* df = *i++; + spdy_framer_->ProcessInput(df->data, df->size); + checkpoint.Call(0); + df = *i++; + spdy_framer_->ProcessInput(df->data, df->size); + + ASSERT_EQ(2, spdy_framer_->frames_received()); + ASSERT_EQ(2u, actual_header_block.size()); + ASSERT_EQ("404 Not Found", actual_header_block["status"]); + ASSERT_EQ("HTTP/1.1", actual_header_block["version"]); + ASSERT_EQ("wtf?", StringPiece(actual_data, actual_size)); +} + +} // namespace + +} // namespace net |