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_write_to_cache_job.h"
      6 
      7 #include "base/debug/trace_event.h"
      8 #include "content/browser/service_worker/service_worker_context_core.h"
      9 #include "content/browser/service_worker/service_worker_disk_cache.h"
     10 #include "content/browser/service_worker/service_worker_metrics.h"
     11 #include "net/base/io_buffer.h"
     12 #include "net/base/net_errors.h"
     13 #include "net/http/http_request_headers.h"
     14 #include "net/http/http_response_headers.h"
     15 #include "net/http/http_util.h"
     16 #include "net/url_request/url_request.h"
     17 #include "net/url_request/url_request_context.h"
     18 #include "net/url_request/url_request_status.h"
     19 
     20 namespace content {
     21 
     22 ServiceWorkerWriteToCacheJob::ServiceWorkerWriteToCacheJob(
     23     net::URLRequest* request,
     24     net::NetworkDelegate* network_delegate,
     25     ResourceType resource_type,
     26     base::WeakPtr<ServiceWorkerContextCore> context,
     27     ServiceWorkerVersion* version,
     28     int extra_load_flags,
     29     int64 response_id)
     30     : net::URLRequestJob(request, network_delegate),
     31       resource_type_(resource_type),
     32       context_(context),
     33       url_(request->url()),
     34       response_id_(response_id),
     35       version_(version),
     36       has_been_killed_(false),
     37       did_notify_started_(false),
     38       did_notify_finished_(false),
     39       weak_factory_(this) {
     40   InitNetRequest(extra_load_flags);
     41 }
     42 
     43 ServiceWorkerWriteToCacheJob::~ServiceWorkerWriteToCacheJob() {
     44   DCHECK_EQ(did_notify_started_, did_notify_finished_);
     45 }
     46 
     47 void ServiceWorkerWriteToCacheJob::Start() {
     48   TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
     49                            "ServiceWorkerWriteToCacheJob::ExecutingJob",
     50                            this,
     51                            "URL", request_->url().spec());
     52   if (!context_) {
     53     NotifyStartError(net::URLRequestStatus(
     54         net::URLRequestStatus::FAILED, net::ERR_FAILED));
     55     return;
     56   }
     57   version_->script_cache_map()->NotifyStartedCaching(
     58       url_, response_id_);
     59   did_notify_started_ = true;
     60   StartNetRequest();
     61 }
     62 
     63 void ServiceWorkerWriteToCacheJob::Kill() {
     64   if (has_been_killed_)
     65     return;
     66   weak_factory_.InvalidateWeakPtrs();
     67   has_been_killed_ = true;
     68   net_request_.reset();
     69   if (did_notify_started_ && !did_notify_finished_) {
     70     version_->script_cache_map()->NotifyFinishedCaching(
     71         url_,
     72         net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_ABORTED));
     73     did_notify_finished_ = true;
     74   }
     75   writer_.reset();
     76   context_.reset();
     77   net::URLRequestJob::Kill();
     78 }
     79 
     80 net::LoadState ServiceWorkerWriteToCacheJob::GetLoadState() const {
     81   if (writer_ && writer_->IsWritePending())
     82     return net::LOAD_STATE_WAITING_FOR_APPCACHE;
     83   if (net_request_)
     84     return net_request_->GetLoadState().state;
     85   return net::LOAD_STATE_IDLE;
     86 }
     87 
     88 bool ServiceWorkerWriteToCacheJob::GetCharset(std::string* charset) {
     89   if (!http_info())
     90     return false;
     91   return http_info()->headers->GetCharset(charset);
     92 }
     93 
     94 bool ServiceWorkerWriteToCacheJob::GetMimeType(std::string* mime_type) const {
     95   if (!http_info())
     96     return false;
     97   return http_info()->headers->GetMimeType(mime_type);
     98 }
     99 
    100 void ServiceWorkerWriteToCacheJob::GetResponseInfo(
    101     net::HttpResponseInfo* info) {
    102   if (!http_info())
    103     return;
    104   *info = *http_info();
    105 }
    106 
    107 int ServiceWorkerWriteToCacheJob::GetResponseCode() const {
    108   if (!http_info())
    109     return -1;
    110   return http_info()->headers->response_code();
    111 }
    112 
    113 void ServiceWorkerWriteToCacheJob::SetExtraRequestHeaders(
    114       const net::HttpRequestHeaders& headers) {
    115   std::string value;
    116   DCHECK(!headers.GetHeader(net::HttpRequestHeaders::kRange, &value));
    117   net_request_->SetExtraRequestHeaders(headers);
    118 }
    119 
    120 bool ServiceWorkerWriteToCacheJob::ReadRawData(
    121     net::IOBuffer* buf,
    122     int buf_size,
    123     int *bytes_read) {
    124   net::URLRequestStatus status = ReadNetData(buf, buf_size, bytes_read);
    125   SetStatus(status);
    126   if (status.is_io_pending())
    127     return false;
    128 
    129   // No more data to process, the job is complete.
    130   io_buffer_ = NULL;
    131   version_->script_cache_map()->NotifyFinishedCaching(
    132       url_, net::URLRequestStatus());
    133   did_notify_finished_ = true;
    134   return status.is_success();
    135 }
    136 
    137 const net::HttpResponseInfo* ServiceWorkerWriteToCacheJob::http_info() const {
    138   return http_info_.get();
    139 }
    140 
    141 void ServiceWorkerWriteToCacheJob::InitNetRequest(
    142     int extra_load_flags) {
    143   DCHECK(request());
    144   net_request_ = request()->context()->CreateRequest(
    145       request()->url(),
    146       request()->priority(),
    147       this,
    148       this->GetCookieStore());
    149   net_request_->set_first_party_for_cookies(
    150       request()->first_party_for_cookies());
    151   net_request_->SetReferrer(request()->referrer());
    152   if (extra_load_flags)
    153     net_request_->SetLoadFlags(net_request_->load_flags() | extra_load_flags);
    154 
    155   if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER) {
    156     // This will get copied into net_request_ when URLRequest::StartJob calls
    157     // ServiceWorkerWriteToCacheJob::SetExtraRequestHeaders.
    158     request()->SetExtraRequestHeaderByName("Service-Worker", "script", true);
    159   }
    160 }
    161 
    162 void ServiceWorkerWriteToCacheJob::StartNetRequest() {
    163   TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker",
    164                                "ServiceWorkerWriteToCacheJob::ExecutingJob",
    165                                this,
    166                                "NetRequest");
    167   net_request_->Start();  // We'll continue in OnResponseStarted.
    168 }
    169 
    170 net::URLRequestStatus ServiceWorkerWriteToCacheJob::ReadNetData(
    171     net::IOBuffer* buf,
    172     int buf_size,
    173     int *bytes_read) {
    174   DCHECK_GT(buf_size, 0);
    175   DCHECK(bytes_read);
    176 
    177   *bytes_read = 0;
    178   io_buffer_ = buf;
    179   int net_bytes_read = 0;
    180   if (!net_request_->Read(buf, buf_size, &net_bytes_read)) {
    181     if (net_request_->status().is_io_pending())
    182       return net_request_->status();
    183     DCHECK(!net_request_->status().is_success());
    184     return net_request_->status();
    185   }
    186 
    187   if (net_bytes_read != 0) {
    188     WriteDataToCache(net_bytes_read);
    189     DCHECK(GetStatus().is_io_pending());
    190     return GetStatus();
    191   }
    192 
    193   DCHECK(net_request_->status().is_success());
    194   return net_request_->status();
    195 }
    196 
    197 void ServiceWorkerWriteToCacheJob::WriteHeadersToCache() {
    198   if (!context_) {
    199     AsyncNotifyDoneHelper(net::URLRequestStatus(
    200         net::URLRequestStatus::FAILED, net::ERR_FAILED));
    201     return;
    202   }
    203   TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker",
    204                                "ServiceWorkerWriteToCacheJob::ExecutingJob",
    205                                this,
    206                                "WriteHeadersToCache");
    207   writer_ = context_->storage()->CreateResponseWriter(response_id_);
    208   info_buffer_ = new HttpResponseInfoIOBuffer(
    209       new net::HttpResponseInfo(net_request_->response_info()));
    210   writer_->WriteInfo(
    211       info_buffer_.get(),
    212       base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete,
    213                  weak_factory_.GetWeakPtr()));
    214   SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
    215 }
    216 
    217 void ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete(int result) {
    218   SetStatus(net::URLRequestStatus());  // Clear the IO_PENDING status
    219   if (result < 0) {
    220     ServiceWorkerMetrics::CountWriteResponseResult(
    221         ServiceWorkerMetrics::WRITE_HEADERS_ERROR);
    222     AsyncNotifyDoneHelper(net::URLRequestStatus(
    223         net::URLRequestStatus::FAILED, result));
    224     return;
    225   }
    226   http_info_.reset(info_buffer_->http_info.release());
    227   info_buffer_ = NULL;
    228   TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker",
    229                                "ServiceWorkerWriteToCacheJob::ExecutingJob",
    230                                this,
    231                                "WriteHeadersToCacheCompleted");
    232   NotifyHeadersComplete();
    233 }
    234 
    235 void ServiceWorkerWriteToCacheJob::WriteDataToCache(int amount_to_write) {
    236   DCHECK_NE(0, amount_to_write);
    237   TRACE_EVENT_ASYNC_STEP_INTO1("ServiceWorker",
    238                                "ServiceWorkerWriteToCacheJob::ExecutingJob",
    239                                this,
    240                                "WriteDataToCache",
    241                                "Amount to write", amount_to_write);
    242   SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
    243   writer_->WriteData(
    244       io_buffer_.get(),
    245       amount_to_write,
    246       base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteDataComplete,
    247                  weak_factory_.GetWeakPtr()));
    248 }
    249 
    250 void ServiceWorkerWriteToCacheJob::OnWriteDataComplete(int result) {
    251   DCHECK_NE(0, result);
    252   io_buffer_ = NULL;
    253   if (!context_) {
    254     AsyncNotifyDoneHelper(net::URLRequestStatus(
    255         net::URLRequestStatus::FAILED, net::ERR_FAILED));
    256     return;
    257   }
    258   if (result < 0) {
    259     ServiceWorkerMetrics::CountWriteResponseResult(
    260         ServiceWorkerMetrics::WRITE_DATA_ERROR);
    261     AsyncNotifyDoneHelper(net::URLRequestStatus(
    262         net::URLRequestStatus::FAILED, result));
    263     return;
    264   }
    265   ServiceWorkerMetrics::CountWriteResponseResult(
    266       ServiceWorkerMetrics::WRITE_OK);
    267   SetStatus(net::URLRequestStatus());  // Clear the IO_PENDING status
    268   NotifyReadComplete(result);
    269   TRACE_EVENT_ASYNC_END0("ServiceWorker",
    270                          "ServiceWorkerWriteToCacheJob::ExecutingJob",
    271                          this);
    272 }
    273 
    274 void ServiceWorkerWriteToCacheJob::OnReceivedRedirect(
    275     net::URLRequest* request,
    276     const net::RedirectInfo& redirect_info,
    277     bool* defer_redirect) {
    278   DCHECK_EQ(net_request_, request);
    279   TRACE_EVENT0("ServiceWorker",
    280                "ServiceWorkerWriteToCacheJob::OnReceivedRedirect");
    281   // Script resources can't redirect.
    282   AsyncNotifyDoneHelper(net::URLRequestStatus(
    283       net::URLRequestStatus::FAILED, net::ERR_UNSAFE_REDIRECT));
    284 }
    285 
    286 void ServiceWorkerWriteToCacheJob::OnAuthRequired(
    287     net::URLRequest* request,
    288     net::AuthChallengeInfo* auth_info) {
    289   DCHECK_EQ(net_request_, request);
    290   TRACE_EVENT0("ServiceWorker",
    291                "ServiceWorkerWriteToCacheJob::OnAuthRequired");
    292   // TODO(michaeln): Pass this thru to our jobs client.
    293   AsyncNotifyDoneHelper(net::URLRequestStatus(
    294       net::URLRequestStatus::FAILED, net::ERR_FAILED));
    295 }
    296 
    297 void ServiceWorkerWriteToCacheJob::OnCertificateRequested(
    298     net::URLRequest* request,
    299     net::SSLCertRequestInfo* cert_request_info) {
    300   DCHECK_EQ(net_request_, request);
    301   TRACE_EVENT0("ServiceWorker",
    302                "ServiceWorkerWriteToCacheJob::OnCertificateRequested");
    303   // TODO(michaeln): Pass this thru to our jobs client.
    304   // see NotifyCertificateRequested.
    305   AsyncNotifyDoneHelper(net::URLRequestStatus(
    306       net::URLRequestStatus::FAILED, net::ERR_FAILED));
    307 }
    308 
    309 void ServiceWorkerWriteToCacheJob:: OnSSLCertificateError(
    310     net::URLRequest* request,
    311     const net::SSLInfo& ssl_info,
    312     bool fatal) {
    313   DCHECK_EQ(net_request_, request);
    314   TRACE_EVENT0("ServiceWorker",
    315                "ServiceWorkerWriteToCacheJob::OnSSLCertificateError");
    316   // TODO(michaeln): Pass this thru to our jobs client,
    317   // see NotifySSLCertificateError.
    318   AsyncNotifyDoneHelper(net::URLRequestStatus(
    319       net::URLRequestStatus::FAILED, net::ERR_FAILED));
    320 }
    321 
    322 void ServiceWorkerWriteToCacheJob::OnBeforeNetworkStart(
    323     net::URLRequest* request,
    324     bool* defer) {
    325   DCHECK_EQ(net_request_, request);
    326   TRACE_EVENT0("ServiceWorker",
    327                "ServiceWorkerWriteToCacheJob::OnBeforeNetworkStart");
    328   NotifyBeforeNetworkStart(defer);
    329 }
    330 
    331 void ServiceWorkerWriteToCacheJob::OnResponseStarted(
    332     net::URLRequest* request) {
    333   DCHECK_EQ(net_request_, request);
    334   if (!request->status().is_success()) {
    335     AsyncNotifyDoneHelper(request->status());
    336     return;
    337   }
    338   if (request->GetResponseCode() / 100 != 2) {
    339     AsyncNotifyDoneHelper(net::URLRequestStatus(
    340         net::URLRequestStatus::FAILED, net::ERR_FAILED));
    341     // TODO(michaeln): Instead of error'ing immediately, send the net
    342     // response to our consumer, just don't cache it?
    343     return;
    344   }
    345   // To prevent most user-uploaded content from being used as a serviceworker.
    346   if (version_->script_url() == url_) {
    347     std::string mime_type;
    348     request->GetMimeType(&mime_type);
    349     if (mime_type != "application/x-javascript" &&
    350         mime_type != "text/javascript" &&
    351         mime_type != "application/javascript") {
    352       AsyncNotifyDoneHelper(net::URLRequestStatus(
    353           net::URLRequestStatus::FAILED, net::ERR_INSECURE_RESPONSE));
    354       return;
    355     }
    356   }
    357   WriteHeadersToCache();
    358 }
    359 
    360 void ServiceWorkerWriteToCacheJob::OnReadCompleted(
    361     net::URLRequest* request,
    362     int bytes_read) {
    363   DCHECK_EQ(net_request_, request);
    364   if (!request->status().is_success()) {
    365     AsyncNotifyDoneHelper(request->status());
    366     return;
    367   }
    368   if (bytes_read > 0) {
    369     WriteDataToCache(bytes_read);
    370     return;
    371   }
    372   TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker",
    373                                "ServiceWorkerWriteToCacheJob::ExecutingJob",
    374                                this,
    375                                "WriteHeadersToCache");
    376   // We're done with all.
    377   AsyncNotifyDoneHelper(request->status());
    378   return;
    379 }
    380 
    381 void ServiceWorkerWriteToCacheJob::AsyncNotifyDoneHelper(
    382     const net::URLRequestStatus& status) {
    383   DCHECK(!status.is_io_pending());
    384   version_->script_cache_map()->NotifyFinishedCaching(url_, status);
    385   did_notify_finished_ = true;
    386   SetStatus(status);
    387   NotifyDone(status);
    388 }
    389 
    390 }  // namespace content
    391