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