summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorwillchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-10 19:29:42 +0000
committerwillchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-10 19:29:42 +0000
commit651b77c761749949fc7f148e3c20fe203a61bf7d (patch)
treece50cb8834b1a94120ba9c42a8dceb751aeeb4d7 /net
parent576a52b94483a5b1702f0ac0ad41810bb747a98a (diff)
downloadchromium_src-651b77c761749949fc7f148e3c20fe203a61bf7d.zip
chromium_src-651b77c761749949fc7f148e3c20fe203a61bf7d.tar.gz
chromium_src-651b77c761749949fc7f148e3c20fe203a61bf7d.tar.bz2
SPDY: Basic peer GoAway frame support.
This only handles peer GoAway frames. Does not implement support for client GoAway frames. TODO: Cancel any streams that are past the GoAway frame's last_accepted_stream_id. Review URL: http://codereview.chromium.org/820002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@41189 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/spdy/spdy_session.cc62
-rw-r--r--net/spdy/spdy_session.h11
-rw-r--r--net/spdy/spdy_session_unittest.cc87
3 files changed, 130 insertions, 30 deletions
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index b81661c..33fa576 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -280,6 +280,7 @@ net::Error SpdySession::Connect(const std::string& group_name,
// work until after the connect completes asynchronously later.
if (rv == net::ERR_IO_PENDING)
return net::OK;
+ OnTCPConnect(rv);
return static_cast<net::Error>(rv);
}
@@ -844,9 +845,9 @@ void SpdySession::OnStreamFrameData(spdy::SpdyStreamId stream_id,
DeactivateStream(stream_id);
}
-void SpdySession::OnSyn(const spdy::SpdySynStreamControlFrame* frame,
- const spdy::SpdyHeaderBlock* headers) {
- spdy::SpdyStreamId stream_id = frame->stream_id();
+void SpdySession::OnSyn(const spdy::SpdySynStreamControlFrame& frame,
+ const spdy::SpdyHeaderBlock& headers) {
+ spdy::SpdyStreamId stream_id = frame.stream_id();
LOG(INFO) << "Spdy SynStream for stream " << stream_id;
@@ -866,12 +867,12 @@ void SpdySession::OnSyn(const spdy::SpdySynStreamControlFrame* frame,
LOG(INFO) << "SpdySession: Syn received for stream: " << stream_id;
LOG(INFO) << "SPDY SYN RESPONSE HEADERS -----------------------";
- DumpSpdyHeaders(*headers);
+ DumpSpdyHeaders(headers);
// TODO(mbelshe): DCHECK that this is a GET method?
- const std::string& path = ContainsKey(*headers, "path") ?
- headers->find("path")->second : "";
+ const std::string& path = ContainsKey(headers, "path") ?
+ headers.find("path")->second : "";
// Verify that the response had a URL for us.
DCHECK(!path.empty());
@@ -920,7 +921,7 @@ void SpdySession::OnSyn(const spdy::SpdySynStreamControlFrame* frame,
// to a string of headers; this is because the HttpResponseInfo
// is a bit rigid for its http (non-spdy) design.
HttpResponseInfo response;
- if (SpdyHeadersToHttpResponse(*headers, &response)) {
+ if (SpdyHeadersToHttpResponse(headers, &response)) {
GetSSLInfo(&response.ssl_info);
stream->OnResponseReceived(response);
} else {
@@ -935,10 +936,9 @@ void SpdySession::OnSyn(const spdy::SpdySynStreamControlFrame* frame,
push_requests.Increment();
}
-void SpdySession::OnSynReply(const spdy::SpdySynReplyControlFrame* frame,
- const spdy::SpdyHeaderBlock* headers) {
- DCHECK(headers);
- spdy::SpdyStreamId stream_id = frame->stream_id();
+void SpdySession::OnSynReply(const spdy::SpdySynReplyControlFrame& frame,
+ const spdy::SpdyHeaderBlock& headers) {
+ spdy::SpdyStreamId stream_id = frame.stream_id();
LOG(INFO) << "Spdy SynReply for stream " << stream_id;
bool valid_stream = IsStreamActive(stream_id);
@@ -949,14 +949,14 @@ void SpdySession::OnSynReply(const spdy::SpdySynReplyControlFrame* frame,
}
LOG(INFO) << "SPDY SYN_REPLY RESPONSE HEADERS for stream: " << stream_id;
- DumpSpdyHeaders(*headers);
+ DumpSpdyHeaders(headers);
// We record content declared as being pushed so that we don't
// request a duplicate stream which is already scheduled to be
// sent to us.
spdy::SpdyHeaderBlock::const_iterator it;
- it = headers->find("X-Associated-Content");
- if (it != headers->end()) {
+ it = headers.find("X-Associated-Content");
+ if (it != headers.end()) {
const std::string& content = it->second;
std::string::size_type start = 0;
std::string::size_type end = 0;
@@ -983,7 +983,7 @@ void SpdySession::OnSynReply(const spdy::SpdySynReplyControlFrame* frame,
CHECK_EQ(stream->stream_id(), stream_id);
CHECK(!stream->cancelled());
HttpResponseInfo response;
- if (SpdyHeadersToHttpResponse(*headers, &response)) {
+ if (SpdyHeadersToHttpResponse(headers, &response)) {
GetSSLInfo(&response.ssl_info);
stream->OnResponseReceived(response);
} else {
@@ -1005,24 +1005,27 @@ void SpdySession::OnControl(const spdy::SpdyControlFrame* frame) {
switch (type) {
case spdy::SYN_STREAM:
- OnSyn(reinterpret_cast<const spdy::SpdySynStreamControlFrame*>(frame),
- &headers);
+ OnSyn(*reinterpret_cast<const spdy::SpdySynStreamControlFrame*>(frame),
+ headers);
break;
case spdy::SYN_REPLY:
OnSynReply(
- reinterpret_cast<const spdy::SpdySynReplyControlFrame*>(frame),
- &headers);
+ *reinterpret_cast<const spdy::SpdySynReplyControlFrame*>(frame),
+ headers);
break;
case spdy::RST_STREAM:
- OnFin(reinterpret_cast<const spdy::SpdyRstStreamControlFrame*>(frame));
+ OnFin(*reinterpret_cast<const spdy::SpdyRstStreamControlFrame*>(frame));
+ break;
+ case spdy::GOAWAY:
+ OnGoAway(*reinterpret_cast<const spdy::SpdyGoAwayControlFrame*>(frame));
break;
default:
DCHECK(false); // Error!
}
}
-void SpdySession::OnFin(const spdy::SpdyRstStreamControlFrame* frame) {
- spdy::SpdyStreamId stream_id = frame->stream_id();
+void SpdySession::OnFin(const spdy::SpdyRstStreamControlFrame& frame) {
+ spdy::SpdyStreamId stream_id = frame.stream_id();
LOG(INFO) << "Spdy Fin for stream " << stream_id;
bool valid_stream = IsStreamActive(stream_id);
@@ -1034,10 +1037,10 @@ void SpdySession::OnFin(const spdy::SpdyRstStreamControlFrame* frame) {
scoped_refptr<SpdyStream> stream = active_streams_[stream_id];
CHECK_EQ(stream->stream_id(), stream_id);
CHECK(!stream->cancelled());
- if (frame->status() == 0) {
+ if (frame.status() == 0) {
stream->OnDataReceived(NULL, 0);
} else {
- LOG(ERROR) << "Spdy stream closed: " << frame->status();
+ LOG(ERROR) << "Spdy stream closed: " << frame.status();
// TODO(mbelshe): Map from Spdy-protocol errors to something sensical.
// For now, it doesn't matter much - it is a protocol error.
stream->OnClose(ERR_FAILED);
@@ -1046,4 +1049,15 @@ void SpdySession::OnFin(const spdy::SpdyRstStreamControlFrame* frame) {
DeactivateStream(stream_id);
}
+void SpdySession::OnGoAway(const spdy::SpdyGoAwayControlFrame& frame) {
+ session_->spdy_session_pool()->Remove(this);
+
+ // TODO(willchan): Cancel any streams that are past the GoAway frame's
+ // |last_accepted_stream_id|.
+
+ // Don't bother killing any streams that are still reading. They'll either
+ // complete successfully or get an ERR_CONNECTION_CLOSED when the socket is
+ // closed.
+}
+
} // namespace net
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h
index 2f2465b..961e07b 100644
--- a/net/spdy/spdy_session.h
+++ b/net/spdy/spdy_session.h
@@ -118,11 +118,12 @@ class SpdySession : public base::RefCounted<SpdySession>,
virtual void OnControl(const spdy::SpdyControlFrame* frame);
// Control frame handlers.
- void OnSyn(const spdy::SpdySynStreamControlFrame* frame,
- const spdy::SpdyHeaderBlock* headers);
- void OnSynReply(const spdy::SpdySynReplyControlFrame* frame,
- const spdy::SpdyHeaderBlock* headers);
- void OnFin(const spdy::SpdyRstStreamControlFrame* frame);
+ void OnSyn(const spdy::SpdySynStreamControlFrame& frame,
+ const spdy::SpdyHeaderBlock& headers);
+ void OnSynReply(const spdy::SpdySynReplyControlFrame& frame,
+ const spdy::SpdyHeaderBlock& headers);
+ void OnFin(const spdy::SpdyRstStreamControlFrame& frame);
+ void OnGoAway(const spdy::SpdyGoAwayControlFrame& frame);
// IO Callbacks
void OnTCPConnect(int result);
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc
index 5392363..49e4491 100644
--- a/net/spdy/spdy_session_unittest.cc
+++ b/net/spdy/spdy_session_unittest.cc
@@ -4,18 +4,53 @@
#include "net/spdy/spdy_io_buffer.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/mock_host_resolver.h"
+#include "net/base/ssl_config_service_defaults.h"
#include "net/base/test_completion_callback.h"
+#include "net/http/http_network_session.h"
+#include "net/proxy/proxy_service.h"
#include "net/socket/socket_test_util.h"
#include "net/spdy/spdy_session.h"
+#include "net/spdy/spdy_session_pool.h"
#include "net/spdy/spdy_stream.h"
#include "testing/platform_test.h"
namespace net {
-class SpdySessionTest : public PlatformTest {
+namespace {
+
+// 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(ProxyService::CreateNull()),
+ ssl_config_service(new SSLConfigServiceDefaults),
+ 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_refptr<SpdySessionPool> spdy_session_pool;
};
+HttpNetworkSession* CreateSession(SessionDependencies* session_deps) {
+ return new HttpNetworkSession(NULL,
+ session_deps->host_resolver,
+ session_deps->proxy_service,
+ &session_deps->socket_factory,
+ session_deps->ssl_config_service,
+ session_deps->spdy_session_pool,
+ NULL);
+}
+
+typedef PlatformTest SpdySessionTest;
+
// Test the SpdyIOBuffer class.
TEST_F(SpdySessionTest, SpdyIOBuffer) {
std::priority_queue<SpdyIOBuffer> queue_;
@@ -53,4 +88,54 @@ TEST_F(SpdySessionTest, SpdyIOBuffer) {
}
}
+static const unsigned char kGoAway[] = {
+ 0x80, 0x01, 0x00, 0x07, // header
+ 0x00, 0x00, 0x00, 0x04, // flags, len
+ 0x00, 0x00, 0x00, 0x00, // last-accepted-stream-id
+};
+
+TEST_F(SpdySessionTest, GoAway) {
+ SessionDependencies session_deps;
+ session_deps.host_resolver->set_synchronous_mode(true);
+
+ MockConnect connect_data(false, OK);
+ MockRead reads[] = {
+ MockRead(false, reinterpret_cast<const char*>(kGoAway),
+ arraysize(kGoAway)),
+ MockRead(false, 0, 0) // EOF
+ };
+ StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0);
+ data.set_connect_data(connect_data);
+ session_deps.socket_factory.AddSocketDataProvider(&data);
+
+ SSLSocketDataProvider ssl(false, OK);
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
+
+ scoped_refptr<HttpNetworkSession> http_session(CreateSession(&session_deps));
+
+ const std::string kTestHost("www.foo.com");
+ const int kTestPort = 80;
+ HostPortPair test_host_port_pair;
+ test_host_port_pair.host = kTestHost;
+ test_host_port_pair.port = kTestPort;
+
+ scoped_refptr<SpdySessionPool> spdy_session_pool(
+ http_session->spdy_session_pool());
+ EXPECT_FALSE(spdy_session_pool->HasSession(test_host_port_pair));
+ scoped_refptr<SpdySession> session =
+ spdy_session_pool->Get(test_host_port_pair, http_session.get());
+ EXPECT_TRUE(spdy_session_pool->HasSession(test_host_port_pair));
+
+ TCPSocketParams tcp_params(kTestHost, kTestPort, MEDIUM, GURL(), false);
+ int rv = session->Connect(kTestHost, tcp_params, MEDIUM, NULL);
+ ASSERT_EQ(OK, rv);
+
+ // Flush the SpdySession::OnReadComplete() task.
+ MessageLoop::current()->RunAllPending();
+
+ EXPECT_FALSE(spdy_session_pool->HasSession(test_host_port_pair));
+}
+
+} // namespace
+
} // namespace net