diff options
author | yhirano@chromium.org <yhirano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-21 06:50:50 +0000 |
---|---|---|
committer | yhirano@chromium.org <yhirano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-21 06:50:50 +0000 |
commit | 3732cea41964d9db33072fafde00b95913f1c1b7 (patch) | |
tree | a9d82b879d0f5247f96802dfac2c1f959296a423 /net | |
parent | ebd73710916292f90280b4e0516045e25044e431 (diff) | |
download | chromium_src-3732cea41964d9db33072fafde00b95913f1c1b7.zip chromium_src-3732cea41964d9db33072fafde00b95913f1c1b7.tar.gz chromium_src-3732cea41964d9db33072fafde00b95913f1c1b7.tar.bz2 |
Introduce RequestWebSocketStream into HttpStreamFactory
Introduce RequestWebSocketStream into HttpStreamFactory to reuse its functionality that handles socket pool, proxy and SSL.
BUG=237444
Review URL: https://chromiumcodereview.appspot.com/14813024
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@207735 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
21 files changed, 1038 insertions, 101 deletions
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc index 4762c71..20f71f4 100644 --- a/net/http/http_network_session.cc +++ b/net/http/http_network_session.cc @@ -126,7 +126,8 @@ HttpNetworkSession::HttpNetworkSession(const Params& params) params.spdy_max_concurrent_streams_limit, params.time_func, params.trusted_spdy_proxy), - http_stream_factory_(new HttpStreamFactoryImpl(this)), + http_stream_factory_(new HttpStreamFactoryImpl(this, false)), + websocket_stream_factory_(new HttpStreamFactoryImpl(this, true)), params_(params) { DCHECK(proxy_service_); DCHECK(ssl_config_service_.get()); diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h index 5e86f9c..1e1af31 100644 --- a/net/http/http_network_session.h +++ b/net/http/http_network_session.h @@ -6,6 +6,9 @@ #define NET_HTTP_HTTP_NETWORK_SESSION_H_ #include <set> +#include <string> + +#include "base/basictypes.h" #include "base/memory/ref_counted.h" #include "base/threading/non_thread_safe.h" #include "net/base/host_port_pair.h" @@ -136,6 +139,9 @@ class NET_EXPORT HttpNetworkSession HttpStreamFactory* http_stream_factory() { return http_stream_factory_.get(); } + HttpStreamFactory* websocket_stream_factory() { + return websocket_stream_factory_.get(); + } NetLog* net_log() { return net_log_; } @@ -190,6 +196,7 @@ class NET_EXPORT HttpNetworkSession QuicStreamFactory quic_stream_factory_; SpdySessionPool spdy_session_pool_; scoped_ptr<HttpStreamFactory> http_stream_factory_; + scoped_ptr<HttpStreamFactory> websocket_stream_factory_; std::set<HttpResponseBodyDrainer*> response_drainers_; Params params_; diff --git a/net/http/http_network_session_peer.cc b/net/http/http_network_session_peer.cc index 9fbac05..74b0d41 100644 --- a/net/http/http_network_session_peer.cc +++ b/net/http/http_network_session_peer.cc @@ -34,4 +34,9 @@ void HttpNetworkSessionPeer::SetHttpStreamFactory( session_->http_stream_factory_.reset(http_stream_factory); } +void HttpNetworkSessionPeer::SetWebSocketStreamFactory( + HttpStreamFactory* http_stream_factory) { + session_->websocket_stream_factory_.reset(http_stream_factory); +} + } // namespace net diff --git a/net/http/http_network_session_peer.h b/net/http/http_network_session_peer.h index 3d995cf..eeccfef 100644 --- a/net/http/http_network_session_peer.h +++ b/net/http/http_network_session_peer.h @@ -27,6 +27,7 @@ class NET_EXPORT_PRIVATE HttpNetworkSessionPeer { void SetProxyService(ProxyService* proxy_service); void SetHttpStreamFactory(HttpStreamFactory* http_stream_factory); + void SetWebSocketStreamFactory(HttpStreamFactory* websocket_stream_factory); private: const scoped_refptr<HttpNetworkSession> session_; diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index b59dfd1..55cfb2e 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -440,6 +440,13 @@ void HttpNetworkTransaction::OnStreamReady(const SSLConfig& used_ssl_config, OnIOComplete(OK); } +void HttpNetworkTransaction::OnWebSocketStreamReady( + const SSLConfig& used_ssl_config, + const ProxyInfo& used_proxy_info, + WebSocketStreamBase* stream) { + NOTREACHED() << "This function should never be called."; +} + void HttpNetworkTransaction::OnStreamFailed(int result, const SSLConfig& used_ssl_config) { DCHECK_EQ(STATE_CREATE_STREAM_COMPLETE, next_state_); diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h index f19b635..2e98fc2 100644 --- a/net/http/http_network_transaction.h +++ b/net/http/http_network_transaction.h @@ -24,11 +24,13 @@ namespace net { +class ClientSocketHandle; class HttpAuthController; class HttpNetworkSession; class HttpStreamBase; class HttpStreamRequest; class IOBuffer; +class SpdySession; struct HttpRequestInfo; class NET_EXPORT_PRIVATE HttpNetworkTransaction @@ -71,6 +73,10 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction virtual void OnStreamReady(const SSLConfig& used_ssl_config, const ProxyInfo& used_proxy_info, HttpStreamBase* stream) OVERRIDE; + virtual void OnWebSocketStreamReady( + const SSLConfig& used_ssl_config, + const ProxyInfo& used_proxy_info, + WebSocketStreamBase* stream) OVERRIDE; virtual void OnStreamFailed(int status, const SSLConfig& used_ssl_config) OVERRIDE; virtual void OnCertificateError(int status, diff --git a/net/http/http_stream_factory.h b/net/http/http_stream_factory.h index fdcbaf0b..3fa31b8 100644 --- a/net/http/http_stream_factory.h +++ b/net/http/http_stream_factory.h @@ -18,6 +18,10 @@ #include "net/base/request_priority.h" #include "net/http/http_server_properties.h" #include "net/socket/ssl_client_socket.h" +// This file can be included from net/http even though +// it is in net/websockets because it doesn't +// introduce any link dependency to net/websockets. +#include "net/websockets/websocket_stream_base.h" class GURL; @@ -55,7 +59,7 @@ class NET_EXPORT_PRIVATE HttpStreamRequest { public: virtual ~Delegate() {} - // This is the success case. + // This is the success case for RequestStream. // |stream| is now owned by the delegate. // |used_ssl_config| indicates the actual SSL configuration used for this // stream, since the HttpStreamRequest may have modified the configuration @@ -67,6 +71,18 @@ class NET_EXPORT_PRIVATE HttpStreamRequest { const ProxyInfo& used_proxy_info, HttpStreamBase* stream) = 0; + // This is the success case for RequestWebSocketStream. + // |stream| is now owned by the delegate. + // |used_ssl_config| indicates the actual SSL configuration used for this + // stream, since the HttpStreamRequest may have modified the configuration + // during stream processing. + // |used_proxy_info| indicates the actual ProxyInfo used for this stream, + // since the HttpStreamRequest performs the proxy resolution. + virtual void OnWebSocketStreamReady( + const SSLConfig& used_ssl_config, + const ProxyInfo& used_proxy_info, + WebSocketStreamBase* stream) = 0; + // This is the failure to create a stream case. // |used_ssl_config| indicates the actual SSL configuration used for this // stream, since the HttpStreamRequest may have modified the configuration @@ -169,7 +185,7 @@ class NET_EXPORT HttpStreamFactory { // Virtual interface methods. // Request a stream. - // Will callback to the HttpStreamRequestDelegate upon completion. + // Will call delegate->OnStreamReady on successful completion. virtual HttpStreamRequest* RequestStream( const HttpRequestInfo& info, RequestPriority priority, @@ -178,6 +194,17 @@ class NET_EXPORT HttpStreamFactory { HttpStreamRequest::Delegate* delegate, const BoundNetLog& net_log) = 0; + // Request a WebSocket stream. + // Will call delegate->OnWebSocketStreamReady on successful completion. + virtual HttpStreamRequest* RequestWebSocketStream( + const HttpRequestInfo& info, + RequestPriority priority, + const SSLConfig& server_ssl_config, + const SSLConfig& proxy_ssl_config, + HttpStreamRequest::Delegate* delegate, + WebSocketStreamBase::Factory* factory, + const BoundNetLog& net_log) = 0; + // Requests that enough connections for |num_streams| be opened. virtual void PreconnectStreams(int num_streams, const HttpRequestInfo& info, diff --git a/net/http/http_stream_factory_impl.cc b/net/http/http_stream_factory_impl.cc index c31ba44..6c7c46b 100644 --- a/net/http/http_stream_factory_impl.cc +++ b/net/http/http_stream_factory_impl.cc @@ -4,6 +4,8 @@ #include "net/http/http_stream_factory_impl.h" +#include <string> + #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "googleurl/src/gurl.h" @@ -39,11 +41,13 @@ GURL UpgradeUrlToHttps(const GURL& original_url, int port) { } // namespace -HttpStreamFactoryImpl::HttpStreamFactoryImpl(HttpNetworkSession* session) +HttpStreamFactoryImpl::HttpStreamFactoryImpl(HttpNetworkSession* session, + bool for_websockets) : session_(session), http_pipelined_host_pool_(this, NULL, session_->http_server_properties(), - session_->force_http_pipelining()) {} + session_->force_http_pipelining()), + for_websockets_(for_websockets) {} HttpStreamFactoryImpl::~HttpStreamFactoryImpl() { DCHECK(request_map_.empty()); @@ -68,7 +72,48 @@ HttpStreamRequest* HttpStreamFactoryImpl::RequestStream( const SSLConfig& proxy_ssl_config, HttpStreamRequest::Delegate* delegate, const BoundNetLog& net_log) { - Request* request = new Request(request_info.url, this, delegate, net_log); + DCHECK(!for_websockets_); + return RequestStreamInternal(request_info, + priority, + server_ssl_config, + proxy_ssl_config, + delegate, + NULL, + net_log); +} + +HttpStreamRequest* HttpStreamFactoryImpl::RequestWebSocketStream( + const HttpRequestInfo& request_info, + RequestPriority priority, + const SSLConfig& server_ssl_config, + const SSLConfig& proxy_ssl_config, + HttpStreamRequest::Delegate* delegate, + WebSocketStreamBase::Factory* factory, + const BoundNetLog& net_log) { + DCHECK(for_websockets_); + DCHECK(factory); + return RequestStreamInternal(request_info, + priority, + server_ssl_config, + proxy_ssl_config, + delegate, + factory, + net_log); +} + +HttpStreamRequest* HttpStreamFactoryImpl::RequestStreamInternal( + const HttpRequestInfo& request_info, + RequestPriority priority, + const SSLConfig& server_ssl_config, + const SSLConfig& proxy_ssl_config, + HttpStreamRequest::Delegate* delegate, + WebSocketStreamBase::Factory* websocket_stream_factory, + const BoundNetLog& net_log) { + Request* request = new Request(request_info.url, + this, + delegate, + websocket_stream_factory, + net_log); GURL alternate_url; PortAlternateProtocolPair alternate = @@ -113,6 +158,7 @@ void HttpStreamFactoryImpl::PreconnectStreams( RequestPriority priority, const SSLConfig& server_ssl_config, const SSLConfig& proxy_ssl_config) { + DCHECK(!for_websockets_); GURL alternate_url; PortAlternateProtocolPair alternate = GetAlternateProtocolRequestFor(request_info.url, &alternate_url); @@ -198,7 +244,7 @@ PortAlternateProtocolPair HttpStreamFactoryImpl::GetAlternateProtocolRequestFor( // for the proxy to use to reach the original URL via TCP. But // the alternate request will be going via UDP to a different port. *alternate_url = original_url; - } + } return alternate; } @@ -213,7 +259,7 @@ void HttpStreamFactoryImpl::OrphanJob(Job* job, const Request* request) { job->Orphan(request); } -void HttpStreamFactoryImpl::OnSpdySessionReady( +void HttpStreamFactoryImpl::OnNewSpdySessionReady( scoped_refptr<SpdySession> spdy_session, bool direct, const SSLConfig& used_ssl_config, @@ -239,12 +285,20 @@ void HttpStreamFactoryImpl::OnSpdySessionReady( protocol_negotiated, using_spdy, net_log); - bool use_relative_url = direct || request->url().SchemeIs("https"); - request->OnStreamReady( - NULL, - used_ssl_config, - used_proxy_info, - new SpdyHttpStream(spdy_session.get(), use_relative_url)); + if (for_websockets_) { + WebSocketStreamBase::Factory* factory = + request->websocket_stream_factory(); + DCHECK(factory); + bool use_relative_url = direct || request->url().SchemeIs("wss"); + request->OnWebSocketStreamReady( + NULL, used_ssl_config, used_proxy_info, + factory->CreateSpdyStream(spdy_session, use_relative_url)); + } else { + bool use_relative_url = direct || request->url().SchemeIs("https"); + request->OnStreamReady(NULL, used_ssl_config, used_proxy_info, + new SpdyHttpStream(spdy_session, + use_relative_url)); + } } // TODO(mbelshe): Alert other valid requests. } diff --git a/net/http/http_stream_factory_impl.h b/net/http/http_stream_factory_impl.h index 80e96ce..3cfda3c 100644 --- a/net/http/http_stream_factory_impl.h +++ b/net/http/http_stream_factory_impl.h @@ -28,7 +28,9 @@ class NET_EXPORT_PRIVATE HttpStreamFactoryImpl : public HttpStreamFactory, public HttpPipelinedHostPool::Delegate { public: - explicit HttpStreamFactoryImpl(HttpNetworkSession* session); + // RequestStream may only be called if |for_websockets| is false. + // RequestWebSocketStream may only be called if |for_websockets| is true. + HttpStreamFactoryImpl(HttpNetworkSession* session, bool for_websockets); virtual ~HttpStreamFactoryImpl(); // HttpStreamFactory interface @@ -40,6 +42,15 @@ class NET_EXPORT_PRIVATE HttpStreamFactoryImpl : HttpStreamRequest::Delegate* delegate, const BoundNetLog& net_log) OVERRIDE; + virtual HttpStreamRequest* RequestWebSocketStream( + const HttpRequestInfo& info, + RequestPriority priority, + const SSLConfig& server_ssl_config, + const SSLConfig& proxy_ssl_config, + HttpStreamRequest::Delegate* delegate, + WebSocketStreamBase::Factory* factory, + const BoundNetLog& net_log) OVERRIDE; + virtual void PreconnectStreams(int num_streams, const HttpRequestInfo& info, RequestPriority priority, @@ -52,6 +63,8 @@ class NET_EXPORT_PRIVATE HttpStreamFactoryImpl : virtual void OnHttpPipelinedHostHasAdditionalCapacity( HttpPipelinedHost* host) OVERRIDE; + size_t num_orphaned_jobs() const { return orphaned_job_set_.size(); } + private: class Request; class Job; @@ -62,6 +75,15 @@ class NET_EXPORT_PRIVATE HttpStreamFactoryImpl : typedef std::map<HttpPipelinedHost::Key, RequestVector> HttpPipeliningRequestMap; + HttpStreamRequest* RequestStreamInternal( + const HttpRequestInfo& info, + RequestPriority priority, + const SSLConfig& server_ssl_config, + const SSLConfig& proxy_ssl_config, + HttpStreamRequest::Delegate* delegate, + WebSocketStreamBase::Factory* factory, + const BoundNetLog& net_log); + PortAlternateProtocolPair GetAlternateProtocolRequestFor( const GURL& original_url, GURL* alternate_url) const; @@ -72,14 +94,14 @@ class NET_EXPORT_PRIVATE HttpStreamFactoryImpl : // Called when a SpdySession is ready. It will find appropriate Requests and // fulfill them. |direct| indicates whether or not |spdy_session| uses a // proxy. - void OnSpdySessionReady(scoped_refptr<SpdySession> spdy_session, - bool direct, - const SSLConfig& used_ssl_config, - const ProxyInfo& used_proxy_info, - bool was_npn_negotiated, - NextProto protocol_negotiated, - bool using_spdy, - const BoundNetLog& net_log); + void OnNewSpdySessionReady(scoped_refptr<SpdySession> spdy_session, + bool direct, + const SSLConfig& used_ssl_config, + const ProxyInfo& used_proxy_info, + bool was_npn_negotiated, + NextProto protocol_negotiated, + bool using_spdy, + const BoundNetLog& net_log); // Called when the Job detects that the endpoint indicated by the // Alternate-Protocol does not work. Lets the factory update @@ -124,6 +146,7 @@ class NET_EXPORT_PRIVATE HttpStreamFactoryImpl : // deleted when the factory is destroyed. std::set<const Job*> preconnect_job_set_; + const bool for_websockets_; DISALLOW_COPY_AND_ASSIGN(HttpStreamFactoryImpl); }; diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc index 179d8c4..c51f1c5 100644 --- a/net/http/http_stream_factory_impl_job.cc +++ b/net/http/http_stream_factory_impl_job.cc @@ -4,6 +4,8 @@ #include "net/http/http_stream_factory_impl_job.h" +#include <string> + #include "base/bind.h" #include "base/bind_helpers.h" #include "base/logging.h" @@ -207,12 +209,22 @@ void HttpStreamFactoryImpl::Job::Resume(Job* job) { void HttpStreamFactoryImpl::Job::Orphan(const Request* request) { DCHECK_EQ(request_, request); request_ = NULL; - // We've been orphaned, but there's a job we're blocked on. Don't bother - // racing, just cancel ourself. if (blocking_job_) { + // We've been orphaned, but there's a job we're blocked on. Don't bother + // racing, just cancel ourself. DCHECK(blocking_job_->waiting_job_); blocking_job_->waiting_job_ = NULL; blocking_job_ = NULL; + if (stream_factory_->for_websockets_ && + connection_ && connection_->socket()) + connection_->socket()->Disconnect(); + stream_factory_->OnOrphanedJobComplete(this); + } else if (stream_factory_->for_websockets_) { + // We cancel this job because WebSocketStream can't be created + // without a WebSocketStreamBase::Factory which is stored in Request class + // and isn't accessible from this job. + if (connection_ && connection_->socket()) + connection_->socket()->Disconnect(); stream_factory_->OnOrphanedJobComplete(this); } } @@ -274,6 +286,7 @@ bool HttpStreamFactoryImpl::Job::CanUseExistingSpdySession() const { // https (the normal case) or if we're connection to a SPDY proxy, or // if we're running with force_spdy_always_. crbug.com/133176 return request_info_.url.SchemeIs("https") || + request_info_.url.SchemeIs("wss") || proxy_info_.proxy_server().is_https() || force_spdy_always_; } @@ -281,6 +294,7 @@ bool HttpStreamFactoryImpl::Job::CanUseExistingSpdySession() const { void HttpStreamFactoryImpl::Job::OnStreamReadyCallback() { DCHECK(stream_.get()); DCHECK(!IsPreconnecting()); + DCHECK(!stream_factory_->for_websockets_); if (IsOrphaned()) { stream_factory_->OnOrphanedJobComplete(this); } else { @@ -294,7 +308,25 @@ void HttpStreamFactoryImpl::Job::OnStreamReadyCallback() { // |this| may be deleted after this call. } -void HttpStreamFactoryImpl::Job::OnSpdySessionReadyCallback() { +void HttpStreamFactoryImpl::Job::OnWebSocketStreamReadyCallback() { + DCHECK(websocket_stream_); + DCHECK(!IsPreconnecting()); + DCHECK(stream_factory_->for_websockets_); + // An orphaned WebSocket job will be closed immediately and + // never be ready. + DCHECK(!IsOrphaned()); + request_->Complete(was_npn_negotiated(), + protocol_negotiated(), + using_spdy(), + net_log_); + request_->OnWebSocketStreamReady(this, + server_ssl_config_, + proxy_info_, + websocket_stream_.release()); + // |this| may be deleted after this call. +} + +void HttpStreamFactoryImpl::Job::OnNewSpdySessionReadyCallback() { DCHECK(!stream_.get()); DCHECK(!IsPreconnecting()); DCHECK(using_spdy()); @@ -302,12 +334,12 @@ void HttpStreamFactoryImpl::Job::OnSpdySessionReadyCallback() { scoped_refptr<SpdySession> spdy_session = new_spdy_session_; new_spdy_session_ = NULL; if (IsOrphaned()) { - stream_factory_->OnSpdySessionReady( + stream_factory_->OnNewSpdySessionReady( spdy_session, spdy_session_direct_, server_ssl_config_, proxy_info_, was_npn_negotiated(), protocol_negotiated(), using_spdy(), net_log_); stream_factory_->OnOrphanedJobComplete(this); } else { - request_->OnSpdySessionReady(this, spdy_session, spdy_session_direct_); + request_->OnNewSpdySessionReady(this, spdy_session, spdy_session_direct_); } // |this| may be deleted after this call. } @@ -367,15 +399,11 @@ void HttpStreamFactoryImpl::Job::OnHttpsProxyTunnelResponseCallback( void HttpStreamFactoryImpl::Job::OnPreconnectsComplete() { DCHECK(!request_); - if (new_spdy_session_.get()) { - stream_factory_->OnSpdySessionReady(new_spdy_session_, - spdy_session_direct_, - server_ssl_config_, - proxy_info_, - was_npn_negotiated(), - protocol_negotiated(), - using_spdy(), - net_log_); + if (new_spdy_session_) { + stream_factory_->OnNewSpdySessionReady( + new_spdy_session_, spdy_session_direct_, server_ssl_config_, + proxy_info_, was_npn_negotiated(), protocol_negotiated(), using_spdy(), + net_log_); } stream_factory_->OnPreconnectsComplete(this); // |this| may be deleted after this call. @@ -448,7 +476,7 @@ int HttpStreamFactoryImpl::Job::RunLoop(int result) { base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind( - &HttpStreamFactoryImpl::Job::OnNeedsProxyAuthCallback, + &Job::OnNeedsProxyAuthCallback, ptr_factory_.GetWeakPtr(), *tunnel_auth_response, proxy_socket->GetAuthController())); @@ -459,7 +487,7 @@ int HttpStreamFactoryImpl::Job::RunLoop(int result) { base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind( - &HttpStreamFactoryImpl::Job::OnNeedsClientAuthCallback, + &Job::OnNeedsClientAuthCallback, ptr_factory_.GetWeakPtr(), connection_->ssl_error_response_info().cert_request_info)); return ERR_IO_PENDING; @@ -475,7 +503,7 @@ int HttpStreamFactoryImpl::Job::RunLoop(int result) { base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind( - &HttpStreamFactoryImpl::Job::OnHttpsProxyTunnelResponseCallback, + &Job::OnHttpsProxyTunnelResponseCallback, ptr_factory_.GetWeakPtr(), *proxy_socket->GetConnectResponseInfo(), proxy_socket->CreateConnectResponseStream())); @@ -484,16 +512,25 @@ int HttpStreamFactoryImpl::Job::RunLoop(int result) { case OK: next_state_ = STATE_DONE; - if (new_spdy_session_.get()) { + if (new_spdy_session_) { base::MessageLoop::current()->PostTask( FROM_HERE, - base::Bind(&HttpStreamFactoryImpl::Job::OnSpdySessionReadyCallback, - ptr_factory_.GetWeakPtr())); + base::Bind( + &Job::OnNewSpdySessionReadyCallback, + ptr_factory_.GetWeakPtr())); + } else if (stream_factory_->for_websockets_) { + DCHECK(websocket_stream_); + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind( + &Job::OnWebSocketStreamReadyCallback, + ptr_factory_.GetWeakPtr())); } else { + DCHECK(stream_.get()); base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind( - &HttpStreamFactoryImpl::Job::OnStreamReadyCallback, + &Job::OnStreamReadyCallback, ptr_factory_.GetWeakPtr())); } return ERR_IO_PENDING; @@ -502,7 +539,7 @@ int HttpStreamFactoryImpl::Job::RunLoop(int result) { base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind( - &HttpStreamFactoryImpl::Job::OnStreamFailedCallback, + &Job::OnStreamFailedCallback, ptr_factory_.GetWeakPtr(), result)); return ERR_IO_PENDING; @@ -689,7 +726,8 @@ int HttpStreamFactoryImpl::Job::DoInitConnection() { DCHECK(proxy_info_.proxy_server().is_valid()); next_state_ = STATE_INIT_CONNECTION_COMPLETE; - using_ssl_ = request_info_.url.SchemeIs("https") || ShouldForceSpdySSL(); + using_ssl_ = request_info_.url.SchemeIs("https") || + request_info_.url.SchemeIs("wss") || ShouldForceSpdySSL(); using_spdy_ = false; if (ShouldForceQuic()) @@ -774,6 +812,7 @@ int HttpStreamFactoryImpl::Job::DoInitConnection() { } if (IsPreconnecting()) { + DCHECK(!stream_factory_->for_websockets_); return PreconnectSocketsForHttpRequest( origin_url_, request_info_.extra_headers, @@ -795,6 +834,14 @@ int HttpStreamFactoryImpl::Job::DoInitConnection() { base::Bind(&Job::OnHostResolution, session_->spdy_session_pool(), GetSpdySessionKey()) : OnHostResolutionCallback(); + if (stream_factory_->for_websockets_) { + return InitSocketHandleForWebSocketRequest( + origin_url_, request_info_.extra_headers, request_info_.load_flags, + priority_, session_, proxy_info_, ShouldForceSpdySSL(), + want_spdy_over_npn, server_ssl_config_, proxy_ssl_config_, + request_info_.privacy_mode, net_log_, + connection_.get(), resolution_callback, io_callback_); + } return InitSocketHandleForHttpRequest( origin_url_, request_info_.extra_headers, request_info_.load_flags, priority_, session_, proxy_info_, ShouldForceSpdySSL(), @@ -864,9 +911,9 @@ int HttpStreamFactoryImpl::Job::DoInitConnectionComplete(int result) { SSLClientSocket::NextProtoFromString(proto); protocol_negotiated_ = protocol_negotiated; net_log_.AddEvent( - NetLog::TYPE_HTTP_STREAM_REQUEST_PROTO, - base::Bind(&NetLogHttpStreamProtoCallback, - status, &proto, &server_protos)); + NetLog::TYPE_HTTP_STREAM_REQUEST_PROTO, + base::Bind(&NetLogHttpStreamProtoCallback, + status, &proto, &server_protos)); if (ssl_socket->was_spdy_negotiated()) SwitchToSpdyMode(); } @@ -982,10 +1029,17 @@ int HttpStreamFactoryImpl::Job::DoCreateStream() { request_info_.url.SchemeIs("ftp")); if (stream_factory_->http_pipelined_host_pool_. IsExistingPipelineAvailableForKey(*http_pipelining_key_.get())) { + DCHECK(!stream_factory_->for_websockets_); stream_.reset(stream_factory_->http_pipelined_host_pool_. CreateStreamOnExistingPipeline( *http_pipelining_key_.get())); CHECK(stream_.get()); + } else if (stream_factory_->for_websockets_) { + DCHECK(request_); + DCHECK(request_->websocket_stream_factory()); + websocket_stream_.reset( + request_->websocket_stream_factory()->CreateBasicStream( + connection_.release(), using_proxy)); } else if (!using_proxy && IsRequestEligibleForPipelining()) { // TODO(simonjam): Support proxies. stream_.reset( @@ -1057,8 +1111,17 @@ int HttpStreamFactoryImpl::Job::DoCreateStream() { // HttpStreamFactoryImpl will be creating all the SpdyHttpStreams, since it // will know when SpdySessions become available. - bool use_relative_url = direct || request_info_.url.SchemeIs("https"); - stream_.reset(new SpdyHttpStream(spdy_session.get(), use_relative_url)); + if (stream_factory_->for_websockets_) { + DCHECK(request_); + DCHECK(request_->websocket_stream_factory()); + bool use_relative_url = direct || request_info_.url.SchemeIs("wss"); + websocket_stream_.reset( + request_->websocket_stream_factory()->CreateSpdyStream( + spdy_session, use_relative_url)); + } else { + bool use_relative_url = direct || request_info_.url.SchemeIs("https"); + stream_.reset(new SpdyHttpStream(spdy_session, use_relative_url)); + } return OK; } @@ -1359,6 +1422,9 @@ bool HttpStreamFactoryImpl::Job::IsRequestEligibleForPipelining() { if (IsPreconnecting() || !request_) { return false; } + if (stream_factory_->for_websockets_) { + return false; + } if (session_->force_http_pipelining()) { return true; } diff --git a/net/http/http_stream_factory_impl_job.h b/net/http/http_stream_factory_impl_job.h index a6a3603..6e34f5f 100644 --- a/net/http/http_stream_factory_impl_job.h +++ b/net/http/http_stream_factory_impl_job.h @@ -127,7 +127,9 @@ class HttpStreamFactoryImpl::Job { }; void OnStreamReadyCallback(); - void OnSpdySessionReadyCallback(); + void OnWebSocketStreamReadyCallback(); + // This callback function is called when a new SPDY session is created. + void OnNewSpdySessionReadyCallback(); void OnStreamFailedCallback(int result); void OnCertificateErrorCallback(int result, const SSLInfo& ssl_info); void OnNeedsProxyAuthCallback(const HttpResponseInfo& response_info, @@ -292,6 +294,7 @@ class HttpStreamFactoryImpl::Job { bool establishing_tunnel_; scoped_ptr<HttpStream> stream_; + scoped_ptr<WebSocketStreamBase> websocket_stream_; // True if we negotiated NPN. bool was_npn_negotiated_; diff --git a/net/http/http_stream_factory_impl_request.cc b/net/http/http_stream_factory_impl_request.cc index 3fcc7ae..a29e593 100644 --- a/net/http/http_stream_factory_impl_request.cc +++ b/net/http/http_stream_factory_impl_request.cc @@ -13,12 +13,15 @@ namespace net { -HttpStreamFactoryImpl::Request::Request(const GURL& url, - HttpStreamFactoryImpl* factory, - HttpStreamRequest::Delegate* delegate, - const BoundNetLog& net_log) +HttpStreamFactoryImpl::Request::Request( + const GURL& url, + HttpStreamFactoryImpl* factory, + HttpStreamRequest::Delegate* delegate, + WebSocketStreamBase::Factory* websocket_stream_factory, + const BoundNetLog& net_log) : url_(url), factory_(factory), + websocket_stream_factory_(websocket_stream_factory), delegate_(delegate), net_log_(net_log), completed_(false), @@ -99,33 +102,27 @@ void HttpStreamFactoryImpl::Request::OnStreamReady( const SSLConfig& used_ssl_config, const ProxyInfo& used_proxy_info, HttpStreamBase* stream) { + DCHECK(!factory_->for_websockets_); DCHECK(stream); DCHECK(completed_); - // |job| should only be NULL if we're being serviced by a late bound - // SpdySession or HttpPipelinedConnection (one that was not created by a job - // in our |jobs_| set). - if (!job) { - DCHECK(!bound_job_.get()); - DCHECK(!jobs_.empty()); - // NOTE(willchan): We do *NOT* call OrphanJobs() here. The reason is because - // we *WANT* to cancel the unnecessary Jobs from other requests if another - // Job completes first. - // TODO(mbelshe): Revisit this when we implement ip connection pooling of - // SpdySessions. Do we want to orphan the jobs for a different hostname so - // they complete? Or do we want to prevent connecting a new SpdySession if - // we've already got one available for a different hostname where the ip - // address matches up? - } else if (!bound_job_.get()) { - // We may have other jobs in |jobs_|. For example, if we start multiple jobs - // for Alternate-Protocol. - OrphanJobsExcept(job); - } else { - DCHECK(jobs_.empty()); - } + OnJobSucceeded(job); delegate_->OnStreamReady(used_ssl_config, used_proxy_info, stream); } +void HttpStreamFactoryImpl::Request::OnWebSocketStreamReady( + Job* job, + const SSLConfig& used_ssl_config, + const ProxyInfo& used_proxy_info, + WebSocketStreamBase* stream) { + DCHECK(factory_->for_websockets_); + DCHECK(stream); + DCHECK(completed_); + + OnJobSucceeded(job); + delegate_->OnWebSocketStreamReady(used_ssl_config, used_proxy_info, stream); +} + void HttpStreamFactoryImpl::Request::OnStreamFailed( Job* job, int status, @@ -280,7 +277,7 @@ HttpStreamFactoryImpl::Request::RemoveRequestFromHttpPipeliningRequestMap() { } } -void HttpStreamFactoryImpl::Request::OnSpdySessionReady( +void HttpStreamFactoryImpl::Request::OnNewSpdySessionReady( Job* job, scoped_refptr<SpdySession> spdy_session, bool direct) { @@ -290,7 +287,7 @@ void HttpStreamFactoryImpl::Request::OnSpdySessionReady( // The first case is the usual case. if (!bound_job_.get()) { OrphanJobsExcept(job); - } else { // This is the case for HTTPS proxy tunneling. + } else { // This is the case for HTTPS proxy tunneling. DCHECK_EQ(bound_job_.get(), job); DCHECK(jobs_.empty()); } @@ -308,14 +305,23 @@ void HttpStreamFactoryImpl::Request::OnSpdySessionReady( // Cache this so we can still use it if the request is deleted. HttpStreamFactoryImpl* factory = factory_; - - bool use_relative_url = direct || url().SchemeIs("https"); - delegate_->OnStreamReady( - job->server_ssl_config(), - job->proxy_info(), - new SpdyHttpStream(spdy_session.get(), use_relative_url)); + if (factory->for_websockets_) { + DCHECK(websocket_stream_factory_); + bool use_relative_url = direct || url().SchemeIs("wss"); + delegate_->OnWebSocketStreamReady( + job->server_ssl_config(), + job->proxy_info(), + websocket_stream_factory_->CreateSpdyStream( + spdy_session, use_relative_url)); + } else { + bool use_relative_url = direct || url().SchemeIs("https"); + delegate_->OnStreamReady( + job->server_ssl_config(), + job->proxy_info(), + new SpdyHttpStream(spdy_session, use_relative_url)); + } // |this| may be deleted after this point. - factory->OnSpdySessionReady( + factory->OnNewSpdySessionReady( spdy_session, direct, used_ssl_config, used_proxy_info, was_npn_negotiated, protocol_negotiated, using_spdy, net_log); } @@ -342,4 +348,28 @@ void HttpStreamFactoryImpl::Request::OrphanJobs() { factory_->OrphanJob(*it, this); } +void HttpStreamFactoryImpl::Request::OnJobSucceeded(Job* job) { + // |job| should only be NULL if we're being serviced by a late bound + // SpdySession or HttpPipelinedConnection (one that was not created by a job + // in our |jobs_| set). + if (!job) { + DCHECK(!bound_job_.get()); + DCHECK(!jobs_.empty()); + // NOTE(willchan): We do *NOT* call OrphanJobs() here. The reason is because + // we *WANT* to cancel the unnecessary Jobs from other requests if another + // Job completes first. + // TODO(mbelshe): Revisit this when we implement ip connection pooling of + // SpdySessions. Do we want to orphan the jobs for a different hostname so + // they complete? Or do we want to prevent connecting a new SpdySession if + // we've already got one available for a different hostname where the ip + // address matches up? + } else if (!bound_job_.get()) { + // We may have other jobs in |jobs_|. For example, if we start multiple jobs + // for Alternate-Protocol. + OrphanJobsExcept(job); + } else { + DCHECK(jobs_.empty()); + } +} + } // namespace net diff --git a/net/http/http_stream_factory_impl_request.h b/net/http/http_stream_factory_impl_request.h index 18633fa..ff4f8af 100644 --- a/net/http/http_stream_factory_impl_request.h +++ b/net/http/http_stream_factory_impl_request.h @@ -15,11 +15,15 @@ namespace net { +class ClientSocketHandle; +class SpdySession; + class HttpStreamFactoryImpl::Request : public HttpStreamRequest { public: Request(const GURL& url, HttpStreamFactoryImpl* factory, HttpStreamRequest::Delegate* delegate, + WebSocketStreamBase::Factory* websocket_stream_factory, const BoundNetLog& net_log); virtual ~Request(); @@ -58,9 +62,13 @@ class HttpStreamFactoryImpl::Request : public HttpStreamRequest { void RemoveRequestFromHttpPipeliningRequestMap(); // Called by an attached Job if it sets up a SpdySession. - void OnSpdySessionReady(Job* job, - scoped_refptr<SpdySession> spdy_session, - bool direct); + void OnNewSpdySessionReady(Job* job, + scoped_refptr<SpdySession> spdy_session, + bool direct); + + WebSocketStreamBase::Factory* websocket_stream_factory() { + return websocket_stream_factory_; + } // HttpStreamRequest::Delegate methods which we implement. Note we don't // actually subclass HttpStreamRequest::Delegate. @@ -69,6 +77,10 @@ class HttpStreamFactoryImpl::Request : public HttpStreamRequest { const SSLConfig& used_ssl_config, const ProxyInfo& used_proxy_info, HttpStreamBase* stream); + void OnWebSocketStreamReady(Job* job, + const SSLConfig& used_ssl_config, + const ProxyInfo& used_proxy_info, + WebSocketStreamBase* stream); void OnStreamFailed(Job* job, int status, const SSLConfig& used_ssl_config); void OnCertificateError(Job* job, int status, @@ -106,8 +118,12 @@ class HttpStreamFactoryImpl::Request : public HttpStreamRequest { // Used to orphan all jobs in |jobs_|. void OrphanJobs(); + // Called when a Job succeeds. + void OnJobSucceeded(Job* job); + const GURL url_; HttpStreamFactoryImpl* const factory_; + WebSocketStreamBase::Factory* const websocket_stream_factory_; HttpStreamRequest::Delegate* const delegate_; const BoundNetLog net_log_; @@ -128,4 +144,4 @@ class HttpStreamFactoryImpl::Request : public HttpStreamRequest { } // namespace net -#endif // NET_HTTP_HTTP_STREAM_FACTORY_IMPL_H_ +#endif // NET_HTTP_HTTP_STREAM_FACTORY_IMPL_REQUEST_H_ diff --git a/net/http/http_stream_factory_impl_unittest.cc b/net/http/http_stream_factory_impl_unittest.cc index 0200b8f..210e940 100644 --- a/net/http/http_stream_factory_impl_unittest.cc +++ b/net/http/http_stream_factory_impl_unittest.cc @@ -5,6 +5,7 @@ #include "net/http/http_stream_factory_impl.h" #include <string> +#include <vector> #include "base/basictypes.h" #include "net/base/net_log.h" @@ -16,26 +17,72 @@ #include "net/http/http_network_session_peer.h" #include "net/http/http_network_transaction.h" #include "net/http/http_request_info.h" +#include "net/http/http_server_properties.h" #include "net/http/http_server_properties_impl.h" #include "net/http/http_stream.h" #include "net/http/transport_security_state.h" #include "net/proxy/proxy_info.h" #include "net/proxy/proxy_service.h" +#include "net/socket/client_socket_handle.h" #include "net/socket/mock_client_socket_pool_manager.h" +#include "net/socket/next_proto.h" #include "net/socket/socket_test_util.h" #include "net/spdy/spdy_session.h" #include "net/spdy/spdy_session_pool.h" +#include "net/spdy/spdy_test_util_common.h" +#include "net/ssl/ssl_config_service.h" #include "net/ssl/ssl_config_service_defaults.h" +// This file can be included from net/http even though +// it is in net/websockets because it doesn't +// introduce any link dependency to net/websockets. +#include "net/websockets/websocket_stream_base.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { namespace { -class MockHttpStreamFactoryImpl : public HttpStreamFactoryImpl { +class UseAlternateProtocolsScopedSetter { public: - MockHttpStreamFactoryImpl(HttpNetworkSession* session) - : HttpStreamFactoryImpl(session), + UseAlternateProtocolsScopedSetter(bool use_alternate_protocols) + : use_alternate_protocols_(HttpStreamFactory::use_alternate_protocols()) { + HttpStreamFactory::set_use_alternate_protocols(use_alternate_protocols); + } + ~UseAlternateProtocolsScopedSetter() { + HttpStreamFactory::set_use_alternate_protocols(use_alternate_protocols_); + } + + private: + bool use_alternate_protocols_; +}; + +class MockWebSocketStream : public WebSocketStreamBase { + public: + enum StreamType { + kStreamTypeBasic, + kStreamTypeSpdy, + }; + + explicit MockWebSocketStream(StreamType type) : type_(type) {} + + virtual ~MockWebSocketStream() {} + + virtual WebSocketStream* AsWebSocketStream() OVERRIDE { return NULL; } + + StreamType type() const { + return type_; + } + + private: + const StreamType type_; +}; + +// HttpStreamFactoryImpl subclass that can wait until a preconnect is complete. +class MockHttpStreamFactoryImplForPreconnect : public HttpStreamFactoryImpl { + public: + MockHttpStreamFactoryImplForPreconnect(HttpNetworkSession* session, + bool for_websockets) + : HttpStreamFactoryImpl(session, for_websockets), preconnect_done_(false), waiting_for_preconnect_(false) {} @@ -77,6 +124,19 @@ class StreamRequestWaiter : public HttpStreamRequest::Delegate { base::MessageLoop::current()->Quit(); stream_.reset(stream); used_ssl_config_ = used_ssl_config; + used_proxy_info_ = used_proxy_info; + } + + virtual void OnWebSocketStreamReady( + const SSLConfig& used_ssl_config, + const ProxyInfo& used_proxy_info, + WebSocketStreamBase* stream) OVERRIDE { + stream_done_ = true; + if (waiting_for_stream_) + base::MessageLoop::current()->Quit(); + websocket_stream_.reset(stream); + used_ssl_config_ = used_ssl_config; + used_proxy_info_ = used_proxy_info; } virtual void OnStreamFailed( @@ -113,20 +173,75 @@ class StreamRequestWaiter : public HttpStreamRequest::Delegate { return used_ssl_config_; } + const ProxyInfo& used_proxy_info() const { + return used_proxy_info_; + } + HttpStreamBase* stream() { return stream_.get(); } + MockWebSocketStream* websocket_stream() { + return static_cast<MockWebSocketStream*>(websocket_stream_.get()); + } + + bool stream_done() const { return stream_done_; } private: bool waiting_for_stream_; bool stream_done_; scoped_ptr<HttpStreamBase> stream_; + scoped_ptr<WebSocketStreamBase> websocket_stream_; SSLConfig used_ssl_config_; + ProxyInfo used_proxy_info_; DISALLOW_COPY_AND_ASSIGN(StreamRequestWaiter); }; +class WebSocketSpdyStream : public MockWebSocketStream { + public: + explicit WebSocketSpdyStream(SpdySession* spdy_session) + : MockWebSocketStream(kStreamTypeSpdy), spdy_session_(spdy_session) {} + + virtual ~WebSocketSpdyStream() {} + + SpdySession* spdy_session() { return spdy_session_.get(); } + + private: + scoped_refptr<SpdySession> spdy_session_; +}; + +class WebSocketBasicStream : public MockWebSocketStream { + public: + explicit WebSocketBasicStream(ClientSocketHandle* connection) + : MockWebSocketStream(kStreamTypeBasic), connection_(connection) {} + + virtual ~WebSocketBasicStream() { + connection_->socket()->Disconnect(); + } + + ClientSocketHandle* connection() { return connection_.get(); } + + private: + scoped_ptr<ClientSocketHandle> connection_; +}; + +class WebSocketStreamFactory : public WebSocketStreamBase::Factory { + public: + virtual ~WebSocketStreamFactory() {} + + virtual WebSocketStreamBase* CreateBasicStream(ClientSocketHandle* connection, + bool using_proxy) OVERRIDE { + return new WebSocketBasicStream(connection); + } + + virtual WebSocketStreamBase* CreateSpdyStream( + SpdySession* spdy_session, + bool use_relative_url) OVERRIDE { + return new WebSocketSpdyStream(spdy_session); + } +}; + struct SessionDependencies { // Custom proxy service dependency. explicit SessionDependencies(ProxyService* proxy_service) @@ -163,6 +278,7 @@ HttpNetworkSession* CreateSession(SessionDependencies* session_deps) { session_deps->http_auth_handler_factory.get(); params.net_log = session_deps->net_log; params.http_server_properties = &session_deps->http_server_properties; + return new HttpNetworkSession(params); } @@ -182,8 +298,8 @@ void PreconnectHelperForURL(int num_streams, const GURL& url, HttpNetworkSession* session) { HttpNetworkSessionPeer peer(session); - MockHttpStreamFactoryImpl* mock_factory = - new MockHttpStreamFactoryImpl(session); + MockHttpStreamFactoryImplForPreconnect* mock_factory = + new MockHttpStreamFactoryImplForPreconnect(session, false); peer.SetHttpStreamFactory(mock_factory); SSLConfig ssl_config; session->ssl_config_service()->GetSSLConfig(&ssl_config); @@ -465,7 +581,7 @@ TEST(HttpStreamFactoryTest, JobNotifiesProxy) { scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - // Now request a stream. It should succeed using the second proxy in the + // Now request a stream. It should succeed using the second proxy in the // list. HttpRequestInfo request_info; request_info.method = "GET"; @@ -537,7 +653,7 @@ int GetSocketPoolGroupCount(ClientSocketPool* pool) { } return count; } -}; +} // namespace TEST(HttpStreamFactoryTest, PrivacyModeUsesDifferentSocketPoolGroup) { SessionDependencies session_deps(ProxyService::CreateDirect()); @@ -615,6 +731,442 @@ TEST(HttpStreamFactoryTest, GetLoadState) { waiter.WaitForStream(); } +TEST(HttpStreamFactoryTest, RequestHttpStream) { + SessionDependencies session_deps(ProxyService::CreateDirect()); + + StaticSocketDataProvider socket_data; + socket_data.set_connect_data(MockConnect(ASYNC, OK)); + session_deps.socket_factory.AddSocketDataProvider(&socket_data); + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + + // Now request a stream. It should succeed using the second proxy in the + // list. + HttpRequestInfo request_info; + request_info.method = "GET"; + request_info.url = GURL("http://www.google.com"); + request_info.load_flags = 0; + + SSLConfig ssl_config; + StreamRequestWaiter waiter; + scoped_ptr<HttpStreamRequest> request( + session->http_stream_factory()->RequestStream( + request_info, + DEFAULT_PRIORITY, + ssl_config, + ssl_config, + &waiter, + BoundNetLog())); + waiter.WaitForStream(); + EXPECT_TRUE(waiter.stream_done()); + ASSERT_TRUE(NULL != waiter.stream()); + EXPECT_TRUE(NULL == waiter.websocket_stream()); + EXPECT_FALSE(waiter.stream()->IsSpdyHttpStream()); + + EXPECT_EQ(1, GetSocketPoolGroupCount( + session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); + EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool( + HttpNetworkSession::NORMAL_SOCKET_POOL))); + EXPECT_TRUE(waiter.used_proxy_info().is_direct()); +} + +TEST(HttpStreamFactoryTest, RequestHttpStreamOverSSL) { + SessionDependencies session_deps(ProxyService::CreateDirect()); + + MockRead mock_read(ASYNC, OK); + StaticSocketDataProvider socket_data(&mock_read, 1, NULL, 0); + socket_data.set_connect_data(MockConnect(ASYNC, OK)); + session_deps.socket_factory.AddSocketDataProvider(&socket_data); + + SSLSocketDataProvider ssl_socket_data(ASYNC, OK); + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_socket_data); + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + + // Now request a stream. + HttpRequestInfo request_info; + request_info.method = "GET"; + request_info.url = GURL("https://www.google.com"); + request_info.load_flags = 0; + + SSLConfig ssl_config; + StreamRequestWaiter waiter; + scoped_ptr<HttpStreamRequest> request( + session->http_stream_factory()->RequestStream( + request_info, + DEFAULT_PRIORITY, + ssl_config, + ssl_config, + &waiter, + BoundNetLog())); + waiter.WaitForStream(); + EXPECT_TRUE(waiter.stream_done()); + ASSERT_TRUE(NULL != waiter.stream()); + EXPECT_TRUE(NULL == waiter.websocket_stream()); + EXPECT_FALSE(waiter.stream()->IsSpdyHttpStream()); + EXPECT_EQ(1, GetSocketPoolGroupCount( + session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); + EXPECT_EQ(1, GetSocketPoolGroupCount( + session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); + EXPECT_TRUE(waiter.used_proxy_info().is_direct()); +} + +TEST(HttpStreamFactoryTest, RequestHttpStreamOverProxy) { + SessionDependencies session_deps(ProxyService::CreateFixed("myproxy:8888")); + + StaticSocketDataProvider socket_data; + socket_data.set_connect_data(MockConnect(ASYNC, OK)); + session_deps.socket_factory.AddSocketDataProvider(&socket_data); + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + + // Now request a stream. It should succeed using the second proxy in the + // list. + HttpRequestInfo request_info; + request_info.method = "GET"; + request_info.url = GURL("http://www.google.com"); + request_info.load_flags = 0; + + SSLConfig ssl_config; + StreamRequestWaiter waiter; + scoped_ptr<HttpStreamRequest> request( + session->http_stream_factory()->RequestStream( + request_info, + DEFAULT_PRIORITY, + ssl_config, + ssl_config, + &waiter, + BoundNetLog())); + waiter.WaitForStream(); + EXPECT_TRUE(waiter.stream_done()); + ASSERT_TRUE(NULL != waiter.stream()); + EXPECT_TRUE(NULL == waiter.websocket_stream()); + EXPECT_FALSE(waiter.stream()->IsSpdyHttpStream()); + EXPECT_EQ(0, GetSocketPoolGroupCount( + session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); + EXPECT_EQ(0, GetSocketPoolGroupCount( + session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); + EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSocketPoolForHTTPProxy( + HttpNetworkSession::NORMAL_SOCKET_POOL, + HostPortPair("myproxy", 8888)))); + EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPoolForSSLWithProxy( + HttpNetworkSession::NORMAL_SOCKET_POOL, + HostPortPair("myproxy", 8888)))); + EXPECT_FALSE(waiter.used_proxy_info().is_direct()); +} + +TEST(HttpStreamFactoryTest, RequestWebSocketBasicStream) { + SessionDependencies session_deps(ProxyService::CreateDirect()); + + StaticSocketDataProvider socket_data; + socket_data.set_connect_data(MockConnect(ASYNC, OK)); + session_deps.socket_factory.AddSocketDataProvider(&socket_data); + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + + // Now request a stream. + HttpRequestInfo request_info; + request_info.method = "GET"; + request_info.url = GURL("ws://www.google.com"); + request_info.load_flags = 0; + + SSLConfig ssl_config; + StreamRequestWaiter waiter; + WebSocketStreamFactory factory; + scoped_ptr<HttpStreamRequest> request( + session->websocket_stream_factory()->RequestWebSocketStream( + request_info, + DEFAULT_PRIORITY, + ssl_config, + ssl_config, + &waiter, + &factory, + BoundNetLog())); + waiter.WaitForStream(); + EXPECT_TRUE(waiter.stream_done()); + EXPECT_TRUE(NULL == waiter.stream()); + ASSERT_TRUE(NULL != waiter.websocket_stream()); + EXPECT_EQ(MockWebSocketStream::kStreamTypeBasic, + waiter.websocket_stream()->type()); + EXPECT_EQ(1, GetSocketPoolGroupCount( + session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); + EXPECT_EQ(0, GetSocketPoolGroupCount( + session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); + EXPECT_TRUE(waiter.used_proxy_info().is_direct()); +} + +TEST(HttpStreamFactoryTest, RequestWebSocketBasicStreamOverSSL) { + SessionDependencies session_deps(ProxyService::CreateDirect()); + + MockRead mock_read(ASYNC, OK); + StaticSocketDataProvider socket_data(&mock_read, 1, NULL, 0); + socket_data.set_connect_data(MockConnect(ASYNC, OK)); + session_deps.socket_factory.AddSocketDataProvider(&socket_data); + + SSLSocketDataProvider ssl_socket_data(ASYNC, OK); + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_socket_data); + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + + // Now request a stream. + HttpRequestInfo request_info; + request_info.method = "GET"; + request_info.url = GURL("wss://www.google.com"); + request_info.load_flags = 0; + + SSLConfig ssl_config; + StreamRequestWaiter waiter; + WebSocketStreamFactory factory; + scoped_ptr<HttpStreamRequest> request( + session->websocket_stream_factory()->RequestWebSocketStream( + request_info, + DEFAULT_PRIORITY, + ssl_config, + ssl_config, + &waiter, + &factory, + BoundNetLog())); + waiter.WaitForStream(); + EXPECT_TRUE(waiter.stream_done()); + EXPECT_TRUE(NULL == waiter.stream()); + ASSERT_TRUE(NULL != waiter.websocket_stream()); + EXPECT_EQ(MockWebSocketStream::kStreamTypeBasic, + waiter.websocket_stream()->type()); + EXPECT_EQ(1, GetSocketPoolGroupCount( + session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); + EXPECT_EQ(1, GetSocketPoolGroupCount( + session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); + EXPECT_TRUE(waiter.used_proxy_info().is_direct()); +} + +TEST(HttpStreamFactoryTest, RequestWebSocketBasicStreamOverProxy) { + SessionDependencies session_deps(ProxyService::CreateFixed("myproxy:8888")); + + MockRead read(SYNCHRONOUS, "HTTP/1.0 200 Connection established\r\n\r\n"); + StaticSocketDataProvider socket_data(&read, 1, 0, 0); + socket_data.set_connect_data(MockConnect(ASYNC, OK)); + session_deps.socket_factory.AddSocketDataProvider(&socket_data); + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + + // Now request a stream. + HttpRequestInfo request_info; + request_info.method = "GET"; + request_info.url = GURL("ws://www.google.com"); + request_info.load_flags = 0; + + SSLConfig ssl_config; + StreamRequestWaiter waiter; + WebSocketStreamFactory factory; + scoped_ptr<HttpStreamRequest> request( + session->websocket_stream_factory()->RequestWebSocketStream( + request_info, + DEFAULT_PRIORITY, + ssl_config, + ssl_config, + &waiter, + &factory, + BoundNetLog())); + waiter.WaitForStream(); + EXPECT_TRUE(waiter.stream_done()); + EXPECT_TRUE(NULL == waiter.stream()); + ASSERT_TRUE(NULL != waiter.websocket_stream()); + EXPECT_EQ(MockWebSocketStream::kStreamTypeBasic, + waiter.websocket_stream()->type()); + EXPECT_EQ(0, GetSocketPoolGroupCount( + session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); + EXPECT_EQ(0, GetSocketPoolGroupCount( + session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); + EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSocketPoolForHTTPProxy( + HttpNetworkSession::NORMAL_SOCKET_POOL, + HostPortPair("myproxy", 8888)))); + EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPoolForSSLWithProxy( + HttpNetworkSession::NORMAL_SOCKET_POOL, + HostPortPair("myproxy", 8888)))); + EXPECT_FALSE(waiter.used_proxy_info().is_direct()); +} + +TEST(HttpStreamFactoryTest, RequestSpdyHttpStream) { + SpdySessionDependencies session_deps(kProtoSPDY3, + ProxyService::CreateDirect()); + + MockRead mock_read(ASYNC, OK); + StaticSocketDataProvider socket_data(&mock_read, 1, NULL, 0); + socket_data.set_connect_data(MockConnect(ASYNC, OK)); + session_deps.socket_factory->AddSocketDataProvider(&socket_data); + + SSLSocketDataProvider ssl_socket_data(ASYNC, OK); + ssl_socket_data.SetNextProto(kProtoSPDY3); + session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); + + HostPortPair host_port_pair("www.google.com", 443); + scoped_refptr<HttpNetworkSession> + session(SpdySessionDependencies::SpdyCreateSession(&session_deps)); + + // Now request a stream. + HttpRequestInfo request_info; + request_info.method = "GET"; + request_info.url = GURL("https://www.google.com"); + request_info.load_flags = 0; + + SSLConfig ssl_config; + StreamRequestWaiter waiter; + scoped_ptr<HttpStreamRequest> request( + session->http_stream_factory()->RequestStream( + request_info, + DEFAULT_PRIORITY, + ssl_config, + ssl_config, + &waiter, + BoundNetLog())); + waiter.WaitForStream(); + EXPECT_TRUE(waiter.stream_done()); + EXPECT_TRUE(NULL == waiter.websocket_stream()); + ASSERT_TRUE(NULL != waiter.stream()); + EXPECT_TRUE(waiter.stream()->IsSpdyHttpStream()); + EXPECT_EQ(1, GetSocketPoolGroupCount( + session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); + EXPECT_EQ(1, GetSocketPoolGroupCount( + session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); + EXPECT_TRUE(waiter.used_proxy_info().is_direct()); +} + +TEST(HttpStreamFactoryTest, RequestWebSocketSpdyStream) { + SpdySessionDependencies session_deps(kProtoSPDY3, + ProxyService::CreateDirect()); + + MockRead mock_read(SYNCHRONOUS, ERR_IO_PENDING); + StaticSocketDataProvider socket_data(&mock_read, 1, NULL, 0); + socket_data.set_connect_data(MockConnect(ASYNC, OK)); + session_deps.socket_factory->AddSocketDataProvider(&socket_data); + + SSLSocketDataProvider ssl_socket_data(ASYNC, OK); + ssl_socket_data.SetNextProto(kProtoSPDY3); + session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); + + HostPortPair host_port_pair("www.google.com", 80); + scoped_refptr<HttpNetworkSession> + session(SpdySessionDependencies::SpdyCreateSession(&session_deps)); + + // Now request a stream. + HttpRequestInfo request_info; + request_info.method = "GET"; + request_info.url = GURL("wss://www.google.com"); + request_info.load_flags = 0; + + SSLConfig ssl_config; + StreamRequestWaiter waiter1; + WebSocketStreamFactory factory; + scoped_ptr<HttpStreamRequest> request1( + session->websocket_stream_factory()->RequestWebSocketStream( + request_info, + DEFAULT_PRIORITY, + ssl_config, + ssl_config, + &waiter1, + &factory, + BoundNetLog())); + waiter1.WaitForStream(); + EXPECT_TRUE(waiter1.stream_done()); + ASSERT_TRUE(NULL != waiter1.websocket_stream()); + EXPECT_EQ(MockWebSocketStream::kStreamTypeSpdy, + waiter1.websocket_stream()->type()); + EXPECT_TRUE(NULL == waiter1.stream()); + + StreamRequestWaiter waiter2; + scoped_ptr<HttpStreamRequest> request2( + session->websocket_stream_factory()->RequestWebSocketStream( + request_info, + DEFAULT_PRIORITY, + ssl_config, + ssl_config, + &waiter2, + &factory, + BoundNetLog())); + waiter2.WaitForStream(); + EXPECT_TRUE(waiter2.stream_done()); + ASSERT_TRUE(NULL != waiter2.websocket_stream()); + EXPECT_EQ(MockWebSocketStream::kStreamTypeSpdy, + waiter2.websocket_stream()->type()); + EXPECT_TRUE(NULL == waiter2.stream()); + EXPECT_NE(waiter2.websocket_stream(), waiter1.websocket_stream()); + EXPECT_EQ(static_cast<WebSocketSpdyStream*>(waiter2.websocket_stream())-> + spdy_session(), + static_cast<WebSocketSpdyStream*>(waiter1.websocket_stream())-> + spdy_session()); + + EXPECT_EQ(1, GetSocketPoolGroupCount( + session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); + EXPECT_EQ(1, GetSocketPoolGroupCount( + session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); + EXPECT_TRUE(waiter1.used_proxy_info().is_direct()); +} + +TEST(HttpStreamFactoryTest, OrphanedWebSocketStream) { + UseAlternateProtocolsScopedSetter use_alternate_protocols(true); + SpdySessionDependencies session_deps(kProtoSPDY3, + ProxyService::CreateDirect()); + + MockRead mock_read(ASYNC, OK); + StaticSocketDataProvider socket_data(&mock_read, 1, NULL, 0); + socket_data.set_connect_data(MockConnect(ASYNC, OK)); + session_deps.socket_factory->AddSocketDataProvider(&socket_data); + + MockRead mock_read2(ASYNC, OK); + StaticSocketDataProvider socket_data2(&mock_read2, 1, NULL, 0); + socket_data2.set_connect_data(MockConnect(ASYNC, ERR_IO_PENDING)); + session_deps.socket_factory->AddSocketDataProvider(&socket_data2); + + SSLSocketDataProvider ssl_socket_data(ASYNC, OK); + ssl_socket_data.SetNextProto(kProtoSPDY3); + session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); + + scoped_refptr<HttpNetworkSession> + session(SpdySessionDependencies::SpdyCreateSession(&session_deps)); + + // Now request a stream. + HttpRequestInfo request_info; + request_info.method = "GET"; + request_info.url = GURL("ws://www.google.com:8888"); + request_info.load_flags = 0; + + session->http_server_properties()->SetAlternateProtocol( + HostPortPair("www.google.com", 8888), + 9999, + NPN_SPDY_3); + + SSLConfig ssl_config; + StreamRequestWaiter waiter; + WebSocketStreamFactory factory; + scoped_ptr<HttpStreamRequest> request( + session->websocket_stream_factory()->RequestWebSocketStream( + request_info, + DEFAULT_PRIORITY, + ssl_config, + ssl_config, + &waiter, + &factory, + BoundNetLog())); + waiter.WaitForStream(); + EXPECT_TRUE(waiter.stream_done()); + EXPECT_TRUE(NULL == waiter.stream()); + ASSERT_TRUE(NULL != waiter.websocket_stream()); + EXPECT_EQ(MockWebSocketStream::kStreamTypeSpdy, + waiter.websocket_stream()->type()); + + // Make sure that there was an alternative connection + // which consumes extra connections. + EXPECT_EQ(2, GetSocketPoolGroupCount( + session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); + EXPECT_EQ(1, GetSocketPoolGroupCount( + session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); + EXPECT_TRUE(waiter.used_proxy_info().is_direct()); + + // Make sure there is no orphaned job. it is already canceled. + ASSERT_EQ(0u, static_cast<HttpStreamFactoryImpl*>( + session->websocket_stream_factory())->num_orphaned_jobs()); +} + } // namespace } // namespace net diff --git a/net/net.gyp b/net/net.gyp index e84f851..c9d37a7 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -1078,6 +1078,7 @@ 'websockets/websocket_net_log_params.cc', 'websockets/websocket_net_log_params.h', 'websockets/websocket_stream.h', + 'websockets/websocket_stream_base.h', 'websockets/websocket_throttle.cc', 'websockets/websocket_throttle.h', ], diff --git a/net/socket/client_socket_pool_manager.cc b/net/socket/client_socket_pool_manager.cc index 0b49b56..c00b5ec 100644 --- a/net/socket/client_socket_pool_manager.cc +++ b/net/socket/client_socket_pool_manager.cc @@ -88,7 +88,8 @@ int InitSocketPoolHelper(const GURL& request_url, scoped_refptr<SOCKSSocketParams> socks_params; scoped_ptr<HostPortPair> proxy_host_port; - bool using_ssl = request_url.SchemeIs("https") || force_spdy_over_ssl; + bool using_ssl = request_url.SchemeIs("https") || + request_url.SchemeIs("wss") || force_spdy_over_ssl; HostPortPair origin_host_port = HostPortPair(request_url.HostNoBrackets(), @@ -393,6 +394,30 @@ int InitSocketHandleForHttpRequest( 0, socket_handle, resolution_callback, callback); } +int InitSocketHandleForWebSocketRequest( + const GURL& request_url, + const HttpRequestHeaders& request_extra_headers, + int request_load_flags, + RequestPriority request_priority, + HttpNetworkSession* session, + const ProxyInfo& proxy_info, + bool force_spdy_over_ssl, + bool want_spdy_over_npn, + const SSLConfig& ssl_config_for_origin, + const SSLConfig& ssl_config_for_proxy, + PrivacyMode privacy_mode, + const BoundNetLog& net_log, + ClientSocketHandle* socket_handle, + const OnHostResolutionCallback& resolution_callback, + const CompletionCallback& callback) { + DCHECK(socket_handle); + return InitSocketPoolHelper( + request_url, request_extra_headers, request_load_flags, request_priority, + session, proxy_info, force_spdy_over_ssl, want_spdy_over_npn, + ssl_config_for_origin, ssl_config_for_proxy, true, privacy_mode, net_log, + 0, socket_handle, resolution_callback, callback); +} + int InitSocketHandleForRawConnect( const HostPortPair& host_port_pair, HttpNetworkSession* session, diff --git a/net/socket/client_socket_pool_manager.h b/net/socket/client_socket_pool_manager.h index 434a463..a514d25 100644 --- a/net/socket/client_socket_pool_manager.h +++ b/net/socket/client_socket_pool_manager.h @@ -108,6 +108,31 @@ int InitSocketHandleForHttpRequest( // A helper method that uses the passed in proxy information to initialize a // ClientSocketHandle with the relevant socket pool. Use this method for +// HTTP/HTTPS requests for WebSocket handshake. +// |ssl_config_for_origin| is only used if the request +// uses SSL and |ssl_config_for_proxy| is used if the proxy server is HTTPS. +// |resolution_callback| will be invoked after the the hostname is +// resolved. If |resolution_callback| does not return OK, then the +// connection will be aborted with that value. +int InitSocketHandleForWebSocketRequest( + const GURL& request_url, + const HttpRequestHeaders& request_extra_headers, + int request_load_flags, + RequestPriority request_priority, + HttpNetworkSession* session, + const ProxyInfo& proxy_info, + bool force_spdy_over_ssl, + bool want_spdy_over_npn, + const SSLConfig& ssl_config_for_origin, + const SSLConfig& ssl_config_for_proxy, + PrivacyMode privacy_mode, + const BoundNetLog& net_log, + ClientSocketHandle* socket_handle, + const OnHostResolutionCallback& resolution_callback, + const CompletionCallback& callback); + +// A helper method that uses the passed in proxy information to initialize a +// ClientSocketHandle with the relevant socket pool. Use this method for // a raw socket connection to a host-port pair (that needs to tunnel through // the proxies). NET_EXPORT int InitSocketHandleForRawConnect( diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc index a14ab2a..dbc59d7 100644 --- a/net/socket/socket_test_util.cc +++ b/net/socket/socket_test_util.cc @@ -877,7 +877,10 @@ int MockTCPClientSocket::Connect(const CompletionCallback& callback) { connected_ = true; peer_closed_connection_ = false; if (data_->connect_data().mode == ASYNC) { - RunCallbackAsync(callback, data_->connect_data().result); + if (data_->connect_data().result == ERR_IO_PENDING) + pending_callback_ = callback; + else + RunCallbackAsync(callback, data_->connect_data().result); return ERR_IO_PENDING; } return data_->connect_data().result; @@ -940,6 +943,11 @@ void MockTCPClientSocket::OnReadComplete(const MockRead& data) { RunCallback(callback, rv); } +void MockTCPClientSocket::OnConnectComplete(const MockConnect& data) { + CompletionCallback callback = pending_callback_; + RunCallback(callback, data.result); +} + int MockTCPClientSocket::CompleteRead() { DCHECK(pending_buf_); DCHECK(pending_buf_len_ > 0); @@ -1171,6 +1179,10 @@ const BoundNetLog& DeterministicMockUDPClientSocket::NetLog() const { void DeterministicMockUDPClientSocket::OnReadComplete(const MockRead& data) {} +void DeterministicMockUDPClientSocket::OnConnectComplete( + const MockConnect& data) { + NOTIMPLEMENTED(); +} DeterministicMockTCPClientSocket::DeterministicMockTCPClientSocket( net::NetLog* net_log, @@ -1261,6 +1273,9 @@ bool DeterministicMockTCPClientSocket::GetSSLInfo(SSLInfo* ssl_info) { void DeterministicMockTCPClientSocket::OnReadComplete(const MockRead& data) {} +void DeterministicMockTCPClientSocket::OnConnectComplete( + const MockConnect& data) {} + // static void MockSSLClientSocket::ConnectCallback( MockSSLClientSocket *ssl_client_socket, @@ -1407,6 +1422,10 @@ void MockSSLClientSocket::OnReadComplete(const MockRead& data) { NOTIMPLEMENTED(); } +void MockSSLClientSocket::OnConnectComplete(const MockConnect& data) { + NOTIMPLEMENTED(); +} + MockUDPClientSocket::MockUDPClientSocket(SocketDataProvider* data, net::NetLog* net_log) : connected_(false), @@ -1526,6 +1545,10 @@ void MockUDPClientSocket::OnReadComplete(const MockRead& data) { RunCallback(callback, rv); } +void MockUDPClientSocket::OnConnectComplete(const MockConnect& data) { + NOTIMPLEMENTED(); +} + int MockUDPClientSocket::CompleteRead() { DCHECK(pending_buf_); DCHECK(pending_buf_len_ > 0); diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h index bab980c..6afe170 100644 --- a/net/socket/socket_test_util.h +++ b/net/socket/socket_test_util.h @@ -199,6 +199,7 @@ class AsyncSocket { // data.async is ignored, and this read is completed synchronously as // part of this call. virtual void OnReadComplete(const MockRead& data) = 0; + virtual void OnConnectComplete(const MockConnect& data) = 0; }; // SocketDataProvider which responds based on static tables of mock reads and @@ -229,7 +230,7 @@ class StaticSocketDataProvider : public SocketDataProvider { // SocketDataProvider implementation. virtual MockRead GetNextRead() OVERRIDE; virtual MockWriteResult OnWrite(const std::string& data) OVERRIDE; - virtual void Reset() OVERRIDE; + ; virtual void Reset() OVERRIDE; private: MockRead* reads_; @@ -696,6 +697,7 @@ class MockTCPClientSocket : public MockClientSocket, public AsyncSocket { // AsyncSocket: virtual void OnReadComplete(const MockRead& data) OVERRIDE; + virtual void OnConnectComplete(const MockConnect& data) OVERRIDE; private: int CompleteRead(); @@ -803,6 +805,7 @@ class DeterministicMockUDPClientSocket // AsyncSocket implementation. virtual void OnReadComplete(const MockRead& data) OVERRIDE; + virtual void OnConnectComplete(const MockConnect& data) OVERRIDE; private: bool connected_; @@ -845,6 +848,7 @@ class DeterministicMockTCPClientSocket // AsyncSocket: virtual void OnReadComplete(const MockRead& data) OVERRIDE; + virtual void OnConnectComplete(const MockConnect& data) OVERRIDE; private: DeterministicSocketHelper helper_; @@ -887,6 +891,7 @@ class MockSSLClientSocket : public MockClientSocket, public AsyncSocket { // This MockSocket does not implement the manual async IO feature. virtual void OnReadComplete(const MockRead& data) OVERRIDE; + virtual void OnConnectComplete(const MockConnect& data) OVERRIDE; virtual bool WasChannelIDSent() const OVERRIDE; virtual void set_channel_id_sent(bool channel_id_sent) OVERRIDE; @@ -931,6 +936,7 @@ class MockUDPClientSocket // AsyncSocket implementation. virtual void OnReadComplete(const MockRead& data) OVERRIDE; + virtual void OnConnectComplete(const MockConnect& data) OVERRIDE; private: int CompleteRead(); diff --git a/net/websockets/websocket_stream.h b/net/websockets/websocket_stream.h index c69aa85..10631f4 100644 --- a/net/websockets/websocket_stream.h +++ b/net/websockets/websocket_stream.h @@ -8,6 +8,7 @@ #include "base/basictypes.h" #include "base/memory/scoped_vector.h" #include "net/base/completion_callback.h" +#include "net/websockets/websocket_stream_base.h" namespace net { @@ -30,7 +31,7 @@ struct WebSocketFrameChunk; // |callback| will be called when the operation is finished. Non-null |callback| // must be provided to these functions. -class WebSocketStream { +class WebSocketStream : public WebSocketStreamBase { public: WebSocketStream() {} @@ -95,6 +96,9 @@ class WebSocketStream { // - RenewStreamForAuth for authentication (is this necessary?) // - GetSSLInfo, GetSSLCertRequsetInfo for SSL + // WebSocketStreamBase derived functions + virtual WebSocketStream* AsWebSocketStream() { return this; } + private: DISALLOW_COPY_AND_ASSIGN(WebSocketStream); }; diff --git a/net/websockets/websocket_stream_base.h b/net/websockets/websocket_stream_base.h new file mode 100644 index 0000000..7786064 --- /dev/null +++ b/net/websockets/websocket_stream_base.h @@ -0,0 +1,55 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_WEBSOCKETS_WEBSOCKET_STREAM_BASE_H_ +#define NET_WEBSOCKETS_WEBSOCKET_STREAM_BASE_H_ + +// This file is included from net/http files. +// Since net/http can be built without linking net/websockets code, +// this file should not depend on net/websockets. + +#include <base/basictypes.h> + +namespace net { + +class ClientSocketHandle; +class SpdySession; +class WebSocketStream; + +// WebSocketStreamBase is the base class of WebSocketStream. +// net/http code uses this interface to handle WebSocketStream. +class WebSocketStreamBase { + public: + class Factory { + public: + virtual ~Factory() {} + + // Create a WebSocketBasicStream. + // This function (or the returned object) takes the ownership + // of |connection|. + virtual WebSocketStreamBase* CreateBasicStream( + ClientSocketHandle* connection, + bool using_proxy) = 0; + + // Create a WebSocketSpdyStream. + virtual WebSocketStreamBase* CreateSpdyStream( + SpdySession* session, + bool use_relative_url) = 0; + }; + + virtual ~WebSocketStreamBase() {} + + // Return this object as a WebSocketStream. + virtual WebSocketStream* AsWebSocketStream() = 0; + + protected: + WebSocketStreamBase() {} + + private: + DISALLOW_COPY_AND_ASSIGN(WebSocketStreamBase); +}; + +} // namespace net + +#endif // NET_WEBSOCKETS_WEBSOCKET_STREAM_BASE_H_ |