diff options
author | ukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-17 05:09:22 +0000 |
---|---|---|
committer | ukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-17 05:09:22 +0000 |
commit | af63c908603f8a2f58f69167129f819d5d30820c (patch) | |
tree | dcc8658fe7baa5f4ab5f3038595a0ca4747e8ac9 /net/websockets | |
parent | 24d3500b4139d481adb2b4bde98433f0dd8a56b0 (diff) | |
download | chromium_src-af63c908603f8a2f58f69167129f819d5d30820c.zip chromium_src-af63c908603f8a2f58f69167129f819d5d30820c.tar.gz chromium_src-af63c908603f8a2f58f69167129f819d5d30820c.tar.bz2 |
Refactor WebSocketHandshake out of WebSocket.
Make similar code structure with WebKit.
Add unittest for WebSocketHandshake.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/783005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@41810 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/websockets')
-rw-r--r-- | net/websockets/websocket.cc | 215 | ||||
-rw-r--r-- | net/websockets/websocket.h | 35 | ||||
-rw-r--r-- | net/websockets/websocket_handshake.cc | 209 | ||||
-rw-r--r-- | net/websockets/websocket_handshake.h | 80 | ||||
-rw-r--r-- | net/websockets/websocket_handshake_unittest.cc | 235 | ||||
-rw-r--r-- | net/websockets/websocket_unittest.cc | 125 |
6 files changed, 543 insertions, 356 deletions
diff --git a/net/websockets/websocket.cc b/net/websockets/websocket.cc index ad7acaf..dc4f568 100644 --- a/net/websockets/websocket.cc +++ b/net/websockets/websocket.cc @@ -8,33 +8,14 @@ #include "net/websockets/websocket.h" #include "base/message_loop.h" -#include "net/http/http_response_headers.h" -#include "net/http/http_util.h" +#include "net/websockets/websocket_handshake.h" namespace net { -static const int kWebSocketPort = 80; -static const int kSecureWebSocketPort = 443; - -static const char kServerHandshakeHeader[] = - "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"; -static const size_t kServerHandshakeHeaderLength = - sizeof(kServerHandshakeHeader) - 1; - -static const char kUpgradeHeader[] = "Upgrade: WebSocket\r\n"; -static const size_t kUpgradeHeaderLength = sizeof(kUpgradeHeader) - 1; - -static const char kConnectionHeader[] = "Connection: Upgrade\r\n"; -static const size_t kConnectionHeaderLength = sizeof(kConnectionHeader) - 1; - -bool WebSocket::Request::is_secure() const { - return url_.SchemeIs("wss"); -} - WebSocket::WebSocket(Request* request, WebSocketDelegate* delegate) : ready_state_(INITIALIZED), - mode_(MODE_INCOMPLETE), request_(request), + handshake_(NULL), delegate_(delegate), origin_loop_(MessageLoop::current()), socket_stream_(NULL), @@ -119,7 +100,12 @@ void WebSocket::OnConnected(SocketStream* socket_stream, read_consumed_len_ = 0; DCHECK(!current_write_buf_); - const std::string msg = request_->CreateClientHandshakeMessage(); + DCHECK(!handshake_.get()); + handshake_.reset(new WebSocketHandshake( + request_->url(), request_->origin(), request_->location(), + request_->protocol())); + + const std::string msg = handshake_->CreateClientHandshakeMessage(); IOBufferWithSize* buf = new IOBufferWithSize(msg.size()); memcpy(buf->data(), msg.data(), msg.size()); pending_write_bufs_.push_back(buf); @@ -158,181 +144,6 @@ void WebSocket::OnError(const SocketStream* socket_stream, int error) { NewRunnableMethod(this, &WebSocket::DoError, error)); } -std::string WebSocket::Request::CreateClientHandshakeMessage() const { - std::string msg; - msg = "GET "; - msg += url_.path(); - if (url_.has_query()) { - msg += "?"; - msg += url_.query(); - } - msg += " HTTP/1.1\r\n"; - msg += kUpgradeHeader; - msg += kConnectionHeader; - msg += "Host: "; - msg += StringToLowerASCII(url_.host()); - if (url_.has_port()) { - bool secure = is_secure(); - int port = url_.EffectiveIntPort(); - if ((!secure && - port != kWebSocketPort && port != url_parse::PORT_UNSPECIFIED) || - (secure && - port != kSecureWebSocketPort && port != url_parse::PORT_UNSPECIFIED)) { - msg += ":"; - msg += IntToString(port); - } - } - msg += "\r\n"; - msg += "Origin: "; - // It's OK to lowercase the origin as the Origin header does not contain - // the path or query portions, as per - // http://tools.ietf.org/html/draft-abarth-origin-00. - // - // TODO(satorux): Should we trim the port portion here if it's 80 for - // http:// or 443 for https:// ? Or can we assume it's done by the - // client of the library? - msg += StringToLowerASCII(origin_); - msg += "\r\n"; - if (!protocol_.empty()) { - msg += "WebSocket-Protocol: "; - msg += protocol_; - msg += "\r\n"; - } - // TODO(ukai): Add cookie if necessary. - msg += "\r\n"; - return msg; -} - -int WebSocket::CheckHandshake() { - DCHECK(current_read_buf_); - DCHECK(ready_state_ == CONNECTING); - mode_ = MODE_INCOMPLETE; - const char *start = current_read_buf_->StartOfBuffer() + read_consumed_len_; - const char *p = start; - size_t len = current_read_buf_->offset() - read_consumed_len_; - if (len < kServerHandshakeHeaderLength) { - return -1; - } - if (!memcmp(p, kServerHandshakeHeader, kServerHandshakeHeaderLength)) { - mode_ = MODE_NORMAL; - } else { - int eoh = HttpUtil::LocateEndOfHeaders(p, len); - if (eoh < 0) - return -1; - scoped_refptr<HttpResponseHeaders> headers( - new HttpResponseHeaders(HttpUtil::AssembleRawHeaders(p, eoh))); - if (headers->response_code() == 407) { - mode_ = MODE_AUTHENTICATE; - // TODO(ukai): Implement authentication handlers. - } - DLOG(INFO) << "non-normal websocket connection. " - << "response_code=" << headers->response_code() - << " mode=" << mode_; - // Invalid response code. - ready_state_ = CLOSED; - return eoh; - } - const char* end = p + len + 1; - p += kServerHandshakeHeaderLength; - - if (mode_ == MODE_NORMAL) { - size_t header_size = end - p; - if (header_size < kUpgradeHeaderLength) - return -1; - if (memcmp(p, kUpgradeHeader, kUpgradeHeaderLength)) { - DLOG(INFO) << "Bad Upgrade Header " - << std::string(p, kUpgradeHeaderLength); - ready_state_ = CLOSED; - return p - start; - } - p += kUpgradeHeaderLength; - - header_size = end - p; - if (header_size < kConnectionHeaderLength) - return -1; - if (memcmp(p, kConnectionHeader, kConnectionHeaderLength)) { - DLOG(INFO) << "Bad Connection Header " - << std::string(p, kConnectionHeaderLength); - ready_state_ = CLOSED; - return p - start; - } - p += kConnectionHeaderLength; - } - int eoh = HttpUtil::LocateEndOfHeaders(start, len); - if (eoh == -1) - return eoh; - scoped_refptr<HttpResponseHeaders> headers( - new HttpResponseHeaders(HttpUtil::AssembleRawHeaders(start, eoh))); - if (!ProcessHeaders(*headers)) { - DLOG(INFO) << "Process Headers failed: " - << std::string(start, eoh); - ready_state_ = CLOSED; - return eoh; - } - switch (mode_) { - case MODE_NORMAL: - if (CheckResponseHeaders()) { - ready_state_ = OPEN; - } else { - ready_state_ = CLOSED; - } - break; - default: - ready_state_ = CLOSED; - break; - } - if (ready_state_ == CLOSED) - DLOG(INFO) << "CheckHandshake mode=" << mode_ - << " " << std::string(start, eoh); - return eoh; -} - -// Gets the value of the specified header. -// It assures only one header of |name| in |headers|. -// Returns true iff single header of |name| is found in |headers| -// and |value| is filled with the value. -// Returns false otherwise. -static bool GetSingleHeader(const HttpResponseHeaders& headers, - const std::string& name, - std::string* value) { - std::string first_value; - void* iter = NULL; - if (!headers.EnumerateHeader(&iter, name, &first_value)) - return false; - - // Checks no more |name| found in |headers|. - // Second call of EnumerateHeader() must return false. - std::string second_value; - if (headers.EnumerateHeader(&iter, name, &second_value)) - return false; - *value = first_value; - return true; -} - -bool WebSocket::ProcessHeaders(const HttpResponseHeaders& headers) { - if (!GetSingleHeader(headers, "websocket-origin", &ws_origin_)) - return false; - - if (!GetSingleHeader(headers, "websocket-location", &ws_location_)) - return false; - - if (!request_->protocol().empty() - && !GetSingleHeader(headers, "websocket-protocol", &ws_protocol_)) - return false; - return true; -} - -bool WebSocket::CheckResponseHeaders() const { - DCHECK(mode_ == MODE_NORMAL); - if (!LowerCaseEqualsASCII(request_->origin(), ws_origin_.c_str())) - return false; - if (request_->location() != ws_location_) - return false; - if (request_->protocol() != ws_protocol_) - return false; - return true; -} - void WebSocket::SendPending() { DCHECK(MessageLoop::current() == origin_loop_); DCHECK(socket_stream_); @@ -355,18 +166,24 @@ void WebSocket::DoReceivedData() { switch (ready_state_) { case CONNECTING: { - int eoh = CheckHandshake(); + DCHECK(handshake_.get()); + DCHECK(current_read_buf_); + const char* data = + current_read_buf_->StartOfBuffer() + read_consumed_len_; + size_t len = current_read_buf_->offset() - read_consumed_len_; + int eoh = handshake_->ReadServerHandshake(data, len); if (eoh < 0) { // Not enough data, Retry when more data is available. return; } SkipReadBuffer(eoh); } - if (ready_state_ != OPEN) { + if (handshake_->mode() != WebSocketHandshake::MODE_CONNECTED) { // Handshake failed. socket_stream_->Close(); return; } + ready_state_ = OPEN; if (delegate_) delegate_->OnOpen(this); if (current_read_buf_->offset() == read_consumed_len_) { diff --git a/net/websockets/websocket.h b/net/websockets/websocket.h index 0cf95db..5e58391 100644 --- a/net/websockets/websocket.h +++ b/net/websockets/websocket.h @@ -15,6 +15,7 @@ #include <string> #include "base/ref_counted.h" +#include "base/scoped_ptr.h" #include "googleurl/src/gurl.h" #include "net/base/io_buffer.h" #include "net/socket_stream/socket_stream.h" @@ -26,9 +27,9 @@ namespace net { class ClientSocketFactory; class HostResolver; -class HttpResponseHeaders; class WebSocket; +class WebSocketHandshake; // Delegate methods will be called on the same message loop as // WebSocket is constructed. @@ -74,7 +75,6 @@ class WebSocket : public base::RefCountedThreadSafe<WebSocket>, ~Request() {} const GURL& url() const { return url_; } - bool is_secure() const; const std::string& protocol() const { return protocol_; } const std::string& origin() const { return origin_; } const std::string& location() const { return location_; } @@ -95,9 +95,6 @@ class WebSocket : public base::RefCountedThreadSafe<WebSocket>, return client_socket_factory_; } - // Creates the client handshake message from |this|. - std::string CreateClientHandshakeMessage() const; - private: GURL url_; std::string protocol_; @@ -147,9 +144,6 @@ class WebSocket : public base::RefCountedThreadSafe<WebSocket>, virtual void OnError(const SocketStream* socket, int error); private: - enum Mode { - MODE_INCOMPLETE, MODE_NORMAL, MODE_AUTHENTICATE, - }; typedef std::deque< scoped_refptr<IOBufferWithSize> > PendingDataQueue; friend class WebSocketTest; @@ -157,24 +151,6 @@ class WebSocket : public base::RefCountedThreadSafe<WebSocket>, friend class base::RefCountedThreadSafe<WebSocket>; virtual ~WebSocket(); - // Checks handshake. - // Prerequisite: Server handshake message is received in |current_read_buf_|. - // Returns number of bytes for server handshake message, - // or negative if server handshake message is not received fully yet. - int CheckHandshake(); - - // Processes server handshake message, parsed as |headers|, and updates - // |ws_origin_|, |ws_location_| and |ws_protocol_|. - // Returns true if it's ok. - // Returns false otherwise (e.g. duplicate WebSocket-Origin: header, etc.) - bool ProcessHeaders(const HttpResponseHeaders& headers); - - // Checks |ws_origin_|, |ws_location_| and |ws_protocol_| are valid - // against |request_|. - // Returns true if it's ok. - // Returns false otherwise (e.g. origin mismatch, etc.) - bool CheckResponseHeaders() const; - // Sends pending data in |current_write_buf_| and/or |pending_write_bufs_|. void SendPending(); @@ -197,16 +173,11 @@ class WebSocket : public base::RefCountedThreadSafe<WebSocket>, void DoError(int error); State ready_state_; - Mode mode_; scoped_ptr<Request> request_; + scoped_ptr<WebSocketHandshake> handshake_; WebSocketDelegate* delegate_; MessageLoop* origin_loop_; - // Handshake messages that server sent. - std::string ws_origin_; - std::string ws_location_; - std::string ws_protocol_; - scoped_refptr<SocketStream> socket_stream_; int max_pending_send_allowed_; diff --git a/net/websockets/websocket_handshake.cc b/net/websockets/websocket_handshake.cc new file mode 100644 index 0000000..c17ea34 --- /dev/null +++ b/net/websockets/websocket_handshake.cc @@ -0,0 +1,209 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/websockets/websocket_handshake.h" + +#include "base/ref_counted.h" +#include "base/string_util.h" +#include "net/http/http_response_headers.h" +#include "net/http/http_util.h" + +namespace net { + +const int WebSocketHandshake::kWebSocketPort = 80; +const int WebSocketHandshake::kSecureWebSocketPort = 443; + +const char WebSocketHandshake::kServerHandshakeHeader[] = + "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"; +const size_t WebSocketHandshake::kServerHandshakeHeaderLength = + sizeof(kServerHandshakeHeader) - 1; + +const char WebSocketHandshake::kUpgradeHeader[] = "Upgrade: WebSocket\r\n"; +const size_t WebSocketHandshake::kUpgradeHeaderLength = + sizeof(kUpgradeHeader) - 1; + +const char WebSocketHandshake::kConnectionHeader[] = "Connection: Upgrade\r\n"; +const size_t WebSocketHandshake::kConnectionHeaderLength = + sizeof(kConnectionHeader) - 1; + +WebSocketHandshake::WebSocketHandshake( + const GURL& url, + const std::string& origin, + const std::string& location, + const std::string& protocol) + : url_(url), + origin_(origin), + location_(location), + protocol_(protocol), + mode_(MODE_INCOMPLETE) { +} + +WebSocketHandshake::~WebSocketHandshake() { +} + +bool WebSocketHandshake::is_secure() const { + return url_.SchemeIs("wss"); +} + +std::string WebSocketHandshake::CreateClientHandshakeMessage() const { + std::string msg; + msg = "GET "; + msg += url_.path(); + if (url_.has_query()) { + msg += "?"; + msg += url_.query(); + } + msg += " HTTP/1.1\r\n"; + msg += kUpgradeHeader; + msg += kConnectionHeader; + msg += "Host: "; + msg += StringToLowerASCII(url_.host()); + if (url_.has_port()) { + bool secure = is_secure(); + int port = url_.EffectiveIntPort(); + if ((!secure && + port != kWebSocketPort && port != url_parse::PORT_UNSPECIFIED) || + (secure && + port != kSecureWebSocketPort && port != url_parse::PORT_UNSPECIFIED)) { + msg += ":"; + msg += IntToString(port); + } + } + msg += "\r\n"; + msg += "Origin: "; + // It's OK to lowercase the origin as the Origin header does not contain + // the path or query portions, as per + // http://tools.ietf.org/html/draft-abarth-origin-00. + // + // TODO(satorux): Should we trim the port portion here if it's 80 for + // http:// or 443 for https:// ? Or can we assume it's done by the + // client of the library? + msg += StringToLowerASCII(origin_); + msg += "\r\n"; + if (!protocol_.empty()) { + msg += "WebSocket-Protocol: "; + msg += protocol_; + msg += "\r\n"; + } + // TODO(ukai): Add cookie if necessary. + msg += "\r\n"; + return msg; +} + +int WebSocketHandshake::ReadServerHandshake(const char* data, size_t len) { + mode_ = MODE_INCOMPLETE; + if (len < kServerHandshakeHeaderLength) { + return -1; + } + if (!memcmp(data, kServerHandshakeHeader, kServerHandshakeHeaderLength)) { + mode_ = MODE_NORMAL; + } else { + int eoh = HttpUtil::LocateEndOfHeaders(data, len); + if (eoh < 0) + return -1; + return eoh; + } + const char* p = data + kServerHandshakeHeaderLength; + const char* end = data + len + 1; + + if (mode_ == MODE_NORMAL) { + size_t header_size = end - p; + if (header_size < kUpgradeHeaderLength) + return -1; + if (memcmp(p, kUpgradeHeader, kUpgradeHeaderLength)) { + mode_ = MODE_FAILED; + DLOG(INFO) << "Bad Upgrade Header " + << std::string(p, kUpgradeHeaderLength); + return p - data; + } + p += kUpgradeHeaderLength; + header_size = end - p; + if (header_size < kConnectionHeaderLength) + return -1; + if (memcmp(p, kConnectionHeader, kConnectionHeaderLength)) { + mode_ = MODE_FAILED; + DLOG(INFO) << "Bad Connection Header " + << std::string(p, kConnectionHeaderLength); + return p - data; + } + p += kConnectionHeaderLength; + } + + int eoh = HttpUtil::LocateEndOfHeaders(data, len); + if (eoh == -1) + return eoh; + + scoped_refptr<HttpResponseHeaders> headers( + new HttpResponseHeaders(HttpUtil::AssembleRawHeaders(data, eoh))); + if (!ProcessHeaders(*headers)) { + DLOG(INFO) << "Process Headers failed: " + << std::string(data, eoh); + mode_ = MODE_FAILED; + } + switch (mode_) { + case MODE_NORMAL: + if (CheckResponseHeaders()) { + mode_ = MODE_CONNECTED; + } else { + mode_ = MODE_FAILED; + } + break; + default: + mode_ = MODE_FAILED; + break; + } + return eoh; +} + +// Gets the value of the specified header. +// It assures only one header of |name| in |headers|. +// Returns true iff single header of |name| is found in |headers| +// and |value| is filled with the value. +// Returns false otherwise. +static bool GetSingleHeader(const HttpResponseHeaders& headers, + const std::string& name, + std::string* value) { + std::string first_value; + void* iter = NULL; + if (!headers.EnumerateHeader(&iter, name, &first_value)) + return false; + + // Checks no more |name| found in |headers|. + // Second call of EnumerateHeader() must return false. + std::string second_value; + if (headers.EnumerateHeader(&iter, name, &second_value)) + return false; + *value = first_value; + return true; +} + +bool WebSocketHandshake::ProcessHeaders(const HttpResponseHeaders& headers) { + if (!GetSingleHeader(headers, "websocket-origin", &ws_origin_)) + return false; + + if (!GetSingleHeader(headers, "websocket-location", &ws_location_)) + return false; + + // If |protocol_| is not specified by client, we don't care if there's + // protocol field or not as specified in the spec. + if (!protocol_.empty() + && !GetSingleHeader(headers, "websocket-protocol", &ws_protocol_)) + return false; + return true; +} + +bool WebSocketHandshake::CheckResponseHeaders() const { + DCHECK(mode_ == MODE_NORMAL); + if (!LowerCaseEqualsASCII(origin_, ws_origin_.c_str())) + return false; + if (location_ != ws_location_) + return false; + if (!protocol_.empty() && protocol_ != ws_protocol_) + return false; + return true; +} + + + +} // namespace net diff --git a/net/websockets/websocket_handshake.h b/net/websockets/websocket_handshake.h new file mode 100644 index 0000000..1e94eff --- /dev/null +++ b/net/websockets/websocket_handshake.h @@ -0,0 +1,80 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_WEBSOCKETS_WEBSOCKET_HANDSHAKE_H_ +#define NET_WEBSOCKETS_WEBSOCKET_HANDSHAKE_H_ + +#include <string> + +#include "base/basictypes.h" +#include "googleurl/src/gurl.h" + +namespace net { + +class HttpResponseHeaders; + +class WebSocketHandshake { + public: + static const int kWebSocketPort; + static const int kSecureWebSocketPort; + static const char kServerHandshakeHeader[]; + static const size_t kServerHandshakeHeaderLength; + static const char kUpgradeHeader[]; + static const size_t kUpgradeHeaderLength; + static const char kConnectionHeader[]; + static const size_t kConnectionHeaderLength; + + enum Mode { + MODE_INCOMPLETE, MODE_NORMAL, MODE_FAILED, MODE_CONNECTED + }; + WebSocketHandshake(const GURL& url, + const std::string& origin, + const std::string& location, + const std::string& protocol); + ~WebSocketHandshake(); + + bool is_secure() const; + // Creates the client handshake message from |this|. + std::string CreateClientHandshakeMessage() const; + + // Reads server handshake message in |len| of |data|, updates |mode_| and + // returns number of bytes of the server handshake message. + // Once connection is established, |mode_| will be MODE_CONNECTED. + // If connection establishment failed, |mode_| will be MODE_FAILED. + // Returns negative if the server handshake message is incomplete. + int ReadServerHandshake(const char* data, size_t len); + Mode mode() const { return mode_; } + + private: + // Processes server handshake message, parsed as |headers|, and updates + // |ws_origin_|, |ws_location_| and |ws_protocol_|. + // Returns true if it's ok. + // Returns false otherwise (e.g. duplicate WebSocket-Origin: header, etc.) + bool ProcessHeaders(const HttpResponseHeaders& headers); + + // Checks |ws_origin_|, |ws_location_| and |ws_protocol_| are valid + // against |origin_|, |location_| and |protocol_|. + // Returns true if it's ok. + // Returns false otherwise (e.g. origin mismatch, etc.) + bool CheckResponseHeaders() const; + + GURL url_; + // Handshake messages that the client is going to send out. + std::string origin_; + std::string location_; + std::string protocol_; + + Mode mode_; + + // Handshake messages that server sent. + std::string ws_origin_; + std::string ws_location_; + std::string ws_protocol_; + + DISALLOW_COPY_AND_ASSIGN(WebSocketHandshake); +}; + +} // namespace net + +#endif // NET_WEBSOCKETS_WEBSOCKET_HANDSHAKE_H_ diff --git a/net/websockets/websocket_handshake_unittest.cc b/net/websockets/websocket_handshake_unittest.cc new file mode 100644 index 0000000..beae805 --- /dev/null +++ b/net/websockets/websocket_handshake_unittest.cc @@ -0,0 +1,235 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <string> +#include <vector> + +#include "base/scoped_ptr.h" +#include "net/websockets/websocket_handshake.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/platform_test.h" + +namespace net { + +TEST(WebSocketHandshakeTest, Connect) { + const std::string kExpectedClientHandshakeMessage = + "GET /demo HTTP/1.1\r\n" + "Upgrade: WebSocket\r\n" + "Connection: Upgrade\r\n" + "Host: example.com\r\n" + "Origin: http://example.com\r\n" + "WebSocket-Protocol: sample\r\n" + "\r\n"; + + scoped_ptr<WebSocketHandshake> handshake( + new WebSocketHandshake(GURL("ws://example.com/demo"), + "http://example.com", + "ws://example.com/demo", + "sample")); + EXPECT_EQ(WebSocketHandshake::MODE_INCOMPLETE, handshake->mode()); + EXPECT_EQ(kExpectedClientHandshakeMessage, + handshake->CreateClientHandshakeMessage()); + + const char kResponse[] = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" + "Upgrade: WebSocket\r\n" + "Connection: Upgrade\r\n" + "WebSocket-Origin: http://example.com\r\n" + "WebSocket-Location: ws://example.com/demo\r\n" + "WebSocket-Protocol: sample\r\n" + "\r\n"; + + EXPECT_EQ(WebSocketHandshake::MODE_INCOMPLETE, handshake->mode()); + // too short + EXPECT_EQ(-1, handshake->ReadServerHandshake(kResponse, 16)); + EXPECT_EQ(WebSocketHandshake::MODE_INCOMPLETE, handshake->mode()); + // only status line + EXPECT_EQ(-1, handshake->ReadServerHandshake( + kResponse, + WebSocketHandshake::kServerHandshakeHeaderLength)); + EXPECT_EQ(WebSocketHandshake::MODE_NORMAL, handshake->mode()); + // by upgrade header + EXPECT_EQ(-1, handshake->ReadServerHandshake( + kResponse, + WebSocketHandshake::kServerHandshakeHeaderLength + + WebSocketHandshake::kUpgradeHeaderLength)); + EXPECT_EQ(WebSocketHandshake::MODE_NORMAL, handshake->mode()); + // by connection header + EXPECT_EQ(-1, handshake->ReadServerHandshake( + kResponse, + WebSocketHandshake::kServerHandshakeHeaderLength + + WebSocketHandshake::kUpgradeHeaderLength + + WebSocketHandshake::kConnectionHeaderLength)); + EXPECT_EQ(WebSocketHandshake::MODE_NORMAL, handshake->mode()); + + EXPECT_EQ(-1, handshake->ReadServerHandshake( + kResponse, sizeof(kResponse) - 2)); + EXPECT_EQ(WebSocketHandshake::MODE_NORMAL, handshake->mode()); + + int handshake_length = strlen(kResponse); + EXPECT_EQ(handshake_length, handshake->ReadServerHandshake( + kResponse, sizeof(kResponse) - 1)); // -1 for terminating \0 + EXPECT_EQ(WebSocketHandshake::MODE_CONNECTED, handshake->mode()); +} + +TEST(WebSocketHandshakeTest, ServerSentData) { + const std::string kExpectedClientHandshakeMessage = + "GET /demo HTTP/1.1\r\n" + "Upgrade: WebSocket\r\n" + "Connection: Upgrade\r\n" + "Host: example.com\r\n" + "Origin: http://example.com\r\n" + "WebSocket-Protocol: sample\r\n" + "\r\n"; + scoped_ptr<WebSocketHandshake> handshake( + new WebSocketHandshake(GURL("ws://example.com/demo"), + "http://example.com", + "ws://example.com/demo", + "sample")); + EXPECT_EQ(WebSocketHandshake::MODE_INCOMPLETE, handshake->mode()); + EXPECT_EQ(kExpectedClientHandshakeMessage, + handshake->CreateClientHandshakeMessage()); + + const char kResponse[] ="HTTP/1.1 101 Web Socket Protocol Handshake\r\n" + "Upgrade: WebSocket\r\n" + "Connection: Upgrade\r\n" + "WebSocket-Origin: http://example.com\r\n" + "WebSocket-Location: ws://example.com/demo\r\n" + "WebSocket-Protocol: sample\r\n" + "\r\n" + "\0Hello\xff"; + + int handshake_length = strlen(kResponse); + EXPECT_EQ(handshake_length, handshake->ReadServerHandshake( + kResponse, sizeof(kResponse) - 1)); // -1 for terminating \0 + EXPECT_EQ(WebSocketHandshake::MODE_CONNECTED, handshake->mode()); +} + +TEST(WebSocketHandshakeTest, is_secure_false) { + scoped_ptr<WebSocketHandshake> handshake( + new WebSocketHandshake(GURL("ws://example.com/demo"), + "http://example.com", + "ws://example.com/demo", + "sample")); + EXPECT_FALSE(handshake->is_secure()); +} + +TEST(WebSocketHandshakeTest, is_secure_true) { + // wss:// is secure. + scoped_ptr<WebSocketHandshake> handshake( + new WebSocketHandshake(GURL("wss://example.com/demo"), + "http://example.com", + "wss://example.com/demo", + "sample")); + EXPECT_TRUE(handshake->is_secure()); +} + +TEST(WebSocketHandshakeTest, CreateClientHandshakeMessage_Simple) { + scoped_ptr<WebSocketHandshake> handshake( + new WebSocketHandshake(GURL("ws://example.com/demo"), + "http://example.com", + "ws://example.com/demo", + "sample")); + EXPECT_EQ("GET /demo HTTP/1.1\r\n" + "Upgrade: WebSocket\r\n" + "Connection: Upgrade\r\n" + "Host: example.com\r\n" + "Origin: http://example.com\r\n" + "WebSocket-Protocol: sample\r\n" + "\r\n", + handshake->CreateClientHandshakeMessage()); +} + +TEST(WebSocketHandshakeTest, CreateClientHandshakeMessage_PathAndQuery) { + scoped_ptr<WebSocketHandshake> handshake( + new WebSocketHandshake(GURL("ws://example.com/Test?q=xxx&p=%20"), + "http://example.com", + "ws://example.com/demo", + "sample")); + // Path and query should be preserved as-is. + EXPECT_THAT(handshake->CreateClientHandshakeMessage(), + testing::HasSubstr("GET /Test?q=xxx&p=%20 HTTP/1.1\r\n")); +} + +TEST(WebSocketHandshakeTest, CreateClientHandshakeMessage_Host) { + scoped_ptr<WebSocketHandshake> handshake( + new WebSocketHandshake(GURL("ws://Example.Com/demo"), + "http://Example.Com", + "ws://Example.Com/demo", + "sample")); + // Host should be lowercased + EXPECT_THAT(handshake->CreateClientHandshakeMessage(), + testing::HasSubstr("Host: example.com\r\n")); + EXPECT_THAT(handshake->CreateClientHandshakeMessage(), + testing::HasSubstr("Origin: http://example.com\r\n")); +} + +TEST(WebSocketHandshakeTest, CreateClientHandshakeMessage_TrimPort80) { + scoped_ptr<WebSocketHandshake> handshake( + new WebSocketHandshake(GURL("ws://example.com:80/demo"), + "http://example.com", + "ws://example.com/demo", + "sample")); + // :80 should be trimmed as it's the default port for ws://. + EXPECT_THAT(handshake->CreateClientHandshakeMessage(), + testing::HasSubstr("Host: example.com\r\n")); +} + +TEST(WebSocketHandshakeTest, CreateClientHandshakeMessage_TrimPort443) { + scoped_ptr<WebSocketHandshake> handshake( + new WebSocketHandshake(GURL("wss://example.com:443/demo"), + "http://example.com", + "wss://example.com/demo", + "sample")); + // :443 should be trimmed as it's the default port for wss://. + EXPECT_THAT(handshake->CreateClientHandshakeMessage(), + testing::HasSubstr("Host: example.com\r\n")); +} + +TEST(WebSocketHandshakeTest, CreateClientHandshakeMessage_NonDefaultPortForWs) { + scoped_ptr<WebSocketHandshake> handshake( + new WebSocketHandshake(GURL("ws://example.com:8080/demo"), + "http://example.com", + "wss://example.com/demo", + "sample")); + // :8080 should be preserved as it's not the default port for ws://. + EXPECT_THAT(handshake->CreateClientHandshakeMessage(), + testing::HasSubstr("Host: example.com:8080\r\n")); +} + +TEST(WebSocketHandshakeTest, + CreateClientHandshakeMessage_NonDefaultPortForWss) { + scoped_ptr<WebSocketHandshake> handshake( + new WebSocketHandshake(GURL("wss://example.com:4443/demo"), + "http://example.com", + "wss://example.com/demo", + "sample")); + // :4443 should be preserved as it's not the default port for wss://. + EXPECT_THAT(handshake->CreateClientHandshakeMessage(), + testing::HasSubstr("Host: example.com:4443\r\n")); +} + +TEST(WebSocketHandshakeTest, CreateClientHandshakeMessage_WsBut443) { + scoped_ptr<WebSocketHandshake> handshake( + new WebSocketHandshake(GURL("ws://example.com:443/demo"), + "http://example.com", + "ws://example.com/demo", + "sample")); + // :443 should be preserved as it's not the default port for ws://. + EXPECT_THAT(handshake->CreateClientHandshakeMessage(), + testing::HasSubstr("Host: example.com:443\r\n")); +} + +TEST(WebSocketHandshakeTest, CreateClientHandshakeMessage_WssBut80) { + scoped_ptr<WebSocketHandshake> handshake( + new WebSocketHandshake(GURL("wss://example.com:80/demo"), + "http://example.com", + "wss://example.com/demo", + "sample")); + // :80 should be preserved as it's not the default port for wss://. + EXPECT_THAT(handshake->CreateClientHandshakeMessage(), + testing::HasSubstr("Host: example.com:80\r\n")); +} + +} // namespace net diff --git a/net/websockets/websocket_unittest.cc b/net/websockets/websocket_unittest.cc index ea3d1fe..e18f712 100644 --- a/net/websockets/websocket_unittest.cc +++ b/net/websockets/websocket_unittest.cc @@ -329,129 +329,4 @@ TEST_F(WebSocketTest, ProcessFrameDataForUnterminatedString) { websocket->DetachDelegate(); } -TEST(WebSocketRequestTest, is_secure_false) { - WebSocket::Request request(GURL("ws://example.com/demo"), - "sample", - "http://example.com", - "ws://example.com/demo", - NULL); - EXPECT_FALSE(request.is_secure()); -} - -TEST(WebSocketRequestTest, is_secure_true) { - // wss:// is secure. - WebSocket::Request request(GURL("wss://example.com/demo"), - "sample", - "http://example.com", - "wss://example.com/demo", - NULL); - EXPECT_TRUE(request.is_secure()); -} - -TEST(WebSocketRequestTest, CreateClientHandshakeMessage_Simple) { - WebSocket::Request request(GURL("ws://example.com/demo"), - "sample", - "http://example.com", - "ws://example.com/demo", - NULL); - EXPECT_EQ("GET /demo HTTP/1.1\r\n" - "Upgrade: WebSocket\r\n" - "Connection: Upgrade\r\n" - "Host: example.com\r\n" - "Origin: http://example.com\r\n" - "WebSocket-Protocol: sample\r\n" - "\r\n", - request.CreateClientHandshakeMessage()); -} - -TEST(WebSocketRequestTest, CreateClientHandshakeMessage_PathAndQuery) { - WebSocket::Request request(GURL("ws://example.com/Test?q=xxx&p=%20"), - "sample", - "http://example.com", - "ws://example.com/demo", - NULL); - // Path and query should be preserved as-is. - EXPECT_THAT(request.CreateClientHandshakeMessage(), - testing::HasSubstr("GET /Test?q=xxx&p=%20 HTTP/1.1\r\n")); -} - -TEST(WebSocketRequestTest, CreateClientHandshakeMessage_Host) { - WebSocket::Request request(GURL("ws://Example.Com/demo"), - "sample", - "http://Example.Com", - "ws://Example.Com/demo", - NULL); - // Host should be lowercased - EXPECT_THAT(request.CreateClientHandshakeMessage(), - testing::HasSubstr("Host: example.com\r\n")); - EXPECT_THAT(request.CreateClientHandshakeMessage(), - testing::HasSubstr("Origin: http://example.com\r\n")); -} - -TEST(WebSocketRequestTest, CreateClientHandshakeMessage_TrimPort80) { - WebSocket::Request request(GURL("ws://example.com:80/demo"), - "sample", - "http://example.com", - "ws://example.com/demo", - NULL); - // :80 should be trimmed as it's the default port for ws://. - EXPECT_THAT(request.CreateClientHandshakeMessage(), - testing::HasSubstr("Host: example.com\r\n")); -} - -TEST(WebSocketRequestTest, CreateClientHandshakeMessage_TrimPort443) { - WebSocket::Request request(GURL("wss://example.com:443/demo"), - "sample", - "http://example.com", - "wss://example.com/demo", - NULL); - // :443 should be trimmed as it's the default port for wss://. - EXPECT_THAT(request.CreateClientHandshakeMessage(), - testing::HasSubstr("Host: example.com\r\n")); -} - -TEST(WebSocketRequestTest, CreateClientHandshakeMessage_NonDefaultPortForWs) { - WebSocket::Request request(GURL("ws://example.com:8080/demo"), - "sample", - "http://example.com", - "wss://example.com/demo", - NULL); - // :8080 should be preserved as it's not the default port for ws://. - EXPECT_THAT(request.CreateClientHandshakeMessage(), - testing::HasSubstr("Host: example.com:8080\r\n")); -} - -TEST(WebSocketRequestTest, CreateClientHandshakeMessage_NonDefaultPortForWss) { - WebSocket::Request request(GURL("wss://example.com:4443/demo"), - "sample", - "http://example.com", - "wss://example.com/demo", - NULL); - // :4443 should be preserved as it's not the default port for wss://. - EXPECT_THAT(request.CreateClientHandshakeMessage(), - testing::HasSubstr("Host: example.com:4443\r\n")); -} - -TEST(WebSocketRequestTest, CreateClientHandshakeMessage_WsBut443) { - WebSocket::Request request(GURL("ws://example.com:443/demo"), - "sample", - "http://example.com", - "ws://example.com/demo", - NULL); - // :443 should be preserved as it's not the default port for ws://. - EXPECT_THAT(request.CreateClientHandshakeMessage(), - testing::HasSubstr("Host: example.com:443\r\n")); -} - -TEST(WebSocketRequestTest, CreateClientHandshakeMessage_WssBut80) { - WebSocket::Request request(GURL("wss://example.com:80/demo"), - "sample", - "http://example.com", - "wss://example.com/demo", - NULL); - // :80 should be preserved as it's not the default port for wss://. - EXPECT_THAT(request.CreateClientHandshakeMessage(), - testing::HasSubstr("Host: example.com:80\r\n")); -} - } // namespace net |