diff options
author | mbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-09 20:18:50 +0000 |
---|---|---|
committer | mbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-09 20:18:50 +0000 |
commit | 74188f235dd5b4f8e80ca30d6e3a4d4c3061d84d (patch) | |
tree | b27e432a39963d7738c1464001158d2b905c6fc4 /net | |
parent | 6c86698860a88d07b21da6efdaf41ceb8273ccb1 (diff) | |
download | chromium_src-74188f235dd5b4f8e80ca30d6e3a4d4c3061d84d.zip chromium_src-74188f235dd5b4f8e80ca30d6e3a4d4c3061d84d.tar.gz chromium_src-74188f235dd5b4f8e80ca30d6e3a4d4c3061d84d.tar.bz2 |
Take 2 at landing the SPDY SETTINGS frame patch.
BUG=34749
TEST=SpdyNetworkTransactionTest.Setting*
Review URL: http://codereview.chromium.org/1547028
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@44118 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/http/http_network_session.h | 10 | ||||
-rw-r--r-- | net/net.gyp | 2 | ||||
-rw-r--r-- | net/spdy/spdy_framer.cc | 5 | ||||
-rw-r--r-- | net/spdy/spdy_framer.h | 3 | ||||
-rw-r--r-- | net/spdy/spdy_network_transaction_unittest.cc | 263 | ||||
-rw-r--r-- | net/spdy/spdy_protocol.h | 6 | ||||
-rw-r--r-- | net/spdy/spdy_session.cc | 111 | ||||
-rw-r--r-- | net/spdy/spdy_session.h | 14 | ||||
-rw-r--r-- | net/spdy/spdy_settings_storage.cc | 47 | ||||
-rw-r--r-- | net/spdy/spdy_settings_storage.h | 40 |
10 files changed, 453 insertions, 48 deletions
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h index 540c59b..b8ad729 100644 --- a/net/http/http_network_session.h +++ b/net/http/http_network_session.h @@ -16,6 +16,7 @@ #include "net/proxy/proxy_service.h" #include "net/socket/socks_client_socket_pool.h" #include "net/socket/tcp_client_socket_pool.h" +#include "net/spdy/spdy_settings_storage.h" namespace net { @@ -50,6 +51,14 @@ class HttpNetworkSession return &alternate_protocols_; } + // Access to the SpdySettingsStorage + const SpdySettingsStorage& spdy_settings() const { + return spdy_settings_; + } + SpdySettingsStorage* mutable_spdy_settings() { + return &spdy_settings_; + } + // TCP sockets come from the tcp_socket_pool(). const scoped_refptr<TCPClientSocketPool>& tcp_socket_pool() { return tcp_socket_pool_; @@ -119,6 +128,7 @@ class HttpNetworkSession scoped_refptr<SpdySessionPool> spdy_session_pool_; HttpAuthHandlerFactory* http_auth_handler_factory_; scoped_ptr<URLSecurityManager> url_security_manager_; + SpdySettingsStorage spdy_settings_; }; } // namespace net diff --git a/net/net.gyp b/net/net.gyp index 7a34e40..a0602e1 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -476,6 +476,8 @@ 'spdy/spdy_session.h', 'spdy/spdy_session_pool.cc', 'spdy/spdy_session_pool.h', + 'spdy/spdy_settings_storage.cc', + 'spdy/spdy_settings_storage.h', 'spdy/spdy_stream.cc', 'spdy/spdy_stream.h', 'spdy/spdy_transaction_factory.h', diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc index 864a0d9..65ef2e5 100644 --- a/net/spdy/spdy_framer.cc +++ b/net/spdy/spdy_framer.cc @@ -298,6 +298,11 @@ void SpdyFramer::ProcessControlFrameHeader() { SpdyGoAwayControlFrame::size() - SpdyFrame::size()) set_error(SPDY_INVALID_CONTROL_FRAME); break; + case SETTINGS: + if (current_control_frame.length() < + SpdySettingsControlFrame::size() - SpdyControlFrame::size()) + set_error(SPDY_INVALID_CONTROL_FRAME); + break; default: LOG(WARNING) << "Valid spdy control frame with unknown type: " << current_control_frame.type(); diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h index ab454cd..c265be9 100644 --- a/net/spdy/spdy_framer.h +++ b/net/spdy/spdy_framer.h @@ -43,7 +43,8 @@ void FramerSetEnableCompressionHelper(SpdyFramer* framer, bool compress); typedef std::map<std::string, std::string> SpdyHeaderBlock; // A datastructure for holding a set of ID/value pairs for a SETTINGS frame. -typedef std::list<std::pair<spdy::SettingsFlagsAndId, uint32> > SpdySettings; +typedef std::pair<spdy::SettingsFlagsAndId, uint32> SpdySetting; +typedef std::list<SpdySetting> SpdySettings; // SpdyFramerVisitorInterface is a set of callbacks for the SpdyFramer. // Implement this interface to receive event callbacks as frames are diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc index fb06906..d7d0cdf 100644 --- a/net/spdy/spdy_network_transaction_unittest.cc +++ b/net/spdy/spdy_network_transaction_unittest.cc @@ -294,6 +294,10 @@ void AppendHeadersToSpdyFrame(const char* const extra_headers[], spdy::SpdyHeaderBlock* headers) { std::string this_header; std::string this_value; + + if (!extra_header_count) + return; + // Sanity check: Non-NULL header list. DCHECK(NULL != extra_headers) << "NULL header value pair list"; // Sanity check: Non-NULL header map. @@ -496,6 +500,14 @@ int ConstructSpdyReply(const char* const extra_headers[], return packet_size; } +// Construct an expected SPDY SETTINGS frame. +// |settings| are the settings to set. +// Returns the constructed frame. The caller takes ownership of the frame. +spdy::SpdyFrame* ConstructSpdySettings(spdy::SpdySettings settings) { + spdy::SpdyFramer framer; + return framer.CreateSettings(settings); +} + // Construct a single SPDY header entry, for validation. // |extra_headers| are the extra header-value pairs. // |buffer| is the buffer we're filling in. @@ -792,16 +804,28 @@ class SpdyNetworkTransactionTest : public PlatformTest { TransactionHelperResult TransactionHelper(const HttpRequestInfo& request, DelayedSocketData* data, const BoundNetLog& log) { + SessionDependencies session_deps; + HttpNetworkSession* session = CreateSession(&session_deps); + return TransactionHelperWithSession(request, data, log, &session_deps, + session); + } + + TransactionHelperResult TransactionHelperWithSession( + const HttpRequestInfo& request, DelayedSocketData* data, + const BoundNetLog& log, SessionDependencies* session_deps, + HttpNetworkSession* session) { + CHECK(session); + CHECK(session_deps); + TransactionHelperResult out; // We disable SSL for this test. SpdySession::SetSSLMode(false); - SessionDependencies session_deps; scoped_ptr<SpdyNetworkTransaction> trans( - new SpdyNetworkTransaction(CreateSession(&session_deps))); + new SpdyNetworkTransaction(session)); - session_deps.socket_factory.AddSocketDataProvider(data); + session_deps->socket_factory.AddSocketDataProvider(data); TestCompletionCallback callback; @@ -2454,4 +2478,237 @@ TEST_F(SpdyNetworkTransactionTest, BufferedCancelled) { MessageLoop::current()->RunAllPending(); } +// Test that if the server requests persistence of settings, that we save +// the settings in the SpdySettingsStorage. +TEST_F(SpdyNetworkTransactionTest, SettingsSaved) { + static const SpdyHeaderInfo kSynReplyInfo = { + spdy::SYN_REPLY, // Syn Reply + 1, // Stream ID + 0, // Associated Stream ID + 3, // Priority + spdy::CONTROL_FLAG_NONE, // Control Flags + false, // Compressed + 200, // Status + NULL, // Data + 0, // Data Length + spdy::DATA_FLAG_NONE // Data Flags + }; + static const char* const kExtraHeaders[] = { + "status", "200", + "version", "HTTP/1.1" + }; + + SessionDependencies session_deps; + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + + // Verify that no settings exist initially. + HostPortPair host_port_pair("www.google.com", 80); + EXPECT_TRUE(session->spdy_settings().Get(host_port_pair).empty()); + + // Construct the request. + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + + MockWrite writes[] = { + MockWrite(true, req->data(), req->length() + spdy::SpdyFrame::size()), + }; + + // Construct the reply. + scoped_ptr<spdy::SpdyFrame> reply( + ConstructSpdyPacket(&kSynReplyInfo, + kExtraHeaders, + arraysize(kExtraHeaders) / 2, + NULL, + 0)); + + unsigned int kSampleId1 = 0x1; + unsigned int kSampleValue1 = 0x0a0a0a0a; + unsigned int kSampleId2 = 0x2; + unsigned int kSampleValue2 = 0x0b0b0b0b; + unsigned int kSampleId3 = 0xababab; + unsigned int kSampleValue3 = 0x0c0c0c0c; + scoped_ptr<spdy::SpdyFrame> settings_frame; + { + // Construct the SETTINGS frame. + spdy::SpdySettings settings; + spdy::SettingsFlagsAndId setting(0); + // First add a persisted setting + setting.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST); + setting.set_id(kSampleId1); + settings.push_back(std::make_pair(setting, kSampleValue1)); + // Next add a non-persisted setting + setting.set_flags(0); + setting.set_id(kSampleId2); + settings.push_back(std::make_pair(setting, kSampleValue2)); + // Next add another persisted setting + setting.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST); + setting.set_id(kSampleId3); + settings.push_back(std::make_pair(setting, kSampleValue3)); + settings_frame.reset(ConstructSpdySettings(settings)); + } + + MockRead reads[] = { + MockRead(true, reply->data(), reply->length() + spdy::SpdyFrame::size()), + MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), + arraysize(kGetBodyFrame)), + MockRead(true, settings_frame->data(), + settings_frame->length() + spdy::SpdyFrame::size()), + MockRead(true, 0, 0) // EOF + }; + + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + + scoped_refptr<DelayedSocketData> data( + new DelayedSocketData(1, reads, arraysize(reads), + writes, arraysize(writes))); + TransactionHelperResult out = TransactionHelperWithSession(request, + data.get(), + NULL, + &session_deps, + session.get()); + EXPECT_EQ(OK, out.rv); + EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); + EXPECT_EQ("hello!", out.response_data); + + { + // Verify we had two persisted settings. + spdy::SpdySettings saved_settings = + session->spdy_settings().Get(host_port_pair); + ASSERT_EQ(2u, saved_settings.size()); + + // Verify the first persisted setting. + spdy::SpdySetting setting = saved_settings.front(); + saved_settings.pop_front(); + EXPECT_EQ(spdy::SETTINGS_FLAG_PERSISTED, setting.first.flags()); + EXPECT_EQ(kSampleId1, setting.first.id()); + EXPECT_EQ(kSampleValue1, setting.second); + + // Verify the second persisted setting. + setting = saved_settings.front(); + saved_settings.pop_front(); + EXPECT_EQ(spdy::SETTINGS_FLAG_PERSISTED, setting.first.flags()); + EXPECT_EQ(kSampleId3, setting.first.id()); + EXPECT_EQ(kSampleValue3, setting.second); + } +} + +// Test that when there are settings saved that they are sent back to the +// server upon session establishment. +TEST_F(SpdyNetworkTransactionTest, SettingsPlayback) { + static const SpdyHeaderInfo kSynReplyInfo = { + spdy::SYN_REPLY, // Syn Reply + 1, // Stream ID + 0, // Associated Stream ID + 3, // Priority + spdy::CONTROL_FLAG_NONE, // Control Flags + false, // Compressed + 200, // Status + NULL, // Data + 0, // Data Length + spdy::DATA_FLAG_NONE // Data Flags + }; + static const char* kExtraHeaders[] = { + "status", "200", + "version", "HTTP/1.1" + }; + + SessionDependencies session_deps; + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + + // Verify that no settings exist initially. + HostPortPair host_port_pair("www.google.com", 80); + EXPECT_TRUE(session->spdy_settings().Get(host_port_pair).empty()); + + unsigned int kSampleId1 = 0x1; + unsigned int kSampleValue1 = 0x0a0a0a0a; + unsigned int kSampleId2 = 0xababab; + unsigned int kSampleValue2 = 0x0c0c0c0c; + // Manually insert settings into the SpdySettingsStorage here. + { + spdy::SpdySettings settings; + spdy::SettingsFlagsAndId setting(0); + // First add a persisted setting + setting.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST); + setting.set_id(kSampleId1); + settings.push_back(std::make_pair(setting, kSampleValue1)); + // Next add another persisted setting + setting.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST); + setting.set_id(kSampleId2); + settings.push_back(std::make_pair(setting, kSampleValue2)); + + session->mutable_spdy_settings()->Set(host_port_pair, settings); + } + + EXPECT_EQ(2u, session->spdy_settings().Get(host_port_pair).size()); + + // Construct the SETTINGS frame. + const spdy::SpdySettings& settings = + session->spdy_settings().Get(host_port_pair); + scoped_ptr<spdy::SpdyFrame> settings_frame(ConstructSpdySettings(settings)); + + // Construct the request. + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + + MockWrite writes[] = { + MockWrite(true, settings_frame->data(), + settings_frame->length() + spdy::SpdyFrame::size()), + MockWrite(true, req->data(), req->length() + spdy::SpdyFrame::size()), + }; + + // Construct the reply. + scoped_ptr<spdy::SpdyFrame> reply( + ConstructSpdyPacket(&kSynReplyInfo, + kExtraHeaders, + arraysize(kExtraHeaders) / 2, + NULL, + 0)); + + MockRead reads[] = { + MockRead(true, reply->data(), reply->length() + spdy::SpdyFrame::size()), + MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), + arraysize(kGetBodyFrame)), + MockRead(true, 0, 0) // EOF + }; + + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + + scoped_refptr<DelayedSocketData> data( + new DelayedSocketData(2, reads, arraysize(reads), + writes, arraysize(writes))); + TransactionHelperResult out = TransactionHelperWithSession(request, + data.get(), + NULL, + &session_deps, + session.get()); + EXPECT_EQ(OK, out.rv); + EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); + EXPECT_EQ("hello!", out.response_data); + + { + // Verify we had two persisted settings. + spdy::SpdySettings saved_settings = + session->spdy_settings().Get(host_port_pair); + ASSERT_EQ(2u, saved_settings.size()); + + // Verify the first persisted setting. + spdy::SpdySetting setting = saved_settings.front(); + saved_settings.pop_front(); + EXPECT_EQ(spdy::SETTINGS_FLAG_PERSISTED, setting.first.flags()); + EXPECT_EQ(kSampleId1, setting.first.id()); + EXPECT_EQ(kSampleValue1, setting.second); + + // Verify the second persisted setting. + setting = saved_settings.front(); + saved_settings.pop_front(); + EXPECT_EQ(spdy::SETTINGS_FLAG_PERSISTED, setting.first.flags()); + EXPECT_EQ(kSampleId2, setting.first.id()); + EXPECT_EQ(kSampleValue2, setting.second); + } +} + } // namespace net diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h index c8d5830..435c55b 100644 --- a/net/spdy/spdy_protocol.h +++ b/net/spdy/spdy_protocol.h @@ -241,7 +241,13 @@ union SettingsFlagsAndId { SettingsFlagsAndId(uint32 val) : id_(val) {}; uint8 flags() const { return flags_[0]; } + void set_flags(uint8 flags) { flags_[0] = flags; } uint32 id() const { return (ntohl(id_) & kSettingsIdMask); }; + void set_id(uint32 id) { + DCHECK_EQ(0u, (id & ~kSettingsIdMask)); + id = htonl(id & kSettingsIdMask); + id_ = flags() | id; + } }; // A SETTINGS Control Frame structure. diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc index 2607f90..1ab3904 100644 --- a/net/spdy/spdy_session.cc +++ b/net/spdy/spdy_session.cc @@ -25,6 +25,7 @@ #include "net/socket/ssl_client_socket.h" #include "net/spdy/spdy_frame_builder.h" #include "net/spdy/spdy_protocol.h" +#include "net/spdy/spdy_settings_storage.h" #include "net/spdy/spdy_stream.h" #include "net/tools/dump_cache/url_to_filename_encoder.h" @@ -215,6 +216,8 @@ SpdySession::SpdySession(const HostPortPair& host_port_pair, spdy_framer_.set_visitor(this); session_->ssl_config_service()->GetSSLConfig(&ssl_config_); + + SendSettings(); } SpdySession::~SpdySession() { @@ -367,10 +370,7 @@ scoped_refptr<SpdyStream> SpdySession::GetOrCreateStream( scoped_ptr<spdy::SpdySynStreamControlFrame> syn_frame( spdy_framer_.CreateSynStream(stream_id, 0, request.priority, flags, false, &headers)); - int length = spdy::SpdyFrame::size() + syn_frame->length(); - IOBuffer* buffer = new IOBuffer(length); - memcpy(buffer->data(), syn_frame->data(), length); - queue_.push(SpdyIOBuffer(buffer, length, request.priority, stream)); + QueueFrame(syn_frame.get(), request.priority, stream); static StatsCounter spdy_requests("spdy.requests"); spdy_requests.Increment(); @@ -381,14 +381,6 @@ scoped_refptr<SpdyStream> SpdySession::GetOrCreateStream( LOG(INFO) << "SPDY SYN_STREAM HEADERS ----------------------------------"; DumpSpdyHeaders(headers); - // Schedule to write to the socket after we've made it back - // to the message loop so that we can aggregate multiple - // requests. - // TODO(mbelshe): Should we do the "first" request immediately? - // maybe we should only 'do later' for subsequent - // requests. - WriteSocketLater(); - return stream; } @@ -423,15 +415,7 @@ int SpdySession::WriteStreamData(spdy::SpdyStreamId stream_id, // TODO(mbelshe): reduce memory copies here. scoped_ptr<spdy::SpdyDataFrame> frame( spdy_framer_.CreateDataFrame(stream_id, data->data(), len, flags)); - int length = spdy::SpdyFrame::size() + frame->length(); - IOBufferWithSize* buffer = new IOBufferWithSize(length); - memcpy(buffer->data(), frame->data(), length); - queue_.push(SpdyIOBuffer(buffer, length, stream->priority(), stream)); - - // Whenever we queue onto the socket we need to ensure that we will write to - // it later. - WriteSocketLater(); - + QueueFrame(frame.get(), stream->priority(), stream); return ERR_IO_PENDING; } @@ -580,8 +564,10 @@ void SpdySession::OnWriteComplete(int result) { write_pending_ = false; - LOG(INFO) << "Spdy write complete (result=" << result << ") for stream: " - << in_flight_write_.stream()->stream_id(); + scoped_refptr<SpdyStream> stream = in_flight_write_.stream(); + + LOG(INFO) << "Spdy write complete (result=" << result << ")" + << (stream ? " for stream " + stream->stream_id() : ""); if (result >= 0) { // It should not be possible to have written more bytes than our @@ -593,22 +579,23 @@ void SpdySession::OnWriteComplete(int result) { // We only notify the stream when we've fully written the pending frame. if (!in_flight_write_.buffer()->BytesRemaining()) { scoped_refptr<SpdyStream> stream = in_flight_write_.stream(); - DCHECK(stream.get()); - - // Report the number of bytes written to the caller, but exclude the - // frame size overhead. NOTE: if this frame was compressed the reported - // bytes written is the compressed size, not the original size. - if (result > 0) { - result = in_flight_write_.buffer()->size(); - DCHECK_GT(result, static_cast<int>(spdy::SpdyFrame::size())); - result -= static_cast<int>(spdy::SpdyFrame::size()); + if (stream) { + // Report the number of bytes written to the caller, but exclude the + // frame size overhead. NOTE: if this frame was compressed the + // reported bytes written is the compressed size, not the original + // size. + if (result > 0) { + result = in_flight_write_.buffer()->size(); + DCHECK_GT(result, static_cast<int>(spdy::SpdyFrame::size())); + result -= static_cast<int>(spdy::SpdyFrame::size()); + } + + // It is possible that the stream was cancelled while we were writing + // to the socket. + if (!stream->cancelled()) + stream->OnWriteComplete(result); } - // It is possible that the stream was cancelled while we were writing - // to the socket. - if (!stream->cancelled()) - stream->OnWriteComplete(result); - // Cleanup the write which just completed. in_flight_write_.release(); } @@ -664,6 +651,9 @@ void SpdySession::WriteSocketLater() { if (delayed_write_pending_) return; + if (state_ < CONNECTED) + return; + delayed_write_pending_ = true; MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( this, &SpdySession::WriteSocket)); @@ -781,6 +771,17 @@ int SpdySession::GetNewStreamId() { return id; } +void SpdySession::QueueFrame(spdy::SpdyFrame* frame, + spdy::SpdyPriority priority, + SpdyStream* stream) { + int length = spdy::SpdyFrame::size() + frame->length(); + IOBuffer* buffer = new IOBuffer(length); + memcpy(buffer->data(), frame->data(), length); + queue_.push(SpdyIOBuffer(buffer, length, priority, stream)); + + WriteSocketLater(); +} + void SpdySession::CloseSessionOnError(net::Error err) { DCHECK_LT(err, OK); LOG(INFO) << "spdy::CloseSessionOnError(" << err << ") for " << @@ -1040,6 +1041,16 @@ void SpdySession::OnControl(const spdy::SpdyControlFrame* frame) { } switch (type) { + case spdy::GOAWAY: + OnGoAway(*reinterpret_cast<const spdy::SpdyGoAwayControlFrame*>(frame)); + break; + case spdy::SETTINGS: + OnSettings( + *reinterpret_cast<const spdy::SpdySettingsControlFrame*>(frame)); + break; + case spdy::RST_STREAM: + OnFin(*reinterpret_cast<const spdy::SpdyRstStreamControlFrame*>(frame)); + break; case spdy::SYN_STREAM: OnSyn(*reinterpret_cast<const spdy::SpdySynStreamControlFrame*>(frame), headers); @@ -1049,12 +1060,6 @@ void SpdySession::OnControl(const spdy::SpdyControlFrame* frame) { *reinterpret_cast<const spdy::SpdySynReplyControlFrame*>(frame), headers); break; - case spdy::RST_STREAM: - OnFin(*reinterpret_cast<const spdy::SpdyRstStreamControlFrame*>(frame)); - break; - case spdy::GOAWAY: - OnGoAway(*reinterpret_cast<const spdy::SpdyGoAwayControlFrame*>(frame)); - break; default: DCHECK(false); // Error! } @@ -1098,4 +1103,24 @@ void SpdySession::OnGoAway(const spdy::SpdyGoAwayControlFrame& frame) { // closed. } +void SpdySession::OnSettings(const spdy::SpdySettingsControlFrame& frame) { + spdy::SpdySettings settings; + if (spdy_framer_.ParseSettings(&frame, &settings)) { + SpdySettingsStorage* settings_storage = session_->mutable_spdy_settings(); + settings_storage->Set(host_port_pair_, settings); + } +} + +void SpdySession::SendSettings() { + const SpdySettingsStorage& settings_storage = session_->spdy_settings(); + const spdy::SpdySettings& settings = settings_storage.Get(host_port_pair_); + if (settings.empty()) + return; + + // Create the SETTINGS frame and send it. + scoped_ptr<spdy::SpdySettingsControlFrame> settings_frame( + spdy_framer_.CreateSettings(settings)); + QueueFrame(settings_frame.get(), 0, NULL); +} + } // namespace net diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h index 653f193..d7f60c0 100644 --- a/net/spdy/spdy_session.h +++ b/net/spdy/spdy_session.h @@ -91,7 +91,8 @@ class SpdySession : public base::RefCounted<SpdySession>, spdy::SpdyFramer* GetFramer() { return &spdy_framer_; } // Create a new SpdySession. - // |host| is the hostname that this session connects to. + // |host_port_pair| is the host/port that this session connects to. + // |session| is the HttpNetworkSession SpdySession(const HostPortPair& host_port_pair, HttpNetworkSession* session); // Closes all open streams. Used as part of shutdown. @@ -124,6 +125,7 @@ class SpdySession : public base::RefCounted<SpdySession>, const spdy::SpdyHeaderBlock& headers); void OnFin(const spdy::SpdyRstStreamControlFrame& frame); void OnGoAway(const spdy::SpdyGoAwayControlFrame& frame); + void OnSettings(const spdy::SpdySettingsControlFrame& frame); // IO Callbacks void OnTCPConnect(int result); @@ -131,6 +133,9 @@ class SpdySession : public base::RefCounted<SpdySession>, void OnReadComplete(int result); void OnWriteComplete(int result); + // Send relevant SETTINGS. This is generally called on connection setup. + void SendSettings(); + // Start reading from the socket. void ReadSocket(); @@ -141,6 +146,13 @@ class SpdySession : public base::RefCounted<SpdySession>, // Get a new stream id. int GetNewStreamId(); + // Queue a frame for sending. + // |frame| is the frame to send. + // |priority| is the priority for insertion into the queue. + // |stream| is the stream which this IO is associated with (or NULL). + void QueueFrame(spdy::SpdyFrame* frame, spdy::SpdyPriority priority, + SpdyStream* stream); + // Closes this session. This will close all active streams and mark // the session as permanently closed. // |err| should not be OK; this function is intended to be called on diff --git a/net/spdy/spdy_settings_storage.cc b/net/spdy/spdy_settings_storage.cc new file mode 100644 index 0000000..32069b7 --- /dev/null +++ b/net/spdy/spdy_settings_storage.cc @@ -0,0 +1,47 @@ +// 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_settings_storage.h" + +#include <utility> + +namespace net { + +SpdySettingsStorage::SpdySettingsStorage() { +} + +const spdy::SpdySettings& SpdySettingsStorage::Get( + const HostPortPair& host_port_pair) const { + SettingsMap::const_iterator it = settings_map_.find(host_port_pair); + if (it == settings_map_.end()) { + static const spdy::SpdySettings kEmpty; + return kEmpty; + } + return it->second; +} + +void SpdySettingsStorage::Set(const HostPortPair& host_port_pair, + const spdy::SpdySettings& settings) { + spdy::SpdySettings persistent_settings; + + // Iterate through the list, and only copy those settings which are marked + // for persistence. + spdy::SpdySettings::const_iterator it; + for (it = settings.begin(); it != settings.end(); ++it) { + spdy::SettingsFlagsAndId id = it->first; + if (id.flags() & spdy::SETTINGS_FLAG_PLEASE_PERSIST) { + id.set_flags(spdy::SETTINGS_FLAG_PERSISTED); + persistent_settings.push_back(std::make_pair(id, it->second)); + } + } + + // If we didn't persist anything, then we are done. + if (persistent_settings.empty()) + return; + + settings_map_[host_port_pair] = persistent_settings; +} + +} // namespace net + diff --git a/net/spdy/spdy_settings_storage.h b/net/spdy/spdy_settings_storage.h new file mode 100644 index 0000000..93b44dc --- /dev/null +++ b/net/spdy/spdy_settings_storage.h @@ -0,0 +1,40 @@ +// 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. + +#ifndef NET_SPDY_SPDY_SETTING_STORAGE_H_ +#define NET_SPDY_SPDY_SETTING_STORAGE_H_ + +#include <map> +#include "base/basictypes.h" +#include "net/base/host_port_pair.h" +#include "net/spdy/spdy_framer.h" + +namespace net { + +// SpdySettingsStorage stores SpdySettings which have been transmitted between +// endpoints for the SPDY SETTINGS frame. +class SpdySettingsStorage { + public: + SpdySettingsStorage(); + + // Get a copy of the SpdySettings stored for a host. + // If no settings are stored, returns an empty set of settings. + const spdy::SpdySettings& Get(const HostPortPair& host_port_pair) const; + + // Save settings for a host. + void Set(const HostPortPair& host_port_pair, + const spdy::SpdySettings& settings); + + private: + typedef std::map<HostPortPair, spdy::SpdySettings> SettingsMap; + + SettingsMap settings_map_; + + DISALLOW_COPY_AND_ASSIGN(SpdySettingsStorage); +}; + +} // namespace net + +#endif // NET_SPDY_SPDY_SETTING_STORAGE_H_ + |