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