| // Copyright (c) 2012 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_HTTP_HTTP_STREAM_FACTORY_IMPL_JOB_H_ |
| #define NET_HTTP_HTTP_STREAM_FACTORY_IMPL_JOB_H_ |
| |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "net/base/completion_callback.h" |
| #include "net/base/net_log.h" |
| #include "net/base/ssl_config_service.h" |
| #include "net/http/http_auth.h" |
| #include "net/http/http_auth_controller.h" |
| #include "net/http/http_pipelined_host.h" |
| #include "net/http/http_request_info.h" |
| #include "net/http/http_stream_factory_impl.h" |
| #include "net/proxy/proxy_service.h" |
| #include "net/quic/quic_stream_factory.h" |
| #include "net/socket/client_socket_handle.h" |
| #include "net/socket/ssl_client_socket.h" |
| |
| namespace net { |
| |
| class ClientSocketHandle; |
| class HttpAuthController; |
| class HttpNetworkSession; |
| class HttpStream; |
| class SpdySessionPool; |
| class QuicHttpStream; |
| |
| // An HttpStreamRequestImpl exists for each stream which is in progress of being |
| // created for the StreamFactory. |
| class HttpStreamFactoryImpl::Job { |
| public: |
| Job(HttpStreamFactoryImpl* stream_factory, |
| HttpNetworkSession* session, |
| const HttpRequestInfo& request_info, |
| const SSLConfig& server_ssl_config, |
| const SSLConfig& proxy_ssl_config, |
| NetLog* net_log); |
| ~Job(); |
| |
| // Start initiates the process of creating a new HttpStream. |request| will be |
| // notified upon completion if the Job has not been Orphan()'d. |
| void Start(Request* request); |
| |
| // Preconnect will attempt to request |num_streams| sockets from the |
| // appropriate ClientSocketPool. |
| int Preconnect(int num_streams); |
| |
| int RestartTunnelWithProxyAuth(const AuthCredentials& credentials); |
| LoadState GetLoadState() const; |
| |
| // Marks this Job as the "alternate" job, from Alternate-Protocol. Tracks the |
| // original url so we can mark the Alternate-Protocol as broken if |
| // we fail to connect. |
| void MarkAsAlternate(const GURL& original_url); |
| |
| // Tells |this| to wait for |job| to resume it. |
| void WaitFor(Job* job); |
| |
| // Tells |this| that |job| has determined it still needs to continue |
| // connecting, so allow |this| to continue. If this is not called, then |
| // |request_| is expected to cancel |this| by deleting it. |
| void Resume(Job* job); |
| |
| // Used to detach the Job from |request|. |
| void Orphan(const Request* request); |
| |
| bool was_npn_negotiated() const; |
| NextProto protocol_negotiated() const; |
| bool using_spdy() const; |
| const BoundNetLog& net_log() const { return net_log_; } |
| |
| const SSLConfig& server_ssl_config() const; |
| const SSLConfig& proxy_ssl_config() const; |
| const ProxyInfo& proxy_info() const; |
| |
| // Indicates whether or not this job is performing a preconnect. |
| bool IsPreconnecting() const; |
| |
| // Indicates whether or not this Job has been orphaned by a Request. |
| bool IsOrphaned() const; |
| |
| private: |
| enum State { |
| STATE_START, |
| STATE_RESOLVE_PROXY, |
| STATE_RESOLVE_PROXY_COMPLETE, |
| |
| // Note that when Alternate-Protocol says we can connect to an alternate |
| // port using a different protocol, we have the choice of communicating over |
| // the original protocol, or speaking the alternate protocol (currently, |
| // only npn-spdy) over an alternate port. For a cold page load, the http |
| // connection that delivers the http response that has the |
| // Alternate-Protocol header will already be warm. So, blocking the next |
| // http request on establishing a new npn-spdy connection would incur extra |
| // latency. Even if the http connection was not reused, establishing a new |
| // http connection is typically faster than npn-spdy, since npn-spdy |
| // requires a SSL handshake. Therefore, we start both the http and the |
| // npn-spdy jobs in parallel. In order not to unnecessarily waste sockets, |
| // we have the http job block on the npn-spdy job after proxy resolution. |
| // The npn-spdy job will Resume() the http job if, in |
| // STATE_INIT_CONNECTION_COMPLETE, it detects an error or does not find an |
| // existing SpdySession. In that case, the http and npn-spdy jobs will race. |
| STATE_WAIT_FOR_JOB, |
| STATE_WAIT_FOR_JOB_COMPLETE, |
| |
| STATE_INIT_CONNECTION, |
| STATE_INIT_CONNECTION_COMPLETE, |
| STATE_WAITING_USER_ACTION, |
| STATE_RESTART_TUNNEL_AUTH, |
| STATE_RESTART_TUNNEL_AUTH_COMPLETE, |
| STATE_CREATE_STREAM, |
| STATE_CREATE_STREAM_COMPLETE, |
| STATE_DRAIN_BODY_FOR_AUTH_RESTART, |
| STATE_DRAIN_BODY_FOR_AUTH_RESTART_COMPLETE, |
| STATE_DONE, |
| STATE_NONE |
| }; |
| |
| void OnStreamReadyCallback(); |
| void OnSpdySessionReadyCallback(); |
| void OnStreamFailedCallback(int result); |
| void OnCertificateErrorCallback(int result, const SSLInfo& ssl_info); |
| void OnNeedsProxyAuthCallback(const HttpResponseInfo& response_info, |
| HttpAuthController* auth_controller); |
| void OnNeedsClientAuthCallback(SSLCertRequestInfo* cert_info); |
| void OnHttpsProxyTunnelResponseCallback(const HttpResponseInfo& response_info, |
| HttpStream* stream); |
| void OnPreconnectsComplete(); |
| |
| void OnIOComplete(int result); |
| int RunLoop(int result); |
| int DoLoop(int result); |
| int StartInternal(); |
| |
| // Each of these methods corresponds to a State value. Those with an input |
| // argument receive the result from the previous state. If a method returns |
| // ERR_IO_PENDING, then the result from OnIOComplete will be passed to the |
| // next state method as the result arg. |
| int DoStart(); |
| int DoResolveProxy(); |
| int DoResolveProxyComplete(int result); |
| int DoWaitForJob(); |
| int DoWaitForJobComplete(int result); |
| int DoInitConnection(); |
| int DoInitConnectionComplete(int result); |
| int DoWaitingUserAction(int result); |
| int DoCreateStream(); |
| int DoCreateStreamComplete(int result); |
| int DoRestartTunnelAuth(); |
| int DoRestartTunnelAuthComplete(int result); |
| |
| // Returns to STATE_INIT_CONNECTION and resets some state. |
| void ReturnToStateInitConnection(bool close_connection); |
| |
| // Set the motivation for this request onto the underlying socket. |
| void SetSocketMotivation(); |
| |
| bool IsHttpsProxyAndHttpUrl() const; |
| |
| // Sets several fields of ssl_config for the given origin_server based on the |
| // proxy info and other factors. |
| void InitSSLConfig(const HostPortPair& origin_server, |
| SSLConfig* ssl_config) const; |
| |
| // AlternateProtocol API |
| void MarkBrokenAlternateProtocolAndFallback(); |
| |
| // Retrieve SSLInfo from our SSL Socket. |
| // This must only be called when we are using an SSLSocket. |
| // After calling, the caller can use ssl_info_. |
| void GetSSLInfo(); |
| |
| HostPortProxyPair GetSpdySessionKey() const; |
| |
| // Returns true if the current request can use an existing spdy session. |
| bool CanUseExistingSpdySession() const; |
| |
| // Called when we encounter a network error that could be resolved by trying |
| // a new proxy configuration. If there is another proxy configuration to try |
| // then this method sets next_state_ appropriately and returns either OK or |
| // ERR_IO_PENDING depending on whether or not the new proxy configuration is |
| // available synchronously or asynchronously. Otherwise, the given error |
| // code is simply returned. |
| int ReconsiderProxyAfterError(int error); |
| |
| // Called to handle a certificate error. Stores the certificate in the |
| // allowed_bad_certs list, and checks if the error can be ignored. Returns |
| // OK if it can be ignored, or the error code otherwise. |
| int HandleCertificateError(int error); |
| |
| // Called to handle a client certificate request. |
| int HandleCertificateRequest(int error); |
| |
| // Moves this stream request into SPDY mode. |
| void SwitchToSpdyMode(); |
| |
| // Should we force SPDY to run over SSL for this stream request. |
| bool ShouldForceSpdySSL() const; |
| |
| // Should we force SPDY to run without SSL for this stream request. |
| bool ShouldForceSpdyWithoutSSL() const; |
| |
| // Should we force QUIC for this stream request. |
| bool ShouldForceQuic() const; |
| |
| bool IsRequestEligibleForPipelining(); |
| |
| // Record histograms of latency until Connect() completes. |
| static void LogHttpConnectedMetrics(const ClientSocketHandle& handle); |
| |
| // Invoked by the transport socket pool after host resolution is complete |
| // to allow the connection to be aborted, if a matching SPDY session can |
| // be found. Will return ERR_SPDY_SESSION_ALREADY_EXISTS if such a |
| // session is found, and OK otherwise. |
| static int OnHostResolution(SpdySessionPool* spdy_session_pool, |
| const HostPortProxyPair& spdy_session_key, |
| const AddressList& addresses, |
| const BoundNetLog& net_log); |
| |
| Request* request_; |
| |
| const HttpRequestInfo request_info_; |
| ProxyInfo proxy_info_; |
| SSLConfig server_ssl_config_; |
| SSLConfig proxy_ssl_config_; |
| const BoundNetLog net_log_; |
| |
| CompletionCallback io_callback_; |
| scoped_ptr<ClientSocketHandle> connection_; |
| HttpNetworkSession* const session_; |
| HttpStreamFactoryImpl* const stream_factory_; |
| State next_state_; |
| ProxyService::PacRequest* pac_request_; |
| SSLInfo ssl_info_; |
| |
| // The origin server we're trying to reach. |
| HostPortPair origin_; |
| |
| // The origin url we're trying to reach. This url may be different from the |
| // original request when host mapping rules are set-up. |
| GURL origin_url_; |
| |
| // If this is a Job for an "Alternate-Protocol", then this will be non-NULL |
| // and will specify the original URL. |
| scoped_ptr<GURL> original_url_; |
| |
| // This is the Job we're dependent on. It will notify us if/when it's OK to |
| // proceed. |
| Job* blocking_job_; |
| |
| // |waiting_job_| is a Job waiting to see if |this| can reuse a connection. |
| // If |this| is unable to do so, we'll notify |waiting_job_| that it's ok to |
| // proceed and then race the two Jobs. |
| Job* waiting_job_; |
| |
| // True if handling a HTTPS request, or using SPDY with SSL |
| bool using_ssl_; |
| |
| // True if this network transaction is using SPDY instead of HTTP. |
| bool using_spdy_; |
| |
| // True if this network transaction is using QUIC instead of HTTP. |
| bool using_quic_; |
| QuicStreamRequest quic_request_; |
| |
| // Force spdy for all connections. |
| bool force_spdy_always_; |
| |
| // Force spdy only for SSL connections. |
| bool force_spdy_over_ssl_; |
| |
| // Force quic for a specific port. |
| int force_quic_port_; |
| |
| // The certificate error while using SPDY over SSL for insecure URLs. |
| int spdy_certificate_error_; |
| |
| scoped_refptr<HttpAuthController> |
| auth_controllers_[HttpAuth::AUTH_NUM_TARGETS]; |
| |
| // True when the tunnel is in the process of being established - we can't |
| // read from the socket until the tunnel is done. |
| bool establishing_tunnel_; |
| |
| scoped_ptr<HttpStream> stream_; |
| |
| // True if we negotiated NPN. |
| bool was_npn_negotiated_; |
| |
| // Protocol negotiated with the server. |
| NextProto protocol_negotiated_; |
| |
| // 0 if we're not preconnecting. Otherwise, the number of streams to |
| // preconnect. |
| int num_streams_; |
| |
| // Initialized when we create a new SpdySession. |
| scoped_refptr<SpdySession> new_spdy_session_; |
| |
| // Initialized when we have an existing SpdySession. |
| scoped_refptr<SpdySession> existing_spdy_session_; |
| |
| // Only used if |new_spdy_session_| is non-NULL. |
| bool spdy_session_direct_; |
| |
| // Key used to identify the HttpPipelinedHost for |request_|. |
| scoped_ptr<HttpPipelinedHost::Key> http_pipelining_key_; |
| |
| // True if an existing pipeline can handle this job's request. |
| bool existing_available_pipeline_; |
| |
| base::WeakPtrFactory<Job> ptr_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Job); |
| }; |
| |
| } // namespace net |
| |
| #endif // NET_HTTP_HTTP_STREAM_FACTORY_IMPL_JOB_H_ |