Index: content/browser/appcache/appcache_url_loader_job.cc |
diff --git a/content/browser/appcache/appcache_url_loader_job.cc b/content/browser/appcache/appcache_url_loader_job.cc |
index 09f15459e3bd1a54fd050e1c1669795c9991956b..86d90fe24cec7749b2483bae97ab7a42bfdee739 100644 |
--- a/content/browser/appcache/appcache_url_loader_job.cc |
+++ b/content/browser/appcache/appcache_url_loader_job.cc |
@@ -3,51 +3,288 @@ |
// found in the LICENSE file. |
#include "content/browser/appcache/appcache_url_loader_job.h" |
-#include "content/browser/appcache/appcache_entry.h" |
+ |
+#include "base/strings/string_number_conversions.h" |
+#include "content/browser/appcache/appcache_histograms.h" |
+#include "content/common/net_adapters.h" |
+#include "content/public/common/resource_type.h" |
+#include "net/http/http_status_code.h" |
namespace content { |
-AppCacheURLLoaderJob::~AppCacheURLLoaderJob() {} |
+AppCacheURLLoaderJob::~AppCacheURLLoaderJob() { |
+ if (storage_.get()) |
+ storage_->CancelDelegateCallbacks(this); |
+} |
void AppCacheURLLoaderJob::Kill() {} |
bool AppCacheURLLoaderJob::IsStarted() const { |
- return false; |
+ return delivery_type_ != AWAITING_DELIVERY_ORDERS; |
} |
-bool AppCacheURLLoaderJob::IsWaiting() const { |
- return false; |
+void AppCacheURLLoaderJob::DeliverAppCachedResponse(const GURL& manifest_url, |
+ int64_t cache_id, |
+ const AppCacheEntry& entry, |
+ bool is_fallback) { |
+ if (!storage_.get()) { |
+ DeliverErrorResponse(); |
+ return; |
+ } |
+ |
+ delivery_type_ = APPCACHED_DELIVERY; |
+ |
+ AppCacheHistograms::AddAppCacheJobStartDelaySample(base::TimeTicks::Now() - |
+ start_time_tick_); |
+ |
+ manifest_url_ = manifest_url; |
+ cache_id_ = cache_id; |
+ entry_ = entry; |
+ is_fallback_ = is_fallback; |
+ |
+ // TODO(ananta) |
+ // Implement the AppCacheServiceImpl::Observer interface or add weak pointer |
+ // support to it. |
+ storage_->LoadResponseInfo(manifest_url_, entry_.response_id(), this); |
} |
-bool AppCacheURLLoaderJob::IsDeliveringAppCacheResponse() const { |
- return false; |
+void AppCacheURLLoaderJob::DeliverNetworkResponse() { |
+ delivery_type_ = NETWORK_DELIVERY; |
+ |
+ AppCacheHistograms::AddNetworkJobStartDelaySample(base::TimeTicks::Now() - |
+ start_time_tick_); |
+ |
+ DCHECK(!loader_callback_.is_null()); |
+ // In network service land, if we are processing a navigation request, we |
+ // need to inform the loader callback that we are not going to handle this |
+ // request. The loader callback is valid only for navigation requests. |
+ std::move(loader_callback_).Run(StartLoaderCallback()); |
} |
-bool AppCacheURLLoaderJob::IsDeliveringNetworkResponse() const { |
- return false; |
+void AppCacheURLLoaderJob::DeliverErrorResponse() { |
+ delivery_type_ = ERROR_DELIVERY; |
+ |
+ // We expect the URLLoaderClient pointer to be valid at this point. |
+ DCHECK(client_info_); |
+ |
+ // AppCacheURLRequestJob uses ERR_FAILED as the error code here. That seems |
+ // to map to HTTP_INTERNAL_SERVER_ERROR. |
+ std::string status("HTTP/1.1 "); |
+ status.append(base::IntToString(net::HTTP_INTERNAL_SERVER_ERROR)); |
+ status.append(" "); |
+ status.append(net::GetHttpReasonPhrase(net::HTTP_INTERNAL_SERVER_ERROR)); |
+ status.append("\0\0", 2); |
+ |
+ ResourceResponseHead response; |
+ response.headers = new net::HttpResponseHeaders(status); |
+ client_info_->OnReceiveResponse(response, base::nullopt, nullptr); |
+ |
+ NotifyCompleted(net::ERR_FAILED); |
+ |
+ AppCacheHistograms::AddErrorJobStartDelaySample(base::TimeTicks::Now() - |
+ start_time_tick_); |
} |
-bool AppCacheURLLoaderJob::IsDeliveringErrorResponse() const { |
- return false; |
+const GURL& AppCacheURLLoaderJob::GetURL() const { |
+ return request_.url; |
} |
-bool AppCacheURLLoaderJob::IsCacheEntryNotFound() const { |
- return false; |
+AppCacheURLLoaderJob* AppCacheURLLoaderJob::AsURLLoaderJob() { |
+ return this; |
} |
-void AppCacheURLLoaderJob::DeliverAppCachedResponse(const GURL& manifest_url, |
- int64_t cache_id, |
- const AppCacheEntry& entry, |
- bool is_fallback) {} |
+void AppCacheURLLoaderJob::FollowRedirect() { |
+ DCHECK(false); |
+} |
+ |
+void AppCacheURLLoaderJob::SetPriority(net::RequestPriority priority, |
+ int32_t intra_priority_value) { |
+ NOTREACHED() << "We don't support SetPriority()"; |
+} |
-void AppCacheURLLoaderJob::DeliverNetworkResponse() {} |
+void AppCacheURLLoaderJob::Start(mojom::URLLoaderRequest request, |
+ mojom::URLLoaderClientPtr client) { |
+ DCHECK(!binding_.is_bound()); |
+ binding_.Bind(std::move(request)); |
-void AppCacheURLLoaderJob::DeliverErrorResponse() {} |
+ binding_.set_connection_error_handler(base::Bind( |
+ &AppCacheURLLoaderJob::OnConnectionError, StaticAsWeakPtr(this))); |
-const GURL& AppCacheURLLoaderJob::GetURL() const { |
- return url_; |
+ client_info_ = std::move(client); |
+ |
+ // Send the cached AppCacheResponse if any. |
+ if (info_.get()) |
+ SendResponseInfo(); |
+} |
+ |
+AppCacheURLLoaderJob::AppCacheURLLoaderJob(const ResourceRequest& request, |
+ AppCacheStorage* storage) |
+ : request_(request), |
+ storage_(storage->GetWeakPtr()), |
+ start_time_tick_(base::TimeTicks::Now()), |
+ cache_id_(kAppCacheNoCacheId), |
+ is_fallback_(false), |
+ binding_(this), |
+ writable_handle_watcher_(FROM_HERE, |
+ mojo::SimpleWatcher::ArmingPolicy::MANUAL) {} |
+ |
+void AppCacheURLLoaderJob::OnResponseInfoLoaded( |
+ AppCacheResponseInfo* response_info, |
+ int64_t response_id) { |
+ DCHECK(IsDeliveringAppCacheResponse()); |
+ |
+ if (!storage_.get()) { |
+ DeliverErrorResponse(); |
+ return; |
+ } |
+ |
+ if (response_info) { |
+ info_ = response_info; |
+ reader_.reset( |
+ storage_->CreateResponseReader(manifest_url_, entry_.response_id())); |
+ |
+ DCHECK(!loader_callback_.is_null()); |
+ std::move(loader_callback_) |
+ .Run(base::Bind(&AppCacheURLLoaderJob::Start, StaticAsWeakPtr(this))); |
+ |
+ // TODO(ananta) |
+ // Handle range requests. |
+ |
+ response_body_stream_ = std::move(data_pipe_.producer_handle); |
+ |
+ // TODO(ananta) |
+ // Move the asynchronous reading and mojo pipe handling code to a helper |
+ // class. That would also need a change to BlobURLLoader. |
+ |
+ // Wait for the data pipe to be ready to accept data. |
+ writable_handle_watcher_.Watch( |
+ response_body_stream_.get(), MOJO_HANDLE_SIGNAL_WRITABLE, |
+ base::Bind(&AppCacheURLLoaderJob::OnResponseBodyStreamReady, |
+ StaticAsWeakPtr(this))); |
+ |
+ if (client_info_) |
+ SendResponseInfo(); |
+ |
+ ReadMore(); |
+ } else { |
+ // Error case here. We fallback to the network. |
+ DeliverNetworkResponse(); |
+ AppCacheHistograms::CountResponseRetrieval( |
+ false, IsResourceTypeFrame(request_.resource_type), |
+ manifest_url_.GetOrigin()); |
+ |
+ cache_entry_not_found_ = true; |
+ } |
+} |
+ |
+void AppCacheURLLoaderJob::OnReadComplete(int result) { |
+ DLOG(WARNING) << "AppCache read completed with result: " << result; |
+ |
+ bool is_main_resource = IsResourceTypeFrame(request_.resource_type); |
+ |
+ if (result == 0) { |
+ NotifyCompleted(result); |
+ AppCacheHistograms::CountResponseRetrieval(true, is_main_resource, |
+ manifest_url_.GetOrigin()); |
+ return; |
+ } else if (result < 0) { |
+ // TODO(ananta) |
+ // Populate the relevant fields of the ResourceRequestCompletionStatus |
+ // structure. |
+ NotifyCompleted(result); |
+ AppCacheHistograms::CountResponseRetrieval(false, is_main_resource, |
+ manifest_url_.GetOrigin()); |
+ return; |
+ } |
+ |
+ uint32_t bytes_written = static_cast<uint32_t>(result); |
+ response_body_stream_ = pending_write_->Complete(bytes_written); |
+ pending_write_ = nullptr; |
+ ReadMore(); |
} |
-AppCacheURLLoaderJob::AppCacheURLLoaderJob() {} |
+void AppCacheURLLoaderJob::OnConnectionError() { |
+ if (storage_.get()) |
+ storage_->CancelDelegateCallbacks(this); |
+ base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this); |
+} |
+ |
+void AppCacheURLLoaderJob::SendResponseInfo() { |
+ DCHECK(client_info_); |
+ |
+ // If this is null it means the response information was sent to the client. |
+ if (!data_pipe_.consumer_handle.is_valid()) |
+ return; |
+ |
+ const net::HttpResponseInfo* http_info = info_->http_response_info(); |
+ |
+ ResourceResponseHead response_head; |
+ response_head.headers = http_info->headers; |
+ |
+ // TODO(ananta) |
+ // Copy more fields. |
+ http_info->headers->GetMimeType(&response_head.mime_type); |
+ http_info->headers->GetCharset(&response_head.charset); |
+ |
+ response_head.request_time = http_info->request_time; |
+ response_head.response_time = http_info->response_time; |
+ response_head.content_length = info_->response_data_size(); |
+ |
+ client_info_->OnReceiveResponse(response_head, http_info->ssl_info, |
+ mojom::DownloadedTempFilePtr()); |
+ |
+ client_info_->OnStartLoadingResponseBody( |
+ std::move(data_pipe_.consumer_handle)); |
+} |
+ |
+void AppCacheURLLoaderJob::ReadMore() { |
+ DCHECK(!pending_write_.get()); |
+ |
+ uint32_t num_bytes; |
+ // TODO: we should use the abstractions in MojoAsyncResourceHandler. |
+ MojoResult result = NetToMojoPendingBuffer::BeginWrite( |
+ &response_body_stream_, &pending_write_, &num_bytes); |
+ if (result == MOJO_RESULT_SHOULD_WAIT) { |
+ // The pipe is full. We need to wait for it to have more space. |
+ writable_handle_watcher_.ArmOrNotify(); |
+ return; |
+ } else if (result != MOJO_RESULT_OK) { |
+ // The response body stream is in a bad state. Bail. |
+ // TODO(ananta) |
+ // Add proper error handling here. |
+ NotifyCompleted(net::ERR_FAILED); |
+ writable_handle_watcher_.Cancel(); |
+ response_body_stream_.reset(); |
+ return; |
+ } |
+ |
+ CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes); |
+ scoped_refptr<NetToMojoIOBuffer> buffer = |
+ new NetToMojoIOBuffer(pending_write_.get()); |
+ |
+ reader_->ReadData( |
+ buffer.get(), info_->response_data_size(), |
+ base::Bind(&AppCacheURLLoaderJob::OnReadComplete, StaticAsWeakPtr(this))); |
+} |
+ |
+void AppCacheURLLoaderJob::OnResponseBodyStreamReady(MojoResult result) { |
+ // TODO(ananta) |
+ // Add proper error handling here. |
+ if (result != MOJO_RESULT_OK) { |
+ DCHECK(false); |
+ NotifyCompleted(net::ERR_FAILED); |
+ } |
+ ReadMore(); |
+} |
+ |
+void AppCacheURLLoaderJob::NotifyCompleted(int error_code) { |
+ if (storage_.get()) |
+ storage_->CancelDelegateCallbacks(this); |
+ // TODO(ananta) |
+ // Fill other details in the ResourceRequestCompletionStatus structure. |
+ ResourceRequestCompletionStatus request_complete_data; |
+ request_complete_data.error_code = error_code; |
+ client_info_->OnComplete(request_complete_data); |
+} |
} // namespace content |