text: // Copyright 2015 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/dns/host_resolver_mojo.h"
  
  #include "net/base/address_list.h"
  #include "net/base/net_errors.h"
  #include "net/dns/mojo_host_type_converters.h"
  #include "net/log/net_log.h"
  #include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h"
  #include "third_party/mojo/src/mojo/public/cpp/bindings/error_handler.h"
  
  namespace net {
  namespace {
  
  // Default TTL for successful host resolutions.
  const int kCacheEntryTTLSeconds = 5;
  
  // Default TTL for unsuccessful host resolutions.
  const int kNegativeCacheEntryTTLSeconds = 0;
  
  HostCache::Key CacheKeyForRequest(const HostResolver::RequestInfo& info) {
    return HostCache::Key(info.hostname(), info.address_family(),
                          info.host_resolver_flags());
  }
  
  }  // namespace
  
  class HostResolverMojo::Job : public interfaces::HostResolverRequestClient,
                                public mojo::ErrorHandler {
   public:
    Job(const HostCache::Key& key,
        AddressList* addresses,
        const CompletionCallback& callback,
        mojo::InterfaceRequest<interfaces::HostResolverRequestClient> request,
        base::WeakPtr<HostCache> host_cache);
  
   private:
    // interfaces::HostResolverRequestClient override.
    void ReportResult(int32_t error,
                      interfaces::AddressListPtr address_list) override;
  
    // mojo::ErrorHandler override.
    void OnConnectionError() override;
  
    const HostCache::Key key_;
    AddressList* addresses_;
    CompletionCallback callback_;
    mojo::Binding<interfaces::HostResolverRequestClient> binding_;
    base::WeakPtr<HostCache> host_cache_;
  };
  
  HostResolverMojo::HostResolverMojo(interfaces::HostResolverPtr resolver,
                                     const base::Closure& disconnect_callback)
      : resolver_(resolver.Pass()),
        disconnect_callback_(disconnect_callback),
        host_cache_(HostCache::CreateDefaultCache()),
        host_cache_weak_factory_(host_cache_.get()) {
    if (!disconnect_callback_.is_null())
      resolver_.set_error_handler(this);
  }
  
  HostResolverMojo::~HostResolverMojo() = default;
  
  int HostResolverMojo::Resolve(const RequestInfo& info,
                                RequestPriority priority,
                                AddressList* addresses,
                                const CompletionCallback& callback,
                                RequestHandle* request_handle,
                                const BoundNetLog& source_net_log) {
    DCHECK(thread_checker_.CalledOnValidThread());
    DVLOG(1) << "Resolve " << info.host_port_pair().ToString();
  
    HostCache::Key key = CacheKeyForRequest(info);
    int cached_result = ResolveFromCacheInternal(info, key, addresses);
    if (cached_result != ERR_DNS_CACHE_MISS) {
      DVLOG(1) << "Resolved " << info.host_port_pair().ToString()
               << " from cache";
      return cached_result;
    }
  
    interfaces::HostResolverRequestClientPtr handle;
    *request_handle = new Job(key, addresses, callback, mojo::GetProxy(&handle),
                              host_cache_weak_factory_.GetWeakPtr());
    resolver_->Resolve(interfaces::HostResolverRequestInfo::From(info),
                       handle.Pass());
    return ERR_IO_PENDING;
  }
  
  int HostResolverMojo::ResolveFromCache(const RequestInfo& info,
                                         AddressList* addresses,
                                         const BoundNetLog& source_net_log) {
    DCHECK(thread_checker_.CalledOnValidThread());
    DVLOG(1) << "ResolveFromCache " << info.host_port_pair().ToString();
    return ResolveFromCacheInternal(info, CacheKeyForRequest(info), addresses);
  }
  
  void HostResolverMojo::CancelRequest(RequestHandle req) {
    DCHECK(thread_checker_.CalledOnValidThread());
    // Deleting the Job closes the HostResolverRequestClient connection,
    // signalling cancellation of the request.
    delete static_cast<Job*>(req);
  }
  
  HostCache* HostResolverMojo::GetHostCache() {
    return host_cache_.get();
  }
  
  void HostResolverMojo::OnConnectionError() {
    DCHECK(!disconnect_callback_.is_null());
    disconnect_callback_.Run();
  }
  
  int HostResolverMojo::ResolveFromCacheInternal(const RequestInfo& info,
                                                 const HostCache::Key& key,
                                                 AddressList* addresses) {
    if (!info.allow_cached_response())
      return ERR_DNS_CACHE_MISS;
  
    const HostCache::Entry* entry =
        host_cache_->Lookup(key, base::TimeTicks::Now());
    if (!entry)
      return ERR_DNS_CACHE_MISS;
  
    *addresses = AddressList::CopyWithPort(entry->addrlist, info.port());
    return entry->error;
  }
  
  HostResolverMojo::Job::Job(
      const HostCache::Key& key,
      AddressList* addresses,
      const CompletionCallback& callback,
      mojo::InterfaceRequest<interfaces::HostResolverRequestClient> request,
      base::WeakPtr<HostCache> host_cache)
      : key_(key),
        addresses_(addresses),
        callback_(callback),
        binding_(this, request.Pass()),
        host_cache_(host_cache) {
    binding_.set_error_handler(this);
  }
  
  void HostResolverMojo::Job::ReportResult(
      int32_t error,
      interfaces::AddressListPtr address_list) {
    if (error == OK && address_list)
      *addresses_ = address_list->To<AddressList>();
    if (host_cache_) {
      base::TimeDelta ttl = base::TimeDelta::FromSeconds(
          error == OK ? kCacheEntryTTLSeconds : kNegativeCacheEntryTTLSeconds);
      HostCache::Entry entry(error, *addresses_, ttl);
      host_cache_->Set(key_, entry, base::TimeTicks::Now(), ttl);
    }
    callback_.Run(error);
    delete this;
  }
  
  void HostResolverMojo::Job::OnConnectionError() {
    ReportResult(ERR_FAILED, interfaces::AddressListPtr());
  }
  
  }  // namespace net
  