service_worker_url_request_job.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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(net::URLRequest* request, 121 const GURL& new_url, 122 bool* defer_redirect) { 123 NOTREACHED(); 124} 125 126void ServiceWorkerURLRequestJob::OnAuthRequired( 127 net::URLRequest* request, 128 net::AuthChallengeInfo* auth_info) { 129 NOTREACHED(); 130} 131 132void ServiceWorkerURLRequestJob::OnCertificateRequested( 133 net::URLRequest* request, 134 net::SSLCertRequestInfo* cert_request_info) { 135 NOTREACHED(); 136} 137 138void ServiceWorkerURLRequestJob::OnSSLCertificateError( 139 net::URLRequest* request, 140 const net::SSLInfo& ssl_info, 141 bool fatal) { 142 NOTREACHED(); 143} 144 145void ServiceWorkerURLRequestJob::OnBeforeNetworkStart(net::URLRequest* request, 146 bool* defer) { 147 NOTREACHED(); 148} 149 150void ServiceWorkerURLRequestJob::OnResponseStarted(net::URLRequest* request) { 151 // TODO(falken): Add Content-Length, Content-Type if they were not provided in 152 // the ServiceWorkerResponse. 153 CommitResponseHeader(); 154} 155 156void ServiceWorkerURLRequestJob::OnReadCompleted(net::URLRequest* request, 157 int bytes_read) { 158 SetStatus(request->status()); 159 if (!request->status().is_success()) { 160 NotifyDone(request->status()); 161 return; 162 } 163 NotifyReadComplete(bytes_read); 164 if (bytes_read == 0) 165 NotifyDone(request->status()); 166} 167 168const net::HttpResponseInfo* ServiceWorkerURLRequestJob::http_info() const { 169 if (!http_response_info_) 170 return NULL; 171 if (range_response_info_) 172 return range_response_info_.get(); 173 return http_response_info_.get(); 174} 175 176void ServiceWorkerURLRequestJob::GetExtraResponseInfo( 177 bool* was_fetched_via_service_worker, 178 GURL* original_url_via_service_worker) const { 179 if (response_type_ != FORWARD_TO_SERVICE_WORKER) { 180 *was_fetched_via_service_worker = false; 181 *original_url_via_service_worker = GURL(); 182 return; 183 } 184 *was_fetched_via_service_worker = true; 185 *original_url_via_service_worker = response_url_; 186} 187 188 189ServiceWorkerURLRequestJob::~ServiceWorkerURLRequestJob() { 190} 191 192void ServiceWorkerURLRequestJob::MaybeStartRequest() { 193 if (is_started_ && response_type_ != NOT_DETERMINED) { 194 // Start asynchronously. 195 base::MessageLoop::current()->PostTask( 196 FROM_HERE, 197 base::Bind(&ServiceWorkerURLRequestJob::StartRequest, 198 weak_factory_.GetWeakPtr())); 199 } 200} 201 202void ServiceWorkerURLRequestJob::StartRequest() { 203 switch (response_type_) { 204 case NOT_DETERMINED: 205 NOTREACHED(); 206 return; 207 208 case FALLBACK_TO_NETWORK: 209 // Restart the request to create a new job. Our request handler will 210 // return NULL, and the default job (which will hit network) should be 211 // created. 212 NotifyRestartRequired(); 213 return; 214 215 case FORWARD_TO_SERVICE_WORKER: 216 DCHECK(provider_host_ && provider_host_->active_version()); 217 DCHECK(!fetch_dispatcher_); 218 219 // Send a fetch event to the ServiceWorker associated to the 220 // provider_host. 221 fetch_dispatcher_.reset(new ServiceWorkerFetchDispatcher( 222 request(), provider_host_->active_version(), 223 base::Bind(&ServiceWorkerURLRequestJob::DidDispatchFetchEvent, 224 weak_factory_.GetWeakPtr()))); 225 fetch_dispatcher_->Run(); 226 return; 227 } 228 229 NOTREACHED(); 230} 231 232void ServiceWorkerURLRequestJob::DidDispatchFetchEvent( 233 ServiceWorkerStatusCode status, 234 ServiceWorkerFetchEventResult fetch_result, 235 const ServiceWorkerResponse& response) { 236 fetch_dispatcher_.reset(); 237 238 // Check if we're not orphaned. 239 if (!request()) 240 return; 241 242 if (status != SERVICE_WORKER_OK) { 243 // Dispatching event has been failed, falling back to the network. 244 // (Tentative behavior described on github) 245 // TODO(kinuko): consider returning error if we've come here because 246 // unexpected worker termination etc (so that we could fix bugs). 247 // TODO(kinuko): Would be nice to log the error case. 248 response_type_ = FALLBACK_TO_NETWORK; 249 NotifyRestartRequired(); 250 return; 251 } 252 253 if (fetch_result == SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK) { 254 // Change the response type and restart the request to fallback to 255 // the network. 256 response_type_ = FALLBACK_TO_NETWORK; 257 NotifyRestartRequired(); 258 return; 259 } 260 261 // We should have a response now. 262 DCHECK_EQ(SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE, fetch_result); 263 264 // Set up a request for reading the blob. 265 if (!response.blob_uuid.empty() && blob_storage_context_) { 266 scoped_ptr<webkit_blob::BlobDataHandle> blob_data_handle = 267 blob_storage_context_->GetBlobDataFromUUID(response.blob_uuid); 268 if (!blob_data_handle) { 269 // The renderer gave us a bad blob UUID. 270 DeliverErrorResponse(); 271 return; 272 } 273 blob_request_ = webkit_blob::BlobProtocolHandler::CreateBlobRequest( 274 blob_data_handle.Pass(), request()->context(), this); 275 blob_request_->Start(); 276 } 277 278 response_url_ = response.url; 279 CreateResponseHeader( 280 response.status_code, response.status_text, response.headers); 281 if (!blob_request_) 282 CommitResponseHeader(); 283} 284 285void ServiceWorkerURLRequestJob::CreateResponseHeader( 286 int status_code, 287 const std::string& status_text, 288 const std::map<std::string, std::string>& headers) { 289 // TODO(kinuko): If the response has an identifier to on-disk cache entry, 290 // pull response header from the disk. 291 std::string status_line( 292 base::StringPrintf("HTTP/1.1 %d %s", status_code, status_text.c_str())); 293 status_line.push_back('\0'); 294 http_response_headers_ = new net::HttpResponseHeaders(status_line); 295 for (std::map<std::string, std::string>::const_iterator it = headers.begin(); 296 it != headers.end(); 297 ++it) { 298 std::string header; 299 header.reserve(it->first.size() + 2 + it->second.size()); 300 header.append(it->first); 301 header.append(": "); 302 header.append(it->second); 303 http_response_headers_->AddHeader(header); 304 } 305} 306 307void ServiceWorkerURLRequestJob::CommitResponseHeader() { 308 http_response_info_.reset(new net::HttpResponseInfo()); 309 http_response_info_->headers.swap(http_response_headers_); 310 NotifyHeadersComplete(); 311} 312 313void ServiceWorkerURLRequestJob::DeliverErrorResponse() { 314 // TODO(falken): Print an error to the console of the ServiceWorker and of 315 // the requesting page. 316 CreateResponseHeader(500, 317 "Service Worker Response Error", 318 std::map<std::string, std::string>()); 319 CommitResponseHeader(); 320} 321 322} // namespace content 323