service_worker_url_request_job.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
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 "base/bind.h" 8#include "base/strings/stringprintf.h" 9#include "content/browser/service_worker/service_worker_fetch_dispatcher.h" 10#include "content/browser/service_worker/service_worker_provider_host.h" 11#include "net/http/http_request_headers.h" 12#include "net/http/http_response_headers.h" 13#include "net/http/http_response_info.h" 14#include "net/http/http_util.h" 15 16namespace content { 17 18ServiceWorkerURLRequestJob::ServiceWorkerURLRequestJob( 19 net::URLRequest* request, 20 net::NetworkDelegate* network_delegate, 21 base::WeakPtr<ServiceWorkerProviderHost> provider_host) 22 : net::URLRequestJob(request, network_delegate), 23 provider_host_(provider_host), 24 response_type_(NOT_DETERMINED), 25 is_started_(false), 26 weak_factory_(this) { 27} 28 29void ServiceWorkerURLRequestJob::FallbackToNetwork() { 30 DCHECK_EQ(NOT_DETERMINED, response_type_); 31 response_type_ = FALLBACK_TO_NETWORK; 32 MaybeStartRequest(); 33} 34 35void ServiceWorkerURLRequestJob::ForwardToServiceWorker() { 36 DCHECK_EQ(NOT_DETERMINED, response_type_); 37 response_type_ = FORWARD_TO_SERVICE_WORKER; 38 MaybeStartRequest(); 39} 40 41void ServiceWorkerURLRequestJob::Start() { 42 is_started_ = true; 43 MaybeStartRequest(); 44} 45 46void ServiceWorkerURLRequestJob::Kill() { 47 net::URLRequestJob::Kill(); 48 fetch_dispatcher_.reset(); 49 weak_factory_.InvalidateWeakPtrs(); 50} 51 52net::LoadState ServiceWorkerURLRequestJob::GetLoadState() const { 53 // TODO(kinuko): refine this for better debug. 54 return net::URLRequestJob::GetLoadState(); 55} 56 57bool ServiceWorkerURLRequestJob::GetCharset(std::string* charset) { 58 if (!http_info()) 59 return false; 60 return http_info()->headers->GetCharset(charset); 61} 62 63bool ServiceWorkerURLRequestJob::GetMimeType(std::string* mime_type) const { 64 if (!http_info()) 65 return false; 66 return http_info()->headers->GetMimeType(mime_type); 67} 68 69void ServiceWorkerURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) { 70 if (!http_info()) 71 return; 72 *info = *http_info(); 73} 74 75int ServiceWorkerURLRequestJob::GetResponseCode() const { 76 if (!http_info()) 77 return -1; 78 return http_info()->headers->response_code(); 79} 80 81void ServiceWorkerURLRequestJob::SetExtraRequestHeaders( 82 const net::HttpRequestHeaders& headers) { 83 std::string range_header; 84 std::vector<net::HttpByteRange> ranges; 85 if (!headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header) || 86 !net::HttpUtil::ParseRangeHeader(range_header, &ranges)) { 87 return; 88 } 89 90 // We don't support multiple range requests in one single URL request. 91 if (ranges.size() == 1U) 92 byte_range_ = ranges[0]; 93} 94 95bool ServiceWorkerURLRequestJob::ReadRawData( 96 net::IOBuffer* buf, int buf_size, int *bytes_read) { 97 // TODO(kinuko): Implement this. 98 // If the response returned from ServiceWorker had an 99 // identifier to on-disk data (e.g. blob or cache entry) we'll need to 100 // pull the body from disk. 101 NOTIMPLEMENTED(); 102 *bytes_read = 0; 103 return true; 104} 105 106const net::HttpResponseInfo* ServiceWorkerURLRequestJob::http_info() const { 107 if (!http_response_info_) 108 return NULL; 109 if (range_response_info_) 110 return range_response_info_.get(); 111 return http_response_info_.get(); 112} 113 114ServiceWorkerURLRequestJob::~ServiceWorkerURLRequestJob() { 115} 116 117void ServiceWorkerURLRequestJob::MaybeStartRequest() { 118 if (is_started_ && response_type_ != NOT_DETERMINED) { 119 // Start asynchronously. 120 base::MessageLoop::current()->PostTask( 121 FROM_HERE, 122 base::Bind(&ServiceWorkerURLRequestJob::StartRequest, 123 weak_factory_.GetWeakPtr())); 124 } 125} 126 127void ServiceWorkerURLRequestJob::StartRequest() { 128 switch (response_type_) { 129 case NOT_DETERMINED: 130 NOTREACHED(); 131 return; 132 133 case FALLBACK_TO_NETWORK: 134 // Restart the request to create a new job. Our request handler will 135 // return NULL, and the default job (which will hit network) should be 136 // created. 137 NotifyRestartRequired(); 138 return; 139 140 case FORWARD_TO_SERVICE_WORKER: 141 DCHECK(provider_host_ && provider_host_->active_version()); 142 DCHECK(!fetch_dispatcher_); 143 144 // Send a fetch event to the ServiceWorker associated to the 145 // provider_host. 146 fetch_dispatcher_.reset(new ServiceWorkerFetchDispatcher( 147 request(), provider_host_->active_version(), 148 base::Bind(&ServiceWorkerURLRequestJob::DidDispatchFetchEvent, 149 weak_factory_.GetWeakPtr()))); 150 fetch_dispatcher_->Run(); 151 return; 152 } 153 154 NOTREACHED(); 155} 156 157void ServiceWorkerURLRequestJob::DidDispatchFetchEvent( 158 ServiceWorkerStatusCode status, 159 ServiceWorkerFetchEventResult fetch_result, 160 const ServiceWorkerResponse& response) { 161 fetch_dispatcher_.reset(); 162 163 // Check if we're not orphaned. 164 if (!request()) 165 return; 166 167 if (status != SERVICE_WORKER_OK) { 168 // Dispatching event has been failed, falling back to the network. 169 // (Tentative behavior described on github) 170 // TODO(kinuko): consider returning error if we've come here because 171 // unexpected worker termination etc (so that we could fix bugs). 172 // TODO(kinuko): Would be nice to log the error case. 173 response_type_ = FALLBACK_TO_NETWORK; 174 NotifyRestartRequired(); 175 } 176 177 if (fetch_result == SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK) { 178 // Change the response type and restart the request to fallback to 179 // the network. 180 response_type_ = FALLBACK_TO_NETWORK; 181 NotifyRestartRequired(); 182 return; 183 } 184 185 // We should have response now. 186 DCHECK_EQ(SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE, fetch_result); 187 188 CreateResponseHeader(response); 189 NotifyHeadersComplete(); 190} 191 192void ServiceWorkerURLRequestJob::CreateResponseHeader( 193 const ServiceWorkerResponse& response) { 194 // TODO(kinuko): If the response has an identifier to on-disk cache entry, 195 // pull response header from the disk. 196 std::string status_line(base::StringPrintf("HTTP/1.1 %d %s", 197 response.status_code, 198 response.status_text.c_str())); 199 status_line.push_back('\0'); 200 scoped_refptr<net::HttpResponseHeaders> headers( 201 new net::HttpResponseHeaders(status_line)); 202 for (std::map<std::string, std::string>::const_iterator it = 203 response.headers.begin(); 204 it != response.headers.end(); ++it) { 205 std::string header; 206 header.reserve(it->first.size() + 2 + it->second.size()); 207 header.append(it->first); 208 header.append(": "); 209 header.append(it->second); 210 headers->AddHeader(header); 211 } 212 213 http_response_info_.reset(new net::HttpResponseInfo()); 214 http_response_info_->headers = headers; 215} 216 217} // namespace content 218