1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <string>
6 #include <vector>
7
8 #include "base/basictypes.h"
9 #include "base/string_util.h"
10 #include "googleurl/src/gurl.h"
11 #include "net/http/http_response_headers.h"
12 #include "net/http/http_util.h"
13 #include "net/websockets/websocket_handshake_handler.h"
14
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 #include "testing/platform_test.h"
18
19 namespace {
20
21 const char* const kCookieHeaders[] = {
22 "cookie", "cookie2"
23 };
24
25 const char* const kSetCookieHeaders[] = {
26 "set-cookie", "set-cookie2"
27 };
28
29 }
30
31 namespace net {
32
TEST(WebSocketHandshakeRequestHandlerTest,SimpleRequest)33 TEST(WebSocketHandshakeRequestHandlerTest, SimpleRequest) {
34 WebSocketHandshakeRequestHandler handler;
35
36 static const char* kHandshakeRequestMessage =
37 "GET /demo HTTP/1.1\r\n"
38 "Host: example.com\r\n"
39 "Connection: Upgrade\r\n"
40 "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n"
41 "Sec-WebSocket-Protocol: sample\r\n"
42 "Upgrade: WebSocket\r\n"
43 "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
44 "Origin: http://example.com\r\n"
45 "\r\n"
46 "^n:ds[4U";
47
48 EXPECT_TRUE(handler.ParseRequest(kHandshakeRequestMessage,
49 strlen(kHandshakeRequestMessage)));
50
51 handler.RemoveHeaders(kCookieHeaders, arraysize(kCookieHeaders));
52
53 EXPECT_EQ(kHandshakeRequestMessage, handler.GetRawRequest());
54 }
55
TEST(WebSocketHandshakeRequestHandlerTest,ReplaceRequestCookies)56 TEST(WebSocketHandshakeRequestHandlerTest, ReplaceRequestCookies) {
57 WebSocketHandshakeRequestHandler handler;
58
59 static const char* kHandshakeRequestMessage =
60 "GET /demo HTTP/1.1\r\n"
61 "Host: example.com\r\n"
62 "Connection: Upgrade\r\n"
63 "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n"
64 "Sec-WebSocket-Protocol: sample\r\n"
65 "Upgrade: WebSocket\r\n"
66 "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
67 "Origin: http://example.com\r\n"
68 "Cookie: WK-websocket-test=1\r\n"
69 "\r\n"
70 "^n:ds[4U";
71
72 EXPECT_TRUE(handler.ParseRequest(kHandshakeRequestMessage,
73 strlen(kHandshakeRequestMessage)));
74
75 handler.RemoveHeaders(kCookieHeaders, arraysize(kCookieHeaders));
76
77 handler.AppendHeaderIfMissing("Cookie",
78 "WK-websocket-test=1; "
79 "WK-websocket-test-httponly=1");
80
81 static const char* kHandshakeRequestExpectedMessage =
82 "GET /demo HTTP/1.1\r\n"
83 "Host: example.com\r\n"
84 "Connection: Upgrade\r\n"
85 "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n"
86 "Sec-WebSocket-Protocol: sample\r\n"
87 "Upgrade: WebSocket\r\n"
88 "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
89 "Origin: http://example.com\r\n"
90 "Cookie: WK-websocket-test=1; WK-websocket-test-httponly=1\r\n"
91 "\r\n"
92 "^n:ds[4U";
93
94 EXPECT_EQ(kHandshakeRequestExpectedMessage, handler.GetRawRequest());
95 }
96
TEST(WebSocketHandshakeResponseHandlerTest,SimpleResponse)97 TEST(WebSocketHandshakeResponseHandlerTest, SimpleResponse) {
98 WebSocketHandshakeResponseHandler handler;
99
100 static const char* kHandshakeResponseMessage =
101 "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
102 "Upgrade: WebSocket\r\n"
103 "Connection: Upgrade\r\n"
104 "Sec-WebSocket-Origin: http://example.com\r\n"
105 "Sec-WebSocket-Location: ws://example.com/demo\r\n"
106 "Sec-WebSocket-Protocol: sample\r\n"
107 "\r\n"
108 "8jKS'y:G*Co,Wxa-";
109
110 EXPECT_EQ(strlen(kHandshakeResponseMessage),
111 handler.ParseRawResponse(kHandshakeResponseMessage,
112 strlen(kHandshakeResponseMessage)));
113 EXPECT_TRUE(handler.HasResponse());
114
115 handler.RemoveHeaders(kCookieHeaders, arraysize(kCookieHeaders));
116
117 EXPECT_EQ(kHandshakeResponseMessage, handler.GetResponse());
118 }
119
TEST(WebSocketHandshakeResponseHandlerTest,ReplaceResponseCookies)120 TEST(WebSocketHandshakeResponseHandlerTest, ReplaceResponseCookies) {
121 WebSocketHandshakeResponseHandler handler;
122
123 static const char* kHandshakeResponseMessage =
124 "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
125 "Upgrade: WebSocket\r\n"
126 "Connection: Upgrade\r\n"
127 "Sec-WebSocket-Origin: http://example.com\r\n"
128 "Sec-WebSocket-Location: ws://example.com/demo\r\n"
129 "Sec-WebSocket-Protocol: sample\r\n"
130 "Set-Cookie: WK-websocket-test-1\r\n"
131 "Set-Cookie: WK-websocket-test-httponly=1; HttpOnly\r\n"
132 "\r\n"
133 "8jKS'y:G*Co,Wxa-";
134
135 EXPECT_EQ(strlen(kHandshakeResponseMessage),
136 handler.ParseRawResponse(kHandshakeResponseMessage,
137 strlen(kHandshakeResponseMessage)));
138 EXPECT_TRUE(handler.HasResponse());
139 std::vector<std::string> cookies;
140 handler.GetHeaders(kSetCookieHeaders, arraysize(kSetCookieHeaders), &cookies);
141 ASSERT_EQ(2U, cookies.size());
142 EXPECT_EQ("WK-websocket-test-1", cookies[0]);
143 EXPECT_EQ("WK-websocket-test-httponly=1; HttpOnly", cookies[1]);
144 handler.RemoveHeaders(kSetCookieHeaders, arraysize(kSetCookieHeaders));
145
146 static const char* kHandshakeResponseExpectedMessage =
147 "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
148 "Upgrade: WebSocket\r\n"
149 "Connection: Upgrade\r\n"
150 "Sec-WebSocket-Origin: http://example.com\r\n"
151 "Sec-WebSocket-Location: ws://example.com/demo\r\n"
152 "Sec-WebSocket-Protocol: sample\r\n"
153 "\r\n"
154 "8jKS'y:G*Co,Wxa-";
155
156 EXPECT_EQ(kHandshakeResponseExpectedMessage, handler.GetResponse());
157 }
158
TEST(WebSocketHandshakeResponseHandlerTest,BadResponse)159 TEST(WebSocketHandshakeResponseHandlerTest, BadResponse) {
160 WebSocketHandshakeResponseHandler handler;
161
162 static const char* kBadMessage = "\n\n\r\net-Location: w";
163 EXPECT_EQ(strlen(kBadMessage),
164 handler.ParseRawResponse(kBadMessage, strlen(kBadMessage)));
165 EXPECT_TRUE(handler.HasResponse());
166 EXPECT_EQ(kBadMessage, handler.GetResponse());
167 }
168
TEST(WebSocketHandshakeResponseHandlerTest,BadResponse2)169 TEST(WebSocketHandshakeResponseHandlerTest, BadResponse2) {
170 WebSocketHandshakeResponseHandler handler;
171
172 static const char* kBadMessage = "\n\r\n\r\net-Location: w";
173 EXPECT_EQ(strlen(kBadMessage),
174 handler.ParseRawResponse(kBadMessage, strlen(kBadMessage)));
175 EXPECT_TRUE(handler.HasResponse());
176 EXPECT_EQ(kBadMessage, handler.GetResponse());
177 }
178
TEST(WebSocketHandshakeHandlerTest,HttpRequestResponse)179 TEST(WebSocketHandshakeHandlerTest, HttpRequestResponse) {
180 WebSocketHandshakeRequestHandler request_handler;
181
182 static const char* kHandshakeRequestMessage =
183 "GET /demo HTTP/1.1\r\n"
184 "Host: example.com\r\n"
185 "Connection: Upgrade\r\n"
186 "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n"
187 "Sec-WebSocket-Protocol: sample\r\n"
188 "Upgrade: WebSocket\r\n"
189 "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
190 "Origin: http://example.com\r\n"
191 "\r\n"
192 "^n:ds[4U";
193
194 EXPECT_TRUE(request_handler.ParseRequest(kHandshakeRequestMessage,
195 strlen(kHandshakeRequestMessage)));
196
197 GURL url("ws://example.com/demo");
198 std::string challenge;
199 const HttpRequestInfo& request_info =
200 request_handler.GetRequestInfo(url, &challenge);
201
202 EXPECT_EQ(url, request_info.url);
203 EXPECT_EQ("GET", request_info.method);
204 EXPECT_FALSE(request_info.extra_headers.HasHeader("Upgrade"));
205 EXPECT_FALSE(request_info.extra_headers.HasHeader("Connection"));
206 EXPECT_FALSE(request_info.extra_headers.HasHeader("Sec-WebSocket-Key1"));
207 EXPECT_FALSE(request_info.extra_headers.HasHeader("Sec-WebSocket-Key2"));
208 std::string value;
209 EXPECT_TRUE(request_info.extra_headers.GetHeader("Host", &value));
210 EXPECT_EQ("example.com", value);
211 EXPECT_TRUE(request_info.extra_headers.GetHeader("Origin", &value));
212 EXPECT_EQ("http://example.com", value);
213 EXPECT_TRUE(request_info.extra_headers.GetHeader("Sec-WebSocket-Protocol",
214 &value));
215 EXPECT_EQ("sample", value);
216
217 const char expected_challenge[] = "\x31\x6e\x41\x13\x0f\x7e\xd6\x3c^n:ds[4U";
218
219 EXPECT_EQ(expected_challenge, challenge);
220
221 static const char* kHandshakeResponseHeader =
222 "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
223 "Sec-WebSocket-Origin: http://example.com\r\n"
224 "Sec-WebSocket-Location: ws://example.com/demo\r\n"
225 "Sec-WebSocket-Protocol: sample\r\n";
226
227 std::string raw_headers =
228 HttpUtil::AssembleRawHeaders(kHandshakeResponseHeader,
229 strlen(kHandshakeResponseHeader));
230 HttpResponseInfo response_info;
231 response_info.headers = new HttpResponseHeaders(raw_headers);
232
233 EXPECT_TRUE(StartsWithASCII(response_info.headers->GetStatusLine(),
234 "HTTP/1.1 101 ", false));
235 EXPECT_FALSE(response_info.headers->HasHeader("Upgrade"));
236 EXPECT_FALSE(response_info.headers->HasHeader("Connection"));
237 EXPECT_TRUE(response_info.headers->HasHeaderValue("Sec-WebSocket-Origin",
238 "http://example.com"));
239 EXPECT_TRUE(response_info.headers->HasHeaderValue("Sec-WebSocket-Location",
240 "ws://example.com/demo"));
241 EXPECT_TRUE(response_info.headers->HasHeaderValue("Sec-WebSocket-Protocol",
242 "sample"));
243
244 WebSocketHandshakeResponseHandler response_handler;
245 EXPECT_TRUE(response_handler.ParseResponseInfo(response_info, challenge));
246 EXPECT_TRUE(response_handler.HasResponse());
247
248 static const char* kHandshakeResponseExpectedMessage =
249 "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
250 "Upgrade: WebSocket\r\n"
251 "Connection: Upgrade\r\n"
252 "Sec-WebSocket-Origin: http://example.com\r\n"
253 "Sec-WebSocket-Location: ws://example.com/demo\r\n"
254 "Sec-WebSocket-Protocol: sample\r\n"
255 "\r\n"
256 "8jKS'y:G*Co,Wxa-";
257
258 EXPECT_EQ(kHandshakeResponseExpectedMessage, response_handler.GetResponse());
259 }
260
TEST(WebSocketHandshakeHandlerTest,SpdyRequestResponse)261 TEST(WebSocketHandshakeHandlerTest, SpdyRequestResponse) {
262 WebSocketHandshakeRequestHandler request_handler;
263
264 static const char* kHandshakeRequestMessage =
265 "GET /demo HTTP/1.1\r\n"
266 "Host: example.com\r\n"
267 "Connection: Upgrade\r\n"
268 "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n"
269 "Sec-WebSocket-Protocol: sample\r\n"
270 "Upgrade: WebSocket\r\n"
271 "X-bogus-header: X\r\n"
272 "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
273 "Origin: http://example.com\r\n"
274 "X-bogus-header: Y\r\n"
275 "\r\n"
276 "^n:ds[4U";
277
278 EXPECT_TRUE(request_handler.ParseRequest(kHandshakeRequestMessage,
279 strlen(kHandshakeRequestMessage)));
280
281 GURL url("ws://example.com/demo");
282 std::string challenge;
283 spdy::SpdyHeaderBlock headers;
284 ASSERT_TRUE(request_handler.GetRequestHeaderBlock(url, &headers, &challenge));
285
286 EXPECT_EQ(url.spec(), headers["url"]);
287 EXPECT_TRUE(headers.find("upgrade") == headers.end());
288 EXPECT_TRUE(headers.find("Upgrade") == headers.end());
289 EXPECT_TRUE(headers.find("connection") == headers.end());
290 EXPECT_TRUE(headers.find("Connection") == headers.end());
291 EXPECT_TRUE(headers.find("Sec-WebSocket-Key1") == headers.end());
292 EXPECT_TRUE(headers.find("sec-websocket-key1") == headers.end());
293 EXPECT_TRUE(headers.find("Sec-WebSocket-Key2") == headers.end());
294 EXPECT_TRUE(headers.find("sec-websocket-key2") == headers.end());
295 EXPECT_EQ("example.com", headers["host"]);
296 EXPECT_EQ("http://example.com", headers["origin"]);
297 EXPECT_EQ("sample", headers["sec-websocket-protocol"]);
298 const char bogus_header[] = "X\0Y";
299 std::string bogus_header_str(bogus_header, sizeof(bogus_header) - 1);
300 EXPECT_EQ(bogus_header_str, headers["x-bogus-header"]);
301
302 const char expected_challenge[] = "\x31\x6e\x41\x13\x0f\x7e\xd6\x3c^n:ds[4U";
303
304 EXPECT_EQ(expected_challenge, challenge);
305
306 headers.clear();
307
308 headers["sec-websocket-origin"] = "http://example.com";
309 headers["sec-websocket-location"] = "ws://example.com/demo";
310 headers["sec-websocket-protocol"] = "sample";
311
312 WebSocketHandshakeResponseHandler response_handler;
313 EXPECT_TRUE(response_handler.ParseResponseHeaderBlock(headers, challenge));
314 EXPECT_TRUE(response_handler.HasResponse());
315
316 // Note that order of sec-websocket-* is sensitive with hash_map order.
317 static const char* kHandshakeResponseExpectedMessage =
318 "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
319 "Upgrade: WebSocket\r\n"
320 "Connection: Upgrade\r\n"
321 "sec-websocket-location: ws://example.com/demo\r\n"
322 "sec-websocket-origin: http://example.com\r\n"
323 "sec-websocket-protocol: sample\r\n"
324 "\r\n"
325 "8jKS'y:G*Co,Wxa-";
326
327 EXPECT_EQ(kHandshakeResponseExpectedMessage, response_handler.GetResponse());
328 }
329
330
TEST(WebSocketHandshakeHandlerTest,SpdyRequestResponseWithCookies)331 TEST(WebSocketHandshakeHandlerTest, SpdyRequestResponseWithCookies) {
332 WebSocketHandshakeRequestHandler request_handler;
333
334 // Note that websocket won't use multiple headers in request now.
335 static const char* kHandshakeRequestMessage =
336 "GET /demo HTTP/1.1\r\n"
337 "Host: example.com\r\n"
338 "Connection: Upgrade\r\n"
339 "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n"
340 "Sec-WebSocket-Protocol: sample\r\n"
341 "Upgrade: WebSocket\r\n"
342 "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
343 "Origin: http://example.com\r\n"
344 "Cookie: WK-websocket-test=1; WK-websocket-test-httponly=1\r\n"
345 "\r\n"
346 "^n:ds[4U";
347
348 EXPECT_TRUE(request_handler.ParseRequest(kHandshakeRequestMessage,
349 strlen(kHandshakeRequestMessage)));
350
351 GURL url("ws://example.com/demo");
352 std::string challenge;
353 spdy::SpdyHeaderBlock headers;
354 ASSERT_TRUE(request_handler.GetRequestHeaderBlock(url, &headers, &challenge));
355
356 EXPECT_EQ(url.spec(), headers["url"]);
357 EXPECT_TRUE(headers.find("upgrade") == headers.end());
358 EXPECT_TRUE(headers.find("Upgrade") == headers.end());
359 EXPECT_TRUE(headers.find("connection") == headers.end());
360 EXPECT_TRUE(headers.find("Connection") == headers.end());
361 EXPECT_TRUE(headers.find("Sec-WebSocket-Key1") == headers.end());
362 EXPECT_TRUE(headers.find("sec-websocket-key1") == headers.end());
363 EXPECT_TRUE(headers.find("Sec-WebSocket-Key2") == headers.end());
364 EXPECT_TRUE(headers.find("sec-websocket-key2") == headers.end());
365 EXPECT_EQ("example.com", headers["host"]);
366 EXPECT_EQ("http://example.com", headers["origin"]);
367 EXPECT_EQ("sample", headers["sec-websocket-protocol"]);
368 EXPECT_EQ("WK-websocket-test=1; WK-websocket-test-httponly=1",
369 headers["cookie"]);
370
371 const char expected_challenge[] = "\x31\x6e\x41\x13\x0f\x7e\xd6\x3c^n:ds[4U";
372
373 EXPECT_EQ(expected_challenge, challenge);
374
375 headers.clear();
376
377 headers["sec-websocket-origin"] = "http://example.com";
378 headers["sec-websocket-location"] = "ws://example.com/demo";
379 headers["sec-websocket-protocol"] = "sample";
380 std::string cookie = "WK-websocket-test=1";
381 cookie.append(1, '\0');
382 cookie += "WK-websocket-test-httponly=1; HttpOnly";
383 headers["set-cookie"] = cookie;
384
385 WebSocketHandshakeResponseHandler response_handler;
386 EXPECT_TRUE(response_handler.ParseResponseHeaderBlock(headers, challenge));
387 EXPECT_TRUE(response_handler.HasResponse());
388
389 // Note that order of sec-websocket-* is sensitive with hash_map order.
390 static const char* kHandshakeResponseExpectedMessage =
391 "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
392 "Upgrade: WebSocket\r\n"
393 "Connection: Upgrade\r\n"
394 "sec-websocket-location: ws://example.com/demo\r\n"
395 "sec-websocket-origin: http://example.com\r\n"
396 "sec-websocket-protocol: sample\r\n"
397 "set-cookie: WK-websocket-test=1\r\n"
398 "set-cookie: WK-websocket-test-httponly=1; HttpOnly\r\n"
399 "\r\n"
400 "8jKS'y:G*Co,Wxa-";
401
402 EXPECT_EQ(kHandshakeResponseExpectedMessage, response_handler.GetResponse());
403 }
404
405 } // namespace net
406