summaryrefslogtreecommitdiffstats
path: root/net/http/http_stream_factory_impl_job.h
blob: 04342bff28d6eff23e74a4cc644d99ad68757530 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
// 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/request_priority.h"
#include "net/http/http_auth.h"
#include "net/http/http_auth_controller.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"
#include "net/spdy/spdy_session_key.h"
#include "net/ssl/ssl_config_service.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,
      RequestPriority priority,
      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.  |alternate| specifies the alternate protocol to use
  // and alternate port to connect to.
  void MarkAsAlternate(const GURL& original_url,
                       AlternateProtocolInfo alternate);

  // 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);

  void SetPriority(RequestPriority priority);

  RequestPriority priority() const { return priority_; }
  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;

  // Called to indicate that this job succeeded, and some other jobs
  // will be orphaned.
  void ReportJobSuccededForRequest();

  // Marks that the other |job| has completed.
  void MarkOtherJobComplete(const Job& job);

 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
  };

  enum JobStatus {
    STATUS_RUNNING,
    STATUS_FAILED,
    STATUS_BROKEN,
    STATUS_SUCCEEDED
  };

  void OnStreamReadyCallback();
  void OnWebSocketHandshakeStreamReadyCallback();
  // 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,
                                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);

  // Creates a SpdyHttpStream from the given values and sets to |stream_|. Does
  // nothing if |stream_factory_| is for WebSockets.
  int SetSpdyHttpStream(base::WeakPtr<SpdySession> session, bool direct);

  // 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,
                     bool is_proxy) const;

  // 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();

  SpdySessionKey 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;

  void MaybeMarkAlternateProtocolBroken();

  // 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 SpdySessionKey& spdy_session_key,
                              const AddressList& addresses,
                              const BoundNetLog& net_log);

  Request* request_;

  const HttpRequestInfo request_info_;
  RequestPriority priority_;
  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_;

  // AlternateProtocol for this job if this is an alternate job.
  AlternateProtocolInfo alternate_protocol_;

  // AlternateProtocol for the other job if this is not an alternate job.
  AlternateProtocolInfo other_job_alternate_protocol_;

  // 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_;

  // True if this job used an existing QUIC session.
  bool using_existing_quic_session_;

  // 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_;
  scoped_ptr<WebSocketHandshakeStreamBase> websocket_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.
  base::WeakPtr<SpdySession> new_spdy_session_;

  // Initialized when we have an existing SpdySession.
  base::WeakPtr<SpdySession> existing_spdy_session_;

  // Only used if |new_spdy_session_| is non-NULL.
  bool spdy_session_direct_;

  JobStatus job_status_;
  JobStatus other_job_status_;

  base::WeakPtrFactory<Job> ptr_factory_;

  DISALLOW_COPY_AND_ASSIGN(Job);
};

}  // namespace net

#endif  // NET_HTTP_HTTP_STREAM_FACTORY_IMPL_JOB_H_