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