1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/websockets/websocket_handshake_draft75.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/memory/ref_counted.h"
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h"
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/http/http_response_headers.h"
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/http/http_util.h"
11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace net {
13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst char WebSocketHandshakeDraft75::kServerHandshakeHeader[] =
15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    "HTTP/1.1 101 Web Socket Protocol Handshake\r\n";
16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst size_t WebSocketHandshakeDraft75::kServerHandshakeHeaderLength =
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    sizeof(kServerHandshakeHeader) - 1;
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst char WebSocketHandshakeDraft75::kUpgradeHeader[] =
20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    "Upgrade: WebSocket\r\n";
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst size_t WebSocketHandshakeDraft75::kUpgradeHeaderLength =
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    sizeof(kUpgradeHeader) - 1;
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst char WebSocketHandshakeDraft75::kConnectionHeader[] =
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    "Connection: Upgrade\r\n";
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst size_t WebSocketHandshakeDraft75::kConnectionHeaderLength =
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    sizeof(kConnectionHeader) - 1;
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochWebSocketHandshakeDraft75::WebSocketHandshakeDraft75(
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const GURL& url,
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const std::string& origin,
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const std::string& location,
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const std::string& protocol)
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : WebSocketHandshake(url, origin, location, protocol) {
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochWebSocketHandshakeDraft75::~WebSocketHandshakeDraft75() {
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstd::string WebSocketHandshakeDraft75::CreateClientHandshakeMessage() {
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::string msg;
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  msg = "GET ";
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  msg += GetResourceName();
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  msg += " HTTP/1.1\r\n";
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  msg += kUpgradeHeader;
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  msg += kConnectionHeader;
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  msg += "Host: ";
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  msg += GetHostFieldValue();
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  msg += "\r\n";
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  msg += "Origin: ";
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  msg += GetOriginFieldValue();
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  msg += "\r\n";
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!protocol_.empty()) {
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    msg += "WebSocket-Protocol: ";
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    msg += protocol_;
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    msg += "\r\n";
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // TODO(ukai): Add cookie if necessary.
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  msg += "\r\n";
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return msg;
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint WebSocketHandshakeDraft75::ReadServerHandshake(
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const char* data, size_t len) {
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  mode_ = MODE_INCOMPLETE;
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (len < kServerHandshakeHeaderLength) {
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return -1;
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!memcmp(data, kServerHandshakeHeader, kServerHandshakeHeaderLength)) {
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    mode_ = MODE_NORMAL;
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int eoh = HttpUtil::LocateEndOfHeaders(data, len);
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (eoh < 0)
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return -1;
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return eoh;
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const char* p = data + kServerHandshakeHeaderLength;
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const char* end = data + len;
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (mode_ == MODE_NORMAL) {
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    size_t header_size = end - p;
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (header_size < kUpgradeHeaderLength)
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return -1;
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (memcmp(p, kUpgradeHeader, kUpgradeHeaderLength)) {
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      mode_ = MODE_FAILED;
86731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      DVLOG(1) << "Bad Upgrade Header " << std::string(p, kUpgradeHeaderLength);
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return p - data;
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    p += kUpgradeHeaderLength;
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    header_size = end - p;
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (header_size < kConnectionHeaderLength)
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return -1;
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (memcmp(p, kConnectionHeader, kConnectionHeaderLength)) {
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      mode_ = MODE_FAILED;
95731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      DVLOG(1) << "Bad Connection Header "
96731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick               << std::string(p, kConnectionHeaderLength);
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return p - data;
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    p += kConnectionHeaderLength;
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int eoh = HttpUtil::LocateEndOfHeaders(data, len);
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (eoh == -1)
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return eoh;
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scoped_refptr<HttpResponseHeaders> headers(
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      new HttpResponseHeaders(HttpUtil::AssembleRawHeaders(data, eoh)));
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!ProcessHeaders(*headers)) {
109731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    DVLOG(1) << "Process Headers failed: " << std::string(data, eoh);
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    mode_ = MODE_FAILED;
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  switch (mode_) {
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case MODE_NORMAL:
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (CheckResponseHeaders()) {
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        mode_ = MODE_CONNECTED;
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      } else {
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        mode_ = MODE_FAILED;
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    default:
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      mode_ = MODE_FAILED;
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return eoh;
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool WebSocketHandshakeDraft75::ProcessHeaders(
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const HttpResponseHeaders& headers) {
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!GetSingleHeader(headers, "websocket-origin", &ws_origin_))
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!GetSingleHeader(headers, "websocket-location", &ws_location_))
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If |protocol_| is not specified by client, we don't care if there's
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // protocol field or not as specified in the spec.
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!protocol_.empty()
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      && !GetSingleHeader(headers, "websocket-protocol", &ws_protocol_))
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool WebSocketHandshakeDraft75::CheckResponseHeaders() const {
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(mode_ == MODE_NORMAL);
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!LowerCaseEqualsASCII(origin_, ws_origin_.c_str()))
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (location_ != ws_location_)
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!protocol_.empty() && protocol_ != ws_protocol_)
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace net
155