From f972422c66af92a5c6abf9852c904b92ea9255ea Mon Sep 17 00:00:00 2001 From: Adrien Destugues Date: Sun, 14 Jun 2015 14:44:46 +0200 Subject: [PATCH] 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. --- headers/os/net/NetworkAddressResolver.h | 50 ++++++++++++- src/kits/network/libnetapi/NetworkAddress.cpp | 40 ++++++---- .../libnetapi/NetworkAddressResolver.cpp | 74 +++++++++++++++++++ 3 files changed, 149 insertions(+), 15 deletions(-) diff --git a/headers/os/net/NetworkAddressResolver.h b/headers/os/net/NetworkAddressResolver.h index ed507a580e..c5bd627bcb 100644 --- a/headers/os/net/NetworkAddressResolver.h +++ b/headers/os/net/NetworkAddressResolver.h @@ -6,6 +6,9 @@ #define _NETWORK_ADDRESS_RESOLVER_H +#include +#include +#include #include @@ -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 Resolve( + const char* address, const char* service, + uint32 flags = 0); + static BReference Resolve( + const char* address, uint16 port = 0, + uint32 flags = 0); + static BReference Resolve(int family, + const char* address, const char* service, + uint32 flags = 0); + static BReference 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 fResolver; + }; + + static BObjectList sCacheMap; }; diff --git a/src/kits/network/libnetapi/NetworkAddress.cpp b/src/kits/network/libnetapi/NetworkAddress.cpp index b6eca702d9..307e173375 100644 --- a/src/kits/network/libnetapi/NetworkAddress.cpp +++ b/src/kits/network/libnetapi/NetworkAddress.cpp @@ -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 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 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 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 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); } diff --git a/src/kits/network/libnetapi/NetworkAddressResolver.cpp b/src/kits/network/libnetapi/NetworkAddressResolver.cpp index 970767cb24..4249c110da 100644 --- a/src/kits/network/libnetapi/NetworkAddressResolver.cpp +++ b/src/kits/network/libnetapi/NetworkAddressResolver.cpp @@ -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 +BNetworkAddressResolver::Resolve(const char* address, const char* service, + uint32 flags) +{ + return Resolve(AF_UNSPEC, address, service, flags); +} + + +/*static*/ BReference +BNetworkAddressResolver::Resolve(const char* address, uint16 port, uint32 flags) +{ + return Resolve(AF_UNSPEC, address, port, flags); +} + + +/*static*/ BReference +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 +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(resolver, true); +} + + +BObjectList + BNetworkAddressResolver::sCacheMap;