This source file includes following definitions.
- request_count_
- GetProxyForURL
- CancelRequest
- GetLoadState
- CancelSetPacScript
- SetPacScript
- request_count
- last_script_data
- SetResolveLatency
- CheckIsOnWorkerThread
- blocked_
- Block
- Unblock
- WaitUntilBlocked
- GetProxyForURL
- impl_
- GetProxyForURL
- CancelRequest
- GetLoadState
- CancelSetPacScript
- SetPacScript
- resolver_
- CreateProxyResolver
- CreateProxyResolver
- resolvers
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
- TEST
#include "net/proxy/multi_threaded_proxy_resolver.h"
#include "base/message_loop/message_loop.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/platform_thread.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
#include "net/base/net_log_unittest.h"
#include "net/base/test_completion_callback.h"
#include "net/proxy/proxy_info.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
using base::ASCIIToUTF16;
namespace net {
namespace {
class MockProxyResolver : public ProxyResolver {
public:
MockProxyResolver()
: ProxyResolver(true ),
wrong_loop_(base::MessageLoop::current()),
request_count_(0) {}
virtual int GetProxyForURL(const GURL& query_url,
ProxyInfo* results,
const CompletionCallback& callback,
RequestHandle* request,
const BoundNetLog& net_log) OVERRIDE {
if (resolve_latency_ != base::TimeDelta())
base::PlatformThread::Sleep(resolve_latency_);
CheckIsOnWorkerThread();
EXPECT_TRUE(callback.is_null());
EXPECT_TRUE(request == NULL);
net_log.BeginEvent(NetLog::TYPE_PAC_JAVASCRIPT_ALERT);
results->UseNamedProxy(query_url.host());
return request_count_++;
}
virtual void CancelRequest(RequestHandle request) OVERRIDE {
NOTREACHED();
}
virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE {
NOTREACHED();
return LOAD_STATE_IDLE;
}
virtual void CancelSetPacScript() OVERRIDE {
NOTREACHED();
}
virtual int SetPacScript(
const scoped_refptr<ProxyResolverScriptData>& script_data,
const CompletionCallback& callback) OVERRIDE {
CheckIsOnWorkerThread();
last_script_data_ = script_data;
return OK;
}
int request_count() const { return request_count_; }
const ProxyResolverScriptData* last_script_data() const {
return last_script_data_.get();
}
void SetResolveLatency(base::TimeDelta latency) {
resolve_latency_ = latency;
}
private:
void CheckIsOnWorkerThread() {
EXPECT_NE(base::MessageLoop::current(), wrong_loop_);
}
base::MessageLoop* wrong_loop_;
int request_count_;
scoped_refptr<ProxyResolverScriptData> last_script_data_;
base::TimeDelta resolve_latency_;
};
class BlockableProxyResolver : public MockProxyResolver {
public:
BlockableProxyResolver()
: should_block_(false),
unblocked_(true, true),
blocked_(true, false) {
}
void Block() {
should_block_ = true;
unblocked_.Reset();
}
void Unblock() {
should_block_ = false;
blocked_.Reset();
unblocked_.Signal();
}
void WaitUntilBlocked() {
blocked_.Wait();
}
virtual int GetProxyForURL(const GURL& query_url,
ProxyInfo* results,
const CompletionCallback& callback,
RequestHandle* request,
const BoundNetLog& net_log) OVERRIDE {
if (should_block_) {
blocked_.Signal();
unblocked_.Wait();
}
return MockProxyResolver::GetProxyForURL(
query_url, results, callback, request, net_log);
}
private:
bool should_block_;
base::WaitableEvent unblocked_;
base::WaitableEvent blocked_;
};
class ForwardingProxyResolver : public ProxyResolver {
public:
explicit ForwardingProxyResolver(ProxyResolver* impl)
: ProxyResolver(impl->expects_pac_bytes()),
impl_(impl) {}
virtual int GetProxyForURL(const GURL& query_url,
ProxyInfo* results,
const CompletionCallback& callback,
RequestHandle* request,
const BoundNetLog& net_log) OVERRIDE {
return impl_->GetProxyForURL(
query_url, results, callback, request, net_log);
}
virtual void CancelRequest(RequestHandle request) OVERRIDE {
impl_->CancelRequest(request);
}
virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE {
NOTREACHED();
return LOAD_STATE_IDLE;
}
virtual void CancelSetPacScript() OVERRIDE {
impl_->CancelSetPacScript();
}
virtual int SetPacScript(
const scoped_refptr<ProxyResolverScriptData>& script_data,
const CompletionCallback& callback) OVERRIDE {
return impl_->SetPacScript(script_data, callback);
}
private:
ProxyResolver* impl_;
};
class ForwardingProxyResolverFactory : public ProxyResolverFactory {
public:
explicit ForwardingProxyResolverFactory(ProxyResolver* resolver)
: ProxyResolverFactory(resolver->expects_pac_bytes()),
resolver_(resolver) {}
virtual ProxyResolver* CreateProxyResolver() OVERRIDE {
return new ForwardingProxyResolver(resolver_);
}
private:
ProxyResolver* resolver_;
};
class BlockableProxyResolverFactory : public ProxyResolverFactory {
public:
BlockableProxyResolverFactory() : ProxyResolverFactory(true) {}
virtual ~BlockableProxyResolverFactory() {
STLDeleteElements(&resolvers_);
}
virtual ProxyResolver* CreateProxyResolver() OVERRIDE {
BlockableProxyResolver* resolver = new BlockableProxyResolver;
resolvers_.push_back(resolver);
return new ForwardingProxyResolver(resolver);
}
std::vector<BlockableProxyResolver*> resolvers() {
return resolvers_;
}
private:
std::vector<BlockableProxyResolver*> resolvers_;
};
TEST(MultiThreadedProxyResolverTest, SingleThread_Basic) {
const size_t kNumThreads = 1u;
scoped_ptr<MockProxyResolver> mock(new MockProxyResolver);
MultiThreadedProxyResolver resolver(
new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
int rv;
EXPECT_TRUE(resolver.expects_pac_bytes());
TestCompletionCallback set_script_callback;
rv = resolver.SetPacScript(
ProxyResolverScriptData::FromUTF8("pac script bytes"),
set_script_callback.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, set_script_callback.WaitForResult());
EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
mock->last_script_data()->utf16());
TestCompletionCallback callback0;
CapturingBoundNetLog log0;
ProxyInfo results0;
rv = resolver.GetProxyForURL(GURL("http://request0"), &results0,
callback0.callback(), NULL, log0.bound());
EXPECT_EQ(ERR_IO_PENDING, rv);
rv = callback0.WaitForResult();
EXPECT_EQ(0, rv);
EXPECT_EQ("PROXY request0:80", results0.ToPacString());
CapturingNetLog::CapturedEntryList entries0;
log0.GetEntries(&entries0);
ASSERT_EQ(2u, entries0.size());
EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD, entries0[0].type);
TestCompletionCallback callback1;
ProxyInfo results1;
rv = resolver.GetProxyForURL(GURL("http://request1"), &results1,
callback1.callback(), NULL, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
TestCompletionCallback callback2;
ProxyInfo results2;
rv = resolver.GetProxyForURL(GURL("http://request2"), &results2,
callback2.callback(), NULL, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
TestCompletionCallback callback3;
ProxyInfo results3;
rv = resolver.GetProxyForURL(GURL("http://request3"), &results3,
callback3.callback(), NULL, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
rv = callback1.WaitForResult();
EXPECT_EQ(1, rv);
EXPECT_EQ("PROXY request1:80", results1.ToPacString());
rv = callback2.WaitForResult();
EXPECT_EQ(2, rv);
EXPECT_EQ("PROXY request2:80", results2.ToPacString());
rv = callback3.WaitForResult();
EXPECT_EQ(3, rv);
EXPECT_EQ("PROXY request3:80", results3.ToPacString());
}
TEST(MultiThreadedProxyResolverTest,
SingleThread_UpdatesNetLogWithThreadWait) {
const size_t kNumThreads = 1u;
scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
MultiThreadedProxyResolver resolver(
new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
int rv;
TestCompletionCallback init_callback;
rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
init_callback.callback());
EXPECT_EQ(OK, init_callback.WaitForResult());
mock->Block();
ProxyResolver::RequestHandle request0;
TestCompletionCallback callback0;
ProxyInfo results0;
CapturingBoundNetLog log0;
rv = resolver.GetProxyForURL(GURL("http://request0"), &results0,
callback0.callback(), &request0, log0.bound());
EXPECT_EQ(ERR_IO_PENDING, rv);
TestCompletionCallback callback1;
ProxyInfo results1;
CapturingBoundNetLog log1;
rv = resolver.GetProxyForURL(GURL("http://request1"), &results1,
callback1.callback(), NULL, log1.bound());
EXPECT_EQ(ERR_IO_PENDING, rv);
ProxyResolver::RequestHandle request2;
TestCompletionCallback callback2;
ProxyInfo results2;
CapturingBoundNetLog log2;
rv = resolver.GetProxyForURL(GURL("http://request2"), &results2,
callback2.callback(), &request2, log2.bound());
EXPECT_EQ(ERR_IO_PENDING, rv);
mock->WaitUntilBlocked();
mock->Unblock();
EXPECT_EQ(0, callback0.WaitForResult());
EXPECT_EQ("PROXY request0:80", results0.ToPacString());
CapturingNetLog::CapturedEntryList entries0;
log0.GetEntries(&entries0);
ASSERT_EQ(2u, entries0.size());
EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD,
entries0[0].type);
EXPECT_EQ(1, callback1.WaitForResult());
EXPECT_EQ("PROXY request1:80", results1.ToPacString());
CapturingNetLog::CapturedEntryList entries1;
log1.GetEntries(&entries1);
ASSERT_EQ(4u, entries1.size());
EXPECT_TRUE(LogContainsBeginEvent(
entries1, 0,
NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
EXPECT_TRUE(LogContainsEndEvent(
entries1, 1,
NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
EXPECT_EQ(2, callback2.WaitForResult());
EXPECT_EQ("PROXY request2:80", results2.ToPacString());
CapturingNetLog::CapturedEntryList entries2;
log2.GetEntries(&entries2);
ASSERT_EQ(4u, entries2.size());
EXPECT_TRUE(LogContainsBeginEvent(
entries2, 0,
NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
EXPECT_TRUE(LogContainsEndEvent(
entries2, 1,
NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
}
TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequest) {
const size_t kNumThreads = 1u;
scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
MultiThreadedProxyResolver resolver(
new ForwardingProxyResolverFactory(mock.get()),
kNumThreads);
int rv;
TestCompletionCallback init_callback;
rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
init_callback.callback());
EXPECT_EQ(OK, init_callback.WaitForResult());
mock->Block();
ProxyResolver::RequestHandle request0;
TestCompletionCallback callback0;
ProxyInfo results0;
rv = resolver.GetProxyForURL(GURL("http://request0"), &results0,
callback0.callback(), &request0, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
mock->WaitUntilBlocked();
TestCompletionCallback callback1;
ProxyInfo results1;
rv = resolver.GetProxyForURL(GURL("http://request1"), &results1,
callback1.callback(), NULL, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
ProxyResolver::RequestHandle request2;
TestCompletionCallback callback2;
ProxyInfo results2;
rv = resolver.GetProxyForURL(GURL("http://request2"), &results2,
callback2.callback(), &request2, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
TestCompletionCallback callback3;
ProxyInfo results3;
rv = resolver.GetProxyForURL(GURL("http://request3"), &results3,
callback3.callback(), NULL, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
resolver.CancelRequest(request0);
resolver.CancelRequest(request2);
mock->Unblock();
rv = callback1.WaitForResult();
EXPECT_EQ(1, rv);
EXPECT_EQ("PROXY request1:80", results1.ToPacString());
rv = callback3.WaitForResult();
EXPECT_EQ(2, rv);
EXPECT_EQ("PROXY request3:80", results3.ToPacString());
EXPECT_FALSE(callback0.have_result());
EXPECT_FALSE(callback2.have_result());
}
TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequestByDeleting) {
const size_t kNumThreads = 1u;
scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
scoped_ptr<MultiThreadedProxyResolver> resolver(
new MultiThreadedProxyResolver(
new ForwardingProxyResolverFactory(mock.get()), kNumThreads));
int rv;
TestCompletionCallback init_callback;
rv = resolver->SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
init_callback.callback());
EXPECT_EQ(OK, init_callback.WaitForResult());
mock->Block();
TestCompletionCallback callback0;
ProxyInfo results0;
rv = resolver->GetProxyForURL(GURL("http://request0"), &results0,
callback0.callback(), NULL, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
TestCompletionCallback callback1;
ProxyInfo results1;
rv = resolver->GetProxyForURL(GURL("http://request1"), &results1,
callback1.callback(), NULL, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
TestCompletionCallback callback2;
ProxyInfo results2;
rv = resolver->GetProxyForURL(GURL("http://request2"), &results2,
callback2.callback(), NULL, BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
mock->WaitUntilBlocked();
mock->SetResolveLatency(base::TimeDelta::FromMilliseconds(100));
mock->Unblock();
resolver.reset();
base::MessageLoop::current()->RunUntilIdle();
EXPECT_FALSE(callback0.have_result());
EXPECT_FALSE(callback1.have_result());
EXPECT_FALSE(callback2.have_result());
}
TEST(MultiThreadedProxyResolverTest, SingleThread_CancelSetPacScript) {
const size_t kNumThreads = 1u;
scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
MultiThreadedProxyResolver resolver(
new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
int rv;
TestCompletionCallback set_pac_script_callback;
rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("data"),
set_pac_script_callback.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
resolver.CancelSetPacScript();
TestCompletionCallback set_pac_script_callback2;
rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("data2"),
set_pac_script_callback2.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
rv = set_pac_script_callback2.WaitForResult();
EXPECT_EQ(0, rv);
EXPECT_EQ(ASCIIToUTF16("data2"), mock->last_script_data()->utf16());
EXPECT_FALSE(set_pac_script_callback.have_result());
}
TEST(MultiThreadedProxyResolverTest, ThreeThreads_Basic) {
const size_t kNumThreads = 3u;
BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory;
MultiThreadedProxyResolver resolver(factory, kNumThreads);
int rv;
EXPECT_TRUE(resolver.expects_pac_bytes());
TestCompletionCallback set_script_callback;
rv = resolver.SetPacScript(
ProxyResolverScriptData::FromUTF8("pac script bytes"),
set_script_callback.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, set_script_callback.WaitForResult());
ASSERT_EQ(1u, factory->resolvers().size());
EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
factory->resolvers()[0]->last_script_data()->utf16());
const int kNumRequests = 9;
TestCompletionCallback callback[kNumRequests];
ProxyInfo results[kNumRequests];
ProxyResolver::RequestHandle request[kNumRequests];
rv = resolver.GetProxyForURL(
GURL("http://request0"), &results[0], callback[0].callback(), &request[0],
BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
rv = callback[0].WaitForResult();
EXPECT_EQ(0, rv);
EXPECT_EQ("PROXY request0:80", results[0].ToPacString());
ASSERT_EQ(1u, factory->resolvers().size());
EXPECT_EQ(1, factory->resolvers()[0]->request_count());
base::MessageLoop::current()->RunUntilIdle();
for (int i = 1; i < kNumRequests; ++i) {
rv = resolver.GetProxyForURL(
GURL(base::StringPrintf("http://request%d", i)), &results[i],
callback[i].callback(), &request[i], BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
}
ASSERT_EQ(3u, factory->resolvers().size());
resolver.CancelRequest(request[1]);
resolver.CancelRequest(request[3]);
resolver.CancelRequest(request[6]);
int kNonCancelledRequests[] = {2, 4, 5, 7, 8};
for (size_t i = 0; i < arraysize(kNonCancelledRequests); ++i) {
int request_index = kNonCancelledRequests[i];
EXPECT_GE(callback[request_index].WaitForResult(), 0);
}
EXPECT_FALSE(callback[1].have_result());
EXPECT_FALSE(callback[3].have_result());
EXPECT_FALSE(callback[6].have_result());
TestCompletionCallback set_script_callback2;
rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("xyz"),
set_script_callback2.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, set_script_callback2.WaitForResult());
ASSERT_EQ(4u, factory->resolvers().size());
for (int i = 0; i < 3; ++i) {
EXPECT_EQ(
ASCIIToUTF16("pac script bytes"),
factory->resolvers()[i]->last_script_data()->utf16()) << "i=" << i;
}
EXPECT_EQ(ASCIIToUTF16("xyz"),
factory->resolvers()[3]->last_script_data()->utf16());
ASSERT_EQ(4u, factory->resolvers().size());
int total_count = 0;
for (int i = 0; i < 3; ++i) {
total_count += factory->resolvers()[i]->request_count();
}
EXPECT_EQ(7, total_count);
}
TEST(MultiThreadedProxyResolverTest, OneThreadBlocked) {
const size_t kNumThreads = 2u;
BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory;
MultiThreadedProxyResolver resolver(factory, kNumThreads);
int rv;
EXPECT_TRUE(resolver.expects_pac_bytes());
TestCompletionCallback set_script_callback;
rv = resolver.SetPacScript(
ProxyResolverScriptData::FromUTF8("pac script bytes"),
set_script_callback.callback());
EXPECT_EQ(ERR_IO_PENDING, rv);
EXPECT_EQ(OK, set_script_callback.WaitForResult());
ASSERT_EQ(1u, factory->resolvers().size());
EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
factory->resolvers()[0]->last_script_data()->utf16());
const int kNumRequests = 4;
TestCompletionCallback callback[kNumRequests];
ProxyInfo results[kNumRequests];
ProxyResolver::RequestHandle request[kNumRequests];
factory->resolvers()[0]->Block();
rv = resolver.GetProxyForURL(
GURL("http://request0"), &results[0], callback[0].callback(), &request[0],
BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
factory->resolvers()[0]->WaitUntilBlocked();
for (int i = 1; i < kNumRequests; ++i) {
rv = resolver.GetProxyForURL(
GURL(base::StringPrintf("http://request%d", i)),
&results[i], callback[i].callback(), &request[i], BoundNetLog());
EXPECT_EQ(ERR_IO_PENDING, rv);
}
for (int i = 1; i < kNumRequests; ++i) {
EXPECT_EQ(i - 1, callback[i].WaitForResult());
}
factory->resolvers()[0]->Unblock();
EXPECT_EQ(0, callback[0].WaitForResult());
ASSERT_EQ(2u, factory->resolvers().size());
EXPECT_EQ(1, factory->resolvers()[0]->request_count());
EXPECT_EQ(3, factory->resolvers()[1]->request_count());
}
}
}