OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/websockets/websocket_stream.h" | 5 #include "net/websockets/websocket_stream.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/run_loop.h" | 10 #include "base/run_loop.h" |
(...skipping 27 matching lines...) Expand all Loading... | |
38 // This will break in an obvious way if the type created by | 38 // This will break in an obvious way if the type created by |
39 // CreateBasicStream() changes. | 39 // CreateBasicStream() changes. |
40 static_cast<WebSocketBasicHandshakeStream*>(stream()) | 40 static_cast<WebSocketBasicHandshakeStream*>(stream()) |
41 ->SetWebSocketKeyForTesting("dGhlIHNhbXBsZSBub25jZQ=="); | 41 ->SetWebSocketKeyForTesting("dGhlIHNhbXBsZSBub25jZQ=="); |
42 return stream(); | 42 return stream(); |
43 } | 43 } |
44 }; | 44 }; |
45 | 45 |
46 class WebSocketStreamCreateTest : public ::testing::Test { | 46 class WebSocketStreamCreateTest : public ::testing::Test { |
47 protected: | 47 protected: |
48 WebSocketStreamCreateTest() : websocket_error_(0) {} | 48 WebSocketStreamCreateTest(): has_failed_(false) {} |
49 | 49 |
50 void CreateAndConnectCustomResponse( | 50 void CreateAndConnectCustomResponse( |
51 const std::string& socket_url, | 51 const std::string& socket_url, |
52 const std::string& socket_path, | 52 const std::string& socket_path, |
53 const std::vector<std::string>& sub_protocols, | 53 const std::vector<std::string>& sub_protocols, |
54 const std::string& origin, | 54 const std::string& origin, |
55 const std::string& extra_request_headers, | 55 const std::string& extra_request_headers, |
56 const std::string& response_body) { | 56 const std::string& response_body) { |
57 std::string sub_protocols_header; | |
tyoshino (SeeGerritForStatus)
2013/12/16 07:26:18
what's this?
yhirano
2013/12/16 08:05:38
Deleted
| |
57 url_request_context_host_.SetExpectations( | 58 url_request_context_host_.SetExpectations( |
58 WebSocketStandardRequest(socket_path, origin, extra_request_headers), | 59 WebSocketStandardRequest(socket_path, origin, extra_request_headers), |
59 response_body); | 60 response_body); |
60 CreateAndConnectStream(socket_url, sub_protocols, origin); | 61 CreateAndConnectStream(socket_url, sub_protocols, origin); |
61 } | 62 } |
62 | 63 |
63 // |extra_request_headers| and |extra_response_headers| must end in "\r\n" or | 64 // |extra_request_headers| and |extra_response_headers| must end in "\r\n" or |
64 // errors like "Unable to perform synchronous IO while stopped" will occur. | 65 // errors like "Unable to perform synchronous IO while stopped" will occur. |
65 void CreateAndConnectStandard(const std::string& socket_url, | 66 void CreateAndConnectStandard(const std::string& socket_url, |
66 const std::string& socket_path, | 67 const std::string& socket_path, |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
103 new TestConnectDelegate(this))); | 104 new TestConnectDelegate(this))); |
104 } | 105 } |
105 | 106 |
106 static void RunUntilIdle() { base::RunLoop().RunUntilIdle(); } | 107 static void RunUntilIdle() { base::RunLoop().RunUntilIdle(); } |
107 | 108 |
108 // A simple function to make the tests more readable. Creates an empty vector. | 109 // A simple function to make the tests more readable. Creates an empty vector. |
109 static std::vector<std::string> NoSubProtocols() { | 110 static std::vector<std::string> NoSubProtocols() { |
110 return std::vector<std::string>(); | 111 return std::vector<std::string>(); |
111 } | 112 } |
112 | 113 |
113 uint16 error() const { return websocket_error_; } | 114 const std::string& failure_message() const { return failure_message_; } |
115 bool has_failed() const { return has_failed_; } | |
114 | 116 |
115 class TestConnectDelegate : public WebSocketStream::ConnectDelegate { | 117 class TestConnectDelegate : public WebSocketStream::ConnectDelegate { |
116 public: | 118 public: |
117 TestConnectDelegate(WebSocketStreamCreateTest* owner) : owner_(owner) {} | 119 explicit TestConnectDelegate(WebSocketStreamCreateTest* owner) |
120 : owner_(owner) {} | |
118 | 121 |
119 virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE { | 122 virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE { |
120 stream.swap(owner_->stream_); | 123 stream.swap(owner_->stream_); |
121 } | 124 } |
122 | 125 |
123 virtual void OnFailure(uint16 websocket_error) OVERRIDE { | 126 virtual void OnFailure(const std::string& message) OVERRIDE { |
124 owner_->websocket_error_ = websocket_error; | 127 owner_->has_failed_ = true; |
128 owner_->failure_message_ = message; | |
125 } | 129 } |
126 | 130 |
127 private: | 131 private: |
128 WebSocketStreamCreateTest* owner_; | 132 WebSocketStreamCreateTest* owner_; |
129 }; | 133 }; |
130 | 134 |
131 WebSocketTestURLRequestContextHost url_request_context_host_; | 135 WebSocketTestURLRequestContextHost url_request_context_host_; |
132 scoped_ptr<WebSocketStreamRequest> stream_request_; | 136 scoped_ptr<WebSocketStreamRequest> stream_request_; |
133 // Only set if the connection succeeded. | 137 // Only set if the connection succeeded. |
134 scoped_ptr<WebSocketStream> stream_; | 138 scoped_ptr<WebSocketStream> stream_; |
135 // Only set if the connection failed. 0 otherwise. | 139 // Only set if the connection failed. |
136 uint16 websocket_error_; | 140 std::string failure_message_; |
141 bool has_failed_; | |
137 }; | 142 }; |
138 | 143 |
139 // Confirm that the basic case works as expected. | 144 // Confirm that the basic case works as expected. |
140 TEST_F(WebSocketStreamCreateTest, SimpleSuccess) { | 145 TEST_F(WebSocketStreamCreateTest, SimpleSuccess) { |
141 CreateAndConnectStandard( | 146 CreateAndConnectStandard( |
142 "ws://localhost/", "/", NoSubProtocols(), "http://localhost/", "", ""); | 147 "ws://localhost/", "/", NoSubProtocols(), "http://localhost/", "", ""); |
143 RunUntilIdle(); | 148 RunUntilIdle(); |
149 EXPECT_FALSE(has_failed()); | |
144 EXPECT_TRUE(stream_); | 150 EXPECT_TRUE(stream_); |
145 } | 151 } |
146 | 152 |
147 // Confirm that the stream isn't established until the message loop runs. | 153 // Confirm that the stream isn't established until the message loop runs. |
148 TEST_F(WebSocketStreamCreateTest, NeedsToRunLoop) { | 154 TEST_F(WebSocketStreamCreateTest, NeedsToRunLoop) { |
149 CreateAndConnectStandard( | 155 CreateAndConnectStandard( |
150 "ws://localhost/", "/", NoSubProtocols(), "http://localhost/", "", ""); | 156 "ws://localhost/", "/", NoSubProtocols(), "http://localhost/", "", ""); |
157 EXPECT_FALSE(has_failed()); | |
151 EXPECT_FALSE(stream_); | 158 EXPECT_FALSE(stream_); |
152 } | 159 } |
153 | 160 |
154 // Check the path is used. | 161 // Check the path is used. |
155 TEST_F(WebSocketStreamCreateTest, PathIsUsed) { | 162 TEST_F(WebSocketStreamCreateTest, PathIsUsed) { |
156 CreateAndConnectStandard("ws://localhost/testing_path", | 163 CreateAndConnectStandard("ws://localhost/testing_path", |
157 "/testing_path", | 164 "/testing_path", |
158 NoSubProtocols(), | 165 NoSubProtocols(), |
159 "http://localhost/", | 166 "http://localhost/", |
160 "", | 167 "", |
161 ""); | 168 ""); |
162 RunUntilIdle(); | 169 RunUntilIdle(); |
170 EXPECT_FALSE(has_failed()); | |
163 EXPECT_TRUE(stream_); | 171 EXPECT_TRUE(stream_); |
164 } | 172 } |
165 | 173 |
166 // Check that the origin is used. | 174 // Check that the origin is used. |
167 TEST_F(WebSocketStreamCreateTest, OriginIsUsed) { | 175 TEST_F(WebSocketStreamCreateTest, OriginIsUsed) { |
168 CreateAndConnectStandard("ws://localhost/testing_path", | 176 CreateAndConnectStandard("ws://localhost/testing_path", |
169 "/testing_path", | 177 "/testing_path", |
170 NoSubProtocols(), | 178 NoSubProtocols(), |
171 "http://google.com/", | 179 "http://google.com/", |
172 "", | 180 "", |
173 ""); | 181 ""); |
174 RunUntilIdle(); | 182 RunUntilIdle(); |
183 EXPECT_FALSE(has_failed()); | |
175 EXPECT_TRUE(stream_); | 184 EXPECT_TRUE(stream_); |
176 } | 185 } |
177 | 186 |
178 // Check that sub-protocols are sent and parsed. | 187 // Check that sub-protocols are sent and parsed. |
179 TEST_F(WebSocketStreamCreateTest, SubProtocolIsUsed) { | 188 TEST_F(WebSocketStreamCreateTest, SubProtocolIsUsed) { |
180 std::vector<std::string> sub_protocols; | 189 std::vector<std::string> sub_protocols; |
181 sub_protocols.push_back("chatv11.chromium.org"); | 190 sub_protocols.push_back("chatv11.chromium.org"); |
182 sub_protocols.push_back("chatv20.chromium.org"); | 191 sub_protocols.push_back("chatv20.chromium.org"); |
183 CreateAndConnectStandard("ws://localhost/testing_path", | 192 CreateAndConnectStandard("ws://localhost/testing_path", |
184 "/testing_path", | 193 "/testing_path", |
185 sub_protocols, | 194 sub_protocols, |
186 "http://google.com/", | 195 "http://google.com/", |
187 "Sec-WebSocket-Protocol: chatv11.chromium.org, " | 196 "Sec-WebSocket-Protocol: chatv11.chromium.org, " |
188 "chatv20.chromium.org\r\n", | 197 "chatv20.chromium.org\r\n", |
189 "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n"); | 198 "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n"); |
190 RunUntilIdle(); | 199 RunUntilIdle(); |
191 EXPECT_TRUE(stream_); | 200 EXPECT_TRUE(stream_); |
201 EXPECT_FALSE(has_failed()); | |
192 EXPECT_EQ("chatv20.chromium.org", stream_->GetSubProtocol()); | 202 EXPECT_EQ("chatv20.chromium.org", stream_->GetSubProtocol()); |
193 } | 203 } |
194 | 204 |
195 // Unsolicited sub-protocols are rejected. | 205 // Unsolicited sub-protocols are rejected. |
196 TEST_F(WebSocketStreamCreateTest, UnsolicitedSubProtocol) { | 206 TEST_F(WebSocketStreamCreateTest, UnsolicitedSubProtocol) { |
197 CreateAndConnectStandard("ws://localhost/testing_path", | 207 CreateAndConnectStandard("ws://localhost/testing_path", |
198 "/testing_path", | 208 "/testing_path", |
199 NoSubProtocols(), | 209 NoSubProtocols(), |
200 "http://google.com/", | 210 "http://google.com/", |
201 "", | 211 "", |
202 "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n"); | 212 "Sec-WebSocket-Protocol: chatv20.chromium.org\r\n"); |
203 RunUntilIdle(); | 213 RunUntilIdle(); |
204 EXPECT_FALSE(stream_); | 214 EXPECT_FALSE(stream_); |
205 EXPECT_EQ(1006, error()); | 215 EXPECT_TRUE(has_failed()); |
216 EXPECT_EQ("Error during WebSocket handshake: " | |
217 "Response must not include 'Sec-WebSocket-Protocol' header " | |
218 "if not present in request: chatv20.chromium.org", | |
219 failure_message()); | |
206 } | 220 } |
207 | 221 |
208 // Missing sub-protocol response is rejected. | 222 // Missing sub-protocol response is rejected. |
209 TEST_F(WebSocketStreamCreateTest, UnacceptedSubProtocol) { | 223 TEST_F(WebSocketStreamCreateTest, UnacceptedSubProtocol) { |
224 std::vector<std::string> sub_protocols; | |
225 sub_protocols.push_back("chat.example.com"); | |
210 CreateAndConnectStandard("ws://localhost/testing_path", | 226 CreateAndConnectStandard("ws://localhost/testing_path", |
211 "/testing_path", | 227 "/testing_path", |
212 std::vector<std::string>(1, "chat.example.com"), | 228 sub_protocols, |
213 "http://localhost/", | 229 "http://localhost/", |
214 "Sec-WebSocket-Protocol: chat.example.com\r\n", | 230 "Sec-WebSocket-Protocol: chat.example.com\r\n", |
215 ""); | 231 ""); |
216 RunUntilIdle(); | 232 RunUntilIdle(); |
217 EXPECT_FALSE(stream_); | 233 EXPECT_FALSE(stream_); |
218 EXPECT_EQ(1006, error()); | 234 EXPECT_TRUE(has_failed()); |
235 EXPECT_EQ("Error during WebSocket handshake: " | |
236 "Sent non-empty 'Sec-WebSocket-Protocol' header " | |
237 "but no response was received", | |
238 failure_message()); | |
219 } | 239 } |
220 | 240 |
221 // Only one sub-protocol can be accepted. | 241 // Only one sub-protocol can be accepted. |
222 TEST_F(WebSocketStreamCreateTest, MultipleSubProtocolsInResponse) { | 242 TEST_F(WebSocketStreamCreateTest, MultipleSubProtocolsInResponse) { |
223 std::vector<std::string> sub_protocols; | 243 std::vector<std::string> sub_protocols; |
224 sub_protocols.push_back("chatv11.chromium.org"); | 244 sub_protocols.push_back("chatv11.chromium.org"); |
225 sub_protocols.push_back("chatv20.chromium.org"); | 245 sub_protocols.push_back("chatv20.chromium.org"); |
226 CreateAndConnectStandard("ws://localhost/testing_path", | 246 CreateAndConnectStandard("ws://localhost/testing_path", |
227 "/testing_path", | 247 "/testing_path", |
228 sub_protocols, | 248 sub_protocols, |
229 "http://google.com/", | 249 "http://google.com/", |
230 "Sec-WebSocket-Protocol: chatv11.chromium.org, " | 250 "Sec-WebSocket-Protocol: chatv11.chromium.org, " |
231 "chatv20.chromium.org\r\n", | 251 "chatv20.chromium.org\r\n", |
232 "Sec-WebSocket-Protocol: chatv11.chromium.org, " | 252 "Sec-WebSocket-Protocol: chatv11.chromium.org, " |
233 "chatv20.chromium.org\r\n"); | 253 "chatv20.chromium.org\r\n"); |
234 RunUntilIdle(); | 254 RunUntilIdle(); |
235 EXPECT_FALSE(stream_); | 255 EXPECT_FALSE(stream_); |
236 EXPECT_EQ(1006, error()); | 256 EXPECT_TRUE(has_failed()); |
257 EXPECT_EQ("Error during WebSocket handshake: " | |
258 "'Sec-WebSocket-Protocol' header must not appear " | |
259 "more than once in a response", | |
260 failure_message()); | |
261 } | |
262 | |
263 // Unmatched sub-protocol should be rejected. | |
264 TEST_F(WebSocketStreamCreateTest, UnmatchedSubProtocolInResponse) { | |
265 std::vector<std::string> sub_protocols; | |
266 sub_protocols.push_back("chatv11.chromium.org"); | |
267 sub_protocols.push_back("chatv20.chromium.org"); | |
268 CreateAndConnectStandard("ws://localhost/testing_path", | |
269 "/testing_path", | |
270 sub_protocols, | |
271 "http://google.com/", | |
272 "Sec-WebSocket-Protocol: chatv11.chromium.org, " | |
273 "chatv20.chromium.org\r\n", | |
274 "Sec-WebSocket-Protocol: chatv21.chromium.org\r\n"); | |
275 RunUntilIdle(); | |
276 EXPECT_FALSE(stream_); | |
277 EXPECT_TRUE(has_failed()); | |
278 EXPECT_EQ("Error during WebSocket handshake: " | |
279 "'Sec-WebSocket-Protocol' header value 'chatv21.chromium.org' " | |
280 "in response does not match any of sent values", | |
281 failure_message()); | |
237 } | 282 } |
238 | 283 |
239 // Unknown extension in the response is rejected | 284 // Unknown extension in the response is rejected |
240 TEST_F(WebSocketStreamCreateTest, UnknownExtension) { | 285 TEST_F(WebSocketStreamCreateTest, UnknownExtension) { |
241 CreateAndConnectStandard("ws://localhost/testing_path", | 286 CreateAndConnectStandard("ws://localhost/testing_path", |
242 "/testing_path", | 287 "/testing_path", |
243 NoSubProtocols(), | 288 NoSubProtocols(), |
244 "http://localhost/", | 289 "http://localhost/", |
245 "", | 290 "", |
246 "Sec-WebSocket-Extensions: x-unknown-extension\r\n"); | 291 "Sec-WebSocket-Extensions: x-unknown-extension\r\n"); |
247 RunUntilIdle(); | 292 RunUntilIdle(); |
248 EXPECT_FALSE(stream_); | 293 EXPECT_FALSE(stream_); |
249 EXPECT_EQ(1006, error()); | 294 EXPECT_TRUE(has_failed()); |
295 EXPECT_EQ("Error during WebSocket handshake: " | |
296 "Found an unsupported extension 'x-unknown-extension' " | |
297 "in 'Sec-WebSocket-Extensions' header", | |
298 failure_message()); | |
250 } | 299 } |
251 | 300 |
252 // Additional Sec-WebSocket-Accept headers should be rejected. | 301 // Additional Sec-WebSocket-Accept headers should be rejected. |
253 TEST_F(WebSocketStreamCreateTest, DoubleAccept) { | 302 TEST_F(WebSocketStreamCreateTest, DoubleAccept) { |
254 CreateAndConnectStandard( | 303 CreateAndConnectStandard( |
255 "ws://localhost/", | 304 "ws://localhost/", |
256 "/", | 305 "/", |
257 NoSubProtocols(), | 306 NoSubProtocols(), |
258 "http://localhost/", | 307 "http://localhost/", |
259 "", | 308 "", |
260 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"); | 309 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"); |
261 RunUntilIdle(); | 310 RunUntilIdle(); |
262 EXPECT_FALSE(stream_); | 311 EXPECT_FALSE(stream_); |
263 EXPECT_EQ(1006, error()); | 312 EXPECT_TRUE(has_failed()); |
313 EXPECT_EQ("Error during WebSocket handshake: " | |
314 "'Sec-WebSocket-Accept' header must not appear " | |
315 "more than once in a response", | |
316 failure_message()); | |
264 } | 317 } |
265 | 318 |
266 // Response code 200 must be rejected. | 319 // Response code 200 must be rejected. |
267 TEST_F(WebSocketStreamCreateTest, InvalidStatusCode) { | 320 TEST_F(WebSocketStreamCreateTest, InvalidStatusCode) { |
268 static const char kInvalidStatusCodeResponse[] = | 321 static const char kInvalidStatusCodeResponse[] = |
269 "HTTP/1.1 200 OK\r\n" | 322 "HTTP/1.1 200 OK\r\n" |
270 "Upgrade: websocket\r\n" | 323 "Upgrade: websocket\r\n" |
271 "Connection: Upgrade\r\n" | 324 "Connection: Upgrade\r\n" |
272 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" | 325 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" |
273 "\r\n"; | 326 "\r\n"; |
274 CreateAndConnectCustomResponse("ws://localhost/", | 327 CreateAndConnectCustomResponse("ws://localhost/", |
275 "/", | 328 "/", |
276 NoSubProtocols(), | 329 NoSubProtocols(), |
277 "http://localhost/", | 330 "http://localhost/", |
278 "", | 331 "", |
279 kInvalidStatusCodeResponse); | 332 kInvalidStatusCodeResponse); |
280 RunUntilIdle(); | 333 RunUntilIdle(); |
281 EXPECT_EQ(1006, error()); | 334 EXPECT_TRUE(has_failed()); |
335 EXPECT_EQ("Unexpected status code: 200", failure_message()); | |
282 } | 336 } |
283 | 337 |
284 // Redirects are not followed (according to the WHATWG WebSocket API, which | 338 // Redirects are not followed (according to the WHATWG WebSocket API, which |
285 // overrides RFC6455 for browser applications). | 339 // overrides RFC6455 for browser applications). |
286 TEST_F(WebSocketStreamCreateTest, RedirectsRejected) { | 340 TEST_F(WebSocketStreamCreateTest, RedirectsRejected) { |
287 static const char kRedirectResponse[] = | 341 static const char kRedirectResponse[] = |
288 "HTTP/1.1 302 Moved Temporarily\r\n" | 342 "HTTP/1.1 302 Moved Temporarily\r\n" |
289 "Content-Type: text/html\r\n" | 343 "Content-Type: text/html\r\n" |
290 "Content-Length: 34\r\n" | 344 "Content-Length: 34\r\n" |
291 "Connection: keep-alive\r\n" | 345 "Connection: keep-alive\r\n" |
292 "Location: ws://localhost/other\r\n" | 346 "Location: ws://localhost/other\r\n" |
293 "\r\n" | 347 "\r\n" |
294 "<title>Moved</title><h1>Moved</h1>"; | 348 "<title>Moved</title><h1>Moved</h1>"; |
295 CreateAndConnectCustomResponse("ws://localhost/", | 349 CreateAndConnectCustomResponse("ws://localhost/", |
296 "/", | 350 "/", |
297 NoSubProtocols(), | 351 NoSubProtocols(), |
298 "http://localhost/", | 352 "http://localhost/", |
299 "", | 353 "", |
300 kRedirectResponse); | 354 kRedirectResponse); |
301 RunUntilIdle(); | 355 RunUntilIdle(); |
302 EXPECT_EQ(1006, error()); | 356 EXPECT_TRUE(has_failed()); |
357 EXPECT_EQ("Unexpected status code: 302", failure_message()); | |
303 } | 358 } |
304 | 359 |
305 // Malformed responses should be rejected. HttpStreamParser will accept just | 360 // Malformed responses should be rejected. HttpStreamParser will accept just |
306 // about any garbage in the middle of the headers. To make it give up, the junk | 361 // about any garbage in the middle of the headers. To make it give up, the junk |
307 // has to be at the start of the response. Even then, it just gets treated as an | 362 // has to be at the start of the response. Even then, it just gets treated as an |
308 // HTTP/0.9 response. | 363 // HTTP/0.9 response. |
309 TEST_F(WebSocketStreamCreateTest, MalformedResponse) { | 364 TEST_F(WebSocketStreamCreateTest, MalformedResponse) { |
310 static const char kMalformedResponse[] = | 365 static const char kMalformedResponse[] = |
311 "220 mx.google.com ESMTP\r\n" | 366 "220 mx.google.com ESMTP\r\n" |
312 "HTTP/1.1 101 OK\r\n" | 367 "HTTP/1.1 101 OK\r\n" |
313 "Upgrade: websocket\r\n" | 368 "Upgrade: websocket\r\n" |
314 "Connection: Upgrade\r\n" | 369 "Connection: Upgrade\r\n" |
315 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" | 370 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" |
316 "\r\n"; | 371 "\r\n"; |
317 CreateAndConnectCustomResponse("ws://localhost/", | 372 CreateAndConnectCustomResponse("ws://localhost/", |
318 "/", | 373 "/", |
319 NoSubProtocols(), | 374 NoSubProtocols(), |
320 "http://localhost/", | 375 "http://localhost/", |
321 "", | 376 "", |
322 kMalformedResponse); | 377 kMalformedResponse); |
323 RunUntilIdle(); | 378 RunUntilIdle(); |
324 EXPECT_EQ(1006, error()); | 379 EXPECT_TRUE(has_failed()); |
380 EXPECT_EQ("Unexpected status code: 200", failure_message()); | |
325 } | 381 } |
326 | 382 |
327 // Upgrade header must be present. | 383 // Upgrade header must be present. |
328 TEST_F(WebSocketStreamCreateTest, MissingUpgradeHeader) { | 384 TEST_F(WebSocketStreamCreateTest, MissingUpgradeHeader) { |
329 static const char kMissingUpgradeResponse[] = | 385 static const char kMissingUpgradeResponse[] = |
330 "HTTP/1.1 101 Switching Protocols\r\n" | 386 "HTTP/1.1 101 Switching Protocols\r\n" |
331 "Connection: Upgrade\r\n" | 387 "Connection: Upgrade\r\n" |
332 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" | 388 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" |
333 "\r\n"; | 389 "\r\n"; |
334 CreateAndConnectCustomResponse("ws://localhost/", | 390 CreateAndConnectCustomResponse("ws://localhost/", |
335 "/", | 391 "/", |
336 NoSubProtocols(), | 392 NoSubProtocols(), |
337 "http://localhost/", | 393 "http://localhost/", |
338 "", | 394 "", |
339 kMissingUpgradeResponse); | 395 kMissingUpgradeResponse); |
340 RunUntilIdle(); | 396 RunUntilIdle(); |
341 EXPECT_EQ(1006, error()); | 397 EXPECT_TRUE(has_failed()); |
398 EXPECT_EQ("Error during WebSocket handshake: 'Upgrade' header is missing", | |
399 failure_message()); | |
342 } | 400 } |
343 | 401 |
344 // There must only be one upgrade header. | 402 // There must only be one upgrade header. |
345 TEST_F(WebSocketStreamCreateTest, DoubleUpgradeHeader) { | 403 TEST_F(WebSocketStreamCreateTest, DoubleUpgradeHeader) { |
346 CreateAndConnectStandard( | 404 CreateAndConnectStandard( |
347 "ws://localhost/", | 405 "ws://localhost/", |
348 "/", | 406 "/", |
349 NoSubProtocols(), | 407 NoSubProtocols(), |
350 "http://localhost/", | 408 "http://localhost/", |
351 "", "Upgrade: HTTP/2.0\r\n"); | 409 "", "Upgrade: HTTP/2.0\r\n"); |
352 RunUntilIdle(); | 410 RunUntilIdle(); |
353 EXPECT_EQ(1006, error()); | 411 EXPECT_TRUE(has_failed()); |
412 EXPECT_EQ("Error during WebSocket handshake: " | |
413 "'Upgrade' header must not appear more than once in a response", | |
414 failure_message()); | |
415 } | |
416 | |
417 // There must only be one correct upgrade header. | |
418 TEST_F(WebSocketStreamCreateTest, IncorrectUpgradeHeader) { | |
419 static const char kMissingUpgradeResponse[] = | |
420 "HTTP/1.1 101 Switching Protocols\r\n" | |
421 "Connection: Upgrade\r\n" | |
422 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" | |
423 "Upgrade: hogefuga\r\n" | |
424 "\r\n"; | |
425 CreateAndConnectCustomResponse("ws://localhost/", | |
426 "/", | |
427 NoSubProtocols(), | |
428 "http://localhost/", | |
429 "", | |
430 kMissingUpgradeResponse); | |
431 RunUntilIdle(); | |
432 EXPECT_TRUE(has_failed()); | |
433 EXPECT_EQ("Error during WebSocket handshake: " | |
434 "'Upgrade' header value is not 'WebSocket': hogefuga", | |
435 failure_message()); | |
354 } | 436 } |
355 | 437 |
356 // Connection header must be present. | 438 // Connection header must be present. |
357 TEST_F(WebSocketStreamCreateTest, MissingConnectionHeader) { | 439 TEST_F(WebSocketStreamCreateTest, MissingConnectionHeader) { |
358 static const char kMissingConnectionResponse[] = | 440 static const char kMissingConnectionResponse[] = |
359 "HTTP/1.1 101 Switching Protocols\r\n" | 441 "HTTP/1.1 101 Switching Protocols\r\n" |
360 "Upgrade: websocket\r\n" | 442 "Upgrade: websocket\r\n" |
361 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" | 443 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" |
362 "\r\n"; | 444 "\r\n"; |
363 CreateAndConnectCustomResponse("ws://localhost/", | 445 CreateAndConnectCustomResponse("ws://localhost/", |
364 "/", | 446 "/", |
365 NoSubProtocols(), | 447 NoSubProtocols(), |
366 "http://localhost/", | 448 "http://localhost/", |
367 "", | 449 "", |
368 kMissingConnectionResponse); | 450 kMissingConnectionResponse); |
369 RunUntilIdle(); | 451 RunUntilIdle(); |
370 EXPECT_EQ(1006, error()); | 452 EXPECT_TRUE(has_failed()); |
453 EXPECT_EQ("Error during WebSocket handshake: " | |
454 "'Connection' header is missing", | |
455 failure_message()); | |
456 } | |
457 | |
458 // Connection header must contain "Upgrade". | |
459 TEST_F(WebSocketStreamCreateTest, IncorrectConnectionHeader) { | |
460 static const char kMissingConnectionResponse[] = | |
461 "HTTP/1.1 101 Switching Protocols\r\n" | |
462 "Upgrade: websocket\r\n" | |
463 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" | |
464 "Connection: hogefuga\r\n" | |
465 "\r\n"; | |
466 CreateAndConnectCustomResponse("ws://localhost/", | |
467 "/", | |
468 NoSubProtocols(), | |
469 "http://localhost/", | |
470 "", | |
471 kMissingConnectionResponse); | |
472 RunUntilIdle(); | |
473 EXPECT_TRUE(has_failed()); | |
474 EXPECT_EQ("Error during WebSocket handshake: " | |
475 "'Connection' header value must contain 'Upgrade'", | |
476 failure_message()); | |
371 } | 477 } |
372 | 478 |
373 // Connection header is permitted to contain other tokens. | 479 // Connection header is permitted to contain other tokens. |
374 TEST_F(WebSocketStreamCreateTest, AdditionalTokenInConnectionHeader) { | 480 TEST_F(WebSocketStreamCreateTest, AdditionalTokenInConnectionHeader) { |
375 static const char kAdditionalConnectionTokenResponse[] = | 481 static const char kAdditionalConnectionTokenResponse[] = |
376 "HTTP/1.1 101 Switching Protocols\r\n" | 482 "HTTP/1.1 101 Switching Protocols\r\n" |
377 "Upgrade: websocket\r\n" | 483 "Upgrade: websocket\r\n" |
378 "Connection: Upgrade, Keep-Alive\r\n" | 484 "Connection: Upgrade, Keep-Alive\r\n" |
379 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" | 485 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" |
380 "\r\n"; | 486 "\r\n"; |
381 CreateAndConnectCustomResponse("ws://localhost/", | 487 CreateAndConnectCustomResponse("ws://localhost/", |
382 "/", | 488 "/", |
383 NoSubProtocols(), | 489 NoSubProtocols(), |
384 "http://localhost/", | 490 "http://localhost/", |
385 "", | 491 "", |
386 kAdditionalConnectionTokenResponse); | 492 kAdditionalConnectionTokenResponse); |
387 RunUntilIdle(); | 493 RunUntilIdle(); |
494 EXPECT_FALSE(has_failed()); | |
388 EXPECT_TRUE(stream_); | 495 EXPECT_TRUE(stream_); |
389 } | 496 } |
390 | 497 |
391 // Sec-WebSocket-Accept header must be present. | 498 // Sec-WebSocket-Accept header must be present. |
392 TEST_F(WebSocketStreamCreateTest, MissingSecWebSocketAccept) { | 499 TEST_F(WebSocketStreamCreateTest, MissingSecWebSocketAccept) { |
393 static const char kMissingAcceptResponse[] = | 500 static const char kMissingAcceptResponse[] = |
394 "HTTP/1.1 101 Switching Protocols\r\n" | 501 "HTTP/1.1 101 Switching Protocols\r\n" |
395 "Upgrade: websocket\r\n" | 502 "Upgrade: websocket\r\n" |
396 "Connection: Upgrade\r\n" | 503 "Connection: Upgrade\r\n" |
397 "\r\n"; | 504 "\r\n"; |
398 CreateAndConnectCustomResponse("ws://localhost/", | 505 CreateAndConnectCustomResponse("ws://localhost/", |
399 "/", | 506 "/", |
400 NoSubProtocols(), | 507 NoSubProtocols(), |
401 "http://localhost/", | 508 "http://localhost/", |
402 "", | 509 "", |
403 kMissingAcceptResponse); | 510 kMissingAcceptResponse); |
404 RunUntilIdle(); | 511 RunUntilIdle(); |
405 EXPECT_EQ(1006, error()); | 512 EXPECT_TRUE(has_failed()); |
513 EXPECT_EQ("Error during WebSocket handshake: " | |
514 "'Sec-WebSocket-Accept' header is missing", | |
515 failure_message()); | |
406 } | 516 } |
407 | 517 |
408 // Sec-WebSocket-Accept header must match the key that was sent. | 518 // Sec-WebSocket-Accept header must match the key that was sent. |
409 TEST_F(WebSocketStreamCreateTest, WrongSecWebSocketAccept) { | 519 TEST_F(WebSocketStreamCreateTest, WrongSecWebSocketAccept) { |
410 static const char kIncorrectAcceptResponse[] = | 520 static const char kIncorrectAcceptResponse[] = |
411 "HTTP/1.1 101 Switching Protocols\r\n" | 521 "HTTP/1.1 101 Switching Protocols\r\n" |
412 "Upgrade: websocket\r\n" | 522 "Upgrade: websocket\r\n" |
413 "Connection: Upgrade\r\n" | 523 "Connection: Upgrade\r\n" |
414 "Sec-WebSocket-Accept: x/byyPZ2tOFvJCGkkugcKvqhhPk=\r\n" | 524 "Sec-WebSocket-Accept: x/byyPZ2tOFvJCGkkugcKvqhhPk=\r\n" |
415 "\r\n"; | 525 "\r\n"; |
416 CreateAndConnectCustomResponse("ws://localhost/", | 526 CreateAndConnectCustomResponse("ws://localhost/", |
417 "/", | 527 "/", |
418 NoSubProtocols(), | 528 NoSubProtocols(), |
419 "http://localhost/", | 529 "http://localhost/", |
420 "", | 530 "", |
421 kIncorrectAcceptResponse); | 531 kIncorrectAcceptResponse); |
422 RunUntilIdle(); | 532 RunUntilIdle(); |
423 EXPECT_EQ(1006, error()); | 533 EXPECT_TRUE(has_failed()); |
534 EXPECT_EQ("Error during WebSocket handshake: " | |
535 "Incorrect 'Sec-WebSocket-Accept' header value", | |
536 failure_message()); | |
424 } | 537 } |
425 | 538 |
426 // Cancellation works. | 539 // Cancellation works. |
427 TEST_F(WebSocketStreamCreateTest, Cancellation) { | 540 TEST_F(WebSocketStreamCreateTest, Cancellation) { |
428 CreateAndConnectStandard( | 541 CreateAndConnectStandard( |
429 "ws://localhost/", "/", NoSubProtocols(), "http://localhost/", "", ""); | 542 "ws://localhost/", "/", NoSubProtocols(), "http://localhost/", "", ""); |
430 stream_request_.reset(); | 543 stream_request_.reset(); |
431 RunUntilIdle(); | 544 RunUntilIdle(); |
545 EXPECT_FALSE(has_failed()); | |
432 EXPECT_FALSE(stream_); | 546 EXPECT_FALSE(stream_); |
433 } | 547 } |
434 | 548 |
435 // Connect failure must look just like negotiation failure. | 549 // Connect failure must look just like negotiation failure. |
436 TEST_F(WebSocketStreamCreateTest, ConnectionFailure) { | 550 TEST_F(WebSocketStreamCreateTest, ConnectionFailure) { |
437 scoped_ptr<DeterministicSocketData> socket_data( | 551 scoped_ptr<DeterministicSocketData> socket_data( |
438 new DeterministicSocketData(NULL, 0, NULL, 0)); | 552 new DeterministicSocketData(NULL, 0, NULL, 0)); |
439 socket_data->set_connect_data( | 553 socket_data->set_connect_data( |
440 MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED)); | 554 MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED)); |
441 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(), | 555 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(), |
442 "http://localhost/", socket_data.Pass()); | 556 "http://localhost/", socket_data.Pass()); |
443 RunUntilIdle(); | 557 RunUntilIdle(); |
444 EXPECT_EQ(1006, error()); | 558 EXPECT_TRUE(has_failed()); |
559 EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_REFUSED", | |
560 failure_message()); | |
445 } | 561 } |
446 | 562 |
447 // Connect timeout must look just like any other failure. | 563 // Connect timeout must look just like any other failure. |
448 TEST_F(WebSocketStreamCreateTest, ConnectionTimeout) { | 564 TEST_F(WebSocketStreamCreateTest, ConnectionTimeout) { |
449 scoped_ptr<DeterministicSocketData> socket_data( | 565 scoped_ptr<DeterministicSocketData> socket_data( |
450 new DeterministicSocketData(NULL, 0, NULL, 0)); | 566 new DeterministicSocketData(NULL, 0, NULL, 0)); |
451 socket_data->set_connect_data( | 567 socket_data->set_connect_data( |
452 MockConnect(ASYNC, ERR_CONNECTION_TIMED_OUT)); | 568 MockConnect(ASYNC, ERR_CONNECTION_TIMED_OUT)); |
453 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(), | 569 CreateAndConnectRawExpectations("ws://localhost/", NoSubProtocols(), |
454 "http://localhost/", socket_data.Pass()); | 570 "http://localhost/", socket_data.Pass()); |
455 RunUntilIdle(); | 571 RunUntilIdle(); |
456 EXPECT_EQ(1006, error()); | 572 EXPECT_TRUE(has_failed()); |
573 EXPECT_EQ("Error in connection establishment: net::ERR_CONNECTION_TIMED_OUT", | |
574 failure_message()); | |
457 } | 575 } |
458 | 576 |
459 // Cancellation during connect works. | 577 // Cancellation during connect works. |
460 TEST_F(WebSocketStreamCreateTest, CancellationDuringConnect) { | 578 TEST_F(WebSocketStreamCreateTest, CancellationDuringConnect) { |
461 scoped_ptr<DeterministicSocketData> socket_data( | 579 scoped_ptr<DeterministicSocketData> socket_data( |
462 new DeterministicSocketData(NULL, 0, NULL, 0)); | 580 new DeterministicSocketData(NULL, 0, NULL, 0)); |
463 socket_data->set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING)); | 581 socket_data->set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING)); |
464 CreateAndConnectRawExpectations("ws://localhost/", | 582 CreateAndConnectRawExpectations("ws://localhost/", |
465 NoSubProtocols(), | 583 NoSubProtocols(), |
466 "http://localhost/", | 584 "http://localhost/", |
467 socket_data.Pass()); | 585 socket_data.Pass()); |
468 stream_request_.reset(); | 586 stream_request_.reset(); |
469 RunUntilIdle(); | 587 RunUntilIdle(); |
588 EXPECT_FALSE(has_failed()); | |
470 EXPECT_FALSE(stream_); | 589 EXPECT_FALSE(stream_); |
471 } | 590 } |
472 | 591 |
473 // Cancellation during write of the request headers works. | 592 // Cancellation during write of the request headers works. |
474 TEST_F(WebSocketStreamCreateTest, CancellationDuringWrite) { | 593 TEST_F(WebSocketStreamCreateTest, CancellationDuringWrite) { |
475 // We seem to need at least two operations in order to use SetStop(). | 594 // We seem to need at least two operations in order to use SetStop(). |
476 MockWrite writes[] = {MockWrite(ASYNC, 0, "GET / HTTP/"), | 595 MockWrite writes[] = {MockWrite(ASYNC, 0, "GET / HTTP/"), |
477 MockWrite(ASYNC, 1, "1.1\r\n")}; | 596 MockWrite(ASYNC, 1, "1.1\r\n")}; |
478 // We keep a copy of the pointer so that we can call RunFor() on it later. | 597 // We keep a copy of the pointer so that we can call RunFor() on it later. |
479 DeterministicSocketData* socket_data( | 598 DeterministicSocketData* socket_data( |
480 new DeterministicSocketData(NULL, 0, writes, arraysize(writes))); | 599 new DeterministicSocketData(NULL, 0, writes, arraysize(writes))); |
481 socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK)); | 600 socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK)); |
482 socket_data->SetStop(1); | 601 socket_data->SetStop(1); |
483 CreateAndConnectRawExpectations("ws://localhost/", | 602 CreateAndConnectRawExpectations("ws://localhost/", |
484 NoSubProtocols(), | 603 NoSubProtocols(), |
485 "http://localhost/", | 604 "http://localhost/", |
486 make_scoped_ptr(socket_data)); | 605 make_scoped_ptr(socket_data)); |
487 socket_data->Run(); | 606 socket_data->Run(); |
488 stream_request_.reset(); | 607 stream_request_.reset(); |
489 RunUntilIdle(); | 608 RunUntilIdle(); |
609 EXPECT_FALSE(has_failed()); | |
490 EXPECT_FALSE(stream_); | 610 EXPECT_FALSE(stream_); |
491 } | 611 } |
492 | 612 |
493 // Cancellation during read of the response headers works. | 613 // Cancellation during read of the response headers works. |
494 TEST_F(WebSocketStreamCreateTest, CancellationDuringRead) { | 614 TEST_F(WebSocketStreamCreateTest, CancellationDuringRead) { |
495 std::string request = WebSocketStandardRequest("/", "http://localhost/", ""); | 615 std::string request = WebSocketStandardRequest("/", "http://localhost/", ""); |
496 MockWrite writes[] = {MockWrite(ASYNC, 0, request.c_str())}; | 616 MockWrite writes[] = {MockWrite(ASYNC, 0, request.c_str())}; |
497 MockRead reads[] = { | 617 MockRead reads[] = { |
498 MockRead(ASYNC, 1, "HTTP/1.1 101 Switching Protocols\r\nUpgr"), | 618 MockRead(ASYNC, 1, "HTTP/1.1 101 Switching Protocols\r\nUpgr"), |
499 }; | 619 }; |
500 DeterministicSocketData* socket_data(new DeterministicSocketData( | 620 DeterministicSocketData* socket_data(new DeterministicSocketData( |
501 reads, arraysize(reads), writes, arraysize(writes))); | 621 reads, arraysize(reads), writes, arraysize(writes))); |
502 socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK)); | 622 socket_data->set_connect_data(MockConnect(SYNCHRONOUS, OK)); |
503 socket_data->SetStop(1); | 623 socket_data->SetStop(1); |
504 CreateAndConnectRawExpectations("ws://localhost/", | 624 CreateAndConnectRawExpectations("ws://localhost/", |
505 NoSubProtocols(), | 625 NoSubProtocols(), |
506 "http://localhost/", | 626 "http://localhost/", |
507 make_scoped_ptr(socket_data)); | 627 make_scoped_ptr(socket_data)); |
508 socket_data->Run(); | 628 socket_data->Run(); |
509 stream_request_.reset(); | 629 stream_request_.reset(); |
510 RunUntilIdle(); | 630 RunUntilIdle(); |
631 EXPECT_FALSE(has_failed()); | |
511 EXPECT_FALSE(stream_); | 632 EXPECT_FALSE(stream_); |
512 } | 633 } |
513 | 634 |
514 } // namespace | 635 } // namespace |
515 } // namespace net | 636 } // namespace net |
OLD | NEW |