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