diff options
author | ricea@chromium.org <ricea@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-29 00:42:45 +0000 |
---|---|---|
committer | ricea@chromium.org <ricea@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-29 00:42:45 +0000 |
commit | 0be9392b7cc037c74baa929bc384baf36b0001f3 (patch) | |
tree | 1d6886902c5cfb1e1db8017483ca703a54bffc06 | |
parent | 73b6ae22b6e1a9ebd6a83de0527267f40d6f22b3 (diff) | |
download | chromium_src-0be9392b7cc037c74baa929bc384baf36b0001f3.zip chromium_src-0be9392b7cc037c74baa929bc384baf36b0001f3.tar.gz chromium_src-0be9392b7cc037c74baa929bc384baf36b0001f3.tar.bz2 |
Advertise the permessage-deflate extension and enable it when requested by the server.
Also validation and parsing of the permessage-deflate extension parameters, and unit tests.
BUG=280910
TEST=net_unittests --gtest_filter=WebSocket*
Review URL: https://codereview.chromium.org/143913003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@247545 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | net/websockets/websocket_basic_handshake_stream.cc | 139 | ||||
-rw-r--r-- | net/websockets/websocket_basic_handshake_stream.h | 6 | ||||
-rw-r--r-- | net/websockets/websocket_handshake_stream_create_helper.cc | 7 | ||||
-rw-r--r-- | net/websockets/websocket_handshake_stream_create_helper_test.cc | 42 | ||||
-rw-r--r-- | net/websockets/websocket_stream_test.cc | 229 | ||||
-rw-r--r-- | net/websockets/websocket_test_util.cc | 11 |
6 files changed, 405 insertions, 29 deletions
diff --git a/net/websockets/websocket_basic_handshake_stream.cc b/net/websockets/websocket_basic_handshake_stream.cc index 3d2bcdf..808383b 100644 --- a/net/websockets/websocket_basic_handshake_stream.cc +++ b/net/websockets/websocket_basic_handshake_stream.cc @@ -6,6 +6,7 @@ #include <algorithm> #include <iterator> +#include <set> #include <string> #include <vector> @@ -14,6 +15,7 @@ #include "base/bind.h" #include "base/containers/hash_tables.h" #include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" @@ -26,6 +28,10 @@ #include "net/http/http_stream_parser.h" #include "net/socket/client_socket_handle.h" #include "net/websockets/websocket_basic_stream.h" +#include "net/websockets/websocket_deflate_predictor.h" +#include "net/websockets/websocket_deflate_predictor_impl.h" +#include "net/websockets/websocket_deflate_stream.h" +#include "net/websockets/websocket_deflater.h" #include "net/websockets/websocket_extension_parser.h" #include "net/websockets/websocket_handshake_constants.h" #include "net/websockets/websocket_handshake_handler.h" @@ -34,6 +40,20 @@ #include "net/websockets/websocket_stream.h" namespace net { + +// TODO(ricea): If more extensions are added, replace this with a more general +// mechanism. +struct WebSocketExtensionParams { + WebSocketExtensionParams() + : deflate_enabled(false), + client_window_bits(15), + deflate_mode(WebSocketDeflater::TAKE_OVER_CONTEXT) {} + + bool deflate_enabled; + int client_window_bits; + WebSocketDeflater::ContextTakeOverMode deflate_mode; +}; + namespace { enum GetHeaderResult { @@ -202,12 +222,80 @@ bool ValidateSubProtocol( return true; } +bool ValidatePerMessageDeflateExtension(const WebSocketExtension& extension, + std::string* failure_message, + WebSocketExtensionParams* params) { + static const char kClientPrefix[] = "client_"; + static const char kServerPrefix[] = "server_"; + static const char kNoContextTakeover[] = "no_context_takeover"; + static const char kMaxWindowBits[] = "max_window_bits"; + const size_t kPrefixLen = arraysize(kClientPrefix) - 1; + COMPILE_ASSERT(kPrefixLen == arraysize(kServerPrefix) - 1, + the_strings_server_and_client_must_be_the_same_length); + typedef std::vector<WebSocketExtension::Parameter> ParameterVector; + + DCHECK(extension.name() == "permessage-deflate"); + const ParameterVector& parameters = extension.parameters(); + std::set<std::string> seen_names; + for (ParameterVector::const_iterator it = parameters.begin(); + it != parameters.end(); ++it) { + const std::string& name = it->name(); + if (seen_names.count(name) != 0) { + *failure_message = + "Received duplicate permessage-deflate extension parameter " + name; + return false; + } + seen_names.insert(name); + const std::string client_or_server(name, 0, kPrefixLen); + const bool is_client = (client_or_server == kClientPrefix); + if (!is_client && client_or_server != kServerPrefix) { + *failure_message = + "Received an unexpected permessage-deflate extension parameter"; + return false; + } + const std::string rest(name, kPrefixLen); + if (rest == kNoContextTakeover) { + if (it->HasValue()) { + *failure_message = "Received invalid " + name + " parameter"; + return false; + } + if (is_client) + params->deflate_mode = WebSocketDeflater::DO_NOT_TAKE_OVER_CONTEXT; + } else if (rest == kMaxWindowBits) { + if (!it->HasValue()) { + *failure_message = name + " must have value"; + return false; + } + int bits = 0; + if (!base::StringToInt(it->value(), &bits) || bits < 8 || bits > 15 || + it->value()[0] == '0' || + it->value().find_first_not_of("0123456789") != std::string::npos) { + *failure_message = "Received invalid " + name + " parameter"; + return false; + } + if (is_client) + params->client_window_bits = bits; + } else { + *failure_message = + "Received an unexpected permessage-deflate extension parameter"; + return false; + } + } + params->deflate_enabled = true; + return true; +} + bool ValidateExtensions(const HttpResponseHeaders* headers, const std::vector<std::string>& requested_extensions, std::string* extensions, - std::string* failure_message) { + std::string* failure_message, + WebSocketExtensionParams* params) { void* state = NULL; std::string value; + std::vector<std::string> accepted_extensions; + // TODO(ricea): If adding support for additional extensions, generalise this + // code. + bool seen_permessage_deflate = false; while (headers->EnumerateHeader( &state, websockets::kSecWebSocketExtensions, &value)) { WebSocketExtensionParser parser; @@ -220,13 +308,25 @@ bool ValidateExtensions(const HttpResponseHeaders* headers, value; return false; } - // TODO(ricea): Accept permessage-deflate with valid parameters. - *failure_message = - "Found an unsupported extension '" + - parser.extension().name() + - "' in 'Sec-WebSocket-Extensions' header"; - return false; + if (parser.extension().name() == "permessage-deflate") { + if (seen_permessage_deflate) { + *failure_message = "Received duplicate permessage-deflate response"; + return false; + } + seen_permessage_deflate = true; + if (!ValidatePerMessageDeflateExtension( + parser.extension(), failure_message, params)) + return false; + } else { + *failure_message = + "Found an unsupported extension '" + + parser.extension().name() + + "' in 'Sec-WebSocket-Extensions' header"; + return false; + } + accepted_extensions.push_back(value); } + *extensions = JoinString(accepted_extensions, ", "); return true; } @@ -284,12 +384,12 @@ int WebSocketBasicHandshakeStream::SendRequest( } enriched_headers.SetHeader(websockets::kSecWebSocketKey, handshake_challenge); - AddVectorHeaderIfNonEmpty(websockets::kSecWebSocketProtocol, - requested_sub_protocols_, - &enriched_headers); AddVectorHeaderIfNonEmpty(websockets::kSecWebSocketExtensions, requested_extensions_, &enriched_headers); + AddVectorHeaderIfNonEmpty(websockets::kSecWebSocketProtocol, + requested_sub_protocols_, + &enriched_headers); ComputeSecWebSocketAccept(handshake_challenge, &handshake_challenge_response_); @@ -393,16 +493,25 @@ void WebSocketBasicHandshakeStream::SetPriority(RequestPriority priority) { } scoped_ptr<WebSocketStream> WebSocketBasicHandshakeStream::Upgrade() { - // TODO(ricea): Add deflate support. - // The HttpStreamParser object has a pointer to our ClientSocketHandle. Make // sure it does not touch it again before it is destroyed. state_.DeleteParser(); - return scoped_ptr<WebSocketStream>( + scoped_ptr<WebSocketStream> basic_stream( new WebSocketBasicStream(state_.ReleaseConnection(), state_.read_buf(), sub_protocol_, extensions_)); + DCHECK(extension_params_.get()); + if (extension_params_->deflate_enabled) { + return scoped_ptr<WebSocketStream>( + new WebSocketDeflateStream(basic_stream.Pass(), + extension_params_->deflate_mode, + extension_params_->client_window_bits, + scoped_ptr<WebSocketDeflatePredictor>( + new WebSocketDeflatePredictorImpl))); + } else { + return basic_stream.Pass(); + } } void WebSocketBasicHandshakeStream::SetWebSocketKeyForTesting( @@ -464,6 +573,7 @@ int WebSocketBasicHandshakeStream::ValidateResponse() { int WebSocketBasicHandshakeStream::ValidateUpgradeResponse( const scoped_refptr<HttpResponseHeaders>& headers) { + extension_params_.reset(new WebSocketExtensionParams); if (ValidateUpgrade(headers.get(), &failure_message_) && ValidateSecWebSocketAccept(headers.get(), handshake_challenge_response_, @@ -476,7 +586,8 @@ int WebSocketBasicHandshakeStream::ValidateUpgradeResponse( ValidateExtensions(headers.get(), requested_extensions_, &extensions_, - &failure_message_)) { + &failure_message_, + extension_params_.get())) { return OK; } failure_message_ = "Error during WebSocket handshake: " + failure_message_; diff --git a/net/websockets/websocket_basic_handshake_stream.h b/net/websockets/websocket_basic_handshake_stream.h index 6ef57d4..71e835f 100644 --- a/net/websockets/websocket_basic_handshake_stream.h +++ b/net/websockets/websocket_basic_handshake_stream.h @@ -22,6 +22,8 @@ class HttpResponseHeaders; class HttpResponseInfo; class HttpStreamParser; +struct WebSocketExtensionParams; + class NET_EXPORT_PRIVATE WebSocketBasicHandshakeStream : public WebSocketHandshakeStreamBase { public: @@ -127,6 +129,10 @@ class NET_EXPORT_PRIVATE WebSocketBasicHandshakeStream // The extension(s) selected by the server. std::string extensions_; + // The extension parameters. The class is defined in the implementation file + // to avoid including extension-related header files here. + scoped_ptr<WebSocketExtensionParams> extension_params_; + std::string failure_message_; DISALLOW_COPY_AND_ASSIGN(WebSocketBasicHandshakeStream); diff --git a/net/websockets/websocket_handshake_stream_create_helper.cc b/net/websockets/websocket_handshake_stream_create_helper.cc index 8268cda..b3ff3e2 100644 --- a/net/websockets/websocket_handshake_stream_create_helper.cc +++ b/net/websockets/websocket_handshake_stream_create_helper.cc @@ -26,12 +26,17 @@ WebSocketHandshakeStreamBase* WebSocketHandshakeStreamCreateHelper::CreateBasicStream( scoped_ptr<ClientSocketHandle> connection, bool using_proxy) { + // The list of supported extensions and parameters is hard-coded. + // TODO(ricea): If more extensions are added, consider a more flexible + // method. + std::vector<std::string> extensions( + 1, "permessage-deflate; client_max_window_bits"); return stream_ = new WebSocketBasicHandshakeStream(connection.Pass(), connect_delegate_, using_proxy, requested_subprotocols_, - std::vector<std::string>()); + extensions); } // TODO(ricea): Create a WebSocketSpdyHandshakeStream. crbug.com/323852 diff --git a/net/websockets/websocket_handshake_stream_create_helper_test.cc b/net/websockets/websocket_handshake_stream_create_helper_test.cc index 7cf7cdb..89e7cc3 100644 --- a/net/websockets/websocket_handshake_stream_create_helper_test.cc +++ b/net/websockets/websocket_handshake_stream_create_helper_test.cc @@ -4,6 +4,9 @@ #include "net/websockets/websocket_handshake_stream_create_helper.h" +#include <string> +#include <vector> + #include "net/base/completion_callback.h" #include "net/base/net_errors.h" #include "net/http/http_request_headers.h" @@ -146,14 +149,47 @@ TEST_F(WebSocketHandshakeStreamCreateHelperTest, SubProtocols) { sub_protocols.push_back("chat"); sub_protocols.push_back("superchat"); scoped_ptr<WebSocketStream> stream = - CreateAndInitializeStream("ws://localhost/", "/", - sub_protocols, "http://localhost/", + CreateAndInitializeStream("ws://localhost/", + "/", + sub_protocols, + "http://localhost/", "Sec-WebSocket-Protocol: chat, superchat\r\n", "Sec-WebSocket-Protocol: superchat\r\n"); EXPECT_EQ("superchat", stream->GetSubProtocol()); } -// TODO(ricea): Test extensions once they are implemented. +// Verify that extension name is available. Bad extension names are tested in +// websocket_stream_test.cc. +TEST_F(WebSocketHandshakeStreamCreateHelperTest, Extensions) { + scoped_ptr<WebSocketStream> stream = CreateAndInitializeStream( + "ws://localhost/", + "/", + std::vector<std::string>(), + "http://localhost/", + "", + "Sec-WebSocket-Extensions: permessage-deflate\r\n"); + EXPECT_EQ("permessage-deflate", stream->GetExtensions()); +} + +// Verify that extension parameters are available. Bad parameters are tested in +// websocket_stream_test.cc. +TEST_F(WebSocketHandshakeStreamCreateHelperTest, ExtensionParameters) { + scoped_ptr<WebSocketStream> stream = CreateAndInitializeStream( + "ws://localhost/", + "/", + std::vector<std::string>(), + "http://localhost/", + "", + "Sec-WebSocket-Extensions: permessage-deflate;" + " client_max_window_bits=14; server_max_window_bits=14;" + " server_no_context_takeover; client_no_context_takeover\r\n"); + + EXPECT_EQ( + "permessage-deflate;" + " client_max_window_bits=14; server_max_window_bits=14;" + " server_no_context_takeover; client_no_context_takeover", + stream->GetExtensions()); +} } // namespace } // namespace net diff --git a/net/websockets/websocket_stream_test.cc b/net/websockets/websocket_stream_test.cc index 300e763..6de9647 100644 --- a/net/websockets/websocket_stream_test.cc +++ b/net/websockets/websocket_stream_test.cc @@ -9,6 +9,7 @@ #include <utility> #include <vector> +#include "base/memory/scoped_vector.h" #include "base/run_loop.h" #include "net/base/net_errors.h" #include "net/http/http_request_headers.h" @@ -17,6 +18,7 @@ #include "net/socket/socket_test_util.h" #include "net/url_request/url_request_test_util.h" #include "net/websockets/websocket_basic_handshake_stream.h" +#include "net/websockets/websocket_frame.h" #include "net/websockets/websocket_handshake_request_info.h" #include "net/websockets/websocket_handshake_response_info.h" #include "net/websockets/websocket_handshake_stream_create_helper.h" @@ -184,6 +186,26 @@ class WebSocketStreamCreateTest : public ::testing::Test { scoped_ptr<WebSocketHandshakeResponseInfo> response_info_; }; +// There are enough tests of the Sec-WebSocket-Extensions header that they +// deserve their own test fixture. +class WebSocketStreamCreateExtensionTest : public WebSocketStreamCreateTest { + public: + // Performs a standard connect, with the value of the Sec-WebSocket-Extensions + // header in the response set to |extensions_header_value|. Runs the event + // loop to allow the connect to complete. + void CreateAndConnectWithExtensions( + const std::string& extensions_header_value) { + CreateAndConnectStandard( + "ws://localhost/testing_path", + "/testing_path", + NoSubProtocols(), + "http://localhost/", + "", + "Sec-WebSocket-Extensions: " + extensions_header_value + "\r\n"); + RunUntilIdle(); + } +}; + // Confirm that the basic case works as expected. TEST_F(WebSocketStreamCreateTest, SimpleSuccess) { CreateAndConnectStandard( @@ -229,7 +251,7 @@ TEST_F(WebSocketStreamCreateTest, HandshakeInfo) { EXPECT_EQ(GURL("ws://localhost/"), response_info_->url); EXPECT_EQ(101, response_info_->status_code); EXPECT_EQ("Switching Protocols", response_info_->status_text); - EXPECT_EQ(9u, request_headers.size()); + EXPECT_EQ(10u, request_headers.size()); EXPECT_EQ(HeaderKeyValuePair("Host", "localhost"), request_headers[0]); EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), request_headers[1]); EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), request_headers[2]); @@ -243,6 +265,9 @@ TEST_F(WebSocketStreamCreateTest, HandshakeInfo) { EXPECT_EQ(HeaderKeyValuePair("Accept-Language", "en-us,fr"), request_headers[7]); EXPECT_EQ("Sec-WebSocket-Key", request_headers[8].first); + EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Extensions", + "permessage-deflate; client_max_window_bits"), + request_headers[9]); std::vector<HeaderKeyValuePair> response_headers = ToVector(*response_info_->headers); @@ -389,15 +414,52 @@ TEST_F(WebSocketStreamCreateTest, UnmatchedSubProtocolInResponse) { failure_message()); } -// Unknown extension in the response is rejected -TEST_F(WebSocketStreamCreateTest, UnknownExtension) { - CreateAndConnectStandard("ws://localhost/testing_path", - "/testing_path", - NoSubProtocols(), - "http://localhost/", - "", - "Sec-WebSocket-Extensions: x-unknown-extension\r\n"); +// permessage-deflate extension basic success case. +TEST_F(WebSocketStreamCreateExtensionTest, PerMessageDeflateSuccess) { + CreateAndConnectWithExtensions("permessage-deflate"); + EXPECT_TRUE(stream_); + EXPECT_FALSE(has_failed()); +} + +// permessage-deflate extensions success with all parameters. +TEST_F(WebSocketStreamCreateExtensionTest, PerMessageDeflateParamsSuccess) { + CreateAndConnectWithExtensions( + "permessage-deflate; client_no_context_takeover; " + "server_max_window_bits=11; client_max_window_bits=13; " + "server_no_context_takeover"); + EXPECT_TRUE(stream_); + EXPECT_FALSE(has_failed()); +} + +// Verify that incoming messages are actually decompressed with +// permessage-deflate enabled. +TEST_F(WebSocketStreamCreateExtensionTest, PerMessageDeflateInflates) { + CreateAndConnectCustomResponse( + "ws://localhost/testing_path", + "/testing_path", + NoSubProtocols(), + "http://localhost/", + "", + WebSocketStandardResponse( + "Sec-WebSocket-Extensions: permessage-deflate\r\n") + + std::string( + "\xc1\x07" // WebSocket header (FIN + RSV1, Text payload 7 bytes) + "\xf2\x48\xcd\xc9\xc9\x07\x00", // "Hello" DEFLATE compressed + 9)); RunUntilIdle(); + + ASSERT_TRUE(stream_); + ScopedVector<WebSocketFrame> frames; + CompletionCallback callback; + ASSERT_EQ(OK, stream_->ReadFrames(&frames, callback)); + ASSERT_EQ(1U, frames.size()); + ASSERT_EQ(5U, frames[0]->header.payload_length); + EXPECT_EQ("Hello", std::string(frames[0]->data->data(), 5)); +} + +// Unknown extension in the response is rejected +TEST_F(WebSocketStreamCreateExtensionTest, UnknownExtension) { + CreateAndConnectWithExtensions("x-unknown-extension"); EXPECT_FALSE(stream_); EXPECT_TRUE(has_failed()); EXPECT_EQ("Error during WebSocket handshake: " @@ -406,6 +468,155 @@ TEST_F(WebSocketStreamCreateTest, UnknownExtension) { failure_message()); } +// Malformed extensions are rejected (this file does not cover all possible +// parse failures, as the parser is covered thoroughly by its own unit tests). +TEST_F(WebSocketStreamCreateExtensionTest, MalformedExtension) { + CreateAndConnectWithExtensions(";"); + EXPECT_FALSE(stream_); + EXPECT_TRUE(has_failed()); + EXPECT_EQ( + "Error during WebSocket handshake: 'Sec-WebSocket-Extensions' header " + "value is rejected by the parser: ;", + failure_message()); +} + +// The permessage-deflate extension may only be specified once. +TEST_F(WebSocketStreamCreateExtensionTest, OnlyOnePerMessageDeflateAllowed) { + CreateAndConnectWithExtensions( + "permessage-deflate, permessage-deflate; client_max_window_bits=10"); + EXPECT_FALSE(stream_); + EXPECT_TRUE(has_failed()); + EXPECT_EQ( + "Error during WebSocket handshake: Received duplicate permessage-deflate " + "response", + failure_message()); +} + +// permessage-deflate parameters may not be duplicated. +TEST_F(WebSocketStreamCreateExtensionTest, NoDuplicateParameters) { + CreateAndConnectWithExtensions( + "permessage-deflate; client_no_context_takeover; " + "client_no_context_takeover"); + EXPECT_FALSE(stream_); + EXPECT_TRUE(has_failed()); + EXPECT_EQ( + "Error during WebSocket handshake: Received duplicate permessage-deflate " + "extension parameter client_no_context_takeover", + failure_message()); +} + +// permessage-deflate parameters must start with "client_" or "server_" +TEST_F(WebSocketStreamCreateExtensionTest, BadParameterPrefix) { + CreateAndConnectWithExtensions( + "permessage-deflate; absurd_no_context_takeover"); + EXPECT_FALSE(stream_); + EXPECT_TRUE(has_failed()); + EXPECT_EQ( + "Error during WebSocket handshake: Received an unexpected " + "permessage-deflate extension parameter", + failure_message()); +} + +// permessage-deflate parameters must be either *_no_context_takeover or +// *_max_window_bits +TEST_F(WebSocketStreamCreateExtensionTest, BadParameterSuffix) { + CreateAndConnectWithExtensions( + "permessage-deflate; client_max_content_bits=5"); + EXPECT_FALSE(stream_); + EXPECT_TRUE(has_failed()); + EXPECT_EQ( + "Error during WebSocket handshake: Received an unexpected " + "permessage-deflate extension parameter", + failure_message()); +} + +// *_no_context_takeover parameters must not have an argument +TEST_F(WebSocketStreamCreateExtensionTest, BadParameterValue) { + CreateAndConnectWithExtensions( + "permessage-deflate; client_no_context_takeover=true"); + EXPECT_FALSE(stream_); + EXPECT_TRUE(has_failed()); + EXPECT_EQ( + "Error during WebSocket handshake: Received invalid " + "client_no_context_takeover parameter", + failure_message()); +} + +// *_max_window_bits must have an argument +TEST_F(WebSocketStreamCreateExtensionTest, NoMaxWindowBitsArgument) { + CreateAndConnectWithExtensions("permessage-deflate; client_max_window_bits"); + EXPECT_FALSE(stream_); + EXPECT_TRUE(has_failed()); + EXPECT_EQ( + "Error during WebSocket handshake: client_max_window_bits must have " + "value", + failure_message()); +} + +// *_max_window_bits must be an integer +TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueInteger) { + CreateAndConnectWithExtensions( + "permessage-deflate; server_max_window_bits=banana"); + EXPECT_FALSE(stream_); + EXPECT_TRUE(has_failed()); + EXPECT_EQ( + "Error during WebSocket handshake: Received invalid " + "server_max_window_bits parameter", + failure_message()); +} + +// *_max_window_bits must be >= 8 +TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueTooSmall) { + CreateAndConnectWithExtensions( + "permessage-deflate; server_max_window_bits=7"); + EXPECT_FALSE(stream_); + EXPECT_TRUE(has_failed()); + EXPECT_EQ( + "Error during WebSocket handshake: Received invalid " + "server_max_window_bits parameter", + failure_message()); +} + +// *_max_window_bits must be <= 15 +TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueTooBig) { + CreateAndConnectWithExtensions( + "permessage-deflate; client_max_window_bits=16"); + EXPECT_FALSE(stream_); + EXPECT_TRUE(has_failed()); + EXPECT_EQ( + "Error during WebSocket handshake: Received invalid " + "client_max_window_bits parameter", + failure_message()); +} + +// *_max_window_bits must not start with 0 +TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueStartsWithZero) { + CreateAndConnectWithExtensions( + "permessage-deflate; client_max_window_bits=08"); + EXPECT_FALSE(stream_); + EXPECT_TRUE(has_failed()); + EXPECT_EQ( + "Error during WebSocket handshake: Received invalid " + "client_max_window_bits parameter", + failure_message()); +} + +// *_max_window_bits must not start with + +TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueStartsWithPlus) { + CreateAndConnectWithExtensions( + "permessage-deflate; server_max_window_bits=+9"); + EXPECT_FALSE(stream_); + EXPECT_TRUE(has_failed()); + EXPECT_EQ( + "Error during WebSocket handshake: Received invalid " + "server_max_window_bits parameter", + failure_message()); +} + +// TODO(ricea): Check that WebSocketDeflateStream is initialised with the +// arguments from the server. This is difficult because the data written to the +// socket is randomly masked. + // Additional Sec-WebSocket-Accept headers should be rejected. TEST_F(WebSocketStreamCreateTest, DoubleAccept) { CreateAndConnectStandard( diff --git a/net/websockets/websocket_test_util.cc b/net/websockets/websocket_test_util.cc index 55113c6..da030c5 100644 --- a/net/websockets/websocket_test_util.cc +++ b/net/websockets/websocket_test_util.cc @@ -44,6 +44,7 @@ std::string WebSocketStandardRequest(const std::string& path, "Accept-Encoding: gzip,deflate\r\n" "Accept-Language: en-us,fr\r\n" "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n" "%s\r\n", path.c_str(), origin.c_str(), @@ -87,8 +88,14 @@ void WebSocketDeterministicMockClientSocketFactoryMaker::SetExpectations( // We need to extend the lifetime of these strings. detail_->expect_written = expect_written; detail_->return_to_read = return_to_read; - detail_->write = MockWrite(SYNCHRONOUS, 0, detail_->expect_written.c_str()); - detail_->read = MockRead(SYNCHRONOUS, 1, detail_->return_to_read.c_str()); + detail_->write = MockWrite(SYNCHRONOUS, + detail_->expect_written.data(), + detail_->expect_written.size(), + 0); + detail_->read = MockRead(SYNCHRONOUS, + detail_->return_to_read.data(), + detail_->return_to_read.size(), + 1); scoped_ptr<DeterministicSocketData> socket_data( new DeterministicSocketData(&detail_->read, 1, &detail_->write, 1)); socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK)); |