service_worker_url_request_job.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
1// Copyright 2014 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 "content/browser/service_worker/service_worker_url_request_job.h" 6 7#include <map> 8#include <string> 9#include <vector> 10 11#include "base/bind.h" 12#include "base/strings/stringprintf.h" 13#include "content/browser/service_worker/service_worker_fetch_dispatcher.h" 14#include "content/browser/service_worker/service_worker_provider_host.h" 15#include "net/http/http_request_headers.h" 16#include "net/http/http_response_headers.h" 17#include "net/http/http_response_info.h" 18#include "net/http/http_util.h" 19#include "webkit/browser/blob/blob_data_handle.h" 20#include "webkit/browser/blob/blob_storage_context.h" 21#include "webkit/browser/blob/blob_url_request_job_factory.h" 22 23namespace content { 24 25ServiceWorkerURLRequestJob::ServiceWorkerURLRequestJob( 26 net::URLRequest* request, 27 net::NetworkDelegate* network_delegate, 28 base::WeakPtr<ServiceWorkerProviderHost> provider_host, 29 base::WeakPtr<webkit_blob::BlobStorageContext> blob_storage_context) 30 : net::URLRequestJob(request, network_delegate), 31 provider_host_(provider_host), 32 response_type_(NOT_DETERMINED), 33 is_started_(false), 34 blob_storage_context_(blob_storage_context), 35 weak_factory_(this) { 36} 37 38void ServiceWorkerURLRequestJob::FallbackToNetwork() { 39 DCHECK_EQ(NOT_DETERMINED, response_type_); 40 response_type_ = FALLBACK_TO_NETWORK; 41 MaybeStartRequest(); 42} 43 44void ServiceWorkerURLRequestJob::ForwardToServiceWorker() { 45 DCHECK_EQ(NOT_DETERMINED, response_type_); 46 response_type_ = FORWARD_TO_SERVICE_WORKER; 47 MaybeStartRequest(); 48} 49 50void ServiceWorkerURLRequestJob::Start() { 51 is_started_ = true; 52 MaybeStartRequest(); 53} 54 55void ServiceWorkerURLRequestJob::Kill() { 56 net::URLRequestJob::Kill(); 57 fetch_dispatcher_.reset(); 58 blob_request_.reset(); 59 weak_factory_.InvalidateWeakPtrs(); 60} 61 62net::LoadState ServiceWorkerURLRequestJob::GetLoadState() const { 63 // TODO(kinuko): refine this for better debug. 64 return net::URLRequestJob::GetLoadState(); 65} 66 67bool ServiceWorkerURLRequestJob::GetCharset(std::string* charset) { 68 if (!http_info()) 69 return false; 70 return http_info()->headers->GetCharset(charset); 71} 72 73bool ServiceWorkerURLRequestJob::GetMimeType(std::string* mime_type) const { 74 if (!http_info()) 75 return false; 76 return http_info()->headers->GetMimeType(mime_type); 77} 78 79void ServiceWorkerURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) { 80 if (!http_info()) 81 return; 82 *info = *http_info(); 83} 84 85int ServiceWorkerURLRequestJob::GetResponseCode() const { 86 if (!http_info()) 87 return -1; 88 return http_info()->headers->response_code(); 89} 90 91void ServiceWorkerURLRequestJob::SetExtraRequestHeaders( 92 const net::HttpRequestHeaders& headers) { 93 std::string range_header; 94 std::vector<net::HttpByteRange> ranges; 95 if (!headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header) || 96 !net::HttpUtil::ParseRangeHeader(range_header, &ranges)) { 97 return; 98 } 99 100 // We don't support multiple range requests in one single URL request. 101 if (ranges.size() == 1U) 102 byte_range_ = ranges[0]; 103} 104 105bool ServiceWorkerURLRequestJob::ReadRawData( 106 net::IOBuffer* buf, int buf_size, int *bytes_read) { 107 if (!blob_request_) { 108 *bytes_read = 0; 109 return true; 110 } 111 112 blob_request_->Read(buf, buf_size, bytes_read); 113 net::URLRequestStatus status = blob_request_->status(); 114 SetStatus(status); 115 if (status.is_io_pending()) 116 return false; 117 return status.is_success(); 118} 119 120void ServiceWorkerURLRequestJob::OnReceivedRedirect( 121 net::URLRequest* request, 122 const net::RedirectInfo& redirect_info, 123 bool* defer_redirect) { 124 NOTREACHED(); 125} 126 127void ServiceWorkerURLRequestJob::OnAuthRequired( 128 net::URLRequest* request, 129 net::AuthChallengeInfo* auth_info) { 130 NOTREACHED(); 131} 132 133void ServiceWorkerURLRequestJob::OnCertificateRequested( 134 net::URLRequest* request, 135 net::SSLCertRequestInfo* cert_request_info) { 136 NOTREACHED(); 137} 138 139void ServiceWorkerURLRequestJob::OnSSLCertificateError( 140 net::URLRequest* request, 141 const net::SSLInfo& ssl_info, 142 bool fatal) { 143 NOTREACHED(); 144} 145 146void ServiceWorkerURLRequestJob::OnBeforeNetworkStart(net::URLRequest* request, 147 bool* defer) { 148 NOTREACHED(); 149} 150 151void ServiceWorkerURLRequestJob::OnResponseStarted(net::URLRequest* request) { 152 // TODO(falken): Add Content-Length, Content-Type if they were not provided in 153 // the ServiceWorkerResponse. 154 CommitResponseHeader(); 155} 156 157void ServiceWorkerURLRequestJob::OnReadCompleted(net::URLRequest* request, 158 int bytes_read) { 159 SetStatus(request->status()); 160 if (!request->status().is_success()) { 161 NotifyDone(request->status()); 162 return; 163 } 164 NotifyReadComplete(bytes_read); 165 if (bytes_read == 0) 166 NotifyDone(request->status()); 167} 168 169const net::HttpResponseInfo* ServiceWorkerURLRequestJob::http_info() const { 170 if (!http_response_info_) 171 return NULL; 172 if (range_response_info_) 173 return range_response_info_.get(); 174 return http_response_info_.get(); 175} 176 177void ServiceWorkerURLRequestJob::GetExtraResponseInfo( 178 bool* was_fetched_via_service_worker, 179 GURL* original_url_via_service_worker) const { 180 if (response_type_ != FORWARD_TO_SERVICE_WORKER) { 181 *was_fetched_via_service_worker = false; 182 *original_url_via_service_worker = GURL(); 183 return; 184 } 185 *was_fetched_via_service_worker = true; 186 *original_url_via_service_worker = response_url_; 187} 188 189 190ServiceWorkerURLRequestJob::~ServiceWorkerURLRequestJob() { 191} 192 193void ServiceWorkerURLRequestJob::MaybeStartRequest() { 194 if (is_started_ && response_type_ != NOT_DETERMINED) { 195 // Start asynchronously. 196 base::MessageLoop::current()->PostTask( 197 FROM_HERE, 198 base::Bind(&ServiceWorkerURLRequestJob::StartRequest, 199 weak_factory_.GetWeakPtr())); 200 } 201} 202 203void ServiceWorkerURLRequestJob::StartRequest() { 204 switch (response_type_) { 205 case NOT_DETERMINED: 206 NOTREACHED(); 207 return; 208 209 case FALLBACK_TO_NETWORK: 210 // Restart the request to create a new job. Our request handler will 211 // return NULL, and the default job (which will hit network) should be 212 // created. 213 NotifyRestartRequired(); 214 return; 215 216 case FORWARD_TO_SERVICE_WORKER: 217 DCHECK(provider_host_ && provider_host_->active_version()); 218 DCHECK(!fetch_dispatcher_); 219 220 // Send a fetch event to the ServiceWorker associated to the 221 // provider_host. 222 fetch_dispatcher_.reset(new ServiceWorkerFetchDispatcher( 223 request(), provider_host_->active_version(), 224 base::Bind(&ServiceWorkerURLRequestJob::DidDispatchFetchEvent, 225 weak_factory_.GetWeakPtr()))); 226 fetch_dispatcher_->Run(); 227 return; 228 } 229 230 NOTREACHED(); 231} 232 233void ServiceWorkerURLRequestJob::DidDispatchFetchEvent( 234 ServiceWorkerStatusCode status, 235 ServiceWorkerFetchEventResult fetch_result, 236 const ServiceWorkerResponse& response) { 237 fetch_dispatcher_.reset(); 238 239 // Check if we're not orphaned. 240 if (!request()) 241 return; 242 243 if (status != SERVICE_WORKER_OK) { 244 // Dispatching event has been failed, falling back to the network. 245 // (Tentative behavior described on github) 246 // TODO(kinuko): consider returning error if we've come here because 247 // unexpected worker termination etc (so that we could fix bugs). 248 // TODO(kinuko): Would be nice to log the error case. 249 response_type_ = FALLBACK_TO_NETWORK; 250 NotifyRestartRequired(); 251 return; 252 } 253 254 if (fetch_result == SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK) { 255 // Change the response type and restart the request to fallback to 256 // the network. 257 response_type_ = FALLBACK_TO_NETWORK; 258 NotifyRestartRequired(); 259 return; 260 } 261 262 // We should have a response now. 263 DCHECK_EQ(SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE, fetch_result); 264 265 // Set up a request for reading the blob. 266 if (!response.blob_uuid.empty() && blob_storage_context_) { 267 scoped_ptr<webkit_blob::BlobDataHandle> blob_data_handle = 268 blob_storage_context_->GetBlobDataFromUUID(response.blob_uuid); 269 if (!blob_data_handle) { 270 // The renderer gave us a bad blob UUID. 271 DeliverErrorResponse(); 272 return; 273 } 274 blob_request_ = webkit_blob::BlobProtocolHandler::CreateBlobRequest( 275 blob_data_handle.Pass(), request()->context(), this); 276 blob_request_->Start(); 277 } 278 279 response_url_ = response.url; 280 CreateResponseHeader( 281 response.status_code, response.status_text, response.headers); 282 if (!blob_request_) 283 CommitResponseHeader(); 284} 285 286void ServiceWorkerURLRequestJob::CreateResponseHeader( 287 int status_code, 288 const std::string& status_text, 289 const std::map<std::string, std::string>& headers) { 290 // TODO(kinuko): If the response has an identifier to on-disk cache entry, 291 // pull response header from the disk. 292 std::string status_line( 293 base::StringPrintf("HTTP/1.1 %d %s", status_code, status_text.c_str())); 294 status_line.push_back('\0'); 295 http_response_headers_ = new net::HttpResponseHeaders(status_line); 296 for (std::map<std::string, std::string>::const_iterator it = headers.begin(); 297 it != headers.end(); 298 ++it) { 299 std::string header; 300 header.reserve(it->first.size() + 2 + it->second.size()); 301 header.append(it->first); 302 header.append(": "); 303 header.append(it->second); 304 http_response_headers_->AddHeader(header); 305 } 306} 307 308void ServiceWorkerURLRequestJob::CommitResponseHeader() { 309 http_response_info_.reset(new net::HttpResponseInfo()); 310 http_response_info_->headers.swap(http_response_headers_); 311 NotifyHeadersComplete(); 312} 313 314void ServiceWorkerURLRequestJob::DeliverErrorResponse() { 315 // TODO(falken): Print an error to the console of the ServiceWorker and of 316 // the requesting page. 317 CreateResponseHeader(500, 318 "Service Worker Response Error", 319 std::map<std::string, std::string>()); 320 CommitResponseHeader(); 321} 322 323} // namespace content 324