summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorricea@chromium.org <ricea@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-29 00:42:45 +0000
committerricea@chromium.org <ricea@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-29 00:42:45 +0000
commit0be9392b7cc037c74baa929bc384baf36b0001f3 (patch)
tree1d6886902c5cfb1e1db8017483ca703a54bffc06
parent73b6ae22b6e1a9ebd6a83de0527267f40d6f22b3 (diff)
downloadchromium_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.cc139
-rw-r--r--net/websockets/websocket_basic_handshake_stream.h6
-rw-r--r--net/websockets/websocket_handshake_stream_create_helper.cc7
-rw-r--r--net/websockets/websocket_handshake_stream_create_helper_test.cc42
-rw-r--r--net/websockets/websocket_stream_test.cc229
-rw-r--r--net/websockets/websocket_test_util.cc11
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));