diff options
55 files changed, 529 insertions, 238 deletions
diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc index 3330b7d..67ee526 100644 --- a/chrome/browser/automation/automation_provider.cc +++ b/chrome/browser/automation/automation_provider.cc @@ -977,7 +977,6 @@ void AutomationProvider::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_HANDLER(AutomationMsg_GetFocusedViewID, GetFocusedViewID) IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_InspectElement, HandleInspectElementRequest) - IPC_MESSAGE_HANDLER(AutomationMsg_SetFilteredInet, SetFilteredInet) IPC_MESSAGE_HANDLER(AutomationMsg_DownloadDirectory, GetDownloadDirectory) IPC_MESSAGE_HANDLER(AutomationMsg_SetProxyConfig, SetProxyConfig); IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_OpenNewBrowserWindow, @@ -2107,11 +2106,6 @@ void AutomationProvider::ReceivedInspectElementResponse(int num_resources) { } } -void AutomationProvider::SetFilteredInet(const IPC::Message& message, - bool enabled) { - chrome_browser_net::SetUrlRequestMocksEnabled(enabled); -} - class SetProxyConfigTask : public Task { public: explicit SetProxyConfigTask(net::ProxyService* proxy_service, diff --git a/chrome/browser/automation/automation_provider.h b/chrome/browser/automation/automation_provider.h index 1c92be6..0d9b08c 100644 --- a/chrome/browser/automation/automation_provider.h +++ b/chrome/browser/automation/automation_provider.h @@ -217,6 +217,7 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>, void GetShelfVisibility(int handle, bool* visible); void SetShelfVisibility(int handle, bool visible); void SetFilteredInet(const IPC::Message& message, bool enabled); + void GetFilteredInetHitCount(int* hit_count); void SetProxyConfig(const std::string& new_proxy_config); #if defined(OS_WIN) diff --git a/chrome/browser/automation/automation_resource_message_filter.cc b/chrome/browser/automation/automation_resource_message_filter.cc index 15e5325..46ee1e8 100644 --- a/chrome/browser/automation/automation_resource_message_filter.cc +++ b/chrome/browser/automation/automation_resource_message_filter.cc @@ -5,7 +5,16 @@ #include "chrome/browser/automation/automation_resource_message_filter.h" #include "base/message_loop.h" +#include "base/path_service.h" #include "chrome/browser/automation/url_request_automation_job.h" +#include "chrome/browser/net/url_request_failed_dns_job.h" +#include "chrome/browser/net/url_request_mock_http_job.h" +#include "chrome/browser/net/url_request_mock_util.h" +#include "chrome/browser/net/url_request_slow_download_job.h" +#include "chrome/browser/net/url_request_slow_http_job.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/test/automation/automation_messages.h" +#include "net/url_request/url_request_filter.h" MessageLoop* AutomationResourceMessageFilter::io_loop_ = NULL; @@ -54,7 +63,16 @@ bool AutomationResourceMessageFilter::OnMessageReceived( } } - return false; + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(AutomationResourceMessageFilter, message) + IPC_MESSAGE_HANDLER(AutomationMsg_SetFilteredInet, + OnSetFilteredInet) + IPC_MESSAGE_HANDLER(AutomationMsg_GetFilteredInetHitCount, + OnGetFilteredInetHitCount) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + return handled; } // Called on the IPC thread: @@ -142,3 +160,11 @@ bool AutomationResourceMessageFilter::LookupRegisteredRenderView( return found; } +void AutomationResourceMessageFilter::OnSetFilteredInet(bool enable) { + chrome_browser_net::SetUrlRequestMocksEnabled(enable); +} + +void AutomationResourceMessageFilter::OnGetFilteredInetHitCount( + int* hit_count) { + *hit_count = URLRequestFilter::GetInstance()->hit_count(); +} diff --git a/chrome/browser/automation/automation_resource_message_filter.h b/chrome/browser/automation/automation_resource_message_filter.h index 240bafb..ebd6464 100644 --- a/chrome/browser/automation/automation_resource_message_filter.h +++ b/chrome/browser/automation/automation_resource_message_filter.h @@ -72,6 +72,9 @@ class AutomationResourceMessageFilter static void UnRegisterRenderViewInIOThread(int renderer_pid, int renderer_id); private: + void OnSetFilteredInet(bool enable); + void OnGetFilteredInetHitCount(int* hit_count); + // A unique renderer id is a combination of renderer process id and // it's routing id. struct RendererId { diff --git a/chrome/browser/net/url_request_mock_http_job.cc b/chrome/browser/net/url_request_mock_http_job.cc index fc654be..7e0cd71 100644 --- a/chrome/browser/net/url_request_mock_http_job.cc +++ b/chrome/browser/net/url_request_mock_http_job.cc @@ -46,21 +46,13 @@ GURL URLRequestMockHTTPJob::GetMockUrl(const std::wstring& path) { FilePath URLRequestMockHTTPJob::GetOnDiskPath(const std::wstring& base_path, URLRequest* request, const std::string& scheme) { - std::wstring file_url(L"file:///"); - file_url += base_path; - const std::string& url = request->url().spec(); - // Fix up the url to be the file url we're loading from disk. - std::string host_prefix("http://"); - host_prefix.append(kMockHostname); - size_t host_prefix_len = host_prefix.length(); - if (url.compare(0, host_prefix_len, host_prefix.data(), - host_prefix_len) == 0) { - file_url += UTF8ToWide(url.substr(host_prefix_len)); - } + std::string file_url("file:///"); + file_url += WideToUTF8(base_path); + file_url += request->url().path(); // Convert the file:/// URL to a path on disk. FilePath file_path; - net::FileURLToFilePath(GURL(WideToUTF8(file_url)), &file_path); + net::FileURLToFilePath(GURL(file_url), &file_path); return file_path; } @@ -74,6 +66,13 @@ void URLRequestMockHTTPJob::GetResponseInfo(net::HttpResponseInfo* info) { GetResponseInfoConst(info); } +bool URLRequestMockHTTPJob::IsRedirectResponse(GURL* location, + int* http_status_code) { + // Override the URLRequestFileJob implementation to invoke the default one + // based on HttpResponseInfo. + return URLRequestJob::IsRedirectResponse(location, http_status_code); +} + // Private const version. void URLRequestMockHTTPJob::GetResponseInfoConst( net::HttpResponseInfo* info) const { diff --git a/chrome/browser/net/url_request_mock_http_job.h b/chrome/browser/net/url_request_mock_http_job.h index 36e6acb..92cc359 100644 --- a/chrome/browser/net/url_request_mock_http_job.h +++ b/chrome/browser/net/url_request_mock_http_job.h @@ -19,6 +19,7 @@ class URLRequestMockHTTPJob : public URLRequestFileJob { virtual bool GetMimeType(std::string* mime_type) const; virtual bool GetCharset(std::string* charset); virtual void GetResponseInfo(net::HttpResponseInfo* info); + virtual bool IsRedirectResponse(GURL* location, int* http_status_code); static URLRequest::ProtocolFactory Factory; diff --git a/chrome/browser/net/url_request_mock_util.cc b/chrome/browser/net/url_request_mock_util.cc index ad0ed09..9a559b1 100644 --- a/chrome/browser/net/url_request_mock_util.cc +++ b/chrome/browser/net/url_request_mock_util.cc @@ -6,11 +6,8 @@ #include <string> -#include "base/message_loop.h" #include "base/path_service.h" -#include "base/task.h" -#include "base/thread.h" -#include "chrome/browser/browser_process.h" +#include "chrome/browser/chrome_thread.h" #include "chrome/browser/net/url_request_failed_dns_job.h" #include "chrome/browser/net/url_request_mock_http_job.h" #include "chrome/browser/net/url_request_slow_download_job.h" @@ -18,47 +15,27 @@ #include "chrome/common/chrome_paths.h" #include "net/url_request/url_request_filter.h" -namespace { +namespace chrome_browser_net { -// Helper class for making changes to the URLRequest ProtocolFactory on the -// IO thread. -class SetUrlRequestMocksEnabledTask : public Task { - public: - explicit SetUrlRequestMocksEnabledTask(bool enabled) : enabled_(enabled) { - } +void SetUrlRequestMocksEnabled(bool enabled) { + // Since this involves changing the URLRequest ProtocolFactory, we need to + // run on the IO thread. + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); - virtual void Run() { - if (enabled_) { - URLRequestFilter::GetInstance()->ClearHandlers(); + if (enabled) { + URLRequestFilter::GetInstance()->ClearHandlers(); - URLRequestFailedDnsJob::AddUrlHandler(); - URLRequestSlowDownloadJob::AddUrlHandler(); + URLRequestFailedDnsJob::AddUrlHandler(); + URLRequestSlowDownloadJob::AddUrlHandler(); - std::wstring root_http; - PathService::Get(chrome::DIR_TEST_DATA, &root_http); - URLRequestMockHTTPJob::AddUrlHandler(root_http); - URLRequestSlowHTTPJob::AddUrlHandler(root_http); - } else { - // Revert to the default handlers. - URLRequestFilter::GetInstance()->ClearHandlers(); - } + std::wstring root_http; + PathService::Get(chrome::DIR_TEST_DATA, &root_http); + URLRequestMockHTTPJob::AddUrlHandler(root_http); + URLRequestSlowHTTPJob::AddUrlHandler(root_http); + } else { + // Revert to the default handlers. + URLRequestFilter::GetInstance()->ClearHandlers(); } - - private: - bool enabled_; - - DISALLOW_COPY_AND_ASSIGN(SetUrlRequestMocksEnabledTask); -}; - -} // namespace - -namespace chrome_browser_net { - -void SetUrlRequestMocksEnabled(bool enabled) { - // Since this involves changing the URLRequest ProtocolFactory, we want to - // run on the IO thread. - g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, - new SetUrlRequestMocksEnabledTask(enabled)); } } // namespace chrome_browser_net diff --git a/chrome/browser/renderer_host/async_resource_handler.cc b/chrome/browser/renderer_host/async_resource_handler.cc index 6b69db3..dfd1f56 100644 --- a/chrome/browser/renderer_host/async_resource_handler.cc +++ b/chrome/browser/renderer_host/async_resource_handler.cc @@ -62,10 +62,12 @@ bool AsyncResourceHandler::OnUploadProgress(int request_id, } bool AsyncResourceHandler::OnRequestRedirected(int request_id, - const GURL& new_url) { - return receiver_->Send(new ViewMsg_Resource_ReceivedRedirect(routing_id_, - request_id, - new_url)); + const GURL& new_url, + ResourceResponse* response, + bool* defer) { + *defer = true; + return receiver_->Send(new ViewMsg_Resource_ReceivedRedirect( + routing_id_, request_id, new_url, response->response_head)); } bool AsyncResourceHandler::OnResponseStarted(int request_id, diff --git a/chrome/browser/renderer_host/async_resource_handler.h b/chrome/browser/renderer_host/async_resource_handler.h index 89ecfad..17e9260 100644 --- a/chrome/browser/renderer_host/async_resource_handler.h +++ b/chrome/browser/renderer_host/async_resource_handler.h @@ -26,7 +26,8 @@ class AsyncResourceHandler : public ResourceHandler { // ResourceHandler implementation: bool OnUploadProgress(int request_id, uint64 position, uint64 size); - bool OnRequestRedirected(int request_id, const GURL& new_url); + bool OnRequestRedirected(int request_id, const GURL& new_url, + ResourceResponse* response, bool* defer); bool OnResponseStarted(int request_id, ResourceResponse* response); bool OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size, int min_size); diff --git a/chrome/browser/renderer_host/buffered_resource_handler.cc b/chrome/browser/renderer_host/buffered_resource_handler.cc index 8d0f17f..b1007fe 100644 --- a/chrome/browser/renderer_host/buffered_resource_handler.cc +++ b/chrome/browser/renderer_host/buffered_resource_handler.cc @@ -60,8 +60,11 @@ bool BufferedResourceHandler::OnUploadProgress(int request_id, } bool BufferedResourceHandler::OnRequestRedirected(int request_id, - const GURL& new_url) { - return real_handler_->OnRequestRedirected(request_id, new_url); + const GURL& new_url, + ResourceResponse* response, + bool* defer) { + return real_handler_->OnRequestRedirected( + request_id, new_url, response, defer); } bool BufferedResourceHandler::OnResponseStarted(int request_id, diff --git a/chrome/browser/renderer_host/buffered_resource_handler.h b/chrome/browser/renderer_host/buffered_resource_handler.h index e553aca..3799b99 100644 --- a/chrome/browser/renderer_host/buffered_resource_handler.h +++ b/chrome/browser/renderer_host/buffered_resource_handler.h @@ -21,7 +21,8 @@ class BufferedResourceHandler : public ResourceHandler { // ResourceHandler implementation: bool OnUploadProgress(int request_id, uint64 position, uint64 size); - bool OnRequestRedirected(int request_id, const GURL& new_url); + bool OnRequestRedirected(int request_id, const GURL& new_url, + ResourceResponse* response, bool* defer); bool OnResponseStarted(int request_id, ResourceResponse* response); bool OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size, int min_size); diff --git a/chrome/browser/renderer_host/cross_site_resource_handler.cc b/chrome/browser/renderer_host/cross_site_resource_handler.cc index 7b05a72..eb688e7 100644 --- a/chrome/browser/renderer_host/cross_site_resource_handler.cc +++ b/chrome/browser/renderer_host/cross_site_resource_handler.cc @@ -84,10 +84,13 @@ CrossSiteResourceHandler::CrossSiteResourceHandler( rdh_(resource_dispatcher_host) {} bool CrossSiteResourceHandler::OnRequestRedirected(int request_id, - const GURL& new_url) { + const GURL& new_url, + ResourceResponse* response, + bool* defer) { // We should not have started the transition before being redirected. DCHECK(!in_cross_site_transition_); - return next_handler_->OnRequestRedirected(request_id, new_url); + return next_handler_->OnRequestRedirected( + request_id, new_url, response, defer); } bool CrossSiteResourceHandler::OnResponseStarted(int request_id, diff --git a/chrome/browser/renderer_host/cross_site_resource_handler.h b/chrome/browser/renderer_host/cross_site_resource_handler.h index b741aa0..b50e641 100644 --- a/chrome/browser/renderer_host/cross_site_resource_handler.h +++ b/chrome/browser/renderer_host/cross_site_resource_handler.h @@ -21,7 +21,8 @@ class CrossSiteResourceHandler : public ResourceHandler { ResourceDispatcherHost* resource_dispatcher_host); // ResourceHandler implementation: - bool OnRequestRedirected(int request_id, const GURL& new_url); + bool OnRequestRedirected(int request_id, const GURL& new_url, + ResourceResponse* response, bool* defer); bool OnResponseStarted(int request_id, ResourceResponse* response); bool OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size, diff --git a/chrome/browser/renderer_host/download_resource_handler.cc b/chrome/browser/renderer_host/download_resource_handler.cc index 6661666..8b189bf 100644 --- a/chrome/browser/renderer_host/download_resource_handler.cc +++ b/chrome/browser/renderer_host/download_resource_handler.cc @@ -35,7 +35,9 @@ DownloadResourceHandler::DownloadResourceHandler(ResourceDispatcherHost* rdh, // Not needed, as this event handler ought to be the final resource. bool DownloadResourceHandler::OnRequestRedirected(int request_id, - const GURL& url) { + const GURL& url, + ResourceResponse* response, + bool* defer) { url_ = url; return true; } diff --git a/chrome/browser/renderer_host/download_resource_handler.h b/chrome/browser/renderer_host/download_resource_handler.h index d9afa42..e4613a8 100644 --- a/chrome/browser/renderer_host/download_resource_handler.h +++ b/chrome/browser/renderer_host/download_resource_handler.h @@ -25,7 +25,8 @@ class DownloadResourceHandler : public ResourceHandler { bool save_as); // Not needed, as this event handler ought to be the final resource. - bool OnRequestRedirected(int request_id, const GURL& url); + bool OnRequestRedirected(int request_id, const GURL& url, + ResourceResponse* response, bool* defer); // Send the download creation information to the download thread. bool OnResponseStarted(int request_id, ResourceResponse* response); diff --git a/chrome/browser/renderer_host/download_throttling_resource_handler.cc b/chrome/browser/renderer_host/download_throttling_resource_handler.cc index f3ad5bb..ee286db 100644 --- a/chrome/browser/renderer_host/download_throttling_resource_handler.cc +++ b/chrome/browser/renderer_host/download_throttling_resource_handler.cc @@ -41,10 +41,15 @@ bool DownloadThrottlingResourceHandler::OnUploadProgress(int request_id, return true; } -bool DownloadThrottlingResourceHandler::OnRequestRedirected(int request_id, - const GURL& url) { - if (download_handler_.get()) - return download_handler_->OnRequestRedirected(request_id, url); +bool DownloadThrottlingResourceHandler::OnRequestRedirected( + int request_id, + const GURL& url, + ResourceResponse* response, + bool* defer) { + if (download_handler_.get()) { + return download_handler_->OnRequestRedirected( + request_id, url, response, defer); + } url_ = url; return true; } diff --git a/chrome/browser/renderer_host/download_throttling_resource_handler.h b/chrome/browser/renderer_host/download_throttling_resource_handler.h index 5a2eea9..117b3d2 100644 --- a/chrome/browser/renderer_host/download_throttling_resource_handler.h +++ b/chrome/browser/renderer_host/download_throttling_resource_handler.h @@ -40,7 +40,8 @@ class DownloadThrottlingResourceHandler virtual bool OnUploadProgress(int request_id, uint64 position, uint64 size); - virtual bool OnRequestRedirected(int request_id, const GURL& url); + virtual bool OnRequestRedirected(int request_id, const GURL& url, + ResourceResponse* response, bool* defer); virtual bool OnResponseStarted(int request_id, ResourceResponse* response); virtual bool OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size, int min_size); diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.cc b/chrome/browser/renderer_host/resource_dispatcher_host.cc index a626f06..94bf0b6 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host.cc +++ b/chrome/browser/renderer_host/resource_dispatcher_host.cc @@ -227,6 +227,20 @@ bool ShouldServiceRequest(ChildProcessInfo::ProcessType process_type, return true; } +void PopulateResourceResponse(URLRequest* request, + FilterPolicy::Type filter_policy, + ResourceResponse* response) { + response->response_head.status = request->status(); + response->response_head.request_time = request->request_time(); + response->response_head.response_time = request->response_time(); + response->response_head.headers = request->response_headers(); + request->GetCharset(&response->response_head.charset); + response->response_head.filter_policy = filter_policy; + response->response_head.content_length = request->GetExpectedContentSize(); + response->response_head.app_cache_id = WebAppCacheContext::kNoAppCacheId; + request->GetMimeType(&response->response_head.mime_type); +} + } // namespace ResourceDispatcherHost::ResourceDispatcherHost(MessageLoop* io_loop) @@ -328,6 +342,7 @@ bool ResourceDispatcherHost::OnMessageReceived(const IPC::Message& message, IPC_MESSAGE_HANDLER(ViewHostMsg_DataReceived_ACK, OnDataReceivedACK) IPC_MESSAGE_HANDLER(ViewHostMsg_UploadProgress_ACK, OnUploadProgressACK) IPC_MESSAGE_HANDLER(ViewHostMsg_CancelRequest, OnCancelRequest) + IPC_MESSAGE_HANDLER(ViewHostMsg_FollowRedirect, OnFollowRedirect) IPC_MESSAGE_HANDLER(ViewHostMsg_ClosePage_ACK, OnClosePageACK) IPC_END_MESSAGE_MAP_EX() @@ -571,6 +586,10 @@ void ResourceDispatcherHost::OnCancelRequest(int request_id) { CancelRequest(receiver_->GetProcessId(), request_id, true, true); } +void ResourceDispatcherHost::OnFollowRedirect(int request_id) { + FollowDeferredRedirect(receiver_->GetProcessId(), request_id); +} + void ResourceDispatcherHost::OnClosePageACK( const ViewMsg_ClosePage_Params& params) { if (params.for_cross_site_transition) { @@ -731,47 +750,16 @@ void ResourceDispatcherHost::CancelRequest(int process_id, CancelRequest(process_id, request_id, from_renderer, true); } -void ResourceDispatcherHost::CancelRequest(int process_id, - int request_id, - bool from_renderer, - bool allow_delete) { +void ResourceDispatcherHost::FollowDeferredRedirect(int process_id, + int request_id) { PendingRequestList::iterator i = pending_requests_.find( GlobalRequestID(process_id, request_id)); if (i == pending_requests_.end()) { - // We probably want to remove this warning eventually, but I wanted to be - // able to notice when this happens during initial development since it - // should be rare and may indicate a bug. - DLOG(WARNING) << "Canceling a request that wasn't found"; + DLOG(WARNING) << "FollowDeferredRedirect for invalid request"; return; } - // WebKit will send us a cancel for downloads since it no longer handles them. - // In this case, ignore the cancel since we handle downloads in the browser. - ExtraRequestInfo* info = ExtraInfoForRequest(i->second); - if (!from_renderer || !info->is_download) { - if (info->login_handler) { - info->login_handler->OnRequestCancelled(); - info->login_handler = NULL; - } - if (info->ssl_client_auth_handler) { - info->ssl_client_auth_handler->OnRequestCancelled(); - info->ssl_client_auth_handler = NULL; - } - if (!i->second->is_pending() && allow_delete) { - // No io is pending, canceling the request won't notify us of anything, - // so we explicitly remove it. - // TODO(sky): removing the request in this manner means we're not - // notifying anyone. We need make sure the event handlers and others are - // notified so that everything is cleaned up properly. - RemovePendingRequest(info->process_id, info->request_id); - } else { - i->second->Cancel(); - } - } - - // Do not remove from the pending requests, as the request will still - // call AllDataReceived, and may even have more data before it does - // that. + i->second->FollowDeferredRedirect(); } bool ResourceDispatcherHost::WillSendData(int process_id, @@ -779,7 +767,7 @@ bool ResourceDispatcherHost::WillSendData(int process_id, PendingRequestList::iterator i = pending_requests_.find( GlobalRequestID(process_id, request_id)); if (i == pending_requests_.end()) { - NOTREACHED() << L"WillSendData for invalid request"; + NOTREACHED() << "WillSendData for invalid request"; return false; } @@ -968,7 +956,10 @@ void ResourceDispatcherHost::OnReceivedRedirect(URLRequest* request, return; } - if (!info->resource_handler->OnRequestRedirected(info->request_id, new_url)) + scoped_refptr<ResourceResponse> response = new ResourceResponse; + PopulateResourceResponse(request, info->filter_policy, response); + if (!info->resource_handler->OnRequestRedirected(info->request_id, new_url, + response, defer_redirect)) CancelRequest(info->process_id, info->request_id, false); } @@ -1050,17 +1041,8 @@ void ResourceDispatcherHost::OnResponseStarted(URLRequest* request) { bool ResourceDispatcherHost::CompleteResponseStarted(URLRequest* request) { ExtraRequestInfo* info = ExtraInfoForRequest(request); - scoped_refptr<ResourceResponse> response(new ResourceResponse); - - response->response_head.status = request->status(); - response->response_head.request_time = request->request_time(); - response->response_head.response_time = request->response_time(); - response->response_head.headers = request->response_headers(); - request->GetCharset(&response->response_head.charset); - response->response_head.filter_policy = info->filter_policy; - response->response_head.content_length = request->GetExpectedContentSize(); - response->response_head.app_cache_id = WebAppCacheContext::kNoAppCacheId; - request->GetMimeType(&response->response_head.mime_type); + scoped_refptr<ResourceResponse> response = new ResourceResponse; + PopulateResourceResponse(request, info->filter_policy, response); const URLRequest::UserData* d = request->GetUserData(&Blacklist::kRequestDataKey); @@ -1093,6 +1075,49 @@ bool ResourceDispatcherHost::CompleteResponseStarted(URLRequest* request) { response.get()); } +void ResourceDispatcherHost::CancelRequest(int process_id, + int request_id, + bool from_renderer, + bool allow_delete) { + PendingRequestList::iterator i = pending_requests_.find( + GlobalRequestID(process_id, request_id)); + if (i == pending_requests_.end()) { + // We probably want to remove this warning eventually, but I wanted to be + // able to notice when this happens during initial development since it + // should be rare and may indicate a bug. + DLOG(WARNING) << "Canceling a request that wasn't found"; + return; + } + + // WebKit will send us a cancel for downloads since it no longer handles them. + // In this case, ignore the cancel since we handle downloads in the browser. + ExtraRequestInfo* info = ExtraInfoForRequest(i->second); + if (!from_renderer || !info->is_download) { + if (info->login_handler) { + info->login_handler->OnRequestCancelled(); + info->login_handler = NULL; + } + if (info->ssl_client_auth_handler) { + info->ssl_client_auth_handler->OnRequestCancelled(); + info->ssl_client_auth_handler = NULL; + } + if (!i->second->is_pending() && allow_delete) { + // No io is pending, canceling the request won't notify us of anything, + // so we explicitly remove it. + // TODO(sky): removing the request in this manner means we're not + // notifying anyone. We need make sure the event handlers and others are + // notified so that everything is cleaned up properly. + RemovePendingRequest(info->process_id, info->request_id); + } else { + i->second->Cancel(); + } + } + + // Do not remove from the pending requests, as the request will still + // call AllDataReceived, and may even have more data before it does + // that. +} + int ResourceDispatcherHost::IncrementOutstandingRequestsMemoryCost( int cost, int process_id) { // Retrieve the previous value (defaulting to 0 if not found). @@ -1675,6 +1700,7 @@ bool ResourceDispatcherHost::IsResourceDispatcherHostMessage( switch (message.type()) { case ViewHostMsg_RequestResource::ID: case ViewHostMsg_CancelRequest::ID: + case ViewHostMsg_FollowRedirect::ID: case ViewHostMsg_ClosePage_ACK::ID: case ViewHostMsg_DataReceived_ACK::ID: case ViewHostMsg_DownloadProgress_ACK::ID: diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.h b/chrome/browser/renderer_host/resource_dispatcher_host.h index b75d408..5353562 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host.h +++ b/chrome/browser/renderer_host/resource_dispatcher_host.h @@ -250,6 +250,10 @@ class ResourceDispatcherHost : public URLRequest::Delegate { int request_id, bool from_renderer); + // Follows a deferred redirect for the given request. + void FollowDeferredRedirect(int process_id, + int request_id); + // Returns true if it's ok to send the data. If there are already too many // data messages pending, it pauses the request and returns false. In this // case the caller should not send the data. @@ -503,6 +507,7 @@ class ResourceDispatcherHost : public URLRequest::Delegate { void OnDataReceivedACK(int request_id); void OnUploadProgressACK(int request_id); void OnCancelRequest(int request_id); + void OnFollowRedirect(int request_id); // Returns true if the message passed in is a resource related message. static bool IsResourceDispatcherHostMessage(const IPC::Message&); diff --git a/chrome/browser/renderer_host/resource_dispatcher_host_uitest.cc b/chrome/browser/renderer_host/resource_dispatcher_host_uitest.cc index c13d3c2..d8654fd 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host_uitest.cc +++ b/chrome/browser/renderer_host/resource_dispatcher_host_uitest.cc @@ -314,4 +314,21 @@ TEST_F(ResourceDispatcherTest, CrossSiteNavigationErrorPage) { EXPECT_EQ(L"Title Of Awesomeness", tab_title); } +TEST_F(ResourceDispatcherTest, CrossOriginRedirectBlocked) { + int before = automation()->GetFilteredInetHitCount(); + CheckTitleTest(L"cross-origin-redirect-blocked.html", + L"done"); + int after = automation()->GetFilteredInetHitCount(); + // + // We expect the following URL requests from this test: + // 1- http://mock.http/cross-origin-redirect-blocked.html + // 2- http://mock.http/redirect-to-title2.html + // 3- http://mock.http/title2.html + // + // If the redirect in #2 were not blocked, we'd also see a request + // for http://mock.http:4000/title2.html. + // + EXPECT_EQ(3, after - before); +} + } // namespace diff --git a/chrome/browser/renderer_host/resource_handler.h b/chrome/browser/renderer_host/resource_handler.h index df1b8a6..76851b6 100644 --- a/chrome/browser/renderer_host/resource_handler.h +++ b/chrome/browser/renderer_host/resource_handler.h @@ -63,8 +63,12 @@ class ResourceHandler : public base::RefCounted<ResourceHandler> { return true; } - // The request was redirected to a new URL. - virtual bool OnRequestRedirected(int request_id, const GURL& url) = 0; + // The request was redirected to a new URL. |*defer| has an initial value of + // false. Set |*defer| to true to defer the redirect. The redirect may be + // followed later on via ResourceDispatcherHost::FollowDeferredRedirect. + virtual bool OnRequestRedirected(int request_id, const GURL& url, + ResourceResponse* response, + bool* defer) = 0; // Response headers and meta data are available. virtual bool OnResponseStarted(int request_id, diff --git a/chrome/browser/renderer_host/safe_browsing_resource_handler.cc b/chrome/browser/renderer_host/safe_browsing_resource_handler.cc index 0d2af57..c215262 100644 --- a/chrome/browser/renderer_host/safe_browsing_resource_handler.cc +++ b/chrome/browser/renderer_host/safe_browsing_resource_handler.cc @@ -56,8 +56,11 @@ bool SafeBrowsingResourceHandler::OnUploadProgress(int request_id, return next_handler_->OnUploadProgress(request_id, position, size); } -bool SafeBrowsingResourceHandler::OnRequestRedirected(int request_id, - const GURL& new_url) { +bool SafeBrowsingResourceHandler::OnRequestRedirected( + int request_id, + const GURL& new_url, + ResourceResponse* response, + bool* defer) { if (in_safe_browsing_check_) { Release(); in_safe_browsing_check_ = false; @@ -73,7 +76,8 @@ bool SafeBrowsingResourceHandler::OnRequestRedirected(int request_id, // Can't pause now because it's too early, so we'll do it in OnWillRead. } - return next_handler_->OnRequestRedirected(request_id, new_url); + return next_handler_->OnRequestRedirected( + request_id, new_url, response, defer); } bool SafeBrowsingResourceHandler::OnResponseStarted( diff --git a/chrome/browser/renderer_host/safe_browsing_resource_handler.h b/chrome/browser/renderer_host/safe_browsing_resource_handler.h index 44018b1..e2901e8 100644 --- a/chrome/browser/renderer_host/safe_browsing_resource_handler.h +++ b/chrome/browser/renderer_host/safe_browsing_resource_handler.h @@ -30,7 +30,8 @@ class SafeBrowsingResourceHandler : public ResourceHandler, // ResourceHandler implementation: bool OnUploadProgress(int request_id, uint64 position, uint64 size); - bool OnRequestRedirected(int request_id, const GURL& new_url); + bool OnRequestRedirected(int request_id, const GURL& new_url, + ResourceResponse* response, bool* defer); bool OnResponseStarted(int request_id, ResourceResponse* response); void OnGetHashTimeout(); bool OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size, diff --git a/chrome/browser/renderer_host/save_file_resource_handler.cc b/chrome/browser/renderer_host/save_file_resource_handler.cc index 3289df1..22f3441 100644 --- a/chrome/browser/renderer_host/save_file_resource_handler.cc +++ b/chrome/browser/renderer_host/save_file_resource_handler.cc @@ -23,7 +23,9 @@ SaveFileResourceHandler::SaveFileResourceHandler(int render_process_host_id, } bool SaveFileResourceHandler::OnRequestRedirected(int request_id, - const GURL& url) { + const GURL& url, + ResourceResponse* response, + bool* defer) { final_url_ = url; return true; } diff --git a/chrome/browser/renderer_host/save_file_resource_handler.h b/chrome/browser/renderer_host/save_file_resource_handler.h index bc3548c..9a976b2 100644 --- a/chrome/browser/renderer_host/save_file_resource_handler.h +++ b/chrome/browser/renderer_host/save_file_resource_handler.h @@ -21,7 +21,8 @@ class SaveFileResourceHandler : public ResourceHandler { // Saves the redirected URL to final_url_, we need to use the original // URL to match original request. - bool OnRequestRedirected(int request_id, const GURL& url); + bool OnRequestRedirected(int request_id, const GURL& url, + ResourceResponse* response, bool* defer); // Sends the download creation information to the download thread. bool OnResponseStarted(int request_id, ResourceResponse* response); diff --git a/chrome/browser/renderer_host/sync_resource_handler.cc b/chrome/browser/renderer_host/sync_resource_handler.cc index 01d0d8f..dd279a0 100644 --- a/chrome/browser/renderer_host/sync_resource_handler.cc +++ b/chrome/browser/renderer_host/sync_resource_handler.cc @@ -26,7 +26,16 @@ SyncResourceHandler::~SyncResourceHandler() { } bool SyncResourceHandler::OnRequestRedirected(int request_id, - const GURL& new_url) { + const GURL& new_url, + ResourceResponse* response, + bool* defer) { + // TODO(darin): It would be much better if this could live in WebCore, but + // doing so requires API changes at all levels. Similar code exists in + // WebCore/platform/network/cf/ResourceHandleCFNet.cpp :-( + if (new_url.GetOrigin() != result_.final_url.GetOrigin()) { + LOG(ERROR) << "Cross origin redirect denied"; + return false; + } result_.final_url = new_url; return true; } diff --git a/chrome/browser/renderer_host/sync_resource_handler.h b/chrome/browser/renderer_host/sync_resource_handler.h index 8aa4681..615d7473 100644 --- a/chrome/browser/renderer_host/sync_resource_handler.h +++ b/chrome/browser/renderer_host/sync_resource_handler.h @@ -20,7 +20,8 @@ class SyncResourceHandler : public ResourceHandler { IPC::Message* result_message); ~SyncResourceHandler(); - bool OnRequestRedirected(int request_id, const GURL& new_url); + bool OnRequestRedirected(int request_id, const GURL& new_url, + ResourceResponse* response, bool* defer); bool OnResponseStarted(int request_id, ResourceResponse* response); bool OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size, int min_size); diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index 96f25e6..029b3cb 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -225,10 +225,13 @@ IPC_BEGIN_MESSAGES(View) int64 /* position */, int64 /* size */) - // Sent when the request has been redirected. - IPC_MESSAGE_ROUTED2(ViewMsg_Resource_ReceivedRedirect, + // Sent when the request has been redirected. The receiver is expected to + // respond with either a FollowRedirect message (if the redirect is to be + // followed) or a CancelRequest message (if it should not be followed). + IPC_MESSAGE_ROUTED3(ViewMsg_Resource_ReceivedRedirect, int /* request_id */, - GURL /* new_url */) + GURL /* new_url */, + ResourceResponseHead) // Sent when some data from a resource request is ready. The handle should // already be mapped into the process that receives this message. @@ -834,6 +837,11 @@ IPC_BEGIN_MESSAGES(ViewHost) IPC_MESSAGE_ROUTED1(ViewHostMsg_CancelRequest, int /* request_id */) + // Follows a redirect that occured for the resource request with the ID given + // as the parameter. + IPC_MESSAGE_ROUTED1(ViewHostMsg_FollowRedirect, + int /* request_id */) + // Makes a synchronous resource request via the browser. IPC_SYNC_MESSAGE_ROUTED2_1(ViewHostMsg_SyncLoad, int /* request_id */, diff --git a/chrome/common/resource_dispatcher.cc b/chrome/common/resource_dispatcher.cc index d598b39..8167901 100644 --- a/chrome/common/resource_dispatcher.cc +++ b/chrome/common/resource_dispatcher.cc @@ -56,7 +56,7 @@ class IPCResourceLoaderBridge : public ResourceLoaderBridge { ResourceType::Type resource_type, uint32 request_context, int app_cache_context_id, - int route_id); + int routing_id); virtual ~IPCResourceLoaderBridge(); // ResourceLoaderBridge @@ -88,7 +88,7 @@ class IPCResourceLoaderBridge : public ResourceLoaderBridge { int request_id_; // The routing id used when sending IPC messages. - int route_id_; + int routing_id_; #ifdef LOG_RESOURCE_REQUESTS // indicates the URL of this resource request for help debugging @@ -110,11 +110,11 @@ IPCResourceLoaderBridge::IPCResourceLoaderBridge( ResourceType::Type resource_type, uint32 request_context, int app_cache_context_id, - int route_id) + int routing_id) : peer_(NULL), dispatcher_(dispatcher), request_id_(-1), - route_id_(route_id) { + routing_id_(routing_id) { DCHECK(dispatcher_) << "no resource dispatcher"; request_.method = method; request_.url = url; @@ -189,7 +189,7 @@ bool IPCResourceLoaderBridge::Start(Peer* peer) { request_id_ = dispatcher_->AddPendingRequest(peer_, request_.resource_type); return dispatcher_->message_sender()->Send( - new ViewHostMsg_RequestResource(route_id_, request_id_, request_)); + new ViewHostMsg_RequestResource(routing_id_, request_id_, request_)); } void IPCResourceLoaderBridge::Cancel() { @@ -200,8 +200,7 @@ void IPCResourceLoaderBridge::Cancel() { RESOURCE_LOG("Canceling request for " << url_); - dispatcher_->message_sender()->Send( - new ViewHostMsg_CancelRequest(route_id_, request_id_)); + dispatcher_->CancelPendingRequest(routing_id_, request_id_); // We can't remove the request ID from the resource dispatcher because more // data might be pending. Sending the cancel message may cause more data @@ -229,7 +228,7 @@ void IPCResourceLoaderBridge::SyncLoad(SyncLoadResponse* response) { request_id_ = MakeRequestID(); SyncLoadResult result; - IPC::Message* msg = new ViewHostMsg_SyncLoad(route_id_, request_id_, + IPC::Message* msg = new ViewHostMsg_SyncLoad(routing_id_, request_id_, request_, &result); if (!dispatcher_->message_sender()->Send(msg)) { response->status.set_status(URLRequestStatus::FAILED); @@ -351,7 +350,7 @@ void ResourceDispatcher::OnReceivedData(const IPC::Message& message, int request_id, base::SharedMemoryHandle shm_handle, int data_len) { - // Acknowlegde the reception of this data. + // Acknowledge the reception of this data. message_sender()->Send( new ViewHostMsg_DataReceived_ACK(message.routing_id(), request_id)); @@ -377,8 +376,11 @@ void ResourceDispatcher::OnReceivedData(const IPC::Message& message, } } -void ResourceDispatcher::OnReceivedRedirect(int request_id, - const GURL& new_url) { +void ResourceDispatcher::OnReceivedRedirect( + const IPC::Message& message, + int request_id, + const GURL& new_url, + const webkit_glue::ResourceLoaderBridge::ResponseInfo& info) { PendingRequestList::iterator it = pending_requests_.find(request_id); if (it == pending_requests_.end()) { // this might happen for kill()ed requests on the webkit end, so perhaps @@ -391,7 +393,13 @@ void ResourceDispatcher::OnReceivedRedirect(int request_id, RESOURCE_LOG("Dispatching redirect for " << request_info.peer->GetURLForDebugging()); - request_info.peer->OnReceivedRedirect(new_url); + + if (request_info.peer->OnReceivedRedirect(new_url, info)) { + message_sender()->Send( + new ViewHostMsg_FollowRedirect(message.routing_id(), request_id)); + } else { + CancelPendingRequest(message.routing_id(), request_id); + } } void ResourceDispatcher::OnRequestComplete(int request_id, @@ -461,10 +469,26 @@ bool ResourceDispatcher::RemovePendingRequest(int request_id) { return true; } +void ResourceDispatcher::CancelPendingRequest(int routing_id, + int request_id) { + PendingRequestList::iterator it = pending_requests_.find(request_id); + if (it == pending_requests_.end()) { + DLOG(ERROR) << "unknown request"; + return; + } + PendingRequestInfo& request_info = it->second; + // Avoid spamming the host with cancel messages. + if (request_info.is_cancelled) + return; + request_info.is_cancelled = true; + message_sender()->Send( + new ViewHostMsg_CancelRequest(routing_id, request_id)); +} + void ResourceDispatcher::SetDefersLoading(int request_id, bool value) { PendingRequestList::iterator it = pending_requests_.find(request_id); if (it == pending_requests_.end()) { - NOTREACHED() << "unknown request"; + DLOG(ERROR) << "unknown request"; return; } PendingRequestInfo& request_info = it->second; diff --git a/chrome/common/resource_dispatcher.h b/chrome/common/resource_dispatcher.h index 2424e2d..a8a9a65 100644 --- a/chrome/common/resource_dispatcher.h +++ b/chrome/common/resource_dispatcher.h @@ -46,7 +46,7 @@ class ResourceDispatcher { ResourceType::Type resource_type, uint32 request_context /* used for plugin->browser requests */, int app_cache_context_id, - int route_id); + int routing_id); // Adds a request from the pending_requests_ list, returning the new // requests' ID @@ -57,6 +57,9 @@ class ResourceDispatcher { // request was found and removed. bool RemovePendingRequest(int request_id); + // Cancels a request in the pending_requests_ list. + void CancelPendingRequest(int routing_id, int request_id); + IPC::Message::Sender* message_sender() const { return message_sender_; } @@ -75,7 +78,8 @@ class ResourceDispatcher { : peer(peer), resource_type(resource_type), filter_policy(FilterPolicy::DONT_FILTER), - is_deferred(false) { + is_deferred(false), + is_cancelled(false) { } ~PendingRequestInfo() { } webkit_glue::ResourceLoaderBridge::Peer* peer; @@ -83,23 +87,31 @@ class ResourceDispatcher { FilterPolicy::Type filter_policy; MessageQueue deferred_message_queue; bool is_deferred; + bool is_cancelled; }; typedef base::hash_map<int, PendingRequestInfo> PendingRequestList; // Message response handlers, called by the message handler for this process. - void OnUploadProgress(const IPC::Message& message, - int request_id, - int64 position, - int64 size); + void OnUploadProgress( + const IPC::Message& message, + int request_id, + int64 position, + int64 size); void OnReceivedResponse(int request_id, const ResourceResponseHead&); - void OnReceivedRedirect(int request_id, const GURL& new_url); - void OnReceivedData(const IPC::Message& message, - int request_id, - base::SharedMemoryHandle data, - int data_len); - void OnRequestComplete(int request_id, - const URLRequestStatus& status, - const std::string& security_info); + void OnReceivedRedirect( + const IPC::Message& message, + int request_id, + const GURL& new_url, + const webkit_glue::ResourceLoaderBridge::ResponseInfo& info); + void OnReceivedData( + const IPC::Message& message, + int request_id, + base::SharedMemoryHandle data, + int data_len); + void OnRequestComplete( + int request_id, + const URLRequestStatus& status, + const std::string& security_info); // Dispatch the message to one of the message response handlers. void DispatchMessage(const IPC::Message& message); diff --git a/chrome/common/resource_dispatcher_unittest.cc b/chrome/common/resource_dispatcher_unittest.cc index 5a8ebf3..e4cdf50 100644 --- a/chrome/common/resource_dispatcher_unittest.cc +++ b/chrome/common/resource_dispatcher_unittest.cc @@ -31,7 +31,10 @@ class TestRequestCallback : public ResourceLoaderBridge::Peer { TestRequestCallback() : complete_(false) { } - virtual void OnReceivedRedirect(const GURL& new_url) { + virtual bool OnReceivedRedirect( + const GURL& new_url, + const ResourceLoaderBridge::ResponseInfo& info) { + return true; } virtual void OnReceivedResponse( diff --git a/chrome/common/security_filter_peer.cc b/chrome/common/security_filter_peer.cc index f76031f..5369199 100644 --- a/chrome/common/security_filter_peer.cc +++ b/chrome/common/security_filter_peer.cc @@ -103,8 +103,11 @@ void SecurityFilterPeer::OnUploadProgress(uint64 position, uint64 size) { original_peer_->OnUploadProgress(position, size); } -void SecurityFilterPeer::OnReceivedRedirect(const GURL& new_url) { +bool SecurityFilterPeer::OnReceivedRedirect( + const GURL& new_url, + const webkit_glue::ResourceLoaderBridge::ResponseInfo& info) { NOTREACHED(); + return false; } void SecurityFilterPeer::OnReceivedResponse( diff --git a/chrome/common/security_filter_peer.h b/chrome/common/security_filter_peer.h index ec90bb5..e9ea54f 100644 --- a/chrome/common/security_filter_peer.h +++ b/chrome/common/security_filter_peer.h @@ -39,7 +39,9 @@ class SecurityFilterPeer : public webkit_glue::ResourceLoaderBridge::Peer { // ResourceLoaderBridge::Peer methods. virtual void OnUploadProgress(uint64 position, uint64 size); - virtual void OnReceivedRedirect(const GURL& new_url); + virtual bool OnReceivedRedirect( + const GURL& new_url, + const webkit_glue::ResourceLoaderBridge::ResponseInfo& info); virtual void OnReceivedResponse( const webkit_glue::ResourceLoaderBridge::ResponseInfo& info, bool content_filtered); diff --git a/chrome/plugin/chrome_plugin_host.cc b/chrome/plugin/chrome_plugin_host.cc index 81a128a..a5c7dfa 100644 --- a/chrome/plugin/chrome_plugin_host.cc +++ b/chrome/plugin/chrome_plugin_host.cc @@ -66,9 +66,12 @@ class PluginRequestHandlerProxy upload_progress(cprequest_.get(), position, size); } - virtual void OnReceivedRedirect(const GURL& new_url) { + virtual bool OnReceivedRedirect( + const GURL& new_url, + const ResourceLoaderBridge::ResponseInfo& info) { plugin_->functions().response_funcs->received_redirect( cprequest_.get(), new_url.spec().c_str()); + return true; } virtual void OnReceivedResponse( diff --git a/chrome/test/automation/automation_messages_internal.h b/chrome/test/automation/automation_messages_internal.h index 9d67065..b2b3f02 100644 --- a/chrome/test/automation/automation_messages_internal.h +++ b/chrome/test/automation/automation_messages_internal.h @@ -982,4 +982,10 @@ IPC_BEGIN_MESSAGES(Automation) IPC_MESSAGE_ROUTED1(AutomationMsg_StopAsync, int /* tab handle */) + + // Returns the number of times a filter was used to service an URL request. + // See AutomationMsg_SetFilteredInet. + IPC_SYNC_MESSAGE_ROUTED0_1(AutomationMsg_GetFilteredInetHitCount, + int /* hit_count */) + IPC_END_MESSAGES(Automation) diff --git a/chrome/test/automation/automation_proxy.cc b/chrome/test/automation/automation_proxy.cc index dc94c18..70e4b33 100644 --- a/chrome/test/automation/automation_proxy.cc +++ b/chrome/test/automation/automation_proxy.cc @@ -371,6 +371,13 @@ bool AutomationProxy::SetFilteredInet(bool enabled) { return Send(new AutomationMsg_SetFilteredInet(0, enabled)); } +int AutomationProxy::GetFilteredInetHitCount() { + int hit_count; + if (!Send(new AutomationMsg_GetFilteredInetHitCount(0, &hit_count))) + return -1; + return hit_count; +} + bool AutomationProxy::SendProxyConfig(const std::string& new_proxy_config) { return Send(new AutomationMsg_SetProxyConfig(0, new_proxy_config)); } diff --git a/chrome/test/automation/automation_proxy.h b/chrome/test/automation/automation_proxy.h index 186d7bf..5bb5ebf 100644 --- a/chrome/test/automation/automation_proxy.h +++ b/chrome/test/automation/automation_proxy.h @@ -150,6 +150,10 @@ class AutomationProxy : public IPC::Channel::Listener, // false if the message fails to send to the browser. bool SetFilteredInet(bool enabled); + // Returns the number of times a network request filter was used to service a + // network request. Returns -1 on error. + int GetFilteredInetHitCount(); + // Sends the browser a new proxy configuration to start using. Returns true // if the proxy config was successfully sent, false otherwise. bool SendProxyConfig(const std::string& new_proxy_config); diff --git a/chrome/test/data/cross-origin-redirect-blocked.html b/chrome/test/data/cross-origin-redirect-blocked.html new file mode 100644 index 0000000..3e9cdd4 --- /dev/null +++ b/chrome/test/data/cross-origin-redirect-blocked.html @@ -0,0 +1,53 @@ +<html> +<head> +<!-- avoid an automatically generated favicon reqeuest --> +<link rel="icon" type="image/vnd.microsoft.icon" href="data:,"> +</head> +<body> +<script> + +function NewXHR(url) { + var r = new XMLHttpRequest + r.open("GET", url); + return r; +} + +function SignalDone() { + document.title = "done"; +} + +function CreateDummyRequest() { + dummy_request = NewXHR("http://mock.http/title2.html"); + dummy_request.onload = SignalDone; + dummy_request.send(null); +} + +function RedirectFailed() { + // Good, the redirect was blocked by WebKit. + // + // We also care that the underlying network stack does not send the redirect. + // We cannot detect that from JS, but our test harness is designed to detect + // that (see ResourceDispatcherTest::CrossOriginRedirectBlocked). Before + // calling SignalDone, we want to allow the browser time to notice a request + // to follow the redirect if one should exist. To do that, we just need to + // make another network request. + // + // The setTimeout call is intended to delay CreateDummyRequest so that any + // processing associated with the current "error" handler completes. + setTimeout(CreateDummyRequest, 0); +} + +function RedirectSucceeded() { + // Oops, the redirect should have been denied! + SignalDone(); +} + +// Kick off a request that will attempt a cross-origin redirect. +request = NewXHR("http://mock.http/redirect-to-title2.html"); +request.onerror = RedirectFailed; +request.onload = RedirectSucceeded; +request.send(null); + +</script> +</body> +</html> diff --git a/chrome/test/data/redirect-to-title2.html b/chrome/test/data/redirect-to-title2.html new file mode 100644 index 0000000..7898192 --- /dev/null +++ b/chrome/test/data/redirect-to-title2.html @@ -0,0 +1 @@ +a diff --git a/chrome/test/data/redirect-to-title2.html.mock-http-headers b/chrome/test/data/redirect-to-title2.html.mock-http-headers new file mode 100644 index 0000000..43a377c --- /dev/null +++ b/chrome/test/data/redirect-to-title2.html.mock-http-headers @@ -0,0 +1,2 @@ +HTTP/1.1 302 Moved +Location: http://mock.http:4000/title2.html diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc index 0f8ec34..9f6e229 100644 --- a/net/url_request/url_request.cc +++ b/net/url_request/url_request.cc @@ -11,6 +11,7 @@ #include "base/string_util.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" +#include "net/base/ssl_cert_request_info.h" #include "net/base/upload_data.h" #include "net/http/http_response_headers.h" #include "net/http/http_util.h" @@ -271,10 +272,7 @@ void URLRequest::Restart() { void URLRequest::RestartWithJob(URLRequestJob *job) { DCHECK(job->request() == this); - job_->Kill(); - OrphanJob(); - status_ = URLRequestStatus(); - is_pending_ = false; + PrepareToRestart(); StartJob(job); } @@ -387,6 +385,17 @@ void URLRequest::ContinueDespiteLastError() { job_->ContinueDespiteLastError(); } +void URLRequest::PrepareToRestart() { + DCHECK(job_); + + job_->Kill(); + OrphanJob(); + + response_info_ = net::HttpResponseInfo(); + status_ = URLRequestStatus(); + is_pending_ = false; +} + void URLRequest::OrphanJob() { job_->Kill(); job_->DetachRequest(); // ensures that the job will not call us again @@ -446,15 +455,10 @@ int URLRequest::Redirect(const GURL& location, int http_status_code) { extra_request_headers_ = StripPostSpecificHeaders(extra_request_headers_); } - if (!final_upload_progress_) { + if (!final_upload_progress_) final_upload_progress_ = job_->GetUploadProgress(); - } - job_->Kill(); - OrphanJob(); - - status_ = URLRequestStatus(); - is_pending_ = false; + PrepareToRestart(); Start(); return net::OK; } diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h index b52fa3b..e9539cb 100644 --- a/net/url_request/url_request.h +++ b/net/url_request/url_request.h @@ -521,7 +521,11 @@ class URLRequest { friend class URLRequestJob; void StartJob(URLRequestJob* job); + + // Restarting involves replacing the current job with a new one such as what + // happens when following a HTTP redirect. void RestartWithJob(URLRequestJob *job); + void PrepareToRestart(); // Detaches the job from this request in preparation for this object going // away or the job being replaced. The job will not call us back when it has diff --git a/net/url_request/url_request_filter.cc b/net/url_request/url_request_filter.cc index 189d8db..c62e96d 100644 --- a/net/url_request/url_request_filter.cc +++ b/net/url_request/url_request_filter.cc @@ -105,6 +105,7 @@ void URLRequestFilter::ClearHandlers() { url_handler_map_.clear(); hostname_handler_map_.clear(); + hit_count_ = 0; } URLRequestJob* URLRequestFilter::FindRequestHandler(URLRequest* request, @@ -127,5 +128,9 @@ URLRequestJob* URLRequestFilter::FindRequestHandler(URLRequest* request, job = i->second(request, scheme); } } + if (job) { + DLOG(INFO) << "URLRequestFilter hit for " << request->url().spec(); + hit_count_++; + } return job; } diff --git a/net/url_request/url_request_filter.h b/net/url_request/url_request_filter.h index 2b74277..3f255b8 100644 --- a/net/url_request/url_request_filter.h +++ b/net/url_request/url_request_filter.h @@ -54,11 +54,14 @@ class URLRequestFilter { void RemoveUrlHandler(const GURL& url); // Clear all the existing URL handlers and unregister with the - // ProtocolFactory. + // ProtocolFactory. Resets the hit count. void ClearHandlers(); + // Returns the number of times a handler was used to service a request. + int hit_count() const { return hit_count_; } + protected: - URLRequestFilter() { } + URLRequestFilter() : hit_count_(0) { } // Helper method that looks up the request in the url_handler_map_. URLRequestJob* FindRequestHandler(URLRequest* request, @@ -70,6 +73,8 @@ class URLRequestFilter { // Maps URLs to factories. UrlHandlerMap url_handler_map_; + int hit_count_; + private: // Singleton instance. static URLRequestFilter* shared_instance_; diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc index cf2b63c..ec3301b 100644 --- a/net/url_request/url_request_http_job.cc +++ b/net/url_request/url_request_http_job.cc @@ -239,20 +239,6 @@ bool URLRequestHttpJob::IsSdchResponse() const { return sdch_dictionary_advertised_; } -bool URLRequestHttpJob::IsRedirectResponse(GURL* location, - int* http_status_code) { - if (!response_info_) - return false; - - std::string value; - if (!response_info_->headers->IsRedirect(&value)) - return false; - - *location = request_->url().Resolve(value); - *http_status_code = response_info_->headers->response_code(); - return true; -} - bool URLRequestHttpJob::IsSafeRedirect(const GURL& location) { // We only allow redirects to certain "safe" protocols. This does not // restrict redirects to externally handled protocols. Our consumer would diff --git a/net/url_request/url_request_http_job.h b/net/url_request/url_request_http_job.h index 3ebc05c..fdb32ae 100644 --- a/net/url_request/url_request_http_job.h +++ b/net/url_request/url_request_http_job.h @@ -47,7 +47,6 @@ class URLRequestHttpJob : public URLRequestJob { std::vector<Filter::FilterType>* encoding_type); virtual bool IsCachedContent() const { return is_cached_content_; } virtual bool IsSdchResponse() const; - virtual bool IsRedirectResponse(GURL* location, int* http_status_code); virtual bool IsSafeRedirect(const GURL& location); virtual bool NeedsAuth(); virtual void GetAuthChallengeInfo(scoped_refptr<net::AuthChallengeInfo>*); diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc index f9043cc..4518d8a 100644 --- a/net/url_request/url_request_job.cc +++ b/net/url_request/url_request_job.cc @@ -10,6 +10,7 @@ #include "net/base/io_buffer.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" +#include "net/http/http_response_headers.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_job_metrics.h" #include "net/url_request/url_request_job_tracker.h" @@ -70,6 +71,22 @@ void URLRequestJob::SetupFilter() { } } +bool URLRequestJob::IsRedirectResponse(GURL* location, + int* http_status_code) { + // For non-HTTP jobs, headers will be null. + net::HttpResponseHeaders* headers = request_->response_headers(); + if (!headers) + return false; + + std::string value; + if (!headers->IsRedirect(&value)) + return false; + + *location = request_->url().Resolve(value); + *http_status_code = headers->response_code(); + return true; +} + void URLRequestJob::GetAuthChallengeInfo( scoped_refptr<net::AuthChallengeInfo>* auth_info) { // This will only be called if NeedsAuth() returns true, in which diff --git a/net/url_request/url_request_job.h b/net/url_request/url_request_job.h index 8e56bea..60554ed 100644 --- a/net/url_request/url_request_job.h +++ b/net/url_request/url_request_job.h @@ -148,10 +148,9 @@ class URLRequestJob : public base::RefCountedThreadSafe<URLRequestJob>, // The caller is responsible for following the redirect by setting up an // appropriate replacement Job. Note that the redirected location may be // invalid, the caller should be sure it can handle this. - virtual bool IsRedirectResponse(GURL* location, - int* http_status_code) { - return false; - } + // + // The default implementation inspects the response_info_. + virtual bool IsRedirectResponse(GURL* location, int* http_status_code); // Called to determine if it is okay to redirect this job to the specified // location. This may be used to implement protocol-specific restrictions. diff --git a/webkit/glue/media/buffered_data_source.cc b/webkit/glue/media/buffered_data_source.cc index 4fc3549..738f30d 100644 --- a/webkit/glue/media/buffered_data_source.cc +++ b/webkit/glue/media/buffered_data_source.cc @@ -194,7 +194,9 @@ void BufferedResourceLoader::Read(int64 position, ///////////////////////////////////////////////////////////////////////////// // BufferedResourceLoader, // webkit_glue::ResourceLoaderBridge::Peer implementations -void BufferedResourceLoader::OnReceivedRedirect(const GURL& new_url) { +bool BufferedResourceLoader::OnReceivedRedirect( + const GURL& new_url, + const webkit_glue::ResourceLoaderBridge::ResponseInfo& info) { DCHECK(bridge_.get()); DCHECK(start_callback_.get()); @@ -206,6 +208,8 @@ void BufferedResourceLoader::OnReceivedRedirect(const GURL& new_url) { DoneStart(net::ERR_ADDRESS_INVALID); Stop(); } + + return true; } void BufferedResourceLoader::OnReceivedResponse( diff --git a/webkit/glue/media/buffered_data_source.h b/webkit/glue/media/buffered_data_source.h index 6dd51a7..ef46477 100644 --- a/webkit/glue/media/buffered_data_source.h +++ b/webkit/glue/media/buffered_data_source.h @@ -86,7 +86,9 @@ class BufferedResourceLoader : public webkit_glue::ResourceLoaderBridge::Peer { ///////////////////////////////////////////////////////////////////////////// // webkit_glue::ResourceLoaderBridge::Peer implementations. virtual void OnUploadProgress(uint64 position, uint64 size) {} - virtual void OnReceivedRedirect(const GURL& new_url); + virtual bool OnReceivedRedirect( + const GURL& new_url, + const webkit_glue::ResourceLoaderBridge::ResponseInfo& info); virtual void OnReceivedResponse( const webkit_glue::ResourceLoaderBridge::ResponseInfo& info, bool content_filtered); diff --git a/webkit/glue/media/simple_data_source.cc b/webkit/glue/media/simple_data_source.cc index 48c7138..639aa5f 100644 --- a/webkit/glue/media/simple_data_source.cc +++ b/webkit/glue/media/simple_data_source.cc @@ -109,8 +109,11 @@ void SimpleDataSource::OnDownloadProgress(uint64 position, uint64 size) {} void SimpleDataSource::OnUploadProgress(uint64 position, uint64 size) {} -void SimpleDataSource::OnReceivedRedirect(const GURL& new_url) { +bool SimpleDataSource::OnReceivedRedirect( + const GURL& new_url, + const webkit_glue::ResourceLoaderBridge::ResponseInfo& info) { SetURL(new_url); + return true; } void SimpleDataSource::OnReceivedResponse( diff --git a/webkit/glue/media/simple_data_source.h b/webkit/glue/media/simple_data_source.h index acfc503..e458c57 100644 --- a/webkit/glue/media/simple_data_source.h +++ b/webkit/glue/media/simple_data_source.h @@ -49,7 +49,9 @@ class SimpleDataSource : public media::DataSource, // webkit_glue::ResourceLoaderBridge::Peer implementation. virtual void OnDownloadProgress(uint64 position, uint64 size); virtual void OnUploadProgress(uint64 position, uint64 size); - virtual void OnReceivedRedirect(const GURL& new_url); + virtual bool OnReceivedRedirect( + const GURL& new_url, + const webkit_glue::ResourceLoaderBridge::ResponseInfo& info); virtual void OnReceivedResponse( const webkit_glue::ResourceLoaderBridge::ResponseInfo& info, bool content_filtered); diff --git a/webkit/glue/resource_loader_bridge.h b/webkit/glue/resource_loader_bridge.h index 2d7ba77..b76fd56 100644 --- a/webkit/glue/resource_loader_bridge.h +++ b/webkit/glue/resource_loader_bridge.h @@ -105,8 +105,12 @@ class ResourceLoaderBridge { // note: only for requests with LOAD_ENABLE_UPLOAD_PROGRESS set virtual void OnUploadProgress(uint64 position, uint64 size) = 0; - // Called when a redirect occurs. - virtual void OnReceivedRedirect(const GURL& new_url) = 0; + // Called when a redirect occurs. The implementation may return false to + // suppress the redirect. The given ResponseInfo provides complete + // information about the redirect, and new_url is the URL that will be + // loaded if this method returns true. + virtual bool OnReceivedRedirect(const GURL& new_url, + const ResponseInfo& info) = 0; // Called when response headers are available (after all redirects have // been followed). |content_filtered| is set to true if the contents is diff --git a/webkit/glue/weburlloader_impl.cc b/webkit/glue/weburlloader_impl.cc index d0158f6..3041049 100644 --- a/webkit/glue/weburlloader_impl.cc +++ b/webkit/glue/weburlloader_impl.cc @@ -205,7 +205,8 @@ class WebURLLoaderImpl::Context : public base::RefCounted<Context>, // ResourceLoaderBridge::Peer methods: virtual void OnUploadProgress(uint64 position, uint64 size); - virtual void OnReceivedRedirect(const GURL& new_url); + virtual bool OnReceivedRedirect( + const GURL& new_url, const ResourceLoaderBridge::ResponseInfo& info); virtual void OnReceivedResponse( const ResourceLoaderBridge::ResponseInfo& info, bool content_filtered); virtual void OnReceivedData(const char* data, int len); @@ -371,30 +372,32 @@ void WebURLLoaderImpl::Context::OnUploadProgress(uint64 position, uint64 size) { client_->didSendData(loader_, position, size); } -void WebURLLoaderImpl::Context::OnReceivedRedirect(const GURL& new_url) { +bool WebURLLoaderImpl::Context::OnReceivedRedirect( + const GURL& new_url, + const ResourceLoaderBridge::ResponseInfo& info) { if (!client_) - return; + return false; - // TODO(darin): We lack sufficient information to construct the - // actual redirect response, so we just simulate it here. - WebURLResponse response(url_); + WebURLResponse response; + response.initialize(); + PopulateURLResponse(url_, info, &response); - // TODO(darin): We lack sufficient information to construct the - // actual request that resulted from the redirect, so we just - // report a GET navigation to the new location. + // TODO(darin): We lack sufficient information to construct the actual + // request that resulted from the redirect, so we just report a GET + // navigation to the new location. WebURLRequest new_request(new_url); url_ = new_url; client_->willSendRequest(loader_, new_request, response); - // TODO(darin): since new_request is sent as a mutable reference, it is - // possible that willSendRequest may have modified it. - // - // andresca on #webkit confirms that that is intentional, so we'll need - // to rework the ResourceLoaderBridge to give us control over what URL - // is really loaded (and with what headers) when a redirect is encountered. - // TODO(darin): we fail this assertion in some layout tests! - //DCHECK(GURL(new_request.url()) == new_url); + // Only follow the redirect if WebKit left the URL unmodified. + if (url_ == new_request.url()) + return true; + + // We assume that WebKit only changes the URL to suppress a redirect, and we + // assume that it does so by setting it to be invalid. + DCHECK(!new_request.url().isValid()); + return false; } void WebURLLoaderImpl::Context::OnReceivedResponse( diff --git a/webkit/tools/test_shell/simple_resource_loader_bridge.cc b/webkit/tools/test_shell/simple_resource_loader_bridge.cc index c11bcec..a7d27a0 100644 --- a/webkit/tools/test_shell/simple_resource_loader_bridge.cc +++ b/webkit/tools/test_shell/simple_resource_loader_bridge.cc @@ -151,9 +151,14 @@ class RequestProxy : public URLRequest::Delegate, // various URLRequest callbacks. The event hooks, defined below, trigger // these methods asynchronously. - void NotifyReceivedRedirect(const GURL& new_url) { - if (peer_) - peer_->OnReceivedRedirect(new_url); + void NotifyReceivedRedirect(const GURL& new_url, + const ResourceLoaderBridge::ResponseInfo& info) { + if (peer_ && peer_->OnReceivedRedirect(new_url, info)) { + io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + this, &RequestProxy::AsyncFollowDeferredRedirect)); + } else { + Cancel(); + } } void NotifyReceivedResponse(const ResourceLoaderBridge::ResponseInfo& info, @@ -230,6 +235,14 @@ class RequestProxy : public URLRequest::Delegate, Done(); } + void AsyncFollowDeferredRedirect() { + // This can be null in cases where the request is already done. + if (!request_.get()) + return; + + request_->FollowDeferredRedirect(); + } + void AsyncReadData() { // This can be null in cases where the request is already done. if (!request_.get()) @@ -252,9 +265,13 @@ class RequestProxy : public URLRequest::Delegate, // callbacks) that run on the IO thread. They are designed to be overridden // by the SyncRequestProxy subclass. - virtual void OnReceivedRedirect(const GURL& new_url) { + virtual void OnReceivedRedirect( + const GURL& new_url, + const ResourceLoaderBridge::ResponseInfo& info, + bool* defer_redirect) { + *defer_redirect = true; // See AsyncFollowDeferredRedirect owner_loop_->PostTask(FROM_HERE, NewRunnableMethod( - this, &RequestProxy::NotifyReceivedRedirect, new_url)); + this, &RequestProxy::NotifyReceivedRedirect, new_url, info)); } virtual void OnReceivedResponse( @@ -282,19 +299,15 @@ class RequestProxy : public URLRequest::Delegate, const GURL& new_url, bool* defer_redirect) { DCHECK(request->status().is_success()); - OnReceivedRedirect(new_url); + ResourceLoaderBridge::ResponseInfo info; + PopulateResponseInfo(request, &info); + OnReceivedRedirect(new_url, info, defer_redirect); } virtual void OnResponseStarted(URLRequest* request) { if (request->status().is_success()) { ResourceLoaderBridge::ResponseInfo info; - info.request_time = request->request_time(); - info.response_time = request->response_time(); - info.headers = request->response_headers(); - info.app_cache_id = WebAppCacheContext::kNoAppCacheId; - request->GetMimeType(&info.mime_type); - request->GetCharset(&info.charset); - info.content_length = request->GetExpectedContentSize(); + PopulateResponseInfo(request, &info); OnReceivedResponse(info, false); AsyncReadData(); // start reading } else { @@ -358,6 +371,17 @@ class RequestProxy : public URLRequest::Delegate, } } + void PopulateResponseInfo(URLRequest* request, + ResourceLoaderBridge::ResponseInfo* info) const { + info->request_time = request->request_time(); + info->response_time = request->response_time(); + info->headers = request->response_headers(); + info->app_cache_id = WebAppCacheContext::kNoAppCacheId; + request->GetMimeType(&info->mime_type); + request->GetCharset(&info->charset); + info->content_length = request->GetExpectedContentSize(); + } + scoped_ptr<URLRequest> request_; // Size of our async IO data buffers @@ -397,7 +421,18 @@ class SyncRequestProxy : public RequestProxy { // -------------------------------------------------------------------------- // Event hooks that run on the IO thread: - virtual void OnReceivedRedirect(const GURL& new_url) { + virtual void OnReceivedRedirect( + const GURL& new_url, + const ResourceLoaderBridge::ResponseInfo& info, + bool* defer_redirect) { + // TODO(darin): It would be much better if this could live in WebCore, but + // doing so requires API changes at all levels. Similar code exists in + // WebCore/platform/network/cf/ResourceHandleCFNet.cpp :-( + if (new_url.GetOrigin() != result_->url.GetOrigin()) { + LOG(ERROR) << "Cross origin redirect denied"; + Cancel(); + return; + } result_->url = new_url; } |