1// Copyright (c) 2010 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_stream_factory_impl.h" 6 7#include "base/string_number_conversions.h" 8#include "base/stl_util-inl.h" 9#include "googleurl/src/gurl.h" 10#include "net/base/net_log.h" 11#include "net/base/net_util.h" 12#include "net/http/http_network_session.h" 13#include "net/http/http_stream_factory_impl_job.h" 14#include "net/http/http_stream_factory_impl_request.h" 15#include "net/spdy/spdy_http_stream.h" 16 17namespace net { 18 19namespace { 20 21GURL UpgradeUrlToHttps(const GURL& original_url) { 22 GURL::Replacements replacements; 23 // new_sheme and new_port need to be in scope here because GURL::Replacements 24 // references the memory contained by them directly. 25 const std::string new_scheme = "https"; 26 const std::string new_port = base::IntToString(443); 27 replacements.SetSchemeStr(new_scheme); 28 replacements.SetPortStr(new_port); 29 return original_url.ReplaceComponents(replacements); 30} 31 32} // namespace 33 34HttpStreamFactoryImpl::HttpStreamFactoryImpl(HttpNetworkSession* session) 35 : session_(session) {} 36 37HttpStreamFactoryImpl::~HttpStreamFactoryImpl() { 38 DCHECK(request_map_.empty()); 39 DCHECK(spdy_session_request_map_.empty()); 40 41 std::set<const Job*> tmp_job_set; 42 tmp_job_set.swap(orphaned_job_set_); 43 STLDeleteContainerPointers(tmp_job_set.begin(), tmp_job_set.end()); 44 DCHECK(orphaned_job_set_.empty()); 45 46 tmp_job_set.clear(); 47 tmp_job_set.swap(preconnect_job_set_); 48 STLDeleteContainerPointers(tmp_job_set.begin(), tmp_job_set.end()); 49 DCHECK(preconnect_job_set_.empty()); 50} 51 52HttpStreamRequest* HttpStreamFactoryImpl::RequestStream( 53 const HttpRequestInfo& request_info, 54 const SSLConfig& ssl_config, 55 HttpStreamRequest::Delegate* delegate, 56 const BoundNetLog& net_log) { 57 Request* request = new Request(request_info.url, this, delegate, net_log); 58 59 GURL alternate_url; 60 bool has_alternate_protocol = 61 GetAlternateProtocolRequestFor(request_info.url, &alternate_url); 62 Job* alternate_job = NULL; 63 if (has_alternate_protocol) { 64 HttpRequestInfo alternate_request_info = request_info; 65 alternate_request_info.url = alternate_url; 66 alternate_job = 67 new Job(this, session_, alternate_request_info, ssl_config, net_log); 68 request->AttachJob(alternate_job); 69 alternate_job->MarkAsAlternate(request_info.url); 70 } 71 72 Job* job = new Job(this, session_, request_info, ssl_config, net_log); 73 request->AttachJob(job); 74 if (alternate_job) { 75 job->WaitFor(alternate_job); 76 // Make sure to wait until we call WaitFor(), before starting 77 // |alternate_job|, otherwise |alternate_job| will not notify |job| 78 // appropriately. 79 alternate_job->Start(request); 80 } 81 // Even if |alternate_job| has already finished, it won't have notified the 82 // request yet, since we defer that to the next iteration of the MessageLoop, 83 // so starting |job| is always safe. 84 job->Start(request); 85 return request; 86} 87 88void HttpStreamFactoryImpl::PreconnectStreams( 89 int num_streams, 90 const HttpRequestInfo& request_info, 91 const SSLConfig& ssl_config, 92 const BoundNetLog& net_log) { 93 GURL alternate_url; 94 bool has_alternate_protocol = 95 GetAlternateProtocolRequestFor(request_info.url, &alternate_url); 96 Job* job = NULL; 97 if (has_alternate_protocol) { 98 HttpRequestInfo alternate_request_info = request_info; 99 alternate_request_info.url = alternate_url; 100 job = new Job(this, session_, alternate_request_info, ssl_config, net_log); 101 job->MarkAsAlternate(request_info.url); 102 } else { 103 job = new Job(this, session_, request_info, ssl_config, net_log); 104 } 105 preconnect_job_set_.insert(job); 106 job->Preconnect(num_streams); 107} 108 109void HttpStreamFactoryImpl::AddTLSIntolerantServer(const HostPortPair& server) { 110 tls_intolerant_servers_.insert(server); 111} 112 113bool HttpStreamFactoryImpl::IsTLSIntolerantServer( 114 const HostPortPair& server) const { 115 return ContainsKey(tls_intolerant_servers_, server); 116} 117 118bool HttpStreamFactoryImpl::GetAlternateProtocolRequestFor( 119 const GURL& original_url, 120 GURL* alternate_url) const { 121 if (!spdy_enabled()) 122 return false; 123 124 if (!use_alternate_protocols()) 125 return false; 126 127 HostPortPair origin = HostPortPair(original_url.HostNoBrackets(), 128 original_url.EffectiveIntPort()); 129 130 const HttpAlternateProtocols& alternate_protocols = 131 session_->alternate_protocols(); 132 if (!alternate_protocols.HasAlternateProtocolFor(origin)) 133 return false; 134 135 HttpAlternateProtocols::PortProtocolPair alternate = 136 alternate_protocols.GetAlternateProtocolFor(origin); 137 if (alternate.protocol == HttpAlternateProtocols::BROKEN) 138 return false; 139 140 DCHECK_LE(HttpAlternateProtocols::NPN_SPDY_1, alternate.protocol); 141 DCHECK_GT(HttpAlternateProtocols::NUM_ALTERNATE_PROTOCOLS, 142 alternate.protocol); 143 144 if (alternate.protocol != HttpAlternateProtocols::NPN_SPDY_2) 145 return false; 146 147 origin.set_port(alternate.port); 148 if (HttpStreamFactory::HasSpdyExclusion(origin)) 149 return false; 150 151 *alternate_url = UpgradeUrlToHttps(original_url); 152 return true; 153} 154 155void HttpStreamFactoryImpl::OrphanJob(Job* job, const Request* request) { 156 DCHECK(ContainsKey(request_map_, job)); 157 DCHECK_EQ(request_map_[job], request); 158 DCHECK(!ContainsKey(orphaned_job_set_, job)); 159 160 request_map_.erase(job); 161 162 orphaned_job_set_.insert(job); 163 job->Orphan(request); 164} 165 166void HttpStreamFactoryImpl::OnSpdySessionReady( 167 scoped_refptr<SpdySession> spdy_session, 168 bool direct, 169 const SSLConfig& used_ssl_config, 170 const ProxyInfo& used_proxy_info, 171 bool was_npn_negotiated, 172 bool using_spdy, 173 const NetLog::Source& source) { 174 const HostPortProxyPair& spdy_session_key = 175 spdy_session->host_port_proxy_pair(); 176 while (!spdy_session->IsClosed()) { 177 // Each iteration may empty out the RequestSet for |spdy_session_key_ in 178 // |spdy_session_request_map_|. So each time, check for RequestSet and use 179 // the first one. 180 // 181 // TODO(willchan): If it's important, switch RequestSet out for a FIFO 182 // pqueue (Order by priority first, then FIFO within same priority). Unclear 183 // that it matters here. 184 if (!ContainsKey(spdy_session_request_map_, spdy_session_key)) 185 break; 186 Request* request = *spdy_session_request_map_[spdy_session_key].begin(); 187 request->Complete(was_npn_negotiated, 188 using_spdy, 189 source); 190 bool use_relative_url = direct || request->url().SchemeIs("https"); 191 request->OnStreamReady(NULL, used_ssl_config, used_proxy_info, 192 new SpdyHttpStream(spdy_session, use_relative_url)); 193 } 194 // TODO(mbelshe): Alert other valid requests. 195} 196 197void HttpStreamFactoryImpl::OnOrphanedJobComplete(const Job* job) { 198 orphaned_job_set_.erase(job); 199 delete job; 200} 201 202void HttpStreamFactoryImpl::OnPreconnectsComplete(const Job* job) { 203 preconnect_job_set_.erase(job); 204 delete job; 205 OnPreconnectsCompleteInternal(); 206} 207 208} // namespace net 209