// 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