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