OLD | NEW |
1 // Copyright (c) 2017 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2017 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/appcache/appcache_url_loader_job.h" | 5 #include "content/browser/appcache/appcache_url_loader_job.h" |
6 #include "content/browser/appcache/appcache_entry.h" | 6 |
| 7 #include "base/strings/string_number_conversions.h" |
| 8 #include "content/browser/appcache/appcache_histograms.h" |
| 9 #include "content/common/net_adapters.h" |
| 10 #include "content/public/common/resource_type.h" |
| 11 #include "net/http/http_status_code.h" |
7 | 12 |
8 namespace content { | 13 namespace content { |
9 | 14 |
10 AppCacheURLLoaderJob::~AppCacheURLLoaderJob() {} | 15 AppCacheURLLoaderJob::~AppCacheURLLoaderJob() { |
| 16 if (storage_.get()) |
| 17 storage_->CancelDelegateCallbacks(this); |
| 18 } |
11 | 19 |
12 void AppCacheURLLoaderJob::Kill() {} | 20 void AppCacheURLLoaderJob::Kill() {} |
13 | 21 |
14 bool AppCacheURLLoaderJob::IsStarted() const { | 22 bool AppCacheURLLoaderJob::IsStarted() const { |
15 return false; | 23 return delivery_type_ != AWAITING_DELIVERY_ORDERS; |
16 } | |
17 | |
18 bool AppCacheURLLoaderJob::IsWaiting() const { | |
19 return false; | |
20 } | |
21 | |
22 bool AppCacheURLLoaderJob::IsDeliveringAppCacheResponse() const { | |
23 return false; | |
24 } | |
25 | |
26 bool AppCacheURLLoaderJob::IsDeliveringNetworkResponse() const { | |
27 return false; | |
28 } | |
29 | |
30 bool AppCacheURLLoaderJob::IsDeliveringErrorResponse() const { | |
31 return false; | |
32 } | |
33 | |
34 bool AppCacheURLLoaderJob::IsCacheEntryNotFound() const { | |
35 return false; | |
36 } | 24 } |
37 | 25 |
38 void AppCacheURLLoaderJob::DeliverAppCachedResponse(const GURL& manifest_url, | 26 void AppCacheURLLoaderJob::DeliverAppCachedResponse(const GURL& manifest_url, |
39 int64_t cache_id, | 27 int64_t cache_id, |
40 const AppCacheEntry& entry, | 28 const AppCacheEntry& entry, |
41 bool is_fallback) {} | 29 bool is_fallback) { |
42 | 30 if (!storage_.get()) { |
43 void AppCacheURLLoaderJob::DeliverNetworkResponse() {} | 31 DeliverErrorResponse(); |
44 | 32 return; |
45 void AppCacheURLLoaderJob::DeliverErrorResponse() {} | 33 } |
| 34 |
| 35 delivery_type_ = APPCACHED_DELIVERY; |
| 36 |
| 37 AppCacheHistograms::AddAppCacheJobStartDelaySample(base::TimeTicks::Now() - |
| 38 start_time_tick_); |
| 39 |
| 40 manifest_url_ = manifest_url; |
| 41 cache_id_ = cache_id; |
| 42 entry_ = entry; |
| 43 is_fallback_ = is_fallback; |
| 44 |
| 45 // TODO(ananta) |
| 46 // Implement the AppCacheServiceImpl::Observer interface or add weak pointer |
| 47 // support to it. |
| 48 storage_->LoadResponseInfo(manifest_url_, entry_.response_id(), this); |
| 49 } |
| 50 |
| 51 void AppCacheURLLoaderJob::DeliverNetworkResponse() { |
| 52 delivery_type_ = NETWORK_DELIVERY; |
| 53 |
| 54 AppCacheHistograms::AddNetworkJobStartDelaySample(base::TimeTicks::Now() - |
| 55 start_time_tick_); |
| 56 |
| 57 DCHECK(!loader_callback_.is_null()); |
| 58 // In network service land, if we are processing a navigation request, we |
| 59 // need to inform the loader callback that we are not going to handle this |
| 60 // request. The loader callback is valid only for navigation requests. |
| 61 std::move(loader_callback_).Run(StartLoaderCallback()); |
| 62 } |
| 63 |
| 64 void AppCacheURLLoaderJob::DeliverErrorResponse() { |
| 65 delivery_type_ = ERROR_DELIVERY; |
| 66 |
| 67 // We expect the URLLoaderClient pointer to be valid at this point. |
| 68 DCHECK(client_info_); |
| 69 |
| 70 // AppCacheURLRequestJob uses ERR_FAILED as the error code here. That seems |
| 71 // to map to HTTP_INTERNAL_SERVER_ERROR. |
| 72 std::string status("HTTP/1.1 "); |
| 73 status.append(base::IntToString(net::HTTP_INTERNAL_SERVER_ERROR)); |
| 74 status.append(" "); |
| 75 status.append(net::GetHttpReasonPhrase(net::HTTP_INTERNAL_SERVER_ERROR)); |
| 76 status.append("\0\0", 2); |
| 77 |
| 78 ResourceResponseHead response; |
| 79 response.headers = new net::HttpResponseHeaders(status); |
| 80 client_info_->OnReceiveResponse(response, base::nullopt, nullptr); |
| 81 |
| 82 NotifyCompleted(net::ERR_FAILED); |
| 83 |
| 84 AppCacheHistograms::AddErrorJobStartDelaySample(base::TimeTicks::Now() - |
| 85 start_time_tick_); |
| 86 } |
46 | 87 |
47 const GURL& AppCacheURLLoaderJob::GetURL() const { | 88 const GURL& AppCacheURLLoaderJob::GetURL() const { |
48 return url_; | 89 return request_.url; |
49 } | 90 } |
50 | 91 |
51 AppCacheURLLoaderJob::AppCacheURLLoaderJob() {} | 92 AppCacheURLLoaderJob* AppCacheURLLoaderJob::AsURLLoaderJob() { |
| 93 return this; |
| 94 } |
| 95 |
| 96 void AppCacheURLLoaderJob::FollowRedirect() { |
| 97 DCHECK(false); |
| 98 } |
| 99 |
| 100 void AppCacheURLLoaderJob::SetPriority(net::RequestPriority priority, |
| 101 int32_t intra_priority_value) { |
| 102 NOTREACHED() << "We don't support SetPriority()"; |
| 103 } |
| 104 |
| 105 void AppCacheURLLoaderJob::Start(mojom::URLLoaderRequest request, |
| 106 mojom::URLLoaderClientPtr client) { |
| 107 DCHECK(!binding_.is_bound()); |
| 108 binding_.Bind(std::move(request)); |
| 109 |
| 110 binding_.set_connection_error_handler(base::Bind( |
| 111 &AppCacheURLLoaderJob::OnConnectionError, StaticAsWeakPtr(this))); |
| 112 |
| 113 client_info_ = std::move(client); |
| 114 |
| 115 // Send the cached AppCacheResponse if any. |
| 116 if (info_.get()) |
| 117 SendResponseInfo(); |
| 118 } |
| 119 |
| 120 AppCacheURLLoaderJob::AppCacheURLLoaderJob(const ResourceRequest& request, |
| 121 AppCacheStorage* storage) |
| 122 : request_(request), |
| 123 storage_(storage->GetWeakPtr()), |
| 124 start_time_tick_(base::TimeTicks::Now()), |
| 125 cache_id_(kAppCacheNoCacheId), |
| 126 is_fallback_(false), |
| 127 binding_(this), |
| 128 writable_handle_watcher_(FROM_HERE, |
| 129 mojo::SimpleWatcher::ArmingPolicy::MANUAL) {} |
| 130 |
| 131 void AppCacheURLLoaderJob::OnResponseInfoLoaded( |
| 132 AppCacheResponseInfo* response_info, |
| 133 int64_t response_id) { |
| 134 DCHECK(IsDeliveringAppCacheResponse()); |
| 135 |
| 136 if (!storage_.get()) { |
| 137 DeliverErrorResponse(); |
| 138 return; |
| 139 } |
| 140 |
| 141 if (response_info) { |
| 142 info_ = response_info; |
| 143 reader_.reset( |
| 144 storage_->CreateResponseReader(manifest_url_, entry_.response_id())); |
| 145 |
| 146 DCHECK(!loader_callback_.is_null()); |
| 147 std::move(loader_callback_) |
| 148 .Run(base::Bind(&AppCacheURLLoaderJob::Start, StaticAsWeakPtr(this))); |
| 149 |
| 150 // TODO(ananta) |
| 151 // Handle range requests. |
| 152 |
| 153 response_body_stream_ = std::move(data_pipe_.producer_handle); |
| 154 |
| 155 // TODO(ananta) |
| 156 // Move the asynchronous reading and mojo pipe handling code to a helper |
| 157 // class. That would also need a change to BlobURLLoader. |
| 158 |
| 159 // Wait for the data pipe to be ready to accept data. |
| 160 writable_handle_watcher_.Watch( |
| 161 response_body_stream_.get(), MOJO_HANDLE_SIGNAL_WRITABLE, |
| 162 base::Bind(&AppCacheURLLoaderJob::OnResponseBodyStreamReady, |
| 163 StaticAsWeakPtr(this))); |
| 164 |
| 165 if (client_info_) |
| 166 SendResponseInfo(); |
| 167 |
| 168 ReadMore(); |
| 169 } else { |
| 170 // Error case here. We fallback to the network. |
| 171 DeliverNetworkResponse(); |
| 172 AppCacheHistograms::CountResponseRetrieval( |
| 173 false, IsResourceTypeFrame(request_.resource_type), |
| 174 manifest_url_.GetOrigin()); |
| 175 |
| 176 cache_entry_not_found_ = true; |
| 177 } |
| 178 } |
| 179 |
| 180 void AppCacheURLLoaderJob::OnReadComplete(int result) { |
| 181 DLOG(WARNING) << "AppCache read completed with result: " << result; |
| 182 |
| 183 bool is_main_resource = IsResourceTypeFrame(request_.resource_type); |
| 184 |
| 185 if (result == 0) { |
| 186 NotifyCompleted(result); |
| 187 AppCacheHistograms::CountResponseRetrieval(true, is_main_resource, |
| 188 manifest_url_.GetOrigin()); |
| 189 return; |
| 190 } else if (result < 0) { |
| 191 // TODO(ananta) |
| 192 // Populate the relevant fields of the ResourceRequestCompletionStatus |
| 193 // structure. |
| 194 NotifyCompleted(result); |
| 195 AppCacheHistograms::CountResponseRetrieval(false, is_main_resource, |
| 196 manifest_url_.GetOrigin()); |
| 197 return; |
| 198 } |
| 199 |
| 200 uint32_t bytes_written = static_cast<uint32_t>(result); |
| 201 response_body_stream_ = pending_write_->Complete(bytes_written); |
| 202 pending_write_ = nullptr; |
| 203 ReadMore(); |
| 204 } |
| 205 |
| 206 void AppCacheURLLoaderJob::OnConnectionError() { |
| 207 if (storage_.get()) |
| 208 storage_->CancelDelegateCallbacks(this); |
| 209 base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this); |
| 210 } |
| 211 |
| 212 void AppCacheURLLoaderJob::SendResponseInfo() { |
| 213 DCHECK(client_info_); |
| 214 |
| 215 // If this is null it means the response information was sent to the client. |
| 216 if (!data_pipe_.consumer_handle.is_valid()) |
| 217 return; |
| 218 |
| 219 const net::HttpResponseInfo* http_info = info_->http_response_info(); |
| 220 |
| 221 ResourceResponseHead response_head; |
| 222 response_head.headers = http_info->headers; |
| 223 |
| 224 // TODO(ananta) |
| 225 // Copy more fields. |
| 226 http_info->headers->GetMimeType(&response_head.mime_type); |
| 227 http_info->headers->GetCharset(&response_head.charset); |
| 228 |
| 229 response_head.request_time = http_info->request_time; |
| 230 response_head.response_time = http_info->response_time; |
| 231 response_head.content_length = info_->response_data_size(); |
| 232 |
| 233 client_info_->OnReceiveResponse(response_head, http_info->ssl_info, |
| 234 mojom::DownloadedTempFilePtr()); |
| 235 |
| 236 client_info_->OnStartLoadingResponseBody( |
| 237 std::move(data_pipe_.consumer_handle)); |
| 238 } |
| 239 |
| 240 void AppCacheURLLoaderJob::ReadMore() { |
| 241 DCHECK(!pending_write_.get()); |
| 242 |
| 243 uint32_t num_bytes; |
| 244 // TODO: we should use the abstractions in MojoAsyncResourceHandler. |
| 245 MojoResult result = NetToMojoPendingBuffer::BeginWrite( |
| 246 &response_body_stream_, &pending_write_, &num_bytes); |
| 247 if (result == MOJO_RESULT_SHOULD_WAIT) { |
| 248 // The pipe is full. We need to wait for it to have more space. |
| 249 writable_handle_watcher_.ArmOrNotify(); |
| 250 return; |
| 251 } else if (result != MOJO_RESULT_OK) { |
| 252 // The response body stream is in a bad state. Bail. |
| 253 // TODO(ananta) |
| 254 // Add proper error handling here. |
| 255 NotifyCompleted(net::ERR_FAILED); |
| 256 writable_handle_watcher_.Cancel(); |
| 257 response_body_stream_.reset(); |
| 258 return; |
| 259 } |
| 260 |
| 261 CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes); |
| 262 scoped_refptr<NetToMojoIOBuffer> buffer = |
| 263 new NetToMojoIOBuffer(pending_write_.get()); |
| 264 |
| 265 reader_->ReadData( |
| 266 buffer.get(), info_->response_data_size(), |
| 267 base::Bind(&AppCacheURLLoaderJob::OnReadComplete, StaticAsWeakPtr(this))); |
| 268 } |
| 269 |
| 270 void AppCacheURLLoaderJob::OnResponseBodyStreamReady(MojoResult result) { |
| 271 // TODO(ananta) |
| 272 // Add proper error handling here. |
| 273 if (result != MOJO_RESULT_OK) { |
| 274 DCHECK(false); |
| 275 NotifyCompleted(net::ERR_FAILED); |
| 276 } |
| 277 ReadMore(); |
| 278 } |
| 279 |
| 280 void AppCacheURLLoaderJob::NotifyCompleted(int error_code) { |
| 281 if (storage_.get()) |
| 282 storage_->CancelDelegateCallbacks(this); |
| 283 // TODO(ananta) |
| 284 // Fill other details in the ResourceRequestCompletionStatus structure. |
| 285 ResourceRequestCompletionStatus request_complete_data; |
| 286 request_complete_data.error_code = error_code; |
| 287 client_info_->OnComplete(request_complete_data); |
| 288 } |
52 | 289 |
53 } // namespace content | 290 } // namespace content |
OLD | NEW |