Make handling of Http Authentication thread safe

* Each BHttpAuthentication object is locked on all field accesses,
* They are owned by the BUrlContext and never deleted, so there is no
need for reference-counting them,
* The BUrlContext itself is now reference counted, and all BUrlRequests
hold a reference to it.

This makes sure using the BHttpAuthentication objects from requests is
thread-safe.
This commit is contained in:
Adrien Destugues 2014-06-11 14:11:01 +02:00
parent 463ffbfde4
commit 895fa41e0b
6 changed files with 50 additions and 27 deletions

View File

@ -1,13 +1,14 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Copyright 2010-2014 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _B_HTTP_AUTHENTICATION_H_
#define _B_HTTP_AUTHENTICATION_H_
#include <Url.h>
#include <Locker.h>
#include <String.h>
#include <Url.h>
// HTTP authentication method
enum BHttpAuthenticationMethod {
@ -89,6 +90,8 @@ private:
BHttpAuthenticationQop fDigestQop;
BString fAuthorizationString;
mutable BLocker fLock;
};
#endif // _B_HTTP_AUTHENTICATION_H_

View File

@ -1,5 +1,5 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Copyright 2010-2014 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _B_URL_CONTEXT_H_
@ -8,15 +8,16 @@
#include <HttpAuthentication.h>
#include <NetworkCookieJar.h>
#include <Referenceable.h>
namespace BPrivate {
template <class key, class value> class HashMap;
template <class key, class value> class SynchronizedHashMap;
class HashString;
}
class BUrlContext {
class BUrlContext: public BReferenceable {
public:
BUrlContext();
~BUrlContext();
@ -25,7 +26,7 @@ public:
void SetCookieJar(
const BNetworkCookieJar& cookieJar);
void AddAuthentication(const BUrl& url,
BHttpAuthentication* const authentication);
const BHttpAuthentication& authentication);
// Context accessors
BNetworkCookieJar& GetCookieJar();
@ -33,7 +34,7 @@ public:
private:
BNetworkCookieJar fCookieJar;
typedef BPrivate::HashMap<BPrivate::HashString,
typedef BPrivate::SynchronizedHashMap<BPrivate::HashString,
BHttpAuthentication*> BHttpAuthenticationMap;
BHttpAuthenticationMap* fAuthenticationMap;
};

View File

@ -1,5 +1,5 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Copyright 2010-2014 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _B_URL_REQUEST_H_
@ -11,6 +11,7 @@
#include <UrlProtocolListener.h>
#include <UrlResult.h>
#include <OS.h>
#include <Referenceable.h>
class BUrlRequest {
@ -52,7 +53,7 @@ protected:
const char* format, ...);
protected:
BUrl fUrl;
BUrlContext* fContext;
BReference<BUrlContext> fContext;
BUrlProtocolListener* fListener;
bool fQuit;

View File

@ -9,9 +9,12 @@
#include <HttpAuthentication.h>
#include <cstdlib>
#include <stdlib.h>
#include <stdio.h>
#include <AutoLocker.h>
#include <cstdio>
#if DEBUG > 0
#define PRINT(x) printf x
#else
@ -56,27 +59,35 @@ BHttpAuthentication::BHttpAuthentication(const BString& username, const BString&
void
BHttpAuthentication::SetUserName(const BString& username)
{
fLock.Lock();
fUserName = username;
fLock.Unlock();
}
void
BHttpAuthentication::SetPassword(const BString& password)
{
fLock.Lock();
fPassword = password;
fLock.Unlock();
}
void
BHttpAuthentication::SetMethod(BHttpAuthenticationMethod method)
{
fLock.Lock();
fAuthenticationMethod = method;
fLock.Unlock();
}
status_t
BHttpAuthentication::Initialize(const BString& wwwAuthenticate)
{
BPrivate::AutoLocker<BLocker> lock(fLock);
fAuthenticationMethod = B_HTTP_AUTHENTICATION_NONE;
fDigestQop = B_HTTP_QOP_NONE;
@ -171,6 +182,7 @@ BHttpAuthentication::Initialize(const BString& wwwAuthenticate)
const BString&
BHttpAuthentication::UserName() const
{
BPrivate::AutoLocker<BLocker> lock(fLock);
return fUserName;
}
@ -178,6 +190,7 @@ BHttpAuthentication::UserName() const
const BString&
BHttpAuthentication::Password() const
{
BPrivate::AutoLocker<BLocker> lock(fLock);
return fPassword;
}
@ -185,6 +198,7 @@ BHttpAuthentication::Password() const
BHttpAuthenticationMethod
BHttpAuthentication::Method() const
{
BPrivate::AutoLocker<BLocker> lock(fLock);
return fAuthenticationMethod;
}
@ -192,6 +206,7 @@ BHttpAuthentication::Method() const
BString
BHttpAuthentication::Authorization(const BUrl& url, const BString& method) const
{
BPrivate::AutoLocker<BLocker> lock(fLock);
BString authorizationString;
switch (fAuthenticationMethod) {

View File

@ -378,15 +378,14 @@ BHttpRequest::_ProtocolLoop()
if (authentication->Method() == B_HTTP_AUTHENTICATION_NONE) {
// There is no authentication context for this
// url yet, so let's create one.
authentication
= new(std::nothrow) BHttpAuthentication();
if (authentication == NULL)
status = B_NO_MEMORY;
else {
status = authentication->Initialize(
fHeaders["WWW-Authenticate"]);
fContext->AddAuthentication(fUrl, authentication);
}
BHttpAuthentication newAuth;
newAuth.Initialize(fHeaders["WWW-Authenticate"]);
fContext->AddAuthentication(fUrl, newAuth);
// Get the copy of the authentication we just added.
// That copy is owned by the BUrlContext and won't be
// deleted (unlike the temporary object above)
authentication = &fContext->GetAuthentication(fUrl);
}
newRequest = false;

View File

@ -52,21 +52,25 @@ BUrlContext::SetCookieJar(const BNetworkCookieJar& cookieJar)
void
BUrlContext::AddAuthentication(const BUrl& url,
BHttpAuthentication* const authentication)
const BHttpAuthentication& authentication)
{
BString domain = url.Host();
domain += url.Path();
BPrivate::HashString hostHash(domain.String(), domain.Length());
fAuthenticationMap->Lock();
BHttpAuthentication* previous = fAuthenticationMap->Get(hostHash);
// Make sure we don't leak memory by overriding a previous
// authentication for the same domain.
if (authentication != previous) {
fAuthenticationMap->Put(hostHash, authentication);
// replaces the old one, or adds it in case previous == NULL
delete previous;
if (previous)
*previous = authentication;
else {
BHttpAuthentication* copy
= new(std::nothrow) BHttpAuthentication(authentication);
fAuthenticationMap->Put(hostHash, copy);
}
fAuthenticationMap->Unlock();
}