123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401 |
- // Copyright 2018 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "net/websockets/websocket_http2_handshake_stream.h"
- #include <cstddef>
- #include <set>
- #include <utility>
- #include "base/bind.h"
- #include "base/check_op.h"
- #include "base/notreached.h"
- #include "base/strings/stringprintf.h"
- #include "base/time/time.h"
- #include "net/base/ip_endpoint.h"
- #include "net/http/http_request_headers.h"
- #include "net/http/http_request_info.h"
- #include "net/http/http_response_headers.h"
- #include "net/http/http_status_code.h"
- #include "net/spdy/spdy_http_utils.h"
- #include "net/spdy/spdy_session.h"
- #include "net/traffic_annotation/network_traffic_annotation.h"
- #include "net/websockets/websocket_basic_stream.h"
- #include "net/websockets/websocket_deflate_parameters.h"
- #include "net/websockets/websocket_deflate_predictor_impl.h"
- #include "net/websockets/websocket_deflate_stream.h"
- #include "net/websockets/websocket_deflater.h"
- #include "net/websockets/websocket_handshake_constants.h"
- #include "net/websockets/websocket_handshake_request_info.h"
- namespace net {
- namespace {
- bool ValidateStatus(const HttpResponseHeaders* headers) {
- return headers->GetStatusLine() == "HTTP/1.1 200";
- }
- } // namespace
- WebSocketHttp2HandshakeStream::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)
- : session_(session),
- connect_delegate_(connect_delegate),
- requested_sub_protocols_(requested_sub_protocols),
- requested_extensions_(requested_extensions),
- stream_request_(request),
- dns_aliases_(std::move(dns_aliases)) {
- DCHECK(connect_delegate);
- DCHECK(request);
- }
- WebSocketHttp2HandshakeStream::~WebSocketHttp2HandshakeStream() {
- spdy_stream_request_.reset();
- RecordHandshakeResult(result_);
- }
- void WebSocketHttp2HandshakeStream::RegisterRequest(
- const HttpRequestInfo* request_info) {
- DCHECK(request_info);
- DCHECK(request_info->traffic_annotation.is_valid());
- request_info_ = request_info;
- }
- int WebSocketHttp2HandshakeStream::InitializeStream(
- bool can_send_early,
- RequestPriority priority,
- const NetLogWithSource& net_log,
- CompletionOnceCallback callback) {
- priority_ = priority;
- net_log_ = net_log;
- return OK;
- }
- int WebSocketHttp2HandshakeStream::SendRequest(
- const HttpRequestHeaders& headers,
- HttpResponseInfo* response,
- CompletionOnceCallback callback) {
- DCHECK(!headers.HasHeader(websockets::kSecWebSocketKey));
- DCHECK(!headers.HasHeader(websockets::kSecWebSocketProtocol));
- DCHECK(!headers.HasHeader(websockets::kSecWebSocketExtensions));
- DCHECK(headers.HasHeader(HttpRequestHeaders::kOrigin));
- DCHECK(headers.HasHeader(websockets::kUpgrade));
- DCHECK(headers.HasHeader(HttpRequestHeaders::kConnection));
- DCHECK(headers.HasHeader(websockets::kSecWebSocketVersion));
- if (!session_) {
- const int rv = ERR_CONNECTION_CLOSED;
- OnFailure("Connection closed before sending request.", rv, absl::nullopt);
- return rv;
- }
- http_response_info_ = response;
- IPEndPoint address;
- int result = session_->GetPeerAddress(&address);
- if (result != OK) {
- OnFailure("Error getting IP address.", result, absl::nullopt);
- return result;
- }
- http_response_info_->remote_endpoint = address;
- auto request = std::make_unique<WebSocketHandshakeRequestInfo>(
- request_info_->url, base::Time::Now());
- request->headers.CopyFrom(headers);
- AddVectorHeaderIfNonEmpty(websockets::kSecWebSocketExtensions,
- requested_extensions_, &request->headers);
- AddVectorHeaderIfNonEmpty(websockets::kSecWebSocketProtocol,
- requested_sub_protocols_, &request->headers);
- CreateSpdyHeadersFromHttpRequestForWebSocket(
- request_info_->url, request->headers, &http2_request_headers_);
- connect_delegate_->OnStartOpeningHandshake(std::move(request));
- callback_ = std::move(callback);
- spdy_stream_request_ = std::make_unique<SpdyStreamRequest>();
- // The initial request for the WebSocket is a CONNECT, so there is no need to
- // call ConfirmHandshake().
- int rv = spdy_stream_request_->StartRequest(
- SPDY_BIDIRECTIONAL_STREAM, session_, request_info_->url, true, priority_,
- request_info_->socket_tag, net_log_,
- base::BindOnce(&WebSocketHttp2HandshakeStream::StartRequestCallback,
- base::Unretained(this)),
- NetworkTrafficAnnotationTag(request_info_->traffic_annotation));
- if (rv == OK) {
- StartRequestCallback(rv);
- return ERR_IO_PENDING;
- }
- return rv;
- }
- int WebSocketHttp2HandshakeStream::ReadResponseHeaders(
- CompletionOnceCallback callback) {
- if (stream_closed_)
- return stream_error_;
- if (response_headers_complete_)
- return ValidateResponse();
- callback_ = std::move(callback);
- return ERR_IO_PENDING;
- }
- int WebSocketHttp2HandshakeStream::ReadResponseBody(
- IOBuffer* buf,
- int buf_len,
- CompletionOnceCallback callback) {
- // Callers should instead call Upgrade() to get a WebSocketStream
- // and call ReadFrames() on that.
- NOTREACHED();
- return OK;
- }
- void WebSocketHttp2HandshakeStream::Close(bool not_reusable) {
- spdy_stream_request_.reset();
- if (stream_) {
- stream_ = nullptr;
- stream_closed_ = true;
- stream_error_ = ERR_CONNECTION_CLOSED;
- }
- stream_adapter_.reset();
- }
- bool WebSocketHttp2HandshakeStream::IsResponseBodyComplete() const {
- return false;
- }
- bool WebSocketHttp2HandshakeStream::IsConnectionReused() const {
- return true;
- }
- void WebSocketHttp2HandshakeStream::SetConnectionReused() {}
- bool WebSocketHttp2HandshakeStream::CanReuseConnection() const {
- return false;
- }
- int64_t WebSocketHttp2HandshakeStream::GetTotalReceivedBytes() const {
- return stream_ ? stream_->raw_received_bytes() : 0;
- }
- int64_t WebSocketHttp2HandshakeStream::GetTotalSentBytes() const {
- return stream_ ? stream_->raw_sent_bytes() : 0;
- }
- bool WebSocketHttp2HandshakeStream::GetAlternativeService(
- AlternativeService* alternative_service) const {
- return false;
- }
- bool WebSocketHttp2HandshakeStream::GetLoadTimingInfo(
- LoadTimingInfo* load_timing_info) const {
- return stream_ && stream_->GetLoadTimingInfo(load_timing_info);
- }
- void WebSocketHttp2HandshakeStream::GetSSLInfo(SSLInfo* ssl_info) {
- if (stream_)
- stream_->GetSSLInfo(ssl_info);
- }
- void WebSocketHttp2HandshakeStream::GetSSLCertRequestInfo(
- SSLCertRequestInfo* cert_request_info) {
- // A multiplexed stream cannot request client certificates. Client
- // authentication may only occur during the initial SSL handshake.
- NOTREACHED();
- }
- int WebSocketHttp2HandshakeStream::GetRemoteEndpoint(IPEndPoint* endpoint) {
- if (!session_)
- return ERR_SOCKET_NOT_CONNECTED;
- return session_->GetRemoteEndpoint(endpoint);
- }
- void WebSocketHttp2HandshakeStream::PopulateNetErrorDetails(
- NetErrorDetails* /*details*/) {
- return;
- }
- void WebSocketHttp2HandshakeStream::Drain(HttpNetworkSession* session) {
- Close(true /* not_reusable */);
- }
- void WebSocketHttp2HandshakeStream::SetPriority(RequestPriority priority) {
- priority_ = priority;
- if (stream_)
- stream_->SetPriority(priority_);
- }
- std::unique_ptr<HttpStream>
- WebSocketHttp2HandshakeStream::RenewStreamForAuth() {
- // Renewing the stream is not supported.
- return nullptr;
- }
- const std::set<std::string>& WebSocketHttp2HandshakeStream::GetDnsAliases()
- const {
- return dns_aliases_;
- }
- base::StringPiece WebSocketHttp2HandshakeStream::GetAcceptChViaAlps() const {
- return {};
- }
- std::unique_ptr<WebSocketStream> WebSocketHttp2HandshakeStream::Upgrade() {
- DCHECK(extension_params_.get());
- stream_adapter_->DetachDelegate();
- std::unique_ptr<WebSocketStream> basic_stream =
- std::make_unique<WebSocketBasicStream>(std::move(stream_adapter_),
- nullptr, sub_protocol_,
- extensions_, net_log_);
- if (!extension_params_->deflate_enabled)
- return basic_stream;
- return std::make_unique<WebSocketDeflateStream>(
- std::move(basic_stream), extension_params_->deflate_parameters,
- std::make_unique<WebSocketDeflatePredictorImpl>());
- }
- base::WeakPtr<WebSocketHandshakeStreamBase>
- WebSocketHttp2HandshakeStream::GetWeakPtr() {
- return weak_ptr_factory_.GetWeakPtr();
- }
- void WebSocketHttp2HandshakeStream::OnHeadersSent() {
- std::move(callback_).Run(OK);
- }
- void WebSocketHttp2HandshakeStream::OnHeadersReceived(
- const spdy::Http2HeaderBlock& response_headers) {
- DCHECK(!response_headers_complete_);
- DCHECK(http_response_info_);
- response_headers_complete_ = true;
- const int rv =
- SpdyHeadersToHttpResponse(response_headers, http_response_info_);
- DCHECK_NE(rv, ERR_INCOMPLETE_HTTP2_HEADERS);
- http_response_info_->response_time = stream_->response_time();
- // Do not store SSLInfo in the response here, HttpNetworkTransaction will take
- // care of that part.
- http_response_info_->was_alpn_negotiated = true;
- http_response_info_->request_time = stream_->GetRequestTime();
- http_response_info_->connection_info =
- HttpResponseInfo::CONNECTION_INFO_HTTP2;
- http_response_info_->alpn_negotiated_protocol =
- HttpResponseInfo::ConnectionInfoToString(
- http_response_info_->connection_info);
- if (callback_)
- std::move(callback_).Run(ValidateResponse());
- }
- void WebSocketHttp2HandshakeStream::OnClose(int status) {
- DCHECK(stream_adapter_);
- DCHECK_GT(ERR_IO_PENDING, status);
- stream_closed_ = true;
- stream_error_ = status;
- stream_ = nullptr;
- stream_adapter_.reset();
- // If response headers have already been received,
- // then ValidateResponse() sets |result_|.
- if (!response_headers_complete_)
- result_ = HandshakeResult::HTTP2_FAILED;
- OnFailure(std::string("Stream closed with error: ") + ErrorToString(status),
- status, absl::nullopt);
- if (callback_)
- std::move(callback_).Run(status);
- }
- void WebSocketHttp2HandshakeStream::StartRequestCallback(int rv) {
- DCHECK(callback_);
- if (rv != OK) {
- spdy_stream_request_.reset();
- std::move(callback_).Run(rv);
- return;
- }
- stream_ = spdy_stream_request_->ReleaseStream();
- spdy_stream_request_.reset();
- stream_adapter_ =
- std::make_unique<WebSocketSpdyStreamAdapter>(stream_, this, net_log_);
- rv = stream_->SendRequestHeaders(std::move(http2_request_headers_),
- MORE_DATA_TO_SEND);
- // SendRequestHeaders() always returns asynchronously,
- // and instead of taking a callback, it calls OnHeadersSent().
- DCHECK_EQ(ERR_IO_PENDING, rv);
- }
- int WebSocketHttp2HandshakeStream::ValidateResponse() {
- DCHECK(http_response_info_);
- const HttpResponseHeaders* headers = http_response_info_->headers.get();
- const int response_code = headers->response_code();
- switch (response_code) {
- case HTTP_OK:
- return ValidateUpgradeResponse(headers);
- // We need to pass these through for authentication to work.
- case HTTP_UNAUTHORIZED:
- case HTTP_PROXY_AUTHENTICATION_REQUIRED:
- return OK;
- // Other status codes are potentially risky (see the warnings in the
- // WHATWG WebSocket API spec) and so are dropped by default.
- default:
- OnFailure(
- base::StringPrintf(
- "Error during WebSocket handshake: Unexpected response code: %d",
- headers->response_code()),
- ERR_FAILED, headers->response_code());
- result_ = HandshakeResult::HTTP2_INVALID_STATUS;
- return ERR_INVALID_RESPONSE;
- }
- }
- int WebSocketHttp2HandshakeStream::ValidateUpgradeResponse(
- const HttpResponseHeaders* headers) {
- extension_params_ = std::make_unique<WebSocketExtensionParams>();
- std::string failure_message;
- if (!ValidateStatus(headers)) {
- result_ = HandshakeResult::HTTP2_INVALID_STATUS;
- } else if (!ValidateSubProtocol(headers, requested_sub_protocols_,
- &sub_protocol_, &failure_message)) {
- result_ = HandshakeResult::HTTP2_FAILED_SUBPROTO;
- } else if (!ValidateExtensions(headers, &extensions_, &failure_message,
- extension_params_.get())) {
- result_ = HandshakeResult::HTTP2_FAILED_EXTENSIONS;
- } else {
- result_ = HandshakeResult::HTTP2_CONNECTED;
- return OK;
- }
- const int rv = ERR_INVALID_RESPONSE;
- OnFailure("Error during WebSocket handshake: " + failure_message, rv,
- absl::nullopt);
- return rv;
- }
- void WebSocketHttp2HandshakeStream::OnFailure(
- const std::string& message,
- int net_error,
- absl::optional<int> response_code) {
- stream_request_->OnFailure(message, net_error, response_code);
- }
- } // namespace net
|