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
19namespace {
20
21const char* const kCookieHeaders[] = {
22  "cookie", "cookie2"
23};
24
25const char* const kSetCookieHeaders[] = {
26  "set-cookie", "set-cookie2"
27};
28
29}
30
31namespace net {
32
33TEST(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
56TEST(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
97TEST(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
120TEST(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
159TEST(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
169TEST(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
179TEST(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
261TEST(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
331TEST(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