From 2a07b88e407be0a16b351147aa8c19c26892729c Mon Sep 17 00:00:00 2001 From: "darin%netscape.com" Date: Wed, 26 Mar 2003 05:05:49 +0000 Subject: [PATCH] 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 --- .../http/public/nsIHttpAuthenticator.idl | 139 +++++- .../protocol/http/src/nsAHttpConnection.h | 11 + .../protocol/http/src/nsAHttpTransaction.h | 17 + mozilla/netwerk/protocol/http/src/nsHttp.h | 12 +- .../protocol/http/src/nsHttpAuthCache.cpp | 318 ++++++++---- .../protocol/http/src/nsHttpAuthCache.h | 123 +++-- .../protocol/http/src/nsHttpAuthManager.cpp | 13 +- .../protocol/http/src/nsHttpBasicAuth.cpp | 57 +-- .../protocol/http/src/nsHttpChannel.cpp | 465 ++++++++++-------- .../netwerk/protocol/http/src/nsHttpChannel.h | 40 +- .../protocol/http/src/nsHttpConnection.cpp | 15 +- .../protocol/http/src/nsHttpConnection.h | 16 +- .../protocol/http/src/nsHttpConnectionMgr.cpp | 220 +++++++-- .../protocol/http/src/nsHttpConnectionMgr.h | 45 +- .../protocol/http/src/nsHttpDigestAuth.cpp | 87 ++-- .../protocol/http/src/nsHttpPipeline.cpp | 12 + .../protocol/http/src/nsHttpPipeline.h | 32 +- .../protocol/http/src/nsHttpTransaction.cpp | 70 ++- .../protocol/http/src/nsHttpTransaction.h | 45 +- 19 files changed, 1140 insertions(+), 597 deletions(-) diff --git a/mozilla/netwerk/protocol/http/public/nsIHttpAuthenticator.idl b/mozilla/netwerk/protocol/http/public/nsIHttpAuthenticator.idl index 23a90358e8c..b4b791f2886 100644 --- a/mozilla/netwerk/protocol/http/public/nsIHttpAuthenticator.idl +++ b/mozilla/netwerk/protocol/http/public/nsIHttpAuthenticator.idl @@ -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=" + * + * where 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++ diff --git a/mozilla/netwerk/protocol/http/src/nsAHttpConnection.h b/mozilla/netwerk/protocol/http/src/nsAHttpConnection.h index 6cfaeab7b7f..f1ae1a1a45a 100644 --- a/mozilla/netwerk/protocol/http/src/nsAHttpConnection.h +++ b/mozilla/netwerk/protocol/http/src/nsAHttpConnection.h @@ -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__ diff --git a/mozilla/netwerk/protocol/http/src/nsAHttpTransaction.h b/mozilla/netwerk/protocol/http/src/nsAHttpTransaction.h index bfd8391d5ee..15e6ba7a6d4 100644 --- a/mozilla/netwerk/protocol/http/src/nsAHttpTransaction.h +++ b/mozilla/netwerk/protocol/http/src/nsAHttpTransaction.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__ diff --git a/mozilla/netwerk/protocol/http/src/nsHttp.h b/mozilla/netwerk/protocol/http/src/nsHttp.h index add29ac81bc..6eb0716efd6 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttp.h +++ b/mozilla/netwerk/protocol/http/src/nsHttp.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 diff --git a/mozilla/netwerk/protocol/http/src/nsHttpAuthCache.cpp b/mozilla/netwerk/protocol/http/src/nsHttpAuthCache.cpp index 725e4a2471a..1847297ef69 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpAuthCache.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpAuthCache.cpp @@ -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 //----------------------------------------------------------------------------- @@ -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; iPath(); - // 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; iRealm())) - 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; iRealm())) - 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; + } +} diff --git a/mozilla/netwerk/protocol/http/src/nsHttpAuthCache.h b/mozilla/netwerk/protocol/http/src/nsHttpAuthCache.h index 542b65c74de..8e320737b4a 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpAuthCache.h +++ b/mozilla/netwerk/protocol/http/src/nsHttpAuthCache.h @@ -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 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 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); diff --git a/mozilla/netwerk/protocol/http/src/nsHttpAuthManager.cpp b/mozilla/netwerk/protocol/http/src/nsHttpAuthManager.cpp index 931d4cd7923..a5eba97d3cd 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpAuthManager.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpAuthManager.cpp @@ -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 diff --git a/mozilla/netwerk/protocol/http/src/nsHttpBasicAuth.cpp b/mozilla/netwerk/protocol/http/src/nsHttpBasicAuth.cpp index 8e18b353201..f89d5116992 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpBasicAuth.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpBasicAuth.cpp @@ -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; } diff --git a/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp b/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp index 32190192660..74a4507efa1 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp @@ -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 //----------------------------------------------------------------------------- @@ -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 //----------------------------------------------------------------------------- +// 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 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 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 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 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 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 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 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 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 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 //----------------------------------------------------------------------------- diff --git a/mozilla/netwerk/protocol/http/src/nsHttpChannel.h b/mozilla/netwerk/protocol/http/src/nsHttpChannel.h index 8df7340474f..ab2c878b556 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpChannel.h +++ b/mozilla/netwerk/protocol/http/src/nsHttpChannel.h @@ -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 mTransactionPump; - nsCOMPtr 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 { diff --git a/mozilla/netwerk/protocol/http/src/nsHttpConnection.cpp b/mozilla/netwerk/protocol/http/src/nsHttpConnection.cpp index b25e396e5a0..63cbc2ab203 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpConnection.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpConnection.cpp @@ -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 diff --git a/mozilla/netwerk/protocol/http/src/nsHttpConnection.h b/mozilla/netwerk/protocol/http/src/nsHttpConnection.h index 79ad4995300..3637c6c168d 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpConnection.h +++ b/mozilla/netwerk/protocol/http/src/nsHttpConnection.h @@ -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; diff --git a/mozilla/netwerk/protocol/http/src/nsHttpConnectionMgr.cpp b/mozilla/netwerk/protocol/http/src/nsHttpConnectionMgr.cpp index ad015bb0c61..bbf93cad454 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpConnectionMgr.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpConnectionMgr.cpp @@ -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); +} diff --git a/mozilla/netwerk/protocol/http/src/nsHttpConnectionMgr.h b/mozilla/netwerk/protocol/http/src/nsHttpConnectionMgr.h index 04ea8cc43a7..f962c2f4b54 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpConnectionMgr.h +++ b/mozilla/netwerk/protocol/http/src/nsHttpConnectionMgr.h @@ -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; diff --git a/mozilla/netwerk/protocol/http/src/nsHttpDigestAuth.cpp b/mozilla/netwerk/protocol/http/src/nsHttpDigestAuth.cpp index a8665302cb0..9dde2493603 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpDigestAuth.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpDigestAuth.cpp @@ -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 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 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 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 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 diff --git a/mozilla/netwerk/protocol/http/src/nsHttpPipeline.cpp b/mozilla/netwerk/protocol/http/src/nsHttpPipeline.cpp index fd3a89bfee9..007fd1404a9 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpPipeline.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpPipeline.cpp @@ -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) { diff --git a/mozilla/netwerk/protocol/http/src/nsHttpPipeline.h b/mozilla/netwerk/protocol/http/src/nsHttpPipeline.h index 94b6e6410ba..3978e101ee1 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpPipeline.h +++ b/mozilla/netwerk/protocol/http/src/nsHttpPipeline.h @@ -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 *, diff --git a/mozilla/netwerk/protocol/http/src/nsHttpTransaction.cpp b/mozilla/netwerk/protocol/http/src/nsHttpTransaction.cpp index 1563b1867b9..0e41f0e1763 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpTransaction.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpTransaction.cpp @@ -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); diff --git a/mozilla/netwerk/protocol/http/src/nsHttpTransaction.h b/mozilla/netwerk/protocol/http/src/nsHttpTransaction.h index 559f4729a8f..72ad2f0fc94 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpTransaction.h +++ b/mozilla/netwerk/protocol/http/src/nsHttpTransaction.h @@ -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