summaryrefslogtreecommitdiffstats
path: root/net/spdy/spdy_stream_unittest.cc
diff options
context:
space:
mode:
authorukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-20 04:19:49 +0000
committerukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-20 04:19:49 +0000
commit4f38642501dba0b9d93f24392daf26db428b8ea8 (patch)
tree4952ee7910b3d0fbde0cd469b74d749034e03e7c /net/spdy/spdy_stream_unittest.cc
parenta38afbfe24dea4fcbece532908af76c52cf3d85a (diff)
downloadchromium_src-4f38642501dba0b9d93f24392daf26db428b8ea8.zip
chromium_src-4f38642501dba0b9d93f24392daf26db428b8ea8.tar.gz
chromium_src-4f38642501dba0b9d93f24392daf26db428b8ea8.tar.bz2
Refactor SpdyStream state for WebSocket support
In HTTP, it doesn't use STATE_READ_BODY/STATE_READ_BODY_COMPLETE states. Reading body message is handled in OnDataReceived() and DoLoop() is not involved in this state. In WebSocket, it will send frame after handshake has been finished, and need to get how many data has been written. STATE_OPEN handles this. BUG=none TEST=none Review URL: http://codereview.chromium.org/2962015 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@53000 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/spdy/spdy_stream_unittest.cc')
-rw-r--r--net/spdy/spdy_stream_unittest.cc284
1 files changed, 284 insertions, 0 deletions
diff --git a/net/spdy/spdy_stream_unittest.cc b/net/spdy/spdy_stream_unittest.cc
new file mode 100644
index 0000000..4105aa6
--- /dev/null
+++ b/net/spdy/spdy_stream_unittest.cc
@@ -0,0 +1,284 @@
+// Copyright (c) 2010 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/spdy/spdy_stream.h"
+#include "base/ref_counted.h"
+#include "base/time.h"
+#include "net/base/mock_host_resolver.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/base/ssl_config_service.h"
+#include "net/base/ssl_config_service_defaults.h"
+#include "net/base/test_completion_callback.h"
+#include "net/http/http_auth_handler_factory.h"
+#include "net/http/http_network_session.h"
+#include "net/http/http_request_info.h"
+#include "net/http/http_response_info.h"
+#include "net/proxy/proxy_service.h"
+#include "net/socket/socket_test_util.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/spdy/spdy_session.h"
+#include "net/spdy/spdy_session_pool.h"
+#include "net/spdy/spdy_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+// TODO(ukai): factor out common part with spdy_http_stream_unittest.cc
+class SpdySessionPoolPeer {
+ public:
+ explicit SpdySessionPoolPeer(const scoped_refptr<SpdySessionPool>& pool)
+ : pool_(pool) {}
+
+ void RemoveSpdySession(const scoped_refptr<SpdySession>& session) {
+ pool_->Remove(session);
+ }
+
+ private:
+ const scoped_refptr<SpdySessionPool> pool_;
+
+ DISALLOW_COPY_AND_ASSIGN(SpdySessionPoolPeer);
+};
+
+namespace {
+
+// Create a proxy service which fails on all requests (falls back to direct).
+ProxyService* CreateNullProxyService() {
+ return ProxyService::CreateNull();
+}
+
+// Helper to manage the lifetimes of the dependencies for a
+// SpdyNetworkTransaction.
+class SessionDependencies {
+ public:
+ // Default set of dependencies -- "null" proxy service.
+ SessionDependencies()
+ : host_resolver(new MockHostResolver),
+ proxy_service(CreateNullProxyService()),
+ ssl_config_service(new SSLConfigServiceDefaults),
+ http_auth_handler_factory(HttpAuthHandlerFactory::CreateDefault()),
+ spdy_session_pool(new SpdySessionPool()) {}
+
+ // Custom proxy service dependency.
+ explicit SessionDependencies(ProxyService* proxy_service)
+ : host_resolver(new MockHostResolver),
+ proxy_service(proxy_service),
+ ssl_config_service(new SSLConfigServiceDefaults),
+ http_auth_handler_factory(HttpAuthHandlerFactory::CreateDefault()),
+ spdy_session_pool(new SpdySessionPool()) {}
+
+ scoped_refptr<MockHostResolverBase> host_resolver;
+ scoped_refptr<ProxyService> proxy_service;
+ scoped_refptr<SSLConfigService> ssl_config_service;
+ MockClientSocketFactory socket_factory;
+ scoped_ptr<HttpAuthHandlerFactory> http_auth_handler_factory;
+ scoped_refptr<SpdySessionPool> spdy_session_pool;
+};
+
+HttpNetworkSession* CreateSession(SessionDependencies* session_deps) {
+ return new HttpNetworkSession(session_deps->host_resolver,
+ session_deps->proxy_service,
+ &session_deps->socket_factory,
+ session_deps->ssl_config_service,
+ session_deps->spdy_session_pool,
+ session_deps->http_auth_handler_factory.get(),
+ NULL,
+ NULL);
+}
+
+class TestSpdyStreamDelegate : public SpdyStream::Delegate {
+ public:
+ TestSpdyStreamDelegate(SpdyStream* stream,
+ IOBufferWithSize* buf,
+ CompletionCallback* callback)
+ : stream_(stream),
+ buf_(buf),
+ callback_(callback),
+ send_headers_completed_(false),
+ response_(new spdy::SpdyHeaderBlock),
+ data_sent_(0),
+ closed_(false) {}
+ virtual ~TestSpdyStreamDelegate() {}
+
+ virtual bool OnSendHeadersComplete(int status) {
+ send_headers_completed_ = true;
+ return true;
+ }
+ virtual int OnSendBody() {
+ ADD_FAILURE() << "OnSendBody should not be called";
+ return ERR_UNEXPECTED;
+ }
+ virtual bool OnSendBodyComplete(int status) {
+ ADD_FAILURE() << "OnSendBodyComplete should not be called";
+ return true;
+ }
+ virtual int OnResponseReceived(const spdy::SpdyHeaderBlock& response,
+ base::Time response_time,
+ int status) {
+ EXPECT_TRUE(send_headers_completed_);
+ *response_ = response;
+ if (buf_) {
+ EXPECT_EQ(ERR_IO_PENDING,
+ stream_->WriteStreamData(buf_.get(), buf_->size(),
+ spdy::DATA_FLAG_NONE));
+ }
+ return status;
+ }
+ virtual void OnDataReceived(const char* buffer, int bytes) {
+ received_data_ += std::string(buffer, bytes);
+ }
+ virtual void OnDataSent(int length) {
+ data_sent_ += length;
+ }
+ virtual void OnClose(int status) {
+ closed_ = true;
+ CompletionCallback* callback = callback_;
+ callback_ = NULL;
+ callback->Run(OK);
+ }
+
+ bool send_headers_completed() const { return send_headers_completed_; }
+ const linked_ptr<spdy::SpdyHeaderBlock>& response() const {
+ return response_;
+ }
+ const std::string& received_data() const { return received_data_; }
+ int data_sent() const { return data_sent_; }
+ bool closed() const { return closed_; }
+
+ private:
+ SpdyStream* stream_;
+ scoped_refptr<IOBufferWithSize> buf_;
+ CompletionCallback* callback_;
+ bool send_headers_completed_;
+ linked_ptr<spdy::SpdyHeaderBlock> response_;
+ std::string received_data_;
+ int data_sent_;
+ bool closed_;
+};
+
+spdy::SpdyFrame* ConstructSpdyBodyFrame(const char* data, int length) {
+ spdy::SpdyFramer framer;
+ return framer.CreateDataFrame(1, data, length, spdy::DATA_FLAG_NONE);
+}
+
+} // anonymous namespace
+
+class SpdyStreamTest : public testing::Test {
+ protected:
+ SpdyStreamTest() {
+ }
+
+ scoped_refptr<SpdySession> CreateSpdySession() {
+ spdy::SpdyFramer::set_enable_compression_default(false);
+ HostPortPair host_port_pair("www.google.com", 80);
+ scoped_refptr<SpdySession> session(
+ session_->spdy_session_pool()->Get(
+ host_port_pair, session_, BoundNetLog()));
+ return session;
+ }
+
+ virtual void TearDown() {
+ MessageLoop::current()->RunAllPending();
+ }
+
+ scoped_refptr<HttpNetworkSession> session_;
+};
+
+TEST_F(SpdyStreamTest, SendDataAfterOpen) {
+ SessionDependencies session_deps;
+
+ session_ = CreateSession(&session_deps);
+ SpdySessionPoolPeer pool_peer_(session_->spdy_session_pool());
+
+ const SpdyHeaderInfo kSynStartHeader = {
+ spdy::SYN_STREAM,
+ 1,
+ 0,
+ SPDY_PRIORITY_LOWEST,
+ spdy::CONTROL_FLAG_NONE,
+ false,
+ spdy::INVALID,
+ NULL,
+ 0,
+ spdy::DATA_FLAG_NONE
+ };
+ static const char* const kGetHeaders[] = {
+ "method",
+ "GET",
+ "url",
+ "http://www.google.com/",
+ "version",
+ "HTTP/1.1",
+ };
+ scoped_ptr<spdy::SpdyFrame> req(
+ ConstructSpdyPacket(
+ kSynStartHeader, NULL, 0, kGetHeaders, arraysize(kGetHeaders) / 2));
+ scoped_ptr<spdy::SpdyFrame> msg(
+ ConstructSpdyBodyFrame("\0hello!\xff", 8));
+ MockWrite writes[] = {
+ CreateMockWrite(*req),
+ CreateMockWrite(*msg),
+ };
+ writes[0].sequence_number = 0;
+ writes[1].sequence_number = 2;
+
+ scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+ scoped_ptr<spdy::SpdyFrame> echo(
+ ConstructSpdyBodyFrame("\0hello!\xff", 8));
+ MockRead reads[] = {
+ CreateMockRead(*resp),
+ CreateMockRead(*echo),
+ MockRead(true, 0, 0), // EOF
+ };
+ reads[0].sequence_number = 1;
+ reads[1].sequence_number = 3;
+ reads[2].sequence_number = 4;
+
+ scoped_refptr<OrderedSocketData> data(
+ new OrderedSocketData(reads, arraysize(reads),
+ writes, arraysize(writes)));
+
+ session_deps.socket_factory.AddSocketDataProvider(data.get());
+ SpdySession::SetSSLMode(false);
+
+ scoped_refptr<SpdySession> session(CreateSpdySession());
+ GURL url("http://www.google.com/");
+
+ HostPortPair host_port_pair("www.google.com", 80);
+ scoped_refptr<TCPSocketParams> tcp_params =
+ new TCPSocketParams(host_port_pair, LOWEST, GURL(), false);
+ EXPECT_EQ(OK, session->Connect("spdy.www.google.com", tcp_params,
+ LOWEST));
+
+ scoped_refptr<SpdyStream> stream;
+ ASSERT_EQ(
+ OK,
+ session->CreateStream(url, LOWEST, &stream, BoundNetLog(), NULL, NULL));
+ scoped_refptr<IOBufferWithSize> buf(new IOBufferWithSize(8));
+ memcpy(buf->data(), "\0hello!\xff", 8);
+ TestCompletionCallback callback;
+
+ scoped_ptr<TestSpdyStreamDelegate> delegate(
+ new TestSpdyStreamDelegate(stream.get(), buf.get(), &callback));
+ stream->SetDelegate(delegate.get());
+
+ linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock);
+ (*headers)["method"] = "GET";
+ (*headers)["url"] = "http://www.google.com/";
+ (*headers)["version"] = "HTTP/1.1";
+ stream->set_spdy_headers(headers);
+
+ EXPECT_EQ(ERR_IO_PENDING, stream->DoSendRequest(true));
+
+ EXPECT_EQ(OK, callback.WaitForResult());
+
+ EXPECT_TRUE(delegate->send_headers_completed());
+ EXPECT_EQ("200", (*delegate->response())["status"]);
+ EXPECT_EQ("HTTP/1.1", (*delegate->response())["version"]);
+ EXPECT_EQ(std::string("\0hello!\xff", 8), delegate->received_data());
+ EXPECT_EQ(8, delegate->data_sent());
+ EXPECT_TRUE(delegate->closed());
+}
+
+} // namespace net