hrev46298 adds 2 changesets to branch 'master' old head: c157484a81328535c34fb21893308f2311d9ec91 new head: 9ce2f7e3863c7c69284eaad6466c6b0247f0037b overview: http://cgit.haiku-os.org/haiku/log/?qt=range&q=9ce2f7e+%5Ec157484 ---------------------------------------------------------------------------- b7617dd: Network Cookie Jar: implement assignment operator. This change is needed for implementing cookie persistence in Web+ using the network kit backend. The current implementation requires the user to unarchive the cookie jar, then hand it over to the BUrlContext which will copy it to its own field. This makes the code simpler, but maybe doing a complete copy (with all the cookies) is an heavy operation and could be avoided. 9ce2f7e: Improve HTTP authentication support. The authentication state is stored (in a hash map, using the domain+path as a key) in the UrlContext class. It can then be reused for multiple requests to the same place. We also lookup stored authentications for parent directories and stop at the first we find. Authentication state is not stored on disk (unlike cookies), and there can only be one for each domain+path. [ Adrien Destugues <pulkomandy@xxxxxxxxxxxxx> ] ---------------------------------------------------------------------------- 6 files changed, 139 insertions(+), 21 deletions(-) headers/os/net/HttpRequest.h | 2 - headers/os/net/NetworkCookieJar.h | 3 + headers/os/net/UrlContext.h | 14 +++++ src/kits/network/libnetapi/HttpRequest.cpp | 54 ++++++++++++------ src/kits/network/libnetapi/NetworkCookieJar.cpp | 26 +++++++++ src/kits/network/libnetapi/UrlContext.cpp | 61 ++++++++++++++++++++- ############################################################################ Commit: b7617ddd68d4b2b95d8394edad08f2954e859bf5 URL: http://cgit.haiku-os.org/haiku/commit/?id=b7617dd Author: Adrien Destugues <pulkomandy@xxxxxxxxxxxxx> Date: Fri Oct 25 15:14:33 2013 UTC Network Cookie Jar: implement assignment operator. This change is needed for implementing cookie persistence in Web+ using the network kit backend. The current implementation requires the user to unarchive the cookie jar, then hand it over to the BUrlContext which will copy it to its own field. This makes the code simpler, but maybe doing a complete copy (with all the cookies) is an heavy operation and could be avoided. ---------------------------------------------------------------------------- diff --git a/headers/os/net/NetworkCookieJar.h b/headers/os/net/NetworkCookieJar.h index 0bf070f..58364a6 100644 --- a/headers/os/net/NetworkCookieJar.h +++ b/headers/os/net/NetworkCookieJar.h @@ -58,10 +58,13 @@ public: virtual status_t Unflatten(type_code code, const void* buffer, ssize_t size); + BNetworkCookieJar& operator=(const BNetworkCookieJar& other); + // Iterators Iterator GetIterator() const; UrlIterator GetUrlIterator(const BUrl& url) const; + private: void _DoFlatten() const; diff --git a/src/kits/network/libnetapi/NetworkCookieJar.cpp b/src/kits/network/libnetapi/NetworkCookieJar.cpp index 03c894c..ee0c4de 100644 --- a/src/kits/network/libnetapi/NetworkCookieJar.cpp +++ b/src/kits/network/libnetapi/NetworkCookieJar.cpp @@ -71,6 +71,8 @@ BNetworkCookieJar::~BNetworkCookieJar() { for (Iterator it = GetIterator(); it.Next() != NULL;) delete it.Remove(); + + delete fCookieHashMap; } @@ -356,6 +358,30 @@ BNetworkCookieJar::Unflatten(type_code, const void* buffer, ssize_t size) } +BNetworkCookieJar& +BNetworkCookieJar::operator=(const BNetworkCookieJar& other) +{ + if(&other == this) + return *this; + + BArchivable::operator=(other); + BFlattenable::operator=(other); + + fFlattened = other.fFlattened; + + delete fCookieHashMap; + fCookieHashMap = new PrivateHashMap(); + + for (Iterator it = other.GetIterator(); it.HasNext();) + { + BNetworkCookie* cookie = it.Next(); + AddCookie(*cookie); // Pass by reference so the cookie is copied. + } + + return *this; +} + + // #pragma mark Iterators ############################################################################ Revision: hrev46298 Commit: 9ce2f7e3863c7c69284eaad6466c6b0247f0037b URL: http://cgit.haiku-os.org/haiku/commit/?id=9ce2f7e Author: Adrien Destugues <pulkomandy@xxxxxxxxxxxxx> Date: Mon Oct 28 16:09:18 2013 UTC Improve HTTP authentication support. The authentication state is stored (in a hash map, using the domain+path as a key) in the UrlContext class. It can then be reused for multiple requests to the same place. We also lookup stored authentications for parent directories and stop at the first we find. Authentication state is not stored on disk (unlike cookies), and there can only be one for each domain+path. ---------------------------------------------------------------------------- diff --git a/headers/os/net/HttpRequest.h b/headers/os/net/HttpRequest.h index 2c4248c..d18222a 100644 --- a/headers/os/net/HttpRequest.h +++ b/headers/os/net/HttpRequest.h @@ -9,7 +9,6 @@ #include <deque> -#include <HttpAuthentication.h> #include <HttpForm.h> #include <HttpHeaders.h> #include <HttpResult.h> @@ -93,7 +92,6 @@ private: BNetBuffer fInputBuffer; BHttpHeaders fHeaders; - BHttpAuthentication fAuthentication; // Request status diff --git a/headers/os/net/UrlContext.h b/headers/os/net/UrlContext.h index 414a371..6a4541f 100644 --- a/headers/os/net/UrlContext.h +++ b/headers/os/net/UrlContext.h @@ -6,22 +6,36 @@ #define _B_URL_CONTEXT_H_ +#include <HttpAuthentication.h> #include <NetworkCookieJar.h> +namespace BPrivate { + template <class key, class value> class HashMap; + class HashString; +} + + class BUrlContext { public: BUrlContext(); + ~BUrlContext(); // Context modifiers void SetCookieJar( const BNetworkCookieJar& cookieJar); + void AddAuthentication(const BUrl& url, + BHttpAuthentication* const authentication); // Context accessors BNetworkCookieJar& GetCookieJar(); + BHttpAuthentication& GetAuthentication(const BUrl& url); private: BNetworkCookieJar fCookieJar; + typedef BPrivate::HashMap<BPrivate::HashString, + BHttpAuthentication*> BHttpAuthenticationMap; + BHttpAuthenticationMap* fAuthenticationMap; }; #endif // _B_URL_CONTEXT_H_ diff --git a/src/kits/network/libnetapi/HttpRequest.cpp b/src/kits/network/libnetapi/HttpRequest.cpp index 7b19990..6330a30 100644 --- a/src/kits/network/libnetapi/HttpRequest.cpp +++ b/src/kits/network/libnetapi/HttpRequest.cpp @@ -344,22 +344,35 @@ BHttpRequest::_ProtocolLoop() break; case B_HTTP_STATUS_CLASS_CLIENT_ERROR: - switch (fResult.StatusCode()) { - case B_HTTP_STATUS_UNAUTHORIZED: - if (fAuthentication.Method() != B_HTTP_AUTHENTICATION_NONE) { - newRequest = false; - break; - } + if(fResult.StatusCode() == B_HTTP_STATUS_UNAUTHORIZED) { + BHttpAuthentication* authentication + = &fContext->GetAuthentication(fUrl); + status_t status = B_OK; + + if (authentication->Method() == B_HTTP_AUTHENTICATION_NONE) + { + // There is no authentication context for this + // url yet, so let's create one. + authentication = new BHttpAuthentication(); + status = authentication->Initialize( + fHeaders["WWW-Authenticate"]); + fContext->AddAuthentication(fUrl, authentication); + } - newRequest = false; - if (fOptUsername.Length() > 0 - && fAuthentication.Initialize(fHeaders["WWW-Authenticate"]) - == B_OK) { - fAuthentication.SetUserName(fOptUsername); - fAuthentication.SetPassword(fOptPassword); - newRequest = true; - } - break; + newRequest = false; + if (fOptUsername.Length() > 0 && status == B_OK) { + // If we received an username and password, add them + // to the request. This will either change the + // credentials for an existing request, or set them + // for a new one we created just above. + // + // If this request handles HTTP redirections, it will + // also automatically retry connecting and send the + // login information. + authentication->SetUserName(fOptUsername); + authentication->SetPassword(fOptPassword); + newRequest = true; + } } break; @@ -811,11 +824,16 @@ BHttpRequest::_AddHeaders() fOutputHeaders.AddHeader("Referer", fOptReferer.String()); // Authentication - if (fAuthentication.Method() != B_HTTP_AUTHENTICATION_NONE) { - BString request(fRequestMethod); + BHttpAuthentication& authentication = fContext->GetAuthentication(fUrl); + if (authentication.Method() != B_HTTP_AUTHENTICATION_NONE) { + if (fOptUsername.Length() > 0) { + authentication.SetUserName(fOptUsername); + authentication.SetPassword(fOptPassword); + } + BString request(fRequestMethod); fOutputHeaders.AddHeader("Authorization", - fAuthentication.Authorization(fUrl, request)); + authentication.Authorization(fUrl, request)); } // Required headers for POST data diff --git a/src/kits/network/libnetapi/UrlContext.cpp b/src/kits/network/libnetapi/UrlContext.cpp index a869304..429eb59 100644 --- a/src/kits/network/libnetapi/UrlContext.cpp +++ b/src/kits/network/libnetapi/UrlContext.cpp @@ -9,11 +9,35 @@ #include <UrlContext.h> +#include <stdio.h> + +#include <HashMap.h> +#include <HashString.h> + BUrlContext::BUrlContext() : - fCookieJar() + fCookieJar(), + fAuthenticationMap(NULL) { + fAuthenticationMap = new(std::nothrow) BHttpAuthenticationMap(); + if(!fAuthenticationMap) + return; + // This is the default authentication, used when nothing else is found. + // The empty string used as a key will match all the domain strings, once + // we have removed all components. + fAuthenticationMap->Put(HashString("", 0), new BHttpAuthentication()); +} + + +BUrlContext::~BUrlContext() +{ + BHttpAuthenticationMap::Iterator iterator = + fAuthenticationMap->GetIterator(); + while(iterator.HasNext()) + delete iterator.Remove().value; + + delete fAuthenticationMap; } @@ -27,6 +51,21 @@ BUrlContext::SetCookieJar(const BNetworkCookieJar& cookieJar) } +void +BUrlContext::AddAuthentication(const BUrl& url, + BHttpAuthentication* const authentication) +{ + BString domain = url.Host(); + domain += url.Path(); + BPrivate::HashString hostHash(domain.String(), domain.Length()); + + delete fAuthenticationMap->Get(hostHash); + // Make sure we don't leak memory by overriding a previous + // authentication for the same domain. + fAuthenticationMap->Put(hostHash, authentication); +} + + // #pragma mark Context accessors @@ -35,3 +74,23 @@ BUrlContext::GetCookieJar() { return fCookieJar; } + + +BHttpAuthentication& +BUrlContext::GetAuthentication(const BUrl& url) +{ + BString domain = url.Host(); + domain += url.Path(); + + BHttpAuthentication* authentication = NULL; + + do { + authentication = fAuthenticationMap->Get( HashString(domain.String(), + domain.Length())); + + domain.Truncate(domain.FindLast('/')); + + } while(authentication == NULL); + + return *authentication; +}