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 22 namespace content { 23 24 ServiceWorkerReadFromCacheJob::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 36 ServiceWorkerReadFromCacheJob::~ServiceWorkerReadFromCacheJob() { 37 } 38 39 void 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 61 void 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 74 net::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 80 bool ServiceWorkerReadFromCacheJob::GetCharset(std::string* charset) { 81 if (!http_info()) 82 return false; 83 return http_info()->headers->GetCharset(charset); 84 } 85 86 bool ServiceWorkerReadFromCacheJob::GetMimeType(std::string* mime_type) const { 87 if (!http_info()) 88 return false; 89 return http_info()->headers->GetMimeType(mime_type); 90 } 91 92 void ServiceWorkerReadFromCacheJob::GetResponseInfo( 93 net::HttpResponseInfo* info) { 94 if (!http_info()) 95 return; 96 *info = *http_info(); 97 } 98 99 int ServiceWorkerReadFromCacheJob::GetResponseCode() const { 100 if (!http_info()) 101 return -1; 102 return http_info()->headers->response_code(); 103 } 104 105 void 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 120 bool 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 138 const 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 146 void 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 168 void 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 191 void 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