QUIC - enable persisting of QUICServerInfo (server config) to disk
[chromium-blink-merge.git] / net / websockets / websocket_stream.cc
blobb6a235991979d01ff9a062c693d73c83cbda1afc
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"
23 #include "url/gurl.h"
24 #include "url/origin.h"
26 namespace net {
27 namespace {
29 class StreamRequestImpl;
31 class Delegate : public URLRequest::Delegate {
32 public:
33 enum HandshakeResult {
34 INCOMPLETE,
35 CONNECTED,
36 FAILED,
37 NUM_HANDSHAKE_RESULT_TYPES,
40 explicit Delegate(StreamRequestImpl* owner)
41 : owner_(owner), result_(INCOMPLETE) {}
42 virtual ~Delegate() {
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,
49 const GURL& new_url,
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
55 // WebSocket request.
56 NOTREACHED();
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)
66 OVERRIDE;
68 virtual void OnSSLCertificateError(URLRequest* request,
69 const SSLInfo& ssl_info,
70 bool fatal) OVERRIDE;
72 virtual void OnReadCompleted(URLRequest* request, int bytes_read) OVERRIDE;
74 private:
75 StreamRequestImpl* owner_;
76 HandshakeResult result_;
79 class StreamRequestImpl : public WebSocketStreamRequest {
80 public:
81 StreamRequestImpl(
82 const GURL& url,
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(),
103 create_helper_);
104 url_request_.SetLoadFlags(LOAD_DISABLE_CACHE |
105 LOAD_BYPASS_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() {}
113 void Start() {
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:
126 break;
127 case URLRequestStatus::CANCELED:
128 failure_message_ = "WebSocket opening handshake was canceled";
129 break;
130 case URLRequestStatus::FAILED:
131 failure_message_ =
132 std::string("Error in connection establishment: ") +
133 ErrorToString(url_request_.status().error());
134 break;
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(),
146 url_request_.url(),
147 url_request_.response_headers(),
148 url_request_.response_time());
151 WebSocketStream::ConnectDelegate* connect_delegate() const {
152 return connect_delegate_.get();
155 private:
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 {
174 public:
175 explicit SSLErrorCallbacks(URLRequest* url_request)
176 : url_request_(url_request) {}
178 virtual void CancelSSLRequest(int error, const SSLInfo* ssl_info) OVERRIDE {
179 if (ssl_info) {
180 url_request_->CancelWithSSLError(error, *ssl_info);
181 } else {
182 url_request_->CancelWithError(error);
186 virtual void ContinueSSLRequest() OVERRIDE {
187 url_request_->ContinueDespiteLastError();
190 private:
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();
202 return;
204 const int response_code = request->GetResponseCode();
205 DVLOG(3) << "OnResponseStarted (response code " << response_code << ")";
206 switch (response_code) {
207 case HTTP_SWITCHING_PROTOCOLS:
208 result_ = CONNECTED;
209 owner_->PerformUpgrade();
210 return;
212 case HTTP_UNAUTHORIZED:
213 result_ = FAILED;
214 owner_->OnFinishOpeningHandshake();
215 owner_->ReportFailureWithMessage(
216 "HTTP Authentication failed; no valid credentials available");
217 return;
219 case HTTP_PROXY_AUTHENTICATION_REQUIRED:
220 result_ = FAILED;
221 owner_->OnFinishOpeningHandshake();
222 owner_->ReportFailureWithMessage("Proxy authentication failed");
223 return;
225 default:
226 result_ = 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
244 // do.
245 request->Cancel();
248 void Delegate::OnSSLCertificateError(URLRequest* request,
249 const SSLInfo& ssl_info,
250 bool fatal) {
251 owner_->connect_delegate()->OnSSLCertificateError(
252 scoped_ptr<WebSocketEventInterface::SSLErrorCallbacks>(
253 new SSLErrorCallbacks(request)),
254 ssl_info,
255 fatal);
258 void Delegate::OnReadCompleted(URLRequest* request, int bytes_read) {
259 NOTREACHED();
262 } // namespace
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,
283 url_request_context,
284 origin,
285 connect_delegate.Pass(),
286 create_helper.Pass()));
287 request->Start();
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,
301 url_request_context,
302 origin,
303 connect_delegate.Pass(),
304 create_helper.Pass()));
305 request->Start();
306 return request.PassAs<WebSocketStreamRequest>();
309 void WebSocketDispatchOnFinishOpeningHandshake(
310 WebSocketStream::ConnectDelegate* connect_delegate,
311 const GURL& url,
312 const scoped_refptr<HttpResponseHeaders>& headers,
313 base::Time response_time) {
314 DCHECK(connect_delegate);
315 if (headers) {
316 connect_delegate->OnFinishOpeningHandshake(make_scoped_ptr(
317 new WebSocketHandshakeResponseInfo(url,
318 headers->response_code(),
319 headers->GetStatusText(),
320 headers,
321 response_time)));
325 } // namespace net