// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/base/host_resolver_impl.h" #include <string> #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" #include "base/message_loop.h" #include "base/string_util.h" #include "base/stringprintf.h" #include "net/base/address_list.h" #include "net/base/completion_callback.h" #include "net/base/mock_host_resolver.h" #include "net/base/net_errors.h" #include "net/base/net_log_unittest.h" #include "net/base/net_util.h" #include "net/base/sys_addrinfo.h" #include "net/base/test_completion_callback.h" #include "testing/gtest/include/gtest/gtest.h" // TODO(eroman): // - Test mixing async with sync (in particular how does sync update the // cache while an async is already pending). namespace net { namespace { HostCache* CreateDefaultCache() { return new HostCache( 100, // max cache entries. base::TimeDelta::FromMinutes(1), base::TimeDelta::FromSeconds(0)); } static const size_t kMaxJobs = 10u; HostResolverImpl* CreateHostResolverImpl(HostResolverProc* resolver_proc) { return new HostResolverImpl(resolver_proc, CreateDefaultCache(), kMaxJobs, NULL); } // Helper to create a HostResolver::RequestInfo. HostResolver::RequestInfo CreateResolverRequest( const std::string& hostname, RequestPriority priority) { HostResolver::RequestInfo info(HostPortPair(hostname, 80)); info.set_priority(priority); return info; } // Helper to create a HostResolver::RequestInfo. HostResolver::RequestInfo CreateResolverRequestForAddressFamily( const std::string& hostname, RequestPriority priority, AddressFamily address_family) { HostResolver::RequestInfo info(HostPortPair(hostname, 80)); info.set_priority(priority); info.set_address_family(address_family); return info; } // A variant of WaitingHostResolverProc that pushes each host mapped into a // list. // (and uses a manual-reset event rather than auto-reset). class CapturingHostResolverProc : public HostResolverProc { public: struct CaptureEntry { CaptureEntry(const std::string& hostname, AddressFamily address_family) : hostname(hostname), address_family(address_family) {} std::string hostname; AddressFamily address_family; }; typedef std::vector<CaptureEntry> CaptureList; explicit CapturingHostResolverProc(HostResolverProc* previous) : HostResolverProc(previous), event_(true, false) { } void Signal() { event_.Signal(); } virtual int Resolve(const std::string& hostname, AddressFamily address_family, HostResolverFlags host_resolver_flags, AddressList* addrlist, int* os_error) { event_.Wait(); { base::AutoLock l(lock_); capture_list_.push_back(CaptureEntry(hostname, address_family)); } return ResolveUsingPrevious(hostname, address_family, host_resolver_flags, addrlist, os_error); } CaptureList GetCaptureList() const { CaptureList copy; { base::AutoLock l(lock_); copy = capture_list_; } return copy; } private: ~CapturingHostResolverProc() {} CaptureList capture_list_; mutable base::Lock lock_; base::WaitableEvent event_; }; // This resolver function creates an IPv4 address, whose numeral value // describes a hash of the requested hostname, and the value of the requested // address_family. // // The resolved address for (hostname, address_family) will take the form: // 192.x.y.z // // Where: // x = length of hostname // y = ASCII value of hostname[0] // z = value of address_family // class EchoingHostResolverProc : public HostResolverProc { public: EchoingHostResolverProc() : HostResolverProc(NULL) {} virtual int Resolve(const std::string& hostname, AddressFamily address_family, HostResolverFlags host_resolver_flags, AddressList* addrlist, int* os_error) { // Encode the request's hostname and address_family in the output address. std::string ip_literal = base::StringPrintf("192.%d.%d.%d", static_cast<int>(hostname.size()), static_cast<int>(hostname[0]), static_cast<int>(address_family)); return SystemHostResolverProc(ip_literal, ADDRESS_FAMILY_UNSPECIFIED, host_resolver_flags, addrlist, os_error); } }; // Helper that represents a single Resolve() result, used to inspect all the // resolve results by forwarding them to Delegate. class ResolveRequest { public: // Delegate interface, for notification when the ResolveRequest completes. class Delegate { public: virtual ~Delegate() {} virtual void OnCompleted(ResolveRequest* resolve) = 0; }; ResolveRequest(HostResolver* resolver, const std::string& hostname, int port, Delegate* delegate) : info_(HostPortPair(hostname, port)), resolver_(resolver), delegate_(delegate), ALLOW_THIS_IN_INITIALIZER_LIST( callback_(this, &ResolveRequest::OnLookupFinished)) { // Start the request. int err = resolver->Resolve(info_, &addrlist_, &callback_, &req_, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, err); } ResolveRequest(HostResolver* resolver, const HostResolver::RequestInfo& info, Delegate* delegate) : info_(info), resolver_(resolver), delegate_(delegate), ALLOW_THIS_IN_INITIALIZER_LIST( callback_(this, &ResolveRequest::OnLookupFinished)) { // Start the request. int err = resolver->Resolve(info, &addrlist_, &callback_, &req_, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, err); } void Cancel() { resolver_->CancelRequest(req_); } const std::string& hostname() const { return info_.hostname(); } int port() const { return info_.port(); } int result() const { return result_; } const AddressList& addrlist() const { return addrlist_; } HostResolver* resolver() const { return resolver_; } private: void OnLookupFinished(int result) { result_ = result; delegate_->OnCompleted(this); } // The request details. HostResolver::RequestInfo info_; HostResolver::RequestHandle req_; // The result of the resolve. int result_; AddressList addrlist_; HostResolver* resolver_; Delegate* delegate_; CompletionCallbackImpl<ResolveRequest> callback_; DISALLOW_COPY_AND_ASSIGN(ResolveRequest); }; class HostResolverImplTest : public testing::Test { public: HostResolverImplTest() : callback_called_(false), ALLOW_THIS_IN_INITIALIZER_LIST( callback_(this, &HostResolverImplTest::OnLookupFinished)) { } protected: bool callback_called_; int callback_result_; CompletionCallbackImpl<HostResolverImplTest> callback_; private: void OnLookupFinished(int result) { callback_called_ = true; callback_result_ = result; MessageLoop::current()->Quit(); } }; TEST_F(HostResolverImplTest, SynchronousLookup) { AddressList addrlist; const int kPortnum = 80; scoped_refptr<RuleBasedHostResolverProc> resolver_proc( new RuleBasedHostResolverProc(NULL)); resolver_proc->AddRule("just.testing", "192.168.1.42"); scoped_ptr<HostResolver> host_resolver( CreateHostResolverImpl(resolver_proc)); HostResolver::RequestInfo info(HostPortPair("just.testing", kPortnum)); CapturingBoundNetLog log(CapturingNetLog::kUnbounded); int err = host_resolver->Resolve(info, &addrlist, NULL, NULL, log.bound()); EXPECT_EQ(OK, err); CapturingNetLog::EntryList entries; log.GetEntries(&entries); EXPECT_EQ(2u, entries.size()); EXPECT_TRUE(LogContainsBeginEvent( entries, 0, NetLog::TYPE_HOST_RESOLVER_IMPL)); EXPECT_TRUE(LogContainsEndEvent( entries, 1, NetLog::TYPE_HOST_RESOLVER_IMPL)); const struct addrinfo* ainfo = addrlist.head(); EXPECT_EQ(static_cast<addrinfo*>(NULL), ainfo->ai_next); EXPECT_EQ(sizeof(struct sockaddr_in), ainfo->ai_addrlen); const struct sockaddr* sa = ainfo->ai_addr; const struct sockaddr_in* sa_in = (const struct sockaddr_in*) sa; EXPECT_TRUE(htons(kPortnum) == sa_in->sin_port); EXPECT_TRUE(htonl(0xc0a8012a) == sa_in->sin_addr.s_addr); } TEST_F(HostResolverImplTest, AsynchronousLookup) { AddressList addrlist; const int kPortnum = 80; scoped_refptr<RuleBasedHostResolverProc> resolver_proc( new RuleBasedHostResolverProc(NULL)); resolver_proc->AddRule("just.testing", "192.168.1.42"); scoped_ptr<HostResolver> host_resolver( CreateHostResolverImpl(resolver_proc)); HostResolver::RequestInfo info(HostPortPair("just.testing", kPortnum)); CapturingBoundNetLog log(CapturingNetLog::kUnbounded); int err = host_resolver->Resolve(info, &addrlist, &callback_, NULL, log.bound()); EXPECT_EQ(ERR_IO_PENDING, err); CapturingNetLog::EntryList entries; log.GetEntries(&entries); EXPECT_EQ(1u, entries.size()); EXPECT_TRUE(LogContainsBeginEvent( entries, 0, NetLog::TYPE_HOST_RESOLVER_IMPL)); MessageLoop::current()->Run(); ASSERT_TRUE(callback_called_); ASSERT_EQ(OK, callback_result_); log.GetEntries(&entries); EXPECT_EQ(2u, entries.size()); EXPECT_TRUE(LogContainsEndEvent( entries, 1, NetLog::TYPE_HOST_RESOLVER_IMPL)); const struct addrinfo* ainfo = addrlist.head(); EXPECT_EQ(static_cast<addrinfo*>(NULL), ainfo->ai_next); EXPECT_EQ(sizeof(struct sockaddr_in), ainfo->ai_addrlen); const struct sockaddr* sa = ainfo->ai_addr; const struct sockaddr_in* sa_in = (const struct sockaddr_in*) sa; EXPECT_TRUE(htons(kPortnum) == sa_in->sin_port); EXPECT_TRUE(htonl(0xc0a8012a) == sa_in->sin_addr.s_addr); } TEST_F(HostResolverImplTest, CanceledAsynchronousLookup) { scoped_refptr<WaitingHostResolverProc> resolver_proc( new WaitingHostResolverProc(NULL)); CapturingNetLog net_log(CapturingNetLog::kUnbounded); CapturingBoundNetLog log(CapturingNetLog::kUnbounded); { scoped_ptr<HostResolver> host_resolver( new HostResolverImpl(resolver_proc, CreateDefaultCache(), kMaxJobs, &net_log)); AddressList addrlist; const int kPortnum = 80; HostResolver::RequestInfo info(HostPortPair("just.testing", kPortnum)); int err = host_resolver->Resolve(info, &addrlist, &callback_, NULL, log.bound()); EXPECT_EQ(ERR_IO_PENDING, err); // Make sure we will exit the queue even when callback is not called. MessageLoop::current()->PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask(), 1000); MessageLoop::current()->Run(); } resolver_proc->Signal(); CapturingNetLog::EntryList entries; log.GetEntries(&entries); EXPECT_EQ(2u, entries.size()); EXPECT_TRUE(LogContainsBeginEvent( entries, 0, NetLog::TYPE_HOST_RESOLVER_IMPL)); EXPECT_TRUE(LogContainsEndEvent( entries, 1, NetLog::TYPE_HOST_RESOLVER_IMPL)); CapturingNetLog::EntryList net_log_entries; net_log.GetEntries(&net_log_entries); int pos = ExpectLogContainsSomewhereAfter(net_log_entries, 0, NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST, NetLog::PHASE_BEGIN); pos = ExpectLogContainsSomewhereAfter(net_log_entries, pos + 1, NetLog::TYPE_HOST_RESOLVER_IMPL_JOB, NetLog::PHASE_BEGIN); // Both Job and Request need to be cancelled. pos = ExpectLogContainsSomewhereAfter(net_log_entries, pos + 1, NetLog::TYPE_CANCELLED, NetLog::PHASE_NONE); // Don't care about order in which they end, or when the other one is // cancelled. ExpectLogContainsSomewhereAfter(net_log_entries, pos + 1, NetLog::TYPE_CANCELLED, NetLog::PHASE_NONE); ExpectLogContainsSomewhereAfter(net_log_entries, pos + 1, NetLog::TYPE_HOST_RESOLVER_IMPL_REQUEST, NetLog::PHASE_END); ExpectLogContainsSomewhereAfter(net_log_entries, pos + 1, NetLog::TYPE_HOST_RESOLVER_IMPL_JOB, NetLog::PHASE_END); EXPECT_FALSE(callback_called_); } TEST_F(HostResolverImplTest, NumericIPv4Address) { // Stevens says dotted quads with AI_UNSPEC resolve to a single sockaddr_in. scoped_refptr<RuleBasedHostResolverProc> resolver_proc( new RuleBasedHostResolverProc(NULL)); resolver_proc->AllowDirectLookup("*"); scoped_ptr<HostResolver> host_resolver( CreateHostResolverImpl(resolver_proc)); AddressList addrlist; const int kPortnum = 5555; HostResolver::RequestInfo info(HostPortPair("127.1.2.3", kPortnum)); int err = host_resolver->Resolve(info, &addrlist, NULL, NULL, BoundNetLog()); EXPECT_EQ(OK, err); const struct addrinfo* ainfo = addrlist.head(); EXPECT_EQ(static_cast<addrinfo*>(NULL), ainfo->ai_next); EXPECT_EQ(sizeof(struct sockaddr_in), ainfo->ai_addrlen); const struct sockaddr* sa = ainfo->ai_addr; const struct sockaddr_in* sa_in = (const struct sockaddr_in*) sa; EXPECT_TRUE(htons(kPortnum) == sa_in->sin_port); EXPECT_TRUE(htonl(0x7f010203) == sa_in->sin_addr.s_addr); } TEST_F(HostResolverImplTest, NumericIPv6Address) { scoped_refptr<RuleBasedHostResolverProc> resolver_proc( new RuleBasedHostResolverProc(NULL)); resolver_proc->AllowDirectLookup("*"); // Resolve a plain IPv6 address. Don't worry about [brackets], because // the caller should have removed them. scoped_ptr<HostResolver> host_resolver( CreateHostResolverImpl(resolver_proc)); AddressList addrlist; const int kPortnum = 5555; HostResolver::RequestInfo info(HostPortPair("2001:db8::1", kPortnum)); int err = host_resolver->Resolve(info, &addrlist, NULL, NULL, BoundNetLog()); EXPECT_EQ(OK, err); const struct addrinfo* ainfo = addrlist.head(); EXPECT_EQ(static_cast<addrinfo*>(NULL), ainfo->ai_next); EXPECT_EQ(sizeof(struct sockaddr_in6), ainfo->ai_addrlen); const struct sockaddr* sa = ainfo->ai_addr; const struct sockaddr_in6* sa_in6 = (const struct sockaddr_in6*) sa; EXPECT_TRUE(htons(kPortnum) == sa_in6->sin6_port); const uint8 expect_addr[] = { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; for (int i = 0; i < 16; i++) { EXPECT_EQ(expect_addr[i], sa_in6->sin6_addr.s6_addr[i]); } } TEST_F(HostResolverImplTest, EmptyHost) { scoped_refptr<RuleBasedHostResolverProc> resolver_proc( new RuleBasedHostResolverProc(NULL)); resolver_proc->AllowDirectLookup("*"); scoped_ptr<HostResolver> host_resolver( CreateHostResolverImpl(resolver_proc)); AddressList addrlist; const int kPortnum = 5555; HostResolver::RequestInfo info(HostPortPair("", kPortnum)); int err = host_resolver->Resolve(info, &addrlist, NULL, NULL, BoundNetLog()); EXPECT_EQ(ERR_NAME_NOT_RESOLVED, err); } TEST_F(HostResolverImplTest, LongHost) { scoped_refptr<RuleBasedHostResolverProc> resolver_proc( new RuleBasedHostResolverProc(NULL)); resolver_proc->AllowDirectLookup("*"); scoped_ptr<HostResolver> host_resolver( CreateHostResolverImpl(resolver_proc)); AddressList addrlist; const int kPortnum = 5555; std::string hostname(4097, 'a'); HostResolver::RequestInfo info(HostPortPair(hostname, kPortnum)); int err = host_resolver->Resolve(info, &addrlist, NULL, NULL, BoundNetLog()); EXPECT_EQ(ERR_NAME_NOT_RESOLVED, err); } // Helper class used by HostResolverImplTest.DeDupeRequests. It receives request // completion notifications for all the resolves, so it can tally up and // determine when we are done. class DeDupeRequestsVerifier : public ResolveRequest::Delegate { public: explicit DeDupeRequestsVerifier(CapturingHostResolverProc* resolver_proc) : count_a_(0), count_b_(0), resolver_proc_(resolver_proc) {} // The test does 5 resolves (which can complete in any order). virtual void OnCompleted(ResolveRequest* resolve) { // Tally up how many requests we have seen. if (resolve->hostname() == "a") { count_a_++; } else if (resolve->hostname() == "b") { count_b_++; } else { FAIL() << "Unexpected hostname: " << resolve->hostname(); } // Check that the port was set correctly. EXPECT_EQ(resolve->port(), resolve->addrlist().GetPort()); // Check whether all the requests have finished yet. int total_completions = count_a_ + count_b_; if (total_completions == 5) { EXPECT_EQ(2, count_a_); EXPECT_EQ(3, count_b_); // The resolver_proc should have been called only twice -- once with "a", // once with "b". CapturingHostResolverProc::CaptureList capture_list = resolver_proc_->GetCaptureList(); EXPECT_EQ(2U, capture_list.size()); // End this test, we are done. MessageLoop::current()->Quit(); } } private: int count_a_; int count_b_; CapturingHostResolverProc* resolver_proc_; DISALLOW_COPY_AND_ASSIGN(DeDupeRequestsVerifier); }; TEST_F(HostResolverImplTest, DeDupeRequests) { // Use a capturing resolver_proc, since the verifier needs to know what calls // reached Resolve(). Also, the capturing resolver_proc is initially blocked. scoped_refptr<CapturingHostResolverProc> resolver_proc( new CapturingHostResolverProc(NULL)); scoped_ptr<HostResolver> host_resolver( CreateHostResolverImpl(resolver_proc)); // The class will receive callbacks for when each resolve completes. It // checks that the right things happened. DeDupeRequestsVerifier verifier(resolver_proc.get()); // Start 5 requests, duplicating hosts "a" and "b". Since the resolver_proc is // blocked, these should all pile up until we signal it. ResolveRequest req1(host_resolver.get(), "a", 80, &verifier); ResolveRequest req2(host_resolver.get(), "b", 80, &verifier); ResolveRequest req3(host_resolver.get(), "b", 81, &verifier); ResolveRequest req4(host_resolver.get(), "a", 82, &verifier); ResolveRequest req5(host_resolver.get(), "b", 83, &verifier); // Ready, Set, GO!!! resolver_proc->Signal(); // |verifier| will send quit message once all the requests have finished. MessageLoop::current()->Run(); } // Helper class used by HostResolverImplTest.CancelMultipleRequests. class CancelMultipleRequestsVerifier : public ResolveRequest::Delegate { public: CancelMultipleRequestsVerifier() {} // The cancels kill all but one request. virtual void OnCompleted(ResolveRequest* resolve) { EXPECT_EQ("a", resolve->hostname()); EXPECT_EQ(82, resolve->port()); // Check that the port was set correctly. EXPECT_EQ(resolve->port(), resolve->addrlist().GetPort()); // End this test, we are done. MessageLoop::current()->Quit(); } private: DISALLOW_COPY_AND_ASSIGN(CancelMultipleRequestsVerifier); }; TEST_F(HostResolverImplTest, CancelMultipleRequests) { // Use a capturing resolver_proc, since the verifier needs to know what calls // reached Resolver(). Also, the capturing resolver_proc is initially // blocked. scoped_refptr<CapturingHostResolverProc> resolver_proc( new CapturingHostResolverProc(NULL)); scoped_ptr<HostResolver> host_resolver( CreateHostResolverImpl(resolver_proc)); // The class will receive callbacks for when each resolve completes. It // checks that the right things happened. CancelMultipleRequestsVerifier verifier; // Start 5 requests, duplicating hosts "a" and "b". Since the resolver_proc is // blocked, these should all pile up until we signal it. ResolveRequest req1(host_resolver.get(), "a", 80, &verifier); ResolveRequest req2(host_resolver.get(), "b", 80, &verifier); ResolveRequest req3(host_resolver.get(), "b", 81, &verifier); ResolveRequest req4(host_resolver.get(), "a", 82, &verifier); ResolveRequest req5(host_resolver.get(), "b", 83, &verifier); // Cancel everything except request 4. req1.Cancel(); req2.Cancel(); req3.Cancel(); req5.Cancel(); // Ready, Set, GO!!! resolver_proc->Signal(); // |verifier| will send quit message once all the requests have finished. MessageLoop::current()->Run(); } // Helper class used by HostResolverImplTest.CancelWithinCallback. class CancelWithinCallbackVerifier : public ResolveRequest::Delegate { public: CancelWithinCallbackVerifier() : req_to_cancel1_(NULL), req_to_cancel2_(NULL), num_completions_(0) { } virtual void OnCompleted(ResolveRequest* resolve) { num_completions_++; // Port 80 is the first request that the callback will be invoked for. // While we are executing within that callback, cancel the other requests // in the job and start another request. if (80 == resolve->port()) { EXPECT_EQ("a", resolve->hostname()); req_to_cancel1_->Cancel(); req_to_cancel2_->Cancel(); // Start a request (so we can make sure the canceled requests don't // complete before "finalrequest" finishes. final_request_.reset(new ResolveRequest( resolve->resolver(), "finalrequest", 70, this)); } else if (83 == resolve->port()) { EXPECT_EQ("a", resolve->hostname()); } else if (resolve->hostname() == "finalrequest") { EXPECT_EQ(70, resolve->addrlist().GetPort()); // End this test, we are done. MessageLoop::current()->Quit(); } else { FAIL() << "Unexpected completion: " << resolve->hostname() << ", " << resolve->port(); } } void SetRequestsToCancel(ResolveRequest* req_to_cancel1, ResolveRequest* req_to_cancel2) { req_to_cancel1_ = req_to_cancel1; req_to_cancel2_ = req_to_cancel2; } private: scoped_ptr<ResolveRequest> final_request_; ResolveRequest* req_to_cancel1_; ResolveRequest* req_to_cancel2_; int num_completions_; DISALLOW_COPY_AND_ASSIGN(CancelWithinCallbackVerifier); }; TEST_F(HostResolverImplTest, CancelWithinCallback) { // Use a capturing resolver_proc, since the verifier needs to know what calls // reached Resolver(). Also, the capturing resolver_proc is initially // blocked. scoped_refptr<CapturingHostResolverProc> resolver_proc( new CapturingHostResolverProc(NULL)); scoped_ptr<HostResolver> host_resolver( CreateHostResolverImpl(resolver_proc)); // The class will receive callbacks for when each resolve completes. It // checks that the right things happened. CancelWithinCallbackVerifier verifier; // Start 4 requests, duplicating hosts "a". Since the resolver_proc is // blocked, these should all pile up until we signal it. ResolveRequest req1(host_resolver.get(), "a", 80, &verifier); ResolveRequest req2(host_resolver.get(), "a", 81, &verifier); ResolveRequest req3(host_resolver.get(), "a", 82, &verifier); ResolveRequest req4(host_resolver.get(), "a", 83, &verifier); // Once "a:80" completes, it will cancel "a:81" and "a:82". verifier.SetRequestsToCancel(&req2, &req3); // Ready, Set, GO!!! resolver_proc->Signal(); // |verifier| will send quit message once all the requests have finished. MessageLoop::current()->Run(); } // Helper class used by HostResolverImplTest.DeleteWithinCallback. class DeleteWithinCallbackVerifier : public ResolveRequest::Delegate { public: // |host_resolver| is the resolver that the the resolve requests were started // with. explicit DeleteWithinCallbackVerifier(HostResolver* host_resolver) : host_resolver_(host_resolver) {} virtual void OnCompleted(ResolveRequest* resolve) { EXPECT_EQ("a", resolve->hostname()); EXPECT_EQ(80, resolve->port()); // Deletes the host resolver. host_resolver_.reset(); // Quit after returning from OnCompleted (to give it a chance at // incorrectly running the cancelled tasks). MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); } private: scoped_ptr<HostResolver> host_resolver_; DISALLOW_COPY_AND_ASSIGN(DeleteWithinCallbackVerifier); }; TEST_F(HostResolverImplTest, DeleteWithinCallback) { // Use a capturing resolver_proc, since the verifier needs to know what calls // reached Resolver(). Also, the capturing resolver_proc is initially // blocked. scoped_refptr<CapturingHostResolverProc> resolver_proc( new CapturingHostResolverProc(NULL)); // The class will receive callbacks for when each resolve completes. It // checks that the right things happened. Note that the verifier holds the // only reference to |host_resolver|, so it can delete it within callback. HostResolver* host_resolver = CreateHostResolverImpl(resolver_proc); DeleteWithinCallbackVerifier verifier(host_resolver); // Start 4 requests, duplicating hosts "a". Since the resolver_proc is // blocked, these should all pile up until we signal it. ResolveRequest req1(host_resolver, "a", 80, &verifier); ResolveRequest req2(host_resolver, "a", 81, &verifier); ResolveRequest req3(host_resolver, "a", 82, &verifier); ResolveRequest req4(host_resolver, "a", 83, &verifier); // Ready, Set, GO!!! resolver_proc->Signal(); // |verifier| will send quit message once all the requests have finished. MessageLoop::current()->Run(); } // Helper class used by HostResolverImplTest.StartWithinCallback. class StartWithinCallbackVerifier : public ResolveRequest::Delegate { public: StartWithinCallbackVerifier() : num_requests_(0) {} virtual void OnCompleted(ResolveRequest* resolve) { EXPECT_EQ("a", resolve->hostname()); if (80 == resolve->port()) { // On completing the first request, start another request for "a". // Since caching is disabled, this will result in another async request. final_request_.reset(new ResolveRequest( resolve->resolver(), "a", 70, this)); } if (++num_requests_ == 5) { // Test is done. MessageLoop::current()->Quit(); } } private: int num_requests_; scoped_ptr<ResolveRequest> final_request_; DISALLOW_COPY_AND_ASSIGN(StartWithinCallbackVerifier); }; TEST_F(HostResolverImplTest, StartWithinCallback) { // Use a capturing resolver_proc, since the verifier needs to know what calls // reached Resolver(). Also, the capturing resolver_proc is initially // blocked. scoped_refptr<CapturingHostResolverProc> resolver_proc( new CapturingHostResolverProc(NULL)); // Turn off caching for this host resolver. scoped_ptr<HostResolver> host_resolver( new HostResolverImpl(resolver_proc, NULL, kMaxJobs, NULL)); // The class will receive callbacks for when each resolve completes. It // checks that the right things happened. StartWithinCallbackVerifier verifier; // Start 4 requests, duplicating hosts "a". Since the resolver_proc is // blocked, these should all pile up until we signal it. ResolveRequest req1(host_resolver.get(), "a", 80, &verifier); ResolveRequest req2(host_resolver.get(), "a", 81, &verifier); ResolveRequest req3(host_resolver.get(), "a", 82, &verifier); ResolveRequest req4(host_resolver.get(), "a", 83, &verifier); // Ready, Set, GO!!! resolver_proc->Signal(); // |verifier| will send quit message once all the requests have finished. MessageLoop::current()->Run(); } // Helper class used by HostResolverImplTest.BypassCache. class BypassCacheVerifier : public ResolveRequest::Delegate { public: BypassCacheVerifier() {} virtual void OnCompleted(ResolveRequest* resolve) { EXPECT_EQ("a", resolve->hostname()); HostResolver* resolver = resolve->resolver(); if (80 == resolve->port()) { // On completing the first request, start another request for "a". // Since caching is enabled, this should complete synchronously. // Note that |junk_callback| shouldn't be used since we are going to // complete synchronously. We can't specify NULL though since that would // mean synchronous mode so we give it a value of 1. CompletionCallback* junk_callback = reinterpret_cast<CompletionCallback*> (1); AddressList addrlist; HostResolver::RequestInfo info(HostPortPair("a", 70)); int error = resolver->Resolve(info, &addrlist, junk_callback, NULL, BoundNetLog()); EXPECT_EQ(OK, error); // Ok good. Now make sure that if we ask to bypass the cache, it can no // longer service the request synchronously. info = HostResolver::RequestInfo(HostPortPair("a", 71)); info.set_allow_cached_response(false); final_request_.reset(new ResolveRequest(resolver, info, this)); } else if (71 == resolve->port()) { // Test is done. MessageLoop::current()->Quit(); } else { FAIL() << "Unexpected port number"; } } private: scoped_ptr<ResolveRequest> final_request_; DISALLOW_COPY_AND_ASSIGN(BypassCacheVerifier); }; TEST_F(HostResolverImplTest, BypassCache) { scoped_ptr<HostResolver> host_resolver( CreateHostResolverImpl(NULL)); // The class will receive callbacks for when each resolve completes. It // checks that the right things happened. BypassCacheVerifier verifier; // Start a request. ResolveRequest req1(host_resolver.get(), "a", 80, &verifier); // |verifier| will send quit message once all the requests have finished. MessageLoop::current()->Run(); } bool operator==(const HostResolver::RequestInfo& a, const HostResolver::RequestInfo& b) { return a.hostname() == b.hostname() && a.port() == b.port() && a.allow_cached_response() == b.allow_cached_response() && a.priority() == b.priority() && a.is_speculative() == b.is_speculative() && a.referrer() == b.referrer(); } // Observer that just makes note of how it was called. The test code can then // inspect to make sure it was called with the right parameters. class CapturingObserver : public HostResolver::Observer { public: // DnsResolutionObserver methods: virtual void OnStartResolution(int id, const HostResolver::RequestInfo& info) { start_log.push_back(StartOrCancelEntry(id, info)); } virtual void OnFinishResolutionWithStatus( int id, bool was_resolved, const HostResolver::RequestInfo& info) { finish_log.push_back(FinishEntry(id, was_resolved, info)); } virtual void OnCancelResolution(int id, const HostResolver::RequestInfo& info) { cancel_log.push_back(StartOrCancelEntry(id, info)); } // Tuple (id, info). struct StartOrCancelEntry { StartOrCancelEntry(int id, const HostResolver::RequestInfo& info) : id(id), info(info) {} bool operator==(const StartOrCancelEntry& other) const { return id == other.id && info == other.info; } int id; HostResolver::RequestInfo info; }; // Tuple (id, was_resolved, info). struct FinishEntry { FinishEntry(int id, bool was_resolved, const HostResolver::RequestInfo& info) : id(id), was_resolved(was_resolved), info(info) {} bool operator==(const FinishEntry& other) const { return id == other.id && was_resolved == other.was_resolved && info == other.info; } int id; bool was_resolved; HostResolver::RequestInfo info; }; std::vector<StartOrCancelEntry> start_log; std::vector<FinishEntry> finish_log; std::vector<StartOrCancelEntry> cancel_log; }; // Test that registering, unregistering, and notifying of observers works. // Does not test the cancellation notification since all resolves are // synchronous. TEST_F(HostResolverImplTest, Observers) { scoped_ptr<HostResolver> host_resolver( CreateHostResolverImpl(NULL)); CapturingObserver observer; host_resolver->AddObserver(&observer); AddressList addrlist; // Resolve "host1". HostResolver::RequestInfo info1(HostPortPair("host1", 70)); CapturingBoundNetLog log(CapturingNetLog::kUnbounded); int rv = host_resolver->Resolve(info1, &addrlist, NULL, NULL, log.bound()); EXPECT_EQ(OK, rv); CapturingNetLog::EntryList entries; log.GetEntries(&entries); EXPECT_EQ(2u, entries.size()); EXPECT_TRUE(LogContainsBeginEvent( entries, 0, NetLog::TYPE_HOST_RESOLVER_IMPL)); EXPECT_TRUE(LogContainsEndEvent( entries, 1, NetLog::TYPE_HOST_RESOLVER_IMPL)); EXPECT_EQ(1U, observer.start_log.size()); EXPECT_EQ(1U, observer.finish_log.size()); EXPECT_EQ(0U, observer.cancel_log.size()); EXPECT_TRUE(observer.start_log[0] == CapturingObserver::StartOrCancelEntry(0, info1)); EXPECT_TRUE(observer.finish_log[0] == CapturingObserver::FinishEntry(0, true, info1)); // Resolve "host1" again -- this time it will be served from cache, but it // should still notify of completion. TestCompletionCallback callback; rv = host_resolver->Resolve(info1, &addrlist, &callback, NULL, BoundNetLog()); ASSERT_EQ(OK, rv); // Should complete synchronously. EXPECT_EQ(2U, observer.start_log.size()); EXPECT_EQ(2U, observer.finish_log.size()); EXPECT_EQ(0U, observer.cancel_log.size()); EXPECT_TRUE(observer.start_log[1] == CapturingObserver::StartOrCancelEntry(1, info1)); EXPECT_TRUE(observer.finish_log[1] == CapturingObserver::FinishEntry(1, true, info1)); // Resolve "host2", setting referrer to "http://foobar.com" HostResolver::RequestInfo info2(HostPortPair("host2", 70)); info2.set_referrer(GURL("http://foobar.com")); rv = host_resolver->Resolve(info2, &addrlist, NULL, NULL, BoundNetLog()); EXPECT_EQ(OK, rv); EXPECT_EQ(3U, observer.start_log.size()); EXPECT_EQ(3U, observer.finish_log.size()); EXPECT_EQ(0U, observer.cancel_log.size()); EXPECT_TRUE(observer.start_log[2] == CapturingObserver::StartOrCancelEntry(2, info2)); EXPECT_TRUE(observer.finish_log[2] == CapturingObserver::FinishEntry(2, true, info2)); // Unregister the observer. host_resolver->RemoveObserver(&observer); // Resolve "host3" HostResolver::RequestInfo info3(HostPortPair("host3", 70)); host_resolver->Resolve(info3, &addrlist, NULL, NULL, BoundNetLog()); // No effect this time, since observer was removed. EXPECT_EQ(3U, observer.start_log.size()); EXPECT_EQ(3U, observer.finish_log.size()); EXPECT_EQ(0U, observer.cancel_log.size()); } // Tests that observers are sent OnCancelResolution() whenever a request is // cancelled. There are two ways to cancel a request: // (1) Delete the HostResolver while job is outstanding. // (2) Call HostResolver::CancelRequest() while a request is outstanding. TEST_F(HostResolverImplTest, CancellationObserver) { CapturingObserver observer; { // Create a host resolver and attach an observer. scoped_ptr<HostResolver> host_resolver( CreateHostResolverImpl(NULL)); host_resolver->AddObserver(&observer); TestCompletionCallback callback; EXPECT_EQ(0U, observer.start_log.size()); EXPECT_EQ(0U, observer.finish_log.size()); EXPECT_EQ(0U, observer.cancel_log.size()); // Start an async resolve for (host1:70). HostResolver::RequestInfo info1(HostPortPair("host1", 70)); HostResolver::RequestHandle req = NULL; AddressList addrlist; int rv = host_resolver->Resolve(info1, &addrlist, &callback, &req, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); EXPECT_TRUE(NULL != req); EXPECT_EQ(1U, observer.start_log.size()); EXPECT_EQ(0U, observer.finish_log.size()); EXPECT_EQ(0U, observer.cancel_log.size()); EXPECT_TRUE(observer.start_log[0] == CapturingObserver::StartOrCancelEntry(0, info1)); // Cancel the request. host_resolver->CancelRequest(req); EXPECT_EQ(1U, observer.start_log.size()); EXPECT_EQ(0U, observer.finish_log.size()); EXPECT_EQ(1U, observer.cancel_log.size()); EXPECT_TRUE(observer.cancel_log[0] == CapturingObserver::StartOrCancelEntry(0, info1)); // Start an async request for (host2:60) HostResolver::RequestInfo info2(HostPortPair("host2", 60)); rv = host_resolver->Resolve(info2, &addrlist, &callback, NULL, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); EXPECT_TRUE(NULL != req); EXPECT_EQ(2U, observer.start_log.size()); EXPECT_EQ(0U, observer.finish_log.size()); EXPECT_EQ(1U, observer.cancel_log.size()); EXPECT_TRUE(observer.start_log[1] == CapturingObserver::StartOrCancelEntry(1, info2)); // Upon exiting this scope, HostResolver is destroyed, so all requests are // implicitly cancelled. } // Check that destroying the HostResolver sent a notification for // cancellation of host2:60 request. EXPECT_EQ(2U, observer.start_log.size()); EXPECT_EQ(0U, observer.finish_log.size()); EXPECT_EQ(2U, observer.cancel_log.size()); HostResolver::RequestInfo info(HostPortPair("host2", 60)); EXPECT_TRUE(observer.cancel_log[1] == CapturingObserver::StartOrCancelEntry(1, info)); } // Test that IP address changes flush the cache. TEST_F(HostResolverImplTest, FlushCacheOnIPAddressChange) { scoped_ptr<HostResolver> host_resolver( new HostResolverImpl(NULL, CreateDefaultCache(), kMaxJobs, NULL)); AddressList addrlist; // Resolve "host1". HostResolver::RequestInfo info1(HostPortPair("host1", 70)); TestCompletionCallback callback; int rv = host_resolver->Resolve(info1, &addrlist, &callback, NULL, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); EXPECT_EQ(OK, callback.WaitForResult()); // Resolve "host1" again -- this time it will be served from cache, but it // should still notify of completion. rv = host_resolver->Resolve(info1, &addrlist, &callback, NULL, BoundNetLog()); ASSERT_EQ(OK, rv); // Should complete synchronously. // Flush cache by triggering an IP address change. NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); MessageLoop::current()->RunAllPending(); // Notification happens async. // Resolve "host1" again -- this time it won't be served from cache, so it // will complete asynchronously. rv = host_resolver->Resolve(info1, &addrlist, &callback, NULL, BoundNetLog()); ASSERT_EQ(ERR_IO_PENDING, rv); // Should complete asynchronously. EXPECT_EQ(OK, callback.WaitForResult()); } // Test that IP address changes send ERR_ABORTED to pending requests. TEST_F(HostResolverImplTest, AbortOnIPAddressChanged) { scoped_refptr<WaitingHostResolverProc> resolver_proc( new WaitingHostResolverProc(NULL)); HostCache* cache = CreateDefaultCache(); scoped_ptr<HostResolver> host_resolver( new HostResolverImpl(resolver_proc, cache, kMaxJobs, NULL)); // Resolve "host1". HostResolver::RequestInfo info(HostPortPair("host1", 70)); TestCompletionCallback callback; AddressList addrlist; int rv = host_resolver->Resolve(info, &addrlist, &callback, NULL, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); // Triggering an IP address change. NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); MessageLoop::current()->RunAllPending(); // Notification happens async. resolver_proc->Signal(); EXPECT_EQ(ERR_ABORTED, callback.WaitForResult()); EXPECT_EQ(0u, cache->size()); } // Obey pool constraints after IP address has changed. TEST_F(HostResolverImplTest, ObeyPoolConstraintsAfterIPAddressChange) { scoped_refptr<WaitingHostResolverProc> resolver_proc( new WaitingHostResolverProc(NULL)); scoped_ptr<MockHostResolver> host_resolver(new MockHostResolver()); host_resolver->Reset(resolver_proc); const size_t kMaxOutstandingJobs = 1u; const size_t kMaxPendingRequests = 1000000u; // not relevant. host_resolver->SetPoolConstraints(HostResolverImpl::POOL_NORMAL, kMaxOutstandingJobs, kMaxPendingRequests); // Resolve "host1". HostResolver::RequestInfo info(HostPortPair("host1", 70)); TestCompletionCallback callback; AddressList addrlist; int rv = host_resolver->Resolve(info, &addrlist, &callback, NULL, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); // Triggering an IP address change. NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); MessageLoop::current()->RunAllPending(); // Notification happens async. resolver_proc->Signal(); EXPECT_EQ(ERR_ABORTED, callback.WaitForResult()); // Don't bother with WaitingHostResolverProc anymore. host_resolver->Reset(NULL); rv = host_resolver->Resolve(info, &addrlist, &callback, NULL, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); EXPECT_EQ(OK, callback.WaitForResult()); } class ResolveWithinCallback : public CallbackRunner< Tuple1<int> > { public: ResolveWithinCallback( MockHostResolver* host_resolver, const HostResolver::RequestInfo& info) : host_resolver_(host_resolver), info_(info) { DCHECK(host_resolver); } virtual void RunWithParams(const Tuple1<int>& params) { // Ditch the WaitingHostResolverProc so that the subsequent request // succeeds. host_resolver_->Reset(NULL); callback_.RunWithParams(params); EXPECT_EQ(ERR_IO_PENDING, host_resolver_->Resolve(info_, &addrlist_, &nested_callback_, NULL, BoundNetLog())); } int WaitForResult() { return callback_.WaitForResult(); } int WaitForNestedResult() { return nested_callback_.WaitForResult(); } private: MockHostResolver* const host_resolver_; const HostResolver::RequestInfo info_; AddressList addrlist_; TestCompletionCallback callback_; TestCompletionCallback nested_callback_; }; TEST_F(HostResolverImplTest, OnlyAbortExistingRequestsOnIPAddressChange) { scoped_refptr<WaitingHostResolverProc> resolver_proc( new WaitingHostResolverProc(NULL)); scoped_ptr<MockHostResolver> host_resolver(new MockHostResolver()); host_resolver->Reset(resolver_proc); // Resolve "host1". HostResolver::RequestInfo info(HostPortPair("host1", 70)); ResolveWithinCallback callback(host_resolver.get(), info); AddressList addrlist; int rv = host_resolver->Resolve(info, &addrlist, &callback, NULL, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); // Triggering an IP address change. NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); MessageLoop::current()->RunAllPending(); // Notification happens async. EXPECT_EQ(ERR_ABORTED, callback.WaitForResult()); resolver_proc->Signal(); EXPECT_EQ(OK, callback.WaitForNestedResult()); } // Tests that when the maximum threads is set to 1, requests are dequeued // in order of priority. TEST_F(HostResolverImplTest, HigherPriorityRequestsStartedFirst) { scoped_refptr<CapturingHostResolverProc> resolver_proc( new CapturingHostResolverProc(NULL)); // This HostResolverImpl will only allow 1 outstanding resolve at a time. size_t kMaxJobs = 1u; scoped_ptr<HostResolver> host_resolver( new HostResolverImpl(resolver_proc, CreateDefaultCache(), kMaxJobs, NULL)); CapturingObserver observer; host_resolver->AddObserver(&observer); // Note that at this point the CapturingHostResolverProc is blocked, so any // requests we make will not complete. HostResolver::RequestInfo req[] = { CreateResolverRequest("req0", LOW), CreateResolverRequest("req1", MEDIUM), CreateResolverRequest("req2", MEDIUM), CreateResolverRequest("req3", LOW), CreateResolverRequest("req4", HIGHEST), CreateResolverRequest("req5", LOW), CreateResolverRequest("req6", LOW), CreateResolverRequest("req5", HIGHEST), }; TestCompletionCallback callback[arraysize(req)]; AddressList addrlist[arraysize(req)]; // Start all of the requests. for (size_t i = 0; i < arraysize(req); ++i) { int rv = host_resolver->Resolve(req[i], &addrlist[i], &callback[i], NULL, BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); } // Unblock the resolver thread so the requests can run. resolver_proc->Signal(); // Wait for all the requests to complete succesfully. for (size_t i = 0; i < arraysize(req); ++i) { EXPECT_EQ(OK, callback[i].WaitForResult()) << "i=" << i; } host_resolver->RemoveObserver(&observer); // Since we have restricted to a single concurrent thread in the jobpool, // the requests should complete in order of priority (with the exception // of the first request, which gets started right away, since there is // nothing outstanding). CapturingHostResolverProc::CaptureList capture_list = resolver_proc->GetCaptureList(); ASSERT_EQ(7u, capture_list.size()); EXPECT_EQ("req0", capture_list[0].hostname); EXPECT_EQ("req4", capture_list[1].hostname); EXPECT_EQ("req5", capture_list[2].hostname); EXPECT_EQ("req1", capture_list[3].hostname); EXPECT_EQ("req2", capture_list[4].hostname); EXPECT_EQ("req3", capture_list[5].hostname); EXPECT_EQ("req6", capture_list[6].hostname); // Also check using the observer's trace. EXPECT_EQ(8U, observer.start_log.size()); EXPECT_EQ(8U, observer.finish_log.size()); EXPECT_EQ(0U, observer.cancel_log.size()); EXPECT_EQ("req0", observer.finish_log[0].info.hostname()); EXPECT_EQ("req4", observer.finish_log[1].info.hostname()); // There were two requests for "req5". The highest priority // one should have been dispatched earlier. EXPECT_EQ("req5", observer.finish_log[2].info.hostname()); EXPECT_EQ("req5", observer.finish_log[3].info.hostname()); EXPECT_EQ(HIGHEST, observer.finish_log[2].info.priority()); EXPECT_EQ(LOW, observer.finish_log[3].info.priority()); EXPECT_EQ("req1", observer.finish_log[4].info.hostname()); EXPECT_EQ("req2", observer.finish_log[5].info.hostname()); EXPECT_EQ("req3", observer.finish_log[6].info.hostname()); EXPECT_EQ("req6", observer.finish_log[7].info.hostname()); } // Try cancelling a request which has not been attached to a job yet. TEST_F(HostResolverImplTest, CancelPendingRequest) { scoped_refptr<CapturingHostResolverProc> resolver_proc( new CapturingHostResolverProc(NULL)); // This HostResolverImpl will only allow 1 outstanding resolve at a time. const size_t kMaxJobs = 1u; scoped_ptr<HostResolver> host_resolver( new HostResolverImpl(resolver_proc, CreateDefaultCache(), kMaxJobs, NULL)); // Note that at this point the CapturingHostResolverProc is blocked, so any // requests we make will not complete. HostResolver::RequestInfo req[] = { CreateResolverRequest("req0", LOWEST), CreateResolverRequest("req1", HIGHEST), // Will cancel. CreateResolverRequest("req2", MEDIUM), CreateResolverRequest("req3", LOW), CreateResolverRequest("req4", HIGHEST), // Will cancel. CreateResolverRequest("req5", LOWEST), // Will cancel. CreateResolverRequest("req6", MEDIUM), }; TestCompletionCallback callback[arraysize(req)]; AddressList addrlist[arraysize(req)]; HostResolver::RequestHandle handle[arraysize(req)]; // Start all of the requests. for (size_t i = 0; i < arraysize(req); ++i) { int rv = host_resolver->Resolve(req[i], &addrlist[i], &callback[i], &handle[i], BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv); } // Cancel some requests host_resolver->CancelRequest(handle[1]); host_resolver->CancelRequest(handle[4]); host_resolver->CancelRequest(handle[5]); handle[1] = handle[4] = handle[5] = NULL; // Unblock the resolver thread so the requests can run. resolver_proc->Signal(); // Wait for all the requests to complete succesfully. for (size_t i = 0; i < arraysize(req); ++i) { if (!handle[i]) continue; // Don't wait for the requests we cancelled. EXPECT_EQ(OK, callback[i].WaitForResult()); } // Verify that they called out the the resolver proc (which runs on the // resolver thread) in the expected order. CapturingHostResolverProc::CaptureList capture_list = resolver_proc->GetCaptureList(); ASSERT_EQ(4u, capture_list.size()); EXPECT_EQ("req0", capture_list[0].hostname); EXPECT_EQ("req2", capture_list[1].hostname); EXPECT_EQ("req6", capture_list[2].hostname); EXPECT_EQ("req3", capture_list[3].hostname); } // Test that when too many requests are enqueued, old ones start to be aborted. TEST_F(HostResolverImplTest, QueueOverflow) { scoped_refptr<CapturingHostResolverProc> resolver_proc( new CapturingHostResolverProc(NULL)); // This HostResolverImpl will only allow 1 outstanding resolve at a time. const size_t kMaxOutstandingJobs = 1u; scoped_ptr<HostResolverImpl> host_resolver(new HostResolverImpl( resolver_proc, CreateDefaultCache(), kMaxOutstandingJobs, NULL)); // Only allow up to 3 requests to be enqueued at a time. const size_t kMaxPendingRequests = 3u; host_resolver->SetPoolConstraints(HostResolverImpl::POOL_NORMAL, kMaxOutstandingJobs, kMaxPendingRequests); // Note that at this point the CapturingHostResolverProc is blocked, so any // requests we make will not complete. HostResolver::RequestInfo req[] = { CreateResolverRequest("req0", LOWEST), CreateResolverRequest("req1", HIGHEST), CreateResolverRequest("req2", MEDIUM), CreateResolverRequest("req3", MEDIUM), // At this point, there are 3 enqueued requests. // Insertion of subsequent requests will cause evictions // based on priority. CreateResolverRequest("req4", LOW), // Evicts itself! CreateResolverRequest("req5", MEDIUM), // Evicts req3 CreateResolverRequest("req6", HIGHEST), // Evicts req5. CreateResolverRequest("req7", MEDIUM), // Evicts req2. }; TestCompletionCallback callback[arraysize(req)]; AddressList addrlist[arraysize(req)]; HostResolver::RequestHandle handle[arraysize(req)]; // Start all of the requests. for (size_t i = 0; i < arraysize(req); ++i) { int rv = host_resolver->Resolve(req[i], &addrlist[i], &callback[i], &handle[i], BoundNetLog()); if (i == 4u) EXPECT_EQ(ERR_HOST_RESOLVER_QUEUE_TOO_LARGE, rv); else EXPECT_EQ(ERR_IO_PENDING, rv) << i; } // Unblock the resolver thread so the requests can run. resolver_proc->Signal(); // Requests 3, 5, 2 will have been evicted due to queue overflow. size_t reqs_expected_to_fail[] = { 2, 3, 5 }; for (size_t i = 0; i < arraysize(reqs_expected_to_fail); ++i) { EXPECT_EQ(ERR_HOST_RESOLVER_QUEUE_TOO_LARGE, callback[reqs_expected_to_fail[i]].WaitForResult()); } // The rest should succeed. size_t reqs_expected_to_succeed[] = { 0, 1, 6, 7 }; for (size_t i = 0; i < arraysize(reqs_expected_to_succeed); ++i) { EXPECT_EQ(OK, callback[reqs_expected_to_succeed[i]].WaitForResult()); } // Verify that they called out the the resolver proc (which runs on the // resolver thread) in the expected order. CapturingHostResolverProc::CaptureList capture_list = resolver_proc->GetCaptureList(); ASSERT_EQ(4u, capture_list.size()); EXPECT_EQ("req0", capture_list[0].hostname); EXPECT_EQ("req1", capture_list[1].hostname); EXPECT_EQ("req6", capture_list[2].hostname); EXPECT_EQ("req7", capture_list[3].hostname); } // Tests that after changing the default AddressFamily to IPV4, requests // with UNSPECIFIED address family map to IPV4. TEST_F(HostResolverImplTest, SetDefaultAddressFamily_IPv4) { scoped_refptr<CapturingHostResolverProc> resolver_proc( new CapturingHostResolverProc(new EchoingHostResolverProc)); // This HostResolverImpl will only allow 1 outstanding resolve at a time. const size_t kMaxOutstandingJobs = 1u; scoped_ptr<HostResolverImpl> host_resolver(new HostResolverImpl( resolver_proc, CreateDefaultCache(), kMaxOutstandingJobs, NULL)); host_resolver->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV4); // Note that at this point the CapturingHostResolverProc is blocked, so any // requests we make will not complete. HostResolver::RequestInfo req[] = { CreateResolverRequestForAddressFamily("h1", MEDIUM, ADDRESS_FAMILY_UNSPECIFIED), CreateResolverRequestForAddressFamily("h1", MEDIUM, ADDRESS_FAMILY_IPV4), CreateResolverRequestForAddressFamily("h1", MEDIUM, ADDRESS_FAMILY_IPV6), }; TestCompletionCallback callback[arraysize(req)]; AddressList addrlist[arraysize(req)]; HostResolver::RequestHandle handle[arraysize(req)]; // Start all of the requests. for (size_t i = 0; i < arraysize(req); ++i) { int rv = host_resolver->Resolve(req[i], &addrlist[i], &callback[i], &handle[i], BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv) << i; } // Unblock the resolver thread so the requests can run. resolver_proc->Signal(); // Wait for all the requests to complete. for (size_t i = 0u; i < arraysize(req); ++i) { EXPECT_EQ(OK, callback[i].WaitForResult()); } // Since the requests all had the same priority and we limited the thread // count to 1, they should have completed in the same order as they were // requested. Moreover, request0 and request1 will have been serviced by // the same job. CapturingHostResolverProc::CaptureList capture_list = resolver_proc->GetCaptureList(); ASSERT_EQ(2u, capture_list.size()); EXPECT_EQ("h1", capture_list[0].hostname); EXPECT_EQ(ADDRESS_FAMILY_IPV4, capture_list[0].address_family); EXPECT_EQ("h1", capture_list[1].hostname); EXPECT_EQ(ADDRESS_FAMILY_IPV6, capture_list[1].address_family); // Now check that the correct resolved IP addresses were returned. // Addresses take the form: 192.x.y.z // x = length of hostname // y = ASCII value of hostname[0] // z = value of address family EXPECT_EQ("192.2.104.1", NetAddressToString(addrlist[0].head())); EXPECT_EQ("192.2.104.1", NetAddressToString(addrlist[1].head())); EXPECT_EQ("192.2.104.2", NetAddressToString(addrlist[2].head())); } // This is the exact same test as SetDefaultAddressFamily_IPv4, except the order // of requests 0 and 1 is flipped, and the default is set to IPv6 in place of // IPv4. TEST_F(HostResolverImplTest, SetDefaultAddressFamily_IPv6) { scoped_refptr<CapturingHostResolverProc> resolver_proc( new CapturingHostResolverProc(new EchoingHostResolverProc)); // This HostResolverImpl will only allow 1 outstanding resolve at a time. const size_t kMaxOutstandingJobs = 1u; scoped_ptr<HostResolverImpl> host_resolver(new HostResolverImpl( resolver_proc, CreateDefaultCache(), kMaxOutstandingJobs, NULL)); host_resolver->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV6); // Note that at this point the CapturingHostResolverProc is blocked, so any // requests we make will not complete. HostResolver::RequestInfo req[] = { CreateResolverRequestForAddressFamily("h1", MEDIUM, ADDRESS_FAMILY_IPV6), CreateResolverRequestForAddressFamily("h1", MEDIUM, ADDRESS_FAMILY_UNSPECIFIED), CreateResolverRequestForAddressFamily("h1", MEDIUM, ADDRESS_FAMILY_IPV4), }; TestCompletionCallback callback[arraysize(req)]; AddressList addrlist[arraysize(req)]; HostResolver::RequestHandle handle[arraysize(req)]; // Start all of the requests. for (size_t i = 0; i < arraysize(req); ++i) { int rv = host_resolver->Resolve(req[i], &addrlist[i], &callback[i], &handle[i], BoundNetLog()); EXPECT_EQ(ERR_IO_PENDING, rv) << i; } // Unblock the resolver thread so the requests can run. resolver_proc->Signal(); // Wait for all the requests to complete. for (size_t i = 0u; i < arraysize(req); ++i) { EXPECT_EQ(OK, callback[i].WaitForResult()); } // Since the requests all had the same priority and we limited the thread // count to 1, they should have completed in the same order as they were // requested. Moreover, request0 and request1 will have been serviced by // the same job. CapturingHostResolverProc::CaptureList capture_list = resolver_proc->GetCaptureList(); ASSERT_EQ(2u, capture_list.size()); EXPECT_EQ("h1", capture_list[0].hostname); EXPECT_EQ(ADDRESS_FAMILY_IPV6, capture_list[0].address_family); EXPECT_EQ("h1", capture_list[1].hostname); EXPECT_EQ(ADDRESS_FAMILY_IPV4, capture_list[1].address_family); // Now check that the correct resolved IP addresses were returned. // Addresses take the form: 192.x.y.z // x = length of hostname // y = ASCII value of hostname[0] // z = value of address family EXPECT_EQ("192.2.104.2", NetAddressToString(addrlist[0].head())); EXPECT_EQ("192.2.104.2", NetAddressToString(addrlist[1].head())); EXPECT_EQ("192.2.104.1", NetAddressToString(addrlist[2].head())); } // This tests that the default address family is respected for synchronous // resolutions. TEST_F(HostResolverImplTest, SetDefaultAddressFamily_Synchronous) { scoped_refptr<CapturingHostResolverProc> resolver_proc( new CapturingHostResolverProc(new EchoingHostResolverProc)); const size_t kMaxOutstandingJobs = 10u; scoped_ptr<HostResolverImpl> host_resolver(new HostResolverImpl( resolver_proc, CreateDefaultCache(), kMaxOutstandingJobs, NULL)); host_resolver->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV4); // Unblock the resolver thread so the requests can run. resolver_proc->Signal(); HostResolver::RequestInfo req[] = { CreateResolverRequestForAddressFamily("b", MEDIUM, ADDRESS_FAMILY_UNSPECIFIED), CreateResolverRequestForAddressFamily("b", MEDIUM, ADDRESS_FAMILY_IPV6), CreateResolverRequestForAddressFamily("b", MEDIUM, ADDRESS_FAMILY_UNSPECIFIED), CreateResolverRequestForAddressFamily("b", MEDIUM, ADDRESS_FAMILY_IPV4), }; AddressList addrlist[arraysize(req)]; // Start and run all of the requests synchronously. for (size_t i = 0; i < arraysize(req); ++i) { int rv = host_resolver->Resolve(req[i], &addrlist[i], NULL, NULL, BoundNetLog()); EXPECT_EQ(OK, rv) << i; } // We should have sent 2 requests to the resolver -- // one for (b, IPv4), and one for (b, IPv6). CapturingHostResolverProc::CaptureList capture_list = resolver_proc->GetCaptureList(); ASSERT_EQ(2u, capture_list.size()); EXPECT_EQ("b", capture_list[0].hostname); EXPECT_EQ(ADDRESS_FAMILY_IPV4, capture_list[0].address_family); EXPECT_EQ("b", capture_list[1].hostname); EXPECT_EQ(ADDRESS_FAMILY_IPV6, capture_list[1].address_family); // Now check that the correct resolved IP addresses were returned. // Addresses take the form: 192.x.y.z // x = length of hostname // y = ASCII value of hostname[0] // z = value of address family EXPECT_EQ("192.1.98.1", NetAddressToString(addrlist[0].head())); EXPECT_EQ("192.1.98.2", NetAddressToString(addrlist[1].head())); EXPECT_EQ("192.1.98.1", NetAddressToString(addrlist[2].head())); EXPECT_EQ("192.1.98.1", NetAddressToString(addrlist[3].head())); } TEST_F(HostResolverImplTest, DisallowNonCachedResponses) { AddressList addrlist; const int kPortnum = 80; scoped_refptr<RuleBasedHostResolverProc> resolver_proc( new RuleBasedHostResolverProc(NULL)); resolver_proc->AddRule("just.testing", "192.168.1.42"); scoped_ptr<HostResolver> host_resolver( CreateHostResolverImpl(resolver_proc)); // First hit will miss the cache. HostResolver::RequestInfo info(HostPortPair("just.testing", kPortnum)); info.set_only_use_cached_response(true); CapturingBoundNetLog log(CapturingNetLog::kUnbounded); int err = host_resolver->Resolve(info, &addrlist, NULL, NULL, log.bound()); EXPECT_EQ(ERR_NAME_NOT_RESOLVED, err); // This time, we fetch normally. info.set_only_use_cached_response(false); err = host_resolver->Resolve(info, &addrlist, NULL, NULL, log.bound()); EXPECT_EQ(OK, err); // Now we should be able to fetch from the cache. info.set_only_use_cached_response(true); err = host_resolver->Resolve(info, &addrlist, NULL, NULL, log.bound()); EXPECT_EQ(OK, err); const struct addrinfo* ainfo = addrlist.head(); EXPECT_EQ(static_cast<addrinfo*>(NULL), ainfo->ai_next); EXPECT_EQ(sizeof(struct sockaddr_in), ainfo->ai_addrlen); const struct sockaddr* sa = ainfo->ai_addr; const struct sockaddr_in* sa_in = reinterpret_cast<const sockaddr_in*>(sa); EXPECT_TRUE(htons(kPortnum) == sa_in->sin_port); EXPECT_TRUE(htonl(0xc0a8012a) == sa_in->sin_addr.s_addr); } // TODO(cbentzel): Test a mix of requests with different HostResolverFlags. } // namespace } // namespace net