Probably broke Win7 Tests (dbg)(6). http://build.chromium.org/p/chromium.win/builders...
[chromium-blink-merge.git] / net / websockets / websocket_stream.cc
blob36b0ad4e5c95352afcf3a80e5af147a0dd7f8b3c
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"
22 #include "url/gurl.h"
23 #include "url/origin.h"
25 namespace net {
26 namespace {
28 class StreamRequestImpl;
30 class Delegate : public URLRequest::Delegate {
31 public:
32 enum HandshakeResult {
33 INCOMPLETE,
34 CONNECTED,
35 FAILED,
36 NUM_HANDSHAKE_RESULT_TYPES,
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);
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();
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)
65 OVERRIDE;
67 virtual void OnSSLCertificateError(URLRequest* request,
68 const SSLInfo& ssl_info,
69 bool fatal) OVERRIDE;
71 virtual void OnReadCompleted(URLRequest* request, int bytes_read) OVERRIDE;
73 private:
74 StreamRequestImpl* owner_;
75 HandshakeResult result_;
78 class 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);
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);
108 // Destroying this object destroys the URLRequest, which cancels the request
109 // and so terminates the handshake if it is incomplete.
110 virtual ~StreamRequestImpl() {}
112 void Start() {
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:
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;
136 connect_delegate_->OnFailure(failure_message_);
139 WebSocketStream::ConnectDelegate* connect_delegate() const {
140 return connect_delegate_.get();
143 private:
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 {
162 public:
163 explicit SSLErrorCallbacks(URLRequest* url_request)
164 : url_request_(url_request) {}
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);
174 virtual void ContinueSSLRequest() OVERRIDE {
175 url_request_->ContinueDespiteLastError();
178 private:
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();
190 return;
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;
200 case HTTP_UNAUTHORIZED:
201 case HTTP_PROXY_AUTHENTICATION_REQUIRED:
202 return;
204 default:
205 result_ = FAILED;
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
223 // do.
224 request->Cancel();
227 void 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);
237 void Delegate::OnReadCompleted(URLRequest* request, int bytes_read) {
238 NOTREACHED();
241 } // namespace
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,
262 url_request_context,
263 origin,
264 connect_delegate.Pass(),
265 create_helper.Pass()));
266 request->Start();
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,
280 url_request_context,
281 origin,
282 connect_delegate.Pass(),
283 create_helper.Pass()));
284 request->Start();
285 return request.PassAs<WebSocketStreamRequest>();
288 } // namespace net