diff --git a/headers/os/net/HttpRequest.h b/headers/os/net/HttpRequest.h index 2c4248cac2..d18222a1b0 100644 --- a/headers/os/net/HttpRequest.h +++ b/headers/os/net/HttpRequest.h @@ -9,7 +9,6 @@ #include -#include #include #include #include @@ -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 414a371106..6a4541f073 100644 --- a/headers/os/net/UrlContext.h +++ b/headers/os/net/UrlContext.h @@ -6,22 +6,36 @@ #define _B_URL_CONTEXT_H_ +#include #include +namespace BPrivate { + template 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 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 7b19990f85..6330a3025c 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; - newRequest = false; - if (fOptUsername.Length() > 0 - && fAuthentication.Initialize(fHeaders["WWW-Authenticate"]) - == B_OK) { - fAuthentication.SetUserName(fOptUsername); - fAuthentication.SetPassword(fOptPassword); - newRequest = true; - } - break; + 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 && 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 a8693043c2..429eb5918d 100644 --- a/src/kits/network/libnetapi/UrlContext.cpp +++ b/src/kits/network/libnetapi/UrlContext.cpp @@ -9,11 +9,35 @@ #include +#include + +#include +#include + 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; +}