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