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.
5 #include "content/browser/service_worker/service_worker_url_request_job.h"
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"
25 ServiceWorkerURLRequestJob::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),
34 blob_storage_context_(blob_storage_context),
38 void ServiceWorkerURLRequestJob::FallbackToNetwork() {
39 DCHECK_EQ(NOT_DETERMINED, response_type_);
40 response_type_ = FALLBACK_TO_NETWORK;
44 void ServiceWorkerURLRequestJob::ForwardToServiceWorker() {
45 DCHECK_EQ(NOT_DETERMINED, response_type_);
46 response_type_ = FORWARD_TO_SERVICE_WORKER;
50 void ServiceWorkerURLRequestJob::Start() {
55 void ServiceWorkerURLRequestJob::Kill() {
56 net::URLRequestJob::Kill();
57 fetch_dispatcher_.reset();
58 blob_request_.reset();
59 weak_factory_.InvalidateWeakPtrs();
62 net::LoadState ServiceWorkerURLRequestJob::GetLoadState() const {
63 // TODO(kinuko): refine this for better debug.
64 return net::URLRequestJob::GetLoadState();
67 bool ServiceWorkerURLRequestJob::GetCharset(std::string* charset) {
70 return http_info()->headers->GetCharset(charset);
73 bool ServiceWorkerURLRequestJob::GetMimeType(std::string* mime_type) const {
76 return http_info()->headers->GetMimeType(mime_type);
79 void ServiceWorkerURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
85 int ServiceWorkerURLRequestJob::GetResponseCode() const {
88 return http_info()->headers->response_code();
91 void 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)) {
100 // We don't support multiple range requests in one single URL request.
101 if (ranges.size() == 1U)
102 byte_range_ = ranges[0];
105 bool ServiceWorkerURLRequestJob::ReadRawData(
106 net::IOBuffer* buf, int buf_size, int *bytes_read) {
107 if (!blob_request_) {
112 blob_request_->Read(buf, buf_size, bytes_read);
113 net::URLRequestStatus status = blob_request_->status();
115 if (status.is_io_pending())
117 return status.is_success();
120 void ServiceWorkerURLRequestJob::OnReceivedRedirect(
121 net::URLRequest* request,
122 const net::RedirectInfo& redirect_info,
123 bool* defer_redirect) {
127 void ServiceWorkerURLRequestJob::OnAuthRequired(
128 net::URLRequest* request,
129 net::AuthChallengeInfo* auth_info) {
133 void ServiceWorkerURLRequestJob::OnCertificateRequested(
134 net::URLRequest* request,
135 net::SSLCertRequestInfo* cert_request_info) {
139 void ServiceWorkerURLRequestJob::OnSSLCertificateError(
140 net::URLRequest* request,
141 const net::SSLInfo& ssl_info,
146 void ServiceWorkerURLRequestJob::OnBeforeNetworkStart(net::URLRequest* request,
151 void ServiceWorkerURLRequestJob::OnResponseStarted(net::URLRequest* request) {
152 // TODO(falken): Add Content-Length, Content-Type if they were not provided in
153 // the ServiceWorkerResponse.
154 CommitResponseHeader();
157 void ServiceWorkerURLRequestJob::OnReadCompleted(net::URLRequest* request,
159 SetStatus(request->status());
160 if (!request->status().is_success()) {
161 NotifyDone(request->status());
164 NotifyReadComplete(bytes_read);
166 NotifyDone(request->status());
169 const net::HttpResponseInfo* ServiceWorkerURLRequestJob::http_info() const {
170 if (!http_response_info_)
172 if (range_response_info_)
173 return range_response_info_.get();
174 return http_response_info_.get();
177 void 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();
185 *was_fetched_via_service_worker = true;
186 *original_url_via_service_worker = response_url_;
190 ServiceWorkerURLRequestJob::~ServiceWorkerURLRequestJob() {
193 void ServiceWorkerURLRequestJob::MaybeStartRequest() {
194 if (is_started_ && response_type_ != NOT_DETERMINED) {
195 // Start asynchronously.
196 base::MessageLoop::current()->PostTask(
198 base::Bind(&ServiceWorkerURLRequestJob::StartRequest,
199 weak_factory_.GetWeakPtr()));
203 void ServiceWorkerURLRequestJob::StartRequest() {
204 switch (response_type_) {
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
213 NotifyRestartRequired();
216 case FORWARD_TO_SERVICE_WORKER:
217 DCHECK(provider_host_ && provider_host_->active_version());
218 DCHECK(!fetch_dispatcher_);
220 // Send a fetch event to the ServiceWorker associated to the
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();
233 void ServiceWorkerURLRequestJob::DidDispatchFetchEvent(
234 ServiceWorkerStatusCode status,
235 ServiceWorkerFetchEventResult fetch_result,
236 const ServiceWorkerResponse& response) {
237 fetch_dispatcher_.reset();
239 // Check if we're not orphaned.
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();
254 if (fetch_result == SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK) {
255 // Change the response type and restart the request to fallback to
257 response_type_ = FALLBACK_TO_NETWORK;
258 NotifyRestartRequired();
262 // We should have a response now.
263 DCHECK_EQ(SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE, fetch_result);
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();
274 blob_request_ = webkit_blob::BlobProtocolHandler::CreateBlobRequest(
275 blob_data_handle.Pass(), request()->context(), this);
276 blob_request_->Start();
279 response_url_ = response.url;
280 CreateResponseHeader(
281 response.status_code, response.status_text, response.headers);
283 CommitResponseHeader();
286 void ServiceWorkerURLRequestJob::CreateResponseHeader(
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();
300 header.reserve(it->first.size() + 2 + it->second.size());
301 header.append(it->first);
303 header.append(it->second);
304 http_response_headers_->AddHeader(header);
308 void ServiceWorkerURLRequestJob::CommitResponseHeader() {
309 http_response_info_.reset(new net::HttpResponseInfo());
310 http_response_info_->headers.swap(http_response_headers_);
311 NotifyHeadersComplete();
314 void 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();
323 } // namespace content