1 // Copyright 2012 The Chromium Authors
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 "net/server/web_socket.h"
6
7 #include <string_view>
8 #include <vector>
9
10 #include "base/base64.h"
11 #include "base/check.h"
12 #include "base/hash/sha1.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/sys_byteorder.h"
16 #include "net/server/http_connection.h"
17 #include "net/server/http_server.h"
18 #include "net/server/http_server_request_info.h"
19 #include "net/server/http_server_response_info.h"
20 #include "net/server/web_socket_encoder.h"
21 #include "net/websockets/websocket_deflate_parameters.h"
22 #include "net/websockets/websocket_extension.h"
23 #include "net/websockets/websocket_handshake_constants.h"
24
25 namespace net {
26
27 namespace {
28
ExtensionsHeaderString(const std::vector<WebSocketExtension> & extensions)29 std::string ExtensionsHeaderString(
30 const std::vector<WebSocketExtension>& extensions) {
31 if (extensions.empty())
32 return std::string();
33
34 std::string result = "Sec-WebSocket-Extensions: " + extensions[0].ToString();
35 for (size_t i = 1; i < extensions.size(); ++i)
36 result += ", " + extensions[i].ToString();
37 return result + "\r\n";
38 }
39
ValidResponseString(const std::string & accept_hash,const std::vector<WebSocketExtension> extensions)40 std::string ValidResponseString(
41 const std::string& accept_hash,
42 const std::vector<WebSocketExtension> extensions) {
43 return base::StringPrintf(
44 "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
45 "Upgrade: WebSocket\r\n"
46 "Connection: Upgrade\r\n"
47 "Sec-WebSocket-Accept: %s\r\n"
48 "%s"
49 "\r\n",
50 accept_hash.c_str(), ExtensionsHeaderString(extensions).c_str());
51 }
52
53 } // namespace
54
WebSocket(HttpServer * server,HttpConnection * connection)55 WebSocket::WebSocket(HttpServer* server, HttpConnection* connection)
56 : server_(server), connection_(connection) {}
57
58 WebSocket::~WebSocket() = default;
59
Accept(const HttpServerRequestInfo & request,const NetworkTrafficAnnotationTag traffic_annotation)60 void WebSocket::Accept(const HttpServerRequestInfo& request,
61 const NetworkTrafficAnnotationTag traffic_annotation) {
62 std::string version = request.GetHeaderValue("sec-websocket-version");
63 if (version != "8" && version != "13") {
64 SendErrorResponse("Invalid request format. The version is not valid.",
65 traffic_annotation);
66 return;
67 }
68
69 std::string key = request.GetHeaderValue("sec-websocket-key");
70 if (key.empty()) {
71 SendErrorResponse(
72 "Invalid request format. Sec-WebSocket-Key is empty or isn't "
73 "specified.",
74 traffic_annotation);
75 return;
76 }
77 std::string encoded_hash = base::Base64Encode(
78 base::SHA1HashString(key + websockets::kWebSocketGuid));
79
80 std::vector<WebSocketExtension> response_extensions;
81 auto i = request.headers.find("sec-websocket-extensions");
82 if (i == request.headers.end()) {
83 encoder_ = WebSocketEncoder::CreateServer();
84 } else {
85 WebSocketDeflateParameters params;
86 encoder_ = WebSocketEncoder::CreateServer(i->second, ¶ms);
87 if (!encoder_) {
88 Fail();
89 return;
90 }
91 if (encoder_->deflate_enabled()) {
92 DCHECK(params.IsValidAsResponse());
93 response_extensions.push_back(params.AsExtension());
94 }
95 }
96 server_->SendRaw(connection_->id(),
97 ValidResponseString(encoded_hash, response_extensions),
98 traffic_annotation);
99 traffic_annotation_ = std::make_unique<NetworkTrafficAnnotationTag>(
100 NetworkTrafficAnnotationTag(traffic_annotation));
101 }
102
Read(std::string * message)103 WebSocket::ParseResult WebSocket::Read(std::string* message) {
104 if (closed_)
105 return FRAME_CLOSE;
106
107 if (!encoder_) {
108 // RFC6455, section 4.1 says "Once the client's opening handshake has been
109 // sent, the client MUST wait for a response from the server before sending
110 // any further data". If |encoder_| is null here, ::Accept either has not
111 // been called at all, or has rejected a request rather than producing
112 // a server handshake. Either way, the client clearly couldn't have gotten
113 // a proper server handshake, so error out, especially since this method
114 // can't proceed without an |encoder_|.
115 return FRAME_ERROR;
116 }
117
118 ParseResult result = FRAME_OK_MIDDLE;
119 HttpConnection::ReadIOBuffer* read_buf = connection_->read_buf();
120 std::string_view frame(read_buf->StartOfBuffer(), read_buf->GetSize());
121 int bytes_consumed = 0;
122 result = encoder_->DecodeFrame(frame, &bytes_consumed, message);
123 read_buf->DidConsume(bytes_consumed);
124
125 if (result == FRAME_CLOSE) {
126 // The current websocket implementation does not initiate the Close
127 // handshake before closing the connection.
128 // Therefore the received Close frame most likely belongs to the client that
129 // initiated the Closing handshake.
130 // According to https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.1
131 // if an endpoint receives a Close frame and did not previously send a
132 // Close frame, the endpoint MUST send a Close frame in response.
133 // It also MAY provide the close reason listed in
134 // https://datatracker.ietf.org/doc/html/rfc6455#section-7.4.1.
135 // As the closure was initiated by the client the "normal closure" status
136 // code is appropriate.
137 std::string code = "\x03\xe8"; // code = 1000;
138 std::string encoded;
139 encoder_->EncodeCloseFrame(code, 0, &encoded);
140 server_->SendRaw(connection_->id(), encoded, *traffic_annotation_);
141
142 closed_ = true;
143 }
144
145 if (result == FRAME_PING) {
146 if (!traffic_annotation_)
147 return FRAME_ERROR;
148 Send(*message, WebSocketFrameHeader::kOpCodePong, *traffic_annotation_);
149 }
150 return result;
151 }
152
Send(std::string_view message,WebSocketFrameHeader::OpCodeEnum op_code,const NetworkTrafficAnnotationTag traffic_annotation)153 void WebSocket::Send(std::string_view message,
154 WebSocketFrameHeader::OpCodeEnum op_code,
155 const NetworkTrafficAnnotationTag traffic_annotation) {
156 if (closed_)
157 return;
158 std::string encoded;
159 switch (op_code) {
160 case WebSocketFrameHeader::kOpCodeText:
161 encoder_->EncodeTextFrame(message, 0, &encoded);
162 break;
163
164 case WebSocketFrameHeader::kOpCodePong:
165 encoder_->EncodePongFrame(message, 0, &encoded);
166 break;
167
168 default:
169 // Only Pong and Text frame types are supported.
170 NOTREACHED();
171 }
172 server_->SendRaw(connection_->id(), encoded, traffic_annotation);
173 }
174
Fail()175 void WebSocket::Fail() {
176 closed_ = true;
177 // TODO(yhirano): The server SHOULD log the problem.
178 server_->Close(connection_->id());
179 }
180
SendErrorResponse(const std::string & message,const NetworkTrafficAnnotationTag traffic_annotation)181 void WebSocket::SendErrorResponse(
182 const std::string& message,
183 const NetworkTrafficAnnotationTag traffic_annotation) {
184 if (closed_)
185 return;
186 closed_ = true;
187 server_->Send500(connection_->id(), message, traffic_annotation);
188 }
189
190 } // namespace net
191