1// Copyright 2013 The Chromium Authors. All rights reserved. 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_stream.h" 6 7#include "base/logging.h" 8#include "base/memory/scoped_ptr.h" 9#include "base/metrics/histogram.h" 10#include "base/metrics/sparse_histogram.h" 11#include "net/base/load_flags.h" 12#include "net/http/http_request_headers.h" 13#include "net/http/http_status_code.h" 14#include "net/url_request/url_request.h" 15#include "net/url_request/url_request_context.h" 16#include "net/websockets/websocket_errors.h" 17#include "net/websockets/websocket_event_interface.h" 18#include "net/websockets/websocket_handshake_constants.h" 19#include "net/websockets/websocket_handshake_stream_base.h" 20#include "net/websockets/websocket_handshake_stream_create_helper.h" 21#include "net/websockets/websocket_test_util.h" 22#include "url/gurl.h" 23#include "url/origin.h" 24 25namespace net { 26namespace { 27 28class StreamRequestImpl; 29 30class Delegate : public URLRequest::Delegate { 31 public: 32 enum HandshakeResult { 33 INCOMPLETE, 34 CONNECTED, 35 FAILED, 36 NUM_HANDSHAKE_RESULT_TYPES, 37 }; 38 39 explicit Delegate(StreamRequestImpl* owner) 40 : owner_(owner), result_(INCOMPLETE) {} 41 virtual ~Delegate() { 42 UMA_HISTOGRAM_ENUMERATION( 43 "Net.WebSocket.HandshakeResult", result_, NUM_HANDSHAKE_RESULT_TYPES); 44 } 45 46 // Implementation of URLRequest::Delegate methods. 47 virtual void OnReceivedRedirect(URLRequest* request, 48 const GURL& new_url, 49 bool* defer_redirect) OVERRIDE { 50 // HTTP status codes returned by HttpStreamParser are filtered by 51 // WebSocketBasicHandshakeStream, and only 101, 401 and 407 are permitted 52 // back up the stack to HttpNetworkTransaction. In particular, redirect 53 // codes are never allowed, and so URLRequest never sees a redirect on a 54 // WebSocket request. 55 NOTREACHED(); 56 } 57 58 virtual void OnResponseStarted(URLRequest* request) OVERRIDE; 59 60 virtual void OnAuthRequired(URLRequest* request, 61 AuthChallengeInfo* auth_info) OVERRIDE; 62 63 virtual void OnCertificateRequested(URLRequest* request, 64 SSLCertRequestInfo* cert_request_info) 65 OVERRIDE; 66 67 virtual void OnSSLCertificateError(URLRequest* request, 68 const SSLInfo& ssl_info, 69 bool fatal) OVERRIDE; 70 71 virtual void OnReadCompleted(URLRequest* request, int bytes_read) OVERRIDE; 72 73 private: 74 StreamRequestImpl* owner_; 75 HandshakeResult result_; 76}; 77 78class StreamRequestImpl : public WebSocketStreamRequest { 79 public: 80 StreamRequestImpl( 81 const GURL& url, 82 const URLRequestContext* context, 83 const url::Origin& origin, 84 scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate, 85 scoped_ptr<WebSocketHandshakeStreamCreateHelper> create_helper) 86 : delegate_(new Delegate(this)), 87 url_request_(url, DEFAULT_PRIORITY, delegate_.get(), context), 88 connect_delegate_(connect_delegate.Pass()), 89 create_helper_(create_helper.release()) { 90 create_helper_->set_failure_message(&failure_message_); 91 HttpRequestHeaders headers; 92 headers.SetHeader(websockets::kUpgrade, websockets::kWebSocketLowercase); 93 headers.SetHeader(HttpRequestHeaders::kConnection, websockets::kUpgrade); 94 headers.SetHeader(HttpRequestHeaders::kOrigin, origin.string()); 95 headers.SetHeader(websockets::kSecWebSocketVersion, 96 websockets::kSupportedVersion); 97 url_request_.SetExtraRequestHeaders(headers); 98 99 // This passes the ownership of |create_helper_| to |url_request_|. 100 url_request_.SetUserData( 101 WebSocketHandshakeStreamBase::CreateHelper::DataKey(), 102 create_helper_); 103 url_request_.SetLoadFlags(LOAD_DISABLE_CACHE | 104 LOAD_BYPASS_CACHE | 105 LOAD_DO_NOT_PROMPT_FOR_LOGIN); 106 } 107 108 // Destroying this object destroys the URLRequest, which cancels the request 109 // and so terminates the handshake if it is incomplete. 110 virtual ~StreamRequestImpl() {} 111 112 void Start() { 113 url_request_.Start(); 114 } 115 116 void PerformUpgrade() { 117 connect_delegate_->OnSuccess(create_helper_->stream()->Upgrade()); 118 } 119 120 void ReportFailure() { 121 if (failure_message_.empty()) { 122 switch (url_request_.status().status()) { 123 case URLRequestStatus::SUCCESS: 124 case URLRequestStatus::IO_PENDING: 125 break; 126 case URLRequestStatus::CANCELED: 127 failure_message_ = "WebSocket opening handshake was canceled"; 128 break; 129 case URLRequestStatus::FAILED: 130 failure_message_ = 131 std::string("Error in connection establishment: ") + 132 ErrorToString(url_request_.status().error()); 133 break; 134 } 135 } 136 connect_delegate_->OnFailure(failure_message_); 137 } 138 139 WebSocketStream::ConnectDelegate* connect_delegate() const { 140 return connect_delegate_.get(); 141 } 142 143 private: 144 // |delegate_| needs to be declared before |url_request_| so that it gets 145 // initialised first. 146 scoped_ptr<Delegate> delegate_; 147 148 // Deleting the StreamRequestImpl object deletes this URLRequest object, 149 // cancelling the whole connection. 150 URLRequest url_request_; 151 152 scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate_; 153 154 // Owned by the URLRequest. 155 WebSocketHandshakeStreamCreateHelper* create_helper_; 156 157 // The failure message supplied by WebSocketBasicHandshakeStream, if any. 158 std::string failure_message_; 159}; 160 161class SSLErrorCallbacks : public WebSocketEventInterface::SSLErrorCallbacks { 162 public: 163 explicit SSLErrorCallbacks(URLRequest* url_request) 164 : url_request_(url_request) {} 165 166 virtual void CancelSSLRequest(int error, const SSLInfo* ssl_info) OVERRIDE { 167 if (ssl_info) { 168 url_request_->CancelWithSSLError(error, *ssl_info); 169 } else { 170 url_request_->CancelWithError(error); 171 } 172 } 173 174 virtual void ContinueSSLRequest() OVERRIDE { 175 url_request_->ContinueDespiteLastError(); 176 } 177 178 private: 179 URLRequest* url_request_; 180}; 181 182void Delegate::OnResponseStarted(URLRequest* request) { 183 // All error codes, including OK and ABORTED, as with 184 // Net.ErrorCodesForMainFrame3 185 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.WebSocket.ErrorCodes", 186 -request->status().error()); 187 if (!request->status().is_success()) { 188 DVLOG(3) << "OnResponseStarted (request failed)"; 189 owner_->ReportFailure(); 190 return; 191 } 192 const int response_code = request->GetResponseCode(); 193 DVLOG(3) << "OnResponseStarted (response code " << response_code << ")"; 194 switch (response_code) { 195 case HTTP_SWITCHING_PROTOCOLS: 196 result_ = CONNECTED; 197 owner_->PerformUpgrade(); 198 return; 199 200 case HTTP_UNAUTHORIZED: 201 case HTTP_PROXY_AUTHENTICATION_REQUIRED: 202 return; 203 204 default: 205 result_ = FAILED; 206 owner_->ReportFailure(); 207 } 208} 209 210void Delegate::OnAuthRequired(URLRequest* request, 211 AuthChallengeInfo* auth_info) { 212 // This should only be called if credentials are not already stored. 213 request->CancelAuth(); 214} 215 216void Delegate::OnCertificateRequested(URLRequest* request, 217 SSLCertRequestInfo* cert_request_info) { 218 // This method is called when a client certificate is requested, and the 219 // request context does not already contain a client certificate selection for 220 // the endpoint. In this case, a main frame resource request would pop-up UI 221 // to permit selection of a client certificate, but since WebSockets are 222 // sub-resources they should not pop-up UI and so there is nothing more we can 223 // do. 224 request->Cancel(); 225} 226 227void Delegate::OnSSLCertificateError(URLRequest* request, 228 const SSLInfo& ssl_info, 229 bool fatal) { 230 owner_->connect_delegate()->OnSSLCertificateError( 231 scoped_ptr<WebSocketEventInterface::SSLErrorCallbacks>( 232 new SSLErrorCallbacks(request)), 233 ssl_info, 234 fatal); 235} 236 237void Delegate::OnReadCompleted(URLRequest* request, int bytes_read) { 238 NOTREACHED(); 239} 240 241} // namespace 242 243WebSocketStreamRequest::~WebSocketStreamRequest() {} 244 245WebSocketStream::WebSocketStream() {} 246WebSocketStream::~WebSocketStream() {} 247 248WebSocketStream::ConnectDelegate::~ConnectDelegate() {} 249 250scoped_ptr<WebSocketStreamRequest> WebSocketStream::CreateAndConnectStream( 251 const GURL& socket_url, 252 const std::vector<std::string>& requested_subprotocols, 253 const url::Origin& origin, 254 URLRequestContext* url_request_context, 255 const BoundNetLog& net_log, 256 scoped_ptr<ConnectDelegate> connect_delegate) { 257 scoped_ptr<WebSocketHandshakeStreamCreateHelper> create_helper( 258 new WebSocketHandshakeStreamCreateHelper(connect_delegate.get(), 259 requested_subprotocols)); 260 scoped_ptr<StreamRequestImpl> request( 261 new StreamRequestImpl(socket_url, 262 url_request_context, 263 origin, 264 connect_delegate.Pass(), 265 create_helper.Pass())); 266 request->Start(); 267 return request.PassAs<WebSocketStreamRequest>(); 268} 269 270// This is declared in websocket_test_util.h. 271scoped_ptr<WebSocketStreamRequest> CreateAndConnectStreamForTesting( 272 const GURL& socket_url, 273 scoped_ptr<WebSocketHandshakeStreamCreateHelper> create_helper, 274 const url::Origin& origin, 275 URLRequestContext* url_request_context, 276 const BoundNetLog& net_log, 277 scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate) { 278 scoped_ptr<StreamRequestImpl> request( 279 new StreamRequestImpl(socket_url, 280 url_request_context, 281 origin, 282 connect_delegate.Pass(), 283 create_helper.Pass())); 284 request->Start(); 285 return request.PassAs<WebSocketStreamRequest>(); 286} 287 288} // namespace net 289