summaryrefslogtreecommitdiffstats
path: root/net/tools/flip_server
diff options
context:
space:
mode:
authoryhirano@chromium.org <yhirano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-19 06:44:45 +0000
committeryhirano@chromium.org <yhirano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-19 06:44:45 +0000
commit139fc2f431e10b4cc52813607f00fb134ebf4cde (patch)
tree80744c04f048c87f2273ec8d33c42389899bec56 /net/tools/flip_server
parent1ca42a4e4ad10553e31cb4e9dce443b270b3ae64 (diff)
downloadchromium_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.cc5
-rw-r--r--net/tools/flip_server/create_listener.h3
-rw-r--r--net/tools/flip_server/epoll_server.cc8
-rw-r--r--net/tools/flip_server/flip_config.cc2
-rw-r--r--net/tools/flip_server/flip_test_utils.cc15
-rw-r--r--net/tools/flip_server/flip_test_utils.h53
-rw-r--r--net/tools/flip_server/http_interface.cc26
-rw-r--r--net/tools/flip_server/http_interface.h15
-rw-r--r--net/tools/flip_server/http_interface_test.cc487
-rw-r--r--net/tools/flip_server/mem_cache.cc30
-rw-r--r--net/tools/flip_server/mem_cache.h6
-rw-r--r--net/tools/flip_server/output_ordering.cc11
-rw-r--r--net/tools/flip_server/output_ordering.h2
-rw-r--r--net/tools/flip_server/sm_connection.cc2
-rw-r--r--net/tools/flip_server/sm_connection.h18
-rw-r--r--net/tools/flip_server/spdy_interface.cc32
-rw-r--r--net/tools/flip_server/spdy_interface.h17
-rw-r--r--net/tools/flip_server/spdy_interface_test.cc634
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