Add a simple DNS caching system to BNetworkAddress

netresolv (and libbind) won't cache DNS requests, which can result in a
lot of DNS requests being made for the same host. Implement a simple
cache in RAM (local to each application) which will keep the most
recently requested addresses cached. This can speed up loading of an
HTTP page a lot, by saving a DNS request for each resource stored on the
same server as the main page.
This commit is contained in:
Adrien Destugues 2015-06-14 14:44:46 +02:00
parent 755f6d2f83
commit f972422c66
3 changed files with 149 additions and 15 deletions

View File

@ -6,6 +6,9 @@
#define _NETWORK_ADDRESS_RESOLVER_H
#include <ObjectList.h>
#include <Referenceable.h>
#include <String.h>
#include <SupportDefs.h>
@ -20,7 +23,7 @@ enum {
};
class BNetworkAddressResolver {
class BNetworkAddressResolver: public BReferenceable {
public:
BNetworkAddressResolver();
BNetworkAddressResolver(const char* address,
@ -53,9 +56,54 @@ public:
status_t GetNextAddress(int family, uint32* cookie,
BNetworkAddress& address) const;
// TODO all the ::Resolve variants are needed. Maybe the SetTo and
// constructors could be removed as ::Resolve is the right way to get a
// resolver (using the cache).
static BReference<const BNetworkAddressResolver> Resolve(
const char* address, const char* service,
uint32 flags = 0);
static BReference<const BNetworkAddressResolver> Resolve(
const char* address, uint16 port = 0,
uint32 flags = 0);
static BReference<const BNetworkAddressResolver> Resolve(int family,
const char* address, const char* service,
uint32 flags = 0);
static BReference<const BNetworkAddressResolver> Resolve(int family,
const char* address, uint16 port = 0,
uint32 flags = 0);
private:
addrinfo* fInfo;
status_t fStatus;
struct CacheEntry {
CacheEntry(int family, const char* address, const char* service,
uint32 flags, BNetworkAddressResolver* resolver)
:
fFamily(family),
fAddress(address),
fService(service),
fFlags(flags),
fResolver(resolver, false)
{
}
bool Matches(int family, BString address, BString service, uint32 flags)
{
return family == fFamily && flags == fFlags && address == fAddress
&& service == fService;
}
int fFamily;
BString fAddress;
BString fService;
uint32 fFlags;
BReference<const BNetworkAddressResolver> fResolver;
};
static BObjectList<CacheEntry> sCacheMap;
};

View File

@ -158,40 +158,46 @@ BNetworkAddress::Unset()
status_t
BNetworkAddress::SetTo(const char* host, uint16 port, uint32 flags)
{
BNetworkAddressResolver resolver;
status_t status = resolver.SetTo(host, port, flags);
BReference<const BNetworkAddressResolver> resolver
= BNetworkAddressResolver::Resolve(host, port, flags);
if (resolver.Get() == NULL)
return B_NO_MEMORY;
status_t status = resolver->InitCheck();
if (status != B_OK)
return status;
// Prefer IPv6 addresses
uint32 cookie = 0;
status = resolver.GetNextAddress(AF_INET6, &cookie, *this);
status = resolver->GetNextAddress(AF_INET6, &cookie, *this);
if (status == B_OK)
return B_OK;
cookie = 0;
return resolver.GetNextAddress(&cookie, *this);
return resolver->GetNextAddress(&cookie, *this);
}
status_t
BNetworkAddress::SetTo(const char* host, const char* service, uint32 flags)
{
BNetworkAddressResolver resolver;
status_t status = resolver.SetTo(host, service, flags);
BReference<const BNetworkAddressResolver> resolver
= BNetworkAddressResolver::Resolve(host, service, flags);
if (resolver.Get() == NULL)
return B_NO_MEMORY;
status_t status = resolver->InitCheck();
if (status != B_OK)
return status;
// Prefer IPv6 addresses
uint32 cookie = 0;
status = resolver.GetNextAddress(AF_INET6, &cookie, *this);
status = resolver->GetNextAddress(AF_INET6, &cookie, *this);
if (status == B_OK)
return B_OK;
cookie = 0;
return resolver.GetNextAddress(&cookie, *this);
return resolver->GetNextAddress(&cookie, *this);
}
@ -204,13 +210,16 @@ BNetworkAddress::SetTo(int family, const char* host, uint16 port, uint32 flags)
return _ParseLinkAddress(host);
}
BNetworkAddressResolver resolver;
status_t status = resolver.SetTo(family, host, port, flags);
BReference<const BNetworkAddressResolver> resolver
= BNetworkAddressResolver::Resolve(family, host, port, flags);
if (resolver.Get() == NULL)
return B_NO_MEMORY;
status_t status = resolver->InitCheck();
if (status != B_OK)
return status;
uint32 cookie = 0;
return resolver.GetNextAddress(&cookie, *this);
return resolver->GetNextAddress(&cookie, *this);
}
@ -224,13 +233,16 @@ BNetworkAddress::SetTo(int family, const char* host, const char* service,
return _ParseLinkAddress(host);
}
BNetworkAddressResolver resolver;
status_t status = resolver.SetTo(family, host, service, flags);
BReference<const BNetworkAddressResolver> resolver
= BNetworkAddressResolver::Resolve(family, host, service, flags);
if (resolver.Get() == NULL)
return B_NO_MEMORY;
status_t status = resolver->InitCheck();
if (status != B_OK)
return status;
uint32 cookie = 0;
return resolver.GetNextAddress(&cookie, *this);
return resolver->GetNextAddress(&cookie, *this);
}

View File

@ -39,6 +39,7 @@ strip_port(BString& host, BString& port)
BNetworkAddressResolver::BNetworkAddressResolver()
:
BReferenceable(),
fInfo(NULL),
fStatus(B_NO_INIT)
{
@ -48,6 +49,7 @@ BNetworkAddressResolver::BNetworkAddressResolver()
BNetworkAddressResolver::BNetworkAddressResolver(const char* address,
uint16 port, uint32 flags)
:
BReferenceable(),
fInfo(NULL),
fStatus(B_NO_INIT)
{
@ -57,6 +59,7 @@ BNetworkAddressResolver::BNetworkAddressResolver(const char* address,
BNetworkAddressResolver::BNetworkAddressResolver(const char* address,
const char* service, uint32 flags)
:
BReferenceable(),
fInfo(NULL),
fStatus(B_NO_INIT)
{
@ -67,6 +70,7 @@ BNetworkAddressResolver::BNetworkAddressResolver(const char* address,
BNetworkAddressResolver::BNetworkAddressResolver(int family,
const char* address, uint16 port, uint32 flags)
:
BReferenceable(),
fInfo(NULL),
fStatus(B_NO_INIT)
{
@ -77,6 +81,7 @@ BNetworkAddressResolver::BNetworkAddressResolver(int family,
BNetworkAddressResolver::BNetworkAddressResolver(int family,
const char* address, const char* service, uint32 flags)
:
BReferenceable(),
fInfo(NULL),
fStatus(B_NO_INIT)
{
@ -258,3 +263,72 @@ BNetworkAddressResolver::GetNextAddress(int family, uint32* cookie,
return B_OK;
}
/*static*/ BReference<const BNetworkAddressResolver>
BNetworkAddressResolver::Resolve(const char* address, const char* service,
uint32 flags)
{
return Resolve(AF_UNSPEC, address, service, flags);
}
/*static*/ BReference<const BNetworkAddressResolver>
BNetworkAddressResolver::Resolve(const char* address, uint16 port, uint32 flags)
{
return Resolve(AF_UNSPEC, address, port, flags);
}
/*static*/ BReference<const BNetworkAddressResolver>
BNetworkAddressResolver::Resolve(int family, const char* address,
uint16 port, uint32 flags)
{
BString service;
service << port;
return Resolve(family, address, port == 0 ? NULL : service.String(), flags);
}
/*static*/ BReference<const BNetworkAddressResolver>
BNetworkAddressResolver::Resolve(int family, const char* address,
const char* service, uint32 flags)
{
// TODO it may be faster to use an hash map to have faster lookup of the
// cache. However, we also need to access the cache by LRU, and for that
// a doubly-linked list is better. We should have these two share the same
// items, so it's easy to remove the LRU from the map, or insert a new
// item in both structures.
for (int i = 0; i < sCacheMap.CountItems(); i++) {
CacheEntry* entry = sCacheMap.ItemAt(i);
if (entry->Matches(family, address, service, flags)) {
// This entry is now the MRU, move to end of list.
// TODO if the item is old (more than 1 minute), it should be
// dropped and a new request made.
sCacheMap.MoveItem(i, sCacheMap.CountItems());
return entry->fResolver;
}
}
BNetworkAddressResolver* resolver = new(std::nothrow)
BNetworkAddressResolver(family, address, service, flags);
if (resolver != NULL && resolver->InitCheck() == B_OK) {
// TODO adjust capacity. Chrome uses 256 entries with a timeout of
// 1 minute, IE uses 1000 entries with a timeout of 30 seconds.
if (sCacheMap.CountItems() > 255)
delete sCacheMap.RemoveItemAt(0);
CacheEntry* entry = new(std::nothrow) CacheEntry(family, address,
service, flags, resolver);
if (entry)
sCacheMap.AddItem(entry, sCacheMap.CountItems());
}
return BReference<const BNetworkAddressResolver>(resolver, true);
}
BObjectList<BNetworkAddressResolver::CacheEntry>
BNetworkAddressResolver::sCacheMap;