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                       PortAlternateProtocolPair 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  // Returns to STATE_INIT_CONNECTION and resets some state.
181  void ReturnToStateInitConnection(bool close_connection);
182
183  // Set the motivation for this request onto the underlying socket.
184  void SetSocketMotivation();
185
186  bool IsHttpsProxyAndHttpUrl() const;
187
188  // Sets several fields of ssl_config for the given origin_server based on the
189  // proxy info and other factors.
190  void InitSSLConfig(const HostPortPair& origin_server,
191                     SSLConfig* ssl_config,
192                     bool is_proxy) const;
193
194  // Retrieve SSLInfo from our SSL Socket.
195  // This must only be called when we are using an SSLSocket.
196  // After calling, the caller can use ssl_info_.
197  void GetSSLInfo();
198
199  SpdySessionKey GetSpdySessionKey() const;
200
201  // Returns true if the current request can use an existing spdy session.
202  bool CanUseExistingSpdySession() const;
203
204  // Called when we encounter a network error that could be resolved by trying
205  // a new proxy configuration.  If there is another proxy configuration to try
206  // then this method sets next_state_ appropriately and returns either OK or
207  // ERR_IO_PENDING depending on whether or not the new proxy configuration is
208  // available synchronously or asynchronously.  Otherwise, the given error
209  // code is simply returned.
210  int ReconsiderProxyAfterError(int error);
211
212  // Called to handle a certificate error.  Stores the certificate in the
213  // allowed_bad_certs list, and checks if the error can be ignored.  Returns
214  // OK if it can be ignored, or the error code otherwise.
215  int HandleCertificateError(int error);
216
217  // Called to handle a client certificate request.
218  int HandleCertificateRequest(int error);
219
220  // Moves this stream request into SPDY mode.
221  void SwitchToSpdyMode();
222
223  // Should we force SPDY to run over SSL for this stream request.
224  bool ShouldForceSpdySSL() const;
225
226  // Should we force SPDY to run without SSL for this stream request.
227  bool ShouldForceSpdyWithoutSSL() const;
228
229  // Should we force QUIC for this stream request.
230  bool ShouldForceQuic() const;
231
232  void MaybeMarkAlternateProtocolBroken();
233
234  // Record histograms of latency until Connect() completes.
235  static void LogHttpConnectedMetrics(const ClientSocketHandle& handle);
236
237  // Invoked by the transport socket pool after host resolution is complete
238  // to allow the connection to be aborted, if a matching SPDY session can
239  // be found.  Will return ERR_SPDY_SESSION_ALREADY_EXISTS if such a
240  // session is found, and OK otherwise.
241  static int OnHostResolution(SpdySessionPool* spdy_session_pool,
242                              const SpdySessionKey& spdy_session_key,
243                              const AddressList& addresses,
244                              const BoundNetLog& net_log);
245
246  Request* request_;
247
248  const HttpRequestInfo request_info_;
249  RequestPriority priority_;
250  ProxyInfo proxy_info_;
251  SSLConfig server_ssl_config_;
252  SSLConfig proxy_ssl_config_;
253  const BoundNetLog net_log_;
254
255  CompletionCallback io_callback_;
256  scoped_ptr<ClientSocketHandle> connection_;
257  HttpNetworkSession* const session_;
258  HttpStreamFactoryImpl* const stream_factory_;
259  State next_state_;
260  ProxyService::PacRequest* pac_request_;
261  SSLInfo ssl_info_;
262
263  // The origin server we're trying to reach.
264  HostPortPair origin_;
265
266  // The origin url we're trying to reach. This url may be different from the
267  // original request when host mapping rules are set-up.
268  GURL origin_url_;
269
270  // If this is a Job for an "Alternate-Protocol", then this will be non-NULL
271  // and will specify the original URL.
272  scoped_ptr<GURL> original_url_;
273
274  // This is the Job we're dependent on. It will notify us if/when it's OK to
275  // proceed.
276  Job* blocking_job_;
277
278  // |waiting_job_| is a Job waiting to see if |this| can reuse a connection.
279  // If |this| is unable to do so, we'll notify |waiting_job_| that it's ok to
280  // proceed and then race the two Jobs.
281  Job* waiting_job_;
282
283  // True if handling a HTTPS request, or using SPDY with SSL
284  bool using_ssl_;
285
286  // True if this network transaction is using SPDY instead of HTTP.
287  bool using_spdy_;
288
289  // True if this network transaction is using QUIC instead of HTTP.
290  bool using_quic_;
291  QuicStreamRequest quic_request_;
292
293  // True if this job used an existing QUIC session.
294  bool using_existing_quic_session_;
295
296  // Force quic for a specific port.
297  int force_quic_port_;
298
299  // The certificate error while using SPDY over SSL for insecure URLs.
300  int spdy_certificate_error_;
301
302  scoped_refptr<HttpAuthController>
303      auth_controllers_[HttpAuth::AUTH_NUM_TARGETS];
304
305  // True when the tunnel is in the process of being established - we can't
306  // read from the socket until the tunnel is done.
307  bool establishing_tunnel_;
308
309  scoped_ptr<HttpStream> stream_;
310  scoped_ptr<WebSocketHandshakeStreamBase> websocket_stream_;
311
312  // True if we negotiated NPN.
313  bool was_npn_negotiated_;
314
315  // Protocol negotiated with the server.
316  NextProto protocol_negotiated_;
317
318  // 0 if we're not preconnecting. Otherwise, the number of streams to
319  // preconnect.
320  int num_streams_;
321
322  // Initialized when we create a new SpdySession.
323  base::WeakPtr<SpdySession> new_spdy_session_;
324
325  // Initialized when we have an existing SpdySession.
326  base::WeakPtr<SpdySession> existing_spdy_session_;
327
328  // Only used if |new_spdy_session_| is non-NULL.
329  bool spdy_session_direct_;
330
331  JobStatus job_status_;
332  JobStatus other_job_status_;
333
334  base::WeakPtrFactory<Job> ptr_factory_;
335
336  DISALLOW_COPY_AND_ASSIGN(Job);
337};
338
339}  // namespace net
340
341#endif  // NET_HTTP_HTTP_STREAM_FACTORY_IMPL_JOB_H_
342