blob: 702f2f05604a62d67e9c0a736a558d76aeb896e3 [file] [log] [blame]
[email protected]b59ff372009-07-15 22:04:321// 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
3// found in the LICENSE file.
4
5#include "net/base/host_resolver_impl.h"
6
7#if defined(OS_WIN)
8#include <ws2tcpip.h>
9#include <wspiapi.h> // Needed for Win2k compat.
10#elif defined(OS_POSIX)
11#include <netdb.h>
12#include <sys/socket.h>
13#endif
14#if defined(OS_LINUX)
15#include <resolv.h>
16#endif
17
18#include "base/compiler_specific.h"
[email protected]bf0b51c2009-08-01 23:46:4019#include "base/debug_util.h"
[email protected]b59ff372009-07-15 22:04:3220#include "base/message_loop.h"
21#include "base/stl_util-inl.h"
22#include "base/string_util.h"
23#include "base/time.h"
24#include "base/worker_pool.h"
25#include "net/base/address_list.h"
26#include "net/base/host_resolver_proc.h"
27#include "net/base/net_errors.h"
28
29#if defined(OS_WIN)
30#include "net/base/winsock_init.h"
31#endif
32
33namespace net {
34
35HostResolver* CreateSystemHostResolver() {
36 static const size_t kMaxHostCacheEntries = 100;
37 static const size_t kHostCacheExpirationMs = 60000; // 1 minute.
38 return new HostResolverImpl(
39 NULL, kMaxHostCacheEntries, kHostCacheExpirationMs);
40}
41
42static int ResolveAddrInfo(HostResolverProc* resolver_proc,
43 const std::string& host, AddressList* out) {
44 if (resolver_proc) {
45 // Use the custom procedure.
46 return resolver_proc->Resolve(host, out);
47 } else {
48 // Use the system procedure (getaddrinfo).
49 return SystemHostResolverProc(host, out);
50 }
51}
52
53//-----------------------------------------------------------------------------
54
55class HostResolverImpl::Request {
56 public:
57 Request(int id, const RequestInfo& info, CompletionCallback* callback,
58 AddressList* addresses)
59 : id_(id), info_(info), job_(NULL), callback_(callback),
60 addresses_(addresses) {}
61
62 // Mark the request as cancelled.
63 void MarkAsCancelled() {
64 job_ = NULL;
65 callback_ = NULL;
66 addresses_ = NULL;
67 }
68
69 bool was_cancelled() const {
70 return callback_ == NULL;
71 }
72
73 void set_job(Job* job) {
74 DCHECK(job != NULL);
75 // Identify which job the request is waiting on.
76 job_ = job;
77 }
78
79 void OnComplete(int error, const AddressList& addrlist) {
80 if (error == OK)
81 addresses_->SetFrom(addrlist, port());
82 callback_->Run(error);
83 }
84
85 int port() const {
86 return info_.port();
87 }
88
89 Job* job() const {
90 return job_;
91 }
92
93 int id() const {
94 return id_;
95 }
96
97 const RequestInfo& info() const {
98 return info_;
99 }
100
101 private:
102 // Unique ID for this request. Used by observers to identify requests.
103 int id_;
104
105 // The request info that started the request.
106 RequestInfo info_;
107
108 // The resolve job (running in worker pool) that this request is dependent on.
109 Job* job_;
110
111 // The user's callback to invoke when the request completes.
112 CompletionCallback* callback_;
113
114 // The address list to save result into.
115 AddressList* addresses_;
116
117 DISALLOW_COPY_AND_ASSIGN(Request);
118};
119
120//-----------------------------------------------------------------------------
121
122// This class represents a request to the worker pool for a "getaddrinfo()"
123// call.
124class HostResolverImpl::Job
125 : public base::RefCountedThreadSafe<HostResolverImpl::Job> {
126 public:
127 Job(HostResolverImpl* resolver, const std::string& host)
128 : host_(host),
129 resolver_(resolver),
130 origin_loop_(MessageLoop::current()),
131 resolver_proc_(resolver->effective_resolver_proc()),
132 error_(OK) {
133 }
134
135 ~Job() {
136 // Free the requests attached to this job.
137 STLDeleteElements(&requests_);
138 }
139
140 // Attaches a request to this job. The job takes ownership of |req| and will
141 // take care to delete it.
142 void AddRequest(Request* req) {
143 req->set_job(this);
144 requests_.push_back(req);
145 }
146
147 // Called from origin loop.
148 void Start() {
149 // Dispatch the job to a worker thread.
150 if (!WorkerPool::PostTask(FROM_HERE,
151 NewRunnableMethod(this, &Job::DoLookup), true)) {
152 NOTREACHED();
153
154 // Since we could be running within Resolve() right now, we can't just
155 // call OnLookupComplete(). Instead we must wait until Resolve() has
156 // returned (IO_PENDING).
157 error_ = ERR_UNEXPECTED;
158 MessageLoop::current()->PostTask(
159 FROM_HERE, NewRunnableMethod(this, &Job::OnLookupComplete));
160 }
161 }
162
163 // Cancels the current job. Callable from origin thread.
164 void Cancel() {
165 HostResolver* resolver = resolver_;
166 resolver_ = NULL;
167
168 // Mark the job as cancelled, so when worker thread completes it will
169 // not try to post completion to origin loop.
170 {
171 AutoLock locked(origin_loop_lock_);
172 origin_loop_ = NULL;
173 }
174
175 // We don't have to do anything further to actually cancel the requests
176 // that were attached to this job (since they are unreachable now).
177 // But we will call HostResolverImpl::CancelRequest(Request*) on each one
178 // in order to notify any observers.
179 for (RequestsList::const_iterator it = requests_.begin();
180 it != requests_.end(); ++it) {
181 HostResolverImpl::Request* req = *it;
182 if (!req->was_cancelled())
183 resolver->CancelRequest(req);
184 }
185 }
186
187 // Called from origin thread.
188 bool was_cancelled() const {
189 return resolver_ == NULL;
190 }
191
192 // Called from origin thread.
193 const std::string& host() const {
194 return host_;
195 }
196
197 // Called from origin thread.
198 const RequestsList& requests() const {
199 return requests_;
200 }
201
202 private:
203 void DoLookup() {
204 // Running on the worker thread
205 error_ = ResolveAddrInfo(resolver_proc_, host_, &results_);
206
207 Task* reply = NewRunnableMethod(this, &Job::OnLookupComplete);
208
209 // The origin loop could go away while we are trying to post to it, so we
210 // need to call its PostTask method inside a lock. See ~HostResolver.
211 {
212 AutoLock locked(origin_loop_lock_);
213 if (origin_loop_) {
214 origin_loop_->PostTask(FROM_HERE, reply);
215 reply = NULL;
216 }
217 }
218
219 // Does nothing if it got posted.
220 delete reply;
221 }
222
223 // Callback for when DoLookup() completes (runs on origin thread).
224 void OnLookupComplete() {
225 // Should be running on origin loop.
226 // TODO(eroman): this is being hit by URLRequestTest.CancelTest*,
227 // because MessageLoop::current() == NULL.
228 //DCHECK_EQ(origin_loop_, MessageLoop::current());
229 DCHECK(error_ || results_.head());
230
231 if (was_cancelled())
232 return;
233
234 DCHECK(!requests_.empty());
235
236 // Use the port number of the first request.
237 if (error_ == OK)
238 results_.SetPort(requests_[0]->port());
239
240 resolver_->OnJobComplete(this, error_, results_);
241 }
242
243 // Set on the origin thread, read on the worker thread.
244 std::string host_;
245
246 // Only used on the origin thread (where Resolve was called).
247 HostResolverImpl* resolver_;
248 RequestsList requests_; // The requests waiting on this job.
249
250 // Used to post ourselves onto the origin thread.
251 Lock origin_loop_lock_;
252 MessageLoop* origin_loop_;
253
254 // Hold an owning reference to the HostResolverProc that we are going to use.
255 // This may not be the current resolver procedure by the time we call
256 // ResolveAddrInfo, but that's OK... we'll use it anyways, and the owning
257 // reference ensures that it remains valid until we are done.
258 scoped_refptr<HostResolverProc> resolver_proc_;
259
260 // Assigned on the worker thread, read on the origin thread.
261 int error_;
262 AddressList results_;
263
264 DISALLOW_COPY_AND_ASSIGN(Job);
265};
266
267//-----------------------------------------------------------------------------
268
269HostResolverImpl::HostResolverImpl(HostResolverProc* resolver_proc,
270 int max_cache_entries,
271 int cache_duration_ms)
272 : cache_(max_cache_entries, cache_duration_ms), next_request_id_(0),
273 resolver_proc_(resolver_proc), shutdown_(false) {
274#if defined(OS_WIN)
275 EnsureWinsockInit();
276#endif
277}
278
279HostResolverImpl::~HostResolverImpl() {
280 // Cancel the outstanding jobs. Those jobs may contain several attached
281 // requests, which will also be cancelled.
282 for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it)
283 it->second->Cancel();
284
285 // In case we are being deleted during the processing of a callback.
286 if (cur_completing_job_)
287 cur_completing_job_->Cancel();
288}
289
290// TODO(eroman): Don't create cache entries for hostnames which are simply IP
291// address literals.
292int HostResolverImpl::Resolve(const RequestInfo& info,
293 AddressList* addresses,
294 CompletionCallback* callback,
295 RequestHandle* out_req) {
296 if (shutdown_)
297 return ERR_UNEXPECTED;
298
299 // Choose a unique ID number for observers to see.
300 int request_id = next_request_id_++;
301
302 // Notify registered observers.
303 NotifyObserversStartRequest(request_id, info);
304
305 // If we have an unexpired cache entry, use it.
306 if (info.allow_cached_response()) {
307 const HostCache::Entry* cache_entry = cache_.Lookup(
308 info.hostname(), base::TimeTicks::Now());
309 if (cache_entry) {
310 addresses->SetFrom(cache_entry->addrlist, info.port());
311 int error = cache_entry->error;
312
313 // Notify registered observers.
314 NotifyObserversFinishRequest(request_id, info, error);
315
316 return error;
317 }
318 }
319
320 // If no callback was specified, do a synchronous resolution.
321 if (!callback) {
322 AddressList addrlist;
323 int error = ResolveAddrInfo(
324 effective_resolver_proc(), info.hostname(), &addrlist);
325 if (error == OK) {
326 addrlist.SetPort(info.port());
327 *addresses = addrlist;
328 }
329
330 // Write to cache.
331 cache_.Set(info.hostname(), error, addrlist, base::TimeTicks::Now());
332
333 // Notify registered observers.
334 NotifyObserversFinishRequest(request_id, info, error);
335
336 return error;
337 }
338
339 // Create a handle for this request, and pass it back to the user if they
340 // asked for it (out_req != NULL).
341 Request* req = new Request(request_id, info, callback, addresses);
342 if (out_req)
343 *out_req = reinterpret_cast<RequestHandle>(req);
344
345 // Next we need to attach our request to a "job". This job is responsible for
346 // calling "getaddrinfo(hostname)" on a worker thread.
347 scoped_refptr<Job> job;
348
349 // If there is already an outstanding job to resolve |info.hostname()|, use
350 // it. This prevents starting concurrent resolves for the same hostname.
351 job = FindOutstandingJob(info.hostname());
352 if (job) {
353 job->AddRequest(req);
354 } else {
355 // Create a new job for this request.
356 job = new Job(this, info.hostname());
357 job->AddRequest(req);
358 AddOutstandingJob(job);
359 // TODO(eroman): Bound the total number of concurrent jobs.
360 // http://crbug.com/9598
361 job->Start();
362 }
363
364 // Completion happens during OnJobComplete(Job*).
365 return ERR_IO_PENDING;
366}
367
368// See OnJobComplete(Job*) for why it is important not to clean out
369// cancelled requests from Job::requests_.
370void HostResolverImpl::CancelRequest(RequestHandle req_handle) {
[email protected]bf0b51c2009-08-01 23:46:40371 if (shutdown_) {
[email protected]ff1f61b92009-08-03 23:20:23372 // TODO(eroman): temp hack for: http://crbug.com/18373
373 // Because we destroy outstanding requests during Shutdown(),
374 // |req_handle| is already cancelled.
[email protected]bf0b51c2009-08-01 23:46:40375 LOG(ERROR) << "Called HostResolverImpl::CancelRequest() after Shutdown().";
376 StackTrace().PrintBacktrace();
377 return;
378 }
[email protected]b59ff372009-07-15 22:04:32379 Request* req = reinterpret_cast<Request*>(req_handle);
380 DCHECK(req);
381 DCHECK(req->job());
382 // NULL out the fields of req, to mark it as cancelled.
383 req->MarkAsCancelled();
384 NotifyObserversCancelRequest(req->id(), req->info());
385}
386
387void HostResolverImpl::AddObserver(Observer* observer) {
388 observers_.push_back(observer);
389}
390
391void HostResolverImpl::RemoveObserver(Observer* observer) {
392 ObserversList::iterator it =
393 std::find(observers_.begin(), observers_.end(), observer);
394
395 // Observer must exist.
396 DCHECK(it != observers_.end());
397
398 observers_.erase(it);
399}
400
401void HostResolverImpl::Shutdown() {
402 shutdown_ = true;
403
404 // Cancel the outstanding jobs.
405 for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it)
406 it->second->Cancel();
407 jobs_.clear();
408}
409
410void HostResolverImpl::AddOutstandingJob(Job* job) {
411 scoped_refptr<Job>& found_job = jobs_[job->host()];
412 DCHECK(!found_job);
413 found_job = job;
414}
415
416HostResolverImpl::Job* HostResolverImpl::FindOutstandingJob(
417 const std::string& hostname) {
418 JobMap::iterator it = jobs_.find(hostname);
419 if (it != jobs_.end())
420 return it->second;
421 return NULL;
422}
423
424void HostResolverImpl::RemoveOutstandingJob(Job* job) {
425 JobMap::iterator it = jobs_.find(job->host());
426 DCHECK(it != jobs_.end());
427 DCHECK_EQ(it->second.get(), job);
428 jobs_.erase(it);
429}
430
431void HostResolverImpl::OnJobComplete(Job* job,
432 int error,
433 const AddressList& addrlist) {
434 RemoveOutstandingJob(job);
435
436 // Write result to the cache.
437 cache_.Set(job->host(), error, addrlist, base::TimeTicks::Now());
438
439 // Make a note that we are executing within OnJobComplete() in case the
440 // HostResolver is deleted by a callback invocation.
441 DCHECK(!cur_completing_job_);
442 cur_completing_job_ = job;
443
444 // Complete all of the requests that were attached to the job.
445 for (RequestsList::const_iterator it = job->requests().begin();
446 it != job->requests().end(); ++it) {
447 Request* req = *it;
448 if (!req->was_cancelled()) {
449 DCHECK_EQ(job, req->job());
450
451 // Notify registered observers.
452 NotifyObserversFinishRequest(req->id(), req->info(), error);
453
454 req->OnComplete(error, addrlist);
455
456 // Check if the job was cancelled as a result of running the callback.
457 // (Meaning that |this| was deleted).
458 if (job->was_cancelled())
459 return;
460 }
461 }
462
463 cur_completing_job_ = NULL;
464}
465
466void HostResolverImpl::NotifyObserversStartRequest(int request_id,
467 const RequestInfo& info) {
468 for (ObserversList::iterator it = observers_.begin();
469 it != observers_.end(); ++it) {
470 (*it)->OnStartResolution(request_id, info);
471 }
472}
473
474void HostResolverImpl::NotifyObserversFinishRequest(int request_id,
475 const RequestInfo& info,
476 int error) {
477 bool was_resolved = error == OK;
478 for (ObserversList::iterator it = observers_.begin();
479 it != observers_.end(); ++it) {
480 (*it)->OnFinishResolutionWithStatus(request_id, was_resolved, info);
481 }
482}
483
484void HostResolverImpl::NotifyObserversCancelRequest(int request_id,
485 const RequestInfo& info) {
486 for (ObserversList::iterator it = observers_.begin();
487 it != observers_.end(); ++it) {
488 (*it)->OnCancelResolution(request_id, info);
489 }
490}
491
492} // namespace net