OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/base/host_resolver.h" | 5 #include "net/base/host_resolver_impl.h" |
6 | 6 |
7 #if defined(OS_WIN) | 7 #if defined(OS_WIN) |
8 #include <ws2tcpip.h> | 8 #include <ws2tcpip.h> |
9 #include <wspiapi.h> // Needed for Win2k compat. | 9 #include <wspiapi.h> // Needed for Win2k compat. |
10 #elif defined(OS_POSIX) | 10 #elif defined(OS_POSIX) |
11 #include <netdb.h> | 11 #include <netdb.h> |
12 #include <sys/socket.h> | 12 #include <sys/socket.h> |
13 #endif | 13 #endif |
14 #if defined(OS_LINUX) | 14 #if defined(OS_LINUX) |
15 #include <resolv.h> | 15 #include <resolv.h> |
16 #endif | 16 #endif |
17 | 17 |
18 #include "base/compiler_specific.h" | 18 #include "base/compiler_specific.h" |
19 #include "base/message_loop.h" | 19 #include "base/message_loop.h" |
20 #include "base/stl_util-inl.h" | 20 #include "base/stl_util-inl.h" |
21 #include "base/string_util.h" | 21 #include "base/string_util.h" |
22 #include "base/time.h" | 22 #include "base/time.h" |
23 #include "base/worker_pool.h" | 23 #include "base/worker_pool.h" |
24 #include "net/base/address_list.h" | 24 #include "net/base/address_list.h" |
| 25 #include "net/base/host_resolver_proc.h" |
25 #include "net/base/net_errors.h" | 26 #include "net/base/net_errors.h" |
26 | 27 |
27 #if defined(OS_LINUX) | |
28 #include "base/singleton.h" | |
29 #include "base/thread_local_storage.h" | |
30 #endif | |
31 | |
32 #if defined(OS_WIN) | 28 #if defined(OS_WIN) |
33 #include "net/base/winsock_init.h" | 29 #include "net/base/winsock_init.h" |
34 #endif | 30 #endif |
35 | 31 |
36 namespace net { | 32 namespace net { |
37 | 33 |
38 //----------------------------------------------------------------------------- | 34 HostResolver* CreateSystemHostResolver() { |
39 | 35 static const size_t kMaxHostCacheEntries = 100; |
40 static HostMapper* host_mapper; | 36 static const size_t kHostCacheExpirationMs = 60000; // 1 minute. |
41 | 37 return new HostResolverImpl( |
42 std::string HostMapper::MapUsingPrevious(const std::string& host) { | 38 NULL, kMaxHostCacheEntries, kHostCacheExpirationMs); |
43 return previous_mapper_.get() ? previous_mapper_->Map(host) : host; | |
44 } | 39 } |
45 | 40 |
46 HostMapper* SetHostMapper(HostMapper* value) { | 41 static int ResolveAddrInfo(HostResolverProc* resolver_proc, |
47 std::swap(host_mapper, value); | 42 const std::string& host, AddressList* out) { |
48 return value; | 43 if (resolver_proc) { |
49 } | 44 // Use the custom procedure. |
50 | 45 return resolver_proc->Resolve(host, out); |
51 #if defined(OS_LINUX) | |
52 // On Linux changes to /etc/resolv.conf can go unnoticed thus resulting in | |
53 // DNS queries failing either because nameservers are unknown on startup | |
54 // or because nameserver info has changed as a result of e.g. connecting to | |
55 // a new network. Some distributions patch glibc to stat /etc/resolv.conf | |
56 // to try to automatically detect such changes but these patches are not | |
57 // universal and even patched systems such as Jaunty appear to need calls | |
58 // to res_ninit to reload the nameserver information in different threads. | |
59 // | |
60 // We adopt the Mozilla solution here which is to call res_ninit when | |
61 // lookups fail and to rate limit the reloading to once per second per | |
62 // thread. | |
63 | |
64 // Keep a timer per calling thread to rate limit the calling of res_ninit. | |
65 class DnsReloadTimer { | |
66 public: | |
67 DnsReloadTimer() { | |
68 tls_index_.Initialize(SlotReturnFunction); | |
69 } | |
70 | |
71 ~DnsReloadTimer() { } | |
72 | |
73 // Check if the timer for the calling thread has expired. When no | |
74 // timer exists for the calling thread, create one. | |
75 bool Expired() { | |
76 const base::TimeDelta kRetryTime = base::TimeDelta::FromSeconds(1); | |
77 base::TimeTicks now = base::TimeTicks::Now(); | |
78 base::TimeTicks* timer_ptr = | |
79 static_cast<base::TimeTicks*>(tls_index_.Get()); | |
80 | |
81 if (!timer_ptr) { | |
82 timer_ptr = new base::TimeTicks(); | |
83 *timer_ptr = base::TimeTicks::Now(); | |
84 tls_index_.Set(timer_ptr); | |
85 // Return true to reload dns info on the first call for each thread. | |
86 return true; | |
87 } else if (now - *timer_ptr > kRetryTime) { | |
88 *timer_ptr = now; | |
89 return true; | |
90 } else { | |
91 return false; | |
92 } | |
93 } | |
94 | |
95 // Free the allocated timer. | |
96 static void SlotReturnFunction(void* data) { | |
97 base::TimeTicks* tls_data = static_cast<base::TimeTicks*>(data); | |
98 delete tls_data; | |
99 } | |
100 | |
101 private: | |
102 // We use thread local storage to identify which base::TimeTicks to | |
103 // interact with. | |
104 static ThreadLocalStorage::Slot tls_index_ ; | |
105 | |
106 DISALLOW_COPY_AND_ASSIGN(DnsReloadTimer); | |
107 }; | |
108 | |
109 // A TLS slot to the TimeTicks for the current thread. | |
110 // static | |
111 ThreadLocalStorage::Slot DnsReloadTimer::tls_index_(base::LINKER_INITIALIZED); | |
112 | |
113 #endif // defined(OS_LINUX) | |
114 | |
115 static int HostResolverProc(const std::string& host, struct addrinfo** out) { | |
116 struct addrinfo hints = {0}; | |
117 hints.ai_family = AF_UNSPEC; | |
118 | |
119 #if defined(OS_WIN) | |
120 // DO NOT USE AI_ADDRCONFIG ON WINDOWS. | |
121 // | |
122 // The following comment in <winsock2.h> is the best documentation I found | |
123 // on AI_ADDRCONFIG for Windows: | |
124 // Flags used in "hints" argument to getaddrinfo() | |
125 // - AI_ADDRCONFIG is supported starting with Vista | |
126 // - default is AI_ADDRCONFIG ON whether the flag is set or not | |
127 // because the performance penalty in not having ADDRCONFIG in | |
128 // the multi-protocol stack environment is severe; | |
129 // this defaulting may be disabled by specifying the AI_ALL flag, | |
130 // in that case AI_ADDRCONFIG must be EXPLICITLY specified to | |
131 // enable ADDRCONFIG behavior | |
132 // | |
133 // Not only is AI_ADDRCONFIG unnecessary, but it can be harmful. If the | |
134 // computer is not connected to a network, AI_ADDRCONFIG causes getaddrinfo | |
135 // to fail with WSANO_DATA (11004) for "localhost", probably because of the | |
136 // following note on AI_ADDRCONFIG in the MSDN getaddrinfo page: | |
137 // The IPv4 or IPv6 loopback address is not considered a valid global | |
138 // address. | |
139 // See http://crbug.com/5234. | |
140 hints.ai_flags = 0; | |
141 #else | |
142 hints.ai_flags = AI_ADDRCONFIG; | |
143 #endif | |
144 | |
145 // Restrict result set to only this socket type to avoid duplicates. | |
146 hints.ai_socktype = SOCK_STREAM; | |
147 | |
148 int err = getaddrinfo(host.c_str(), NULL, &hints, out); | |
149 #if defined(OS_LINUX) | |
150 net::DnsReloadTimer* dns_timer = Singleton<net::DnsReloadTimer>::get(); | |
151 // If we fail, re-initialise the resolver just in case there have been any | |
152 // changes to /etc/resolv.conf and retry. See http://crbug.com/11380 for info. | |
153 if (err && dns_timer->Expired()) { | |
154 res_nclose(&_res); | |
155 if (!res_ninit(&_res)) | |
156 err = getaddrinfo(host.c_str(), NULL, &hints, out); | |
157 } | |
158 #endif | |
159 | |
160 return err ? ERR_NAME_NOT_RESOLVED : OK; | |
161 } | |
162 | |
163 static int ResolveAddrInfo(HostMapper* mapper, const std::string& host, | |
164 struct addrinfo** out) { | |
165 if (mapper) { | |
166 std::string mapped_host = mapper->Map(host); | |
167 | |
168 if (mapped_host.empty()) | |
169 return ERR_NAME_NOT_RESOLVED; | |
170 | |
171 return HostResolverProc(mapped_host, out); | |
172 } else { | 46 } else { |
173 return HostResolverProc(host, out); | 47 // Use the system procedure (getaddrinfo). |
| 48 return SystemHostResolverProc(host, out); |
174 } | 49 } |
175 } | 50 } |
176 | 51 |
177 //----------------------------------------------------------------------------- | 52 //----------------------------------------------------------------------------- |
178 | 53 |
179 class HostResolver::Request { | 54 class HostResolverImpl::Request { |
180 public: | 55 public: |
181 Request(int id, const RequestInfo& info, CompletionCallback* callback, | 56 Request(int id, const RequestInfo& info, CompletionCallback* callback, |
182 AddressList* addresses) | 57 AddressList* addresses) |
183 : id_(id), info_(info), job_(NULL), callback_(callback), | 58 : id_(id), info_(info), job_(NULL), callback_(callback), |
184 addresses_(addresses) {} | 59 addresses_(addresses) {} |
185 | 60 |
186 // Mark the request as cancelled. | 61 // Mark the request as cancelled. |
187 void MarkAsCancelled() { | 62 void MarkAsCancelled() { |
188 job_ = NULL; | 63 job_ = NULL; |
189 callback_ = NULL; | 64 callback_ = NULL; |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
238 // The address list to save result into. | 113 // The address list to save result into. |
239 AddressList* addresses_; | 114 AddressList* addresses_; |
240 | 115 |
241 DISALLOW_COPY_AND_ASSIGN(Request); | 116 DISALLOW_COPY_AND_ASSIGN(Request); |
242 }; | 117 }; |
243 | 118 |
244 //----------------------------------------------------------------------------- | 119 //----------------------------------------------------------------------------- |
245 | 120 |
246 // This class represents a request to the worker pool for a "getaddrinfo()" | 121 // This class represents a request to the worker pool for a "getaddrinfo()" |
247 // call. | 122 // call. |
248 class HostResolver::Job : public base::RefCountedThreadSafe<HostResolver::Job> { | 123 class HostResolverImpl::Job |
| 124 : public base::RefCountedThreadSafe<HostResolverImpl::Job> { |
249 public: | 125 public: |
250 Job(HostResolver* resolver, const std::string& host) | 126 Job(HostResolverImpl* resolver, const std::string& host) |
251 : host_(host), | 127 : host_(host), |
252 resolver_(resolver), | 128 resolver_(resolver), |
253 origin_loop_(MessageLoop::current()), | 129 origin_loop_(MessageLoop::current()), |
254 host_mapper_(host_mapper), | 130 resolver_proc_(resolver->effective_resolver_proc()), |
255 error_(OK), | 131 error_(OK) { |
256 results_(NULL) { | |
257 } | 132 } |
258 | 133 |
259 ~Job() { | 134 ~Job() { |
260 if (results_) | |
261 freeaddrinfo(results_); | |
262 | |
263 // Free the requests attached to this job. | 135 // Free the requests attached to this job. |
264 STLDeleteElements(&requests_); | 136 STLDeleteElements(&requests_); |
265 } | 137 } |
266 | 138 |
267 // Attaches a request to this job. The job takes ownership of |req| and will | 139 // Attaches a request to this job. The job takes ownership of |req| and will |
268 // take care to delete it. | 140 // take care to delete it. |
269 void AddRequest(HostResolver::Request* req) { | 141 void AddRequest(Request* req) { |
270 req->set_job(this); | 142 req->set_job(this); |
271 requests_.push_back(req); | 143 requests_.push_back(req); |
272 } | 144 } |
273 | 145 |
274 // Called from origin loop. | 146 // Called from origin loop. |
275 void Start() { | 147 void Start() { |
276 // Dispatch the job to a worker thread. | 148 // Dispatch the job to a worker thread. |
277 if (!WorkerPool::PostTask(FROM_HERE, | 149 if (!WorkerPool::PostTask(FROM_HERE, |
278 NewRunnableMethod(this, &Job::DoLookup), true)) { | 150 NewRunnableMethod(this, &Job::DoLookup), true)) { |
279 NOTREACHED(); | 151 NOTREACHED(); |
(...skipping 14 matching lines...) Expand all Loading... |
294 | 166 |
295 // Mark the job as cancelled, so when worker thread completes it will | 167 // Mark the job as cancelled, so when worker thread completes it will |
296 // not try to post completion to origin loop. | 168 // not try to post completion to origin loop. |
297 { | 169 { |
298 AutoLock locked(origin_loop_lock_); | 170 AutoLock locked(origin_loop_lock_); |
299 origin_loop_ = NULL; | 171 origin_loop_ = NULL; |
300 } | 172 } |
301 | 173 |
302 // We don't have to do anything further to actually cancel the requests | 174 // We don't have to do anything further to actually cancel the requests |
303 // that were attached to this job (since they are unreachable now). | 175 // that were attached to this job (since they are unreachable now). |
304 // But we will call HostResolver::CancelRequest(Request*) on each one | 176 // But we will call HostResolverImpl::CancelRequest(Request*) on each one |
305 // in order to notify any observers. | 177 // in order to notify any observers. |
306 for (RequestsList::const_iterator it = requests_.begin(); | 178 for (RequestsList::const_iterator it = requests_.begin(); |
307 it != requests_.end(); ++it) { | 179 it != requests_.end(); ++it) { |
308 HostResolver::Request* req = *it; | 180 HostResolverImpl::Request* req = *it; |
309 if (!req->was_cancelled()) | 181 if (!req->was_cancelled()) |
310 resolver->CancelRequest(req); | 182 resolver->CancelRequest(req); |
311 } | 183 } |
312 } | 184 } |
313 | 185 |
314 // Called from origin thread. | 186 // Called from origin thread. |
315 bool was_cancelled() const { | 187 bool was_cancelled() const { |
316 return resolver_ == NULL; | 188 return resolver_ == NULL; |
317 } | 189 } |
318 | 190 |
319 // Called from origin thread. | 191 // Called from origin thread. |
320 const std::string& host() const { | 192 const std::string& host() const { |
321 return host_; | 193 return host_; |
322 } | 194 } |
323 | 195 |
324 // Called from origin thread. | 196 // Called from origin thread. |
325 const RequestsList& requests() const { | 197 const RequestsList& requests() const { |
326 return requests_; | 198 return requests_; |
327 } | 199 } |
328 | 200 |
329 private: | 201 private: |
330 void DoLookup() { | 202 void DoLookup() { |
331 // Running on the worker thread | 203 // Running on the worker thread |
332 error_ = ResolveAddrInfo(host_mapper_, host_, &results_); | 204 error_ = ResolveAddrInfo(resolver_proc_, host_, &results_); |
333 | 205 |
334 Task* reply = NewRunnableMethod(this, &Job::OnLookupComplete); | 206 Task* reply = NewRunnableMethod(this, &Job::OnLookupComplete); |
335 | 207 |
336 // The origin loop could go away while we are trying to post to it, so we | 208 // The origin loop could go away while we are trying to post to it, so we |
337 // need to call its PostTask method inside a lock. See ~HostResolver. | 209 // need to call its PostTask method inside a lock. See ~HostResolver. |
338 { | 210 { |
339 AutoLock locked(origin_loop_lock_); | 211 AutoLock locked(origin_loop_lock_); |
340 if (origin_loop_) { | 212 if (origin_loop_) { |
341 origin_loop_->PostTask(FROM_HERE, reply); | 213 origin_loop_->PostTask(FROM_HERE, reply); |
342 reply = NULL; | 214 reply = NULL; |
343 } | 215 } |
344 } | 216 } |
345 | 217 |
346 // Does nothing if it got posted. | 218 // Does nothing if it got posted. |
347 delete reply; | 219 delete reply; |
348 } | 220 } |
349 | 221 |
350 // Callback for when DoLookup() completes (runs on origin thread). | 222 // Callback for when DoLookup() completes (runs on origin thread). |
351 void OnLookupComplete() { | 223 void OnLookupComplete() { |
352 // Should be running on origin loop. | 224 // Should be running on origin loop. |
353 // TODO(eroman): this is being hit by URLRequestTest.CancelTest*, | 225 // TODO(eroman): this is being hit by URLRequestTest.CancelTest*, |
354 // because MessageLoop::current() == NULL. | 226 // because MessageLoop::current() == NULL. |
355 //DCHECK_EQ(origin_loop_, MessageLoop::current()); | 227 //DCHECK_EQ(origin_loop_, MessageLoop::current()); |
356 DCHECK(error_ || results_); | 228 DCHECK(error_ || results_.head()); |
357 | 229 |
358 if (was_cancelled()) | 230 if (was_cancelled()) |
359 return; | 231 return; |
360 | 232 |
361 DCHECK(!requests_.empty()); | 233 DCHECK(!requests_.empty()); |
362 | 234 |
363 // Adopt the address list using the port number of the first request. | 235 // Use the port number of the first request. |
364 AddressList addrlist; | 236 if (error_ == OK) |
365 if (error_ == OK) { | 237 results_.SetPort(requests_[0]->port()); |
366 addrlist.Adopt(results_); | |
367 addrlist.SetPort(requests_[0]->port()); | |
368 results_ = NULL; | |
369 } | |
370 | 238 |
371 resolver_->OnJobComplete(this, error_, addrlist); | 239 resolver_->OnJobComplete(this, error_, results_); |
372 } | 240 } |
373 | 241 |
374 // Set on the origin thread, read on the worker thread. | 242 // Set on the origin thread, read on the worker thread. |
375 std::string host_; | 243 std::string host_; |
376 | 244 |
377 // Only used on the origin thread (where Resolve was called). | 245 // Only used on the origin thread (where Resolve was called). |
378 HostResolver* resolver_; | 246 HostResolverImpl* resolver_; |
379 RequestsList requests_; // The requests waiting on this job. | 247 RequestsList requests_; // The requests waiting on this job. |
380 | 248 |
381 // Used to post ourselves onto the origin thread. | 249 // Used to post ourselves onto the origin thread. |
382 Lock origin_loop_lock_; | 250 Lock origin_loop_lock_; |
383 MessageLoop* origin_loop_; | 251 MessageLoop* origin_loop_; |
384 | 252 |
385 // Hold an owning reference to the host mapper that we are going to use. | 253 // Hold an owning reference to the HostResolverProc that we are going to use. |
386 // This may not be the current host mapper by the time we call | 254 // This may not be the current resolver procedure by the time we call |
387 // ResolveAddrInfo, but that's OK... we'll use it anyways, and the owning | 255 // ResolveAddrInfo, but that's OK... we'll use it anyways, and the owning |
388 // reference ensures that it remains valid until we are done. | 256 // reference ensures that it remains valid until we are done. |
389 scoped_refptr<HostMapper> host_mapper_; | 257 scoped_refptr<HostResolverProc> resolver_proc_; |
390 | 258 |
391 // Assigned on the worker thread, read on the origin thread. | 259 // Assigned on the worker thread, read on the origin thread. |
392 int error_; | 260 int error_; |
393 struct addrinfo* results_; | 261 AddressList results_; |
394 | 262 |
395 DISALLOW_COPY_AND_ASSIGN(Job); | 263 DISALLOW_COPY_AND_ASSIGN(Job); |
396 }; | 264 }; |
397 | 265 |
398 //----------------------------------------------------------------------------- | 266 //----------------------------------------------------------------------------- |
399 | 267 |
400 HostResolver::HostResolver(int max_cache_entries, int cache_duration_ms) | 268 HostResolverImpl::HostResolverImpl(HostResolverProc* resolver_proc, |
| 269 int max_cache_entries, |
| 270 int cache_duration_ms) |
401 : cache_(max_cache_entries, cache_duration_ms), next_request_id_(0), | 271 : cache_(max_cache_entries, cache_duration_ms), next_request_id_(0), |
402 shutdown_(false) { | 272 resolver_proc_(resolver_proc), shutdown_(false) { |
403 #if defined(OS_WIN) | 273 #if defined(OS_WIN) |
404 EnsureWinsockInit(); | 274 EnsureWinsockInit(); |
405 #endif | 275 #endif |
406 } | 276 } |
407 | 277 |
408 HostResolver::~HostResolver() { | 278 HostResolverImpl::~HostResolverImpl() { |
409 // Cancel the outstanding jobs. Those jobs may contain several attached | 279 // Cancel the outstanding jobs. Those jobs may contain several attached |
410 // requests, which will also be cancelled. | 280 // requests, which will also be cancelled. |
411 for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it) | 281 for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it) |
412 it->second->Cancel(); | 282 it->second->Cancel(); |
413 | 283 |
414 // In case we are being deleted during the processing of a callback. | 284 // In case we are being deleted during the processing of a callback. |
415 if (cur_completing_job_) | 285 if (cur_completing_job_) |
416 cur_completing_job_->Cancel(); | 286 cur_completing_job_->Cancel(); |
417 } | 287 } |
418 | 288 |
419 // TODO(eroman): Don't create cache entries for hostnames which are simply IP | 289 // TODO(eroman): Don't create cache entries for hostnames which are simply IP |
420 // address literals. | 290 // address literals. |
421 int HostResolver::Resolve(const RequestInfo& info, | 291 int HostResolverImpl::Resolve(const RequestInfo& info, |
422 AddressList* addresses, | 292 AddressList* addresses, |
423 CompletionCallback* callback, | 293 CompletionCallback* callback, |
424 Request** out_req) { | 294 RequestHandle* out_req) { |
425 if (shutdown_) | 295 if (shutdown_) |
426 return ERR_UNEXPECTED; | 296 return ERR_UNEXPECTED; |
427 | 297 |
428 // Choose a unique ID number for observers to see. | 298 // Choose a unique ID number for observers to see. |
429 int request_id = next_request_id_++; | 299 int request_id = next_request_id_++; |
430 | 300 |
431 // Notify registered observers. | 301 // Notify registered observers. |
432 NotifyObserversStartRequest(request_id, info); | 302 NotifyObserversStartRequest(request_id, info); |
433 | 303 |
434 // If we have an unexpired cache entry, use it. | 304 // If we have an unexpired cache entry, use it. |
435 if (info.allow_cached_response()) { | 305 if (info.allow_cached_response()) { |
436 const HostCache::Entry* cache_entry = cache_.Lookup( | 306 const HostCache::Entry* cache_entry = cache_.Lookup( |
437 info.hostname(), base::TimeTicks::Now()); | 307 info.hostname(), base::TimeTicks::Now()); |
438 if (cache_entry) { | 308 if (cache_entry) { |
439 addresses->SetFrom(cache_entry->addrlist, info.port()); | 309 addresses->SetFrom(cache_entry->addrlist, info.port()); |
440 int error = OK; | 310 int error = cache_entry->error; |
441 | 311 |
442 // Notify registered observers. | 312 // Notify registered observers. |
443 NotifyObserversFinishRequest(request_id, info, error); | 313 NotifyObserversFinishRequest(request_id, info, error); |
444 | 314 |
445 return error; | 315 return error; |
446 } | 316 } |
447 } | 317 } |
448 | 318 |
449 // If no callback was specified, do a synchronous resolution. | 319 // If no callback was specified, do a synchronous resolution. |
450 if (!callback) { | 320 if (!callback) { |
451 struct addrinfo* results; | |
452 int error = ResolveAddrInfo(host_mapper, info.hostname(), &results); | |
453 | |
454 // Adopt the address list. | |
455 AddressList addrlist; | 321 AddressList addrlist; |
| 322 int error = ResolveAddrInfo( |
| 323 effective_resolver_proc(), info.hostname(), &addrlist); |
456 if (error == OK) { | 324 if (error == OK) { |
457 addrlist.Adopt(results); | |
458 addrlist.SetPort(info.port()); | 325 addrlist.SetPort(info.port()); |
459 *addresses = addrlist; | 326 *addresses = addrlist; |
460 } | 327 } |
461 | 328 |
462 // Write to cache. | 329 // Write to cache. |
463 cache_.Set(info.hostname(), error, addrlist, base::TimeTicks::Now()); | 330 cache_.Set(info.hostname(), error, addrlist, base::TimeTicks::Now()); |
464 | 331 |
465 // Notify registered observers. | 332 // Notify registered observers. |
466 NotifyObserversFinishRequest(request_id, info, error); | 333 NotifyObserversFinishRequest(request_id, info, error); |
467 | 334 |
468 return error; | 335 return error; |
469 } | 336 } |
470 | 337 |
471 // Create a handle for this request, and pass it back to the user if they | 338 // Create a handle for this request, and pass it back to the user if they |
472 // asked for it (out_req != NULL). | 339 // asked for it (out_req != NULL). |
473 Request* req = new Request(request_id, info, callback, addresses); | 340 Request* req = new Request(request_id, info, callback, addresses); |
474 if (out_req) | 341 if (out_req) |
475 *out_req = req; | 342 *out_req = reinterpret_cast<RequestHandle>(req); |
476 | 343 |
477 // Next we need to attach our request to a "job". This job is responsible for | 344 // Next we need to attach our request to a "job". This job is responsible for |
478 // calling "getaddrinfo(hostname)" on a worker thread. | 345 // calling "getaddrinfo(hostname)" on a worker thread. |
479 scoped_refptr<Job> job; | 346 scoped_refptr<Job> job; |
480 | 347 |
481 // If there is already an outstanding job to resolve |info.hostname()|, use | 348 // If there is already an outstanding job to resolve |info.hostname()|, use |
482 // it. This prevents starting concurrent resolves for the same hostname. | 349 // it. This prevents starting concurrent resolves for the same hostname. |
483 job = FindOutstandingJob(info.hostname()); | 350 job = FindOutstandingJob(info.hostname()); |
484 if (job) { | 351 if (job) { |
485 job->AddRequest(req); | 352 job->AddRequest(req); |
486 } else { | 353 } else { |
487 // Create a new job for this request. | 354 // Create a new job for this request. |
488 job = new Job(this, info.hostname()); | 355 job = new Job(this, info.hostname()); |
489 job->AddRequest(req); | 356 job->AddRequest(req); |
490 AddOutstandingJob(job); | 357 AddOutstandingJob(job); |
491 // TODO(eroman): Bound the total number of concurrent jobs. | 358 // TODO(eroman): Bound the total number of concurrent jobs. |
492 // http://crbug.com/9598 | 359 // http://crbug.com/9598 |
493 job->Start(); | 360 job->Start(); |
494 } | 361 } |
495 | 362 |
496 // Completion happens during OnJobComplete(Job*). | 363 // Completion happens during OnJobComplete(Job*). |
497 return ERR_IO_PENDING; | 364 return ERR_IO_PENDING; |
498 } | 365 } |
499 | 366 |
500 // See OnJobComplete(Job*) for why it is important not to clean out | 367 // See OnJobComplete(Job*) for why it is important not to clean out |
501 // cancelled requests from Job::requests_. | 368 // cancelled requests from Job::requests_. |
502 void HostResolver::CancelRequest(Request* req) { | 369 void HostResolverImpl::CancelRequest(RequestHandle req_handle) { |
| 370 Request* req = reinterpret_cast<Request*>(req_handle); |
503 DCHECK(req); | 371 DCHECK(req); |
504 DCHECK(req->job()); | 372 DCHECK(req->job()); |
505 // NULL out the fields of req, to mark it as cancelled. | 373 // NULL out the fields of req, to mark it as cancelled. |
506 req->MarkAsCancelled(); | 374 req->MarkAsCancelled(); |
507 NotifyObserversCancelRequest(req->id(), req->info()); | 375 NotifyObserversCancelRequest(req->id(), req->info()); |
508 } | 376 } |
509 | 377 |
510 void HostResolver::AddObserver(Observer* observer) { | 378 void HostResolverImpl::AddObserver(Observer* observer) { |
511 observers_.push_back(observer); | 379 observers_.push_back(observer); |
512 } | 380 } |
513 | 381 |
514 void HostResolver::RemoveObserver(Observer* observer) { | 382 void HostResolverImpl::RemoveObserver(Observer* observer) { |
515 ObserversList::iterator it = | 383 ObserversList::iterator it = |
516 std::find(observers_.begin(), observers_.end(), observer); | 384 std::find(observers_.begin(), observers_.end(), observer); |
517 | 385 |
518 // Observer must exist. | 386 // Observer must exist. |
519 DCHECK(it != observers_.end()); | 387 DCHECK(it != observers_.end()); |
520 | 388 |
521 observers_.erase(it); | 389 observers_.erase(it); |
522 } | 390 } |
523 | 391 |
524 void HostResolver::Shutdown() { | 392 void HostResolverImpl::Shutdown() { |
525 shutdown_ = true; | 393 shutdown_ = true; |
526 | 394 |
527 // Cancel the outstanding jobs. | 395 // Cancel the outstanding jobs. |
528 for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it) | 396 for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it) |
529 it->second->Cancel(); | 397 it->second->Cancel(); |
530 jobs_.clear(); | 398 jobs_.clear(); |
531 } | 399 } |
532 | 400 |
533 void HostResolver::AddOutstandingJob(Job* job) { | 401 void HostResolverImpl::AddOutstandingJob(Job* job) { |
534 scoped_refptr<Job>& found_job = jobs_[job->host()]; | 402 scoped_refptr<Job>& found_job = jobs_[job->host()]; |
535 DCHECK(!found_job); | 403 DCHECK(!found_job); |
536 found_job = job; | 404 found_job = job; |
537 } | 405 } |
538 | 406 |
539 HostResolver::Job* HostResolver::FindOutstandingJob( | 407 HostResolverImpl::Job* HostResolverImpl::FindOutstandingJob( |
540 const std::string& hostname) { | 408 const std::string& hostname) { |
541 JobMap::iterator it = jobs_.find(hostname); | 409 JobMap::iterator it = jobs_.find(hostname); |
542 if (it != jobs_.end()) | 410 if (it != jobs_.end()) |
543 return it->second; | 411 return it->second; |
544 return NULL; | 412 return NULL; |
545 } | 413 } |
546 | 414 |
547 void HostResolver::RemoveOutstandingJob(Job* job) { | 415 void HostResolverImpl::RemoveOutstandingJob(Job* job) { |
548 JobMap::iterator it = jobs_.find(job->host()); | 416 JobMap::iterator it = jobs_.find(job->host()); |
549 DCHECK(it != jobs_.end()); | 417 DCHECK(it != jobs_.end()); |
550 DCHECK_EQ(it->second.get(), job); | 418 DCHECK_EQ(it->second.get(), job); |
551 jobs_.erase(it); | 419 jobs_.erase(it); |
552 } | 420 } |
553 | 421 |
554 void HostResolver::OnJobComplete(Job* job, | 422 void HostResolverImpl::OnJobComplete(Job* job, |
555 int error, | 423 int error, |
556 const AddressList& addrlist) { | 424 const AddressList& addrlist) { |
557 RemoveOutstandingJob(job); | 425 RemoveOutstandingJob(job); |
558 | 426 |
559 // Write result to the cache. | 427 // Write result to the cache. |
560 cache_.Set(job->host(), error, addrlist, base::TimeTicks::Now()); | 428 cache_.Set(job->host(), error, addrlist, base::TimeTicks::Now()); |
561 | 429 |
562 // Make a note that we are executing within OnJobComplete() in case the | 430 // Make a note that we are executing within OnJobComplete() in case the |
563 // HostResolver is deleted by a callback invocation. | 431 // HostResolver is deleted by a callback invocation. |
564 DCHECK(!cur_completing_job_); | 432 DCHECK(!cur_completing_job_); |
(...skipping 14 matching lines...) Expand all Loading... |
579 // Check if the job was cancelled as a result of running the callback. | 447 // Check if the job was cancelled as a result of running the callback. |
580 // (Meaning that |this| was deleted). | 448 // (Meaning that |this| was deleted). |
581 if (job->was_cancelled()) | 449 if (job->was_cancelled()) |
582 return; | 450 return; |
583 } | 451 } |
584 } | 452 } |
585 | 453 |
586 cur_completing_job_ = NULL; | 454 cur_completing_job_ = NULL; |
587 } | 455 } |
588 | 456 |
589 void HostResolver::NotifyObserversStartRequest(int request_id, | 457 void HostResolverImpl::NotifyObserversStartRequest(int request_id, |
590 const RequestInfo& info) { | 458 const RequestInfo& info) { |
591 for (ObserversList::iterator it = observers_.begin(); | 459 for (ObserversList::iterator it = observers_.begin(); |
592 it != observers_.end(); ++it) { | 460 it != observers_.end(); ++it) { |
593 (*it)->OnStartResolution(request_id, info); | 461 (*it)->OnStartResolution(request_id, info); |
594 } | 462 } |
595 } | 463 } |
596 | 464 |
597 void HostResolver::NotifyObserversFinishRequest(int request_id, | 465 void HostResolverImpl::NotifyObserversFinishRequest(int request_id, |
598 const RequestInfo& info, | 466 const RequestInfo& info, |
599 int error) { | 467 int error) { |
600 bool was_resolved = error == OK; | 468 bool was_resolved = error == OK; |
601 for (ObserversList::iterator it = observers_.begin(); | 469 for (ObserversList::iterator it = observers_.begin(); |
602 it != observers_.end(); ++it) { | 470 it != observers_.end(); ++it) { |
603 (*it)->OnFinishResolutionWithStatus(request_id, was_resolved, info); | 471 (*it)->OnFinishResolutionWithStatus(request_id, was_resolved, info); |
604 } | 472 } |
605 } | 473 } |
606 | 474 |
607 void HostResolver::NotifyObserversCancelRequest(int request_id, | 475 void HostResolverImpl::NotifyObserversCancelRequest(int request_id, |
608 const RequestInfo& info) { | 476 const RequestInfo& info) { |
609 for (ObserversList::iterator it = observers_.begin(); | 477 for (ObserversList::iterator it = observers_.begin(); |
610 it != observers_.end(); ++it) { | 478 it != observers_.end(); ++it) { |
611 (*it)->OnCancelResolution(request_id, info); | 479 (*it)->OnCancelResolution(request_id, info); |
612 } | 480 } |
613 } | 481 } |
614 | 482 |
615 //----------------------------------------------------------------------------- | |
616 | |
617 SingleRequestHostResolver::SingleRequestHostResolver(HostResolver* resolver) | |
618 : resolver_(resolver), | |
619 cur_request_(NULL), | |
620 cur_request_callback_(NULL), | |
621 ALLOW_THIS_IN_INITIALIZER_LIST( | |
622 callback_(this, &SingleRequestHostResolver::OnResolveCompletion)) { | |
623 DCHECK(resolver_ != NULL); | |
624 } | |
625 | |
626 SingleRequestHostResolver::~SingleRequestHostResolver() { | |
627 if (cur_request_) { | |
628 resolver_->CancelRequest(cur_request_); | |
629 } | |
630 } | |
631 | |
632 int SingleRequestHostResolver::Resolve(const HostResolver::RequestInfo& info, | |
633 AddressList* addresses, | |
634 CompletionCallback* callback) { | |
635 DCHECK(!cur_request_ && !cur_request_callback_) << "resolver already in use"; | |
636 | |
637 HostResolver::Request* request = NULL; | |
638 | |
639 // We need to be notified of completion before |callback| is called, so that | |
640 // we can clear out |cur_request_*|. | |
641 CompletionCallback* transient_callback = callback ? &callback_ : NULL; | |
642 | |
643 int rv = resolver_->Resolve(info, addresses, transient_callback, &request); | |
644 | |
645 if (rv == ERR_IO_PENDING) { | |
646 // Cleared in OnResolveCompletion(). | |
647 cur_request_ = request; | |
648 cur_request_callback_ = callback; | |
649 } | |
650 | |
651 return rv; | |
652 } | |
653 | |
654 void SingleRequestHostResolver::OnResolveCompletion(int result) { | |
655 DCHECK(cur_request_ && cur_request_callback_); | |
656 | |
657 CompletionCallback* callback = cur_request_callback_; | |
658 | |
659 // Clear the outstanding request information. | |
660 cur_request_ = NULL; | |
661 cur_request_callback_ = NULL; | |
662 | |
663 // Call the user's original callback. | |
664 callback->Run(result); | |
665 } | |
666 | |
667 } // namespace net | 483 } // namespace net |
OLD | NEW |