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:
parent
e1a6768967
commit
2a07b88e40
@ -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++
|
||||
|
||||
@ -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__
|
||||
|
||||
@ -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__
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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>
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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 *,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user