summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authormbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-09 20:18:50 +0000
committermbelshe@chromium.org <mbelshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-09 20:18:50 +0000
commit74188f235dd5b4f8e80ca30d6e3a4d4c3061d84d (patch)
treeb27e432a39963d7738c1464001158d2b905c6fc4 /net
parent6c86698860a88d07b21da6efdaf41ceb8273ccb1 (diff)
downloadchromium_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.h10
-rw-r--r--net/net.gyp2
-rw-r--r--net/spdy/spdy_framer.cc5
-rw-r--r--net/spdy/spdy_framer.h3
-rw-r--r--net/spdy/spdy_network_transaction_unittest.cc263
-rw-r--r--net/spdy/spdy_protocol.h6
-rw-r--r--net/spdy/spdy_session.cc111
-rw-r--r--net/spdy/spdy_session.h14
-rw-r--r--net/spdy/spdy_settings_storage.cc47
-rw-r--r--net/spdy/spdy_settings_storage.h40
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_
+