summaryrefslogtreecommitdiffstats
path: root/net/websockets
diff options
context:
space:
mode:
authorukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-17 05:09:22 +0000
committerukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-17 05:09:22 +0000
commitaf63c908603f8a2f58f69167129f819d5d30820c (patch)
treedcc8658fe7baa5f4ab5f3038595a0ca4747e8ac9 /net/websockets
parent24d3500b4139d481adb2b4bde98433f0dd8a56b0 (diff)
downloadchromium_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.cc215
-rw-r--r--net/websockets/websocket.h35
-rw-r--r--net/websockets/websocket_handshake.cc209
-rw-r--r--net/websockets/websocket_handshake.h80
-rw-r--r--net/websockets/websocket_handshake_unittest.cc235
-rw-r--r--net/websockets/websocket_unittest.cc125
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