Index: webkit/browser/appcache/appcache_url_request_job.cc |
diff --git a/webkit/browser/appcache/appcache_url_request_job.cc b/webkit/browser/appcache/appcache_url_request_job.cc |
index 077e09d8b99df3fc55de5c3edbeda80ebdf3602d..99915d860fdb9859549408d273fa080d2f3f19f7 100644 |
--- a/webkit/browser/appcache/appcache_url_request_job.cc |
+++ b/webkit/browser/appcache/appcache_url_request_job.cc |
@@ -12,6 +12,7 @@ |
#include "base/compiler_specific.h" |
#include "base/message_loop/message_loop.h" |
#include "base/strings/string_util.h" |
+#include "base/strings/utf_string_conversions.h" |
#include "base/strings/stringprintf.h" |
#include "net/base/io_buffer.h" |
#include "net/base/net_errors.h" |
@@ -26,9 +27,27 @@ |
#include "webkit/browser/appcache/appcache_histograms.h" |
#include "webkit/browser/appcache/appcache_host.h" |
#include "webkit/browser/appcache/appcache_service.h" |
+#include "webkit/browser/blob/blob_reader.h" |
+#include "webkit/browser/blob/blob_storage_controller.h" |
namespace appcache { |
+namespace { |
+ |
+// Helper to make structs for synthesized responses |
+net::HttpResponseInfo* MakeHttpResponseInfo(const std::string& raw_headers) { |
+ net::HttpResponseInfo* info = new net::HttpResponseInfo; |
+ info->request_time = base::Time::Now(); |
+ info->response_time = base::Time::Now(); |
+ info->was_cached = false; |
+ info->headers = new net::HttpResponseHeaders(raw_headers); |
+ std::string headers; |
+ info->headers->GetNormalizedHeaders(&headers); |
+ return info; |
+} |
+ |
+} |
+ |
AppCacheURLRequestJob::AppCacheURLRequestJob( |
net::URLRequest* request, |
net::NetworkDelegate* network_delegate, |
@@ -41,7 +60,8 @@ AppCacheURLRequestJob::AppCacheURLRequestJob( |
delivery_type_(AWAITING_DELIVERY_ORDERS), |
group_id_(0), cache_id_(kNoCacheId), is_fallback_(false), |
cache_entry_not_found_(false), |
- weak_factory_(this) { |
+ weak_factory_(this), |
+ is_main_resource_load_(false) { |
DCHECK(storage_); |
} |
@@ -125,6 +145,11 @@ void AppCacheURLRequestJob::BeginDelivery() { |
manifest_url_, group_id_, entry_.response_id(), this); |
break; |
+ case SYNTHESIZED_DELIVERY: |
+ DCHECK(info_); |
+ NotifyHeadersComplete(); |
+ break; |
+ |
default: |
NOTREACHED(); |
break; |
@@ -134,11 +159,16 @@ void AppCacheURLRequestJob::BeginDelivery() { |
void AppCacheURLRequestJob::BeginExecutableHandlerDelivery() { |
DCHECK(CommandLine::ForCurrentProcess()-> |
HasSwitch(kEnableExecutableHandlers)); |
- if (!storage_->service()->handler_factory()) { |
+ if (!storage_->service()->executable_handler_factory()) { |
BeginErrorDelivery("missing handler factory"); |
return; |
} |
+ if (is_range_request()) { |
+ BeginErrorDelivery("sorry, no support for range requests yet"); |
+ return; |
+ } |
+ |
request()->net_log().AddEvent( |
net::NetLog::TYPE_APPCACHE_DELIVERING_EXECUTABLE_RESPONSE); |
@@ -166,6 +196,14 @@ void AppCacheURLRequestJob::OnCacheLoaded(AppCache* cache, int64 cache_id) { |
cache_ = cache; |
group_ = cache->owning_group(); |
+ // Detect odd ball cases where we want to return the resource instead of |
+ // executing the script. If it's an exact match for the resource, just return it. |
+ if (cache_->GetEntry(request()->url())) { |
+ entry_.clear_types(AppCacheEntry::EXECUTABLE); |
+ delivery_type_ = APPCACHED_DELIVERY; |
+ BeginDelivery(); |
+ return; |
+ } |
// If the handler is spun up, ask it to compute a response. |
AppCacheExecutableHandler* handler = |
cache->GetExecutableHandler(entry_.response_id()); |
@@ -202,11 +240,11 @@ void AppCacheURLRequestJob::OnExecutableSourceLoaded(int result) { |
return; |
} |
- handler_source_buffer_->SetCapacity(result); // Free up some memory. |
+ std::string raw_handler_source(handler_source_buffer_->data(), result); |
+ handler_source_buffer_ = NULL; // Free up some memory. |
AppCacheExecutableHandler* handler = cache_->GetOrCreateExecutableHandler( |
- entry_.response_id(), handler_source_buffer_.get()); |
- handler_source_buffer_ = NULL; // not needed anymore |
+ entry_.response_id(), raw_handler_source); |
if (handler) { |
InvokeExecutableHandler(handler); |
return; |
@@ -217,8 +255,21 @@ void AppCacheURLRequestJob::OnExecutableSourceLoaded(int result) { |
void AppCacheURLRequestJob::InvokeExecutableHandler( |
AppCacheExecutableHandler* handler) { |
+ net::URLRequest* req = request(); |
+ |
+ AppCacheExecutableHandler::Request request; |
+ request.url = req->url(); |
+ request.is_main_resource_load = is_main_resource_load_; |
+ request.method = req->method(); |
+ request.referrer = req->referrer(); |
+ net::HttpRequestHeaders headers; |
+ req->GetFullRequestHeaders(&headers); |
+ net::HttpRequestHeaders::Iterator iter(headers); |
+ while (iter.GetNext()) |
+ request.headers.insert(make_pair(iter.name(), iter.value())); |
+ |
handler->HandleRequest( |
- request(), |
+ request, |
base::Bind(&AppCacheURLRequestJob::OnExecutableResponseCallback, |
weak_factory_.GetWeakPtr())); |
} |
@@ -226,26 +277,67 @@ void AppCacheURLRequestJob::InvokeExecutableHandler( |
void AppCacheURLRequestJob::OnExecutableResponseCallback( |
const AppCacheExecutableHandler::Response& response) { |
DCHECK(!has_been_killed()); |
- if (response.use_network) { |
+ |
+ // Support for .respondWithNetwork(id) |
+ if (response.use_default) { |
delivery_type_ = NETWORK_DELIVERY; |
storage_ = NULL; |
BeginDelivery(); |
return; |
} |
- if (!response.cached_resource_url.is_empty()) { |
- AppCacheEntry* entry_ptr = cache_->GetEntry(response.cached_resource_url); |
- if (entry_ptr && !entry_ptr->IsExecutable()) { |
- entry_ = *entry_ptr; |
- BeginDelivery(); |
- return; |
+ // Support for .respondWith(blob) and .redirectTo(locataion); |
+ if (response.status_code) { |
+ // TODO(michaeln): synthesize a response with the response_code and |
+ // the blob as the body. |
+ webkit_blob::BlobStorageController* blob_storage = |
+ storage_->service()->executable_handler_factory()->GetBlobStorageController(); |
+ fileapi::FileSystemContext* filesystem_context = |
+ storage_->service()->executable_handler_factory()->GetFileSystemContext(); |
+ |
+ scoped_refptr<webkit_blob::BlobData> blob_data; |
+ if (!response.blob_id.empty()) |
+ blob_data = blob_storage->GetBlobDataFromUrl(GURL(response.blob_id)); |
+ |
+ std::string raw_headers = base::StringPrintf( |
+ "HTTP/1.0 %d %S", response.status_code, UTF16ToWide(response.status_text).data()); |
+ raw_headers.append(1, '\0'); |
+ |
+ // TODO(alecflett): content_type is totally messy because blob has |
+ // a content type, as does the response body itself. Further, the |
+ // response header may have additional information on it like |
+ // 'text/html; charset=utf-8' - this code drastically simplifies |
+ // by simply using the blob if they don't match. |
+ string16 content_type; |
+ for (std::map<string16, string16>::const_iterator iter = response.headers.begin(); |
+ iter != response.headers.end(); |
+ ++iter) { |
+ if (LowerCaseEqualsASCII(iter->first, "content-type")) |
+ content_type = iter->second; |
+ else { |
+ raw_headers.append(base::StringPrintf("%s: %s", UTF16ToUTF8(iter->first).data(), UTF16ToUTF8(iter->second).data())); |
+ raw_headers.append(1, '\0'); |
+ } |
} |
- } |
- |
- if (!response.redirect_url.is_empty()) { |
- // TODO(michaeln): playback a redirect |
- // response_headers_(new HttpResponseHeaders(response_headers)), |
- // fallthru for now to deliver an error |
+ if (blob_data) { |
+ if (content_type.empty()) |
+ content_type = UTF8ToUTF16(blob_data->content_type()); |
+ if (!EqualsASCII(content_type, blob_data->content_type())) |
+ LOG(WARNING) << "Blob and header content types don't match, using " << |
+ content_type << ". (blob is " << blob_data->content_type() << " but header is " << content_type << ")"; |
+ raw_headers.append(base::StringPrintf( |
+ "Content-Type: %s", UTF16ToUTF8(content_type).data())); |
+ |
+ raw_headers.append(1, '\0'); |
+ // TODO: content-length header too |
+ blob_reader_.reset(new webkit_blob::BlobReader( |
+ blob_data.get(), filesystem_context)); |
+ } |
+ raw_headers.append(2, '\0'); |
+ info_ = new AppCacheResponseInfo( |
+ manifest_url_, MakeHttpResponseInfo(raw_headers), 0); |
+ BeginSynthesizedDelivery(); |
+ return; |
} |
// Otherwise, return an error. |
@@ -260,6 +352,12 @@ void AppCacheURLRequestJob::BeginErrorDelivery(const char* message) { |
BeginDelivery(); |
} |
+void AppCacheURLRequestJob::BeginSynthesizedDelivery() { |
+ delivery_type_ = SYNTHESIZED_DELIVERY; |
+ storage_ = NULL; |
+ BeginDelivery(); |
+} |
+ |
AppCacheURLRequestJob::~AppCacheURLRequestJob() { |
if (storage_) |
storage_->CancelDelegateCallbacks(this); |
@@ -381,7 +479,8 @@ net::LoadState AppCacheURLRequestJob::GetLoadState() const { |
return net::LOAD_STATE_IDLE; |
if (!has_delivery_orders()) |
return net::LOAD_STATE_WAITING_FOR_APPCACHE; |
- if (delivery_type_ != APPCACHED_DELIVERY) |
+ if (delivery_type_ != APPCACHED_DELIVERY && |
+ delivery_type_ != SYNTHESIZED_DELIVERY) |
return net::LOAD_STATE_IDLE; |
if (!info_.get()) |
return net::LOAD_STATE_WAITING_FOR_APPCACHE; |
@@ -416,9 +515,21 @@ int AppCacheURLRequestJob::GetResponseCode() const { |
bool AppCacheURLRequestJob::ReadRawData(net::IOBuffer* buf, int buf_size, |
int *bytes_read) { |
- DCHECK(is_delivering_appcache_response()); |
+ DCHECK(is_delivering_appcache_response() || |
+ is_delivering_synthesized_response()); |
DCHECK_NE(buf_size, 0); |
DCHECK(bytes_read); |
+ |
+ // Branch to return a 'blob' response provided by an exe handler. |
+ if (blob_reader_) |
+ return ReadRawBlobData(buf, buf_size, bytes_read); |
+ |
+ if (!reader_) { |
+ // No raw data to read, an empty response body. |
+ *bytes_read = 0; |
+ return true; |
+ } |
+ |
DCHECK(!reader_->IsReadPending()); |
reader_->ReadData( |
buf, buf_size, base::Bind(&AppCacheURLRequestJob::OnReadComplete, |
@@ -427,6 +538,36 @@ bool AppCacheURLRequestJob::ReadRawData(net::IOBuffer* buf, int buf_size, |
return false; |
} |
+bool AppCacheURLRequestJob::ReadRawBlobData(net::IOBuffer* buf, int buf_size, |
+ int *bytes_read) { |
+ DCHECK(is_delivering_synthesized_response()); |
+ DCHECK_NE(buf_size, 0); |
+ DCHECK(bytes_read); |
+ |
+ int rv = blob_reader_->Read( |
+ buf, buf_size, base::Bind(&AppCacheURLRequestJob::OnReadBlobDataComplete, |
+ base::Unretained(this))); |
+ SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0)); |
+ if (rv != net::ERR_IO_PENDING) { |
+ base::MessageLoop::current()->PostTask( |
+ FROM_HERE, base::Bind(&AppCacheURLRequestJob::OnReadBlobDataComplete, |
+ weak_factory_.GetWeakPtr(), rv)); |
+ } |
+ return false; |
+} |
+ |
+void AppCacheURLRequestJob::OnReadBlobDataComplete(int result) { |
+ DCHECK(is_delivering_synthesized_response()); |
+ if (result == 0) |
+ NotifyDone(net::URLRequestStatus()); |
+ else if (result < 0) |
+ NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result)); |
+ else |
+ SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status |
+ NotifyReadComplete(result); |
+} |
+ |
+ |
void AppCacheURLRequestJob::SetExtraRequestHeaders( |
const net::HttpRequestHeaders& headers) { |
std::string value; |