service_worker_read_from_cache_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_read_from_cache_job.h"
6
7#include <string>
8#include <vector>
9
10#include "content/browser/service_worker/service_worker_context_core.h"
11#include "content/browser/service_worker/service_worker_disk_cache.h"
12#include "content/browser/service_worker/service_worker_metrics.h"
13#include "net/base/io_buffer.h"
14#include "net/base/net_errors.h"
15#include "net/http/http_request_headers.h"
16#include "net/http/http_response_headers.h"
17#include "net/http/http_util.h"
18#include "net/url_request/url_request.h"
19#include "net/url_request/url_request_status.h"
20
21namespace content {
22
23ServiceWorkerReadFromCacheJob::ServiceWorkerReadFromCacheJob(
24    net::URLRequest* request,
25    net::NetworkDelegate* network_delegate,
26    base::WeakPtr<ServiceWorkerContextCore> context,
27    int64 response_id)
28    : net::URLRequestJob(request, network_delegate),
29      context_(context),
30      response_id_(response_id),
31      has_been_killed_(false),
32      weak_factory_(this) {
33}
34
35ServiceWorkerReadFromCacheJob::~ServiceWorkerReadFromCacheJob() {
36}
37
38void ServiceWorkerReadFromCacheJob::Start() {
39  if (!context_) {
40    NotifyStartError(net::URLRequestStatus(
41        net::URLRequestStatus::FAILED, net::ERR_FAILED));
42    return;
43  }
44
45  // Create a response reader and start reading the headers,
46  // we'll continue when thats done.
47  reader_ = context_->storage()->CreateResponseReader(response_id_);
48  http_info_io_buffer_ = new HttpResponseInfoIOBuffer;
49  reader_->ReadInfo(
50      http_info_io_buffer_,
51      base::Bind(&ServiceWorkerReadFromCacheJob::OnReadInfoComplete,
52                 weak_factory_.GetWeakPtr()));
53  SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
54}
55
56void ServiceWorkerReadFromCacheJob::Kill() {
57  if (has_been_killed_)
58    return;
59  weak_factory_.InvalidateWeakPtrs();
60  has_been_killed_ = true;
61  reader_.reset();
62  context_.reset();
63  http_info_io_buffer_ = NULL;
64  http_info_.reset();
65  range_response_info_.reset();
66  net::URLRequestJob::Kill();
67}
68
69net::LoadState ServiceWorkerReadFromCacheJob::GetLoadState() const {
70  if (reader_.get() && reader_->IsReadPending())
71    return net::LOAD_STATE_READING_RESPONSE;
72  return net::LOAD_STATE_IDLE;
73}
74
75bool ServiceWorkerReadFromCacheJob::GetCharset(std::string* charset) {
76  if (!http_info())
77    return false;
78  return http_info()->headers->GetCharset(charset);
79}
80
81bool ServiceWorkerReadFromCacheJob::GetMimeType(std::string* mime_type) const {
82  if (!http_info())
83    return false;
84  return http_info()->headers->GetMimeType(mime_type);
85}
86
87void ServiceWorkerReadFromCacheJob::GetResponseInfo(
88    net::HttpResponseInfo* info) {
89  if (!http_info())
90    return;
91  *info = *http_info();
92}
93
94int ServiceWorkerReadFromCacheJob::GetResponseCode() const {
95  if (!http_info())
96    return -1;
97  return http_info()->headers->response_code();
98}
99
100void ServiceWorkerReadFromCacheJob::SetExtraRequestHeaders(
101      const net::HttpRequestHeaders& headers) {
102  std::string value;
103  std::vector<net::HttpByteRange> ranges;
104  if (!headers.GetHeader(net::HttpRequestHeaders::kRange, &value) ||
105      !net::HttpUtil::ParseRangeHeader(value, &ranges)) {
106    return;
107  }
108
109  // If multiple ranges are requested, we play dumb and
110  // return the entire response with 200 OK.
111  if (ranges.size() == 1U)
112    range_requested_ = ranges[0];
113}
114
115bool ServiceWorkerReadFromCacheJob::ReadRawData(
116    net::IOBuffer* buf,
117    int buf_size,
118    int *bytes_read) {
119  DCHECK_NE(buf_size, 0);
120  DCHECK(bytes_read);
121  DCHECK(!reader_->IsReadPending());
122  reader_->ReadData(
123      buf, buf_size, base::Bind(&ServiceWorkerReadFromCacheJob::OnReadComplete,
124                                weak_factory_.GetWeakPtr()));
125  SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
126  return false;
127}
128
129const net::HttpResponseInfo* ServiceWorkerReadFromCacheJob::http_info() const {
130  if (!http_info_)
131    return NULL;
132  if (range_response_info_)
133    return range_response_info_.get();
134  return http_info_.get();
135}
136
137void ServiceWorkerReadFromCacheJob::OnReadInfoComplete(int result) {
138  scoped_refptr<ServiceWorkerReadFromCacheJob> protect(this);
139  if (!http_info_io_buffer_->http_info) {
140    DCHECK_LT(result, 0);
141    ServiceWorkerMetrics::CountReadResponseResult(
142        ServiceWorkerMetrics::READ_HEADERS_ERROR);
143    NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result));
144    return;
145  }
146  DCHECK_GE(result, 0);
147  SetStatus(net::URLRequestStatus());  // Clear the IO_PENDING status
148  http_info_.reset(http_info_io_buffer_->http_info.release());
149  if (is_range_request())
150    SetupRangeResponse(http_info_io_buffer_->response_data_size);
151  http_info_io_buffer_ = NULL;
152  NotifyHeadersComplete();
153}
154
155void ServiceWorkerReadFromCacheJob::SetupRangeResponse(int resource_size) {
156  DCHECK(is_range_request() && http_info_.get() && reader_.get());
157  if (resource_size < 0 || !range_requested_.ComputeBounds(resource_size)) {
158    range_requested_ = net::HttpByteRange();
159    return;
160  }
161
162  DCHECK(range_requested_.IsValid());
163  int offset = static_cast<int>(range_requested_.first_byte_position());
164  int length = static_cast<int>(range_requested_.last_byte_position() -
165                                range_requested_.first_byte_position() + 1);
166
167  // Tell the reader about the range to read.
168  reader_->SetReadRange(offset, length);
169
170  // Make a copy of the full response headers and fix them up
171  // for the range we'll be returning.
172  range_response_info_.reset(new net::HttpResponseInfo(*http_info_));
173  net::HttpResponseHeaders* headers = range_response_info_->headers.get();
174  headers->UpdateWithNewRange(
175      range_requested_, resource_size, true /* replace status line */);
176}
177
178void ServiceWorkerReadFromCacheJob::OnReadComplete(int result) {
179  ServiceWorkerMetrics::ReadResponseResult check_result;
180  if (result == 0) {
181    check_result = ServiceWorkerMetrics::READ_OK;
182    NotifyDone(net::URLRequestStatus());
183  } else if (result < 0) {
184    check_result = ServiceWorkerMetrics::READ_DATA_ERROR;
185    NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result));
186  } else {
187    check_result = ServiceWorkerMetrics::READ_OK;
188    SetStatus(net::URLRequestStatus());  // Clear the IO_PENDING status
189  }
190  ServiceWorkerMetrics::CountReadResponseResult(check_result);
191  NotifyReadComplete(result);
192}
193
194}  // namespace content
195