#include <vector>
+#include "base/basictypes.h"
#include "base/bind.h"
+#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_vector.h"
+#include "base/memory/shared_memory.h"
#include "base/message_loop/message_loop.h"
#include "base/pickle.h"
#include "base/run_loop.h"
#include "base/strings/string_split.h"
#include "content/browser/browser_thread_impl.h"
#include "content/browser/child_process_security_policy_impl.h"
+#include "content/browser/loader/cross_site_resource_handler.h"
+#include "content/browser/loader/detachable_resource_handler.h"
#include "content/browser/loader/resource_dispatcher_host_impl.h"
+#include "content/browser/loader/resource_loader.h"
#include "content/browser/loader/resource_message_filter.h"
#include "content/browser/loader/resource_request_info_impl.h"
-#include "content/browser/worker_host/worker_service_impl.h"
+#include "content/common/appcache_interfaces.h"
#include "content/common/child_process_host_impl.h"
#include "content/common/resource_messages.h"
#include "content/common/view_messages.h"
#include "content/public/common/process_type.h"
#include "content/public/common/resource_response.h"
#include "content/public/test/test_browser_context.h"
+#include "content/public/test/test_browser_thread_bundle.h"
#include "content/test/test_content_browser_client.h"
#include "net/base/net_errors.h"
#include "net/base/request_priority.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_job.h"
+#include "net/url_request/url_request_job_factory.h"
#include "net/url_request/url_request_simple_job.h"
#include "net/url_request/url_request_test_job.h"
+#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "webkit/common/appcache/appcache_interfaces.h"
+#include "webkit/common/blob/shareable_file_reference.h"
// TODO(eroman): Write unit tests for SafeBrowsing that exercise
// SafeBrowsingResourceHandler.
+using webkit_blob::ShareableFileReference;
+
namespace content {
namespace {
void GenerateIPCMessage(
scoped_refptr<ResourceMessageFilter> filter,
scoped_ptr<IPC::Message> message) {
- bool msg_is_ok;
ResourceDispatcherHostImpl::Get()->OnMessageReceived(
- *message, filter.get(), &msg_is_ok);
+ *message, filter.get());
+}
+
+// On Windows, ResourceMsg_SetDataBuffer supplies a HANDLE which is not
+// automatically released.
+//
+// See ResourceDispatcher::ReleaseResourcesInDataMessage.
+//
+// TODO(davidben): It would be nice if the behavior for base::SharedMemoryHandle
+// were more like it is in POSIX where the received fds are tracked in a
+// ref-counted core that closes them if not extracted.
+void ReleaseHandlesInMessage(const IPC::Message& message) {
+ if (message.type() == ResourceMsg_SetDataBuffer::ID) {
+ PickleIterator iter(message);
+ int request_id;
+ CHECK(message.ReadInt(&iter, &request_id));
+ base::SharedMemoryHandle shm_handle;
+ if (IPC::ParamTraits<base::SharedMemoryHandle>::Read(&message,
+ &iter,
+ &shm_handle)) {
+ if (base::SharedMemory::IsHandleValid(shm_handle))
+ base::SharedMemory::CloseHandle(shm_handle);
+ }
+ }
}
} // namespace
case ResourceMsg_ReceivedRedirect::ID:
case ResourceMsg_SetDataBuffer::ID:
case ResourceMsg_DataReceived::ID:
+ case ResourceMsg_DataDownloaded::ID:
case ResourceMsg_RequestComplete::ID: {
bool result = PickleIterator(msg).ReadInt(&request_id);
DCHECK(result);
return request_id;
}
-static ResourceHostMsg_Request CreateResourceRequest(
- const char* method,
- ResourceType::Type type,
- const GURL& url) {
+static ResourceHostMsg_Request CreateResourceRequest(const char* method,
+ ResourceType type,
+ const GURL& url) {
ResourceHostMsg_Request request;
request.method = std::string(method);
request.url = url;
request.first_party_for_cookies = url; // bypass third-party cookie blocking
- request.referrer_policy = WebKit::WebReferrerPolicyDefault;
+ request.referrer_policy = blink::WebReferrerPolicyDefault;
request.load_flags = 0;
request.origin_pid = 0;
request.resource_type = type;
request.request_context = 0;
- request.appcache_host_id = appcache::kNoHostId;
+ request.appcache_host_id = kAppCacheNoHostId;
request.download_to_file = false;
request.is_main_frame = true;
- request.frame_id = 0;
request.parent_is_main_frame = false;
- request.parent_frame_id = -1;
+ request.parent_render_frame_id = -1;
request.transition_type = PAGE_TRANSITION_LINK;
request.allow_download = true;
return request;
// We may want to move this to a shared space if it is useful for something else
class ResourceIPCAccumulator {
public:
+ ~ResourceIPCAccumulator() {
+ for (size_t i = 0; i < messages_.size(); i++) {
+ ReleaseHandlesInMessage(messages_[i]);
+ }
+ }
+
+ // On Windows, takes ownership of SharedMemoryHandles in |msg|.
void AddMessage(const IPC::Message& msg) {
messages_.push_back(msg);
}
// This groups the messages by their request ID. The groups will be in order
// that the first message for each request ID was received, and the messages
// within the groups will be in the order that they appeared.
- // Note that this clears messages_.
+ // Note that this clears messages_. The caller takes ownership of any
+ // SharedMemoryHandles in messages placed into |msgs|.
typedef std::vector< std::vector<IPC::Message> > ClassifiedMessages;
void GetClassifiedMessages(ClassifiedMessages* msgs);
}
}
-// This class forwards the incoming messages to the ResourceDispatcherHostTest.
// This is used to emulate different sub-processes, since this filter will
-// have a different ID than the original. For the test, we want all the incoming
-// messages to go to the same place, which is why this forwards.
-class ForwardingFilter : public ResourceMessageFilter {
+// have a different ID than the original.
+class TestFilter : public ResourceMessageFilter {
public:
- explicit ForwardingFilter(IPC::Sender* dest,
- ResourceContext* resource_context)
- : ResourceMessageFilter(
- ChildProcessHostImpl::GenerateChildProcessUniqueId(),
- PROCESS_TYPE_RENDERER, NULL, NULL, NULL,
- base::Bind(&ForwardingFilter::GetContexts,
- base::Unretained(this))),
- dest_(dest),
- resource_context_(resource_context) {
+ explicit TestFilter(ResourceContext* resource_context)
+ : ResourceMessageFilter(
+ ChildProcessHostImpl::GenerateChildProcessUniqueId(),
+ PROCESS_TYPE_RENDERER, NULL, NULL, NULL, NULL,
+ base::Bind(&TestFilter::GetContexts, base::Unretained(this))),
+ resource_context_(resource_context),
+ canceled_(false),
+ received_after_canceled_(0) {
+ ChildProcessSecurityPolicyImpl::GetInstance()->Add(child_id());
set_peer_pid_for_testing(base::GetCurrentProcId());
}
+ void set_canceled(bool canceled) { canceled_ = canceled; }
+ int received_after_canceled() const { return received_after_canceled_; }
+
// ResourceMessageFilter override
virtual bool Send(IPC::Message* msg) OVERRIDE {
- if (!dest_)
- return false;
- return dest_->Send(msg);
+ // No messages should be received when the process has been canceled.
+ if (canceled_)
+ received_after_canceled_++;
+ ReleaseHandlesInMessage(*msg);
+ delete msg;
+ return true;
}
ResourceContext* resource_context() { return resource_context_; }
protected:
- virtual ~ForwardingFilter() {}
+ virtual ~TestFilter() {}
private:
void GetContexts(const ResourceHostMsg_Request& request,
*request_context = resource_context_->GetRequestContext();
}
- IPC::Sender* dest_;
ResourceContext* resource_context_;
+ bool canceled_;
+ int received_after_canceled_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestFilter);
+};
+
+
+// This class forwards the incoming messages to the ResourceDispatcherHostTest.
+// For the test, we want all the incoming messages to go to the same place,
+// which is why this forwards.
+class ForwardingFilter : public TestFilter {
+ public:
+ explicit ForwardingFilter(IPC::Sender* dest,
+ ResourceContext* resource_context)
+ : TestFilter(resource_context),
+ dest_(dest) {
+ }
+
+ // TestFilter override
+ virtual bool Send(IPC::Message* msg) OVERRIDE {
+ return dest_->Send(msg);
+ }
+
+ private:
+ virtual ~ForwardingFilter() {}
+
+ IPC::Sender* dest_;
DISALLOW_COPY_AND_ASSIGN(ForwardingFilter);
};
+// This class is a variation on URLRequestTestJob that will call
+// URLRequest::WillStartUsingNetwork before starting.
+class URLRequestTestDelayedNetworkJob : public net::URLRequestTestJob {
+ public:
+ URLRequestTestDelayedNetworkJob(net::URLRequest* request,
+ net::NetworkDelegate* network_delegate)
+ : net::URLRequestTestJob(request, network_delegate) {}
+
+ // Only start if not deferred for network start.
+ virtual void Start() OVERRIDE {
+ bool defer = false;
+ NotifyBeforeNetworkStart(&defer);
+ if (defer)
+ return;
+ net::URLRequestTestJob::Start();
+ }
+
+ virtual void ResumeNetworkStart() OVERRIDE {
+ net::URLRequestTestJob::StartAsync();
+ }
+
+ private:
+ virtual ~URLRequestTestDelayedNetworkJob() {}
+
+ DISALLOW_COPY_AND_ASSIGN(URLRequestTestDelayedNetworkJob);
+};
+
// This class is a variation on URLRequestTestJob in that it does
// not complete start upon entry, only when specifically told to.
class URLRequestTestDelayedStartJob : public net::URLRequestTestJob {
}
};
+class ResourceDispatcherHostTest;
+
+class TestURLRequestJobFactory : public net::URLRequestJobFactory {
+ public:
+ explicit TestURLRequestJobFactory(ResourceDispatcherHostTest* test_fixture)
+ : test_fixture_(test_fixture),
+ delay_start_(false),
+ delay_complete_(false),
+ network_start_notification_(false),
+ url_request_jobs_created_count_(0) {
+ }
+
+ void HandleScheme(const std::string& scheme) {
+ supported_schemes_.insert(scheme);
+ }
+
+ int url_request_jobs_created_count() const {
+ return url_request_jobs_created_count_;
+ }
+
+ void SetDelayedStartJobGeneration(bool delay_job_start) {
+ delay_start_ = delay_job_start;
+ }
+
+ void SetDelayedCompleteJobGeneration(bool delay_job_complete) {
+ delay_complete_ = delay_job_complete;
+ }
+
+ void SetNetworkStartNotificationJobGeneration(bool notification) {
+ network_start_notification_ = notification;
+ }
+
+ virtual net::URLRequestJob* MaybeCreateJobWithProtocolHandler(
+ const std::string& scheme,
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const OVERRIDE;
+
+ virtual bool IsHandledProtocol(const std::string& scheme) const OVERRIDE {
+ return supported_schemes_.count(scheme) > 0;
+ }
+
+ virtual bool IsHandledURL(const GURL& url) const OVERRIDE {
+ return supported_schemes_.count(url.scheme()) > 0;
+ }
+
+ virtual bool IsSafeRedirectTarget(const GURL& location) const OVERRIDE {
+ return false;
+ }
+
+ private:
+ ResourceDispatcherHostTest* test_fixture_;
+ bool delay_start_;
+ bool delay_complete_;
+ bool network_start_notification_;
+ mutable int url_request_jobs_created_count_;
+ std::set<std::string> supported_schemes_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestURLRequestJobFactory);
+};
+
// Associated with an URLRequest to determine if the URLRequest gets deleted.
class TestUserData : public base::SupportsUserData::Data {
public:
NONE = 0,
DEFER_STARTING_REQUEST = 1 << 0,
DEFER_PROCESSING_RESPONSE = 1 << 1,
- CANCEL_BEFORE_START = 1 << 2
+ CANCEL_BEFORE_START = 1 << 2,
+ DEFER_NETWORK_START = 1 << 3
};
// Throttle that tracks the current throttle blocking a request. Only one
}
}
+ virtual void WillStartUsingNetwork(bool* defer) OVERRIDE {
+ ASSERT_EQ(NULL, active_throttle_);
+
+ if (flags_ & DEFER_NETWORK_START) {
+ active_throttle_ = this;
+ *defer = true;
+ }
+ }
+
+ virtual const char* GetNameForLogging() const OVERRIDE {
+ return "GenericResourceThrottle";
+ }
+
void Resume() {
ASSERT_TRUE(this == active_throttle_);
active_throttle_ = NULL;
virtual void RequestBeginning(
net::URLRequest* request,
ResourceContext* resource_context,
- appcache::AppCacheService* appcache_service,
- ResourceType::Type resource_type,
+ AppCacheService* appcache_service,
+ ResourceType resource_type,
int child_id,
int route_id,
ScopedVector<ResourceThrottle>* throttles) OVERRIDE {
scoped_ptr<base::SupportsUserData::Data> user_data_;
};
+// Waits for a ShareableFileReference to be released.
+class ShareableFileReleaseWaiter {
+ public:
+ ShareableFileReleaseWaiter(const base::FilePath& path) {
+ scoped_refptr<ShareableFileReference> file =
+ ShareableFileReference::Get(path);
+ file->AddFinalReleaseCallback(
+ base::Bind(&ShareableFileReleaseWaiter::Released,
+ base::Unretained(this)));
+ }
+
+ void Wait() {
+ loop_.Run();
+ }
+
+ private:
+ void Released(const base::FilePath& path) {
+ loop_.Quit();
+ }
+
+ base::RunLoop loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShareableFileReleaseWaiter);
+};
+
class ResourceDispatcherHostTest : public testing::Test,
public IPC::Sender {
public:
ResourceDispatcherHostTest()
- : ui_thread_(BrowserThread::UI, &message_loop_),
- file_thread_(BrowserThread::FILE_USER_BLOCKING, &message_loop_),
- cache_thread_(BrowserThread::CACHE, &message_loop_),
- io_thread_(BrowserThread::IO, &message_loop_),
+ : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
old_factory_(NULL),
- resource_type_(ResourceType::SUB_RESOURCE),
send_data_received_acks_(false) {
browser_context_.reset(new TestBrowserContext());
BrowserContext::EnsureResourceContextInitialized(browser_context_.get());
- message_loop_.RunUntilIdle();
- filter_ = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
- }
-
- virtual ~ResourceDispatcherHostTest() {
- for (std::set<int>::iterator it = child_ids_.begin();
- it != child_ids_.end(); ++it) {
- host_.CancelRequestsForProcess(*it);
- }
+ base::RunLoop().RunUntilIdle();
+ filter_ = MakeForwardingFilter();
+ // TODO(cbentzel): Better way to get URLRequestContext?
+ net::URLRequestContext* request_context =
+ browser_context_->GetResourceContext()->GetRequestContext();
+ job_factory_.reset(new TestURLRequestJobFactory(this));
+ request_context->set_job_factory(job_factory_.get());
+ request_context->set_network_delegate(&network_delegate_);
}
// IPC::Sender implementation
GenerateDataReceivedACK(*msg);
}
+ if (wait_for_request_complete_loop_ &&
+ msg->type() == ResourceMsg_RequestComplete::ID) {
+ wait_for_request_complete_loop_->Quit();
+ }
+
+ // Do not release handles in it yet; the accumulator owns them now.
delete msg;
return true;
}
protected:
+ friend class TestURLRequestJobFactory;
+
// testing::Test
- virtual void SetUp() {
- DCHECK(!test_fixture_);
- test_fixture_ = this;
+ virtual void SetUp() OVERRIDE {
ChildProcessSecurityPolicyImpl::GetInstance()->Add(0);
- net::URLRequest::Deprecated::RegisterProtocolFactory(
- "test",
- &ResourceDispatcherHostTest::Factory);
- EnsureTestSchemeIsAllowed();
- delay_start_ = false;
- delay_complete_ = false;
- url_request_jobs_created_count_ = 0;
+ HandleScheme("test");
}
virtual void TearDown() {
- net::URLRequest::Deprecated::RegisterProtocolFactory("test", NULL);
- if (!scheme_.empty())
- net::URLRequest::Deprecated::RegisterProtocolFactory(
- scheme_, old_factory_);
-
EXPECT_TRUE(URLRequestTestDelayedStartJob::DelayedStartQueueEmpty());
URLRequestTestDelayedStartJob::ClearQueue();
- DCHECK(test_fixture_ == this);
- test_fixture_ = NULL;
+ for (std::set<int>::iterator it = child_ids_.begin();
+ it != child_ids_.end(); ++it) {
+ host_.CancelRequestsForProcess(*it);
+ }
host_.Shutdown();
ResourceDispatcherHostImpl::Get()->CancelRequestsForContext(
browser_context_->GetResourceContext());
- WorkerServiceImpl::GetInstance()->PerformTeardownForTesting();
-
browser_context_.reset();
- message_loop_.RunUntilIdle();
+ base::RunLoop().RunUntilIdle();
}
- // Creates a request using the current test object as the filter.
+ // Creates a new ForwardingFilter and registers it with |child_ids_| so as not
+ // to leak per-child state on test shutdown.
+ ForwardingFilter* MakeForwardingFilter() {
+ ForwardingFilter* filter =
+ new ForwardingFilter(this, browser_context_->GetResourceContext());
+ child_ids_.insert(filter->child_id());
+ return filter;
+ }
+
+ // Creates a request using the current test object as the filter and
+ // SubResource as the resource type.
void MakeTestRequest(int render_view_id,
int request_id,
const GURL& url);
- // Generates a request using the given filter. This will probably be a
- // ForwardingFilter.
- void MakeTestRequest(ResourceMessageFilter* filter,
- int render_view_id,
- int request_id,
- const GURL& url);
+ // Generates a request using the given filter and resource type.
+ void MakeTestRequestWithResourceType(ResourceMessageFilter* filter,
+ int render_view_id,
+ int request_id,
+ const GURL& url,
+ ResourceType type);
void CancelRequest(int request_id);
+ void RendererCancelRequest(int request_id) {
+ ResourceMessageFilter* old_filter = SetFilter(filter_.get());
+ host_.OnCancelRequest(request_id);
+ SetFilter(old_filter);
+ }
void CompleteStartRequest(int request_id);
void CompleteStartRequest(ResourceMessageFilter* filter, int request_id);
+ net::TestNetworkDelegate* network_delegate() { return &network_delegate_; }
+
void EnsureSchemeIsAllowed(const std::string& scheme) {
ChildProcessSecurityPolicyImpl* policy =
ChildProcessSecurityPolicyImpl::GetInstance();
policy->RegisterWebSafeScheme(scheme);
}
- void EnsureTestSchemeIsAllowed() {
- EnsureSchemeIsAllowed("test");
- }
-
// Sets a particular response for any request from now on. To switch back to
// the default bahavior, pass an empty |headers|. |headers| should be raw-
// formatted (NULLs instead of EOLs).
SetResponse(headers, std::string());
}
- // Sets a particular resource type for any request from now on.
- void SetResourceType(ResourceType::Type type) {
- resource_type_ = type;
- }
-
void SendDataReceivedACKs(bool send_acks) {
send_data_received_acks_ = send_acks;
}
// Intercepts requests for the given protocol.
void HandleScheme(const std::string& scheme) {
- DCHECK(scheme_.empty());
- DCHECK(!old_factory_);
- scheme_ = scheme;
- old_factory_ = net::URLRequest::Deprecated::RegisterProtocolFactory(
- scheme_, &ResourceDispatcherHostTest::Factory);
+ job_factory_->HandleScheme(scheme);
EnsureSchemeIsAllowed(scheme);
}
- // Our own net::URLRequestJob factory.
- static net::URLRequestJob* Factory(net::URLRequest* request,
- net::NetworkDelegate* network_delegate,
- const std::string& scheme) {
- url_request_jobs_created_count_++;
- if (test_fixture_->response_headers_.empty()) {
- if (delay_start_) {
- return new URLRequestTestDelayedStartJob(request, network_delegate);
- } else if (delay_complete_) {
- return new URLRequestTestDelayedCompletionJob(request,
- network_delegate);
- } else if (scheme == "big-job") {
- return new URLRequestBigJob(request, network_delegate);
- } else {
- return new net::URLRequestTestJob(request, network_delegate);
- }
- } else {
- if (delay_start_) {
- return new URLRequestTestDelayedStartJob(
- request, network_delegate,
- test_fixture_->response_headers_, test_fixture_->response_data_,
- false);
- } else if (delay_complete_) {
- return new URLRequestTestDelayedCompletionJob(
- request, network_delegate,
- test_fixture_->response_headers_, test_fixture_->response_data_,
- false);
- } else {
- return new net::URLRequestTestJob(
- request, network_delegate,
- test_fixture_->response_headers_, test_fixture_->response_data_,
- false);
- }
- }
- }
-
- void SetDelayedStartJobGeneration(bool delay_job_start) {
- delay_start_ = delay_job_start;
- }
-
- void SetDelayedCompleteJobGeneration(bool delay_job_complete) {
- delay_complete_ = delay_job_complete;
- }
-
void GenerateDataReceivedACK(const IPC::Message& msg) {
EXPECT_EQ(ResourceMsg_DataReceived::ID, msg.type());
base::Bind(&GenerateIPCMessage, filter_, base::Passed(&ack)));
}
- base::MessageLoopForIO message_loop_;
- BrowserThreadImpl ui_thread_;
- BrowserThreadImpl file_thread_;
- BrowserThreadImpl cache_thread_;
- BrowserThreadImpl io_thread_;
+ // Setting filters for testing renderer messages.
+ // Returns the previous filter.
+ ResourceMessageFilter* SetFilter(ResourceMessageFilter* new_filter) {
+ ResourceMessageFilter* old_filter = host_.filter_;
+ host_.filter_ = new_filter;
+ return old_filter;
+ }
+
+ void WaitForRequestComplete() {
+ DCHECK(!wait_for_request_complete_loop_);
+ wait_for_request_complete_loop_.reset(new base::RunLoop);
+ wait_for_request_complete_loop_->Run();
+ wait_for_request_complete_loop_.reset();
+ }
+
+ content::TestBrowserThreadBundle thread_bundle_;
scoped_ptr<TestBrowserContext> browser_context_;
+ scoped_ptr<TestURLRequestJobFactory> job_factory_;
scoped_refptr<ForwardingFilter> filter_;
+ net::TestNetworkDelegate network_delegate_;
ResourceDispatcherHostImpl host_;
ResourceIPCAccumulator accum_;
std::string response_headers_;
std::string response_data_;
std::string scheme_;
net::URLRequest::ProtocolFactory* old_factory_;
- ResourceType::Type resource_type_;
bool send_data_received_acks_;
std::set<int> child_ids_;
- static ResourceDispatcherHostTest* test_fixture_;
- static bool delay_start_;
- static bool delay_complete_;
- static int url_request_jobs_created_count_;
+ scoped_ptr<base::RunLoop> wait_for_request_complete_loop_;
};
-// Static.
-ResourceDispatcherHostTest* ResourceDispatcherHostTest::test_fixture_ = NULL;
-bool ResourceDispatcherHostTest::delay_start_ = false;
-bool ResourceDispatcherHostTest::delay_complete_ = false;
-int ResourceDispatcherHostTest::url_request_jobs_created_count_ = 0;
void ResourceDispatcherHostTest::MakeTestRequest(int render_view_id,
int request_id,
const GURL& url) {
- MakeTestRequest(filter_.get(), render_view_id, request_id, url);
+ MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
+ url, RESOURCE_TYPE_SUB_RESOURCE);
}
-void ResourceDispatcherHostTest::MakeTestRequest(
+void ResourceDispatcherHostTest::MakeTestRequestWithResourceType(
ResourceMessageFilter* filter,
int render_view_id,
int request_id,
- const GURL& url) {
- // If it's already there, this'll be dropped on the floor, which is fine.
- child_ids_.insert(filter->child_id());
-
+ const GURL& url,
+ ResourceType type) {
ResourceHostMsg_Request request =
- CreateResourceRequest("GET", resource_type_, url);
+ CreateResourceRequest("GET", type, url);
ResourceHostMsg_RequestResource msg(render_view_id, request_id, request);
- bool msg_was_ok;
- host_.OnMessageReceived(msg, filter, &msg_was_ok);
+ host_.OnMessageReceived(msg, filter);
KickOffRequest();
}
void ResourceDispatcherHostTest::CancelRequest(int request_id) {
- host_.CancelRequest(filter_->child_id(), request_id, false);
+ host_.CancelRequest(filter_->child_id(), request_id);
}
void ResourceDispatcherHostTest::CompleteStartRequest(int request_id) {
URLRequestTestDelayedStartJob::CompleteStart(req);
}
-void CheckSuccessfulRequest(const std::vector<IPC::Message>& messages,
- const std::string& reference_data) {
+void CheckRequestCompleteErrorCode(const IPC::Message& message,
+ int expected_error_code) {
+ // Verify the expected error code was received.
+ int request_id;
+ int error_code;
+
+ ASSERT_EQ(ResourceMsg_RequestComplete::ID, message.type());
+
+ PickleIterator iter(message);
+ ASSERT_TRUE(IPC::ReadParam(&message, &iter, &request_id));
+ ASSERT_TRUE(IPC::ReadParam(&message, &iter, &error_code));
+ ASSERT_EQ(expected_error_code, error_code);
+}
+
+testing::AssertionResult ExtractDataOffsetAndLength(const IPC::Message& message,
+ int* data_offset,
+ int* data_length) {
+ PickleIterator iter(message);
+ int request_id;
+ if (!IPC::ReadParam(&message, &iter, &request_id))
+ return testing::AssertionFailure() << "Could not read request_id";
+ if (!IPC::ReadParam(&message, &iter, data_offset))
+ return testing::AssertionFailure() << "Could not read data_offset";
+ if (!IPC::ReadParam(&message, &iter, data_length))
+ return testing::AssertionFailure() << "Could not read data_length";
+ return testing::AssertionSuccess();
+}
+
+void CheckSuccessfulRequestWithErrorCode(
+ const std::vector<IPC::Message>& messages,
+ const std::string& reference_data,
+ int expected_error) {
// A successful request will have received 4 messages:
// ReceivedResponse (indicates headers received)
// SetDataBuffer (contains shared memory handle)
// DataReceived (data offset and length into shared memory)
// RequestComplete (request is done)
//
- // This function verifies that we received 4 messages and that they
- // are appropriate.
+ // This function verifies that we received 4 messages and that they are
+ // appropriate. It allows for an error code other than net::OK if the request
+ // should successfully receive data and then abort, e.g., on cancel.
ASSERT_EQ(4U, messages.size());
// The first messages should be received response
// should probably test multiple chunks later
ASSERT_EQ(ResourceMsg_DataReceived::ID, messages[2].type());
- PickleIterator iter2(messages[2]);
- ASSERT_TRUE(IPC::ReadParam(&messages[2], &iter2, &request_id));
int data_offset;
- ASSERT_TRUE(IPC::ReadParam(&messages[2], &iter2, &data_offset));
int data_length;
- ASSERT_TRUE(IPC::ReadParam(&messages[2], &iter2, &data_length));
+ ASSERT_TRUE(
+ ExtractDataOffsetAndLength(messages[2], &data_offset, &data_length));
ASSERT_EQ(reference_data.size(), static_cast<size_t>(data_length));
ASSERT_GE(shm_size, data_length);
ASSERT_EQ(0, memcmp(reference_data.c_str(), data, data_length));
// The last message should be all data received.
- ASSERT_EQ(ResourceMsg_RequestComplete::ID, messages[3].type());
+ CheckRequestCompleteErrorCode(messages[3], expected_error);
+}
+
+void CheckSuccessfulRequest(const std::vector<IPC::Message>& messages,
+ const std::string& reference_data) {
+ CheckSuccessfulRequestWithErrorCode(messages, reference_data, net::OK);
+}
+
+void CheckSuccessfulRedirect(const std::vector<IPC::Message>& messages,
+ const std::string& reference_data) {
+ ASSERT_EQ(5U, messages.size());
+ ASSERT_EQ(ResourceMsg_ReceivedRedirect::ID, messages[0].type());
+
+ const std::vector<IPC::Message> second_req_msgs =
+ std::vector<IPC::Message>(messages.begin() + 1, messages.end());
+ CheckSuccessfulRequest(second_req_msgs, reference_data);
}
void CheckFailedRequest(const std::vector<IPC::Message>& messages,
if (messages.size() == 2) {
EXPECT_EQ(ResourceMsg_ReceivedResponse::ID, messages[0].type());
}
- EXPECT_EQ(ResourceMsg_RequestComplete::ID, messages[failure_index].type());
- int request_id;
- int error_code;
-
- PickleIterator iter(messages[failure_index]);
- EXPECT_TRUE(IPC::ReadParam(&messages[failure_index], &iter, &request_id));
- EXPECT_TRUE(IPC::ReadParam(&messages[failure_index], &iter, &error_code));
- EXPECT_EQ(expected_error, error_code);
+ CheckRequestCompleteErrorCode(messages[failure_index], expected_error);
}
// Tests whether many messages get dispatched properly.
MakeTestRequest(0, 1, net::URLRequestTestJob::test_url_1());
MakeTestRequest(0, 2, net::URLRequestTestJob::test_url_2());
MakeTestRequest(0, 3, net::URLRequestTestJob::test_url_3());
+ MakeTestRequestWithResourceType(filter_.get(), 0, 4,
+ net::URLRequestTestJob::test_url_4(),
+ RESOURCE_TYPE_PREFETCH); // detachable type
+ MakeTestRequest(0, 5, net::URLRequestTestJob::test_url_redirect_to_url_2());
+
+ // Finish the redirection
+ ResourceHostMsg_FollowRedirect redirect_msg(5);
+ host_.OnMessageReceived(redirect_msg, filter_.get());
+ base::MessageLoop::current()->RunUntilIdle();
// flush all the pending requests
while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
ResourceIPCAccumulator::ClassifiedMessages msgs;
accum_.GetClassifiedMessages(&msgs);
- // there are three requests, so we should have gotten them classified as such
- ASSERT_EQ(3U, msgs.size());
+ // there are five requests, so we should have gotten them classified as such
+ ASSERT_EQ(5U, msgs.size());
CheckSuccessfulRequest(msgs[0], net::URLRequestTestJob::test_data_1());
CheckSuccessfulRequest(msgs[1], net::URLRequestTestJob::test_data_2());
CheckSuccessfulRequest(msgs[2], net::URLRequestTestJob::test_data_3());
+ CheckSuccessfulRequest(msgs[3], net::URLRequestTestJob::test_data_4());
+ CheckSuccessfulRedirect(msgs[4], net::URLRequestTestJob::test_data_2());
}
-void CheckCancelledRequestCompleteMessage(const IPC::Message& message) {
- ASSERT_EQ(ResourceMsg_RequestComplete::ID, message.type());
-
- int request_id;
- int error_code;
-
- PickleIterator iter(message);
- ASSERT_TRUE(IPC::ReadParam(&message, &iter, &request_id));
- ASSERT_TRUE(IPC::ReadParam(&message, &iter, &error_code));
-
- EXPECT_EQ(net::ERR_ABORTED, error_code);
-}
-
-// Tests whether messages get canceled properly. We issue three requests,
-// cancel one of them, and make sure that each sent the proper notifications.
+// Tests whether messages get canceled properly. We issue four requests,
+// cancel two of them, and make sure that each sent the proper notifications.
TEST_F(ResourceDispatcherHostTest, Cancel) {
MakeTestRequest(0, 1, net::URLRequestTestJob::test_url_1());
MakeTestRequest(0, 2, net::URLRequestTestJob::test_url_2());
MakeTestRequest(0, 3, net::URLRequestTestJob::test_url_3());
+
+ MakeTestRequestWithResourceType(filter_.get(), 0, 4,
+ net::URLRequestTestJob::test_url_4(),
+ RESOURCE_TYPE_PREFETCH); // detachable type
+
CancelRequest(2);
+ // Cancel request must come from the renderer for a detachable resource to
+ // delay.
+ RendererCancelRequest(4);
+
+ // The handler should have been detached now.
+ GlobalRequestID global_request_id(filter_->child_id(), 4);
+ ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(
+ host_.GetURLRequest(global_request_id));
+ ASSERT_TRUE(info->detachable_handler()->is_detached());
+
// flush all the pending requests
while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
base::MessageLoop::current()->RunUntilIdle();
+ // Everything should be out now.
+ EXPECT_EQ(0, host_.pending_requests());
+
ResourceIPCAccumulator::ClassifiedMessages msgs;
accum_.GetClassifiedMessages(&msgs);
- // there are three requests, so we should have gotten them classified as such
- ASSERT_EQ(3U, msgs.size());
+ // there are four requests, so we should have gotten them classified as such
+ ASSERT_EQ(4U, msgs.size());
CheckSuccessfulRequest(msgs[0], net::URLRequestTestJob::test_data_1());
CheckSuccessfulRequest(msgs[2], net::URLRequestTestJob::test_data_3());
- // Check that request 2 got canceled.
- ASSERT_EQ(2U, msgs[1].size());
+ // Check that request 2 and 4 got canceled, as far as the renderer is
+ // concerned. Request 2 will have been deleted.
+ ASSERT_EQ(1U, msgs[1].size());
ASSERT_EQ(ResourceMsg_ReceivedResponse::ID, msgs[1][0].type());
- CheckCancelledRequestCompleteMessage(msgs[1][1]);
+
+ ASSERT_EQ(2U, msgs[3].size());
+ ASSERT_EQ(ResourceMsg_ReceivedResponse::ID, msgs[3][0].type());
+ CheckRequestCompleteErrorCode(msgs[3][1], net::ERR_ABORTED);
+
+ // However, request 4 should have actually gone to completion. (Only request 2
+ // was canceled.)
+ EXPECT_EQ(4, network_delegate()->completed_requests());
+ EXPECT_EQ(1, network_delegate()->canceled_requests());
+ EXPECT_EQ(0, network_delegate()->error_count());
+}
+
+// Shows that detachable requests will timeout if the request takes too long to
+// complete.
+TEST_F(ResourceDispatcherHostTest, DetachedResourceTimesOut) {
+ MakeTestRequestWithResourceType(filter_.get(), 0, 1,
+ net::URLRequestTestJob::test_url_2(),
+ RESOURCE_TYPE_PREFETCH); // detachable type
+ GlobalRequestID global_request_id(filter_->child_id(), 1);
+ ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(
+ host_.GetURLRequest(global_request_id));
+ ASSERT_TRUE(info->detachable_handler());
+ info->detachable_handler()->set_cancel_delay(
+ base::TimeDelta::FromMilliseconds(200));
+ base::MessageLoop::current()->RunUntilIdle();
+
+ RendererCancelRequest(1);
+
+ // From the renderer's perspective, the request was cancelled.
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+ ASSERT_EQ(1U, msgs.size());
+ ASSERT_EQ(2U, msgs[0].size());
+ ASSERT_EQ(ResourceMsg_ReceivedResponse::ID, msgs[0][0].type());
+ CheckRequestCompleteErrorCode(msgs[0][1], net::ERR_ABORTED);
+
+ // But it continues detached.
+ EXPECT_EQ(1, host_.pending_requests());
+ EXPECT_TRUE(info->detachable_handler()->is_detached());
+
+ // Wait until after the delay timer times out before we start processing any
+ // messages.
+ base::OneShotTimer<base::MessageLoop> timer;
+ timer.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(210),
+ base::MessageLoop::current(), &base::MessageLoop::QuitWhenIdle);
+ base::MessageLoop::current()->Run();
+
+ // The prefetch should be cancelled by now.
+ EXPECT_EQ(0, host_.pending_requests());
+ EXPECT_EQ(1, network_delegate()->completed_requests());
+ EXPECT_EQ(1, network_delegate()->canceled_requests());
+ EXPECT_EQ(0, network_delegate()->error_count());
+}
+
+// If the filter has disappeared then detachable resources should continue to
+// load.
+TEST_F(ResourceDispatcherHostTest, DeletedFilterDetached) {
+ // test_url_1's data is available synchronously, so use 2 and 3.
+ ResourceHostMsg_Request request_prefetch = CreateResourceRequest(
+ "GET", RESOURCE_TYPE_PREFETCH, net::URLRequestTestJob::test_url_2());
+ ResourceHostMsg_Request request_ping = CreateResourceRequest(
+ "GET", RESOURCE_TYPE_PING, net::URLRequestTestJob::test_url_3());
+
+ ResourceHostMsg_RequestResource msg_prefetch(0, 1, request_prefetch);
+ host_.OnMessageReceived(msg_prefetch, filter_);
+ ResourceHostMsg_RequestResource msg_ping(0, 2, request_ping);
+ host_.OnMessageReceived(msg_ping, filter_);
+
+ // Remove the filter before processing the requests by simulating channel
+ // closure.
+ ResourceRequestInfoImpl* info_prefetch = ResourceRequestInfoImpl::ForRequest(
+ host_.GetURLRequest(GlobalRequestID(filter_->child_id(), 1)));
+ ResourceRequestInfoImpl* info_ping = ResourceRequestInfoImpl::ForRequest(
+ host_.GetURLRequest(GlobalRequestID(filter_->child_id(), 2)));
+ DCHECK_EQ(filter_.get(), info_prefetch->filter());
+ DCHECK_EQ(filter_.get(), info_ping->filter());
+ filter_->OnChannelClosing();
+ info_prefetch->filter_.reset();
+ info_ping->filter_.reset();
+
+ // From the renderer's perspective, the requests were cancelled.
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+ ASSERT_EQ(2U, msgs.size());
+ CheckRequestCompleteErrorCode(msgs[0][0], net::ERR_ABORTED);
+ CheckRequestCompleteErrorCode(msgs[1][0], net::ERR_ABORTED);
+
+ // But it continues detached.
+ EXPECT_EQ(2, host_.pending_requests());
+ EXPECT_TRUE(info_prefetch->detachable_handler()->is_detached());
+ EXPECT_TRUE(info_ping->detachable_handler()->is_detached());
+
+ KickOffRequest();
+
+ // Make sure the requests weren't canceled early.
+ EXPECT_EQ(2, host_.pending_requests());
+
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_EQ(0, host_.pending_requests());
+ EXPECT_EQ(2, network_delegate()->completed_requests());
+ EXPECT_EQ(0, network_delegate()->canceled_requests());
+ EXPECT_EQ(0, network_delegate()->error_count());
+}
+
+// If the filter has disappeared (original process dies) then detachable
+// resources should continue to load, even when redirected.
+TEST_F(ResourceDispatcherHostTest, DeletedFilterDetachedRedirect) {
+ ResourceHostMsg_Request request = CreateResourceRequest(
+ "GET", RESOURCE_TYPE_PREFETCH,
+ net::URLRequestTestJob::test_url_redirect_to_url_2());
+
+ ResourceHostMsg_RequestResource msg(0, 1, request);
+ host_.OnMessageReceived(msg, filter_);
+
+ // Remove the filter before processing the request by simulating channel
+ // closure.
+ GlobalRequestID global_request_id(filter_->child_id(), 1);
+ ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(
+ host_.GetURLRequest(global_request_id));
+ info->filter_->OnChannelClosing();
+ info->filter_.reset();
+
+ // From the renderer's perspective, the request was cancelled.
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+ ASSERT_EQ(1U, msgs.size());
+ CheckRequestCompleteErrorCode(msgs[0][0], net::ERR_ABORTED);
+
+ // But it continues detached.
+ EXPECT_EQ(1, host_.pending_requests());
+ EXPECT_TRUE(info->detachable_handler()->is_detached());
+
+ // Verify no redirects before resetting the filter.
+ net::URLRequest* url_request = host_.GetURLRequest(global_request_id);
+ EXPECT_EQ(1u, url_request->url_chain().size());
+ KickOffRequest();
+
+ // Verify that a redirect was followed.
+ EXPECT_EQ(2u, url_request->url_chain().size());
+
+ // Make sure the request wasn't canceled early.
+ EXPECT_EQ(1, host_.pending_requests());
+
+ // Finish up the request.
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_EQ(0, host_.pending_requests());
+ EXPECT_EQ(1, network_delegate()->completed_requests());
+ EXPECT_EQ(0, network_delegate()->canceled_requests());
+ EXPECT_EQ(0, network_delegate()->error_count());
}
TEST_F(ResourceDispatcherHostTest, CancelWhileStartIsDeferred) {
host_.SetDelegate(&delegate);
MakeTestRequest(0, 1, net::URLRequestTestJob::test_url_1());
- CancelRequest(1);
+ // We cancel from the renderer because all non-renderer cancels delete
+ // the request synchronously.
+ RendererCancelRequest(1);
// Our TestResourceThrottle should not have been deleted yet. This is to
// ensure that destruction of the URLRequest happens asynchronously to
// calling CancelRequest.
EXPECT_FALSE(was_deleted);
- // flush all the pending requests
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_TRUE(was_deleted);
+}
+
+TEST_F(ResourceDispatcherHostTest, DetachWhileStartIsDeferred) {
+ bool was_deleted = false;
+
+ // Arrange to have requests deferred before starting.
+ TestResourceDispatcherHostDelegate delegate;
+ delegate.set_flags(DEFER_STARTING_REQUEST);
+ delegate.set_url_request_user_data(new TestUserData(&was_deleted));
+ host_.SetDelegate(&delegate);
+
+ MakeTestRequestWithResourceType(filter_.get(), 0, 1,
+ net::URLRequestTestJob::test_url_1(),
+ RESOURCE_TYPE_PREFETCH); // detachable type
+ // Cancel request must come from the renderer for a detachable resource to
+ // detach.
+ RendererCancelRequest(1);
+
+ // Even after driving the event loop, the request has not been deleted.
+ EXPECT_FALSE(was_deleted);
+
+ // However, it is still throttled because the defer happened above the
+ // DetachableResourceHandler.
while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_FALSE(was_deleted);
+ // Resume the request.
+ GenericResourceThrottle* throttle =
+ GenericResourceThrottle::active_throttle();
+ ASSERT_TRUE(throttle);
+ throttle->Resume();
+
+ // Now, the request completes.
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+ base::MessageLoop::current()->RunUntilIdle();
EXPECT_TRUE(was_deleted);
+ EXPECT_EQ(1, network_delegate()->completed_requests());
+ EXPECT_EQ(0, network_delegate()->canceled_requests());
+ EXPECT_EQ(0, network_delegate()->error_count());
}
// Tests if cancel is called in ResourceThrottle::WillStartRequest, then the
// Check that request got canceled.
ASSERT_EQ(1U, msgs[0].size());
- CheckCancelledRequestCompleteMessage(msgs[0][0]);
+ CheckRequestCompleteErrorCode(msgs[0][0], net::ERR_ABORTED);
// Make sure URLRequest is never started.
- EXPECT_EQ(0, url_request_jobs_created_count_);
+ EXPECT_EQ(0, job_factory_->url_request_jobs_created_count());
}
TEST_F(ResourceDispatcherHostTest, PausedStartError) {
delegate.set_flags(DEFER_PROCESSING_RESPONSE);
host_.SetDelegate(&delegate);
- SetDelayedStartJobGeneration(true);
+ job_factory_->SetDelayedStartJobGeneration(true);
MakeTestRequest(0, 1, net::URLRequestTestJob::test_url_error());
CompleteStartRequest(1);
EXPECT_EQ(0, host_.pending_requests());
}
+// Test the WillStartUsingNetwork throttle.
+TEST_F(ResourceDispatcherHostTest, ThrottleNetworkStart) {
+ // Arrange to have requests deferred before processing response headers.
+ TestResourceDispatcherHostDelegate delegate;
+ delegate.set_flags(DEFER_NETWORK_START);
+ host_.SetDelegate(&delegate);
+
+ job_factory_->SetNetworkStartNotificationJobGeneration(true);
+ MakeTestRequest(0, 1, net::URLRequestTestJob::test_url_2());
+
+ // Should have deferred for network start.
+ GenericResourceThrottle* first_throttle =
+ GenericResourceThrottle::active_throttle();
+ ASSERT_TRUE(first_throttle);
+ EXPECT_EQ(0, network_delegate()->completed_requests());
+ EXPECT_EQ(1, host_.pending_requests());
+
+ first_throttle->Resume();
+
+ // Flush all the pending requests.
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_EQ(1, network_delegate()->completed_requests());
+ EXPECT_EQ(0, host_.pending_requests());
+}
+
TEST_F(ResourceDispatcherHostTest, ThrottleAndResumeTwice) {
// Arrange to have requests deferred before starting.
TestResourceDispatcherHostDelegate delegate;
// Check the cancellation
ASSERT_EQ(1U, msgs.size());
ASSERT_EQ(1U, msgs[0].size());
- ASSERT_EQ(ResourceMsg_RequestComplete::ID, msgs[0][0].type());
- int request_id;
- int error_code;
-
- PickleIterator iter(msgs[0][0]);
- ASSERT_TRUE(IPC::ReadParam(&msgs[0][0], &iter, &request_id));
- ASSERT_TRUE(IPC::ReadParam(&msgs[0][0], &iter, &error_code));
-
- EXPECT_EQ(net::ERR_ACCESS_DENIED, error_code);
+ CheckRequestCompleteErrorCode(msgs[0][0], net::ERR_ACCESS_DENIED);
}
-// The host delegate acts as a second one so we can have some requests
-// pending and some canceled.
-class TestFilter : public ForwardingFilter {
- public:
- explicit TestFilter(ResourceContext* resource_context)
- : ForwardingFilter(NULL, resource_context),
- has_canceled_(false),
- received_after_canceled_(0) {
- }
-
- // ForwardingFilter override
- virtual bool Send(IPC::Message* msg) OVERRIDE {
- // no messages should be received when the process has been canceled
- if (has_canceled_)
- received_after_canceled_++;
- delete msg;
- return true;
- }
-
- bool has_canceled_;
- int received_after_canceled_;
-
- private:
- virtual ~TestFilter() {}
-};
-
// Tests CancelRequestsForProcess
TEST_F(ResourceDispatcherHostTest, TestProcessCancel) {
scoped_refptr<TestFilter> test_filter = new TestFilter(
browser_context_->GetResourceContext());
+ child_ids_.insert(test_filter->child_id());
// request 1 goes to the test delegate
ResourceHostMsg_Request request = CreateResourceRequest(
- "GET", ResourceType::SUB_RESOURCE, net::URLRequestTestJob::test_url_1());
+ "GET", RESOURCE_TYPE_SUB_RESOURCE, net::URLRequestTestJob::test_url_1());
- MakeTestRequest(test_filter.get(), 0, 1,
- net::URLRequestTestJob::test_url_1());
+ MakeTestRequestWithResourceType(test_filter.get(), 0, 1,
+ net::URLRequestTestJob::test_url_1(),
+ RESOURCE_TYPE_SUB_RESOURCE);
// request 2 goes to us
MakeTestRequest(0, 2, net::URLRequestTestJob::test_url_2());
// request 3 goes to the test delegate
- MakeTestRequest(test_filter.get(), 0, 3,
- net::URLRequestTestJob::test_url_3());
+ MakeTestRequestWithResourceType(test_filter.get(), 0, 3,
+ net::URLRequestTestJob::test_url_3(),
+ RESOURCE_TYPE_SUB_RESOURCE);
+
+ // request 4 goes to us
+ MakeTestRequestWithResourceType(filter_.get(), 0, 4,
+ net::URLRequestTestJob::test_url_4(),
+ RESOURCE_TYPE_PREFETCH); // detachable type
+
// Make sure all requests have finished stage one. test_url_1 will have
// finished.
// breaks the whole test.
//EXPECT_EQ(3, host_.pending_requests());
- // Process each request for one level so one callback is called.
+ // Process test_url_2 and test_url_3 for one level so one callback is called.
+ // We'll cancel test_url_4 (detachable) before processing it to verify that it
+ // delays the cancel.
for (int i = 0; i < 2; i++)
EXPECT_TRUE(net::URLRequestTestJob::ProcessOnePendingMessage());
// Cancel the requests to the test process.
host_.CancelRequestsForProcess(filter_->child_id());
- test_filter->has_canceled_ = true;
+ test_filter->set_canceled(true);
+
+ // The requests should all be cancelled, except request 4, which is detached.
+ EXPECT_EQ(1, host_.pending_requests());
+ GlobalRequestID global_request_id(filter_->child_id(), 4);
+ ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(
+ host_.GetURLRequest(global_request_id));
+ ASSERT_TRUE(info->detachable_handler()->is_detached());
// Flush all the pending requests.
while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
EXPECT_EQ(0, host_.pending_requests());
// The test delegate should not have gotten any messages after being canceled.
- ASSERT_EQ(0, test_filter->received_after_canceled_);
+ ASSERT_EQ(0, test_filter->received_after_canceled());
- // We should have gotten exactly one result.
+ // There should be two results.
ResourceIPCAccumulator::ClassifiedMessages msgs;
accum_.GetClassifiedMessages(&msgs);
- ASSERT_EQ(1U, msgs.size());
+ ASSERT_EQ(2U, msgs.size());
CheckSuccessfulRequest(msgs[0], net::URLRequestTestJob::test_data_2());
+ // The detachable request was cancelled by the renderer before it
+ // finished. From the perspective of the renderer, it should have cancelled.
+ ASSERT_EQ(2U, msgs[1].size());
+ ASSERT_EQ(ResourceMsg_ReceivedResponse::ID, msgs[1][0].type());
+ CheckRequestCompleteErrorCode(msgs[1][1], net::ERR_ABORTED);
+ // But it completed anyway. For the network stack, no requests were canceled.
+ EXPECT_EQ(4, network_delegate()->completed_requests());
+ EXPECT_EQ(0, network_delegate()->canceled_requests());
+ EXPECT_EQ(0, network_delegate()->error_count());
+}
+
+TEST_F(ResourceDispatcherHostTest, TestProcessCancelDetachedTimesOut) {
+ MakeTestRequestWithResourceType(filter_.get(), 0, 1,
+ net::URLRequestTestJob::test_url_4(),
+ RESOURCE_TYPE_PREFETCH); // detachable type
+ GlobalRequestID global_request_id(filter_->child_id(), 1);
+ ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(
+ host_.GetURLRequest(global_request_id));
+ ASSERT_TRUE(info->detachable_handler());
+ info->detachable_handler()->set_cancel_delay(
+ base::TimeDelta::FromMilliseconds(200));
+ base::MessageLoop::current()->RunUntilIdle();
+
+ // Cancel the requests to the test process.
+ host_.CancelRequestsForProcess(filter_->child_id());
+ EXPECT_EQ(1, host_.pending_requests());
+
+ // Wait until after the delay timer times out before we start processing any
+ // messages.
+ base::OneShotTimer<base::MessageLoop> timer;
+ timer.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(210),
+ base::MessageLoop::current(), &base::MessageLoop::QuitWhenIdle);
+ base::MessageLoop::current()->Run();
+
+ // The prefetch should be cancelled by now.
+ EXPECT_EQ(0, host_.pending_requests());
+
+ // In case any messages are still to be processed.
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+ base::MessageLoop::current()->RunUntilIdle();
+
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+
+ ASSERT_EQ(1U, msgs.size());
+
+ // The request should have cancelled.
+ ASSERT_EQ(2U, msgs[0].size());
+ ASSERT_EQ(ResourceMsg_ReceivedResponse::ID, msgs[0][0].type());
+ CheckRequestCompleteErrorCode(msgs[0][1], net::ERR_ABORTED);
+ // And not run to completion.
+ EXPECT_EQ(1, network_delegate()->completed_requests());
+ EXPECT_EQ(1, network_delegate()->canceled_requests());
+ EXPECT_EQ(0, network_delegate()->error_count());
}
// Tests blocking and resuming requests.
MakeTestRequest(1, 2, net::URLRequestTestJob::test_url_2());
MakeTestRequest(0, 3, net::URLRequestTestJob::test_url_3());
MakeTestRequest(1, 4, net::URLRequestTestJob::test_url_1());
+ // Blocked detachable resources should not delay cancellation.
+ MakeTestRequestWithResourceType(filter_.get(), 1, 5,
+ net::URLRequestTestJob::test_url_4(),
+ RESOURCE_TYPE_PREFETCH); // detachable type
// Flush all the pending requests.
while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
// Tests that blocked requests are canceled if their associated process dies.
TEST_F(ResourceDispatcherHostTest, TestBlockedRequestsProcessDies) {
// This second filter is used to emulate a second process.
- scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
+ scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
host_.BlockRequestsForRoute(second_filter->child_id(), 0);
- MakeTestRequest(filter_.get(), 0, 1, net::URLRequestTestJob::test_url_1());
- MakeTestRequest(second_filter.get(), 0, 2,
- net::URLRequestTestJob::test_url_2());
- MakeTestRequest(filter_.get(), 0, 3, net::URLRequestTestJob::test_url_3());
- MakeTestRequest(second_filter.get(), 0, 4,
- net::URLRequestTestJob::test_url_1());
+ MakeTestRequestWithResourceType(filter_.get(), 0, 1,
+ net::URLRequestTestJob::test_url_1(),
+ RESOURCE_TYPE_SUB_RESOURCE);
+ MakeTestRequestWithResourceType(second_filter.get(), 0, 2,
+ net::URLRequestTestJob::test_url_2(),
+ RESOURCE_TYPE_SUB_RESOURCE);
+ MakeTestRequestWithResourceType(filter_.get(), 0, 3,
+ net::URLRequestTestJob::test_url_3(),
+ RESOURCE_TYPE_SUB_RESOURCE);
+ MakeTestRequestWithResourceType(second_filter.get(), 0, 4,
+ net::URLRequestTestJob::test_url_1(),
+ RESOURCE_TYPE_SUB_RESOURCE);
+ MakeTestRequestWithResourceType(second_filter.get(), 0, 5,
+ net::URLRequestTestJob::test_url_4(),
+ RESOURCE_TYPE_PREFETCH); // detachable type
// Simulate process death.
host_.CancelRequestsForProcess(second_filter->child_id());
ResourceIPCAccumulator::ClassifiedMessages msgs;
accum_.GetClassifiedMessages(&msgs);
- // The 2 requests for the RVH 0 should have been processed.
+ // The 2 requests for the RVH 0 should have been processed. Note that
+ // blocked detachable requests are canceled without delay.
ASSERT_EQ(2U, msgs.size());
CheckSuccessfulRequest(msgs[0], net::URLRequestTestJob::test_data_1());
// destructor to make sure the blocked requests are deleted.
TEST_F(ResourceDispatcherHostTest, TestBlockedRequestsDontLeak) {
// This second filter is used to emulate a second process.
- scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
+ scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
host_.BlockRequestsForRoute(filter_->child_id(), 1);
host_.BlockRequestsForRoute(filter_->child_id(), 2);
host_.BlockRequestsForRoute(second_filter->child_id(), 1);
- MakeTestRequest(filter_.get(), 0, 1, net::URLRequestTestJob::test_url_1());
- MakeTestRequest(filter_.get(), 1, 2, net::URLRequestTestJob::test_url_2());
- MakeTestRequest(filter_.get(), 0, 3, net::URLRequestTestJob::test_url_3());
- MakeTestRequest(second_filter.get(), 1, 4,
- net::URLRequestTestJob::test_url_1());
- MakeTestRequest(filter_.get(), 2, 5, net::URLRequestTestJob::test_url_2());
- MakeTestRequest(filter_.get(), 2, 6, net::URLRequestTestJob::test_url_3());
+ MakeTestRequestWithResourceType(filter_.get(), 0, 1,
+ net::URLRequestTestJob::test_url_1(),
+ RESOURCE_TYPE_SUB_RESOURCE);
+ MakeTestRequestWithResourceType(filter_.get(), 1, 2,
+ net::URLRequestTestJob::test_url_2(),
+ RESOURCE_TYPE_SUB_RESOURCE);
+ MakeTestRequestWithResourceType(filter_.get(), 0, 3,
+ net::URLRequestTestJob::test_url_3(),
+ RESOURCE_TYPE_SUB_RESOURCE);
+ MakeTestRequestWithResourceType(second_filter.get(), 1, 4,
+ net::URLRequestTestJob::test_url_1(),
+ RESOURCE_TYPE_SUB_RESOURCE);
+ MakeTestRequestWithResourceType(filter_.get(), 2, 5,
+ net::URLRequestTestJob::test_url_2(),
+ RESOURCE_TYPE_SUB_RESOURCE);
+ MakeTestRequestWithResourceType(filter_.get(), 2, 6,
+ net::URLRequestTestJob::test_url_3(),
+ RESOURCE_TYPE_SUB_RESOURCE);
+ MakeTestRequestWithResourceType(filter_.get(), 0, 7,
+ net::URLRequestTestJob::test_url_4(),
+ RESOURCE_TYPE_PREFETCH); // detachable type
+ MakeTestRequestWithResourceType(second_filter.get(), 1, 8,
+ net::URLRequestTestJob::test_url_4(),
+ RESOURCE_TYPE_PREFETCH); // detachable type
host_.CancelRequestsForProcess(filter_->child_id());
host_.CancelRequestsForProcess(second_filter->child_id());
size_t kMaxRequests = kMaxCostPerProcess / kMemoryCostOfTest2Req;
// This second filter is used to emulate a second process.
- scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
+ scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
// Saturate the number of outstanding requests for our process.
for (size_t i = 0; i < kMaxRequests; ++i) {
- MakeTestRequest(filter_.get(), 0, i + 1,
- net::URLRequestTestJob::test_url_2());
+ MakeTestRequestWithResourceType(filter_.get(), 0, i + 1,
+ net::URLRequestTestJob::test_url_2(),
+ RESOURCE_TYPE_SUB_RESOURCE);
}
// Issue two more requests for our process -- these should fail immediately.
- MakeTestRequest(filter_.get(), 0, kMaxRequests + 1,
- net::URLRequestTestJob::test_url_2());
- MakeTestRequest(filter_.get(), 0, kMaxRequests + 2,
- net::URLRequestTestJob::test_url_2());
+ MakeTestRequestWithResourceType(filter_.get(), 0, kMaxRequests + 1,
+ net::URLRequestTestJob::test_url_2(),
+ RESOURCE_TYPE_SUB_RESOURCE);
+ MakeTestRequestWithResourceType(filter_.get(), 0, kMaxRequests + 2,
+ net::URLRequestTestJob::test_url_2(),
+ RESOURCE_TYPE_SUB_RESOURCE);
// Issue two requests for the second process -- these should succeed since
// it is just process 0 that is saturated.
- MakeTestRequest(second_filter.get(), 0, kMaxRequests + 3,
- net::URLRequestTestJob::test_url_2());
- MakeTestRequest(second_filter.get(), 0, kMaxRequests + 4,
- net::URLRequestTestJob::test_url_2());
+ MakeTestRequestWithResourceType(second_filter.get(), 0, kMaxRequests + 3,
+ net::URLRequestTestJob::test_url_2(),
+ RESOURCE_TYPE_SUB_RESOURCE);
+ MakeTestRequestWithResourceType(second_filter.get(), 0, kMaxRequests + 4,
+ net::URLRequestTestJob::test_url_2(),
+ RESOURCE_TYPE_SUB_RESOURCE);
// Flush all the pending requests.
while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
host_.set_max_num_in_flight_requests(kMaxRequests);
// Needed to emulate additional processes.
- scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
- scoped_refptr<ForwardingFilter> third_filter = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
+ scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
+ scoped_refptr<ForwardingFilter> third_filter = MakeForwardingFilter();
// Saturate the number of outstanding requests for our process.
for (size_t i = 0; i < kMaxRequestsPerProcess; ++i) {
- MakeTestRequest(filter_.get(), 0, i + 1,
- net::URLRequestTestJob::test_url_2());
+ MakeTestRequestWithResourceType(filter_.get(), 0, i + 1,
+ net::URLRequestTestJob::test_url_2(),
+ RESOURCE_TYPE_SUB_RESOURCE);
}
// Issue another request for our process -- this should fail immediately.
- MakeTestRequest(filter_.get(), 0, kMaxRequestsPerProcess + 1,
- net::URLRequestTestJob::test_url_2());
+ MakeTestRequestWithResourceType(filter_.get(), 0, kMaxRequestsPerProcess + 1,
+ net::URLRequestTestJob::test_url_2(),
+ RESOURCE_TYPE_SUB_RESOURCE);
// Issue a request for the second process -- this should succeed, because it
// is just process 0 that is saturated.
- MakeTestRequest(second_filter.get(), 0, kMaxRequestsPerProcess + 2,
- net::URLRequestTestJob::test_url_2());
+ MakeTestRequestWithResourceType(
+ second_filter.get(), 0, kMaxRequestsPerProcess + 2,
+ net::URLRequestTestJob::test_url_2(), RESOURCE_TYPE_SUB_RESOURCE);
// Issue a request for the third process -- this should fail, because the
// global limit has been reached.
- MakeTestRequest(third_filter.get(), 0, kMaxRequestsPerProcess + 3,
- net::URLRequestTestJob::test_url_2());
+ MakeTestRequestWithResourceType(
+ third_filter.get(), 0, kMaxRequestsPerProcess + 3,
+ net::URLRequestTestJob::test_url_2(), RESOURCE_TYPE_SUB_RESOURCE);
// Flush all the pending requests.
while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
std::string response_data("<html><title>Test One</title></html>");
SetResponse(raw_headers, response_data);
- // Only MAIN_FRAMEs can trigger a download.
- SetResourceType(ResourceType::MAIN_FRAME);
-
HandleScheme("http");
- MakeTestRequest(0, 1, GURL("http:bla"));
+
+ // Only MAIN_FRAMEs can trigger a download.
+ MakeTestRequestWithResourceType(filter_.get(), 0, 1, GURL("http:bla"),
+ RESOURCE_TYPE_MAIN_FRAME);
// Flush all pending requests.
while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+ base::MessageLoop::current()->RunUntilIdle();
// Sorts out all the messages we saw by request.
ResourceIPCAccumulator::ClassifiedMessages msgs;
accum_.GetClassifiedMessages(&msgs);
// We should have gotten one RequestComplete message.
+ ASSERT_EQ(1U, msgs.size());
ASSERT_EQ(1U, msgs[0].size());
EXPECT_EQ(ResourceMsg_RequestComplete::ID, msgs[0][0].type());
// The RequestComplete message should have had the error code of
- // ERR_FILE_NOT_FOUND.
- int request_id;
- int error_code;
-
- PickleIterator iter(msgs[0][0]);
- EXPECT_TRUE(IPC::ReadParam(&msgs[0][0], &iter, &request_id));
- EXPECT_TRUE(IPC::ReadParam(&msgs[0][0], &iter, &error_code));
-
- EXPECT_EQ(1, request_id);
- EXPECT_EQ(net::ERR_FILE_NOT_FOUND, error_code);
+ // ERR_INVALID_RESPONSE.
+ CheckRequestCompleteErrorCode(msgs[0][0], net::ERR_INVALID_RESPONSE);
}
// Test for http://crbug.com/76202 . We don't want to destroy a
response_data.resize(1025, ' ');
SetResponse(raw_headers, response_data);
- SetResourceType(ResourceType::MAIN_FRAME);
- SetDelayedCompleteJobGeneration(true);
+ job_factory_->SetDelayedCompleteJobGeneration(true);
HandleScheme("http");
- MakeTestRequest(render_view_id, request_id, GURL("http://example.com/blah"));
+ MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
+ GURL("http://example.com/blah"),
+ RESOURCE_TYPE_MAIN_FRAME);
// Return some data so that the request is identified as a download
// and the proper resource handlers are created.
EXPECT_TRUE(net::URLRequestTestJob::ProcessOnePendingMessage());
// And now simulate a cancellation coming from the renderer.
ResourceHostMsg_CancelRequest msg(request_id);
- bool msg_was_ok;
- host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok);
+ host_.OnMessageReceived(msg, filter_.get());
// Since the request had already started processing as a download,
// the cancellation above should have been ignored and the request
response_data.resize(1025, ' ');
SetResponse(raw_headers, response_data);
- SetResourceType(ResourceType::MAIN_FRAME);
- SetDelayedCompleteJobGeneration(true);
+ job_factory_->SetDelayedCompleteJobGeneration(true);
HandleScheme("http");
- MakeTestRequest(render_view_id, request_id, GURL("http://example.com/blah"));
+ MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
+ GURL("http://example.com/blah"),
+ RESOURCE_TYPE_MAIN_FRAME);
// Return some data so that the request is identified as a download
// and the proper resource handlers are created.
EXPECT_TRUE(net::URLRequestTestJob::ProcessOnePendingMessage());
// And now simulate a cancellation coming from the renderer.
ResourceHostMsg_CancelRequest msg(request_id);
- bool msg_was_ok;
- host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok);
+ host_.OnMessageReceived(msg, filter_.get());
// Since the request had already started processing as a download,
// the cancellation above should have been ignored and the request
EXPECT_EQ(0, host_.pending_requests());
}
+TEST_F(ResourceDispatcherHostTest, CancelRequestsForContextDetached) {
+ EXPECT_EQ(0, host_.pending_requests());
+
+ int render_view_id = 0;
+ int request_id = 1;
+
+ MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
+ net::URLRequestTestJob::test_url_4(),
+ RESOURCE_TYPE_PREFETCH); // detachable type
+
+ // Simulate a cancel coming from the renderer.
+ RendererCancelRequest(request_id);
+
+ // Since the request had already started processing as detachable,
+ // the cancellation above should have been ignored and the request
+ // should have been detached.
+ EXPECT_EQ(1, host_.pending_requests());
+
+ // Cancelling by other methods should also leave it detached.
+ host_.CancelRequestsForProcess(render_view_id);
+ EXPECT_EQ(1, host_.pending_requests());
+
+ // Cancelling by context should work.
+ host_.CancelRequestsForContext(filter_->resource_context());
+ EXPECT_EQ(0, host_.pending_requests());
+}
+
// Test the cancelling of requests that are being transferred to a new renderer
// due to a redirection.
TEST_F(ResourceDispatcherHostTest, CancelRequestsForContextTransferred) {
std::string response_data("<html>foobar</html>");
SetResponse(raw_headers, response_data);
- SetResourceType(ResourceType::MAIN_FRAME);
HandleScheme("http");
- MakeTestRequest(render_view_id, request_id, GURL("http://example.com/blah"));
+ MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
+ GURL("http://example.com/blah"),
+ RESOURCE_TYPE_MAIN_FRAME);
+
GlobalRequestID global_request_id(filter_->child_id(), request_id);
- host_.MarkAsTransferredNavigation(global_request_id,
- GURL("http://example.com/blah"));
+ host_.MarkAsTransferredNavigation(global_request_id);
// And now simulate a cancellation coming from the renderer.
ResourceHostMsg_CancelRequest msg(request_id);
- bool msg_was_ok;
- host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok);
+ host_.OnMessageReceived(msg, filter_.get());
// Since the request is marked as being transferred,
// the cancellation above should have been ignored and the request
// Test transferred navigations with text/html, which doesn't trigger any
// content sniffing.
TEST_F(ResourceDispatcherHostTest, TransferNavigationHtml) {
+ // This test expects the cross site request to be leaked, so it can transfer
+ // the request directly.
+ CrossSiteResourceHandler::SetLeakRequestsForTesting(true);
+
EXPECT_EQ(0, host_.pending_requests());
int render_view_id = 0;
SetResponse("HTTP/1.1 302 Found\n"
"Location: http://other.com/blech\n\n");
- SetResourceType(ResourceType::MAIN_FRAME);
HandleScheme("http");
// Temporarily replace ContentBrowserClient with one that will trigger the
TransfersAllNavigationsContentBrowserClient new_client;
ContentBrowserClient* old_client = SetBrowserClientForTesting(&new_client);
- MakeTestRequest(render_view_id, request_id, GURL("http://example.com/blah"));
+ MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
+ GURL("http://example.com/blah"),
+ RESOURCE_TYPE_MAIN_FRAME);
// Now that we're blocked on the redirect, update the response and unblock by
// telling the AsyncResourceHandler to follow the redirect.
SetResponse("HTTP/1.1 200 OK\n"
"Content-Type: text/html\n\n",
kResponseBody);
- ResourceHostMsg_FollowRedirect redirect_msg(request_id, false, GURL());
- bool msg_was_ok;
- host_.OnMessageReceived(redirect_msg, filter_.get(), &msg_was_ok);
+ ResourceHostMsg_FollowRedirect redirect_msg(request_id);
+ host_.OnMessageReceived(redirect_msg, filter_.get());
base::MessageLoop::current()->RunUntilIdle();
// Flush all the pending requests to get the response through the
SetBrowserClientForTesting(old_client);
// This second filter is used to emulate a second process.
- scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
+ scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
int new_render_view_id = 1;
int new_request_id = 2;
ResourceHostMsg_Request request =
- CreateResourceRequest("GET", ResourceType::MAIN_FRAME,
+ CreateResourceRequest("GET", RESOURCE_TYPE_MAIN_FRAME,
GURL("http://other.com/blech"));
request.transferred_request_child_id = filter_->child_id();
request.transferred_request_request_id = request_id;
- // For cleanup.
- child_ids_.insert(second_filter->child_id());
ResourceHostMsg_RequestResource transfer_request_msg(
new_render_view_id, new_request_id, request);
- host_.OnMessageReceived(
- transfer_request_msg, second_filter.get(), &msg_was_ok);
+ host_.OnMessageReceived(transfer_request_msg, second_filter.get());
base::MessageLoop::current()->RunUntilIdle();
// Check generated messages.
// BufferedResourceHandler to buffer the response to sniff the content
// before the transfer occurs.
TEST_F(ResourceDispatcherHostTest, TransferNavigationText) {
+ // This test expects the cross site request to be leaked, so it can transfer
+ // the request directly.
+ CrossSiteResourceHandler::SetLeakRequestsForTesting(true);
+
EXPECT_EQ(0, host_.pending_requests());
int render_view_id = 0;
SetResponse("HTTP/1.1 302 Found\n"
"Location: http://other.com/blech\n\n");
- SetResourceType(ResourceType::MAIN_FRAME);
HandleScheme("http");
// Temporarily replace ContentBrowserClient with one that will trigger the
TransfersAllNavigationsContentBrowserClient new_client;
ContentBrowserClient* old_client = SetBrowserClientForTesting(&new_client);
- MakeTestRequest(render_view_id, request_id, GURL("http://example.com/blah"));
+ MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
+ GURL("http://example.com/blah"),
+ RESOURCE_TYPE_MAIN_FRAME);
// Now that we're blocked on the redirect, update the response and unblock by
// telling the AsyncResourceHandler to follow the redirect. Use a text/plain
SetResponse("HTTP/1.1 200 OK\n"
"Content-Type: text/plain\n\n",
kResponseBody);
- ResourceHostMsg_FollowRedirect redirect_msg(request_id, false, GURL());
- bool msg_was_ok;
- host_.OnMessageReceived(redirect_msg, filter_.get(), &msg_was_ok);
+ ResourceHostMsg_FollowRedirect redirect_msg(request_id);
+ host_.OnMessageReceived(redirect_msg, filter_.get());
base::MessageLoop::current()->RunUntilIdle();
// Flush all the pending requests to get the response through the
SetBrowserClientForTesting(old_client);
// This second filter is used to emulate a second process.
- scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
+ scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
int new_render_view_id = 1;
int new_request_id = 2;
ResourceHostMsg_Request request =
- CreateResourceRequest("GET", ResourceType::MAIN_FRAME,
+ CreateResourceRequest("GET", RESOURCE_TYPE_MAIN_FRAME,
GURL("http://other.com/blech"));
request.transferred_request_child_id = filter_->child_id();
request.transferred_request_request_id = request_id;
- // For cleanup.
- child_ids_.insert(second_filter->child_id());
ResourceHostMsg_RequestResource transfer_request_msg(
new_render_view_id, new_request_id, request);
- host_.OnMessageReceived(
- transfer_request_msg, second_filter.get(), &msg_was_ok);
+ host_.OnMessageReceived(transfer_request_msg, second_filter.get());
base::MessageLoop::current()->RunUntilIdle();
// Check generated messages.
}
TEST_F(ResourceDispatcherHostTest, TransferNavigationWithProcessCrash) {
+ // This test expects the cross site request to be leaked, so it can transfer
+ // the request directly.
+ CrossSiteResourceHandler::SetLeakRequestsForTesting(true);
+
EXPECT_EQ(0, host_.pending_requests());
int render_view_id = 0;
"Location: http://other.com/blech\n\n");
const std::string kResponseBody = "hello world";
- SetResourceType(ResourceType::MAIN_FRAME);
HandleScheme("http");
// Temporarily replace ContentBrowserClient with one that will trigger the
// Create a first filter that can be deleted before the second one starts.
{
- scoped_refptr<ForwardingFilter> first_filter = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
+ scoped_refptr<ForwardingFilter> first_filter = MakeForwardingFilter();
first_child_id = first_filter->child_id();
ResourceHostMsg_Request first_request =
- CreateResourceRequest("GET", ResourceType::MAIN_FRAME,
+ CreateResourceRequest("GET", RESOURCE_TYPE_MAIN_FRAME,
GURL("http://example.com/blah"));
- // For cleanup.
- child_ids_.insert(first_child_id);
ResourceHostMsg_RequestResource first_request_msg(
render_view_id, request_id, first_request);
- bool msg_was_ok;
- host_.OnMessageReceived(
- first_request_msg, first_filter.get(), &msg_was_ok);
+ host_.OnMessageReceived(first_request_msg, first_filter.get());
base::MessageLoop::current()->RunUntilIdle();
// Now that we're blocked on the redirect, update the response and unblock
SetResponse("HTTP/1.1 200 OK\n"
"Content-Type: text/html\n\n",
kResponseBody);
- ResourceHostMsg_FollowRedirect redirect_msg(request_id, false, GURL());
- host_.OnMessageReceived(redirect_msg, first_filter.get(), &msg_was_ok);
+ ResourceHostMsg_FollowRedirect redirect_msg(request_id);
+ host_.OnMessageReceived(redirect_msg, first_filter.get());
base::MessageLoop::current()->RunUntilIdle();
// Flush all the pending requests to get the response through the
GlobalRequestID first_global_request_id(first_child_id, request_id);
// This second filter is used to emulate a second process.
- scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
+ scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
int new_render_view_id = 1;
int new_request_id = 2;
ResourceHostMsg_Request request =
- CreateResourceRequest("GET", ResourceType::MAIN_FRAME,
+ CreateResourceRequest("GET", RESOURCE_TYPE_MAIN_FRAME,
GURL("http://other.com/blech"));
request.transferred_request_child_id = first_child_id;
request.transferred_request_request_id = request_id;
child_ids_.insert(second_filter->child_id());
ResourceHostMsg_RequestResource transfer_request_msg(
new_render_view_id, new_request_id, request);
- bool msg_was_ok;
- host_.OnMessageReceived(
- transfer_request_msg, second_filter.get(), &msg_was_ok);
+ host_.OnMessageReceived(transfer_request_msg, second_filter.get());
base::MessageLoop::current()->RunUntilIdle();
// Check generated messages.
}
TEST_F(ResourceDispatcherHostTest, TransferNavigationWithTwoRedirects) {
+ // This test expects the cross site request to be leaked, so it can transfer
+ // the request directly.
+ CrossSiteResourceHandler::SetLeakRequestsForTesting(true);
+
EXPECT_EQ(0, host_.pending_requests());
int render_view_id = 0;
SetResponse("HTTP/1.1 302 Found\n"
"Location: http://other.com/blech\n\n");
- SetResourceType(ResourceType::MAIN_FRAME);
HandleScheme("http");
// Temporarily replace ContentBrowserClient with one that will trigger the
TransfersAllNavigationsContentBrowserClient new_client;
ContentBrowserClient* old_client = SetBrowserClientForTesting(&new_client);
- MakeTestRequest(render_view_id, request_id, GURL("http://example.com/blah"));
+ MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
+ GURL("http://example.com/blah"),
+ RESOURCE_TYPE_MAIN_FRAME);
// Now that we're blocked on the redirect, simulate hitting another redirect.
SetResponse("HTTP/1.1 302 Found\n"
"Location: http://other.com/blerg\n\n");
- ResourceHostMsg_FollowRedirect redirect_msg(request_id, false, GURL());
- bool msg_was_ok;
- host_.OnMessageReceived(redirect_msg, filter_.get(), &msg_was_ok);
+ ResourceHostMsg_FollowRedirect redirect_msg(request_id);
+ host_.OnMessageReceived(redirect_msg, filter_.get());
base::MessageLoop::current()->RunUntilIdle();
// Now that we're blocked on the second redirect, update the response and
SetResponse("HTTP/1.1 200 OK\n"
"Content-Type: text/plain\n\n",
kResponseBody);
- ResourceHostMsg_FollowRedirect redirect_msg2(request_id, false, GURL());
- host_.OnMessageReceived(redirect_msg2, filter_.get(), &msg_was_ok);
+ ResourceHostMsg_FollowRedirect redirect_msg2(request_id);
+ host_.OnMessageReceived(redirect_msg2, filter_.get());
base::MessageLoop::current()->RunUntilIdle();
// Flush all the pending requests to get the response through the
SetBrowserClientForTesting(old_client);
// This second filter is used to emulate a second process.
- scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(
- this, browser_context_->GetResourceContext());
+ scoped_refptr<ForwardingFilter> second_filter = MakeForwardingFilter();
int new_render_view_id = 1;
int new_request_id = 2;
ResourceHostMsg_Request request =
- CreateResourceRequest("GET", ResourceType::MAIN_FRAME,
+ CreateResourceRequest("GET", RESOURCE_TYPE_MAIN_FRAME,
GURL("http://other.com/blech"));
request.transferred_request_child_id = filter_->child_id();
request.transferred_request_request_id = request_id;
child_ids_.insert(second_filter->child_id());
ResourceHostMsg_RequestResource transfer_request_msg(
new_render_view_id, new_request_id, request);
- host_.OnMessageReceived(
- transfer_request_msg, second_filter.get(), &msg_was_ok);
+ host_.OnMessageReceived(transfer_request_msg, second_filter.get());
// Verify that we update the ResourceRequestInfo.
GlobalRequestID global_request_id(second_filter->child_id(), new_request_id);
TEST_F(ResourceDispatcherHostTest, UnknownURLScheme) {
EXPECT_EQ(0, host_.pending_requests());
- SetResourceType(ResourceType::MAIN_FRAME);
HandleScheme("http");
- MakeTestRequest(0, 1, GURL("foo://bar"));
+ MakeTestRequestWithResourceType(filter_.get(), 0, 1, GURL("foo://bar"),
+ RESOURCE_TYPE_MAIN_FRAME);
// Flush all pending requests.
while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
// The RequestComplete message should have the error code of
// ERR_UNKNOWN_URL_SCHEME.
- int request_id;
- int error_code;
-
- PickleIterator iter(msgs[0][0]);
- EXPECT_TRUE(IPC::ReadParam(&msgs[0][0], &iter, &request_id));
- EXPECT_TRUE(IPC::ReadParam(&msgs[0][0], &iter, &error_code));
-
- EXPECT_EQ(1, request_id);
- EXPECT_EQ(net::ERR_UNKNOWN_URL_SCHEME, error_code);
+ CheckRequestCompleteErrorCode(msgs[0][0], net::ERR_UNKNOWN_URL_SCHEME);
}
TEST_F(ResourceDispatcherHostTest, DataReceivedACKs) {
EXPECT_EQ(ResourceMsg_RequestComplete::ID, msgs[0][size - 1].type());
}
+// Request a very large detachable resource and cancel part way. Some of the
+// data should have been sent to the renderer, but not all.
+TEST_F(ResourceDispatcherHostTest, DataSentBeforeDetach) {
+ EXPECT_EQ(0, host_.pending_requests());
+
+ int render_view_id = 0;
+ int request_id = 1;
+
+ std::string raw_headers("HTTP\n"
+ "Content-type: image/jpeg\n\n");
+ std::string response_data("01234567890123456789\x01foobar");
+
+ // Create a response larger than kMaxAllocationSize (currently 32K). Note
+ // that if this increase beyond 512K we'll need to make the response longer.
+ const int kAllocSize = 1024*512;
+ response_data.resize(kAllocSize, ' ');
+
+ SetResponse(raw_headers, response_data);
+ job_factory_->SetDelayedCompleteJobGeneration(true);
+ HandleScheme("http");
+
+ MakeTestRequestWithResourceType(filter_.get(), render_view_id, request_id,
+ GURL("http://example.com/blah"),
+ RESOURCE_TYPE_PREFETCH);
+
+ // Get a bit of data before cancelling.
+ EXPECT_TRUE(net::URLRequestTestJob::ProcessOnePendingMessage());
+
+ // Simulate a cancellation coming from the renderer.
+ ResourceHostMsg_CancelRequest msg(request_id);
+ host_.OnMessageReceived(msg, filter_.get());
+
+ EXPECT_EQ(1, host_.pending_requests());
+
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+
+ // Sort all the messages we saw by request.
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+
+ EXPECT_EQ(4U, msgs[0].size());
+
+ // Figure out how many bytes were received by the renderer.
+ int data_offset;
+ int data_length;
+ ASSERT_TRUE(
+ ExtractDataOffsetAndLength(msgs[0][2], &data_offset, &data_length));
+ EXPECT_LT(0, data_length);
+ EXPECT_GT(kAllocSize, data_length);
+
+ // Verify the data that was received before cancellation. The request should
+ // have appeared to cancel, however.
+ CheckSuccessfulRequestWithErrorCode(
+ msgs[0],
+ std::string(response_data.begin(), response_data.begin() + data_length),
+ net::ERR_ABORTED);
+}
+
TEST_F(ResourceDispatcherHostTest, DelayedDataReceivedACKs) {
EXPECT_EQ(0, host_.pending_requests());
EXPECT_EQ(ResourceMsg_DataReceived::ID, msgs[0][i].type());
ResourceHostMsg_DataReceived_ACK msg(1);
- bool msg_was_ok;
- host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok);
+ host_.OnMessageReceived(msg, filter_.get());
}
base::MessageLoop::current()->RunUntilIdle();
// Send some unexpected ACKs.
for (size_t i = 0; i < 128; ++i) {
ResourceHostMsg_DataReceived_ACK msg(1);
- bool msg_was_ok;
- host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok);
+ host_.OnMessageReceived(msg, filter_.get());
}
msgs[0].erase(msgs[0].begin());
EXPECT_EQ(ResourceMsg_DataReceived::ID, msgs[0][i].type());
ResourceHostMsg_DataReceived_ACK msg(1);
- bool msg_was_ok;
- host_.OnMessageReceived(msg, filter_.get(), &msg_was_ok);
+ host_.OnMessageReceived(msg, filter_.get());
}
base::MessageLoop::current()->RunUntilIdle();
}
}
+// Tests the dispatcher host's temporary file management.
+TEST_F(ResourceDispatcherHostTest, RegisterDownloadedTempFile) {
+ const int kRequestID = 1;
+
+ // Create a temporary file.
+ base::FilePath file_path;
+ ASSERT_TRUE(base::CreateTemporaryFile(&file_path));
+ scoped_refptr<ShareableFileReference> deletable_file =
+ ShareableFileReference::GetOrCreate(
+ file_path,
+ ShareableFileReference::DELETE_ON_FINAL_RELEASE,
+ BrowserThread::GetMessageLoopProxyForThread(
+ BrowserThread::FILE).get());
+
+ // Not readable.
+ EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+ filter_->child_id(), file_path));
+
+ // Register it for a resource request.
+ host_.RegisterDownloadedTempFile(filter_->child_id(), kRequestID, file_path);
+
+ // Should be readable now.
+ EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+ filter_->child_id(), file_path));
+
+ // The child releases from the request.
+ ResourceHostMsg_ReleaseDownloadedFile release_msg(kRequestID);
+ host_.OnMessageReceived(release_msg, filter_);
+
+ // Still readable because there is another reference to the file. (The child
+ // may take additional blob references.)
+ EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+ filter_->child_id(), file_path));
+
+ // Release extra references and wait for the file to be deleted. (This relies
+ // on the delete happening on the FILE thread which is mapped to main thread
+ // in this test.)
+ deletable_file = NULL;
+ base::RunLoop().RunUntilIdle();
+
+ // The file is no longer readable to the child and has been deleted.
+ EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+ filter_->child_id(), file_path));
+ EXPECT_FALSE(base::PathExists(file_path));
+}
+
+// Tests that temporary files held on behalf of child processes are released
+// when the child process dies.
+TEST_F(ResourceDispatcherHostTest, ReleaseTemporiesOnProcessExit) {
+ const int kRequestID = 1;
+
+ // Create a temporary file.
+ base::FilePath file_path;
+ ASSERT_TRUE(base::CreateTemporaryFile(&file_path));
+ scoped_refptr<ShareableFileReference> deletable_file =
+ ShareableFileReference::GetOrCreate(
+ file_path,
+ ShareableFileReference::DELETE_ON_FINAL_RELEASE,
+ BrowserThread::GetMessageLoopProxyForThread(
+ BrowserThread::FILE).get());
+
+ // Register it for a resource request.
+ host_.RegisterDownloadedTempFile(filter_->child_id(), kRequestID, file_path);
+ deletable_file = NULL;
+
+ // Should be readable now.
+ EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+ filter_->child_id(), file_path));
+
+ // Let the process die.
+ filter_->OnChannelClosing();
+ base::RunLoop().RunUntilIdle();
+
+ // The file is no longer readable to the child and has been deleted.
+ EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+ filter_->child_id(), file_path));
+ EXPECT_FALSE(base::PathExists(file_path));
+}
+
+TEST_F(ResourceDispatcherHostTest, DownloadToFile) {
+ // Make a request which downloads to file.
+ ResourceHostMsg_Request request = CreateResourceRequest(
+ "GET", RESOURCE_TYPE_SUB_RESOURCE, net::URLRequestTestJob::test_url_1());
+ request.download_to_file = true;
+ ResourceHostMsg_RequestResource request_msg(0, 1, request);
+ host_.OnMessageReceived(request_msg, filter_);
+
+ // Running the message loop until idle does not work because
+ // RedirectToFileResourceHandler posts things to base::WorkerPool. Instead,
+ // wait for the ResourceMsg_RequestComplete to go out. Then run the event loop
+ // until idle so the loader is gone.
+ WaitForRequestComplete();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, host_.pending_requests());
+
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+
+ ASSERT_EQ(1U, msgs.size());
+ const std::vector<IPC::Message>& messages = msgs[0];
+
+ // The request should contain the following messages:
+ // ReceivedResponse (indicates headers received and filename)
+ // DataDownloaded* (bytes downloaded and total length)
+ // RequestComplete (request is done)
+
+ // ReceivedResponse
+ ResourceResponseHead response_head;
+ GetResponseHead(messages, &response_head);
+ ASSERT_FALSE(response_head.download_file_path.empty());
+
+ // DataDownloaded
+ size_t total_len = 0;
+ for (size_t i = 1; i < messages.size() - 1; i++) {
+ ASSERT_EQ(ResourceMsg_DataDownloaded::ID, messages[i].type());
+ PickleIterator iter(messages[i]);
+ int request_id, data_len;
+ ASSERT_TRUE(IPC::ReadParam(&messages[i], &iter, &request_id));
+ ASSERT_TRUE(IPC::ReadParam(&messages[i], &iter, &data_len));
+ total_len += data_len;
+ }
+ EXPECT_EQ(net::URLRequestTestJob::test_data_1().size(), total_len);
+
+ // RequestComplete
+ CheckRequestCompleteErrorCode(messages.back(), net::OK);
+
+ // Verify that the data ended up in the temporary file.
+ std::string contents;
+ ASSERT_TRUE(base::ReadFileToString(response_head.download_file_path,
+ &contents));
+ EXPECT_EQ(net::URLRequestTestJob::test_data_1(), contents);
+
+ // The file should be readable by the child.
+ EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+ filter_->child_id(), response_head.download_file_path));
+
+ // When the renderer releases the file, it should be deleted. Again,
+ // RunUntilIdle doesn't work because base::WorkerPool is involved.
+ ShareableFileReleaseWaiter waiter(response_head.download_file_path);
+ ResourceHostMsg_ReleaseDownloadedFile release_msg(1);
+ host_.OnMessageReceived(release_msg, filter_);
+ waiter.Wait();
+ // The release callback runs before the delete is scheduled, so pump the
+ // message loop for the delete itself. (This relies on the delete happening on
+ // the FILE thread which is mapped to main thread in this test.)
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(base::PathExists(response_head.download_file_path));
+ EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
+ filter_->child_id(), response_head.download_file_path));
+}
+
+net::URLRequestJob* TestURLRequestJobFactory::MaybeCreateJobWithProtocolHandler(
+ const std::string& scheme,
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const {
+ url_request_jobs_created_count_++;
+ if (test_fixture_->response_headers_.empty()) {
+ if (delay_start_) {
+ return new URLRequestTestDelayedStartJob(request, network_delegate);
+ } else if (delay_complete_) {
+ return new URLRequestTestDelayedCompletionJob(request,
+ network_delegate);
+ } else if (network_start_notification_) {
+ return new URLRequestTestDelayedNetworkJob(request, network_delegate);
+ } else if (scheme == "big-job") {
+ return new URLRequestBigJob(request, network_delegate);
+ } else {
+ return new net::URLRequestTestJob(request, network_delegate);
+ }
+ } else {
+ if (delay_start_) {
+ return new URLRequestTestDelayedStartJob(
+ request, network_delegate,
+ test_fixture_->response_headers_, test_fixture_->response_data_,
+ false);
+ } else if (delay_complete_) {
+ return new URLRequestTestDelayedCompletionJob(
+ request, network_delegate,
+ test_fixture_->response_headers_, test_fixture_->response_data_,
+ false);
+ } else {
+ return new net::URLRequestTestJob(
+ request, network_delegate,
+ test_fixture_->response_headers_, test_fixture_->response_data_,
+ false);
+ }
+ }
+}
+
} // namespace content