fixes bug 219376 "Trying to resolve IP's via DNS (Failed connections stall instead of giving Connection Failure Error; pages stop/don't finish/complete loading if ad hosts/scripts fail)" r=dougt sr=bienvenu,bryner

This commit is contained in:
darin%meer.net 2003-10-07 05:11:41 +00:00
Родитель 2547c1c88c
Коммит 93657ac421
6 изменённых файлов: 285 добавлений и 224 удалений

Просмотреть файл

@ -661,6 +661,7 @@ nsSocketTransport::nsSocketTransport()
, mAttached(PR_FALSE)
, mInputClosed(PR_TRUE)
, mOutputClosed(PR_TRUE)
, mResolving(PR_FALSE)
, mLock(PR_NewLock())
, mFD(nsnull)
, mFDref(0)
@ -802,25 +803,20 @@ nsSocketTransport::ResolveHost()
LOG(("nsSocketTransport::ResolveHost [this=%x]\n", this));
nsresult rv;
// if this is a numeric ip address, then we can simply circumvent the
// DNS resolver.
if (PR_StringToNetAddr(SocketHost().get(), &mNetAddr) == PR_SUCCESS) {
mNetAddr.inet.port = PR_htons(SocketPort());
mState = STATE_RESOLVING;
// not sending a DNS record...
rv = PostEvent(MSG_DNS_LOOKUP_COMPLETE, NS_OK, nsnull);
}
else {
nsCOMPtr<nsIDNSService> dns = do_GetService(kDNSServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
rv = dns->AsyncResolve(SocketHost(), PR_FALSE, this, nsnull,
getter_AddRefs(mDNSRequest));
if (NS_SUCCEEDED(rv)) {
LOG((" advancing to STATE_RESOLVING\n"));
mState = STATE_RESOLVING;
nsCOMPtr<nsIDNSService> dns = do_GetService(kDNSServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
mResolving = PR_TRUE;
rv = dns->AsyncResolve(SocketHost(), PR_FALSE, this, nsnull,
getter_AddRefs(mDNSRequest));
if (NS_SUCCEEDED(rv)) {
LOG((" advancing to STATE_RESOLVING\n"));
mState = STATE_RESOLVING;
// only report that we are resolving if we are still resolving...
if (mResolving)
SendStatus(STATUS_RESOLVING);
}
}
return rv;
}
@ -1631,6 +1627,9 @@ nsSocketTransport::OnLookupComplete(nsIDNSRequest *request,
nsIDNSRecord *rec,
nsresult status)
{
// flag host lookup complete for the benefit of the ResolveHost method.
mResolving = PR_FALSE;
nsresult rv = PostEvent(MSG_DNS_LOOKUP_COMPLETE, status, rec);
// if posting a message fails, then we should assume that the socket

Просмотреть файл

@ -196,6 +196,10 @@ private:
PRPackedBool mInputClosed;
PRPackedBool mOutputClosed;
// this flag is used to determine if the results of a host lookup arrive
// recursively or not. this flag is not protected by any lock.
PRPackedBool mResolving;
nsCOMPtr<nsIDNSRequest> mDNSRequest;
nsCOMPtr<nsIDNSRecord> mDNSRecord;
PRNetAddr mNetAddr;

Просмотреть файл

@ -63,17 +63,17 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDNSRECORD
nsDNSRecord(nsAddrInfo *ai)
: mAddrInfo(ai)
nsDNSRecord(nsHostRecord *hostRecord)
: mHostRecord(hostRecord)
, mIter(nsnull)
, mDone(PR_FALSE) {}
private:
virtual ~nsDNSRecord() {}
nsRefPtr<nsAddrInfo> mAddrInfo;
void *mIter;
PRBool mDone;
nsRefPtr<nsHostRecord> mHostRecord;
void *mIter;
PRBool mDone;
};
NS_IMPL_THREADSAFE_ISUPPORTS1(nsDNSRecord, nsIDNSRecord)
@ -81,23 +81,43 @@ NS_IMPL_THREADSAFE_ISUPPORTS1(nsDNSRecord, nsIDNSRecord)
NS_IMETHODIMP
nsDNSRecord::GetCanonicalName(nsACString &result)
{
NS_ENSURE_TRUE(mAddrInfo, NS_ERROR_NOT_AVAILABLE);
result.Assign(PR_GetCanonNameFromAddrInfo(mAddrInfo->get()));
// if the record is for an IP address literal, then the canonical
// host name is the IP address literal.
const char *cname;
if (mHostRecord->addrinfo)
cname = PR_GetCanonNameFromAddrInfo(mHostRecord->addrinfo);
else
cname = mHostRecord->host;
result.Assign(cname);
return NS_OK;
}
NS_IMETHODIMP
nsDNSRecord::GetNextAddr(PRUint16 port, PRNetAddr *addr)
{
NS_ENSURE_TRUE(mAddrInfo, NS_ERROR_NOT_AVAILABLE);
// not a programming error to poke the DNS record when it has
// no more entries. just silently fail. this enables consumers
// not a programming error to poke the DNS record when it has no more
// entries. just fail without any debug warnings. this enables consumers
// to enumerate the DNS record without calling HasMore.
if (mDone)
return NS_ERROR_NOT_AVAILABLE;
mIter = PR_EnumerateAddrInfo(mIter, mAddrInfo->get(), port, addr);
if (mHostRecord->addrinfo) {
mIter = PR_EnumerateAddrInfo(mIter, mHostRecord->addrinfo, port, addr);
if (!mIter)
return NS_ERROR_NOT_AVAILABLE;
}
else {
mIter = nsnull; // no iterations
NS_ASSERTION(mHostRecord->addr, "no addr");
memcpy(addr, mHostRecord->addr, sizeof(PRNetAddr));
// set given port
port = PR_htons(port);
if (addr->raw.family == PR_AF_INET)
addr->inet.port = port;
else
addr->ipv6.port = port;
}
mDone = !mIter;
return NS_OK;
}
@ -121,7 +141,17 @@ nsDNSRecord::GetNextAddrAsString(nsACString &result)
NS_IMETHODIMP
nsDNSRecord::HasMore(PRBool *result)
{
*result = !mDone;
if (mDone)
*result = PR_FALSE;
else {
// unfortunately, NSPR does not provide a way for us to determine if
// there is another address other than to simply get the next address.
void *iterCopy = mIter;
PRNetAddr addr;
*result = NS_SUCCEEDED(GetNextAddr(0, &addr));
mIter = iterCopy; // backup iterator
mDone = PR_FALSE;
}
return NS_OK;
}
@ -135,7 +165,7 @@ nsDNSRecord::Rewind()
//-----------------------------------------------------------------------------
class nsDNSAsyncRequest : public nsResolveHostCB
class nsDNSAsyncRequest : public nsResolveHostCallback
, public nsIDNSRequest
{
public:
@ -150,7 +180,7 @@ public:
, mListener(listener) {}
virtual ~nsDNSAsyncRequest() {}
void OnLookupComplete(nsHostResolver *, const char *, nsresult, nsAddrInfo *);
void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult);
nsRefPtr<nsHostResolver> mResolver;
nsCString mHost; // hostname we're resolving
@ -159,15 +189,16 @@ public:
void
nsDNSAsyncRequest::OnLookupComplete(nsHostResolver *resolver,
const char *host,
nsresult status,
nsAddrInfo *ai)
nsHostRecord *hostRecord,
nsresult status)
{
nsDNSRecord *rec;
if (!ai)
rec = nsnull;
else {
rec = new nsDNSRecord(ai);
// need to have an owning ref when we issue the callback to enable
// the caller to be able to addref/release multiple times without
// destroying the record prematurely.
nsCOMPtr<nsIDNSRecord> rec;
if (NS_SUCCEEDED(status)) {
NS_ASSERTION(hostRecord, "no host record");
rec = new nsDNSRecord(hostRecord);
if (!rec)
status = NS_ERROR_OUT_OF_MEMORY;
}
@ -191,7 +222,7 @@ nsDNSAsyncRequest::Cancel()
//-----------------------------------------------------------------------------
class nsDNSSyncRequest : public nsResolveHostCB
class nsDNSSyncRequest : public nsResolveHostCallback
{
public:
nsDNSSyncRequest(PRMonitor *mon)
@ -200,27 +231,26 @@ public:
, mMonitor(mon) {}
virtual ~nsDNSSyncRequest() {}
void OnLookupComplete(nsHostResolver *, const char *, nsresult, nsAddrInfo *);
void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult);
PRBool mDone;
nsresult mStatus;
nsRefPtr<nsAddrInfo> mAddrInfo;
PRBool mDone;
nsresult mStatus;
nsRefPtr<nsHostRecord> mHostRecord;
private:
PRMonitor *mMonitor;
PRMonitor *mMonitor;
};
void
nsDNSSyncRequest::OnLookupComplete(nsHostResolver *resolver,
const char *host,
nsresult status,
nsAddrInfo *ai)
nsHostRecord *hostRecord,
nsresult status)
{
// store results, and wake up nsDNSService::Resolve to process results.
PR_EnterMonitor(mMonitor);
mDone = PR_TRUE;
mStatus = status;
mAddrInfo = ai;
mHostRecord = hostRecord;
PR_Notify(mMonitor);
PR_ExitMonitor(mMonitor);
}
@ -325,11 +355,10 @@ nsDNSService::AsyncResolve(const nsACString &hostname,
const nsACString *hostPtr = &hostname;
nsresult rv;
nsCAutoString hostBuf;
nsCAutoString hostACE;
if (idn && !IsASCII(hostname)) {
rv = idn->ConvertUTF8toACE(hostname, hostBuf);
if (NS_SUCCEEDED(rv))
hostPtr = &hostBuf;
if (NS_SUCCEEDED(idn->ConvertUTF8toACE(hostname, hostACE)))
hostPtr = &hostACE;
}
nsCOMPtr<nsIDNSListener> listenerProxy;
@ -377,11 +406,10 @@ nsDNSService::Resolve(const nsACString &hostname,
const nsACString *hostPtr = &hostname;
nsresult rv;
nsCAutoString hostBuf;
nsCAutoString hostACE;
if (idn && !IsASCII(hostname)) {
rv = idn->ConvertUTF8toACE(hostname, hostBuf);
if (NS_SUCCEEDED(rv))
hostPtr = &hostBuf;
if (NS_SUCCEEDED(idn->ConvertUTF8toACE(hostname, hostACE)))
hostPtr = &hostACE;
}
//
@ -408,7 +436,8 @@ nsDNSService::Resolve(const nsACString &hostname,
if (NS_FAILED(syncReq.mStatus))
rv = syncReq.mStatus;
else {
nsDNSRecord *rec = new nsDNSRecord(syncReq.mAddrInfo);
NS_ASSERTION(syncReq.mHostRecord, "no host record");
nsDNSRecord *rec = new nsDNSRecord(syncReq.mHostRecord);
if (!rec)
rv = NS_ERROR_OUT_OF_MEMORY;
else

Просмотреть файл

@ -144,32 +144,36 @@ private:
//----------------------------------------------------------------------------
class nsHostRecord : public PRCList
nsresult
nsHostRecord::Create(const char *host, nsHostRecord **result)
{
public:
NET_DECL_REFCOUNTED_THREADSAFE
size_t hostLen = strlen(host) + 1;
size_t size = hostLen + sizeof(nsHostRecord);
nsHostRecord(const char *h)
: host(PL_strdup(h))
, expireTime(NowInMinutes())
, mRefCount(0)
{
PR_INIT_CLIST(this);
PR_INIT_CLIST(&callbacks);
}
~nsHostRecord()
{
PL_strfree(host);
}
nsHostRecord *rec = (nsHostRecord*) ::operator new(size);
if (!rec)
return NS_ERROR_OUT_OF_MEMORY;
char *host;
nsRefPtr<nsAddrInfo> addr;
PRUint32 expireTime; // minutes since epoch
PRCList callbacks;
rec->_refc = 1; // addref
rec->host = ((char *) rec) + sizeof(nsHostRecord);
rec->addrinfo = nsnull;
rec->addr = nsnull;
rec->expiration = NowInMinutes();
PR_INIT_CLIST(rec);
PR_INIT_CLIST(&rec->callbacks);
memcpy(rec->host, host, hostLen);
private:
PRInt32 mRefCount;
};
*result = rec;
return NS_OK;
}
nsHostRecord::~nsHostRecord()
{
if (addrinfo)
PR_FreeAddrInfo(addrinfo);
if (addr)
free(addr);
}
//----------------------------------------------------------------------------
@ -178,14 +182,14 @@ struct nsHostDBEnt : PLDHashEntryHdr
nsHostRecord *rec;
};
static const void * PR_CALLBACK
PR_STATIC_CALLBACK(const void *)
HostDB_GetKey(PLDHashTable *table, PLDHashEntryHdr *entry)
{
nsHostDBEnt *he = NS_STATIC_CAST(nsHostDBEnt *, entry);
return he->rec->host;
}
static PRBool PR_CALLBACK
PR_STATIC_CALLBACK(PRBool)
HostDB_MatchEntry(PLDHashTable *table,
const PLDHashEntryHdr *entry,
const void *key)
@ -194,7 +198,7 @@ HostDB_MatchEntry(PLDHashTable *table,
return !strcmp(he->rec->host, (const char *) key);
}
static void PR_CALLBACK
PR_STATIC_CALLBACK(void)
HostDB_MoveEntry(PLDHashTable *table,
const PLDHashEntryHdr *from,
PLDHashEntryHdr *to)
@ -203,25 +207,25 @@ HostDB_MoveEntry(PLDHashTable *table,
NS_STATIC_CAST(const nsHostDBEnt *, from)->rec;
}
static void PR_CALLBACK
PR_STATIC_CALLBACK(void)
HostDB_ClearEntry(PLDHashTable *table,
PLDHashEntryHdr *entry)
{
nsHostDBEnt *he = NS_STATIC_CAST(nsHostDBEnt *, entry);
#ifdef DEBUG_HOST_RESOLVER
if (!he->rec->addr)
LOG(("%s: => null\n", he->rec->host));
if (!he->rec->addrinfo)
LOG(("%s: => no addrinfo\n", he->rec->host));
else {
PRInt32 now = (PRInt32) NowInMinutes();
PRInt32 diff = (PRInt32) he->rec->expireTime - now;
PRInt32 diff = (PRInt32) he->rec->expiration - now;
LOG(("%s: exp=%d => %s\n",
he->rec->host, diff,
PR_GetCanonNameFromAddrInfo(he->rec->addr->get())));
PR_GetCanonNameFromAddrInfo(he->rec->addrinfo)));
void *iter = nsnull;
PRNetAddr addr;
char buf[64];
do {
iter = PR_EnumerateAddrInfo(iter, he->rec->addr->get(), 0, &addr);
iter = PR_EnumerateAddrInfo(iter, he->rec->addrinfo, 0, &addr);
PR_NetAddrToString(&addr, buf, sizeof(buf));
LOG((" %s\n", buf));
} while (iter);
@ -230,23 +234,13 @@ HostDB_ClearEntry(PLDHashTable *table,
NS_RELEASE(he->rec);
}
static PRBool PR_CALLBACK
PR_STATIC_CALLBACK(PRBool)
HostDB_InitEntry(PLDHashTable *table,
PLDHashEntryHdr *entry,
const void *key)
{
nsHostDBEnt *he = NS_STATIC_CAST(nsHostDBEnt *, entry);
he->rec = new nsHostRecord(NS_REINTERPRET_CAST(const char *, key));
// addref result if initialized correctly; otherwise, leave record
// null so caller can detect and propagate error.
if (he->rec) {
if (he->rec->host)
NS_ADDREF(he->rec);
else {
delete he->rec;
he->rec = nsnull;
}
}
nsHostRecord::Create(NS_REINTERPRET_CAST(const char *, key), &he->rec);
return PR_TRUE;
}
@ -263,7 +257,7 @@ static PLDHashTableOps gHostDB_ops =
HostDB_InitEntry,
};
static PLDHashOperator PR_CALLBACK
PR_STATIC_CALLBACK(PLDHashOperator)
HostDB_RemoveEntry(PLDHashTable *table,
PLDHashEntryHdr *hdr,
PRUint32 number,
@ -276,8 +270,7 @@ HostDB_RemoveEntry(PLDHashTable *table,
nsHostResolver::nsHostResolver(PRUint32 maxCacheEntries,
PRUint32 maxCacheLifetime)
: mRefCount(0)
, mMaxCacheEntries(maxCacheEntries)
: mMaxCacheEntries(maxCacheEntries)
, mMaxCacheLifetime(maxCacheLifetime)
, mLock(nsnull)
, mIdleThreadCV(nsnull)
@ -352,65 +345,88 @@ nsHostResolver::Shutdown()
}
nsresult
nsHostResolver::ResolveHost(const char *host,
PRBool bypassCache,
nsResolveHostCB *callback)
nsHostResolver::ResolveHost(const char *host,
PRBool bypassCache,
nsResolveHostCallback *callback)
{
NS_ENSURE_TRUE(host && *host, NS_ERROR_UNEXPECTED);
nsAutoLock lock(mLock);
// if result is set inside the lock, then we need to issue the
// callback before returning.
nsRefPtr<nsHostRecord> result;
nsresult status = NS_OK, rv = NS_OK;
{
nsAutoLock lock(mLock);
if (mShutdown)
return NS_ERROR_NOT_INITIALIZED;
// check to see if there is already an entry for this |host|
// in the hash table. if so, then check to see if we can't
// just reuse the lookup result. otherwise, if there are
// any pending callbacks, then add to pending callbacks queue,
// and return. otherwise, add ourselves as first pending
// callback, and proceed to do the lookup.
if (mShutdown)
rv = NS_ERROR_NOT_INITIALIZED;
else {
PRNetAddr tempAddr;
// check to see if there is already an entry for this |host|
// in the hash table. if so, then check to see if we can't
// just reuse the lookup result. otherwise, if there are
// any pending callbacks, then add to pending callbacks queue,
// and return. otherwise, add ourselves as first pending
// callback, and proceed to do the lookup.
PLDHashEntryHdr *hdr;
nsHostDBEnt *he =
NS_STATIC_CAST(nsHostDBEnt *,
PL_DHashTableOperate(&mDB, host, PL_DHASH_ADD));
hdr = PL_DHashTableOperate(&mDB, host, PL_DHASH_ADD);
if (!hdr)
return NS_ERROR_OUT_OF_MEMORY;
// if the record is null, then HostDB_InitEntry failed.
if (!he || !he->rec)
rv = NS_ERROR_OUT_OF_MEMORY;
nsHostDBEnt *he = NS_STATIC_CAST(nsHostDBEnt *, hdr);
if (!he->rec)
return NS_ERROR_OUT_OF_MEMORY;
else if (!bypassCache &&
he->rec->addr && NowInMinutes() <= he->rec->expireTime) {
// ok, we can reuse this result. but, since we are
// making a callback, we must only do so outside the
// lock, and that requires holding an owning reference
// to the addrinfo structure.
nsRefPtr<nsAddrInfo> ai = he->rec->addr;
lock.unlock();
callback->OnLookupComplete(this, host, NS_OK, ai);
lock.lock();
return NS_OK;
}
PRBool doLookup = PR_CLIST_IS_EMPTY(&he->rec->callbacks);
// add callback to the list of pending callbacks
PR_APPEND_LINK(callback, &he->rec->callbacks);
nsresult rv = NS_OK;
if (doLookup) {
rv = IssueLookup(he->rec);
if (NS_FAILED(rv))
PR_REMOVE_AND_INIT_LINK(callback);
// do we have a cached result that we can reuse?
else if (!bypassCache &&
he->rec->HasResult() &&
NowInMinutes() <= he->rec->expiration) {
// put reference to host record on stack...
result = he->rec;
}
// try parsing the host name as an IP address literal to short
// circuit full host resolution. (this is necessary on some
// platforms like Win9x. see bug 219376 for more details.)
else if (PR_StringToNetAddr(host, &tempAddr) == PR_SUCCESS) {
// ok, just copy the result into the host record, and be done
// with it! ;-)
he->rec->addr = (PRNetAddr *) malloc(sizeof(PRNetAddr));
if (!he->rec->addr)
status = NS_ERROR_OUT_OF_MEMORY;
else
memcpy(he->rec->addr, &tempAddr, sizeof(PRNetAddr));
// put reference to host record on stack...
result = he->rec;
}
// otherwise, hit the resolver...
else {
PRBool doLookup = PR_CLIST_IS_EMPTY(&he->rec->callbacks);
// add callback to the list of pending callbacks
PR_APPEND_LINK(callback, &he->rec->callbacks);
nsresult rv = NS_OK;
if (doLookup) {
rv = IssueLookup(he->rec);
if (NS_FAILED(rv))
PR_REMOVE_AND_INIT_LINK(callback);
}
}
}
}
if (result)
callback->OnLookupComplete(this, result, status);
return rv;
}
void
nsHostResolver::DetachCallback(const char *host,
nsResolveHostCB *callback)
nsHostResolver::DetachCallback(const char *host,
nsResolveHostCallback *callback)
{
PRBool doCallback = PR_FALSE;
nsRefPtr<nsHostRecord> rec;
{
nsAutoLock lock(mLock);
@ -421,9 +437,9 @@ nsHostResolver::DetachCallback(const char *host,
// that it will be there!
PRCList *node = he->rec->callbacks.next;
while (node != &he->rec->callbacks) {
if (NS_STATIC_CAST(nsResolveHostCB *, node) == callback) {
if (NS_STATIC_CAST(nsResolveHostCallback *, node) == callback) {
PR_REMOVE_LINK(callback);
doCallback = PR_TRUE;
rec = he->rec;
break;
}
node = node->next;
@ -431,8 +447,10 @@ nsHostResolver::DetachCallback(const char *host,
}
}
if (doCallback)
callback->OnLookupComplete(this, host, NS_ERROR_ABORT, nsnull);
// complete callback with an error code; this would only be done
// if the record was in the process of being resolved.
if (rec)
callback->OnLookupComplete(this, rec, NS_ERROR_ABORT);
}
nsresult
@ -501,17 +519,6 @@ nsHostResolver::GetHostToLookup(nsHostRecord **result)
void
nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, PRAddrInfo *result)
{
nsAddrInfo *ai;
if (!result)
ai = nsnull;
else {
ai = new nsAddrInfo(result);
if (!ai) {
status = NS_ERROR_OUT_OF_MEMORY;
PR_FreeAddrInfo(result);
}
}
// get the list of pending callbacks for this lookup, and notify
// them that the lookup is complete.
PRCList cbs;
@ -519,11 +526,14 @@ nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, PRAddrInfo
{
nsAutoLock lock(mLock);
// grab list of callbacks to notify
MoveCList(rec->callbacks, cbs);
rec->addr = ai;
rec->expireTime = NowInMinutes() + mMaxCacheLifetime;
// update record fields
rec->addrinfo = result;
rec->expiration = NowInMinutes() + mMaxCacheLifetime;
if (rec->addr) {
if (rec->addrinfo) {
// add to mEvictionQ
PR_APPEND_LINK(rec, &mEvictionQ);
NS_ADDREF(rec);
@ -544,9 +554,10 @@ nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, PRAddrInfo
if (!PR_CLIST_IS_EMPTY(&cbs)) {
PRCList *node = cbs.next;
while (node != &cbs) {
nsResolveHostCB *callback = NS_STATIC_CAST(nsResolveHostCB *, node);
nsResolveHostCallback *callback =
NS_STATIC_CAST(nsResolveHostCallback *, node);
node = node->next;
callback->OnLookupComplete(this, rec->host, status, ai);
callback->OnLookupComplete(this, rec, status);
// NOTE: callback must not be dereferenced after this point!!
}
}

Просмотреть файл

@ -43,83 +43,101 @@
#include "prclist.h"
#include "prnetdb.h"
#include "pldhash.h"
#include "nsISupportsImpl.h"
class nsHostResolver;
class nsHostRecord;
class nsResolveHostCB;
class nsAddrInfo;
class nsResolveHostCallback;
/* XXX move this someplace more generic */
#define NET_DECL_REFCOUNTED_THREADSAFE \
PRInt32 AddRef() { \
return PR_AtomicIncrement(&mRefCount); \
} \
PRInt32 Release() { \
PRInt32 n = PR_AtomicDecrement(&mRefCount); \
if (n == 0) \
delete this; \
return n; \
#define NS_DECL_REFCOUNTED_THREADSAFE \
private: \
nsAutoRefCnt _refc; \
public: \
PRInt32 AddRef() { \
return PR_AtomicIncrement((PRInt32*)&_refc); \
} \
PRInt32 Release() { \
PRInt32 n = PR_AtomicDecrement((PRInt32*)&_refc); \
if (n == 0) \
delete this; \
return n; \
}
/**
* reference counted wrapper around PRAddrInfo. these are stored in
* the DNS cache.
* nsHostRecord - ref counted object type stored in host resolver cache.
*/
class nsAddrInfo
class nsHostRecord : public PRCList
{
public:
NET_DECL_REFCOUNTED_THREADSAFE
NS_DECL_REFCOUNTED_THREADSAFE
nsAddrInfo(PRAddrInfo *data)
: mRefCount(0)
, mData(data) {}
/* instantiates a new host record */
static nsresult Create(const char *host, nsHostRecord **record);
const PRAddrInfo *get() const { return mData; }
/* a fully resolved host record has either a non-null |addrinfo| or |addr|
* field. if |addrinfo| is null, it implies that the |host| is an IP
* address literal. in which case, |addr| contains the parsed address.
* otherwise, if |addrinfo| is non-null, then it contains one or many
* IP addresses corresponding to the given host name. if both |addrinfo|
* and |addr| are null, then the given host has not yet been fully resolved.
*/
char *host;
PRAddrInfo *addrinfo;
PRNetAddr *addr;
PRBool HasResult() const { return (addrinfo || addr) != nsnull; }
private:
nsAddrInfo(); // never called
~nsAddrInfo() { if (mData) PR_FreeAddrInfo(mData); }
friend class nsHostResolver;
PRInt32 mRefCount;
PRAddrInfo *mData;
/* these fields are used internally by the host resolver */
PRUint32 expiration; /* measured in minutes since epoch */
PRCList callbacks;
~nsHostRecord();
};
/**
* ResolveHost callback object. It's PRCList members are used by
* the nsHostResolver and should not be used by anything else.
*/
class nsResolveHostCB : public PRCList
class NS_NO_VTABLE nsResolveHostCallback : public PRCList
{
public:
/**
* LookupComplete
* OnLookupComplete
*
* Runs on an unspecified background thread.
* this function is called to complete a host lookup initiated by
* nsHostResolver::ResolveHost. it may be invoked recursively from
* ResolveHost or on an unspecified background thread.
*
* NOTE: it is the responsibility of the implementor of this method
* to handle the callback in a thread safe manner.
*
* @param resolver
* nsHostResolver object associated with this result
* @param host
* hostname that was resolved
* @param record
* the host record containing the results of the lookup
* @param status
* if successful, |result| will be non-null
* @param result
* resulting nsAddrInfo object
* if successful, |record| contains non-null results
*/
virtual void OnLookupComplete(nsHostResolver *resolver,
const char *host,
nsresult status,
nsAddrInfo *result) = 0;
protected:
virtual ~nsResolveHostCB() {}
nsHostRecord *record,
nsresult status) = 0;
};
/**
* nsHostResolver: an asynchronous hostname resolver.
* nsHostResolver - an asynchronous host name resolver.
*/
class nsHostResolver
{
public:
/**
* host resolver instances are reference counted.
*/
NS_DECL_REFCOUNTED_THREADSAFE
/**
* creates an addref'd instance of a nsHostResolver object.
*/
@ -133,11 +151,6 @@ public:
*/
void Shutdown();
/**
* host resolver instances are reference counted.
*/
NET_DECL_REFCOUNTED_THREADSAFE
/**
* resolve the given hostname asynchronously. the caller can synthesize
* a synchronous host lookup using a lock and a cvar. as noted above
@ -145,17 +158,17 @@ public:
* host lookup cannot be canceled (cancelation can be layered above this
* by having the callback implementation return without doing anything).
*/
nsresult ResolveHost(const char *hostname,
PRBool bypassCache,
nsResolveHostCB *callback);
nsresult ResolveHost(const char *hostname,
PRBool bypassCache,
nsResolveHostCallback *callback);
/**
* removes the specified callback from the nsHostRecord for the given
* hostname. this function executes the callback if the callback is
* still pending with the status failure code NS_ERROR_ABORT.
*/
void DetachCallback(const char *hostname,
nsResolveHostCB *callback);
void DetachCallback(const char *hostname,
nsResolveHostCallback *callback);
private:
nsHostResolver(PRUint32 maxCacheEntries=50, PRUint32 maxCacheLifetime=1);
@ -166,9 +179,8 @@ private:
PRBool GetHostToLookup(nsHostRecord **);
void OnLookupComplete(nsHostRecord *, nsresult, PRAddrInfo *);
static void PR_CALLBACK ThreadFunc(void *);
PR_STATIC_CALLBACK(void) ThreadFunc(void *);
PRInt32 mRefCount;
PRUint32 mMaxCacheEntries;
PRUint32 mMaxCacheLifetime;
PRLock *mLock;

Просмотреть файл

@ -60,7 +60,7 @@ public:
nsIDNSRecord *rec,
nsresult status)
{
printf("%d: OnLookupComplete called [host=%s status=%x ai=%p]\n",
printf("%d: OnLookupComplete called [host=%s status=%x rec=%p]\n",
mIndex, mHost.get(), status, (void*)rec);
if (NS_SUCCEEDED(status)) {
@ -69,8 +69,11 @@ public:
rec->GetCanonicalName(buf);
printf("%d: canonname=%s\n", mIndex, buf.get());
while (NS_SUCCEEDED(rec->GetNextAddrAsString(buf)))
PRBool hasMore;
while (NS_SUCCEEDED(rec->HasMore(&hasMore)) && hasMore) {
rec->GetNextAddrAsString(buf);
printf("%d: => %s\n", mIndex, buf.get());
}
}
return NS_OK;
@ -113,7 +116,10 @@ int main(int argc, char **argv)
nsCOMPtr<nsIDNSListener> listener = new myDNSListener(argv[i], i);
nsCOMPtr<nsIDNSRequest> req;
dns->AsyncResolve(hostBuf, PR_FALSE, listener, nsnull, getter_AddRefs(req));
nsresult rv = dns->AsyncResolve(hostBuf, PR_FALSE, listener, nsnull,
getter_AddRefs(req));
if (NS_FAILED(rv))
printf("### AsyncResolve failed [rv=%x]\n", rv);
}
printf("main thread sleeping for %d seconds...\n", sleepLen);