1// Copyright (c) 2011 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#ifndef NET_HTTP_HTTP_STREAM_FACTORY_IMPL_JOB_H_
6#define NET_HTTP_HTTP_STREAM_FACTORY_IMPL_JOB_H_
7
8#include "base/memory/ref_counted.h"
9#include "base/memory/scoped_ptr.h"
10#include "base/task.h"
11#include "net/base/completion_callback.h"
12#include "net/base/net_log.h"
13#include "net/base/ssl_config_service.h"
14#include "net/http/http_alternate_protocols.h"
15#include "net/http/http_auth.h"
16#include "net/http/http_auth_controller.h"
17#include "net/http/http_request_info.h"
18#include "net/http/http_stream_factory_impl.h"
19#include "net/proxy/proxy_service.h"
20#include "net/socket/client_socket_handle.h"
21
22namespace net {
23
24class ClientSocketHandle;
25class HttpAuthController;
26class HttpNetworkSession;
27class HttpProxySocketParams;
28class HttpStream;
29class SOCKSSocketParams;
30class SSLSocketParams;
31class TransportSocketParams;
32
33// An HttpStreamRequestImpl exists for each stream which is in progress of being
34// created for the StreamFactory.
35class HttpStreamFactoryImpl::Job {
36 public:
37  Job(HttpStreamFactoryImpl* stream_factory,
38      HttpNetworkSession* session,
39      const HttpRequestInfo& request_info,
40      const SSLConfig& ssl_config,
41      const BoundNetLog& net_log);
42  ~Job();
43
44  // Start initiates the process of creating a new HttpStream. |request| will be
45  // notified upon completion if the Job has not been Orphan()'d.
46  void Start(Request* request);
47
48  // Preconnect will attempt to request |num_streams| sockets from the
49  // appropriate ClientSocketPool.
50  int Preconnect(int num_streams);
51
52  int RestartTunnelWithProxyAuth(const string16& username,
53                                 const string16& password);
54  LoadState GetLoadState() const;
55
56  // Marks this Job as the "alternate" job, from Alternate-Protocol. Tracks the
57  // original url so we can mark the Alternate-Protocol as broken if
58  // we fail to connect.
59  void MarkAsAlternate(const GURL& original_url);
60
61  // Tells |this| to wait for |job| to resume it.
62  void WaitFor(Job* job);
63
64  // Tells |this| that |job| has determined it still needs to continue
65  // connecting, so allow |this| to continue. If this is not called, then
66  // |request_| is expected to cancel |this| by deleting it.
67  void Resume(Job* job);
68
69  // Used to detach the Job from |request|.
70  void Orphan(const Request* request);
71
72  bool was_npn_negotiated() const;
73  bool using_spdy() const;
74  const BoundNetLog& net_log() const { return net_log_; }
75
76  const SSLConfig& ssl_config() const;
77  const ProxyInfo& proxy_info() const;
78
79  // Indicates whether or not this job is performing a preconnect.
80  bool IsPreconnecting() const;
81
82  // Indicates whether or not this Job has been orphaned by a Request.
83  bool IsOrphaned() const;
84
85 private:
86  enum State {
87    STATE_RESOLVE_PROXY,
88    STATE_RESOLVE_PROXY_COMPLETE,
89
90    // Note that when Alternate-Protocol says we can connect to an alternate
91    // port using a different protocol, we have the choice of communicating over
92    // the original protocol, or speaking the alternate protocol (currently,
93    // only npn-spdy) over an alternate port. For a cold page load, the http
94    // connection that delivers the http response that has the
95    // Alternate-Protocol header will already be warm. So, blocking the next
96    // http request on establishing a new npn-spdy connection would incur extra
97    // latency. Even if the http connection was not reused, establishing a new
98    // http connection is typically faster than npn-spdy, since npn-spdy
99    // requires a SSL handshake. Therefore, we start both the http and the
100    // npn-spdy jobs in parallel. In order not to unnecessarily waste sockets,
101    // we have the http job block on the npn-spdy job after proxy resolution.
102    // The npn-spdy job will Resume() the http job if, in
103    // STATE_INIT_CONNECTION_COMPLETE, it detects an error or does not find an
104    // existing SpdySession. In that case, the http and npn-spdy jobs will race.
105    STATE_WAIT_FOR_JOB,
106    STATE_WAIT_FOR_JOB_COMPLETE,
107
108    STATE_INIT_CONNECTION,
109    STATE_INIT_CONNECTION_COMPLETE,
110    STATE_WAITING_USER_ACTION,
111    STATE_RESTART_TUNNEL_AUTH,
112    STATE_RESTART_TUNNEL_AUTH_COMPLETE,
113    STATE_CREATE_STREAM,
114    STATE_CREATE_STREAM_COMPLETE,
115    STATE_DRAIN_BODY_FOR_AUTH_RESTART,
116    STATE_DRAIN_BODY_FOR_AUTH_RESTART_COMPLETE,
117    STATE_DONE,
118    STATE_NONE
119  };
120
121  void OnStreamReadyCallback();
122  void OnSpdySessionReadyCallback();
123  void OnStreamFailedCallback(int result);
124  void OnCertificateErrorCallback(int result, const SSLInfo& ssl_info);
125  void OnNeedsProxyAuthCallback(const HttpResponseInfo& response_info,
126                                HttpAuthController* auth_controller);
127  void OnNeedsClientAuthCallback(SSLCertRequestInfo* cert_info);
128  void OnHttpsProxyTunnelResponseCallback(const HttpResponseInfo& response_info,
129                                          HttpStream* stream);
130  void OnPreconnectsComplete();
131
132  void OnIOComplete(int result);
133  int RunLoop(int result);
134  int DoLoop(int result);
135  int StartInternal();
136
137  // Each of these methods corresponds to a State value.  Those with an input
138  // argument receive the result from the previous state.  If a method returns
139  // ERR_IO_PENDING, then the result from OnIOComplete will be passed to the
140  // next state method as the result arg.
141  int DoResolveProxy();
142  int DoResolveProxyComplete(int result);
143  int DoWaitForJob();
144  int DoWaitForJobComplete(int result);
145  int DoInitConnection();
146  int DoInitConnectionComplete(int result);
147  int DoWaitingUserAction(int result);
148  int DoCreateStream();
149  int DoCreateStreamComplete(int result);
150  int DoRestartTunnelAuth();
151  int DoRestartTunnelAuthComplete(int result);
152
153  // Returns to STATE_INIT_CONNECTION and resets some state.
154  void ReturnToStateInitConnection(bool close_connection);
155
156  // Set the motivation for this request onto the underlying socket.
157  void SetSocketMotivation();
158
159  bool IsHttpsProxyAndHttpUrl();
160
161// Sets several fields of ssl_config for the given origin_server based on the
162// proxy info and other factors.
163  void InitSSLConfig(const HostPortPair& origin_server,
164                     SSLConfig* ssl_config) const;
165
166  // AlternateProtocol API
167  void MarkBrokenAlternateProtocolAndFallback();
168
169  // Retrieve SSLInfo from our SSL Socket.
170  // This must only be called when we are using an SSLSocket.
171  // After calling, the caller can use ssl_info_.
172  void GetSSLInfo();
173
174  // Called when we encounter a network error that could be resolved by trying
175  // a new proxy configuration.  If there is another proxy configuration to try
176  // then this method sets next_state_ appropriately and returns either OK or
177  // ERR_IO_PENDING depending on whether or not the new proxy configuration is
178  // available synchronously or asynchronously.  Otherwise, the given error
179  // code is simply returned.
180  int ReconsiderProxyAfterError(int error);
181
182  // Called to handle a certificate error.  Stores the certificate in the
183  // allowed_bad_certs list, and checks if the error can be ignored.  Returns
184  // OK if it can be ignored, or the error code otherwise.
185  int HandleCertificateError(int error);
186
187  // Called to handle a client certificate request.
188  int HandleCertificateRequest(int error);
189
190  // Moves this stream request into SPDY mode.
191  void SwitchToSpdyMode();
192
193  // Should we force SPDY to run over SSL for this stream request.
194  bool ShouldForceSpdySSL() const;
195
196  // Should we force SPDY to run without SSL for this stream request.
197  bool ShouldForceSpdyWithoutSSL() const;
198
199  // Record histograms of latency until Connect() completes.
200  static void LogHttpConnectedMetrics(const ClientSocketHandle& handle);
201
202  Request* request_;
203
204  const HttpRequestInfo request_info_;
205  ProxyInfo proxy_info_;
206  SSLConfig ssl_config_;
207  const BoundNetLog net_log_;
208
209  CompletionCallbackImpl<Job> io_callback_;
210  scoped_ptr<ClientSocketHandle> connection_;
211  HttpNetworkSession* const session_;
212  HttpStreamFactoryImpl* const stream_factory_;
213  State next_state_;
214  ProxyService::PacRequest* pac_request_;
215  SSLInfo ssl_info_;
216
217  // The origin server we're trying to reach.
218  HostPortPair origin_;
219
220  // If this is a Job for an "Alternate-Protocol", then this will be non-NULL
221  // and will specify the original URL.
222  scoped_ptr<GURL> original_url_;
223
224  // This is the Job we're dependent on. It will notify us if/when it's OK to
225  // proceed.
226  Job* blocking_job_;
227
228  // |dependent_job_| is dependent on |this|. Notify it when it's ok to proceed.
229  Job* dependent_job_;
230
231  // True if handling a HTTPS request, or using SPDY with SSL
232  bool using_ssl_;
233
234  // True if this network transaction is using SPDY instead of HTTP.
235  bool using_spdy_;
236
237  // Force spdy for all connections.
238  bool force_spdy_always_;
239
240  // Force spdy only for SSL connections.
241  bool force_spdy_over_ssl_;
242
243  // The certificate error while using SPDY over SSL for insecure URLs.
244  int spdy_certificate_error_;
245
246  scoped_refptr<HttpAuthController>
247      auth_controllers_[HttpAuth::AUTH_NUM_TARGETS];
248
249  // True when the tunnel is in the process of being established - we can't
250  // read from the socket until the tunnel is done.
251  bool establishing_tunnel_;
252
253  scoped_ptr<HttpStream> stream_;
254
255  // True if we negotiated NPN.
256  bool was_npn_negotiated_;
257
258  // 0 if we're not preconnecting. Otherwise, the number of streams to
259  // preconnect.
260  int num_streams_;
261
262  // Initialized when we create a new SpdySession.
263  scoped_refptr<SpdySession> new_spdy_session_;
264
265  // Only used if |new_spdy_session_| is non-NULL.
266  bool spdy_session_direct_;
267
268  ScopedRunnableMethodFactory<Job> method_factory_;
269
270  DISALLOW_COPY_AND_ASSIGN(Job);
271};
272
273}  // namespace net
274
275#endif  // NET_HTTP_HTTP_STREAM_FACTORY_IMPL_JOB_H_
276