summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrtenneti@google.com <rtenneti@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-12 18:42:12 +0000
committerrtenneti@google.com <rtenneti@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-12 18:42:12 +0000
commit39c48fcca9e6dfcd163778ed17d3eff865cf204b (patch)
treeeb28e6daabb23f5d290466102dc729da83708271
parenta1b9abbd91e5cfa56fb2649ce86709fac623f4ed (diff)
downloadchromium_src-39c48fcca9e6dfcd163778ed17d3eff865cf204b.zip
chromium_src-39c48fcca9e6dfcd163778ed17d3eff865cf204b.tar.gz
chromium_src-39c48fcca9e6dfcd163778ed17d3eff865cf204b.tar.bz2
SPDY - integration of spdy/3 code.
TEST=network unit tests and browser unit_tests R=willchan Review URL: https://chromiumcodereview.appspot.com/9618002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@126175 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/net/http_server_properties_manager.cc17
-rw-r--r--chrome/browser/net/http_server_properties_manager.h6
-rw-r--r--chrome/browser/net/http_server_properties_manager_unittest.cc32
-rw-r--r--net/base/net_test_suite.cc4
-rw-r--r--net/http/http_network_layer.cc18
-rw-r--r--net/http/http_network_transaction_spdy21_unittest.cc1
-rw-r--r--net/http/http_network_transaction_spdy2_unittest.cc1
-rw-r--r--net/http/http_network_transaction_spdy3_unittest.cc67
-rw-r--r--net/http/http_proxy_client_socket_pool_spdy3_unittest.cc2
-rw-r--r--net/http/http_server_properties.cc4
-rw-r--r--net/http/http_server_properties.h11
-rw-r--r--net/http/http_server_properties_impl.cc27
-rw-r--r--net/http/http_server_properties_impl.h7
-rw-r--r--net/http/http_server_properties_impl_unittest.cc77
-rw-r--r--net/http/http_stream_factory.cc2
-rw-r--r--net/net.gyp9
-rw-r--r--net/socket/ssl_client_socket.cc4
-rw-r--r--net/socket/ssl_client_socket_pool.cc3
-rw-r--r--net/spdy/buffered_spdy_framer.cc19
-rw-r--r--net/spdy/buffered_spdy_framer.h11
-rw-r--r--net/spdy/buffered_spdy_framer_spdy2_unittest.cc25
-rw-r--r--net/spdy/buffered_spdy_framer_spdy3_unittest.cc25
-rw-r--r--net/spdy/spdy_bitmasks.h8
-rw-r--r--net/spdy/spdy_frame_builder.cc136
-rw-r--r--net/spdy/spdy_frame_builder.h44
-rw-r--r--net/spdy/spdy_frame_reader.cc126
-rw-r--r--net/spdy/spdy_frame_reader.h96
-rw-r--r--net/spdy/spdy_frame_reader_test.cc249
-rw-r--r--net/spdy/spdy_framer.cc905
-rw-r--r--net/spdy/spdy_framer.h228
-rw-r--r--net/spdy/spdy_framer_spdy2_test.cc (renamed from net/spdy/spdy_framer_test.cc)1450
-rw-r--r--net/spdy/spdy_framer_spdy3_test.cc2623
-rw-r--r--net/spdy/spdy_http_stream_spdy2_unittest.cc21
-rw-r--r--net/spdy/spdy_http_stream_spdy3_unittest.cc5
-rw-r--r--net/spdy/spdy_network_transaction_spdy21_unittest.cc46
-rw-r--r--net/spdy/spdy_network_transaction_spdy2_unittest.cc58
-rw-r--r--net/spdy/spdy_network_transaction_spdy3_unittest.cc53
-rw-r--r--net/spdy/spdy_protocol.h304
-rw-r--r--net/spdy/spdy_protocol_spdy2_test.cc (renamed from net/spdy/spdy_protocol_test.cc)105
-rw-r--r--net/spdy/spdy_protocol_spdy3_test.cc386
-rw-r--r--net/spdy/spdy_proxy_client_socket_spdy2_unittest.cc4
-rw-r--r--net/spdy/spdy_proxy_client_socket_spdy3_unittest.cc4
-rw-r--r--net/spdy/spdy_session.cc134
-rw-r--r--net/spdy/spdy_session.h15
-rw-r--r--net/spdy/spdy_session_spdy2_unittest.cc34
-rw-r--r--net/spdy/spdy_session_spdy3_unittest.cc34
-rw-r--r--net/spdy/spdy_settings_storage.cc6
-rw-r--r--net/spdy/spdy_stream_spdy2_unittest.cc4
-rw-r--r--net/spdy/spdy_stream_spdy3_unittest.cc4
-rw-r--r--net/spdy/spdy_websocket_stream_spdy2_unittest.cc14
-rw-r--r--net/spdy/spdy_websocket_stream_spdy3_unittest.cc14
-rw-r--r--net/tools/flip_server/spdy_interface.cc4
-rw-r--r--net/tools/flip_server/spdy_interface.h4
-rw-r--r--net/websockets/websocket_job_spdy2_unittest.cc1
-rw-r--r--net/websockets/websocket_job_spdy3_unittest.cc1
55 files changed, 6062 insertions, 1430 deletions
diff --git a/chrome/browser/net/http_server_properties_manager.cc b/chrome/browser/net/http_server_properties_manager.cc
index c39704a..3dabe59 100644
--- a/chrome/browser/net/http_server_properties_manager.cc
+++ b/chrome/browser/net/http_server_properties_manager.cc
@@ -159,6 +159,17 @@ bool HttpServerPropertiesManager::SetSpdySettings(
return persist;
}
+bool HttpServerPropertiesManager::SetSpdySetting(
+ const net::HostPortPair& host_port_pair,
+ const spdy::SpdySetting& setting) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ bool persist = http_server_properties_impl_->SetSpdySetting(
+ host_port_pair, setting);
+ if (persist)
+ ScheduleUpdatePrefsOnIO();
+ return persist;
+}
+
void HttpServerPropertiesManager::ClearSpdySettings() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
http_server_properties_impl_->ClearSpdySettings();
@@ -302,10 +313,8 @@ void HttpServerPropertiesManager::UpdateCacheFromPrefsOnUI() {
continue;
}
- spdy::SettingsFlagsAndId flags_and_id(0);
- flags_and_id.set_id(id);
- flags_and_id.set_flags(spdy::SETTINGS_FLAG_PERSISTED);
-
+ spdy::SettingsFlagsAndId flags_and_id(
+ spdy::SETTINGS_FLAG_PERSISTED, id);
spdy_settings.push_back(spdy::SpdySetting(flags_and_id, value));
}
diff --git a/chrome/browser/net/http_server_properties_manager.h b/chrome/browser/net/http_server_properties_manager.h
index 2196f96..8d5d85e 100644
--- a/chrome/browser/net/http_server_properties_manager.h
+++ b/chrome/browser/net/http_server_properties_manager.h
@@ -117,6 +117,12 @@ class HttpServerPropertiesManager
const net::HostPortPair& host_port_pair,
const spdy::SpdySettings& settings) OVERRIDE;
+ // Saves an individual setting for a host. Returns true if SpdySetting is
+ // persisted.
+ virtual bool SetSpdySetting(
+ const net::HostPortPair& host_port_pair,
+ const spdy::SpdySetting& setting) OVERRIDE;
+
// Clears all spdy_settings.
virtual void ClearSpdySettings() OVERRIDE;
diff --git a/chrome/browser/net/http_server_properties_manager_unittest.cc b/chrome/browser/net/http_server_properties_manager_unittest.cc
index 72fe555..35ca76c 100644
--- a/chrome/browser/net/http_server_properties_manager_unittest.cc
+++ b/chrome/browser/net/http_server_properties_manager_unittest.cc
@@ -281,9 +281,7 @@ TEST_F(HttpServerPropertiesManagerTest, SetSpdySettings) {
// Add SpdySettings for mail.google.com:443.
net::HostPortPair spdy_server_mail("mail.google.com", 443);
spdy::SpdySettings spdy_settings;
- spdy::SettingsFlagsAndId id1(0);
- id1.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
- id1.set_id(1234);
+ spdy::SettingsFlagsAndId id1(spdy::SETTINGS_FLAG_PLEASE_PERSIST, 1234);
spdy_settings.push_back(std::make_pair(id1, 31337));
http_server_props_manager_->SetSpdySettings(spdy_server_mail, spdy_settings);
@@ -302,6 +300,30 @@ TEST_F(HttpServerPropertiesManagerTest, SetSpdySettings) {
Mock::VerifyAndClearExpectations(http_server_props_manager_.get());
}
+TEST_F(HttpServerPropertiesManagerTest, SetSpdySetting) {
+ ExpectPrefsUpdate();
+
+ // Add SpdySetting for mail.google.com:443.
+ net::HostPortPair spdy_server_mail("mail.google.com", 443);
+ spdy::SettingsFlagsAndId id1(spdy::SETTINGS_FLAG_PLEASE_PERSIST, 1234);
+ http_server_props_manager_->SetSpdySetting(
+ spdy_server_mail, std::make_pair(id1, 31337));
+
+ // Run the task.
+ loop_.RunAllPending();
+
+ spdy::SpdySettings spdy_settings_ret =
+ http_server_props_manager_->GetSpdySettings(spdy_server_mail);
+ ASSERT_EQ(1U, spdy_settings_ret.size());
+ spdy::SpdySetting spdy_setting1_ret = spdy_settings_ret.front();
+ spdy::SettingsFlagsAndId id1_ret(spdy_setting1_ret.first);
+ EXPECT_EQ(1234U, id1_ret.id());
+ EXPECT_EQ(spdy::SETTINGS_FLAG_PERSISTED, id1_ret.flags());
+ EXPECT_EQ(31337U, spdy_setting1_ret.second);
+
+ Mock::VerifyAndClearExpectations(http_server_props_manager_.get());
+}
+
TEST_F(HttpServerPropertiesManagerTest, HasAlternateProtocol) {
ExpectPrefsUpdate();
@@ -359,9 +381,7 @@ TEST_F(HttpServerPropertiesManagerTest, Clear) {
spdy_server_mail, 443, net::NPN_SPDY_2);
spdy::SpdySettings spdy_settings;
- spdy::SettingsFlagsAndId id1(0);
- id1.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
- id1.set_id(1234);
+ spdy::SettingsFlagsAndId id1(spdy::SETTINGS_FLAG_PLEASE_PERSIST, 1234);
spdy_settings.push_back(std::make_pair(id1, 31337));
http_server_props_manager_->SetSpdySettings(spdy_server_mail, spdy_settings);
diff --git a/net/base/net_test_suite.cc b/net/base/net_test_suite.cc
index 3dc91b7..2d2b411 100644
--- a/net/base/net_test_suite.cc
+++ b/net/base/net_test_suite.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -7,6 +7,7 @@
#include "base/message_loop.h"
#include "net/base/network_change_notifier.h"
#include "net/http/http_stream_factory.h"
+#include "net/spdy/spdy_session.h"
#if defined(USE_NSS)
#include "net/ocsp/nss_ocsp.h"
#endif
@@ -15,6 +16,7 @@
class StaticReset : public ::testing::EmptyTestEventListener {
virtual void OnTestStart(const ::testing::TestInfo& test_info) OVERRIDE {
net::HttpStreamFactory::ResetStaticSettingsToInit();
+ net::SpdySession::ResetStaticSettingsToInit();
}
};
diff --git a/net/http/http_network_layer.cc b/net/http/http_network_layer.cc
index 0894af7..39d24f4 100644
--- a/net/http/http_network_layer.cc
+++ b/net/http/http_network_layer.cc
@@ -46,7 +46,7 @@ void HttpNetworkLayer::EnableSpdy(const std::string& mode) {
static const char kExclude[] = "exclude"; // Hosts to exclude
static const char kDisableCompression[] = "no-compress";
static const char kDisableAltProtocols[] = "no-alt-protocols";
- static const char kEnableVersionOne[] = "v1";
+ static const char kEnableVersionThree[] = "v3";
static const char kForceAltProtocols[] = "force-alt-protocols";
static const char kSingleDomain[] = "single-domain";
@@ -84,10 +84,12 @@ void HttpNetworkLayer::EnableSpdy(const std::string& mode) {
if (option == kOff) {
HttpStreamFactory::set_spdy_enabled(false);
} else if (option == kDisableSSL) {
+ SpdySession::set_default_protocol(SSLClientSocket::kProtoSPDY2);
SpdySession::SetSSLMode(false); // Disable SSL
HttpStreamFactory::set_force_spdy_over_ssl(false);
HttpStreamFactory::set_force_spdy_always(true);
} else if (option == kSSL) {
+ SpdySession::set_default_protocol(SSLClientSocket::kProtoSPDY2);
HttpStreamFactory::set_force_spdy_over_ssl(true);
HttpStreamFactory::set_force_spdy_always(true);
} else if (option == kDisablePing) {
@@ -102,6 +104,13 @@ void HttpNetworkLayer::EnableSpdy(const std::string& mode) {
next_protos.push_back("http/1.1");
next_protos.push_back("spdy/2");
HttpStreamFactory::SetNextProtos(next_protos);
+ } else if (option == kEnableVersionThree) {
+ std::vector<std::string> next_protos;
+ next_protos.push_back("http/1.1");
+ next_protos.push_back("spdy/2");
+ next_protos.push_back("spdy/2.1");
+ next_protos.push_back("spdy/3");
+ HttpStreamFactory::SetNextProtos(next_protos);
} else if (option == kEnableNpnHttpOnly) {
// Avoid alternate protocol in this case. Otherwise, browser will try SSL
// and then fallback to http. This introduces extra load.
@@ -110,13 +119,6 @@ void HttpNetworkLayer::EnableSpdy(const std::string& mode) {
next_protos.push_back("http/1.1");
next_protos.push_back("http1.1");
HttpStreamFactory::SetNextProtos(next_protos);
- } else if (option == kEnableVersionOne) {
- spdy::SpdyFramer::set_protocol_version(1);
- std::vector<std::string> next_protos;
- // This is a temporary hack to pretend we support version 1.
- next_protos.push_back("http/1.1");
- next_protos.push_back("spdy/1");
- HttpStreamFactory::SetNextProtos(next_protos);
} else if (option == kDisableAltProtocols) {
use_alt_protocols = false;
HttpStreamFactory::set_use_alternate_protocols(false);
diff --git a/net/http/http_network_transaction_spdy21_unittest.cc b/net/http/http_network_transaction_spdy21_unittest.cc
index 3873c84..a4c8834 100644
--- a/net/http/http_network_transaction_spdy21_unittest.cc
+++ b/net/http/http_network_transaction_spdy21_unittest.cc
@@ -164,6 +164,7 @@ class HttpNetworkTransactionSpdy21Test : public PlatformTest {
};
virtual void SetUp() {
+ SpdySession::set_default_protocol(SSLClientSocket::kProtoSPDY21);
NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
MessageLoop::current()->RunAllPending();
spdy::SpdyFramer::set_enable_compression_default(false);
diff --git a/net/http/http_network_transaction_spdy2_unittest.cc b/net/http/http_network_transaction_spdy2_unittest.cc
index 68ae655..4d19b32 100644
--- a/net/http/http_network_transaction_spdy2_unittest.cc
+++ b/net/http/http_network_transaction_spdy2_unittest.cc
@@ -164,6 +164,7 @@ class HttpNetworkTransactionSpdy2Test : public PlatformTest {
};
virtual void SetUp() {
+ SpdySession::set_default_protocol(SSLClientSocket::kProtoSPDY2);
NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
MessageLoop::current()->RunAllPending();
spdy::SpdyFramer::set_enable_compression_default(false);
diff --git a/net/http/http_network_transaction_spdy3_unittest.cc b/net/http/http_network_transaction_spdy3_unittest.cc
index 77954c2..59b45ea 100644
--- a/net/http/http_network_transaction_spdy3_unittest.cc
+++ b/net/http/http_network_transaction_spdy3_unittest.cc
@@ -97,7 +97,7 @@ std::vector<std::string> MakeNextProtos(const char* a, ...) {
// SpdyNextProtos returns a vector of NPN protocol strings for negotiating
// SPDY.
std::vector<std::string> SpdyNextProtos() {
- return MakeNextProtos("http/1.1", "spdy/2", "spdy/2.1", NULL);
+ return MakeNextProtos("http/1.1", "spdy/2", "spdy/2.1", "spdy/3", NULL);
}
} // namespace
@@ -164,6 +164,7 @@ class HttpNetworkTransactionSpdy3Test : public PlatformTest {
};
virtual void SetUp() {
+ SpdySession::set_default_protocol(SSLClientSocket::kProtoSPDY3);
NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
MessageLoop::current()->RunAllPending();
spdy::SpdyFramer::set_enable_compression_default(false);
@@ -385,7 +386,7 @@ CaptureGroupNameSSLSocketPool::CaptureGroupNameSocketPool(
// This is the expected return from a current server advertising SPDY.
static const char kAlternateProtocolHttpHeader[] =
- "Alternate-Protocol: 443:npn-spdy/2.1\r\n\r\n";
+ "Alternate-Protocol: 443:npn-spdy/3\r\n\r\n";
// Helper functions for validating that AuthChallengeInfo's are correctly
// configured for common cases.
@@ -2169,7 +2170,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test, HttpsProxySpdyGet) {
session_deps.socket_factory.AddSocketDataProvider(spdy_data.get());
SSLSocketDataProvider ssl(ASYNC, OK);
- ssl.SetNextProto(SSLClientSocket::kProtoSPDY21);
+ ssl.SetNextProto(SSLClientSocket::kProtoSPDY3);
session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
TestCompletionCallback callback1;
@@ -2253,7 +2254,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test, HttpsProxySpdyGetWithProxyAuth) {
session_deps.socket_factory.AddSocketDataProvider(data.get());
SSLSocketDataProvider ssl(ASYNC, OK);
- ssl.SetNextProto(SSLClientSocket::kProtoSPDY21);
+ ssl.SetNextProto(SSLClientSocket::kProtoSPDY3);
session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
TestCompletionCallback callback1;
@@ -2348,7 +2349,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test, HttpsProxySpdyConnectHttps) {
session_deps.socket_factory.AddSocketDataProvider(spdy_data.get());
SSLSocketDataProvider ssl(ASYNC, OK);
- ssl.SetNextProto(SSLClientSocket::kProtoSPDY21);
+ ssl.SetNextProto(SSLClientSocket::kProtoSPDY3);
session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
SSLSocketDataProvider ssl2(ASYNC, OK);
ssl2.was_npn_negotiated = false;
@@ -2427,11 +2428,11 @@ TEST_F(HttpNetworkTransactionSpdy3Test, HttpsProxySpdyConnectSpdy) {
session_deps.socket_factory.AddSocketDataProvider(spdy_data.get());
SSLSocketDataProvider ssl(ASYNC, OK);
- ssl.SetNextProto(SSLClientSocket::kProtoSPDY21);
+ ssl.SetNextProto(SSLClientSocket::kProtoSPDY3);
session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
SSLSocketDataProvider ssl2(ASYNC, OK);
- ssl2.SetNextProto(SSLClientSocket::kProtoSPDY21);
- ssl2.protocol_negotiated = SSLClientSocket::kProtoSPDY21;
+ ssl2.SetNextProto(SSLClientSocket::kProtoSPDY3);
+ ssl2.protocol_negotiated = SSLClientSocket::kProtoSPDY3;
session_deps.socket_factory.AddSSLSocketDataProvider(&ssl2);
TestCompletionCallback callback1;
@@ -2491,10 +2492,10 @@ TEST_F(HttpNetworkTransactionSpdy3Test, HttpsProxySpdyConnectFailure) {
session_deps.socket_factory.AddSocketDataProvider(spdy_data.get());
SSLSocketDataProvider ssl(ASYNC, OK);
- ssl.SetNextProto(SSLClientSocket::kProtoSPDY21);
+ ssl.SetNextProto(SSLClientSocket::kProtoSPDY3);
session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
SSLSocketDataProvider ssl2(ASYNC, OK);
- ssl2.SetNextProto(SSLClientSocket::kProtoSPDY21);
+ ssl2.SetNextProto(SSLClientSocket::kProtoSPDY3);
session_deps.socket_factory.AddSSLSocketDataProvider(&ssl2);
TestCompletionCallback callback1;
@@ -4596,7 +4597,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test, RedirectOfHttpsConnectViaSpdyProxy) {
data_reads, arraysize(data_reads),
data_writes, arraysize(data_writes)));
SSLSocketDataProvider proxy_ssl(ASYNC, OK); // SSL to the proxy
- proxy_ssl.SetNextProto(SSLClientSocket::kProtoSPDY21);
+ proxy_ssl.SetNextProto(SSLClientSocket::kProtoSPDY3);
session_deps.socket_factory.AddSocketDataProvider(data.get());
session_deps.socket_factory.AddSSLSocketDataProvider(&proxy_ssl);
@@ -4715,7 +4716,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test,
data_reads, arraysize(data_reads),
data_writes, arraysize(data_writes)));
SSLSocketDataProvider proxy_ssl(ASYNC, OK); // SSL to the proxy
- proxy_ssl.SetNextProto(SSLClientSocket::kProtoSPDY21);
+ proxy_ssl.SetNextProto(SSLClientSocket::kProtoSPDY3);
session_deps.socket_factory.AddSocketDataProvider(data.get());
session_deps.socket_factory.AddSSLSocketDataProvider(&proxy_ssl);
@@ -4825,7 +4826,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test, BasicAuthSpdyProxy) {
session_deps.socket_factory.AddSocketDataProvider(spdy_data.get());
// Negotiate SPDY to the proxy
SSLSocketDataProvider proxy(ASYNC, OK);
- proxy.SetNextProto(SSLClientSocket::kProtoSPDY21);
+ proxy.SetNextProto(SSLClientSocket::kProtoSPDY3);
session_deps.socket_factory.AddSSLSocketDataProvider(&proxy);
// Vanilla SSL to the server
SSLSocketDataProvider server(ASYNC, OK);
@@ -5625,7 +5626,7 @@ scoped_refptr<HttpNetworkSession> SetupSessionForGroupNameTests(
session->http_server_properties();
http_server_properties->SetAlternateProtocol(
HostPortPair("host.with.alternate", 80), 443,
- NPN_SPDY_21);
+ NPN_SPDY_3);
return session;
}
@@ -6572,7 +6573,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test, HonorAlternateProtocolHeader) {
http_server_properties.GetAlternateProtocol(http_host_port_pair);
PortAlternateProtocolPair expected_alternate;
expected_alternate.port = 443;
- expected_alternate.protocol = NPN_SPDY_21;
+ expected_alternate.protocol = NPN_SPDY_3;
EXPECT_TRUE(expected_alternate.Equals(alternate));
HttpStreamFactory::set_use_alternate_protocols(false);
@@ -6612,7 +6613,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test,
http_server_properties->SetAlternateProtocol(
HostPortPair::FromURL(request.url),
666 /* port is ignored by MockConnect anyway */,
- NPN_SPDY_21);
+ NPN_SPDY_3);
scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
TestCompletionCallback callback;
@@ -6675,7 +6676,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test,
http_server_properties->SetAlternateProtocol(
HostPortPair::FromURL(restricted_port_request.url),
kUnrestrictedAlternatePort,
- NPN_SPDY_21);
+ NPN_SPDY_3);
scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
TestCompletionCallback callback;
@@ -6725,7 +6726,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test,
http_server_properties->SetAlternateProtocol(
HostPortPair::FromURL(restricted_port_request.url),
kRestrictedAlternatePort,
- NPN_SPDY_21);
+ NPN_SPDY_3);
scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
TestCompletionCallback callback;
@@ -6775,7 +6776,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test,
http_server_properties->SetAlternateProtocol(
HostPortPair::FromURL(unrestricted_port_request.url),
kRestrictedAlternatePort,
- NPN_SPDY_21);
+ NPN_SPDY_3);
scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
TestCompletionCallback callback;
@@ -6825,7 +6826,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test,
http_server_properties->SetAlternateProtocol(
HostPortPair::FromURL(unrestricted_port_request.url),
kUnrestrictedAlternatePort,
- NPN_SPDY_21);
+ NPN_SPDY_3);
scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
TestCompletionCallback callback;
@@ -6870,7 +6871,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test, AlternateProtocolUnsafeBlocked) {
http_server_properties->SetAlternateProtocol(
HostPortPair::FromURL(request.url),
kUnsafePort,
- NPN_SPDY_2);
+ NPN_SPDY_3);
scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
TestCompletionCallback callback;
@@ -6915,7 +6916,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test, UseAlternateProtocolForNpnSpdy) {
session_deps.socket_factory.AddSocketDataProvider(&first_transaction);
SSLSocketDataProvider ssl(ASYNC, OK);
- ssl.SetNextProto(SSLClientSocket::kProtoSPDY21);
+ ssl.SetNextProto(SSLClientSocket::kProtoSPDY3);
session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
@@ -7014,7 +7015,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test, AlternateProtocolWithSpdyLateBinding) {
session_deps.socket_factory.AddSocketDataProvider(&hanging_socket);
SSLSocketDataProvider ssl(ASYNC, OK);
- ssl.SetNextProto(SSLClientSocket::kProtoSPDY21);
+ ssl.SetNextProto(SSLClientSocket::kProtoSPDY3);
session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
scoped_ptr<spdy::SpdyFrame> req1(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
@@ -7120,7 +7121,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test, StallAlternateProtocolForNpnSpdy) {
session_deps.socket_factory.AddSocketDataProvider(&first_transaction);
SSLSocketDataProvider ssl(ASYNC, OK);
- ssl.SetNextProto(SSLClientSocket::kProtoSPDY21);
+ ssl.SetNextProto(SSLClientSocket::kProtoSPDY3);
session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
MockConnect never_finishing_connect(SYNCHRONOUS, ERR_IO_PENDING);
@@ -7253,7 +7254,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test,
session_deps.socket_factory.AddSocketDataProvider(&first_transaction);
SSLSocketDataProvider ssl(ASYNC, OK);
- ssl.SetNextProto(SSLClientSocket::kProtoSPDY21);
+ ssl.SetNextProto(SSLClientSocket::kProtoSPDY3);
session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
@@ -7357,7 +7358,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test,
session_deps.socket_factory.AddSocketDataProvider(&first_transaction);
SSLSocketDataProvider ssl(ASYNC, OK);
- ssl.SetNextProto(SSLClientSocket::kProtoSPDY21);
+ ssl.SetNextProto(SSLClientSocket::kProtoSPDY3);
session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
// Make sure we use ssl for spdy here.
SpdySession::SetSSLMode(true);
@@ -8241,7 +8242,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test, SpdyPostNPNServerHangup) {
request.load_flags = 0;
SSLSocketDataProvider ssl(ASYNC, OK);
- ssl.SetNextProto(SSLClientSocket::kProtoSPDY21);
+ ssl.SetNextProto(SSLClientSocket::kProtoSPDY3);
session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
@@ -8277,7 +8278,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test, SpdyAlternateProtocolThroughProxy) {
HttpStreamFactory::set_use_alternate_protocols(true);
HttpStreamFactory::SetNextProtos(
MakeNextProtos(
- "http/1.1", "http1.1", "spdy/2.1", "spdy/2", "spdy", NULL));
+ "http/1.1", "http1.1", "spdy/2", "spdy/2.1", "spdy/3", "spdy", NULL));
SessionDependencies session_deps(ProxyService::CreateFixed("myproxy:70"));
HttpAuthHandlerMock::Factory* auth_factory =
@@ -8302,7 +8303,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test, SpdyAlternateProtocolThroughProxy) {
MockRead data_reads_1[] = {
MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
MockRead("HTTP/1.1 200 OK\r\n"
- "Alternate-Protocol: 443:npn-spdy/2.1\r\n"
+ "Alternate-Protocol: 443:npn-spdy/3\r\n"
"Proxy-Connection: close\r\n"
"\r\n"),
};
@@ -8372,7 +8373,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test, SpdyAlternateProtocolThroughProxy) {
data_writes_2, arraysize(data_writes_2)));
SSLSocketDataProvider ssl(ASYNC, OK);
- ssl.SetNextProto(SSLClientSocket::kProtoSPDY21);
+ ssl.SetNextProto(SSLClientSocket::kProtoSPDY3);
MockConnect never_finishing_connect(SYNCHRONOUS, ERR_IO_PENDING);
StaticSocketDataProvider hanging_non_alternate_protocol_socket(
@@ -8653,7 +8654,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test, PreconnectWithExistingSpdySession) {
session_deps.socket_factory.AddSocketDataProvider(spdy_data.get());
SSLSocketDataProvider ssl(ASYNC, OK);
- ssl.SetNextProto(SSLClientSocket::kProtoSPDY21);
+ ssl.SetNextProto(SSLClientSocket::kProtoSPDY3);
session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
@@ -9085,7 +9086,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test, UseIPConnectionPooling) {
pool_peer.DisableDomainAuthenticationVerification();
SSLSocketDataProvider ssl(ASYNC, OK);
- ssl.SetNextProto(SSLClientSocket::kProtoSPDY21);
+ ssl.SetNextProto(SSLClientSocket::kProtoSPDY3);
session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
scoped_ptr<spdy::SpdyFrame> host1_req(ConstructSpdyGet(
@@ -9236,7 +9237,7 @@ TEST_F(HttpNetworkTransactionSpdy3Test,
pool_peer.DisableDomainAuthenticationVerification();
SSLSocketDataProvider ssl(ASYNC, OK);
- ssl.SetNextProto(SSLClientSocket::kProtoSPDY21);
+ ssl.SetNextProto(SSLClientSocket::kProtoSPDY3);
session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
scoped_ptr<spdy::SpdyFrame> host1_req(ConstructSpdyGet(
diff --git a/net/http/http_proxy_client_socket_pool_spdy3_unittest.cc b/net/http/http_proxy_client_socket_pool_spdy3_unittest.cc
index 6aa2779..c5c3b1a 100644
--- a/net/http/http_proxy_client_socket_pool_spdy3_unittest.cc
+++ b/net/http/http_proxy_client_socket_pool_spdy3_unittest.cc
@@ -175,7 +175,7 @@ class HttpProxyClientSocketPoolSpdy3Test : public TestWithHttpParam {
void InitializeSpdySsl() {
spdy::SpdyFramer::set_enable_compression_default(false);
- ssl_data_->SetNextProto(SSLClientSocket::kProtoSPDY21);
+ ssl_data_->SetNextProto(SSLClientSocket::kProtoSPDY3);
}
HttpNetworkSession* CreateNetworkSession() {
diff --git a/net/http/http_server_properties.cc b/net/http/http_server_properties.cc
index b685900..5dfcebc 100644
--- a/net/http/http_server_properties.cc
+++ b/net/http/http_server_properties.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -14,6 +14,7 @@ const char* const kAlternateProtocolStrings[] = {
"npn-spdy/1",
"npn-spdy/2",
"npn-spdy/2.1",
+ "npn-spdy/3",
};
static const char* AlternateProtocolToString(AlternateProtocol protocol) {
@@ -21,6 +22,7 @@ static const char* AlternateProtocolToString(AlternateProtocol protocol) {
case NPN_SPDY_1:
case NPN_SPDY_2:
case NPN_SPDY_21:
+ case NPN_SPDY_3:
return kAlternateProtocolStrings[protocol];
case ALTERNATE_PROTOCOL_BROKEN:
return "Broken";
diff --git a/net/http/http_server_properties.h b/net/http/http_server_properties.h
index d9d60fd..3878768 100644
--- a/net/http/http_server_properties.h
+++ b/net/http/http_server_properties.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -19,6 +19,7 @@ enum AlternateProtocol {
NPN_SPDY_1 = 0,
NPN_SPDY_2,
NPN_SPDY_21,
+ NPN_SPDY_3,
NUM_ALTERNATE_PROTOCOLS,
ALTERNATE_PROTOCOL_BROKEN, // The alternate protocol is known to be broken.
UNINITIALIZED_ALTERNATE_PROTOCOL,
@@ -89,10 +90,16 @@ class NET_EXPORT HttpServerProperties {
const HostPortPair& host_port_pair) const = 0;
// Saves settings for a host. Returns true if SpdySettings are to be
- // persisted.
+ // persisted. Used by unittests only.
+ // TODO(rtenneti): Move this method to test utility file.
virtual bool SetSpdySettings(const HostPortPair& host_port_pair,
const spdy::SpdySettings& settings) = 0;
+ // Saves an individual setting for a host. Returns true if SpdySetting is to
+ // be persisted.
+ virtual bool SetSpdySetting(const HostPortPair& host_port_pair,
+ const spdy::SpdySetting& setting) = 0;
+
// Clears all spdy_settings.
virtual void ClearSpdySettings() = 0;
diff --git a/net/http/http_server_properties_impl.cc b/net/http/http_server_properties_impl.cc
index b8a7ed1..6601fe5 100644
--- a/net/http/http_server_properties_impl.cc
+++ b/net/http/http_server_properties_impl.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -241,8 +241,8 @@ bool HttpServerPropertiesImpl::SetSpdySettings(
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));
+ spdy::SettingsFlagsAndId new_id(spdy::SETTINGS_FLAG_PERSISTED, id.id());
+ persistent_settings.push_back(std::make_pair(new_id, it->second));
}
}
@@ -254,6 +254,27 @@ bool HttpServerPropertiesImpl::SetSpdySettings(
return true;
}
+bool HttpServerPropertiesImpl::SetSpdySetting(
+ const HostPortPair& host_port_pair,
+ const spdy::SpdySetting& setting) {
+
+ spdy::SettingsFlagsAndId id = setting.first;
+ if (!(id.flags() & spdy::SETTINGS_FLAG_PLEASE_PERSIST))
+ return false;
+
+ SpdySettingsMap::const_iterator it = spdy_settings_map_.find(host_port_pair);
+ spdy::SpdySettings persistent_settings;
+ if (it != spdy_settings_map_.end()) {
+ persistent_settings = it->second;
+ }
+
+ spdy::SettingsFlagsAndId new_id(spdy::SETTINGS_FLAG_PERSISTED, id.id());
+ persistent_settings.push_back(std::make_pair(new_id, setting.second));
+ spdy_settings_map_[host_port_pair] = persistent_settings;
+
+ return true;
+}
+
void HttpServerPropertiesImpl::ClearSpdySettings() {
spdy_settings_map_.clear();
}
diff --git a/net/http/http_server_properties_impl.h b/net/http/http_server_properties_impl.h
index 487e0fd..b4de808 100644
--- a/net/http/http_server_properties_impl.h
+++ b/net/http/http_server_properties_impl.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -113,6 +113,11 @@ class NET_EXPORT HttpServerPropertiesImpl
virtual bool SetSpdySettings(const HostPortPair& host_port_pair,
const spdy::SpdySettings& settings) OVERRIDE;
+ // Saves an individual setting for a host. Returns true if SpdySetting is to
+ // be persisted because |spdy_settings_map_| has been updated.
+ virtual bool SetSpdySetting(const HostPortPair& host_port_pair,
+ const spdy::SpdySetting& setting) OVERRIDE;
+
// Clears all spdy_settings.
virtual void ClearSpdySettings() OVERRIDE;
diff --git a/net/http/http_server_properties_impl_unittest.cc b/net/http/http_server_properties_impl_unittest.cc
index 724dea0..d58ba31 100644
--- a/net/http/http_server_properties_impl_unittest.cc
+++ b/net/http/http_server_properties_impl_unittest.cc
@@ -291,9 +291,7 @@ TEST_F(SpdySettingsServerPropertiesTest, Initialize) {
// Check by initializing with www.google.com:443 spdy server settings.
spdy::SpdySettings spdy_settings;
- spdy::SettingsFlagsAndId spdy_setting(0);
- spdy_setting.set_flags(spdy::SETTINGS_FLAG_PERSISTED);
- spdy_setting.set_id(1234);
+ spdy::SettingsFlagsAndId spdy_setting(spdy::SETTINGS_FLAG_PERSISTED, 1234);
spdy_settings.push_back(std::make_pair(spdy_setting, 31337));
spdy_settings_map[spdy_server_google] = spdy_settings;
impl_.InitializeSpdySettingsServers(&spdy_settings_map);
@@ -306,7 +304,7 @@ TEST_F(SpdySettingsServerPropertiesTest, Initialize) {
EXPECT_EQ(31337U, spdy_settings2.front().second);
}
-TEST_F(SpdySettingsServerPropertiesTest, SpdySettingsTest) {
+TEST_F(SpdySettingsServerPropertiesTest, SetSpdySettings) {
HostPortPair spdy_server_empty("", 443);
spdy::SpdySettings spdy_settings0 = impl_.GetSpdySettings(spdy_server_empty);
EXPECT_EQ(0U, spdy_settings0.size()); // Returns kEmptySpdySettings
@@ -314,9 +312,7 @@ TEST_F(SpdySettingsServerPropertiesTest, SpdySettingsTest) {
// Add www.google.com:443 as persisting.
HostPortPair spdy_server_google("www.google.com", 443);
spdy::SpdySettings spdy_settings1;
- spdy::SettingsFlagsAndId id1(0);
- id1.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
- id1.set_id(1234);
+ spdy::SettingsFlagsAndId id1(spdy::SETTINGS_FLAG_PLEASE_PERSIST, 1234);
spdy_settings1.push_back(std::make_pair(id1, 31337));
EXPECT_TRUE(impl_.SetSpdySettings(spdy_server_google, spdy_settings1));
spdy::SpdySettings spdy_settings1_ret =
@@ -331,8 +327,7 @@ TEST_F(SpdySettingsServerPropertiesTest, SpdySettingsTest) {
// Add mail.google.com:443 as not persisting.
HostPortPair spdy_server_mail("mail.google.com", 443);
spdy::SpdySettings spdy_settings2;
- spdy::SettingsFlagsAndId id2(0);
- id2.set_id(5678);
+ spdy::SettingsFlagsAndId id2(0, 5678);
spdy_settings2.push_back(std::make_pair(id2, 62667));
EXPECT_FALSE(impl_.SetSpdySettings(spdy_server_mail, spdy_settings2));
spdy::SpdySettings spdy_settings2_ret =
@@ -342,9 +337,7 @@ TEST_F(SpdySettingsServerPropertiesTest, SpdySettingsTest) {
// Add docs.google.com:443 as persisting
HostPortPair spdy_server_docs("docs.google.com", 443);
spdy::SpdySettings spdy_settings3;
- spdy::SettingsFlagsAndId id3(0);
- id3.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
- id3.set_id(9012);
+ spdy::SettingsFlagsAndId id3(spdy::SETTINGS_FLAG_PLEASE_PERSIST, 9012);
spdy_settings3.push_back(std::make_pair(id3, 93997));
EXPECT_TRUE(impl_.SetSpdySettings(spdy_server_docs, spdy_settings3));
spdy::SpdySettings spdy_settings3_ret =
@@ -357,13 +350,63 @@ TEST_F(SpdySettingsServerPropertiesTest, SpdySettingsTest) {
EXPECT_EQ(93997U, spdy_setting3_ret.second);
}
+TEST_F(SpdySettingsServerPropertiesTest, SetSpdySetting) {
+ HostPortPair spdy_server_empty("", 443);
+ spdy::SpdySettings spdy_settings0 = impl_.GetSpdySettings(spdy_server_empty);
+ EXPECT_EQ(0U, spdy_settings0.size()); // Returns kEmptySpdySettings
+
+ // Add www.google.com:443 as persisting.
+ HostPortPair spdy_server_google("www.google.com", 443);
+ spdy::SettingsFlagsAndId id1(spdy::SETTINGS_FLAG_PLEASE_PERSIST, 1234);
+ EXPECT_TRUE(impl_.SetSpdySetting(
+ spdy_server_google, std::make_pair(id1, 31337)));
+ spdy::SpdySettings spdy_settings1_ret =
+ impl_.GetSpdySettings(spdy_server_google);
+ ASSERT_EQ(1U, spdy_settings1_ret.size());
+ spdy::SpdySetting spdy_setting1_ret = spdy_settings1_ret.front();
+ spdy::SettingsFlagsAndId id1_ret(spdy_setting1_ret.first);
+ EXPECT_EQ(1234U, id1_ret.id());
+ EXPECT_EQ(spdy::SETTINGS_FLAG_PERSISTED, id1_ret.flags());
+ EXPECT_EQ(31337U, spdy_setting1_ret.second);
+
+ // Add mail.google.com:443 as not persisting.
+ HostPortPair spdy_server_mail("mail.google.com", 443);
+ spdy::SettingsFlagsAndId id2(0, 5678);
+ EXPECT_FALSE(impl_.SetSpdySetting(
+ spdy_server_mail, std::make_pair(id2, 62667)));
+ spdy::SpdySettings spdy_settings2_ret =
+ impl_.GetSpdySettings(spdy_server_mail);
+ EXPECT_EQ(0U, spdy_settings2_ret.size()); // Returns kEmptySpdySettings
+
+ // Add docs.google.com:443 as persisting
+ HostPortPair spdy_server_docs("docs.google.com", 443);
+ spdy::SettingsFlagsAndId id3(spdy::SETTINGS_FLAG_PLEASE_PERSIST, 9012);
+ EXPECT_TRUE(impl_.SetSpdySetting(
+ spdy_server_docs, std::make_pair(id3, 93997)));
+ spdy::SpdySettings spdy_settings3_ret =
+ impl_.GetSpdySettings(spdy_server_docs);
+ ASSERT_EQ(1U, spdy_settings3_ret.size());
+ spdy::SpdySetting spdy_setting3_ret = spdy_settings3_ret.front();
+ spdy::SettingsFlagsAndId id3_ret(spdy_setting3_ret.first);
+ EXPECT_EQ(9012U, id3_ret.id());
+ EXPECT_EQ(spdy::SETTINGS_FLAG_PERSISTED, id3_ret.flags());
+ EXPECT_EQ(93997U, spdy_setting3_ret.second);
+ // Check data for www.google.com:443.
+ spdy::SpdySettings spdy_settings4_ret =
+ impl_.GetSpdySettings(spdy_server_google);
+ ASSERT_EQ(1U, spdy_settings4_ret.size());
+ spdy::SpdySetting spdy_setting4_ret = spdy_settings4_ret.front();
+ spdy::SettingsFlagsAndId id4_ret(spdy_setting4_ret.first);
+ EXPECT_EQ(1234U, id4_ret.id());
+ EXPECT_EQ(spdy::SETTINGS_FLAG_PERSISTED, id4_ret.flags());
+ EXPECT_EQ(31337U, spdy_setting4_ret.second);
+}
+
TEST_F(SpdySettingsServerPropertiesTest, Clear) {
// Add www.google.com:443 as persisting.
HostPortPair spdy_server_google("www.google.com", 443);
spdy::SpdySettings spdy_settings1;
- spdy::SettingsFlagsAndId id1(0);
- id1.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
- id1.set_id(1234);
+ spdy::SettingsFlagsAndId id1(spdy::SETTINGS_FLAG_PLEASE_PERSIST, 1234);
spdy_settings1.push_back(std::make_pair(id1, 31337));
EXPECT_TRUE(impl_.SetSpdySettings(spdy_server_google, spdy_settings1));
spdy::SpdySettings spdy_settings1_ret =
@@ -378,9 +421,7 @@ TEST_F(SpdySettingsServerPropertiesTest, Clear) {
// Add docs.google.com:443 as persisting
HostPortPair spdy_server_docs("docs.google.com", 443);
spdy::SpdySettings spdy_settings3;
- spdy::SettingsFlagsAndId id3(0);
- id3.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
- id3.set_id(9012);
+ spdy::SettingsFlagsAndId id3(spdy::SETTINGS_FLAG_PLEASE_PERSIST, 9012);
spdy_settings3.push_back(std::make_pair(id3, 93997));
EXPECT_TRUE(impl_.SetSpdySettings(spdy_server_docs, spdy_settings3));
spdy::SpdySettings spdy_settings3_ret =
diff --git a/net/http/http_stream_factory.cc b/net/http/http_stream_factory.cc
index 6d0fc31..dda9951 100644
--- a/net/http/http_stream_factory.cc
+++ b/net/http/http_stream_factory.cc
@@ -167,6 +167,8 @@ void HttpStreamFactory::SetNextProtos(const std::vector<std::string>& value) {
enabled_protocols_[NPN_SPDY_2] = true;
} else if (value[i] == "spdy/2.1") {
enabled_protocols_[NPN_SPDY_21] = true;
+ } else if (value[i] == "spdy/3") {
+ enabled_protocols_[NPN_SPDY_3] = true;
}
}
enabled_protocols_[NPN_SPDY_1] = false;
diff --git a/net/net.gyp b/net/net.gyp
index 2260ae8..a850041 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -646,6 +646,8 @@
'spdy/spdy_credential_state.h',
'spdy/spdy_frame_builder.cc',
'spdy/spdy_frame_builder.h',
+ 'spdy/spdy_frame_reader.cc',
+ 'spdy/spdy_frame_reader.h',
'spdy/spdy_framer.cc',
'spdy/spdy_framer.h',
'spdy/spdy_http_stream.cc',
@@ -1193,13 +1195,16 @@
'spdy/buffered_spdy_framer_spdy3_unittest.cc',
'spdy/buffered_spdy_framer_spdy2_unittest.cc',
'spdy/spdy_credential_state_unittest.cc',
- 'spdy/spdy_framer_test.cc',
+ 'spdy/spdy_frame_reader_test.cc',
+ 'spdy/spdy_framer_spdy3_test.cc',
+ 'spdy/spdy_framer_spdy2_test.cc',
'spdy/spdy_http_stream_spdy3_unittest.cc',
'spdy/spdy_http_stream_spdy2_unittest.cc',
'spdy/spdy_network_transaction_spdy3_unittest.cc',
'spdy/spdy_network_transaction_spdy21_unittest.cc',
'spdy/spdy_network_transaction_spdy2_unittest.cc',
- 'spdy/spdy_protocol_test.cc',
+ 'spdy/spdy_protocol_spdy3_test.cc',
+ 'spdy/spdy_protocol_spdy2_test.cc',
'spdy/spdy_proxy_client_socket_spdy3_unittest.cc',
'spdy/spdy_proxy_client_socket_spdy2_unittest.cc',
'spdy/spdy_session_spdy3_unittest.cc',
diff --git a/net/socket/ssl_client_socket.cc b/net/socket/ssl_client_socket.cc
index 3d881fb..ecee79b 100644
--- a/net/socket/ssl_client_socket.cc
+++ b/net/socket/ssl_client_socket.cc
@@ -25,6 +25,8 @@ SSLClientSocket::NextProto SSLClientSocket::NextProtoFromString(
return kProtoSPDY2;
} else if (proto_string == "spdy/2.1") {
return kProtoSPDY21;
+ } else if (proto_string == "spdy/3") {
+ return kProtoSPDY3;
} else {
return kProtoUnknown;
}
@@ -41,6 +43,8 @@ const char* SSLClientSocket::NextProtoToString(
return "spdy/2";
case kProtoSPDY21:
return "spdy/2.1";
+ case kProtoSPDY3:
+ return "spdy/3";
default:
break;
}
diff --git a/net/socket/ssl_client_socket_pool.cc b/net/socket/ssl_client_socket_pool.cc
index 53f7f97..0c96546 100644
--- a/net/socket/ssl_client_socket_pool.cc
+++ b/net/socket/ssl_client_socket_pool.cc
@@ -303,7 +303,8 @@ int SSLConnectJob::DoSSLConnectComplete(int result) {
// TODO(mbelshe): verify it was a protocol we advertised?
if (protocol_negotiated == SSLClientSocket::kProtoSPDY1 ||
protocol_negotiated == SSLClientSocket::kProtoSPDY2 ||
- protocol_negotiated == SSLClientSocket::kProtoSPDY21) {
+ protocol_negotiated == SSLClientSocket::kProtoSPDY21 ||
+ protocol_negotiated == SSLClientSocket::kProtoSPDY3) {
ssl_socket_->set_was_spdy_negotiated(true);
}
}
diff --git a/net/spdy/buffered_spdy_framer.cc b/net/spdy/buffered_spdy_framer.cc
index a07cb61..909c27f 100644
--- a/net/spdy/buffered_spdy_framer.cc
+++ b/net/spdy/buffered_spdy_framer.cc
@@ -9,7 +9,7 @@
namespace spdy {
BufferedSpdyFramer::BufferedSpdyFramer(int version)
- : spdy_framer_(),
+ : spdy_framer_(version),
visitor_(NULL),
header_buffer_used_(0),
header_buffer_valid_(false),
@@ -49,8 +49,6 @@ void BufferedSpdyFramer::OnControl(const SpdyControlFrame* frame) {
*reinterpret_cast<const spdy::SpdyPingControlFrame*>(frame));
break;
case SETTINGS:
- visitor_->OnSettings(
- *reinterpret_cast<const spdy::SpdySettingsControlFrame*>(frame));
break;
case RST_STREAM:
visitor_->OnRstStream(
@@ -136,6 +134,12 @@ void BufferedSpdyFramer::OnStreamFrameData(SpdyStreamId stream_id,
visitor_->OnStreamFrameData(stream_id, data, len);
}
+void BufferedSpdyFramer::OnSetting(SpdySettingsIds id,
+ uint8 flags,
+ uint32 value) {
+ visitor_->OnSetting(id, flags, value);
+}
+
int BufferedSpdyFramer::protocol_version() {
return spdy_framer_.protocol_version();
}
@@ -164,11 +168,6 @@ bool BufferedSpdyFramer::HasError() {
return spdy_framer_.HasError();
}
-bool BufferedSpdyFramer::ParseHeaderBlock(const SpdyFrame* frame,
- SpdyHeaderBlock* block) {
- return spdy_framer_.ParseHeaderBlock(frame, block);
-}
-
SpdySynStreamControlFrame* BufferedSpdyFramer::CreateSynStream(
SpdyStreamId stream_id,
SpdyStreamId associated_stream_id,
@@ -235,6 +234,10 @@ SpdyDataFrame* BufferedSpdyFramer::CreateDataFrame(SpdyStreamId stream_id,
return spdy_framer_.CreateDataFrame(stream_id, data, len, flags);
}
+SpdyPriority BufferedSpdyFramer::GetHighestPriority() const {
+ return spdy_framer_.GetHighestPriority();
+}
+
SpdyFrame* BufferedSpdyFramer::CompressFrame(const SpdyFrame& frame) {
return spdy_framer_.CompressFrame(frame);
}
diff --git a/net/spdy/buffered_spdy_framer.h b/net/spdy/buffered_spdy_framer.h
index c784764..70859bf 100644
--- a/net/spdy/buffered_spdy_framer.h
+++ b/net/spdy/buffered_spdy_framer.h
@@ -51,9 +51,6 @@ class NET_EXPORT_PRIVATE BufferedSpdyFramerVisitorInterface {
// Called after a PING frame is received.
virtual void OnPing(const spdy::SpdyPingControlFrame& frame) = 0;
- // Called after a SETTINGS frame is received.
- virtual void OnSettings(const spdy::SpdySettingsControlFrame& frame) = 0;
-
// Called after a WINDOW_UPDATE frame is received.
virtual void OnWindowUpdate(
const spdy::SpdyWindowUpdateControlFrame& frame) = 0;
@@ -68,6 +65,10 @@ class NET_EXPORT_PRIVATE BufferedSpdyFramerVisitorInterface {
const char* data,
size_t len) = 0;
+ // Called when an individual setting within a SETTINGS frame has been parsed
+ // and validated.
+ virtual void OnSetting(SpdySettingsIds id, uint8 flags, uint32 value) = 0;
+
private:
DISALLOW_COPY_AND_ASSIGN(BufferedSpdyFramerVisitorInterface);
};
@@ -95,6 +96,8 @@ class NET_EXPORT_PRIVATE BufferedSpdyFramer
virtual void OnStreamFrameData(SpdyStreamId stream_id,
const char* data,
size_t len) OVERRIDE;
+ virtual void OnSetting(
+ SpdySettingsIds id, uint8 flags, uint32 value) OVERRIDE;
virtual void OnDataFrameHeader(const SpdyDataFrame* frame) OVERRIDE;
// SpdyFramer methods.
@@ -105,7 +108,6 @@ class NET_EXPORT_PRIVATE BufferedSpdyFramer
SpdyFramer::SpdyState state() const;
bool MessageFullyRead();
bool HasError();
- bool ParseHeaderBlock(const SpdyFrame* frame, SpdyHeaderBlock* block);
SpdySynStreamControlFrame* CreateSynStream(SpdyStreamId stream_id,
SpdyStreamId associated_stream_id,
int priority,
@@ -135,6 +137,7 @@ class NET_EXPORT_PRIVATE BufferedSpdyFramer
const char* data,
uint32 len,
SpdyDataFlags flags);
+ SpdyPriority GetHighestPriority() const;
SpdyFrame* CompressFrame(const SpdyFrame& frame);
bool IsCompressible(const SpdyFrame& frame) const;
diff --git a/net/spdy/buffered_spdy_framer_spdy2_unittest.cc b/net/spdy/buffered_spdy_framer_spdy2_unittest.cc
index e267603..b001fba 100644
--- a/net/spdy/buffered_spdy_framer_spdy2_unittest.cc
+++ b/net/spdy/buffered_spdy_framer_spdy2_unittest.cc
@@ -18,6 +18,7 @@ class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface {
TestBufferedSpdyVisitor()
: buffered_spdy_framer_(2),
error_count_(0),
+ setting_count_(0),
syn_frame_count_(0),
syn_reply_frame_count_(0),
headers_frame_count_(0),
@@ -66,6 +67,10 @@ class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface {
LOG(FATAL) << "Unexpected OnStreamFrameData call.";
}
+ void OnSetting(SpdySettingsIds id, uint8 flags, uint32 value) {
+ setting_count_++;
+ }
+
bool OnCredentialFrameData(const char*, size_t) {
LOG(FATAL) << "Unexpected OnCredentialFrameData call.";
return false;
@@ -93,7 +98,6 @@ class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface {
void OnRstStream(const spdy::SpdyRstStreamControlFrame& frame) {}
void OnGoAway(const spdy::SpdyGoAwayControlFrame& frame) {}
void OnPing(const spdy::SpdyPingControlFrame& frame) {}
- void OnSettings(const spdy::SpdySettingsControlFrame& frame) {}
void OnWindowUpdate(const spdy::SpdyWindowUpdateControlFrame& frame) {}
void OnCredential(const spdy::SpdyCredentialControlFrame& frame) {}
@@ -123,6 +127,7 @@ class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface {
// Counters from the visitor callbacks.
int error_count_;
+ int setting_count_;
int syn_frame_count_;
int syn_reply_frame_count_;
int headers_frame_count_;
@@ -170,6 +175,24 @@ class BufferedSpdyFramerSpdy2Test : public PlatformTest {
}
};
+TEST_F(BufferedSpdyFramerSpdy2Test, OnSetting) {
+ EnableCompression(false);
+
+ SpdyFramer framer(2);
+ SpdySettings settings;
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 1), 0x00000002));
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 1), 0x00000003));
+
+ scoped_ptr<SpdyFrame> control_frame(framer.CreateSettings(settings));
+ TestBufferedSpdyVisitor visitor;
+
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame->data()),
+ control_frame->length() + SpdyControlFrame::kHeaderSize);
+ EXPECT_EQ(1, visitor.error_count_);
+ EXPECT_EQ(1, visitor.setting_count_);
+}
+
TEST_F(BufferedSpdyFramerSpdy2Test, ReadSynStreamHeaderBlock) {
EnableCompression(false);
diff --git a/net/spdy/buffered_spdy_framer_spdy3_unittest.cc b/net/spdy/buffered_spdy_framer_spdy3_unittest.cc
index 19f07c3..e18c688 100644
--- a/net/spdy/buffered_spdy_framer_spdy3_unittest.cc
+++ b/net/spdy/buffered_spdy_framer_spdy3_unittest.cc
@@ -18,6 +18,7 @@ class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface {
TestBufferedSpdyVisitor()
: buffered_spdy_framer_(3),
error_count_(0),
+ setting_count_(0),
syn_frame_count_(0),
syn_reply_frame_count_(0),
headers_frame_count_(0),
@@ -66,6 +67,10 @@ class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface {
LOG(FATAL) << "Unexpected OnStreamFrameData call.";
}
+ void OnSetting(SpdySettingsIds id, uint8 flags, uint32 value) {
+ setting_count_++;
+ }
+
bool OnCredentialFrameData(const char*, size_t) {
LOG(FATAL) << "Unexpected OnCredentialFrameData call.";
return false;
@@ -93,7 +98,6 @@ class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface {
void OnRstStream(const spdy::SpdyRstStreamControlFrame& frame) {}
void OnGoAway(const spdy::SpdyGoAwayControlFrame& frame) {}
void OnPing(const spdy::SpdyPingControlFrame& frame) {}
- void OnSettings(const spdy::SpdySettingsControlFrame& frame) {}
void OnWindowUpdate(const spdy::SpdyWindowUpdateControlFrame& frame) {}
void OnCredential(const spdy::SpdyCredentialControlFrame& frame) {}
@@ -123,6 +127,7 @@ class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface {
// Counters from the visitor callbacks.
int error_count_;
+ int setting_count_;
int syn_frame_count_;
int syn_reply_frame_count_;
int headers_frame_count_;
@@ -170,6 +175,24 @@ class BufferedSpdyFramerSpdy3Test : public PlatformTest {
}
};
+TEST_F(BufferedSpdyFramerSpdy3Test, OnSetting) {
+ EnableCompression(false);
+
+ SpdyFramer framer(3);
+ SpdySettings settings;
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 1), 0x00000002));
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 1), 0x00000003));
+
+ scoped_ptr<SpdyFrame> control_frame(framer.CreateSettings(settings));
+ TestBufferedSpdyVisitor visitor;
+
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame->data()),
+ control_frame->length() + SpdyControlFrame::kHeaderSize);
+ EXPECT_EQ(1, visitor.error_count_);
+ EXPECT_EQ(1, visitor.setting_count_);
+}
+
TEST_F(BufferedSpdyFramerSpdy3Test, ReadSynStreamHeaderBlock) {
EnableCompression(false);
diff --git a/net/spdy/spdy_bitmasks.h b/net/spdy/spdy_bitmasks.h
index eb34ce6..1d1b42c 100644
--- a/net/spdy/spdy_bitmasks.h
+++ b/net/spdy/spdy_bitmasks.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -15,14 +15,12 @@ const unsigned int kStreamIdMask = 0x7fffffff;
const unsigned int kControlFlagMask = 0x8000;
// Priority mask from the SYN_FRAME
-const unsigned int kPriorityMask = 0xc0;
+const unsigned int kSpdy3PriorityMask = 0xe0;
+const unsigned int kSpdy2PriorityMask = 0xc0;
// Mask the lower 24 bits.
const unsigned int kLengthMask = 0xffffff;
-// Mask the Id from a SETTINGS id.
-const unsigned int kSettingsIdMask = 0xffffff;
-
// Legal flags on data packets.
const int kDataFlagsMask = 0x03;
diff --git a/net/spdy/spdy_frame_builder.cc b/net/spdy/spdy_frame_builder.cc
index fdb7686..e857d90 100644
--- a/net/spdy/spdy_frame_builder.cc
+++ b/net/spdy/spdy_frame_builder.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -12,14 +12,6 @@ namespace spdy {
// We mark a read only SpdyFrameBuilder with a special capacity_.
static const size_t kCapacityReadOnly = std::numeric_limits<size_t>::max();
-SpdyFrameBuilder::SpdyFrameBuilder()
- : buffer_(NULL),
- capacity_(0),
- length_(0),
- variable_buffer_offset_(0) {
- Resize(kInitialPayload);
-}
-
SpdyFrameBuilder::SpdyFrameBuilder(size_t size)
: buffer_(NULL),
capacity_(0),
@@ -28,121 +20,11 @@ SpdyFrameBuilder::SpdyFrameBuilder(size_t size)
Resize(size);
}
-SpdyFrameBuilder::SpdyFrameBuilder(const char* data, int data_len)
- : buffer_(const_cast<char*>(data)),
- capacity_(kCapacityReadOnly),
- length_(data_len),
- variable_buffer_offset_(0) {
-}
-
SpdyFrameBuilder::~SpdyFrameBuilder() {
- if (capacity_ != kCapacityReadOnly)
+ if (buffer_)
delete[] buffer_;
}
-bool SpdyFrameBuilder::ReadUInt16(void** iter, uint16* result) const {
- DCHECK(iter);
- if (!*iter)
- *iter = const_cast<char*>(buffer_);
-
- if (!IteratorHasRoomFor(*iter, sizeof(*result)))
- return false;
-
- *result = ntohs(*(reinterpret_cast<uint16*>(*iter)));
-
- UpdateIter(iter, sizeof(*result));
- return true;
-}
-
-bool SpdyFrameBuilder::ReadUInt32(void** iter, uint32* result) const {
- DCHECK(iter);
- if (!*iter)
- *iter = const_cast<char*>(buffer_);
-
- if (!IteratorHasRoomFor(*iter, sizeof(*result)))
- return false;
-
- *result = ntohl(*(reinterpret_cast<uint32*>(*iter)));
-
- UpdateIter(iter, sizeof(*result));
- return true;
-}
-
-bool SpdyFrameBuilder::ReadString(void** iter, std::string* result) const {
- DCHECK(iter);
-
- uint16 len;
- if (!ReadUInt16(iter, &len))
- return false;
-
- if (!IteratorHasRoomFor(*iter, len))
- return false;
-
- char* chars = reinterpret_cast<char*>(*iter);
- result->assign(chars, len);
-
- UpdateIter(iter, len);
- return true;
-}
-
-bool SpdyFrameBuilder::ReadBytes(void** iter, const char** data,
- uint32 length) const {
- DCHECK(iter);
- DCHECK(data);
-
- if (!IteratorHasRoomFor(*iter, length))
- return false;
-
- *data = reinterpret_cast<const char*>(*iter);
-
- UpdateIter(iter, length);
- return true;
-}
-
-bool SpdyFrameBuilder::ReadData(void** iter, const char** data,
- uint16* length) const {
- DCHECK(iter);
- DCHECK(data);
- DCHECK(length);
-
- if (!ReadUInt16(iter, length))
- return false;
-
- return ReadBytes(iter, data, *length);
-}
-
-bool SpdyFrameBuilder::ReadReadLen32PrefixedData(void** iter,
- const char** data,
- uint32* length) const {
- DCHECK(iter);
- DCHECK(data);
- DCHECK(length);
-
- if (!ReadUInt32(iter, length))
- return false;
-
- return ReadBytes(iter, data, *length);
-}
-
-char* SpdyFrameBuilder::BeginWriteData(uint16 length) {
- DCHECK_EQ(variable_buffer_offset_, 0U) <<
- "There can only be one variable buffer in a SpdyFrameBuilder";
-
- if (!WriteUInt16(length))
- return NULL;
-
- char *data_ptr = BeginWrite(length);
- if (!data_ptr)
- return NULL;
-
- variable_buffer_offset_ = data_ptr - buffer_ - sizeof(int);
-
- // EndWrite doesn't necessarily have to be called after the write operation,
- // so we call it here to pad out what the caller will eventually write.
- EndWrite(data_ptr, length);
- return data_ptr;
-}
-
char* SpdyFrameBuilder::BeginWrite(size_t length) {
size_t offset = length_;
size_t needed_size = length_ + length;
@@ -160,8 +42,6 @@ void SpdyFrameBuilder::EndWrite(char* dest, int length) {
}
bool SpdyFrameBuilder::WriteBytes(const void* data, uint32 data_len) {
- DCHECK(capacity_ != kCapacityReadOnly);
-
if (data_len > kLengthMask) {
return false;
}
@@ -187,10 +67,18 @@ bool SpdyFrameBuilder::WriteString(const std::string& value) {
return WriteBytes(value.data(), static_cast<uint16>(value.size()));
}
+bool SpdyFrameBuilder::WriteStringPiece32(const base::StringPiece& value) {
+ if (!WriteUInt32(value.size())) {
+ return false;
+ }
+
+ return WriteBytes(value.data(), value.size());
+}
+
// TODO(hkhalil) Remove Resize() entirely.
bool SpdyFrameBuilder::Resize(size_t new_capacity) {
DCHECK(new_capacity > 0);
- if (new_capacity <= capacity_)
+ if (new_capacity < capacity_)
return true;
char* p = new char[new_capacity];
@@ -198,6 +86,8 @@ bool SpdyFrameBuilder::Resize(size_t new_capacity) {
memcpy(p, buffer_, capacity_);
delete[] buffer_;
}
+ if (!p && new_capacity > 0)
+ return false;
buffer_ = p;
capacity_ = new_capacity;
return true;
diff --git a/net/spdy/spdy_frame_builder.h b/net/spdy/spdy_frame_builder.h
index 403e819..db5926d6 100644
--- a/net/spdy/spdy_frame_builder.h
+++ b/net/spdy/spdy_frame_builder.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -9,6 +9,7 @@
#include <string>
#include "base/basictypes.h"
+#include "base/string_piece.h"
#include "base/sys_byteorder.h"
#include "net/base/net_export.h"
#include "net/spdy/spdy_protocol.h"
@@ -22,10 +23,6 @@ namespace spdy {
// to a frame instance. The SpdyFrameBuilder grows its internal memory buffer
// dynamically to hold the sequence of primitive values. The internal memory
// buffer is exposed as the "data" of the SpdyFrameBuilder.
-//
-// When reading from a SpdyFrameBuilder the consumer must know what value types
-// to read and in what order to read them as the SpdyFrameBuilder does not keep
-// track of the type of data written to it.
class NET_EXPORT_PRIVATE SpdyFrameBuilder {
public:
~SpdyFrameBuilder();
@@ -36,12 +33,6 @@ class NET_EXPORT_PRIVATE SpdyFrameBuilder {
// The buffer will still be resized as necessary.
explicit SpdyFrameBuilder(size_t size);
- // Initializes a SpdyFrameBuilder from a const block of data. The data is
- // not copied; instead the data is merely referenced by this
- // SpdyFrameBuilder. Only const methods should be used when initialized
- // this way.
- SpdyFrameBuilder(const char* data, int data_len);
-
// Returns the size of the SpdyFrameBuilder's data.
int length() const { return length_; }
@@ -54,22 +45,8 @@ class NET_EXPORT_PRIVATE SpdyFrameBuilder {
return rv;
}
- // Methods for reading the payload of the SpdyFrameBuilder. To read from the
- // start of the SpdyFrameBuilder, initialize *iter to NULL. If successful,
- // these methods return true. Otherwise, false is returned to indicate that
- // the result could not be extracted.
- bool ReadUInt16(void** iter, uint16* result) const;
- bool ReadUInt32(void** iter, uint32* result) const;
- bool ReadString(void** iter, std::string* result) const;
- bool ReadBytes(void** iter, const char** data, uint32 length) const;
- bool ReadData(void** iter, const char** data, uint16* length) const;
- bool ReadReadLen32PrefixedData(void** iter,
- const char** data,
- uint32* length) const;
-
// Methods for adding to the payload. These values are appended to the end
- // of the SpdyFrameBuilder payload. When reading values, you must read them
- // in the order they were added. Note - binary integers are converted from
+ // of the SpdyFrameBuilder payload. Note - binary integers are converted from
// host to network form.
bool WriteUInt16(uint16 value) {
value = htons(value);
@@ -79,7 +56,9 @@ class NET_EXPORT_PRIVATE SpdyFrameBuilder {
value = htonl(value);
return WriteBytes(&value, sizeof(value));
}
+ // TODO(hkhalil) Rename to WriteStringPiece16().
bool WriteString(const std::string& value);
+ bool WriteStringPiece32(const base::StringPiece& value);
bool WriteBytes(const void* data, uint32 data_len);
// Write an integer to a particular offset in the data buffer.
@@ -97,16 +76,6 @@ class NET_EXPORT_PRIVATE SpdyFrameBuilder {
return true;
}
- // Allows the caller to write data directly into the SpdyFrameBuilder.
- // This saves a copy when the data is not already available in a buffer.
- // The caller must not write more than the length it declares it will.
- // Use ReadData to get the data.
- // Returns NULL on failure.
- //
- // The returned pointer will only be valid until the next write operation
- // on this SpdyFrameBuilder.
- char* BeginWriteData(uint16 length);
-
// Returns true if the given iterator could point to data with the given
// length. If there is no room for the given data before the end of the
// payload, returns false.
@@ -152,9 +121,6 @@ class NET_EXPORT_PRIVATE SpdyFrameBuilder {
*iter = static_cast<char*>(*iter) + bytes;
}
- // Initial size of the payload.
- static const int kInitialPayload = 1024;
-
private:
char* buffer_;
size_t capacity_; // Allocation size of payload (or -1 if buffer is const).
diff --git a/net/spdy/spdy_frame_reader.cc b/net/spdy/spdy_frame_reader.cc
new file mode 100644
index 0000000..183bae9
--- /dev/null
+++ b/net/spdy/spdy_frame_reader.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2012 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 <limits>
+
+#include "base/sys_byteorder.h"
+#include "net/spdy/spdy_frame_reader.h"
+
+namespace spdy {
+
+SpdyFrameReader::SpdyFrameReader(const char* data, const size_t len)
+ : data_(data),
+ len_(len),
+ ofs_(0) {
+}
+
+bool SpdyFrameReader::ReadUInt16(uint16* result) {
+ // Make sure that we have the whole uint16.
+ if (!CanRead(2)) {
+ OnFailure();
+ return false;
+ }
+
+ // Read into result.
+ *result = ntohs(*(reinterpret_cast<const uint16*>(data_ + ofs_)));
+
+ // Iterate.
+ ofs_ += 2;
+
+ return true;
+}
+
+bool SpdyFrameReader::ReadUInt32(uint32* result) {
+ // Make sure that we have the whole uint32.
+ if (!CanRead(4)) {
+ OnFailure();
+ return false;
+ }
+
+ // Read into result.
+ *result = ntohl(*(reinterpret_cast<const uint32*>(data_ + ofs_)));
+
+ // Iterate.
+ ofs_ += 4;
+
+ return true;
+}
+
+bool SpdyFrameReader::ReadStringPiece16(base::StringPiece* result) {
+ // Read resultant length.
+ uint16 result_len;
+ if (!ReadUInt16(&result_len)) {
+ // OnFailure() already called.
+ return false;
+ }
+
+ // Make sure that we have the whole string.
+ if (!CanRead(result_len)) {
+ OnFailure();
+ return false;
+ }
+
+ // Set result.
+ result->set(data_ + ofs_, result_len);
+
+ // Iterate.
+ ofs_ += result_len;
+
+ return true;
+}
+
+bool SpdyFrameReader::ReadStringPiece32(base::StringPiece* result) {
+ // Read resultant length.
+ uint32 result_len;
+ if (!ReadUInt32(&result_len)) {
+ // OnFailure() already called.
+ return false;
+ }
+
+ // Make sure that we have the whole string.
+ if (!CanRead(result_len)) {
+ OnFailure();
+ return false;
+ }
+
+ // Set result.
+ result->set(data_ + ofs_, result_len);
+
+ // Iterate.
+ ofs_ += result_len;
+
+ return true;
+}
+
+bool SpdyFrameReader::ReadBytes(void* result, size_t size) {
+ // Make sure that we have enough data to read.
+ if (!CanRead(size)) {
+ OnFailure();
+ return false;
+ }
+
+ // Read into result.
+ memcpy(result, data_ + ofs_, size);
+
+ // Iterate.
+ ofs_ += size;
+
+ return true;
+}
+
+bool SpdyFrameReader::IsDoneReading() const {
+ return len_ == ofs_;
+}
+
+bool SpdyFrameReader::CanRead(size_t bytes) const {
+ return bytes <= (len_ - ofs_);
+}
+
+void SpdyFrameReader::OnFailure() {
+ // Set our iterator to the end of the buffer so that further reads fail
+ // immediately.
+ ofs_ = len_;
+}
+
+} // namespace spdy
diff --git a/net/spdy/spdy_frame_reader.h b/net/spdy/spdy_frame_reader.h
new file mode 100644
index 0000000..72805a4
--- /dev/null
+++ b/net/spdy/spdy_frame_reader.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2012 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_FRAME_READER_H_
+#define NET_SPDY_FRAME_READER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/string_piece.h"
+#include "net/base/net_export.h"
+
+namespace spdy {
+
+// Used for reading SPDY frames. Though there isn't really anything terribly
+// SPDY-specific here, it's a helper class that's useful when doing SPDY
+// framing.
+//
+// To use, simply construct a SpdyFramerReader using the underlying buffer that
+// you'd like to read fields from, then call one of the Read*() methods to
+// actually do some reading.
+//
+// This class keeps an internal iterator to keep track of what's already been
+// read and each successive Read*() call automatically increments said iterator
+// on success. On failure, internal state of the SpdyFrameReader should not be
+// trusted and it is up to the caller to throw away the failed instance and
+// handle the error as appropriate. None of the Read*() methods should ever be
+// called after failure, as they will also fail immediately.
+class NET_EXPORT_PRIVATE SpdyFrameReader {
+ public:
+ // Caller must provide an underlying buffer to work on.
+ SpdyFrameReader(const char* data, const size_t len);
+
+ // Empty destructor.
+ ~SpdyFrameReader() {}
+
+ // Reads a 16-bit unsigned integer into the given output parameter.
+ // Forwards the internal iterater on success.
+ // Returns true on success, false otherwise.
+ bool ReadUInt16(uint16* result);
+
+ // Reads a 32-bit unsigned integer into the given output parameter.
+ // Forwards the internal iterater on success.
+ // Returns true on success, false otherwise.
+ bool ReadUInt32(uint32* result);
+
+ // Reads a string prefixed with 16-bit length into the given output parameter.
+ //
+ // NOTE: Does not copy but rather references strings in the underlying buffer.
+ // This should be kept in mind when handling memory management!
+ //
+ // Forwards the internal iterater on success.
+ // Returns true on success, false otherwise.
+ bool ReadStringPiece16(base::StringPiece* result);
+
+ // Reads a string prefixed with 32-bit length into the given output parameter.
+ //
+ // NOTE: Does not copy but rather references strings in the underlying buffer.
+ // This should be kept in mind when handling memory management!
+ //
+ // Forwards the internal iterater on success.
+ // Returns true on success, false otherwise.
+ bool ReadStringPiece32(base::StringPiece* result);
+
+ // Reads a given number of bytes into the given buffer. The buffer
+ // must be of adequate size.
+ // Forwards the internal iterater on success.
+ // Returns true on success, false otherwise.
+ bool ReadBytes(void* result, size_t size);
+
+ // Returns true if the entirety of the underlying buffer has been read via
+ // Read*() calls.
+ bool IsDoneReading() const;
+
+ private:
+ // Returns true if the underlying buffer has enough room to read the given
+ // amount of bytes.
+ bool CanRead(size_t bytes) const;
+
+ // To be called when a read fails for any reason.
+ void OnFailure();
+
+ // The data buffer that we're reading from.
+ const char* data_;
+
+ // The length of the data buffer that we're reading from.
+ const size_t len_;
+
+ // The location of the next read from our data buffer.
+ size_t ofs_;
+};
+
+} // namespace spdy
+
+#endif // NET_SPDY_FRAME_READER_H_
+
diff --git a/net/spdy/spdy_frame_reader_test.cc b/net/spdy/spdy_frame_reader_test.cc
new file mode 100644
index 0000000..aeaca94
--- /dev/null
+++ b/net/spdy/spdy_frame_reader_test.cc
@@ -0,0 +1,249 @@
+// Copyright (c) 2012 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 <algorithm>
+#include <iostream>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/sys_byteorder.h"
+#include "net/spdy/spdy_frame_reader.h"
+#include "testing/platform_test.h"
+
+namespace spdy {
+
+TEST(SpdyFrameReaderTest, ReadUInt16) {
+ // Frame data in network byte order.
+ const uint16 kFrameData[] = {
+ htons(1), htons(1<<15),
+ };
+
+ SpdyFrameReader frame_reader(reinterpret_cast<const char *>(kFrameData),
+ arraysize(kFrameData) * sizeof(uint16));
+ EXPECT_FALSE(frame_reader.IsDoneReading());
+
+ uint16 uint16_val;
+ EXPECT_TRUE(frame_reader.ReadUInt16(&uint16_val));
+ EXPECT_FALSE(frame_reader.IsDoneReading());
+ EXPECT_EQ(1u, uint16_val);
+
+ EXPECT_TRUE(frame_reader.ReadUInt16(&uint16_val));
+ EXPECT_TRUE(frame_reader.IsDoneReading());
+ EXPECT_EQ(1<<15, uint16_val);
+}
+
+TEST(SpdyFrameReaderTest, ReadUInt32) {
+ // Frame data in network byte order.
+ const uint32 kFrameData[] = {
+ htonl(1), htonl(1<<31),
+ };
+
+ SpdyFrameReader frame_reader(reinterpret_cast<const char *>(kFrameData),
+ arraysize(kFrameData) * sizeof(uint32));
+ EXPECT_FALSE(frame_reader.IsDoneReading());
+
+ uint32 uint32_val;
+ EXPECT_TRUE(frame_reader.ReadUInt32(&uint32_val));
+ EXPECT_FALSE(frame_reader.IsDoneReading());
+ EXPECT_EQ(1u, uint32_val);
+
+ EXPECT_TRUE(frame_reader.ReadUInt32(&uint32_val));
+ EXPECT_TRUE(frame_reader.IsDoneReading());
+ EXPECT_EQ(static_cast<uint32>(1<<31), uint32_val);
+}
+
+TEST(SpdyFrameReaderTest, ReadStringPiece16) {
+ // Frame data in network byte order.
+ const char kFrameData[] = {
+ 0x00, 0x02, // uint16(2)
+ 0x48, 0x69, // "Hi"
+ 0x00, 0x10, // uint16(16)
+ 0x54, 0x65, 0x73, 0x74,
+ 0x69, 0x6e, 0x67, 0x2c,
+ 0x20, 0x31, 0x2c, 0x20,
+ 0x32, 0x2c, 0x20, 0x33, // "Testing, 1, 2, 3"
+ };
+
+ SpdyFrameReader frame_reader(kFrameData, arraysize(kFrameData));
+ EXPECT_FALSE(frame_reader.IsDoneReading());
+
+ base::StringPiece stringpiece_val;
+ EXPECT_TRUE(frame_reader.ReadStringPiece16(&stringpiece_val));
+ EXPECT_FALSE(frame_reader.IsDoneReading());
+ EXPECT_EQ(0, stringpiece_val.compare("Hi"));
+
+ EXPECT_TRUE(frame_reader.ReadStringPiece16(&stringpiece_val));
+ EXPECT_TRUE(frame_reader.IsDoneReading());
+ EXPECT_EQ(0, stringpiece_val.compare("Testing, 1, 2, 3"));
+}
+
+TEST(SpdyFrameReaderTest, ReadStringPiece32) {
+ // Frame data in network byte order.
+ const char kFrameData[] = {
+ 0x00, 0x00, 0x00, 0x03, // uint32(3)
+ 0x66, 0x6f, 0x6f, // "foo"
+ 0x00, 0x00, 0x00, 0x10, // uint32(16)
+ 0x54, 0x65, 0x73, 0x74,
+ 0x69, 0x6e, 0x67, 0x2c,
+ 0x20, 0x34, 0x2c, 0x20,
+ 0x35, 0x2c, 0x20, 0x36, // "Testing, 4, 5, 6"
+ };
+
+ SpdyFrameReader frame_reader(kFrameData, arraysize(kFrameData));
+ EXPECT_FALSE(frame_reader.IsDoneReading());
+
+ base::StringPiece stringpiece_val;
+ EXPECT_TRUE(frame_reader.ReadStringPiece32(&stringpiece_val));
+ EXPECT_FALSE(frame_reader.IsDoneReading());
+ EXPECT_EQ(0, stringpiece_val.compare("foo"));
+
+ EXPECT_TRUE(frame_reader.ReadStringPiece32(&stringpiece_val));
+ EXPECT_TRUE(frame_reader.IsDoneReading());
+ EXPECT_EQ(0, stringpiece_val.compare("Testing, 4, 5, 6"));
+}
+
+TEST(SpdyFrameReaderTest, ReadUInt16WithBufferTooSmall) {
+ // Frame data in network byte order.
+ const char kFrameData[] = {
+ 0x00, // part of a uint16
+ };
+
+ SpdyFrameReader frame_reader(kFrameData, arraysize(kFrameData));
+ EXPECT_FALSE(frame_reader.IsDoneReading());
+
+ uint16 uint16_val;
+ EXPECT_FALSE(frame_reader.ReadUInt16(&uint16_val));
+}
+
+TEST(SpdyFrameReaderTest, ReadUInt32WithBufferTooSmall) {
+ // Frame data in network byte order.
+ const char kFrameData[] = {
+ 0x00, 0x00, 0x00, // part of a uint32
+ };
+
+ SpdyFrameReader frame_reader(kFrameData, arraysize(kFrameData));
+ EXPECT_FALSE(frame_reader.IsDoneReading());
+
+ uint32 uint32_val;
+ EXPECT_FALSE(frame_reader.ReadUInt32(&uint32_val));
+
+ // Also make sure that trying to read a uint16, which technically could work,
+ // fails immediately due to previously encountered failed read.
+ uint16 uint16_val;
+ EXPECT_FALSE(frame_reader.ReadUInt16(&uint16_val));
+}
+
+// Tests ReadStringPiece16() with a buffer too small to fit the entire string.
+TEST(SpdyFrameReaderTest, ReadStringPiece16WithBufferTooSmall) {
+ // Frame data in network byte order.
+ const char kFrameData[] = {
+ 0x00, 0x03, // uint16(3)
+ 0x48, 0x69, // "Hi"
+ };
+
+ SpdyFrameReader frame_reader(kFrameData, arraysize(kFrameData));
+ EXPECT_FALSE(frame_reader.IsDoneReading());
+
+ base::StringPiece stringpiece_val;
+ EXPECT_FALSE(frame_reader.ReadStringPiece16(&stringpiece_val));
+
+ // Also make sure that trying to read a uint16, which technically could work,
+ // fails immediately due to previously encountered failed read.
+ uint16 uint16_val;
+ EXPECT_FALSE(frame_reader.ReadUInt16(&uint16_val));
+}
+
+// Tests ReadStringPiece16() with a buffer too small even to fit the length.
+TEST(SpdyFrameReaderTest, ReadStringPiece16WithBufferWayTooSmall) {
+ // Frame data in network byte order.
+ const char kFrameData[] = {
+ 0x00, // part of a uint16
+ };
+
+ SpdyFrameReader frame_reader(kFrameData, arraysize(kFrameData));
+ EXPECT_FALSE(frame_reader.IsDoneReading());
+
+ base::StringPiece stringpiece_val;
+ EXPECT_FALSE(frame_reader.ReadStringPiece16(&stringpiece_val));
+
+ // Also make sure that trying to read a uint16, which technically could work,
+ // fails immediately due to previously encountered failed read.
+ uint16 uint16_val;
+ EXPECT_FALSE(frame_reader.ReadUInt16(&uint16_val));
+}
+
+// Tests ReadStringPiece32() with a buffer too small to fit the entire string.
+TEST(SpdyFrameReaderTest, ReadStringPiece32WithBufferTooSmall) {
+ // Frame data in network byte order.
+ const char kFrameData[] = {
+ 0x00, 0x00, 0x00, 0x03, // uint32(3)
+ 0x48, 0x69, // "Hi"
+ };
+
+ SpdyFrameReader frame_reader(kFrameData, arraysize(kFrameData));
+ EXPECT_FALSE(frame_reader.IsDoneReading());
+
+ base::StringPiece stringpiece_val;
+ EXPECT_FALSE(frame_reader.ReadStringPiece32(&stringpiece_val));
+
+ // Also make sure that trying to read a uint16, which technically could work,
+ // fails immediately due to previously encountered failed read.
+ uint16 uint16_val;
+ EXPECT_FALSE(frame_reader.ReadUInt16(&uint16_val));
+}
+
+// Tests ReadStringPiece32() with a buffer too small even to fit the length.
+TEST(SpdyFrameReaderTest, ReadStringPiece32WithBufferWayTooSmall) {
+ // Frame data in network byte order.
+ const char kFrameData[] = {
+ 0x00, 0x00, 0x00, // part of a uint32
+ };
+
+ SpdyFrameReader frame_reader(kFrameData, arraysize(kFrameData));
+ EXPECT_FALSE(frame_reader.IsDoneReading());
+
+ base::StringPiece stringpiece_val;
+ EXPECT_FALSE(frame_reader.ReadStringPiece32(&stringpiece_val));
+
+ // Also make sure that trying to read a uint16, which technically could work,
+ // fails immediately due to previously encountered failed read.
+ uint16 uint16_val;
+ EXPECT_FALSE(frame_reader.ReadUInt16(&uint16_val));
+}
+
+TEST(SpdyFrameReaderTest, ReadBytes) {
+ // Frame data in network byte order.
+ const char kFrameData[] = {
+ 0x66, 0x6f, 0x6f, // "foo"
+ 0x48, 0x69, // "Hi"
+ };
+
+ SpdyFrameReader frame_reader(kFrameData, arraysize(kFrameData));
+ EXPECT_FALSE(frame_reader.IsDoneReading());
+
+ char dest1[3] = {};
+ EXPECT_TRUE(frame_reader.ReadBytes(&dest1, arraysize(dest1)));
+ EXPECT_FALSE(frame_reader.IsDoneReading());
+ EXPECT_EQ("foo", base::StringPiece(dest1, arraysize(dest1)));
+
+ char dest2[2] = {};
+ EXPECT_TRUE(frame_reader.ReadBytes(&dest2, arraysize(dest2)));
+ EXPECT_TRUE(frame_reader.IsDoneReading());
+ EXPECT_EQ("Hi", base::StringPiece(dest2, arraysize(dest2)));
+}
+
+TEST(SpdyFrameReaderTest, ReadBytesWithBufferTooSmall) {
+ // Frame data in network byte order.
+ const char kFrameData[] = {
+ 0x01,
+ };
+
+ SpdyFrameReader frame_reader(kFrameData, arraysize(kFrameData));
+ EXPECT_FALSE(frame_reader.IsDoneReading());
+
+ char dest[arraysize(kFrameData) + 2] = {};
+ EXPECT_FALSE(frame_reader.ReadBytes(&dest, arraysize(kFrameData) + 1));
+ EXPECT_STREQ("", dest);
+}
+
+} // namespace
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc
index ff6268a..ccf7d26 100644
--- a/net/spdy/spdy_framer.cc
+++ b/net/spdy/spdy_framer.cc
@@ -12,6 +12,7 @@
#include "base/metrics/stats_counters.h"
#include "base/third_party/valgrind/memcheck.h"
#include "net/spdy/spdy_frame_builder.h"
+#include "net/spdy/spdy_frame_reader.h"
#include "net/spdy/spdy_bitmasks.h"
#if defined(USE_SYSTEM_ZLIB)
@@ -24,62 +25,21 @@ using std::vector;
namespace spdy {
-SpdyCredential::SpdyCredential() : slot(0) { }
-SpdyCredential::~SpdyCredential() { }
-
// Compute the id of our dictionary so that we know we're using the
// right one when asked for it.
-uLong CalculateDictionaryId() {
+uLong CalculateDictionaryId(const char* dictionary,
+ const size_t dictionary_size) {
uLong initial_value = adler32(0L, Z_NULL, 0);
return adler32(initial_value,
- reinterpret_cast<const Bytef*>(SpdyFramer::kDictionary),
- SpdyFramer::kDictionarySize);
+ reinterpret_cast<const Bytef*>(dictionary),
+ dictionary_size);
}
-// Adler ID for the SPDY header compressor dictionary.
-const uLong kDictionaryId = CalculateDictionaryId();
-
-int DecompressHeaderBlockInZStream(z_stream* decompressor) {
- int rv = inflate(decompressor, Z_SYNC_FLUSH);
- if (rv == Z_NEED_DICT) {
- // Need to try again with the right dictionary.
- if (decompressor->adler == kDictionaryId) {
- rv = inflateSetDictionary(decompressor,
- (const Bytef*)SpdyFramer::kDictionary,
- SpdyFramer::kDictionarySize);
- if (rv == Z_OK)
- rv = inflate(decompressor, Z_SYNC_FLUSH);
- }
- }
- return rv;
-}
-
-// Retrieve serialized length of SpdyHeaderBlock.
-size_t GetSerializedLength(const SpdyHeaderBlock* headers) {
- size_t total_length = SpdyControlFrame::kNumNameValuePairsSize;
- SpdyHeaderBlock::const_iterator it;
- for (it = headers->begin(); it != headers->end(); ++it) {
- // We add space for the length of the name and the length of the value as
- // well as the length of the name and the length of the value.
- total_length += SpdyControlFrame::kLengthOfNameSize +
- it->first.size() +
- SpdyControlFrame::kLengthOfValueSize +
- it->second.size();
- }
- return total_length;
-}
-
-// Serializes a SpdyHeaderBlock.
-void WriteHeaderBlock(SpdyFrameBuilder* frame, const SpdyHeaderBlock* headers) {
- frame->WriteUInt16(headers->size()); // Number of headers.
- SpdyHeaderBlock::const_iterator it;
- for (it = headers->begin(); it != headers->end(); ++it) {
- bool wrote_header;
- wrote_header = frame->WriteString(it->first);
- wrote_header &= frame->WriteString(it->second);
- DCHECK(wrote_header);
- }
-}
+// Adler ID for the SPDY header compressor dictionaries.
+const uLong kV2DictionaryId = CalculateDictionaryId(kV2Dictionary,
+ kV2DictionarySize);
+const uLong kV3DictionaryId = CalculateDictionaryId(kV3Dictionary,
+ kV3DictionarySize);
// Creates a FlagsAndLength.
FlagsAndLength CreateFlagsAndLength(SpdyControlFlags flags, size_t length) {
@@ -103,11 +63,19 @@ size_t SpdyFramer::kControlFrameBufferInitialSize = 32 * 1024;
// TODO(mbelshe): We should make this stream-based so there are no limits.
size_t SpdyFramer::kControlFrameBufferMaxSize = 64 * 1024;
-int SpdyFramer::spdy_version_ = kSpdyProtocolVersion;
+// The initial size of the control frame buffer when compression is disabled.
+// This exists because we don't do stream (de)compressed control frame data to
+// our visitor; we instead buffer the entirety of the control frame and then
+// decompress in one fell swoop.
+// Since this is only used for control frame headers, the maximum control
+// frame header size (18B) is sufficient; all remaining control frame data is
+// streamed to the visitor.
+size_t SpdyFramer::kUncompressedControlFrameBufferInitialSize = 18;
const SpdyStreamId SpdyFramer::kInvalidStream = -1;
const size_t SpdyFramer::kHeaderDataChunkMaxSize = 1024;
+
#ifdef DEBUG_SPDY_STATE_CHANGES
#define CHANGE_STATE(newstate) \
{ \
@@ -122,7 +90,42 @@ const size_t SpdyFramer::kHeaderDataChunkMaxSize = 1024;
#define CHANGE_STATE(newstate) (state_ = newstate)
#endif
-SpdyFramer::SpdyFramer()
+SettingsFlagsAndId SettingsFlagsAndId::FromWireFormat(int version,
+ uint32 wire) {
+ if (version < 3) {
+ ConvertFlagsAndIdForSpdy2(&wire);
+ }
+ return SettingsFlagsAndId(ntohl(wire) >> 24, ntohl(wire) & 0x00ffffff);
+}
+
+SettingsFlagsAndId::SettingsFlagsAndId(uint8 flags, uint32 id)
+ : flags_(flags), id_(id & 0x00ffffff) {
+ DCHECK_GT(static_cast<uint32>(1 << 24), id);
+}
+
+uint32 SettingsFlagsAndId::GetWireFormat(int version) const {
+ uint32 wire = htonl(id_ & 0x00ffffff) | htonl(flags_ << 24);
+ if (version < 3) {
+ ConvertFlagsAndIdForSpdy2(&wire);
+ }
+ return wire;
+}
+
+// SPDY 2 had a bug in it with respect to byte ordering of id/flags field.
+// This method is used to preserve buggy behavior and works on both
+// little-endian and big-endian hosts.
+// This method is also bidirectional (can be used to translate SPDY 2 to SPDY 3
+// as well as vice versa).
+void SettingsFlagsAndId::ConvertFlagsAndIdForSpdy2(uint32* val) {
+ uint8* wire_array = reinterpret_cast<uint8*>(val);
+ std::swap(wire_array[0], wire_array[3]);
+ std::swap(wire_array[1], wire_array[2]);
+}
+
+SpdyCredential::SpdyCredential() : slot(0) { }
+SpdyCredential::~SpdyCredential() { }
+
+SpdyFramer::SpdyFramer(int version)
: state_(SPDY_RESET),
error_code_(SPDY_NO_ERROR),
remaining_data_(0),
@@ -134,7 +137,12 @@ SpdyFramer::SpdyFramer()
validate_control_frame_sizes_(true),
enable_compression_(compression_default_),
visitor_(NULL),
- display_protocol_("SPDY") {
+ display_protocol_("SPDY"),
+ spdy_version_(version),
+ syn_frame_processed_(false),
+ probable_http_response_(false) {
+ DCHECK_GE(3, version);
+ DCHECK_LE(2, version);
}
SpdyFramer::~SpdyFramer() {
@@ -155,6 +163,7 @@ void SpdyFramer::Reset() {
remaining_control_payload_ = 0;
remaining_control_header_ = 0;
current_frame_len_ = 0;
+ settings_scratch_.Reset();
// TODO(hkhalil): Remove once initial_size == kControlFrameBufferInitialSize.
size_t initial_size = kControlFrameBufferInitialSize;
if (!enable_compression_) {
@@ -192,6 +201,8 @@ const char* SpdyFramer::StateToString(int state) {
return "SPDY_CONTROL_FRAME_HEADER_BLOCK";
case SPDY_CREDENTIAL_FRAME_PAYLOAD:
return "SPDY_CREDENTIAL_FRAME_PAYLOAD";
+ case SPDY_SETTINGS_FRAME_PAYLOAD:
+ return "SPDY_SETTINGS_FRAME_PAYLOAD";
}
return "UNKNOWN_STATE";
}
@@ -304,6 +315,10 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) {
// 1. SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK
// 2. SPDY_CONTROL_FRAME_HEADER_BLOCK
//
+ // SETTINGS frames take a slightly modified route:
+ // 1. SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK
+ // 2. SPDY_SETTINGS_FRAME_PAYLOAD
+ //
// All other control frames will use the alternate route directly to
// SPDY_CONTROL_FRAME_PAYLOAD
int bytes_read = ProcessControlFrameBeforeHeaderBlock(data, len);
@@ -312,6 +327,13 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) {
continue;
}
+ case SPDY_SETTINGS_FRAME_PAYLOAD: {
+ int bytes_read = ProcessSettingsFramePayload(data, len);
+ len -= bytes_read;
+ data += bytes_read;
+ continue;
+ }
+
case SPDY_CONTROL_FRAME_HEADER_BLOCK: {
int bytes_read = ProcessControlFrameHeaderBlock(data, len);
len -= bytes_read;
@@ -323,7 +345,6 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) {
size_t bytes_read = ProcessCredentialFramePayload(data, len);
len -= bytes_read;
data += bytes_read;
- continue;
}
case SPDY_CONTROL_FRAME_PAYLOAD: {
@@ -342,6 +363,8 @@ size_t SpdyFramer::ProcessInput(const char* data, size_t len) {
continue;
}
default:
+ LOG(ERROR) << "Invalid value for " << display_protocol_
+ << " framer state: " << state_;
// This ensures that we don't infinite-loop if state_ gets an
// invalid value somehow, such as due to a SpdyFramer getting deleted
// from a callback it calls.
@@ -383,8 +406,16 @@ size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) {
// This is just a sanity check for help debugging early frame errors.
if (remaining_data_ > 1000000u) {
- LOG(WARNING) << "Unexpectedly large frame. " << display_protocol_
- << " session is likely corrupt.";
+ // The strncmp for 5 is safe because we only hit this point if we
+ // have SpdyFrame::kHeaderSize (8) bytes
+ if (!syn_frame_processed_ &&
+ strncmp(current_frame_buffer_, "HTTP/", 5) == 0) {
+ LOG(WARNING) << "Unexpected HTTP response to spdy request";
+ probable_http_response_ = true;
+ } else {
+ LOG(WARNING) << "Unexpectedly large frame. " << display_protocol_
+ << " session is likely corrupt.";
+ }
}
// if we're here, then we have the common header all received.
@@ -407,6 +438,8 @@ void SpdyFramer::ProcessControlFrameHeader() {
// We check version before we check validity: version can never be 'invalid',
// it can only be unsupported.
if (current_control_frame.version() != spdy_version_) {
+ DLOG(INFO) << "Unsupported SPDY version " << current_control_frame.version()
+ << " (expected " << spdy_version_ << ")";
set_error(SPDY_UNSUPPORTED_VERSION);
return;
}
@@ -445,9 +478,15 @@ void SpdyFramer::ProcessControlFrameHeader() {
set_error(SPDY_INVALID_CONTROL_FRAME);
break;
case SETTINGS:
+ // Make sure that we have an integral number of 8-byte key/value pairs,
+ // plus a 4-byte length field.
if (current_control_frame.length() <
- SpdySettingsControlFrame::size() - SpdyControlFrame::kHeaderSize)
+ SpdySettingsControlFrame::size() - SpdyControlFrame::kHeaderSize ||
+ (current_control_frame.length() % 8 != 4)) {
+ DLOG(WARNING) << "Invalid length for SETTINGS frame: "
+ << current_control_frame.length();
set_error(SPDY_INVALID_CONTROL_FRAME);
+ }
break;
case GOAWAY:
if (current_control_frame.length() !=
@@ -485,12 +524,6 @@ void SpdyFramer::ProcessControlFrameHeader() {
}
}
- // We only support version 1 of this protocol.
- if (current_control_frame.version() != spdy_version_) {
- set_error(SPDY_UNSUPPORTED_VERSION);
- return;
- }
-
remaining_control_payload_ = current_control_frame.length();
if (remaining_control_payload_ >
kControlFrameBufferMaxSize - SpdyFrame::kHeaderSize) {
@@ -504,37 +537,54 @@ void SpdyFramer::ProcessControlFrameHeader() {
return;
}
- int32 frame_size_without_header_block;
+ // Determine the frame size without variable-length data.
+ int32 frame_size_without_variable_data;
switch (current_control_frame.type()) {
case SYN_STREAM:
- frame_size_without_header_block = SpdySynStreamControlFrame::size();
+ syn_frame_processed_ = true;
+ frame_size_without_variable_data = SpdySynStreamControlFrame::size();
break;
case SYN_REPLY:
- frame_size_without_header_block = SpdySynReplyControlFrame::size();
+ syn_frame_processed_ = true;
+ frame_size_without_variable_data = SpdySynReplyControlFrame::size();
+ // SPDY 2 had two bytes of unused space preceeding payload.
+ if (spdy_version_ < 3) {
+ frame_size_without_variable_data += 2;
+ }
break;
case HEADERS:
- frame_size_without_header_block = SpdyHeadersControlFrame::size();
+ frame_size_without_variable_data = SpdyHeadersControlFrame::size();
+ // SPDY 2 had two bytes of unused space preceeding payload.
+ if (spdy_version_ < 3) {
+ frame_size_without_variable_data += 2;
+ }
+ break;
+ case SETTINGS:
+ frame_size_without_variable_data = SpdySettingsControlFrame::size();
break;
default:
- frame_size_without_header_block = -1;
- LOG_IF(DFATAL, remaining_control_payload_ + SpdyFrame::kHeaderSize >
- current_frame_capacity_)
- << display_protocol_
- << " control frame buffer too small for fixed-length frame.";
- ExpandControlFrameBuffer(remaining_control_payload_);
+ frame_size_without_variable_data = -1;
break;
}
- if (frame_size_without_header_block > 0) {
+ if (frame_size_without_variable_data == -1) {
+ LOG_IF(ERROR, remaining_control_payload_ + SpdyFrame::kHeaderSize >
+ current_frame_capacity_)
+ << display_protocol_
+ << " control frame buffer too small for fixed-length frame.";
+ // TODO(hkhalil): Remove ExpandControlFrameBuffer().
+ ExpandControlFrameBuffer(remaining_control_payload_);
+ }
+ if (frame_size_without_variable_data > 0) {
// We have a control frame with a header block. We need to parse the
// remainder of the control frame's header before we can parse the header
// block. The start of the header block varies with the control type.
- DCHECK_GE(static_cast<uint32>(frame_size_without_header_block),
- current_frame_len_);
- remaining_control_header_ = frame_size_without_header_block -
+ DCHECK_GE(frame_size_without_variable_data,
+ static_cast<int32>(current_frame_len_));
+ remaining_control_header_ = frame_size_without_variable_data -
current_frame_len_;
remaining_control_payload_ += SpdyFrame::kHeaderSize -
- frame_size_without_header_block;
+ frame_size_without_variable_data;
CHANGE_STATE(SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK);
return;
}
@@ -553,6 +603,46 @@ size_t SpdyFramer::UpdateCurrentFrameBuffer(const char** data, size_t* len,
return bytes_to_read;
}
+size_t SpdyFramer::GetSerializedLength(const SpdyHeaderBlock* headers) const {
+ const size_t num_name_value_pairs_size
+ = (spdy_version_ < 3) ? sizeof(uint16) : sizeof(uint32);
+ const size_t length_of_name_size = num_name_value_pairs_size;
+ const size_t length_of_value_size = num_name_value_pairs_size;
+
+ size_t total_length = num_name_value_pairs_size;
+ for (SpdyHeaderBlock::const_iterator it = headers->begin();
+ it != headers->end();
+ ++it) {
+ // We add space for the length of the name and the length of the value as
+ // well as the length of the name and the length of the value.
+ total_length += length_of_name_size + it->first.size() +
+ length_of_value_size + it->second.size();
+ }
+ return total_length;
+}
+
+void SpdyFramer::WriteHeaderBlock(SpdyFrameBuilder* frame,
+ const SpdyHeaderBlock* headers) const {
+ if (spdy_version_ < 3) {
+ frame->WriteUInt16(headers->size()); // Number of headers.
+ } else {
+ frame->WriteUInt32(headers->size()); // Number of headers.
+ }
+ SpdyHeaderBlock::const_iterator it;
+ for (it = headers->begin(); it != headers->end(); ++it) {
+ bool wrote_header;
+ if (spdy_version_ < 3) {
+ wrote_header = frame->WriteString(it->first);
+ wrote_header &= frame->WriteString(it->second);
+ } else {
+ wrote_header = frame->WriteStringPiece32(it->first);
+ wrote_header &= frame->WriteStringPiece32(it->second);
+ }
+ DCHECK(wrote_header);
+ }
+}
+
+
size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data,
size_t len) {
DCHECK_EQ(SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK, state_);
@@ -567,10 +657,15 @@ size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data,
SpdyControlFrame control_frame(current_frame_buffer_, false);
DCHECK(control_frame.type() == SYN_STREAM ||
control_frame.type() == SYN_REPLY ||
- control_frame.type() == HEADERS);
+ control_frame.type() == HEADERS ||
+ control_frame.type() == SETTINGS);
visitor_->OnControl(&control_frame);
- CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK);
+ if (control_frame.type() == SETTINGS) {
+ CHANGE_STATE(SPDY_SETTINGS_FRAME_PAYLOAD);
+ } else {
+ CHANGE_STATE(SPDY_CONTROL_FRAME_HEADER_BLOCK);
+ }
}
}
return original_len - len;
@@ -625,6 +720,110 @@ size_t SpdyFramer::ProcessControlFrameHeaderBlock(const char* data,
return process_bytes;
}
+size_t SpdyFramer::ProcessSettingsFramePayload(const char* data,
+ size_t data_len) {
+ DCHECK_EQ(SPDY_SETTINGS_FRAME_PAYLOAD, state_);
+ SpdyControlFrame control_frame(current_frame_buffer_, false);
+ DCHECK_EQ(control_frame.type(), SETTINGS);
+ size_t unprocessed_bytes = std::min(data_len, remaining_control_payload_);
+ size_t processed_bytes = 0;
+ DCHECK_GT(unprocessed_bytes, 0u);
+
+ // Loop over our incoming data.
+ while (unprocessed_bytes > 0) {
+ // Process up to one setting at a time.
+ size_t processing = std::min(
+ unprocessed_bytes,
+ static_cast<size_t>(8 - settings_scratch_.setting_buf_len));
+
+ // Check if we have a complete setting in our input.
+ if (processing == 8) {
+ // Parse the setting directly out of the input without buffering.
+ if (!ProcessSetting(data + processed_bytes)) {
+ set_error(SPDY_INVALID_CONTROL_FRAME);
+ return processed_bytes;
+ }
+ } else {
+ // Continue updating settings_scratch_.setting_buf.
+ memcpy(settings_scratch_.setting_buf + settings_scratch_.setting_buf_len,
+ data + processed_bytes,
+ processing);
+ settings_scratch_.setting_buf_len += processing;
+
+ // Check if we have a complete setting buffered.
+ if (settings_scratch_.setting_buf_len == 8) {
+ if (!ProcessSetting(settings_scratch_.setting_buf)) {
+ set_error(SPDY_INVALID_CONTROL_FRAME);
+ return processed_bytes;
+ }
+ // Reset settings_scratch_.setting_buf for our next setting.
+ settings_scratch_.setting_buf_len = 0;
+ }
+ }
+
+ // Iterate.
+ unprocessed_bytes -= processing;
+ processed_bytes += processing;
+ }
+
+ // Check if we're done handling this SETTINGS frame.
+ remaining_control_payload_ -= processed_bytes;
+ if (remaining_control_payload_ == 0) {
+ CHANGE_STATE(SPDY_AUTO_RESET);
+ }
+
+ return processed_bytes;
+}
+
+bool SpdyFramer::ProcessSetting(const char* data) {
+ // Extract fields.
+ // Maintain behavior of old SPDY 2 bug with byte ordering of flags/id.
+ const uint32 id_and_flags_wire = *(reinterpret_cast<const uint32*>(data));
+ SettingsFlagsAndId id_and_flags =
+ SettingsFlagsAndId::FromWireFormat(spdy_version_, id_and_flags_wire);
+ uint8 flags = id_and_flags.flags();
+ uint32 value = ntohl(*(reinterpret_cast<const uint32*>(data + 4)));
+
+ // Validate id.
+ switch (id_and_flags.id()) {
+ case SETTINGS_UPLOAD_BANDWIDTH:
+ case SETTINGS_DOWNLOAD_BANDWIDTH:
+ case SETTINGS_ROUND_TRIP_TIME:
+ case SETTINGS_MAX_CONCURRENT_STREAMS:
+ case SETTINGS_CURRENT_CWND:
+ case SETTINGS_DOWNLOAD_RETRANS_RATE:
+ case SETTINGS_INITIAL_WINDOW_SIZE:
+ // Valid values.
+ break;
+ default:
+ DLOG(WARNING) << "Unknown SETTINGS ID: " << id_and_flags.id();
+ return false;
+ }
+ SpdySettingsIds id = static_cast<SpdySettingsIds>(id_and_flags.id());
+
+ // Detect duplciates.
+ if (static_cast<uint32>(id) <= settings_scratch_.last_setting_id) {
+ DLOG(WARNING) << "Duplicate entry or invalid ordering for id " << id
+ << " in " << display_protocol_ << " SETTINGS frame "
+ << "(last settikng id was "
+ << settings_scratch_.last_setting_id << ").";
+ return false;
+ }
+ settings_scratch_.last_setting_id = id;
+
+ // Validate flags.
+ uint8 kFlagsMask = SETTINGS_FLAG_PLEASE_PERSIST | SETTINGS_FLAG_PERSISTED;
+ if ((flags & ~(kFlagsMask)) != 0) {
+ DLOG(WARNING) << "Unknown SETTINGS flags provided for id " << id << ": "
+ << flags;
+ return false;
+ }
+
+ // Validation succeeded. Pass on to visitor.
+ visitor_->OnSetting(id, flags, value);
+ return true;
+}
+
size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) {
size_t original_len = len;
if (remaining_control_payload_) {
@@ -729,95 +928,62 @@ void SpdyFramer::ExpandControlFrameBuffer(size_t size) {
current_frame_buffer_ = new_buffer;
}
-/* static */
bool SpdyFramer::ParseHeaderBlockInBuffer(const char* header_data,
size_t header_length,
SpdyHeaderBlock* block) {
- SpdyFrameBuilder builder(header_data, header_length);
- void* iter = NULL;
- uint16 num_headers;
- if (builder.ReadUInt16(&iter, &num_headers)) {
- for (int index = 0; index < num_headers; ++index) {
- std::string name;
- std::string value;
- if (!builder.ReadString(&iter, &name))
- return false;
- if (!builder.ReadString(&iter, &value))
- return false;
- if (block->find(name) == block->end()) {
- (*block)[name] = value;
- } else {
- return false;
- }
+ SpdyFrameReader reader(header_data, header_length);
+
+ // Read number of headers.
+ uint32 num_headers;
+ if (spdy_version_ < 3) {
+ uint16 temp;
+ if (!reader.ReadUInt16(&temp)) {
+ DLOG(INFO) << "Unable to read number of headers.";
+ return false;
+ }
+ num_headers = temp;
+ } else {
+ if (!reader.ReadUInt32(&num_headers)) {
+ DLOG(INFO) << "Unable to read number of headers.";
+ return false;
}
- return true;
}
- return false;
-}
-
-bool SpdyFramer::ParseHeaderBlock(const SpdyFrame* frame,
- SpdyHeaderBlock* block) {
- SpdyControlFrame control_frame(frame->data(), false);
- uint32 type = control_frame.type();
- if (type != SYN_STREAM && type != SYN_REPLY && type != HEADERS)
- return false;
-
- // Find the header data within the control frame.
- scoped_ptr<SpdyFrame> decompressed_frame(DecompressFrame(*frame));
- if (!decompressed_frame.get())
- return false;
- const char *header_data = NULL;
- int header_length = 0;
+ // Read each header.
+ for (uint32 index = 0; index < num_headers; ++index) {
+ base::StringPiece temp;
- switch (type) {
- case SYN_STREAM:
- {
- SpdySynStreamControlFrame syn_frame(decompressed_frame->data(), false);
- header_data = syn_frame.header_block();
- header_length = syn_frame.header_block_len();
- }
- break;
- case SYN_REPLY:
- {
- SpdySynReplyControlFrame syn_frame(decompressed_frame->data(), false);
- header_data = syn_frame.header_block();
- header_length = syn_frame.header_block_len();
- }
- break;
- case HEADERS:
- {
- SpdyHeadersControlFrame header_frame(decompressed_frame->data(), false);
- header_data = header_frame.header_block();
- header_length = header_frame.header_block_len();
- }
- break;
- }
+ // Read header name.
+ if ((spdy_version_ < 3) ? !reader.ReadStringPiece16(&temp)
+ : !reader.ReadStringPiece32(&temp)) {
+ DLOG(INFO) << "Unable to read header name (" << index + 1 << " of "
+ << num_headers << ").";
+ return false;
+ }
+ std::string name;
+ temp.CopyToString(&name);
+
+ // Read header value.
+ if ((spdy_version_ < 3) ? !reader.ReadStringPiece16(&temp)
+ : !reader.ReadStringPiece32(&temp)) {
+ DLOG(INFO) << "Unable to read header value (" << index + 1 << " of "
+ << num_headers << ").";
+ return false;
+ }
+ std::string value;
+ temp.CopyToString(&value);
- SpdyFrameBuilder builder(header_data, header_length);
- void* iter = NULL;
- uint16 num_headers;
- if (builder.ReadUInt16(&iter, &num_headers)) {
- int index;
- for (index = 0; index < num_headers; ++index) {
- std::string name;
- std::string value;
- if (!builder.ReadString(&iter, &name))
- break;
- if (!builder.ReadString(&iter, &value))
- break;
- if (!name.size() || !value.size())
- return false;
- if (block->find(name) == block->end()) {
- (*block)[name] = value;
- } else {
- return false;
- }
+ // Ensure no duplicates.
+ if (block->find(name) != block->end()) {
+ DLOG(INFO) << "Duplicate header '" << name << "' (" << index + 1 << " of "
+ << num_headers << ").";
+ return false;
}
- return index == num_headers &&
- iter == header_data + header_length;
+
+ // Store header.
+ (*block)[name] = value;
}
- return false;
+ return true;
}
/* static */
@@ -826,16 +992,20 @@ bool SpdyFramer::ParseSettings(const SpdySettingsControlFrame* frame,
DCHECK_EQ(frame->type(), SETTINGS);
DCHECK(settings);
- SpdyFrameBuilder parser(frame->header_block(), frame->header_block_len());
- void* iter = NULL;
+ SpdyFrameReader parser(frame->header_block(), frame->header_block_len());
for (size_t index = 0; index < frame->num_entries(); ++index) {
- uint32 id;
+ uint32 id_and_flags_wire;
uint32 value;
- if (!parser.ReadUInt32(&iter, &id))
+ // SettingsFlagsAndId accepts off-the-wire (network byte order) data, so we
+ // use ReadBytes() instead of ReadUInt32() as the latter calls ntohl().
+ if (!parser.ReadBytes(&id_and_flags_wire, 4)) {
return false;
- if (!parser.ReadUInt32(&iter, &value))
+ }
+ if (!parser.ReadUInt32(&value))
return false;
- settings->insert(settings->end(), std::make_pair(id, value));
+ SettingsFlagsAndId id_and_flags =
+ SettingsFlagsAndId::FromWireFormat(frame->version(), id_and_flags_wire);
+ settings->insert(settings->end(), std::make_pair(id_and_flags, value));
}
return true;
}
@@ -845,32 +1015,35 @@ bool SpdyFramer::ParseCredentialData(const char* data, size_t len,
SpdyCredential* credential) {
DCHECK(credential);
- void* iter = NULL;
- SpdyFrameBuilder parser(data, len);
- if (!parser.ReadUInt16(&iter, &credential->slot))
+ SpdyFrameReader parser(data, len);
+ base::StringPiece temp;
+ if (!parser.ReadUInt16(&credential->slot)) {
return false;
+ }
- uint32 proof_len;
- const char* proof_data;
- if (!parser.ReadReadLen32PrefixedData(&iter, &proof_data, &proof_len))
+ if (!parser.ReadStringPiece32(&temp)) {
return false;
- credential->proof.assign(proof_data, proof_len);
+ }
+ temp.CopyToString(&credential->proof);
- while (parser.IteratorHasRoomFor(iter, 1)) {
- uint32 cert_len;
- const char* cert_data;
- if (!parser.ReadReadLen32PrefixedData(&iter, &cert_data, &cert_len))
+ while (!parser.IsDoneReading()) {
+ if (!parser.ReadStringPiece32(&temp)) {
return false;
- credential->certs.push_back("");
- credential->certs.back().assign(cert_data, cert_len);
+ }
+ std::string cert;
+ temp.CopyToString(&cert);
+ credential->certs.push_back(cert);
}
return true;
}
SpdySynStreamControlFrame* SpdyFramer::CreateSynStream(
- SpdyStreamId stream_id, SpdyStreamId associated_stream_id, int priority,
- SpdyControlFlags flags, bool compressed, const SpdyHeaderBlock* headers) {
- DCHECK_GT(stream_id, static_cast<SpdyStreamId>(0));
+ SpdyStreamId stream_id,
+ SpdyStreamId associated_stream_id,
+ SpdyPriority priority,
+ SpdyControlFlags flags,
+ bool compressed,
+ const SpdyHeaderBlock* headers) {
DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
DCHECK_EQ(0u, associated_stream_id & ~kStreamIdMask);
@@ -889,7 +1062,13 @@ SpdySynStreamControlFrame* SpdyFramer::CreateSynStream(
frame.WriteBytes(&flags_length, sizeof(flags_length));
frame.WriteUInt32(stream_id);
frame.WriteUInt32(associated_stream_id);
- frame.WriteUInt16(ntohs(priority) << 6); // Priority.
+ // Cap as appropriate.
+ if (priority > GetLowestPriority()) {
+ DLOG(ERROR) << "Priority out-of-bounds.";
+ priority = GetLowestPriority();
+ }
+ // Priority is 2 bits for <spdy3, 3 bits otherwise.
+ frame.WriteUInt16(ntohs(priority) << (spdy_version_ < 3 ? 6 : 5));
WriteHeaderBlock(&frame, headers);
scoped_ptr<SpdySynStreamControlFrame> syn_frame(
@@ -901,14 +1080,21 @@ SpdySynStreamControlFrame* SpdyFramer::CreateSynStream(
return syn_frame.release();
}
-SpdySynReplyControlFrame* SpdyFramer::CreateSynReply(SpdyStreamId stream_id,
- SpdyControlFlags flags, bool compressed, const SpdyHeaderBlock* headers) {
+SpdySynReplyControlFrame* SpdyFramer::CreateSynReply(
+ SpdyStreamId stream_id,
+ SpdyControlFlags flags,
+ bool compressed,
+ const SpdyHeaderBlock* headers) {
DCHECK_GT(stream_id, 0u);
DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
// Find our length.
size_t expected_frame_size = SpdySynReplyControlFrame::size() +
GetSerializedLength(headers);
+ // In SPDY 2, there were 2 unused bytes before payload.
+ if (spdy_version_ < 3) {
+ expected_frame_size += 2;
+ }
// Create our FlagsAndLength.
FlagsAndLength flags_length = CreateFlagsAndLength(
@@ -920,7 +1106,9 @@ SpdySynReplyControlFrame* SpdyFramer::CreateSynReply(SpdyStreamId stream_id,
frame.WriteUInt16(SYN_REPLY);
frame.WriteBytes(&flags_length, sizeof(flags_length));
frame.WriteUInt32(stream_id);
- frame.WriteUInt16(0); // Unused
+ if (spdy_version_ < 3) {
+ frame.WriteUInt16(0); // Unused
+ }
WriteHeaderBlock(&frame, headers);
scoped_ptr<SpdySynReplyControlFrame> reply_frame(
@@ -932,9 +1120,9 @@ SpdySynReplyControlFrame* SpdyFramer::CreateSynReply(SpdyStreamId stream_id,
return reply_frame.release();
}
-/* static */
-SpdyRstStreamControlFrame* SpdyFramer::CreateRstStream(SpdyStreamId stream_id,
- SpdyStatusCodes status) {
+SpdyRstStreamControlFrame* SpdyFramer::CreateRstStream(
+ SpdyStreamId stream_id,
+ SpdyStatusCodes status) const {
DCHECK_GT(stream_id, 0u);
DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
DCHECK_NE(status, INVALID);
@@ -949,9 +1137,8 @@ SpdyRstStreamControlFrame* SpdyFramer::CreateRstStream(SpdyStreamId stream_id,
return reinterpret_cast<SpdyRstStreamControlFrame*>(frame.take());
}
-/* static */
SpdySettingsControlFrame* SpdyFramer::CreateSettings(
- const SpdySettings& values) {
+ const SpdySettings& values) const {
SpdyFrameBuilder frame(SpdySettingsControlFrame::size() + 8 * values.size());
frame.WriteUInt16(kControlFlagMask | spdy_version_);
frame.WriteUInt16(SETTINGS);
@@ -962,24 +1149,15 @@ SpdySettingsControlFrame* SpdyFramer::CreateSettings(
frame.WriteUInt32(values.size());
SpdySettings::const_iterator it = values.begin();
while (it != values.end()) {
- frame.WriteUInt32(it->first.id_);
+ uint32 id_and_flags_wire = it->first.GetWireFormat(spdy_version_);
+ frame.WriteBytes(&id_and_flags_wire, 4);
frame.WriteUInt32(it->second);
++it;
}
return reinterpret_cast<SpdySettingsControlFrame*>(frame.take());
}
-/* static */
-SpdyNoOpControlFrame* SpdyFramer::CreateNopFrame() {
- SpdyFrameBuilder frame(SpdyNoOpControlFrame::size());
- frame.WriteUInt16(kControlFlagMask | spdy_version_);
- frame.WriteUInt16(NOOP);
- frame.WriteUInt32(0);
- return reinterpret_cast<SpdyNoOpControlFrame*>(frame.take());
-}
-
-/* static */
-SpdyPingControlFrame* SpdyFramer::CreatePingFrame(uint32 unique_id) {
+SpdyPingControlFrame* SpdyFramer::CreatePingFrame(uint32 unique_id) const {
SpdyFrameBuilder frame(SpdyPingControlFrame::size());
frame.WriteUInt16(kControlFlagMask | spdy_version_);
frame.WriteUInt16(PING);
@@ -989,9 +1167,8 @@ SpdyPingControlFrame* SpdyFramer::CreatePingFrame(uint32 unique_id) {
return reinterpret_cast<SpdyPingControlFrame*>(frame.take());
}
-/* static */
SpdyGoAwayControlFrame* SpdyFramer::CreateGoAway(
- SpdyStreamId last_accepted_stream_id) {
+ SpdyStreamId last_accepted_stream_id) const {
DCHECK_EQ(0u, last_accepted_stream_id & ~kStreamIdMask);
SpdyFrameBuilder frame(SpdyGoAwayControlFrame::size());
@@ -1003,8 +1180,11 @@ SpdyGoAwayControlFrame* SpdyFramer::CreateGoAway(
return reinterpret_cast<SpdyGoAwayControlFrame*>(frame.take());
}
-SpdyHeadersControlFrame* SpdyFramer::CreateHeaders(SpdyStreamId stream_id,
- SpdyControlFlags flags, bool compressed, const SpdyHeaderBlock* headers) {
+SpdyHeadersControlFrame* SpdyFramer::CreateHeaders(
+ SpdyStreamId stream_id,
+ SpdyControlFlags flags,
+ bool compressed,
+ const SpdyHeaderBlock* headers) {
// Basically the same as CreateSynReply().
DCHECK_GT(stream_id, 0u);
DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
@@ -1012,6 +1192,10 @@ SpdyHeadersControlFrame* SpdyFramer::CreateHeaders(SpdyStreamId stream_id,
// Find our length.
size_t expected_frame_size = SpdyHeadersControlFrame::size() +
GetSerializedLength(headers);
+ // In SPDY 2, there were 2 unused bytes before payload.
+ if (spdy_version_ < 3) {
+ expected_frame_size += 2;
+ }
// Create our FlagsAndLength.
FlagsAndLength flags_length = CreateFlagsAndLength(
@@ -1023,7 +1207,9 @@ SpdyHeadersControlFrame* SpdyFramer::CreateHeaders(SpdyStreamId stream_id,
frame.WriteUInt16(HEADERS);
frame.WriteBytes(&flags_length, sizeof(flags_length));
frame.WriteUInt32(stream_id);
- frame.WriteUInt16(0); // Unused
+ if (spdy_version_ < 3) {
+ frame.WriteUInt16(0); // Unused
+ }
WriteHeaderBlock(&frame, headers);
DCHECK_EQ(static_cast<size_t>(frame.length()), expected_frame_size);
@@ -1036,14 +1222,14 @@ SpdyHeadersControlFrame* SpdyFramer::CreateHeaders(SpdyStreamId stream_id,
return headers_frame.release();
}
-/* static */
SpdyWindowUpdateControlFrame* SpdyFramer::CreateWindowUpdate(
SpdyStreamId stream_id,
- uint32 delta_window_size) {
+ uint32 delta_window_size) const {
DCHECK_GT(stream_id, 0u);
DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
DCHECK_GT(delta_window_size, 0u);
- DCHECK_LE(delta_window_size, spdy::kSpdyStreamMaximumWindowSize);
+ DCHECK_LE(delta_window_size,
+ static_cast<uint32>(spdy::kSpdyStreamMaximumWindowSize));
SpdyFrameBuilder frame(SpdyWindowUpdateControlFrame::size());
frame.WriteUInt16(kControlFlagMask | spdy_version_);
@@ -1056,25 +1242,24 @@ SpdyWindowUpdateControlFrame* SpdyFramer::CreateWindowUpdate(
return reinterpret_cast<SpdyWindowUpdateControlFrame*>(frame.take());
}
-/* static */
SpdyCredentialControlFrame* SpdyFramer::CreateCredentialFrame(
- const SpdyCredential& credential) {
+ const SpdyCredential& credential) const {
// Calculate the size of the frame by adding the size of the
// variable length data to the size of the fixed length data.
size_t frame_size = SpdyCredentialControlFrame::size() +
credential.proof.length();
DCHECK_EQ(SpdyCredentialControlFrame::size(), 14u);
- for (vector<std::string>::const_iterator cert = credential.certs.begin();
+ for (std::vector<std::string>::const_iterator cert = credential.certs.begin();
cert != credential.certs.end();
- cert++) {
+ ++cert) {
frame_size += sizeof(uint32); // size of the cert_length field
- frame_size += cert->length(); // size of the cert_data field
+ frame_size += cert->length(); // size of the cert_data field
}
size_t payload_size = frame_size - SpdyFrame::kHeaderSize;
SpdyFrameBuilder frame(frame_size);
// Create our FlagsAndLength.
- SpdyControlFlags flags = spdy::CONTROL_FLAG_NONE;
+ SpdyControlFlags flags = CONTROL_FLAG_NONE;
FlagsAndLength flags_length = CreateFlagsAndLength(flags, payload_size);
frame.WriteUInt16(kControlFlagMask | spdy_version_);
@@ -1083,9 +1268,9 @@ SpdyCredentialControlFrame* SpdyFramer::CreateCredentialFrame(
frame.WriteUInt16(credential.slot);
frame.WriteUInt32(credential.proof.size());
frame.WriteBytes(credential.proof.c_str(), credential.proof.size());
- for (vector<std::string>::const_iterator cert = credential.certs.begin();
+ for (std::vector<std::string>::const_iterator cert = credential.certs.begin();
cert != credential.certs.end();
- cert++) {
+ ++cert) {
frame.WriteUInt32(cert->length());
frame.WriteBytes(cert->c_str(), cert->length());
}
@@ -1095,7 +1280,6 @@ SpdyCredentialControlFrame* SpdyFramer::CreateCredentialFrame(
SpdyDataFrame* SpdyFramer::CreateDataFrame(SpdyStreamId stream_id,
const char* data,
uint32 len, SpdyDataFlags flags) {
- DCHECK_GT(stream_id, 0u);
DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
SpdyFrameBuilder frame(SpdyDataFrame::size() + len);
@@ -1112,10 +1296,10 @@ SpdyDataFrame* SpdyFramer::CreateDataFrame(SpdyStreamId stream_id,
scoped_ptr<SpdyFrame> data_frame(frame.take());
SpdyDataFrame* rv;
if (flags & DATA_FLAG_COMPRESSED) {
- rv = reinterpret_cast<SpdyDataFrame*>(CompressFrame(*data_frame.get()));
- } else {
- rv = reinterpret_cast<SpdyDataFrame*>(data_frame.release());
+ LOG(DFATAL) << "DATA_FLAG_COMPRESSED invalid for " << display_protocol_
+ << ".";
}
+ rv = reinterpret_cast<SpdyDataFrame*>(data_frame.release());
if (flags & DATA_FLAG_FIN) {
CleanupCompressorForStream(stream_id);
@@ -1131,46 +1315,18 @@ static const int kCompressorLevel = 9;
static const int kCompressorWindowSizeInBits = 11;
static const int kCompressorMemLevel = 1;
-// This is just a hacked dictionary to use for shrinking HTTP-like headers.
-// TODO(mbelshe): Use a scientific methodology for computing the dictionary.
-const char SpdyFramer::kDictionary[] =
- "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-"
- "languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi"
- "f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser"
- "-agent10010120020120220320420520630030130230330430530630740040140240340440"
- "5406407408409410411412413414415416417500501502503504505accept-rangesageeta"
- "glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic"
- "ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran"
- "sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati"
- "oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo"
- "ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe"
- "pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic"
- "ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1"
- ".1statusversionurl";
-const int SpdyFramer::kDictionarySize = arraysize(kDictionary);
-
SpdyFrame* SpdyFramer::CompressFrame(const SpdyFrame& frame) {
if (frame.is_control_frame()) {
return CompressControlFrame(
reinterpret_cast<const SpdyControlFrame&>(frame));
}
- return CompressDataFrame(reinterpret_cast<const SpdyDataFrame&>(frame));
-}
-
-SpdyFrame* SpdyFramer::DecompressFrame(const SpdyFrame& frame) {
- if (frame.is_control_frame()) {
- return DecompressControlFrame(
- reinterpret_cast<const SpdyControlFrame&>(frame));
- }
- return DecompressDataFrame(reinterpret_cast<const SpdyDataFrame&>(frame));
+ return NULL;
}
bool SpdyFramer::IsCompressible(const SpdyFrame& frame) const {
// The important frames to compress are those which contain large
// amounts of compressible data - namely the headers in the SYN_STREAM
// and SYN_REPLY.
- // TODO(mbelshe): Reconcile this with the spec when the spec is
- // explicit about which frames compress and which do not.
if (frame.is_control_frame()) {
const SpdyControlFrame& control_frame =
reinterpret_cast<const SpdyControlFrame&>(frame);
@@ -1178,9 +1334,8 @@ bool SpdyFramer::IsCompressible(const SpdyFrame& frame) const {
control_frame.type() == SYN_REPLY;
}
- const SpdyDataFrame& data_frame =
- reinterpret_cast<const SpdyDataFrame&>(frame);
- return (data_frame.flags() & DATA_FLAG_COMPRESSED) != 0;
+ // We don't compress Data frames.
+ return false;
}
z_stream* SpdyFramer::GetHeaderCompressor() {
@@ -1196,10 +1351,15 @@ z_stream* SpdyFramer::GetHeaderCompressor() {
kCompressorWindowSizeInBits,
kCompressorMemLevel,
Z_DEFAULT_STRATEGY);
- if (success == Z_OK)
+ if (success == Z_OK) {
+ const char* dictionary = (spdy_version_ < 3) ? kV2Dictionary
+ : kV3Dictionary;
+ const int dictionary_size = (spdy_version_ < 3) ? kV2DictionarySize
+ : kV3DictionarySize;
success = deflateSetDictionary(header_compressor_.get(),
- reinterpret_cast<const Bytef*>(kDictionary),
- kDictionarySize);
+ reinterpret_cast<const Bytef*>(dictionary),
+ dictionary_size);
+ }
if (success != Z_OK) {
LOG(WARNING) << "deflateSetDictionary failure: " << success;
header_compressor_.reset(NULL);
@@ -1224,27 +1384,6 @@ z_stream* SpdyFramer::GetHeaderDecompressor() {
return header_decompressor_.get();
}
-z_stream* SpdyFramer::GetStreamCompressor(SpdyStreamId stream_id) {
- CompressorMap::iterator it = stream_compressors_.find(stream_id);
- if (it != stream_compressors_.end())
- return it->second; // Already initialized.
-
- scoped_ptr<z_stream> compressor(new z_stream);
- memset(compressor.get(), 0, sizeof(z_stream));
-
- int success = deflateInit2(compressor.get(),
- kCompressorLevel,
- Z_DEFLATED,
- kCompressorWindowSizeInBits,
- kCompressorMemLevel,
- Z_DEFAULT_STRATEGY);
- if (success != Z_OK) {
- LOG(WARNING) << "deflateInit failure: " << success;
- return NULL;
- }
- return stream_compressors_[stream_id] = compressor.release();
-}
-
z_stream* SpdyFramer::GetStreamDecompressor(SpdyStreamId stream_id) {
CompressorMap::iterator it = stream_decompressors_.find(stream_id);
if (it != stream_decompressors_.end())
@@ -1288,6 +1427,11 @@ bool SpdyFramer::GetFrameBoundaries(const SpdyFrame& frame,
*payload_length = syn_frame.header_block_len();
*header_length = frame_size;
*payload = frame.data() + *header_length;
+ // SPDY 2 had two bytes of unused space preceeding payload.
+ if (spdy_version_ < 3) {
+ *header_length += 2;
+ *payload += 2;
+ }
}
break;
case HEADERS:
@@ -1298,6 +1442,11 @@ bool SpdyFramer::GetFrameBoundaries(const SpdyFrame& frame,
*payload_length = headers_frame.header_block_len();
*header_length = frame_size;
*payload = frame.data() + *header_length;
+ // SPDY 2 had two bytes of unused space preceeding payload.
+ if (spdy_version_ < 3) {
+ *header_length += 2;
+ *payload += 2;
+ }
}
break;
default:
@@ -1318,40 +1467,7 @@ SpdyControlFrame* SpdyFramer::CompressControlFrame(
z_stream* compressor = GetHeaderCompressor();
if (!compressor)
return NULL;
- return reinterpret_cast<SpdyControlFrame*>(
- CompressFrameWithZStream(frame, compressor));
-}
-SpdyDataFrame* SpdyFramer::CompressDataFrame(const SpdyDataFrame& frame) {
- z_stream* compressor = GetStreamCompressor(frame.stream_id());
- if (!compressor)
- return NULL;
- return reinterpret_cast<SpdyDataFrame*>(
- CompressFrameWithZStream(frame, compressor));
-}
-
-SpdyControlFrame* SpdyFramer::DecompressControlFrame(
- const SpdyControlFrame& frame) {
- z_stream* decompressor = GetHeaderDecompressor();
- if (!decompressor) {
- LOG(DFATAL) << "Couldn't get decompressor for handling control frame.";
- set_error(SPDY_DECOMPRESS_FAILURE);
- return NULL;
- }
- return reinterpret_cast<SpdyControlFrame*>(
- DecompressFrameWithZStream(frame, decompressor));
-}
-
-SpdyDataFrame* SpdyFramer::DecompressDataFrame(const SpdyDataFrame& frame) {
- z_stream* decompressor = GetStreamDecompressor(frame.stream_id());
- if (!decompressor)
- return NULL;
- return reinterpret_cast<SpdyDataFrame*>(
- DecompressFrameWithZStream(frame, decompressor));
-}
-
-SpdyFrame* SpdyFramer::CompressFrameWithZStream(const SpdyFrame& frame,
- z_stream* compressor) {
int payload_length;
int header_length;
const char* payload;
@@ -1361,7 +1477,7 @@ SpdyFrame* SpdyFramer::CompressFrameWithZStream(const SpdyFrame& frame,
base::StatsCounter post_compress_bytes("spdy.PostCompressSize");
if (!enable_compression_)
- return DuplicateFrame(frame);
+ return reinterpret_cast<SpdyControlFrame*>(DuplicateFrame(frame));
if (!GetFrameBoundaries(frame, &payload_length, &header_length, &payload))
return NULL;
@@ -1369,7 +1485,13 @@ SpdyFrame* SpdyFramer::CompressFrameWithZStream(const SpdyFrame& frame,
// Create an output frame.
int compressed_max_size = deflateBound(compressor, payload_length);
int new_frame_size = header_length + compressed_max_size;
- scoped_ptr<SpdyFrame> new_frame(new SpdyFrame(new_frame_size));
+ if ((frame.type() == SYN_REPLY || frame.type() == HEADERS) &&
+ spdy_version_ < 3) {
+ new_frame_size += 2;
+ }
+ DCHECK_GE(new_frame_size,
+ static_cast<int>(frame.length() + SpdyFrame::kHeaderSize));
+ scoped_ptr<SpdyControlFrame> new_frame(new SpdyControlFrame(new_frame_size));
memcpy(new_frame->data(), frame.data(),
frame.length() + SpdyFrame::kHeaderSize);
@@ -1380,6 +1502,9 @@ SpdyFrame* SpdyFramer::CompressFrameWithZStream(const SpdyFrame& frame,
compressor->avail_out = compressed_max_size;
// Data packets have a 'compressed' flag.
+ // TODO(hkhalil): Remove post code-yellow. It's impossible to execute this
+ // branch given that SpdyControlFrame::is_control_frame always returns true.
+ DCHECK(new_frame->is_control_frame());
if (!new_frame->is_control_frame()) {
SpdyDataFrame* data_frame =
reinterpret_cast<SpdyDataFrame*>(new_frame.get());
@@ -1416,85 +1541,6 @@ SpdyFrame* SpdyFramer::CompressFrameWithZStream(const SpdyFrame& frame,
return new_frame.release();
}
-SpdyFrame* SpdyFramer::DecompressFrameWithZStream(const SpdyFrame& frame,
- z_stream* decompressor) {
- int payload_length;
- int header_length;
- const char* payload;
-
- base::StatsCounter decompressed_frames("spdy.DecompressedFrames");
- base::StatsCounter pre_decompress_bytes("spdy.PreDeCompressSize");
- base::StatsCounter post_decompress_bytes("spdy.PostDeCompressSize");
-
- if (!enable_compression_)
- return DuplicateFrame(frame);
-
- if (!GetFrameBoundaries(frame, &payload_length, &header_length, &payload))
- return NULL;
-
- if (!frame.is_control_frame()) {
- const SpdyDataFrame& data_frame =
- reinterpret_cast<const SpdyDataFrame&>(frame);
- if ((data_frame.flags() & DATA_FLAG_COMPRESSED) == 0)
- return DuplicateFrame(frame);
- }
-
- // Create an output frame. Assume it does not need to be longer than
- // the input data.
- size_t decompressed_max_size = kControlFrameBufferInitialSize;
- int new_frame_size = header_length + decompressed_max_size;
- if (frame.length() > decompressed_max_size)
- return NULL;
- scoped_ptr<SpdyFrame> new_frame(new SpdyFrame(new_frame_size));
- memcpy(new_frame->data(), frame.data(),
- frame.length() + SpdyFrame::kHeaderSize);
-
- decompressor->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(payload));
- decompressor->avail_in = payload_length;
- decompressor->next_out = reinterpret_cast<Bytef*>(new_frame->data()) +
- header_length;
- decompressor->avail_out = decompressed_max_size;
-
- int rv = inflate(decompressor, Z_SYNC_FLUSH);
- if (rv == Z_NEED_DICT) {
- // Need to try again with the right dictionary.
- if (decompressor->adler == kDictionaryId) {
- rv = inflateSetDictionary(decompressor,
- (const Bytef*)SpdyFramer::kDictionary,
- SpdyFramer::kDictionarySize);
- if (rv == Z_OK)
- rv = inflate(decompressor, Z_SYNC_FLUSH);
- }
- }
- if (rv != Z_OK) { // How can we know that it decompressed everything?
- LOG(WARNING) << "inflate failure: " << rv;
- return NULL;
- }
-
- // Unset the compressed flag for data frames.
- if (!new_frame->is_control_frame()) {
- SpdyDataFrame* data_frame =
- reinterpret_cast<SpdyDataFrame*>(new_frame.get());
- data_frame->set_flags(data_frame->flags() & ~DATA_FLAG_COMPRESSED);
- }
-
- int decompressed_size = decompressed_max_size - decompressor->avail_out;
- new_frame->set_length(
- header_length + decompressed_size - SpdyFrame::kHeaderSize);
-
- // If there is data left, then the frame didn't fully decompress. This
- // means that there is stranded data at the end of this frame buffer which
- // will be ignored.
- DCHECK_EQ(decompressor->avail_in, 0u);
-
- pre_decompress_bytes.Add(frame.length());
- post_decompress_bytes.Add(new_frame->length());
-
- decompressed_frames.Increment();
-
- return new_frame.release();
-}
-
// Incrementally decompress the control frame's header block, feeding the
// result to the visitor in chunks. Continue this until the visitor
// indicates that it cannot process any more data, or (more commonly) we
@@ -1521,12 +1567,31 @@ bool SpdyFramer::IncrementallyDecompressControlFrameHeaderData(
while (decomp->avail_in > 0 && processed_successfully) {
decomp->next_out = reinterpret_cast<Bytef*>(buffer);
decomp->avail_out = arraysize(buffer);
- int rv = DecompressHeaderBlockInZStream(decomp);
- if (rv != Z_OK && rv != Z_BUF_ERROR) {
- set_error(SPDY_DECOMPRESS_FAILURE);
- DLOG(WARNING) << "inflate failure: " << rv;
- processed_successfully = false;
- } else {
+
+ int rv = inflate(decomp, Z_SYNC_FLUSH);
+ if (rv == Z_NEED_DICT) {
+ const char* dictionary = (spdy_version_ < 3) ? kV2Dictionary
+ : kV3Dictionary;
+ const int dictionary_size = (spdy_version_ < 3) ? kV2DictionarySize
+ : kV3DictionarySize;
+ const uLong dictionary_id = (spdy_version_ < 3) ? kV2DictionaryId
+ : kV3DictionaryId;
+ // Need to try again with the right dictionary.
+ if (decomp->adler == dictionary_id) {
+ rv = inflateSetDictionary(decomp,
+ reinterpret_cast<const Bytef*>(dictionary),
+ dictionary_size);
+ if (rv == Z_OK)
+ rv = inflate(decomp, Z_SYNC_FLUSH);
+ }
+ }
+
+ // Inflate will generate a Z_BUF_ERROR if it runs out of input
+ // without producing any output. The input is consumed and
+ // buffered internally by zlib so we can detect this condition by
+ // checking if avail_in is 0 after the call to inflate.
+ bool input_exhausted = ((rv == Z_BUF_ERROR) && (decomp->avail_in == 0));
+ if ((rv == Z_OK) || input_exhausted) {
size_t decompressed_len = arraysize(buffer) - decomp->avail_out;
if (decompressed_len > 0) {
processed_successfully = visitor_->OnControlFrameHeaderData(
@@ -1537,6 +1602,10 @@ bool SpdyFramer::IncrementallyDecompressControlFrameHeaderData(
// visitor.
set_error(SPDY_CONTROL_PAYLOAD_TOO_LARGE);
}
+ } else {
+ DLOG(WARNING) << "inflate failure: " << rv << " " << len;
+ set_error(SPDY_DECOMPRESS_FAILURE);
+ processed_successfully = false;
}
}
return processed_successfully;
@@ -1546,7 +1615,6 @@ bool SpdyFramer::IncrementallyDeliverControlFrameHeaderData(
const SpdyControlFrame* control_frame, const char* data, size_t len) {
bool read_successfully = true;
const SpdyStreamId stream_id = GetControlFrameStreamId(control_frame);
- DCHECK_LT(0u, stream_id);
while (read_successfully && len > 0) {
size_t bytes_to_deliver = std::min(len, kHeaderDataChunkMaxSize);
read_successfully = visitor_->OnControlFrameHeaderData(stream_id, data,
@@ -1622,7 +1690,10 @@ size_t SpdyFramer::GetMinimumControlFrameSize(SpdyControlType type) {
case SETTINGS:
return SpdySettingsControlFrame::size();
case NOOP:
- return SpdyNoOpControlFrame::size();
+ // Even though NOOP is no longer supported, we still correctly report its
+ // size so that it can be handled correctly as incoming data if
+ // implementations so desire.
+ return SpdyFrame::kHeaderSize;
case PING:
return SpdyPingControlFrame::size();
case GOAWAY:
@@ -1682,42 +1753,16 @@ SpdyStreamId SpdyFramer::GetControlFrameStreamId(
return stream_id;
}
-size_t SpdyFramer::BytesSafeToRead() const {
- switch (state_) {
- case SPDY_ERROR:
- case SPDY_DONE:
- case SPDY_AUTO_RESET:
- case SPDY_RESET:
- return 0;
- case SPDY_READING_COMMON_HEADER:
- DCHECK_LT(current_frame_len_,
- static_cast<size_t>(SpdyFrame::kHeaderSize));
- return SpdyFrame::kHeaderSize - current_frame_len_;
- // TODO(rtenneti): Add support for SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK
- // and SPDY_CONTROL_FRAME_HEADER_BLOCK.
- case SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK:
- case SPDY_CONTROL_FRAME_HEADER_BLOCK:
- return 0;
- case SPDY_CONTROL_FRAME_PAYLOAD:
- case SPDY_CREDENTIAL_FRAME_PAYLOAD:
- case SPDY_IGNORE_REMAINING_PAYLOAD:
- case SPDY_FORWARD_STREAM_FRAME:
- return remaining_data_;
- }
- // We should never get to here.
- return 0;
-}
-
void SpdyFramer::set_enable_compression(bool value) {
enable_compression_ = value;
}
-void SpdyFramer::set_enable_compression_default(bool value) {
- compression_default_ = value;
-}
-
void SpdyFramer::set_validate_control_frame_sizes(bool value) {
validate_control_frame_sizes_ = value;
}
+void SpdyFramer::set_enable_compression_default(bool value) {
+ compression_default_ = value;
+}
+
} // namespace spdy
diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h
index f4a3a3a61..1b0ce62 100644
--- a/net/spdy/spdy_framer.h
+++ b/net/spdy/spdy_framer.h
@@ -37,19 +37,51 @@ class WebSocketJobTest;
namespace spdy {
class SpdyFramer;
-class SpdyFramerTest;
+class SpdyFrameBuilder;
+class SpdyFramerSpdy2Test;
+class SpdyFramerSpdy3Test;
+
+namespace test_spdy2 {
+
+class TestSpdyVisitor;
+
+} // namespace test_spdy2
+
+namespace test_spdy3 {
-namespace test {
class TestSpdyVisitor;
-void FramerSetEnableCompressionHelper(SpdyFramer* framer, bool compress);
-} // namespace test
+
+} // namespace test_spdy3
// A datastructure for holding a set of headers from either a
// SYN_STREAM or SYN_REPLY frame.
typedef std::map<std::string, std::string> SpdyHeaderBlock;
+// A datastructure for holding the ID and flag fields for SETTINGS.
+// Conveniently handles converstion to/from wire format.
+class NET_EXPORT_PRIVATE SettingsFlagsAndId {
+ public:
+ static SettingsFlagsAndId FromWireFormat(int version, uint32 wire);
+
+ SettingsFlagsAndId() : flags_(0), id_(0) {}
+
+ // TODO(hkhalil): restrict to enums instead of free-form ints.
+ SettingsFlagsAndId(uint8 flags, uint32 id);
+
+ uint32 GetWireFormat(int version) const;
+
+ uint32 id() const { return id_; }
+ uint8 flags() const { return flags_; }
+
+ private:
+ static void ConvertFlagsAndIdForSpdy2(uint32* val);
+
+ uint8 flags_;
+ uint32 id_;
+};
+
// A datastructure for holding a set of ID/value pairs for a SETTINGS frame.
-typedef std::pair<spdy::SettingsFlagsAndId, uint32> SpdySetting;
+typedef std::pair<SettingsFlagsAndId, uint32> SpdySetting;
typedef std::list<SpdySetting> SpdySettings;
// A datastrcture for holding the contents of a CREDENTIAL frame.
@@ -62,6 +94,27 @@ struct NET_EXPORT_PRIVATE SpdyCredential {
std::string proof;
};
+// Scratch space necessary for processing SETTINGS frames.
+struct NET_EXPORT_PRIVATE SpdySettingsScratch {
+ SpdySettingsScratch() { Reset(); }
+
+ void Reset() {
+ setting_buf_len = 0;
+ last_setting_id = 0;
+ }
+
+ // Buffer contains up to one complete key/value pair.
+ char setting_buf[8];
+
+ // The amount of the buffer that is filled with valid data.
+ size_t setting_buf_len;
+
+ // The ID of the last setting that was processed in the current SETTINGS
+ // frame. Used for detecting out-of-order or duplicate keys within a settings
+ // frame. Set to 0 before first key/value pair is processed.
+ uint32 last_setting_id;
+};
+
// SpdyFramerVisitorInterface is a set of callbacks for the SpdyFramer.
// Implement this interface to receive event callbacks as frames are
// decoded from the framer.
@@ -113,13 +166,13 @@ class NET_EXPORT_PRIVATE SpdyFramerVisitorInterface {
// Called when a chunk of payload data for a credential frame is available.
// This is called after OnControl() is called with the credential frame
// associated with the payload being delivered here.
- // |frame_data| A buffer containing the header data chunk received.
+ // |header_data| A buffer containing the header data chunk received.
// |len| The length of the header data buffer. A length of zero indicates
// that the header data block has been completely sent.
// When this function returns true the visitor indicates that it accepted
// all of the data. Returning false indicates that that an unrecoverable
// error has occurred, such as bad header data or resource exhaustion.
- virtual bool OnCredentialFrameData(const char* frame_data,
+ virtual bool OnCredentialFrameData(const char* header_data,
size_t len) = 0;
// Called when a data frame header is received. The frame's data
@@ -136,6 +189,10 @@ class NET_EXPORT_PRIVATE SpdyFramerVisitorInterface {
virtual void OnStreamFrameData(SpdyStreamId stream_id,
const char* data,
size_t len) = 0;
+
+ // Called when a complete setting within a SETTINGS frame has been parsed and
+ // validated.
+ virtual void OnSetting(SpdySettingsIds id, uint8 flags, uint32 value) = 0;
};
class NET_EXPORT_PRIVATE SpdyFramer {
@@ -155,6 +212,7 @@ class NET_EXPORT_PRIVATE SpdyFramer {
SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK,
SPDY_CONTROL_FRAME_HEADER_BLOCK,
SPDY_CREDENTIAL_FRAME_PAYLOAD,
+ SPDY_SETTINGS_FRAME_PAYLOAD,
};
// SPDY error codes.
@@ -179,8 +237,8 @@ class NET_EXPORT_PRIVATE SpdyFramer {
// purposes.)
static const size_t kHeaderDataChunkMaxSize;
- // Create a new Framer.
- SpdyFramer();
+ // Create a new Framer, provided a SPDY version.
+ explicit SpdyFramer(int version);
virtual ~SpdyFramer();
// Set callbacks to be called from the framer. A visitor must be set, or
@@ -209,31 +267,26 @@ class NET_EXPORT_PRIVATE SpdyFramer {
}
bool HasError() { return state_ == SPDY_ERROR; }
- // Further parsing utilities.
- // Given a control frame, parse out a SpdyHeaderBlock. Only
- // valid for SYN_STREAM and SYN_REPLY frames.
- // Returns true if successfully parsed, false otherwise.
- bool ParseHeaderBlock(const SpdyFrame* frame, SpdyHeaderBlock* block);
-
// Given a buffer containing a decompressed header block in SPDY
// serialized format, parse out a SpdyHeaderBlock, putting the results
// in the given header block.
// Returns true if successfully parsed, false otherwise.
- static bool ParseHeaderBlockInBuffer(const char* header_data,
- size_t header_length,
- SpdyHeaderBlock* block);
+ bool ParseHeaderBlockInBuffer(const char* header_data,
+ size_t header_length,
+ SpdyHeaderBlock* block);
// Create a SpdySynStreamControlFrame.
// |stream_id| is the id for this stream.
// |associated_stream_id| is the associated stream id for this stream.
- // |priority| is the priority (0-3) for this stream.
+ // |priority| is the priority (GetHighestPriority()-GetLowestPriority) for
+ // this stream.
// |flags| is the flags to use with the data.
// To mark this frame as the last frame, enable CONTROL_FLAG_FIN.
// |compressed| specifies whether the frame should be compressed.
// |headers| is the header block to include in the frame.
SpdySynStreamControlFrame* CreateSynStream(SpdyStreamId stream_id,
SpdyStreamId associated_stream_id,
- int priority,
+ SpdyPriority priority,
SpdyControlFlags flags,
bool compressed,
const SpdyHeaderBlock* headers);
@@ -249,26 +302,23 @@ class NET_EXPORT_PRIVATE SpdyFramer {
bool compressed,
const SpdyHeaderBlock* headers);
- static SpdyRstStreamControlFrame* CreateRstStream(SpdyStreamId stream_id,
- SpdyStatusCodes status);
+ SpdyRstStreamControlFrame* CreateRstStream(SpdyStreamId stream_id,
+ SpdyStatusCodes status) const;
// Creates an instance of SpdySettingsControlFrame. The SETTINGS frame is
// used to communicate name/value pairs relevant to the communication channel.
- // TODO(mbelshe): add the name/value pairs!!
- static SpdySettingsControlFrame* CreateSettings(const SpdySettings& values);
-
- static SpdyNoOpControlFrame* CreateNopFrame();
+ SpdySettingsControlFrame* CreateSettings(const SpdySettings& values) const;
// Creates an instance of SpdyPingControlFrame. The unique_id is used to
// identify the ping request/response.
- static SpdyPingControlFrame* CreatePingFrame(uint32 unique_id);
+ SpdyPingControlFrame* CreatePingFrame(uint32 unique_id) const;
// Creates an instance of SpdyGoAwayControlFrame. The GOAWAY frame is used
// prior to the shutting down of the TCP connection, and includes the
// stream_id of the last stream the sender of the frame is willing to process
// to completion.
- static SpdyGoAwayControlFrame* CreateGoAway(
- SpdyStreamId last_accepted_stream_id);
+ SpdyGoAwayControlFrame* CreateGoAway(
+ SpdyStreamId last_accepted_stream_id) const;
// Creates an instance of SpdyHeadersControlFrame. The HEADERS frame is used
// for sending additional headers outside of a SYN_STREAM/SYN_REPLY. The
@@ -280,15 +330,15 @@ class NET_EXPORT_PRIVATE SpdyFramer {
// Creates an instance of SpdyWindowUpdateControlFrame. The WINDOW_UPDATE
// frame is used to implement per stream flow control in SPDY.
- static SpdyWindowUpdateControlFrame* CreateWindowUpdate(
+ SpdyWindowUpdateControlFrame* CreateWindowUpdate(
SpdyStreamId stream_id,
- uint32 delta_window_size);
+ uint32 delta_window_size) const;
// Creates an instance of SpdyCredentialControlFrame. The CREDENTIAL
// frame is used to send a client certificate to the server when
// request more than one origin are sent over the same SPDY session.
- static SpdyCredentialControlFrame* CreateCredentialFrame(
- const SpdyCredential& credential);
+ SpdyCredentialControlFrame* CreateCredentialFrame(
+ const SpdyCredential& credential) const;
// Given a SpdySettingsControlFrame, extract the settings.
// Returns true on successful parse, false otherwise.
@@ -330,13 +380,6 @@ class NET_EXPORT_PRIVATE SpdyFramer {
// On failure, returns NULL.
SpdyFrame* CompressFrame(const SpdyFrame& frame);
- // Decompresses a SpdyFrame.
- // On success, returns a new SpdyFrame with the payload decompressed.
- // Compression state is maintained as part of the SpdyFramer.
- // Returned frame must be freed with "delete".
- // On failure, returns NULL.
- SpdyFrame* DecompressFrame(const SpdyFrame& frame);
-
// Create a copy of a frame.
// Returned frame must be freed with "delete".
SpdyFrame* DuplicateFrame(const SpdyFrame& frame);
@@ -373,18 +416,44 @@ class NET_EXPORT_PRIVATE SpdyFramer {
static const char* StatusCodeToString(int status_code);
static const char* ControlTypeToString(SpdyControlType type);
- static void set_protocol_version(int version) { spdy_version_= version; }
- static int protocol_version() { return spdy_version_; }
+ // TODO(hkhalil): Remove SpdyFramer::set_protocol_version()
+ void set_protocol_version(int version) { spdy_version_= version; }
+ int protocol_version() const { return spdy_version_; }
+
+ bool probable_http_response() { return probable_http_response_; }
- // Export the compression dictionary
- static const char kDictionary[];
- static const int kDictionarySize;
+ SpdyPriority GetLowestPriority() const { return spdy_version_ < 3 ? 3 : 7; }
+ SpdyPriority GetHighestPriority() const { return 0; }
protected:
- FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, HeaderCompression);
- FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, ExpandBuffer_HeapSmash);
- FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, HugeHeaderBlock);
- FRIEND_TEST_ALL_PREFIXES(SpdyFramerTest, UnclosedStreamDataCompressors);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test, BasicCompression);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test, ControlFrameSizesAreValidated);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test, HeaderCompression);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test, DecompressUncompressedFrame);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test, ExpandBuffer_HeapSmash);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test, HugeHeaderBlock);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test, UnclosedStreamDataCompressors);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test,
+ UnclosedStreamDataCompressorsOneByteAtATime);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test,
+ UncompressLargerThanFrameBufferInitialSize);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test, ReadLargeSettingsFrame);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy2Test,
+ ReadLargeSettingsFrameInSmallChunks);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test, BasicCompression);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test, ControlFrameSizesAreValidated);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test, HeaderCompression);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test, DecompressUncompressedFrame);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test, ExpandBuffer_HeapSmash);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test, HugeHeaderBlock);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test, UnclosedStreamDataCompressors);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test,
+ UnclosedStreamDataCompressorsOneByteAtATime);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test,
+ UncompressLargerThanFrameBufferInitialSize);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test, ReadLargeSettingsFrame);
+ FRIEND_TEST_ALL_PREFIXES(SpdyFramerSpdy3Test,
+ ReadLargeSettingsFrameInSmallChunks);
friend class net::HttpNetworkLayer; // This is temporary for the server.
friend class net::HttpNetworkTransactionTest;
friend class net::HttpProxyClientSocketPoolTest;
@@ -395,45 +464,37 @@ class NET_EXPORT_PRIVATE SpdyFramer {
friend class net::SpdyStreamTest;
friend class net::SpdyWebSocketStreamTest;
friend class net::WebSocketJobTest;
- friend class test::TestSpdyVisitor;
- friend void test::FramerSetEnableCompressionHelper(SpdyFramer* framer,
- bool compress);
+ friend class test_spdy2::TestSpdyVisitor;
+ friend class test_spdy3::TestSpdyVisitor;
private:
typedef std::map<SpdyStreamId, z_stream*> CompressorMap;
- // Internal breakout from ProcessInput. Returns the number of bytes
+ // Internal breakouts from ProcessInput. Each returns the number of bytes
// consumed from the data.
size_t ProcessCommonHeader(const char* data, size_t len);
- void ProcessControlFrameHeader();
size_t ProcessControlFramePayload(const char* data, size_t len);
size_t ProcessCredentialFramePayload(const char* data, size_t len);
size_t ProcessControlFrameBeforeHeaderBlock(const char* data, size_t len);
size_t ProcessControlFrameHeaderBlock(const char* data, size_t len);
+ size_t ProcessSettingsFramePayload(const char* data, size_t len);
size_t ProcessDataFramePayload(const char* data, size_t len);
+ // Helpers for above internal breakouts from ProcessInput.
+ void ProcessControlFrameHeader();
+ bool ProcessSetting(const char* data); // Always passed exactly 8 bytes.
+
// Get (and lazily initialize) the ZLib state.
z_stream* GetHeaderCompressor();
z_stream* GetHeaderDecompressor();
- z_stream* GetStreamCompressor(SpdyStreamId id);
z_stream* GetStreamDecompressor(SpdyStreamId id);
// Compression helpers
SpdyControlFrame* CompressControlFrame(const SpdyControlFrame& frame);
- SpdyDataFrame* CompressDataFrame(const SpdyDataFrame& frame);
- SpdyControlFrame* DecompressControlFrame(const SpdyControlFrame& frame);
- SpdyDataFrame* DecompressDataFrame(const SpdyDataFrame& frame);
- SpdyFrame* CompressFrameWithZStream(const SpdyFrame& frame,
- z_stream* compressor);
- SpdyFrame* DecompressFrameWithZStream(const SpdyFrame& frame,
- z_stream* decompressor);
void CleanupCompressorForStream(SpdyStreamId id);
void CleanupDecompressorForStream(SpdyStreamId id);
void CleanupStreamCompressorsAndDecompressors();
- // Not used (yet)
- size_t BytesSafeToRead() const;
-
// Deliver the given control frame's compressed headers block to the visitor
// in decompressed form, in chunks. Returns true if the visitor has
// accepted all of the chunks.
@@ -458,6 +519,13 @@ class NET_EXPORT_PRIVATE SpdyFramer {
size_t UpdateCurrentFrameBuffer(const char** data, size_t* len,
size_t max_bytes);
+ // Retrieve serialized length of SpdyHeaderBlock.
+ size_t GetSerializedLength(const SpdyHeaderBlock* headers) const;
+
+ // Serializes a SpdyHeaderBlock.
+ void WriteHeaderBlock(SpdyFrameBuilder* frame,
+ const SpdyHeaderBlock* headers) const;
+
// Set the error code and moves the framer into the error state.
void set_error(SpdyError error);
@@ -486,19 +554,14 @@ class NET_EXPORT_PRIVATE SpdyFramer {
// Since this is only used for control frame headers, the maximum control
// frame header size (18B) is sufficient; all remaining control frame data is
// streamed to the visitor.
- // In addition to the maximum control frame header size, we account for any
- // LOAS checksumming (16B) that may occur in the VTL case.
// TODO(hkhalil): Remove post code-yellow once streamed inflate is properly
// implemented.
- static const size_t kUncompressedControlFrameBufferInitialSize = 18 + 16;
+ static size_t kUncompressedControlFrameBufferInitialSize;
// The maximum size of the control frame buffer that we support.
// TODO(mbelshe): We should make this stream-based so there are no limits.
static size_t kControlFrameBufferMaxSize;
- // The size of the buffer into which compressed frames are inflated.
- static const size_t kDecompressionBufferSize = 8 * 1024;
-
SpdyState state_;
SpdyError error_code_;
size_t remaining_data_;
@@ -516,6 +579,11 @@ class NET_EXPORT_PRIVATE SpdyFramer {
size_t current_frame_len_; // Number of bytes read into the current_frame_.
size_t current_frame_capacity_;
+ // Scratch space for handling SETTINGS frames.
+ // TODO(hkhalil): Unify memory for this scratch space with
+ // current_frame_buffer_.
+ SpdySettingsScratch settings_scratch_;
+
bool validate_control_frame_sizes_;
bool enable_compression_; // Controls all compression
// SPDY header compressors.
@@ -530,8 +598,24 @@ class NET_EXPORT_PRIVATE SpdyFramer {
std::string display_protocol_;
+ int spdy_version_;
+
+ // Tracks if we've ever gotten far enough in framing to see a control frame of
+ // type SYN_STREAM or SYN_REPLY.
+ //
+ // If we ever get something which looks like a data frame before we've had a
+ // SYN, we explicitly check to see if it looks like we got an HTTP response to
+ // a SPDY request. This boolean lets us do that.
+ bool syn_frame_processed_;
+
+ // If we ever get a data frame before a SYN frame, we check to see if it
+ // starts with HTTP. If it does, we likely have an HTTP response. This
+ // isn't guaranteed though: we could have gotten a settings frame and then
+ // corrupt data that just looks like HTTP, but deterministic checking requires
+ // a lot more state.
+ bool probable_http_response_;
+
static bool compression_default_;
- static int spdy_version_;
};
} // namespace spdy
diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_spdy2_test.cc
index e2590e0..f6bd35d 100644
--- a/net/spdy/spdy_framer_test.cc
+++ b/net/spdy/spdy_framer_spdy2_test.cc
@@ -11,13 +11,152 @@
#include "net/spdy/spdy_frame_builder.h"
#include "testing/platform_test.h"
+namespace {
+
+// Default SPDY version for unit tests.
+const int SPDY_VERSION_FOR_TESTS = 2;
+
+// The current default spdy version as a byte to be included in const
+// byte arrays below. Name choice is unfortunate, but better to fit to four
+// bytes than not.
+unsigned char kVer = SPDY_VERSION_FOR_TESTS;
+
+spdy::SpdySetting SpdySettingFromWireFormat(uint32 key, uint32 value) {
+ return spdy::SpdySetting(
+ spdy::SettingsFlagsAndId::FromWireFormat(SPDY_VERSION_FOR_TESTS, key),
+ value);
+}
+
+} // namespace
+
namespace spdy {
-namespace test {
+namespace test_spdy2 {
+
+static const size_t kMaxDecompressedSize = 1024;
+
+class SpdyFramerTestUtil {
+ public:
+ // Decompress a single frame using the decompression context held by
+ // the SpdyFramer. The implemention will CHECK fail if the input is anything
+ // other than a single, well-formed compressed frame.
+ //
+ // Returns a new decompressed SpdyFrame.
+ template<class SpdyFrameType> static SpdyFrame* DecompressFrame(
+ SpdyFramer* framer, const SpdyFrameType& frame) {
+ DecompressionVisitor visitor;
+ framer->set_visitor(&visitor);
+ size_t input_size = frame.length() + SpdyFrame::kHeaderSize;
+ CHECK_EQ(input_size, framer->ProcessInput(frame.data(), input_size));
+ CHECK_EQ(SpdyFramer::SPDY_RESET, framer->state());
+ framer->set_visitor(NULL);
+
+ char* buffer = visitor.ReleaseBuffer();
+ CHECK(buffer);
+ SpdyFrame* decompressed_frame = new SpdyFrame(buffer, true);
+ decompressed_frame->set_length(visitor.size() - SpdyFrame::kHeaderSize);
+ return decompressed_frame;
+ }
+
+ class DecompressionVisitor : public SpdyFramerVisitorInterface {
+ public:
+ DecompressionVisitor()
+ : buffer_(NULL), size_(0), finished_(false), allow_data_frames_(false) {
+ }
+
+ virtual void OnControl(const SpdyControlFrame* frame) {
+ CHECK(frame->has_header_block());
+ CHECK(!buffer_.get());
+ CHECK_EQ(size_, 0u);
+ CHECK(!finished_);
+
+ int32 control_frame_header_size = 0;
+ switch (frame->type()) {
+ case SYN_STREAM:
+ control_frame_header_size = SpdySynStreamControlFrame::size();
+ break;
+ case SYN_REPLY:
+ control_frame_header_size = SpdySynReplyControlFrame::size();
+ break;
+ case HEADERS:
+ control_frame_header_size = SpdyHeadersControlFrame::size();
+ break;
+ default:
+ LOG(FATAL);
+ return;
+ }
+
+ // Allocate space for the frame, and the copy header over.
+ buffer_.reset(new char[kMaxDecompressedSize]);
+ memcpy(buffer_.get(), frame->data(), control_frame_header_size);
+ size_ += control_frame_header_size;
+ }
+
+ virtual bool OnControlFrameHeaderData(SpdyStreamId stream_id,
+ const char* header_data,
+ size_t len) {
+ CHECK(buffer_.get() != NULL);
+ CHECK_GE(kMaxDecompressedSize, size_ + len);
+ CHECK(!finished_);
+ if (len != 0) {
+ memcpy(buffer_.get() + size_, header_data, len);
+ size_ += len;
+ } else {
+ // Done.
+ finished_ = true;
+ }
+ return true;
+ }
+
+ virtual bool OnCredentialFrameData(const char* header_data,
+ size_t len) {
+ LOG(FATAL) << "Unexpected CREDENTIAL Frame";
+ return false;
+ }
+
+ virtual void OnError(SpdyFramer* framer) { LOG(FATAL); }
+ virtual void OnDataFrameHeader(const SpdyDataFrame* frame) {
+ // For most tests, this class does not expect to see OnDataFrameHeader
+ // calls. Individual tests can override this if they need to.
+ if (!allow_data_frames_) {
+ LOG(FATAL) << "Unexpected data frame header";
+ }
+ }
+ virtual void OnStreamFrameData(SpdyStreamId stream_id,
+ const char* data,
+ size_t len) {
+ LOG(FATAL);
+ }
+ virtual void OnSetting(SpdySettingsIds id, uint8 flags, uint32 value) {
+ LOG(FATAL);
+ }
+
+ char* ReleaseBuffer() {
+ CHECK(finished_);
+ return buffer_.release();
+ }
+
+ size_t size() const {
+ CHECK(finished_);
+ return size_;
+ }
+ void set_allow_data_frames(bool allow) { allow_data_frames_ = allow; }
+
+ private:
+ scoped_array<char> buffer_;
+ size_t size_;
+ bool finished_;
+ bool allow_data_frames_;
+
+ DISALLOW_COPY_AND_ASSIGN(DecompressionVisitor);
+ };
+
+ DISALLOW_COPY_AND_ASSIGN(SpdyFramerTestUtil);
+};
std::string HexDumpWithMarks(const unsigned char* data, int length,
const bool* marks, int mark_length) {
- static const char kHexChars[] = "0123456789ABCDEF";
+ static const char kHexChars[] = "0123456789abcdef";
static const int kColumns = 4;
const int kSizeLimit = 1024;
@@ -58,8 +197,8 @@ void CompareCharArraysWithHexError(
const int actual_len,
const unsigned char* expected,
const int expected_len) {
- const int min_len = actual_len > expected_len ? expected_len : actual_len;
- const int max_len = actual_len > expected_len ? actual_len : expected_len;
+ const int min_len = std::min(actual_len, expected_len);
+ const int max_len = std::max(actual_len, expected_len);
scoped_array<bool> marks(new bool[max_len]);
bool identical = (actual_len == expected_len);
for (int i = 0; i < min_len; ++i) {
@@ -89,13 +228,16 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface {
static const size_t kDefaultCredentialBufferSize = 16 * 1024;
TestSpdyVisitor()
- : use_compression_(false),
+ : framer_(kVer),
+ use_compression_(false),
error_count_(0),
syn_frame_count_(0),
syn_reply_frame_count_(0),
headers_frame_count_(0),
goaway_count_(0),
credential_count_(0),
+ settings_frame_count_(0),
+ setting_count_(0),
data_bytes_(0),
fin_frame_count_(0),
fin_flag_count_(0),
@@ -166,6 +308,9 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface {
case CREDENTIAL:
credential_count_++;
break;
+ case SETTINGS:
+ settings_frame_count_++;
+ break;
default:
DLOG(FATAL); // Error!
}
@@ -173,6 +318,10 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface {
++fin_flag_count_;
}
+ virtual void OnSetting(SpdySettingsIds id, uint8 flags, uint32 value) {
+ setting_count_++;
+ }
+
bool OnControlFrameHeaderData(SpdyStreamId stream_id,
const char* header_data,
size_t len) {
@@ -182,7 +331,7 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface {
++zero_length_control_frame_header_data_count_;
// Indicates end-of-header-block.
CHECK(header_buffer_valid_);
- bool parsed_headers = SpdyFramer::ParseHeaderBlockInBuffer(
+ bool parsed_headers = framer_.ParseHeaderBlockInBuffer(
header_buffer_.get(), header_buffer_length_, &headers_);
DCHECK(parsed_headers);
return true;
@@ -197,7 +346,8 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface {
return true;
}
- bool OnCredentialFrameData(const char* credential_data, size_t len) {
+ bool OnCredentialFrameData(const char* credential_data,
+ size_t len) {
if (len == 0) {
if (!framer_.ParseCredentialData(credential_buffer_.get(),
credential_buffer_length_,
@@ -272,6 +422,8 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface {
int headers_frame_count_;
int goaway_count_;
int credential_count_;
+ int settings_frame_count_;
+ int setting_count_;
int data_bytes_;
int fin_frame_count_; // The count of RST_STREAM type frames received.
int fin_flag_count_; // The count of frames with the FIN flag set.
@@ -297,7 +449,7 @@ class TestSpdyVisitor : public SpdyFramerVisitorInterface {
SpdyCredential credential_;
};
-} // namespace test
+} // namespace test_spdy2
} // namespace spdy
@@ -315,12 +467,13 @@ using spdy::CONTROL_FLAG_NONE;
using spdy::DATA_FLAG_COMPRESSED;
using spdy::DATA_FLAG_FIN;
using spdy::SYN_STREAM;
-using spdy::test::CompareCharArraysWithHexError;
-using spdy::test::TestSpdyVisitor;
+using spdy::test_spdy2::CompareCharArraysWithHexError;
+using spdy::test_spdy2::SpdyFramerTestUtil;
+using spdy::test_spdy2::TestSpdyVisitor;
namespace spdy {
-TEST(SpdyFrameBuilderTest, WriteLimits) {
+TEST(SpdyFrameBuilderSpdy2Test, WriteLimits) {
SpdyFrameBuilder builder(kLengthMask + 4);
// length field should fail.
EXPECT_FALSE(builder.WriteBytes(reinterpret_cast<const void*>(0x1),
@@ -335,7 +488,7 @@ TEST(SpdyFrameBuilderTest, WriteLimits) {
EXPECT_EQ(4 + kLengthMask, static_cast<unsigned>(builder.length()));
}
-class SpdyFramerTest : public PlatformTest {
+class SpdyFramerSpdy2Test : public PlatformTest {
public:
virtual void TearDown() {}
@@ -382,11 +535,11 @@ class SpdyFramerTest : public PlatformTest {
// Test that we can encode and decode a SpdyHeaderBlock in serialized form.
-TEST_F(SpdyFramerTest, HeaderBlockInBuffer) {
+TEST_F(SpdyFramerSpdy2Test, HeaderBlockInBuffer) {
SpdyHeaderBlock headers;
headers["alpha"] = "beta";
headers["gamma"] = "charlie";
- SpdyFramer framer;
+ SpdyFramer framer(kVer);
// Encode the header block into a SynStream frame.
scoped_ptr<SpdySynStreamControlFrame> frame(
@@ -405,11 +558,11 @@ TEST_F(SpdyFramerTest, HeaderBlockInBuffer) {
}
// Test that if there's not a full frame, we fail to parse it.
-TEST_F(SpdyFramerTest, UndersizedHeaderBlockInBuffer) {
+TEST_F(SpdyFramerSpdy2Test, UndersizedHeaderBlockInBuffer) {
SpdyHeaderBlock headers;
headers["alpha"] = "beta";
headers["gamma"] = "charlie";
- SpdyFramer framer;
+ SpdyFramer framer(kVer);
// Encode the header block into a SynStream frame.
scoped_ptr<SpdySynStreamControlFrame> frame(
@@ -424,7 +577,7 @@ TEST_F(SpdyFramerTest, UndersizedHeaderBlockInBuffer) {
&new_headers));
}
-TEST_F(SpdyFramerTest, OutOfOrderHeaders) {
+TEST_F(SpdyFramerSpdy2Test, OutOfOrderHeaders) {
// Frame builder with plentiful buffer size.
SpdyFrameBuilder frame(1024);
@@ -435,12 +588,19 @@ TEST_F(SpdyFramerTest, OutOfOrderHeaders) {
frame.WriteUInt32(0); // Associated stream id
frame.WriteUInt16(0); // Priority.
- frame.WriteUInt16(2); // Number of headers.
- SpdyHeaderBlock::iterator it;
- frame.WriteString("gamma");
- frame.WriteString("gamma");
- frame.WriteString("alpha");
- frame.WriteString("alpha");
+ if (SPDY_VERSION_FOR_TESTS < 3) {
+ frame.WriteUInt16(2); // Number of headers.
+ frame.WriteString("gamma");
+ frame.WriteString("gamma");
+ frame.WriteString("alpha");
+ frame.WriteString("alpha");
+ } else {
+ frame.WriteUInt32(2); // Number of headers.
+ frame.WriteStringPiece32("gamma");
+ frame.WriteStringPiece32("gamma");
+ frame.WriteStringPiece32("alpha");
+ frame.WriteStringPiece32("alpha");
+ }
// write the length
frame.WriteUInt32ToOffset(4, frame.length() - SpdyFrame::kHeaderSize);
@@ -449,14 +609,85 @@ TEST_F(SpdyFramerTest, OutOfOrderHeaders) {
SpdySynStreamControlFrame syn_frame(control_frame->data(), false);
std::string serialized_headers(syn_frame.header_block(),
syn_frame.header_block_len());
- SpdyFramer framer;
+ SpdyFramer framer(kVer);
framer.set_enable_compression(false);
EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.c_str(),
serialized_headers.size(),
&new_headers));
}
-TEST_F(SpdyFramerTest, DuplicateHeader) {
+TEST_F(SpdyFramerSpdy2Test, CreateCredential) {
+ SpdyFramer framer(kVer);
+
+ {
+ const char kDescription[] = "CREDENTIAL frame";
+ const unsigned char kFrameData[] = {
+ 0x80, kVer, 0x00, 0x0A,
+ 0x00, 0x00, 0x00, 0x33,
+ 0x00, 0x03, 0x00, 0x00,
+ 0x00, 0x05, 'p', 'r',
+ 'o', 'o', 'f', 0x00,
+ 0x00, 0x00, 0x06, 'a',
+ ' ', 'c', 'e', 'r',
+ 't', 0x00, 0x00, 0x00,
+ 0x0C, 'a', 'n', 'o',
+ 't', 'h', 'e', 'r',
+ ' ', 'c', 'e', 'r',
+ 't', 0x00, 0x00, 0x00,
+ 0x0A, 'f', 'i', 'n',
+ 'a', 'l', ' ', 'c',
+ 'e', 'r', 't',
+ };
+ SpdyCredential credential;
+ credential.slot = 3;
+ credential.proof = "proof";
+ credential.certs.push_back("a cert");
+ credential.certs.push_back("another cert");
+ credential.certs.push_back("final cert");
+ scoped_ptr<SpdyFrame> frame(framer.CreateCredentialFrame(credential));
+ CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ }
+}
+
+TEST_F(SpdyFramerSpdy2Test, ParseCredentialFrameData) {
+ SpdyFramer framer(kVer);
+
+ {
+ unsigned char kFrameData[] = {
+ 0x80, kVer, 0x00, 0x0A,
+ 0x00, 0x00, 0x00, 0x33,
+ 0x00, 0x03, 0x00, 0x00,
+ 0x00, 0x05, 'p', 'r',
+ 'o', 'o', 'f', 0x00,
+ 0x00, 0x00, 0x06, 'a',
+ ' ', 'c', 'e', 'r',
+ 't', 0x00, 0x00, 0x00,
+ 0x0C, 'a', 'n', 'o',
+ 't', 'h', 'e', 'r',
+ ' ', 'c', 'e', 'r',
+ 't', 0x00, 0x00, 0x00,
+ 0x0A, 'f', 'i', 'n',
+ 'a', 'l', ' ', 'c',
+ 'e', 'r', 't',
+ };
+ SpdyCredentialControlFrame frame(reinterpret_cast<char*>(kFrameData),
+ false);
+ SpdyCredential credential;
+ EXPECT_TRUE(SpdyFramer::ParseCredentialData(frame.payload(), frame.length(),
+ &credential));
+ EXPECT_EQ(3u, credential.slot);
+ EXPECT_EQ("proof", credential.proof);
+ EXPECT_EQ("a cert", credential.certs.front());
+ credential.certs.erase(credential.certs.begin());
+ EXPECT_EQ("another cert", credential.certs.front());
+ credential.certs.erase(credential.certs.begin());
+ EXPECT_EQ("final cert", credential.certs.front());
+ credential.certs.erase(credential.certs.begin());
+ EXPECT_TRUE(credential.certs.empty());
+ }
+}
+
+TEST_F(SpdyFramerSpdy2Test, DuplicateHeader) {
// Frame builder with plentiful buffer size.
SpdyFrameBuilder frame(1024);
@@ -467,12 +698,19 @@ TEST_F(SpdyFramerTest, DuplicateHeader) {
frame.WriteUInt32(0); // associated stream id
frame.WriteUInt16(0); // Priority.
- frame.WriteUInt16(2); // Number of headers.
- SpdyHeaderBlock::iterator it;
- frame.WriteString("name");
- frame.WriteString("value1");
- frame.WriteString("name");
- frame.WriteString("value2");
+ if (SPDY_VERSION_FOR_TESTS < 3) {
+ frame.WriteUInt16(2); // Number of headers.
+ frame.WriteString("name");
+ frame.WriteString("value1");
+ frame.WriteString("name");
+ frame.WriteString("value2");
+ } else {
+ frame.WriteUInt32(2); // Number of headers.
+ frame.WriteStringPiece32("name");
+ frame.WriteStringPiece32("value1");
+ frame.WriteStringPiece32("name");
+ frame.WriteStringPiece32("value2");
+ }
// write the length
frame.WriteUInt32ToOffset(4, frame.length() - SpdyFrame::kHeaderSize);
@@ -481,7 +719,7 @@ TEST_F(SpdyFramerTest, DuplicateHeader) {
SpdySynStreamControlFrame syn_frame(control_frame->data(), false);
std::string serialized_headers(syn_frame.header_block(),
syn_frame.header_block_len());
- SpdyFramer framer;
+ SpdyFramer framer(kVer);
framer.set_enable_compression(false);
// This should fail because duplicate headers are verboten by the spec.
EXPECT_FALSE(framer.ParseHeaderBlockInBuffer(serialized_headers.c_str(),
@@ -489,7 +727,7 @@ TEST_F(SpdyFramerTest, DuplicateHeader) {
&new_headers));
}
-TEST_F(SpdyFramerTest, MultiValueHeader) {
+TEST_F(SpdyFramerSpdy2Test, MultiValueHeader) {
// Frame builder with plentiful buffer size.
SpdyFrameBuilder frame(1024);
@@ -500,11 +738,16 @@ TEST_F(SpdyFramerTest, MultiValueHeader) {
frame.WriteUInt32(0); // associated stream id
frame.WriteUInt16(0); // Priority.
- frame.WriteUInt16(1); // Number of headers.
- SpdyHeaderBlock::iterator it;
- frame.WriteString("name");
std::string value("value1\0value2");
- frame.WriteString(value);
+ if (SPDY_VERSION_FOR_TESTS < 3) {
+ frame.WriteUInt16(1); // Number of headers.
+ frame.WriteString("name");
+ frame.WriteString(value);
+ } else {
+ frame.WriteUInt32(1); // Number of headers.
+ frame.WriteStringPiece32("name");
+ frame.WriteStringPiece32(value);
+ }
// write the length
frame.WriteUInt32ToOffset(4, frame.length() - SpdyFrame::kHeaderSize);
@@ -513,7 +756,7 @@ TEST_F(SpdyFramerTest, MultiValueHeader) {
SpdySynStreamControlFrame syn_frame(control_frame->data(), false);
std::string serialized_headers(syn_frame.header_block(),
syn_frame.header_block_len());
- SpdyFramer framer;
+ SpdyFramer framer(kVer);
framer.set_enable_compression(false);
EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.c_str(),
serialized_headers.size(),
@@ -522,7 +765,7 @@ TEST_F(SpdyFramerTest, MultiValueHeader) {
EXPECT_EQ(value, new_headers.find("name")->second);
}
-TEST_F(SpdyFramerTest, BasicCompression) {
+TEST_F(SpdyFramerSpdy2Test, BasicCompression) {
SpdyHeaderBlock headers;
headers["server"] = "SpdyServer 1.0";
headers["date"] = "Mon 12 Jan 2009 12:12:12 PST";
@@ -531,7 +774,7 @@ TEST_F(SpdyFramerTest, BasicCompression) {
headers["content-type"] = "text/html";
headers["content-length"] = "12";
- SpdyFramer framer;
+ SpdyFramer framer(kVer);
framer.set_enable_compression(true);
scoped_ptr<SpdySynStreamControlFrame>
frame1(framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, true,
@@ -544,10 +787,12 @@ TEST_F(SpdyFramerTest, BasicCompression) {
EXPECT_LE(frame2->length(), frame1->length());
// Decompress the first frame
- scoped_ptr<SpdyFrame> frame3(framer.DecompressFrame(*frame1.get()));
+ scoped_ptr<SpdyFrame> frame3(SpdyFramerTestUtil::DecompressFrame(
+ &framer, *frame1.get()));
// Decompress the second frame
- scoped_ptr<SpdyFrame> frame4(framer.DecompressFrame(*frame2.get()));
+ scoped_ptr<SpdyFrame> frame4(SpdyFramerTestUtil::DecompressFrame(
+ &framer, *frame2.get()));
// Expect frames 3 & 4 to be the same.
EXPECT_EQ(0,
@@ -566,30 +811,9 @@ TEST_F(SpdyFramerTest, BasicCompression) {
SpdyFrame::kHeaderSize + uncompressed_frame->length()));
}
-TEST_F(SpdyFramerTest, DecompressUncompressedFrame) {
- SpdyHeaderBlock headers;
- headers["server"] = "SpdyServer 1.0";
- headers["date"] = "Mon 12 Jan 2009 12:12:12 PST";
- headers["status"] = "200";
- headers["version"] = "HTTP/1.1";
- headers["content-type"] = "text/html";
- headers["content-length"] = "12";
-
- SpdyFramer framer;
- framer.set_enable_compression(true);
- scoped_ptr<SpdySynStreamControlFrame>
- frame1(framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, false,
- &headers));
-
- // Decompress the frame
- scoped_ptr<SpdyFrame> frame2(framer.DecompressFrame(*frame1.get()));
-
- EXPECT_EQ(NULL, frame2.get());
-}
-
-TEST_F(SpdyFramerTest, Basic) {
- const unsigned char input[] = {
- 0x80, 0x02, 0x00, 0x01, // SYN Stream #1
+TEST_F(SpdyFramerSpdy2Test, Basic) {
+ const unsigned char kV2Input[] = {
+ 0x80, kVer, 0x00, 0x01, // SYN Stream #1
0x00, 0x00, 0x00, 0x14,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
@@ -597,7 +821,7 @@ TEST_F(SpdyFramerTest, Basic) {
0x00, 0x02, 'h', 'h',
0x00, 0x02, 'v', 'v',
- 0x80, 0x02, 0x00, 0x08, // HEADERS on Stream #1
+ 0x80, kVer, 0x00, 0x08, // HEADERS on Stream #1
0x00, 0x00, 0x00, 0x18,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x02,
@@ -608,11 +832,11 @@ TEST_F(SpdyFramerTest, Basic) {
0x00, 0x00, 0x00, 0x01, // DATA on Stream #1
0x00, 0x00, 0x00, 0x0c,
- 0xde, 0xad, 0xbe, 0xef,
- 0xde, 0xad, 0xbe, 0xef,
- 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
- 0x80, 0x02, 0x00, 0x01, // SYN Stream #3
+ 0x80, kVer, 0x00, 0x01, // SYN Stream #3
0x00, 0x00, 0x00, 0x0c,
0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x00,
@@ -620,14 +844,14 @@ TEST_F(SpdyFramerTest, Basic) {
0x00, 0x00, 0x00, 0x03, // DATA on Stream #3
0x00, 0x00, 0x00, 0x08,
- 0xde, 0xad, 0xbe, 0xef,
- 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
0x00, 0x00, 0x00, 0x01, // DATA on Stream #1
0x00, 0x00, 0x00, 0x04,
- 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
- 0x80, 0x02, 0x00, 0x03, // RST_STREAM on Stream #1
+ 0x80, kVer, 0x00, 0x03, // RST_STREAM on Stream #1
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
@@ -635,14 +859,77 @@ TEST_F(SpdyFramerTest, Basic) {
0x00, 0x00, 0x00, 0x03, // DATA on Stream #3
0x00, 0x00, 0x00, 0x00,
- 0x80, 0x02, 0x00, 0x03, // RST_STREAM on Stream #3
+ 0x80, kVer, 0x00, 0x03, // RST_STREAM on Stream #3
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ const unsigned char kV3Input[] = {
+ 0x80, kVer, 0x00, 0x01, // SYN Stream #1
+ 0x00, 0x00, 0x00, 0x1a,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x02, 'h', 'h',
+ 0x00, 0x00, 0x00, 0x02,
+ 'v', 'v',
+
+ 0x80, kVer, 0x00, 0x08, // HEADERS on Stream #1
+ 0x00, 0x00, 0x00, 0x22,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x00,
+ 0x00, 0x02, 'h', '2',
+ 0x00, 0x00, 0x00, 0x02,
+ 'v', '2', 0x00, 0x00,
+ 0x00, 0x02, 'h', '3',
+ 0x00, 0x00, 0x00, 0x02,
+ 'v', '3',
+
+ 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1
+ 0x00, 0x00, 0x00, 0x0c,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+
+ 0x80, kVer, 0x00, 0x01, // SYN Stream #3
+ 0x00, 0x00, 0x00, 0x0e,
+ 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x03, // DATA on Stream #3
+ 0x00, 0x00, 0x00, 0x08,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+
+ 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1
+ 0x00, 0x00, 0x00, 0x04,
+ 0xde, 0xad, 0xbe, 0xef,
+
+ 0x80, kVer, 0x00, 0x03, // RST_STREAM on Stream #1
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x03, // DATA on Stream #3
+ 0x00, 0x00, 0x00, 0x00,
+
+ 0x80, kVer, 0x00, 0x03, // RST_STREAM on Stream #3
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x00,
};
TestSpdyVisitor visitor;
- visitor.SimulateInFramer(input, sizeof(input));
+ if (SPDY_VERSION_FOR_TESTS < 3) {
+ visitor.SimulateInFramer(kV2Input, sizeof(kV2Input));
+ } else {
+ visitor.SimulateInFramer(kV3Input, sizeof(kV3Input));
+ }
EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(2, visitor.syn_frame_count_);
@@ -656,9 +943,9 @@ TEST_F(SpdyFramerTest, Basic) {
}
// Test that the FIN flag on a data frame signifies EOF.
-TEST_F(SpdyFramerTest, FinOnDataFrame) {
- const unsigned char input[] = {
- 0x80, 0x02, 0x00, 0x01, // SYN Stream #1
+TEST_F(SpdyFramerSpdy2Test, FinOnDataFrame) {
+ const unsigned char kV2Input[] = {
+ 0x80, kVer, 0x00, 0x01, // SYN Stream #1
0x00, 0x00, 0x00, 0x14,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
@@ -666,7 +953,7 @@ TEST_F(SpdyFramerTest, FinOnDataFrame) {
0x00, 0x02, 'h', 'h',
0x00, 0x02, 'v', 'v',
- 0x80, 0x02, 0x00, 0x02, // SYN REPLY Stream #1
+ 0x80, kVer, 0x00, 0x02, // SYN REPLY Stream #1
0x00, 0x00, 0x00, 0x10,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01,
@@ -683,9 +970,43 @@ TEST_F(SpdyFramerTest, FinOnDataFrame) {
0x01, 0x00, 0x00, 0x04,
0xde, 0xad, 0xbe, 0xef,
};
+ const unsigned char kV3Input[] = {
+ 0x80, kVer, 0x00, 0x01, // SYN Stream #1
+ 0x00, 0x00, 0x00, 0x1a,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x02, 'h', 'h',
+ 0x00, 0x00, 0x00, 0x02,
+ 'v', 'v',
+
+ 0x80, kVer, 0x00, 0x02, // SYN REPLY Stream #1
+ 0x00, 0x00, 0x00, 0x16,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x02, 'a', 'a',
+ 0x00, 0x00, 0x00, 0x02,
+ 'b', 'b',
+
+ 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1
+ 0x00, 0x00, 0x00, 0x0c,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+
+ 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1, with EOF
+ 0x01, 0x00, 0x00, 0x04,
+ 0xde, 0xad, 0xbe, 0xef,
+ };
TestSpdyVisitor visitor;
- visitor.SimulateInFramer(input, sizeof(input));
+ if (SPDY_VERSION_FOR_TESTS < 3) {
+ visitor.SimulateInFramer(kV2Input, sizeof(kV2Input));
+ } else {
+ visitor.SimulateInFramer(kV3Input, sizeof(kV3Input));
+ }
EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(1, visitor.syn_frame_count_);
@@ -699,9 +1020,9 @@ TEST_F(SpdyFramerTest, FinOnDataFrame) {
}
// Test that the FIN flag on a SYN reply frame signifies EOF.
-TEST_F(SpdyFramerTest, FinOnSynReplyFrame) {
- const unsigned char input[] = {
- 0x80, 0x02, 0x00, 0x01, // SYN Stream #1
+TEST_F(SpdyFramerSpdy2Test, FinOnSynReplyFrame) {
+ const unsigned char kV2Input[] = {
+ 0x80, kVer, 0x00, 0x01, // SYN Stream #1
0x00, 0x00, 0x00, 0x14,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
@@ -709,7 +1030,7 @@ TEST_F(SpdyFramerTest, FinOnSynReplyFrame) {
0x00, 0x02, 'h', 'h',
0x00, 0x02, 'v', 'v',
- 0x80, 0x02, 0x00, 0x02, // SYN REPLY Stream #1
+ 0x80, kVer, 0x00, 0x02, // SYN REPLY Stream #1
0x01, 0x00, 0x00, 0x14,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
@@ -717,9 +1038,34 @@ TEST_F(SpdyFramerTest, FinOnSynReplyFrame) {
0x00, 0x02, 'a', 'a',
0x00, 0x02, 'b', 'b',
};
+ const unsigned char kV3Input[] = {
+ 0x80, kVer, 0x00, 0x01, // SYN Stream #1
+ 0x00, 0x00, 0x00, 0x1a,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x02, 'h', 'h',
+ 0x00, 0x00, 0x00, 0x02,
+ 'v', 'v',
+
+ 0x80, kVer, 0x00, 0x02, // SYN REPLY Stream #1
+ 0x01, 0x00, 0x00, 0x1a,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x02, 'a', 'a',
+ 0x00, 0x00, 0x00, 0x02,
+ 'b', 'b',
+ };
TestSpdyVisitor visitor;
- visitor.SimulateInFramer(input, sizeof(input));
+ if (SPDY_VERSION_FOR_TESTS < 3) {
+ visitor.SimulateInFramer(kV2Input, sizeof(kV2Input));
+ } else {
+ visitor.SimulateInFramer(kV3Input, sizeof(kV3Input));
+ }
EXPECT_EQ(0, visitor.error_count_);
EXPECT_EQ(1, visitor.syn_frame_count_);
@@ -732,9 +1078,9 @@ TEST_F(SpdyFramerTest, FinOnSynReplyFrame) {
EXPECT_EQ(0, visitor.data_frame_count_);
}
-TEST_F(SpdyFramerTest, HeaderCompression) {
- SpdyFramer send_framer;
- SpdyFramer recv_framer;
+TEST_F(SpdyFramerSpdy2Test, HeaderCompression) {
+ SpdyFramer send_framer(kVer);
+ SpdyFramer recv_framer(kVer);
send_framer.set_enable_compression(true);
recv_framer.set_enable_compression(true);
@@ -751,33 +1097,30 @@ TEST_F(SpdyFramerTest, HeaderCompression) {
block[kHeader1] = kValue1;
block[kHeader2] = kValue2;
SpdyControlFlags flags(CONTROL_FLAG_NONE);
- scoped_ptr<spdy::SpdyFrame> syn_frame_1(
+ scoped_ptr<SpdySynStreamControlFrame> syn_frame_1(
send_framer.CreateSynStream(1, 0, 0, flags, true, &block));
EXPECT_TRUE(syn_frame_1.get() != NULL);
// SYN_STREAM #2
block[kHeader3] = kValue3;
- scoped_ptr<spdy::SpdyFrame> syn_frame_2(
+ scoped_ptr<SpdySynStreamControlFrame> syn_frame_2(
send_framer.CreateSynStream(3, 0, 0, flags, true, &block));
EXPECT_TRUE(syn_frame_2.get() != NULL);
// Now start decompressing
scoped_ptr<SpdyFrame> decompressed;
- scoped_ptr<SpdyFrame> decompressed_syn_frame;
- SpdySynStreamControlFrame* syn_frame;
+ scoped_ptr<SpdySynStreamControlFrame> syn_frame;
scoped_ptr<std::string> serialized_headers;
SpdyHeaderBlock decompressed_headers;
// Decompress SYN_STREAM #1
- decompressed.reset(recv_framer.DecompressFrame(*syn_frame_1.get()));
+ decompressed.reset(SpdyFramerTestUtil::DecompressFrame(
+ &recv_framer, *syn_frame_1.get()));
EXPECT_TRUE(decompressed.get() != NULL);
EXPECT_TRUE(decompressed->is_control_frame());
EXPECT_EQ(SYN_STREAM,
reinterpret_cast<SpdyControlFrame*>(decompressed.get())->type());
- decompressed_syn_frame.reset(
- new SpdySynStreamControlFrame(decompressed->data(), false));
- syn_frame = reinterpret_cast<SpdySynStreamControlFrame*>(
- decompressed_syn_frame.get());
+ syn_frame.reset(new SpdySynStreamControlFrame(decompressed->data(), false));
serialized_headers.reset(new std::string(syn_frame->header_block(),
syn_frame->header_block_len()));
EXPECT_TRUE(recv_framer.ParseHeaderBlockInBuffer(serialized_headers->c_str(),
@@ -788,15 +1131,13 @@ TEST_F(SpdyFramerTest, HeaderCompression) {
EXPECT_EQ(kValue2, decompressed_headers[kHeader2]);
// Decompress SYN_STREAM #2
- decompressed.reset(recv_framer.DecompressFrame(*syn_frame_2.get()));
+ decompressed.reset(SpdyFramerTestUtil::DecompressFrame(
+ &recv_framer, *syn_frame_2.get()));
EXPECT_TRUE(decompressed.get() != NULL);
EXPECT_TRUE(decompressed->is_control_frame());
EXPECT_EQ(SYN_STREAM,
reinterpret_cast<SpdyControlFrame*>(decompressed.get())->type());
- decompressed_syn_frame.reset(
- new SpdySynStreamControlFrame(decompressed->data(), false));
- syn_frame = reinterpret_cast<SpdySynStreamControlFrame*>(
- decompressed_syn_frame.get());
+ syn_frame.reset(new SpdySynStreamControlFrame(decompressed->data(), false));
serialized_headers.reset(new std::string(syn_frame->header_block(),
syn_frame->header_block_len()));
decompressed_headers.clear();
@@ -816,8 +1157,8 @@ TEST_F(SpdyFramerTest, HeaderCompression) {
}
// Verify we don't leak when we leave streams unclosed
-TEST_F(SpdyFramerTest, UnclosedStreamDataCompressors) {
- SpdyFramer send_framer;
+TEST_F(SpdyFramerSpdy2Test, UnclosedStreamDataCompressors) {
+ SpdyFramer send_framer(kVer);
send_framer.set_enable_compression(true);
@@ -830,7 +1171,7 @@ TEST_F(SpdyFramerTest, UnclosedStreamDataCompressors) {
block[kHeader1] = kValue1;
block[kHeader2] = kValue2;
SpdyControlFlags flags(CONTROL_FLAG_NONE);
- scoped_ptr<spdy::SpdyFrame> syn_frame(
+ scoped_ptr<SpdyFrame> syn_frame(
send_framer.CreateSynStream(1, 0, 0, flags, true, &block));
EXPECT_TRUE(syn_frame.get() != NULL);
@@ -838,7 +1179,7 @@ TEST_F(SpdyFramerTest, UnclosedStreamDataCompressors) {
scoped_ptr<SpdyFrame> send_frame(
send_framer.CreateDataFrame(
1, bytes, arraysize(bytes),
- DATA_FLAG_FIN));
+ static_cast<SpdyDataFlags>(DATA_FLAG_FIN)));
EXPECT_TRUE(send_frame.get() != NULL);
// Run the inputs through the framer.
@@ -867,12 +1208,76 @@ TEST_F(SpdyFramerTest, UnclosedStreamDataCompressors) {
EXPECT_EQ(0, send_framer.num_stream_decompressors());
}
-TEST_F(SpdyFramerTest, WindowUpdateFrame) {
+// Verify we can decompress the stream even if handed over to the
+// framer 1 byte at a time.
+TEST_F(SpdyFramerSpdy2Test, UnclosedStreamDataCompressorsOneByteAtATime) {
+ SpdyFramer send_framer(kVer);
+
+ send_framer.set_enable_compression(true);
+
+ const char kHeader1[] = "header1";
+ const char kHeader2[] = "header2";
+ const char kValue1[] = "value1";
+ const char kValue2[] = "value2";
+
+ SpdyHeaderBlock block;
+ block[kHeader1] = kValue1;
+ block[kHeader2] = kValue2;
+ SpdyControlFlags flags(CONTROL_FLAG_NONE);
+ scoped_ptr<SpdyFrame> syn_frame(
+ send_framer.CreateSynStream(1, 0, 0, flags, true, &block));
+ EXPECT_TRUE(syn_frame.get() != NULL);
+
+ const char bytes[] = "this is a test test test test test!";
+ scoped_ptr<SpdyFrame> send_frame(
+ send_framer.CreateDataFrame(
+ 1, bytes, arraysize(bytes),
+ static_cast<SpdyDataFlags>(DATA_FLAG_FIN)));
+ EXPECT_TRUE(send_frame.get() != NULL);
+
+ // Run the inputs through the framer.
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = true;
+ const unsigned char* data;
+ data = reinterpret_cast<const unsigned char*>(syn_frame->data());
+ for (size_t idx = 0;
+ idx < syn_frame->length() + SpdyFrame::kHeaderSize;
+ ++idx) {
+ visitor.SimulateInFramer(data + idx, 1);
+ ASSERT_EQ(0, visitor.error_count_);
+ }
+ data = reinterpret_cast<const unsigned char*>(send_frame->data());
+ for (size_t idx = 0;
+ idx < send_frame->length() + SpdyFrame::kHeaderSize;
+ ++idx) {
+ visitor.SimulateInFramer(data + idx, 1);
+ ASSERT_EQ(0, visitor.error_count_);
+ }
+
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(1, visitor.syn_frame_count_);
+ EXPECT_EQ(0, visitor.syn_reply_frame_count_);
+ EXPECT_EQ(0, visitor.headers_frame_count_);
+ EXPECT_EQ(arraysize(bytes), static_cast<unsigned>(visitor.data_bytes_));
+ EXPECT_EQ(0, visitor.fin_frame_count_);
+ EXPECT_EQ(0, visitor.fin_flag_count_);
+ EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
+ EXPECT_EQ(1, visitor.data_frame_count_);
+
+ // We closed the streams, so all compressors should be down.
+ EXPECT_EQ(0, visitor.framer_.num_stream_compressors());
+ EXPECT_EQ(0, visitor.framer_.num_stream_decompressors());
+ EXPECT_EQ(0, send_framer.num_stream_compressors());
+ EXPECT_EQ(0, send_framer.num_stream_decompressors());
+}
+
+TEST_F(SpdyFramerSpdy2Test, WindowUpdateFrame) {
+ SpdyFramer framer(kVer);
scoped_ptr<SpdyWindowUpdateControlFrame> window_update_frame(
- SpdyFramer::CreateWindowUpdate(1, 0x12345678));
+ framer.CreateWindowUpdate(1, 0x12345678));
const unsigned char expected_data_frame[] = {
- 0x80, 0x02, 0x00, 0x09,
+ 0x80, kVer, 0x00, 0x09,
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x01,
0x12, 0x34, 0x56, 0x78
@@ -883,8 +1288,8 @@ TEST_F(SpdyFramerTest, WindowUpdateFrame) {
memcmp(window_update_frame->data(), expected_data_frame, 16));
}
-TEST_F(SpdyFramerTest, CreateDataFrame) {
- SpdyFramer framer;
+TEST_F(SpdyFramerSpdy2Test, CreateDataFrame) {
+ SpdyFramer framer(kVer);
{
const char kDescription[] = "'hello' data frame, no FIN";
@@ -969,8 +1374,8 @@ TEST_F(SpdyFramerTest, CreateDataFrame) {
}
}
-TEST_F(SpdyFramerTest, CreateSynStreamUncompressed) {
- SpdyFramer framer;
+TEST_F(SpdyFramerSpdy2Test, CreateSynStreamUncompressed) {
+ SpdyFramer framer(kVer);
framer.set_enable_compression(false);
{
@@ -980,24 +1385,43 @@ TEST_F(SpdyFramerTest, CreateSynStreamUncompressed) {
headers["bar"] = "foo";
headers["foo"] = "bar";
- const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x01,
+ const unsigned char kPri =
+ (SPDY_VERSION_FOR_TESTS != 2) ? 0xE0 : 0xC0;
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x01,
0x00, 0x00, 0x00, 0x20,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
- 0xC0, 0x00, 0x00, 0x02,
+ kPri, 0x00, 0x00, 0x02,
0x00, 0x03, 'b', 'a',
'r', 0x00, 0x03, 'f',
'o', 'o', 0x00, 0x03,
'f', 'o', 'o', 0x00,
0x03, 'b', 'a', 'r'
};
- scoped_ptr<SpdyFrame> frame(framer.CreateSynStream(
- 1, 0, SPDY_PRIORITY_LOWEST, CONTROL_FLAG_NONE,
- false, &headers));
- CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
- EXPECT_EQ(1u, SpdyFramer::GetControlFrameStreamId(
- reinterpret_cast<const SpdyControlFrame*>(frame.get())));
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x2a,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ kPri, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x00,
+ 0x00, 0x03, 'b', 'a',
+ 'r', 0x00, 0x00, 0x00,
+ 0x03, 'f', 'o', 'o',
+ 0x00, 0x00, 0x00, 0x03,
+ 'f', 'o', 'o', 0x00,
+ 0x00, 0x00, 0x03, 'b',
+ 'a', 'r'
+ };
+ scoped_ptr<SpdySynStreamControlFrame> frame(framer.CreateSynStream(
+ 1, 0, framer.GetLowestPriority(), CONTROL_FLAG_NONE, false, &headers));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
+ EXPECT_EQ(1u, SpdyFramer::GetControlFrameStreamId(frame.get()));
}
{
@@ -1009,8 +1433,8 @@ TEST_F(SpdyFramerTest, CreateSynStreamUncompressed) {
headers[""] = "foo";
headers["foo"] = "bar";
- const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x01,
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x01,
0x01, 0x00, 0x00, 0x1D,
0x7f, 0xff, 0xff, 0xff,
0x7f, 0xff, 0xff, 0xff,
@@ -1021,58 +1445,97 @@ TEST_F(SpdyFramerTest, CreateSynStreamUncompressed) {
0x00, 0x03, 'b', 'a',
'r'
};
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x01,
+ 0x01, 0x00, 0x00, 0x27,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 'f', 'o',
+ 'o', 0x00, 0x00, 0x00,
+ 0x03, 'f', 'o', 'o',
+ 0x00, 0x00, 0x00, 0x03,
+ 'b', 'a', 'r'
+ };
scoped_ptr<SpdyFrame> frame(framer.CreateSynStream(
- 0x7fffffff, 0x7fffffff, SPDY_PRIORITY_HIGHEST, CONTROL_FLAG_FIN,
+ 0x7fffffff, 0x7fffffff, framer.GetHighestPriority(), CONTROL_FLAG_FIN,
false, &headers));
- CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
}
{
const char kDescription[] =
- "SYN_STREAM frame with a 0-length header val, highest pri, FIN, "
+ "SYN_STREAM frame with a 0-length header val, high pri, FIN, "
"max stream ID";
SpdyHeaderBlock headers;
headers["bar"] = "foo";
headers["foo"] = "";
- const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x01,
+ const unsigned char kPri =
+ (SPDY_VERSION_FOR_TESTS != 2) ? 0x20 : 0x40;
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x01,
0x01, 0x00, 0x00, 0x1D,
0x7f, 0xff, 0xff, 0xff,
0x7f, 0xff, 0xff, 0xff,
- 0x00, 0x00, 0x00, 0x02,
+ kPri, 0x00, 0x00, 0x02,
0x00, 0x03, 'b', 'a',
'r', 0x00, 0x03, 'f',
'o', 'o', 0x00, 0x03,
'f', 'o', 'o', 0x00,
0x00
};
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x01,
+ 0x01, 0x00, 0x00, 0x27,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x7f, 0xff, 0xff, 0xff,
+ kPri, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x00,
+ 0x00, 0x03, 'b', 'a',
+ 'r', 0x00, 0x00, 0x00,
+ 0x03, 'f', 'o', 'o',
+ 0x00, 0x00, 0x00, 0x03,
+ 'f', 'o', 'o', 0x00,
+ 0x00, 0x00, 0x00
+ };
scoped_ptr<SpdyFrame> frame(framer.CreateSynStream(
- 0x7fffffff, 0x7fffffff, SPDY_PRIORITY_HIGHEST, CONTROL_FLAG_FIN,
- false, &headers));
- CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ 0x7fffffff, 0x7fffffff, 1, CONTROL_FLAG_FIN, false, &headers));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
}
}
-TEST_F(SpdyFramerTest, CreateSynStreamCompressed) {
- SpdyFramer framer;
+TEST_F(SpdyFramerSpdy2Test, CreateSynStreamCompressed) {
+ SpdyFramer framer(kVer);
framer.set_enable_compression(true);
{
const char kDescription[] =
- "SYN_STREAM frame, lowest pri, no FIN";
+ "SYN_STREAM frame, low pri, no FIN";
SpdyHeaderBlock headers;
headers["bar"] = "foo";
headers["foo"] = "bar";
- const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x01,
+ const SpdyPriority priority =
+ (SPDY_VERSION_FOR_TESTS != 2) ? 4 : 2;
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x01,
0x00, 0x00, 0x00, 0x25,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
- 0xC0, 0x00, 0x38, 0xea,
+ 0x80, 0x00, 0x38, 0xea,
0xdf, 0xa2, 0x51, 0xb2,
0x62, 0x60, 0x62, 0x60,
0x4e, 0x4a, 0x2c, 0x62,
@@ -1081,15 +1544,32 @@ TEST_F(SpdyFramerTest, CreateSynStreamCompressed) {
0x00, 0x00, 0x00, 0xff,
0xff
};
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x27,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x80, 0x00, 0x38, 0xEA,
+ 0xE3, 0xC6, 0xA7, 0xC2,
+ 0x02, 0xE5, 0x0E, 0x50,
+ 0xC2, 0x4B, 0x4A, 0x04,
+ 0xE5, 0x0B, 0xE6, 0xB4,
+ 0xFC, 0x7C, 0x24, 0x0A,
+ 0x28, 0x08, 0x00, 0x00,
+ 0x00, 0xFF, 0xFF
+ };
scoped_ptr<SpdyFrame> frame(framer.CreateSynStream(
- 1, 0, SPDY_PRIORITY_LOWEST, CONTROL_FLAG_NONE,
- true, &headers));
- CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ 1, 0, priority, CONTROL_FLAG_NONE, true, &headers));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
}
}
-TEST_F(SpdyFramerTest, CreateSynReplyUncompressed) {
- SpdyFramer framer;
+TEST_F(SpdyFramerSpdy2Test, CreateSynReplyUncompressed) {
+ SpdyFramer framer(kVer);
framer.set_enable_compression(false);
{
@@ -1099,8 +1579,8 @@ TEST_F(SpdyFramerTest, CreateSynReplyUncompressed) {
headers["bar"] = "foo";
headers["foo"] = "bar";
- const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x02,
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x02,
0x00, 0x00, 0x00, 0x1C,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x02,
@@ -1110,9 +1590,26 @@ TEST_F(SpdyFramerTest, CreateSynReplyUncompressed) {
'f', 'o', 'o', 0x00,
0x03, 'b', 'a', 'r'
};
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x24,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x03,
+ 'b', 'a', 'r', 0x00,
+ 0x00, 0x00, 0x03, 'f',
+ 'o', 'o', 0x00, 0x00,
+ 0x00, 0x03, 'f', 'o',
+ 'o', 0x00, 0x00, 0x00,
+ 0x03, 'b', 'a', 'r'
+ };
scoped_ptr<SpdyFrame> frame(framer.CreateSynReply(
1, CONTROL_FLAG_NONE, false, &headers));
- CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
}
{
@@ -1123,8 +1620,8 @@ TEST_F(SpdyFramerTest, CreateSynReplyUncompressed) {
headers[""] = "foo";
headers["foo"] = "bar";
- const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x02,
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x02,
0x01, 0x00, 0x00, 0x19,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x02,
@@ -1134,9 +1631,26 @@ TEST_F(SpdyFramerTest, CreateSynReplyUncompressed) {
0x00, 0x03, 'b', 'a',
'r'
};
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x02,
+ 0x01, 0x00, 0x00, 0x21,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03,
+ 'f', 'o', 'o', 0x00,
+ 0x00, 0x00, 0x03, 'f',
+ 'o', 'o', 0x00, 0x00,
+ 0x00, 0x03, 'b', 'a',
+ 'r'
+ };
scoped_ptr<SpdyFrame> frame(framer.CreateSynReply(
0x7fffffff, CONTROL_FLAG_FIN, false, &headers));
- CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
}
{
@@ -1147,8 +1661,8 @@ TEST_F(SpdyFramerTest, CreateSynReplyUncompressed) {
headers["bar"] = "foo";
headers["foo"] = "";
- const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x02,
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x02,
0x01, 0x00, 0x00, 0x19,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x02,
@@ -1158,14 +1672,31 @@ TEST_F(SpdyFramerTest, CreateSynReplyUncompressed) {
'f', 'o', 'o', 0x00,
0x00
};
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x02,
+ 0x01, 0x00, 0x00, 0x21,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x03,
+ 'b', 'a', 'r', 0x00,
+ 0x00, 0x00, 0x03, 'f',
+ 'o', 'o', 0x00, 0x00,
+ 0x00, 0x03, 'f', 'o',
+ 'o', 0x00, 0x00, 0x00,
+ 0x00
+ };
scoped_ptr<SpdyFrame> frame(framer.CreateSynReply(
0x7fffffff, CONTROL_FLAG_FIN, false, &headers));
- CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
}
}
-TEST_F(SpdyFramerTest, CreateSynReplyCompressed) {
- SpdyFramer framer;
+TEST_F(SpdyFramerSpdy2Test, CreateSynReplyCompressed) {
+ SpdyFramer framer(kVer);
framer.set_enable_compression(true);
{
@@ -1175,8 +1706,8 @@ TEST_F(SpdyFramerTest, CreateSynReplyCompressed) {
headers["bar"] = "foo";
headers["foo"] = "bar";
- const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x02,
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x02,
0x00, 0x00, 0x00, 0x21,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x38, 0xea,
@@ -1188,33 +1719,50 @@ TEST_F(SpdyFramerTest, CreateSynReplyCompressed) {
0x00, 0x00, 0x00, 0xff,
0xff
};
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x21,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x38, 0xea, 0xe3, 0xc6,
+ 0xa7, 0xc2, 0x02, 0xe5,
+ 0x0e, 0x50, 0xc2, 0x4b,
+ 0x4a, 0x04, 0xe5, 0x0b,
+ 0xe6, 0xb4, 0xfc, 0x7c,
+ 0x24, 0x0a, 0x28, 0x08,
+ 0x00, 0x00, 0x00, 0xff,
+ 0xff
+ };
scoped_ptr<SpdyFrame> frame(framer.CreateSynReply(
1, CONTROL_FLAG_NONE, true, &headers));
- CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
}
}
-TEST_F(SpdyFramerTest, CreateRstStream) {
- SpdyFramer framer;
+TEST_F(SpdyFramerSpdy2Test, CreateRstStream) {
+ SpdyFramer framer(kVer);
{
const char kDescription[] = "RST_STREAM frame";
const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x03,
+ 0x80, kVer, 0x00, 0x03,
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01,
};
- scoped_ptr<SpdyFrame> frame(framer.CreateRstStream(1, PROTOCOL_ERROR));
+ scoped_ptr<SpdyRstStreamControlFrame> frame(
+ framer.CreateRstStream(1, PROTOCOL_ERROR));
CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
- EXPECT_EQ(1u, SpdyFramer::GetControlFrameStreamId(
- reinterpret_cast<const SpdyControlFrame*>(frame.get())));
+ EXPECT_EQ(1u, SpdyFramer::GetControlFrameStreamId(frame.get()));
}
{
const char kDescription[] = "RST_STREAM frame with max stream ID";
const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x03,
+ 0x80, kVer, 0x00, 0x03,
0x00, 0x00, 0x00, 0x08,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x01,
@@ -1227,7 +1775,7 @@ TEST_F(SpdyFramerTest, CreateRstStream) {
{
const char kDescription[] = "RST_STREAM frame with max status code";
const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x03,
+ 0x80, kVer, 0x00, 0x03,
0x00, 0x00, 0x00, 0x08,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x06,
@@ -1238,51 +1786,108 @@ TEST_F(SpdyFramerTest, CreateRstStream) {
}
}
-TEST_F(SpdyFramerTest, CreateSettings) {
- SpdyFramer framer;
+TEST_F(SpdyFramerSpdy2Test, CreateSettings) {
+ SpdyFramer framer(kVer);
+
+ {
+ const char kDescription[] = "Network byte order SETTINGS frame";
+
+ uint32 kValue = 0x0a0b0c0d;
+ uint8 kFlags = 0x04;
+ uint32 kId = 0x030201;
+ SettingsFlagsAndId idAndFlags(kFlags, kId);
+
+ SpdySettings settings;
+ settings.push_back(SpdySetting(idAndFlags, kValue));
+
+ EXPECT_EQ(kValue, settings.back().second);
+ EXPECT_EQ(kFlags, settings.back().first.flags());
+ EXPECT_EQ(kId, settings.back().first.id());
+
+ const unsigned char kFrameDatav2[] = {
+ 0x80, kVer, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x0c,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x02, 0x03, 0x04,
+ 0x0a, 0x0b, 0x0c, 0x0d,
+ };
+
+ const unsigned char kFrameDatav3[] = {
+ 0x80, kVer, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x0c,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x04, 0x03, 0x02, 0x01,
+ 0x0a, 0x0b, 0x0c, 0x0d,
+ };
+
+ scoped_ptr<SpdySettingsControlFrame> frame(framer.CreateSettings(settings));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kFrameDatav2 : kFrameDatav3,
+ arraysize(kFrameDatav3)); // Size is unchanged among versions.
+ EXPECT_EQ(SpdyFramer::kInvalidStream,
+ SpdyFramer::GetControlFrameStreamId(frame.get()));
+
+ // Make sure that ParseSettings also works as advertised.
+ SpdySettings parsed_settings;
+ EXPECT_TRUE(framer.ParseSettings(frame.get(), &parsed_settings));
+ EXPECT_EQ(settings.size(), parsed_settings.size());
+ EXPECT_EQ(kFlags, parsed_settings.back().first.flags());
+ EXPECT_EQ(kId, parsed_settings.back().first.id());
+ }
{
const char kDescription[] = "Basic SETTINGS frame";
SpdySettings settings;
- settings.push_back(SpdySetting(0x00000000, 0x00000000));
- settings.push_back(SpdySetting(0xffffffff, 0x00000001));
- settings.push_back(SpdySetting(0xff000001, 0x00000002));
+ settings.push_back(
+ SpdySettingFromWireFormat(0x00000000, 0x00000000)); // 1st Setting
+ settings.push_back(
+ SpdySettingFromWireFormat(0xffffffff, 0x00000001)); // 2nd Setting
+ settings.push_back(
+ SpdySettingFromWireFormat(0xff000001, 0x00000002)); // 3rd Setting
// Duplicates allowed
- settings.push_back(SpdySetting(0x01000002, 0x00000003));
- settings.push_back(SpdySetting(0x01000002, 0x00000003));
-
- settings.push_back(SpdySetting(0x01000003, 0x000000ff));
- settings.push_back(SpdySetting(0x01000004, 0xff000001));
- settings.push_back(SpdySetting(0x01000004, 0xffffffff));
+ settings.push_back(
+ SpdySettingFromWireFormat(0x01000002, 0x00000003)); // 4th Setting
+ settings.push_back(
+ SpdySettingFromWireFormat(0x01000002, 0x00000003)); // 5th Setting
+
+ settings.push_back(
+ SpdySettingFromWireFormat(0x01000003, 0x000000ff)); // 6th Setting
+ settings.push_back(
+ SpdySettingFromWireFormat(0x01000004, 0xff000001)); // 7th Setting
+ settings.push_back(
+ SpdySettingFromWireFormat(0x01000004, 0xffffffff)); // 8th Setting
const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x04,
+ 0x80, kVer, 0x00, 0x04,
0x00, 0x00, 0x00, 0x44,
0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, // 1st Setting
0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, // 2nd Setting
0x00, 0x00, 0x00, 0x01,
- 0xff, 0x00, 0x00, 0x01,
+ 0x01, 0x00, 0x00, 0xff, // 3rd Setting
0x00, 0x00, 0x00, 0x02,
- 0x01, 0x00, 0x00, 0x02,
+ 0x02, 0x00, 0x00, 0x01, // 4th Setting
0x00, 0x00, 0x00, 0x03,
- 0x01, 0x00, 0x00, 0x02,
+ 0x02, 0x00, 0x00, 0x01, // 5th Setting
0x00, 0x00, 0x00, 0x03,
- 0x01, 0x00, 0x00, 0x03,
+ 0x03, 0x00, 0x00, 0x01, // 6th Setting
0x00, 0x00, 0x00, 0xff,
- 0x01, 0x00, 0x00, 0x04,
+ 0x04, 0x00, 0x00, 0x01, // 7th Setting
0xff, 0x00, 0x00, 0x01,
- 0x01, 0x00, 0x00, 0x04,
+ 0x04, 0x00, 0x00, 0x01, // 8th Setting
0xff, 0xff, 0xff, 0xff,
};
- scoped_ptr<SpdyFrame> frame(framer.CreateSettings(settings));
- CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ scoped_ptr<SpdySettingsControlFrame> frame(framer.CreateSettings(settings));
+ CompareFrame(kDescription,
+ *frame,
+ kFrameData,
+ arraysize(kFrameData));
EXPECT_EQ(SpdyFramer::kInvalidStream,
- SpdyFramer::GetControlFrameStreamId(
- reinterpret_cast<const SpdyControlFrame*>(frame.get())));
+ SpdyFramer::GetControlFrameStreamId(frame.get()));
}
{
@@ -1291,7 +1896,7 @@ TEST_F(SpdyFramerTest, CreateSettings) {
SpdySettings settings;
const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x04,
+ 0x80, kVer, 0x00, 0x04,
0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00,
};
@@ -1300,62 +1905,43 @@ TEST_F(SpdyFramerTest, CreateSettings) {
}
}
-TEST_F(SpdyFramerTest, CreateNopFrame) {
- SpdyFramer framer;
-
- {
- const char kDescription[] = "NOOP frame";
- const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x05,
- 0x00, 0x00, 0x00, 0x00,
- };
- scoped_ptr<SpdyFrame> frame(framer.CreateNopFrame());
- CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
- EXPECT_EQ(SpdyFramer::kInvalidStream,
- SpdyFramer::GetControlFrameStreamId(
- reinterpret_cast<const SpdyControlFrame*>(frame.get())));
- }
-}
-
-TEST_F(SpdyFramerTest, CreatePingFrame) {
- SpdyFramer framer;
+TEST_F(SpdyFramerSpdy2Test, CreatePingFrame) {
+ SpdyFramer framer(kVer);
{
const char kDescription[] = "PING frame";
const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x06,
+ 0x80, kVer, 0x00, 0x06,
0x00, 0x00, 0x00, 0x04,
0x12, 0x34, 0x56, 0x78,
};
- scoped_ptr<SpdyFrame> frame(framer.CreatePingFrame(0x12345678u));
+ scoped_ptr<SpdyPingControlFrame> frame(framer.CreatePingFrame(0x12345678u));
CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
EXPECT_EQ(SpdyFramer::kInvalidStream,
- SpdyFramer::GetControlFrameStreamId(
- reinterpret_cast<const SpdyControlFrame*>(frame.get())));
+ SpdyFramer::GetControlFrameStreamId(frame.get()));
}
}
-TEST_F(SpdyFramerTest, CreateGoAway) {
- SpdyFramer framer;
+TEST_F(SpdyFramerSpdy2Test, CreateGoAway) {
+ SpdyFramer framer(kVer);
{
const char kDescription[] = "GOAWAY frame";
const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x07,
+ 0x80, kVer, 0x00, 0x07,
0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00,
};
- scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(0));
+ scoped_ptr<SpdyGoAwayControlFrame> frame(framer.CreateGoAway(0));
CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
EXPECT_EQ(SpdyFramer::kInvalidStream,
- SpdyFramer::GetControlFrameStreamId(
- reinterpret_cast<const SpdyControlFrame*>(frame.get())));
+ SpdyFramer::GetControlFrameStreamId(frame.get()));
}
{
const char kDescription[] = "GOAWAY frame with max stream ID";
const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x07,
+ 0x80, kVer, 0x00, 0x07,
0x00, 0x00, 0x00, 0x04,
0x7f, 0xff, 0xff, 0xff,
};
@@ -1364,8 +1950,8 @@ TEST_F(SpdyFramerTest, CreateGoAway) {
}
}
-TEST_F(SpdyFramerTest, CreateHeadersUncompressed) {
- SpdyFramer framer;
+TEST_F(SpdyFramerSpdy2Test, CreateHeadersUncompressed) {
+ SpdyFramer framer(kVer);
framer.set_enable_compression(false);
{
@@ -1375,8 +1961,8 @@ TEST_F(SpdyFramerTest, CreateHeadersUncompressed) {
headers["bar"] = "foo";
headers["foo"] = "bar";
- const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x08,
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x08,
0x00, 0x00, 0x00, 0x1C,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x02,
@@ -1386,9 +1972,26 @@ TEST_F(SpdyFramerTest, CreateHeadersUncompressed) {
'f', 'o', 'o', 0x00,
0x03, 'b', 'a', 'r'
};
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x24,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x03,
+ 'b', 'a', 'r', 0x00,
+ 0x00, 0x00, 0x03, 'f',
+ 'o', 'o', 0x00, 0x00,
+ 0x00, 0x03, 'f', 'o',
+ 'o', 0x00, 0x00, 0x00,
+ 0x03, 'b', 'a', 'r'
+ };
scoped_ptr<SpdyFrame> frame(framer.CreateHeaders(
1, CONTROL_FLAG_NONE, false, &headers));
- CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
}
{
@@ -1399,8 +2002,8 @@ TEST_F(SpdyFramerTest, CreateHeadersUncompressed) {
headers[""] = "foo";
headers["foo"] = "bar";
- const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x08,
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x08,
0x01, 0x00, 0x00, 0x19,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x02,
@@ -1410,9 +2013,26 @@ TEST_F(SpdyFramerTest, CreateHeadersUncompressed) {
0x00, 0x03, 'b', 'a',
'r'
};
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x08,
+ 0x01, 0x00, 0x00, 0x21,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03,
+ 'f', 'o', 'o', 0x00,
+ 0x00, 0x00, 0x03, 'f',
+ 'o', 'o', 0x00, 0x00,
+ 0x00, 0x03, 'b', 'a',
+ 'r'
+ };
scoped_ptr<SpdyFrame> frame(framer.CreateHeaders(
0x7fffffff, CONTROL_FLAG_FIN, false, &headers));
- CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
}
{
@@ -1423,8 +2043,8 @@ TEST_F(SpdyFramerTest, CreateHeadersUncompressed) {
headers["bar"] = "foo";
headers["foo"] = "";
- const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x08,
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x08,
0x01, 0x00, 0x00, 0x19,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x02,
@@ -1434,14 +2054,31 @@ TEST_F(SpdyFramerTest, CreateHeadersUncompressed) {
'f', 'o', 'o', 0x00,
0x00
};
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x08,
+ 0x01, 0x00, 0x00, 0x21,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x03,
+ 'b', 'a', 'r', 0x00,
+ 0x00, 0x00, 0x03, 'f',
+ 'o', 'o', 0x00, 0x00,
+ 0x00, 0x03, 'f', 'o',
+ 'o', 0x00, 0x00, 0x00,
+ 0x00
+ };
scoped_ptr<SpdyFrame> frame(framer.CreateHeaders(
0x7fffffff, CONTROL_FLAG_FIN, false, &headers));
- CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
}
}
-TEST_F(SpdyFramerTest, CreateHeadersCompressed) {
- SpdyFramer framer;
+TEST_F(SpdyFramerSpdy2Test, CreateHeadersCompressed) {
+ SpdyFramer framer(kVer);
framer.set_enable_compression(true);
{
@@ -1451,8 +2088,8 @@ TEST_F(SpdyFramerTest, CreateHeadersCompressed) {
headers["bar"] = "foo";
headers["foo"] = "bar";
- const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x08,
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x08,
0x00, 0x00, 0x00, 0x21,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x38, 0xea,
@@ -1464,33 +2101,50 @@ TEST_F(SpdyFramerTest, CreateHeadersCompressed) {
0x00, 0x00, 0x00, 0xff,
0xff
};
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x21,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x38, 0xea, 0xe3, 0xc6,
+ 0xa7, 0xc2, 0x02, 0xe5,
+ 0x0e, 0x50, 0xc2, 0x4b,
+ 0x4a, 0x04, 0xe5, 0x0b,
+ 0xe6, 0xb4, 0xfc, 0x7c,
+ 0x24, 0x0a, 0x28, 0x08,
+ 0x00, 0x00, 0x00, 0xff,
+ 0xff
+ };
scoped_ptr<SpdyFrame> frame(framer.CreateHeaders(
1, CONTROL_FLAG_NONE, true, &headers));
- CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
}
}
-TEST_F(SpdyFramerTest, CreateWindowUpdate) {
- SpdyFramer framer;
+TEST_F(SpdyFramerSpdy2Test, CreateWindowUpdate) {
+ SpdyFramer framer(kVer);
{
const char kDescription[] = "WINDOW_UPDATE frame";
const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x09,
+ 0x80, kVer, 0x00, 0x09,
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01,
};
- scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(1, 1));
+ scoped_ptr<SpdyWindowUpdateControlFrame> frame(
+ framer.CreateWindowUpdate(1, 1));
CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
- EXPECT_EQ(1u, SpdyFramer::GetControlFrameStreamId(
- reinterpret_cast<const SpdyControlFrame*>(frame.get())));
+ EXPECT_EQ(1u, SpdyFramer::GetControlFrameStreamId(frame.get()));
}
{
const char kDescription[] = "WINDOW_UPDATE frame with max stream ID";
const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x09,
+ 0x80, kVer, 0x00, 0x09,
0x00, 0x00, 0x00, 0x08,
0x7f, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x01,
@@ -1502,7 +2156,7 @@ TEST_F(SpdyFramerTest, CreateWindowUpdate) {
{
const char kDescription[] = "WINDOW_UPDATE frame with max window delta";
const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x09,
+ 0x80, kVer, 0x00, 0x09,
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x01,
0x7f, 0xff, 0xff, 0xff,
@@ -1512,85 +2166,13 @@ TEST_F(SpdyFramerTest, CreateWindowUpdate) {
}
}
-TEST_F(SpdyFramerTest, CreateCredential) {
- SpdyFramer framer;
-
- {
- const char kDescription[] = "CREDENTIAL frame";
- const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x0A,
- 0x00, 0x00, 0x00, 0x33,
- 0x00, 0x03, 0x00, 0x00,
- 0x00, 0x05, 'p', 'r',
- 'o', 'o', 'f', 0x00,
- 0x00, 0x00, 0x06, 'a',
- ' ', 'c', 'e', 'r',
- 't', 0x00, 0x00, 0x00,
- 0x0C, 'a', 'n', 'o',
- 't', 'h', 'e', 'r',
- ' ', 'c', 'e', 'r',
- 't', 0x00, 0x00, 0x00,
- 0x0A, 'f', 'i', 'n',
- 'a', 'l', ' ', 'c',
- 'e', 'r', 't',
- };
- SpdyCredential credential;
- credential.slot = 3;
- credential.proof = "proof";
- credential.certs.push_back("a cert");
- credential.certs.push_back("another cert");
- credential.certs.push_back("final cert");
- scoped_ptr<SpdyFrame> frame(framer.CreateCredentialFrame(credential));
- CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
- }
-}
-
-TEST_F(SpdyFramerTest, ParseCredentialFrame) {
- SpdyFramer framer;
-
- {
- unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x0A,
- 0x00, 0x00, 0x00, 0x33,
- 0x00, 0x03, 0x00, 0x00,
- 0x00, 0x05, 'p', 'r',
- 'o', 'o', 'f', 0x00,
- 0x00, 0x00, 0x06, 'a',
- ' ', 'c', 'e', 'r',
- 't', 0x00, 0x00, 0x00,
- 0x0C, 'a', 'n', 'o',
- 't', 'h', 'e', 'r',
- ' ', 'c', 'e', 'r',
- 't', 0x00, 0x00, 0x00,
- 0x0A, 'f', 'i', 'n',
- 'a', 'l', ' ', 'c',
- 'e', 'r', 't',
- };
- SpdyCredentialControlFrame frame(reinterpret_cast<char*>(kFrameData),
- false);
- SpdyCredential credential;
- EXPECT_TRUE(SpdyFramer::ParseCredentialData(frame.payload(),
- frame.length(),
- &credential));
- EXPECT_EQ(3u, credential.slot);
- EXPECT_EQ("proof", credential.proof);
- EXPECT_EQ("a cert", credential.certs.front());
- credential.certs.erase(credential.certs.begin());
- EXPECT_EQ("another cert", credential.certs.front());
- credential.certs.erase(credential.certs.begin());
- EXPECT_EQ("final cert", credential.certs.front());
- credential.certs.erase(credential.certs.begin());
- EXPECT_TRUE(credential.certs.empty());
- }
-}
-
-TEST_F(SpdyFramerTest, DuplicateFrame) {
- SpdyFramer framer;
+TEST_F(SpdyFramerSpdy2Test, DuplicateFrame) {
+ SpdyFramer framer(kVer);
{
const char kDescription[] = "PING frame";
const unsigned char kFrameData[] = {
- 0x80, 0x02, 0x00, 0x06,
+ 0x80, kVer, 0x00, 0x06,
0x00, 0x00, 0x00, 0x04,
0x12, 0x34, 0x56, 0x78,
};
@@ -1607,7 +2189,7 @@ TEST_F(SpdyFramerTest, DuplicateFrame) {
// the framer to overrun the buffer, and smash other heap contents. This test
// relies on the debug version of the heap manager, which checks for buffer
// overrun errors during delete processing. Regression test for b/2974814.
-TEST_F(SpdyFramerTest, ExpandBuffer_HeapSmash) {
+TEST_F(SpdyFramerSpdy2Test, ExpandBuffer_HeapSmash) {
// Sweep through the area of problematic values, to make sure we always cover
// the danger zone, even if it moves around at bit due to SPDY changes.
for (uint16 val2_len = SpdyFramer::kControlFrameBufferInitialSize - 50;
@@ -1618,7 +2200,7 @@ TEST_F(SpdyFramerTest, ExpandBuffer_HeapSmash) {
headers["bar"] = "foo";
headers["foo"] = "baz";
headers["grue"] = val2.c_str();
- SpdyFramer framer;
+ SpdyFramer framer(kVer);
scoped_ptr<SpdySynStreamControlFrame> template_frame(
framer.CreateSynStream(1, // stream_id
0, // associated_stream_id
@@ -1635,14 +2217,158 @@ TEST_F(SpdyFramerTest, ExpandBuffer_HeapSmash) {
}
}
-TEST_F(SpdyFramerTest, ReadCredentialFrame) {
+TEST_F(SpdyFramerSpdy2Test, ControlFrameSizesAreValidated) {
+ // Create a GoAway frame that has a few extra bytes at the end.
+ // We create enough overhead to require the framer to expand its frame buffer.
+ size_t overhead = SpdyFramer::kUncompressedControlFrameBufferInitialSize;
+ SpdyFramer framer(kVer);
+ scoped_ptr<SpdyGoAwayControlFrame> goaway(framer.CreateGoAway(1));
+ goaway->set_length(goaway->length() + overhead);
+ std::string pad('A', overhead);
+ TestSpdyVisitor visitor;
+
+ // First attempt without validation on.
+ visitor.framer_.set_validate_control_frame_sizes(false);
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(goaway->data()),
+ goaway->length() - overhead + SpdyControlFrame::kHeaderSize);
+ visitor.SimulateInFramer(
+ reinterpret_cast<const unsigned char*>(pad.c_str()),
+ overhead);
+ EXPECT_EQ(0, visitor.error_count_); // Not an error.
+ EXPECT_EQ(1, visitor.goaway_count_); // The goaway was parsed.
+
+ // Attempt with validation on.
+ visitor.framer_.set_validate_control_frame_sizes(true);
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(goaway->data()),
+ goaway->length() - overhead + SpdyControlFrame::kHeaderSize);
+ visitor.SimulateInFramer(
+ reinterpret_cast<const unsigned char*>(pad.c_str()),
+ overhead);
+ EXPECT_EQ(1, visitor.error_count_); // This generated an error.
+ EXPECT_EQ(1, visitor.goaway_count_); // Unchanged from before.
+}
+
+TEST_F(SpdyFramerSpdy2Test, ReadZeroLenSettingsFrame) {
+ SpdyFramer framer(kVer);
+ SpdySettings settings;
+ scoped_ptr<SpdyFrame> control_frame(framer.CreateSettings(settings));
+ control_frame->set_length(0);
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = false;
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame->data()),
+ control_frame.get()->length() + SpdyControlFrame::kHeaderSize);
+ // Should generate an error, since zero-len settings frames are unsupported.
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
+// Tests handling of SETTINGS frames with invalid length.
+TEST_F(SpdyFramerSpdy2Test, ReadBogusLenSettingsFrame) {
+ SpdyFramer framer(kVer);
+ SpdySettings settings;
+ // Add a setting to pad the frame so that we don't get a buffer overflow when
+ // calling SimulateInFramer() below.
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 1), 0x00000002));
+ scoped_ptr<SpdyFrame> control_frame(framer.CreateSettings(settings));
+ control_frame->set_length(5);
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = false;
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame->data()),
+ control_frame.get()->length() + SpdyControlFrame::kHeaderSize);
+ // Should generate an error, since zero-len settings frames are unsupported.
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
+// Tests handling of SETTINGS frames larger than the frame buffer size.
+TEST_F(SpdyFramerSpdy2Test, ReadLargeSettingsFrame) {
+ SpdyFramer framer(kVer);
+ SpdySettings settings;
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 1), 0x00000002));
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 2), 0x00000003));
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 3), 0x00000004));
+ scoped_ptr<SpdyFrame> control_frame(framer.CreateSettings(settings));
+ EXPECT_LT(SpdyFramer::kUncompressedControlFrameBufferInitialSize,
+ control_frame->length() + SpdyControlFrame::kHeaderSize);
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = false;
+
+ // Read all at once.
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame->data()),
+ control_frame->length() + SpdyControlFrame::kHeaderSize);
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(settings.size(), static_cast<unsigned>(visitor.setting_count_));
+ EXPECT_EQ(1, visitor.settings_frame_count_);
+
+ // Read data in small chunks.
+ size_t framed_data = 0;
+ size_t unframed_data = control_frame->length() +
+ SpdyControlFrame::kHeaderSize;
+ size_t kReadChunkSize = 5; // Read five bytes at a time.
+ while (unframed_data > 0) {
+ size_t to_read = std::min(kReadChunkSize, unframed_data);
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame->data() + framed_data),
+ to_read);
+ unframed_data -= to_read;
+ framed_data += to_read;
+ }
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(settings.size() * 2, static_cast<unsigned>(visitor.setting_count_));
+ EXPECT_EQ(2, visitor.settings_frame_count_);
+}
+
+// Tests handling of SETTINGS frame with duplicate entries.
+TEST_F(SpdyFramerSpdy2Test, ReadDuplicateSettings) {
+ SpdyFramer framer(kVer);
+ SpdySettings settings;
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 1), 0x00000002));
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 1), 0x00000003));
+ // This last setting should not be processed due to error above.
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 3), 0x00000003));
+ scoped_ptr<SpdyFrame> control_frame(framer.CreateSettings(settings));
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = false;
+
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame->data()),
+ control_frame->length() + SpdyControlFrame::kHeaderSize);
+ EXPECT_EQ(1, visitor.error_count_);
+ EXPECT_EQ(1, visitor.setting_count_);
+ EXPECT_EQ(1, visitor.settings_frame_count_);
+}
+
+// Tests handling of SETTINGS frame with entries out of order.
+TEST_F(SpdyFramerSpdy2Test, ReadOutOfOrderSettings) {
+ SpdyFramer framer(kVer);
+ SpdySettings settings;
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 2), 0x00000002));
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 1), 0x00000003));
+ // This last setting should not be processed due to error above.
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 3), 0x00000003));
+ scoped_ptr<SpdyFrame> control_frame(framer.CreateSettings(settings));
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = false;
+
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame->data()),
+ control_frame->length() + SpdyControlFrame::kHeaderSize);
+ EXPECT_EQ(1, visitor.error_count_);
+ EXPECT_EQ(1, visitor.setting_count_);
+ EXPECT_EQ(1, visitor.settings_frame_count_);
+}
+
+TEST_F(SpdyFramerSpdy2Test, ReadCredentialFrame) {
SpdyCredential credential;
credential.slot = 3;
credential.proof = "proof";
credential.certs.push_back("a cert");
credential.certs.push_back("another cert");
credential.certs.push_back("final cert");
- SpdyFramer framer;
+ SpdyFramer framer(kVer);
scoped_ptr<SpdyFrame> control_frame(
framer.CreateCredentialFrame(credential));
EXPECT_TRUE(control_frame.get() != NULL);
@@ -1662,14 +2388,14 @@ TEST_F(SpdyFramerTest, ReadCredentialFrame) {
}
}
-TEST_F(SpdyFramerTest, ReadCredentialFrameWithCorruptProof) {
+TEST_F(SpdyFramerSpdy2Test, ReadCredentialFrameWithCorruptProof) {
SpdyCredential credential;
credential.slot = 3;
credential.proof = "proof";
credential.certs.push_back("a cert");
credential.certs.push_back("another cert");
credential.certs.push_back("final cert");
- SpdyFramer framer;
+ SpdyFramer framer(kVer);
scoped_ptr<SpdyFrame> control_frame(
framer.CreateCredentialFrame(credential));
EXPECT_TRUE(control_frame.get() != NULL);
@@ -1684,14 +2410,14 @@ TEST_F(SpdyFramerTest, ReadCredentialFrameWithCorruptProof) {
EXPECT_EQ(1, visitor.error_count_);
}
-TEST_F(SpdyFramerTest, ReadCredentialFrameWithCorruptCertificate) {
+TEST_F(SpdyFramerSpdy2Test, ReadCredentialFrameWithCorruptCertificate) {
SpdyCredential credential;
credential.slot = 3;
credential.proof = "proof";
credential.certs.push_back("a cert");
credential.certs.push_back("another cert");
credential.certs.push_back("final cert");
- SpdyFramer framer;
+ SpdyFramer framer(kVer);
scoped_ptr<SpdyFrame> control_frame(
framer.CreateCredentialFrame(credential));
EXPECT_TRUE(control_frame.get() != NULL);
@@ -1706,8 +2432,8 @@ TEST_F(SpdyFramerTest, ReadCredentialFrameWithCorruptCertificate) {
EXPECT_EQ(1, visitor.error_count_);
}
-TEST_F(SpdyFramerTest, ReadGarbage) {
- SpdyFramer framer;
+TEST_F(SpdyFramerSpdy2Test, ReadGarbage) {
+ SpdyFramer framer(kVer);
unsigned char garbage_frame[256];
memset(garbage_frame, ~0, sizeof(garbage_frame));
TestSpdyVisitor visitor;
@@ -1716,12 +2442,12 @@ TEST_F(SpdyFramerTest, ReadGarbage) {
EXPECT_EQ(1, visitor.error_count_);
}
-TEST_F(SpdyFramerTest, ReadGarbageWithValidVersion) {
- SpdyFramer framer;
+TEST_F(SpdyFramerSpdy2Test, ReadGarbageWithValidVersion) {
+ SpdyFramer framer(kVer);
char garbage_frame[256];
memset(garbage_frame, ~0, sizeof(garbage_frame));
SpdyControlFrame control_frame(&garbage_frame[0], false);
- control_frame.set_version(kSpdyProtocolVersion);
+ control_frame.set_version(SPDY_VERSION_FOR_TESTS);
TestSpdyVisitor visitor;
visitor.use_compression_ = false;
visitor.SimulateInFramer(
@@ -1730,7 +2456,7 @@ TEST_F(SpdyFramerTest, ReadGarbageWithValidVersion) {
EXPECT_EQ(1, visitor.error_count_);
}
-TEST(SpdyFramer, StateToStringTest) {
+TEST_F(SpdyFramerSpdy2Test, StateToStringTest) {
EXPECT_STREQ("ERROR",
SpdyFramer::StateToString(SpdyFramer::SPDY_ERROR));
EXPECT_STREQ("DONE",
@@ -1760,12 +2486,15 @@ TEST(SpdyFramer, StateToStringTest) {
EXPECT_STREQ("SPDY_CREDENTIAL_FRAME_PAYLOAD",
SpdyFramer::StateToString(
SpdyFramer::SPDY_CREDENTIAL_FRAME_PAYLOAD));
+ EXPECT_STREQ("SPDY_SETTINGS_FRAME_PAYLOAD",
+ SpdyFramer::StateToString(
+ SpdyFramer::SPDY_SETTINGS_FRAME_PAYLOAD));
EXPECT_STREQ("UNKNOWN_STATE",
SpdyFramer::StateToString(
- SpdyFramer::SPDY_CREDENTIAL_FRAME_PAYLOAD + 1));
+ SpdyFramer::SPDY_SETTINGS_FRAME_PAYLOAD + 1));
}
-TEST(SpdyFramer, ErrorCodeToStringTest) {
+TEST_F(SpdyFramerSpdy2Test, ErrorCodeToStringTest) {
EXPECT_STREQ("NO_ERROR",
SpdyFramer::ErrorCodeToString(SpdyFramer::SPDY_NO_ERROR));
EXPECT_STREQ("INVALID_CONTROL_FRAME",
@@ -1790,7 +2519,7 @@ TEST(SpdyFramer, ErrorCodeToStringTest) {
SpdyFramer::ErrorCodeToString(SpdyFramer::LAST_ERROR));
}
-TEST(SpdyFramer, StatusCodeToStringTest) {
+TEST_F(SpdyFramerSpdy2Test, StatusCodeToStringTest) {
EXPECT_STREQ("INVALID",
SpdyFramer::StatusCodeToString(INVALID));
EXPECT_STREQ("PROTOCOL_ERROR",
@@ -1811,7 +2540,7 @@ TEST(SpdyFramer, StatusCodeToStringTest) {
SpdyFramer::StatusCodeToString(NUM_STATUS_CODES));
}
-TEST(SpdyFramer, ControlTypeToStringTest) {
+TEST_F(SpdyFramerSpdy2Test, ControlTypeToStringTest) {
EXPECT_STREQ("SYN_STREAM",
SpdyFramer::ControlTypeToString(SYN_STREAM));
EXPECT_STREQ("SYN_REPLY",
@@ -1830,13 +2559,13 @@ TEST(SpdyFramer, ControlTypeToStringTest) {
SpdyFramer::ControlTypeToString(HEADERS));
EXPECT_STREQ("WINDOW_UPDATE",
SpdyFramer::ControlTypeToString(WINDOW_UPDATE));
- EXPECT_STREQ("SETTINGS",
- SpdyFramer::ControlTypeToString(SETTINGS));
+ EXPECT_STREQ("CREDENTIAL",
+ SpdyFramer::ControlTypeToString(CREDENTIAL));
EXPECT_STREQ("UNKNOWN_CONTROL_TYPE",
SpdyFramer::ControlTypeToString(NUM_CONTROL_FRAME_TYPES));
}
-TEST(SpdyFramer, GetMinimumControlFrameSizeTest) {
+TEST_F(SpdyFramerSpdy2Test, GetMinimumControlFrameSizeTest) {
EXPECT_EQ(SpdySynStreamControlFrame::size(),
SpdyFramer::GetMinimumControlFrameSize(SYN_STREAM));
EXPECT_EQ(SpdySynReplyControlFrame::size(),
@@ -1845,7 +2574,7 @@ TEST(SpdyFramer, GetMinimumControlFrameSizeTest) {
SpdyFramer::GetMinimumControlFrameSize(RST_STREAM));
EXPECT_EQ(SpdySettingsControlFrame::size(),
SpdyFramer::GetMinimumControlFrameSize(SETTINGS));
- EXPECT_EQ(SpdyNoOpControlFrame::size(),
+ EXPECT_EQ(SpdyFrame::kHeaderSize,
SpdyFramer::GetMinimumControlFrameSize(NOOP));
EXPECT_EQ(SpdyPingControlFrame::size(),
SpdyFramer::GetMinimumControlFrameSize(PING));
@@ -1861,11 +2590,34 @@ TEST(SpdyFramer, GetMinimumControlFrameSizeTest) {
SpdyFramer::GetMinimumControlFrameSize(NUM_CONTROL_FRAME_TYPES));
}
-std::string RandomString(int length) {
- std::string rv;
- for (int index = 0; index < length; index++)
- rv += static_cast<char>('a' + (rand() % 26));
- return rv;
+TEST_F(SpdyFramerSpdy2Test, CatchProbableHttpResponse) {
+ SpdyFramerTestUtil::DecompressionVisitor visitor;
+ visitor.set_allow_data_frames(true);
+ {
+ SpdyFramer framer(kVer);
+ framer.set_visitor(&visitor);
+ framer.ProcessInput("HTTP/1.1", 8);
+ EXPECT_TRUE(framer.probable_http_response());
+ }
+ {
+ SpdyFramer framer(kVer);
+ framer.set_visitor(&visitor);
+ framer.ProcessInput("HTTP/1.0", 8);
+ EXPECT_TRUE(framer.probable_http_response());
+ }
+}
+
+TEST_F(SpdyFramerSpdy2Test, SettingsFlagsAndId) {
+ const uint32 kId = 0x020304;
+ const uint32 kFlags = 0x01;
+ const uint32 kWireFormat =
+ htonl((SPDY_VERSION_FOR_TESTS < 3) ? 0x04030201 : 0x01020304);
+
+ SettingsFlagsAndId id_and_flags =
+ SettingsFlagsAndId::FromWireFormat(SPDY_VERSION_FOR_TESTS, kWireFormat);
+ EXPECT_EQ(kId, id_and_flags.id());
+ EXPECT_EQ(kFlags, id_and_flags.flags());
+ EXPECT_EQ(kWireFormat, id_and_flags.GetWireFormat(SPDY_VERSION_FOR_TESTS));
}
} // namespace
diff --git a/net/spdy/spdy_framer_spdy3_test.cc b/net/spdy/spdy_framer_spdy3_test.cc
new file mode 100644
index 0000000..2cc0de9
--- /dev/null
+++ b/net/spdy/spdy_framer_spdy3_test.cc
@@ -0,0 +1,2623 @@
+// Copyright (c) 2012 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 <algorithm>
+#include <iostream>
+
+#include "base/memory/scoped_ptr.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/spdy/spdy_protocol.h"
+#include "net/spdy/spdy_frame_builder.h"
+#include "testing/platform_test.h"
+
+namespace {
+
+// Default SPDY version for unit tests.
+const int SPDY_VERSION_FOR_TESTS = 3;
+
+// The current default spdy version as a byte to be included in const
+// byte arrays below. Name choice is unfortunate, but better to fit to four
+// bytes than not.
+unsigned char kVer = SPDY_VERSION_FOR_TESTS;
+
+spdy::SpdySetting SpdySettingFromWireFormat(uint32 key, uint32 value) {
+ return spdy::SpdySetting(
+ spdy::SettingsFlagsAndId::FromWireFormat(SPDY_VERSION_FOR_TESTS, key),
+ value);
+}
+
+} // namespace
+
+namespace spdy {
+
+namespace test_spdy3 {
+
+static const size_t kMaxDecompressedSize = 1024;
+
+class SpdyFramerTestUtil {
+ public:
+ // Decompress a single frame using the decompression context held by
+ // the SpdyFramer. The implemention will CHECK fail if the input is anything
+ // other than a single, well-formed compressed frame.
+ //
+ // Returns a new decompressed SpdyFrame.
+ template<class SpdyFrameType> static SpdyFrame* DecompressFrame(
+ SpdyFramer* framer, const SpdyFrameType& frame) {
+ DecompressionVisitor visitor;
+ framer->set_visitor(&visitor);
+ size_t input_size = frame.length() + SpdyFrame::kHeaderSize;
+ CHECK_EQ(input_size, framer->ProcessInput(frame.data(), input_size));
+ CHECK_EQ(SpdyFramer::SPDY_RESET, framer->state());
+ framer->set_visitor(NULL);
+
+ char* buffer = visitor.ReleaseBuffer();
+ CHECK(buffer);
+ SpdyFrame* decompressed_frame = new SpdyFrame(buffer, true);
+ decompressed_frame->set_length(visitor.size() - SpdyFrame::kHeaderSize);
+ return decompressed_frame;
+ }
+
+ class DecompressionVisitor : public SpdyFramerVisitorInterface {
+ public:
+ DecompressionVisitor()
+ : buffer_(NULL), size_(0), finished_(false), allow_data_frames_(false) {
+ }
+
+ virtual void OnControl(const SpdyControlFrame* frame) {
+ CHECK(frame->has_header_block());
+ CHECK(!buffer_.get());
+ CHECK_EQ(size_, 0u);
+ CHECK(!finished_);
+
+ int32 control_frame_header_size = 0;
+ switch (frame->type()) {
+ case SYN_STREAM:
+ control_frame_header_size = SpdySynStreamControlFrame::size();
+ break;
+ case SYN_REPLY:
+ control_frame_header_size = SpdySynReplyControlFrame::size();
+ break;
+ case HEADERS:
+ control_frame_header_size = SpdyHeadersControlFrame::size();
+ break;
+ default:
+ LOG(FATAL);
+ return;
+ }
+
+ // Allocate space for the frame, and the copy header over.
+ buffer_.reset(new char[kMaxDecompressedSize]);
+ memcpy(buffer_.get(), frame->data(), control_frame_header_size);
+ size_ += control_frame_header_size;
+ }
+
+ virtual bool OnControlFrameHeaderData(SpdyStreamId stream_id,
+ const char* header_data,
+ size_t len) {
+ CHECK(buffer_.get() != NULL);
+ CHECK_GE(kMaxDecompressedSize, size_ + len);
+ CHECK(!finished_);
+ if (len != 0) {
+ memcpy(buffer_.get() + size_, header_data, len);
+ size_ += len;
+ } else {
+ // Done.
+ finished_ = true;
+ }
+ return true;
+ }
+
+ virtual bool OnCredentialFrameData(const char* header_data,
+ size_t len) {
+ LOG(FATAL) << "Unexpected CREDENTIAL Frame";
+ return false;
+ }
+
+ virtual void OnError(SpdyFramer* framer) { LOG(FATAL); }
+ virtual void OnDataFrameHeader(const SpdyDataFrame* frame) {
+ // For most tests, this class does not expect to see OnDataFrameHeader
+ // calls. Individual tests can override this if they need to.
+ if (!allow_data_frames_) {
+ LOG(FATAL) << "Unexpected data frame header";
+ }
+ }
+ virtual void OnStreamFrameData(SpdyStreamId stream_id,
+ const char* data,
+ size_t len) {
+ LOG(FATAL);
+ }
+ virtual void OnSetting(SpdySettingsIds id, uint8 flags, uint32 value) {
+ LOG(FATAL);
+ }
+
+ char* ReleaseBuffer() {
+ CHECK(finished_);
+ return buffer_.release();
+ }
+
+ size_t size() const {
+ CHECK(finished_);
+ return size_;
+ }
+ void set_allow_data_frames(bool allow) { allow_data_frames_ = allow; }
+
+ private:
+ scoped_array<char> buffer_;
+ size_t size_;
+ bool finished_;
+ bool allow_data_frames_;
+
+ DISALLOW_COPY_AND_ASSIGN(DecompressionVisitor);
+ };
+
+ DISALLOW_COPY_AND_ASSIGN(SpdyFramerTestUtil);
+};
+
+std::string HexDumpWithMarks(const unsigned char* data, int length,
+ const bool* marks, int mark_length) {
+ static const char kHexChars[] = "0123456789abcdef";
+ static const int kColumns = 4;
+
+ const int kSizeLimit = 1024;
+ if (length > kSizeLimit || mark_length > kSizeLimit) {
+ LOG(ERROR) << "Only dumping first " << kSizeLimit << " bytes.";
+ length = std::min(length, kSizeLimit);
+ mark_length = std::min(mark_length, kSizeLimit);
+ }
+
+ std::string hex;
+ for (const unsigned char* row = data; length > 0;
+ row += kColumns, length -= kColumns) {
+ for (const unsigned char *p = row; p < row + 4; ++p) {
+ if (p < row + length) {
+ const bool mark =
+ (marks && (p - data) < mark_length && marks[p - data]);
+ hex += mark ? '*' : ' ';
+ hex += kHexChars[(*p & 0xf0) >> 4];
+ hex += kHexChars[*p & 0x0f];
+ hex += mark ? '*' : ' ';
+ } else {
+ hex += " ";
+ }
+ }
+ hex = hex + " ";
+
+ for (const unsigned char *p = row; p < row + 4 && p < row + length; ++p)
+ hex += (*p >= 0x20 && *p <= 0x7f) ? (*p) : '.';
+
+ hex = hex + '\n';
+ }
+ return hex;
+}
+
+void CompareCharArraysWithHexError(
+ const std::string& description,
+ const unsigned char* actual,
+ const int actual_len,
+ const unsigned char* expected,
+ const int expected_len) {
+ const int min_len = std::min(actual_len, expected_len);
+ const int max_len = std::max(actual_len, expected_len);
+ scoped_array<bool> marks(new bool[max_len]);
+ bool identical = (actual_len == expected_len);
+ for (int i = 0; i < min_len; ++i) {
+ if (actual[i] != expected[i]) {
+ marks[i] = true;
+ identical = false;
+ } else {
+ marks[i] = false;
+ }
+ }
+ for (int i = min_len; i < max_len; ++i) {
+ marks[i] = true;
+ }
+ if (identical) return;
+ ADD_FAILURE()
+ << "Description:\n"
+ << description
+ << "\n\nExpected:\n"
+ << HexDumpWithMarks(expected, expected_len, marks.get(), max_len)
+ << "\nActual:\n"
+ << HexDumpWithMarks(actual, actual_len, marks.get(), max_len);
+}
+
+class TestSpdyVisitor : public SpdyFramerVisitorInterface {
+ public:
+ static const size_t kDefaultHeaderBufferSize = 64 * 1024;
+ static const size_t kDefaultCredentialBufferSize = 16 * 1024;
+
+ TestSpdyVisitor()
+ : framer_(kVer),
+ use_compression_(false),
+ error_count_(0),
+ syn_frame_count_(0),
+ syn_reply_frame_count_(0),
+ headers_frame_count_(0),
+ goaway_count_(0),
+ credential_count_(0),
+ settings_frame_count_(0),
+ setting_count_(0),
+ data_bytes_(0),
+ fin_frame_count_(0),
+ fin_flag_count_(0),
+ zero_length_data_frame_count_(0),
+ header_blocks_count_(0),
+ control_frame_header_data_count_(0),
+ zero_length_control_frame_header_data_count_(0),
+ data_frame_count_(0),
+ header_buffer_(new char[kDefaultHeaderBufferSize]),
+ header_buffer_length_(0),
+ header_buffer_size_(kDefaultHeaderBufferSize),
+ header_stream_id_(-1),
+ header_control_type_(NUM_CONTROL_FRAME_TYPES),
+ header_buffer_valid_(false),
+ credential_buffer_(new char[kDefaultCredentialBufferSize]),
+ credential_buffer_length_(0),
+ credential_buffer_size_(kDefaultCredentialBufferSize) {
+ }
+
+ void OnError(SpdyFramer* f) {
+ LOG(INFO) << "SpdyFramer Error: "
+ << SpdyFramer::ErrorCodeToString(f->error_code());
+ error_count_++;
+ }
+
+ void OnDataFrameHeader(const SpdyDataFrame* frame) {
+ data_frame_count_++;
+ header_stream_id_ = frame->stream_id();
+ }
+
+ void OnStreamFrameData(SpdyStreamId stream_id,
+ const char* data,
+ size_t len) {
+ EXPECT_EQ(header_stream_id_, stream_id);
+ if (len == 0)
+ ++zero_length_data_frame_count_;
+
+ data_bytes_ += len;
+ std::cerr << "OnStreamFrameData(" << stream_id << ", \"";
+ if (len > 0) {
+ for (size_t i = 0 ; i < len; ++i) {
+ std::cerr << std::hex << (0xFF & (unsigned int)data[i]) << std::dec;
+ }
+ }
+ std::cerr << "\", " << len << ")\n";
+ }
+
+ void OnControl(const SpdyControlFrame* frame) {
+ switch (frame->type()) {
+ case SYN_STREAM:
+ syn_frame_count_++;
+ InitHeaderStreaming(frame);
+ break;
+ case SYN_REPLY:
+ syn_reply_frame_count_++;
+ InitHeaderStreaming(frame);
+ break;
+ case RST_STREAM:
+ fin_frame_count_++;
+ break;
+ case HEADERS:
+ headers_frame_count_++;
+ InitHeaderStreaming(frame);
+ break;
+ case GOAWAY:
+ goaway_count_++;
+ break;
+ case CREDENTIAL:
+ credential_count_++;
+ break;
+ case SETTINGS:
+ settings_frame_count_++;
+ break;
+ default:
+ DLOG(FATAL); // Error!
+ }
+ if (frame->flags() & CONTROL_FLAG_FIN)
+ ++fin_flag_count_;
+ }
+
+ virtual void OnSetting(SpdySettingsIds id, uint8 flags, uint32 value) {
+ setting_count_++;
+ }
+
+ bool OnControlFrameHeaderData(SpdyStreamId stream_id,
+ const char* header_data,
+ size_t len) {
+ ++control_frame_header_data_count_;
+ CHECK_EQ(header_stream_id_, stream_id);
+ if (len == 0) {
+ ++zero_length_control_frame_header_data_count_;
+ // Indicates end-of-header-block.
+ CHECK(header_buffer_valid_);
+ bool parsed_headers = framer_.ParseHeaderBlockInBuffer(
+ header_buffer_.get(), header_buffer_length_, &headers_);
+ DCHECK(parsed_headers);
+ return true;
+ }
+ const size_t available = header_buffer_size_ - header_buffer_length_;
+ if (len > available) {
+ header_buffer_valid_ = false;
+ return false;
+ }
+ memcpy(header_buffer_.get() + header_buffer_length_, header_data, len);
+ header_buffer_length_ += len;
+ return true;
+ }
+
+ bool OnCredentialFrameData(const char* credential_data,
+ size_t len) {
+ if (len == 0) {
+ if (!framer_.ParseCredentialData(credential_buffer_.get(),
+ credential_buffer_length_,
+ &credential_)) {
+ ++error_count_;
+ }
+ return true;
+ }
+ const size_t available =
+ credential_buffer_size_ - credential_buffer_length_;
+ if (len > available) {
+ return false;
+ }
+ memcpy(credential_buffer_.get() + credential_buffer_length_,
+ credential_data, len);
+ credential_buffer_length_ += len;
+ return true;
+ }
+
+ // Convenience function which runs a framer simulation with particular input.
+ void SimulateInFramer(const unsigned char* input, size_t size) {
+ framer_.set_enable_compression(use_compression_);
+ framer_.set_visitor(this);
+ size_t input_remaining = size;
+ const char* input_ptr = reinterpret_cast<const char*>(input);
+ while (input_remaining > 0 &&
+ framer_.error_code() == SpdyFramer::SPDY_NO_ERROR) {
+ // To make the tests more interesting, we feed random (amd small) chunks
+ // into the framer. This simulates getting strange-sized reads from
+ // the socket.
+ const size_t kMaxReadSize = 32;
+ size_t bytes_read =
+ (rand() % std::min(input_remaining, kMaxReadSize)) + 1;
+ size_t bytes_processed = framer_.ProcessInput(input_ptr, bytes_read);
+ input_remaining -= bytes_processed;
+ input_ptr += bytes_processed;
+ if (framer_.state() == SpdyFramer::SPDY_DONE)
+ framer_.Reset();
+ }
+ }
+
+ void InitHeaderStreaming(const SpdyControlFrame* frame) {
+ memset(header_buffer_.get(), 0, header_buffer_size_);
+ header_buffer_length_ = 0;
+ header_stream_id_ = SpdyFramer::GetControlFrameStreamId(frame);
+ header_control_type_ = frame->type();
+ header_buffer_valid_ = true;
+ DCHECK_NE(header_stream_id_, SpdyFramer::kInvalidStream);
+ }
+
+ // Override the default buffer size (16K). Call before using the framer!
+ void set_header_buffer_size(size_t header_buffer_size) {
+ header_buffer_size_ = header_buffer_size;
+ header_buffer_.reset(new char[header_buffer_size]);
+ }
+
+ static size_t control_frame_buffer_max_size() {
+ return SpdyFramer::kControlFrameBufferMaxSize;
+ }
+
+ static size_t header_data_chunk_max_size() {
+ return SpdyFramer::kHeaderDataChunkMaxSize;
+ }
+
+ SpdyFramer framer_;
+ bool use_compression_;
+
+ // Counters from the visitor callbacks.
+ int error_count_;
+ int syn_frame_count_;
+ int syn_reply_frame_count_;
+ int headers_frame_count_;
+ int goaway_count_;
+ int credential_count_;
+ int settings_frame_count_;
+ int setting_count_;
+ int data_bytes_;
+ int fin_frame_count_; // The count of RST_STREAM type frames received.
+ int fin_flag_count_; // The count of frames with the FIN flag set.
+ int zero_length_data_frame_count_; // The count of zero-length data frames.
+ int header_blocks_count_;
+ int control_frame_header_data_count_; // The count of chunks received.
+ // The count of zero-length control frame header data chunks received.
+ int zero_length_control_frame_header_data_count_;
+ int data_frame_count_;
+
+ // Header block streaming state:
+ scoped_array<char> header_buffer_;
+ size_t header_buffer_length_;
+ size_t header_buffer_size_;
+ SpdyStreamId header_stream_id_;
+ SpdyControlType header_control_type_;
+ bool header_buffer_valid_;
+ SpdyHeaderBlock headers_;
+
+ scoped_array<char> credential_buffer_;
+ size_t credential_buffer_length_;
+ size_t credential_buffer_size_;
+ SpdyCredential credential_;
+};
+
+} // namespace test_spdy3
+
+} // namespace spdy
+
+using spdy::SpdyControlFlags;
+using spdy::SpdyControlFrame;
+using spdy::SpdyDataFrame;
+using spdy::SpdyFrame;
+using spdy::SpdyFrameBuilder;
+using spdy::SpdyFramer;
+using spdy::SpdyHeaderBlock;
+using spdy::SpdySynStreamControlFrame;
+using spdy::kControlFlagMask;
+using spdy::kLengthMask;
+using spdy::CONTROL_FLAG_NONE;
+using spdy::DATA_FLAG_COMPRESSED;
+using spdy::DATA_FLAG_FIN;
+using spdy::SYN_STREAM;
+using spdy::test_spdy3::CompareCharArraysWithHexError;
+using spdy::test_spdy3::SpdyFramerTestUtil;
+using spdy::test_spdy3::TestSpdyVisitor;
+
+namespace spdy {
+
+TEST(SpdyFrameBuilderSpdy3Test, WriteLimits) {
+ SpdyFrameBuilder builder(kLengthMask + 4);
+ // length field should fail.
+ EXPECT_FALSE(builder.WriteBytes(reinterpret_cast<const void*>(0x1),
+ kLengthMask + 1));
+ EXPECT_EQ(0, builder.length());
+
+ // Writing a block of the maximum allowed size should succeed.
+ const std::string kLargeData(kLengthMask, 'A');
+ builder.WriteUInt32(kLengthMask);
+ EXPECT_EQ(4, builder.length());
+ EXPECT_TRUE(builder.WriteBytes(kLargeData.data(), kLengthMask));
+ EXPECT_EQ(4 + kLengthMask, static_cast<unsigned>(builder.length()));
+}
+
+class SpdyFramerSpdy3Test : public PlatformTest {
+ public:
+ virtual void TearDown() {}
+
+ protected:
+ void CompareFrame(const std::string& description,
+ const SpdyFrame& actual_frame,
+ const unsigned char* expected,
+ const int expected_len) {
+ const unsigned char* actual =
+ reinterpret_cast<const unsigned char*>(actual_frame.data());
+ int actual_len = actual_frame.length() + SpdyFrame::kHeaderSize;
+ CompareCharArraysWithHexError(
+ description, actual, actual_len, expected, expected_len);
+ }
+
+ // Returns true if the two header blocks have equivalent content.
+ bool CompareHeaderBlocks(const SpdyHeaderBlock* expected,
+ const SpdyHeaderBlock* actual) {
+ if (expected->size() != actual->size()) {
+ LOG(ERROR) << "Expected " << expected->size() << " headers; actually got "
+ << actual->size() << "." << std::endl;
+ return false;
+ }
+ for (SpdyHeaderBlock::const_iterator it = expected->begin();
+ it != expected->end();
+ ++it) {
+ SpdyHeaderBlock::const_iterator it2 = actual->find(it->first);
+ if (it2 == actual->end()) {
+ LOG(ERROR) << "Expected header name '" << it->first << "'."
+ << std::endl;
+ return false;
+ }
+ if (it->second.compare(it2->second) != 0) {
+ LOG(ERROR) << "Expected header named '" << it->first
+ << "' to have a value of '" << it->second
+ << "'. The actual value received was '" << it2->second
+ << "'." << std::endl;
+ return false;
+ }
+ }
+ return true;
+ }
+};
+
+
+// Test that we can encode and decode a SpdyHeaderBlock in serialized form.
+TEST_F(SpdyFramerSpdy3Test, HeaderBlockInBuffer) {
+ SpdyHeaderBlock headers;
+ headers["alpha"] = "beta";
+ headers["gamma"] = "charlie";
+ SpdyFramer framer(kVer);
+
+ // Encode the header block into a SynStream frame.
+ scoped_ptr<SpdySynStreamControlFrame> frame(
+ framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, false, &headers));
+ EXPECT_TRUE(frame.get() != NULL);
+ std::string serialized_headers(frame->header_block(),
+ frame->header_block_len());
+ SpdyHeaderBlock new_headers;
+ EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.c_str(),
+ serialized_headers.size(),
+ &new_headers));
+
+ EXPECT_EQ(headers.size(), new_headers.size());
+ EXPECT_EQ(headers["alpha"], new_headers["alpha"]);
+ EXPECT_EQ(headers["gamma"], new_headers["gamma"]);
+}
+
+// Test that if there's not a full frame, we fail to parse it.
+TEST_F(SpdyFramerSpdy3Test, UndersizedHeaderBlockInBuffer) {
+ SpdyHeaderBlock headers;
+ headers["alpha"] = "beta";
+ headers["gamma"] = "charlie";
+ SpdyFramer framer(kVer);
+
+ // Encode the header block into a SynStream frame.
+ scoped_ptr<SpdySynStreamControlFrame> frame(
+ framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, false, &headers));
+ EXPECT_TRUE(frame.get() != NULL);
+
+ std::string serialized_headers(frame->header_block(),
+ frame->header_block_len());
+ SpdyHeaderBlock new_headers;
+ EXPECT_FALSE(framer.ParseHeaderBlockInBuffer(serialized_headers.c_str(),
+ serialized_headers.size() - 2,
+ &new_headers));
+}
+
+TEST_F(SpdyFramerSpdy3Test, OutOfOrderHeaders) {
+ // Frame builder with plentiful buffer size.
+ SpdyFrameBuilder frame(1024);
+
+ frame.WriteUInt16(kControlFlagMask | 1);
+ frame.WriteUInt16(SYN_STREAM);
+ frame.WriteUInt32(0); // Placeholder for the length.
+ frame.WriteUInt32(3); // stream_id
+ frame.WriteUInt32(0); // Associated stream id
+ frame.WriteUInt16(0); // Priority.
+
+ if (SPDY_VERSION_FOR_TESTS < 3) {
+ frame.WriteUInt16(2); // Number of headers.
+ frame.WriteString("gamma");
+ frame.WriteString("gamma");
+ frame.WriteString("alpha");
+ frame.WriteString("alpha");
+ } else {
+ frame.WriteUInt32(2); // Number of headers.
+ frame.WriteStringPiece32("gamma");
+ frame.WriteStringPiece32("gamma");
+ frame.WriteStringPiece32("alpha");
+ frame.WriteStringPiece32("alpha");
+ }
+ // write the length
+ frame.WriteUInt32ToOffset(4, frame.length() - SpdyFrame::kHeaderSize);
+
+ SpdyHeaderBlock new_headers;
+ scoped_ptr<SpdyFrame> control_frame(frame.take());
+ SpdySynStreamControlFrame syn_frame(control_frame->data(), false);
+ std::string serialized_headers(syn_frame.header_block(),
+ syn_frame.header_block_len());
+ SpdyFramer framer(kVer);
+ framer.set_enable_compression(false);
+ EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.c_str(),
+ serialized_headers.size(),
+ &new_headers));
+}
+
+TEST_F(SpdyFramerSpdy3Test, CreateCredential) {
+ SpdyFramer framer(kVer);
+
+ {
+ const char kDescription[] = "CREDENTIAL frame";
+ const unsigned char kFrameData[] = {
+ 0x80, kVer, 0x00, 0x0A,
+ 0x00, 0x00, 0x00, 0x33,
+ 0x00, 0x03, 0x00, 0x00,
+ 0x00, 0x05, 'p', 'r',
+ 'o', 'o', 'f', 0x00,
+ 0x00, 0x00, 0x06, 'a',
+ ' ', 'c', 'e', 'r',
+ 't', 0x00, 0x00, 0x00,
+ 0x0C, 'a', 'n', 'o',
+ 't', 'h', 'e', 'r',
+ ' ', 'c', 'e', 'r',
+ 't', 0x00, 0x00, 0x00,
+ 0x0A, 'f', 'i', 'n',
+ 'a', 'l', ' ', 'c',
+ 'e', 'r', 't',
+ };
+ SpdyCredential credential;
+ credential.slot = 3;
+ credential.proof = "proof";
+ credential.certs.push_back("a cert");
+ credential.certs.push_back("another cert");
+ credential.certs.push_back("final cert");
+ scoped_ptr<SpdyFrame> frame(framer.CreateCredentialFrame(credential));
+ CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ }
+}
+
+TEST_F(SpdyFramerSpdy3Test, ParseCredentialFrameData) {
+ SpdyFramer framer(kVer);
+
+ {
+ unsigned char kFrameData[] = {
+ 0x80, kVer, 0x00, 0x0A,
+ 0x00, 0x00, 0x00, 0x33,
+ 0x00, 0x03, 0x00, 0x00,
+ 0x00, 0x05, 'p', 'r',
+ 'o', 'o', 'f', 0x00,
+ 0x00, 0x00, 0x06, 'a',
+ ' ', 'c', 'e', 'r',
+ 't', 0x00, 0x00, 0x00,
+ 0x0C, 'a', 'n', 'o',
+ 't', 'h', 'e', 'r',
+ ' ', 'c', 'e', 'r',
+ 't', 0x00, 0x00, 0x00,
+ 0x0A, 'f', 'i', 'n',
+ 'a', 'l', ' ', 'c',
+ 'e', 'r', 't',
+ };
+ SpdyCredentialControlFrame frame(reinterpret_cast<char*>(kFrameData),
+ false);
+ SpdyCredential credential;
+ EXPECT_TRUE(SpdyFramer::ParseCredentialData(frame.payload(), frame.length(),
+ &credential));
+ EXPECT_EQ(3u, credential.slot);
+ EXPECT_EQ("proof", credential.proof);
+ EXPECT_EQ("a cert", credential.certs.front());
+ credential.certs.erase(credential.certs.begin());
+ EXPECT_EQ("another cert", credential.certs.front());
+ credential.certs.erase(credential.certs.begin());
+ EXPECT_EQ("final cert", credential.certs.front());
+ credential.certs.erase(credential.certs.begin());
+ EXPECT_TRUE(credential.certs.empty());
+ }
+}
+
+TEST_F(SpdyFramerSpdy3Test, DuplicateHeader) {
+ // Frame builder with plentiful buffer size.
+ SpdyFrameBuilder frame(1024);
+
+ frame.WriteUInt16(kControlFlagMask | 1);
+ frame.WriteUInt16(SYN_STREAM);
+ frame.WriteUInt32(0); // Placeholder for the length.
+ frame.WriteUInt32(3); // stream_id
+ frame.WriteUInt32(0); // associated stream id
+ frame.WriteUInt16(0); // Priority.
+
+ if (SPDY_VERSION_FOR_TESTS < 3) {
+ frame.WriteUInt16(2); // Number of headers.
+ frame.WriteString("name");
+ frame.WriteString("value1");
+ frame.WriteString("name");
+ frame.WriteString("value2");
+ } else {
+ frame.WriteUInt32(2); // Number of headers.
+ frame.WriteStringPiece32("name");
+ frame.WriteStringPiece32("value1");
+ frame.WriteStringPiece32("name");
+ frame.WriteStringPiece32("value2");
+ }
+ // write the length
+ frame.WriteUInt32ToOffset(4, frame.length() - SpdyFrame::kHeaderSize);
+
+ SpdyHeaderBlock new_headers;
+ scoped_ptr<SpdyFrame> control_frame(frame.take());
+ SpdySynStreamControlFrame syn_frame(control_frame->data(), false);
+ std::string serialized_headers(syn_frame.header_block(),
+ syn_frame.header_block_len());
+ SpdyFramer framer(kVer);
+ framer.set_enable_compression(false);
+ // This should fail because duplicate headers are verboten by the spec.
+ EXPECT_FALSE(framer.ParseHeaderBlockInBuffer(serialized_headers.c_str(),
+ serialized_headers.size(),
+ &new_headers));
+}
+
+TEST_F(SpdyFramerSpdy3Test, MultiValueHeader) {
+ // Frame builder with plentiful buffer size.
+ SpdyFrameBuilder frame(1024);
+
+ frame.WriteUInt16(kControlFlagMask | 1);
+ frame.WriteUInt16(SYN_STREAM);
+ frame.WriteUInt32(0); // Placeholder for the length.
+ frame.WriteUInt32(3); // stream_id
+ frame.WriteUInt32(0); // associated stream id
+ frame.WriteUInt16(0); // Priority.
+
+ std::string value("value1\0value2");
+ if (SPDY_VERSION_FOR_TESTS < 3) {
+ frame.WriteUInt16(1); // Number of headers.
+ frame.WriteString("name");
+ frame.WriteString(value);
+ } else {
+ frame.WriteUInt32(1); // Number of headers.
+ frame.WriteStringPiece32("name");
+ frame.WriteStringPiece32(value);
+ }
+ // write the length
+ frame.WriteUInt32ToOffset(4, frame.length() - SpdyFrame::kHeaderSize);
+
+ SpdyHeaderBlock new_headers;
+ scoped_ptr<SpdyFrame> control_frame(frame.take());
+ SpdySynStreamControlFrame syn_frame(control_frame->data(), false);
+ std::string serialized_headers(syn_frame.header_block(),
+ syn_frame.header_block_len());
+ SpdyFramer framer(kVer);
+ framer.set_enable_compression(false);
+ EXPECT_TRUE(framer.ParseHeaderBlockInBuffer(serialized_headers.c_str(),
+ serialized_headers.size(),
+ &new_headers));
+ EXPECT_TRUE(new_headers.find("name") != new_headers.end());
+ EXPECT_EQ(value, new_headers.find("name")->second);
+}
+
+TEST_F(SpdyFramerSpdy3Test, BasicCompression) {
+ SpdyHeaderBlock headers;
+ headers["server"] = "SpdyServer 1.0";
+ headers["date"] = "Mon 12 Jan 2009 12:12:12 PST";
+ headers["status"] = "200";
+ headers["version"] = "HTTP/1.1";
+ headers["content-type"] = "text/html";
+ headers["content-length"] = "12";
+
+ SpdyFramer framer(kVer);
+ framer.set_enable_compression(true);
+ scoped_ptr<SpdySynStreamControlFrame>
+ frame1(framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, true,
+ &headers));
+ scoped_ptr<SpdySynStreamControlFrame>
+ frame2(framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, true,
+ &headers));
+
+ // Expect the second frame to be more compact than the first.
+ EXPECT_LE(frame2->length(), frame1->length());
+
+ // Decompress the first frame
+ scoped_ptr<SpdyFrame> frame3(SpdyFramerTestUtil::DecompressFrame(
+ &framer, *frame1.get()));
+
+ // Decompress the second frame
+ scoped_ptr<SpdyFrame> frame4(SpdyFramerTestUtil::DecompressFrame(
+ &framer, *frame2.get()));
+
+ // Expect frames 3 & 4 to be the same.
+ EXPECT_EQ(0,
+ memcmp(frame3->data(), frame4->data(),
+ SpdyFrame::kHeaderSize + frame3->length()));
+
+
+ // Expect frames 3 to be the same as a uncompressed frame created
+ // from scratch.
+ scoped_ptr<SpdySynStreamControlFrame>
+ uncompressed_frame(framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE,
+ false, &headers));
+ EXPECT_EQ(frame3->length(), uncompressed_frame->length());
+ EXPECT_EQ(0,
+ memcmp(frame3->data(), uncompressed_frame->data(),
+ SpdyFrame::kHeaderSize + uncompressed_frame->length()));
+}
+
+TEST_F(SpdyFramerSpdy3Test, Basic) {
+ const unsigned char kV2Input[] = {
+ 0x80, kVer, 0x00, 0x01, // SYN Stream #1
+ 0x00, 0x00, 0x00, 0x14,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x02, 'h', 'h',
+ 0x00, 0x02, 'v', 'v',
+
+ 0x80, kVer, 0x00, 0x08, // HEADERS on Stream #1
+ 0x00, 0x00, 0x00, 0x18,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x02, 'h', '2',
+ 0x00, 0x02, 'v', '2',
+ 0x00, 0x02, 'h', '3',
+ 0x00, 0x02, 'v', '3',
+
+ 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1
+ 0x00, 0x00, 0x00, 0x0c,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+
+ 0x80, kVer, 0x00, 0x01, // SYN Stream #3
+ 0x00, 0x00, 0x00, 0x0c,
+ 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x03, // DATA on Stream #3
+ 0x00, 0x00, 0x00, 0x08,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+
+ 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1
+ 0x00, 0x00, 0x00, 0x04,
+ 0xde, 0xad, 0xbe, 0xef,
+
+ 0x80, kVer, 0x00, 0x03, // RST_STREAM on Stream #1
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x03, // DATA on Stream #3
+ 0x00, 0x00, 0x00, 0x00,
+
+ 0x80, kVer, 0x00, 0x03, // RST_STREAM on Stream #3
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ const unsigned char kV3Input[] = {
+ 0x80, kVer, 0x00, 0x01, // SYN Stream #1
+ 0x00, 0x00, 0x00, 0x1a,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x02, 'h', 'h',
+ 0x00, 0x00, 0x00, 0x02,
+ 'v', 'v',
+
+ 0x80, kVer, 0x00, 0x08, // HEADERS on Stream #1
+ 0x00, 0x00, 0x00, 0x22,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x00,
+ 0x00, 0x02, 'h', '2',
+ 0x00, 0x00, 0x00, 0x02,
+ 'v', '2', 0x00, 0x00,
+ 0x00, 0x02, 'h', '3',
+ 0x00, 0x00, 0x00, 0x02,
+ 'v', '3',
+
+ 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1
+ 0x00, 0x00, 0x00, 0x0c,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+
+ 0x80, kVer, 0x00, 0x01, // SYN Stream #3
+ 0x00, 0x00, 0x00, 0x0e,
+ 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x03, // DATA on Stream #3
+ 0x00, 0x00, 0x00, 0x08,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+
+ 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1
+ 0x00, 0x00, 0x00, 0x04,
+ 0xde, 0xad, 0xbe, 0xef,
+
+ 0x80, kVer, 0x00, 0x03, // RST_STREAM on Stream #1
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x03, // DATA on Stream #3
+ 0x00, 0x00, 0x00, 0x00,
+
+ 0x80, kVer, 0x00, 0x03, // RST_STREAM on Stream #3
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ TestSpdyVisitor visitor;
+ if (SPDY_VERSION_FOR_TESTS < 3) {
+ visitor.SimulateInFramer(kV2Input, sizeof(kV2Input));
+ } else {
+ visitor.SimulateInFramer(kV3Input, sizeof(kV3Input));
+ }
+
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(2, visitor.syn_frame_count_);
+ EXPECT_EQ(0, visitor.syn_reply_frame_count_);
+ EXPECT_EQ(1, visitor.headers_frame_count_);
+ EXPECT_EQ(24, visitor.data_bytes_);
+ EXPECT_EQ(2, visitor.fin_frame_count_);
+ EXPECT_EQ(0, visitor.fin_flag_count_);
+ EXPECT_EQ(0, visitor.zero_length_data_frame_count_);
+ EXPECT_EQ(4, visitor.data_frame_count_);
+}
+
+// Test that the FIN flag on a data frame signifies EOF.
+TEST_F(SpdyFramerSpdy3Test, FinOnDataFrame) {
+ const unsigned char kV2Input[] = {
+ 0x80, kVer, 0x00, 0x01, // SYN Stream #1
+ 0x00, 0x00, 0x00, 0x14,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x02, 'h', 'h',
+ 0x00, 0x02, 'v', 'v',
+
+ 0x80, kVer, 0x00, 0x02, // SYN REPLY Stream #1
+ 0x00, 0x00, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x02, 'a', 'a',
+ 0x00, 0x02, 'b', 'b',
+
+ 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1
+ 0x00, 0x00, 0x00, 0x0c,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+
+ 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1, with EOF
+ 0x01, 0x00, 0x00, 0x04,
+ 0xde, 0xad, 0xbe, 0xef,
+ };
+ const unsigned char kV3Input[] = {
+ 0x80, kVer, 0x00, 0x01, // SYN Stream #1
+ 0x00, 0x00, 0x00, 0x1a,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x02, 'h', 'h',
+ 0x00, 0x00, 0x00, 0x02,
+ 'v', 'v',
+
+ 0x80, kVer, 0x00, 0x02, // SYN REPLY Stream #1
+ 0x00, 0x00, 0x00, 0x16,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x02, 'a', 'a',
+ 0x00, 0x00, 0x00, 0x02,
+ 'b', 'b',
+
+ 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1
+ 0x00, 0x00, 0x00, 0x0c,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+ 0xde, 0xad, 0xbe, 0xef,
+
+ 0x00, 0x00, 0x00, 0x01, // DATA on Stream #1, with EOF
+ 0x01, 0x00, 0x00, 0x04,
+ 0xde, 0xad, 0xbe, 0xef,
+ };
+
+ TestSpdyVisitor visitor;
+ if (SPDY_VERSION_FOR_TESTS < 3) {
+ visitor.SimulateInFramer(kV2Input, sizeof(kV2Input));
+ } else {
+ visitor.SimulateInFramer(kV3Input, sizeof(kV3Input));
+ }
+
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(1, visitor.syn_frame_count_);
+ EXPECT_EQ(1, visitor.syn_reply_frame_count_);
+ EXPECT_EQ(0, visitor.headers_frame_count_);
+ EXPECT_EQ(16, visitor.data_bytes_);
+ EXPECT_EQ(0, visitor.fin_frame_count_);
+ EXPECT_EQ(0, visitor.fin_flag_count_);
+ EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
+ EXPECT_EQ(2, visitor.data_frame_count_);
+}
+
+// Test that the FIN flag on a SYN reply frame signifies EOF.
+TEST_F(SpdyFramerSpdy3Test, FinOnSynReplyFrame) {
+ const unsigned char kV2Input[] = {
+ 0x80, kVer, 0x00, 0x01, // SYN Stream #1
+ 0x00, 0x00, 0x00, 0x14,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x02, 'h', 'h',
+ 0x00, 0x02, 'v', 'v',
+
+ 0x80, kVer, 0x00, 0x02, // SYN REPLY Stream #1
+ 0x01, 0x00, 0x00, 0x14,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x02, 'a', 'a',
+ 0x00, 0x02, 'b', 'b',
+ };
+ const unsigned char kV3Input[] = {
+ 0x80, kVer, 0x00, 0x01, // SYN Stream #1
+ 0x00, 0x00, 0x00, 0x1a,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x02, 'h', 'h',
+ 0x00, 0x00, 0x00, 0x02,
+ 'v', 'v',
+
+ 0x80, kVer, 0x00, 0x02, // SYN REPLY Stream #1
+ 0x01, 0x00, 0x00, 0x1a,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x02, 'a', 'a',
+ 0x00, 0x00, 0x00, 0x02,
+ 'b', 'b',
+ };
+
+ TestSpdyVisitor visitor;
+ if (SPDY_VERSION_FOR_TESTS < 3) {
+ visitor.SimulateInFramer(kV2Input, sizeof(kV2Input));
+ } else {
+ visitor.SimulateInFramer(kV3Input, sizeof(kV3Input));
+ }
+
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(1, visitor.syn_frame_count_);
+ EXPECT_EQ(1, visitor.syn_reply_frame_count_);
+ EXPECT_EQ(0, visitor.headers_frame_count_);
+ EXPECT_EQ(0, visitor.data_bytes_);
+ EXPECT_EQ(0, visitor.fin_frame_count_);
+ EXPECT_EQ(1, visitor.fin_flag_count_);
+ EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
+ EXPECT_EQ(0, visitor.data_frame_count_);
+}
+
+TEST_F(SpdyFramerSpdy3Test, HeaderCompression) {
+ SpdyFramer send_framer(kVer);
+ SpdyFramer recv_framer(kVer);
+
+ send_framer.set_enable_compression(true);
+ recv_framer.set_enable_compression(true);
+
+ const char kHeader1[] = "header1";
+ const char kHeader2[] = "header2";
+ const char kHeader3[] = "header3";
+ const char kValue1[] = "value1";
+ const char kValue2[] = "value2";
+ const char kValue3[] = "value3";
+
+ // SYN_STREAM #1
+ SpdyHeaderBlock block;
+ block[kHeader1] = kValue1;
+ block[kHeader2] = kValue2;
+ SpdyControlFlags flags(CONTROL_FLAG_NONE);
+ scoped_ptr<SpdySynStreamControlFrame> syn_frame_1(
+ send_framer.CreateSynStream(1, 0, 0, flags, true, &block));
+ EXPECT_TRUE(syn_frame_1.get() != NULL);
+
+ // SYN_STREAM #2
+ block[kHeader3] = kValue3;
+ scoped_ptr<SpdySynStreamControlFrame> syn_frame_2(
+ send_framer.CreateSynStream(3, 0, 0, flags, true, &block));
+ EXPECT_TRUE(syn_frame_2.get() != NULL);
+
+ // Now start decompressing
+ scoped_ptr<SpdyFrame> decompressed;
+ scoped_ptr<SpdySynStreamControlFrame> syn_frame;
+ scoped_ptr<std::string> serialized_headers;
+ SpdyHeaderBlock decompressed_headers;
+
+ // Decompress SYN_STREAM #1
+ decompressed.reset(SpdyFramerTestUtil::DecompressFrame(
+ &recv_framer, *syn_frame_1.get()));
+ EXPECT_TRUE(decompressed.get() != NULL);
+ EXPECT_TRUE(decompressed->is_control_frame());
+ EXPECT_EQ(SYN_STREAM,
+ reinterpret_cast<SpdyControlFrame*>(decompressed.get())->type());
+ syn_frame.reset(new SpdySynStreamControlFrame(decompressed->data(), false));
+ serialized_headers.reset(new std::string(syn_frame->header_block(),
+ syn_frame->header_block_len()));
+ EXPECT_TRUE(recv_framer.ParseHeaderBlockInBuffer(serialized_headers->c_str(),
+ serialized_headers->size(),
+ &decompressed_headers));
+ EXPECT_EQ(2u, decompressed_headers.size());
+ EXPECT_EQ(kValue1, decompressed_headers[kHeader1]);
+ EXPECT_EQ(kValue2, decompressed_headers[kHeader2]);
+
+ // Decompress SYN_STREAM #2
+ decompressed.reset(SpdyFramerTestUtil::DecompressFrame(
+ &recv_framer, *syn_frame_2.get()));
+ EXPECT_TRUE(decompressed.get() != NULL);
+ EXPECT_TRUE(decompressed->is_control_frame());
+ EXPECT_EQ(SYN_STREAM,
+ reinterpret_cast<SpdyControlFrame*>(decompressed.get())->type());
+ syn_frame.reset(new SpdySynStreamControlFrame(decompressed->data(), false));
+ serialized_headers.reset(new std::string(syn_frame->header_block(),
+ syn_frame->header_block_len()));
+ decompressed_headers.clear();
+ EXPECT_TRUE(recv_framer.ParseHeaderBlockInBuffer(serialized_headers->c_str(),
+ serialized_headers->size(),
+ &decompressed_headers));
+ EXPECT_EQ(3u, decompressed_headers.size());
+ EXPECT_EQ(kValue1, decompressed_headers[kHeader1]);
+ EXPECT_EQ(kValue2, decompressed_headers[kHeader2]);
+ EXPECT_EQ(kValue3, decompressed_headers[kHeader3]);
+
+ // We didn't have data streams, so we shouldn't have (de)compressors.
+ EXPECT_EQ(0, send_framer.num_stream_compressors());
+ EXPECT_EQ(0, send_framer.num_stream_decompressors());
+ EXPECT_EQ(0, recv_framer.num_stream_compressors());
+ EXPECT_EQ(0, recv_framer.num_stream_decompressors());
+}
+
+// Verify we don't leak when we leave streams unclosed
+TEST_F(SpdyFramerSpdy3Test, UnclosedStreamDataCompressors) {
+ SpdyFramer send_framer(kVer);
+
+ send_framer.set_enable_compression(true);
+
+ const char kHeader1[] = "header1";
+ const char kHeader2[] = "header2";
+ const char kValue1[] = "value1";
+ const char kValue2[] = "value2";
+
+ SpdyHeaderBlock block;
+ block[kHeader1] = kValue1;
+ block[kHeader2] = kValue2;
+ SpdyControlFlags flags(CONTROL_FLAG_NONE);
+ scoped_ptr<SpdyFrame> syn_frame(
+ send_framer.CreateSynStream(1, 0, 0, flags, true, &block));
+ EXPECT_TRUE(syn_frame.get() != NULL);
+
+ const char bytes[] = "this is a test test test test test!";
+ scoped_ptr<SpdyFrame> send_frame(
+ send_framer.CreateDataFrame(
+ 1, bytes, arraysize(bytes),
+ static_cast<SpdyDataFlags>(DATA_FLAG_FIN)));
+ EXPECT_TRUE(send_frame.get() != NULL);
+
+ // Run the inputs through the framer.
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = true;
+ const unsigned char* data;
+ data = reinterpret_cast<const unsigned char*>(syn_frame->data());
+ visitor.SimulateInFramer(data, syn_frame->length() + SpdyFrame::kHeaderSize);
+ data = reinterpret_cast<const unsigned char*>(send_frame->data());
+ visitor.SimulateInFramer(data, send_frame->length() + SpdyFrame::kHeaderSize);
+
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(1, visitor.syn_frame_count_);
+ EXPECT_EQ(0, visitor.syn_reply_frame_count_);
+ EXPECT_EQ(0, visitor.headers_frame_count_);
+ EXPECT_EQ(arraysize(bytes), static_cast<unsigned>(visitor.data_bytes_));
+ EXPECT_EQ(0, visitor.fin_frame_count_);
+ EXPECT_EQ(0, visitor.fin_flag_count_);
+ EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
+ EXPECT_EQ(1, visitor.data_frame_count_);
+
+ // We closed the streams, so all compressors should be down.
+ EXPECT_EQ(0, visitor.framer_.num_stream_compressors());
+ EXPECT_EQ(0, visitor.framer_.num_stream_decompressors());
+ EXPECT_EQ(0, send_framer.num_stream_compressors());
+ EXPECT_EQ(0, send_framer.num_stream_decompressors());
+}
+
+// Verify we can decompress the stream even if handed over to the
+// framer 1 byte at a time.
+TEST_F(SpdyFramerSpdy3Test, UnclosedStreamDataCompressorsOneByteAtATime) {
+ SpdyFramer send_framer(kVer);
+
+ send_framer.set_enable_compression(true);
+
+ const char kHeader1[] = "header1";
+ const char kHeader2[] = "header2";
+ const char kValue1[] = "value1";
+ const char kValue2[] = "value2";
+
+ SpdyHeaderBlock block;
+ block[kHeader1] = kValue1;
+ block[kHeader2] = kValue2;
+ SpdyControlFlags flags(CONTROL_FLAG_NONE);
+ scoped_ptr<SpdyFrame> syn_frame(
+ send_framer.CreateSynStream(1, 0, 0, flags, true, &block));
+ EXPECT_TRUE(syn_frame.get() != NULL);
+
+ const char bytes[] = "this is a test test test test test!";
+ scoped_ptr<SpdyFrame> send_frame(
+ send_framer.CreateDataFrame(
+ 1, bytes, arraysize(bytes),
+ static_cast<SpdyDataFlags>(DATA_FLAG_FIN)));
+ EXPECT_TRUE(send_frame.get() != NULL);
+
+ // Run the inputs through the framer.
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = true;
+ const unsigned char* data;
+ data = reinterpret_cast<const unsigned char*>(syn_frame->data());
+ for (size_t idx = 0;
+ idx < syn_frame->length() + SpdyFrame::kHeaderSize;
+ ++idx) {
+ visitor.SimulateInFramer(data + idx, 1);
+ ASSERT_EQ(0, visitor.error_count_);
+ }
+ data = reinterpret_cast<const unsigned char*>(send_frame->data());
+ for (size_t idx = 0;
+ idx < send_frame->length() + SpdyFrame::kHeaderSize;
+ ++idx) {
+ visitor.SimulateInFramer(data + idx, 1);
+ ASSERT_EQ(0, visitor.error_count_);
+ }
+
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(1, visitor.syn_frame_count_);
+ EXPECT_EQ(0, visitor.syn_reply_frame_count_);
+ EXPECT_EQ(0, visitor.headers_frame_count_);
+ EXPECT_EQ(arraysize(bytes), static_cast<unsigned>(visitor.data_bytes_));
+ EXPECT_EQ(0, visitor.fin_frame_count_);
+ EXPECT_EQ(0, visitor.fin_flag_count_);
+ EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
+ EXPECT_EQ(1, visitor.data_frame_count_);
+
+ // We closed the streams, so all compressors should be down.
+ EXPECT_EQ(0, visitor.framer_.num_stream_compressors());
+ EXPECT_EQ(0, visitor.framer_.num_stream_decompressors());
+ EXPECT_EQ(0, send_framer.num_stream_compressors());
+ EXPECT_EQ(0, send_framer.num_stream_decompressors());
+}
+
+TEST_F(SpdyFramerSpdy3Test, WindowUpdateFrame) {
+ SpdyFramer framer(kVer);
+ scoped_ptr<SpdyWindowUpdateControlFrame> window_update_frame(
+ framer.CreateWindowUpdate(1, 0x12345678));
+
+ const unsigned char expected_data_frame[] = {
+ 0x80, kVer, 0x00, 0x09,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x12, 0x34, 0x56, 0x78
+ };
+
+ EXPECT_EQ(16u, window_update_frame->size());
+ EXPECT_EQ(0,
+ memcmp(window_update_frame->data(), expected_data_frame, 16));
+}
+
+TEST_F(SpdyFramerSpdy3Test, CreateDataFrame) {
+ SpdyFramer framer(kVer);
+
+ {
+ const char kDescription[] = "'hello' data frame, no FIN";
+ const unsigned char kFrameData[] = {
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x05,
+ 'h', 'e', 'l', 'l',
+ 'o'
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame(
+ 1, "hello", 5, DATA_FLAG_NONE));
+ CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ }
+
+ {
+ const char kDescription[] = "Data frame with negative data byte, no FIN";
+ const unsigned char kFrameData[] = {
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x01,
+ 0xff
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame(
+ 1, "\xff", 1, DATA_FLAG_NONE));
+ CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ }
+
+ {
+ const char kDescription[] = "'hello' data frame, with FIN";
+ const unsigned char kFrameData[] = {
+ 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x00, 0x00, 0x05,
+ 'h', 'e', 'l', 'l',
+ 'o'
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame(
+ 1, "hello", 5, DATA_FLAG_FIN));
+ CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ }
+
+ {
+ const char kDescription[] = "Empty data frame";
+ const unsigned char kFrameData[] = {
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame(
+ 1, "", 0, DATA_FLAG_NONE));
+ CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ }
+
+ {
+ const char kDescription[] = "Data frame with max stream ID";
+ const unsigned char kFrameData[] = {
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x01, 0x00, 0x00, 0x05,
+ 'h', 'e', 'l', 'l',
+ 'o'
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame(
+ 0x7fffffff, "hello", 5, DATA_FLAG_FIN));
+ CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ }
+
+ {
+ const char kDescription[] = "Large data frame";
+ const int kDataSize = 4 * 1024 * 1024; // 4 MB
+ const std::string kData(kDataSize, 'A');
+ const unsigned char kFrameHeader[] = {
+ 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x40, 0x00, 0x00,
+ };
+
+ const int kFrameSize = arraysize(kFrameHeader) + kDataSize;
+ scoped_array<unsigned char> expected_frame_data(
+ new unsigned char[kFrameSize]);
+ memcpy(expected_frame_data.get(), kFrameHeader, arraysize(kFrameHeader));
+ memset(expected_frame_data.get() + arraysize(kFrameHeader), 'A', kDataSize);
+
+ scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame(
+ 1, kData.data(), kData.size(), DATA_FLAG_FIN));
+ CompareFrame(kDescription, *frame, expected_frame_data.get(), kFrameSize);
+ }
+}
+
+TEST_F(SpdyFramerSpdy3Test, CreateSynStreamUncompressed) {
+ SpdyFramer framer(kVer);
+ framer.set_enable_compression(false);
+
+ {
+ const char kDescription[] = "SYN_STREAM frame, lowest pri, no FIN";
+
+ SpdyHeaderBlock headers;
+ headers["bar"] = "foo";
+ headers["foo"] = "bar";
+
+ const unsigned char kPri =
+ (SPDY_VERSION_FOR_TESTS != 2) ? 0xE0 : 0xC0;
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x20,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ kPri, 0x00, 0x00, 0x02,
+ 0x00, 0x03, 'b', 'a',
+ 'r', 0x00, 0x03, 'f',
+ 'o', 'o', 0x00, 0x03,
+ 'f', 'o', 'o', 0x00,
+ 0x03, 'b', 'a', 'r'
+ };
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x2a,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ kPri, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x00,
+ 0x00, 0x03, 'b', 'a',
+ 'r', 0x00, 0x00, 0x00,
+ 0x03, 'f', 'o', 'o',
+ 0x00, 0x00, 0x00, 0x03,
+ 'f', 'o', 'o', 0x00,
+ 0x00, 0x00, 0x03, 'b',
+ 'a', 'r'
+ };
+ scoped_ptr<SpdySynStreamControlFrame> frame(framer.CreateSynStream(
+ 1, 0, framer.GetLowestPriority(), CONTROL_FLAG_NONE, false, &headers));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
+ EXPECT_EQ(1u, SpdyFramer::GetControlFrameStreamId(frame.get()));
+ }
+
+ {
+ const char kDescription[] =
+ "SYN_STREAM frame with a 0-length header name, highest pri, FIN, "
+ "max stream ID";
+
+ SpdyHeaderBlock headers;
+ headers[""] = "foo";
+ headers["foo"] = "bar";
+
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x01,
+ 0x01, 0x00, 0x00, 0x1D,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x03,
+ 'f', 'o', 'o', 0x00,
+ 0x03, 'f', 'o', 'o',
+ 0x00, 0x03, 'b', 'a',
+ 'r'
+ };
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x01,
+ 0x01, 0x00, 0x00, 0x27,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 'f', 'o',
+ 'o', 0x00, 0x00, 0x00,
+ 0x03, 'f', 'o', 'o',
+ 0x00, 0x00, 0x00, 0x03,
+ 'b', 'a', 'r'
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateSynStream(
+ 0x7fffffff, 0x7fffffff, framer.GetHighestPriority(), CONTROL_FLAG_FIN,
+ false, &headers));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
+ }
+
+ {
+ const char kDescription[] =
+ "SYN_STREAM frame with a 0-length header val, high pri, FIN, "
+ "max stream ID";
+
+ SpdyHeaderBlock headers;
+ headers["bar"] = "foo";
+ headers["foo"] = "";
+
+ const unsigned char kPri =
+ (SPDY_VERSION_FOR_TESTS != 2) ? 0x20 : 0x40;
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x01,
+ 0x01, 0x00, 0x00, 0x1D,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x7f, 0xff, 0xff, 0xff,
+ kPri, 0x00, 0x00, 0x02,
+ 0x00, 0x03, 'b', 'a',
+ 'r', 0x00, 0x03, 'f',
+ 'o', 'o', 0x00, 0x03,
+ 'f', 'o', 'o', 0x00,
+ 0x00
+ };
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x01,
+ 0x01, 0x00, 0x00, 0x27,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x7f, 0xff, 0xff, 0xff,
+ kPri, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x00,
+ 0x00, 0x03, 'b', 'a',
+ 'r', 0x00, 0x00, 0x00,
+ 0x03, 'f', 'o', 'o',
+ 0x00, 0x00, 0x00, 0x03,
+ 'f', 'o', 'o', 0x00,
+ 0x00, 0x00, 0x00
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateSynStream(
+ 0x7fffffff, 0x7fffffff, 1, CONTROL_FLAG_FIN, false, &headers));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
+ }
+}
+
+TEST_F(SpdyFramerSpdy3Test, CreateSynStreamCompressed) {
+ SpdyFramer framer(kVer);
+ framer.set_enable_compression(true);
+
+ {
+ const char kDescription[] =
+ "SYN_STREAM frame, low pri, no FIN";
+
+ SpdyHeaderBlock headers;
+ headers["bar"] = "foo";
+ headers["foo"] = "bar";
+
+ const SpdyPriority priority =
+ (SPDY_VERSION_FOR_TESTS != 2) ? 4 : 2;
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x25,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x80, 0x00, 0x38, 0xea,
+ 0xdf, 0xa2, 0x51, 0xb2,
+ 0x62, 0x60, 0x62, 0x60,
+ 0x4e, 0x4a, 0x2c, 0x62,
+ 0x60, 0x4e, 0xcb, 0xcf,
+ 0x87, 0x12, 0x40, 0x2e,
+ 0x00, 0x00, 0x00, 0xff,
+ 0xff
+ };
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x27,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x80, 0x00, 0x38, 0xEA,
+ 0xE3, 0xC6, 0xA7, 0xC2,
+ 0x02, 0xE5, 0x0E, 0x50,
+ 0xC2, 0x4B, 0x4A, 0x04,
+ 0xE5, 0x0B, 0xE6, 0xB4,
+ 0xFC, 0x7C, 0x24, 0x0A,
+ 0x28, 0x08, 0x00, 0x00,
+ 0x00, 0xFF, 0xFF
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateSynStream(
+ 1, 0, priority, CONTROL_FLAG_NONE, true, &headers));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
+ }
+}
+
+TEST_F(SpdyFramerSpdy3Test, CreateSynReplyUncompressed) {
+ SpdyFramer framer(kVer);
+ framer.set_enable_compression(false);
+
+ {
+ const char kDescription[] = "SYN_REPLY frame, no FIN";
+
+ SpdyHeaderBlock headers;
+ headers["bar"] = "foo";
+ headers["foo"] = "bar";
+
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x1C,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x03, 'b', 'a',
+ 'r', 0x00, 0x03, 'f',
+ 'o', 'o', 0x00, 0x03,
+ 'f', 'o', 'o', 0x00,
+ 0x03, 'b', 'a', 'r'
+ };
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x24,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x03,
+ 'b', 'a', 'r', 0x00,
+ 0x00, 0x00, 0x03, 'f',
+ 'o', 'o', 0x00, 0x00,
+ 0x00, 0x03, 'f', 'o',
+ 'o', 0x00, 0x00, 0x00,
+ 0x03, 'b', 'a', 'r'
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateSynReply(
+ 1, CONTROL_FLAG_NONE, false, &headers));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
+ }
+
+ {
+ const char kDescription[] =
+ "SYN_REPLY frame with a 0-length header name, FIN, max stream ID";
+
+ SpdyHeaderBlock headers;
+ headers[""] = "foo";
+ headers["foo"] = "bar";
+
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x02,
+ 0x01, 0x00, 0x00, 0x19,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x03,
+ 'f', 'o', 'o', 0x00,
+ 0x03, 'f', 'o', 'o',
+ 0x00, 0x03, 'b', 'a',
+ 'r'
+ };
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x02,
+ 0x01, 0x00, 0x00, 0x21,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03,
+ 'f', 'o', 'o', 0x00,
+ 0x00, 0x00, 0x03, 'f',
+ 'o', 'o', 0x00, 0x00,
+ 0x00, 0x03, 'b', 'a',
+ 'r'
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateSynReply(
+ 0x7fffffff, CONTROL_FLAG_FIN, false, &headers));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
+ }
+
+ {
+ const char kDescription[] =
+ "SYN_REPLY frame with a 0-length header val, FIN, max stream ID";
+
+ SpdyHeaderBlock headers;
+ headers["bar"] = "foo";
+ headers["foo"] = "";
+
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x02,
+ 0x01, 0x00, 0x00, 0x19,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x03, 'b', 'a',
+ 'r', 0x00, 0x03, 'f',
+ 'o', 'o', 0x00, 0x03,
+ 'f', 'o', 'o', 0x00,
+ 0x00
+ };
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x02,
+ 0x01, 0x00, 0x00, 0x21,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x03,
+ 'b', 'a', 'r', 0x00,
+ 0x00, 0x00, 0x03, 'f',
+ 'o', 'o', 0x00, 0x00,
+ 0x00, 0x03, 'f', 'o',
+ 'o', 0x00, 0x00, 0x00,
+ 0x00
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateSynReply(
+ 0x7fffffff, CONTROL_FLAG_FIN, false, &headers));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
+ }
+}
+
+TEST_F(SpdyFramerSpdy3Test, CreateSynReplyCompressed) {
+ SpdyFramer framer(kVer);
+ framer.set_enable_compression(true);
+
+ {
+ const char kDescription[] = "SYN_REPLY frame, no FIN";
+
+ SpdyHeaderBlock headers;
+ headers["bar"] = "foo";
+ headers["foo"] = "bar";
+
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x21,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x38, 0xea,
+ 0xdf, 0xa2, 0x51, 0xb2,
+ 0x62, 0x60, 0x62, 0x60,
+ 0x4e, 0x4a, 0x2c, 0x62,
+ 0x60, 0x4e, 0xcb, 0xcf,
+ 0x87, 0x12, 0x40, 0x2e,
+ 0x00, 0x00, 0x00, 0xff,
+ 0xff
+ };
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x21,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x38, 0xea, 0xe3, 0xc6,
+ 0xa7, 0xc2, 0x02, 0xe5,
+ 0x0e, 0x50, 0xc2, 0x4b,
+ 0x4a, 0x04, 0xe5, 0x0b,
+ 0xe6, 0xb4, 0xfc, 0x7c,
+ 0x24, 0x0a, 0x28, 0x08,
+ 0x00, 0x00, 0x00, 0xff,
+ 0xff
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateSynReply(
+ 1, CONTROL_FLAG_NONE, true, &headers));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
+ }
+}
+
+TEST_F(SpdyFramerSpdy3Test, CreateRstStream) {
+ SpdyFramer framer(kVer);
+
+ {
+ const char kDescription[] = "RST_STREAM frame";
+ const unsigned char kFrameData[] = {
+ 0x80, kVer, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x01,
+ };
+ scoped_ptr<SpdyRstStreamControlFrame> frame(
+ framer.CreateRstStream(1, PROTOCOL_ERROR));
+ CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ EXPECT_EQ(1u, SpdyFramer::GetControlFrameStreamId(frame.get()));
+ }
+
+ {
+ const char kDescription[] = "RST_STREAM frame with max stream ID";
+ const unsigned char kFrameData[] = {
+ 0x80, kVer, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x01,
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateRstStream(0x7FFFFFFF,
+ PROTOCOL_ERROR));
+ CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ }
+
+ {
+ const char kDescription[] = "RST_STREAM frame with max status code";
+ const unsigned char kFrameData[] = {
+ 0x80, kVer, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x06,
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateRstStream(0x7FFFFFFF,
+ INTERNAL_ERROR));
+ CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ }
+}
+
+TEST_F(SpdyFramerSpdy3Test, CreateSettings) {
+ SpdyFramer framer(kVer);
+
+ {
+ const char kDescription[] = "Network byte order SETTINGS frame";
+
+ uint32 kValue = 0x0a0b0c0d;
+ uint8 kFlags = 0x04;
+ uint32 kId = 0x030201;
+ SettingsFlagsAndId idAndFlags(kFlags, kId);
+
+ SpdySettings settings;
+ settings.push_back(SpdySetting(idAndFlags, kValue));
+
+ EXPECT_EQ(kValue, settings.back().second);
+ EXPECT_EQ(kFlags, settings.back().first.flags());
+ EXPECT_EQ(kId, settings.back().first.id());
+
+ const unsigned char kFrameDatav2[] = {
+ 0x80, kVer, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x0c,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x02, 0x03, 0x04,
+ 0x0a, 0x0b, 0x0c, 0x0d,
+ };
+
+ const unsigned char kFrameDatav3[] = {
+ 0x80, kVer, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x0c,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x04, 0x03, 0x02, 0x01,
+ 0x0a, 0x0b, 0x0c, 0x0d,
+ };
+
+ scoped_ptr<SpdySettingsControlFrame> frame(framer.CreateSettings(settings));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kFrameDatav2 : kFrameDatav3,
+ arraysize(kFrameDatav3)); // Size is unchanged among versions.
+ EXPECT_EQ(SpdyFramer::kInvalidStream,
+ SpdyFramer::GetControlFrameStreamId(frame.get()));
+
+ // Make sure that ParseSettings also works as advertised.
+ SpdySettings parsed_settings;
+ EXPECT_TRUE(framer.ParseSettings(frame.get(), &parsed_settings));
+ EXPECT_EQ(settings.size(), parsed_settings.size());
+ EXPECT_EQ(kFlags, parsed_settings.back().first.flags());
+ EXPECT_EQ(kId, parsed_settings.back().first.id());
+ }
+
+ {
+ const char kDescription[] = "Basic SETTINGS frame";
+
+ SpdySettings settings;
+ settings.push_back(
+ SpdySettingFromWireFormat(0x00000000, 0x00000000)); // 1st Setting
+ settings.push_back(
+ SpdySettingFromWireFormat(0xffffffff, 0x00000001)); // 2nd Setting
+ settings.push_back(
+ SpdySettingFromWireFormat(0xff000001, 0x00000002)); // 3rd Setting
+
+ // Duplicates allowed
+ settings.push_back(
+ SpdySettingFromWireFormat(0x01000002, 0x00000003)); // 4th Setting
+ settings.push_back(
+ SpdySettingFromWireFormat(0x01000002, 0x00000003)); // 5th Setting
+
+ settings.push_back(
+ SpdySettingFromWireFormat(0x01000003, 0x000000ff)); // 6th Setting
+ settings.push_back(
+ SpdySettingFromWireFormat(0x01000004, 0xff000001)); // 7th Setting
+ settings.push_back(
+ SpdySettingFromWireFormat(0x01000004, 0xffffffff)); // 8th Setting
+
+ const unsigned char kFrameData[] = {
+ 0x80, kVer, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x44,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, // 1st Setting
+ 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, // 2nd Setting
+ 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x00, 0x00, 0xff, // 3rd Setting
+ 0x00, 0x00, 0x00, 0x02,
+ 0x02, 0x00, 0x00, 0x01, // 4th Setting
+ 0x00, 0x00, 0x00, 0x03,
+ 0x02, 0x00, 0x00, 0x01, // 5th Setting
+ 0x00, 0x00, 0x00, 0x03,
+ 0x03, 0x00, 0x00, 0x01, // 6th Setting
+ 0x00, 0x00, 0x00, 0xff,
+ 0x04, 0x00, 0x00, 0x01, // 7th Setting
+ 0xff, 0x00, 0x00, 0x01,
+ 0x04, 0x00, 0x00, 0x01, // 8th Setting
+ 0xff, 0xff, 0xff, 0xff,
+ };
+ scoped_ptr<SpdySettingsControlFrame> frame(framer.CreateSettings(settings));
+ CompareFrame(kDescription,
+ *frame,
+ kFrameData,
+ arraysize(kFrameData));
+ EXPECT_EQ(SpdyFramer::kInvalidStream,
+ SpdyFramer::GetControlFrameStreamId(frame.get()));
+ }
+
+ {
+ const char kDescription[] = "Empty SETTINGS frame";
+
+ SpdySettings settings;
+
+ const unsigned char kFrameData[] = {
+ 0x80, kVer, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateSettings(settings));
+ CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ }
+}
+
+TEST_F(SpdyFramerSpdy3Test, CreatePingFrame) {
+ SpdyFramer framer(kVer);
+
+ {
+ const char kDescription[] = "PING frame";
+ const unsigned char kFrameData[] = {
+ 0x80, kVer, 0x00, 0x06,
+ 0x00, 0x00, 0x00, 0x04,
+ 0x12, 0x34, 0x56, 0x78,
+ };
+ scoped_ptr<SpdyPingControlFrame> frame(framer.CreatePingFrame(0x12345678u));
+ CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ EXPECT_EQ(SpdyFramer::kInvalidStream,
+ SpdyFramer::GetControlFrameStreamId(frame.get()));
+ }
+}
+
+TEST_F(SpdyFramerSpdy3Test, CreateGoAway) {
+ SpdyFramer framer(kVer);
+
+ {
+ const char kDescription[] = "GOAWAY frame";
+ const unsigned char kFrameData[] = {
+ 0x80, kVer, 0x00, 0x07,
+ 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+ scoped_ptr<SpdyGoAwayControlFrame> frame(framer.CreateGoAway(0));
+ CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ EXPECT_EQ(SpdyFramer::kInvalidStream,
+ SpdyFramer::GetControlFrameStreamId(frame.get()));
+ }
+
+ {
+ const char kDescription[] = "GOAWAY frame with max stream ID";
+ const unsigned char kFrameData[] = {
+ 0x80, kVer, 0x00, 0x07,
+ 0x00, 0x00, 0x00, 0x04,
+ 0x7f, 0xff, 0xff, 0xff,
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(0x7FFFFFFF));
+ CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ }
+}
+
+TEST_F(SpdyFramerSpdy3Test, CreateHeadersUncompressed) {
+ SpdyFramer framer(kVer);
+ framer.set_enable_compression(false);
+
+ {
+ const char kDescription[] = "HEADERS frame, no FIN";
+
+ SpdyHeaderBlock headers;
+ headers["bar"] = "foo";
+ headers["foo"] = "bar";
+
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x1C,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x03, 'b', 'a',
+ 'r', 0x00, 0x03, 'f',
+ 'o', 'o', 0x00, 0x03,
+ 'f', 'o', 'o', 0x00,
+ 0x03, 'b', 'a', 'r'
+ };
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x24,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x03,
+ 'b', 'a', 'r', 0x00,
+ 0x00, 0x00, 0x03, 'f',
+ 'o', 'o', 0x00, 0x00,
+ 0x00, 0x03, 'f', 'o',
+ 'o', 0x00, 0x00, 0x00,
+ 0x03, 'b', 'a', 'r'
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateHeaders(
+ 1, CONTROL_FLAG_NONE, false, &headers));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
+ }
+
+ {
+ const char kDescription[] =
+ "HEADERS frame with a 0-length header name, FIN, max stream ID";
+
+ SpdyHeaderBlock headers;
+ headers[""] = "foo";
+ headers["foo"] = "bar";
+
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x08,
+ 0x01, 0x00, 0x00, 0x19,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x03,
+ 'f', 'o', 'o', 0x00,
+ 0x03, 'f', 'o', 'o',
+ 0x00, 0x03, 'b', 'a',
+ 'r'
+ };
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x08,
+ 0x01, 0x00, 0x00, 0x21,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03,
+ 'f', 'o', 'o', 0x00,
+ 0x00, 0x00, 0x03, 'f',
+ 'o', 'o', 0x00, 0x00,
+ 0x00, 0x03, 'b', 'a',
+ 'r'
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateHeaders(
+ 0x7fffffff, CONTROL_FLAG_FIN, false, &headers));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
+ }
+
+ {
+ const char kDescription[] =
+ "HEADERS frame with a 0-length header val, FIN, max stream ID";
+
+ SpdyHeaderBlock headers;
+ headers["bar"] = "foo";
+ headers["foo"] = "";
+
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x08,
+ 0x01, 0x00, 0x00, 0x19,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x03, 'b', 'a',
+ 'r', 0x00, 0x03, 'f',
+ 'o', 'o', 0x00, 0x03,
+ 'f', 'o', 'o', 0x00,
+ 0x00
+ };
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x08,
+ 0x01, 0x00, 0x00, 0x21,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x03,
+ 'b', 'a', 'r', 0x00,
+ 0x00, 0x00, 0x03, 'f',
+ 'o', 'o', 0x00, 0x00,
+ 0x00, 0x03, 'f', 'o',
+ 'o', 0x00, 0x00, 0x00,
+ 0x00
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateHeaders(
+ 0x7fffffff, CONTROL_FLAG_FIN, false, &headers));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
+ }
+}
+
+TEST_F(SpdyFramerSpdy3Test, CreateHeadersCompressed) {
+ SpdyFramer framer(kVer);
+ framer.set_enable_compression(true);
+
+ {
+ const char kDescription[] = "HEADERS frame, no FIN";
+
+ SpdyHeaderBlock headers;
+ headers["bar"] = "foo";
+ headers["foo"] = "bar";
+
+ const unsigned char kV2FrameData[] = {
+ 0x80, kVer, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x21,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x38, 0xea,
+ 0xdf, 0xa2, 0x51, 0xb2,
+ 0x62, 0x60, 0x62, 0x60,
+ 0x4e, 0x4a, 0x2c, 0x62,
+ 0x60, 0x4e, 0xcb, 0xcf,
+ 0x87, 0x12, 0x40, 0x2e,
+ 0x00, 0x00, 0x00, 0xff,
+ 0xff
+ };
+ const unsigned char kV3FrameData[] = {
+ 0x80, kVer, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x21,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x38, 0xea, 0xe3, 0xc6,
+ 0xa7, 0xc2, 0x02, 0xe5,
+ 0x0e, 0x50, 0xc2, 0x4b,
+ 0x4a, 0x04, 0xe5, 0x0b,
+ 0xe6, 0xb4, 0xfc, 0x7c,
+ 0x24, 0x0a, 0x28, 0x08,
+ 0x00, 0x00, 0x00, 0xff,
+ 0xff
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateHeaders(
+ 1, CONTROL_FLAG_NONE, true, &headers));
+ CompareFrame(kDescription,
+ *frame,
+ (SPDY_VERSION_FOR_TESTS < 3) ? kV2FrameData : kV3FrameData,
+ (SPDY_VERSION_FOR_TESTS < 3) ? arraysize(kV2FrameData)
+ : arraysize(kV3FrameData));
+ }
+}
+
+TEST_F(SpdyFramerSpdy3Test, CreateWindowUpdate) {
+ SpdyFramer framer(kVer);
+
+ {
+ const char kDescription[] = "WINDOW_UPDATE frame";
+ const unsigned char kFrameData[] = {
+ 0x80, kVer, 0x00, 0x09,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x01,
+ };
+ scoped_ptr<SpdyWindowUpdateControlFrame> frame(
+ framer.CreateWindowUpdate(1, 1));
+ CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ EXPECT_EQ(1u, SpdyFramer::GetControlFrameStreamId(frame.get()));
+ }
+
+ {
+ const char kDescription[] = "WINDOW_UPDATE frame with max stream ID";
+ const unsigned char kFrameData[] = {
+ 0x80, kVer, 0x00, 0x09,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x7f, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x01,
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(0x7FFFFFFF, 1));
+ CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ }
+
+ {
+ const char kDescription[] = "WINDOW_UPDATE frame with max window delta";
+ const unsigned char kFrameData[] = {
+ 0x80, kVer, 0x00, 0x09,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x01,
+ 0x7f, 0xff, 0xff, 0xff,
+ };
+ scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(1, 0x7FFFFFFF));
+ CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+ }
+}
+
+TEST_F(SpdyFramerSpdy3Test, DuplicateFrame) {
+ SpdyFramer framer(kVer);
+
+ {
+ const char kDescription[] = "PING frame";
+ const unsigned char kFrameData[] = {
+ 0x80, kVer, 0x00, 0x06,
+ 0x00, 0x00, 0x00, 0x04,
+ 0x12, 0x34, 0x56, 0x78,
+ };
+ scoped_ptr<SpdyFrame> frame1(framer.CreatePingFrame(0x12345678u));
+ CompareFrame(kDescription, *frame1, kFrameData, arraysize(kFrameData));
+
+ scoped_ptr<SpdyFrame> frame2(framer.DuplicateFrame(*frame1));
+ CompareFrame(kDescription, *frame2, kFrameData, arraysize(kFrameData));
+ }
+}
+
+// This test case reproduces conditions that caused ExpandControlFrameBuffer to
+// fail to expand the buffer control frame buffer when it should have, allowing
+// the framer to overrun the buffer, and smash other heap contents. This test
+// relies on the debug version of the heap manager, which checks for buffer
+// overrun errors during delete processing. Regression test for b/2974814.
+TEST_F(SpdyFramerSpdy3Test, ExpandBuffer_HeapSmash) {
+ // Sweep through the area of problematic values, to make sure we always cover
+ // the danger zone, even if it moves around at bit due to SPDY changes.
+ for (uint16 val2_len = SpdyFramer::kControlFrameBufferInitialSize - 50;
+ val2_len < SpdyFramer::kControlFrameBufferInitialSize;
+ val2_len++) {
+ std::string val2 = std::string(val2_len, 'a');
+ SpdyHeaderBlock headers;
+ headers["bar"] = "foo";
+ headers["foo"] = "baz";
+ headers["grue"] = val2.c_str();
+ SpdyFramer framer(kVer);
+ scoped_ptr<SpdySynStreamControlFrame> template_frame(
+ framer.CreateSynStream(1, // stream_id
+ 0, // associated_stream_id
+ 1, // priority
+ CONTROL_FLAG_NONE,
+ false, // compress
+ &headers));
+ EXPECT_TRUE(template_frame.get() != NULL);
+ TestSpdyVisitor visitor;
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(template_frame.get()->data()),
+ template_frame.get()->length() + SpdyControlFrame::kHeaderSize);
+ EXPECT_EQ(1, visitor.syn_frame_count_);
+ }
+}
+
+TEST_F(SpdyFramerSpdy3Test, ControlFrameSizesAreValidated) {
+ // Create a GoAway frame that has a few extra bytes at the end.
+ // We create enough overhead to require the framer to expand its frame buffer.
+ size_t overhead = SpdyFramer::kUncompressedControlFrameBufferInitialSize;
+ SpdyFramer framer(kVer);
+ scoped_ptr<SpdyGoAwayControlFrame> goaway(framer.CreateGoAway(1));
+ goaway->set_length(goaway->length() + overhead);
+ std::string pad('A', overhead);
+ TestSpdyVisitor visitor;
+
+ // First attempt without validation on.
+ visitor.framer_.set_validate_control_frame_sizes(false);
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(goaway->data()),
+ goaway->length() - overhead + SpdyControlFrame::kHeaderSize);
+ visitor.SimulateInFramer(
+ reinterpret_cast<const unsigned char*>(pad.c_str()),
+ overhead);
+ EXPECT_EQ(0, visitor.error_count_); // Not an error.
+ EXPECT_EQ(1, visitor.goaway_count_); // The goaway was parsed.
+
+ // Attempt with validation on.
+ visitor.framer_.set_validate_control_frame_sizes(true);
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(goaway->data()),
+ goaway->length() - overhead + SpdyControlFrame::kHeaderSize);
+ visitor.SimulateInFramer(
+ reinterpret_cast<const unsigned char*>(pad.c_str()),
+ overhead);
+ EXPECT_EQ(1, visitor.error_count_); // This generated an error.
+ EXPECT_EQ(1, visitor.goaway_count_); // Unchanged from before.
+}
+
+TEST_F(SpdyFramerSpdy3Test, ReadZeroLenSettingsFrame) {
+ SpdyFramer framer(kVer);
+ SpdySettings settings;
+ scoped_ptr<SpdyFrame> control_frame(framer.CreateSettings(settings));
+ control_frame->set_length(0);
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = false;
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame->data()),
+ control_frame.get()->length() + SpdyControlFrame::kHeaderSize);
+ // Should generate an error, since zero-len settings frames are unsupported.
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
+// Tests handling of SETTINGS frames with invalid length.
+TEST_F(SpdyFramerSpdy3Test, ReadBogusLenSettingsFrame) {
+ SpdyFramer framer(kVer);
+ SpdySettings settings;
+ // Add a setting to pad the frame so that we don't get a buffer overflow when
+ // calling SimulateInFramer() below.
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 1), 0x00000002));
+ scoped_ptr<SpdyFrame> control_frame(framer.CreateSettings(settings));
+ control_frame->set_length(5);
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = false;
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame->data()),
+ control_frame.get()->length() + SpdyControlFrame::kHeaderSize);
+ // Should generate an error, since zero-len settings frames are unsupported.
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
+// Tests handling of SETTINGS frames larger than the frame buffer size.
+TEST_F(SpdyFramerSpdy3Test, ReadLargeSettingsFrame) {
+ SpdyFramer framer(kVer);
+ SpdySettings settings;
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 1), 0x00000002));
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 2), 0x00000003));
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 3), 0x00000004));
+ scoped_ptr<SpdyFrame> control_frame(framer.CreateSettings(settings));
+ EXPECT_LT(SpdyFramer::kUncompressedControlFrameBufferInitialSize,
+ control_frame->length() + SpdyControlFrame::kHeaderSize);
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = false;
+
+ // Read all at once.
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame->data()),
+ control_frame->length() + SpdyControlFrame::kHeaderSize);
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(settings.size(), static_cast<unsigned>(visitor.setting_count_));
+ EXPECT_EQ(1, visitor.settings_frame_count_);
+
+ // Read data in small chunks.
+ size_t framed_data = 0;
+ size_t unframed_data = control_frame->length() +
+ SpdyControlFrame::kHeaderSize;
+ size_t kReadChunkSize = 5; // Read five bytes at a time.
+ while (unframed_data > 0) {
+ size_t to_read = std::min(kReadChunkSize, unframed_data);
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame->data() + framed_data),
+ to_read);
+ unframed_data -= to_read;
+ framed_data += to_read;
+ }
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(settings.size() * 2, static_cast<unsigned>(visitor.setting_count_));
+ EXPECT_EQ(2, visitor.settings_frame_count_);
+}
+
+// Tests handling of SETTINGS frame with duplicate entries.
+TEST_F(SpdyFramerSpdy3Test, ReadDuplicateSettings) {
+ SpdyFramer framer(kVer);
+ SpdySettings settings;
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 1), 0x00000002));
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 1), 0x00000003));
+ // This last setting should not be processed due to error above.
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 3), 0x00000003));
+ scoped_ptr<SpdyFrame> control_frame(framer.CreateSettings(settings));
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = false;
+
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame->data()),
+ control_frame->length() + SpdyControlFrame::kHeaderSize);
+ EXPECT_EQ(1, visitor.error_count_);
+ EXPECT_EQ(1, visitor.setting_count_);
+ EXPECT_EQ(1, visitor.settings_frame_count_);
+}
+
+// Tests handling of SETTINGS frame with entries out of order.
+TEST_F(SpdyFramerSpdy3Test, ReadOutOfOrderSettings) {
+ SpdyFramer framer(kVer);
+ SpdySettings settings;
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 2), 0x00000002));
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 1), 0x00000003));
+ // This last setting should not be processed due to error above.
+ settings.push_back(SpdySetting(SettingsFlagsAndId(0, 3), 0x00000003));
+ scoped_ptr<SpdyFrame> control_frame(framer.CreateSettings(settings));
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = false;
+
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame->data()),
+ control_frame->length() + SpdyControlFrame::kHeaderSize);
+ EXPECT_EQ(1, visitor.error_count_);
+ EXPECT_EQ(1, visitor.setting_count_);
+ EXPECT_EQ(1, visitor.settings_frame_count_);
+}
+
+TEST_F(SpdyFramerSpdy3Test, ReadCredentialFrame) {
+ SpdyCredential credential;
+ credential.slot = 3;
+ credential.proof = "proof";
+ credential.certs.push_back("a cert");
+ credential.certs.push_back("another cert");
+ credential.certs.push_back("final cert");
+ SpdyFramer framer(kVer);
+ scoped_ptr<SpdyFrame> control_frame(
+ framer.CreateCredentialFrame(credential));
+ EXPECT_TRUE(control_frame.get() != NULL);
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = false;
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame.get()->data()),
+ control_frame.get()->length() + SpdyControlFrame::kHeaderSize);
+ EXPECT_EQ(0, visitor.error_count_);
+ EXPECT_EQ(1, visitor.credential_count_);
+ EXPECT_EQ(control_frame->length(), visitor.credential_buffer_length_);
+ EXPECT_EQ(credential.slot, visitor.credential_.slot);
+ EXPECT_EQ(credential.proof, visitor.credential_.proof);
+ EXPECT_EQ(credential.certs.size(), visitor.credential_.certs.size());
+ for (size_t i = 0; i < credential.certs.size(); i++) {
+ EXPECT_EQ(credential.certs[i], visitor.credential_.certs[i]);
+ }
+}
+
+TEST_F(SpdyFramerSpdy3Test, ReadCredentialFrameWithCorruptProof) {
+ SpdyCredential credential;
+ credential.slot = 3;
+ credential.proof = "proof";
+ credential.certs.push_back("a cert");
+ credential.certs.push_back("another cert");
+ credential.certs.push_back("final cert");
+ SpdyFramer framer(kVer);
+ scoped_ptr<SpdyFrame> control_frame(
+ framer.CreateCredentialFrame(credential));
+ EXPECT_TRUE(control_frame.get() != NULL);
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = false;
+ unsigned char* data =
+ reinterpret_cast<unsigned char*>(control_frame.get()->data());
+ size_t offset = SpdyControlFrame::kHeaderSize + 4;
+ data[offset] = 0xFF; // Proof length is past the end of the frame
+ visitor.SimulateInFramer(
+ data, control_frame.get()->length() + SpdyControlFrame::kHeaderSize);
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
+TEST_F(SpdyFramerSpdy3Test, ReadCredentialFrameWithCorruptCertificate) {
+ SpdyCredential credential;
+ credential.slot = 3;
+ credential.proof = "proof";
+ credential.certs.push_back("a cert");
+ credential.certs.push_back("another cert");
+ credential.certs.push_back("final cert");
+ SpdyFramer framer(kVer);
+ scoped_ptr<SpdyFrame> control_frame(
+ framer.CreateCredentialFrame(credential));
+ EXPECT_TRUE(control_frame.get() != NULL);
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = false;
+ unsigned char* data =
+ reinterpret_cast<unsigned char*>(control_frame.get()->data());
+ size_t offset = SpdyControlFrame::kHeaderSize + credential.proof.length();
+ data[offset] = 0xFF; // Certificate length is past the end of the frame
+ visitor.SimulateInFramer(
+ data, control_frame.get()->length() + SpdyControlFrame::kHeaderSize);
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
+TEST_F(SpdyFramerSpdy3Test, ReadGarbage) {
+ SpdyFramer framer(kVer);
+ unsigned char garbage_frame[256];
+ memset(garbage_frame, ~0, sizeof(garbage_frame));
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = false;
+ visitor.SimulateInFramer(garbage_frame, sizeof(garbage_frame));
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
+TEST_F(SpdyFramerSpdy3Test, ReadGarbageWithValidVersion) {
+ SpdyFramer framer(kVer);
+ char garbage_frame[256];
+ memset(garbage_frame, ~0, sizeof(garbage_frame));
+ SpdyControlFrame control_frame(&garbage_frame[0], false);
+ control_frame.set_version(SPDY_VERSION_FOR_TESTS);
+ TestSpdyVisitor visitor;
+ visitor.use_compression_ = false;
+ visitor.SimulateInFramer(
+ reinterpret_cast<unsigned char*>(control_frame.data()),
+ sizeof(garbage_frame));
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
+TEST_F(SpdyFramerSpdy3Test, StateToStringTest) {
+ EXPECT_STREQ("ERROR",
+ SpdyFramer::StateToString(SpdyFramer::SPDY_ERROR));
+ EXPECT_STREQ("DONE",
+ SpdyFramer::StateToString(SpdyFramer::SPDY_DONE));
+ EXPECT_STREQ("AUTO_RESET",
+ SpdyFramer::StateToString(SpdyFramer::SPDY_AUTO_RESET));
+ EXPECT_STREQ("RESET",
+ SpdyFramer::StateToString(SpdyFramer::SPDY_RESET));
+ EXPECT_STREQ("READING_COMMON_HEADER",
+ SpdyFramer::StateToString(
+ SpdyFramer::SPDY_READING_COMMON_HEADER));
+ EXPECT_STREQ("CONTROL_FRAME_PAYLOAD",
+ SpdyFramer::StateToString(
+ SpdyFramer::SPDY_CONTROL_FRAME_PAYLOAD));
+ EXPECT_STREQ("IGNORE_REMAINING_PAYLOAD",
+ SpdyFramer::StateToString(
+ SpdyFramer::SPDY_IGNORE_REMAINING_PAYLOAD));
+ EXPECT_STREQ("FORWARD_STREAM_FRAME",
+ SpdyFramer::StateToString(
+ SpdyFramer::SPDY_FORWARD_STREAM_FRAME));
+ EXPECT_STREQ("SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK",
+ SpdyFramer::StateToString(
+ SpdyFramer::SPDY_CONTROL_FRAME_BEFORE_HEADER_BLOCK));
+ EXPECT_STREQ("SPDY_CONTROL_FRAME_HEADER_BLOCK",
+ SpdyFramer::StateToString(
+ SpdyFramer::SPDY_CONTROL_FRAME_HEADER_BLOCK));
+ EXPECT_STREQ("SPDY_CREDENTIAL_FRAME_PAYLOAD",
+ SpdyFramer::StateToString(
+ SpdyFramer::SPDY_CREDENTIAL_FRAME_PAYLOAD));
+ EXPECT_STREQ("SPDY_SETTINGS_FRAME_PAYLOAD",
+ SpdyFramer::StateToString(
+ SpdyFramer::SPDY_SETTINGS_FRAME_PAYLOAD));
+ EXPECT_STREQ("UNKNOWN_STATE",
+ SpdyFramer::StateToString(
+ SpdyFramer::SPDY_SETTINGS_FRAME_PAYLOAD + 1));
+}
+
+TEST_F(SpdyFramerSpdy3Test, ErrorCodeToStringTest) {
+ EXPECT_STREQ("NO_ERROR",
+ SpdyFramer::ErrorCodeToString(SpdyFramer::SPDY_NO_ERROR));
+ EXPECT_STREQ("INVALID_CONTROL_FRAME",
+ SpdyFramer::ErrorCodeToString(
+ SpdyFramer::SPDY_INVALID_CONTROL_FRAME));
+ EXPECT_STREQ("CONTROL_PAYLOAD_TOO_LARGE",
+ SpdyFramer::ErrorCodeToString(
+ SpdyFramer::SPDY_CONTROL_PAYLOAD_TOO_LARGE));
+ EXPECT_STREQ("ZLIB_INIT_FAILURE",
+ SpdyFramer::ErrorCodeToString(
+ SpdyFramer::SPDY_ZLIB_INIT_FAILURE));
+ EXPECT_STREQ("UNSUPPORTED_VERSION",
+ SpdyFramer::ErrorCodeToString(
+ SpdyFramer::SPDY_UNSUPPORTED_VERSION));
+ EXPECT_STREQ("DECOMPRESS_FAILURE",
+ SpdyFramer::ErrorCodeToString(
+ SpdyFramer::SPDY_DECOMPRESS_FAILURE));
+ EXPECT_STREQ("COMPRESS_FAILURE",
+ SpdyFramer::ErrorCodeToString(
+ SpdyFramer::SPDY_COMPRESS_FAILURE));
+ EXPECT_STREQ("UNKNOWN_ERROR",
+ SpdyFramer::ErrorCodeToString(SpdyFramer::LAST_ERROR));
+}
+
+TEST_F(SpdyFramerSpdy3Test, StatusCodeToStringTest) {
+ EXPECT_STREQ("INVALID",
+ SpdyFramer::StatusCodeToString(INVALID));
+ EXPECT_STREQ("PROTOCOL_ERROR",
+ SpdyFramer::StatusCodeToString(PROTOCOL_ERROR));
+ EXPECT_STREQ("INVALID_STREAM",
+ SpdyFramer::StatusCodeToString(INVALID_STREAM));
+ EXPECT_STREQ("REFUSED_STREAM",
+ SpdyFramer::StatusCodeToString(REFUSED_STREAM));
+ EXPECT_STREQ("UNSUPPORTED_VERSION",
+ SpdyFramer::StatusCodeToString(UNSUPPORTED_VERSION));
+ EXPECT_STREQ("CANCEL",
+ SpdyFramer::StatusCodeToString(CANCEL));
+ EXPECT_STREQ("INTERNAL_ERROR",
+ SpdyFramer::StatusCodeToString(INTERNAL_ERROR));
+ EXPECT_STREQ("FLOW_CONTROL_ERROR",
+ SpdyFramer::StatusCodeToString(FLOW_CONTROL_ERROR));
+ EXPECT_STREQ("UNKNOWN_STATUS",
+ SpdyFramer::StatusCodeToString(NUM_STATUS_CODES));
+}
+
+TEST_F(SpdyFramerSpdy3Test, ControlTypeToStringTest) {
+ EXPECT_STREQ("SYN_STREAM",
+ SpdyFramer::ControlTypeToString(SYN_STREAM));
+ EXPECT_STREQ("SYN_REPLY",
+ SpdyFramer::ControlTypeToString(SYN_REPLY));
+ EXPECT_STREQ("RST_STREAM",
+ SpdyFramer::ControlTypeToString(RST_STREAM));
+ EXPECT_STREQ("SETTINGS",
+ SpdyFramer::ControlTypeToString(SETTINGS));
+ EXPECT_STREQ("NOOP",
+ SpdyFramer::ControlTypeToString(NOOP));
+ EXPECT_STREQ("PING",
+ SpdyFramer::ControlTypeToString(PING));
+ EXPECT_STREQ("GOAWAY",
+ SpdyFramer::ControlTypeToString(GOAWAY));
+ EXPECT_STREQ("HEADERS",
+ SpdyFramer::ControlTypeToString(HEADERS));
+ EXPECT_STREQ("WINDOW_UPDATE",
+ SpdyFramer::ControlTypeToString(WINDOW_UPDATE));
+ EXPECT_STREQ("CREDENTIAL",
+ SpdyFramer::ControlTypeToString(CREDENTIAL));
+ EXPECT_STREQ("UNKNOWN_CONTROL_TYPE",
+ SpdyFramer::ControlTypeToString(NUM_CONTROL_FRAME_TYPES));
+}
+
+TEST_F(SpdyFramerSpdy3Test, GetMinimumControlFrameSizeTest) {
+ EXPECT_EQ(SpdySynStreamControlFrame::size(),
+ SpdyFramer::GetMinimumControlFrameSize(SYN_STREAM));
+ EXPECT_EQ(SpdySynReplyControlFrame::size(),
+ SpdyFramer::GetMinimumControlFrameSize(SYN_REPLY));
+ EXPECT_EQ(SpdyRstStreamControlFrame::size(),
+ SpdyFramer::GetMinimumControlFrameSize(RST_STREAM));
+ EXPECT_EQ(SpdySettingsControlFrame::size(),
+ SpdyFramer::GetMinimumControlFrameSize(SETTINGS));
+ EXPECT_EQ(SpdyFrame::kHeaderSize,
+ SpdyFramer::GetMinimumControlFrameSize(NOOP));
+ EXPECT_EQ(SpdyPingControlFrame::size(),
+ SpdyFramer::GetMinimumControlFrameSize(PING));
+ EXPECT_EQ(SpdyGoAwayControlFrame::size(),
+ SpdyFramer::GetMinimumControlFrameSize(GOAWAY));
+ EXPECT_EQ(SpdyHeadersControlFrame::size(),
+ SpdyFramer::GetMinimumControlFrameSize(HEADERS));
+ EXPECT_EQ(SpdyWindowUpdateControlFrame::size(),
+ SpdyFramer::GetMinimumControlFrameSize(WINDOW_UPDATE));
+ EXPECT_EQ(SpdyCredentialControlFrame::size(),
+ SpdyFramer::GetMinimumControlFrameSize(CREDENTIAL));
+ EXPECT_EQ(static_cast<size_t>(0x7FFFFFFF),
+ SpdyFramer::GetMinimumControlFrameSize(NUM_CONTROL_FRAME_TYPES));
+}
+
+TEST_F(SpdyFramerSpdy3Test, CatchProbableHttpResponse) {
+ SpdyFramerTestUtil::DecompressionVisitor visitor;
+ visitor.set_allow_data_frames(true);
+ {
+ SpdyFramer framer(kVer);
+ framer.set_visitor(&visitor);
+ framer.ProcessInput("HTTP/1.1", 8);
+ EXPECT_TRUE(framer.probable_http_response());
+ }
+ {
+ SpdyFramer framer(kVer);
+ framer.set_visitor(&visitor);
+ framer.ProcessInput("HTTP/1.0", 8);
+ EXPECT_TRUE(framer.probable_http_response());
+ }
+}
+
+TEST_F(SpdyFramerSpdy3Test, SettingsFlagsAndId) {
+ const uint32 kId = 0x020304;
+ const uint32 kFlags = 0x01;
+ const uint32 kWireFormat =
+ htonl((SPDY_VERSION_FOR_TESTS < 3) ? 0x04030201 : 0x01020304);
+
+ SettingsFlagsAndId id_and_flags =
+ SettingsFlagsAndId::FromWireFormat(SPDY_VERSION_FOR_TESTS, kWireFormat);
+ EXPECT_EQ(kId, id_and_flags.id());
+ EXPECT_EQ(kFlags, id_and_flags.flags());
+ EXPECT_EQ(kWireFormat, id_and_flags.GetWireFormat(SPDY_VERSION_FOR_TESTS));
+}
+
+} // namespace
diff --git a/net/spdy/spdy_http_stream_spdy2_unittest.cc b/net/spdy/spdy_http_stream_spdy2_unittest.cc
index 244f84e..836d50dc 100644
--- a/net/spdy/spdy_http_stream_spdy2_unittest.cc
+++ b/net/spdy/spdy_http_stream_spdy2_unittest.cc
@@ -29,10 +29,15 @@ class SpdyHttpStreamSpdy2Test : public testing::Test {
spdy::SpdyFramer::set_enable_compression_default(enabled);
}
+ virtual void SetUp() {
+ SpdySession::set_default_protocol(SSLClientSocket::kProtoSPDY2);
+ }
+
virtual void TearDown() {
crypto::ECSignatureCreator::SetFactoryForTesting(NULL);
MessageLoop::current()->RunAllPending();
}
+
int InitSession(MockRead* reads, size_t reads_count,
MockWrite* writes, size_t writes_count,
HostPortPair& host_port_pair) {
@@ -447,22 +452,6 @@ class MockECSignatureCreatorFactory : public crypto::ECSignatureCreatorFactory {
DISALLOW_COPY_AND_ASSIGN(MockECSignatureCreatorFactory);
};
-TEST_F(SpdyHttpStreamSpdy2Test, SendCredentialsEC) {
- scoped_ptr<crypto::ECSignatureCreatorFactory> ec_signature_creator_factory(
- new MockECSignatureCreatorFactory());
- crypto::ECSignatureCreator::SetFactoryForTesting(
- ec_signature_creator_factory.get());
-
- scoped_ptr<OriginBoundCertService> obc_service(
- new OriginBoundCertService(new DefaultOriginBoundCertStore(NULL)));
- std::string cert;
- std::string proof;
- GetECOriginBoundCertAndProof("http://www.gmail.com/", obc_service.get(),
- &cert, &proof);
-
- TestSendCredentials(obc_service.get(), cert, proof, CLIENT_CERT_ECDSA_SIGN);
-}
-
#endif // !defined(USE_OPENSSL)
// TODO(willchan): Write a longer test for SpdyStream that exercises all
diff --git a/net/spdy/spdy_http_stream_spdy3_unittest.cc b/net/spdy/spdy_http_stream_spdy3_unittest.cc
index 79390df..49f61a6 100644
--- a/net/spdy/spdy_http_stream_spdy3_unittest.cc
+++ b/net/spdy/spdy_http_stream_spdy3_unittest.cc
@@ -29,10 +29,15 @@ class SpdyHttpStreamSpdy3Test : public testing::Test {
spdy::SpdyFramer::set_enable_compression_default(enabled);
}
+ virtual void SetUp() {
+ SpdySession::set_default_protocol(SSLClientSocket::kProtoSPDY3);
+ }
+
virtual void TearDown() {
crypto::ECSignatureCreator::SetFactoryForTesting(NULL);
MessageLoop::current()->RunAllPending();
}
+
int InitSession(MockRead* reads, size_t reads_count,
MockWrite* writes, size_t writes_count,
HostPortPair& host_port_pair) {
diff --git a/net/spdy/spdy_network_transaction_spdy21_unittest.cc b/net/spdy/spdy_network_transaction_spdy21_unittest.cc
index e4a5f22..39a7473 100644
--- a/net/spdy/spdy_network_transaction_spdy21_unittest.cc
+++ b/net/spdy/spdy_network_transaction_spdy21_unittest.cc
@@ -41,6 +41,7 @@ class SpdyNetworkTransactionSpdy21Test
virtual void SetUp() {
// By default, all tests turn off compression.
EnableCompression(false);
+ SpdySession::set_default_protocol(SSLClientSocket::kProtoSPDY21);
google_get_request_initialized_ = false;
google_post_request_initialized_ = false;
google_chunked_post_request_initialized_ = false;
@@ -939,8 +940,7 @@ TEST_P(SpdyNetworkTransactionSpdy21Test, ThreeGetsWithMaxConcurrent) {
scoped_ptr<spdy::SpdyFrame> fbody3(ConstructSpdyBodyFrame(5, true));
spdy::SpdySettings settings;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
+ spdy::SettingsFlagsAndId id(0, spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
const size_t max_concurrent_streams = 1;
settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
@@ -1078,8 +1078,7 @@ TEST_P(SpdyNetworkTransactionSpdy21Test, FourGetsWithMaxConcurrentPriority) {
spdy::SpdySettings settings;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
+ spdy::SettingsFlagsAndId id(0, spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
const size_t max_concurrent_streams = 1;
settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
@@ -1223,8 +1222,7 @@ TEST_P(SpdyNetworkTransactionSpdy21Test, ThreeGetsWithMaxConcurrentDelete) {
scoped_ptr<spdy::SpdyFrame> fbody2(ConstructSpdyBodyFrame(3, true));
spdy::SpdySettings settings;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
+ spdy::SettingsFlagsAndId id(0, spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
const size_t max_concurrent_streams = 1;
settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
@@ -1360,8 +1358,7 @@ TEST_P(SpdyNetworkTransactionSpdy21Test,
scoped_ptr<spdy::SpdyFrame> resp2(ConstructSpdyGetSynReply(NULL, 0, 3));
spdy::SpdySettings settings;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
+ spdy::SettingsFlagsAndId id(0, spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
const size_t max_concurrent_streams = 1;
settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
@@ -4445,25 +4442,23 @@ TEST_P(SpdyNetworkTransactionSpdy21Test, SettingsSaved) {
unsigned int kSampleValue1 = 0x0a0a0a0a;
unsigned int kSampleId2 = 0x2;
unsigned int kSampleValue2 = 0x0b0b0b0b;
- unsigned int kSampleId3 = 0xababab;
+ unsigned int kSampleId3 = 0x3;
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));
+ spdy::SettingsFlagsAndId setting1(spdy::SETTINGS_FLAG_PLEASE_PERSIST,
+ kSampleId1);
+ settings.push_back(std::make_pair(setting1, kSampleValue1));
// Next add a non-persisted setting
- setting.set_flags(0);
- setting.set_id(kSampleId2);
- settings.push_back(std::make_pair(setting, kSampleValue2));
+ spdy::SettingsFlagsAndId setting2(0, kSampleId2);
+ settings.push_back(std::make_pair(setting2, 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));
+ spdy::SettingsFlagsAndId setting3(spdy::SETTINGS_FLAG_PLEASE_PERSIST,
+ kSampleId3);
+ settings.push_back(std::make_pair(setting3, kSampleValue3));
settings_frame.reset(ConstructSpdySettings(settings));
}
@@ -4547,15 +4542,14 @@ TEST_P(SpdyNetworkTransactionSpdy21Test, SettingsPlayback) {
// 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));
+ spdy::SettingsFlagsAndId setting1(spdy::SETTINGS_FLAG_PLEASE_PERSIST,
+ kSampleId1);
+ settings.push_back(std::make_pair(setting1, 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));
+ spdy::SettingsFlagsAndId setting2(spdy::SETTINGS_FLAG_PLEASE_PERSIST,
+ kSampleId2);
+ settings.push_back(std::make_pair(setting2, kSampleValue2));
spdy_session_pool->http_server_properties()->SetSpdySettings(
host_port_pair, settings);
diff --git a/net/spdy/spdy_network_transaction_spdy2_unittest.cc b/net/spdy/spdy_network_transaction_spdy2_unittest.cc
index a083b53..1e9d335 100644
--- a/net/spdy/spdy_network_transaction_spdy2_unittest.cc
+++ b/net/spdy/spdy_network_transaction_spdy2_unittest.cc
@@ -41,6 +41,7 @@ class SpdyNetworkTransactionSpdy2Test
virtual void SetUp() {
// By default, all tests turn off compression.
EnableCompression(false);
+ SpdySession::set_default_protocol(SSLClientSocket::kProtoSPDY2);
google_get_request_initialized_ = false;
google_post_request_initialized_ = false;
google_chunked_post_request_initialized_ = false;
@@ -939,8 +940,7 @@ TEST_P(SpdyNetworkTransactionSpdy2Test, ThreeGetsWithMaxConcurrent) {
scoped_ptr<spdy::SpdyFrame> fbody3(ConstructSpdyBodyFrame(5, true));
spdy::SpdySettings settings;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
+ spdy::SettingsFlagsAndId id(0, spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
const size_t max_concurrent_streams = 1;
settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
@@ -1078,8 +1078,7 @@ TEST_P(SpdyNetworkTransactionSpdy2Test, FourGetsWithMaxConcurrentPriority) {
spdy::SpdySettings settings;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
+ spdy::SettingsFlagsAndId id(0, spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
const size_t max_concurrent_streams = 1;
settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
@@ -1223,8 +1222,7 @@ TEST_P(SpdyNetworkTransactionSpdy2Test, ThreeGetsWithMaxConcurrentDelete) {
scoped_ptr<spdy::SpdyFrame> fbody2(ConstructSpdyBodyFrame(3, true));
spdy::SpdySettings settings;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
+ spdy::SettingsFlagsAndId id(0, spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
const size_t max_concurrent_streams = 1;
settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
@@ -1359,8 +1357,7 @@ TEST_P(SpdyNetworkTransactionSpdy2Test, ThreeGetsWithMaxConcurrentSocketClose) {
scoped_ptr<spdy::SpdyFrame> resp2(ConstructSpdyGetSynReply(NULL, 0, 3));
spdy::SpdySettings settings;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
+ spdy::SettingsFlagsAndId id(0, spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
const size_t max_concurrent_streams = 1;
settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
@@ -2789,8 +2786,6 @@ TEST_P(SpdyNetworkTransactionSpdy2Test, ServerPushMultipleDataFrame) {
TEST_P(SpdyNetworkTransactionSpdy2Test,
ServerPushMultipleDataFrameInterrupted) {
- SpdySession::set_use_flow_control(SpdySession::kDisableFlowControl);
-
static const unsigned char kPushBodyFrame1[] = {
0x00, 0x00, 0x00, 0x02, // header, ID
0x01, 0x00, 0x00, 0x1F, // FIN, length
@@ -2852,8 +2847,6 @@ TEST_P(SpdyNetworkTransactionSpdy2Test,
// Verify the pushed stream.
EXPECT_TRUE(response2.headers != NULL);
EXPECT_EQ("HTTP/1.1 200 OK", response2.headers->GetStatusLine());
-
- SpdySession::set_use_flow_control(SpdySession::kFlowControlBasedOnNPN);
}
TEST_P(SpdyNetworkTransactionSpdy2Test, ServerPushInvalidAssociatedStreamID0) {
@@ -3568,8 +3561,6 @@ TEST_P(SpdyNetworkTransactionSpdy2Test, NetLog) {
// on the network, but issued a Read for only 5 of those bytes) that the data
// flow still works correctly.
TEST_P(SpdyNetworkTransactionSpdy2Test, BufferFull) {
- SpdySession::set_use_flow_control(SpdySession::kDisableFlowControl);
-
spdy::BufferedSpdyFramer framer(2);
scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
@@ -3659,16 +3650,12 @@ TEST_P(SpdyNetworkTransactionSpdy2Test, BufferFull) {
EXPECT_EQ(OK, out.rv);
EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
EXPECT_EQ("goodbye world", out.response_data);
-
- SpdySession::set_use_flow_control(SpdySession::kFlowControlBasedOnNPN);
}
// Verify that basic buffering works; when multiple data frames arrive
// at the same time, ensure that we don't notify a read completion for
// each data frame individually.
TEST_P(SpdyNetworkTransactionSpdy2Test, Buffering) {
- SpdySession::set_use_flow_control(SpdySession::kDisableFlowControl);
-
spdy::BufferedSpdyFramer framer(2);
scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
@@ -3760,8 +3747,6 @@ TEST_P(SpdyNetworkTransactionSpdy2Test, Buffering) {
EXPECT_EQ(OK, out.rv);
EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
EXPECT_EQ("messagemessagemessagemessage", out.response_data);
-
- SpdySession::set_use_flow_control(SpdySession::kFlowControlBasedOnNPN);
}
// Verify the case where we buffer data but read it after it has been buffered.
@@ -4065,25 +4050,23 @@ TEST_P(SpdyNetworkTransactionSpdy2Test, SettingsSaved) {
unsigned int kSampleValue1 = 0x0a0a0a0a;
unsigned int kSampleId2 = 0x2;
unsigned int kSampleValue2 = 0x0b0b0b0b;
- unsigned int kSampleId3 = 0xababab;
+ unsigned int kSampleId3 = 0x3;
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));
+ spdy::SettingsFlagsAndId setting1(spdy::SETTINGS_FLAG_PLEASE_PERSIST,
+ kSampleId1);
+ settings.push_back(std::make_pair(setting1, kSampleValue1));
// Next add a non-persisted setting
- setting.set_flags(0);
- setting.set_id(kSampleId2);
- settings.push_back(std::make_pair(setting, kSampleValue2));
+ spdy::SettingsFlagsAndId setting2(0, kSampleId2);
+ settings.push_back(std::make_pair(setting2, 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));
+ spdy::SettingsFlagsAndId setting3(spdy::SETTINGS_FLAG_PLEASE_PERSIST,
+ kSampleId3);
+ settings.push_back(std::make_pair(setting3, kSampleValue3));
settings_frame.reset(ConstructSpdySettings(settings));
}
@@ -4167,15 +4150,14 @@ TEST_P(SpdyNetworkTransactionSpdy2Test, SettingsPlayback) {
// 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));
+ spdy::SettingsFlagsAndId setting1(spdy::SETTINGS_FLAG_PLEASE_PERSIST,
+ kSampleId1);
+ settings.push_back(std::make_pair(setting1, 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));
+ spdy::SettingsFlagsAndId setting2(spdy::SETTINGS_FLAG_PLEASE_PERSIST,
+ kSampleId2);
+ settings.push_back(std::make_pair(setting2, kSampleValue2));
spdy_session_pool->http_server_properties()->SetSpdySettings(
host_port_pair, settings);
diff --git a/net/spdy/spdy_network_transaction_spdy3_unittest.cc b/net/spdy/spdy_network_transaction_spdy3_unittest.cc
index c156345..be8eb99 100644
--- a/net/spdy/spdy_network_transaction_spdy3_unittest.cc
+++ b/net/spdy/spdy_network_transaction_spdy3_unittest.cc
@@ -41,6 +41,7 @@ class SpdyNetworkTransactionSpdy3Test
virtual void SetUp() {
// By default, all tests turn off compression.
EnableCompression(false);
+ SpdySession::set_default_protocol(SSLClientSocket::kProtoSPDY3);
google_get_request_initialized_ = false;
google_post_request_initialized_ = false;
google_chunked_post_request_initialized_ = false;
@@ -125,12 +126,13 @@ class SpdyNetworkTransactionSpdy3Test
next_protos.push_back("http/1.1");
next_protos.push_back("spdy/2");
next_protos.push_back("spdy/2.1");
+ next_protos.push_back("spdy/3");
switch (test_type_) {
case SPDYNPN:
session_->http_server_properties()->SetAlternateProtocol(
HostPortPair("www.google.com", 80), 443,
- NPN_SPDY_21);
+ NPN_SPDY_3);
HttpStreamFactory::set_use_alternate_protocols(true);
HttpStreamFactory::SetNextProtos(next_protos);
break;
@@ -236,7 +238,7 @@ class SpdyNetworkTransactionSpdy3Test
linked_ptr<SSLSocketDataProvider> ssl_(
new SSLSocketDataProvider(ASYNC, OK));
if (test_type_ == SPDYNPN) {
- ssl_->SetNextProto(SSLClientSocket::kProtoSPDY21);
+ ssl_->SetNextProto(SSLClientSocket::kProtoSPDY3);
}
ssl_vector_.push_back(ssl_);
if (test_type_ == SPDYNPN || test_type_ == SPDYSSL)
@@ -261,7 +263,7 @@ class SpdyNetworkTransactionSpdy3Test
linked_ptr<SSLSocketDataProvider> ssl_(
new SSLSocketDataProvider(ASYNC, OK));
if (test_type_ == SPDYNPN) {
- ssl_->SetNextProto(SSLClientSocket::kProtoSPDY21);
+ ssl_->SetNextProto(SSLClientSocket::kProtoSPDY3);
}
ssl_vector_.push_back(ssl_);
if (test_type_ == SPDYNPN || test_type_ == SPDYSSL) {
@@ -939,8 +941,7 @@ TEST_P(SpdyNetworkTransactionSpdy3Test, ThreeGetsWithMaxConcurrent) {
scoped_ptr<spdy::SpdyFrame> fbody3(ConstructSpdyBodyFrame(5, true));
spdy::SpdySettings settings;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
+ spdy::SettingsFlagsAndId id(0, spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
const size_t max_concurrent_streams = 1;
settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
@@ -1078,8 +1079,7 @@ TEST_P(SpdyNetworkTransactionSpdy3Test, FourGetsWithMaxConcurrentPriority) {
spdy::SpdySettings settings;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
+ spdy::SettingsFlagsAndId id(0, spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
const size_t max_concurrent_streams = 1;
settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
@@ -1223,8 +1223,7 @@ TEST_P(SpdyNetworkTransactionSpdy3Test, ThreeGetsWithMaxConcurrentDelete) {
scoped_ptr<spdy::SpdyFrame> fbody2(ConstructSpdyBodyFrame(3, true));
spdy::SpdySettings settings;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
+ spdy::SettingsFlagsAndId id(0, spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
const size_t max_concurrent_streams = 1;
settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
@@ -1359,8 +1358,7 @@ TEST_P(SpdyNetworkTransactionSpdy3Test, ThreeGetsWithMaxConcurrentSocketClose) {
scoped_ptr<spdy::SpdyFrame> resp2(ConstructSpdyGetSynReply(NULL, 0, 3));
spdy::SpdySettings settings;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
+ spdy::SettingsFlagsAndId id(0, spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
const size_t max_concurrent_streams = 1;
settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
@@ -4444,25 +4442,23 @@ TEST_P(SpdyNetworkTransactionSpdy3Test, SettingsSaved) {
unsigned int kSampleValue1 = 0x0a0a0a0a;
unsigned int kSampleId2 = 0x2;
unsigned int kSampleValue2 = 0x0b0b0b0b;
- unsigned int kSampleId3 = 0xababab;
+ unsigned int kSampleId3 = 0x3;
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));
+ spdy::SettingsFlagsAndId setting1(spdy::SETTINGS_FLAG_PLEASE_PERSIST,
+ kSampleId1);
+ settings.push_back(std::make_pair(setting1, kSampleValue1));
// Next add a non-persisted setting
- setting.set_flags(0);
- setting.set_id(kSampleId2);
- settings.push_back(std::make_pair(setting, kSampleValue2));
+ spdy::SettingsFlagsAndId setting2(0, kSampleId2);
+ settings.push_back(std::make_pair(setting2, 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));
+ spdy::SettingsFlagsAndId setting3(spdy::SETTINGS_FLAG_PLEASE_PERSIST,
+ kSampleId3);
+ settings.push_back(std::make_pair(setting3, kSampleValue3));
settings_frame.reset(ConstructSpdySettings(settings));
}
@@ -4546,15 +4542,14 @@ TEST_P(SpdyNetworkTransactionSpdy3Test, SettingsPlayback) {
// 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));
+ spdy::SettingsFlagsAndId setting1(spdy::SETTINGS_FLAG_PLEASE_PERSIST,
+ kSampleId1);
+ settings.push_back(std::make_pair(setting1, 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));
+ spdy::SettingsFlagsAndId setting2(spdy::SETTINGS_FLAG_PLEASE_PERSIST,
+ kSampleId2);
+ settings.push_back(std::make_pair(setting2, kSampleValue2));
spdy_session_pool->http_server_properties()->SetSpdySettings(
host_port_pair, settings);
diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h
index fcfe8b5..cb09456 100644
--- a/net/spdy/spdy_protocol.h
+++ b/net/spdy/spdy_protocol.h
@@ -141,17 +141,14 @@
// | Certificate | |
// +----------------------------------+ <+
//
-namespace spdy {
-// This implementation of Spdy is version 2; It's like version 1, with some
-// minor tweaks.
-const int kSpdyProtocolVersion = 2;
+namespace spdy {
// Initial window size for a Spdy stream
-const size_t kSpdyStreamInitialWindowSize = 64 * 1024; // 64 KBytes
+const int32 kSpdyStreamInitialWindowSize = 64 * 1024; // 64 KBytes
// Maximum window size for a Spdy stream
-const size_t kSpdyStreamMaximumWindowSize = 0x7FFFFFFF; // Max signed 32bit int
+const int32 kSpdyStreamMaximumWindowSize = 0x7FFFFFFF; // Max signed 32bit int
// HTTP-over-SPDY header constants
const char kMethod[] = "method";
@@ -162,6 +159,207 @@ const char kVersion[] = "version";
// push headers so that the client will know what url the data corresponds to.
const char kPath[] = "path";
+// SPDY 2 dictionary.
+// This is just a hacked dictionary to use for shrinking HTTP-like headers.
+const char kV2Dictionary[] =
+ "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-"
+ "languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi"
+ "f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser"
+ "-agent10010120020120220320420520630030130230330430530630740040140240340440"
+ "5406407408409410411412413414415416417500501502503504505accept-rangesageeta"
+ "glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic"
+ "ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran"
+ "sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati"
+ "oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo"
+ "ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe"
+ "pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic"
+ "ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1"
+ ".1statusversionurl";
+const int kV2DictionarySize = arraysize(kV2Dictionary);
+
+// SPDY 3 dictionary.
+const char kV3Dictionary[] = {
+ 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, // ....opti
+ 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, // ons....h
+ 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, // ead....p
+ 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, // ost....p
+ 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, // ut....de
+ 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, // lete....
+ 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, // trace...
+ 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, // .accept.
+ 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, // ...accep
+ 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // t-charse
+ 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, // t....acc
+ 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // ept-enco
+ 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, // ding....
+ 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, // accept-l
+ 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, // anguage.
+ 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, // ...accep
+ 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, // t-ranges
+ 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, // ....age.
+ 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, // ...allow
+ 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, // ....auth
+ 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, // orizatio
+ 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, // n....cac
+ 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, // he-contr
+ 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, // ol....co
+ 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, // nnection
+ 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, // ....cont
+ 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, // ent-base
+ 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, // ....cont
+ 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // ent-enco
+ 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, // ding....
+ 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, // content-
+ 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, // language
+ 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, // ....cont
+ 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, // ent-leng
+ 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, // th....co
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, // ntent-lo
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, // cation..
+ 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // ..conten
+ 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, // t-md5...
+ 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, // .content
+ 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, // -range..
+ 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // ..conten
+ 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, // t-type..
+ 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, // ..date..
+ 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, // ..etag..
+ 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, // ..expect
+ 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, // ....expi
+ 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, // res....f
+ 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, // rom....h
+ 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, // ost....i
+ 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, // f-match.
+ 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, // ...if-mo
+ 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, // dified-s
+ 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, // ince....
+ 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, // if-none-
+ 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, // match...
+ 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, // .if-rang
+ 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, // e....if-
+ 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, // unmodifi
+ 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, // ed-since
+ 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, // ....last
+ 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, // -modifie
+ 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, // d....loc
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, // ation...
+ 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, // .max-for
+ 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, // wards...
+ 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, // .pragma.
+ 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, // ...proxy
+ 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, // -authent
+ 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, // icate...
+ 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, // .proxy-a
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, // uthoriza
+ 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, // tion....
+ 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, // range...
+ 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, // .referer
+ 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, // ....retr
+ 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, // y-after.
+ 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, // ...serve
+ 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, // r....te.
+ 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, // ...trail
+ 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, // er....tr
+ 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, // ansfer-e
+ 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, // ncoding.
+ 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, // ...upgra
+ 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, // de....us
+ 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, // er-agent
+ 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, // ....vary
+ 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, // ....via.
+ 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, // ...warni
+ 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, // ng....ww
+ 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, // w-authen
+ 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, // ticate..
+ 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, // ..method
+ 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, // ....get.
+ 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, // ...statu
+ 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, // s....200
+ 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, // .OK....v
+ 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, // ersion..
+ 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, // ..HTTP.1
+ 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, // .1....ur
+ 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, // l....pub
+ 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, // lic....s
+ 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, // et-cooki
+ 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, // e....kee
+ 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, // p-alive.
+ 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, // ...origi
+ 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, // n1001012
+ 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, // 01202205
+ 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, // 20630030
+ 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, // 23033043
+ 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, // 05306307
+ 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, // 40240540
+ 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, // 64074084
+ 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, // 09410411
+ 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, // 41241341
+ 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, // 44154164
+ 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, // 17502504
+ 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, // 505203.N
+ 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, // on-Autho
+ 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, // ritative
+ 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, // .Informa
+ 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, // tion204.
+ 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, // No.Conte
+ 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, // nt301.Mo
+ 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, // ved.Perm
+ 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, // anently4
+ 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, // 00.Bad.R
+ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, // equest40
+ 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, // 1.Unauth
+ 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, // orized40
+ 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, // 3.Forbid
+ 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, // den404.N
+ 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, // ot.Found
+ 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, // 500.Inte
+ 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, // rnal.Ser
+ 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, // ver.Erro
+ 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, // r501.Not
+ 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, // .Impleme
+ 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, // nted503.
+ 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, // Service.
+ 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, // Unavaila
+ 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, // bleJan.F
+ 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, // eb.Mar.A
+ 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, // pr.May.J
+ 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, // un.Jul.A
+ 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, // ug.Sept.
+ 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, // Oct.Nov.
+ 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, // Dec.00.0
+ 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, // 0.00.Mon
+ 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, // ..Tue..W
+ 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, // ed..Thu.
+ 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, // .Fri..Sa
+ 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, // t..Sun..
+ 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, // GMTchunk
+ 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, // ed.text.
+ 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, // html.ima
+ 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, // ge.png.i
+ 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, // mage.jpg
+ 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, // .image.g
+ 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // if.appli
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // cation.x
+ 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // ml.appli
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // cation.x
+ 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, // html.xml
+ 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, // .text.pl
+ 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, // ain.text
+ 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, // .javascr
+ 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, // ipt.publ
+ 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, // icprivat
+ 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, // emax-age
+ 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, // .gzip.de
+ 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, // flate.sd
+ 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // chcharse
+ 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, // t.utf-8c
+ 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, // harset.i
+ 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, // so-8859-
+ 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, // 1.utf-..
+ 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e // .enq.0.
+};
+const int kV3DictionarySize = arraysize(kV3Dictionary);
+
// Note: all protocol data structures are on-the-wire format. That means that
// data is stored in network-normalized order. Readers must use the
// accessors provided or call ntohX() functions.
@@ -172,7 +370,7 @@ enum SpdyControlType {
SYN_REPLY,
RST_STREAM,
SETTINGS,
- NOOP,
+ NOOP, // Because it is valid in SPDY/2, kept for identifiability/enum order.
PING,
GOAWAY,
HEADERS,
@@ -239,7 +437,9 @@ enum SpdyStatusCodes {
// A SPDY stream id is a 31 bit entity.
typedef uint32 SpdyStreamId;
-// A SPDY priority is a number between 0 and 3 (inclusive).
+// A SPDY priority is a number between 0 and 7 (inclusive).
+// SPDY priority range is version-dependant. For SPDY 2 and below, priority is a
+// number between 0 and 3.
typedef uint8 SpdyPriority;
// SPDY Priorities. (there are only 2 bits)
@@ -285,7 +485,6 @@ struct SpdySynStreamControlFrameBlock : SpdyFrameBlock {
// A SYN_REPLY Control Frame structure.
struct SpdySynReplyControlFrameBlock : SpdyFrameBlock {
SpdyStreamId stream_id_;
- uint16 unused_;
};
// A RST_STREAM Control Frame structure.
@@ -300,15 +499,12 @@ struct SpdySettingsControlFrameBlock : SpdyFrameBlock {
// Variable data here.
};
-// A NOOP Control Frame structure.
-struct SpdyNoopControlFrameBlock : SpdyFrameBlock {
-};
-
// A PING Control Frame structure.
struct SpdyPingControlFrameBlock : SpdyFrameBlock {
uint32 unique_id_;
};
+// TODO(avd): remove this struct
// A CREDENTIAL Control Frame structure.
struct SpdyCredentialControlFrameBlock : SpdyFrameBlock {
uint16 slot_;
@@ -326,7 +522,6 @@ struct SpdyGoAwayControlFrameBlock : SpdyFrameBlock {
// A HEADERS Control Frame structure.
struct SpdyHeadersControlFrameBlock : SpdyFrameBlock {
SpdyStreamId stream_id_;
- uint16 unused_;
};
// A WINDOW_UPDATE Control Frame structure
@@ -335,22 +530,6 @@ struct SpdyWindowUpdateControlFrameBlock : SpdyFrameBlock {
uint32 delta_window_size_;
};
-// A structure for the 8 bit flags and 24 bit ID fields.
-union SettingsFlagsAndId {
- // Sets both flags and id to the value for flags-and-id as sent over the wire
- 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((flags() << 24) | (id & kSettingsIdMask));
- }
-
- uint8 flags_[4]; // 8 bits
- uint32 id_; // 24 bits
-};
-
#pragma pack(pop)
// -------------------------------------------------------------------------
@@ -467,7 +646,10 @@ class SpdyControlFrame : public SpdyFrame {
bool AppearsToBeAValidControlFrame() const {
// Right now we only check if the frame has an out-of-bounds type.
uint16 type = ntohs(block()->control_.type_);
- return (type >= SYN_STREAM && type < NUM_CONTROL_FRAME_TYPES);
+ // NOOP is not a 'valid' control frame in SPDY/3 and beyond.
+ return type >= SYN_STREAM &&
+ type < NUM_CONTROL_FRAME_TYPES &&
+ (version() == 2 || type != NOOP);
}
uint16 version() const {
@@ -476,13 +658,15 @@ class SpdyControlFrame : public SpdyFrame {
}
void set_version(uint16 version) {
- DCHECK_EQ(0u, version & kControlFlagMask);
- mutable_block()->control_.version_ = htons(kControlFlagMask | version);
+ const uint16 kControlBit = 0x80;
+ DCHECK_EQ(0, version & kControlBit);
+ mutable_block()->control_.version_ = kControlBit | htons(version);
}
SpdyControlType type() const {
uint16 type = ntohs(block()->control_.type_);
- DCHECK(type >= SYN_STREAM && type < NUM_CONTROL_FRAME_TYPES);
+ LOG_IF(DFATAL, type < SYN_STREAM || type >= NUM_CONTROL_FRAME_TYPES)
+ << "Invalid control frame type " << type;
return static_cast<SpdyControlType>(type);
}
@@ -497,15 +681,6 @@ class SpdyControlFrame : public SpdyFrame {
return type() == SYN_STREAM || type() == SYN_REPLY || type() == HEADERS;
}
- // The size of the 'Number of Name/Value pairs' field in a Name/Value block.
- static const size_t kNumNameValuePairsSize = 2;
-
- // The size of the 'Length of a name' field in a Name/Value block.
- static const size_t kLengthOfNameSize = 2;
-
- // The size of the 'Length of a value' field in a Name/Value block.
- static const size_t kLengthOfValueSize = 2;
-
private:
const struct SpdyFrameBlock* block() const {
return frame_;
@@ -540,7 +715,11 @@ class SpdySynStreamControlFrame : public SpdyControlFrame {
}
SpdyPriority priority() const {
- return (block()->priority_ & kPriorityMask) >> 6;
+ if (version() < 3) {
+ return (block()->priority_ & kSpdy2PriorityMask) >> 6;
+ } else {
+ return (block()->priority_ & kSpdy3PriorityMask) >> 5;
+ }
}
// The number of bytes in the header block beyond the frame header length.
@@ -582,11 +761,21 @@ class SpdySynReplyControlFrame : public SpdyControlFrame {
}
int header_block_len() const {
- return length() - (size() - SpdyFrame::kHeaderSize);
+ size_t header_block_len = length() - (size() - SpdyFrame::kHeaderSize);
+ // SPDY 2 had 2 bytes of unused space preceeding the header block.
+ if (version() < 3) {
+ header_block_len -= 2;
+ }
+ return header_block_len;
}
const char* header_block() const {
- return reinterpret_cast<const char*>(block()) + size();
+ const char* header_block = reinterpret_cast<const char*>(block()) + size();
+ // SPDY 2 had 2 bytes of unused space preceeding the header block.
+ if (version() < 3) {
+ header_block += 2;
+ }
+ return header_block;
}
// Returns the size of the SpdySynReplyControlFrameBlock structure.
@@ -680,15 +869,6 @@ class SpdySettingsControlFrame : public SpdyControlFrame {
DISALLOW_COPY_AND_ASSIGN(SpdySettingsControlFrame);
};
-class SpdyNoOpControlFrame : public SpdyControlFrame {
- public:
- SpdyNoOpControlFrame() : SpdyControlFrame(size()) {}
- SpdyNoOpControlFrame(char* data, bool owns_buffer)
- : SpdyControlFrame(data, owns_buffer) {}
-
- static size_t size() { return sizeof(SpdyNoopControlFrameBlock); }
-};
-
class SpdyPingControlFrame : public SpdyControlFrame {
public:
SpdyPingControlFrame() : SpdyControlFrame(size()) {}
@@ -776,11 +956,21 @@ class SpdyHeadersControlFrame : public SpdyControlFrame {
// The number of bytes in the header block beyond the frame header length.
int header_block_len() const {
- return length() - (size() - SpdyFrame::kHeaderSize);
+ size_t header_block_len = length() - (size() - SpdyFrame::kHeaderSize);
+ // SPDY 2 had 2 bytes of unused space preceeding the header block.
+ if (version() < 3) {
+ header_block_len -= 2;
+ }
+ return header_block_len;
}
const char* header_block() const {
- return reinterpret_cast<const char*>(block()) + size();
+ const char* header_block = reinterpret_cast<const char*>(block()) + size();
+ // SPDY 2 had 2 bytes of unused space preceeding the header block.
+ if (version() < 3) {
+ header_block += 2;
+ }
+ return header_block;
}
// Returns the size of the SpdyHeadersControlFrameBlock structure.
diff --git a/net/spdy/spdy_protocol_test.cc b/net/spdy/spdy_protocol_spdy2_test.cc
index 46dd5f0..cfa7951 100644
--- a/net/spdy/spdy_protocol_test.cc
+++ b/net/spdy/spdy_protocol_spdy2_test.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -11,6 +11,7 @@
using spdy::CONTROL_FLAG_FIN;
using spdy::CONTROL_FLAG_NONE;
+using spdy::FlagsAndLength;
using spdy::GOAWAY;
using spdy::HEADERS;
using spdy::NOOP;
@@ -20,17 +21,15 @@ using spdy::RST_STREAM;
using spdy::SETTINGS;
using spdy::SYN_REPLY;
using spdy::SYN_STREAM;
-using spdy::WINDOW_UPDATE;
-using spdy::FlagsAndLength;
+using spdy::SettingsFlagsAndId;
using spdy::SpdyControlFrame;
using spdy::SpdyControlType;
using spdy::SpdyDataFrame;
using spdy::SpdyFrame;
using spdy::SpdyFramer;
+using spdy::SpdyGoAwayControlFrame;
using spdy::SpdyHeaderBlock;
using spdy::SpdyHeadersControlFrame;
-using spdy::SpdyGoAwayControlFrame;
-using spdy::SpdyNoOpControlFrame;
using spdy::SpdyPingControlFrame;
using spdy::SpdyRstStreamControlFrame;
using spdy::SpdySettings;
@@ -39,26 +38,27 @@ using spdy::SpdyStatusCodes;
using spdy::SpdySynReplyControlFrame;
using spdy::SpdySynStreamControlFrame;
using spdy::SpdyWindowUpdateControlFrame;
-using spdy::SettingsFlagsAndId;
+using spdy::WINDOW_UPDATE;
using spdy::kLengthMask;
-using spdy::kSpdyProtocolVersion;
using spdy::kStreamIdMask;
namespace {
+// Default SPDY version for unit tests.
+const int SPDY_VERSION_FOR_TESTS = 2;
+
// Test our protocol constants
-TEST(SpdyProtocolTest, ProtocolConstants) {
+TEST(SpdyProtocolSpdy2Test, ProtocolConstants) {
EXPECT_EQ(8u, SpdyFrame::kHeaderSize);
EXPECT_EQ(8u, SpdyDataFrame::size());
EXPECT_EQ(8u, SpdyControlFrame::kHeaderSize);
EXPECT_EQ(18u, SpdySynStreamControlFrame::size());
- EXPECT_EQ(14u, SpdySynReplyControlFrame::size());
+ EXPECT_EQ(12u, SpdySynReplyControlFrame::size());
EXPECT_EQ(16u, SpdyRstStreamControlFrame::size());
EXPECT_EQ(12u, SpdySettingsControlFrame::size());
- EXPECT_EQ(8u, SpdyNoOpControlFrame::size());
EXPECT_EQ(12u, SpdyPingControlFrame::size());
EXPECT_EQ(12u, SpdyGoAwayControlFrame::size());
- EXPECT_EQ(14u, SpdyHeadersControlFrame::size());
+ EXPECT_EQ(12u, SpdyHeadersControlFrame::size());
EXPECT_EQ(16u, SpdyWindowUpdateControlFrame::size());
EXPECT_EQ(4u, sizeof(FlagsAndLength));
EXPECT_EQ(1, SYN_STREAM);
@@ -73,7 +73,7 @@ TEST(SpdyProtocolTest, ProtocolConstants) {
}
// Test some of the protocol helper functions
-TEST(SpdyProtocolTest, FrameStructs) {
+TEST(SpdyProtocolSpdy2Test, FrameStructs) {
SpdyFrame frame(SpdyFrame::kHeaderSize);
frame.set_length(12345);
frame.set_flags(10);
@@ -88,25 +88,26 @@ TEST(SpdyProtocolTest, FrameStructs) {
EXPECT_FALSE(frame.is_control_frame());
}
-TEST(SpdyProtocolTest, DataFrameStructs) {
+TEST(SpdyProtocolSpdy2Test, DataFrameStructs) {
SpdyDataFrame data_frame;
data_frame.set_stream_id(12345);
EXPECT_EQ(12345u, data_frame.stream_id());
}
-TEST(SpdyProtocolTest, ControlFrameStructs) {
- SpdyFramer framer;
+TEST(SpdyProtocolSpdy2Test, ControlFrameStructs) {
+ SpdyFramer framer(SPDY_VERSION_FOR_TESTS);
SpdyHeaderBlock headers;
scoped_ptr<SpdySynStreamControlFrame> syn_frame(
framer.CreateSynStream(123, 456, 2, CONTROL_FLAG_FIN, false, &headers));
- EXPECT_EQ(kSpdyProtocolVersion, syn_frame->version());
+ EXPECT_EQ(framer.protocol_version(), syn_frame->version());
EXPECT_TRUE(syn_frame->is_control_frame());
EXPECT_EQ(SYN_STREAM, syn_frame->type());
EXPECT_EQ(123u, syn_frame->stream_id());
EXPECT_EQ(456u, syn_frame->associated_stream_id());
EXPECT_EQ(2u, syn_frame->priority());
- EXPECT_EQ(2, syn_frame->header_block_len());
+ EXPECT_EQ((SPDY_VERSION_FOR_TESTS < 3) ? 2 : 4,
+ syn_frame->header_block_len());
EXPECT_EQ(1u, syn_frame->flags());
syn_frame->set_associated_stream_id(999u);
EXPECT_EQ(123u, syn_frame->stream_id());
@@ -114,16 +115,17 @@ TEST(SpdyProtocolTest, ControlFrameStructs) {
scoped_ptr<SpdySynReplyControlFrame> syn_reply(
framer.CreateSynReply(123, CONTROL_FLAG_NONE, false, &headers));
- EXPECT_EQ(kSpdyProtocolVersion, syn_reply->version());
+ EXPECT_EQ(framer.protocol_version(), syn_reply->version());
EXPECT_TRUE(syn_reply->is_control_frame());
EXPECT_EQ(SYN_REPLY, syn_reply->type());
EXPECT_EQ(123u, syn_reply->stream_id());
- EXPECT_EQ(2, syn_reply->header_block_len());
+ EXPECT_EQ((SPDY_VERSION_FOR_TESTS < 3) ? 2 : 4,
+ syn_reply->header_block_len());
EXPECT_EQ(0, syn_reply->flags());
scoped_ptr<SpdyRstStreamControlFrame> rst_frame(
framer.CreateRstStream(123, spdy::PROTOCOL_ERROR));
- EXPECT_EQ(kSpdyProtocolVersion, rst_frame->version());
+ EXPECT_EQ(framer.protocol_version(), rst_frame->version());
EXPECT_TRUE(rst_frame->is_control_frame());
EXPECT_EQ(RST_STREAM, rst_frame->type());
EXPECT_EQ(123u, rst_frame->stream_id());
@@ -132,18 +134,11 @@ TEST(SpdyProtocolTest, ControlFrameStructs) {
EXPECT_EQ(spdy::INVALID_STREAM, rst_frame->status());
EXPECT_EQ(0, rst_frame->flags());
- scoped_ptr<SpdyNoOpControlFrame> noop_frame(
- framer.CreateNopFrame());
- EXPECT_EQ(kSpdyProtocolVersion, noop_frame->version());
- EXPECT_TRUE(noop_frame->is_control_frame());
- EXPECT_EQ(NOOP, noop_frame->type());
- EXPECT_EQ(0, noop_frame->flags());
-
const uint32 kUniqueId = 1234567u;
const uint32 kUniqueId2 = 31415926u;
scoped_ptr<SpdyPingControlFrame> ping_frame(
framer.CreatePingFrame(kUniqueId));
- EXPECT_EQ(kSpdyProtocolVersion, ping_frame->version());
+ EXPECT_EQ(framer.protocol_version(), ping_frame->version());
EXPECT_TRUE(ping_frame->is_control_frame());
EXPECT_EQ(PING, ping_frame->type());
EXPECT_EQ(kUniqueId, ping_frame->unique_id());
@@ -152,30 +147,31 @@ TEST(SpdyProtocolTest, ControlFrameStructs) {
scoped_ptr<SpdyGoAwayControlFrame> goaway_frame(
framer.CreateGoAway(123));
- EXPECT_EQ(kSpdyProtocolVersion, goaway_frame->version());
+ EXPECT_EQ(framer.protocol_version(), goaway_frame->version());
EXPECT_TRUE(goaway_frame->is_control_frame());
EXPECT_EQ(GOAWAY, goaway_frame->type());
EXPECT_EQ(123u, goaway_frame->last_accepted_stream_id());
scoped_ptr<SpdyHeadersControlFrame> headers_frame(
framer.CreateHeaders(123, CONTROL_FLAG_NONE, false, &headers));
- EXPECT_EQ(kSpdyProtocolVersion, headers_frame->version());
+ EXPECT_EQ(framer.protocol_version(), headers_frame->version());
EXPECT_TRUE(headers_frame->is_control_frame());
EXPECT_EQ(HEADERS, headers_frame->type());
EXPECT_EQ(123u, headers_frame->stream_id());
- EXPECT_EQ(2, headers_frame->header_block_len());
+ EXPECT_EQ((SPDY_VERSION_FOR_TESTS < 3) ? 2 : 4,
+ headers_frame->header_block_len());
EXPECT_EQ(0, headers_frame->flags());
scoped_ptr<SpdyWindowUpdateControlFrame> window_update_frame(
framer.CreateWindowUpdate(123, 456));
- EXPECT_EQ(kSpdyProtocolVersion, window_update_frame->version());
+ EXPECT_EQ(framer.protocol_version(), window_update_frame->version());
EXPECT_TRUE(window_update_frame->is_control_frame());
EXPECT_EQ(WINDOW_UPDATE, window_update_frame->type());
EXPECT_EQ(123u, window_update_frame->stream_id());
EXPECT_EQ(456u, window_update_frame->delta_window_size());
}
-TEST(SpdyProtocolTest, TestDataFrame) {
+TEST(SpdyProtocolSpdy2Test, TestDataFrame) {
SpdyDataFrame frame;
// Set the stream ID to various values.
@@ -218,14 +214,14 @@ TEST(SpdyProtocolTest, TestDataFrame) {
}
// Test various types of SETTINGS frames.
-TEST(SpdyProtocolTest, TestSpdySettingsFrame) {
- SpdyFramer framer;
+TEST(SpdyProtocolSpdy2Test, TestSpdySettingsFrame) {
+ SpdyFramer framer(SPDY_VERSION_FOR_TESTS);
// Create a settings frame with no settings.
SpdySettings settings;
scoped_ptr<SpdySettingsControlFrame> settings_frame(
framer.CreateSettings(settings));
- EXPECT_EQ(kSpdyProtocolVersion, settings_frame->version());
+ EXPECT_EQ(framer.protocol_version(), settings_frame->version());
EXPECT_TRUE(settings_frame->is_control_frame());
EXPECT_EQ(SETTINGS, settings_frame->type());
EXPECT_EQ(0u, settings_frame->num_entries());
@@ -233,38 +229,40 @@ TEST(SpdyProtocolTest, TestSpdySettingsFrame) {
// We'll add several different ID/Flag combinations and then verify
// that they encode and decode properly.
SettingsFlagsAndId ids[] = {
- 0x00000000,
- 0xffffffff,
- 0xff000001,
- 0x01000002,
+ SettingsFlagsAndId::FromWireFormat(SPDY_VERSION_FOR_TESTS, 0x00000000),
+ SettingsFlagsAndId::FromWireFormat(SPDY_VERSION_FOR_TESTS, 0xffffffff),
+ SettingsFlagsAndId::FromWireFormat(SPDY_VERSION_FOR_TESTS, 0xff000001),
+ SettingsFlagsAndId::FromWireFormat(SPDY_VERSION_FOR_TESTS, 0xffffffff),
+ SettingsFlagsAndId::FromWireFormat(SPDY_VERSION_FOR_TESTS, 0x01000002),
+ SettingsFlagsAndId(3, 1)
};
for (size_t index = 0; index < arraysize(ids); ++index) {
settings.insert(settings.end(), std::make_pair(ids[index], index));
settings_frame.reset(framer.CreateSettings(settings));
- EXPECT_EQ(kSpdyProtocolVersion, settings_frame->version());
+ EXPECT_EQ(framer.protocol_version(), settings_frame->version());
EXPECT_TRUE(settings_frame->is_control_frame());
EXPECT_EQ(SETTINGS, settings_frame->type());
EXPECT_EQ(index + 1, settings_frame->num_entries());
SpdySettings parsed_settings;
EXPECT_TRUE(framer.ParseSettings(settings_frame.get(), &parsed_settings));
- EXPECT_EQ(parsed_settings.size(), settings.size());
+ EXPECT_EQ(settings.size(), parsed_settings.size());
SpdySettings::const_iterator it = parsed_settings.begin();
int pos = 0;
while (it != parsed_settings.end()) {
SettingsFlagsAndId parsed = it->first;
uint32 value = it->second;
- EXPECT_EQ(parsed.flags(), ids[pos].flags());
- EXPECT_EQ(parsed.id(), ids[pos].id());
- EXPECT_EQ(value, static_cast<uint32>(pos));
+ EXPECT_EQ(ids[pos].flags(), parsed.flags());
+ EXPECT_EQ(ids[pos].id(), parsed.id());
+ EXPECT_EQ(static_cast<uint32>(pos), value);
++it;
++pos;
}
}
}
-TEST(SpdyProtocolTest, HasHeaderBlock) {
+TEST(SpdyProtocolSpdy2Test, HasHeaderBlock) {
SpdyControlFrame frame(SpdyControlFrame::kHeaderSize);
for (SpdyControlType type = SYN_STREAM;
type < NUM_CONTROL_FRAME_TYPES;
@@ -281,7 +279,7 @@ TEST(SpdyProtocolTest, HasHeaderBlock) {
// Make sure that overflows both die in debug mode, and do not cause problems
// in opt mode. Note: The EXPECT_DEBUG_DEATH call does not work on Win32 yet,
// so we comment it out.
-TEST(SpdyProtocolDeathTest, TestDataFrame) {
+TEST(SpdyProtocolDeathSpdy2Test, TestDataFrame) {
SpdyDataFrame frame;
frame.set_stream_id(0);
@@ -306,7 +304,7 @@ TEST(SpdyProtocolDeathTest, TestDataFrame) {
EXPECT_EQ(0, frame.flags());
}
-TEST(SpdyProtocolDeathTest, TestSpdyControlFrameStreamId) {
+TEST(SpdyProtocolDeathSpdy2Test, TestSpdyControlFrameStreamId) {
SpdyControlFrame frame_store(SpdySynStreamControlFrame::size());
memset(frame_store.data(), '1', SpdyControlFrame::kHeaderSize);
SpdySynStreamControlFrame* frame =
@@ -321,7 +319,7 @@ TEST(SpdyProtocolDeathTest, TestSpdyControlFrameStreamId) {
EXPECT_FALSE(frame->is_control_frame());
}
-TEST(SpdyProtocolDeathTest, TestSpdyControlFrameVersion) {
+TEST(SpdyProtocolDeathSpdy2Test, TestSpdyControlFrameVersion) {
const unsigned int kVersionMask = 0x7fff;
SpdyControlFrame frame(SpdySynStreamControlFrame::size());
memset(frame.data(), '1', SpdyControlFrame::kHeaderSize);
@@ -342,16 +340,17 @@ TEST(SpdyProtocolDeathTest, TestSpdyControlFrameVersion) {
EXPECT_EQ(SYN_STREAM, frame.type());
}
-TEST(SpdyProtocolDeathTest, TestSpdyControlFrameType) {
+TEST(SpdyProtocolDeathSpdy2Test, TestSpdyControlFrameType) {
SpdyControlFrame frame(SpdyControlFrame::kHeaderSize);
memset(frame.data(), 255, SpdyControlFrame::kHeaderSize);
// type() should be out of bounds.
EXPECT_FALSE(frame.AppearsToBeAValidControlFrame());
+ frame.set_version(SPDY_VERSION_FOR_TESTS);
uint16 version = frame.version();
- for (int i = SYN_STREAM; i <= spdy::NOOP; ++i) {
+ for (int i = SYN_STREAM; i <= spdy::WINDOW_UPDATE; ++i) {
frame.set_type(static_cast<SpdyControlType>(i));
EXPECT_EQ(i, static_cast<int>(frame.type()));
EXPECT_TRUE(frame.AppearsToBeAValidControlFrame());
@@ -361,8 +360,8 @@ TEST(SpdyProtocolDeathTest, TestSpdyControlFrameType) {
}
}
-TEST(SpdyProtocolDeathTest, TestRstStreamStatusBounds) {
- SpdyFramer framer;
+TEST(SpdyProtocolDeathSpdy2Test, TestRstStreamStatusBounds) {
+ SpdyFramer framer(SPDY_VERSION_FOR_TESTS);
scoped_ptr<SpdyRstStreamControlFrame> rst_frame;
rst_frame.reset(framer.CreateRstStream(123, spdy::PROTOCOL_ERROR));
diff --git a/net/spdy/spdy_protocol_spdy3_test.cc b/net/spdy/spdy_protocol_spdy3_test.cc
new file mode 100644
index 0000000..918436b
--- /dev/null
+++ b/net/spdy/spdy_protocol_spdy3_test.cc
@@ -0,0 +1,386 @@
+// Copyright (c) 2012 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_protocol.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "net/spdy/spdy_bitmasks.h"
+#include "net/spdy/spdy_framer.h"
+#include "testing/platform_test.h"
+
+using spdy::CONTROL_FLAG_FIN;
+using spdy::CONTROL_FLAG_NONE;
+using spdy::FlagsAndLength;
+using spdy::GOAWAY;
+using spdy::HEADERS;
+using spdy::NOOP;
+using spdy::NUM_CONTROL_FRAME_TYPES;
+using spdy::PING;
+using spdy::RST_STREAM;
+using spdy::SETTINGS;
+using spdy::SYN_REPLY;
+using spdy::SYN_STREAM;
+using spdy::SettingsFlagsAndId;
+using spdy::SpdyControlFrame;
+using spdy::SpdyControlType;
+using spdy::SpdyDataFrame;
+using spdy::SpdyFrame;
+using spdy::SpdyFramer;
+using spdy::SpdyGoAwayControlFrame;
+using spdy::SpdyHeaderBlock;
+using spdy::SpdyHeadersControlFrame;
+using spdy::SpdyPingControlFrame;
+using spdy::SpdyRstStreamControlFrame;
+using spdy::SpdySettings;
+using spdy::SpdySettingsControlFrame;
+using spdy::SpdyStatusCodes;
+using spdy::SpdySynReplyControlFrame;
+using spdy::SpdySynStreamControlFrame;
+using spdy::SpdyWindowUpdateControlFrame;
+using spdy::WINDOW_UPDATE;
+using spdy::kLengthMask;
+using spdy::kStreamIdMask;
+
+namespace {
+
+// Default SPDY version for unit tests.
+const int SPDY_VERSION_FOR_TESTS = 3;
+
+// Test our protocol constants
+TEST(SpdyProtocolSpdy3Test, ProtocolConstants) {
+ EXPECT_EQ(8u, SpdyFrame::kHeaderSize);
+ EXPECT_EQ(8u, SpdyDataFrame::size());
+ EXPECT_EQ(8u, SpdyControlFrame::kHeaderSize);
+ EXPECT_EQ(18u, SpdySynStreamControlFrame::size());
+ EXPECT_EQ(12u, SpdySynReplyControlFrame::size());
+ EXPECT_EQ(16u, SpdyRstStreamControlFrame::size());
+ EXPECT_EQ(12u, SpdySettingsControlFrame::size());
+ EXPECT_EQ(12u, SpdyPingControlFrame::size());
+ EXPECT_EQ(12u, SpdyGoAwayControlFrame::size());
+ EXPECT_EQ(12u, SpdyHeadersControlFrame::size());
+ EXPECT_EQ(16u, SpdyWindowUpdateControlFrame::size());
+ EXPECT_EQ(4u, sizeof(FlagsAndLength));
+ EXPECT_EQ(1, SYN_STREAM);
+ EXPECT_EQ(2, SYN_REPLY);
+ EXPECT_EQ(3, RST_STREAM);
+ EXPECT_EQ(4, SETTINGS);
+ EXPECT_EQ(5, NOOP);
+ EXPECT_EQ(6, PING);
+ EXPECT_EQ(7, GOAWAY);
+ EXPECT_EQ(8, HEADERS);
+ EXPECT_EQ(9, WINDOW_UPDATE);
+}
+
+// Test some of the protocol helper functions
+TEST(SpdyProtocolSpdy3Test, FrameStructs) {
+ SpdyFrame frame(SpdyFrame::kHeaderSize);
+ frame.set_length(12345);
+ frame.set_flags(10);
+ EXPECT_EQ(12345u, frame.length());
+ EXPECT_EQ(10u, frame.flags());
+ EXPECT_FALSE(frame.is_control_frame());
+
+ frame.set_length(0);
+ frame.set_flags(10);
+ EXPECT_EQ(0u, frame.length());
+ EXPECT_EQ(10u, frame.flags());
+ EXPECT_FALSE(frame.is_control_frame());
+}
+
+TEST(SpdyProtocolSpdy3Test, DataFrameStructs) {
+ SpdyDataFrame data_frame;
+ data_frame.set_stream_id(12345);
+ EXPECT_EQ(12345u, data_frame.stream_id());
+}
+
+TEST(SpdyProtocolSpdy3Test, ControlFrameStructs) {
+ SpdyFramer framer(SPDY_VERSION_FOR_TESTS);
+ SpdyHeaderBlock headers;
+
+ scoped_ptr<SpdySynStreamControlFrame> syn_frame(
+ framer.CreateSynStream(123, 456, 2, CONTROL_FLAG_FIN, false, &headers));
+ EXPECT_EQ(framer.protocol_version(), syn_frame->version());
+ EXPECT_TRUE(syn_frame->is_control_frame());
+ EXPECT_EQ(SYN_STREAM, syn_frame->type());
+ EXPECT_EQ(123u, syn_frame->stream_id());
+ EXPECT_EQ(456u, syn_frame->associated_stream_id());
+ EXPECT_EQ(2u, syn_frame->priority());
+ EXPECT_EQ((SPDY_VERSION_FOR_TESTS < 3) ? 2 : 4,
+ syn_frame->header_block_len());
+ EXPECT_EQ(1u, syn_frame->flags());
+ syn_frame->set_associated_stream_id(999u);
+ EXPECT_EQ(123u, syn_frame->stream_id());
+ EXPECT_EQ(999u, syn_frame->associated_stream_id());
+
+ scoped_ptr<SpdySynReplyControlFrame> syn_reply(
+ framer.CreateSynReply(123, CONTROL_FLAG_NONE, false, &headers));
+ EXPECT_EQ(framer.protocol_version(), syn_reply->version());
+ EXPECT_TRUE(syn_reply->is_control_frame());
+ EXPECT_EQ(SYN_REPLY, syn_reply->type());
+ EXPECT_EQ(123u, syn_reply->stream_id());
+ EXPECT_EQ((SPDY_VERSION_FOR_TESTS < 3) ? 2 : 4,
+ syn_reply->header_block_len());
+ EXPECT_EQ(0, syn_reply->flags());
+
+ scoped_ptr<SpdyRstStreamControlFrame> rst_frame(
+ framer.CreateRstStream(123, spdy::PROTOCOL_ERROR));
+ EXPECT_EQ(framer.protocol_version(), rst_frame->version());
+ EXPECT_TRUE(rst_frame->is_control_frame());
+ EXPECT_EQ(RST_STREAM, rst_frame->type());
+ EXPECT_EQ(123u, rst_frame->stream_id());
+ EXPECT_EQ(spdy::PROTOCOL_ERROR, rst_frame->status());
+ rst_frame->set_status(spdy::INVALID_STREAM);
+ EXPECT_EQ(spdy::INVALID_STREAM, rst_frame->status());
+ EXPECT_EQ(0, rst_frame->flags());
+
+ const uint32 kUniqueId = 1234567u;
+ const uint32 kUniqueId2 = 31415926u;
+ scoped_ptr<SpdyPingControlFrame> ping_frame(
+ framer.CreatePingFrame(kUniqueId));
+ EXPECT_EQ(framer.protocol_version(), ping_frame->version());
+ EXPECT_TRUE(ping_frame->is_control_frame());
+ EXPECT_EQ(PING, ping_frame->type());
+ EXPECT_EQ(kUniqueId, ping_frame->unique_id());
+ ping_frame->set_unique_id(kUniqueId2);
+ EXPECT_EQ(kUniqueId2, ping_frame->unique_id());
+
+ scoped_ptr<SpdyGoAwayControlFrame> goaway_frame(
+ framer.CreateGoAway(123));
+ EXPECT_EQ(framer.protocol_version(), goaway_frame->version());
+ EXPECT_TRUE(goaway_frame->is_control_frame());
+ EXPECT_EQ(GOAWAY, goaway_frame->type());
+ EXPECT_EQ(123u, goaway_frame->last_accepted_stream_id());
+
+ scoped_ptr<SpdyHeadersControlFrame> headers_frame(
+ framer.CreateHeaders(123, CONTROL_FLAG_NONE, false, &headers));
+ EXPECT_EQ(framer.protocol_version(), headers_frame->version());
+ EXPECT_TRUE(headers_frame->is_control_frame());
+ EXPECT_EQ(HEADERS, headers_frame->type());
+ EXPECT_EQ(123u, headers_frame->stream_id());
+ EXPECT_EQ((SPDY_VERSION_FOR_TESTS < 3) ? 2 : 4,
+ headers_frame->header_block_len());
+ EXPECT_EQ(0, headers_frame->flags());
+
+ scoped_ptr<SpdyWindowUpdateControlFrame> window_update_frame(
+ framer.CreateWindowUpdate(123, 456));
+ EXPECT_EQ(framer.protocol_version(), window_update_frame->version());
+ EXPECT_TRUE(window_update_frame->is_control_frame());
+ EXPECT_EQ(WINDOW_UPDATE, window_update_frame->type());
+ EXPECT_EQ(123u, window_update_frame->stream_id());
+ EXPECT_EQ(456u, window_update_frame->delta_window_size());
+}
+
+TEST(SpdyProtocolSpdy3Test, TestDataFrame) {
+ SpdyDataFrame frame;
+
+ // Set the stream ID to various values.
+ frame.set_stream_id(0);
+ EXPECT_EQ(0u, frame.stream_id());
+ EXPECT_FALSE(frame.is_control_frame());
+ frame.set_stream_id(~0 & kStreamIdMask);
+ EXPECT_EQ(~0 & kStreamIdMask, frame.stream_id());
+ EXPECT_FALSE(frame.is_control_frame());
+
+ // Set length to various values. Make sure that when you set_length(x),
+ // length() == x. Also make sure the flags are unaltered.
+ memset(frame.data(), '1', SpdyDataFrame::size());
+ int8 flags = frame.flags();
+ frame.set_length(0);
+ EXPECT_EQ(0u, frame.length());
+ EXPECT_EQ(flags, frame.flags());
+ frame.set_length(kLengthMask);
+ EXPECT_EQ(kLengthMask, frame.length());
+ EXPECT_EQ(flags, frame.flags());
+ frame.set_length(5u);
+ EXPECT_EQ(5u, frame.length());
+ EXPECT_EQ(flags, frame.flags());
+
+ // Set flags to various values. Make sure that when you set_flags(x),
+ // flags() == x. Also make sure the length is unaltered.
+ memset(frame.data(), '1', SpdyDataFrame::size());
+ uint32 length = frame.length();
+ frame.set_flags(0u);
+ EXPECT_EQ(0u, frame.flags());
+ EXPECT_EQ(length, frame.length());
+ int8 all_flags = ~0;
+ frame.set_flags(all_flags);
+ flags = frame.flags();
+ EXPECT_EQ(all_flags, flags);
+ EXPECT_EQ(length, frame.length());
+ frame.set_flags(5u);
+ EXPECT_EQ(5u, frame.flags());
+ EXPECT_EQ(length, frame.length());
+}
+
+// Test various types of SETTINGS frames.
+TEST(SpdyProtocolSpdy3Test, TestSpdySettingsFrame) {
+ SpdyFramer framer(SPDY_VERSION_FOR_TESTS);
+
+ // Create a settings frame with no settings.
+ SpdySettings settings;
+ scoped_ptr<SpdySettingsControlFrame> settings_frame(
+ framer.CreateSettings(settings));
+ EXPECT_EQ(framer.protocol_version(), settings_frame->version());
+ EXPECT_TRUE(settings_frame->is_control_frame());
+ EXPECT_EQ(SETTINGS, settings_frame->type());
+ EXPECT_EQ(0u, settings_frame->num_entries());
+
+ // We'll add several different ID/Flag combinations and then verify
+ // that they encode and decode properly.
+ SettingsFlagsAndId ids[] = {
+ SettingsFlagsAndId::FromWireFormat(SPDY_VERSION_FOR_TESTS, 0x00000000),
+ SettingsFlagsAndId::FromWireFormat(SPDY_VERSION_FOR_TESTS, 0xffffffff),
+ SettingsFlagsAndId::FromWireFormat(SPDY_VERSION_FOR_TESTS, 0xff000001),
+ SettingsFlagsAndId::FromWireFormat(SPDY_VERSION_FOR_TESTS, 0xffffffff),
+ SettingsFlagsAndId::FromWireFormat(SPDY_VERSION_FOR_TESTS, 0x01000002),
+ SettingsFlagsAndId(3, 1)
+ };
+
+ for (size_t index = 0; index < arraysize(ids); ++index) {
+ settings.insert(settings.end(), std::make_pair(ids[index], index));
+ settings_frame.reset(framer.CreateSettings(settings));
+ EXPECT_EQ(framer.protocol_version(), settings_frame->version());
+ EXPECT_TRUE(settings_frame->is_control_frame());
+ EXPECT_EQ(SETTINGS, settings_frame->type());
+ EXPECT_EQ(index + 1, settings_frame->num_entries());
+
+ SpdySettings parsed_settings;
+ EXPECT_TRUE(framer.ParseSettings(settings_frame.get(), &parsed_settings));
+ EXPECT_EQ(settings.size(), parsed_settings.size());
+ SpdySettings::const_iterator it = parsed_settings.begin();
+ int pos = 0;
+ while (it != parsed_settings.end()) {
+ SettingsFlagsAndId parsed = it->first;
+ uint32 value = it->second;
+ EXPECT_EQ(ids[pos].flags(), parsed.flags());
+ EXPECT_EQ(ids[pos].id(), parsed.id());
+ EXPECT_EQ(static_cast<uint32>(pos), value);
+ ++it;
+ ++pos;
+ }
+ }
+}
+
+TEST(SpdyProtocolSpdy3Test, HasHeaderBlock) {
+ SpdyControlFrame frame(SpdyControlFrame::kHeaderSize);
+ for (SpdyControlType type = SYN_STREAM;
+ type < NUM_CONTROL_FRAME_TYPES;
+ type = static_cast<SpdyControlType>(type + 1)) {
+ frame.set_type(type);
+ if (type == SYN_STREAM || type == SYN_REPLY || type == HEADERS) {
+ EXPECT_TRUE(frame.has_header_block());
+ } else {
+ EXPECT_FALSE(frame.has_header_block());
+ }
+ }
+}
+
+// Make sure that overflows both die in debug mode, and do not cause problems
+// in opt mode. Note: The EXPECT_DEBUG_DEATH call does not work on Win32 yet,
+// so we comment it out.
+TEST(SpdyProtocolDeathSpdy3Test, TestDataFrame) {
+ SpdyDataFrame frame;
+
+ frame.set_stream_id(0);
+ // TODO(mbelshe): implement EXPECT_DEBUG_DEATH on windows.
+#if !defined(WIN32) && defined(GTEST_HAS_DEATH_TEST)
+#if !defined(DCHECK_ALWAYS_ON)
+ EXPECT_DEBUG_DEATH(frame.set_stream_id(~0), "");
+#else
+ EXPECT_DEATH(frame.set_stream_id(~0), "");
+#endif
+#endif
+ EXPECT_FALSE(frame.is_control_frame());
+
+ frame.set_flags(0);
+#if !defined(WIN32) && defined(GTEST_HAS_DEATH_TEST)
+#if !defined(DCHECK_ALWAYS_ON)
+ EXPECT_DEBUG_DEATH(frame.set_length(~0), "");
+#else
+ EXPECT_DEATH(frame.set_length(~0), "");
+#endif
+#endif
+ EXPECT_EQ(0, frame.flags());
+}
+
+TEST(SpdyProtocolDeathSpdy3Test, TestSpdyControlFrameStreamId) {
+ SpdyControlFrame frame_store(SpdySynStreamControlFrame::size());
+ memset(frame_store.data(), '1', SpdyControlFrame::kHeaderSize);
+ SpdySynStreamControlFrame* frame =
+ reinterpret_cast<SpdySynStreamControlFrame*>(&frame_store);
+
+ // Set the stream ID to various values.
+ frame->set_stream_id(0);
+ EXPECT_EQ(0u, frame->stream_id());
+ EXPECT_FALSE(frame->is_control_frame());
+ frame->set_stream_id(kStreamIdMask);
+ EXPECT_EQ(kStreamIdMask, frame->stream_id());
+ EXPECT_FALSE(frame->is_control_frame());
+}
+
+TEST(SpdyProtocolDeathSpdy3Test, TestSpdyControlFrameVersion) {
+ const unsigned int kVersionMask = 0x7fff;
+ SpdyControlFrame frame(SpdySynStreamControlFrame::size());
+ memset(frame.data(), '1', SpdyControlFrame::kHeaderSize);
+
+ // Set the version to various values, and make sure it does not affect the
+ // type.
+ frame.set_type(SYN_STREAM);
+ frame.set_version(0);
+ EXPECT_EQ(0, frame.version());
+ EXPECT_TRUE(frame.is_control_frame());
+ EXPECT_EQ(SYN_STREAM, frame.type());
+
+ SpdySynStreamControlFrame* syn_stream =
+ reinterpret_cast<SpdySynStreamControlFrame*>(&frame);
+ syn_stream->set_stream_id(~0 & kVersionMask);
+ EXPECT_EQ(~0 & kVersionMask, syn_stream->stream_id());
+ EXPECT_TRUE(frame.is_control_frame());
+ EXPECT_EQ(SYN_STREAM, frame.type());
+}
+
+TEST(SpdyProtocolDeathSpdy3Test, TestSpdyControlFrameType) {
+ SpdyControlFrame frame(SpdyControlFrame::kHeaderSize);
+ memset(frame.data(), 255, SpdyControlFrame::kHeaderSize);
+
+ // type() should be out of bounds.
+ EXPECT_FALSE(frame.AppearsToBeAValidControlFrame());
+
+ frame.set_version(SPDY_VERSION_FOR_TESTS);
+ uint16 version = frame.version();
+
+ for (int i = SYN_STREAM; i <= spdy::WINDOW_UPDATE; ++i) {
+ frame.set_type(static_cast<SpdyControlType>(i));
+ EXPECT_EQ(i, static_cast<int>(frame.type()));
+ if (i == spdy::NOOP) {
+ // NOOP frames aren't 'valid'.
+ EXPECT_FALSE(frame.AppearsToBeAValidControlFrame());
+ } else {
+ EXPECT_TRUE(frame.AppearsToBeAValidControlFrame());
+ }
+ // Make sure setting type does not alter the version block.
+ EXPECT_EQ(version, frame.version());
+ EXPECT_TRUE(frame.is_control_frame());
+ }
+}
+
+TEST(SpdyProtocolDeathSpdy3Test, TestRstStreamStatusBounds) {
+ SpdyFramer framer(SPDY_VERSION_FOR_TESTS);
+ scoped_ptr<SpdyRstStreamControlFrame> rst_frame;
+
+ rst_frame.reset(framer.CreateRstStream(123, spdy::PROTOCOL_ERROR));
+ EXPECT_EQ(spdy::PROTOCOL_ERROR, rst_frame->status());
+
+ rst_frame->set_status(spdy::INVALID);
+ EXPECT_EQ(spdy::INVALID, rst_frame->status());
+
+ rst_frame->set_status(
+ static_cast<spdy::SpdyStatusCodes>(spdy::INVALID - 1));
+ EXPECT_EQ(spdy::INVALID, rst_frame->status());
+
+ rst_frame->set_status(spdy::NUM_STATUS_CODES);
+ EXPECT_EQ(spdy::INVALID, rst_frame->status());
+}
+
+} // namespace
diff --git a/net/spdy/spdy_proxy_client_socket_spdy2_unittest.cc b/net/spdy/spdy_proxy_client_socket_spdy2_unittest.cc
index eaf4fbe..5963e96 100644
--- a/net/spdy/spdy_proxy_client_socket_spdy2_unittest.cc
+++ b/net/spdy/spdy_proxy_client_socket_spdy2_unittest.cc
@@ -65,6 +65,10 @@ class SpdyProxyClientSocketSpdy2Test : public PlatformTest {
virtual void TearDown();
protected:
+ virtual void SetUp() {
+ SpdySession::set_default_protocol(SSLClientSocket::kProtoSPDY2);
+ }
+
void Initialize(MockRead* reads, size_t reads_count, MockWrite* writes,
size_t writes_count);
spdy::SpdyFrame* ConstructConnectRequestFrame();
diff --git a/net/spdy/spdy_proxy_client_socket_spdy3_unittest.cc b/net/spdy/spdy_proxy_client_socket_spdy3_unittest.cc
index da3456b..0073243 100644
--- a/net/spdy/spdy_proxy_client_socket_spdy3_unittest.cc
+++ b/net/spdy/spdy_proxy_client_socket_spdy3_unittest.cc
@@ -65,6 +65,10 @@ class SpdyProxyClientSocketSpdy3Test : public PlatformTest {
virtual void TearDown();
protected:
+ virtual void SetUp() {
+ SpdySession::set_default_protocol(SSLClientSocket::kProtoSPDY3);
+ }
+
void Initialize(MockRead* reads, size_t reads_count, MockWrite* writes,
size_t writes_count);
spdy::SpdyFrame* ConstructConnectRequestFrame();
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index 78b5993..7281a47 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -4,6 +4,8 @@
#include "net/spdy/spdy_session.h"
+#include <map>
+
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/memory/linked_ptr.h"
@@ -27,7 +29,6 @@
#include "net/base/origin_bound_cert_service.h"
#include "net/http/http_network_session.h"
#include "net/http/http_server_properties.h"
-#include "net/socket/ssl_client_socket.h"
#include "net/spdy/spdy_frame_builder.h"
#include "net/spdy/spdy_http_utils.h"
#include "net/spdy/spdy_protocol.h"
@@ -270,6 +271,9 @@ bool SpdySession::use_ssl_ = true;
SpdySession::FlowControl SpdySession::use_flow_control_ =
SpdySession::kFlowControlBasedOnNPN;
+SSLClientSocket::NextProto SpdySession::default_protocol_ =
+ SSLClientSocket::kProtoUnknown;
+
// static
size_t SpdySession::init_max_concurrent_streams_ = 10;
@@ -288,6 +292,13 @@ int SpdySession::trailing_ping_delay_time_ms_ = 1000; // 1 second
// static
int SpdySession::hung_interval_ms_ = 10000; // 10 seconds
+// static
+void SpdySession::ResetStaticSettingsToInit() {
+ // WARNING: These must match the initializers above.
+ use_flow_control_ = SpdySession::kFlowControlBasedOnNPN;
+ default_protocol_ = SSLClientSocket::kProtoUnknown;
+}
+
SpdySession::SpdySession(const HostPortProxyPair& host_port_proxy_pair,
SpdySessionPool* spdy_session_pool,
HttpServerProperties* http_server_properties,
@@ -381,13 +392,16 @@ net::Error SpdySession::InitializeWithSocket(
is_secure_ = is_secure;
certificate_error_code_ = certificate_error_code;
+ SSLClientSocket::NextProto protocol = default_protocol_;
if (is_secure_) {
SSLClientSocket* ssl_socket = GetSSLClientSocket();
SSLClientSocket::NextProto protocol_negotiated =
ssl_socket->protocol_negotiated();
- if (protocol_negotiated != SSLClientSocket::kProtoUnknown)
+ if (protocol_negotiated != SSLClientSocket::kProtoUnknown) {
+ protocol = protocol_negotiated;
flow_control_ = (protocol_negotiated >= SSLClientSocket::kProtoSPDY21);
+ }
if (ssl_socket->WasOriginBoundCertSent()) {
// According to the SPDY spec, the credential associated with the TLS
@@ -396,7 +410,21 @@ net::Error SpdySession::InitializeWithSocket(
}
}
- buffered_spdy_framer_.reset(new spdy::BufferedSpdyFramer(2));
+ int version = 2;
+ switch (protocol) {
+ case SSLClientSocket::kProtoSPDY2:
+ case SSLClientSocket::kProtoSPDY21:
+ version = 2;
+ break;
+ case SSLClientSocket::kProtoSPDY3:
+ version = 3;
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ buffered_spdy_framer_.reset(new spdy::BufferedSpdyFramer(version));
buffered_spdy_framer_->set_visitor(this);
SendSettings();
@@ -1274,6 +1302,24 @@ void SpdySession::OnStreamFrameData(spdy::SpdyStreamId stream_id,
stream->OnDataReceived(data, len);
}
+void SpdySession::OnSetting(spdy::SpdySettingsIds id,
+ uint8 flags,
+ uint32 value) {
+ HandleSetting(id, value);
+ spdy::SettingsFlagsAndId flags_and_id(flags, id);
+ http_server_properties_->SetSpdySetting(
+ host_port_pair(), std::make_pair(flags_and_id, value));
+
+ received_settings_ = true;
+
+ // Log the settings.
+ spdy::SpdySettings settings;
+ settings.insert(settings.end(), std::make_pair(flags_and_id, value));
+ net_log_.AddEvent(
+ NetLog::TYPE_SPDY_SESSION_RECV_SETTINGS,
+ make_scoped_refptr(new NetLogSpdySettingsParameter(settings)));
+}
+
bool SpdySession::Respond(const spdy::SpdyHeaderBlock& headers,
const scoped_refptr<SpdyStream> stream) {
int rv = OK;
@@ -1535,20 +1581,6 @@ void SpdySession::OnPing(const spdy::SpdyPingControlFrame& frame) {
PlanToSendTrailingPing();
}
-void SpdySession::OnSettings(const spdy::SpdySettingsControlFrame& frame) {
- spdy::SpdySettings settings;
- if (spdy::SpdyFramer::ParseSettings(&frame, &settings)) {
- HandleSettings(settings);
- http_server_properties_->SetSpdySettings(host_port_pair(), settings);
- }
-
- received_settings_ = true;
-
- net_log_.AddEvent(
- NetLog::TYPE_SPDY_SESSION_RECV_SETTINGS,
- make_scoped_refptr(new NetLogSpdySettingsParameter(settings)));
-}
-
void SpdySession::OnWindowUpdate(
const spdy::SpdyWindowUpdateControlFrame& frame) {
spdy::SpdyStreamId stream_id = frame.stream_id();
@@ -1627,6 +1659,9 @@ void SpdySession::SendSettings() {
if (settings.empty())
return;
+ typedef std::map<uint32, spdy::SpdySetting> SpdySettingsMap;
+ SpdySettingsMap unique_settings;
+
// Record Histogram Data and Apply the SpdyCwnd FieldTrial if applicable.
for (spdy::SpdySettings::iterator i = settings.begin(),
end = settings.end(); i != end; ++i) {
@@ -1640,12 +1675,17 @@ void SpdySession::SendSettings() {
cwnd,
1, 200, 100);
if (cwnd != val) {
+ spdy::SettingsFlagsAndId new_id(spdy::SETTINGS_FLAG_PLEASE_PERSIST,
+ id);
i->second = cwnd;
- i->first.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
- http_server_properties_->SetSpdySettings(host_port_pair(), settings);
+ i->first = new_id;
+ spdy::SpdySetting setting(new_id, val);
+ http_server_properties_->SetSpdySetting(host_port_pair(), setting);
+ unique_settings[id] = setting;
+ continue;
}
- break;
}
+ unique_settings[id] = *i;
}
HandleSettings(settings);
@@ -1654,10 +1694,17 @@ void SpdySession::SendSettings() {
NetLog::TYPE_SPDY_SESSION_SEND_SETTINGS,
make_scoped_refptr(new NetLogSpdySettingsParameter(settings)));
+ spdy::SpdySettings sorted_settings;
+ for (SpdySettingsMap::iterator it = unique_settings.begin();
+ unique_settings.end() != it;
+ ++it) {
+ sorted_settings.push_back(it->second);
+ }
+
// Create the SETTINGS frame and send it.
DCHECK(buffered_spdy_framer_.get());
scoped_ptr<spdy::SpdySettingsControlFrame> settings_frame(
- buffered_spdy_framer_->CreateSettings(settings));
+ buffered_spdy_framer_->CreateSettings(sorted_settings));
sent_settings_ = true;
QueueFrame(settings_frame.get(), 0, NULL);
}
@@ -1665,25 +1712,27 @@ void SpdySession::SendSettings() {
void SpdySession::HandleSettings(const spdy::SpdySettings& settings) {
for (spdy::SpdySettings::const_iterator i = settings.begin(),
end = settings.end(); i != end; ++i) {
- const uint32 id = i->first.id();
- const uint32 val = i->second;
- switch (id) {
- case spdy::SETTINGS_MAX_CONCURRENT_STREAMS:
- max_concurrent_streams_ = std::min(static_cast<size_t>(val),
- max_concurrent_stream_limit_);
- ProcessPendingCreateStreams();
- break;
- case spdy::SETTINGS_INITIAL_WINDOW_SIZE:
- // INITIAL_WINDOW_SIZE updates initial_send_window_size_ only.
- // TODO(rtenneti): discuss with the server team about
- // initial_recv_window_size_.
- int32 prev_initial_send_window_size = initial_send_window_size_;
- initial_send_window_size_ = val;
- int32 delta_window_size =
- initial_send_window_size_ - prev_initial_send_window_size;
- UpdateStreamsSendWindowSize(delta_window_size);
- break;
- }
+ HandleSetting(i->first.id(), i->second);
+ }
+}
+
+void SpdySession::HandleSetting(uint32 id, uint32 value) {
+ switch (id) {
+ case spdy::SETTINGS_MAX_CONCURRENT_STREAMS:
+ max_concurrent_streams_ = std::min(static_cast<size_t>(value),
+ max_concurrent_stream_limit_);
+ ProcessPendingCreateStreams();
+ break;
+ case spdy::SETTINGS_INITIAL_WINDOW_SIZE:
+ // INITIAL_WINDOW_SIZE updates initial_send_window_size_ only.
+ // TODO(rtenneti): discuss with the server team about
+ // initial_recv_window_size_.
+ int32 prev_initial_send_window_size = initial_send_window_size_;
+ initial_send_window_size_ = value;
+ int32 delta_window_size =
+ initial_send_window_size_ - prev_initial_send_window_size;
+ UpdateStreamsSendWindowSize(delta_window_size);
+ break;
}
}
@@ -1736,8 +1785,9 @@ void SpdySession::SendTrailingPing() {
void SpdySession::WritePingFrame(uint32 unique_id) {
DCHECK(buffered_spdy_framer_.get());
scoped_ptr<spdy::SpdyPingControlFrame> ping_frame(
- spdy::SpdyFramer::CreatePingFrame(next_ping_id_));
- QueueFrame(ping_frame.get(), SPDY_PRIORITY_HIGHEST, NULL);
+ buffered_spdy_framer_->CreatePingFrame(next_ping_id_));
+ QueueFrame(
+ ping_frame.get(), buffered_spdy_framer_->GetHighestPriority(), NULL);
if (net_log().IsLoggingAllEvents()) {
net_log().AddEvent(
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h
index 6f33dfc..6114056 100644
--- a/net/spdy/spdy_session.h
+++ b/net/spdy/spdy_session.h
@@ -174,6 +174,9 @@ class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>,
// CLIENT_CERT_INVALID_TYPE if none was sent.
SSLClientCertType GetOriginBoundCertType() const;
+ // Reset all static settings to initialized values. Used to init test suite.
+ static void ResetStaticSettingsToInit();
+
// Enable or disable SSL.
static void SetSSLMode(bool enable) { use_ssl_ = enable; }
static bool SSLMode() { return use_ssl_; }
@@ -184,6 +187,13 @@ class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>,
use_flow_control_ = flow_control;
}
+ // Specify the SPDY protocol to be used for SPDY session which do not use NPN
+ // to negotiate a particular protocol.
+ static void set_default_protocol(
+ SSLClientSocket::NextProto default_protocol) {
+ default_protocol_ = default_protocol;
+ }
+
// Sets the max concurrent streams per session, as a ceiling on any server
// specific SETTINGS value.
static void set_max_concurrent_streams(size_t value) {
@@ -354,6 +364,7 @@ class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>,
// Handle SETTINGS. Either when we send settings, or when we receive a
// SETTINGS control frame, update our SpdySession accordingly.
void HandleSettings(const spdy::SpdySettings& settings);
+ void HandleSetting(uint32 id, uint32 value);
// Adjust the send window size of all ActiveStreams and PendingCreateStreams.
void UpdateStreamsSendWindowSize(int32 delta_window_size);
@@ -437,12 +448,13 @@ class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>,
const spdy::SpdyRstStreamControlFrame& frame) OVERRIDE;
virtual void OnGoAway(const spdy::SpdyGoAwayControlFrame& frame) OVERRIDE;
virtual void OnPing(const spdy::SpdyPingControlFrame& frame) OVERRIDE;
- virtual void OnSettings(const spdy::SpdySettingsControlFrame& frame) OVERRIDE;
virtual void OnWindowUpdate(
const spdy::SpdyWindowUpdateControlFrame& frame) OVERRIDE;
virtual void OnStreamFrameData(spdy::SpdyStreamId stream_id,
const char* data,
size_t len) OVERRIDE;
+ virtual void OnSetting(
+ spdy::SpdySettingsIds id, uint8 flags, uint32 value) OVERRIDE;
virtual void OnSynStream(
const spdy::SpdySynStreamControlFrame& frame,
const linked_ptr<spdy::SpdyHeaderBlock>& headers) OVERRIDE;
@@ -623,6 +635,7 @@ class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>,
static bool use_ssl_;
static FlowControl use_flow_control_;
+ static SSLClientSocket::NextProto default_protocol_;
static size_t init_max_concurrent_streams_;
static size_t max_concurrent_stream_limit_;
diff --git a/net/spdy/spdy_session_spdy2_unittest.cc b/net/spdy/spdy_session_spdy2_unittest.cc
index f5f33fc..a5f8af7 100644
--- a/net/spdy/spdy_session_spdy2_unittest.cc
+++ b/net/spdy/spdy_session_spdy2_unittest.cc
@@ -24,6 +24,10 @@ class SpdySessionSpdy2Test : public PlatformTest {
spdy::SpdyFramer::set_enable_compression_default(false);
}
protected:
+ virtual void SetUp() {
+ SpdySession::set_default_protocol(SSLClientSocket::kProtoSPDY2);
+ }
+
virtual void TearDown() {
// Wanted to be 100% sure PING is disabled.
SpdySession::set_enable_ping_based_connection_checking(false);
@@ -497,8 +501,7 @@ TEST_F(SpdySessionSpdy2Test, OnSettings) {
session_deps.host_resolver->set_synchronous_mode(true);
spdy::SpdySettings new_settings;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
+ spdy::SettingsFlagsAndId id(0, spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
const size_t max_concurrent_streams = 2;
new_settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
@@ -530,8 +533,8 @@ TEST_F(SpdySessionSpdy2Test, OnSettings) {
// Initialize the SpdySettingsStorage with 1 max concurrent streams.
SpdySessionPool* spdy_session_pool(http_session->spdy_session_pool());
spdy::SpdySettings old_settings;
- id.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
- old_settings.push_back(spdy::SpdySetting(id, 1));
+ spdy::SettingsFlagsAndId id1(spdy::SETTINGS_FLAG_PLEASE_PERSIST, id.id());
+ old_settings.push_back(spdy::SpdySetting(id1, 1));
spdy_session_pool->http_server_properties()->SetSpdySettings(
test_host_port_pair, old_settings);
@@ -612,9 +615,8 @@ TEST_F(SpdySessionSpdy2Test, CancelPendingCreateStream) {
// Initialize the SpdySettingsStorage with 1 max concurrent streams.
SpdySessionPool* spdy_session_pool(http_session->spdy_session_pool());
spdy::SpdySettings settings;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
- id.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
+ spdy::SettingsFlagsAndId id(spdy::SETTINGS_FLAG_PLEASE_PERSIST,
+ spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
settings.push_back(spdy::SpdySetting(id, 1));
spdy_session_pool->http_server_properties()->SetSpdySettings(
test_host_port_pair, settings);
@@ -685,9 +687,7 @@ TEST_F(SpdySessionSpdy2Test, SendSettingsOnNewSession) {
spdy::SpdySettings settings;
const uint32 kBogusSettingId = 0xABAB;
const uint32 kBogusSettingValue = 0xCDCD;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(kBogusSettingId);
- id.set_flags(spdy::SETTINGS_FLAG_PERSISTED);
+ spdy::SettingsFlagsAndId id(spdy::SETTINGS_FLAG_PERSISTED, kBogusSettingId);
settings.push_back(spdy::SpdySetting(id, kBogusSettingValue));
MockConnect connect_data(SYNCHRONOUS, OK);
scoped_ptr<spdy::SpdyFrame> settings_frame(
@@ -712,9 +712,9 @@ TEST_F(SpdySessionSpdy2Test, SendSettingsOnNewSession) {
HostPortPair test_host_port_pair(kTestHost, kTestPort);
HostPortProxyPair pair(test_host_port_pair, ProxyServer::Direct());
- id.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
+ spdy::SettingsFlagsAndId id1(spdy::SETTINGS_FLAG_PLEASE_PERSIST, id.id());
settings.clear();
- settings.push_back(spdy::SpdySetting(id, kBogusSettingValue));
+ settings.push_back(spdy::SpdySetting(id1, kBogusSettingValue));
SpdySessionPool* spdy_session_pool(http_session->spdy_session_pool());
spdy_session_pool->http_server_properties()->SetSpdySettings(
test_host_port_pair, settings);
@@ -873,9 +873,8 @@ TEST_F(SpdySessionSpdy2Test, ClearSettingsStorage) {
const int kTestPort = 80;
HostPortPair test_host_port_pair(kTestHost, kTestPort);
spdy::SpdySettings test_settings;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
- id.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
+ spdy::SettingsFlagsAndId id(spdy::SETTINGS_FLAG_PLEASE_PERSIST,
+ spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
const size_t max_concurrent_streams = 2;
test_settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
@@ -897,9 +896,8 @@ TEST_F(SpdySessionSpdy2Test, ClearSettingsStorageOnIPAddressChanged) {
HttpServerProperties* test_http_server_properties =
spdy_session_pool->http_server_properties();
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
- id.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
+ spdy::SettingsFlagsAndId id(spdy::SETTINGS_FLAG_PLEASE_PERSIST,
+ spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
const size_t max_concurrent_streams = 2;
spdy::SpdySettings test_settings;
test_settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
diff --git a/net/spdy/spdy_session_spdy3_unittest.cc b/net/spdy/spdy_session_spdy3_unittest.cc
index 7a8b086..d850c6a 100644
--- a/net/spdy/spdy_session_spdy3_unittest.cc
+++ b/net/spdy/spdy_session_spdy3_unittest.cc
@@ -24,6 +24,10 @@ class SpdySessionSpdy3Test : public PlatformTest {
spdy::SpdyFramer::set_enable_compression_default(false);
}
protected:
+ virtual void SetUp() {
+ SpdySession::set_default_protocol(SSLClientSocket::kProtoSPDY3);
+ }
+
virtual void TearDown() {
// Wanted to be 100% sure PING is disabled.
SpdySession::set_enable_ping_based_connection_checking(false);
@@ -497,8 +501,7 @@ TEST_F(SpdySessionSpdy3Test, OnSettings) {
session_deps.host_resolver->set_synchronous_mode(true);
spdy::SpdySettings new_settings;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
+ spdy::SettingsFlagsAndId id(0, spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
const size_t max_concurrent_streams = 2;
new_settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
@@ -530,8 +533,8 @@ TEST_F(SpdySessionSpdy3Test, OnSettings) {
// Initialize the SpdySettingsStorage with 1 max concurrent streams.
SpdySessionPool* spdy_session_pool(http_session->spdy_session_pool());
spdy::SpdySettings old_settings;
- id.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
- old_settings.push_back(spdy::SpdySetting(id, 1));
+ spdy::SettingsFlagsAndId id1(spdy::SETTINGS_FLAG_PLEASE_PERSIST, id.id());
+ old_settings.push_back(spdy::SpdySetting(id1, 1));
spdy_session_pool->http_server_properties()->SetSpdySettings(
test_host_port_pair, old_settings);
@@ -612,9 +615,8 @@ TEST_F(SpdySessionSpdy3Test, CancelPendingCreateStream) {
// Initialize the SpdySettingsStorage with 1 max concurrent streams.
SpdySessionPool* spdy_session_pool(http_session->spdy_session_pool());
spdy::SpdySettings settings;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
- id.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
+ spdy::SettingsFlagsAndId id(spdy::SETTINGS_FLAG_PLEASE_PERSIST,
+ spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
settings.push_back(spdy::SpdySetting(id, 1));
spdy_session_pool->http_server_properties()->SetSpdySettings(
test_host_port_pair, settings);
@@ -685,9 +687,7 @@ TEST_F(SpdySessionSpdy3Test, SendSettingsOnNewSession) {
spdy::SpdySettings settings;
const uint32 kBogusSettingId = 0xABAB;
const uint32 kBogusSettingValue = 0xCDCD;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(kBogusSettingId);
- id.set_flags(spdy::SETTINGS_FLAG_PERSISTED);
+ spdy::SettingsFlagsAndId id(spdy::SETTINGS_FLAG_PERSISTED, kBogusSettingId);
settings.push_back(spdy::SpdySetting(id, kBogusSettingValue));
MockConnect connect_data(SYNCHRONOUS, OK);
scoped_ptr<spdy::SpdyFrame> settings_frame(
@@ -712,9 +712,9 @@ TEST_F(SpdySessionSpdy3Test, SendSettingsOnNewSession) {
HostPortPair test_host_port_pair(kTestHost, kTestPort);
HostPortProxyPair pair(test_host_port_pair, ProxyServer::Direct());
- id.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
+ spdy::SettingsFlagsAndId id1(spdy::SETTINGS_FLAG_PLEASE_PERSIST, id.id());
settings.clear();
- settings.push_back(spdy::SpdySetting(id, kBogusSettingValue));
+ settings.push_back(spdy::SpdySetting(id1, kBogusSettingValue));
SpdySessionPool* spdy_session_pool(http_session->spdy_session_pool());
spdy_session_pool->http_server_properties()->SetSpdySettings(
test_host_port_pair, settings);
@@ -873,9 +873,8 @@ TEST_F(SpdySessionSpdy3Test, ClearSettingsStorage) {
const int kTestPort = 80;
HostPortPair test_host_port_pair(kTestHost, kTestPort);
spdy::SpdySettings test_settings;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
- id.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
+ spdy::SettingsFlagsAndId id(spdy::SETTINGS_FLAG_PLEASE_PERSIST,
+ spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
const size_t max_concurrent_streams = 2;
test_settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
@@ -897,9 +896,8 @@ TEST_F(SpdySessionSpdy3Test, ClearSettingsStorageOnIPAddressChanged) {
HttpServerProperties* test_http_server_properties =
spdy_session_pool->http_server_properties();
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
- id.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
+ spdy::SettingsFlagsAndId id(spdy::SETTINGS_FLAG_PLEASE_PERSIST,
+ spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
const size_t max_concurrent_streams = 2;
spdy::SpdySettings test_settings;
test_settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
diff --git a/net/spdy/spdy_settings_storage.cc b/net/spdy/spdy_settings_storage.cc
index f714249..19df996 100644
--- a/net/spdy/spdy_settings_storage.cc
+++ b/net/spdy/spdy_settings_storage.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -34,8 +34,8 @@ void SpdySettingsStorage::Set(const HostPortPair& host_port_pair,
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));
+ spdy::SettingsFlagsAndId new_id(spdy::SETTINGS_FLAG_PERSISTED, id.id());
+ persistent_settings.push_back(std::make_pair(new_id, it->second));
}
}
diff --git a/net/spdy/spdy_stream_spdy2_unittest.cc b/net/spdy/spdy_stream_spdy2_unittest.cc
index e57105c..b04b42b 100644
--- a/net/spdy/spdy_stream_spdy2_unittest.cc
+++ b/net/spdy/spdy_stream_spdy2_unittest.cc
@@ -113,6 +113,10 @@ class SpdyStreamSpdy2Test : public testing::Test {
return session;
}
+ virtual void SetUp() {
+ SpdySession::set_default_protocol(SSLClientSocket::kProtoSPDY2);
+ }
+
virtual void TearDown() {
MessageLoop::current()->RunAllPending();
}
diff --git a/net/spdy/spdy_stream_spdy3_unittest.cc b/net/spdy/spdy_stream_spdy3_unittest.cc
index 38f9787..e51b8a5 100644
--- a/net/spdy/spdy_stream_spdy3_unittest.cc
+++ b/net/spdy/spdy_stream_spdy3_unittest.cc
@@ -113,6 +113,10 @@ class SpdyStreamSpdy3Test : public testing::Test {
return session;
}
+ virtual void SetUp() {
+ SpdySession::set_default_protocol(SSLClientSocket::kProtoSPDY3);
+ }
+
virtual void TearDown() {
MessageLoop::current()->RunAllPending();
}
diff --git a/net/spdy/spdy_websocket_stream_spdy2_unittest.cc b/net/spdy/spdy_websocket_stream_spdy2_unittest.cc
index aae61b8..311e300 100644
--- a/net/spdy/spdy_websocket_stream_spdy2_unittest.cc
+++ b/net/spdy/spdy_websocket_stream_spdy2_unittest.cc
@@ -11,6 +11,7 @@
#include "base/bind_helpers.h"
#include "net/base/completion_callback.h"
#include "net/proxy/proxy_server.h"
+#include "net/socket/ssl_client_socket.h"
#include "net/spdy/spdy_http_utils.h"
#include "net/spdy/spdy_protocol.h"
#include "net/spdy/spdy_session.h"
@@ -189,6 +190,7 @@ class SpdyWebSocketStreamSpdy2Test : public testing::Test {
virtual void SetUp() {
EnableCompression(false);
+ SpdySession::set_default_protocol(SSLClientSocket::kProtoSPDY2);
SpdySession::SetSSLMode(false);
host_port_pair_.set_host("example.com");
@@ -197,17 +199,17 @@ class SpdyWebSocketStreamSpdy2Test : public testing::Test {
host_port_proxy_pair_.second = ProxyServer::Direct();
const size_t max_concurrent_streams = 1;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
-
- id.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
+ spdy::SettingsFlagsAndId id(spdy::SETTINGS_FLAG_PLEASE_PERSIST,
+ spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
spdy_settings_to_set_.push_back(
spdy::SpdySetting(id, max_concurrent_streams));
- id.set_flags(spdy::SETTINGS_FLAG_PERSISTED);
+ spdy::SettingsFlagsAndId id1(spdy::SETTINGS_FLAG_PERSISTED,
+ spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
spdy_settings_to_send_.push_back(
- spdy::SpdySetting(id, max_concurrent_streams));
+ spdy::SpdySetting(id1, max_concurrent_streams));
}
+
virtual void TearDown() {
MessageLoop::current()->RunAllPending();
}
diff --git a/net/spdy/spdy_websocket_stream_spdy3_unittest.cc b/net/spdy/spdy_websocket_stream_spdy3_unittest.cc
index 0e90553..b78b9f5 100644
--- a/net/spdy/spdy_websocket_stream_spdy3_unittest.cc
+++ b/net/spdy/spdy_websocket_stream_spdy3_unittest.cc
@@ -11,6 +11,7 @@
#include "base/bind_helpers.h"
#include "net/base/completion_callback.h"
#include "net/proxy/proxy_server.h"
+#include "net/socket/ssl_client_socket.h"
#include "net/spdy/spdy_http_utils.h"
#include "net/spdy/spdy_protocol.h"
#include "net/spdy/spdy_session.h"
@@ -189,6 +190,7 @@ class SpdyWebSocketStreamSpdy3Test : public testing::Test {
virtual void SetUp() {
EnableCompression(false);
+ SpdySession::set_default_protocol(SSLClientSocket::kProtoSPDY3);
SpdySession::SetSSLMode(false);
host_port_pair_.set_host("example.com");
@@ -197,17 +199,17 @@ class SpdyWebSocketStreamSpdy3Test : public testing::Test {
host_port_proxy_pair_.second = ProxyServer::Direct();
const size_t max_concurrent_streams = 1;
- spdy::SettingsFlagsAndId id(0);
- id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
-
- id.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
+ spdy::SettingsFlagsAndId id(spdy::SETTINGS_FLAG_PLEASE_PERSIST,
+ spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
spdy_settings_to_set_.push_back(
spdy::SpdySetting(id, max_concurrent_streams));
- id.set_flags(spdy::SETTINGS_FLAG_PERSISTED);
+ spdy::SettingsFlagsAndId id1(spdy::SETTINGS_FLAG_PERSISTED,
+ spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
spdy_settings_to_send_.push_back(
- spdy::SpdySetting(id, max_concurrent_streams));
+ spdy::SpdySetting(id1, max_concurrent_streams));
}
+
virtual void TearDown() {
MessageLoop::current()->RunAllPending();
}
diff --git a/net/tools/flip_server/spdy_interface.cc b/net/tools/flip_server/spdy_interface.cc
index 3ccc672..4638b1f 100644
--- a/net/tools/flip_server/spdy_interface.cc
+++ b/net/tools/flip_server/spdy_interface.cc
@@ -319,10 +319,10 @@ void SpdySM::ResetForNewConnection() {
// Send a settings frame
int SpdySM::PostAcceptHook() {
SpdySettings settings;
- SettingsFlagsAndId settings_id(SETTINGS_MAX_CONCURRENT_STREAMS);
+ SettingsFlagsAndId settings_id(0, SETTINGS_MAX_CONCURRENT_STREAMS);
settings.push_back(SpdySetting(settings_id, 100));
SpdySettingsControlFrame* settings_frame =
- SpdyFramer::CreateSettings(settings);
+ buffered_spdy_framer_->CreateSettings(settings);
VLOG(1) << ACCEPTOR_CLIENT_IDENT << "Sending Settings Frame";
EnqueueDataFrame(new SpdyFrameDataFrame(settings_frame));
diff --git a/net/tools/flip_server/spdy_interface.h b/net/tools/flip_server/spdy_interface.h
index d0e30ee..621a4ee 100644
--- a/net/tools/flip_server/spdy_interface.h
+++ b/net/tools/flip_server/spdy_interface.h
@@ -68,13 +68,13 @@ class SpdySM : public spdy::BufferedSpdyFramerVisitorInterface,
const spdy::SpdyRstStreamControlFrame& frame) OVERRIDE;
virtual void OnGoAway(const spdy::SpdyGoAwayControlFrame& frame) OVERRIDE {}
virtual void OnPing(const spdy::SpdyPingControlFrame& frame) OVERRIDE {}
- virtual void OnSettings(
- const spdy::SpdySettingsControlFrame& frame) OVERRIDE {}
virtual void OnWindowUpdate(
const spdy::SpdyWindowUpdateControlFrame& frame) OVERRIDE {}
virtual void OnStreamFrameData(spdy::SpdyStreamId stream_id,
const char* data,
size_t len) OVERRIDE;
+ virtual void OnSetting(
+ spdy::SpdySettingsIds id, uint8 flags, uint32 value) OVERRIDE {}
virtual void OnSynStream(
const spdy::SpdySynStreamControlFrame& frame,
const linked_ptr<spdy::SpdyHeaderBlock>& headers) OVERRIDE;
diff --git a/net/websockets/websocket_job_spdy2_unittest.cc b/net/websockets/websocket_job_spdy2_unittest.cc
index 36e7802..ad00fde 100644
--- a/net/websockets/websocket_job_spdy2_unittest.cc
+++ b/net/websockets/websocket_job_spdy2_unittest.cc
@@ -319,6 +319,7 @@ class WebSocketJobSpdy2Test : public PlatformTest {
public:
virtual void SetUp() {
spdy::SpdyFramer::set_enable_compression_default(false);
+ SpdySession::set_default_protocol(SSLClientSocket::kProtoSPDY2);
stream_type_ = STREAM_INVALID;
cookie_store_ = new MockCookieStore;
context_ = new MockURLRequestContext(cookie_store_.get());
diff --git a/net/websockets/websocket_job_spdy3_unittest.cc b/net/websockets/websocket_job_spdy3_unittest.cc
index 19a089d..235760b 100644
--- a/net/websockets/websocket_job_spdy3_unittest.cc
+++ b/net/websockets/websocket_job_spdy3_unittest.cc
@@ -319,6 +319,7 @@ class WebSocketJobSpdy3Test : public PlatformTest {
public:
virtual void SetUp() {
spdy::SpdyFramer::set_enable_compression_default(false);
+ SpdySession::set_default_protocol(SSLClientSocket::kProtoSPDY3);
stream_type_ = STREAM_INVALID;
cookie_store_ = new MockCookieStore;
context_ = new MockURLRequestContext(cookie_store_.get());