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.
5 #include "net/websockets/websocket_stream.h"
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_response_headers.h"
14 #include "net/http/http_status_code.h"
15 #include "net/url_request/redirect_info.h"
16 #include "net/url_request/url_request.h"
17 #include "net/url_request/url_request_context.h"
18 #include "net/websockets/websocket_errors.h"
19 #include "net/websockets/websocket_event_interface.h"
20 #include "net/websockets/websocket_handshake_constants.h"
21 #include "net/websockets/websocket_handshake_stream_base.h"
22 #include "net/websockets/websocket_handshake_stream_create_helper.h"
23 #include "net/websockets/websocket_test_util.h"
25 #include "url/origin.h"
30 class StreamRequestImpl
;
32 class Delegate
: public URLRequest::Delegate
{
34 enum HandshakeResult
{
38 NUM_HANDSHAKE_RESULT_TYPES
,
41 explicit Delegate(StreamRequestImpl
* owner
)
42 : owner_(owner
), result_(INCOMPLETE
) {}
44 UMA_HISTOGRAM_ENUMERATION(
45 "Net.WebSocket.HandshakeResult", result_
, NUM_HANDSHAKE_RESULT_TYPES
);
48 // Implementation of URLRequest::Delegate methods.
49 virtual void OnReceivedRedirect(URLRequest
* request
,
50 const RedirectInfo
& redirect_info
,
51 bool* defer_redirect
) OVERRIDE
{
52 // HTTP status codes returned by HttpStreamParser are filtered by
53 // WebSocketBasicHandshakeStream, and only 101, 401 and 407 are permitted
54 // back up the stack to HttpNetworkTransaction. In particular, redirect
55 // codes are never allowed, and so URLRequest never sees a redirect on a
60 virtual void OnResponseStarted(URLRequest
* request
) OVERRIDE
;
62 virtual void OnAuthRequired(URLRequest
* request
,
63 AuthChallengeInfo
* auth_info
) OVERRIDE
;
65 virtual void OnCertificateRequested(URLRequest
* request
,
66 SSLCertRequestInfo
* cert_request_info
)
69 virtual void OnSSLCertificateError(URLRequest
* request
,
70 const SSLInfo
& ssl_info
,
73 virtual void OnReadCompleted(URLRequest
* request
, int bytes_read
) OVERRIDE
;
76 StreamRequestImpl
* owner_
;
77 HandshakeResult result_
;
80 class StreamRequestImpl
: public WebSocketStreamRequest
{
84 const URLRequestContext
* context
,
85 const url::Origin
& origin
,
86 scoped_ptr
<WebSocketStream::ConnectDelegate
> connect_delegate
,
87 scoped_ptr
<WebSocketHandshakeStreamCreateHelper
> create_helper
)
88 : delegate_(new Delegate(this)),
89 url_request_(context
->CreateRequest(url
, DEFAULT_PRIORITY
,
90 delegate_
.get(), NULL
)),
91 connect_delegate_(connect_delegate
.Pass()),
92 create_helper_(create_helper
.release()) {
93 create_helper_
->set_failure_message(&failure_message_
);
94 HttpRequestHeaders headers
;
95 headers
.SetHeader(websockets::kUpgrade
, websockets::kWebSocketLowercase
);
96 headers
.SetHeader(HttpRequestHeaders::kConnection
, websockets::kUpgrade
);
97 headers
.SetHeader(HttpRequestHeaders::kOrigin
, origin
.string());
98 headers
.SetHeader(websockets::kSecWebSocketVersion
,
99 websockets::kSupportedVersion
);
100 url_request_
->SetExtraRequestHeaders(headers
);
102 // This passes the ownership of |create_helper_| to |url_request_|.
103 url_request_
->SetUserData(
104 WebSocketHandshakeStreamBase::CreateHelper::DataKey(),
106 url_request_
->SetLoadFlags(LOAD_DISABLE_CACHE
|
108 LOAD_DO_NOT_PROMPT_FOR_LOGIN
);
111 // Destroying this object destroys the URLRequest, which cancels the request
112 // and so terminates the handshake if it is incomplete.
113 virtual ~StreamRequestImpl() {}
116 url_request_
->Start();
119 void PerformUpgrade() {
120 connect_delegate_
->OnSuccess(create_helper_
->Upgrade());
123 void ReportFailure() {
124 if (failure_message_
.empty()) {
125 switch (url_request_
->status().status()) {
126 case URLRequestStatus::SUCCESS
:
127 case URLRequestStatus::IO_PENDING
:
129 case URLRequestStatus::CANCELED
:
130 failure_message_
= "WebSocket opening handshake was canceled";
132 case URLRequestStatus::FAILED
:
134 std::string("Error in connection establishment: ") +
135 ErrorToString(url_request_
->status().error());
139 ReportFailureWithMessage(failure_message_
);
142 void ReportFailureWithMessage(const std::string
& failure_message
) {
143 connect_delegate_
->OnFailure(failure_message
);
146 void OnFinishOpeningHandshake() {
147 WebSocketDispatchOnFinishOpeningHandshake(connect_delegate(),
149 url_request_
->response_headers(),
150 url_request_
->response_time());
153 WebSocketStream::ConnectDelegate
* connect_delegate() const {
154 return connect_delegate_
.get();
158 // |delegate_| needs to be declared before |url_request_| so that it gets
159 // initialised first.
160 scoped_ptr
<Delegate
> delegate_
;
162 // Deleting the StreamRequestImpl object deletes this URLRequest object,
163 // cancelling the whole connection.
164 scoped_ptr
<URLRequest
> url_request_
;
166 scoped_ptr
<WebSocketStream::ConnectDelegate
> connect_delegate_
;
168 // Owned by the URLRequest.
169 WebSocketHandshakeStreamCreateHelper
* create_helper_
;
171 // The failure message supplied by WebSocketBasicHandshakeStream, if any.
172 std::string failure_message_
;
175 class SSLErrorCallbacks
: public WebSocketEventInterface::SSLErrorCallbacks
{
177 explicit SSLErrorCallbacks(URLRequest
* url_request
)
178 : url_request_(url_request
) {}
180 virtual void CancelSSLRequest(int error
, const SSLInfo
* ssl_info
) OVERRIDE
{
182 url_request_
->CancelWithSSLError(error
, *ssl_info
);
184 url_request_
->CancelWithError(error
);
188 virtual void ContinueSSLRequest() OVERRIDE
{
189 url_request_
->ContinueDespiteLastError();
193 URLRequest
* url_request_
;
196 void Delegate::OnResponseStarted(URLRequest
* request
) {
197 // All error codes, including OK and ABORTED, as with
198 // Net.ErrorCodesForMainFrame3
199 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.WebSocket.ErrorCodes",
200 -request
->status().error());
201 if (!request
->status().is_success()) {
202 DVLOG(3) << "OnResponseStarted (request failed)";
203 owner_
->ReportFailure();
206 const int response_code
= request
->GetResponseCode();
207 DVLOG(3) << "OnResponseStarted (response code " << response_code
<< ")";
208 switch (response_code
) {
209 case HTTP_SWITCHING_PROTOCOLS
:
211 owner_
->PerformUpgrade();
214 case HTTP_UNAUTHORIZED
:
216 owner_
->OnFinishOpeningHandshake();
217 owner_
->ReportFailureWithMessage(
218 "HTTP Authentication failed; no valid credentials available");
221 case HTTP_PROXY_AUTHENTICATION_REQUIRED
:
223 owner_
->OnFinishOpeningHandshake();
224 owner_
->ReportFailureWithMessage("Proxy authentication failed");
229 owner_
->ReportFailure();
233 void Delegate::OnAuthRequired(URLRequest
* request
,
234 AuthChallengeInfo
* auth_info
) {
235 // This should only be called if credentials are not already stored.
236 request
->CancelAuth();
239 void Delegate::OnCertificateRequested(URLRequest
* request
,
240 SSLCertRequestInfo
* cert_request_info
) {
241 // This method is called when a client certificate is requested, and the
242 // request context does not already contain a client certificate selection for
243 // the endpoint. In this case, a main frame resource request would pop-up UI
244 // to permit selection of a client certificate, but since WebSockets are
245 // sub-resources they should not pop-up UI and so there is nothing more we can
250 void Delegate::OnSSLCertificateError(URLRequest
* request
,
251 const SSLInfo
& ssl_info
,
253 owner_
->connect_delegate()->OnSSLCertificateError(
254 scoped_ptr
<WebSocketEventInterface::SSLErrorCallbacks
>(
255 new SSLErrorCallbacks(request
)),
260 void Delegate::OnReadCompleted(URLRequest
* request
, int bytes_read
) {
266 WebSocketStreamRequest::~WebSocketStreamRequest() {}
268 WebSocketStream::WebSocketStream() {}
269 WebSocketStream::~WebSocketStream() {}
271 WebSocketStream::ConnectDelegate::~ConnectDelegate() {}
273 scoped_ptr
<WebSocketStreamRequest
> WebSocketStream::CreateAndConnectStream(
274 const GURL
& socket_url
,
275 const std::vector
<std::string
>& requested_subprotocols
,
276 const url::Origin
& origin
,
277 URLRequestContext
* url_request_context
,
278 const BoundNetLog
& net_log
,
279 scoped_ptr
<ConnectDelegate
> connect_delegate
) {
280 scoped_ptr
<WebSocketHandshakeStreamCreateHelper
> create_helper(
281 new WebSocketHandshakeStreamCreateHelper(connect_delegate
.get(),
282 requested_subprotocols
));
283 scoped_ptr
<StreamRequestImpl
> request(
284 new StreamRequestImpl(socket_url
,
287 connect_delegate
.Pass(),
288 create_helper
.Pass()));
290 return request
.PassAs
<WebSocketStreamRequest
>();
293 // This is declared in websocket_test_util.h.
294 scoped_ptr
<WebSocketStreamRequest
> CreateAndConnectStreamForTesting(
295 const GURL
& socket_url
,
296 scoped_ptr
<WebSocketHandshakeStreamCreateHelper
> create_helper
,
297 const url::Origin
& origin
,
298 URLRequestContext
* url_request_context
,
299 const BoundNetLog
& net_log
,
300 scoped_ptr
<WebSocketStream::ConnectDelegate
> connect_delegate
) {
301 scoped_ptr
<StreamRequestImpl
> request(
302 new StreamRequestImpl(socket_url
,
305 connect_delegate
.Pass(),
306 create_helper
.Pass()));
308 return request
.PassAs
<WebSocketStreamRequest
>();
311 void WebSocketDispatchOnFinishOpeningHandshake(
312 WebSocketStream::ConnectDelegate
* connect_delegate
,
314 const scoped_refptr
<HttpResponseHeaders
>& headers
,
315 base::Time response_time
) {
316 DCHECK(connect_delegate
);
318 connect_delegate
->OnFinishOpeningHandshake(make_scoped_ptr(
319 new WebSocketHandshakeResponseInfo(url
,
320 headers
->response_code(),
321 headers
->GetStatusText(),