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