Home | History | Annotate | Download | only in websockets
      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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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