• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/websockets/websocket_http2_handshake_stream.h"
6 
7 #include <set>
8 #include <utility>
9 
10 #include "base/check.h"
11 #include "base/check_op.h"
12 #include "base/functional/bind.h"
13 #include "base/functional/callback.h"
14 #include "base/memory/scoped_refptr.h"
15 #include "base/notreached.h"
16 #include "base/strings/strcat.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/time/time.h"
19 #include "net/base/ip_endpoint.h"
20 #include "net/http/http_request_headers.h"
21 #include "net/http/http_request_info.h"
22 #include "net/http/http_response_headers.h"
23 #include "net/http/http_response_info.h"
24 #include "net/http/http_status_code.h"
25 #include "net/spdy/spdy_http_utils.h"
26 #include "net/spdy/spdy_session.h"
27 #include "net/spdy/spdy_stream.h"
28 #include "net/third_party/quiche/src/quiche/spdy/core/http2_header_block.h"
29 #include "net/traffic_annotation/network_traffic_annotation.h"
30 #include "net/websockets/websocket_basic_stream.h"
31 #include "net/websockets/websocket_deflate_predictor_impl.h"
32 #include "net/websockets/websocket_deflate_stream.h"
33 #include "net/websockets/websocket_handshake_constants.h"
34 #include "net/websockets/websocket_handshake_request_info.h"
35 
36 namespace net {
37 
38 namespace {
39 
ValidateStatus(const HttpResponseHeaders * headers)40 bool ValidateStatus(const HttpResponseHeaders* headers) {
41   return headers->GetStatusLine() == "HTTP/1.1 200";
42 }
43 
44 }  // namespace
45 
WebSocketHttp2HandshakeStream(base::WeakPtr<SpdySession> session,WebSocketStream::ConnectDelegate * connect_delegate,std::vector<std::string> requested_sub_protocols,std::vector<std::string> requested_extensions,WebSocketStreamRequestAPI * request,std::set<std::string> dns_aliases)46 WebSocketHttp2HandshakeStream::WebSocketHttp2HandshakeStream(
47     base::WeakPtr<SpdySession> session,
48     WebSocketStream::ConnectDelegate* connect_delegate,
49     std::vector<std::string> requested_sub_protocols,
50     std::vector<std::string> requested_extensions,
51     WebSocketStreamRequestAPI* request,
52     std::set<std::string> dns_aliases)
53     : session_(session),
54       connect_delegate_(connect_delegate),
55       requested_sub_protocols_(requested_sub_protocols),
56       requested_extensions_(requested_extensions),
57       stream_request_(request),
58       dns_aliases_(std::move(dns_aliases)) {
59   DCHECK(connect_delegate);
60   DCHECK(request);
61 }
62 
~WebSocketHttp2HandshakeStream()63 WebSocketHttp2HandshakeStream::~WebSocketHttp2HandshakeStream() {
64   spdy_stream_request_.reset();
65   RecordHandshakeResult(result_);
66 }
67 
RegisterRequest(const HttpRequestInfo * request_info)68 void WebSocketHttp2HandshakeStream::RegisterRequest(
69     const HttpRequestInfo* request_info) {
70   DCHECK(request_info);
71   DCHECK(request_info->traffic_annotation.is_valid());
72   request_info_ = request_info;
73 }
74 
InitializeStream(bool can_send_early,RequestPriority priority,const NetLogWithSource & net_log,CompletionOnceCallback callback)75 int WebSocketHttp2HandshakeStream::InitializeStream(
76     bool can_send_early,
77     RequestPriority priority,
78     const NetLogWithSource& net_log,
79     CompletionOnceCallback callback) {
80   priority_ = priority;
81   net_log_ = net_log;
82   return OK;
83 }
84 
SendRequest(const HttpRequestHeaders & headers,HttpResponseInfo * response,CompletionOnceCallback callback)85 int WebSocketHttp2HandshakeStream::SendRequest(
86     const HttpRequestHeaders& headers,
87     HttpResponseInfo* response,
88     CompletionOnceCallback callback) {
89   DCHECK(!headers.HasHeader(websockets::kSecWebSocketKey));
90   DCHECK(!headers.HasHeader(websockets::kSecWebSocketProtocol));
91   DCHECK(!headers.HasHeader(websockets::kSecWebSocketExtensions));
92   DCHECK(headers.HasHeader(HttpRequestHeaders::kOrigin));
93   DCHECK(headers.HasHeader(websockets::kUpgrade));
94   DCHECK(headers.HasHeader(HttpRequestHeaders::kConnection));
95   DCHECK(headers.HasHeader(websockets::kSecWebSocketVersion));
96 
97   if (!session_) {
98     const int rv = ERR_CONNECTION_CLOSED;
99     OnFailure("Connection closed before sending request.", rv, absl::nullopt);
100     return rv;
101   }
102 
103   http_response_info_ = response;
104 
105   IPEndPoint address;
106   int result = session_->GetPeerAddress(&address);
107   if (result != OK) {
108     OnFailure("Error getting IP address.", result, absl::nullopt);
109     return result;
110   }
111   http_response_info_->remote_endpoint = address;
112 
113   auto request = std::make_unique<WebSocketHandshakeRequestInfo>(
114       request_info_->url, base::Time::Now());
115   request->headers = headers;
116 
117   AddVectorHeaderIfNonEmpty(websockets::kSecWebSocketExtensions,
118                             requested_extensions_, &request->headers);
119   AddVectorHeaderIfNonEmpty(websockets::kSecWebSocketProtocol,
120                             requested_sub_protocols_, &request->headers);
121 
122   CreateSpdyHeadersFromHttpRequestForWebSocket(
123       request_info_->url, request->headers, &http2_request_headers_);
124 
125   connect_delegate_->OnStartOpeningHandshake(std::move(request));
126 
127   callback_ = std::move(callback);
128   spdy_stream_request_ = std::make_unique<SpdyStreamRequest>();
129   // The initial request for the WebSocket is a CONNECT, so there is no need to
130   // call ConfirmHandshake().
131   int rv = spdy_stream_request_->StartRequest(
132       SPDY_BIDIRECTIONAL_STREAM, session_, request_info_->url, true, priority_,
133       request_info_->socket_tag, net_log_,
134       base::BindOnce(&WebSocketHttp2HandshakeStream::StartRequestCallback,
135                      base::Unretained(this)),
136       NetworkTrafficAnnotationTag(request_info_->traffic_annotation));
137   if (rv == OK) {
138     StartRequestCallback(rv);
139     return ERR_IO_PENDING;
140   }
141   return rv;
142 }
143 
ReadResponseHeaders(CompletionOnceCallback callback)144 int WebSocketHttp2HandshakeStream::ReadResponseHeaders(
145     CompletionOnceCallback callback) {
146   if (stream_closed_)
147     return stream_error_;
148 
149   if (response_headers_complete_)
150     return ValidateResponse();
151 
152   callback_ = std::move(callback);
153   return ERR_IO_PENDING;
154 }
155 
ReadResponseBody(IOBuffer * buf,int buf_len,CompletionOnceCallback callback)156 int WebSocketHttp2HandshakeStream::ReadResponseBody(
157     IOBuffer* buf,
158     int buf_len,
159     CompletionOnceCallback callback) {
160   // Callers should instead call Upgrade() to get a WebSocketStream
161   // and call ReadFrames() on that.
162   NOTREACHED();
163   return OK;
164 }
165 
Close(bool not_reusable)166 void WebSocketHttp2HandshakeStream::Close(bool not_reusable) {
167   spdy_stream_request_.reset();
168   if (stream_) {
169     stream_ = nullptr;
170     stream_closed_ = true;
171     stream_error_ = ERR_CONNECTION_CLOSED;
172   }
173   stream_adapter_.reset();
174 }
175 
IsResponseBodyComplete() const176 bool WebSocketHttp2HandshakeStream::IsResponseBodyComplete() const {
177   return false;
178 }
179 
IsConnectionReused() const180 bool WebSocketHttp2HandshakeStream::IsConnectionReused() const {
181   return true;
182 }
183 
SetConnectionReused()184 void WebSocketHttp2HandshakeStream::SetConnectionReused() {}
185 
CanReuseConnection() const186 bool WebSocketHttp2HandshakeStream::CanReuseConnection() const {
187   return false;
188 }
189 
GetTotalReceivedBytes() const190 int64_t WebSocketHttp2HandshakeStream::GetTotalReceivedBytes() const {
191   return stream_ ? stream_->raw_received_bytes() : 0;
192 }
193 
GetTotalSentBytes() const194 int64_t WebSocketHttp2HandshakeStream::GetTotalSentBytes() const {
195   return stream_ ? stream_->raw_sent_bytes() : 0;
196 }
197 
GetAlternativeService(AlternativeService * alternative_service) const198 bool WebSocketHttp2HandshakeStream::GetAlternativeService(
199     AlternativeService* alternative_service) const {
200   return false;
201 }
202 
GetLoadTimingInfo(LoadTimingInfo * load_timing_info) const203 bool WebSocketHttp2HandshakeStream::GetLoadTimingInfo(
204     LoadTimingInfo* load_timing_info) const {
205   return stream_ && stream_->GetLoadTimingInfo(load_timing_info);
206 }
207 
GetSSLInfo(SSLInfo * ssl_info)208 void WebSocketHttp2HandshakeStream::GetSSLInfo(SSLInfo* ssl_info) {
209   if (stream_)
210     stream_->GetSSLInfo(ssl_info);
211 }
212 
GetSSLCertRequestInfo(SSLCertRequestInfo * cert_request_info)213 void WebSocketHttp2HandshakeStream::GetSSLCertRequestInfo(
214     SSLCertRequestInfo* cert_request_info) {
215   // A multiplexed stream cannot request client certificates. Client
216   // authentication may only occur during the initial SSL handshake.
217   NOTREACHED();
218 }
219 
GetRemoteEndpoint(IPEndPoint * endpoint)220 int WebSocketHttp2HandshakeStream::GetRemoteEndpoint(IPEndPoint* endpoint) {
221   if (!session_)
222     return ERR_SOCKET_NOT_CONNECTED;
223 
224   return session_->GetRemoteEndpoint(endpoint);
225 }
226 
PopulateNetErrorDetails(NetErrorDetails *)227 void WebSocketHttp2HandshakeStream::PopulateNetErrorDetails(
228     NetErrorDetails* /*details*/) {
229   return;
230 }
231 
Drain(HttpNetworkSession * session)232 void WebSocketHttp2HandshakeStream::Drain(HttpNetworkSession* session) {
233   Close(true /* not_reusable */);
234 }
235 
SetPriority(RequestPriority priority)236 void WebSocketHttp2HandshakeStream::SetPriority(RequestPriority priority) {
237   priority_ = priority;
238   if (stream_)
239     stream_->SetPriority(priority_);
240 }
241 
242 std::unique_ptr<HttpStream>
RenewStreamForAuth()243 WebSocketHttp2HandshakeStream::RenewStreamForAuth() {
244   // Renewing the stream is not supported.
245   return nullptr;
246 }
247 
GetDnsAliases() const248 const std::set<std::string>& WebSocketHttp2HandshakeStream::GetDnsAliases()
249     const {
250   return dns_aliases_;
251 }
252 
GetAcceptChViaAlps() const253 base::StringPiece WebSocketHttp2HandshakeStream::GetAcceptChViaAlps() const {
254   return {};
255 }
256 
Upgrade()257 std::unique_ptr<WebSocketStream> WebSocketHttp2HandshakeStream::Upgrade() {
258   DCHECK(extension_params_.get());
259 
260   stream_adapter_->DetachDelegate();
261   std::unique_ptr<WebSocketStream> basic_stream =
262       std::make_unique<WebSocketBasicStream>(std::move(stream_adapter_),
263                                              nullptr, sub_protocol_,
264                                              extensions_, net_log_);
265 
266   if (!extension_params_->deflate_enabled)
267     return basic_stream;
268 
269   return std::make_unique<WebSocketDeflateStream>(
270       std::move(basic_stream), extension_params_->deflate_parameters,
271       std::make_unique<WebSocketDeflatePredictorImpl>());
272 }
273 
CanReadFromStream() const274 bool WebSocketHttp2HandshakeStream::CanReadFromStream() const {
275   return stream_adapter_ && stream_adapter_->is_initialized();
276 }
277 
278 base::WeakPtr<WebSocketHandshakeStreamBase>
GetWeakPtr()279 WebSocketHttp2HandshakeStream::GetWeakPtr() {
280   return weak_ptr_factory_.GetWeakPtr();
281 }
282 
OnHeadersSent()283 void WebSocketHttp2HandshakeStream::OnHeadersSent() {
284   std::move(callback_).Run(OK);
285 }
286 
OnHeadersReceived(const spdy::Http2HeaderBlock & response_headers)287 void WebSocketHttp2HandshakeStream::OnHeadersReceived(
288     const spdy::Http2HeaderBlock& response_headers) {
289   DCHECK(!response_headers_complete_);
290   DCHECK(http_response_info_);
291 
292   response_headers_complete_ = true;
293 
294   const int rv =
295       SpdyHeadersToHttpResponse(response_headers, http_response_info_);
296   DCHECK_NE(rv, ERR_INCOMPLETE_HTTP2_HEADERS);
297 
298   http_response_info_->response_time = stream_->response_time();
299   // Do not store SSLInfo in the response here, HttpNetworkTransaction will take
300   // care of that part.
301   http_response_info_->was_alpn_negotiated = true;
302   http_response_info_->request_time = stream_->GetRequestTime();
303   http_response_info_->connection_info = HttpConnectionInfo::kHTTP2;
304   http_response_info_->alpn_negotiated_protocol =
305       HttpConnectionInfoToString(http_response_info_->connection_info);
306 
307   if (callback_)
308     std::move(callback_).Run(ValidateResponse());
309 }
310 
OnClose(int status)311 void WebSocketHttp2HandshakeStream::OnClose(int status) {
312   DCHECK(stream_adapter_);
313   DCHECK_GT(ERR_IO_PENDING, status);
314 
315   stream_closed_ = true;
316   stream_error_ = status;
317   stream_ = nullptr;
318 
319   stream_adapter_.reset();
320 
321   // If response headers have already been received,
322   // then ValidateResponse() sets |result_|.
323   if (!response_headers_complete_)
324     result_ = HandshakeResult::HTTP2_FAILED;
325 
326   OnFailure(base::StrCat({"Stream closed with error: ", ErrorToString(status)}),
327             status, absl::nullopt);
328 
329   if (callback_)
330     std::move(callback_).Run(status);
331 }
332 
StartRequestCallback(int rv)333 void WebSocketHttp2HandshakeStream::StartRequestCallback(int rv) {
334   DCHECK(callback_);
335   if (rv != OK) {
336     spdy_stream_request_.reset();
337     std::move(callback_).Run(rv);
338     return;
339   }
340   stream_ = spdy_stream_request_->ReleaseStream();
341   spdy_stream_request_.reset();
342   stream_adapter_ =
343       std::make_unique<WebSocketSpdyStreamAdapter>(stream_, this, net_log_);
344   rv = stream_->SendRequestHeaders(std::move(http2_request_headers_),
345                                    MORE_DATA_TO_SEND);
346   // SendRequestHeaders() always returns asynchronously,
347   // and instead of taking a callback, it calls OnHeadersSent().
348   DCHECK_EQ(ERR_IO_PENDING, rv);
349 }
350 
ValidateResponse()351 int WebSocketHttp2HandshakeStream::ValidateResponse() {
352   DCHECK(http_response_info_);
353   const HttpResponseHeaders* headers = http_response_info_->headers.get();
354   const int response_code = headers->response_code();
355   switch (response_code) {
356     case HTTP_OK:
357       return ValidateUpgradeResponse(headers);
358 
359     // We need to pass these through for authentication to work.
360     case HTTP_UNAUTHORIZED:
361     case HTTP_PROXY_AUTHENTICATION_REQUIRED:
362       return OK;
363 
364     // Other status codes are potentially risky (see the warnings in the
365     // WHATWG WebSocket API spec) and so are dropped by default.
366     default:
367       OnFailure(
368           base::StringPrintf(
369               "Error during WebSocket handshake: Unexpected response code: %d",
370               headers->response_code()),
371           ERR_FAILED, headers->response_code());
372       result_ = HandshakeResult::HTTP2_INVALID_STATUS;
373       return ERR_INVALID_RESPONSE;
374   }
375 }
376 
ValidateUpgradeResponse(const HttpResponseHeaders * headers)377 int WebSocketHttp2HandshakeStream::ValidateUpgradeResponse(
378     const HttpResponseHeaders* headers) {
379   extension_params_ = std::make_unique<WebSocketExtensionParams>();
380   std::string failure_message;
381   if (!ValidateStatus(headers)) {
382     result_ = HandshakeResult::HTTP2_INVALID_STATUS;
383   } else if (!ValidateSubProtocol(headers, requested_sub_protocols_,
384                                   &sub_protocol_, &failure_message)) {
385     result_ = HandshakeResult::HTTP2_FAILED_SUBPROTO;
386   } else if (!ValidateExtensions(headers, &extensions_, &failure_message,
387                                  extension_params_.get())) {
388     result_ = HandshakeResult::HTTP2_FAILED_EXTENSIONS;
389   } else {
390     result_ = HandshakeResult::HTTP2_CONNECTED;
391     return OK;
392   }
393 
394   const int rv = ERR_INVALID_RESPONSE;
395   OnFailure("Error during WebSocket handshake: " + failure_message, rv,
396             absl::nullopt);
397   return rv;
398 }
399 
OnFailure(const std::string & message,int net_error,absl::optional<int> response_code)400 void WebSocketHttp2HandshakeStream::OnFailure(
401     const std::string& message,
402     int net_error,
403     absl::optional<int> response_code) {
404   stream_request_->OnFailure(message, net_error, response_code);
405 }
406 
407 }  // namespace net
408