OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/http/http_stream_factory_impl_job.h" | 5 #include "net/http/http_stream_factory_impl_job.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "base/stl_util.h" | 8 #include "base/stl_util.h" |
9 #include "base/string_util.h" | 9 #include "base/string_util.h" |
10 #include "base/stringprintf.h" | 10 #include "base/stringprintf.h" |
11 #include "base/values.h" | 11 #include "base/values.h" |
12 #include "build/build_config.h" | 12 #include "build/build_config.h" |
13 #include "net/base/connection_type_histograms.h" | 13 #include "net/base/connection_type_histograms.h" |
14 #include "net/base/net_log.h" | 14 #include "net/base/net_log.h" |
15 #include "net/base/net_util.h" | 15 #include "net/base/net_util.h" |
16 #include "net/base/ssl_cert_request_info.h" | 16 #include "net/base/ssl_cert_request_info.h" |
17 #include "net/http/http_basic_stream.h" | 17 #include "net/http/http_basic_stream.h" |
18 #include "net/http/http_network_session.h" | 18 #include "net/http/http_network_session.h" |
| 19 #include "net/http/http_pipelined_connection.h" |
| 20 #include "net/http/http_pipelined_host.h" |
| 21 #include "net/http/http_pipelined_host_pool.h" |
| 22 #include "net/http/http_pipelined_stream.h" |
19 #include "net/http/http_proxy_client_socket.h" | 23 #include "net/http/http_proxy_client_socket.h" |
20 #include "net/http/http_proxy_client_socket_pool.h" | 24 #include "net/http/http_proxy_client_socket_pool.h" |
21 #include "net/http/http_request_info.h" | 25 #include "net/http/http_request_info.h" |
| 26 #include "net/http/http_stream_factory.h" |
22 #include "net/http/http_stream_factory_impl_request.h" | 27 #include "net/http/http_stream_factory_impl_request.h" |
23 #include "net/socket/client_socket_handle.h" | 28 #include "net/socket/client_socket_handle.h" |
24 #include "net/socket/client_socket_pool.h" | 29 #include "net/socket/client_socket_pool.h" |
25 #include "net/socket/socks_client_socket_pool.h" | 30 #include "net/socket/socks_client_socket_pool.h" |
26 #include "net/socket/ssl_client_socket.h" | 31 #include "net/socket/ssl_client_socket.h" |
27 #include "net/socket/ssl_client_socket_pool.h" | 32 #include "net/socket/ssl_client_socket_pool.h" |
28 #include "net/spdy/spdy_http_stream.h" | 33 #include "net/spdy/spdy_http_stream.h" |
29 #include "net/spdy/spdy_session.h" | 34 #include "net/spdy/spdy_session.h" |
30 #include "net/spdy/spdy_session_pool.h" | 35 #include "net/spdy/spdy_session_pool.h" |
31 | 36 |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
80 dependent_job_(NULL), | 85 dependent_job_(NULL), |
81 using_ssl_(false), | 86 using_ssl_(false), |
82 using_spdy_(false), | 87 using_spdy_(false), |
83 force_spdy_always_(HttpStreamFactory::force_spdy_always()), | 88 force_spdy_always_(HttpStreamFactory::force_spdy_always()), |
84 force_spdy_over_ssl_(HttpStreamFactory::force_spdy_over_ssl()), | 89 force_spdy_over_ssl_(HttpStreamFactory::force_spdy_over_ssl()), |
85 spdy_certificate_error_(OK), | 90 spdy_certificate_error_(OK), |
86 establishing_tunnel_(false), | 91 establishing_tunnel_(false), |
87 was_npn_negotiated_(false), | 92 was_npn_negotiated_(false), |
88 num_streams_(0), | 93 num_streams_(0), |
89 spdy_session_direct_(false), | 94 spdy_session_direct_(false), |
| 95 existing_available_pipeline_(false), |
90 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { | 96 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { |
91 DCHECK(stream_factory); | 97 DCHECK(stream_factory); |
92 DCHECK(session); | 98 DCHECK(session); |
93 } | 99 } |
94 | 100 |
95 HttpStreamFactoryImpl::Job::~Job() { | 101 HttpStreamFactoryImpl::Job::~Job() { |
96 net_log_.EndEvent(NetLog::TYPE_HTTP_STREAM_JOB, NULL); | 102 net_log_.EndEvent(NetLog::TYPE_HTTP_STREAM_JOB, NULL); |
97 | 103 |
98 // When we're in a partially constructed state, waiting for the user to | 104 // When we're in a partially constructed state, waiting for the user to |
99 // provide certificate handling information or authentication, we can't reuse | 105 // provide certificate handling information or authentication, we can't reuse |
(...skipping 480 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
580 // actually need to preconnect any sockets, so we're done. | 586 // actually need to preconnect any sockets, so we're done. |
581 if (IsPreconnecting()) | 587 if (IsPreconnecting()) |
582 return OK; | 588 return OK; |
583 using_spdy_ = true; | 589 using_spdy_ = true; |
584 next_state_ = STATE_CREATE_STREAM; | 590 next_state_ = STATE_CREATE_STREAM; |
585 existing_spdy_session_ = spdy_session; | 591 existing_spdy_session_ = spdy_session; |
586 return OK; | 592 return OK; |
587 } else if (request_ && (using_ssl_ || ShouldForceSpdyWithoutSSL())) { | 593 } else if (request_ && (using_ssl_ || ShouldForceSpdyWithoutSSL())) { |
588 // Update the spdy session key for the request that launched this job. | 594 // Update the spdy session key for the request that launched this job. |
589 request_->SetSpdySessionKey(spdy_session_key); | 595 request_->SetSpdySessionKey(spdy_session_key); |
| 596 } else if (IsRequestEligibleForPipelining()) { |
| 597 // TODO(simonjam): With pipelining, we might be better off using fewer |
| 598 // connections and thus should make fewer preconnections. Explore |
| 599 // preconnecting fewer than the requested num_connections. |
| 600 existing_available_pipeline_ = session_->http_pipelined_host_pool()-> |
| 601 IsExistingPipelineAvailableForOrigin(origin_); |
| 602 request_->SetHttpPipeliningKey(origin_); |
590 } | 603 } |
591 | 604 |
592 // OK, there's no available SPDY session. Let |dependent_job_| resume if it's | 605 // OK, there's no available SPDY session. Let |dependent_job_| resume if it's |
593 // paused. | 606 // paused. |
594 | 607 |
595 if (dependent_job_) { | 608 if (dependent_job_) { |
596 dependent_job_->Resume(this); | 609 dependent_job_->Resume(this); |
597 dependent_job_ = NULL; | 610 dependent_job_ = NULL; |
598 } | 611 } |
599 | 612 |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
738 if (result == OK && !connection_->socket()->IsConnectedAndIdle()) { | 751 if (result == OK && !connection_->socket()->IsConnectedAndIdle()) { |
739 ReturnToStateInitConnection(true /* close connection */); | 752 ReturnToStateInitConnection(true /* close connection */); |
740 return result; | 753 return result; |
741 } | 754 } |
742 } | 755 } |
743 } | 756 } |
744 if (result < 0) | 757 if (result < 0) |
745 return result; | 758 return result; |
746 } | 759 } |
747 | 760 |
748 if (!connection_->socket()) { | |
749 HACKCrashHereToDebug80095(); | |
750 } | |
751 | |
752 next_state_ = STATE_CREATE_STREAM; | 761 next_state_ = STATE_CREATE_STREAM; |
753 return OK; | 762 return OK; |
754 } | 763 } |
755 | 764 |
756 int HttpStreamFactoryImpl::Job::DoWaitingUserAction(int result) { | 765 int HttpStreamFactoryImpl::Job::DoWaitingUserAction(int result) { |
757 // This state indicates that the stream request is in a partially | 766 // This state indicates that the stream request is in a partially |
758 // completed state, and we've called back to the delegate for more | 767 // completed state, and we've called back to the delegate for more |
759 // information. | 768 // information. |
760 | 769 |
761 // We're always waiting here for the delegate to call us back. | 770 // We're always waiting here for the delegate to call us back. |
762 return ERR_IO_PENDING; | 771 return ERR_IO_PENDING; |
763 } | 772 } |
764 | 773 |
765 int HttpStreamFactoryImpl::Job::DoCreateStream() { | 774 int HttpStreamFactoryImpl::Job::DoCreateStream() { |
766 if (!connection_->socket() && !existing_spdy_session_) | 775 DCHECK(connection_->socket() || existing_spdy_session_ || |
767 HACKCrashHereToDebug80095(); | 776 existing_available_pipeline_); |
768 | 777 |
769 next_state_ = STATE_CREATE_STREAM_COMPLETE; | 778 next_state_ = STATE_CREATE_STREAM_COMPLETE; |
770 | 779 |
771 // We only set the socket motivation if we're the first to use | 780 // We only set the socket motivation if we're the first to use |
772 // this socket. Is there a race for two SPDY requests? We really | 781 // this socket. Is there a race for two SPDY requests? We really |
773 // need to plumb this through to the connect level. | 782 // need to plumb this through to the connect level. |
774 if (connection_->socket() && !connection_->is_reused()) | 783 if (connection_->socket() && !connection_->is_reused()) |
775 SetSocketMotivation(); | 784 SetSocketMotivation(); |
776 | 785 |
777 const ProxyServer& proxy_server = proxy_info_.proxy_server(); | 786 const ProxyServer& proxy_server = proxy_info_.proxy_server(); |
778 | 787 |
779 if (!using_spdy_) { | 788 if (!using_spdy_) { |
780 bool using_proxy = (proxy_info_.is_http() || proxy_info_.is_https()) && | 789 bool using_proxy = (proxy_info_.is_http() || proxy_info_.is_https()) && |
781 request_info_.url.SchemeIs("http"); | 790 request_info_.url.SchemeIs("http"); |
782 stream_.reset(new HttpBasicStream(connection_.release(), NULL, | 791 // TODO(simonjam): Support proxies. |
783 using_proxy)); | 792 if (existing_available_pipeline_) { |
| 793 stream_.reset( |
| 794 session_->http_pipelined_host_pool()->CreateStreamOnExistingPipeline( |
| 795 origin_)); |
| 796 } else if (!using_proxy && IsRequestEligibleForPipelining()) { |
| 797 stream_.reset( |
| 798 session_->http_pipelined_host_pool()->CreateStreamOnNewPipeline( |
| 799 origin_, |
| 800 connection_.release(), |
| 801 server_ssl_config_, |
| 802 proxy_info_, |
| 803 net_log_, |
| 804 was_npn_negotiated_)); |
| 805 } else { |
| 806 stream_.reset(new HttpBasicStream(connection_.release(), NULL, |
| 807 using_proxy)); |
| 808 } |
784 return OK; | 809 return OK; |
785 } | 810 } |
786 | 811 |
787 CHECK(!stream_.get()); | 812 CHECK(!stream_.get()); |
788 | 813 |
789 if (!connection_->socket() && !existing_spdy_session_) | |
790 HACKCrashHereToDebug80095(); | |
791 | |
792 bool direct = true; | 814 bool direct = true; |
793 HostPortProxyPair pair(origin_, proxy_server); | 815 HostPortProxyPair pair(origin_, proxy_server); |
794 if (IsHttpsProxyAndHttpUrl()) { | 816 if (IsHttpsProxyAndHttpUrl()) { |
795 // If we don't have a direct SPDY session, and we're using an HTTPS | 817 // If we don't have a direct SPDY session, and we're using an HTTPS |
796 // proxy, then we might have a SPDY session to the proxy. | 818 // proxy, then we might have a SPDY session to the proxy. |
797 pair = HostPortProxyPair(proxy_server.host_port_pair(), | 819 pair = HostPortProxyPair(proxy_server.host_port_pair(), |
798 ProxyServer::Direct()); | 820 ProxyServer::Direct()); |
799 direct = false; | 821 direct = false; |
800 } | 822 } |
801 | 823 |
802 scoped_refptr<SpdySession> spdy_session; | 824 scoped_refptr<SpdySession> spdy_session; |
803 if (existing_spdy_session_) { | 825 if (existing_spdy_session_) { |
804 // We picked up an existing session, so we don't need our socket. | 826 // We picked up an existing session, so we don't need our socket. |
805 if (connection_->socket()) | 827 if (connection_->socket()) |
806 connection_->socket()->Disconnect(); | 828 connection_->socket()->Disconnect(); |
807 connection_->Reset(); | 829 connection_->Reset(); |
808 spdy_session.swap(existing_spdy_session_); | 830 spdy_session.swap(existing_spdy_session_); |
809 } else { | 831 } else { |
810 SpdySessionPool* spdy_pool = session_->spdy_session_pool(); | 832 SpdySessionPool* spdy_pool = session_->spdy_session_pool(); |
811 spdy_session = spdy_pool->GetIfExists(pair, net_log_); | 833 spdy_session = spdy_pool->GetIfExists(pair, net_log_); |
812 if (!spdy_session) { | 834 if (!spdy_session) { |
813 // SPDY can be negotiated using the TLS next protocol negotiation (NPN) | |
814 // extension, or just directly using SSL. Either way, |connection_| must | |
815 // contain an SSLClientSocket. | |
816 if (!connection_->socket()) | |
817 HACKCrashHereToDebug80095(); | |
818 | |
819 int error = spdy_pool->GetSpdySessionFromSocket( | 835 int error = spdy_pool->GetSpdySessionFromSocket( |
820 pair, connection_.release(), net_log_, spdy_certificate_error_, | 836 pair, connection_.release(), net_log_, spdy_certificate_error_, |
821 &new_spdy_session_, using_ssl_); | 837 &new_spdy_session_, using_ssl_); |
822 if (error != OK) | 838 if (error != OK) |
823 return error; | 839 return error; |
824 spdy_session_direct_ = direct; | 840 spdy_session_direct_ = direct; |
825 return OK; | 841 return OK; |
826 } | 842 } |
827 } | 843 } |
828 | 844 |
(...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1079 | 1095 |
1080 bool HttpStreamFactoryImpl::Job::IsPreconnecting() const { | 1096 bool HttpStreamFactoryImpl::Job::IsPreconnecting() const { |
1081 DCHECK_GE(num_streams_, 0); | 1097 DCHECK_GE(num_streams_, 0); |
1082 return num_streams_ > 0; | 1098 return num_streams_ > 0; |
1083 } | 1099 } |
1084 | 1100 |
1085 bool HttpStreamFactoryImpl::Job::IsOrphaned() const { | 1101 bool HttpStreamFactoryImpl::Job::IsOrphaned() const { |
1086 return !IsPreconnecting() && !request_; | 1102 return !IsPreconnecting() && !request_; |
1087 } | 1103 } |
1088 | 1104 |
1089 #if defined(OS_WIN) | 1105 bool HttpStreamFactoryImpl::Job::IsRequestEligibleForPipelining() const { |
1090 #pragma warning (disable: 4748) | 1106 if (!HttpStreamFactory::http_pipelining_enabled()) { |
1091 #pragma optimize( "", off ) | 1107 return false; |
1092 #endif | |
1093 | |
1094 void HttpStreamFactoryImpl::Job::HACKCrashHereToDebug80095() { | |
1095 // If we enter this code path, then we'll cause a crash later in | |
1096 // DoCreateStream(). Crash now and figure out what happened: | |
1097 // http://crbug.com/80095. | |
1098 GURL url = original_url_.get() ? *original_url_ : request_info_.url; | |
1099 bool using_ssl = using_ssl_; | |
1100 bool using_spdy = using_spdy_; | |
1101 char url_buf[512]; | |
1102 base::strlcpy(url_buf, url.spec().data(), arraysize(url_buf)); | |
1103 | |
1104 // Note that these local variables have their addresses referenced to | |
1105 // prevent the compiler from optimizing them away. | |
1106 if (using_spdy) { | |
1107 LOG(FATAL) << "Crashing here because willchan doesn't know why we're " | |
1108 << "crashing later. Sorry! I'll give you a cookie later. " | |
1109 << "Cheers mate!\n" | |
1110 << "url[" << &url << "]: " << url << "\n" | |
1111 << "using_ssl[" << &using_ssl << "]: " | |
1112 << (using_ssl ? "true\n" : "false\n") | |
1113 << "using_spdy[" << &using_spdy << "]: " | |
1114 << (using_spdy ? "true\n" : "false\n"); | |
1115 } else { | |
1116 LOG(FATAL) << "Crashing here because willchan doesn't know why we're " | |
1117 << "crashing later. Sorry! I'll give you a cookie later. " | |
1118 << "Cheers mate!\n" | |
1119 << "url[" << &url << "]: " << url << "\n" | |
1120 << "using_ssl[" << &using_ssl << "]: " | |
1121 << (using_ssl ? "true\n" : "false\n") | |
1122 << "using_spdy[" << &using_spdy << "]: " | |
1123 << (using_spdy ? "true\n" : "false\n"); | |
1124 } | 1108 } |
| 1109 if (IsPreconnecting() || !request_) { |
| 1110 return false; |
| 1111 } |
| 1112 if (using_ssl_) { |
| 1113 return false; |
| 1114 } |
| 1115 return request_info_.method == "GET" || request_info_.method == "HEAD"; |
1125 } | 1116 } |
1126 | 1117 |
1127 #if defined(OS_WIN) | |
1128 #pragma optimize( "", on ) | |
1129 #pragma warning (default: 4748) | |
1130 #endif | |
1131 | |
1132 } // namespace net | 1118 } // namespace net |
OLD | NEW |