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/url_request.h"
16 #include "net/url_request/url_request_context.h"
17 #include "net/websockets/websocket_errors.h"
18 #include "net/websockets/websocket_event_interface.h"
19 #include "net/websockets/websocket_handshake_constants.h"
20 #include "net/websockets/websocket_handshake_stream_base.h"
21 #include "net/websockets/websocket_handshake_stream_create_helper.h"
22 #include "net/websockets/websocket_test_util.h"
24 #include "url/origin.h"
29 class StreamRequestImpl
;
31 class Delegate
: public URLRequest::Delegate
{
33 enum HandshakeResult
{
37 NUM_HANDSHAKE_RESULT_TYPES
,
40 explicit Delegate(StreamRequestImpl
* owner
)
41 : owner_(owner
), result_(INCOMPLETE
) {}
43 UMA_HISTOGRAM_ENUMERATION(
44 "Net.WebSocket.HandshakeResult", result_
, NUM_HANDSHAKE_RESULT_TYPES
);
47 // Implementation of URLRequest::Delegate methods.
48 virtual void OnReceivedRedirect(URLRequest
* request
,
50 bool* defer_redirect
) OVERRIDE
{
51 // HTTP status codes returned by HttpStreamParser are filtered by
52 // WebSocketBasicHandshakeStream, and only 101, 401 and 407 are permitted
53 // back up the stack to HttpNetworkTransaction. In particular, redirect
54 // codes are never allowed, and so URLRequest never sees a redirect on a
59 virtual void OnResponseStarted(URLRequest
* request
) OVERRIDE
;
61 virtual void OnAuthRequired(URLRequest
* request
,
62 AuthChallengeInfo
* auth_info
) OVERRIDE
;
64 virtual void OnCertificateRequested(URLRequest
* request
,
65 SSLCertRequestInfo
* cert_request_info
)
68 virtual void OnSSLCertificateError(URLRequest
* request
,
69 const SSLInfo
& ssl_info
,
72 virtual void OnReadCompleted(URLRequest
* request
, int bytes_read
) OVERRIDE
;
75 StreamRequestImpl
* owner_
;
76 HandshakeResult result_
;
79 class StreamRequestImpl
: public WebSocketStreamRequest
{
83 const URLRequestContext
* context
,
84 const url::Origin
& origin
,
85 scoped_ptr
<WebSocketStream::ConnectDelegate
> connect_delegate
,
86 scoped_ptr
<WebSocketHandshakeStreamCreateHelper
> create_helper
)
87 : delegate_(new Delegate(this)),
88 url_request_(url
, DEFAULT_PRIORITY
, delegate_
.get(), context
),
89 connect_delegate_(connect_delegate
.Pass()),
90 create_helper_(create_helper
.release()) {
91 create_helper_
->set_failure_message(&failure_message_
);
92 HttpRequestHeaders headers
;
93 headers
.SetHeader(websockets::kUpgrade
, websockets::kWebSocketLowercase
);
94 headers
.SetHeader(HttpRequestHeaders::kConnection
, websockets::kUpgrade
);
95 headers
.SetHeader(HttpRequestHeaders::kOrigin
, origin
.string());
96 headers
.SetHeader(websockets::kSecWebSocketVersion
,
97 websockets::kSupportedVersion
);
98 url_request_
.SetExtraRequestHeaders(headers
);
100 // This passes the ownership of |create_helper_| to |url_request_|.
101 url_request_
.SetUserData(
102 WebSocketHandshakeStreamBase::CreateHelper::DataKey(),
104 url_request_
.SetLoadFlags(LOAD_DISABLE_CACHE
|
106 LOAD_DO_NOT_PROMPT_FOR_LOGIN
);
109 // Destroying this object destroys the URLRequest, which cancels the request
110 // and so terminates the handshake if it is incomplete.
111 virtual ~StreamRequestImpl() {}
114 url_request_
.Start();
117 void PerformUpgrade() {
118 connect_delegate_
->OnSuccess(create_helper_
->Upgrade());
121 void ReportFailure() {
122 if (failure_message_
.empty()) {
123 switch (url_request_
.status().status()) {
124 case URLRequestStatus::SUCCESS
:
125 case URLRequestStatus::IO_PENDING
:
127 case URLRequestStatus::CANCELED
:
128 failure_message_
= "WebSocket opening handshake was canceled";
130 case URLRequestStatus::FAILED
:
132 std::string("Error in connection establishment: ") +
133 ErrorToString(url_request_
.status().error());
137 ReportFailureWithMessage(failure_message_
);
140 void ReportFailureWithMessage(const std::string
& failure_message
) {
141 connect_delegate_
->OnFailure(failure_message
);
144 void OnFinishOpeningHandshake() {
145 WebSocketDispatchOnFinishOpeningHandshake(connect_delegate(),
147 url_request_
.response_headers(),
148 url_request_
.response_time());
151 WebSocketStream::ConnectDelegate
* connect_delegate() const {
152 return connect_delegate_
.get();
156 // |delegate_| needs to be declared before |url_request_| so that it gets
157 // initialised first.
158 scoped_ptr
<Delegate
> delegate_
;
160 // Deleting the StreamRequestImpl object deletes this URLRequest object,
161 // cancelling the whole connection.
162 URLRequest url_request_
;
164 scoped_ptr
<WebSocketStream::ConnectDelegate
> connect_delegate_
;
166 // Owned by the URLRequest.
167 WebSocketHandshakeStreamCreateHelper
* create_helper_
;
169 // The failure message supplied by WebSocketBasicHandshakeStream, if any.
170 std::string failure_message_
;
173 class SSLErrorCallbacks
: public WebSocketEventInterface::SSLErrorCallbacks
{
175 explicit SSLErrorCallbacks(URLRequest
* url_request
)
176 : url_request_(url_request
) {}
178 virtual void CancelSSLRequest(int error
, const SSLInfo
* ssl_info
) OVERRIDE
{
180 url_request_
->CancelWithSSLError(error
, *ssl_info
);
182 url_request_
->CancelWithError(error
);
186 virtual void ContinueSSLRequest() OVERRIDE
{
187 url_request_
->ContinueDespiteLastError();
191 URLRequest
* url_request_
;
194 void Delegate::OnResponseStarted(URLRequest
* request
) {
195 // All error codes, including OK and ABORTED, as with
196 // Net.ErrorCodesForMainFrame3
197 UMA_HISTOGRAM_SPARSE_SLOWLY("Net.WebSocket.ErrorCodes",
198 -request
->status().error());
199 if (!request
->status().is_success()) {
200 DVLOG(3) << "OnResponseStarted (request failed)";
201 owner_
->ReportFailure();
204 const int response_code
= request
->GetResponseCode();
205 DVLOG(3) << "OnResponseStarted (response code " << response_code
<< ")";
206 switch (response_code
) {
207 case HTTP_SWITCHING_PROTOCOLS
:
209 owner_
->PerformUpgrade();
212 case HTTP_UNAUTHORIZED
:
214 owner_
->OnFinishOpeningHandshake();
215 owner_
->ReportFailureWithMessage(
216 "HTTP Authentication failed; no valid credentials available");
219 case HTTP_PROXY_AUTHENTICATION_REQUIRED
:
221 owner_
->OnFinishOpeningHandshake();
222 owner_
->ReportFailureWithMessage("Proxy authentication failed");
227 owner_
->ReportFailure();
231 void Delegate::OnAuthRequired(URLRequest
* request
,
232 AuthChallengeInfo
* auth_info
) {
233 // This should only be called if credentials are not already stored.
234 request
->CancelAuth();
237 void Delegate::OnCertificateRequested(URLRequest
* request
,
238 SSLCertRequestInfo
* cert_request_info
) {
239 // This method is called when a client certificate is requested, and the
240 // request context does not already contain a client certificate selection for
241 // the endpoint. In this case, a main frame resource request would pop-up UI
242 // to permit selection of a client certificate, but since WebSockets are
243 // sub-resources they should not pop-up UI and so there is nothing more we can
248 void Delegate::OnSSLCertificateError(URLRequest
* request
,
249 const SSLInfo
& ssl_info
,
251 owner_
->connect_delegate()->OnSSLCertificateError(
252 scoped_ptr
<WebSocketEventInterface::SSLErrorCallbacks
>(
253 new SSLErrorCallbacks(request
)),
258 void Delegate::OnReadCompleted(URLRequest
* request
, int bytes_read
) {
264 WebSocketStreamRequest::~WebSocketStreamRequest() {}
266 WebSocketStream::WebSocketStream() {}
267 WebSocketStream::~WebSocketStream() {}
269 WebSocketStream::ConnectDelegate::~ConnectDelegate() {}
271 scoped_ptr
<WebSocketStreamRequest
> WebSocketStream::CreateAndConnectStream(
272 const GURL
& socket_url
,
273 const std::vector
<std::string
>& requested_subprotocols
,
274 const url::Origin
& origin
,
275 URLRequestContext
* url_request_context
,
276 const BoundNetLog
& net_log
,
277 scoped_ptr
<ConnectDelegate
> connect_delegate
) {
278 scoped_ptr
<WebSocketHandshakeStreamCreateHelper
> create_helper(
279 new WebSocketHandshakeStreamCreateHelper(connect_delegate
.get(),
280 requested_subprotocols
));
281 scoped_ptr
<StreamRequestImpl
> request(
282 new StreamRequestImpl(socket_url
,
285 connect_delegate
.Pass(),
286 create_helper
.Pass()));
288 return request
.PassAs
<WebSocketStreamRequest
>();
291 // This is declared in websocket_test_util.h.
292 scoped_ptr
<WebSocketStreamRequest
> CreateAndConnectStreamForTesting(
293 const GURL
& socket_url
,
294 scoped_ptr
<WebSocketHandshakeStreamCreateHelper
> create_helper
,
295 const url::Origin
& origin
,
296 URLRequestContext
* url_request_context
,
297 const BoundNetLog
& net_log
,
298 scoped_ptr
<WebSocketStream::ConnectDelegate
> connect_delegate
) {
299 scoped_ptr
<StreamRequestImpl
> request(
300 new StreamRequestImpl(socket_url
,
303 connect_delegate
.Pass(),
304 create_helper
.Pass()));
306 return request
.PassAs
<WebSocketStreamRequest
>();
309 void WebSocketDispatchOnFinishOpeningHandshake(
310 WebSocketStream::ConnectDelegate
* connect_delegate
,
312 const scoped_refptr
<HttpResponseHeaders
>& headers
,
313 base::Time response_time
) {
314 DCHECK(connect_delegate
);
316 connect_delegate
->OnFinishOpeningHandshake(make_scoped_ptr(
317 new WebSocketHandshakeResponseInfo(url
,
318 headers
->response_code(),
319 headers
->GetStatusText(),