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 3// found in the LICENSE file. 4 5#include "net/http/http_stream_factory_impl_request.h" 6 7#include "base/logging.h" 8#include "base/stl_util-inl.h" 9#include "net/http/http_stream_factory_impl_job.h" 10#include "net/spdy/spdy_http_stream.h" 11#include "net/spdy/spdy_session.h" 12 13namespace net { 14 15HttpStreamFactoryImpl::Request::Request(const GURL& url, 16 HttpStreamFactoryImpl* factory, 17 HttpStreamRequest::Delegate* delegate, 18 const BoundNetLog& net_log) 19 : url_(url), 20 factory_(factory), 21 delegate_(delegate), 22 net_log_(net_log), 23 completed_(false), 24 was_npn_negotiated_(false), 25 using_spdy_(false) { 26 DCHECK(factory_); 27 DCHECK(delegate_); 28 29 net_log_.BeginEvent(NetLog::TYPE_HTTP_STREAM_REQUEST, NULL); 30} 31 32HttpStreamFactoryImpl::Request::~Request() { 33 if (bound_job_.get()) 34 DCHECK(jobs_.empty()); 35 else 36 DCHECK(!jobs_.empty()); 37 38 net_log_.EndEvent(NetLog::TYPE_HTTP_STREAM_REQUEST, NULL); 39 40 for (std::set<Job*>::iterator it = jobs_.begin(); it != jobs_.end(); ++it) 41 factory_->request_map_.erase(*it); 42 43 STLDeleteElements(&jobs_); 44 45 RemoveRequestFromSpdySessionRequestMap(); 46} 47 48void HttpStreamFactoryImpl::Request::SetSpdySessionKey( 49 const HostPortProxyPair& spdy_session_key) { 50 DCHECK(!spdy_session_key_.get()); 51 spdy_session_key_.reset(new HostPortProxyPair(spdy_session_key)); 52 RequestSet& request_set = 53 factory_->spdy_session_request_map_[spdy_session_key]; 54 DCHECK(!ContainsKey(request_set, this)); 55 request_set.insert(this); 56} 57 58void HttpStreamFactoryImpl::Request::AttachJob(Job* job) { 59 DCHECK(job); 60 jobs_.insert(job); 61 factory_->request_map_[job] = this; 62} 63 64void HttpStreamFactoryImpl::Request::Complete( 65 bool was_npn_negotiated, 66 bool using_spdy, 67 const NetLog::Source& job_source) { 68 DCHECK(!completed_); 69 completed_ = true; 70 was_npn_negotiated_ = was_npn_negotiated; 71 using_spdy_ = using_spdy; 72 net_log_.AddEvent( 73 NetLog::TYPE_HTTP_STREAM_REQUEST_BOUND_TO_JOB, 74 make_scoped_refptr(new NetLogSourceParameter( 75 "source_dependency", job_source))); 76} 77 78void HttpStreamFactoryImpl::Request::OnStreamReady( 79 Job* job, 80 const SSLConfig& used_ssl_config, 81 const ProxyInfo& used_proxy_info, 82 HttpStream* stream) { 83 DCHECK(stream); 84 DCHECK(completed_); 85 86 // |job| should only be NULL if we're being serviced by a late bound 87 // SpdySession (one that was not created by a job in our |jobs_| set). 88 if (!job) { 89 DCHECK(!bound_job_.get()); 90 DCHECK(!jobs_.empty()); 91 // NOTE(willchan): We do *NOT* call OrphanJobs() here. The reason is because 92 // we *WANT* to cancel the unnecessary Jobs from other requests if another 93 // Job completes first. 94 // TODO(mbelshe): Revisit this when we implement ip connection pooling of 95 // SpdySessions. Do we want to orphan the jobs for a different hostname so 96 // they complete? Or do we want to prevent connecting a new SpdySession if 97 // we've already got one available for a different hostname where the ip 98 // address matches up? 99 } else if (!bound_job_.get()) { 100 // We may have other jobs in |jobs_|. For example, if we start multiple jobs 101 // for Alternate-Protocol. 102 OrphanJobsExcept(job); 103 } else { 104 DCHECK(jobs_.empty()); 105 } 106 delegate_->OnStreamReady(used_ssl_config, used_proxy_info, stream); 107} 108 109void HttpStreamFactoryImpl::Request::OnStreamFailed( 110 Job* job, 111 int status, 112 const SSLConfig& used_ssl_config) { 113 DCHECK_NE(OK, status); 114 if (!bound_job_.get()) { 115 // Hey, we've got other jobs! Maybe one of them will succeed, let's just 116 // ignore this failure. 117 if (jobs_.size() > 1) { 118 jobs_.erase(job); 119 factory_->request_map_.erase(job); 120 delete job; 121 return; 122 } else { 123 bound_job_.reset(job); 124 jobs_.erase(job); 125 DCHECK(jobs_.empty()); 126 factory_->request_map_.erase(job); 127 } 128 } else { 129 DCHECK(jobs_.empty()); 130 } 131 delegate_->OnStreamFailed(status, used_ssl_config); 132} 133 134void HttpStreamFactoryImpl::Request::OnCertificateError( 135 Job* job, 136 int status, 137 const SSLConfig& used_ssl_config, 138 const SSLInfo& ssl_info) { 139 DCHECK_NE(OK, status); 140 if (!bound_job_.get()) 141 OrphanJobsExcept(job); 142 else 143 DCHECK(jobs_.empty()); 144 delegate_->OnCertificateError(status, used_ssl_config, ssl_info); 145} 146 147void HttpStreamFactoryImpl::Request::OnNeedsProxyAuth( 148 Job* job, 149 const HttpResponseInfo& proxy_response, 150 const SSLConfig& used_ssl_config, 151 const ProxyInfo& used_proxy_info, 152 HttpAuthController* auth_controller) { 153 if (!bound_job_.get()) 154 OrphanJobsExcept(job); 155 else 156 DCHECK(jobs_.empty()); 157 delegate_->OnNeedsProxyAuth( 158 proxy_response, used_ssl_config, used_proxy_info, auth_controller); 159} 160 161void HttpStreamFactoryImpl::Request::OnNeedsClientAuth( 162 Job* job, 163 const SSLConfig& used_ssl_config, 164 SSLCertRequestInfo* cert_info) { 165 if (!bound_job_.get()) 166 OrphanJobsExcept(job); 167 else 168 DCHECK(jobs_.empty()); 169 delegate_->OnNeedsClientAuth(used_ssl_config, cert_info); 170} 171 172void HttpStreamFactoryImpl::Request::OnHttpsProxyTunnelResponse( 173 Job *job, 174 const HttpResponseInfo& response_info, 175 const SSLConfig& used_ssl_config, 176 const ProxyInfo& used_proxy_info, 177 HttpStream* stream) { 178 if (!bound_job_.get()) 179 OrphanJobsExcept(job); 180 else 181 DCHECK(jobs_.empty()); 182 delegate_->OnHttpsProxyTunnelResponse( 183 response_info, used_ssl_config, used_proxy_info, stream); 184} 185 186int HttpStreamFactoryImpl::Request::RestartTunnelWithProxyAuth( 187 const string16& username, 188 const string16& password) { 189 DCHECK(bound_job_.get()); 190 return bound_job_->RestartTunnelWithProxyAuth(username, password); 191} 192 193LoadState HttpStreamFactoryImpl::Request::GetLoadState() const { 194 if (bound_job_.get()) 195 return bound_job_->GetLoadState(); 196 DCHECK(!jobs_.empty()); 197 198 // Just pick the first one. 199 return (*jobs_.begin())->GetLoadState(); 200} 201 202bool HttpStreamFactoryImpl::Request::was_npn_negotiated() const { 203 DCHECK(completed_); 204 return was_npn_negotiated_; 205} 206 207bool HttpStreamFactoryImpl::Request::using_spdy() const { 208 DCHECK(completed_); 209 return using_spdy_; 210} 211 212void 213HttpStreamFactoryImpl::Request::RemoveRequestFromSpdySessionRequestMap() { 214 if (spdy_session_key_.get()) { 215 SpdySessionRequestMap& spdy_session_request_map = 216 factory_->spdy_session_request_map_; 217 DCHECK(ContainsKey(spdy_session_request_map, *spdy_session_key_)); 218 RequestSet& request_set = 219 spdy_session_request_map[*spdy_session_key_]; 220 DCHECK(ContainsKey(request_set, this)); 221 request_set.erase(this); 222 if (request_set.empty()) 223 spdy_session_request_map.erase(*spdy_session_key_); 224 spdy_session_key_.reset(); 225 } 226} 227 228void HttpStreamFactoryImpl::Request::OnSpdySessionReady( 229 Job* job, 230 scoped_refptr<SpdySession> spdy_session, 231 bool direct) { 232 DCHECK(job); 233 DCHECK(job->using_spdy()); 234 235 // The first case is the usual case. 236 if (!bound_job_.get()) { 237 OrphanJobsExcept(job); 238 } else { // This is the case for HTTPS proxy tunneling. 239 DCHECK_EQ(bound_job_.get(), job); 240 DCHECK(jobs_.empty()); 241 } 242 243 // Cache these values in case the job gets deleted. 244 const SSLConfig used_ssl_config = job->ssl_config(); 245 const ProxyInfo used_proxy_info = job->proxy_info(); 246 const bool was_npn_negotiated = job->was_npn_negotiated(); 247 const bool using_spdy = job->using_spdy(); 248 const NetLog::Source source = job->net_log().source(); 249 250 Complete(was_npn_negotiated, 251 using_spdy, 252 source); 253 254 // Cache this so we can still use it if the request is deleted. 255 HttpStreamFactoryImpl* factory = factory_; 256 257 bool use_relative_url = direct || url().SchemeIs("https"); 258 delegate_->OnStreamReady( 259 job->ssl_config(), 260 job->proxy_info(), 261 new SpdyHttpStream(spdy_session, use_relative_url)); 262 // |this| may be deleted after this point. 263 factory->OnSpdySessionReady( 264 spdy_session, direct, used_ssl_config, used_proxy_info, 265 was_npn_negotiated, using_spdy, source); 266} 267 268void HttpStreamFactoryImpl::Request::OrphanJobsExcept(Job* job) { 269 DCHECK(job); 270 DCHECK(!bound_job_.get()); 271 DCHECK(ContainsKey(jobs_, job)); 272 bound_job_.reset(job); 273 jobs_.erase(job); 274 factory_->request_map_.erase(job); 275 276 OrphanJobs(); 277} 278 279void HttpStreamFactoryImpl::Request::OrphanJobs() { 280 RemoveRequestFromSpdySessionRequestMap(); 281 282 std::set<Job*> tmp; 283 tmp.swap(jobs_); 284 285 for (std::set<Job*>::iterator it = tmp.begin(); it != tmp.end(); ++it) 286 factory_->OrphanJob(*it, this); 287} 288 289} // namespace net 290