Home | History | Annotate | Download | only in websockets
      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.
      4 
      5 #include "net/websockets/websocket_stream.h"
      6 
      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"
     24 
     25 namespace net {
     26 namespace {
     27 
     28 class StreamRequestImpl;
     29 
     30 class Delegate : public URLRequest::Delegate {
     31  public:
     32   enum HandshakeResult {
     33     INCOMPLETE,
     34     CONNECTED,
     35     FAILED,
     36     NUM_HANDSHAKE_RESULT_TYPES,
     37   };
     38 
     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);
     44   }
     45 
     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();
     56   }
     57 
     58   virtual void OnResponseStarted(URLRequest* request) OVERRIDE;
     59 
     60   virtual void OnAuthRequired(URLRequest* request,
     61                               AuthChallengeInfo* auth_info) OVERRIDE;
     62 
     63   virtual void OnCertificateRequested(URLRequest* request,
     64                                       SSLCertRequestInfo* cert_request_info)
     65       OVERRIDE;
     66 
     67   virtual void OnSSLCertificateError(URLRequest* request,
     68                                      const SSLInfo& ssl_info,
     69                                      bool fatal) OVERRIDE;
     70 
     71   virtual void OnReadCompleted(URLRequest* request, int bytes_read) OVERRIDE;
     72 
     73  private:
     74   StreamRequestImpl* owner_;
     75   HandshakeResult result_;
     76 };
     77 
     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);
     98 
     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);
    106   }
    107 
    108   // Destroying this object destroys the URLRequest, which cancels the request
    109   // and so terminates the handshake if it is incomplete.
    110   virtual ~StreamRequestImpl() {}
    111 
    112   void Start() {
    113     url_request_.Start();
    114   }
    115 
    116   void PerformUpgrade() {
    117     connect_delegate_->OnSuccess(create_helper_->stream()->Upgrade());
    118   }
    119 
    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;
    134       }
    135     }
    136     connect_delegate_->OnFailure(failure_message_);
    137   }
    138 
    139   WebSocketStream::ConnectDelegate* connect_delegate() const {
    140     return connect_delegate_.get();
    141   }
    142 
    143  private:
    144   // |delegate_| needs to be declared before |url_request_| so that it gets
    145   // initialised first.
    146   scoped_ptr<Delegate> delegate_;
    147 
    148   // Deleting the StreamRequestImpl object deletes this URLRequest object,
    149   // cancelling the whole connection.
    150   URLRequest url_request_;
    151 
    152   scoped_ptr<WebSocketStream::ConnectDelegate> connect_delegate_;
    153 
    154   // Owned by the URLRequest.
    155   WebSocketHandshakeStreamCreateHelper* create_helper_;
    156 
    157   // The failure message supplied by WebSocketBasicHandshakeStream, if any.
    158   std::string failure_message_;
    159 };
    160 
    161 class SSLErrorCallbacks : public WebSocketEventInterface::SSLErrorCallbacks {
    162  public:
    163   explicit SSLErrorCallbacks(URLRequest* url_request)
    164       : url_request_(url_request) {}
    165 
    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);
    171     }
    172   }
    173 
    174   virtual void ContinueSSLRequest() OVERRIDE {
    175     url_request_->ContinueDespiteLastError();
    176   }
    177 
    178  private:
    179   URLRequest* url_request_;
    180 };
    181 
    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;
    191   }
    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;
    199 
    200     case HTTP_UNAUTHORIZED:
    201     case HTTP_PROXY_AUTHENTICATION_REQUIRED:
    202       return;
    203 
    204     default:
    205       result_ = FAILED;
    206       owner_->ReportFailure();
    207   }
    208 }
    209 
    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();
    214 }
    215 
    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();
    225 }
    226 
    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);
    235 }
    236 
    237 void Delegate::OnReadCompleted(URLRequest* request, int bytes_read) {
    238   NOTREACHED();
    239 }
    240 
    241 }  // namespace
    242 
    243 WebSocketStreamRequest::~WebSocketStreamRequest() {}
    244 
    245 WebSocketStream::WebSocketStream() {}
    246 WebSocketStream::~WebSocketStream() {}
    247 
    248 WebSocketStream::ConnectDelegate::~ConnectDelegate() {}
    249 
    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>();
    268 }
    269 
    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>();
    286 }
    287 
    288 }  // namespace net
    289