1*6777b538SAndroid Build Coastguard Worker // Copyright 2013 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/websockets/websocket_channel.h"
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Worker #include <limits.h> // for INT_MAX
8*6777b538SAndroid Build Coastguard Worker #include <stddef.h>
9*6777b538SAndroid Build Coastguard Worker #include <string.h>
10*6777b538SAndroid Build Coastguard Worker
11*6777b538SAndroid Build Coastguard Worker #include <algorithm>
12*6777b538SAndroid Build Coastguard Worker #include <iterator>
13*6777b538SAndroid Build Coastguard Worker #include <ostream>
14*6777b538SAndroid Build Coastguard Worker #include <string_view>
15*6777b538SAndroid Build Coastguard Worker #include <utility>
16*6777b538SAndroid Build Coastguard Worker #include <vector>
17*6777b538SAndroid Build Coastguard Worker
18*6777b538SAndroid Build Coastguard Worker #include "base/big_endian.h"
19*6777b538SAndroid Build Coastguard Worker #include "base/check.h"
20*6777b538SAndroid Build Coastguard Worker #include "base/check_op.h"
21*6777b538SAndroid Build Coastguard Worker #include "base/functional/bind.h"
22*6777b538SAndroid Build Coastguard Worker #include "base/location.h"
23*6777b538SAndroid Build Coastguard Worker #include "base/logging.h"
24*6777b538SAndroid Build Coastguard Worker #include "base/memory/raw_ptr.h"
25*6777b538SAndroid Build Coastguard Worker #include "base/numerics/byte_conversions.h"
26*6777b538SAndroid Build Coastguard Worker #include "base/numerics/safe_conversions.h"
27*6777b538SAndroid Build Coastguard Worker #include "base/ranges/algorithm.h"
28*6777b538SAndroid Build Coastguard Worker #include "base/strings/stringprintf.h"
29*6777b538SAndroid Build Coastguard Worker #include "base/time/time.h"
30*6777b538SAndroid Build Coastguard Worker #include "base/values.h"
31*6777b538SAndroid Build Coastguard Worker #include "net/base/io_buffer.h"
32*6777b538SAndroid Build Coastguard Worker #include "net/base/net_errors.h"
33*6777b538SAndroid Build Coastguard Worker #include "net/http/http_response_headers.h"
34*6777b538SAndroid Build Coastguard Worker #include "net/log/net_log_event_type.h"
35*6777b538SAndroid Build Coastguard Worker #include "net/log/net_log_with_source.h"
36*6777b538SAndroid Build Coastguard Worker #include "net/traffic_annotation/network_traffic_annotation.h"
37*6777b538SAndroid Build Coastguard Worker #include "net/websockets/websocket_errors.h"
38*6777b538SAndroid Build Coastguard Worker #include "net/websockets/websocket_event_interface.h"
39*6777b538SAndroid Build Coastguard Worker #include "net/websockets/websocket_frame.h"
40*6777b538SAndroid Build Coastguard Worker #include "net/websockets/websocket_handshake_request_info.h"
41*6777b538SAndroid Build Coastguard Worker #include "net/websockets/websocket_handshake_response_info.h"
42*6777b538SAndroid Build Coastguard Worker #include "net/websockets/websocket_stream.h"
43*6777b538SAndroid Build Coastguard Worker
44*6777b538SAndroid Build Coastguard Worker namespace net {
45*6777b538SAndroid Build Coastguard Worker class AuthChallengeInfo;
46*6777b538SAndroid Build Coastguard Worker class AuthCredentials;
47*6777b538SAndroid Build Coastguard Worker class SSLInfo;
48*6777b538SAndroid Build Coastguard Worker
49*6777b538SAndroid Build Coastguard Worker namespace {
50*6777b538SAndroid Build Coastguard Worker
51*6777b538SAndroid Build Coastguard Worker using base::StreamingUtf8Validator;
52*6777b538SAndroid Build Coastguard Worker
53*6777b538SAndroid Build Coastguard Worker constexpr size_t kWebSocketCloseCodeLength = 2;
54*6777b538SAndroid Build Coastguard Worker // Timeout for waiting for the server to acknowledge a closing handshake.
55*6777b538SAndroid Build Coastguard Worker constexpr int kClosingHandshakeTimeoutSeconds = 60;
56*6777b538SAndroid Build Coastguard Worker // We wait for the server to close the underlying connection as recommended in
57*6777b538SAndroid Build Coastguard Worker // https://tools.ietf.org/html/rfc6455#section-7.1.1
58*6777b538SAndroid Build Coastguard Worker // We don't use 2MSL since there're server implementations that don't follow
59*6777b538SAndroid Build Coastguard Worker // the recommendation and wait for the client to close the underlying
60*6777b538SAndroid Build Coastguard Worker // connection. It leads to unnecessarily long time before CloseEvent
61*6777b538SAndroid Build Coastguard Worker // invocation. We want to avoid this rather than strictly following the spec
62*6777b538SAndroid Build Coastguard Worker // recommendation.
63*6777b538SAndroid Build Coastguard Worker constexpr int kUnderlyingConnectionCloseTimeoutSeconds = 2;
64*6777b538SAndroid Build Coastguard Worker
65*6777b538SAndroid Build Coastguard Worker using ChannelState = WebSocketChannel::ChannelState;
66*6777b538SAndroid Build Coastguard Worker
67*6777b538SAndroid Build Coastguard Worker // Maximum close reason length = max control frame payload -
68*6777b538SAndroid Build Coastguard Worker // status code length
69*6777b538SAndroid Build Coastguard Worker // = 125 - 2
70*6777b538SAndroid Build Coastguard Worker constexpr size_t kMaximumCloseReasonLength = 125 - kWebSocketCloseCodeLength;
71*6777b538SAndroid Build Coastguard Worker
72*6777b538SAndroid Build Coastguard Worker // Check a close status code for strict compliance with RFC6455. This is only
73*6777b538SAndroid Build Coastguard Worker // used for close codes received from a renderer that we are intending to send
74*6777b538SAndroid Build Coastguard Worker // out over the network. See ParseClose() for the restrictions on incoming close
75*6777b538SAndroid Build Coastguard Worker // codes. The |code| parameter is type int for convenience of implementation;
76*6777b538SAndroid Build Coastguard Worker // the real type is uint16_t. Code 1005 is treated specially; it cannot be set
77*6777b538SAndroid Build Coastguard Worker // explicitly by Javascript but the renderer uses it to indicate we should send
78*6777b538SAndroid Build Coastguard Worker // a Close frame with no payload.
IsStrictlyValidCloseStatusCode(int code)79*6777b538SAndroid Build Coastguard Worker bool IsStrictlyValidCloseStatusCode(int code) {
80*6777b538SAndroid Build Coastguard Worker static constexpr int kInvalidRanges[] = {
81*6777b538SAndroid Build Coastguard Worker // [BAD, OK)
82*6777b538SAndroid Build Coastguard Worker 0, 1000, // 1000 is the first valid code
83*6777b538SAndroid Build Coastguard Worker 1006, 1007, // 1006 MUST NOT be set.
84*6777b538SAndroid Build Coastguard Worker 1014, 3000, // 1014 unassigned; 1015 up to 2999 are reserved.
85*6777b538SAndroid Build Coastguard Worker 5000, 65536, // Codes above 5000 are invalid.
86*6777b538SAndroid Build Coastguard Worker };
87*6777b538SAndroid Build Coastguard Worker const int* const kInvalidRangesEnd =
88*6777b538SAndroid Build Coastguard Worker kInvalidRanges + std::size(kInvalidRanges);
89*6777b538SAndroid Build Coastguard Worker
90*6777b538SAndroid Build Coastguard Worker DCHECK_GE(code, 0);
91*6777b538SAndroid Build Coastguard Worker DCHECK_LT(code, 65536);
92*6777b538SAndroid Build Coastguard Worker const int* upper = std::upper_bound(kInvalidRanges, kInvalidRangesEnd, code);
93*6777b538SAndroid Build Coastguard Worker DCHECK_NE(kInvalidRangesEnd, upper);
94*6777b538SAndroid Build Coastguard Worker DCHECK_GT(upper, kInvalidRanges);
95*6777b538SAndroid Build Coastguard Worker DCHECK_GT(*upper, code);
96*6777b538SAndroid Build Coastguard Worker DCHECK_LE(*(upper - 1), code);
97*6777b538SAndroid Build Coastguard Worker return ((upper - kInvalidRanges) % 2) == 0;
98*6777b538SAndroid Build Coastguard Worker }
99*6777b538SAndroid Build Coastguard Worker
100*6777b538SAndroid Build Coastguard Worker // Sets |name| to the name of the frame type for the given |opcode|. Note that
101*6777b538SAndroid Build Coastguard Worker // for all of Text, Binary and Continuation opcode, this method returns
102*6777b538SAndroid Build Coastguard Worker // "Data frame".
GetFrameTypeForOpcode(WebSocketFrameHeader::OpCode opcode,std::string * name)103*6777b538SAndroid Build Coastguard Worker void GetFrameTypeForOpcode(WebSocketFrameHeader::OpCode opcode,
104*6777b538SAndroid Build Coastguard Worker std::string* name) {
105*6777b538SAndroid Build Coastguard Worker switch (opcode) {
106*6777b538SAndroid Build Coastguard Worker case WebSocketFrameHeader::kOpCodeText: // fall-thru
107*6777b538SAndroid Build Coastguard Worker case WebSocketFrameHeader::kOpCodeBinary: // fall-thru
108*6777b538SAndroid Build Coastguard Worker case WebSocketFrameHeader::kOpCodeContinuation:
109*6777b538SAndroid Build Coastguard Worker *name = "Data frame";
110*6777b538SAndroid Build Coastguard Worker break;
111*6777b538SAndroid Build Coastguard Worker
112*6777b538SAndroid Build Coastguard Worker case WebSocketFrameHeader::kOpCodePing:
113*6777b538SAndroid Build Coastguard Worker *name = "Ping";
114*6777b538SAndroid Build Coastguard Worker break;
115*6777b538SAndroid Build Coastguard Worker
116*6777b538SAndroid Build Coastguard Worker case WebSocketFrameHeader::kOpCodePong:
117*6777b538SAndroid Build Coastguard Worker *name = "Pong";
118*6777b538SAndroid Build Coastguard Worker break;
119*6777b538SAndroid Build Coastguard Worker
120*6777b538SAndroid Build Coastguard Worker case WebSocketFrameHeader::kOpCodeClose:
121*6777b538SAndroid Build Coastguard Worker *name = "Close";
122*6777b538SAndroid Build Coastguard Worker break;
123*6777b538SAndroid Build Coastguard Worker
124*6777b538SAndroid Build Coastguard Worker default:
125*6777b538SAndroid Build Coastguard Worker *name = "Unknown frame type";
126*6777b538SAndroid Build Coastguard Worker break;
127*6777b538SAndroid Build Coastguard Worker }
128*6777b538SAndroid Build Coastguard Worker
129*6777b538SAndroid Build Coastguard Worker return;
130*6777b538SAndroid Build Coastguard Worker }
131*6777b538SAndroid Build Coastguard Worker
NetLogFailParam(uint16_t code,std::string_view reason,std::string_view message)132*6777b538SAndroid Build Coastguard Worker base::Value::Dict NetLogFailParam(uint16_t code,
133*6777b538SAndroid Build Coastguard Worker std::string_view reason,
134*6777b538SAndroid Build Coastguard Worker std::string_view message) {
135*6777b538SAndroid Build Coastguard Worker base::Value::Dict dict;
136*6777b538SAndroid Build Coastguard Worker dict.Set("code", code);
137*6777b538SAndroid Build Coastguard Worker dict.Set("reason", reason);
138*6777b538SAndroid Build Coastguard Worker dict.Set("internal_reason", message);
139*6777b538SAndroid Build Coastguard Worker return dict;
140*6777b538SAndroid Build Coastguard Worker }
141*6777b538SAndroid Build Coastguard Worker
142*6777b538SAndroid Build Coastguard Worker class DependentIOBuffer : public WrappedIOBuffer {
143*6777b538SAndroid Build Coastguard Worker public:
DependentIOBuffer(scoped_refptr<IOBufferWithSize> buffer,size_t offset)144*6777b538SAndroid Build Coastguard Worker DependentIOBuffer(scoped_refptr<IOBufferWithSize> buffer, size_t offset)
145*6777b538SAndroid Build Coastguard Worker : WrappedIOBuffer(buffer->span().subspan(offset)),
146*6777b538SAndroid Build Coastguard Worker buffer_(std::move(buffer)) {}
147*6777b538SAndroid Build Coastguard Worker
148*6777b538SAndroid Build Coastguard Worker private:
~DependentIOBuffer()149*6777b538SAndroid Build Coastguard Worker ~DependentIOBuffer() override {
150*6777b538SAndroid Build Coastguard Worker // Prevent `data_` from dangling should this destructor remove the
151*6777b538SAndroid Build Coastguard Worker // last reference to `buffer_`.
152*6777b538SAndroid Build Coastguard Worker data_ = nullptr;
153*6777b538SAndroid Build Coastguard Worker }
154*6777b538SAndroid Build Coastguard Worker
155*6777b538SAndroid Build Coastguard Worker scoped_refptr<IOBufferWithSize> buffer_;
156*6777b538SAndroid Build Coastguard Worker };
157*6777b538SAndroid Build Coastguard Worker
158*6777b538SAndroid Build Coastguard Worker } // namespace
159*6777b538SAndroid Build Coastguard Worker
160*6777b538SAndroid Build Coastguard Worker // A class to encapsulate a set of frames and information about the size of
161*6777b538SAndroid Build Coastguard Worker // those frames.
162*6777b538SAndroid Build Coastguard Worker class WebSocketChannel::SendBuffer {
163*6777b538SAndroid Build Coastguard Worker public:
164*6777b538SAndroid Build Coastguard Worker SendBuffer() = default;
165*6777b538SAndroid Build Coastguard Worker
166*6777b538SAndroid Build Coastguard Worker // Add a WebSocketFrame to the buffer and increase total_bytes_.
167*6777b538SAndroid Build Coastguard Worker void AddFrame(std::unique_ptr<WebSocketFrame> chunk,
168*6777b538SAndroid Build Coastguard Worker scoped_refptr<IOBuffer> buffer);
169*6777b538SAndroid Build Coastguard Worker
170*6777b538SAndroid Build Coastguard Worker // Return a pointer to the frames_ for write purposes.
frames()171*6777b538SAndroid Build Coastguard Worker std::vector<std::unique_ptr<WebSocketFrame>>* frames() { return &frames_; }
172*6777b538SAndroid Build Coastguard Worker
173*6777b538SAndroid Build Coastguard Worker private:
174*6777b538SAndroid Build Coastguard Worker // The frames_ that will be sent in the next call to WriteFrames().
175*6777b538SAndroid Build Coastguard Worker std::vector<std::unique_ptr<WebSocketFrame>> frames_;
176*6777b538SAndroid Build Coastguard Worker // References of each WebSocketFrame.data;
177*6777b538SAndroid Build Coastguard Worker std::vector<scoped_refptr<IOBuffer>> buffers_;
178*6777b538SAndroid Build Coastguard Worker
179*6777b538SAndroid Build Coastguard Worker // The total size of the payload data in |frames_|. This will be used to
180*6777b538SAndroid Build Coastguard Worker // measure the throughput of the link.
181*6777b538SAndroid Build Coastguard Worker // TODO(ricea): Measure the throughput of the link.
182*6777b538SAndroid Build Coastguard Worker uint64_t total_bytes_ = 0;
183*6777b538SAndroid Build Coastguard Worker };
184*6777b538SAndroid Build Coastguard Worker
AddFrame(std::unique_ptr<WebSocketFrame> frame,scoped_refptr<IOBuffer> buffer)185*6777b538SAndroid Build Coastguard Worker void WebSocketChannel::SendBuffer::AddFrame(
186*6777b538SAndroid Build Coastguard Worker std::unique_ptr<WebSocketFrame> frame,
187*6777b538SAndroid Build Coastguard Worker scoped_refptr<IOBuffer> buffer) {
188*6777b538SAndroid Build Coastguard Worker total_bytes_ += frame->header.payload_length;
189*6777b538SAndroid Build Coastguard Worker frames_.push_back(std::move(frame));
190*6777b538SAndroid Build Coastguard Worker buffers_.push_back(std::move(buffer));
191*6777b538SAndroid Build Coastguard Worker }
192*6777b538SAndroid Build Coastguard Worker
193*6777b538SAndroid Build Coastguard Worker // Implementation of WebSocketStream::ConnectDelegate that simply forwards the
194*6777b538SAndroid Build Coastguard Worker // calls on to the WebSocketChannel that created it.
195*6777b538SAndroid Build Coastguard Worker class WebSocketChannel::ConnectDelegate
196*6777b538SAndroid Build Coastguard Worker : public WebSocketStream::ConnectDelegate {
197*6777b538SAndroid Build Coastguard Worker public:
ConnectDelegate(WebSocketChannel * creator)198*6777b538SAndroid Build Coastguard Worker explicit ConnectDelegate(WebSocketChannel* creator) : creator_(creator) {}
199*6777b538SAndroid Build Coastguard Worker
200*6777b538SAndroid Build Coastguard Worker ConnectDelegate(const ConnectDelegate&) = delete;
201*6777b538SAndroid Build Coastguard Worker ConnectDelegate& operator=(const ConnectDelegate&) = delete;
202*6777b538SAndroid Build Coastguard Worker
OnCreateRequest(URLRequest * request)203*6777b538SAndroid Build Coastguard Worker void OnCreateRequest(URLRequest* request) override {
204*6777b538SAndroid Build Coastguard Worker creator_->OnCreateURLRequest(request);
205*6777b538SAndroid Build Coastguard Worker }
206*6777b538SAndroid Build Coastguard Worker
OnURLRequestConnected(URLRequest * request,const TransportInfo & info)207*6777b538SAndroid Build Coastguard Worker void OnURLRequestConnected(URLRequest* request,
208*6777b538SAndroid Build Coastguard Worker const TransportInfo& info) override {
209*6777b538SAndroid Build Coastguard Worker creator_->OnURLRequestConnected(request, info);
210*6777b538SAndroid Build Coastguard Worker }
211*6777b538SAndroid Build Coastguard Worker
OnSuccess(std::unique_ptr<WebSocketStream> stream,std::unique_ptr<WebSocketHandshakeResponseInfo> response)212*6777b538SAndroid Build Coastguard Worker void OnSuccess(
213*6777b538SAndroid Build Coastguard Worker std::unique_ptr<WebSocketStream> stream,
214*6777b538SAndroid Build Coastguard Worker std::unique_ptr<WebSocketHandshakeResponseInfo> response) override {
215*6777b538SAndroid Build Coastguard Worker creator_->OnConnectSuccess(std::move(stream), std::move(response));
216*6777b538SAndroid Build Coastguard Worker // |this| may have been deleted.
217*6777b538SAndroid Build Coastguard Worker }
218*6777b538SAndroid Build Coastguard Worker
OnFailure(const std::string & message,int net_error,std::optional<int> response_code)219*6777b538SAndroid Build Coastguard Worker void OnFailure(const std::string& message,
220*6777b538SAndroid Build Coastguard Worker int net_error,
221*6777b538SAndroid Build Coastguard Worker std::optional<int> response_code) override {
222*6777b538SAndroid Build Coastguard Worker creator_->OnConnectFailure(message, net_error, response_code);
223*6777b538SAndroid Build Coastguard Worker // |this| has been deleted.
224*6777b538SAndroid Build Coastguard Worker }
225*6777b538SAndroid Build Coastguard Worker
OnStartOpeningHandshake(std::unique_ptr<WebSocketHandshakeRequestInfo> request)226*6777b538SAndroid Build Coastguard Worker void OnStartOpeningHandshake(
227*6777b538SAndroid Build Coastguard Worker std::unique_ptr<WebSocketHandshakeRequestInfo> request) override {
228*6777b538SAndroid Build Coastguard Worker creator_->OnStartOpeningHandshake(std::move(request));
229*6777b538SAndroid Build Coastguard Worker }
230*6777b538SAndroid Build Coastguard Worker
OnSSLCertificateError(std::unique_ptr<WebSocketEventInterface::SSLErrorCallbacks> ssl_error_callbacks,int net_error,const SSLInfo & ssl_info,bool fatal)231*6777b538SAndroid Build Coastguard Worker void OnSSLCertificateError(
232*6777b538SAndroid Build Coastguard Worker std::unique_ptr<WebSocketEventInterface::SSLErrorCallbacks>
233*6777b538SAndroid Build Coastguard Worker ssl_error_callbacks,
234*6777b538SAndroid Build Coastguard Worker int net_error,
235*6777b538SAndroid Build Coastguard Worker const SSLInfo& ssl_info,
236*6777b538SAndroid Build Coastguard Worker bool fatal) override {
237*6777b538SAndroid Build Coastguard Worker creator_->OnSSLCertificateError(std::move(ssl_error_callbacks), net_error,
238*6777b538SAndroid Build Coastguard Worker ssl_info, fatal);
239*6777b538SAndroid Build Coastguard Worker }
240*6777b538SAndroid Build Coastguard Worker
OnAuthRequired(const AuthChallengeInfo & auth_info,scoped_refptr<HttpResponseHeaders> headers,const IPEndPoint & remote_endpoint,base::OnceCallback<void (const AuthCredentials *)> callback,std::optional<AuthCredentials> * credentials)241*6777b538SAndroid Build Coastguard Worker int OnAuthRequired(const AuthChallengeInfo& auth_info,
242*6777b538SAndroid Build Coastguard Worker scoped_refptr<HttpResponseHeaders> headers,
243*6777b538SAndroid Build Coastguard Worker const IPEndPoint& remote_endpoint,
244*6777b538SAndroid Build Coastguard Worker base::OnceCallback<void(const AuthCredentials*)> callback,
245*6777b538SAndroid Build Coastguard Worker std::optional<AuthCredentials>* credentials) override {
246*6777b538SAndroid Build Coastguard Worker return creator_->OnAuthRequired(auth_info, std::move(headers),
247*6777b538SAndroid Build Coastguard Worker remote_endpoint, std::move(callback),
248*6777b538SAndroid Build Coastguard Worker credentials);
249*6777b538SAndroid Build Coastguard Worker }
250*6777b538SAndroid Build Coastguard Worker
251*6777b538SAndroid Build Coastguard Worker private:
252*6777b538SAndroid Build Coastguard Worker // A pointer to the WebSocketChannel that created this object. There is no
253*6777b538SAndroid Build Coastguard Worker // danger of this pointer being stale, because deleting the WebSocketChannel
254*6777b538SAndroid Build Coastguard Worker // cancels the connect process, deleting this object and preventing its
255*6777b538SAndroid Build Coastguard Worker // callbacks from being called.
256*6777b538SAndroid Build Coastguard Worker const raw_ptr<WebSocketChannel, DanglingUntriaged> creator_;
257*6777b538SAndroid Build Coastguard Worker };
258*6777b538SAndroid Build Coastguard Worker
WebSocketChannel(std::unique_ptr<WebSocketEventInterface> event_interface,URLRequestContext * url_request_context)259*6777b538SAndroid Build Coastguard Worker WebSocketChannel::WebSocketChannel(
260*6777b538SAndroid Build Coastguard Worker std::unique_ptr<WebSocketEventInterface> event_interface,
261*6777b538SAndroid Build Coastguard Worker URLRequestContext* url_request_context)
262*6777b538SAndroid Build Coastguard Worker : event_interface_(std::move(event_interface)),
263*6777b538SAndroid Build Coastguard Worker url_request_context_(url_request_context),
264*6777b538SAndroid Build Coastguard Worker closing_handshake_timeout_(
265*6777b538SAndroid Build Coastguard Worker base::Seconds(kClosingHandshakeTimeoutSeconds)),
266*6777b538SAndroid Build Coastguard Worker underlying_connection_close_timeout_(
267*6777b538SAndroid Build Coastguard Worker base::Seconds(kUnderlyingConnectionCloseTimeoutSeconds)) {}
268*6777b538SAndroid Build Coastguard Worker
~WebSocketChannel()269*6777b538SAndroid Build Coastguard Worker WebSocketChannel::~WebSocketChannel() {
270*6777b538SAndroid Build Coastguard Worker // The stream may hold a pointer to read_frames_, and so it needs to be
271*6777b538SAndroid Build Coastguard Worker // destroyed first.
272*6777b538SAndroid Build Coastguard Worker stream_.reset();
273*6777b538SAndroid Build Coastguard Worker // The timer may have a callback pointing back to us, so stop it just in case
274*6777b538SAndroid Build Coastguard Worker // someone decides to run the event loop from their destructor.
275*6777b538SAndroid Build Coastguard Worker close_timer_.Stop();
276*6777b538SAndroid Build Coastguard Worker }
277*6777b538SAndroid Build Coastguard Worker
SendAddChannelRequest(const GURL & socket_url,const std::vector<std::string> & requested_subprotocols,const url::Origin & origin,const SiteForCookies & site_for_cookies,bool has_storage_access,const IsolationInfo & isolation_info,const HttpRequestHeaders & additional_headers,NetworkTrafficAnnotationTag traffic_annotation)278*6777b538SAndroid Build Coastguard Worker void WebSocketChannel::SendAddChannelRequest(
279*6777b538SAndroid Build Coastguard Worker const GURL& socket_url,
280*6777b538SAndroid Build Coastguard Worker const std::vector<std::string>& requested_subprotocols,
281*6777b538SAndroid Build Coastguard Worker const url::Origin& origin,
282*6777b538SAndroid Build Coastguard Worker const SiteForCookies& site_for_cookies,
283*6777b538SAndroid Build Coastguard Worker bool has_storage_access,
284*6777b538SAndroid Build Coastguard Worker const IsolationInfo& isolation_info,
285*6777b538SAndroid Build Coastguard Worker const HttpRequestHeaders& additional_headers,
286*6777b538SAndroid Build Coastguard Worker NetworkTrafficAnnotationTag traffic_annotation) {
287*6777b538SAndroid Build Coastguard Worker SendAddChannelRequestWithSuppliedCallback(
288*6777b538SAndroid Build Coastguard Worker socket_url, requested_subprotocols, origin, site_for_cookies,
289*6777b538SAndroid Build Coastguard Worker has_storage_access, isolation_info, additional_headers,
290*6777b538SAndroid Build Coastguard Worker traffic_annotation,
291*6777b538SAndroid Build Coastguard Worker base::BindOnce(&WebSocketStream::CreateAndConnectStream));
292*6777b538SAndroid Build Coastguard Worker }
293*6777b538SAndroid Build Coastguard Worker
SetState(State new_state)294*6777b538SAndroid Build Coastguard Worker void WebSocketChannel::SetState(State new_state) {
295*6777b538SAndroid Build Coastguard Worker DCHECK_NE(state_, new_state);
296*6777b538SAndroid Build Coastguard Worker
297*6777b538SAndroid Build Coastguard Worker state_ = new_state;
298*6777b538SAndroid Build Coastguard Worker }
299*6777b538SAndroid Build Coastguard Worker
InClosingState() const300*6777b538SAndroid Build Coastguard Worker bool WebSocketChannel::InClosingState() const {
301*6777b538SAndroid Build Coastguard Worker // The state RECV_CLOSED is not supported here, because it is only used in one
302*6777b538SAndroid Build Coastguard Worker // code path and should not leak into the code in general.
303*6777b538SAndroid Build Coastguard Worker DCHECK_NE(RECV_CLOSED, state_)
304*6777b538SAndroid Build Coastguard Worker << "InClosingState called with state_ == RECV_CLOSED";
305*6777b538SAndroid Build Coastguard Worker return state_ == SEND_CLOSED || state_ == CLOSE_WAIT || state_ == CLOSED;
306*6777b538SAndroid Build Coastguard Worker }
307*6777b538SAndroid Build Coastguard Worker
SendFrame(bool fin,WebSocketFrameHeader::OpCode op_code,scoped_refptr<IOBuffer> buffer,size_t buffer_size)308*6777b538SAndroid Build Coastguard Worker WebSocketChannel::ChannelState WebSocketChannel::SendFrame(
309*6777b538SAndroid Build Coastguard Worker bool fin,
310*6777b538SAndroid Build Coastguard Worker WebSocketFrameHeader::OpCode op_code,
311*6777b538SAndroid Build Coastguard Worker scoped_refptr<IOBuffer> buffer,
312*6777b538SAndroid Build Coastguard Worker size_t buffer_size) {
313*6777b538SAndroid Build Coastguard Worker DCHECK_LE(buffer_size, static_cast<size_t>(INT_MAX));
314*6777b538SAndroid Build Coastguard Worker DCHECK(stream_) << "Got SendFrame without a connection established; fin="
315*6777b538SAndroid Build Coastguard Worker << fin << " op_code=" << op_code
316*6777b538SAndroid Build Coastguard Worker << " buffer_size=" << buffer_size;
317*6777b538SAndroid Build Coastguard Worker
318*6777b538SAndroid Build Coastguard Worker if (InClosingState()) {
319*6777b538SAndroid Build Coastguard Worker DVLOG(1) << "SendFrame called in state " << state_
320*6777b538SAndroid Build Coastguard Worker << ". This may be a bug, or a harmless race.";
321*6777b538SAndroid Build Coastguard Worker return CHANNEL_ALIVE;
322*6777b538SAndroid Build Coastguard Worker }
323*6777b538SAndroid Build Coastguard Worker
324*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(state_, CONNECTED);
325*6777b538SAndroid Build Coastguard Worker
326*6777b538SAndroid Build Coastguard Worker DCHECK(WebSocketFrameHeader::IsKnownDataOpCode(op_code))
327*6777b538SAndroid Build Coastguard Worker << "Got SendFrame with bogus op_code " << op_code << " fin=" << fin
328*6777b538SAndroid Build Coastguard Worker << " buffer_size=" << buffer_size;
329*6777b538SAndroid Build Coastguard Worker
330*6777b538SAndroid Build Coastguard Worker if (op_code == WebSocketFrameHeader::kOpCodeText ||
331*6777b538SAndroid Build Coastguard Worker (op_code == WebSocketFrameHeader::kOpCodeContinuation &&
332*6777b538SAndroid Build Coastguard Worker sending_text_message_)) {
333*6777b538SAndroid Build Coastguard Worker StreamingUtf8Validator::State state = outgoing_utf8_validator_.AddBytes(
334*6777b538SAndroid Build Coastguard Worker base::make_span(buffer->bytes(), buffer_size));
335*6777b538SAndroid Build Coastguard Worker if (state == StreamingUtf8Validator::INVALID ||
336*6777b538SAndroid Build Coastguard Worker (state == StreamingUtf8Validator::VALID_MIDPOINT && fin)) {
337*6777b538SAndroid Build Coastguard Worker // TODO(ricea): Kill renderer.
338*6777b538SAndroid Build Coastguard Worker FailChannel("Browser sent a text frame containing invalid UTF-8",
339*6777b538SAndroid Build Coastguard Worker kWebSocketErrorGoingAway, "");
340*6777b538SAndroid Build Coastguard Worker return CHANNEL_DELETED;
341*6777b538SAndroid Build Coastguard Worker // |this| has been deleted.
342*6777b538SAndroid Build Coastguard Worker }
343*6777b538SAndroid Build Coastguard Worker sending_text_message_ = !fin;
344*6777b538SAndroid Build Coastguard Worker DCHECK(!fin || state == StreamingUtf8Validator::VALID_ENDPOINT);
345*6777b538SAndroid Build Coastguard Worker }
346*6777b538SAndroid Build Coastguard Worker
347*6777b538SAndroid Build Coastguard Worker return SendFrameInternal(fin, op_code, std::move(buffer), buffer_size);
348*6777b538SAndroid Build Coastguard Worker // |this| may have been deleted.
349*6777b538SAndroid Build Coastguard Worker }
350*6777b538SAndroid Build Coastguard Worker
StartClosingHandshake(uint16_t code,const std::string & reason)351*6777b538SAndroid Build Coastguard Worker ChannelState WebSocketChannel::StartClosingHandshake(
352*6777b538SAndroid Build Coastguard Worker uint16_t code,
353*6777b538SAndroid Build Coastguard Worker const std::string& reason) {
354*6777b538SAndroid Build Coastguard Worker if (InClosingState()) {
355*6777b538SAndroid Build Coastguard Worker // When the associated renderer process is killed while the channel is in
356*6777b538SAndroid Build Coastguard Worker // CLOSING state we reach here.
357*6777b538SAndroid Build Coastguard Worker DVLOG(1) << "StartClosingHandshake called in state " << state_
358*6777b538SAndroid Build Coastguard Worker << ". This may be a bug, or a harmless race.";
359*6777b538SAndroid Build Coastguard Worker return CHANNEL_ALIVE;
360*6777b538SAndroid Build Coastguard Worker }
361*6777b538SAndroid Build Coastguard Worker if (has_received_close_frame_) {
362*6777b538SAndroid Build Coastguard Worker // We reach here if the client wants to start a closing handshake while
363*6777b538SAndroid Build Coastguard Worker // the browser is waiting for the client to consume incoming data frames
364*6777b538SAndroid Build Coastguard Worker // before responding to a closing handshake initiated by the server.
365*6777b538SAndroid Build Coastguard Worker // As the client doesn't want the data frames any more, we can respond to
366*6777b538SAndroid Build Coastguard Worker // the closing handshake initiated by the server.
367*6777b538SAndroid Build Coastguard Worker return RespondToClosingHandshake();
368*6777b538SAndroid Build Coastguard Worker }
369*6777b538SAndroid Build Coastguard Worker if (state_ == CONNECTING) {
370*6777b538SAndroid Build Coastguard Worker // Abort the in-progress handshake and drop the connection immediately.
371*6777b538SAndroid Build Coastguard Worker stream_request_.reset();
372*6777b538SAndroid Build Coastguard Worker SetState(CLOSED);
373*6777b538SAndroid Build Coastguard Worker DoDropChannel(false, kWebSocketErrorAbnormalClosure, "");
374*6777b538SAndroid Build Coastguard Worker return CHANNEL_DELETED;
375*6777b538SAndroid Build Coastguard Worker }
376*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(state_, CONNECTED);
377*6777b538SAndroid Build Coastguard Worker
378*6777b538SAndroid Build Coastguard Worker DCHECK(!close_timer_.IsRunning());
379*6777b538SAndroid Build Coastguard Worker // This use of base::Unretained() is safe because we stop the timer in the
380*6777b538SAndroid Build Coastguard Worker // destructor.
381*6777b538SAndroid Build Coastguard Worker close_timer_.Start(
382*6777b538SAndroid Build Coastguard Worker FROM_HERE, closing_handshake_timeout_,
383*6777b538SAndroid Build Coastguard Worker base::BindOnce(&WebSocketChannel::CloseTimeout, base::Unretained(this)));
384*6777b538SAndroid Build Coastguard Worker
385*6777b538SAndroid Build Coastguard Worker // Javascript actually only permits 1000 and 3000-4999, but the implementation
386*6777b538SAndroid Build Coastguard Worker // itself may produce different codes. The length of |reason| is also checked
387*6777b538SAndroid Build Coastguard Worker // by Javascript.
388*6777b538SAndroid Build Coastguard Worker if (!IsStrictlyValidCloseStatusCode(code) ||
389*6777b538SAndroid Build Coastguard Worker reason.size() > kMaximumCloseReasonLength) {
390*6777b538SAndroid Build Coastguard Worker // "InternalServerError" is actually used for errors from any endpoint, per
391*6777b538SAndroid Build Coastguard Worker // errata 3227 to RFC6455. If the renderer is sending us an invalid code or
392*6777b538SAndroid Build Coastguard Worker // reason it must be malfunctioning in some way, and based on that we
393*6777b538SAndroid Build Coastguard Worker // interpret this as an internal error.
394*6777b538SAndroid Build Coastguard Worker if (SendClose(kWebSocketErrorInternalServerError, "") == CHANNEL_DELETED)
395*6777b538SAndroid Build Coastguard Worker return CHANNEL_DELETED;
396*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(CONNECTED, state_);
397*6777b538SAndroid Build Coastguard Worker SetState(SEND_CLOSED);
398*6777b538SAndroid Build Coastguard Worker return CHANNEL_ALIVE;
399*6777b538SAndroid Build Coastguard Worker }
400*6777b538SAndroid Build Coastguard Worker if (SendClose(code, StreamingUtf8Validator::Validate(reason)
401*6777b538SAndroid Build Coastguard Worker ? reason
402*6777b538SAndroid Build Coastguard Worker : std::string()) == CHANNEL_DELETED)
403*6777b538SAndroid Build Coastguard Worker return CHANNEL_DELETED;
404*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(CONNECTED, state_);
405*6777b538SAndroid Build Coastguard Worker SetState(SEND_CLOSED);
406*6777b538SAndroid Build Coastguard Worker return CHANNEL_ALIVE;
407*6777b538SAndroid Build Coastguard Worker }
408*6777b538SAndroid Build Coastguard Worker
SendAddChannelRequestForTesting(const GURL & socket_url,const std::vector<std::string> & requested_subprotocols,const url::Origin & origin,const SiteForCookies & site_for_cookies,bool has_storage_access,const IsolationInfo & isolation_info,const HttpRequestHeaders & additional_headers,NetworkTrafficAnnotationTag traffic_annotation,WebSocketStreamRequestCreationCallback callback)409*6777b538SAndroid Build Coastguard Worker void WebSocketChannel::SendAddChannelRequestForTesting(
410*6777b538SAndroid Build Coastguard Worker const GURL& socket_url,
411*6777b538SAndroid Build Coastguard Worker const std::vector<std::string>& requested_subprotocols,
412*6777b538SAndroid Build Coastguard Worker const url::Origin& origin,
413*6777b538SAndroid Build Coastguard Worker const SiteForCookies& site_for_cookies,
414*6777b538SAndroid Build Coastguard Worker bool has_storage_access,
415*6777b538SAndroid Build Coastguard Worker const IsolationInfo& isolation_info,
416*6777b538SAndroid Build Coastguard Worker const HttpRequestHeaders& additional_headers,
417*6777b538SAndroid Build Coastguard Worker NetworkTrafficAnnotationTag traffic_annotation,
418*6777b538SAndroid Build Coastguard Worker WebSocketStreamRequestCreationCallback callback) {
419*6777b538SAndroid Build Coastguard Worker SendAddChannelRequestWithSuppliedCallback(
420*6777b538SAndroid Build Coastguard Worker socket_url, requested_subprotocols, origin, site_for_cookies,
421*6777b538SAndroid Build Coastguard Worker has_storage_access, isolation_info, additional_headers,
422*6777b538SAndroid Build Coastguard Worker traffic_annotation, std::move(callback));
423*6777b538SAndroid Build Coastguard Worker }
424*6777b538SAndroid Build Coastguard Worker
SetClosingHandshakeTimeoutForTesting(base::TimeDelta delay)425*6777b538SAndroid Build Coastguard Worker void WebSocketChannel::SetClosingHandshakeTimeoutForTesting(
426*6777b538SAndroid Build Coastguard Worker base::TimeDelta delay) {
427*6777b538SAndroid Build Coastguard Worker closing_handshake_timeout_ = delay;
428*6777b538SAndroid Build Coastguard Worker }
429*6777b538SAndroid Build Coastguard Worker
SetUnderlyingConnectionCloseTimeoutForTesting(base::TimeDelta delay)430*6777b538SAndroid Build Coastguard Worker void WebSocketChannel::SetUnderlyingConnectionCloseTimeoutForTesting(
431*6777b538SAndroid Build Coastguard Worker base::TimeDelta delay) {
432*6777b538SAndroid Build Coastguard Worker underlying_connection_close_timeout_ = delay;
433*6777b538SAndroid Build Coastguard Worker }
434*6777b538SAndroid Build Coastguard Worker
SendAddChannelRequestWithSuppliedCallback(const GURL & socket_url,const std::vector<std::string> & requested_subprotocols,const url::Origin & origin,const SiteForCookies & site_for_cookies,bool has_storage_access,const IsolationInfo & isolation_info,const HttpRequestHeaders & additional_headers,NetworkTrafficAnnotationTag traffic_annotation,WebSocketStreamRequestCreationCallback callback)435*6777b538SAndroid Build Coastguard Worker void WebSocketChannel::SendAddChannelRequestWithSuppliedCallback(
436*6777b538SAndroid Build Coastguard Worker const GURL& socket_url,
437*6777b538SAndroid Build Coastguard Worker const std::vector<std::string>& requested_subprotocols,
438*6777b538SAndroid Build Coastguard Worker const url::Origin& origin,
439*6777b538SAndroid Build Coastguard Worker const SiteForCookies& site_for_cookies,
440*6777b538SAndroid Build Coastguard Worker bool has_storage_access,
441*6777b538SAndroid Build Coastguard Worker const IsolationInfo& isolation_info,
442*6777b538SAndroid Build Coastguard Worker const HttpRequestHeaders& additional_headers,
443*6777b538SAndroid Build Coastguard Worker NetworkTrafficAnnotationTag traffic_annotation,
444*6777b538SAndroid Build Coastguard Worker WebSocketStreamRequestCreationCallback callback) {
445*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(FRESHLY_CONSTRUCTED, state_);
446*6777b538SAndroid Build Coastguard Worker if (!socket_url.SchemeIsWSOrWSS()) {
447*6777b538SAndroid Build Coastguard Worker // TODO(ricea): Kill the renderer (this error should have been caught by
448*6777b538SAndroid Build Coastguard Worker // Javascript).
449*6777b538SAndroid Build Coastguard Worker event_interface_->OnFailChannel("Invalid scheme", ERR_FAILED, std::nullopt);
450*6777b538SAndroid Build Coastguard Worker // |this| is deleted here.
451*6777b538SAndroid Build Coastguard Worker return;
452*6777b538SAndroid Build Coastguard Worker }
453*6777b538SAndroid Build Coastguard Worker socket_url_ = socket_url;
454*6777b538SAndroid Build Coastguard Worker auto connect_delegate = std::make_unique<ConnectDelegate>(this);
455*6777b538SAndroid Build Coastguard Worker stream_request_ = std::move(callback).Run(
456*6777b538SAndroid Build Coastguard Worker socket_url_, requested_subprotocols, origin, site_for_cookies,
457*6777b538SAndroid Build Coastguard Worker has_storage_access, isolation_info, additional_headers,
458*6777b538SAndroid Build Coastguard Worker url_request_context_.get(), NetLogWithSource(), traffic_annotation,
459*6777b538SAndroid Build Coastguard Worker std::move(connect_delegate));
460*6777b538SAndroid Build Coastguard Worker SetState(CONNECTING);
461*6777b538SAndroid Build Coastguard Worker }
462*6777b538SAndroid Build Coastguard Worker
OnCreateURLRequest(URLRequest * request)463*6777b538SAndroid Build Coastguard Worker void WebSocketChannel::OnCreateURLRequest(URLRequest* request) {
464*6777b538SAndroid Build Coastguard Worker event_interface_->OnCreateURLRequest(request);
465*6777b538SAndroid Build Coastguard Worker }
466*6777b538SAndroid Build Coastguard Worker
OnURLRequestConnected(URLRequest * request,const TransportInfo & info)467*6777b538SAndroid Build Coastguard Worker void WebSocketChannel::OnURLRequestConnected(URLRequest* request,
468*6777b538SAndroid Build Coastguard Worker const TransportInfo& info) {
469*6777b538SAndroid Build Coastguard Worker event_interface_->OnURLRequestConnected(request, info);
470*6777b538SAndroid Build Coastguard Worker }
471*6777b538SAndroid Build Coastguard Worker
OnConnectSuccess(std::unique_ptr<WebSocketStream> stream,std::unique_ptr<WebSocketHandshakeResponseInfo> response)472*6777b538SAndroid Build Coastguard Worker void WebSocketChannel::OnConnectSuccess(
473*6777b538SAndroid Build Coastguard Worker std::unique_ptr<WebSocketStream> stream,
474*6777b538SAndroid Build Coastguard Worker std::unique_ptr<WebSocketHandshakeResponseInfo> response) {
475*6777b538SAndroid Build Coastguard Worker DCHECK(stream);
476*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(CONNECTING, state_);
477*6777b538SAndroid Build Coastguard Worker
478*6777b538SAndroid Build Coastguard Worker stream_ = std::move(stream);
479*6777b538SAndroid Build Coastguard Worker
480*6777b538SAndroid Build Coastguard Worker SetState(CONNECTED);
481*6777b538SAndroid Build Coastguard Worker
482*6777b538SAndroid Build Coastguard Worker // |stream_request_| is not used once the connection has succeeded.
483*6777b538SAndroid Build Coastguard Worker stream_request_.reset();
484*6777b538SAndroid Build Coastguard Worker
485*6777b538SAndroid Build Coastguard Worker event_interface_->OnAddChannelResponse(
486*6777b538SAndroid Build Coastguard Worker std::move(response), stream_->GetSubProtocol(), stream_->GetExtensions());
487*6777b538SAndroid Build Coastguard Worker // |this| may have been deleted after OnAddChannelResponse.
488*6777b538SAndroid Build Coastguard Worker }
489*6777b538SAndroid Build Coastguard Worker
OnConnectFailure(const std::string & message,int net_error,std::optional<int> response_code)490*6777b538SAndroid Build Coastguard Worker void WebSocketChannel::OnConnectFailure(const std::string& message,
491*6777b538SAndroid Build Coastguard Worker int net_error,
492*6777b538SAndroid Build Coastguard Worker std::optional<int> response_code) {
493*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(CONNECTING, state_);
494*6777b538SAndroid Build Coastguard Worker
495*6777b538SAndroid Build Coastguard Worker // Copy the message before we delete its owner.
496*6777b538SAndroid Build Coastguard Worker std::string message_copy = message;
497*6777b538SAndroid Build Coastguard Worker
498*6777b538SAndroid Build Coastguard Worker SetState(CLOSED);
499*6777b538SAndroid Build Coastguard Worker stream_request_.reset();
500*6777b538SAndroid Build Coastguard Worker
501*6777b538SAndroid Build Coastguard Worker event_interface_->OnFailChannel(message_copy, net_error, response_code);
502*6777b538SAndroid Build Coastguard Worker // |this| has been deleted.
503*6777b538SAndroid Build Coastguard Worker }
504*6777b538SAndroid Build Coastguard Worker
OnSSLCertificateError(std::unique_ptr<WebSocketEventInterface::SSLErrorCallbacks> ssl_error_callbacks,int net_error,const SSLInfo & ssl_info,bool fatal)505*6777b538SAndroid Build Coastguard Worker void WebSocketChannel::OnSSLCertificateError(
506*6777b538SAndroid Build Coastguard Worker std::unique_ptr<WebSocketEventInterface::SSLErrorCallbacks>
507*6777b538SAndroid Build Coastguard Worker ssl_error_callbacks,
508*6777b538SAndroid Build Coastguard Worker int net_error,
509*6777b538SAndroid Build Coastguard Worker const SSLInfo& ssl_info,
510*6777b538SAndroid Build Coastguard Worker bool fatal) {
511*6777b538SAndroid Build Coastguard Worker event_interface_->OnSSLCertificateError(
512*6777b538SAndroid Build Coastguard Worker std::move(ssl_error_callbacks), socket_url_, net_error, ssl_info, fatal);
513*6777b538SAndroid Build Coastguard Worker }
514*6777b538SAndroid Build Coastguard Worker
OnAuthRequired(const AuthChallengeInfo & auth_info,scoped_refptr<HttpResponseHeaders> response_headers,const IPEndPoint & remote_endpoint,base::OnceCallback<void (const AuthCredentials *)> callback,std::optional<AuthCredentials> * credentials)515*6777b538SAndroid Build Coastguard Worker int WebSocketChannel::OnAuthRequired(
516*6777b538SAndroid Build Coastguard Worker const AuthChallengeInfo& auth_info,
517*6777b538SAndroid Build Coastguard Worker scoped_refptr<HttpResponseHeaders> response_headers,
518*6777b538SAndroid Build Coastguard Worker const IPEndPoint& remote_endpoint,
519*6777b538SAndroid Build Coastguard Worker base::OnceCallback<void(const AuthCredentials*)> callback,
520*6777b538SAndroid Build Coastguard Worker std::optional<AuthCredentials>* credentials) {
521*6777b538SAndroid Build Coastguard Worker return event_interface_->OnAuthRequired(
522*6777b538SAndroid Build Coastguard Worker auth_info, std::move(response_headers), remote_endpoint,
523*6777b538SAndroid Build Coastguard Worker std::move(callback), credentials);
524*6777b538SAndroid Build Coastguard Worker }
525*6777b538SAndroid Build Coastguard Worker
OnStartOpeningHandshake(std::unique_ptr<WebSocketHandshakeRequestInfo> request)526*6777b538SAndroid Build Coastguard Worker void WebSocketChannel::OnStartOpeningHandshake(
527*6777b538SAndroid Build Coastguard Worker std::unique_ptr<WebSocketHandshakeRequestInfo> request) {
528*6777b538SAndroid Build Coastguard Worker event_interface_->OnStartOpeningHandshake(std::move(request));
529*6777b538SAndroid Build Coastguard Worker }
530*6777b538SAndroid Build Coastguard Worker
WriteFrames()531*6777b538SAndroid Build Coastguard Worker ChannelState WebSocketChannel::WriteFrames() {
532*6777b538SAndroid Build Coastguard Worker int result = OK;
533*6777b538SAndroid Build Coastguard Worker do {
534*6777b538SAndroid Build Coastguard Worker // This use of base::Unretained is safe because this object owns the
535*6777b538SAndroid Build Coastguard Worker // WebSocketStream and destroying it cancels all callbacks.
536*6777b538SAndroid Build Coastguard Worker result = stream_->WriteFrames(
537*6777b538SAndroid Build Coastguard Worker data_being_sent_->frames(),
538*6777b538SAndroid Build Coastguard Worker base::BindOnce(base::IgnoreResult(&WebSocketChannel::OnWriteDone),
539*6777b538SAndroid Build Coastguard Worker base::Unretained(this), false));
540*6777b538SAndroid Build Coastguard Worker if (result != ERR_IO_PENDING) {
541*6777b538SAndroid Build Coastguard Worker if (OnWriteDone(true, result) == CHANNEL_DELETED)
542*6777b538SAndroid Build Coastguard Worker return CHANNEL_DELETED;
543*6777b538SAndroid Build Coastguard Worker // OnWriteDone() returns CHANNEL_DELETED on error. Here |state_| is
544*6777b538SAndroid Build Coastguard Worker // guaranteed to be the same as before OnWriteDone() call.
545*6777b538SAndroid Build Coastguard Worker }
546*6777b538SAndroid Build Coastguard Worker } while (result == OK && data_being_sent_);
547*6777b538SAndroid Build Coastguard Worker return CHANNEL_ALIVE;
548*6777b538SAndroid Build Coastguard Worker }
549*6777b538SAndroid Build Coastguard Worker
OnWriteDone(bool synchronous,int result)550*6777b538SAndroid Build Coastguard Worker ChannelState WebSocketChannel::OnWriteDone(bool synchronous, int result) {
551*6777b538SAndroid Build Coastguard Worker DCHECK_NE(FRESHLY_CONSTRUCTED, state_);
552*6777b538SAndroid Build Coastguard Worker DCHECK_NE(CONNECTING, state_);
553*6777b538SAndroid Build Coastguard Worker DCHECK_NE(ERR_IO_PENDING, result);
554*6777b538SAndroid Build Coastguard Worker DCHECK(data_being_sent_);
555*6777b538SAndroid Build Coastguard Worker switch (result) {
556*6777b538SAndroid Build Coastguard Worker case OK:
557*6777b538SAndroid Build Coastguard Worker if (data_to_send_next_) {
558*6777b538SAndroid Build Coastguard Worker data_being_sent_ = std::move(data_to_send_next_);
559*6777b538SAndroid Build Coastguard Worker if (!synchronous)
560*6777b538SAndroid Build Coastguard Worker return WriteFrames();
561*6777b538SAndroid Build Coastguard Worker } else {
562*6777b538SAndroid Build Coastguard Worker data_being_sent_.reset();
563*6777b538SAndroid Build Coastguard Worker event_interface_->OnSendDataFrameDone();
564*6777b538SAndroid Build Coastguard Worker }
565*6777b538SAndroid Build Coastguard Worker return CHANNEL_ALIVE;
566*6777b538SAndroid Build Coastguard Worker
567*6777b538SAndroid Build Coastguard Worker // If a recoverable error condition existed, it would go here.
568*6777b538SAndroid Build Coastguard Worker
569*6777b538SAndroid Build Coastguard Worker default:
570*6777b538SAndroid Build Coastguard Worker DCHECK_LT(result, 0)
571*6777b538SAndroid Build Coastguard Worker << "WriteFrames() should only return OK or ERR_ codes";
572*6777b538SAndroid Build Coastguard Worker
573*6777b538SAndroid Build Coastguard Worker stream_->Close();
574*6777b538SAndroid Build Coastguard Worker SetState(CLOSED);
575*6777b538SAndroid Build Coastguard Worker DoDropChannel(false, kWebSocketErrorAbnormalClosure, "");
576*6777b538SAndroid Build Coastguard Worker return CHANNEL_DELETED;
577*6777b538SAndroid Build Coastguard Worker }
578*6777b538SAndroid Build Coastguard Worker }
579*6777b538SAndroid Build Coastguard Worker
ReadFrames()580*6777b538SAndroid Build Coastguard Worker ChannelState WebSocketChannel::ReadFrames() {
581*6777b538SAndroid Build Coastguard Worker DCHECK(stream_);
582*6777b538SAndroid Build Coastguard Worker DCHECK(state_ == CONNECTED || state_ == SEND_CLOSED || state_ == CLOSE_WAIT);
583*6777b538SAndroid Build Coastguard Worker DCHECK(read_frames_.empty());
584*6777b538SAndroid Build Coastguard Worker if (is_reading_) {
585*6777b538SAndroid Build Coastguard Worker return CHANNEL_ALIVE;
586*6777b538SAndroid Build Coastguard Worker }
587*6777b538SAndroid Build Coastguard Worker
588*6777b538SAndroid Build Coastguard Worker if (!InClosingState() && has_received_close_frame_) {
589*6777b538SAndroid Build Coastguard Worker DCHECK(!event_interface_->HasPendingDataFrames());
590*6777b538SAndroid Build Coastguard Worker // We've been waiting for the client to consume the frames before
591*6777b538SAndroid Build Coastguard Worker // responding to the closing handshake initiated by the server.
592*6777b538SAndroid Build Coastguard Worker if (RespondToClosingHandshake() == CHANNEL_DELETED) {
593*6777b538SAndroid Build Coastguard Worker return CHANNEL_DELETED;
594*6777b538SAndroid Build Coastguard Worker }
595*6777b538SAndroid Build Coastguard Worker }
596*6777b538SAndroid Build Coastguard Worker
597*6777b538SAndroid Build Coastguard Worker // TODO(crbug.com/999235): Remove this CHECK.
598*6777b538SAndroid Build Coastguard Worker CHECK(event_interface_);
599*6777b538SAndroid Build Coastguard Worker while (!event_interface_->HasPendingDataFrames()) {
600*6777b538SAndroid Build Coastguard Worker DCHECK(stream_);
601*6777b538SAndroid Build Coastguard Worker // This use of base::Unretained is safe because this object owns the
602*6777b538SAndroid Build Coastguard Worker // WebSocketStream, and any pending reads will be cancelled when it is
603*6777b538SAndroid Build Coastguard Worker // destroyed.
604*6777b538SAndroid Build Coastguard Worker const int result = stream_->ReadFrames(
605*6777b538SAndroid Build Coastguard Worker &read_frames_,
606*6777b538SAndroid Build Coastguard Worker base::BindOnce(base::IgnoreResult(&WebSocketChannel::OnReadDone),
607*6777b538SAndroid Build Coastguard Worker base::Unretained(this), false));
608*6777b538SAndroid Build Coastguard Worker if (result == ERR_IO_PENDING) {
609*6777b538SAndroid Build Coastguard Worker is_reading_ = true;
610*6777b538SAndroid Build Coastguard Worker return CHANNEL_ALIVE;
611*6777b538SAndroid Build Coastguard Worker }
612*6777b538SAndroid Build Coastguard Worker if (OnReadDone(true, result) == CHANNEL_DELETED) {
613*6777b538SAndroid Build Coastguard Worker return CHANNEL_DELETED;
614*6777b538SAndroid Build Coastguard Worker }
615*6777b538SAndroid Build Coastguard Worker DCHECK_NE(CLOSED, state_);
616*6777b538SAndroid Build Coastguard Worker // TODO(crbug.com/999235): Remove this CHECK.
617*6777b538SAndroid Build Coastguard Worker CHECK(event_interface_);
618*6777b538SAndroid Build Coastguard Worker }
619*6777b538SAndroid Build Coastguard Worker return CHANNEL_ALIVE;
620*6777b538SAndroid Build Coastguard Worker }
621*6777b538SAndroid Build Coastguard Worker
OnReadDone(bool synchronous,int result)622*6777b538SAndroid Build Coastguard Worker ChannelState WebSocketChannel::OnReadDone(bool synchronous, int result) {
623*6777b538SAndroid Build Coastguard Worker DVLOG(3) << "WebSocketChannel::OnReadDone synchronous?" << synchronous
624*6777b538SAndroid Build Coastguard Worker << ", result=" << result
625*6777b538SAndroid Build Coastguard Worker << ", read_frames_.size=" << read_frames_.size();
626*6777b538SAndroid Build Coastguard Worker DCHECK_NE(FRESHLY_CONSTRUCTED, state_);
627*6777b538SAndroid Build Coastguard Worker DCHECK_NE(CONNECTING, state_);
628*6777b538SAndroid Build Coastguard Worker DCHECK_NE(ERR_IO_PENDING, result);
629*6777b538SAndroid Build Coastguard Worker switch (result) {
630*6777b538SAndroid Build Coastguard Worker case OK:
631*6777b538SAndroid Build Coastguard Worker // ReadFrames() must use ERR_CONNECTION_CLOSED for a closed connection
632*6777b538SAndroid Build Coastguard Worker // with no data read, not an empty response.
633*6777b538SAndroid Build Coastguard Worker DCHECK(!read_frames_.empty())
634*6777b538SAndroid Build Coastguard Worker << "ReadFrames() returned OK, but nothing was read.";
635*6777b538SAndroid Build Coastguard Worker for (auto& read_frame : read_frames_) {
636*6777b538SAndroid Build Coastguard Worker if (HandleFrame(std::move(read_frame)) == CHANNEL_DELETED)
637*6777b538SAndroid Build Coastguard Worker return CHANNEL_DELETED;
638*6777b538SAndroid Build Coastguard Worker }
639*6777b538SAndroid Build Coastguard Worker read_frames_.clear();
640*6777b538SAndroid Build Coastguard Worker DCHECK_NE(CLOSED, state_);
641*6777b538SAndroid Build Coastguard Worker if (!synchronous) {
642*6777b538SAndroid Build Coastguard Worker is_reading_ = false;
643*6777b538SAndroid Build Coastguard Worker if (!event_interface_->HasPendingDataFrames()) {
644*6777b538SAndroid Build Coastguard Worker return ReadFrames();
645*6777b538SAndroid Build Coastguard Worker }
646*6777b538SAndroid Build Coastguard Worker }
647*6777b538SAndroid Build Coastguard Worker return CHANNEL_ALIVE;
648*6777b538SAndroid Build Coastguard Worker
649*6777b538SAndroid Build Coastguard Worker case ERR_WS_PROTOCOL_ERROR:
650*6777b538SAndroid Build Coastguard Worker // This could be kWebSocketErrorProtocolError (specifically, non-minimal
651*6777b538SAndroid Build Coastguard Worker // encoding of payload length) or kWebSocketErrorMessageTooBig, or an
652*6777b538SAndroid Build Coastguard Worker // extension-specific error.
653*6777b538SAndroid Build Coastguard Worker FailChannel("Invalid frame header", kWebSocketErrorProtocolError,
654*6777b538SAndroid Build Coastguard Worker "WebSocket Protocol Error");
655*6777b538SAndroid Build Coastguard Worker return CHANNEL_DELETED;
656*6777b538SAndroid Build Coastguard Worker
657*6777b538SAndroid Build Coastguard Worker default:
658*6777b538SAndroid Build Coastguard Worker DCHECK_LT(result, 0)
659*6777b538SAndroid Build Coastguard Worker << "ReadFrames() should only return OK or ERR_ codes";
660*6777b538SAndroid Build Coastguard Worker
661*6777b538SAndroid Build Coastguard Worker stream_->Close();
662*6777b538SAndroid Build Coastguard Worker SetState(CLOSED);
663*6777b538SAndroid Build Coastguard Worker
664*6777b538SAndroid Build Coastguard Worker uint16_t code = kWebSocketErrorAbnormalClosure;
665*6777b538SAndroid Build Coastguard Worker std::string reason = "";
666*6777b538SAndroid Build Coastguard Worker bool was_clean = false;
667*6777b538SAndroid Build Coastguard Worker if (has_received_close_frame_) {
668*6777b538SAndroid Build Coastguard Worker code = received_close_code_;
669*6777b538SAndroid Build Coastguard Worker reason = received_close_reason_;
670*6777b538SAndroid Build Coastguard Worker was_clean = (result == ERR_CONNECTION_CLOSED);
671*6777b538SAndroid Build Coastguard Worker }
672*6777b538SAndroid Build Coastguard Worker
673*6777b538SAndroid Build Coastguard Worker DoDropChannel(was_clean, code, reason);
674*6777b538SAndroid Build Coastguard Worker return CHANNEL_DELETED;
675*6777b538SAndroid Build Coastguard Worker }
676*6777b538SAndroid Build Coastguard Worker }
677*6777b538SAndroid Build Coastguard Worker
HandleFrame(std::unique_ptr<WebSocketFrame> frame)678*6777b538SAndroid Build Coastguard Worker ChannelState WebSocketChannel::HandleFrame(
679*6777b538SAndroid Build Coastguard Worker std::unique_ptr<WebSocketFrame> frame) {
680*6777b538SAndroid Build Coastguard Worker if (frame->header.masked) {
681*6777b538SAndroid Build Coastguard Worker // RFC6455 Section 5.1 "A client MUST close a connection if it detects a
682*6777b538SAndroid Build Coastguard Worker // masked frame."
683*6777b538SAndroid Build Coastguard Worker FailChannel(
684*6777b538SAndroid Build Coastguard Worker "A server must not mask any frames that it sends to the "
685*6777b538SAndroid Build Coastguard Worker "client.",
686*6777b538SAndroid Build Coastguard Worker kWebSocketErrorProtocolError, "Masked frame from server");
687*6777b538SAndroid Build Coastguard Worker return CHANNEL_DELETED;
688*6777b538SAndroid Build Coastguard Worker }
689*6777b538SAndroid Build Coastguard Worker const WebSocketFrameHeader::OpCode opcode = frame->header.opcode;
690*6777b538SAndroid Build Coastguard Worker DCHECK(!WebSocketFrameHeader::IsKnownControlOpCode(opcode) ||
691*6777b538SAndroid Build Coastguard Worker frame->header.final);
692*6777b538SAndroid Build Coastguard Worker if (frame->header.reserved1 || frame->header.reserved2 ||
693*6777b538SAndroid Build Coastguard Worker frame->header.reserved3) {
694*6777b538SAndroid Build Coastguard Worker FailChannel(
695*6777b538SAndroid Build Coastguard Worker base::StringPrintf("One or more reserved bits are on: reserved1 = %d, "
696*6777b538SAndroid Build Coastguard Worker "reserved2 = %d, reserved3 = %d",
697*6777b538SAndroid Build Coastguard Worker static_cast<int>(frame->header.reserved1),
698*6777b538SAndroid Build Coastguard Worker static_cast<int>(frame->header.reserved2),
699*6777b538SAndroid Build Coastguard Worker static_cast<int>(frame->header.reserved3)),
700*6777b538SAndroid Build Coastguard Worker kWebSocketErrorProtocolError, "Invalid reserved bit");
701*6777b538SAndroid Build Coastguard Worker return CHANNEL_DELETED;
702*6777b538SAndroid Build Coastguard Worker }
703*6777b538SAndroid Build Coastguard Worker
704*6777b538SAndroid Build Coastguard Worker // Respond to the frame appropriately to its type.
705*6777b538SAndroid Build Coastguard Worker return HandleFrameByState(
706*6777b538SAndroid Build Coastguard Worker opcode, frame->header.final,
707*6777b538SAndroid Build Coastguard Worker base::make_span(frame->payload, base::checked_cast<size_t>(
708*6777b538SAndroid Build Coastguard Worker frame->header.payload_length)));
709*6777b538SAndroid Build Coastguard Worker }
710*6777b538SAndroid Build Coastguard Worker
HandleFrameByState(const WebSocketFrameHeader::OpCode opcode,bool final,base::span<const char> payload)711*6777b538SAndroid Build Coastguard Worker ChannelState WebSocketChannel::HandleFrameByState(
712*6777b538SAndroid Build Coastguard Worker const WebSocketFrameHeader::OpCode opcode,
713*6777b538SAndroid Build Coastguard Worker bool final,
714*6777b538SAndroid Build Coastguard Worker base::span<const char> payload) {
715*6777b538SAndroid Build Coastguard Worker DCHECK_NE(RECV_CLOSED, state_)
716*6777b538SAndroid Build Coastguard Worker << "HandleFrame() does not support being called re-entrantly from within "
717*6777b538SAndroid Build Coastguard Worker "SendClose()";
718*6777b538SAndroid Build Coastguard Worker DCHECK_NE(CLOSED, state_);
719*6777b538SAndroid Build Coastguard Worker if (state_ == CLOSE_WAIT) {
720*6777b538SAndroid Build Coastguard Worker std::string frame_name;
721*6777b538SAndroid Build Coastguard Worker GetFrameTypeForOpcode(opcode, &frame_name);
722*6777b538SAndroid Build Coastguard Worker
723*6777b538SAndroid Build Coastguard Worker // FailChannel() won't send another Close frame.
724*6777b538SAndroid Build Coastguard Worker FailChannel(frame_name + " received after close",
725*6777b538SAndroid Build Coastguard Worker kWebSocketErrorProtocolError, "");
726*6777b538SAndroid Build Coastguard Worker return CHANNEL_DELETED;
727*6777b538SAndroid Build Coastguard Worker }
728*6777b538SAndroid Build Coastguard Worker switch (opcode) {
729*6777b538SAndroid Build Coastguard Worker case WebSocketFrameHeader::kOpCodeText: // fall-thru
730*6777b538SAndroid Build Coastguard Worker case WebSocketFrameHeader::kOpCodeBinary:
731*6777b538SAndroid Build Coastguard Worker case WebSocketFrameHeader::kOpCodeContinuation:
732*6777b538SAndroid Build Coastguard Worker return HandleDataFrame(opcode, final, std::move(payload));
733*6777b538SAndroid Build Coastguard Worker
734*6777b538SAndroid Build Coastguard Worker case WebSocketFrameHeader::kOpCodePing:
735*6777b538SAndroid Build Coastguard Worker DVLOG(1) << "Got Ping of size " << payload.size();
736*6777b538SAndroid Build Coastguard Worker if (state_ == CONNECTED) {
737*6777b538SAndroid Build Coastguard Worker auto buffer = base::MakeRefCounted<IOBufferWithSize>(payload.size());
738*6777b538SAndroid Build Coastguard Worker base::ranges::copy(payload, buffer->data());
739*6777b538SAndroid Build Coastguard Worker return SendFrameInternal(true, WebSocketFrameHeader::kOpCodePong,
740*6777b538SAndroid Build Coastguard Worker std::move(buffer), payload.size());
741*6777b538SAndroid Build Coastguard Worker }
742*6777b538SAndroid Build Coastguard Worker DVLOG(3) << "Ignored ping in state " << state_;
743*6777b538SAndroid Build Coastguard Worker return CHANNEL_ALIVE;
744*6777b538SAndroid Build Coastguard Worker
745*6777b538SAndroid Build Coastguard Worker case WebSocketFrameHeader::kOpCodePong:
746*6777b538SAndroid Build Coastguard Worker DVLOG(1) << "Got Pong of size " << payload.size();
747*6777b538SAndroid Build Coastguard Worker // There is no need to do anything with pong messages.
748*6777b538SAndroid Build Coastguard Worker return CHANNEL_ALIVE;
749*6777b538SAndroid Build Coastguard Worker
750*6777b538SAndroid Build Coastguard Worker case WebSocketFrameHeader::kOpCodeClose: {
751*6777b538SAndroid Build Coastguard Worker uint16_t code = kWebSocketNormalClosure;
752*6777b538SAndroid Build Coastguard Worker std::string reason;
753*6777b538SAndroid Build Coastguard Worker std::string message;
754*6777b538SAndroid Build Coastguard Worker if (!ParseClose(payload, &code, &reason, &message)) {
755*6777b538SAndroid Build Coastguard Worker FailChannel(message, code, reason);
756*6777b538SAndroid Build Coastguard Worker return CHANNEL_DELETED;
757*6777b538SAndroid Build Coastguard Worker }
758*6777b538SAndroid Build Coastguard Worker // TODO(ricea): Find a way to safely log the message from the close
759*6777b538SAndroid Build Coastguard Worker // message (escape control codes and so on).
760*6777b538SAndroid Build Coastguard Worker return HandleCloseFrame(code, reason);
761*6777b538SAndroid Build Coastguard Worker }
762*6777b538SAndroid Build Coastguard Worker
763*6777b538SAndroid Build Coastguard Worker default:
764*6777b538SAndroid Build Coastguard Worker FailChannel(base::StringPrintf("Unrecognized frame opcode: %d", opcode),
765*6777b538SAndroid Build Coastguard Worker kWebSocketErrorProtocolError, "Unknown opcode");
766*6777b538SAndroid Build Coastguard Worker return CHANNEL_DELETED;
767*6777b538SAndroid Build Coastguard Worker }
768*6777b538SAndroid Build Coastguard Worker }
769*6777b538SAndroid Build Coastguard Worker
HandleDataFrame(WebSocketFrameHeader::OpCode opcode,bool final,base::span<const char> payload)770*6777b538SAndroid Build Coastguard Worker ChannelState WebSocketChannel::HandleDataFrame(
771*6777b538SAndroid Build Coastguard Worker WebSocketFrameHeader::OpCode opcode,
772*6777b538SAndroid Build Coastguard Worker bool final,
773*6777b538SAndroid Build Coastguard Worker base::span<const char> payload) {
774*6777b538SAndroid Build Coastguard Worker DVLOG(3) << "WebSocketChannel::HandleDataFrame opcode=" << opcode
775*6777b538SAndroid Build Coastguard Worker << ", final?" << final << ", data=" << (void*)payload.data()
776*6777b538SAndroid Build Coastguard Worker << ", size=" << payload.size();
777*6777b538SAndroid Build Coastguard Worker if (state_ != CONNECTED) {
778*6777b538SAndroid Build Coastguard Worker DVLOG(3) << "Ignored data packet received in state " << state_;
779*6777b538SAndroid Build Coastguard Worker return CHANNEL_ALIVE;
780*6777b538SAndroid Build Coastguard Worker }
781*6777b538SAndroid Build Coastguard Worker if (has_received_close_frame_) {
782*6777b538SAndroid Build Coastguard Worker DVLOG(3) << "Ignored data packet as we've received a close frame.";
783*6777b538SAndroid Build Coastguard Worker return CHANNEL_ALIVE;
784*6777b538SAndroid Build Coastguard Worker }
785*6777b538SAndroid Build Coastguard Worker DCHECK(opcode == WebSocketFrameHeader::kOpCodeContinuation ||
786*6777b538SAndroid Build Coastguard Worker opcode == WebSocketFrameHeader::kOpCodeText ||
787*6777b538SAndroid Build Coastguard Worker opcode == WebSocketFrameHeader::kOpCodeBinary);
788*6777b538SAndroid Build Coastguard Worker const bool got_continuation =
789*6777b538SAndroid Build Coastguard Worker (opcode == WebSocketFrameHeader::kOpCodeContinuation);
790*6777b538SAndroid Build Coastguard Worker if (got_continuation != expecting_to_handle_continuation_) {
791*6777b538SAndroid Build Coastguard Worker const std::string console_log = got_continuation
792*6777b538SAndroid Build Coastguard Worker ? "Received unexpected continuation frame."
793*6777b538SAndroid Build Coastguard Worker : "Received start of new message but previous message is unfinished.";
794*6777b538SAndroid Build Coastguard Worker const std::string reason = got_continuation
795*6777b538SAndroid Build Coastguard Worker ? "Unexpected continuation"
796*6777b538SAndroid Build Coastguard Worker : "Previous data frame unfinished";
797*6777b538SAndroid Build Coastguard Worker FailChannel(console_log, kWebSocketErrorProtocolError, reason);
798*6777b538SAndroid Build Coastguard Worker return CHANNEL_DELETED;
799*6777b538SAndroid Build Coastguard Worker }
800*6777b538SAndroid Build Coastguard Worker expecting_to_handle_continuation_ = !final;
801*6777b538SAndroid Build Coastguard Worker WebSocketFrameHeader::OpCode opcode_to_send = opcode;
802*6777b538SAndroid Build Coastguard Worker if (!initial_frame_forwarded_ &&
803*6777b538SAndroid Build Coastguard Worker opcode == WebSocketFrameHeader::kOpCodeContinuation) {
804*6777b538SAndroid Build Coastguard Worker opcode_to_send = receiving_text_message_
805*6777b538SAndroid Build Coastguard Worker ? WebSocketFrameHeader::kOpCodeText
806*6777b538SAndroid Build Coastguard Worker : WebSocketFrameHeader::kOpCodeBinary;
807*6777b538SAndroid Build Coastguard Worker }
808*6777b538SAndroid Build Coastguard Worker if (opcode == WebSocketFrameHeader::kOpCodeText ||
809*6777b538SAndroid Build Coastguard Worker (opcode == WebSocketFrameHeader::kOpCodeContinuation &&
810*6777b538SAndroid Build Coastguard Worker receiving_text_message_)) {
811*6777b538SAndroid Build Coastguard Worker // This call is not redundant when size == 0 because it tells us what
812*6777b538SAndroid Build Coastguard Worker // the current state is.
813*6777b538SAndroid Build Coastguard Worker StreamingUtf8Validator::State state =
814*6777b538SAndroid Build Coastguard Worker incoming_utf8_validator_.AddBytes(base::as_byte_span(payload));
815*6777b538SAndroid Build Coastguard Worker if (state == StreamingUtf8Validator::INVALID ||
816*6777b538SAndroid Build Coastguard Worker (state == StreamingUtf8Validator::VALID_MIDPOINT && final)) {
817*6777b538SAndroid Build Coastguard Worker FailChannel("Could not decode a text frame as UTF-8.",
818*6777b538SAndroid Build Coastguard Worker kWebSocketErrorProtocolError, "Invalid UTF-8 in text frame");
819*6777b538SAndroid Build Coastguard Worker return CHANNEL_DELETED;
820*6777b538SAndroid Build Coastguard Worker }
821*6777b538SAndroid Build Coastguard Worker receiving_text_message_ = !final;
822*6777b538SAndroid Build Coastguard Worker DCHECK(!final || state == StreamingUtf8Validator::VALID_ENDPOINT);
823*6777b538SAndroid Build Coastguard Worker }
824*6777b538SAndroid Build Coastguard Worker if (payload.size() == 0U && !final)
825*6777b538SAndroid Build Coastguard Worker return CHANNEL_ALIVE;
826*6777b538SAndroid Build Coastguard Worker
827*6777b538SAndroid Build Coastguard Worker initial_frame_forwarded_ = !final;
828*6777b538SAndroid Build Coastguard Worker // Sends the received frame to the renderer process.
829*6777b538SAndroid Build Coastguard Worker event_interface_->OnDataFrame(final, opcode_to_send, payload);
830*6777b538SAndroid Build Coastguard Worker return CHANNEL_ALIVE;
831*6777b538SAndroid Build Coastguard Worker }
832*6777b538SAndroid Build Coastguard Worker
HandleCloseFrame(uint16_t code,const std::string & reason)833*6777b538SAndroid Build Coastguard Worker ChannelState WebSocketChannel::HandleCloseFrame(uint16_t code,
834*6777b538SAndroid Build Coastguard Worker const std::string& reason) {
835*6777b538SAndroid Build Coastguard Worker DVLOG(1) << "Got Close with code " << code;
836*6777b538SAndroid Build Coastguard Worker switch (state_) {
837*6777b538SAndroid Build Coastguard Worker case CONNECTED:
838*6777b538SAndroid Build Coastguard Worker has_received_close_frame_ = true;
839*6777b538SAndroid Build Coastguard Worker received_close_code_ = code;
840*6777b538SAndroid Build Coastguard Worker received_close_reason_ = reason;
841*6777b538SAndroid Build Coastguard Worker if (event_interface_->HasPendingDataFrames()) {
842*6777b538SAndroid Build Coastguard Worker // We have some data to be sent to the renderer before sending this
843*6777b538SAndroid Build Coastguard Worker // frame.
844*6777b538SAndroid Build Coastguard Worker return CHANNEL_ALIVE;
845*6777b538SAndroid Build Coastguard Worker }
846*6777b538SAndroid Build Coastguard Worker return RespondToClosingHandshake();
847*6777b538SAndroid Build Coastguard Worker
848*6777b538SAndroid Build Coastguard Worker case SEND_CLOSED:
849*6777b538SAndroid Build Coastguard Worker SetState(CLOSE_WAIT);
850*6777b538SAndroid Build Coastguard Worker DCHECK(close_timer_.IsRunning());
851*6777b538SAndroid Build Coastguard Worker close_timer_.Stop();
852*6777b538SAndroid Build Coastguard Worker // This use of base::Unretained() is safe because we stop the timer
853*6777b538SAndroid Build Coastguard Worker // in the destructor.
854*6777b538SAndroid Build Coastguard Worker close_timer_.Start(FROM_HERE, underlying_connection_close_timeout_,
855*6777b538SAndroid Build Coastguard Worker base::BindOnce(&WebSocketChannel::CloseTimeout,
856*6777b538SAndroid Build Coastguard Worker base::Unretained(this)));
857*6777b538SAndroid Build Coastguard Worker
858*6777b538SAndroid Build Coastguard Worker // From RFC6455 section 7.1.5: "Each endpoint
859*6777b538SAndroid Build Coastguard Worker // will see the status code sent by the other end as _The WebSocket
860*6777b538SAndroid Build Coastguard Worker // Connection Close Code_."
861*6777b538SAndroid Build Coastguard Worker has_received_close_frame_ = true;
862*6777b538SAndroid Build Coastguard Worker received_close_code_ = code;
863*6777b538SAndroid Build Coastguard Worker received_close_reason_ = reason;
864*6777b538SAndroid Build Coastguard Worker break;
865*6777b538SAndroid Build Coastguard Worker
866*6777b538SAndroid Build Coastguard Worker default:
867*6777b538SAndroid Build Coastguard Worker LOG(DFATAL) << "Got Close in unexpected state " << state_;
868*6777b538SAndroid Build Coastguard Worker break;
869*6777b538SAndroid Build Coastguard Worker }
870*6777b538SAndroid Build Coastguard Worker return CHANNEL_ALIVE;
871*6777b538SAndroid Build Coastguard Worker }
872*6777b538SAndroid Build Coastguard Worker
RespondToClosingHandshake()873*6777b538SAndroid Build Coastguard Worker ChannelState WebSocketChannel::RespondToClosingHandshake() {
874*6777b538SAndroid Build Coastguard Worker DCHECK(has_received_close_frame_);
875*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(CONNECTED, state_);
876*6777b538SAndroid Build Coastguard Worker SetState(RECV_CLOSED);
877*6777b538SAndroid Build Coastguard Worker if (SendClose(received_close_code_, received_close_reason_) ==
878*6777b538SAndroid Build Coastguard Worker CHANNEL_DELETED)
879*6777b538SAndroid Build Coastguard Worker return CHANNEL_DELETED;
880*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(RECV_CLOSED, state_);
881*6777b538SAndroid Build Coastguard Worker
882*6777b538SAndroid Build Coastguard Worker SetState(CLOSE_WAIT);
883*6777b538SAndroid Build Coastguard Worker DCHECK(!close_timer_.IsRunning());
884*6777b538SAndroid Build Coastguard Worker // This use of base::Unretained() is safe because we stop the timer
885*6777b538SAndroid Build Coastguard Worker // in the destructor.
886*6777b538SAndroid Build Coastguard Worker close_timer_.Start(
887*6777b538SAndroid Build Coastguard Worker FROM_HERE, underlying_connection_close_timeout_,
888*6777b538SAndroid Build Coastguard Worker base::BindOnce(&WebSocketChannel::CloseTimeout, base::Unretained(this)));
889*6777b538SAndroid Build Coastguard Worker
890*6777b538SAndroid Build Coastguard Worker event_interface_->OnClosingHandshake();
891*6777b538SAndroid Build Coastguard Worker return CHANNEL_ALIVE;
892*6777b538SAndroid Build Coastguard Worker }
893*6777b538SAndroid Build Coastguard Worker
SendFrameInternal(bool fin,WebSocketFrameHeader::OpCode op_code,scoped_refptr<IOBuffer> buffer,uint64_t buffer_size)894*6777b538SAndroid Build Coastguard Worker ChannelState WebSocketChannel::SendFrameInternal(
895*6777b538SAndroid Build Coastguard Worker bool fin,
896*6777b538SAndroid Build Coastguard Worker WebSocketFrameHeader::OpCode op_code,
897*6777b538SAndroid Build Coastguard Worker scoped_refptr<IOBuffer> buffer,
898*6777b538SAndroid Build Coastguard Worker uint64_t buffer_size) {
899*6777b538SAndroid Build Coastguard Worker DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED);
900*6777b538SAndroid Build Coastguard Worker DCHECK(stream_);
901*6777b538SAndroid Build Coastguard Worker
902*6777b538SAndroid Build Coastguard Worker auto frame = std::make_unique<WebSocketFrame>(op_code);
903*6777b538SAndroid Build Coastguard Worker WebSocketFrameHeader& header = frame->header;
904*6777b538SAndroid Build Coastguard Worker header.final = fin;
905*6777b538SAndroid Build Coastguard Worker header.masked = true;
906*6777b538SAndroid Build Coastguard Worker header.payload_length = buffer_size;
907*6777b538SAndroid Build Coastguard Worker frame->payload = buffer->data();
908*6777b538SAndroid Build Coastguard Worker
909*6777b538SAndroid Build Coastguard Worker if (data_being_sent_) {
910*6777b538SAndroid Build Coastguard Worker // Either the link to the WebSocket server is saturated, or several messages
911*6777b538SAndroid Build Coastguard Worker // are being sent in a batch.
912*6777b538SAndroid Build Coastguard Worker if (!data_to_send_next_)
913*6777b538SAndroid Build Coastguard Worker data_to_send_next_ = std::make_unique<SendBuffer>();
914*6777b538SAndroid Build Coastguard Worker data_to_send_next_->AddFrame(std::move(frame), std::move(buffer));
915*6777b538SAndroid Build Coastguard Worker return CHANNEL_ALIVE;
916*6777b538SAndroid Build Coastguard Worker }
917*6777b538SAndroid Build Coastguard Worker
918*6777b538SAndroid Build Coastguard Worker data_being_sent_ = std::make_unique<SendBuffer>();
919*6777b538SAndroid Build Coastguard Worker data_being_sent_->AddFrame(std::move(frame), std::move(buffer));
920*6777b538SAndroid Build Coastguard Worker return WriteFrames();
921*6777b538SAndroid Build Coastguard Worker }
922*6777b538SAndroid Build Coastguard Worker
FailChannel(const std::string & message,uint16_t code,const std::string & reason)923*6777b538SAndroid Build Coastguard Worker void WebSocketChannel::FailChannel(const std::string& message,
924*6777b538SAndroid Build Coastguard Worker uint16_t code,
925*6777b538SAndroid Build Coastguard Worker const std::string& reason) {
926*6777b538SAndroid Build Coastguard Worker DCHECK_NE(FRESHLY_CONSTRUCTED, state_);
927*6777b538SAndroid Build Coastguard Worker DCHECK_NE(CONNECTING, state_);
928*6777b538SAndroid Build Coastguard Worker DCHECK_NE(CLOSED, state_);
929*6777b538SAndroid Build Coastguard Worker
930*6777b538SAndroid Build Coastguard Worker stream_->GetNetLogWithSource().AddEvent(
931*6777b538SAndroid Build Coastguard Worker net::NetLogEventType::WEBSOCKET_INVALID_FRAME,
932*6777b538SAndroid Build Coastguard Worker [&] { return NetLogFailParam(code, reason, message); });
933*6777b538SAndroid Build Coastguard Worker
934*6777b538SAndroid Build Coastguard Worker if (state_ == CONNECTED) {
935*6777b538SAndroid Build Coastguard Worker if (SendClose(code, reason) == CHANNEL_DELETED)
936*6777b538SAndroid Build Coastguard Worker return;
937*6777b538SAndroid Build Coastguard Worker }
938*6777b538SAndroid Build Coastguard Worker
939*6777b538SAndroid Build Coastguard Worker // Careful study of RFC6455 section 7.1.7 and 7.1.1 indicates the browser
940*6777b538SAndroid Build Coastguard Worker // should close the connection itself without waiting for the closing
941*6777b538SAndroid Build Coastguard Worker // handshake.
942*6777b538SAndroid Build Coastguard Worker stream_->Close();
943*6777b538SAndroid Build Coastguard Worker SetState(CLOSED);
944*6777b538SAndroid Build Coastguard Worker event_interface_->OnFailChannel(message, ERR_FAILED, std::nullopt);
945*6777b538SAndroid Build Coastguard Worker }
946*6777b538SAndroid Build Coastguard Worker
SendClose(uint16_t code,const std::string & reason)947*6777b538SAndroid Build Coastguard Worker ChannelState WebSocketChannel::SendClose(uint16_t code,
948*6777b538SAndroid Build Coastguard Worker const std::string& reason) {
949*6777b538SAndroid Build Coastguard Worker DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED);
950*6777b538SAndroid Build Coastguard Worker DCHECK_LE(reason.size(), kMaximumCloseReasonLength);
951*6777b538SAndroid Build Coastguard Worker scoped_refptr<IOBuffer> body;
952*6777b538SAndroid Build Coastguard Worker uint64_t size = 0;
953*6777b538SAndroid Build Coastguard Worker if (code == kWebSocketErrorNoStatusReceived) {
954*6777b538SAndroid Build Coastguard Worker // Special case: translate kWebSocketErrorNoStatusReceived into a Close
955*6777b538SAndroid Build Coastguard Worker // frame with no payload.
956*6777b538SAndroid Build Coastguard Worker DCHECK(reason.empty());
957*6777b538SAndroid Build Coastguard Worker body = base::MakeRefCounted<IOBufferWithSize>();
958*6777b538SAndroid Build Coastguard Worker } else {
959*6777b538SAndroid Build Coastguard Worker const size_t payload_length = kWebSocketCloseCodeLength + reason.length();
960*6777b538SAndroid Build Coastguard Worker body = base::MakeRefCounted<IOBufferWithSize>(payload_length);
961*6777b538SAndroid Build Coastguard Worker size = payload_length;
962*6777b538SAndroid Build Coastguard Worker auto [code_span, body_span] =
963*6777b538SAndroid Build Coastguard Worker body->span().split_at<kWebSocketCloseCodeLength>();
964*6777b538SAndroid Build Coastguard Worker base::as_writable_bytes(code_span).copy_from(base::U16ToBigEndian(code));
965*6777b538SAndroid Build Coastguard Worker static_assert(sizeof(code) == kWebSocketCloseCodeLength,
966*6777b538SAndroid Build Coastguard Worker "they should both be two");
967*6777b538SAndroid Build Coastguard Worker body_span.copy_from(reason);
968*6777b538SAndroid Build Coastguard Worker }
969*6777b538SAndroid Build Coastguard Worker
970*6777b538SAndroid Build Coastguard Worker return SendFrameInternal(true, WebSocketFrameHeader::kOpCodeClose,
971*6777b538SAndroid Build Coastguard Worker std::move(body), size);
972*6777b538SAndroid Build Coastguard Worker }
973*6777b538SAndroid Build Coastguard Worker
ParseClose(base::span<const char> payload,uint16_t * code,std::string * reason,std::string * message)974*6777b538SAndroid Build Coastguard Worker bool WebSocketChannel::ParseClose(base::span<const char> payload,
975*6777b538SAndroid Build Coastguard Worker uint16_t* code,
976*6777b538SAndroid Build Coastguard Worker std::string* reason,
977*6777b538SAndroid Build Coastguard Worker std::string* message) {
978*6777b538SAndroid Build Coastguard Worker const uint64_t size = static_cast<uint64_t>(payload.size());
979*6777b538SAndroid Build Coastguard Worker reason->clear();
980*6777b538SAndroid Build Coastguard Worker if (size < kWebSocketCloseCodeLength) {
981*6777b538SAndroid Build Coastguard Worker if (size == 0U) {
982*6777b538SAndroid Build Coastguard Worker *code = kWebSocketErrorNoStatusReceived;
983*6777b538SAndroid Build Coastguard Worker return true;
984*6777b538SAndroid Build Coastguard Worker }
985*6777b538SAndroid Build Coastguard Worker
986*6777b538SAndroid Build Coastguard Worker DVLOG(1) << "Close frame with payload size " << size << " received "
987*6777b538SAndroid Build Coastguard Worker << "(the first byte is " << std::hex
988*6777b538SAndroid Build Coastguard Worker << static_cast<int>(payload[0]) << ")";
989*6777b538SAndroid Build Coastguard Worker *code = kWebSocketErrorProtocolError;
990*6777b538SAndroid Build Coastguard Worker *message =
991*6777b538SAndroid Build Coastguard Worker "Received a broken close frame containing an invalid size body.";
992*6777b538SAndroid Build Coastguard Worker return false;
993*6777b538SAndroid Build Coastguard Worker }
994*6777b538SAndroid Build Coastguard Worker
995*6777b538SAndroid Build Coastguard Worker const char* data = payload.data();
996*6777b538SAndroid Build Coastguard Worker uint16_t unchecked_code =
997*6777b538SAndroid Build Coastguard Worker base::U16FromBigEndian(base::as_byte_span(payload).first<2>());
998*6777b538SAndroid Build Coastguard Worker static_assert(sizeof(unchecked_code) == kWebSocketCloseCodeLength,
999*6777b538SAndroid Build Coastguard Worker "they should both be two bytes");
1000*6777b538SAndroid Build Coastguard Worker
1001*6777b538SAndroid Build Coastguard Worker switch (unchecked_code) {
1002*6777b538SAndroid Build Coastguard Worker case kWebSocketErrorNoStatusReceived:
1003*6777b538SAndroid Build Coastguard Worker case kWebSocketErrorAbnormalClosure:
1004*6777b538SAndroid Build Coastguard Worker case kWebSocketErrorTlsHandshake:
1005*6777b538SAndroid Build Coastguard Worker *code = kWebSocketErrorProtocolError;
1006*6777b538SAndroid Build Coastguard Worker *message =
1007*6777b538SAndroid Build Coastguard Worker "Received a broken close frame containing a reserved status code.";
1008*6777b538SAndroid Build Coastguard Worker return false;
1009*6777b538SAndroid Build Coastguard Worker
1010*6777b538SAndroid Build Coastguard Worker default:
1011*6777b538SAndroid Build Coastguard Worker *code = unchecked_code;
1012*6777b538SAndroid Build Coastguard Worker break;
1013*6777b538SAndroid Build Coastguard Worker }
1014*6777b538SAndroid Build Coastguard Worker
1015*6777b538SAndroid Build Coastguard Worker std::string text(data + kWebSocketCloseCodeLength, data + size);
1016*6777b538SAndroid Build Coastguard Worker if (StreamingUtf8Validator::Validate(text)) {
1017*6777b538SAndroid Build Coastguard Worker reason->swap(text);
1018*6777b538SAndroid Build Coastguard Worker return true;
1019*6777b538SAndroid Build Coastguard Worker }
1020*6777b538SAndroid Build Coastguard Worker
1021*6777b538SAndroid Build Coastguard Worker *code = kWebSocketErrorProtocolError;
1022*6777b538SAndroid Build Coastguard Worker *reason = "Invalid UTF-8 in Close frame";
1023*6777b538SAndroid Build Coastguard Worker *message = "Received a broken close frame containing invalid UTF-8.";
1024*6777b538SAndroid Build Coastguard Worker return false;
1025*6777b538SAndroid Build Coastguard Worker }
1026*6777b538SAndroid Build Coastguard Worker
DoDropChannel(bool was_clean,uint16_t code,const std::string & reason)1027*6777b538SAndroid Build Coastguard Worker void WebSocketChannel::DoDropChannel(bool was_clean,
1028*6777b538SAndroid Build Coastguard Worker uint16_t code,
1029*6777b538SAndroid Build Coastguard Worker const std::string& reason) {
1030*6777b538SAndroid Build Coastguard Worker event_interface_->OnDropChannel(was_clean, code, reason);
1031*6777b538SAndroid Build Coastguard Worker }
1032*6777b538SAndroid Build Coastguard Worker
CloseTimeout()1033*6777b538SAndroid Build Coastguard Worker void WebSocketChannel::CloseTimeout() {
1034*6777b538SAndroid Build Coastguard Worker stream_->GetNetLogWithSource().AddEvent(
1035*6777b538SAndroid Build Coastguard Worker net::NetLogEventType::WEBSOCKET_CLOSE_TIMEOUT);
1036*6777b538SAndroid Build Coastguard Worker stream_->Close();
1037*6777b538SAndroid Build Coastguard Worker SetState(CLOSED);
1038*6777b538SAndroid Build Coastguard Worker if (has_received_close_frame_) {
1039*6777b538SAndroid Build Coastguard Worker DoDropChannel(true, received_close_code_, received_close_reason_);
1040*6777b538SAndroid Build Coastguard Worker } else {
1041*6777b538SAndroid Build Coastguard Worker DoDropChannel(false, kWebSocketErrorAbnormalClosure, "");
1042*6777b538SAndroid Build Coastguard Worker }
1043*6777b538SAndroid Build Coastguard Worker // |this| has been deleted.
1044*6777b538SAndroid Build Coastguard Worker }
1045*6777b538SAndroid Build Coastguard Worker
1046*6777b538SAndroid Build Coastguard Worker } // namespace net
1047