diff options
author | willchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-10 19:29:42 +0000 |
---|---|---|
committer | willchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-10 19:29:42 +0000 |
commit | 651b77c761749949fc7f148e3c20fe203a61bf7d (patch) | |
tree | ce50cb8834b1a94120ba9c42a8dceb751aeeb4d7 /net | |
parent | 576a52b94483a5b1702f0ac0ad41810bb747a98a (diff) | |
download | chromium_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.cc | 62 | ||||
-rw-r--r-- | net/spdy/spdy_session.h | 11 | ||||
-rw-r--r-- | net/spdy/spdy_session_unittest.cc | 87 |
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 |