| // 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 <memory> |
| |
| #include "base/stl_util.h" |
| #include "base/strings/string_piece.h" |
| #include "net/tools/balsa/balsa_enums.h" |
| #include "net/tools/balsa/balsa_frame.h" |
| #include "net/tools/balsa/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())); |
| } |
| |
| 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_t stream_id) { |
| return interface_->output_ordering().ExistsInPriorityMaps(stream_id); |
| } |
| |
| protected: |
| std::unique_ptr<MockSMInterface> mock_another_interface_; |
| std::unique_ptr<MemoryCache> memory_cache_; |
| std::unique_ptr<FlipAcceptor> acceptor_; |
| std::unique_ptr<EpollServer> epoll_server_; |
| std::unique_ptr<MockSMConnection> connection_; |
| std::unique_ptr<HttpSM> interface_; |
| }; |
| |
| class FlipHttpSMProxyTest : public FlipHttpSMTest { |
| public: |
| FlipHttpSMProxyTest() : FlipHttpSMTest(FLIP_HANDLER_PROXY) {} |
| ~FlipHttpSMProxyTest() override {} |
| }; |
| |
| class FlipHttpSMHttpTest : public FlipHttpSMTest { |
| public: |
| FlipHttpSMHttpTest() : FlipHttpSMTest(FLIP_HANDLER_HTTP_SERVER) {} |
| ~FlipHttpSMHttpTest() override {} |
| }; |
| |
| class FlipHttpSMSpdyTest : public FlipHttpSMTest { |
| public: |
| FlipHttpSMSpdyTest() : FlipHttpSMTest(FLIP_HANDLER_SPDY_SERVER) {} |
| ~FlipHttpSMSpdyTest() override {} |
| }; |
| |
| TEST_F(FlipHttpSMTest, Construct) { |
| ASSERT_FALSE(interface_->spdy_framer()->is_request()); |
| } |
| |
| TEST_F(FlipHttpSMTest, AddToOutputOrder) { |
| uint32_t 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) { |
| std::unique_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; // NOLINT |
| { |
| 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; // NOLINT |
| { |
| 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; // NOLINT |
| { |
| 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_t 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_t 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_t 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_t 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 |