1// Copyright (c) 2012 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/memory/weak_ptr.h"
11#include "net/base/completion_callback.h"
12#include "net/base/net_log.h"
13#include "net/base/request_priority.h"
14#include "net/http/http_auth.h"
15#include "net/http/http_auth_controller.h"
16#include "net/http/http_request_info.h"
17#include "net/http/http_stream_factory_impl.h"
18#include "net/proxy/proxy_service.h"
19#include "net/quic/quic_stream_factory.h"
20#include "net/socket/client_socket_handle.h"
21#include "net/socket/ssl_client_socket.h"
22#include "net/spdy/spdy_session_key.h"
23#include "net/ssl/ssl_config_service.h"
24
25namespace net {
26
27class ClientSocketHandle;
28class HttpAuthController;
29class HttpNetworkSession;
30class HttpStream;
31class SpdySessionPool;
32class QuicHttpStream;
33
34// An HttpStreamRequestImpl exists for each stream which is in progress of being
35// created for the StreamFactory.
36class HttpStreamFactoryImpl::Job {
37 public:
38  Job(HttpStreamFactoryImpl* stream_factory,
39      HttpNetworkSession* session,
40      const HttpRequestInfo& request_info,
41      RequestPriority priority,
42      const SSLConfig& server_ssl_config,
43      const SSLConfig& proxy_ssl_config,
44      NetLog* net_log);
45  ~Job();
46
47  // Start initiates the process of creating a new HttpStream. |request| will be
48  // notified upon completion if the Job has not been Orphan()'d.
49  void Start(Request* request);
50
51  // Preconnect will attempt to request |num_streams| sockets from the
52  // appropriate ClientSocketPool.
53  int Preconnect(int num_streams);
54
55  int RestartTunnelWithProxyAuth(const AuthCredentials& credentials);
56  LoadState GetLoadState() const;
57
58  // Marks this Job as the "alternate" job, from Alternate-Protocol. Tracks the
59  // original url so we can mark the Alternate-Protocol as broken if
60  // we fail to connect.  |alternate| specifies the alternate protocol to use
61  // and alternate port to connect to.
62  void MarkAsAlternate(const GURL& original_url,
63                       AlternateProtocolInfo alternate);
64
65  // Tells |this| to wait for |job| to resume it.
66  void WaitFor(Job* job);
67
68  // Tells |this| that |job| has determined it still needs to continue
69  // connecting, so allow |this| to continue. If this is not called, then
70  // |request_| is expected to cancel |this| by deleting it.
71  void Resume(Job* job);
72
73  // Used to detach the Job from |request|.
74  void Orphan(const Request* request);
75
76  void SetPriority(RequestPriority priority);
77
78  RequestPriority priority() const { return priority_; }
79  bool was_npn_negotiated() const;
80  NextProto protocol_negotiated() const;
81  bool using_spdy() const;
82  const BoundNetLog& net_log() const { return net_log_; }
83
84  const SSLConfig& server_ssl_config() const;
85  const SSLConfig& proxy_ssl_config() const;
86  const ProxyInfo& proxy_info() const;
87
88  // Indicates whether or not this job is performing a preconnect.
89  bool IsPreconnecting() const;
90
91  // Indicates whether or not this Job has been orphaned by a Request.
92  bool IsOrphaned() const;
93
94  // Called to indicate that this job succeeded, and some other jobs
95  // will be orphaned.
96  void ReportJobSuccededForRequest();
97
98  // Marks that the other |job| has completed.
99  void MarkOtherJobComplete(const Job& job);
100
101 private:
102  enum State {
103    STATE_START,
104    STATE_RESOLVE_PROXY,
105    STATE_RESOLVE_PROXY_COMPLETE,
106
107    // Note that when Alternate-Protocol says we can connect to an alternate
108    // port using a different protocol, we have the choice of communicating over
109    // the original protocol, or speaking the alternate protocol (currently,
110    // only npn-spdy) over an alternate port. For a cold page load, the http
111    // connection that delivers the http response that has the
112    // Alternate-Protocol header will already be warm. So, blocking the next
113    // http request on establishing a new npn-spdy connection would incur extra
114    // latency. Even if the http connection was not reused, establishing a new
115    // http connection is typically faster than npn-spdy, since npn-spdy
116    // requires a SSL handshake. Therefore, we start both the http and the
117    // npn-spdy jobs in parallel. In order not to unnecessarily waste sockets,
118    // we have the http job block on the npn-spdy job after proxy resolution.
119    // The npn-spdy job will Resume() the http job if, in
120    // STATE_INIT_CONNECTION_COMPLETE, it detects an error or does not find an
121    // existing SpdySession. In that case, the http and npn-spdy jobs will race.
122    STATE_WAIT_FOR_JOB,
123    STATE_WAIT_FOR_JOB_COMPLETE,
124
125    STATE_INIT_CONNECTION,
126    STATE_INIT_CONNECTION_COMPLETE,
127    STATE_WAITING_USER_ACTION,
128    STATE_RESTART_TUNNEL_AUTH,
129    STATE_RESTART_TUNNEL_AUTH_COMPLETE,
130    STATE_CREATE_STREAM,
131    STATE_CREATE_STREAM_COMPLETE,
132    STATE_DRAIN_BODY_FOR_AUTH_RESTART,
133    STATE_DRAIN_BODY_FOR_AUTH_RESTART_COMPLETE,
134    STATE_DONE,
135    STATE_NONE
136  };
137
138  enum JobStatus {
139    STATUS_RUNNING,
140    STATUS_FAILED,
141    STATUS_BROKEN,
142    STATUS_SUCCEEDED
143  };
144
145  void OnStreamReadyCallback();
146  void OnWebSocketHandshakeStreamReadyCallback();
147  // This callback function is called when a new SPDY session is created.
148  void OnNewSpdySessionReadyCallback();
149  void OnStreamFailedCallback(int result);
150  void OnCertificateErrorCallback(int result, const SSLInfo& ssl_info);
151  void OnNeedsProxyAuthCallback(const HttpResponseInfo& response_info,
152                                HttpAuthController* auth_controller);
153  void OnNeedsClientAuthCallback(SSLCertRequestInfo* cert_info);
154  void OnHttpsProxyTunnelResponseCallback(const HttpResponseInfo& response_info,
155                                          HttpStream* stream);
156  void OnPreconnectsComplete();
157
158  void OnIOComplete(int result);
159  int RunLoop(int result);
160  int DoLoop(int result);
161  int StartInternal();
162
163  // Each of these methods corresponds to a State value.  Those with an input
164  // argument receive the result from the previous state.  If a method returns
165  // ERR_IO_PENDING, then the result from OnIOComplete will be passed to the
166  // next state method as the result arg.
167  int DoStart();
168  int DoResolveProxy();
169  int DoResolveProxyComplete(int result);
170  int DoWaitForJob();
171  int DoWaitForJobComplete(int result);
172  int DoInitConnection();
173  int DoInitConnectionComplete(int result);
174  int DoWaitingUserAction(int result);
175  int DoCreateStream();
176  int DoCreateStreamComplete(int result);
177  int DoRestartTunnelAuth();
178  int DoRestartTunnelAuthComplete(int result);
179
180  // Creates a SpdyHttpStream from the given values and sets to |stream_|. Does
181  // nothing if |stream_factory_| is for WebSockets.
182  int SetSpdyHttpStream(base::WeakPtr<SpdySession> session, bool direct);
183
184  // Returns to STATE_INIT_CONNECTION and resets some state.
185  void ReturnToStateInitConnection(bool close_connection);
186
187  // Set the motivation for this request onto the underlying socket.
188  void SetSocketMotivation();
189
190  bool IsHttpsProxyAndHttpUrl() const;
191
192  // Sets several fields of ssl_config for the given origin_server based on the
193  // proxy info and other factors.
194  void InitSSLConfig(const HostPortPair& origin_server,
195                     SSLConfig* ssl_config,
196                     bool is_proxy) const;
197
198  // Retrieve SSLInfo from our SSL Socket.
199  // This must only be called when we are using an SSLSocket.
200  // After calling, the caller can use ssl_info_.
201  void GetSSLInfo();
202
203  SpdySessionKey GetSpdySessionKey() const;
204
205  // Returns true if the current request can use an existing spdy session.
206  bool CanUseExistingSpdySession() const;
207
208  // Called when we encounter a network error that could be resolved by trying
209  // a new proxy configuration.  If there is another proxy configuration to try
210  // then this method sets next_state_ appropriately and returns either OK or
211  // ERR_IO_PENDING depending on whether or not the new proxy configuration is
212  // available synchronously or asynchronously.  Otherwise, the given error
213  // code is simply returned.
214  int ReconsiderProxyAfterError(int error);
215
216  // Called to handle a certificate error.  Stores the certificate in the
217  // allowed_bad_certs list, and checks if the error can be ignored.  Returns
218  // OK if it can be ignored, or the error code otherwise.
219  int HandleCertificateError(int error);
220
221  // Called to handle a client certificate request.
222  int HandleCertificateRequest(int error);
223
224  // Moves this stream request into SPDY mode.
225  void SwitchToSpdyMode();
226
227  // Should we force SPDY to run over SSL for this stream request.
228  bool ShouldForceSpdySSL() const;
229
230  // Should we force SPDY to run without SSL for this stream request.
231  bool ShouldForceSpdyWithoutSSL() const;
232
233  // Should we force QUIC for this stream request.
234  bool ShouldForceQuic() const;
235
236  void MaybeMarkAlternateProtocolBroken();
237
238  // Record histograms of latency until Connect() completes.
239  static void LogHttpConnectedMetrics(const ClientSocketHandle& handle);
240
241  // Invoked by the transport socket pool after host resolution is complete
242  // to allow the connection to be aborted, if a matching SPDY session can
243  // be found.  Will return ERR_SPDY_SESSION_ALREADY_EXISTS if such a
244  // session is found, and OK otherwise.
245  static int OnHostResolution(SpdySessionPool* spdy_session_pool,
246                              const SpdySessionKey& spdy_session_key,
247                              const AddressList& addresses,
248                              const BoundNetLog& net_log);
249
250  Request* request_;
251
252  const HttpRequestInfo request_info_;
253  RequestPriority priority_;
254  ProxyInfo proxy_info_;
255  SSLConfig server_ssl_config_;
256  SSLConfig proxy_ssl_config_;
257  const BoundNetLog net_log_;
258
259  CompletionCallback io_callback_;
260  scoped_ptr<ClientSocketHandle> connection_;
261  HttpNetworkSession* const session_;
262  HttpStreamFactoryImpl* const stream_factory_;
263  State next_state_;
264  ProxyService::PacRequest* pac_request_;
265  SSLInfo ssl_info_;
266
267  // The origin server we're trying to reach.
268  HostPortPair origin_;
269
270  // The origin url we're trying to reach. This url may be different from the
271  // original request when host mapping rules are set-up.
272  GURL origin_url_;
273
274  // If this is a Job for an "Alternate-Protocol", then this will be non-NULL
275  // and will specify the original URL.
276  scoped_ptr<GURL> original_url_;
277
278  // This is the Job we're dependent on. It will notify us if/when it's OK to
279  // proceed.
280  Job* blocking_job_;
281
282  // |waiting_job_| is a Job waiting to see if |this| can reuse a connection.
283  // If |this| is unable to do so, we'll notify |waiting_job_| that it's ok to
284  // proceed and then race the two Jobs.
285  Job* waiting_job_;
286
287  // True if handling a HTTPS request, or using SPDY with SSL
288  bool using_ssl_;
289
290  // True if this network transaction is using SPDY instead of HTTP.
291  bool using_spdy_;
292
293  // True if this network transaction is using QUIC instead of HTTP.
294  bool using_quic_;
295  QuicStreamRequest quic_request_;
296
297  // True if this job used an existing QUIC session.
298  bool using_existing_quic_session_;
299
300  // Force quic for a specific port.
301  int force_quic_port_;
302
303  // The certificate error while using SPDY over SSL for insecure URLs.
304  int spdy_certificate_error_;
305
306  scoped_refptr<HttpAuthController>
307      auth_controllers_[HttpAuth::AUTH_NUM_TARGETS];
308
309  // True when the tunnel is in the process of being established - we can't
310  // read from the socket until the tunnel is done.
311  bool establishing_tunnel_;
312
313  scoped_ptr<HttpStream> stream_;
314  scoped_ptr<WebSocketHandshakeStreamBase> websocket_stream_;
315
316  // True if we negotiated NPN.
317  bool was_npn_negotiated_;
318
319  // Protocol negotiated with the server.
320  NextProto protocol_negotiated_;
321
322  // 0 if we're not preconnecting. Otherwise, the number of streams to
323  // preconnect.
324  int num_streams_;
325
326  // Initialized when we create a new SpdySession.
327  base::WeakPtr<SpdySession> new_spdy_session_;
328
329  // Initialized when we have an existing SpdySession.
330  base::WeakPtr<SpdySession> existing_spdy_session_;
331
332  // Only used if |new_spdy_session_| is non-NULL.
333  bool spdy_session_direct_;
334
335  JobStatus job_status_;
336  JobStatus other_job_status_;
337
338  base::WeakPtrFactory<Job> ptr_factory_;
339
340  DISALLOW_COPY_AND_ASSIGN(Job);
341};
342
343}  // namespace net
344
345#endif  // NET_HTTP_HTTP_STREAM_FACTORY_IMPL_JOB_H_
346