landing framework changes to support NTLM authentication b=159015 r=dougt,cathleen sr=alecf

git-svn-id: svn://10.0.0.236/trunk@140336 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
darin%netscape.com 2003-03-26 05:05:49 +00:00
parent e1a6768967
commit 2a07b88e40
19 changed files with 1140 additions and 597 deletions

View File

@ -25,42 +25,137 @@
interface nsIHttpChannel;
[scriptable, uuid(5c6a8dda-0417-4694-807d-aa504699224e)]
/**
* nsIHttpAuthenticator
*
* Interface designed to allow for pluggable HTTP authentication modules.
* Implementations are registered under the ContractID:
*
* "@mozilla.org/network/http-authenticator;1?scheme=<auth-scheme>"
*
* where <auth-scheme> is the lower-cased value of the authentication scheme
* found in the server challenge per the rules of RFC 2617.
*/
[scriptable, uuid(0713691d-0d4a-49d2-8050-3219b032aa97)]
interface nsIHttpAuthenticator : nsISupports
{
/**
* Called to generate the authentication credentials for a particular
* server/proxy challenge.
* Upon receipt of a server challenge, this function is called to determine
* whether or not the current user identity has been rejected. If true,
* then the user will be prompted by the channel to enter (or revise) their
* identity. Following this, generateCredentials will be called.
*
* @param channel - the http channel requesting credentials
* @param challenge - the server specified auth challenge
* @param username - the username from which to generate credentials
* @param password - the password from which to generate credentials
* @param extra - additional information stored in the auth cache
* If the IDENTITY_IGNORED auth flag is set, then the aInvalidateIdentity
* return value will be ignored, and user prompting will be suppressed.
*
* @param aChannel
* the http channel that received the challenge.
* @param aChallenge
* the challenge from the WWW-Authenticate/Proxy-Authenticate
* server response header. (possibly from the auth cache.)
* @param aSessionState
* see description below for generateCredentials.
* @param aContinuationState
* see description below for generateCredentials.
* @param aInvalidateIdentity
* return value indicating whether or not to prompt the user for a
* revised identity.
*/
string generateCredentials(in nsIHttpChannel channel,
in string challenge,
in wstring username,
in wstring password,
in nsISupports metadata);
void challengeReceived(in nsIHttpChannel aChannel,
in string aChallenge,
inout nsISupports aSessionState,
inout nsISupports aContinuationState,
out boolean aInvalidatesIdentity);
/**
* indicates if credentials returned from GenerateCredentials can be reused
* Called to generate the authentication credentials for a particular
* server/proxy challenge. This is the value that will be sent back
* to the server via an Authorization/Proxy-Authorization header.
*
* This function may be called using a cached challenge provided the
* authenticator sets the REUSABLE_CHALLENGE flag.
*
* @param aChannel
* the http channel requesting credentials
* @param aChallenge
* the challenge from the WWW-Authenticate/Proxy-Authenticate
* server response header. (possibly from the auth cache.)
* @param aDomain
* string containing the domain name (if appropriate)
* @param aUser
* string containing the user name
* @param aPassword
* string containing the password
* @param aSessionState
* state stored along side the user's identity in the auth cache
* for the lifetime of the browser session. if a new auth cache
* entry is created for this challenge, then this parameter will
* be null. on return, the result will be stored in the new auth
* cache entry. this parameter is non-null when an auth cache entry
* is being reused.
* @param aContinuationState
* state held by the channel between consecutive calls to
* generateCredentials, assuming multiple calls are required
* to authenticate. this state is held for at most the lifetime of
* the channel.
*/
boolean areCredentialsReusable();
string generateCredentials(in nsIHttpChannel aChannel,
in string aChallenge,
in wstring aDomain,
in wstring aUser,
in wstring aPassword,
inout nsISupports aSessionState,
inout nsISupports aContinuationState);
/**
* indicates if the challenge requires a new username/password (e.g.,
* if the response was invalid because the nonce has timed out, then
* the old username/password is still valid).
* Flags defining various properties of the authenticator.
*/
boolean challengeRequiresUserPass(in string challenge);
readonly attribute unsigned long authFlags;
/**
* allocate authenticator specific metadata implementation. implementations
* that don't need to store extra data in the auth cache can simply return NULL.
* A request based authentication scheme only authenticates an individual
* request (or a set of requests under the same authentication domain as
* defined by RFC 2617). BASIC and DIGEST are request based authentication
* schemes.
*/
nsISupports allocateMetaData();
const unsigned long REQUEST_BASED = (1<<0);
/**
* A connection based authentication scheme authenticates an individual
* connection. Multiple requests may be issued over the connection without
* repeating the authentication steps. Connection based authentication
* schemes can associate state with the connection being authenticated via
* the aContinuationState parameter (see generateCredentials).
*/
const unsigned long CONNECTION_BASED = (1<<1);
/**
* The credentials returned from generateCredentials may be reused with any
* other URLs within "the protection space" as defined by RFC 2617 section
* 1.2. If this flag is not set, then generateCredentials must be called
* for each request within the protection space. REUSABLE_CREDENTIALS
* implies REUSABLE_CHALLENGE.
*/
const unsigned long REUSABLE_CREDENTIALS = (1<<2);
/**
* A challenge may be reused to later generate credentials in anticipation
* of a duplicate server challenge for URLs within "the protection space"
* as defined by RFC 2617 section 1.2.
*/
const unsigned long REUSABLE_CHALLENGE = (1<<3);
/**
* This flag indicates that the identity of the user is not required by
* this authentication scheme.
*/
const unsigned long IDENTITY_IGNORED = (1<<10);
/**
* This flag indicates that the identity of the user includes a domain
* attribute that the user must supply.
*/
const unsigned long IDENTITY_INCLUDES_DOMAIN = (1<<11);
};
%{C++

View File

@ -107,4 +107,15 @@ public:
virtual nsresult PushBack(const char *data, PRUint32 length) = 0;
};
#define NS_DECL_NSAHTTPCONNECTION \
nsresult OnHeadersAvailable(nsAHttpTransaction *, nsHttpRequestHead *, nsHttpResponseHead *, PRBool *reset); \
nsresult ResumeSend(); \
nsresult ResumeRecv(); \
void CloseTransaction(nsAHttpTransaction *, nsresult); \
void GetConnectionInfo(nsHttpConnectionInfo **); \
void GetSecurityInfo(nsISupports **); \
PRBool IsPersistent(); \
PRBool IsReused(); \
nsresult PushBack(const char *, PRUint32);
#endif // nsAHttpConnection_h__

View File

@ -86,6 +86,17 @@ public:
virtual void Close(nsresult reason) = 0;
};
#define NS_DECL_NSAHTTPTRANSACTION \
void SetConnection(nsAHttpConnection *); \
void GetSecurityCallbacks(nsIInterfaceRequestor **); \
void OnTransportStatus(nsresult status, PRUint32 progress); \
PRBool IsDone(); \
nsresult Status(); \
PRUint32 Available(); \
nsresult ReadSegments(nsAHttpSegmentReader *, PRUint32, PRUint32 *); \
nsresult WriteSegments(nsAHttpSegmentWriter *, PRUint32, PRUint32 *); \
void Close(nsresult reason);
//-----------------------------------------------------------------------------
// nsAHttpSegmentReader
//-----------------------------------------------------------------------------
@ -99,6 +110,9 @@ public:
PRUint32 *countRead) = 0;
};
#define NS_DECL_NSAHTTPSEGMENTREADER \
nsresult OnReadSegment(const char *, PRUint32, PRUint32 *);
//-----------------------------------------------------------------------------
// nsAHttpSegmentWriter
//-----------------------------------------------------------------------------
@ -112,4 +126,7 @@ public:
PRUint32 *countWritten) = 0;
};
#define NS_DECL_NSAHTTPSEGMENTWRITER \
nsresult OnWriteSegment(char *, PRUint32, PRUint32 *);
#endif // nsAHttpTransaction_h__

View File

@ -76,10 +76,20 @@ extern PRLogModuleInfo *gHttpLog;
typedef PRUint8 nsHttpVersion;
//-----------------------------------------------------------------------------
// http connection capabilities
//-----------------------------------------------------------------------------
#define NS_HTTP_ALLOW_KEEPALIVE (1<<0)
#define NS_HTTP_ALLOW_PIPELINING (1<<1)
#define NS_HTTP_DONT_REPORT_PROGRESS (1<<2)
// a transaction with this caps flag will continue to own the connection,
// preventing it from being reclaimed, even after the transaction completes.
#define NS_HTTP_STICKY_CONNECTION (1<<2)
//-----------------------------------------------------------------------------
// some default values
//-----------------------------------------------------------------------------
// hard upper limit on the number of requests that can be pipelined
#define NS_HTTP_MAX_PIPELINED_REQUESTS 8

View File

@ -28,6 +28,29 @@
#include "nsCRT.h"
#include "prprf.h"
static inline void
GetAuthKey(const char *host, PRInt32 port, nsCString &key)
{
key.Assign(host);
key.Append(':');
key.AppendInt(port);
}
// return true if the two strings are equal or both empty. an empty string
// is either null or zero length.
static PRBool
StrEquivalent(const PRUnichar *a, const PRUnichar *b)
{
static const PRUnichar emptyStr[] = {0};
if (!a)
a = emptyStr;
if (!b)
b = emptyStr;
return nsCRT::strcmp(a, b) == 0;
}
//-----------------------------------------------------------------------------
// nsHttpAuthCache <public>
//-----------------------------------------------------------------------------
@ -73,7 +96,8 @@ nsHttpAuthCache::GetAuthEntryForPath(const char *host,
if (!node)
return NS_ERROR_NOT_AVAILABLE;
return node->GetAuthEntryForPath(path, entry);
*entry = node->LookupEntryByPath(path);
return *entry ? NS_OK : NS_ERROR_NOT_AVAILABLE;
}
nsresult
@ -91,7 +115,8 @@ nsHttpAuthCache::GetAuthEntryForDomain(const char *host,
if (!node)
return NS_ERROR_NOT_AVAILABLE;
return node->GetAuthEntryForRealm(realm, entry);
*entry = node->LookupEntryByRealm(realm);
return *entry ? NS_OK : NS_ERROR_NOT_AVAILABLE;
}
nsresult
@ -100,9 +125,8 @@ nsHttpAuthCache::SetAuthEntry(const char *host,
const char *path,
const char *realm,
const char *creds,
const PRUnichar *user,
const PRUnichar *pass,
const char *challenge,
const nsHttpAuthIdentity &ident,
nsISupports *metadata)
{
nsresult rv;
@ -119,15 +143,11 @@ nsHttpAuthCache::SetAuthEntry(const char *host,
nsHttpAuthNode *node = LookupAuthNode(host, port, key);
if (!node) {
// only create a new node if we have a real entry
if (!creds && !user && !pass && !challenge)
return NS_OK;
// create a new entry node and set the given entry
node = new nsHttpAuthNode();
if (!node)
return NS_ERROR_OUT_OF_MEMORY;
rv = node->SetAuthEntry(path, realm, creds, user, pass, challenge, metadata);
rv = node->SetAuthEntry(path, realm, creds, challenge, ident, metadata);
if (NS_FAILED(rv))
delete node;
else
@ -135,13 +155,20 @@ nsHttpAuthCache::SetAuthEntry(const char *host,
return rv;
}
rv = node->SetAuthEntry(path, realm, creds, user, pass, challenge, metadata);
if (NS_SUCCEEDED(rv) && (node->EntryCount() == 0)) {
// the node has no longer has any entries
PL_HashTableRemove(mDB, key.get());
}
return node->SetAuthEntry(path, realm, creds, challenge, ident, metadata);
}
return rv;
void
nsHttpAuthCache::ClearAuthEntry(const char *host,
PRInt32 port,
const char *realm)
{
if (!mDB)
return;
nsCAutoString key;
GetAuthKey(host, port, key);
PL_HashTableRemove(mDB, key.get());
}
nsresult
@ -161,18 +188,12 @@ nsHttpAuthCache::ClearAll()
//-----------------------------------------------------------------------------
nsHttpAuthNode *
nsHttpAuthCache::LookupAuthNode(const char *host, PRInt32 port, nsAFlatCString &key)
nsHttpAuthCache::LookupAuthNode(const char *host, PRInt32 port, nsCString &key)
{
char buf[32];
if (!mDB)
return nsnull;
PR_snprintf(buf, sizeof(buf), "%d", port);
key.Assign(host);
key.Append(':');
key.Append(buf);
GetAuthKey(host, port, key);
return (nsHttpAuthNode *) PL_HashTableLookup(mDB, key.get());
}
@ -220,35 +241,135 @@ PLHashAllocOps nsHttpAuthCache::gHashAllocOps =
nsHttpAuthCache::FreeEntry
};
//-----------------------------------------------------------------------------
// nsHttpAuthIdentity
//-----------------------------------------------------------------------------
nsresult
nsHttpAuthIdentity::Set(const PRUnichar *domain,
const PRUnichar *user,
const PRUnichar *pass)
{
PRUnichar *newUser, *newPass, *newDomain;
int domainLen = domain ? nsCRT::strlen(domain) : 0;
int userLen = user ? nsCRT::strlen(user) : 0;
int passLen = pass ? nsCRT::strlen(pass) : 0;
int len = userLen + 1 + passLen + 1 + domainLen + 1;
newUser = (PRUnichar *) malloc(len * sizeof(PRUnichar));
if (!newUser)
return NS_ERROR_OUT_OF_MEMORY;
if (user)
memcpy(newUser, user, userLen * sizeof(PRUnichar));
newUser[userLen] = 0;
newPass = &newUser[userLen + 1];
if (pass)
memcpy(newPass, pass, passLen * sizeof(PRUnichar));
newPass[passLen] = 0;
newDomain = &newPass[passLen + 1];
if (domain)
memcpy(newDomain, domain, domainLen * sizeof(PRUnichar));
newDomain[domainLen] = 0;
// wait until the end to clear member vars in case input params
// reference our members!
if (mUser)
free(mUser);
mUser = newUser;
mPass = newPass;
mDomain = newDomain;
return NS_OK;
}
void
nsHttpAuthIdentity::Clear()
{
if (mUser) {
free(mUser);
mUser = nsnull;
mPass = nsnull;
mDomain = nsnull;
}
}
PRBool
nsHttpAuthIdentity::Equals(const nsHttpAuthIdentity &ident) const
{
// we could probably optimize this with a single loop, but why bother?
return StrEquivalent(mUser, ident.mUser) &&
StrEquivalent(mPass, ident.mPass) &&
StrEquivalent(mDomain, ident.mDomain);
}
//-----------------------------------------------------------------------------
// nsHttpAuthEntry
//-----------------------------------------------------------------------------
nsHttpAuthEntry::nsHttpAuthEntry(const char *path, const char *realm,
const char *creds, const PRUnichar *user,
const PRUnichar *pass, const char *challenge,
nsISupports *metadata)
: mPath(strdup_if(path))
, mRealm(strdup_if(realm))
, mCreds(strdup_if(creds))
, mUser(strdup_if(user))
, mPass(strdup_if(pass))
, mChallenge(strdup_if(challenge))
, mMetaData(metadata)
{
LOG(("Creating nsHttpAuthCache::nsEntry @%x\n", this));
}
nsHttpAuthEntry::~nsHttpAuthEntry()
{
LOG(("Destroying nsHttpAuthCache::nsEntry @%x\n", this));
if (mPath)
free(mPath);
}
CRTFREEIF(mPath);
CRTFREEIF(mRealm);
CRTFREEIF(mCreds);
CRTFREEIF(mUser);
CRTFREEIF(mPass);
CRTFREEIF(mChallenge);
nsresult
nsHttpAuthEntry::Set(const char *path,
const char *realm,
const char *creds,
const char *chall,
const nsHttpAuthIdentity &ident,
nsISupports *metadata)
{
char *newPath, *newRealm, *newCreds, *newChall;
int pathLen = path ? nsCRT::strlen(path) : 0;
int realmLen = realm ? nsCRT::strlen(realm) : 0;
int credsLen = creds ? nsCRT::strlen(creds) : 0;
int challLen = chall ? nsCRT::strlen(chall) : 0;
int len = pathLen + 1 + realmLen + 1 + credsLen + 1 + challLen + 1;
newPath = (char *) malloc(len);
if (!newPath)
return NS_ERROR_OUT_OF_MEMORY;
if (path)
memcpy(newPath, path, pathLen);
newPath[pathLen] = 0;
newRealm = &newPath[pathLen + 1];
if (realm)
memcpy(newRealm, realm, realmLen);
newRealm[realmLen] = 0;
newCreds = &newRealm[realmLen + 1];
if (creds)
memcpy(newCreds, creds, credsLen);
newCreds[credsLen] = 0;
newChall = &newCreds[credsLen + 1];
if (chall)
memcpy(newChall, chall, challLen);
newChall[challLen] = 0;
nsresult rv = mIdent.Set(ident);
if (NS_FAILED(rv)) {
free(newPath);
return rv;
}
// wait until the end to clear member vars in case input params
// reference our members!
if (mPath)
free(mPath);
mPath = newPath;
mRealm = newRealm;
mCreds = newCreds;
mChallenge = newChall;
mMetaData = metadata;
return NS_OK;
}
//-----------------------------------------------------------------------------
@ -270,90 +391,67 @@ nsHttpAuthNode::~nsHttpAuthNode()
mList.Clear();
}
nsresult
nsHttpAuthNode::GetAuthEntryForPath(const char *path,
nsHttpAuthEntry **result)
nsHttpAuthEntry *
nsHttpAuthNode::LookupEntryByPath(const char *path)
{
*result = nsnull;
nsHttpAuthEntry *entry;
// null path matches empty path
if (!path)
path = "";
// look for an entry that either matches or contains this directory.
// ie. we'll give out credentials if the given directory is a sub-
// directory of an existing entry.
for (PRInt32 i=0; i<mList.Count(); ++i) {
nsHttpAuthEntry *entry = (nsHttpAuthEntry *) mList[i];
entry = (nsHttpAuthEntry *) mList[i];
const char *entryPath = entry->Path();
// path's can be empty (even NULL)
if (!path || !*path) {
if (!entryPath || !*entryPath) {
*result = entry;
break;
}
}
else if (!entryPath || !*entryPath)
continue;
else if (!nsCRT::strncmp(path, entryPath, (unsigned int)strlen(entryPath))) {
*result = entry;
break;
// proxy auth entries have no path, so require exact match on
// empty path string.
if (entryPath[0] == '\0') {
if (path[0] == '\0')
return entry;
}
else if (strncmp(path, entryPath, strlen(entryPath)) == 0)
return entry;
}
return *result ? NS_OK : NS_ERROR_NOT_AVAILABLE;
return nsnull;
}
nsresult
nsHttpAuthNode::GetAuthEntryForRealm(const char *realm,
nsHttpAuthEntry **entry)
nsHttpAuthEntry *
nsHttpAuthNode::LookupEntryByRealm(const char *realm)
{
NS_ENSURE_ARG_POINTER(realm);
nsHttpAuthEntry *entry;
*entry = nsnull;
// null realm matches empty realm
if (!realm)
realm = "";
// look for an entry that matches this realm
PRInt32 i;
for (i=0; i<mList.Count(); ++i) {
*entry = (nsHttpAuthEntry *) mList[i];
if (!nsCRT::strcmp(realm, (*entry)->Realm()))
break;
*entry = nsnull;
entry = (nsHttpAuthEntry *) mList[i];
if (strcmp(realm, entry->Realm()) == 0)
return entry;
}
return *entry ? NS_OK : NS_ERROR_NOT_AVAILABLE;
return nsnull;
}
nsresult
nsHttpAuthNode::SetAuthEntry(const char *path,
const char *realm,
const char *creds,
const PRUnichar *user,
const PRUnichar *pass,
const char *challenge,
const nsHttpAuthIdentity &ident,
nsISupports *metadata)
{
NS_ENSURE_ARG_POINTER(realm);
nsHttpAuthEntry *entry = nsnull;
// look for an entry with a matching realm
PRInt32 i;
for (i=0; i<mList.Count(); ++i) {
entry = (nsHttpAuthEntry *) mList[i];
if (!nsCRT::strcmp(realm, entry->Realm()))
break;
entry = nsnull;
}
nsHttpAuthEntry *entry = LookupEntryByRealm(realm);
if (!entry) {
if (creds || user || pass || challenge) {
entry = new nsHttpAuthEntry(path, realm, creds, user, pass, challenge, metadata);
if (!entry)
return NS_ERROR_OUT_OF_MEMORY;
mList.AppendElement(entry);
}
// else, nothing to do
}
else if (!creds && !user && !pass && !challenge) {
mList.RemoveElementAt(i);
delete entry;
entry = new nsHttpAuthEntry(path, realm, creds, challenge, ident, metadata);
if (!entry)
return NS_ERROR_OUT_OF_MEMORY;
mList.AppendElement(entry);
}
else {
// update the entry...
@ -361,15 +459,21 @@ nsHttpAuthNode::SetAuthEntry(const char *path,
// we should hold onto the top-most of the two path
PRUint32 len1 = strlen(path);
PRUint32 len2 = strlen(entry->Path());
if (len1 < len2)
entry->SetPath(path);
if (len1 >= len2)
path = entry->Path(); // keep the old path
}
entry->SetCreds(creds);
entry->SetUser(user);
entry->SetPass(pass);
entry->SetChallenge(challenge);
entry->SetMetaData(metadata);
entry->Set(path, realm, creds, challenge, ident, metadata);
}
return NS_OK;
}
void
nsHttpAuthNode::ClearAuthEntry(const char *realm)
{
nsHttpAuthEntry *entry = LookupEntryByRealm(realm);
if (entry) {
mList.RemoveElement(entry); // double search OK
delete entry;
}
}

View File

@ -49,6 +49,51 @@
#include "plhash.h"
#include "nsCRT.h"
//-----------------------------------------------------------------------------
// nsHttpAuthIdentity
//-----------------------------------------------------------------------------
class nsHttpAuthIdentity
{
public:
nsHttpAuthIdentity()
: mUser(nsnull)
, mPass(nsnull)
, mDomain(nsnull)
{
}
nsHttpAuthIdentity(const PRUnichar *domain,
const PRUnichar *user,
const PRUnichar *password)
: mUser(nsnull)
{
Set(domain, user, password);
}
~nsHttpAuthIdentity()
{
Clear();
}
const PRUnichar *Domain() const { return mDomain; }
const PRUnichar *User() const { return mUser; }
const PRUnichar *Password() const { return mPass; }
nsresult Set(const PRUnichar *domain,
const PRUnichar *user,
const PRUnichar *password);
nsresult Set(const nsHttpAuthIdentity &other) { return Set(other.mDomain, other.mUser, other.mPass); }
void Clear();
PRBool Equals(const nsHttpAuthIdentity &other) const;
PRBool IsEmpty() const { return !mUser; }
private:
// allocated as one contiguous blob, starting at mUser.
PRUnichar *mUser;
PRUnichar *mPass;
PRUnichar *mDomain;
};
//-----------------------------------------------------------------------------
// nsHttpAuthEntry
//-----------------------------------------------------------------------------
@ -56,42 +101,48 @@
class nsHttpAuthEntry
{
public:
const char *Path() { return mPath; }
const char *Realm() { return mRealm; }
const char *Creds() { return mCreds; }
const PRUnichar *User() { return mUser; }
const PRUnichar *Pass() { return mPass; }
const char *Challenge() { return mChallenge; }
nsISupports *MetaData() { return mMetaData; }
const char *Path() const { return mPath; }
const char *Realm() const { return mRealm; }
const char *Creds() const { return mCreds; }
const char *Challenge() const { return mChallenge; }
const PRUnichar *Domain() const { return mIdent.Domain(); }
const PRUnichar *User() const { return mIdent.User(); }
const PRUnichar *Pass() const { return mIdent.Password(); }
const nsHttpAuthIdentity &Identity() const { return mIdent; }
nsCOMPtr<nsISupports> mMetaData;
private:
nsHttpAuthEntry(const char *path,
const char *realm,
const char *creds,
const PRUnichar *user,
const PRUnichar *pass,
const char *challenge,
nsISupports *metadata);
const nsHttpAuthIdentity &ident,
nsISupports *metadata)
: mPath(nsnull)
{
Set(path, realm, creds, challenge, ident, metadata);
}
~nsHttpAuthEntry();
void SetPath(const char *v) { CRTFREEIF(mPath); mPath = strdup_if(v); }
void SetCreds(const char *v) { CRTFREEIF(mCreds); mCreds = strdup_if(v); }
void SetUser(const PRUnichar *v) { CRTFREEIF(mUser); mUser = strdup_if(v); }
void SetPass(const PRUnichar *v) { CRTFREEIF(mPass); mPass = strdup_if(v); }
void SetChallenge(const char *v) { CRTFREEIF(mChallenge); mChallenge = strdup_if(v); }
void SetMetaData(nsISupports *v) { mMetaData = v; }
private:
char *mPath;
char *mRealm;
char *mCreds;
PRUnichar *mUser;
PRUnichar *mPass;
char *mChallenge;
nsCOMPtr<nsISupports> mMetaData;
nsresult Set(const char *path,
const char *realm,
const char *creds,
const char *challenge,
const nsHttpAuthIdentity &ident,
nsISupports *metadata);
nsHttpAuthIdentity mIdent;
// allocated together in one blob, starting with mPath.
char *mPath;
char *mRealm;
char *mCreds;
char *mChallenge;
friend class nsHttpAuthCache;
friend class nsHttpAuthNode;
friend class nsHttpAuthCache;
};
//-----------------------------------------------------------------------------
@ -106,22 +157,21 @@ private:
// path can be null, in which case we'll search for an entry
// with a null path.
nsresult GetAuthEntryForPath(const char *path,
nsHttpAuthEntry **entry);
nsHttpAuthEntry *LookupEntryByPath(const char *path);
// realm must not be null
nsresult GetAuthEntryForRealm(const char *realm,
nsHttpAuthEntry **entry);
nsHttpAuthEntry *LookupEntryByRealm(const char *realm);
// if a matching entry is found, then credentials will be changed.
nsresult SetAuthEntry(const char *path,
const char *realm,
const char *credentials,
const PRUnichar *user,
const PRUnichar *pass,
const char *challenge,
const nsHttpAuthIdentity &ident,
nsISupports *metadata);
void ClearAuthEntry(const char *realm);
PRUint32 EntryCount() { return (PRUint32) mList.Count(); }
private:
@ -169,16 +219,19 @@ public:
const char *directory,
const char *realm,
const char *credentials,
const PRUnichar *user,
const PRUnichar *pass,
const char *challenge,
const nsHttpAuthIdentity &ident,
nsISupports *metadata);
void ClearAuthEntry(const char *host,
PRInt32 port,
const char *realm);
// expire all existing auth list entries including proxy auths.
nsresult ClearAll();
private:
nsHttpAuthNode *LookupAuthNode(const char *host, PRInt32 port, nsAFlatCString &key);
nsHttpAuthNode *LookupAuthNode(const char *host, PRInt32 port, nsCString &key);
// hash table allocation functions
static void* PR_CALLBACK AllocTable(void *, PRSize size);

View File

@ -111,15 +111,18 @@ nsHttpAuthManager::SetAuthIdentity(const nsACString & aHost,
const nsAString & aUserName,
const nsAString & aUserPassword)
{
nsHttpAuthIdentity ident(PromiseFlatString(aUserDomain).get(),
PromiseFlatString(aUserName).get(),
PromiseFlatString(aUserPassword).get());
return mAuthCache->SetAuthEntry(PromiseFlatCString(aHost).get(),
aPort,
PromiseFlatCString(aPath).get(),
PromiseFlatCString(aRealm).get(),
nsnull,
PromiseFlatString(aUserName).get(),
PromiseFlatString(aUserPassword).get(),
nsnull,
nsnull);
nsnull, // credentials
nsnull, // challenge
ident,
nsnull); // metadata
}
NS_IMETHODIMP

View File

@ -69,12 +69,27 @@ NS_IMPL_ISUPPORTS1(nsHttpBasicAuth, nsIHttpAuthenticator);
// nsHttpBasicAuth::nsIHttpAuthenticator
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsHttpBasicAuth::ChallengeReceived(nsIHttpChannel *httpChannel,
const char *challenge,
nsISupports **sessionState,
nsISupports **continuationState,
PRBool *identityInvalid)
{
// if challenged, then the username:password that was sent must
// have been wrong.
*identityInvalid = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
nsHttpBasicAuth::GenerateCredentials(nsIHttpChannel *httpChannel,
const char *challenge,
const PRUnichar *username,
const PRUnichar *domain,
const PRUnichar *user,
const PRUnichar *password,
nsISupports *extra,
nsISupports **sessionState,
nsISupports **continuationState,
char **creds)
{
@ -88,47 +103,25 @@ nsHttpBasicAuth::GenerateCredentials(nsIHttpChannel *httpChannel,
// we work with ASCII around here
nsCAutoString userpass;
userpass.AssignWithConversion(username);
userpass.AssignWithConversion(user);
userpass.Append(':'); // always send a ':' (see bug 129565)
if (password)
userpass.AppendWithConversion(password);
char *b64userpass = PL_Base64Encode(userpass.get(),
userpass.Length(),
nsnull);
if (!b64userpass)
return NS_ERROR_OUT_OF_MEMORY;
// allocate a buffer sizeof("Basic" + " " + b64userpass + "\0")
*creds = (char *) malloc(6 + strlen(b64userpass) + 1);
// plbase64.h provides this worst-case output buffer size calculation.
// use calloc, since PL_Base64Encode does not null terminate.
*creds = (char *) calloc(6 + ((userpass.Length() + 2)/3)*4 + 1, 1);
if (!*creds)
return NS_ERROR_OUT_OF_MEMORY;
PL_strcpy(*creds, "Basic ");
PL_strcpy(*creds + 6, b64userpass);
PR_Free(b64userpass);
memcpy(*creds, "Basic ", 6);
PL_Base64Encode(userpass.get(), userpass.Length(), *creds + 6);
return NS_OK;
}
NS_IMETHODIMP
nsHttpBasicAuth::AreCredentialsReusable(PRBool *result)
nsHttpBasicAuth::GetAuthFlags(nsresult *flags)
{
*result = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
nsHttpBasicAuth::ChallengeRequiresUserPass(const char *challenge,
PRBool *result)
{
*result = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
nsHttpBasicAuth::AllocateMetaData(nsISupports **result)
{
*result = nsnull;
*flags = REQUEST_BASED | REUSABLE_CREDENTIALS | REUSABLE_CHALLENGE;
return NS_OK;
}

View File

@ -45,12 +45,24 @@
#include "nsString.h"
#include "nsPrintfCString.h"
#include "nsReadableUtils.h"
#include "nsAutoPtr.h"
#include "plstr.h"
#include "prprf.h"
#include "nsEscape.h"
static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID);
static NS_METHOD DiscardSegments(nsIInputStream *input,
void *closure,
const char *buf,
PRUint32 offset,
PRUint32 count,
PRUint32 *countRead)
{
*countRead = count;
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsHttpChannel <public>
//-----------------------------------------------------------------------------
@ -58,7 +70,6 @@ static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID);
nsHttpChannel::nsHttpChannel()
: mResponseHead(nsnull)
, mTransaction(nsnull)
, mPrevTransaction(nsnull)
, mConnectionInfo(nsnull)
, mLoadFlags(LOAD_NORMAL)
, mStatus(NS_OK)
@ -68,6 +79,7 @@ nsHttpChannel::nsHttpChannel()
, mCacheAccess(0)
, mPostID(0)
, mRequestTime(0)
, mAuthContinuationState(nsnull)
, mRedirectionLimit(gHttpHandler->RedirectionLimit())
, mIsPending(PR_FALSE)
, mApplyConversion(PR_TRUE)
@ -76,7 +88,9 @@ nsHttpChannel::nsHttpChannel()
, mCachedContentIsPartial(PR_FALSE)
, mResponseHeadersModified(PR_FALSE)
, mCanceled(PR_FALSE)
, mTransactionReplaced(PR_FALSE)
, mUploadStreamHasHeaders(PR_FALSE)
, mAuthRetryPending(PR_FALSE)
{
LOG(("Creating nsHttpChannel @%x\n", this));
@ -100,7 +114,8 @@ nsHttpChannel::~nsHttpChannel()
NS_IF_RELEASE(mConnectionInfo);
NS_IF_RELEASE(mTransaction);
NS_IF_RELEASE(mPrevTransaction);
NS_IF_RELEASE(mAuthContinuationState);
// release our reference to the handler
nsHttpHandler *handler = gHttpHandler;
@ -595,6 +610,7 @@ nsHttpChannel::CallOnStartRequest()
}
}
LOG((" calling mListener->OnStartRequest\n"));
nsresult rv = mListener->OnStartRequest(this, mListenerContext);
if (NS_FAILED(rv)) return rv;
@ -921,15 +937,12 @@ nsHttpChannel::ProcessNotModified()
rv = UpdateExpirationTime();
if (NS_FAILED(rv)) return rv;
// drop our reference to the current transaction... ie. let it finish
// in the background, since we can most likely reuse the connection.
mPrevTransaction = mTransaction;
mPrevTransactionPump = mTransactionPump;
mTransaction = nsnull;
mTransactionPump = 0;
mCachedContentIsValid = PR_TRUE;
return ReadFromCache();
rv = ReadFromCache();
if (NS_FAILED(rv)) return rv;
mTransactionReplaced = PR_TRUE;
return NS_OK;
}
nsresult
@ -1685,29 +1698,54 @@ nsHttpChannel::ProcessRedirection(PRUint32 redirectType)
// nsHttpChannel <auth>
//-----------------------------------------------------------------------------
// buf contains "domain\user"
static void ParseUserDomain(PRUnichar *buf,
const PRUnichar **user,
const PRUnichar **domain)
{
PRUnichar *p = buf;
while (*p && *p != '\\') ++p;
if (!*p)
return;
*p = '\0';
*domain = buf;
*user = p + 1;
}
// helper function for setting identity from raw user:pass
static void SetIdent(nsHttpAuthIdentity &ident,
PRUint32 authFlags,
PRUnichar *userBuf,
PRUnichar *passBuf)
{
const PRUnichar *user = userBuf;
const PRUnichar *domain = nsnull;
if (authFlags & nsIHttpAuthenticator::IDENTITY_INCLUDES_DOMAIN)
ParseUserDomain(userBuf, &user, &domain);
ident.Set(domain, user, passBuf);
}
nsresult
nsHttpChannel::ProcessAuthentication(PRUint32 httpStatus)
{
LOG(("nsHttpChannel::ProcessAuthentication [this=%x code=%u]\n",
this, httpStatus));
const char *challenge;
const char *challenges;
PRBool proxyAuth = (httpStatus == 407);
if (proxyAuth)
challenge = mResponseHead->PeekHeader(nsHttp::Proxy_Authenticate);
challenges = mResponseHead->PeekHeader(nsHttp::Proxy_Authenticate);
else
challenge = mResponseHead->PeekHeader(nsHttp::WWW_Authenticate);
challenges = mResponseHead->PeekHeader(nsHttp::WWW_Authenticate);
NS_ENSURE_TRUE(challenges, NS_ERROR_UNEXPECTED);
if (!challenge) {
LOG(("null challenge!\n"));
return NS_ERROR_UNEXPECTED;
}
LOG(("challenge=%s\n", challenge));
LOG((" challenge=%s\n", challenges));
nsCAutoString creds;
nsresult rv = GetCredentials(challenge, proxyAuth, creds);
nsresult rv = GetCredentials(challenges, proxyAuth, creds);
if (NS_FAILED(rv)) return rv;
// set the authentication credentials
@ -1716,40 +1754,9 @@ nsHttpChannel::ProcessAuthentication(PRUint32 httpStatus)
else
mRequestHead.SetHeader(nsHttp::Authorization, creds);
// kill off the current transaction
gHttpHandler->CancelTransaction(mTransaction, NS_BINDING_REDIRECTED);
mPrevTransaction = mTransaction;
mPrevTransactionPump = mTransactionPump;
mTransaction = nsnull;
mTransactionPump = 0;
// toggle mIsPending to allow nsIHttpNotify implementations to modify
// the request headers (bug 95044).
mIsPending = PR_FALSE;
// notify nsIHttpNotify implementations.. the server response could
// have included cookies that must be sent with this authentication
// attempt (bug 84794).
rv = gHttpHandler->OnModifyRequest(this);
NS_ASSERTION(NS_SUCCEEDED(rv), "OnModifyRequest failed");
mIsPending = PR_TRUE;
// and create a new one...
rv = SetupTransaction();
if (NS_FAILED(rv)) return rv;
// rewind the upload stream
if (mUploadStream) {
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream);
if (seekable)
seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
}
rv = gHttpHandler->InitiateTransaction(mTransaction);
if (NS_FAILED(rv)) return rv;
return mTransactionPump->AsyncRead(this, nsnull);
// see DoAuthRetry
mAuthRetryPending = PR_TRUE;
return NS_OK;
}
nsresult
@ -1766,43 +1773,53 @@ nsHttpChannel::GetCredentials(const char *challenges,
if (!authCache)
return NS_ERROR_NOT_INITIALIZED;
// proxy auth's never in prehost. only take user:pass from URL if this
// is the first 401 response (mUser and mPass hold previously attempted
// username and password).
if (!proxyAuth && (mUser == nsnull) && (mPass == nsnull))
GetUserPassFromURI(getter_Copies(mUser), getter_Copies(mPass));
// figure out which challenge we can handle and which authenticator to use.
nsCAutoString challenge;
nsCAutoString challenge, scheme;
nsCOMPtr<nsIHttpAuthenticator> auth;
rv = SelectChallenge(challenges, challenge, getter_AddRefs(auth));
if (!auth) {
rv = SelectChallenge(challenges, challenge, scheme, getter_AddRefs(auth));
if (NS_FAILED(rv)) {
LOG(("authentication type not supported\n"));
return NS_ERROR_FAILURE;
return rv;
}
PRUint32 authFlags;
rv = auth->GetAuthFlags(&authFlags);
if (NS_FAILED(rv)) return rv;
nsCAutoString realm;
ParseRealm(challenge.get(), realm);
// if no realm, then use the auth scheme as the realm. ToUpperCase so the
// ficticious realm stands out a bit more.
// XXX this will cause some single signon misses!
// XXX this will cause problems when we expose the auth cache to OJI!
// XXX this was meant to be used with NTLM, which supplies no realm.
/*
if (realm.IsEmpty()) {
realm = scheme;
ToUpperCase(realm);
}
*/
// proxy auth's never in prehost. only take user:pass from URL if this
// is the first 401 response (mIdent holds previously attempted identity).
if (!proxyAuth && mIdent.IsEmpty())
GetIdentityFromURI(authFlags, mIdent);
const char *host;
nsCAutoString path;
nsXPIDLString *user;
nsXPIDLString *pass;
PRInt32 port;
nsHttpAuthIdentity *ident;
nsCAutoString path;
if (proxyAuth) {
host = mConnectionInfo->ProxyHost();
port = mConnectionInfo->ProxyPort();
user = &mProxyUser;
pass = &mProxyPass;
ident = &mProxyIdent;
}
else {
host = mConnectionInfo->Host();
port = mConnectionInfo->Port();
user = &mUser;
pass = &mPass;
ident = &mIdent;
rv = GetCurrentPath(path);
if (NS_FAILED(rv)) return rv;
@ -1817,66 +1834,75 @@ nsHttpChannel::GetCredentials(const char *challenges,
nsHttpAuthEntry *entry = nsnull;
authCache->GetAuthEntryForDomain(host, port, realm.get(), &entry);
PRBool requireUserPass = PR_FALSE;
rv = auth->ChallengeRequiresUserPass(challenge.get(), &requireUserPass);
// hold reference to the auth session state (in case we clear our
// reference to the entry).
nsCOMPtr<nsISupports> sessionStateGrip;
if (entry)
sessionStateGrip = entry->mMetaData;
// for digest auth, maybe our cached nonce value simply timed out...
PRBool identityInvalid;
nsISupports *sessionState = sessionStateGrip;
rv = auth->ChallengeReceived(this,
challenge.get(),
&sessionState,
&mAuthContinuationState,
&identityInvalid);
sessionStateGrip.swap(sessionState);
if (NS_FAILED(rv)) return rv;
if (requireUserPass) {
if (identityInvalid) {
if (entry) {
if (user->Equals(entry->User()) && pass->Equals(entry->Pass())) {
LOG(("clearing bad credentials from the auth cache\n"));
// ok, we've already tried this user:pass combo, so clear the
if (ident->Equals(entry->Identity())) {
LOG((" clearing bad auth cache entry\n"));
// ok, we've already tried this user identity, so clear the
// corresponding entry from the auth cache.
ClearPasswordManagerEntry(host, port, realm.get(), entry->User());
authCache->SetAuthEntry(host, port, nsnull, realm.get(),
nsnull, nsnull, nsnull, nsnull, nsnull);
authCache->ClearAuthEntry(host, port, realm.get());
entry = nsnull;
user->Adopt(0);
pass->Adopt(0);
ident->Clear();
}
else {
LOG(("taking user:pass from auth cache\n"));
user->Adopt(nsCRT::strdup(entry->User()));
pass->Adopt(nsCRT::strdup(entry->Pass()));
if (entry->Creds()) {
LOG(("using cached credentials!\n"));
LOG((" taking identity from auth cache\n"));
ident->Set(entry->Identity());
if (entry->Creds()[0] != '\0') {
LOG((" using cached credentials!\n"));
creds.Assign(entry->Creds());
return NS_OK;
}
}
}
if (!entry && user->IsEmpty()) {
if (!entry && ident->IsEmpty()) {
// at this point we are forced to interact with the user to get their
// username and password for this domain.
rv = PromptForUserPass(host, port, proxyAuth, realm.get(),
getter_Copies(*user),
getter_Copies(*pass));
rv = PromptForIdentity(host, port, proxyAuth, realm.get(), scheme.get(), authFlags, *ident);
if (NS_FAILED(rv)) return rv;
}
}
// ask the auth cache for a container for any meta data it might want to
// store in the auth cache.
nsCOMPtr<nsISupports> metadata;
rv = auth->AllocateMetaData(getter_AddRefs(metadata));
if (NS_FAILED(rv)) return rv;
// get credentials for the given user:pass
nsXPIDLCString result;
sessionState = sessionStateGrip;
rv = auth->GenerateCredentials(this, challenge.get(),
user->get(), pass->get(), metadata,
ident->Domain(),
ident->User(),
ident->Password(),
&sessionState,
&mAuthContinuationState,
getter_Copies(result));
sessionStateGrip.swap(sessionState);
if (NS_FAILED(rv)) return rv;
// find out if this authenticator allows reuse of credentials
PRBool reusable;
rv = auth->AreCredentialsReusable(&reusable);
if (NS_FAILED(rv)) return rv;
LOG(("generated creds: %s\n", result.get()));
// let's try these credentials
creds = result;
// find out if this authenticator allows reuse of credentials
PRBool saveCreds = (authFlags & nsIHttpAuthenticator::REUSABLE_CREDENTIALS);
PRBool saveChallenge = (authFlags & nsIHttpAuthenticator::REUSABLE_CHALLENGE);
// create a cache entry. we do this even though we don't yet know that
// these credentials are valid b/c we need to avoid prompting the user more
// than once in case the credentials are valid.
@ -1884,18 +1910,17 @@ nsHttpChannel::GetCredentials(const char *challenges,
// if the credentials are not reusable, then we don't bother sticking them
// in the auth cache.
return authCache->SetAuthEntry(host, port, path.get(), realm.get(),
reusable ? creds.get() : nsnull,
user->get(), pass->get(),
challenge.get(), metadata);
saveCreds ? creds.get() : nsnull,
saveChallenge ? challenge.get() : nsnull,
*ident, sessionState);
}
nsresult
nsHttpChannel::SelectChallenge(const char *challenges,
nsAFlatCString &challenge,
nsCString &challenge,
nsCString &scheme,
nsIHttpAuthenticator **auth)
{
nsCAutoString scheme;
LOG(("nsHttpChannel::SelectChallenge [this=%x]\n", this));
// loop over the various challenges (LF separated)...
@ -1942,27 +1967,28 @@ nsHttpChannel::GetAuthenticator(const char *scheme, nsIHttpAuthenticator **auth)
}
void
nsHttpChannel::GetUserPassFromURI(PRUnichar **user,
PRUnichar **pass)
nsHttpChannel::GetIdentityFromURI(PRUint32 authFlags, nsHttpAuthIdentity &ident)
{
LOG(("nsHttpChannel::GetUserPassFromURI [this=%x]\n", this));
LOG(("nsHttpChannel::GetIdentityFromURI [this=%x]\n", this));
nsCAutoString buf;
*user = nsnull;
*pass = nsnull;
nsAutoString userBuf;
nsAutoString passBuf;
// XXX i18n
nsCAutoString buf;
mURI->GetUsername(buf);
if (!buf.IsEmpty()) {
NS_UnescapeURL(buf);
*user = ToNewUnicode(NS_ConvertASCIItoUCS2(buf));
CopyASCIItoUCS2(buf, userBuf);
mURI->GetPassword(buf);
if (!buf.IsEmpty()) {
NS_UnescapeURL(buf);
*pass = ToNewUnicode(NS_ConvertASCIItoUCS2(buf));
CopyASCIItoUCS2(buf, passBuf);
}
}
if (!userBuf.IsEmpty())
SetIdent(ident, authFlags, (PRUnichar *) userBuf.get(), (PRUnichar *) passBuf.get());
}
void
@ -1993,30 +2019,37 @@ nsHttpChannel::ParseRealm(const char *challenge, nsACString &realm)
}
nsresult
nsHttpChannel::PromptForUserPass(const char *host,
nsHttpChannel::PromptForIdentity(const char *host,
PRInt32 port,
PRBool proxyAuth,
const char *realm,
PRUnichar **user,
PRUnichar **pass)
const char *scheme,
PRUint32 authFlags,
nsHttpAuthIdentity &ident)
{
LOG(("nsHttpChannel::PromptForUserPass [this=%x realm=%s]\n", this, realm));
LOG(("nsHttpChannel::PromptForIdentity [this=%x]\n", this));
// XXX i18n: IDN not supported.
nsresult rv;
nsCOMPtr<nsIAuthPrompt> authPrompt;
rv = GetCallback(NS_GET_IID(nsIAuthPrompt), getter_AddRefs(authPrompt));
if (NS_FAILED(rv)) return rv;
// construct the domain string
// we always add the port to domain since it is used
// as the key for storing in password maanger.
nsCAutoString domain;
domain.Assign(host);
domain.Append(':');
domain.AppendInt(port);
domain.Append(" (");
domain.Append(realm);
domain.Append(')');
//
// construct the single signon key
//
// we always add the port to domain since it is used as the key for storing
// in password maanger. THE FORMAT OF THIS KEY IS SACROSANCT!! do not
// even think about changing the format of this key.
//
nsAutoString key;
key.AssignWithConversion(host);
key.Append(PRUnichar(':'));
key.AppendInt(port);
key.AppendWithConversion(" (");
key.AppendWithConversion(realm);
key.Append(PRUnichar(')'));
// construct the message string
nsCOMPtr<nsIStringBundleService> bundleSvc =
@ -2028,19 +2061,18 @@ nsHttpChannel::PromptForUserPass(const char *host,
if (NS_FAILED(rv)) return rv;
// figure out what message to display...
nsCAutoString displayHost;
displayHost.Assign(host);
nsAutoString displayHost;
displayHost.AssignWithConversion(host);
// Add port only if it was originally specified in the URI
PRInt32 uriPort = -1;
mURI->GetPort(&uriPort);
if (uriPort != -1) {
displayHost.Append(':');
displayHost.Append(PRUnichar(':'));
displayHost.AppendInt(port);
}
NS_ConvertASCIItoUCS2 hostU(displayHost);
nsXPIDLString message;
if (proxyAuth) {
const PRUnichar *strings[] = { hostU.get() };
const PRUnichar *strings[] = { displayHost.get() };
rv = bundle->FormatStringFromName(
NS_LITERAL_STRING("EnterUserPasswordForProxy").get(),
strings, 1,
@ -2048,11 +2080,11 @@ nsHttpChannel::PromptForUserPass(const char *host,
}
else {
nsAutoString realmU;
realmU.Assign(NS_LITERAL_STRING("\""));
realmU.Assign(PRUnichar('\"'));
realmU.AppendWithConversion(realm);
realmU.Append(NS_LITERAL_STRING("\""));
realmU.Append(PRUnichar('\"'));
const PRUnichar *strings[] = { realmU.get(), hostU.get() };
const PRUnichar *strings[] = { realmU.get(), displayHost.get() };
rv = bundle->FormatStringFromName(
NS_LITERAL_STRING("EnterUserPasswordForRealm").get(),
strings, 2,
@ -2062,21 +2094,20 @@ nsHttpChannel::PromptForUserPass(const char *host,
// prompt the user...
PRBool retval = PR_FALSE;
PRUnichar *user = nsnull, *pass = nsnull;
rv = authPrompt->PromptUsernameAndPassword(nsnull, message.get(),
NS_ConvertASCIItoUCS2(domain).get(),
key.get(),
nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY,
user, pass, &retval);
&user, &pass, &retval);
if (NS_FAILED(rv))
return rv;
if (!retval)
if (!retval || !user || !pass)
return NS_ERROR_ABORT;
// if prompting succeeds, then username and password must be non-null.
if (*user == nsnull)
*user = ToNewUnicode(NS_LITERAL_STRING(""));
if (*pass == nsnull)
*pass = ToNewUnicode(NS_LITERAL_STRING(""));
SetIdent(ident, authFlags, user, pass);
nsMemory::Free(user);
nsMemory::Free(pass);
return NS_OK;
}
@ -2086,8 +2117,7 @@ nsHttpChannel::SetAuthorizationHeader(nsHttpAuthCache *authCache,
const char *host,
PRInt32 port,
const char *path,
PRUnichar **user,
PRUnichar **pass)
nsHttpAuthIdentity &ident)
{
nsCOMPtr<nsIHttpAuthenticator> auth;
nsHttpAuthEntry *entry = nsnull;
@ -2096,24 +2126,31 @@ nsHttpChannel::SetAuthorizationHeader(nsHttpAuthCache *authCache,
rv = authCache->GetAuthEntryForPath(host, port, path, &entry);
if (NS_SUCCEEDED(rv)) {
nsXPIDLCString temp;
const char *creds = entry->Creds();
const char *creds = entry->Creds();
const char *challenge = entry->Challenge();
if (!creds && challenge) {
nsCAutoString foo;
rv = SelectChallenge(challenge, foo, getter_AddRefs(auth));
// we can only send a preemptive Authorization header if we have either
// stored credentials or a stored challenge from which to derive
// credentials.
if (!creds[0] && challenge[0]) {
nsCAutoString unused1, unused2;
rv = SelectChallenge(challenge, unused1, unused2, getter_AddRefs(auth));
if (NS_SUCCEEDED(rv)) {
nsISupports *sessionState = entry->mMetaData;
rv = auth->GenerateCredentials(this, challenge,
entry->User(), entry->Pass(),
entry->MetaData(),
entry->Domain(),
entry->User(),
entry->Pass(),
&sessionState,
&mAuthContinuationState,
getter_Copies(temp));
entry->mMetaData.swap(sessionState);
if (NS_SUCCEEDED(rv)) {
creds = temp.get();
*user = nsCRT::strdup(entry->User());
*pass = nsCRT::strdup(entry->Pass());
ident.Set(entry->Identity());
}
}
}
if (creds) {
if (creds[0]) {
LOG((" adding \"%s\" request header\n", header.get()));
mRequestHead.SetHeader(header, nsDependentCString(creds));
}
@ -2131,9 +2168,8 @@ nsHttpChannel::AddAuthorizationHeaders()
if (proxyHost)
SetAuthorizationHeader(authCache, nsHttp::Proxy_Authorization,
proxyHost, mConnectionInfo->ProxyPort(),
nsnull,
getter_Copies(mProxyUser),
getter_Copies(mProxyPass));
nsnull, // proxy has no path
mProxyIdent);
// check if server credentials should be sent
nsCAutoString path;
@ -2142,8 +2178,7 @@ nsHttpChannel::AddAuthorizationHeaders()
mConnectionInfo->Host(),
mConnectionInfo->Port(),
path.get(),
getter_Copies(mUser),
getter_Copies(mPass));
mIdent);
}
}
@ -2954,13 +2989,6 @@ nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult st
if (mCanceled)
status = mStatus;
// if the request is a previous transaction, then simply release it.
if (request == mPrevTransactionPump) {
NS_RELEASE(mPrevTransaction);
mPrevTransaction = nsnull;
mPrevTransactionPump = 0;
}
if (mCachedContentIsPartial && NS_SUCCEEDED(status)) {
// mTransactionPump should be suspended
NS_ASSERTION(request != mTransactionPump,
@ -2977,37 +3005,45 @@ nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult st
NS_NOTREACHED("unexpected request");
}
// if the request is for something we no longer reference, then simply
// drop this event.
if ((request != mTransactionPump) && (request != mCachePump))
return NS_OK;
mIsPending = PR_FALSE;
mStatus = status;
PRBool isPartial = PR_FALSE;
if (mTransaction) {
if (mCacheEntry) {
// find out if the transaction ran to completion...
// find out if the transaction ran to completion...
if (mCacheEntry)
isPartial = !mTransaction->ResponseIsComplete();
}
// grab reference to connection in case we need to retry an
// authentication request over it.
nsRefPtr<nsAHttpConnection> conn = mTransaction->Connection();
if (conn)
mTransaction->SetConnection(nsnull);
// at this point, we're done with the transaction
NS_RELEASE(mTransaction);
mTransaction = nsnull;
mTransactionPump = 0;
// handle auth retry...
if (mAuthRetryPending && NS_SUCCEEDED(status)) {
mAuthRetryPending = PR_FALSE;
status = DoAuthRetry(conn);
if (NS_SUCCEEDED(status))
return NS_OK;
}
// if this transaction has been replaced, then bail.
if (mTransactionReplaced)
return NS_OK;
}
mIsPending = PR_FALSE;
mStatus = status;
// perform any final cache operations before we close the cache entry.
if (mCacheEntry && (mCacheAccess & nsICache::ACCESS_WRITE))
FinalizeCacheEntry();
// we don't support overlapped i/o (bug 82418)
#if 0
if (mCacheEntry && NS_SUCCEEDED(status))
mCacheEntry->MarkValid();
#endif
if (mListener) {
LOG((" calling OnStopRequest\n"));
mListener->OnStopRequest(this, mListenerContext, status);
mListener = 0;
mListenerContext = 0;
@ -3056,11 +3092,9 @@ nsHttpChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
NS_ASSERTION(!(mCachedContentIsPartial && (request == mTransactionPump)),
"transaction pump not suspended");
// if the request is for something we no longer reference, then simply
// drop this event.
if ((request != mTransactionPump) && (request != mCachePump)) {
NS_NOTREACHED("got stale request... why wasn't it cancelled?");
return NS_BASE_STREAM_CLOSED;
if (mAuthRetryPending || (request == mTransactionPump && mTransactionReplaced)) {
PRUint32 n;
return input->ReadSegments(DiscardSegments, nsnull, count, &n);
}
if (mListener) {
@ -3289,6 +3323,51 @@ nsHttpChannel::ClearPasswordManagerEntry(const char *host, PRInt32 port, const c
}
}
nsresult
nsHttpChannel::DoAuthRetry(nsAHttpConnection *conn)
{
LOG(("nsHttpChannel::DoAuthRetry [this=%x]\n", this));
NS_ASSERTION(!mTransaction, "should not have a transaction");
nsresult rv;
// toggle mIsPending to allow nsIHttpNotify implementations to modify
// the request headers (bug 95044).
mIsPending = PR_FALSE;
// notify nsIHttpNotify implementations.. the server response could
// have included cookies that must be sent with this authentication
// attempt (bug 84794).
rv = gHttpHandler->OnModifyRequest(this);
NS_ASSERTION(NS_SUCCEEDED(rv), "OnModifyRequest failed");
mIsPending = PR_TRUE;
// set sticky connection flag and disable pipelining.
mCaps |= NS_HTTP_STICKY_CONNECTION;
mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
// and create a new one...
rv = SetupTransaction();
if (NS_FAILED(rv)) return rv;
// transfer ownership of connection to transaction
if (conn)
mTransaction->SetConnection(conn);
// rewind the upload stream
if (mUploadStream) {
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream);
if (seekable)
seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
}
rv = gHttpHandler->InitiateTransaction(mTransaction);
if (NS_FAILED(rv)) return rv;
return mTransactionPump->AsyncRead(this, nsnull);
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsContentEncodings <public>
//-----------------------------------------------------------------------------

View File

@ -26,6 +26,7 @@
#include "nsHttpTransaction.h"
#include "nsHttpRequestHead.h"
#include "nsHttpAuthCache.h"
#include "nsXPIDLString.h"
#include "nsCOMPtr.h"
@ -54,7 +55,7 @@
#include "nsIPrompt.h"
class nsHttpResponseHead;
class nsHttpAuthCache;
class nsAHttpConnection;
class nsIHttpAuthenticator;
class nsIProxyInfo;
@ -140,15 +141,16 @@ private:
// auth specific methods
nsresult GetCredentials(const char *challenges, PRBool proxyAuth, nsAFlatCString &creds);
nsresult SelectChallenge(const char *challenges, nsAFlatCString &challenge, nsIHttpAuthenticator **);
nsresult SelectChallenge(const char *challenges, nsCString &challenge, nsCString &scheme, nsIHttpAuthenticator **);
nsresult GetAuthenticator(const char *scheme, nsIHttpAuthenticator **);
void GetUserPassFromURI(PRUnichar **user, PRUnichar **pass);
void ParseRealm(const char *challenge, nsACString &realm);
nsresult PromptForUserPass(const char *host, PRInt32 port, PRBool proxyAuth, const char *realm, PRUnichar **user, PRUnichar **pass);
void SetAuthorizationHeader(nsHttpAuthCache *, nsHttpAtom header, const char *host, PRInt32 port, const char *path, PRUnichar **user, PRUnichar **pass);
void GetIdentityFromURI(PRUint32 authFlags, nsHttpAuthIdentity&);
nsresult PromptForIdentity(const char *host, PRInt32 port, PRBool proxyAuth, const char *realm, const char *scheme, PRUint32 authFlags, nsHttpAuthIdentity &);
void SetAuthorizationHeader(nsHttpAuthCache *, nsHttpAtom header, const char *host, PRInt32 port, const char *path, nsHttpAuthIdentity &ident);
void AddAuthorizationHeaders();
nsresult GetCurrentPath(nsACString &);
void ClearPasswordManagerEntry(const char *host, PRInt32 port, const char *realm, const PRUnichar *user);
nsresult DoAuthRetry(nsAHttpConnection *);
static void *PR_CALLBACK AsyncCall_EventHandlerFunc(PLEvent *);
static void PR_CALLBACK AsyncCall_EventCleanupFunc(PLEvent *);
@ -173,9 +175,7 @@ private:
nsHttpResponseHead *mResponseHead;
nsCOMPtr<nsIInputStreamPump> mTransactionPump;
nsCOMPtr<nsIInputStreamPump> mPrevTransactionPump;
nsHttpTransaction *mTransaction; // hard ref
nsHttpTransaction *mPrevTransaction; // hard ref
nsHttpConnectionInfo *mConnectionInfo; // hard ref
nsCString mSpec; // ASCII encoded URL spec
@ -194,22 +194,24 @@ private:
PRUint32 mRequestTime;
// auth specific data
nsXPIDLString mUser;
nsXPIDLString mPass;
nsXPIDLString mProxyUser;
nsXPIDLString mProxyPass;
nsISupports *mAuthContinuationState;
nsHttpAuthIdentity mIdent;
nsHttpAuthIdentity mProxyIdent;
// redirection specific data.
PRUint8 mRedirectionLimit;
PRPackedBool mIsPending;
PRPackedBool mApplyConversion;
PRPackedBool mAllowPipelining;
PRPackedBool mCachedContentIsValid;
PRPackedBool mCachedContentIsPartial;
PRPackedBool mResponseHeadersModified;
PRPackedBool mCanceled;
PRPackedBool mUploadStreamHasHeaders;
// state flags
PRUint32 mIsPending : 1;
PRUint32 mApplyConversion : 1;
PRUint32 mAllowPipelining : 1;
PRUint32 mCachedContentIsValid : 1;
PRUint32 mCachedContentIsPartial : 1;
PRUint32 mResponseHeadersModified : 1;
PRUint32 mCanceled : 1;
PRUint32 mTransactionReplaced : 1;
PRUint32 mUploadStreamHasHeaders : 1;
PRUint32 mAuthRetryPending : 1;
class nsContentEncodings : public nsISimpleEnumerator
{

View File

@ -55,8 +55,6 @@ nsHttpConnection::nsHttpConnection()
, mSuspendCount(0)
, mLastReadTime(0)
, mIdleTimeout(0)
, mTransactionStatus(NS_OK)
, mTransactionDone(PR_TRUE)
, mKeepAlive(PR_TRUE) // assume to keep-alive by default
, mKeepAliveMask(PR_TRUE)
, mSupportsPipelining(PR_FALSE) // assume low-grade server
@ -110,6 +108,8 @@ nsHttpConnection::Init(nsHttpConnectionInfo *info, PRUint16 maxHangTime)
nsresult
nsHttpConnection::Activate(nsAHttpTransaction *trans, PRUint8 caps)
{
nsresult rv;
LOG(("nsHttpConnection::Activate [this=%x trans=%x caps=%x]\n",
this, trans, caps));
@ -120,10 +120,6 @@ nsHttpConnection::Activate(nsAHttpTransaction *trans, PRUint8 caps)
mTransaction = trans;
NS_ADDREF(mTransaction);
mTransaction->SetConnection(this);
nsresult rv;
// set mKeepAlive according to what will be requested
mKeepAliveMask = mKeepAlive = (caps & NS_HTTP_ALLOW_KEEPALIVE);
@ -253,7 +249,7 @@ nsHttpConnection::SupportsPipelining(nsHttpResponseHead *responseHead)
}
//----------------------------------------------------------------------------
// nsHttpConnection::nsAHttpConnection
// nsHttpConnection::nsAHttpConnection compatible methods
//----------------------------------------------------------------------------
nsresult
@ -451,11 +447,12 @@ nsHttpConnection::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
NS_ASSERTION(trans == mTransaction, "wrong transaction");
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
// ignore this error code because its not a real error.
// mask this error code because its not a real error.
if (reason == NS_BASE_STREAM_CLOSED)
reason = NS_OK;
mTransaction->Close(reason);
NS_RELEASE(mTransaction);
mTransaction = 0;
@ -465,8 +462,6 @@ nsHttpConnection::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
// flag the connection as reused here for convenience sake. certainly
// it might be going away instead ;-)
mIsReused = PR_TRUE;
gHttpHandler->ReclaimConnection(this);
}
NS_METHOD

View File

@ -46,8 +46,7 @@
// accessed from any other thread.
//-----------------------------------------------------------------------------
class nsHttpConnection : public nsAHttpConnection
, public nsAHttpSegmentReader
class nsHttpConnection : public nsAHttpSegmentReader
, public nsAHttpSegmentWriter
, public nsIInputStreamNotify
, public nsIOutputStreamNotify
@ -56,6 +55,8 @@ class nsHttpConnection : public nsAHttpConnection
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSAHTTPSEGMENTREADER
NS_DECL_NSAHTTPSEGMENTWRITER
NS_DECL_NSIINPUTSTREAMNOTIFY
NS_DECL_NSIOUTPUTSTREAMNOTIFY
NS_DECL_NSITRANSPORTEVENTSINK
@ -92,7 +93,7 @@ public:
nsAHttpTransaction *Transaction() { return mTransaction; }
nsHttpConnectionInfo *ConnectionInfo() { return mConnInfo; }
// nsAHttpConnection methods:
// nsAHttpConnection compatible methods (non-virtual):
nsresult OnHeadersAvailable(nsAHttpTransaction *, nsHttpRequestHead *, nsHttpResponseHead *, PRBool *reset);
void CloseTransaction(nsAHttpTransaction *, nsresult reason);
void GetConnectionInfo(nsHttpConnectionInfo **ci) { NS_IF_ADDREF(*ci = mConnInfo); }
@ -103,12 +104,6 @@ public:
nsresult ResumeSend();
nsresult ResumeRecv();
// nsAHttpSegmentReader methods:
nsresult OnReadSegment(const char *, PRUint32, PRUint32 *);
// nsAHttpSegmentWriter methods:
nsresult OnWriteSegment(char *, PRUint32, PRUint32 *);
static NS_METHOD ReadFromStream(nsIInputStream *, void *, const char *,
PRUint32, PRUint32, PRUint32 *);
@ -147,9 +142,6 @@ private:
PRUint16 mMaxHangTime; // max download time before dropping keep-alive status
PRUint16 mIdleTimeout; // value of keep-alive: timeout=
nsresult mTransactionStatus;
PRPackedBool mTransactionDone;
PRPackedBool mKeepAlive;
PRPackedBool mKeepAliveMask;
PRPackedBool mSupportsPipelining;

View File

@ -38,16 +38,15 @@
#include "nsHttpConnectionMgr.h"
#include "nsHttpConnection.h"
#include "nsHttpPipeline.h"
#include "nsHttpHandler.h"
#include "nsAutoLock.h"
#include "nsNetCID.h"
#include "nsCOMPtr.h"
#include "nsIServiceManager.h"
#ifdef DEBUG
// defined by the socket transport service while active
extern PRThread *gSocketThread;
#endif
static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
@ -226,40 +225,11 @@ nsHttpConnectionMgr::ReclaimConnection(nsHttpConnection *conn)
{
LOG(("nsHttpConnectionMgr::ReclaimConnection [conn=%x]\n", conn));
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
//
// 1) remove the connection from the active list
// 2) if keep-alive, add connection to idle list
// 3) post event to process the pending transaction queue
//
nsHttpConnectionInfo *ci = conn->ConnectionInfo();
nsCStringKey key(ci->HashKey());
nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
NS_ASSERTION(ent, "no connection entry");
if (ent) {
ent->mActiveConns.RemoveElement(conn);
mNumActiveConns--;
if (conn->CanReuse()) {
LOG((" adding connection to idle list\n"));
// hold onto this connection in the idle list. we push it to
// the end of the list so as to ensure that we'll visit older
// connections first before getting to this one.
ent->mIdleConns.AppendElement(conn);
mNumIdleConns++;
}
else {
LOG((" connection cannot be reused; closing connection\n"));
// make sure the connection is closed and release our reference.
conn->Close(NS_ERROR_ABORT);
NS_RELEASE(conn);
}
}
return ProcessPendingQ(ci);
NS_ADDREF(conn);
nsresult rv = PostEvent(MSG_RECLAIM_CONNECTION, 0, conn);
if (NS_FAILED(rv))
NS_RELEASE(conn);
return rv;
}
nsresult
@ -271,7 +241,6 @@ nsHttpConnectionMgr::ProcessPendingQ(nsHttpConnectionInfo *ci)
nsresult rv = PostEvent(MSG_PROCESS_PENDING_Q, 0, ci);
if (NS_FAILED(rv))
NS_RELEASE(ci);
return rv;
}
@ -329,6 +298,16 @@ nsHttpConnectionMgr::PruneDeadConnectionsCB(nsHashKey *key, void *data, void *cl
}
}
#ifdef DEBUG
count = ent->mActiveConns.Count();
if (count > 0) {
for (PRInt32 i=count-1; i>=0; --i) {
nsHttpConnection *conn = (nsHttpConnection *) ent->mActiveConns[i];
LOG((" active conn [%x] with trans [%x]\n", conn, conn->Transaction()));
}
}
#endif
// if this entry is empty, then we can remove it.
if (ent->mIdleConns.Count() == 0 &&
ent->mActiveConns.Count() == 0 &&
@ -549,6 +528,11 @@ nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
LOG(("nsHttpConnectionMgr::DispatchTransaction [ci=%s trans=%x caps=%x conn=%x]\n",
ent->mConnInfo->HashKey().get(), trans, caps, conn));
nsConnectionHandle *handle = new nsConnectionHandle(conn);
if (!handle)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(handle);
nsHttpPipeline *pipeline = nsnull;
if (conn->SupportsPipelining() && (caps & NS_HTTP_ALLOW_PIPELINING)) {
LOG((" looking to build pipeline...\n"));
@ -561,6 +545,9 @@ nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
mNumActiveConns++;
NS_ADDREF(conn);
// give the transaction the indirect reference to the connection.
trans->SetConnection(handle);
nsresult rv = conn->Activate(trans, caps);
if (NS_FAILED(rv)) {
@ -575,6 +562,7 @@ nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
// pipeline to be restarted.
NS_IF_RELEASE(pipeline);
NS_RELEASE(handle);
return rv;
}
@ -663,7 +651,34 @@ nsHttpConnectionMgr::OnMsgNewTransaction(nsHttpTransaction *trans)
}
nsHttpConnection *conn;
GetConnection(ent, caps, &conn);
// check if the transaction already has a sticky reference to a connection.
// if so, then we can just use it directly. XXX check if alive??
// XXX add a TakeConnection method or something to make this clearer!
nsConnectionHandle *handle = (nsConnectionHandle *) trans->Connection();
if (handle) {
NS_ASSERTION(caps & NS_HTTP_STICKY_CONNECTION, "unexpected caps");
NS_ASSERTION(handle->mConn, "no connection");
// steal reference from connection handle.
// XXX prevent SetConnection(nsnull) from calling ReclaimConnection
conn = handle->mConn;
handle->mConn = nsnull;
// destroy connection handle.
trans->SetConnection(nsnull);
// remove sticky connection from active connection list; we'll add it
// right back in DispatchTransaction.
if (ent->mActiveConns.RemoveElement(conn))
mNumActiveConns--;
else {
NS_ERROR("sticky connection not found in active list");
return NS_ERROR_UNEXPECTED;
}
}
else
GetConnection(ent, caps, &conn);
nsresult rv;
if (!conn) {
@ -687,12 +702,15 @@ nsHttpConnectionMgr::OnMsgCancelTransaction(nsHttpTransaction *trans,
nsresult reason)
{
LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%x]\n", trans));
//
// if the transaction owns a connection and the transaction is not done,
// then ask the connection to close the transaction. otherwise, close the
// transaction directly (removing it from the pending queue first).
//
nsAHttpConnection *conn = trans->Connection();
if (conn)
if (conn && !trans->IsDone())
conn->CloseTransaction(trans, reason);
else {
// make sure the transaction is not on the pending queue...
nsHttpConnectionInfo *ci = trans->ConnectionInfo();
nsCStringKey key(ci->HashKey());
nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
@ -730,10 +748,53 @@ nsHttpConnectionMgr::OnMsgPruneDeadConnections()
{
LOG(("nsHttpConnectionMgr::OnMsgPruneDeadConnections\n"));
#ifndef DEBUG
if (mNumIdleConns > 0)
#endif
mCT.Enumerate(PruneDeadConnectionsCB, this);
}
void
nsHttpConnectionMgr::OnMsgReclaimConnection(nsHttpConnection *conn)
{
LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection [conn=%x]\n", conn));
//
// 1) remove the connection from the active list
// 2) if keep-alive, add connection to idle list
// 3) post event to process the pending transaction queue
//
nsHttpConnectionInfo *ci = conn->ConnectionInfo();
NS_ADDREF(ci);
nsCStringKey key(ci->HashKey());
nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
NS_ASSERTION(ent, "no connection entry");
if (ent) {
ent->mActiveConns.RemoveElement(conn);
mNumActiveConns--;
if (conn->CanReuse()) {
LOG((" adding connection to idle list\n"));
// hold onto this connection in the idle list. we push it to
// the end of the list so as to ensure that we'll visit older
// connections first before getting to this one.
ent->mIdleConns.AppendElement(conn);
mNumIdleConns++;
}
else {
LOG((" connection cannot be reused; closing connection\n"));
// make sure the connection is closed and release our reference.
conn->Close(NS_ERROR_ABORT);
NS_RELEASE(conn);
}
}
OnMsgProcessPendingQ(ci);
NS_RELEASE(ci);
}
//-----------------------------------------------------------------------------
NS_IMPL_THREADSAFE_ISUPPORTS1(nsHttpConnectionMgr, nsISocketEventHandler)
@ -774,6 +835,83 @@ nsHttpConnectionMgr::OnSocketEvent(PRUint32 type, PRUint32 uparam, void *vparam)
case MSG_PRUNE_DEAD_CONNECTIONS:
OnMsgPruneDeadConnections();
break;
case MSG_RECLAIM_CONNECTION:
{
nsHttpConnection *conn = (nsHttpConnection *) vparam;
OnMsgReclaimConnection(conn);
NS_RELEASE(conn);
}
break;
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsHttpConnectionMgr::nsConnectionHandle
nsHttpConnectionMgr::nsConnectionHandle::~nsConnectionHandle()
{
if (mConn) {
gHttpHandler->ReclaimConnection(mConn);
NS_RELEASE(mConn);
}
}
NS_IMPL_THREADSAFE_ISUPPORTS0(nsHttpConnectionMgr::nsConnectionHandle)
nsresult
nsHttpConnectionMgr::nsConnectionHandle::OnHeadersAvailable(nsAHttpTransaction *trans,
nsHttpRequestHead *req,
nsHttpResponseHead *resp,
PRBool *reset)
{
return mConn->OnHeadersAvailable(trans, req, resp, reset);
}
nsresult
nsHttpConnectionMgr::nsConnectionHandle::ResumeSend()
{
return mConn->ResumeSend();
}
nsresult
nsHttpConnectionMgr::nsConnectionHandle::ResumeRecv()
{
return mConn->ResumeRecv();
}
void
nsHttpConnectionMgr::nsConnectionHandle::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
{
mConn->CloseTransaction(trans, reason);
}
void
nsHttpConnectionMgr::nsConnectionHandle::GetConnectionInfo(nsHttpConnectionInfo **result)
{
mConn->GetConnectionInfo(result);
}
void
nsHttpConnectionMgr::nsConnectionHandle::GetSecurityInfo(nsISupports **result)
{
mConn->GetSecurityInfo(result);
}
PRBool
nsHttpConnectionMgr::nsConnectionHandle::IsPersistent()
{
return mConn->IsPersistent();
}
PRBool
nsHttpConnectionMgr::nsConnectionHandle::IsReused()
{
return mConn->IsReused();
}
nsresult
nsHttpConnectionMgr::nsConnectionHandle::PushBack(const char *buf, PRUint32 bufLen)
{
return mConn->PushBack(buf, bufLen);
}

View File

@ -39,15 +39,13 @@
#define nsHttpConnectionMgr_h__
#include "nsHttpConnectionInfo.h"
#include "nsHttpConnection.h"
#include "nsHttpTransaction.h"
#include "nsHashtable.h"
#include "prmon.h"
#include "nsISocketTransportService.h"
//-----------------------------------------------------------------------------
class nsHttpConnection;
class nsHttpPipeline;
//-----------------------------------------------------------------------------
@ -93,16 +91,18 @@ public:
// transport service is not available when the connection manager is down.
nsresult GetSTS(nsISocketTransportService **);
// called when a connection is done processing a transaction. if the
// connection can be reused then it will be added to the idle list, else
// it will be closed.
nsresult ReclaimConnection(nsHttpConnection *conn);
//-------------------------------------------------------------------------
// NOTE: functions below may be called only on the socket thread.
//-------------------------------------------------------------------------
// removes the next transaction for the specified connection from the
// pending transaction queue.
void AddTransactionToPipeline(nsHttpPipeline *);
// called by a connection when it has been closed or when it becomes idle.
nsresult ReclaimConnection(nsHttpConnection *conn);
void AddTransactionToPipeline(nsHttpPipeline *);
// called to force the transaction queue to be processed once more, giving
// preference to the specified connection.
@ -115,9 +115,16 @@ private:
MSG_NEW_TRANSACTION,
MSG_CANCEL_TRANSACTION,
MSG_PROCESS_PENDING_Q,
MSG_PRUNE_DEAD_CONNECTIONS
MSG_PRUNE_DEAD_CONNECTIONS,
MSG_RECLAIM_CONNECTION
};
// nsConnectionEntry
//
// mCT maps connection info hash key to nsConnectionEntry object, which
// contains list of active and idle connections as well as the list of
// pending transactions.
//
struct nsConnectionEntry
{
nsConnectionEntry(nsHttpConnectionInfo *ci)
@ -133,6 +140,27 @@ private:
nsVoidArray mIdleConns; // idle persistent connections
};
// nsConnectionHandle
//
// thin wrapper around a real connection, used to keep track of references
// to the connection to determine when the connection may be reused. the
// transaction (or pipeline) owns a reference to this handle. this extra
// layer of indirection greatly simplifies consumer code, avoiding the
// need for consumer code to know when to give the connection back to the
// connection manager.
//
class nsConnectionHandle : public nsAHttpConnection
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSAHTTPCONNECTION
nsConnectionHandle(nsHttpConnection *conn) { NS_ADDREF(mConn = conn); }
virtual ~nsConnectionHandle();
nsHttpConnection *mConn;
};
//-------------------------------------------------------------------------
// NOTE: these members may be accessed from any thread (use mMonitor)
//-------------------------------------------------------------------------
@ -171,6 +199,7 @@ private:
void OnMsgCancelTransaction(nsHttpTransaction *trans, nsresult reason);
void OnMsgProcessPendingQ(nsHttpConnectionInfo *ci);
void OnMsgPruneDeadConnections();
void OnMsgReclaimConnection(nsHttpConnection *);
// counters
PRUint16 mNumActiveConns;

View File

@ -95,12 +95,39 @@ nsHttpDigestAuth::MD5Hash(const char *buf, PRUint32 len)
// nsHttpDigestAuth::nsIHttpAuthenticator
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsHttpDigestAuth::ChallengeReceived(nsIHttpChannel *httpChannel,
const char *challenge,
nsISupports **sessionState,
nsISupports **continuationState,
PRBool *result)
{
nsCAutoString realm, domain, nonce, opaque;
PRBool stale;
PRUint16 algorithm, qop;
nsresult rv = ParseChallenge(challenge, realm, domain, nonce, opaque,
&stale, &algorithm, &qop);
if (NS_FAILED(rv)) return rv;
// if the challenge has the "stale" flag set, then the user identity is not
// necessarily invalid. by returning FALSE here we can suppress username
// and password prompting that usually accompanies a 401/407 challenge.
*result = !stale;
// clear any existing nonce_count since we have a new challenge.
NS_IF_RELEASE(*sessionState);
return NS_OK;
}
NS_IMETHODIMP
nsHttpDigestAuth::GenerateCredentials(nsIHttpChannel *httpChannel,
const char *challenge,
const PRUnichar *userdomain,
const PRUnichar *username,
const PRUnichar *password,
nsISupports *extra,
nsISupports **sessionState,
nsISupports **continuationState,
char **creds)
{
@ -197,12 +224,20 @@ nsHttpDigestAuth::GenerateCredentials(nsIHttpChannel *httpChannel,
// prevent spoofing). we increase this count every time.
//
char nonce_count[NONCE_COUNT_LENGTH+1] = "00000001"; // in hex
nsCOMPtr<nsISupportsPRUint32> v(do_QueryInterface(extra));
if (v) {
PRUint32 nc;
v->GetData(&nc);
PR_snprintf(nonce_count, sizeof(nonce_count), "%08x", ++nc);
v->SetData(nc);
if (*sessionState) {
nsCOMPtr<nsISupportsPRUint32> v(do_QueryInterface(*sessionState));
if (v) {
PRUint32 nc;
v->GetData(&nc);
PR_snprintf(nonce_count, sizeof(nonce_count), "%08x", ++nc);
v->SetData(nc);
}
}
else {
nsCOMPtr<nsISupportsPRUint32> v(
do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv));
v->SetData(1);
NS_ADDREF(*sessionState = v);
}
LOG((" nonce_count=%s\n", nonce_count));
@ -279,40 +314,16 @@ nsHttpDigestAuth::GenerateCredentials(nsIHttpChannel *httpChannel,
}
NS_IMETHODIMP
nsHttpDigestAuth::AreCredentialsReusable(PRBool *result)
nsHttpDigestAuth::GetAuthFlags(PRUint32 *flags)
{
*result = PR_FALSE;
*flags = REQUEST_BASED | REUSABLE_CHALLENGE;
//
// NOTE: digest auth credentials must be uniquely computed for each request,
// so we do not set the REUSABLE_CREDENTIALS flag.
//
return NS_OK;
}
NS_IMETHODIMP
nsHttpDigestAuth::ChallengeRequiresUserPass(const char *challenge,
PRBool *result)
{
nsCAutoString realm, domain, nonce, opaque;
PRBool stale;
PRUint16 algorithm, qop;
nsresult rv = ParseChallenge(challenge, realm, domain, nonce, opaque,
&stale, &algorithm, &qop);
if (NS_FAILED(rv)) {
*result = PR_TRUE;
return rv;
}
*result = !stale;
return NS_OK;
}
NS_IMETHODIMP
nsHttpDigestAuth::AllocateMetaData(nsISupports **result)
{
nsresult rv;
nsCOMPtr<nsISupportsPRUint32> v(
do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv));
NS_ADDREF(*result = v);
return rv;
}
nsresult
nsHttpDigestAuth::CalculateResponse(const char * ha1_digest,
const char * ha2_digest,
@ -586,3 +597,5 @@ nsHttpDigestAuth::ParseChallenge(const char * challenge,
}
return NS_OK;
}
// vim: ts=2 sw=2

View File

@ -223,6 +223,18 @@ nsHttpPipeline::GetSecurityInfo(nsISupports **result)
mConnection->GetSecurityInfo(result);
}
PRBool
nsHttpPipeline::IsPersistent()
{
return PR_TRUE; // pipelining requires this
}
PRBool
nsHttpPipeline::IsReused()
{
return PR_TRUE; // pipelining requires this
}
nsresult
nsHttpPipeline::PushBack(const char *data, PRUint32 length)
{

View File

@ -38,42 +38,16 @@ class nsHttpPipeline : public nsAHttpConnection
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSAHTTPCONNECTION
NS_DECL_NSAHTTPTRANSACTION
NS_DECL_NSAHTTPSEGMENTREADER
nsHttpPipeline();
virtual ~nsHttpPipeline();
nsresult AddTransaction(nsAHttpTransaction *);
// nsAHttpConnection methods:
nsresult OnHeadersAvailable(nsAHttpTransaction *, nsHttpRequestHead *, nsHttpResponseHead *, PRBool *reset);
nsresult ResumeSend();
nsresult ResumeRecv();
void CloseTransaction(nsAHttpTransaction *, nsresult);
void GetConnectionInfo(nsHttpConnectionInfo **);
void GetSecurityInfo(nsISupports **);
PRBool IsPersistent() { return PR_TRUE; } // pipelining requires this
PRBool IsReused() { return PR_TRUE; } // pipelining requires this
nsresult PushBack(const char *, PRUint32);
// nsAHttpTransaction methods:
void SetConnection(nsAHttpConnection *);
void GetSecurityCallbacks(nsIInterfaceRequestor **);
void OnTransportStatus(nsresult status, PRUint32 progress);
PRBool IsDone();
nsresult Status();
PRUint32 Available();
nsresult ReadSegments(nsAHttpSegmentReader *, PRUint32, PRUint32 *);
nsresult WriteSegments(nsAHttpSegmentWriter *, PRUint32, PRUint32 *);
void Close(nsresult reason);
// nsAHttpSegmentReader methods:
nsresult OnReadSegment(const char *, PRUint32, PRUint32 *);
// nsAHttpSegmentWriter methods:
nsresult OnWriteSegment(char *, PRUint32, PRUint32 *);
private:
nsresult FillSendBuf();
static NS_METHOD ReadFromPipe(nsIInputStream *, void *, const char *,

View File

@ -111,9 +111,10 @@ nsHttpTransaction::nsHttpTransaction()
, mTransportStatus(0)
, mTransportProgress(0)
, mTransportProgressMax(0)
, mTransportStatusInProgress(PR_FALSE)
, mRestartCount(0)
, mCaps(0)
, mClosed(PR_FALSE)
, mDestroying(PR_FALSE)
, mConnected(PR_FALSE)
, mHaveStatusLine(PR_FALSE)
, mHaveAllHeaders(PR_FALSE)
@ -122,8 +123,7 @@ nsHttpTransaction::nsHttpTransaction()
, mDidContentStart(PR_FALSE)
, mNoContent(PR_FALSE)
, mReceivedData(PR_FALSE)
, mDestroying(PR_FALSE)
, mClosed(PR_FALSE)
, mStatusEventPending(PR_FALSE)
{
LOG(("Creating nsHttpTransaction @%x\n", this));
}
@ -249,6 +249,19 @@ nsHttpTransaction::TakeResponseHead()
// nsHttpTransaction::nsAHttpTransaction
//----------------------------------------------------------------------------
void
nsHttpTransaction::SetConnection(nsAHttpConnection *conn)
{
NS_IF_RELEASE(mConnection);
NS_IF_ADDREF(mConnection = conn);
}
void
nsHttpTransaction::GetSecurityCallbacks(nsIInterfaceRequestor **cb)
{
NS_IF_ADDREF(*cb = mCallbacks);
}
void
nsHttpTransaction::OnTransportStatus(nsresult status, PRUint32 progress)
{
@ -283,8 +296,8 @@ nsHttpTransaction::OnTransportStatus(nsresult status, PRUint32 progress)
mTransportProgressMax = 0;
}
postEvent = !mTransportStatusInProgress;
mTransportStatusInProgress = PR_TRUE;
postEvent = !mStatusEventPending;
mStatusEventPending = PR_TRUE;
}
// only post an event if there is not already an event in progress. we
@ -300,6 +313,18 @@ nsHttpTransaction::OnTransportStatus(nsresult status, PRUint32 progress)
}
}
PRBool
nsHttpTransaction::IsDone()
{
return mTransactionDone;
}
nsresult
nsHttpTransaction::Status()
{
return mStatus;
}
PRUint32
nsHttpTransaction::Available()
{
@ -414,10 +439,8 @@ nsHttpTransaction::Close(nsresult reason)
// we must no longer reference the connection! find out if the
// connection was being reused before letting it go.
PRBool connReused = PR_FALSE;
if (mConnection) {
if (mConnection)
connReused = mConnection->IsReused();
NS_RELEASE(mConnection);
}
mConnected = PR_FALSE;
//
@ -434,18 +457,36 @@ nsHttpTransaction::Close(nsresult reason)
return;
}
if (NS_SUCCEEDED(reason) && !mHaveAllHeaders && !mLineBuf.IsEmpty()) {
PRBool relConn = PR_TRUE;
if (NS_SUCCEEDED(reason)) {
// the server has not sent the final \r\n terminating the header section,
// and there is still a header line unparsed. let's make sure we parse
// the remaining header line, and then hopefully, the response will be
// usable (see bug 88792).
ParseLineSegment("\n", 1);
}
if (!mHaveAllHeaders && !mLineBuf.IsEmpty())
ParseLineSegment("\n", 1);
// honor the sticky connection flag...
if (mCaps & NS_HTTP_STICKY_CONNECTION)
relConn = PR_FALSE;
}
if (relConn && mConnection)
NS_RELEASE(mConnection);
mTransactionDone = PR_TRUE; // force this flag
mStatus = reason;
mTransactionDone = PR_TRUE; // force this flag
mClosed = PR_TRUE;
// release some resources that we no longer need
mRequestStream = nsnull;
mReqHeaderBuf.Truncate();
mLineBuf.Truncate();
if (mChunkedDecoder) {
delete mChunkedDecoder;
mChunkedDecoder = nsnull;
}
// closing this pipe signals triggers the channel's OnStopRequest method.
mPipeOut->CloseEx(reason);
}
@ -471,8 +512,9 @@ nsHttpTransaction::Restart()
if (seekable)
seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
// clear the old socket info
// clear old connection state...
mSecurityInfo = 0;
NS_IF_RELEASE(mConnection);
return gHttpHandler->InitiateTransaction(this);
}
@ -829,7 +871,7 @@ nsHttpTransaction::TransportStatus_Handler(PLEvent *ev)
progress = trans->mTransportProgress;
progressMax = trans->mTransportProgressMax;
trans->mTransportStatusInProgress = PR_FALSE;
trans->mStatusEventPending = PR_FALSE;
}
trans->mTransportSink->OnTransportStatus(nsnull, status, progress, progressMax);

View File

@ -56,6 +56,7 @@ class nsHttpTransaction : public nsAHttpTransaction
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSAHTTPTRANSACTION
NS_DECL_NSIOUTPUTSTREAMNOTIFY
NS_DECL_NSISOCKETEVENTHANDLER
@ -113,27 +114,6 @@ public:
// Called to find out if the transaction generated a complete response.
PRBool ResponseIsComplete() { return mResponseIsComplete; }
//-------------------------------------------------------------------------
// nsAHttpTransaction methods:
//-------------------------------------------------------------------------
void SetConnection(nsAHttpConnection *conn)
{
NS_IF_RELEASE(mConnection);
NS_IF_ADDREF(mConnection = conn);
}
void GetSecurityCallbacks(nsIInterfaceRequestor **cb)
{
NS_IF_ADDREF(*cb = mCallbacks);
}
void OnTransportStatus(nsresult status, PRUint32 progress);
PRBool IsDone() { return mTransactionDone; }
nsresult Status() { return mStatus; }
PRUint32 Available();
nsresult ReadSegments(nsAHttpSegmentReader *, PRUint32, PRUint32 *);
nsresult WriteSegments(nsAHttpSegmentWriter *, PRUint32, PRUint32 *);
void Close(nsresult);
private:
nsresult Restart();
void ParseLine(char *line);
@ -191,21 +171,22 @@ private:
nsresult mTransportStatus;
PRUint32 mTransportProgress;
PRUint32 mTransportProgressMax;
PRPackedBool mTransportStatusInProgress;
PRUint16 mRestartCount; // the number of times this transaction has been restarted
PRUint8 mCaps;
PRPackedBool mConnected;
PRPackedBool mHaveStatusLine;
PRPackedBool mHaveAllHeaders;
PRPackedBool mTransactionDone;
PRPackedBool mResponseIsComplete; // == mTransactionDone && NS_SUCCEEDED(mStatus) ?
PRPackedBool mDidContentStart;
PRPackedBool mNoContent; // expecting an empty entity body?
PRPackedBool mReceivedData;
PRPackedBool mDestroying;
PRPackedBool mClosed;
// state flags
PRUint32 mClosed : 1;
PRUint32 mDestroying : 1;
PRUint32 mConnected : 1;
PRUint32 mHaveStatusLine : 1;
PRUint32 mHaveAllHeaders : 1;
PRUint32 mTransactionDone : 1;
PRUint32 mResponseIsComplete : 1;
PRUint32 mDidContentStart : 1;
PRUint32 mNoContent : 1; // expecting an empty entity body
PRUint32 mReceivedData : 1;
PRUint32 mStatusEventPending : 1;
// mClosed := transaction has been explicitly closed
// mTransactionDone := transaction ran to completion or was interrupted