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_stream_factory_impl_request.h" 6 7 #include "base/callback.h" 8 #include "base/logging.h" 9 #include "base/stl_util.h" 10 #include "net/http/http_stream_factory_impl_job.h" 11 #include "net/spdy/spdy_http_stream.h" 12 #include "net/spdy/spdy_session.h" 13 14 namespace net { 15 16 HttpStreamFactoryImpl::Request::Request( 17 const GURL& url, 18 HttpStreamFactoryImpl* factory, 19 HttpStreamRequest::Delegate* delegate, 20 WebSocketHandshakeStreamBase::CreateHelper* 21 websocket_handshake_stream_create_helper, 22 const BoundNetLog& net_log) 23 : url_(url), 24 factory_(factory), 25 websocket_handshake_stream_create_helper_( 26 websocket_handshake_stream_create_helper), 27 delegate_(delegate), 28 net_log_(net_log), 29 completed_(false), 30 was_npn_negotiated_(false), 31 protocol_negotiated_(kProtoUnknown), 32 using_spdy_(false) { 33 DCHECK(factory_); 34 DCHECK(delegate_); 35 36 net_log_.BeginEvent(NetLog::TYPE_HTTP_STREAM_REQUEST); 37 } 38 39 HttpStreamFactoryImpl::Request::~Request() { 40 if (bound_job_.get()) 41 DCHECK(jobs_.empty()); 42 else 43 DCHECK(!jobs_.empty()); 44 45 net_log_.EndEvent(NetLog::TYPE_HTTP_STREAM_REQUEST); 46 47 for (std::set<Job*>::iterator it = jobs_.begin(); it != jobs_.end(); ++it) 48 factory_->request_map_.erase(*it); 49 50 RemoveRequestFromSpdySessionRequestMap(); 51 52 STLDeleteElements(&jobs_); 53 } 54 55 void HttpStreamFactoryImpl::Request::SetSpdySessionKey( 56 const SpdySessionKey& spdy_session_key) { 57 CHECK(!spdy_session_key_.get()); 58 spdy_session_key_.reset(new SpdySessionKey(spdy_session_key)); 59 RequestSet& request_set = 60 factory_->spdy_session_request_map_[spdy_session_key]; 61 DCHECK(!ContainsKey(request_set, this)); 62 request_set.insert(this); 63 } 64 65 void HttpStreamFactoryImpl::Request::AttachJob(Job* job) { 66 DCHECK(job); 67 jobs_.insert(job); 68 factory_->request_map_[job] = this; 69 } 70 71 void HttpStreamFactoryImpl::Request::Complete( 72 bool was_npn_negotiated, 73 NextProto protocol_negotiated, 74 bool using_spdy, 75 const BoundNetLog& job_net_log) { 76 DCHECK(!completed_); 77 completed_ = true; 78 was_npn_negotiated_ = was_npn_negotiated; 79 protocol_negotiated_ = protocol_negotiated; 80 using_spdy_ = using_spdy; 81 net_log_.AddEvent( 82 NetLog::TYPE_HTTP_STREAM_REQUEST_BOUND_TO_JOB, 83 job_net_log.source().ToEventParametersCallback()); 84 job_net_log.AddEvent( 85 NetLog::TYPE_HTTP_STREAM_JOB_BOUND_TO_REQUEST, 86 net_log_.source().ToEventParametersCallback()); 87 } 88 89 void HttpStreamFactoryImpl::Request::OnStreamReady( 90 Job* job, 91 const SSLConfig& used_ssl_config, 92 const ProxyInfo& used_proxy_info, 93 HttpStreamBase* stream) { 94 DCHECK(!factory_->for_websockets_); 95 DCHECK(stream); 96 DCHECK(completed_); 97 98 OnJobSucceeded(job); 99 delegate_->OnStreamReady(used_ssl_config, used_proxy_info, stream); 100 } 101 102 void HttpStreamFactoryImpl::Request::OnWebSocketHandshakeStreamReady( 103 Job* job, 104 const SSLConfig& used_ssl_config, 105 const ProxyInfo& used_proxy_info, 106 WebSocketHandshakeStreamBase* stream) { 107 DCHECK(factory_->for_websockets_); 108 DCHECK(stream); 109 DCHECK(completed_); 110 111 OnJobSucceeded(job); 112 delegate_->OnWebSocketHandshakeStreamReady( 113 used_ssl_config, used_proxy_info, stream); 114 } 115 116 void HttpStreamFactoryImpl::Request::OnStreamFailed( 117 Job* job, 118 int status, 119 const SSLConfig& used_ssl_config) { 120 DCHECK_NE(OK, status); 121 DCHECK(job); 122 if (!bound_job_.get()) { 123 // Hey, we've got other jobs! Maybe one of them will succeed, let's just 124 // ignore this failure. 125 if (jobs_.size() > 1) { 126 jobs_.erase(job); 127 factory_->request_map_.erase(job); 128 // Notify all the other jobs that this one failed. 129 for (std::set<Job*>::iterator it = jobs_.begin(); it != jobs_.end(); ++it) 130 (*it)->MarkOtherJobComplete(*job); 131 delete job; 132 return; 133 } else { 134 bound_job_.reset(job); 135 jobs_.erase(job); 136 DCHECK(jobs_.empty()); 137 factory_->request_map_.erase(job); 138 } 139 } else { 140 DCHECK(jobs_.empty()); 141 } 142 delegate_->OnStreamFailed(status, used_ssl_config); 143 } 144 145 void HttpStreamFactoryImpl::Request::OnCertificateError( 146 Job* job, 147 int status, 148 const SSLConfig& used_ssl_config, 149 const SSLInfo& ssl_info) { 150 DCHECK_NE(OK, status); 151 if (!bound_job_.get()) 152 OrphanJobsExcept(job); 153 else 154 DCHECK(jobs_.empty()); 155 delegate_->OnCertificateError(status, used_ssl_config, ssl_info); 156 } 157 158 void HttpStreamFactoryImpl::Request::OnNeedsProxyAuth( 159 Job* job, 160 const HttpResponseInfo& proxy_response, 161 const SSLConfig& used_ssl_config, 162 const ProxyInfo& used_proxy_info, 163 HttpAuthController* auth_controller) { 164 if (!bound_job_.get()) 165 OrphanJobsExcept(job); 166 else 167 DCHECK(jobs_.empty()); 168 delegate_->OnNeedsProxyAuth( 169 proxy_response, used_ssl_config, used_proxy_info, auth_controller); 170 } 171 172 void HttpStreamFactoryImpl::Request::OnNeedsClientAuth( 173 Job* job, 174 const SSLConfig& used_ssl_config, 175 SSLCertRequestInfo* cert_info) { 176 if (!bound_job_.get()) 177 OrphanJobsExcept(job); 178 else 179 DCHECK(jobs_.empty()); 180 delegate_->OnNeedsClientAuth(used_ssl_config, cert_info); 181 } 182 183 void HttpStreamFactoryImpl::Request::OnHttpsProxyTunnelResponse( 184 Job *job, 185 const HttpResponseInfo& response_info, 186 const SSLConfig& used_ssl_config, 187 const ProxyInfo& used_proxy_info, 188 HttpStreamBase* stream) { 189 if (!bound_job_.get()) 190 OrphanJobsExcept(job); 191 else 192 DCHECK(jobs_.empty()); 193 delegate_->OnHttpsProxyTunnelResponse( 194 response_info, used_ssl_config, used_proxy_info, stream); 195 } 196 197 int HttpStreamFactoryImpl::Request::RestartTunnelWithProxyAuth( 198 const AuthCredentials& credentials) { 199 DCHECK(bound_job_.get()); 200 return bound_job_->RestartTunnelWithProxyAuth(credentials); 201 } 202 203 void HttpStreamFactoryImpl::Request::SetPriority(RequestPriority priority) { 204 for (std::set<HttpStreamFactoryImpl::Job*>::const_iterator it = jobs_.begin(); 205 it != jobs_.end(); ++it) { 206 (*it)->SetPriority(priority); 207 } 208 if (bound_job_) 209 bound_job_->SetPriority(priority); 210 } 211 212 LoadState HttpStreamFactoryImpl::Request::GetLoadState() const { 213 if (bound_job_.get()) 214 return bound_job_->GetLoadState(); 215 DCHECK(!jobs_.empty()); 216 217 // Just pick the first one. 218 return (*jobs_.begin())->GetLoadState(); 219 } 220 221 bool HttpStreamFactoryImpl::Request::was_npn_negotiated() const { 222 DCHECK(completed_); 223 return was_npn_negotiated_; 224 } 225 226 NextProto HttpStreamFactoryImpl::Request::protocol_negotiated() 227 const { 228 DCHECK(completed_); 229 return protocol_negotiated_; 230 } 231 232 bool HttpStreamFactoryImpl::Request::using_spdy() const { 233 DCHECK(completed_); 234 return using_spdy_; 235 } 236 237 void 238 HttpStreamFactoryImpl::Request::RemoveRequestFromSpdySessionRequestMap() { 239 if (spdy_session_key_.get()) { 240 SpdySessionRequestMap& spdy_session_request_map = 241 factory_->spdy_session_request_map_; 242 DCHECK(ContainsKey(spdy_session_request_map, *spdy_session_key_)); 243 RequestSet& request_set = 244 spdy_session_request_map[*spdy_session_key_]; 245 DCHECK(ContainsKey(request_set, this)); 246 request_set.erase(this); 247 if (request_set.empty()) 248 spdy_session_request_map.erase(*spdy_session_key_); 249 spdy_session_key_.reset(); 250 } 251 } 252 253 bool HttpStreamFactoryImpl::Request::HasSpdySessionKey() const { 254 return spdy_session_key_.get() != NULL; 255 } 256 257 // TODO(jgraettinger): Currently, HttpStreamFactoryImpl::Job notifies a 258 // Request that the session is ready, which in turn notifies it's delegate, 259 // and then it notifies HttpStreamFactoryImpl so that /other/ requests may 260 // be woken, but only if the spdy_session is still okay. This is tough to grok. 261 // Instead, see if Job can notify HttpStreamFactoryImpl only, and have one 262 // path for notifying any requests waiting for the session (including the 263 // request which spawned it). 264 void HttpStreamFactoryImpl::Request::OnNewSpdySessionReady( 265 Job* job, 266 scoped_ptr<HttpStream> stream, 267 const base::WeakPtr<SpdySession>& spdy_session, 268 bool direct) { 269 DCHECK(job); 270 DCHECK(job->using_spdy()); 271 272 // Note: |spdy_session| may be NULL. In that case, |delegate_| should still 273 // receive |stream| so the error propogates up correctly, however there is no 274 // point in broadcasting |spdy_session| to other requests. 275 276 // The first case is the usual case. 277 if (!bound_job_.get()) { 278 OrphanJobsExcept(job); 279 } else { // This is the case for HTTPS proxy tunneling. 280 DCHECK_EQ(bound_job_.get(), job); 281 DCHECK(jobs_.empty()); 282 } 283 284 // Cache these values in case the job gets deleted. 285 const SSLConfig used_ssl_config = job->server_ssl_config(); 286 const ProxyInfo used_proxy_info = job->proxy_info(); 287 const bool was_npn_negotiated = job->was_npn_negotiated(); 288 const NextProto protocol_negotiated = 289 job->protocol_negotiated(); 290 const bool using_spdy = job->using_spdy(); 291 const BoundNetLog net_log = job->net_log(); 292 293 Complete(was_npn_negotiated, protocol_negotiated, using_spdy, net_log); 294 295 // Cache this so we can still use it if the request is deleted. 296 HttpStreamFactoryImpl* factory = factory_; 297 if (factory->for_websockets_) { 298 // TODO(ricea): Re-instate this code when WebSockets over SPDY is 299 // implemented. 300 NOTREACHED(); 301 } else { 302 delegate_->OnStreamReady(job->server_ssl_config(), job->proxy_info(), 303 stream.release()); 304 } 305 // |this| may be deleted after this point. 306 if (spdy_session && spdy_session->IsAvailable()) { 307 factory->OnNewSpdySessionReady(spdy_session, 308 direct, 309 used_ssl_config, 310 used_proxy_info, 311 was_npn_negotiated, 312 protocol_negotiated, 313 using_spdy, 314 net_log); 315 } 316 } 317 318 void HttpStreamFactoryImpl::Request::OrphanJobsExcept(Job* job) { 319 DCHECK(job); 320 DCHECK(!bound_job_.get()); 321 DCHECK(ContainsKey(jobs_, job)); 322 bound_job_.reset(job); 323 jobs_.erase(job); 324 factory_->request_map_.erase(job); 325 326 OrphanJobs(); 327 } 328 329 void HttpStreamFactoryImpl::Request::OrphanJobs() { 330 RemoveRequestFromSpdySessionRequestMap(); 331 332 std::set<Job*> tmp; 333 tmp.swap(jobs_); 334 335 for (std::set<Job*>::iterator it = tmp.begin(); it != tmp.end(); ++it) 336 factory_->OrphanJob(*it, this); 337 } 338 339 void HttpStreamFactoryImpl::Request::OnJobSucceeded(Job* job) { 340 // |job| should only be NULL if we're being serviced by a late bound 341 // SpdySession (one that was not created by a job in our |jobs_| set). 342 if (!job) { 343 DCHECK(!bound_job_.get()); 344 DCHECK(!jobs_.empty()); 345 // NOTE(willchan): We do *NOT* call OrphanJobs() here. The reason is because 346 // we *WANT* to cancel the unnecessary Jobs from other requests if another 347 // Job completes first. 348 // TODO(mbelshe): Revisit this when we implement ip connection pooling of 349 // SpdySessions. Do we want to orphan the jobs for a different hostname so 350 // they complete? Or do we want to prevent connecting a new SpdySession if 351 // we've already got one available for a different hostname where the ip 352 // address matches up? 353 return; 354 } 355 if (!bound_job_.get()) { 356 if (jobs_.size() > 1) 357 job->ReportJobSuccededForRequest(); 358 // Notify all the other jobs that this one succeeded. 359 for (std::set<Job*>::iterator it = jobs_.begin(); it != jobs_.end(); ++it) { 360 if (*it != job) { 361 (*it)->MarkOtherJobComplete(*job); 362 } 363 } 364 // We may have other jobs in |jobs_|. For example, if we start multiple jobs 365 // for Alternate-Protocol. 366 OrphanJobsExcept(job); 367 return; 368 } 369 DCHECK(jobs_.empty()); 370 } 371 372 } // namespace net 373