summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorlevin@chromium.org <levin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-01 06:09:30 +0000
committerlevin@chromium.org <levin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-01 06:09:30 +0000
commit9490734d8a4fc608b690bdbb06c97bfdc87fff22 (patch)
tree828b05d72987bc318067517215a0eb7b3c853bf9 /net
parentaae4c8d74f2367bfe9e02253cc42d3e1be07f13a (diff)
downloadchromium_src-9490734d8a4fc608b690bdbb06c97bfdc87fff22.zip
chromium_src-9490734d8a4fc608b690bdbb06c97bfdc87fff22.tar.gz
chromium_src-9490734d8a4fc608b690bdbb06c97bfdc87fff22.tar.bz2
Revert 99135 - Refactoring and remaining work on DnsConfigService:
- Isolate WatchingFileReader for reusability and testability - ParseHosts to parse /etc/hosts - tests BUG=90881 TEST=./net_unittests --gtest_filter='DnsConfig*:DnsHosts*:WatchingFileReader*' Review URL: http://codereview.chromium.org/7753027 TBR=szym@chromium.org Review URL: http://codereview.chromium.org/7829001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@99138 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/dns/dns_config_service.cc83
-rw-r--r--net/dns/dns_config_service.h67
-rw-r--r--net/dns/dns_config_service_posix.cc205
-rw-r--r--net/dns/dns_config_service_posix.h81
-rw-r--r--net/dns/dns_config_service_posix_unittest.cc349
-rw-r--r--net/dns/dns_config_service_unittest.cc108
-rw-r--r--net/dns/dns_hosts.cc47
-rw-r--r--net/dns/dns_hosts.h41
-rw-r--r--net/dns/dns_hosts_unittest.cc61
-rw-r--r--net/dns/watching_file_reader.cc133
-rw-r--r--net/dns/watching_file_reader.h133
-rw-r--r--net/dns/watching_file_reader_unittest.cc289
-rw-r--r--net/net.gyp7
13 files changed, 611 insertions, 993 deletions
diff --git a/net/dns/dns_config_service.cc b/net/dns/dns_config_service.cc
index b3a27d7..f32f293 100644
--- a/net/dns/dns_config_service.cc
+++ b/net/dns/dns_config_service.cc
@@ -4,8 +4,6 @@
#include "net/dns/dns_config_service.h"
-#include "base/file_util.h"
-#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
namespace net {
@@ -20,7 +18,7 @@ DnsConfig::DnsConfig()
DnsConfig::~DnsConfig() {}
-bool DnsConfig::EqualsIgnoreHosts(const DnsConfig& d) const {
+bool DnsConfig::Equals(const DnsConfig& d) const {
return (nameservers == d.nameservers) &&
(search == d.search) &&
(ndots == d.ndots) &&
@@ -30,84 +28,5 @@ bool DnsConfig::EqualsIgnoreHosts(const DnsConfig& d) const {
(edns0 == d.edns0);
}
-bool DnsConfig::Equals(const DnsConfig& d) const {
- return EqualsIgnoreHosts(d) && (hosts == d.hosts);
-}
-
-DnsConfigService::DnsConfigService()
- : have_config_(false),
- have_hosts_(false) {}
-
-DnsConfigService::~DnsConfigService() {}
-
-void DnsConfigService::AddObserver(Observer* observer) {
- DCHECK(CalledOnValidThread());
- observers_.AddObserver(observer);
- if (have_config_ && have_hosts_) {
- observer->OnConfigChanged(dns_config_);
- }
-}
-
-void DnsConfigService::RemoveObserver(Observer* observer) {
- DCHECK(CalledOnValidThread());
- observers_.RemoveObserver(observer);
-}
-
-void DnsConfigService::OnConfigRead(const DnsConfig& config) {
- DCHECK(CalledOnValidThread());
- if (!config.EqualsIgnoreHosts(dns_config_)) {
- DnsConfig copy = config;
- copy.hosts.swap(dns_config_.hosts);
- dns_config_ = copy;
- have_config_ = true;
- if (have_hosts_) {
- FOR_EACH_OBSERVER(Observer, observers_, OnConfigChanged(dns_config_));
- }
- }
-}
-
-void DnsConfigService::OnHostsRead(const DnsHosts& hosts) {
- DCHECK(CalledOnValidThread());
- if (hosts != dns_config_.hosts || !have_hosts_) {
- dns_config_.hosts = hosts;
- have_hosts_ = true;
- if (have_config_) {
- FOR_EACH_OBSERVER(Observer, observers_, OnConfigChanged(dns_config_));
- }
- }
-}
-
-DnsHostsReader::DnsHostsReader(DnsConfigService* service)
- : service_(service),
- success_(false) {
- DCHECK(service);
-}
-
-// Reads the contents of the file at |path| into |str| if the total length is
-// less than |size|.
-static bool ReadFile(const FilePath& path, int64 size, std::string* str) {
- int64 sz;
- if (!file_util::GetFileSize(path, &sz) || sz > size)
- return false;
- return file_util::ReadFileToString(path, str);
-}
-
-void DnsHostsReader::DoRead() {
- success_ = false;
- std::string contents;
- const int64 kMaxHostsSize = 1 << 16;
- if (ReadFile(get_path(), kMaxHostsSize, &contents)) {
- success_ = true;
- ParseHosts(contents, &dns_hosts_);
- }
-}
-
-void DnsHostsReader::OnReadFinished() {
- if (success_)
- service_->OnHostsRead(dns_hosts_);
-}
-
-DnsHostsReader::~DnsHostsReader() {}
-
} // namespace net
diff --git a/net/dns/dns_config_service.h b/net/dns/dns_config_service.h
index 6ceeaa6..535dcc1 100644
--- a/net/dns/dns_config_service.h
+++ b/net/dns/dns_config_service.h
@@ -6,20 +6,16 @@
#define NET_DNS_DNS_CONFIG_SERVICE_H_
#pragma once
-#include <map>
#include <string>
#include <vector>
-#include "base/gtest_prod_util.h"
-#include "base/observer_list.h"
#include "base/time.h"
-#include "net/base/ip_endpoint.h" // win requires size of IPEndPoint
#include "net/base/net_export.h"
-#include "net/dns/dns_hosts.h"
-#include "net/dns/watching_file_reader.h"
namespace net {
+class IPEndPoint;
+
// DnsConfig stores configuration of the system resolver.
struct NET_EXPORT_PRIVATE DnsConfig {
DnsConfig();
@@ -27,9 +23,7 @@ struct NET_EXPORT_PRIVATE DnsConfig {
bool Equals(const DnsConfig& d) const;
- bool EqualsIgnoreHosts(const DnsConfig& d) const;
-
- bool IsValid() const {
+ bool Valid() const {
return !nameservers.empty();
}
@@ -39,12 +33,9 @@ struct NET_EXPORT_PRIVATE DnsConfig {
// is less than |ndots|.
std::vector<std::string> search;
- DnsHosts hosts;
-
// Resolver options; see man resolv.conf.
// TODO(szym): use |ndots| and |search| to determine the sequence of FQDNs
// to query given a specific name.
- // TODO(szym): implement DNS Devolution for windows
// Minimum number of dots before global resolution precedes |search|.
int ndots;
@@ -58,11 +49,9 @@ struct NET_EXPORT_PRIVATE DnsConfig {
bool edns0;
};
-
// Service for watching when the system DNS settings have changed.
// Depending on the platform, watches files in /etc/ or win registry.
-class NET_EXPORT_PRIVATE DnsConfigService
- : NON_EXPORTED_BASE(public base::NonThreadSafe) {
+class NET_EXPORT_PRIVATE DnsConfigService {
public:
// Callback interface for the client. The observer is called on the same
// thread as Watch(). Observer must outlive the service.
@@ -70,62 +59,28 @@ class NET_EXPORT_PRIVATE DnsConfigService
public:
virtual ~Observer() {}
- // Called only when |dns_config| is different from the last call.
+ // Called only when |dns_config| is different from the last check.
virtual void OnConfigChanged(const DnsConfig& dns_config) = 0;
};
// Creates the platform-specific DnsConfigService.
static DnsConfigService* CreateSystemService();
- DnsConfigService();
- virtual ~DnsConfigService();
+ DnsConfigService() {}
+ virtual ~DnsConfigService() {}
// Immediately starts watching system configuration for changes and attempts
// to read the configuration. For some platform implementations, the current
// thread must have an IO loop (for base::files::FilePathWatcher).
- virtual void Watch() {}
+ virtual void Watch() = 0;
// If a config is available, |observer| will immediately be called with
// OnConfigChanged.
- void AddObserver(Observer* observer);
- void RemoveObserver(Observer* observer);
-
- protected:
- FRIEND_TEST_ALL_PREFIXES(DnsConfigServiceTest, NotifyOnChange);
- friend class DnsHostsReader;
- // To be called with new config. |config|.hosts is ignored.
- virtual void OnConfigRead(const DnsConfig& config);
- // To be called with new hosts. Rest of the config is assumed unchanged.
- virtual void OnHostsRead(const DnsHosts& hosts);
-
- DnsConfig dns_config_;
- // True after first OnConfigRead and OnHostsRead; indicate complete config.
- bool have_config_;
- bool have_hosts_;
-
- ObserverList<Observer> observers_;
- private:
- DISALLOW_COPY_AND_ASSIGN(DnsConfigService);
-};
-
-// A WatchingFileReader that reads a HOSTS file and notifies
-// DnsConfigService::OnHostsRead().
-class DnsHostsReader : public WatchingFileReader {
- public:
- explicit DnsHostsReader(DnsConfigService* service);
-
- virtual void DoRead() OVERRIDE;
- virtual void OnReadFinished() OVERRIDE;
+ virtual void AddObserver(Observer* observer) = 0;
+ virtual void RemoveObserver(Observer* observer) = 0;
private:
- virtual ~DnsHostsReader();
-
- DnsConfigService* service_;
- // Written in DoRead, read in OnReadFinished, no locking necessary.
- DnsHosts dns_hosts_;
- bool success_;
-
- DISALLOW_COPY_AND_ASSIGN(DnsHostsReader);
+ DISALLOW_COPY_AND_ASSIGN(DnsConfigService);
};
} // namespace net
diff --git a/net/dns/dns_config_service_posix.cc b/net/dns/dns_config_service_posix.cc
index c8b4ab1..c79e43c 100644
--- a/net/dns/dns_config_service_posix.cc
+++ b/net/dns/dns_config_service_posix.cc
@@ -6,10 +6,16 @@
#include <string>
+#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/file_path.h"
-#include "base/file_util.h"
+#include "base/files/file_path_watcher.h"
+#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "base/observer_list.h"
+#include "base/threading/worker_pool.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_util.h"
@@ -19,53 +25,198 @@
namespace net {
-// A WatcherDelegate that uses ResolverLib to initialize res_state and converts
-// it to DnsConfig.
-class DnsConfigServicePosix::DnsConfigReader : public WatchingFileReader {
+// FilePathWatcher::Delegate is refcounted, so we separate it from the Service
+// It also hosts callbacks on the WorkerPool.
+class DnsConfigServicePosix::WatcherDelegate
+ : public base::files::FilePathWatcher::Delegate {
public:
- explicit DnsConfigReader(DnsConfigServicePosix* service)
+ // Takes ownership of |lib|.
+ WatcherDelegate(DnsConfigServicePosix* service,
+ DnsConfigServicePosix::ResolverLib* lib)
: service_(service),
- success_(false) {}
+ resolver_lib_(lib),
+ message_loop_(base::MessageLoopProxy::current()),
+ reading_(false),
+ read_pending_(false) {}
- void DoRead() OVERRIDE {
- struct __res_state res;
- if ((res_ninit(&res) == 0) && (res.options & RES_INIT)) {
- success_ = ConvertResToConfig(res, &dns_config_);
+ void Cancel() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ service_ = NULL;
+ }
+
+ void RescheduleWatch() {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ // Retry Watch in 100ms or so.
+ message_loop_->PostDelayedTask(
+ FROM_HERE, base::Bind(&WatcherDelegate::StartWatch, this), 100);
+ }
+
+ // FilePathWatcher::Delegate interface
+ virtual void OnFilePathChanged(const FilePath& path) OVERRIDE {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ if (!service_)
+ return;
+ ScheduleRead();
+ }
+
+ virtual void OnFilePathError(const FilePath& path) OVERRIDE {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ StartWatch();
+ }
+
+ private:
+ virtual ~WatcherDelegate() {}
+
+ // Unless already scheduled, post DoRead to WorkerPool.
+ void ScheduleRead() {
+ if (reading_) {
+ // Mark that we need to re-read after DoRead posts results.
+ read_pending_ = true;
} else {
- // Note: res_ninit in glibc always returns 0 and sets RES_INIT.
- success_ = false;
+ if (!base::WorkerPool::PostTask(FROM_HERE, base::Bind(
+ &WatcherDelegate::DoRead, this), false)) {
+ // See worker_pool_posix.cc.
+ NOTREACHED() << "WorkerPool::PostTask is not expected to fail on posix";
+ }
+ reading_ = true;
+ read_pending_ = false;
+ }
+ }
+
+ // Reads DnsConfig and posts OnResultAvailable to |message_loop_|.
+ // Must be called on the worker thread.
+ void DoRead() {
+ DnsConfig config;
+ struct __res_state res;
+ bool success = false;
+ if (resolver_lib_->ninit(&res) == 0) {
+ success = ConvertResToConfig(res, &config);
+ resolver_lib_->nclose(&res);
}
- res_nclose(&res);
+ // If this fails, the loop is gone, so there is no point retrying.
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &WatcherDelegate::OnResultAvailable, this, config, success));
}
- void OnReadFinished() OVERRIDE {
- if (success_)
- service_->OnConfigRead(dns_config_);
+ // Communicates result to the service. Must be called on the the same thread
+ // that constructed WatcherDelegate.
+ void OnResultAvailable(const DnsConfig &config, bool success) {
+ DCHECK(message_loop_->BelongsToCurrentThread());
+ if (!service_)
+ return;
+ reading_ = false;
+ if (read_pending_) {
+ // Discard this result and re-schedule.
+ ScheduleRead();
+ return;
+ }
+ if (!success) {
+ VLOG(1) << "Failed to read DnsConfig";
+ } else {
+ service_->OnConfigRead(config);
+ }
}
- private:
- virtual ~DnsConfigReader() {}
+ // To avoid refcounting the service, use this method in tasks/callbacks.
+ void StartWatch() {
+ if (!service_)
+ return;
+ service_->StartWatch();
+ }
DnsConfigServicePosix* service_;
- // Written in DoRead, read in OnReadFinished, no locking necessary.
- DnsConfig dns_config_;
- bool success_;
+ scoped_ptr<DnsConfigServicePosix::ResolverLib> resolver_lib_;
+ // Message loop for the thread on which Watch is called (of TYPE_IO).
+ scoped_refptr<base::MessageLoopProxy> message_loop_;
+ // True after DoRead before OnResultsAvailable.
+ bool reading_;
+ // True after OnFilePathChanged fires while |reading_| is true.
+ bool read_pending_;
};
DnsConfigServicePosix::DnsConfigServicePosix()
- : config_reader_(new DnsConfigReader(this)),
- hosts_reader_(new DnsHostsReader(this)) {}
+ : have_config_(false),
+ resolver_lib_(new ResolverLib()),
+ watcher_factory_(new FilePathWatcherFactory()) {
+}
DnsConfigServicePosix::~DnsConfigServicePosix() {
DCHECK(CalledOnValidThread());
- config_reader_->Cancel();
- hosts_reader_->Cancel();
+ if (watcher_delegate_.get())
+ watcher_delegate_->Cancel();
+}
+
+void DnsConfigServicePosix::AddObserver(Observer* observer) {
+ DCHECK(CalledOnValidThread());
+ observers_.AddObserver(observer);
+ if (have_config_) {
+ observer->OnConfigChanged(dns_config_);
+ }
+}
+
+void DnsConfigServicePosix::RemoveObserver(Observer* observer) {
+ DCHECK(CalledOnValidThread());
+ observers_.RemoveObserver(observer);
}
void DnsConfigServicePosix::Watch() {
DCHECK(CalledOnValidThread());
- config_reader_->StartWatch(FilePath(FILE_PATH_LITERAL(_PATH_RESCONF)));
- hosts_reader_->StartWatch(FilePath(FILE_PATH_LITERAL("/etc/hosts")));
+ DCHECK(!watcher_delegate_.get());
+ DCHECK(!resolv_file_watcher_.get());
+ DCHECK(resolver_lib_.get());
+ DCHECK(watcher_factory_.get());
+
+ watcher_delegate_ = new WatcherDelegate(this, resolver_lib_.release());
+ StartWatch();
+}
+
+void DnsConfigServicePosix::OnConfigRead(const DnsConfig& config) {
+ DCHECK(CalledOnValidThread());
+ if (!config.Equals(dns_config_)) {
+ dns_config_ = config;
+ have_config_ = true;
+ FOR_EACH_OBSERVER(Observer, observers_, OnConfigChanged(config));
+ }
+}
+
+void DnsConfigServicePosix::StartWatch() {
+ DCHECK(CalledOnValidThread());
+ DCHECK(watcher_delegate_.get());
+
+ FilePath path(FILE_PATH_LITERAL(_PATH_RESCONF));
+
+ // FilePathWatcher allows only one Watch per lifetime, so we need a new one.
+ resolv_file_watcher_.reset(watcher_factory_->CreateFilePathWatcher());
+ if (resolv_file_watcher_->Watch(path, watcher_delegate_.get())) {
+ // Make the initial read after watch is installed.
+ watcher_delegate_->OnFilePathChanged(path);
+ } else {
+ VLOG(1) << "Watch failed, scheduling restart";
+ watcher_delegate_->RescheduleWatch();
+ }
+}
+
+int DnsConfigServicePosix::ResolverLib::ninit(res_state statp) {
+ return ::res_ninit(statp);
+}
+
+void DnsConfigServicePosix::ResolverLib::nclose(res_state statp) {
+ return ::res_nclose(statp);
+}
+
+DnsConfigServicePosix::FilePathWatcherShim::FilePathWatcherShim()
+ : watcher_(new base::files::FilePathWatcher()) {}
+DnsConfigServicePosix::FilePathWatcherShim::~FilePathWatcherShim() {}
+
+bool DnsConfigServicePosix::FilePathWatcherShim::Watch(
+ const FilePath& path,
+ base::files::FilePathWatcher::Delegate* delegate) {
+ return watcher_->Watch(path, delegate);
+}
+
+DnsConfigServicePosix::FilePathWatcherShim*
+DnsConfigServicePosix::FilePathWatcherFactory::CreateFilePathWatcher() {
+ return new FilePathWatcherShim();
}
// static
diff --git a/net/dns/dns_config_service_posix.h b/net/dns/dns_config_service_posix.h
index 5349adf..1cdd0b2 100644
--- a/net/dns/dns_config_service_posix.h
+++ b/net/dns/dns_config_service_posix.h
@@ -9,29 +9,102 @@
#include <resolv.h>
#include "base/compiler_specific.h"
+#include "base/files/file_path_watcher.h"
#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "base/threading/non_thread_safe.h"
#include "net/base/net_export.h"
#include "net/dns/dns_config_service.h"
namespace net {
class NET_EXPORT_PRIVATE DnsConfigServicePosix
- : NON_EXPORTED_BASE(public DnsConfigService) {
+ : public NON_EXPORTED_BASE(DnsConfigService),
+ public NON_EXPORTED_BASE(base::NonThreadSafe) {
public:
- class DnsConfigReader;
+ class ResolverLib;
+ class FilePathWatcherShim;
+ class FilePathWatcherFactory;
+ class WatcherDelegate;
DnsConfigServicePosix();
virtual ~DnsConfigServicePosix();
+ virtual void AddObserver(Observer* observer) OVERRIDE;
+ virtual void RemoveObserver(Observer* observer) OVERRIDE;
virtual void Watch() OVERRIDE;
+ // Takes ownership of |lib|. Must be set before Watch.
+ void set_resolver_lib(ResolverLib* lib) {
+ DCHECK(!watcher_delegate_.get());
+ resolver_lib_.reset(lib);
+ }
+
+ // Takes ownership of |factory|. Must be set before Watch.
+ void set_watcher_factory(FilePathWatcherFactory* factory) {
+ DCHECK(!watcher_delegate_.get());
+ watcher_factory_.reset(factory);
+ }
+
private:
- scoped_refptr<WatchingFileReader> config_reader_;
- scoped_refptr<WatchingFileReader> hosts_reader_;
+ void OnConfigRead(const DnsConfig& config);
+
+ // Configure a FilePathWatcher and install watcher_delegate_. Executed each
+ // time FilePathWatcher fails.
+ void StartWatch();
+
+ DnsConfig dns_config_;
+ // True after first OnConfigChanged, that is, dns_config_ is valid.
+ bool have_config_;
+
+ scoped_ptr<ResolverLib> resolver_lib_;
+ scoped_ptr<FilePathWatcherFactory> watcher_factory_;
+ scoped_ptr<FilePathWatcherShim> resolv_file_watcher_;
+ scoped_refptr<WatcherDelegate> watcher_delegate_;
+ ObserverList<Observer> observers_;
DISALLOW_COPY_AND_ASSIGN(DnsConfigServicePosix);
};
+
+// Allows mocking res_ninit.
+class NET_EXPORT_PRIVATE DnsConfigServicePosix::ResolverLib
+ : public NON_EXPORTED_BASE(base::NonThreadSafe) {
+ public:
+ ResolverLib() {}
+ virtual ~ResolverLib() {}
+ virtual int ninit(res_state statp);
+ virtual void nclose(res_state statp);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResolverLib);
+};
+
+// Allows mocking FilePathWatcher
+class NET_EXPORT_PRIVATE DnsConfigServicePosix::FilePathWatcherShim
+ : public NON_EXPORTED_BASE(base::NonThreadSafe) {
+ public:
+ FilePathWatcherShim();
+ virtual ~FilePathWatcherShim();
+
+ virtual bool Watch(
+ const FilePath& path,
+ base::files::FilePathWatcher::Delegate* delegate) WARN_UNUSED_RESULT;
+ private:
+ scoped_ptr<base::files::FilePathWatcher> watcher_;
+ DISALLOW_COPY_AND_ASSIGN(FilePathWatcherShim);
+};
+
+class NET_EXPORT_PRIVATE DnsConfigServicePosix::FilePathWatcherFactory
+ : public NON_EXPORTED_BASE(base::NonThreadSafe) {
+ public:
+ FilePathWatcherFactory() {}
+ virtual ~FilePathWatcherFactory() {}
+ virtual FilePathWatcherShim* CreateFilePathWatcher();
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FilePathWatcherFactory);
+};
+
// Fills in |dns_config| from |res|. Exposed for tests.
bool NET_EXPORT_PRIVATE ConvertResToConfig(const struct __res_state& res,
DnsConfig* dns_config);
diff --git a/net/dns/dns_config_service_posix_unittest.cc b/net/dns/dns_config_service_posix_unittest.cc
index a3f90aa..d38d589 100644
--- a/net/dns/dns_config_service_posix_unittest.cc
+++ b/net/dns/dns_config_service_posix_unittest.cc
@@ -5,8 +5,14 @@
#include <arpa/inet.h>
#include <resolv.h>
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/message_loop.h"
+#include "base/message_loop_proxy.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "net/base/ip_endpoint.h"
#include "net/dns/dns_config_service_posix.h"
-
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
@@ -108,18 +114,264 @@ void CloseResState(res_state res) {
#endif
}
-} // namespace
+class DnsConfigServiceTest : public testing::Test,
+ public DnsConfigService::Observer {
+ public:
+ // Mocks
+
+ // DnsConfigService owns the instances of ResolverLib and
+ // FilePathWatcherFactory that it gets, so use simple proxies to call
+ // DnsConfigServiceTest.
+
+ // ResolverLib is owned by WatcherDelegate which is posted to WorkerPool so
+ // it must be canceled before the test is over.
+ class MockResolverLib : public DnsConfigServicePosix::ResolverLib {
+ public:
+ explicit MockResolverLib(DnsConfigServiceTest *test) : test_(test) {}
+ virtual ~MockResolverLib() {
+ base::AutoLock lock(lock_);
+ if (test_) {
+ EXPECT_TRUE(test_->IsComplete());
+ }
+ }
+ virtual int ninit(res_state res) OVERRIDE {
+ base::AutoLock lock(lock_);
+ if (test_)
+ return test_->OnNinit(res);
+ else
+ return -1;
+ }
+ virtual void nclose(res_state res) OVERRIDE {
+ CloseResState(res);
+ }
+ void Cancel() {
+ base::AutoLock lock(lock_);
+ test_ = NULL;
+ }
+ private:
+ base::Lock lock_;
+ DnsConfigServiceTest *test_;
+ };
+
+ class MockFilePathWatcherShim
+ : public DnsConfigServicePosix::FilePathWatcherShim {
+ public:
+ typedef base::files::FilePathWatcher::Delegate Delegate;
+
+ explicit MockFilePathWatcherShim(DnsConfigServiceTest* t) : test_(t) {}
+ virtual ~MockFilePathWatcherShim() {
+ test_->OnShimDestroyed(this);
+ }
+
+ // Enforce one-Watch-per-lifetime as the original FilePathWatcher
+ virtual bool Watch(const FilePath& path,
+ Delegate* delegate) OVERRIDE {
+ EXPECT_TRUE(path_.empty()) << "Only one-Watch-per-lifetime allowed.";
+ EXPECT_TRUE(!delegate_.get());
+ path_ = path;
+ delegate_ = delegate;
+ return test_->OnWatch();
+ }
+
+ void PathChanged() {
+ delegate_->OnFilePathChanged(path_);
+ }
+
+ void PathError() {
+ delegate_->OnFilePathError(path_);
+ }
+
+ private:
+ FilePath path_;
+ scoped_refptr<Delegate> delegate_;
+ DnsConfigServiceTest* test_;
+ };
+
+ class MockFilePathWatcherFactory
+ : public DnsConfigServicePosix::FilePathWatcherFactory {
+ public:
+ explicit MockFilePathWatcherFactory(DnsConfigServiceTest* t) : test(t) {}
+ virtual ~MockFilePathWatcherFactory() {
+ EXPECT_TRUE(test->IsComplete());
+ }
+ virtual DnsConfigServicePosix::FilePathWatcherShim*
+ CreateFilePathWatcher() OVERRIDE {
+ return test->CreateFilePathWatcher();
+ }
+ DnsConfigServiceTest* test;
+ };
+
+ // Helpers for mocks.
+
+ DnsConfigServicePosix::FilePathWatcherShim* CreateFilePathWatcher() {
+ watcher_shim_ = new MockFilePathWatcherShim(this);
+ return watcher_shim_;
+ }
+
+ void OnShimDestroyed(MockFilePathWatcherShim* destroyed_shim) {
+ // Precaution to avoid segfault.
+ if (watcher_shim_ == destroyed_shim)
+ watcher_shim_ = NULL;
+ }
+
+ // On each event, post QuitTask to allow use of MessageLoop::Run() to
+ // synchronize the threads.
+
+ bool OnWatch() {
+ EXPECT_TRUE(message_loop_ == MessageLoop::current());
+ watch_called_ = true;
+ BreakNow("OnWatch");
+ return !fail_on_watch_;
+ }
+
+ int OnNinit(res_state res) {
+ { // Check that res_ninit is executed serially.
+ base::AutoLock lock(ninit_lock_);
+ EXPECT_FALSE(ninit_running_) << "res_ninit is not called serially!";
+ ninit_running_ = true;
+ }
+ BreakNow("OnNinit");
+ ninit_allowed_.Wait();
+ // Calling from another thread is a bit dirty, but it's protected.
+ int rv = OnNinitNonThreadSafe(res);
+ // This lock might be destroyed after ninit_called_ is signalled.
+ {
+ base::AutoLock lock(ninit_lock_);
+ ninit_running_ = false;
+ }
+ ninit_called_.Signal();
+ return rv;
+ }
+
+ virtual void OnConfigChanged(const DnsConfig& new_config) OVERRIDE {
+ EXPECT_TRUE(message_loop_ == MessageLoop::current());
+ CompareConfig(res_, new_config);
+ EXPECT_FALSE(new_config.Equals(last_config_)) <<
+ "Config must be different from last call.";
+ last_config_ = new_config;
+ got_config_ = true;
+ BreakNow("OnConfigChanged");
+ }
+
+ bool IsComplete() {
+ return complete_;
+ }
+
+ protected:
+ friend class BreakTask;
+ class BreakTask : public Task {
+ public:
+ BreakTask(DnsConfigServiceTest* test, std::string breakpoint)
+ : test_(test), breakpoint_(breakpoint) {}
+ virtual ~BreakTask() {}
+ virtual void Run() OVERRIDE {
+ test_->breakpoint_ = breakpoint_;
+ MessageLoop::current()->QuitNow();
+ }
+ private:
+ DnsConfigServiceTest* test_;
+ std::string breakpoint_;
+ };
+
+ void BreakNow(std::string b) {
+ message_loop_->PostTask(FROM_HERE, new BreakTask(this, b));
+ }
+
+ void RunUntilBreak(std::string b) {
+ message_loop_->Run();
+ ASSERT_EQ(breakpoint_, b);
+ }
+
+ DnsConfigServiceTest()
+ : res_lib_(new MockResolverLib(this)),
+ watcher_shim_(NULL),
+ res_generation_(1),
+ watch_called_(false),
+ got_config_(false),
+ fail_on_watch_(false),
+ fail_on_ninit_(false),
+ complete_(false),
+ ninit_allowed_(false, false),
+ ninit_called_(false, false),
+ ninit_running_(false) {
+ }
+
+ // This is on WorkerPool, but protected by ninit_allowed_.
+ int OnNinitNonThreadSafe(res_state res) {
+ if (fail_on_ninit_)
+ return -1;
+ InitializeResState(res, res_generation_);
+ // Store a (deep) copy in the fixture to later verify correctness.
+ CloseResState(&res_);
+ InitializeResState(&res_, res_generation_);
+ return 0;
+ }
+
+ // Helpers for tests.
+
+ // Lets OnNinit run OnNinitNonThreadSafe and waits for it to complete.
+ // Might get OnConfigChanged scheduled on the loop but we have no certainty.
+ void WaitForNinit() {
+ RunUntilBreak("OnNinit");
+ ninit_allowed_.Signal();
+ ninit_called_.Wait();
+ }
+
+ // test::Test methods
+ virtual void SetUp() OVERRIDE {
+ message_loop_ = MessageLoop::current();
+ service_.reset(new DnsConfigServicePosix());
+ service_->set_resolver_lib(res_lib_);
+ service_->set_watcher_factory(new MockFilePathWatcherFactory(this));
+ memset(&res_, 0, sizeof(res_));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ // res_lib_ could outlive the test, so make sure it doesn't call it.
+ res_lib_->Cancel();
+ CloseResState(&res_);
+ // Allow service_ to clean up ResolverLib and FilePathWatcherFactory.
+ complete_ = true;
+ }
+
+ MockResolverLib* res_lib_;
+ MockFilePathWatcherShim* watcher_shim_;
+ // Adds variety to the content of res_state.
+ int res_generation_;
+ struct __res_state res_;
+ DnsConfig last_config_;
+
+ bool watch_called_;
+ bool got_config_;
+ bool fail_on_watch_;
+ bool fail_on_ninit_;
+ bool complete_;
+
+ // Ninit is called on WorkerPool so we need to synchronize with it.
+ base::WaitableEvent ninit_allowed_;
+ base::WaitableEvent ninit_called_;
+
+ // Protected by ninit_lock_. Used to verify that Ninit calls are serialized.
+ bool ninit_running_;
+ base::Lock ninit_lock_;
+
+ // Loop for this thread.
+ MessageLoop* message_loop_;
+
+ // Service under test.
+ scoped_ptr<DnsConfigServicePosix> service_;
+
+ std::string breakpoint_;
+};
TEST(DnsConfigTest, ResolverConfigConvertAndEquals) {
struct __res_state res[2];
DnsConfig config[2];
for (int i = 0; i < 2; ++i) {
- EXPECT_FALSE(config[i].IsValid());
InitializeResState(&res[i], i);
ASSERT_TRUE(ConvertResToConfig(res[i], &config[i]));
}
for (int i = 0; i < 2; ++i) {
- EXPECT_TRUE(config[i].IsValid());
CompareConfig(res[i], config[i]);
CloseResState(&res[i]);
}
@@ -128,6 +380,93 @@ TEST(DnsConfigTest, ResolverConfigConvertAndEquals) {
EXPECT_FALSE(config[1].Equals(config[0]));
}
-} // namespace net
+TEST_F(DnsConfigServiceTest, FilePathWatcherFailures) {
+ // For these tests, disable ninit.
+ res_lib_->Cancel();
+
+ fail_on_watch_ = true;
+ service_->Watch();
+ RunUntilBreak("OnWatch");
+ EXPECT_TRUE(watch_called_) << "Must call FilePathWatcher::Watch().";
+
+ fail_on_watch_ = false;
+ watch_called_ = false;
+ RunUntilBreak("OnWatch"); // Due to backoff this will take 100ms.
+ EXPECT_TRUE(watch_called_) <<
+ "Must restart on FilePathWatcher::Watch() failure.";
+
+ watch_called_ = false;
+ ASSERT_TRUE(watcher_shim_);
+ watcher_shim_->PathError();
+ RunUntilBreak("OnWatch");
+ EXPECT_TRUE(watch_called_) <<
+ "Must restart on FilePathWatcher::Delegate::OnFilePathError().";
+ // Worker thread could still be posting OnResultAvailable to the message loop
+}
+
+TEST_F(DnsConfigServiceTest, NotifyOnValidAndDistinctConfig) {
+ service_->AddObserver(this);
+ service_->Watch();
+ RunUntilBreak("OnWatch");
+ fail_on_ninit_ = true;
+ WaitForNinit();
+
+ // If OnNinit posts OnResultAvailable before the next call, then this test
+ // verifies that failure on ninit should not cause OnConfigChanged.
+ // Otherwise, this only verifies that ninit calls are serialized.
+
+ fail_on_ninit_ = false;
+ ASSERT_TRUE(watcher_shim_);
+ watcher_shim_->PathChanged();
+ WaitForNinit();
+
+ RunUntilBreak("OnConfigChanged");
+ EXPECT_TRUE(got_config_);
+
+ message_loop_->AssertIdle();
+
+ got_config_ = false;
+ // Forget about the config to test if we get it again on AddObserver.
+ last_config_ = DnsConfig();
+ service_->RemoveObserver(this);
+ service_->AddObserver(this);
+ RunUntilBreak("OnConfigChanged");
+ EXPECT_TRUE(got_config_) << "Did not get config after AddObserver.";
+
+ // Simulate spurious FilePathChanged.
+ ASSERT_TRUE(watcher_shim_);
+ watcher_shim_->PathChanged();
+ WaitForNinit();
+
+ // OnConfigChanged will catch that the config did not actually change.
+
+ got_config_ = false;
+ ++res_generation_;
+ ASSERT_TRUE(watcher_shim_);
+ watcher_shim_->PathChanged();
+ WaitForNinit();
+ RunUntilBreak("OnConfigChanged");
+ EXPECT_TRUE(got_config_) << "Did not get config after change";
+
+ message_loop_->AssertIdle();
+
+ // Schedule two calls. OnNinit checks if it is called serially.
+ ++res_generation_;
+ ASSERT_TRUE(watcher_shim_);
+ watcher_shim_->PathChanged();
+ // ninit is blocked, so this will have to induce read_pending
+ watcher_shim_->PathChanged();
+ WaitForNinit();
+ WaitForNinit();
+ RunUntilBreak("OnConfigChanged");
+ EXPECT_TRUE(got_config_) << "Did not get config after change";
+
+ // We should be done with all tasks.
+ message_loop_->AssertIdle();
+}
+
+} // namespace
+
+} // namespace net
diff --git a/net/dns/dns_config_service_unittest.cc b/net/dns/dns_config_service_unittest.cc
deleted file mode 100644
index 5500e70..0000000
--- a/net/dns/dns_config_service_unittest.cc
+++ /dev/null
@@ -1,108 +0,0 @@
-// 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/dns/dns_config_service.h"
-
-#include "base/compiler_specific.h"
-#include "base/message_loop.h"
-#include "base/task.h"
-#include "base/test/test_timeouts.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-
-namespace {
-
-class DnsConfigServiceTest : public testing::Test,
- public DnsConfigService::Observer {
- public:
- void OnConfigChanged(const DnsConfig& config) OVERRIDE {
- EXPECT_FALSE(config.Equals(last_config_)) <<
- "Config must be different from last call.";
- last_config_ = config;
- if (quit_on_config_)
- MessageLoop::current()->Quit();
- }
- void Timeout() const {
- MessageLoop::current()->Quit();
- }
-
- protected:
- // test::Test methods
- virtual void SetUp() OVERRIDE {
- service_.reset(new DnsConfigService());
- quit_on_config_ = false;
- }
-
- DnsConfig last_config_;
- bool quit_on_config_;
-
- // Service under test.
- scoped_ptr<DnsConfigService> service_;
-};
-
-} // namespace
-
-TEST_F(DnsConfigServiceTest, NotifyOnChange) {
- service_->AddObserver(this);
- service_->Watch();
-
- DnsConfig config;
- IPAddressNumber ip;
- ASSERT_TRUE(ParseIPLiteralToNumber("1.2.3.4", &ip));
- config.nameservers.push_back(IPEndPoint(ip, 53));
- ASSERT_TRUE(config.IsValid());
- service_->OnConfigRead(config);
-
- EXPECT_FALSE(last_config_.Equals(config)) <<
- "Unexpected notification with incomplete config.";
-
- DnsHosts hosts;
- ParseHosts("127.0.0.1 localhost", &hosts);
- ASSERT_FALSE(hosts.empty());
- service_->OnHostsRead(hosts);
-
- EXPECT_TRUE(last_config_.hosts == hosts);
- EXPECT_TRUE(last_config_.EqualsIgnoreHosts(config));
-
- DnsConfig complete_config = config;
- complete_config.hosts = hosts;
- EXPECT_TRUE(last_config_.Equals(complete_config));
-
- // Check if it ignores no-change in config or hosts.
- service_->OnConfigRead(config);
- service_->OnHostsRead(hosts);
-
- // Check if it provides config and hosts after AddObserver;
- service_->RemoveObserver(this);
- last_config_ = DnsConfig();
- service_->AddObserver(this);
- EXPECT_TRUE(last_config_.Equals(complete_config));
-}
-
-#if defined(OS_POSIX)
-// TODO(szym): enable OS_WIN once ready
-// This is really an integration test.
-TEST_F(DnsConfigServiceTest, GetSystemConfig) {
- DnsConfigService* service = DnsConfigService::CreateSystemService();
-
- // Quit the loop after timeout unless cancelled
- const int64 kTimeout = TestTimeouts::action_timeout_ms();
- ScopedRunnableMethodFactory<DnsConfigServiceTest> factory_(this);
- MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- factory_.NewRunnableMethod(&DnsConfigServiceTest::Timeout),
- kTimeout);
-
- service->AddObserver(this);
- service->Watch();
- quit_on_config_ = true;
- MessageLoop::current()->Run();
- ASSERT_TRUE(last_config_.IsValid()) << "Did not receive DnsConfig in " <<
- kTimeout << "ms";
-}
-#endif // OS_POSIX
-
-} // namespace net
-
diff --git a/net/dns/dns_hosts.cc b/net/dns/dns_hosts.cc
deleted file mode 100644
index 1342808..0000000
--- a/net/dns/dns_hosts.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-// 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/dns/dns_hosts.h"
-
-#include "base/logging.h"
-#include "base/string_tokenizer.h"
-
-namespace net {
-
-void ParseHosts(const std::string& contents, DnsHosts* dns_hosts) {
- CHECK(dns_hosts);
- DnsHosts& hosts = *dns_hosts;
- // Split into lines. Accept CR for Windows.
- StringTokenizer contents_lines(contents, "\n\r");
- while (contents_lines.GetNext()) {
- // Ignore comments after '#'.
- std::string line = contents_lines.token();
- StringTokenizer line_parts(line, "#");
- line_parts.set_options(StringTokenizer::RETURN_DELIMS);
-
- if (line_parts.GetNext() && !line_parts.token_is_delim()) {
- // Split and trim whitespace.
- std::string part = line_parts.token();
- StringTokenizer tokens(part, " \t");
-
- if (tokens.GetNext()) {
- IPAddressNumber ip;
- // TODO(szym): handle %iface notation on mac
- if (!ParseIPLiteralToNumber(tokens.token(), &ip))
- continue; // Ignore malformed lines.
- AddressFamily fam = (ip.size() == 4) ? ADDRESS_FAMILY_IPV4 :
- ADDRESS_FAMILY_IPV6;
- while (tokens.GetNext()) {
- IPAddressNumber& mapped_ip = hosts[DnsHostsKey(tokens.token(), fam)];
- if (mapped_ip.empty())
- mapped_ip = ip;
- // else ignore this entry (first hit counts)
- }
- }
- }
- }
-}
-
-} // namespace net
-
diff --git a/net/dns/dns_hosts.h b/net/dns/dns_hosts.h
deleted file mode 100644
index fda7733..0000000
--- a/net/dns/dns_hosts.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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.
-
-#ifndef NET_DNS_DNS_HOSTS_H_
-#define NET_DNS_DNS_HOSTS_H_
-#pragma once
-
-#include <map>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "net/base/address_family.h"
-#include "net/base/net_export.h"
-#include "net/base/net_util.h" // can't forward-declare IPAddressNumber
-
-namespace net {
-
-// Parsed results of a Hosts file.
-//
-// Although Hosts files map IP address to a list of domain names, for name
-// resolution the desired mapping direction is: domain name to IP address.
-// When parsing Hosts, we apply the "first hit" rule as Windows and glibc do.
-// With a Hosts file of:
-// 300.300.300.300 localhost # bad ip
-// 127.0.0.1 localhost
-// 10.0.0.1 localhost
-// The expected resolution of localhost is 127.0.0.1.
-typedef std::pair<std::string, AddressFamily> DnsHostsKey;
-typedef std::map<DnsHostsKey, IPAddressNumber> DnsHosts;
-
-// Parses |contents| (as read from /etc/hosts or equivalent) and stores results
-// in |dns_hosts|. Invalid lines are ignored (as in most implementations).
-void NET_EXPORT_PRIVATE ParseHosts(const std::string& contents,
- DnsHosts* dns_hosts);
-
-} // namespace net
-
-#endif // NET_DNS_DNS_HOSTS_H_
-
diff --git a/net/dns/dns_hosts_unittest.cc b/net/dns/dns_hosts_unittest.cc
deleted file mode 100644
index 6da41a9..0000000
--- a/net/dns/dns_hosts_unittest.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-// 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/dns/dns_hosts.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-
-namespace {
-
-TEST(DnsHostsTest, ParseHosts) {
- std::string contents =
- "127.0.0.1 localhost\tlocalhost.localdomain # standard\n"
- "\n"
- "1.0.0.1 localhost # ignored, first hit above\n"
- "fe00::x example company # ignored, malformed IPv6\n"
- "1.0.0.300 company # ignored, malformed IPv4\n"
- "1.0.0.1 # ignored, missing hostname\n"
- "1.0.0.1\t company \n"
- "::1\tlocalhost ip6-localhost ip6-loopback # comment # within a comment\n"
- "\t fe00::0 ip6-localnet\r\n"
- "2048::2 example\n"
- "2048::1 company example # ignored for 'example' \n"
- "gibberish";
-
- const struct {
- const char* host;
- AddressFamily family;
- const char* ip;
- } entries[] = {
- { "localhost", ADDRESS_FAMILY_IPV4, "127.0.0.1" },
- { "localhost.localdomain", ADDRESS_FAMILY_IPV4, "127.0.0.1" },
- { "company", ADDRESS_FAMILY_IPV4, "1.0.0.1" },
- { "localhost", ADDRESS_FAMILY_IPV6, "::1" },
- { "ip6-localhost", ADDRESS_FAMILY_IPV6, "::1" },
- { "ip6-loopback", ADDRESS_FAMILY_IPV6, "::1" },
- { "ip6-localnet", ADDRESS_FAMILY_IPV6, "fe00::0" },
- { "company", ADDRESS_FAMILY_IPV6, "2048::1" },
- { "example", ADDRESS_FAMILY_IPV6, "2048::2" },
- };
-
- DnsHosts expected;
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(entries); ++i) {
- DnsHostsKey key(entries[i].host, entries[i].family);
- IPAddressNumber& ip = expected[key];
- ASSERT_TRUE(ip.empty());
- ASSERT_TRUE(ParseIPLiteralToNumber(entries[i].ip, &ip));
- ASSERT_EQ(ip.size(), (entries[i].family == ADDRESS_FAMILY_IPV4) ? 4u : 16u);
- }
-
- DnsHosts hosts;
- ParseHosts(contents, &hosts);
- ASSERT_EQ(expected, hosts);
-}
-
-} // namespace
-
-} // namespace net
-
diff --git a/net/dns/watching_file_reader.cc b/net/dns/watching_file_reader.cc
deleted file mode 100644
index b5f9159..0000000
--- a/net/dns/watching_file_reader.cc
+++ /dev/null
@@ -1,133 +0,0 @@
-// 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/dns/watching_file_reader.h"
-
-#include "base/bind.h"
-#include "base/message_loop_proxy.h"
-#include "base/threading/worker_pool.h"
-
-namespace net {
-
-FilePathWatcherShim::FilePathWatcherShim()
- : watcher_(new base::files::FilePathWatcher()) {}
-FilePathWatcherShim::~FilePathWatcherShim() {}
-
-bool FilePathWatcherShim::Watch(
- const FilePath& path,
- base::files::FilePathWatcher::Delegate* delegate) {
- DCHECK(CalledOnValidThread());
- return watcher_->Watch(path, delegate);
-}
-
-FilePathWatcherShim*
-FilePathWatcherFactory::CreateFilePathWatcher() {
- DCHECK(CalledOnValidThread());
- return new FilePathWatcherShim();
-}
-
-WatchingFileReader::WatchingFileReader()
- : message_loop_(base::MessageLoopProxy::current()),
- factory_(new FilePathWatcherFactory()),
- reading_(false),
- read_pending_(false),
- cancelled_(false) {}
-
-void WatchingFileReader::StartWatch(const FilePath& path) {
- DCHECK(message_loop_->BelongsToCurrentThread());
- DCHECK(!watcher_.get());
- DCHECK(path_.empty());
- path_ = path;
- RestartWatch();
-}
-
-void WatchingFileReader::Cancel() {
- DCHECK(message_loop_->BelongsToCurrentThread());
- cancelled_ = true;
- // Let go of the watcher to break the reference cycle.
- watcher_.reset();
-}
-
-void WatchingFileReader::OnFilePathChanged(const FilePath& path) {
- DCHECK(message_loop_->BelongsToCurrentThread());
- ReadNow();
-}
-
-void WatchingFileReader::OnFilePathError(const FilePath& path) {
- DCHECK(message_loop_->BelongsToCurrentThread());
- RestartWatch();
-}
-
-WatchingFileReader::~WatchingFileReader() {}
-
-void WatchingFileReader::RescheduleWatch() {
- DCHECK(message_loop_->BelongsToCurrentThread());
- message_loop_->PostDelayedTask(
- FROM_HERE,
- base::Bind(&WatchingFileReader::RestartWatch, this),
- kWatchRetryDelayMs);
-}
-
-void WatchingFileReader::RestartWatch() {
- if (cancelled_)
- return;
- watcher_.reset(factory_->CreateFilePathWatcher());
- if (watcher_->Watch(path_, this)) {
- ReadNow();
- } else {
- LOG(WARNING) << "Watch on " <<
- path_.LossyDisplayName() <<
- " failed, scheduling restart";
- RescheduleWatch();
- }
-}
-
-void WatchingFileReader::ReadNow() {
- if (cancelled_)
- return;
- if (reading_) {
- // Remember to re-read after DoRead posts results.
- read_pending_ = true;
- } else {
- if (!base::WorkerPool::PostTask(FROM_HERE, base::Bind(
- &WatchingFileReader::DoReadJob, this), false)) {
-#if defined(OS_POSIX)
- // See worker_pool_posix.cc.
- NOTREACHED() << "WorkerPool::PostTask is not expected to fail on posix";
-#else
- LOG(WARNING) << "Failed to WorkerPool::PostTask, will retry later";
- message_loop_->PostDelayedTask(
- FROM_HERE,
- base::Bind(&WatchingFileReader::ReadNow, this),
- kWorkerPoolRetryDelayMs);
- return;
-#endif
- }
- reading_ = true;
- read_pending_ = false;
- }
-}
-
-void WatchingFileReader::DoReadJob() {
- this->DoRead();
- // If this fails, the loop is gone, so there is no point retrying.
- message_loop_->PostTask(FROM_HERE, base::Bind(
- &WatchingFileReader::OnReadJobFinished, this));
-}
-
-void WatchingFileReader::OnReadJobFinished() {
- DCHECK(message_loop_->BelongsToCurrentThread());
- if (cancelled_)
- return;
- reading_ = false;
- if (read_pending_) {
- // Discard this result and re-read.
- ReadNow();
- return;
- }
- this->OnReadFinished();
-}
-
-} // namespace net
-
diff --git a/net/dns/watching_file_reader.h b/net/dns/watching_file_reader.h
deleted file mode 100644
index 77a9f12..0000000
--- a/net/dns/watching_file_reader.h
+++ /dev/null
@@ -1,133 +0,0 @@
-// 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.
-
-#ifndef NET_DNS_WATCHING_FILE_READER_H_
-#define NET_DNS_WATCHING_FILE_READER_H_
-#pragma once
-
-#include <string>
-
-#include "base/compiler_specific.h"
-#include "base/files/file_path_watcher.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/threading/non_thread_safe.h"
-#include "net/base/net_export.h"
-
-// Forward declaration
-namespace base {
-class MessageLoopProxy;
-}
-
-namespace net {
-
-// Allows mocking FilePathWatcher
-class NET_EXPORT_PRIVATE FilePathWatcherShim
- : NON_EXPORTED_BASE(public base::NonThreadSafe) {
- public:
- FilePathWatcherShim();
- virtual ~FilePathWatcherShim();
-
- virtual bool Watch(
- const FilePath& path,
- base::files::FilePathWatcher::Delegate* delegate) WARN_UNUSED_RESULT;
- private:
- scoped_ptr<base::files::FilePathWatcher> watcher_;
- DISALLOW_COPY_AND_ASSIGN(FilePathWatcherShim);
-};
-
-class NET_EXPORT_PRIVATE FilePathWatcherFactory
- : NON_EXPORTED_BASE(public base::NonThreadSafe) {
- public:
- FilePathWatcherFactory() {}
- virtual ~FilePathWatcherFactory() {}
- virtual FilePathWatcherShim* CreateFilePathWatcher();
- private:
- DISALLOW_COPY_AND_ASSIGN(FilePathWatcherFactory);
-};
-
-// A FilePathWatcher::Delegate that restarts Watch on failures and executes
-// DoRead on WorkerPool (one at a time) in response to FilePathChanged.
-// Derived classes should store results of the work done in DoRead in dedicated
-// fields and read them in OnReadFinished.
-//
-// This implementation avoids locking by using the reading_ flag to ensure that
-// DoRead and OnReadFinished cannot execute in parallel.
-//
-// There's no need to call Cancel() on destruction. On the contrary, a ref-cycle
-// between the WatchingFileReader and a FilePathWatcher will prevent destruction
-// until Cancel() is called.
-//
-// TODO(szym): update to WorkerPool::PostTaskAndReply once available.
-class NET_EXPORT_PRIVATE WatchingFileReader
- : public base::files::FilePathWatcher::Delegate {
- public:
- WatchingFileReader();
-
- // Must be called at most once. Made virtual to allow mocking.
- virtual void StartWatch(const FilePath& path);
-
- // Unless already scheduled, post DoRead to WorkerPool.
- void ReadNow();
-
- // Do not perform any further work, except perhaps a latent DoRead().
- // Always call it once done with the delegate to ensure destruction.
- void Cancel();
-
- void set_watcher_factory(FilePathWatcherFactory* factory) {
- DCHECK(!watcher_.get());
- factory_.reset(factory);
- }
-
- FilePath get_path() const {
- return path_;
- }
-
- // Delay between calls to FilePathWatcher::Watch.
- static const int kWatchRetryDelayMs = 100;
-
- // Delay between calls to WorkerPool::PostTask
- static const int kWorkerPoolRetryDelayMs = 100;
-
- protected:
- // protected to allow sub-classing, but prevent deleting
- virtual ~WatchingFileReader();
-
- // Executed on WorkerPool, at most once at a time.
- virtual void DoRead() = 0;
- // Executed on origin thread after DoRead() completes.
- virtual void OnReadFinished() = 0;
-
- private:
- // FilePathWatcher::Delegate interface
- virtual void OnFilePathChanged(const FilePath& path) OVERRIDE;
- virtual void OnFilePathError(const FilePath& path) OVERRIDE;
-
- void RescheduleWatch();
- void RestartWatch();
-
- // Called on the worker thread, executes DoRead and notifies the origin
- // thread.
- void DoReadJob();
-
- // Called on the the origin thread after DoRead completes.
- void OnReadJobFinished();
-
- // Message loop for the thread on which watcher is used (of TYPE_IO).
- scoped_refptr<base::MessageLoopProxy> message_loop_;
- FilePath path_;
- scoped_ptr<FilePathWatcherFactory> factory_;
- scoped_ptr<FilePathWatcherShim> watcher_;
- // True after DoRead before OnResultsAvailable.
- bool reading_;
- // True after OnFilePathChanged fires while |reading_| is true.
- bool read_pending_;
- bool cancelled_;
-
- DISALLOW_COPY_AND_ASSIGN(WatchingFileReader);
-};
-
-} // namespace net
-
-#endif // NET_DNS_WATCHING_FILE_READER_H_
-
diff --git a/net/dns/watching_file_reader_unittest.cc b/net/dns/watching_file_reader_unittest.cc
deleted file mode 100644
index 24021f0..0000000
--- a/net/dns/watching_file_reader_unittest.cc
+++ /dev/null
@@ -1,289 +0,0 @@
-// 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/dns/watching_file_reader.h"
-
-#include "base/message_loop.h"
-#include "base/synchronization/lock.h"
-#include "base/synchronization/waitable_event.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-
-namespace {
-
-class WatchingFileReaderTest : public testing::Test {
- public:
-
- // The class under test
- class TestWatchingFileReader : public WatchingFileReader {
- public:
- explicit TestWatchingFileReader(WatchingFileReaderTest* t)
- : test_(t) {}
- virtual void DoRead() OVERRIDE {
- ASSERT_TRUE(test_);
- test_->OnRead();
- }
- virtual void OnReadFinished() OVERRIDE {
- ASSERT_TRUE(test_);
- test_->OnReadFinished();
- }
- void TestCancel() {
- // Could execute concurrently with DoRead()
- test_ = NULL;
- }
- private:
- virtual ~TestWatchingFileReader() {}
- WatchingFileReaderTest* test_;
- base::Lock lock_;
- };
-
- // Mocks
-
- // WatcherDelegate owns the FilePathWatcherFactory it gets, so use simple
- // proxies to call the test fixture.
-
- class MockFilePathWatcherShim
- : public FilePathWatcherShim {
- public:
- typedef base::files::FilePathWatcher::Delegate Delegate;
-
- explicit MockFilePathWatcherShim(WatchingFileReaderTest* t) : test_(t) {}
- virtual ~MockFilePathWatcherShim() {
- test_->OnShimDestroyed(this);
- }
-
- // Enforce one-Watch-per-lifetime as the original FilePathWatcher
- virtual bool Watch(const FilePath& path,
- Delegate* delegate) OVERRIDE {
- EXPECT_TRUE(path_.empty()) << "Only one-Watch-per-lifetime allowed.";
- EXPECT_TRUE(!delegate_.get());
- path_ = path;
- delegate_ = delegate;
- return test_->OnWatch();
- }
-
- void PathChanged() {
- delegate_->OnFilePathChanged(path_);
- }
-
- void PathError() {
- delegate_->OnFilePathError(path_);
- }
-
- private:
- FilePath path_;
- scoped_refptr<Delegate> delegate_;
- WatchingFileReaderTest* test_;
- };
-
- class MockFilePathWatcherFactory
- : public FilePathWatcherFactory {
- public:
- explicit MockFilePathWatcherFactory(WatchingFileReaderTest* t)
- : test(t) {}
- virtual FilePathWatcherShim*
- CreateFilePathWatcher() OVERRIDE {
- return test->CreateFilePathWatcher();
- }
- WatchingFileReaderTest* test;
- };
-
- // Helpers for mocks.
-
- FilePathWatcherShim* CreateFilePathWatcher() {
- watcher_shim_ = new MockFilePathWatcherShim(this);
- return watcher_shim_;
- }
-
- void OnShimDestroyed(MockFilePathWatcherShim* destroyed_shim) {
- // Precaution to avoid segfault.
- if (watcher_shim_ == destroyed_shim)
- watcher_shim_ = NULL;
- }
-
- // On each event, post QuitTask to allow use of MessageLoop::Run() to
- // synchronize the threads.
-
- bool OnWatch() {
- EXPECT_TRUE(message_loop_ == MessageLoop::current());
- BreakNow("OnWatch");
- return !fail_on_watch_;
- }
-
- void OnRead() {
- { // Check that OnRead is executed serially.
- base::AutoLock lock(read_lock_);
- EXPECT_FALSE(read_running_) << "DoRead is not called serially!";
- read_running_ = true;
- }
- BreakNow("OnRead");
- read_allowed_.Wait();
- // Calling from WorkerPool, but protected by read_allowed_/read_called_.
- output_value_ = input_value_;
-
- { // This lock might be destroyed after read_called_ is signalled.
- base::AutoLock lock(read_lock_);
- read_running_ = false;
- }
- read_called_.Signal();
- }
-
- void OnReadFinished() {
- EXPECT_TRUE(message_loop_ == MessageLoop::current());
- EXPECT_EQ(output_value_, input_value_);
- BreakNow("OnReadFinished");
- }
-
- protected:
- friend class BreakTask;
- class BreakTask : public Task {
- public:
- BreakTask(WatchingFileReaderTest* test, std::string breakpoint)
- : test_(test), breakpoint_(breakpoint) {}
- virtual ~BreakTask() {}
- virtual void Run() OVERRIDE {
- test_->breakpoint_ = breakpoint_;
- MessageLoop::current()->QuitNow();
- }
- private:
- WatchingFileReaderTest* test_;
- std::string breakpoint_;
- };
-
- void BreakNow(std::string b) {
- message_loop_->PostTask(FROM_HERE, new BreakTask(this, b));
- }
-
- void RunUntilBreak(std::string b) {
- message_loop_->Run();
- ASSERT_EQ(breakpoint_, b);
- }
-
- WatchingFileReaderTest()
- : watcher_shim_(NULL),
- fail_on_watch_(false),
- input_value_(0),
- output_value_(-1),
- read_allowed_(false, false),
- read_called_(false, false),
- read_running_(false) {
- }
-
- // Helpers for tests.
-
- // Lets OnRead run and waits for it to complete. Can only return if OnRead is
- // executed on a concurrent thread.
- void WaitForRead() {
- RunUntilBreak("OnRead");
- read_allowed_.Signal();
- read_called_.Wait();
- }
-
- // test::Test methods
- virtual void SetUp() OVERRIDE {
- message_loop_ = MessageLoop::current();
- watcher_ = new TestWatchingFileReader(this);
- watcher_->set_watcher_factory(new MockFilePathWatcherFactory(this));
- }
-
- virtual void TearDown() OVERRIDE {
- // Cancel the watcher to catch if it makes a late DoRead call.
- watcher_->TestCancel();
- watcher_->Cancel();
- // Check if OnRead is stalled.
- EXPECT_FALSE(read_running_) << "OnRead should be done by TearDown";
- // Release it for cleanliness.
- if (read_running_) {
- WaitForRead();
- }
- }
-
- MockFilePathWatcherShim* watcher_shim_;
-
- bool fail_on_watch_;
-
- // Input value read on WorkerPool.
- int input_value_;
- // Output value written on WorkerPool.
- int output_value_;
-
- // read is called on WorkerPool so we need to synchronize with it.
- base::WaitableEvent read_allowed_;
- base::WaitableEvent read_called_;
-
- // Protected by read_lock_. Used to verify that read calls are serialized.
- bool read_running_;
- base::Lock read_lock_;
-
- // Loop for this thread.
- MessageLoop* message_loop_;
-
- // WatcherDelegate under test.
- scoped_refptr<TestWatchingFileReader> watcher_;
-
- std::string breakpoint_;
-};
-
-TEST_F(WatchingFileReaderTest, FilePathWatcherFailures) {
- fail_on_watch_ = true;
- watcher_->StartWatch(FilePath(FILE_PATH_LITERAL("some_file_name")));
- RunUntilBreak("OnWatch");
-
- fail_on_watch_ = false;
- RunUntilBreak("OnWatch"); // Due to backoff this will take 100ms.
- WaitForRead();
- RunUntilBreak("OnReadFinished");
-
- ASSERT_TRUE(watcher_shim_);
- watcher_shim_->PathError();
- RunUntilBreak("OnWatch");
- WaitForRead();
- RunUntilBreak("OnReadFinished");
-
- message_loop_->AssertIdle();
-}
-
-TEST_F(WatchingFileReaderTest, ExecuteAndSerializeReads) {
- watcher_->StartWatch(FilePath(FILE_PATH_LITERAL("some_file_name")));
- RunUntilBreak("OnWatch");
- WaitForRead();
- RunUntilBreak("OnReadFinished");
-
- message_loop_->AssertIdle();
-
- ++input_value_;
- ASSERT_TRUE(watcher_shim_);
- watcher_shim_->PathChanged();
- WaitForRead();
- RunUntilBreak("OnReadFinished");
-
- message_loop_->AssertIdle();
-
- ++input_value_;
- ASSERT_TRUE(watcher_shim_);
- watcher_shim_->PathChanged();
- WaitForRead();
- RunUntilBreak("OnReadFinished");
-
- message_loop_->AssertIdle();
-
- // Schedule two calls. OnRead checks if it is called serially.
- ++input_value_;
- ASSERT_TRUE(watcher_shim_);
- watcher_shim_->PathChanged();
- // read is blocked, so this will have to induce read_pending
- watcher_shim_->PathChanged();
- WaitForRead();
- WaitForRead();
- RunUntilBreak("OnReadFinished");
-
- // No more tasks should remain.
- message_loop_->AssertIdle();
-}
-
-} // namespace
-
-} // namespace net
-
diff --git a/net/net.gyp b/net/net.gyp
index a551c5e..73b6d62 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -303,16 +303,12 @@
'dns/dns_config_service.h',
'dns/dns_config_service_posix.cc',
'dns/dns_config_service_posix.h',
- 'dns/dns_hosts.cc',
- 'dns/dns_hosts.h',
'dns/dns_query.cc',
'dns/dns_query.h',
'dns/dns_response.cc',
'dns/dns_response.h',
'dns/dns_transaction.cc',
'dns/dns_transaction.h',
- 'dns/watching_file_reader.cc',
- 'dns/watching_file_reader.h',
'ftp/ftp_auth_cache.cc',
'ftp/ftp_auth_cache.h',
'ftp/ftp_ctrl_response_buffer.cc',
@@ -972,12 +968,9 @@
'disk_cache/storage_block_unittest.cc',
'dns/async_host_resolver_unittest.cc',
'dns/dns_config_service_posix_unittest.cc',
- 'dns/dns_config_service_unittest.cc',
- 'dns/dns_hosts_unittest.cc',
'dns/dns_query_unittest.cc',
'dns/dns_response_unittest.cc',
'dns/dns_transaction_unittest.cc',
- 'dns/watching_file_reader_unittest.cc',
'ftp/ftp_auth_cache_unittest.cc',
'ftp/ftp_ctrl_response_buffer_unittest.cc',
'ftp/ftp_directory_listing_parser_ls_unittest.cc',