Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / net / http / http_proxy_client_socket_pool.cc
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 #include "net/http/http_proxy_client_socket_pool.h"
6
7 #include <algorithm>
8
9 #include "base/compiler_specific.h"
10 #include "base/time/time.h"
11 #include "base/values.h"
12 #include "net/base/load_flags.h"
13 #include "net/base/net_errors.h"
14 #include "net/base/proxy_delegate.h"
15 #include "net/http/http_network_session.h"
16 #include "net/http/http_proxy_client_socket.h"
17 #include "net/socket/client_socket_factory.h"
18 #include "net/socket/client_socket_handle.h"
19 #include "net/socket/client_socket_pool_base.h"
20 #include "net/socket/ssl_client_socket.h"
21 #include "net/socket/ssl_client_socket_pool.h"
22 #include "net/socket/transport_client_socket_pool.h"
23 #include "net/spdy/spdy_proxy_client_socket.h"
24 #include "net/spdy/spdy_session.h"
25 #include "net/spdy/spdy_session_pool.h"
26 #include "net/spdy/spdy_stream.h"
27 #include "net/ssl/ssl_cert_request_info.h"
28 #include "url/gurl.h"
29
30 namespace net {
31
32 HttpProxySocketParams::HttpProxySocketParams(
33     const scoped_refptr<TransportSocketParams>& transport_params,
34     const scoped_refptr<SSLSocketParams>& ssl_params,
35     const GURL& request_url,
36     const std::string& user_agent,
37     const HostPortPair& endpoint,
38     HttpAuthCache* http_auth_cache,
39     HttpAuthHandlerFactory* http_auth_handler_factory,
40     SpdySessionPool* spdy_session_pool,
41     bool tunnel,
42     ProxyDelegate* proxy_delegate)
43     : transport_params_(transport_params),
44       ssl_params_(ssl_params),
45       spdy_session_pool_(spdy_session_pool),
46       request_url_(request_url),
47       user_agent_(user_agent),
48       endpoint_(endpoint),
49       http_auth_cache_(tunnel ? http_auth_cache : NULL),
50       http_auth_handler_factory_(tunnel ? http_auth_handler_factory : NULL),
51       tunnel_(tunnel),
52       proxy_delegate_(proxy_delegate) {
53   DCHECK((transport_params.get() == NULL && ssl_params.get() != NULL) ||
54          (transport_params.get() != NULL && ssl_params.get() == NULL));
55   if (transport_params_.get()) {
56     ignore_limits_ = transport_params->ignore_limits();
57   } else {
58     ignore_limits_ = ssl_params->ignore_limits();
59   }
60 }
61
62 const HostResolver::RequestInfo& HttpProxySocketParams::destination() const {
63   if (transport_params_.get() == NULL) {
64     return ssl_params_->GetDirectConnectionParams()->destination();
65   } else {
66     return transport_params_->destination();
67   }
68 }
69
70 HttpProxySocketParams::~HttpProxySocketParams() {}
71
72 // HttpProxyConnectJobs will time out after this many seconds.  Note this is on
73 // top of the timeout for the transport socket.
74 // TODO(kundaji): Proxy connect timeout should be independent of platform and be
75 // based on proxy. Bug http://crbug.com/407446.
76 #if defined(OS_ANDROID) || defined(OS_IOS)
77 static const int kHttpProxyConnectJobTimeoutInSeconds = 10;
78 #else
79 static const int kHttpProxyConnectJobTimeoutInSeconds = 30;
80 #endif
81
82 HttpProxyConnectJob::HttpProxyConnectJob(
83     const std::string& group_name,
84     RequestPriority priority,
85     const scoped_refptr<HttpProxySocketParams>& params,
86     const base::TimeDelta& timeout_duration,
87     TransportClientSocketPool* transport_pool,
88     SSLClientSocketPool* ssl_pool,
89     HostResolver* host_resolver,
90     Delegate* delegate,
91     NetLog* net_log)
92     : ConnectJob(group_name, timeout_duration, priority, delegate,
93                  BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)),
94       params_(params),
95       transport_pool_(transport_pool),
96       ssl_pool_(ssl_pool),
97       resolver_(host_resolver),
98       using_spdy_(false),
99       protocol_negotiated_(kProtoUnknown),
100       weak_ptr_factory_(this) {
101     callback_= base::Bind(&HttpProxyConnectJob::OnIOComplete,
102                            weak_ptr_factory_.GetWeakPtr());
103 }
104
105 HttpProxyConnectJob::~HttpProxyConnectJob() {}
106
107 LoadState HttpProxyConnectJob::GetLoadState() const {
108   switch (next_state_) {
109     case STATE_TCP_CONNECT:
110     case STATE_TCP_CONNECT_COMPLETE:
111     case STATE_SSL_CONNECT:
112     case STATE_SSL_CONNECT_COMPLETE:
113       return transport_socket_handle_->GetLoadState();
114     case STATE_HTTP_PROXY_CONNECT:
115     case STATE_HTTP_PROXY_CONNECT_COMPLETE:
116     case STATE_SPDY_PROXY_CREATE_STREAM:
117     case STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE:
118       return LOAD_STATE_ESTABLISHING_PROXY_TUNNEL;
119     default:
120       NOTREACHED();
121       return LOAD_STATE_IDLE;
122   }
123 }
124
125 void HttpProxyConnectJob::GetAdditionalErrorState(ClientSocketHandle * handle) {
126   if (error_response_info_.cert_request_info.get()) {
127     handle->set_ssl_error_response_info(error_response_info_);
128     handle->set_is_ssl_error(true);
129   }
130 }
131
132 void HttpProxyConnectJob::OnIOComplete(int result) {
133   int rv = DoLoop(result);
134   if (rv != ERR_IO_PENDING) {
135     NotifyProxyDelegateOfCompletion(rv);
136     NotifyDelegateOfCompletion(rv);  // Deletes |this|
137   }
138 }
139
140 int HttpProxyConnectJob::DoLoop(int result) {
141   DCHECK_NE(next_state_, STATE_NONE);
142
143   int rv = result;
144   do {
145     State state = next_state_;
146     next_state_ = STATE_NONE;
147     switch (state) {
148       case STATE_TCP_CONNECT:
149         DCHECK_EQ(OK, rv);
150         rv = DoTransportConnect();
151         break;
152       case STATE_TCP_CONNECT_COMPLETE:
153         rv = DoTransportConnectComplete(rv);
154         break;
155       case STATE_SSL_CONNECT:
156         DCHECK_EQ(OK, rv);
157         rv = DoSSLConnect();
158         break;
159       case STATE_SSL_CONNECT_COMPLETE:
160         rv = DoSSLConnectComplete(rv);
161         break;
162       case STATE_HTTP_PROXY_CONNECT:
163         DCHECK_EQ(OK, rv);
164         rv = DoHttpProxyConnect();
165         break;
166       case STATE_HTTP_PROXY_CONNECT_COMPLETE:
167         rv = DoHttpProxyConnectComplete(rv);
168         break;
169       case STATE_SPDY_PROXY_CREATE_STREAM:
170         DCHECK_EQ(OK, rv);
171         rv = DoSpdyProxyCreateStream();
172         break;
173       case STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE:
174         rv = DoSpdyProxyCreateStreamComplete(rv);
175         break;
176       default:
177         NOTREACHED() << "bad state";
178         rv = ERR_FAILED;
179         break;
180     }
181   } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
182
183   return rv;
184 }
185
186 int HttpProxyConnectJob::DoTransportConnect() {
187   next_state_ = STATE_TCP_CONNECT_COMPLETE;
188   transport_socket_handle_.reset(new ClientSocketHandle());
189   return transport_socket_handle_->Init(group_name(),
190                                         params_->transport_params(),
191                                         priority(),
192                                         callback_,
193                                         transport_pool_,
194                                         net_log());
195 }
196
197 int HttpProxyConnectJob::DoTransportConnectComplete(int result) {
198   if (result != OK)
199     return ERR_PROXY_CONNECTION_FAILED;
200
201   // Reset the timer to just the length of time allowed for HttpProxy handshake
202   // so that a fast TCP connection plus a slow HttpProxy failure doesn't take
203   // longer to timeout than it should.
204   ResetTimer(base::TimeDelta::FromSeconds(
205       kHttpProxyConnectJobTimeoutInSeconds));
206
207   next_state_ = STATE_HTTP_PROXY_CONNECT;
208   return result;
209 }
210
211 int HttpProxyConnectJob::DoSSLConnect() {
212   if (params_->tunnel()) {
213     SpdySessionKey key(params_->destination().host_port_pair(),
214                        ProxyServer::Direct(),
215                        PRIVACY_MODE_DISABLED);
216     if (params_->spdy_session_pool()->FindAvailableSession(key, net_log())) {
217       using_spdy_ = true;
218       next_state_ = STATE_SPDY_PROXY_CREATE_STREAM;
219       return OK;
220     }
221   }
222   next_state_ = STATE_SSL_CONNECT_COMPLETE;
223   transport_socket_handle_.reset(new ClientSocketHandle());
224   return transport_socket_handle_->Init(
225       group_name(), params_->ssl_params(), priority(), callback_,
226       ssl_pool_, net_log());
227 }
228
229 int HttpProxyConnectJob::DoSSLConnectComplete(int result) {
230   if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
231     error_response_info_ = transport_socket_handle_->ssl_error_response_info();
232     DCHECK(error_response_info_.cert_request_info.get());
233     error_response_info_.cert_request_info->is_proxy = true;
234     return result;
235   }
236   if (IsCertificateError(result)) {
237     if (params_->ssl_params()->load_flags() & LOAD_IGNORE_ALL_CERT_ERRORS) {
238       result = OK;
239     } else {
240       // TODO(rch): allow the user to deal with proxy cert errors in the
241       // same way as server cert errors.
242       transport_socket_handle_->socket()->Disconnect();
243       return ERR_PROXY_CERTIFICATE_INVALID;
244     }
245   }
246   // A SPDY session to the proxy completed prior to resolving the proxy
247   // hostname. Surface this error, and allow the delegate to retry.
248   // See crbug.com/334413.
249   if (result == ERR_SPDY_SESSION_ALREADY_EXISTS) {
250     DCHECK(!transport_socket_handle_->socket());
251     return ERR_SPDY_SESSION_ALREADY_EXISTS;
252   }
253   if (result < 0) {
254     if (transport_socket_handle_->socket())
255       transport_socket_handle_->socket()->Disconnect();
256     return ERR_PROXY_CONNECTION_FAILED;
257   }
258
259   SSLClientSocket* ssl =
260       static_cast<SSLClientSocket*>(transport_socket_handle_->socket());
261   using_spdy_ = ssl->was_spdy_negotiated();
262   protocol_negotiated_ = ssl->GetNegotiatedProtocol();
263
264   // Reset the timer to just the length of time allowed for HttpProxy handshake
265   // so that a fast SSL connection plus a slow HttpProxy failure doesn't take
266   // longer to timeout than it should.
267   ResetTimer(base::TimeDelta::FromSeconds(
268       kHttpProxyConnectJobTimeoutInSeconds));
269   // TODO(rch): If we ever decide to implement a "trusted" SPDY proxy
270   // (one that we speak SPDY over SSL to, but to which we send HTTPS
271   // request directly instead of through CONNECT tunnels, then we
272   // need to add a predicate to this if statement so we fall through
273   // to the else case. (HttpProxyClientSocket currently acts as
274   // a "trusted" SPDY proxy).
275   if (using_spdy_ && params_->tunnel()) {
276     next_state_ = STATE_SPDY_PROXY_CREATE_STREAM;
277   } else {
278     next_state_ = STATE_HTTP_PROXY_CONNECT;
279   }
280   return result;
281 }
282
283 int HttpProxyConnectJob::DoHttpProxyConnect() {
284   next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE;
285   const HostResolver::RequestInfo& tcp_destination = params_->destination();
286   const HostPortPair& proxy_server = tcp_destination.host_port_pair();
287
288   // Add a HttpProxy connection on top of the tcp socket.
289   transport_socket_.reset(
290       new HttpProxyClientSocket(transport_socket_handle_.release(),
291                                 params_->request_url(),
292                                 params_->user_agent(),
293                                 params_->endpoint(),
294                                 proxy_server,
295                                 params_->http_auth_cache(),
296                                 params_->http_auth_handler_factory(),
297                                 params_->tunnel(),
298                                 using_spdy_,
299                                 protocol_negotiated_,
300                                 params_->proxy_delegate(),
301                                 params_->ssl_params().get() != NULL));
302   return transport_socket_->Connect(callback_);
303 }
304
305 int HttpProxyConnectJob::DoHttpProxyConnectComplete(int result) {
306   if (result == OK || result == ERR_PROXY_AUTH_REQUESTED ||
307       result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE) {
308     SetSocket(transport_socket_.Pass());
309   }
310
311   return result;
312 }
313
314 int HttpProxyConnectJob::DoSpdyProxyCreateStream() {
315   DCHECK(using_spdy_);
316   DCHECK(params_->tunnel());
317   SpdySessionKey key(params_->destination().host_port_pair(),
318                      ProxyServer::Direct(),
319                      PRIVACY_MODE_DISABLED);
320   SpdySessionPool* spdy_pool = params_->spdy_session_pool();
321   base::WeakPtr<SpdySession> spdy_session =
322       spdy_pool->FindAvailableSession(key, net_log());
323   // It's possible that a session to the proxy has recently been created
324   if (spdy_session) {
325     if (transport_socket_handle_.get()) {
326       if (transport_socket_handle_->socket())
327         transport_socket_handle_->socket()->Disconnect();
328       transport_socket_handle_->Reset();
329     }
330   } else {
331     // Create a session direct to the proxy itself
332     spdy_session =
333         spdy_pool->CreateAvailableSessionFromSocket(
334             key, transport_socket_handle_.Pass(),
335             net_log(), OK, /*using_ssl_*/ true);
336     DCHECK(spdy_session);
337   }
338
339   next_state_ = STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE;
340   return spdy_stream_request_.StartRequest(SPDY_BIDIRECTIONAL_STREAM,
341                                            spdy_session,
342                                            params_->request_url(),
343                                            priority(),
344                                            spdy_session->net_log(),
345                                            callback_);
346 }
347
348 int HttpProxyConnectJob::DoSpdyProxyCreateStreamComplete(int result) {
349   if (result < 0)
350     return result;
351
352   next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE;
353   base::WeakPtr<SpdyStream> stream = spdy_stream_request_.ReleaseStream();
354   DCHECK(stream.get());
355   // |transport_socket_| will set itself as |stream|'s delegate.
356   transport_socket_.reset(
357       new SpdyProxyClientSocket(stream,
358                                 params_->user_agent(),
359                                 params_->endpoint(),
360                                 params_->request_url(),
361                                 params_->destination().host_port_pair(),
362                                 net_log(),
363                                 params_->http_auth_cache(),
364                                 params_->http_auth_handler_factory()));
365   return transport_socket_->Connect(callback_);
366 }
367
368 void HttpProxyConnectJob::NotifyProxyDelegateOfCompletion(int result) {
369   if (!params_->proxy_delegate())
370     return;
371
372   const HostPortPair& proxy_server = params_->destination().host_port_pair();
373   params_->proxy_delegate()->OnTunnelConnectCompleted(params_->endpoint(),
374                                                       proxy_server,
375                                                       result);
376 }
377
378 int HttpProxyConnectJob::ConnectInternal() {
379   if (params_->transport_params().get()) {
380     next_state_ = STATE_TCP_CONNECT;
381   } else {
382     next_state_ = STATE_SSL_CONNECT;
383   }
384
385   int rv = DoLoop(OK);
386   if (rv != ERR_IO_PENDING) {
387     NotifyProxyDelegateOfCompletion(rv);
388   }
389
390   return rv;
391 }
392
393 HttpProxyClientSocketPool::
394 HttpProxyConnectJobFactory::HttpProxyConnectJobFactory(
395     TransportClientSocketPool* transport_pool,
396     SSLClientSocketPool* ssl_pool,
397     HostResolver* host_resolver,
398     const ProxyDelegate* proxy_delegate,
399     NetLog* net_log)
400     : transport_pool_(transport_pool),
401       ssl_pool_(ssl_pool),
402       host_resolver_(host_resolver),
403       proxy_delegate_(proxy_delegate),
404       net_log_(net_log) {
405   base::TimeDelta max_pool_timeout = base::TimeDelta();
406
407 // TODO(kundaji): Proxy connect timeout should be independent of platform and be
408 // based on proxy. Bug http://crbug.com/407446.
409 #if (defined(OS_ANDROID) || defined(OS_IOS))
410 #else
411   if (transport_pool_)
412     max_pool_timeout = transport_pool_->ConnectionTimeout();
413   if (ssl_pool_)
414     max_pool_timeout = std::max(max_pool_timeout,
415                                 ssl_pool_->ConnectionTimeout());
416 #endif
417   timeout_ = max_pool_timeout +
418     base::TimeDelta::FromSeconds(kHttpProxyConnectJobTimeoutInSeconds);
419 }
420
421
422 scoped_ptr<ConnectJob>
423 HttpProxyClientSocketPool::HttpProxyConnectJobFactory::NewConnectJob(
424     const std::string& group_name,
425     const PoolBase::Request& request,
426     ConnectJob::Delegate* delegate) const {
427   return scoped_ptr<ConnectJob>(new HttpProxyConnectJob(group_name,
428                                                         request.priority(),
429                                                         request.params(),
430                                                         ConnectionTimeout(),
431                                                         transport_pool_,
432                                                         ssl_pool_,
433                                                         host_resolver_,
434                                                         delegate,
435                                                         net_log_));
436 }
437
438 base::TimeDelta
439 HttpProxyClientSocketPool::HttpProxyConnectJobFactory::ConnectionTimeout(
440     ) const {
441   return timeout_;
442 }
443
444 HttpProxyClientSocketPool::HttpProxyClientSocketPool(
445     int max_sockets,
446     int max_sockets_per_group,
447     ClientSocketPoolHistograms* histograms,
448     HostResolver* host_resolver,
449     TransportClientSocketPool* transport_pool,
450     SSLClientSocketPool* ssl_pool,
451     const ProxyDelegate* proxy_delegate,
452     NetLog* net_log)
453     : transport_pool_(transport_pool),
454       ssl_pool_(ssl_pool),
455       base_(this, max_sockets, max_sockets_per_group, histograms,
456             ClientSocketPool::unused_idle_socket_timeout(),
457             ClientSocketPool::used_idle_socket_timeout(),
458             new HttpProxyConnectJobFactory(transport_pool,
459                                            ssl_pool,
460                                            host_resolver,
461                                            proxy_delegate,
462                                            net_log)) {
463   // We should always have a |transport_pool_| except in unit tests.
464   if (transport_pool_)
465     base_.AddLowerLayeredPool(transport_pool_);
466   if (ssl_pool_)
467     base_.AddLowerLayeredPool(ssl_pool_);
468 }
469
470 HttpProxyClientSocketPool::~HttpProxyClientSocketPool() {
471 }
472
473 int HttpProxyClientSocketPool::RequestSocket(
474     const std::string& group_name, const void* socket_params,
475     RequestPriority priority, ClientSocketHandle* handle,
476     const CompletionCallback& callback, const BoundNetLog& net_log) {
477   const scoped_refptr<HttpProxySocketParams>* casted_socket_params =
478       static_cast<const scoped_refptr<HttpProxySocketParams>*>(socket_params);
479
480   return base_.RequestSocket(group_name, *casted_socket_params, priority,
481                              handle, callback, net_log);
482 }
483
484 void HttpProxyClientSocketPool::RequestSockets(
485     const std::string& group_name,
486     const void* params,
487     int num_sockets,
488     const BoundNetLog& net_log) {
489   const scoped_refptr<HttpProxySocketParams>* casted_params =
490       static_cast<const scoped_refptr<HttpProxySocketParams>*>(params);
491
492   base_.RequestSockets(group_name, *casted_params, num_sockets, net_log);
493 }
494
495 void HttpProxyClientSocketPool::CancelRequest(
496     const std::string& group_name,
497     ClientSocketHandle* handle) {
498   base_.CancelRequest(group_name, handle);
499 }
500
501 void HttpProxyClientSocketPool::ReleaseSocket(const std::string& group_name,
502                                               scoped_ptr<StreamSocket> socket,
503                                               int id) {
504   base_.ReleaseSocket(group_name, socket.Pass(), id);
505 }
506
507 void HttpProxyClientSocketPool::FlushWithError(int error) {
508   base_.FlushWithError(error);
509 }
510
511 void HttpProxyClientSocketPool::CloseIdleSockets() {
512   base_.CloseIdleSockets();
513 }
514
515 int HttpProxyClientSocketPool::IdleSocketCount() const {
516   return base_.idle_socket_count();
517 }
518
519 int HttpProxyClientSocketPool::IdleSocketCountInGroup(
520     const std::string& group_name) const {
521   return base_.IdleSocketCountInGroup(group_name);
522 }
523
524 LoadState HttpProxyClientSocketPool::GetLoadState(
525     const std::string& group_name, const ClientSocketHandle* handle) const {
526   return base_.GetLoadState(group_name, handle);
527 }
528
529 base::DictionaryValue* HttpProxyClientSocketPool::GetInfoAsValue(
530     const std::string& name,
531     const std::string& type,
532     bool include_nested_pools) const {
533   base::DictionaryValue* dict = base_.GetInfoAsValue(name, type);
534   if (include_nested_pools) {
535     base::ListValue* list = new base::ListValue();
536     if (transport_pool_) {
537       list->Append(transport_pool_->GetInfoAsValue("transport_socket_pool",
538                                                    "transport_socket_pool",
539                                                    true));
540     }
541     if (ssl_pool_) {
542       list->Append(ssl_pool_->GetInfoAsValue("ssl_socket_pool",
543                                              "ssl_socket_pool",
544                                              true));
545     }
546     dict->Set("nested_pools", list);
547   }
548   return dict;
549 }
550
551 base::TimeDelta HttpProxyClientSocketPool::ConnectionTimeout() const {
552   return base_.ConnectionTimeout();
553 }
554
555 ClientSocketPoolHistograms* HttpProxyClientSocketPool::histograms() const {
556   return base_.histograms();
557 }
558
559 bool HttpProxyClientSocketPool::IsStalled() const {
560   return base_.IsStalled();
561 }
562
563 void HttpProxyClientSocketPool::AddHigherLayeredPool(
564     HigherLayeredPool* higher_pool) {
565   base_.AddHigherLayeredPool(higher_pool);
566 }
567
568 void HttpProxyClientSocketPool::RemoveHigherLayeredPool(
569     HigherLayeredPool* higher_pool) {
570   base_.RemoveHigherLayeredPool(higher_pool);
571 }
572
573 bool HttpProxyClientSocketPool::CloseOneIdleConnection() {
574   if (base_.CloseOneIdleSocket())
575     return true;
576   return base_.CloseOneIdleConnectionInHigherLayeredPool();
577 }
578
579 }  // namespace net