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