Lots of random cleanups, mostly for native_theme_win.cc:
[chromium-blink-merge.git] / net / websockets / websocket_stream_test.cc
blobb7229f93188a43543b4871d98f5f418de70503ff
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/websockets/websocket_stream.h"
7 #include <algorithm>
8 #include <string>
9 #include <utility>
10 #include <vector>
12 #include "base/compiler_specific.h"
13 #include "base/memory/scoped_vector.h"
14 #include "base/metrics/histogram.h"
15 #include "base/metrics/histogram_samples.h"
16 #include "base/metrics/statistics_recorder.h"
17 #include "base/run_loop.h"
18 #include "base/strings/stringprintf.h"
19 #include "net/base/net_errors.h"
20 #include "net/base/test_data_directory.h"
21 #include "net/http/http_request_headers.h"
22 #include "net/http/http_response_headers.h"
23 #include "net/socket/client_socket_handle.h"
24 #include "net/socket/socket_test_util.h"
25 #include "net/test/cert_test_util.h"
26 #include "net/url_request/url_request_test_util.h"
27 #include "net/websockets/websocket_basic_handshake_stream.h"
28 #include "net/websockets/websocket_frame.h"
29 #include "net/websockets/websocket_handshake_request_info.h"
30 #include "net/websockets/websocket_handshake_response_info.h"
31 #include "net/websockets/websocket_handshake_stream_create_helper.h"
32 #include "net/websockets/websocket_test_util.h"
33 #include "testing/gtest/include/gtest/gtest.h"
34 #include "url/gurl.h"
35 #include "url/origin.h"
37 namespace net {
38 namespace {
40 typedef std::pair<std::string, std::string> HeaderKeyValuePair;
42 std::vector<HeaderKeyValuePair> ToVector(const HttpRequestHeaders& headers) {
43 HttpRequestHeaders::Iterator it(headers);
44 std::vector<HeaderKeyValuePair> result;
45 while (it.GetNext())
46 result.push_back(HeaderKeyValuePair(it.name(), it.value()));
47 return result;
50 std::vector<HeaderKeyValuePair> ToVector(const HttpResponseHeaders& headers) {
51 void* iter = NULL;
52 std::string name, value;
53 std::vector<HeaderKeyValuePair> result;
54 while (headers.EnumerateHeaderLines(&iter, &name, &value))
55 result.push_back(HeaderKeyValuePair(name, value));
56 return result;
59 // A sub-class of WebSocketHandshakeStreamCreateHelper which always sets a
60 // deterministic key to use in the WebSocket handshake.
61 class DeterministicKeyWebSocketHandshakeStreamCreateHelper
62 : public WebSocketHandshakeStreamCreateHelper {
63 public:
64 DeterministicKeyWebSocketHandshakeStreamCreateHelper(
65 WebSocketStream::ConnectDelegate* connect_delegate,
66 const std::vector<std::string>& requested_subprotocols)
67 : WebSocketHandshakeStreamCreateHelper(connect_delegate,
68 requested_subprotocols) {}
70 virtual void OnStreamCreated(WebSocketBasicHandshakeStream* stream) OVERRIDE {
71 stream->SetWebSocketKeyForTesting("dGhlIHNhbXBsZSBub25jZQ==");
75 class WebSocketStreamCreateTest : public ::testing::Test {
76 public:
77 WebSocketStreamCreateTest() : has_failed_(false), ssl_fatal_(false) {}
79 void CreateAndConnectCustomResponse(
80 const std::string& socket_url,
81 const std::string& socket_path,
82 const std::vector<std::string>& sub_protocols,
83 const std::string& origin,
84 const std::string& extra_request_headers,
85 const std::string& response_body) {
86 url_request_context_host_.SetExpectations(
87 WebSocketStandardRequest(socket_path, origin, extra_request_headers),
88 response_body);
89 CreateAndConnectStream(socket_url, sub_protocols, origin);
92 // |extra_request_headers| and |extra_response_headers| must end in "\r\n" or
93 // errors like "Unable to perform synchronous IO while stopped" will occur.
94 void CreateAndConnectStandard(const std::string& socket_url,
95 const std::string& socket_path,
96 const std::vector<std::string>& sub_protocols,
97 const std::string& origin,
98 const std::string& extra_request_headers,
99 const std::string& extra_response_headers) {
100 CreateAndConnectCustomResponse(
101 socket_url,
102 socket_path,
103 sub_protocols,
104 origin,
105 extra_request_headers,
106 WebSocketStandardResponse(extra_response_headers));
109 void CreateAndConnectRawExpectations(
110 const std::string& socket_url,
111 const std::vector<std::string>& sub_protocols,
112 const std::string& origin,
113 scoped_ptr<DeterministicSocketData> socket_data) {
114 url_request_context_host_.AddRawExpectations(socket_data.Pass());
115 CreateAndConnectStream(socket_url, sub_protocols, origin);
118 // A wrapper for CreateAndConnectStreamForTesting that knows about our default
119 // parameters.
120 void CreateAndConnectStream(const std::string& socket_url,
121 const std::vector<std::string>& sub_protocols,
122 const std::string& origin) {
123 for (size_t i = 0; i < ssl_data_.size(); ++i) {
124 scoped_ptr<SSLSocketDataProvider> ssl_data(ssl_data_[i]);
125 ssl_data_[i] = NULL;
126 url_request_context_host_.AddSSLSocketDataProvider(ssl_data.Pass());
128 ssl_data_.clear();
129 scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate(
130 new TestConnectDelegate(this));
131 WebSocketStream::ConnectDelegate* delegate = connect_delegate.get();
132 scoped_ptr<WebSocketHandshakeStreamCreateHelper> create_helper(
133 new DeterministicKeyWebSocketHandshakeStreamCreateHelper(
134 delegate, sub_protocols));
135 stream_request_ = ::net::CreateAndConnectStreamForTesting(
136 GURL(socket_url),
137 create_helper.Pass(),
138 url::Origin(origin),
139 url_request_context_host_.GetURLRequestContext(),
140 BoundNetLog(),
141 connect_delegate.Pass());
144 static void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
146 // A simple function to make the tests more readable. Creates an empty vector.
147 static std::vector<std::string> NoSubProtocols() {
148 return std::vector<std::string>();
151 const std::string& failure_message() const { return failure_message_; }
152 bool has_failed() const { return has_failed_; }
154 class TestConnectDelegate : public WebSocketStream::ConnectDelegate {
155 public:
156 explicit TestConnectDelegate(WebSocketStreamCreateTest* owner)
157 : owner_(owner) {}
159 virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE {
160 stream.swap(owner_->stream_);
163 virtual void OnFailure(const std::string& message) OVERRIDE {
164 owner_->has_failed_ = true;
165 owner_->failure_message_ = message;
168 virtual void OnStartOpeningHandshake(
169 scoped_ptr<WebSocketHandshakeRequestInfo> request) OVERRIDE {
170 if (owner_->request_info_)
171 ADD_FAILURE();
172 owner_->request_info_ = request.Pass();
174 virtual void OnFinishOpeningHandshake(
175 scoped_ptr<WebSocketHandshakeResponseInfo> response) OVERRIDE {
176 if (owner_->response_info_)
177 ADD_FAILURE();
178 owner_->response_info_ = response.Pass();
180 virtual void OnSSLCertificateError(
181 scoped_ptr<WebSocketEventInterface::SSLErrorCallbacks>
182 ssl_error_callbacks,
183 const SSLInfo& ssl_info,
184 bool fatal) OVERRIDE {
185 owner_->ssl_error_callbacks_ = ssl_error_callbacks.Pass();
186 owner_->ssl_info_ = ssl_info;
187 owner_->ssl_fatal_ = fatal;
190 private:
191 WebSocketStreamCreateTest* owner_;
194 WebSocketTestURLRequestContextHost url_request_context_host_;
195 scoped_ptr<WebSocketStreamRequest> stream_request_;
196 // Only set if the connection succeeded.
197 scoped_ptr<WebSocketStream> stream_;
198 // Only set if the connection failed.
199 std::string failure_message_;
200 bool has_failed_;
201 scoped_ptr<WebSocketHandshakeRequestInfo> request_info_;
202 scoped_ptr<WebSocketHandshakeResponseInfo> response_info_;
203 scoped_ptr<WebSocketEventInterface::SSLErrorCallbacks> ssl_error_callbacks_;
204 SSLInfo ssl_info_;
205 bool ssl_fatal_;
206 ScopedVector<SSLSocketDataProvider> ssl_data_;
209 // There are enough tests of the Sec-WebSocket-Extensions header that they
210 // deserve their own test fixture.
211 class WebSocketStreamCreateExtensionTest : public WebSocketStreamCreateTest {
212 public:
213 // Performs a standard connect, with the value of the Sec-WebSocket-Extensions
214 // header in the response set to |extensions_header_value|. Runs the event
215 // loop to allow the connect to complete.
216 void CreateAndConnectWithExtensions(
217 const std::string& extensions_header_value) {
218 CreateAndConnectStandard(
219 "ws://localhost/testing_path",
220 "/testing_path",
221 NoSubProtocols(),
222 "http://localhost",
224 "Sec-WebSocket-Extensions: " + extensions_header_value + "\r\n");
225 RunUntilIdle();
229 class WebSocketStreamCreateUMATest : public ::testing::Test {
230 public:
231 // This enum should match with the enum in Delegate in websocket_stream.cc.
232 enum HandshakeResult {
233 INCOMPLETE,
234 CONNECTED,
235 FAILED,
236 NUM_HANDSHAKE_RESULT_TYPES,
239 class StreamCreation : public WebSocketStreamCreateTest {
240 virtual void TestBody() OVERRIDE {}
243 scoped_ptr<base::HistogramSamples> GetSamples(const std::string& name) {
244 base::HistogramBase* histogram =
245 base::StatisticsRecorder::FindHistogram(name);
246 return histogram ? histogram->SnapshotSamples()
247 : scoped_ptr<base::HistogramSamples>();
251 // Confirm that the basic case works as expected.
252 TEST_F(WebSocketStreamCreateTest, SimpleSuccess) {
253 CreateAndConnectStandard(
254 "ws://localhost/", "/", NoSubProtocols(), "http://localhost", "", "");
255 EXPECT_FALSE(request_info_);
256 EXPECT_FALSE(response_info_);
257 RunUntilIdle();
258 EXPECT_FALSE(has_failed());
259 EXPECT_TRUE(stream_);
260 EXPECT_TRUE(request_info_);
261 EXPECT_TRUE(response_info_);
264 TEST_F(WebSocketStreamCreateTest, HandshakeInfo) {
265 static const char kResponse[] =
266 "HTTP/1.1 101 Switching Protocols\r\n"
267 "Upgrade: websocket\r\n"
268 "Connection: Upgrade\r\n"
269 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
270 "foo: bar, baz\r\n"
271 "hoge: fuga\r\n"
272 "hoge: piyo\r\n"
273 "\r\n";
275 CreateAndConnectCustomResponse(
276 "ws://localhost/",
277 "/",
278 NoSubProtocols(),
279 "http://localhost",
281 kResponse);
282 EXPECT_FALSE(request_info_);
283 EXPECT_FALSE(response_info_);
284 RunUntilIdle();
285 EXPECT_TRUE(stream_);
286 ASSERT_TRUE(request_info_);
287 ASSERT_TRUE(response_info_);
288 std::vector<HeaderKeyValuePair> request_headers =
289 ToVector(request_info_->headers);
290 // We examine the contents of request_info_ and response_info_
291 // mainly only in this test case.
292 EXPECT_EQ(GURL("ws://localhost/"), request_info_->url);
293 EXPECT_EQ(GURL("ws://localhost/"), response_info_->url);
294 EXPECT_EQ(101, response_info_->status_code);
295 EXPECT_EQ("Switching Protocols", response_info_->status_text);
296 ASSERT_EQ(12u, request_headers.size());
297 EXPECT_EQ(HeaderKeyValuePair("Host", "localhost"), request_headers[0]);
298 EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), request_headers[1]);
299 EXPECT_EQ(HeaderKeyValuePair("Pragma", "no-cache"), request_headers[2]);
300 EXPECT_EQ(HeaderKeyValuePair("Cache-Control", "no-cache"),
301 request_headers[3]);
302 EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), request_headers[4]);
303 EXPECT_EQ(HeaderKeyValuePair("Origin", "http://localhost"),
304 request_headers[5]);
305 EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Version", "13"),
306 request_headers[6]);
307 EXPECT_EQ(HeaderKeyValuePair("User-Agent", ""), request_headers[7]);
308 EXPECT_EQ(HeaderKeyValuePair("Accept-Encoding", "gzip,deflate"),
309 request_headers[8]);
310 EXPECT_EQ(HeaderKeyValuePair("Accept-Language", "en-us,fr"),
311 request_headers[9]);
312 EXPECT_EQ("Sec-WebSocket-Key", request_headers[10].first);
313 EXPECT_EQ(HeaderKeyValuePair("Sec-WebSocket-Extensions",
314 "permessage-deflate; client_max_window_bits"),
315 request_headers[11]);
317 std::vector<HeaderKeyValuePair> response_headers =
318 ToVector(*response_info_->headers);
319 ASSERT_EQ(6u, response_headers.size());
320 // Sort the headers for ease of verification.
321 std::sort(response_headers.begin(), response_headers.end());
323 EXPECT_EQ(HeaderKeyValuePair("Connection", "Upgrade"), response_headers[0]);
324 EXPECT_EQ("Sec-WebSocket-Accept", response_headers[1].first);
325 EXPECT_EQ(HeaderKeyValuePair("Upgrade", "websocket"), response_headers[2]);
326 EXPECT_EQ(HeaderKeyValuePair("foo", "bar, baz"), response_headers[3]);
327 EXPECT_EQ(HeaderKeyValuePair("hoge", "fuga"), response_headers[4]);
328 EXPECT_EQ(HeaderKeyValuePair("hoge", "piyo"), response_headers[5]);
331 // Confirm that the stream isn't established until the message loop runs.
332 TEST_F(WebSocketStreamCreateTest, NeedsToRunLoop) {
333 CreateAndConnectStandard(
334 "ws://localhost/", "/", NoSubProtocols(), "http://localhost", "", "");
335 EXPECT_FALSE(has_failed());
336 EXPECT_FALSE(stream_);
339 // Check the path is used.
340 TEST_F(WebSocketStreamCreateTest, PathIsUsed) {
341 CreateAndConnectStandard("ws://localhost/testing_path",
342 "/testing_path",
343 NoSubProtocols(),
344 "http://localhost",
346 "");
347 RunUntilIdle();
348 EXPECT_FALSE(has_failed());
349 EXPECT_TRUE(stream_);
352 // Check that the origin is used.
353 TEST_F(WebSocketStreamCreateTest, OriginIsUsed) {
354 CreateAndConnectStandard("ws://localhost/testing_path",
355 "/testing_path",
356 NoSubProtocols(),
357 "http://google.com",
359 "");
360 RunUntilIdle();
361 EXPECT_FALSE(has_failed());
362 EXPECT_TRUE(stream_);
365 // Check that sub-protocols are sent and parsed.
366 TEST_F(WebSocketStreamCreateTest, SubProtocolIsUsed) {
367 std::vector<std::string> sub_protocols;
368 sub_protocols.push_back("chatv11.chromium.org");
369 sub_protocols.push_back("chatv20.chromium.org");
370 CreateAndConnectStandard("ws://localhost/testing_path",
371 "/testing_path",
372 sub_protocols,
373 "http://google.com",
374 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
375 "chatv20.chromium.org\r\n",
376 "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
377 RunUntilIdle();
378 EXPECT_TRUE(stream_);
379 EXPECT_FALSE(has_failed());
380 EXPECT_EQ("chatv20.chromium.org", stream_->GetSubProtocol());
383 // Unsolicited sub-protocols are rejected.
384 TEST_F(WebSocketStreamCreateTest, UnsolicitedSubProtocol) {
385 CreateAndConnectStandard("ws://localhost/testing_path",
386 "/testing_path",
387 NoSubProtocols(),
388 "http://google.com",
390 "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n");
391 RunUntilIdle();
392 EXPECT_FALSE(stream_);
393 EXPECT_TRUE(has_failed());
394 EXPECT_EQ("Error during WebSocket handshake: "
395 "Response must not include 'Sec-WebSocket-Protocol' header "
396 "if not present in request: chatv20.chromium.org",
397 failure_message());
400 // Missing sub-protocol response is rejected.
401 TEST_F(WebSocketStreamCreateTest, UnacceptedSubProtocol) {
402 std::vector<std::string> sub_protocols;
403 sub_protocols.push_back("chat.example.com");
404 CreateAndConnectStandard("ws://localhost/testing_path",
405 "/testing_path",
406 sub_protocols,
407 "http://localhost",
408 "Sec-WebSocket-Protocol: chat.example.com\r\n",
409 "");
410 RunUntilIdle();
411 EXPECT_FALSE(stream_);
412 EXPECT_TRUE(has_failed());
413 EXPECT_EQ("Error during WebSocket handshake: "
414 "Sent non-empty 'Sec-WebSocket-Protocol' header "
415 "but no response was received",
416 failure_message());
419 // Only one sub-protocol can be accepted.
420 TEST_F(WebSocketStreamCreateTest, MultipleSubProtocolsInResponse) {
421 std::vector<std::string> sub_protocols;
422 sub_protocols.push_back("chatv11.chromium.org");
423 sub_protocols.push_back("chatv20.chromium.org");
424 CreateAndConnectStandard("ws://localhost/testing_path",
425 "/testing_path",
426 sub_protocols,
427 "http://google.com",
428 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
429 "chatv20.chromium.org\r\n",
430 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
431 "chatv20.chromium.org\r\n");
432 RunUntilIdle();
433 EXPECT_FALSE(stream_);
434 EXPECT_TRUE(has_failed());
435 EXPECT_EQ("Error during WebSocket handshake: "
436 "'Sec-WebSocket-Protocol' header must not appear "
437 "more than once in a response",
438 failure_message());
441 // Unmatched sub-protocol should be rejected.
442 TEST_F(WebSocketStreamCreateTest, UnmatchedSubProtocolInResponse) {
443 std::vector<std::string> sub_protocols;
444 sub_protocols.push_back("chatv11.chromium.org");
445 sub_protocols.push_back("chatv20.chromium.org");
446 CreateAndConnectStandard("ws://localhost/testing_path",
447 "/testing_path",
448 sub_protocols,
449 "http://google.com",
450 "Sec-WebSocket-Protocol: chatv11.chromium.org, "
451 "chatv20.chromium.org\r\n",
452 "Sec-WebSocket-Protocol: chatv21.chromium.org\r\n");
453 RunUntilIdle();
454 EXPECT_FALSE(stream_);
455 EXPECT_TRUE(has_failed());
456 EXPECT_EQ("Error during WebSocket handshake: "
457 "'Sec-WebSocket-Protocol' header value 'chatv21.chromium.org' "
458 "in response does not match any of sent values",
459 failure_message());
462 // permessage-deflate extension basic success case.
463 TEST_F(WebSocketStreamCreateExtensionTest, PerMessageDeflateSuccess) {
464 CreateAndConnectWithExtensions("permessage-deflate");
465 EXPECT_TRUE(stream_);
466 EXPECT_FALSE(has_failed());
469 // permessage-deflate extensions success with all parameters.
470 TEST_F(WebSocketStreamCreateExtensionTest, PerMessageDeflateParamsSuccess) {
471 CreateAndConnectWithExtensions(
472 "permessage-deflate; client_no_context_takeover; "
473 "server_max_window_bits=11; client_max_window_bits=13; "
474 "server_no_context_takeover");
475 EXPECT_TRUE(stream_);
476 EXPECT_FALSE(has_failed());
479 // Verify that incoming messages are actually decompressed with
480 // permessage-deflate enabled.
481 TEST_F(WebSocketStreamCreateExtensionTest, PerMessageDeflateInflates) {
482 CreateAndConnectCustomResponse(
483 "ws://localhost/testing_path",
484 "/testing_path",
485 NoSubProtocols(),
486 "http://localhost",
488 WebSocketStandardResponse(
489 "Sec-WebSocket-Extensions: permessage-deflate\r\n") +
490 std::string(
491 "\xc1\x07" // WebSocket header (FIN + RSV1, Text payload 7 bytes)
492 "\xf2\x48\xcd\xc9\xc9\x07\x00", // "Hello" DEFLATE compressed
493 9));
494 RunUntilIdle();
496 ASSERT_TRUE(stream_);
497 ScopedVector<WebSocketFrame> frames;
498 CompletionCallback callback;
499 ASSERT_EQ(OK, stream_->ReadFrames(&frames, callback));
500 ASSERT_EQ(1U, frames.size());
501 ASSERT_EQ(5U, frames[0]->header.payload_length);
502 EXPECT_EQ("Hello", std::string(frames[0]->data->data(), 5));
505 // Unknown extension in the response is rejected
506 TEST_F(WebSocketStreamCreateExtensionTest, UnknownExtension) {
507 CreateAndConnectWithExtensions("x-unknown-extension");
508 EXPECT_FALSE(stream_);
509 EXPECT_TRUE(has_failed());
510 EXPECT_EQ("Error during WebSocket handshake: "
511 "Found an unsupported extension 'x-unknown-extension' "
512 "in 'Sec-WebSocket-Extensions' header",
513 failure_message());
516 // Malformed extensions are rejected (this file does not cover all possible
517 // parse failures, as the parser is covered thoroughly by its own unit tests).
518 TEST_F(WebSocketStreamCreateExtensionTest, MalformedExtension) {
519 CreateAndConnectWithExtensions(";");
520 EXPECT_FALSE(stream_);
521 EXPECT_TRUE(has_failed());
522 EXPECT_EQ(
523 "Error during WebSocket handshake: 'Sec-WebSocket-Extensions' header "
524 "value is rejected by the parser: ;",
525 failure_message());
528 // The permessage-deflate extension may only be specified once.
529 TEST_F(WebSocketStreamCreateExtensionTest, OnlyOnePerMessageDeflateAllowed) {
530 CreateAndConnectWithExtensions(
531 "permessage-deflate, permessage-deflate; client_max_window_bits=10");
532 EXPECT_FALSE(stream_);
533 EXPECT_TRUE(has_failed());
534 EXPECT_EQ(
535 "Error during WebSocket handshake: "
536 "Received duplicate permessage-deflate response",
537 failure_message());
540 // permessage-deflate parameters may not be duplicated.
541 TEST_F(WebSocketStreamCreateExtensionTest, NoDuplicateParameters) {
542 CreateAndConnectWithExtensions(
543 "permessage-deflate; client_no_context_takeover; "
544 "client_no_context_takeover");
545 EXPECT_FALSE(stream_);
546 EXPECT_TRUE(has_failed());
547 EXPECT_EQ(
548 "Error during WebSocket handshake: Error in permessage-deflate: "
549 "Received duplicate permessage-deflate extension parameter "
550 "client_no_context_takeover",
551 failure_message());
554 // permessage-deflate parameters must start with "client_" or "server_"
555 TEST_F(WebSocketStreamCreateExtensionTest, BadParameterPrefix) {
556 CreateAndConnectWithExtensions(
557 "permessage-deflate; absurd_no_context_takeover");
558 EXPECT_FALSE(stream_);
559 EXPECT_TRUE(has_failed());
560 EXPECT_EQ(
561 "Error during WebSocket handshake: Error in permessage-deflate: "
562 "Received an unexpected permessage-deflate extension parameter",
563 failure_message());
566 // permessage-deflate parameters must be either *_no_context_takeover or
567 // *_max_window_bits
568 TEST_F(WebSocketStreamCreateExtensionTest, BadParameterSuffix) {
569 CreateAndConnectWithExtensions(
570 "permessage-deflate; client_max_content_bits=5");
571 EXPECT_FALSE(stream_);
572 EXPECT_TRUE(has_failed());
573 EXPECT_EQ(
574 "Error during WebSocket handshake: Error in permessage-deflate: "
575 "Received an unexpected permessage-deflate extension parameter",
576 failure_message());
579 // *_no_context_takeover parameters must not have an argument
580 TEST_F(WebSocketStreamCreateExtensionTest, BadParameterValue) {
581 CreateAndConnectWithExtensions(
582 "permessage-deflate; client_no_context_takeover=true");
583 EXPECT_FALSE(stream_);
584 EXPECT_TRUE(has_failed());
585 EXPECT_EQ(
586 "Error during WebSocket handshake: Error in permessage-deflate: "
587 "Received invalid client_no_context_takeover parameter",
588 failure_message());
591 // *_max_window_bits must have an argument
592 TEST_F(WebSocketStreamCreateExtensionTest, NoMaxWindowBitsArgument) {
593 CreateAndConnectWithExtensions("permessage-deflate; client_max_window_bits");
594 EXPECT_FALSE(stream_);
595 EXPECT_TRUE(has_failed());
596 EXPECT_EQ(
597 "Error during WebSocket handshake: Error in permessage-deflate: "
598 "client_max_window_bits must have value",
599 failure_message());
602 // *_max_window_bits must be an integer
603 TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueInteger) {
604 CreateAndConnectWithExtensions(
605 "permessage-deflate; server_max_window_bits=banana");
606 EXPECT_FALSE(stream_);
607 EXPECT_TRUE(has_failed());
608 EXPECT_EQ(
609 "Error during WebSocket handshake: Error in permessage-deflate: "
610 "Received invalid server_max_window_bits parameter",
611 failure_message());
614 // *_max_window_bits must be >= 8
615 TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueTooSmall) {
616 CreateAndConnectWithExtensions(
617 "permessage-deflate; server_max_window_bits=7");
618 EXPECT_FALSE(stream_);
619 EXPECT_TRUE(has_failed());
620 EXPECT_EQ(
621 "Error during WebSocket handshake: Error in permessage-deflate: "
622 "Received invalid server_max_window_bits parameter",
623 failure_message());
626 // *_max_window_bits must be <= 15
627 TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueTooBig) {
628 CreateAndConnectWithExtensions(
629 "permessage-deflate; client_max_window_bits=16");
630 EXPECT_FALSE(stream_);
631 EXPECT_TRUE(has_failed());
632 EXPECT_EQ(
633 "Error during WebSocket handshake: Error in permessage-deflate: "
634 "Received invalid client_max_window_bits parameter",
635 failure_message());
638 // *_max_window_bits must not start with 0
639 TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueStartsWithZero) {
640 CreateAndConnectWithExtensions(
641 "permessage-deflate; client_max_window_bits=08");
642 EXPECT_FALSE(stream_);
643 EXPECT_TRUE(has_failed());
644 EXPECT_EQ(
645 "Error during WebSocket handshake: Error in permessage-deflate: "
646 "Received invalid client_max_window_bits parameter",
647 failure_message());
650 // *_max_window_bits must not start with +
651 TEST_F(WebSocketStreamCreateExtensionTest, MaxWindowBitsValueStartsWithPlus) {
652 CreateAndConnectWithExtensions(
653 "permessage-deflate; server_max_window_bits=+9");
654 EXPECT_FALSE(stream_);
655 EXPECT_TRUE(has_failed());
656 EXPECT_EQ(
657 "Error during WebSocket handshake: Error in permessage-deflate: "
658 "Received invalid server_max_window_bits parameter",
659 failure_message());
662 // TODO(ricea): Check that WebSocketDeflateStream is initialised with the
663 // arguments from the server. This is difficult because the data written to the
664 // socket is randomly masked.
666 // Additional Sec-WebSocket-Accept headers should be rejected.
667 TEST_F(WebSocketStreamCreateTest, DoubleAccept) {
668 CreateAndConnectStandard(
669 "ws://localhost/",
670 "/",
671 NoSubProtocols(),
672 "http://localhost",
674 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n");
675 RunUntilIdle();
676 EXPECT_FALSE(stream_);
677 EXPECT_TRUE(has_failed());
678 EXPECT_EQ("Error during WebSocket handshake: "
679 "'Sec-WebSocket-Accept' header must not appear "
680 "more than once in a response",
681 failure_message());
684 // Response code 200 must be rejected.
685 TEST_F(WebSocketStreamCreateTest, InvalidStatusCode) {
686 static const char kInvalidStatusCodeResponse[] =
687 "HTTP/1.1 200 OK\r\n"
688 "Upgrade: websocket\r\n"
689 "Connection: Upgrade\r\n"
690 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
691 "\r\n";
692 CreateAndConnectCustomResponse("ws://localhost/",
693 "/",
694 NoSubProtocols(),
695 "http://localhost",
697 kInvalidStatusCodeResponse);
698 RunUntilIdle();
699 EXPECT_TRUE(has_failed());
700 EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 200",
701 failure_message());
704 // Redirects are not followed (according to the WHATWG WebSocket API, which
705 // overrides RFC6455 for browser applications).
706 TEST_F(WebSocketStreamCreateTest, RedirectsRejected) {
707 static const char kRedirectResponse[] =
708 "HTTP/1.1 302 Moved Temporarily\r\n"
709 "Content-Type: text/html\r\n"
710 "Content-Length: 34\r\n"
711 "Connection: keep-alive\r\n"
712 "Location: ws://localhost/other\r\n"
713 "\r\n"
714 "<title>Moved</title><h1>Moved</h1>";
715 CreateAndConnectCustomResponse("ws://localhost/",
716 "/",
717 NoSubProtocols(),
718 "http://localhost",
720 kRedirectResponse);
721 RunUntilIdle();
722 EXPECT_TRUE(has_failed());
723 EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 302",
724 failure_message());
727 // Malformed responses should be rejected. HttpStreamParser will accept just
728 // about any garbage in the middle of the headers. To make it give up, the junk
729 // has to be at the start of the response. Even then, it just gets treated as an
730 // HTTP/0.9 response.
731 TEST_F(WebSocketStreamCreateTest, MalformedResponse) {
732 static const char kMalformedResponse[] =
733 "220 mx.google.com ESMTP\r\n"
734 "HTTP/1.1 101 OK\r\n"
735 "Upgrade: websocket\r\n"
736 "Connection: Upgrade\r\n"
737 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
738 "\r\n";
739 CreateAndConnectCustomResponse("ws://localhost/",
740 "/",
741 NoSubProtocols(),
742 "http://localhost",
744 kMalformedResponse);
745 RunUntilIdle();
746 EXPECT_TRUE(has_failed());
747 EXPECT_EQ("Error during WebSocket handshake: Invalid status line",
748 failure_message());
751 // Upgrade header must be present.
752 TEST_F(WebSocketStreamCreateTest, MissingUpgradeHeader) {
753 static const char kMissingUpgradeResponse[] =
754 "HTTP/1.1 101 Switching Protocols\r\n"
755 "Connection: Upgrade\r\n"
756 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
757 "\r\n";
758 CreateAndConnectCustomResponse("ws://localhost/",
759 "/",
760 NoSubProtocols(),
761 "http://localhost",
763 kMissingUpgradeResponse);
764 RunUntilIdle();
765 EXPECT_TRUE(has_failed());
766 EXPECT_EQ("Error during WebSocket handshake: 'Upgrade' header is missing",
767 failure_message());
770 // There must only be one upgrade header.
771 TEST_F(WebSocketStreamCreateTest, DoubleUpgradeHeader) {
772 CreateAndConnectStandard(
773 "ws://localhost/",
774 "/",
775 NoSubProtocols(),
776 "http://localhost",
777 "", "Upgrade: HTTP/2.0\r\n");
778 RunUntilIdle();
779 EXPECT_TRUE(has_failed());
780 EXPECT_EQ("Error during WebSocket handshake: "
781 "'Upgrade' header must not appear more than once in a response",
782 failure_message());
785 // There must only be one correct upgrade header.
786 TEST_F(WebSocketStreamCreateTest, IncorrectUpgradeHeader) {
787 static const char kMissingUpgradeResponse[] =
788 "HTTP/1.1 101 Switching Protocols\r\n"
789 "Connection: Upgrade\r\n"
790 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
791 "Upgrade: hogefuga\r\n"
792 "\r\n";
793 CreateAndConnectCustomResponse("ws://localhost/",
794 "/",
795 NoSubProtocols(),
796 "http://localhost",
798 kMissingUpgradeResponse);
799 RunUntilIdle();
800 EXPECT_TRUE(has_failed());
801 EXPECT_EQ("Error during WebSocket handshake: "
802 "'Upgrade' header value is not 'WebSocket': hogefuga",
803 failure_message());
806 // Connection header must be present.
807 TEST_F(WebSocketStreamCreateTest, MissingConnectionHeader) {
808 static const char kMissingConnectionResponse[] =
809 "HTTP/1.1 101 Switching Protocols\r\n"
810 "Upgrade: websocket\r\n"
811 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
812 "\r\n";
813 CreateAndConnectCustomResponse("ws://localhost/",
814 "/",
815 NoSubProtocols(),
816 "http://localhost",
818 kMissingConnectionResponse);
819 RunUntilIdle();
820 EXPECT_TRUE(has_failed());
821 EXPECT_EQ("Error during WebSocket handshake: "
822 "'Connection' header is missing",
823 failure_message());
826 // Connection header must contain "Upgrade".
827 TEST_F(WebSocketStreamCreateTest, IncorrectConnectionHeader) {
828 static const char kMissingConnectionResponse[] =
829 "HTTP/1.1 101 Switching Protocols\r\n"
830 "Upgrade: websocket\r\n"
831 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
832 "Connection: hogefuga\r\n"
833 "\r\n";
834 CreateAndConnectCustomResponse("ws://localhost/",
835 "/",
836 NoSubProtocols(),
837 "http://localhost",
839 kMissingConnectionResponse);
840 RunUntilIdle();
841 EXPECT_TRUE(has_failed());
842 EXPECT_EQ("Error during WebSocket handshake: "
843 "'Connection' header value must contain 'Upgrade'",
844 failure_message());
847 // Connection header is permitted to contain other tokens.
848 TEST_F(WebSocketStreamCreateTest, AdditionalTokenInConnectionHeader) {
849 static const char kAdditionalConnectionTokenResponse[] =
850 "HTTP/1.1 101 Switching Protocols\r\n"
851 "Upgrade: websocket\r\n"
852 "Connection: Upgrade, Keep-Alive\r\n"
853 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
854 "\r\n";
855 CreateAndConnectCustomResponse("ws://localhost/",
856 "/",
857 NoSubProtocols(),
858 "http://localhost",
860 kAdditionalConnectionTokenResponse);
861 RunUntilIdle();
862 EXPECT_FALSE(has_failed());
863 EXPECT_TRUE(stream_);
866 // Sec-WebSocket-Accept header must be present.
867 TEST_F(WebSocketStreamCreateTest, MissingSecWebSocketAccept) {
868 static const char kMissingAcceptResponse[] =
869 "HTTP/1.1 101 Switching Protocols\r\n"
870 "Upgrade: websocket\r\n"
871 "Connection: Upgrade\r\n"
872 "\r\n";
873 CreateAndConnectCustomResponse("ws://localhost/",
874 "/",
875 NoSubProtocols(),
876 "http://localhost",
878 kMissingAcceptResponse);
879 RunUntilIdle();
880 EXPECT_TRUE(has_failed());
881 EXPECT_EQ("Error during WebSocket handshake: "
882 "'Sec-WebSocket-Accept' header is missing",
883 failure_message());
886 // Sec-WebSocket-Accept header must match the key that was sent.
887 TEST_F(WebSocketStreamCreateTest, WrongSecWebSocketAccept) {
888 static const char kIncorrectAcceptResponse[] =
889 "HTTP/1.1 101 Switching Protocols\r\n"
890 "Upgrade: websocket\r\n"
891 "Connection: Upgrade\r\n"
892 "Sec-WebSocket-Accept: x/byyPZ2tOFvJCGkkugcKvqhhPk=\r\n"
893 "\r\n";
894 CreateAndConnectCustomResponse("ws://localhost/",
895 "/",
896 NoSubProtocols(),
897 "http://localhost",
899 kIncorrectAcceptResponse);
900 RunUntilIdle();
901 EXPECT_TRUE(has_failed());
902 EXPECT_EQ("Error during WebSocket handshake: "
903 "Incorrect 'Sec-WebSocket-Accept' header value",
904 failure_message());
907 // Cancellation works.
908 TEST_F(WebSocketStreamCreateTest, Cancellation) {
909 CreateAndConnectStandard(
910 "ws://localhost/", "/", NoSubProtocols(), "http://localhost", "", "");
911 stream_request_.reset();
912 RunUntilIdle();
913 EXPECT_FALSE(has_failed());
914 EXPECT_FALSE(stream_);
915 EXPECT_FALSE(request_info_);
916 EXPECT_FALSE(response_info_);
919 // Connect failure must look just like negotiation failure.
920 TEST_F(WebSocketStreamCreateTest, ConnectionFailure) {
921 scoped_ptr<DeterministicSocketData> socket_data(
922 new DeterministicSocketData(NULL, 0, NULL, 0));
923 socket_data->set_connect_data(
924 MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
925 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
926 "http://localhost", socket_data.Pass());
927 RunUntilIdle();
928 EXPECT_TRUE(has_failed());
929 EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_REFUSED",
930 failure_message());
931 EXPECT_FALSE(request_info_);
932 EXPECT_FALSE(response_info_);
935 // Connect timeout must look just like any other failure.
936 TEST_F(WebSocketStreamCreateTest, ConnectionTimeout) {
937 scoped_ptr<DeterministicSocketData> socket_data(
938 new DeterministicSocketData(NULL, 0, NULL, 0));
939 socket_data->set_connect_data(
940 MockConnect(ASYNC, ERR_CONNECTION_TIMED_OUT));
941 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(),
942 "http://localhost", socket_data.Pass());
943 RunUntilIdle();
944 EXPECT_TRUE(has_failed());
945 EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_TIMED_OUT",
946 failure_message());
949 // Cancellation during connect works.
950 TEST_F(WebSocketStreamCreateTest, CancellationDuringConnect) {
951 scoped_ptr<DeterministicSocketData> socket_data(
952 new DeterministicSocketData(NULL, 0, NULL, 0));
953 socket_data->set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
954 CreateAndConnectRawExpectations("ws://localhost/",
955 NoSubProtocols(),
956 "http://localhost",
957 socket_data.Pass());
958 stream_request_.reset();
959 RunUntilIdle();
960 EXPECT_FALSE(has_failed());
961 EXPECT_FALSE(stream_);
964 // Cancellation during write of the request headers works.
965 TEST_F(WebSocketStreamCreateTest, CancellationDuringWrite) {
966 // We seem to need at least two operations in order to use SetStop().
967 MockWrite writes[] = {MockWrite(ASYNC, 0, "GET / HTTP/"),
968 MockWrite(ASYNC, 1, "1.1\r\n")};
969 // We keep a copy of the pointer so that we can call RunFor() on it later.
970 DeterministicSocketData* socket_data(
971 new DeterministicSocketData(NULL, 0, writes, arraysize(writes)));
972 socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
973 socket_data->SetStop(1);
974 CreateAndConnectRawExpectations("ws://localhost/",
975 NoSubProtocols(),
976 "http://localhost",
977 make_scoped_ptr(socket_data));
978 socket_data->Run();
979 stream_request_.reset();
980 RunUntilIdle();
981 EXPECT_FALSE(has_failed());
982 EXPECT_FALSE(stream_);
983 EXPECT_TRUE(request_info_);
984 EXPECT_FALSE(response_info_);
987 // Cancellation during read of the response headers works.
988 TEST_F(WebSocketStreamCreateTest, CancellationDuringRead) {
989 std::string request = WebSocketStandardRequest("/", "http://localhost", "");
990 MockWrite writes[] = {MockWrite(ASYNC, 0, request.c_str())};
991 MockRead reads[] = {
992 MockRead(ASYNC, 1, "HTTP/1.1 101 Switching Protocols\r\nUpgr"),
994 DeterministicSocketData* socket_data(new DeterministicSocketData(
995 reads, arraysize(reads), writes, arraysize(writes)));
996 socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
997 socket_data->SetStop(1);
998 CreateAndConnectRawExpectations("ws://localhost/",
999 NoSubProtocols(),
1000 "http://localhost",
1001 make_scoped_ptr(socket_data));
1002 socket_data->Run();
1003 stream_request_.reset();
1004 RunUntilIdle();
1005 EXPECT_FALSE(has_failed());
1006 EXPECT_FALSE(stream_);
1007 EXPECT_TRUE(request_info_);
1008 EXPECT_FALSE(response_info_);
1011 // Over-size response headers (> 256KB) should not cause a crash. This is a
1012 // regression test for crbug.com/339456. It is based on the layout test
1013 // "cookie-flood.html".
1014 TEST_F(WebSocketStreamCreateTest, VeryLargeResponseHeaders) {
1015 std::string set_cookie_headers;
1016 set_cookie_headers.reserve(45 * 10000);
1017 for (int i = 0; i < 10000; ++i) {
1018 set_cookie_headers +=
1019 base::StringPrintf("Set-Cookie: WK-websocket-test-flood-%d=1\r\n", i);
1021 CreateAndConnectStandard("ws://localhost/", "/", NoSubProtocols(),
1022 "http://localhost", "", set_cookie_headers);
1023 RunUntilIdle();
1024 EXPECT_TRUE(has_failed());
1025 EXPECT_FALSE(response_info_);
1028 // If the remote host closes the connection without sending headers, we should
1029 // log the console message "Connection closed before receiving a handshake
1030 // response".
1031 TEST_F(WebSocketStreamCreateTest, NoResponse) {
1032 std::string request = WebSocketStandardRequest("/", "http://localhost", "");
1033 MockWrite writes[] = {MockWrite(ASYNC, request.data(), request.size(), 0)};
1034 MockRead reads[] = {MockRead(ASYNC, 0, 1)};
1035 DeterministicSocketData* socket_data(new DeterministicSocketData(
1036 reads, arraysize(reads), writes, arraysize(writes)));
1037 socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK));
1038 CreateAndConnectRawExpectations("ws://localhost/",
1039 NoSubProtocols(),
1040 "http://localhost",
1041 make_scoped_ptr(socket_data));
1042 socket_data->RunFor(2);
1043 EXPECT_TRUE(has_failed());
1044 EXPECT_FALSE(stream_);
1045 EXPECT_FALSE(response_info_);
1046 EXPECT_EQ("Connection closed before receiving a handshake response",
1047 failure_message());
1050 TEST_F(WebSocketStreamCreateTest, SelfSignedCertificateFailure) {
1051 ssl_data_.push_back(
1052 new SSLSocketDataProvider(ASYNC, ERR_CERT_AUTHORITY_INVALID));
1053 ssl_data_[0]->cert =
1054 ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
1055 ASSERT_TRUE(ssl_data_[0]->cert);
1056 scoped_ptr<DeterministicSocketData> raw_socket_data(
1057 new DeterministicSocketData(NULL, 0, NULL, 0));
1058 CreateAndConnectRawExpectations("wss://localhost/",
1059 NoSubProtocols(),
1060 "http://localhost",
1061 raw_socket_data.Pass());
1062 RunUntilIdle();
1063 EXPECT_FALSE(has_failed());
1064 ASSERT_TRUE(ssl_error_callbacks_);
1065 ssl_error_callbacks_->CancelSSLRequest(ERR_CERT_AUTHORITY_INVALID,
1066 &ssl_info_);
1067 RunUntilIdle();
1068 EXPECT_TRUE(has_failed());
1071 TEST_F(WebSocketStreamCreateTest, SelfSignedCertificateSuccess) {
1072 scoped_ptr<SSLSocketDataProvider> ssl_data(
1073 new SSLSocketDataProvider(ASYNC, ERR_CERT_AUTHORITY_INVALID));
1074 ssl_data->cert =
1075 ImportCertFromFile(GetTestCertsDirectory(), "unittest.selfsigned.der");
1076 ASSERT_TRUE(ssl_data->cert);
1077 ssl_data_.push_back(ssl_data.release());
1078 ssl_data.reset(new SSLSocketDataProvider(ASYNC, OK));
1079 ssl_data_.push_back(ssl_data.release());
1080 url_request_context_host_.AddRawExpectations(
1081 make_scoped_ptr(new DeterministicSocketData(NULL, 0, NULL, 0)));
1082 CreateAndConnectStandard(
1083 "wss://localhost/", "/", NoSubProtocols(), "http://localhost", "", "");
1084 RunUntilIdle();
1085 ASSERT_TRUE(ssl_error_callbacks_);
1086 ssl_error_callbacks_->ContinueSSLRequest();
1087 RunUntilIdle();
1088 EXPECT_FALSE(has_failed());
1089 EXPECT_TRUE(stream_);
1092 TEST_F(WebSocketStreamCreateUMATest, Incomplete) {
1093 const std::string name("Net.WebSocket.HandshakeResult");
1094 scoped_ptr<base::HistogramSamples> original(GetSamples(name));
1097 StreamCreation creation;
1098 creation.CreateAndConnectStandard("ws://localhost/",
1099 "/",
1100 creation.NoSubProtocols(),
1101 "http://localhost",
1103 "");
1106 scoped_ptr<base::HistogramSamples> samples(GetSamples(name));
1107 ASSERT_TRUE(samples);
1108 if (original) {
1109 samples->Subtract(*original); // Cancel the original values.
1111 EXPECT_EQ(1, samples->GetCount(INCOMPLETE));
1112 EXPECT_EQ(0, samples->GetCount(CONNECTED));
1113 EXPECT_EQ(0, samples->GetCount(FAILED));
1116 TEST_F(WebSocketStreamCreateUMATest, Connected) {
1117 const std::string name("Net.WebSocket.HandshakeResult");
1118 scoped_ptr<base::HistogramSamples> original(GetSamples(name));
1121 StreamCreation creation;
1122 creation.CreateAndConnectStandard("ws://localhost/",
1123 "/",
1124 creation.NoSubProtocols(),
1125 "http://localhost",
1127 "");
1128 creation.RunUntilIdle();
1131 scoped_ptr<base::HistogramSamples> samples(GetSamples(name));
1132 ASSERT_TRUE(samples);
1133 if (original) {
1134 samples->Subtract(*original); // Cancel the original values.
1136 EXPECT_EQ(0, samples->GetCount(INCOMPLETE));
1137 EXPECT_EQ(1, samples->GetCount(CONNECTED));
1138 EXPECT_EQ(0, samples->GetCount(FAILED));
1141 TEST_F(WebSocketStreamCreateUMATest, Failed) {
1142 const std::string name("Net.WebSocket.HandshakeResult");
1143 scoped_ptr<base::HistogramSamples> original(GetSamples(name));
1146 StreamCreation creation;
1147 static const char kInvalidStatusCodeResponse[] =
1148 "HTTP/1.1 200 OK\r\n"
1149 "Upgrade: websocket\r\n"
1150 "Connection: Upgrade\r\n"
1151 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
1152 "\r\n";
1153 creation.CreateAndConnectCustomResponse("ws://localhost/",
1154 "/",
1155 creation.NoSubProtocols(),
1156 "http://localhost",
1158 kInvalidStatusCodeResponse);
1159 creation.RunUntilIdle();
1162 scoped_ptr<base::HistogramSamples> samples(GetSamples(name));
1163 ASSERT_TRUE(samples);
1164 if (original) {
1165 samples->Subtract(*original); // Cancel the original values.
1167 EXPECT_EQ(1, samples->GetCount(INCOMPLETE));
1168 EXPECT_EQ(0, samples->GetCount(CONNECTED));
1169 EXPECT_EQ(0, samples->GetCount(FAILED));
1172 } // namespace
1173 } // namespace net