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_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"
23 #include "url/origin.h"
28 class StreamRequestImpl
;
30 class Delegate
: public URLRequest::Delegate
{
32 enum HandshakeResult
{
36 NUM_HANDSHAKE_RESULT_TYPES
,
39 explicit Delegate(StreamRequestImpl
* owner
)
40 : owner_(owner
), result_(INCOMPLETE
) {}
42 UMA_HISTOGRAM_ENUMERATION(
43 "Net.WebSocket.HandshakeResult", result_
, NUM_HANDSHAKE_RESULT_TYPES
);
46 // Implementation of URLRequest::Delegate methods.
47 virtual void OnReceivedRedirect(URLRequest
* request
,
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
58 virtual void OnResponseStarted(URLRequest
* request
) OVERRIDE
;
60 virtual void OnAuthRequired(URLRequest
* request
,
61 AuthChallengeInfo
* auth_info
) OVERRIDE
;
63 virtual void OnCertificateRequested(URLRequest
* request
,
64 SSLCertRequestInfo
* cert_request_info
)
67 virtual void OnSSLCertificateError(URLRequest
* request
,
68 const SSLInfo
& ssl_info
,
71 virtual void OnReadCompleted(URLRequest
* request
, int bytes_read
) OVERRIDE
;
74 StreamRequestImpl
* owner_
;
75 HandshakeResult result_
;
78 class StreamRequestImpl
: public WebSocketStreamRequest
{
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
);
99 // This passes the ownership of |create_helper_| to |url_request_|.
100 url_request_
.SetUserData(
101 WebSocketHandshakeStreamBase::CreateHelper::DataKey(),
103 url_request_
.SetLoadFlags(LOAD_DISABLE_CACHE
|
105 LOAD_DO_NOT_PROMPT_FOR_LOGIN
);
108 // Destroying this object destroys the URLRequest, which cancels the request
109 // and so terminates the handshake if it is incomplete.
110 virtual ~StreamRequestImpl() {}
113 url_request_
.Start();
116 void PerformUpgrade() {
117 connect_delegate_
->OnSuccess(create_helper_
->Upgrade());
120 void ReportFailure() {
121 if (failure_message_
.empty()) {
122 switch (url_request_
.status().status()) {
123 case URLRequestStatus::SUCCESS
:
124 case URLRequestStatus::IO_PENDING
:
126 case URLRequestStatus::CANCELED
:
127 failure_message_
= "WebSocket opening handshake was canceled";
129 case URLRequestStatus::FAILED
:
131 std::string("Error in connection establishment: ") +
132 ErrorToString(url_request_
.status().error());
136 connect_delegate_
->OnFailure(failure_message_
);
139 WebSocketStream::ConnectDelegate
* connect_delegate() const {
140 return connect_delegate_
.get();
144 // |delegate_| needs to be declared before |url_request_| so that it gets
145 // initialised first.
146 scoped_ptr
<Delegate
> delegate_
;
148 // Deleting the StreamRequestImpl object deletes this URLRequest object,
149 // cancelling the whole connection.
150 URLRequest url_request_
;
152 scoped_ptr
<WebSocketStream::ConnectDelegate
> connect_delegate_
;
154 // Owned by the URLRequest.
155 WebSocketHandshakeStreamCreateHelper
* create_helper_
;
157 // The failure message supplied by WebSocketBasicHandshakeStream, if any.
158 std::string failure_message_
;
161 class SSLErrorCallbacks
: public WebSocketEventInterface::SSLErrorCallbacks
{
163 explicit SSLErrorCallbacks(URLRequest
* url_request
)
164 : url_request_(url_request
) {}
166 virtual void CancelSSLRequest(int error
, const SSLInfo
* ssl_info
) OVERRIDE
{
168 url_request_
->CancelWithSSLError(error
, *ssl_info
);
170 url_request_
->CancelWithError(error
);
174 virtual void ContinueSSLRequest() OVERRIDE
{
175 url_request_
->ContinueDespiteLastError();
179 URLRequest
* url_request_
;
182 void 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();
192 const int response_code
= request
->GetResponseCode();
193 DVLOG(3) << "OnResponseStarted (response code " << response_code
<< ")";
194 switch (response_code
) {
195 case HTTP_SWITCHING_PROTOCOLS
:
197 owner_
->PerformUpgrade();
200 case HTTP_UNAUTHORIZED
:
201 case HTTP_PROXY_AUTHENTICATION_REQUIRED
:
206 owner_
->ReportFailure();
210 void Delegate::OnAuthRequired(URLRequest
* request
,
211 AuthChallengeInfo
* auth_info
) {
212 // This should only be called if credentials are not already stored.
213 request
->CancelAuth();
216 void 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
227 void Delegate::OnSSLCertificateError(URLRequest
* request
,
228 const SSLInfo
& ssl_info
,
230 owner_
->connect_delegate()->OnSSLCertificateError(
231 scoped_ptr
<WebSocketEventInterface::SSLErrorCallbacks
>(
232 new SSLErrorCallbacks(request
)),
237 void Delegate::OnReadCompleted(URLRequest
* request
, int bytes_read
) {
243 WebSocketStreamRequest::~WebSocketStreamRequest() {}
245 WebSocketStream::WebSocketStream() {}
246 WebSocketStream::~WebSocketStream() {}
248 WebSocketStream::ConnectDelegate::~ConnectDelegate() {}
250 scoped_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
,
264 connect_delegate
.Pass(),
265 create_helper
.Pass()));
267 return request
.PassAs
<WebSocketStreamRequest
>();
270 // This is declared in websocket_test_util.h.
271 scoped_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
,
282 connect_delegate
.Pass(),
283 create_helper
.Pass()));
285 return request
.PassAs
<WebSocketStreamRequest
>();