diff --git a/docshell/base/appstrings.properties b/docshell/base/appstrings.properties index c98c172915c..bf1fed3c425 100644 --- a/docshell/base/appstrings.properties +++ b/docshell/base/appstrings.properties @@ -23,6 +23,7 @@ dnsNotFound=%s could not be found. Please check the name and try again. protocolNotFound=%s is not a registered protocol. connectionFailure=The connection was refused when attempting to contact %s. netTimeout=The operation timed out when attempting to contact %s. +redirectLoop=Redirection limit for this URL exceeded. Loading aborted. repost=The page you are trying to view contains POSTDATA that has expired from cache. If you resend the data, any action the form carried out (such as a search or online purchase) will be repeated. To resend the data, click OK. Otherwise, click Cancel. repostConfirm=The page you are trying to view contains POSTDATA. If you resend the data, any action the form carried out (such as a search or online purchase) will be repeated. To resend the data, click OK. Otherwise, click Cancel. fileNotFound=%s does not exist or could not be accessed. Please check the file path and try again. diff --git a/docshell/base/nsWebShell.cpp b/docshell/base/nsWebShell.cpp index 00864114547..840dd082d1c 100644 --- a/docshell/base/nsWebShell.cpp +++ b/docshell/base/nsWebShell.cpp @@ -1218,6 +1218,26 @@ nsresult nsWebShell::EndPageLoad(nsIWebProgress *aProgress, } } } + // + // Doc failed to load because the server generated too many redirects + // + else if (aStatus == NS_ERROR_REDIRECT_LOOP) { + nsCOMPtr prompter; + nsCOMPtr stringBundle; + + rv = GetPromptAndStringBundle(getter_AddRefs(prompter), + getter_AddRefs(stringBundle)); + if (!stringBundle) { + return rv; + } + + nsXPIDLString messageStr; + rv = stringBundle->GetStringFromName(NS_LITERAL_STRING("redirectLoop").get(), + getter_Copies(messageStr)); + if (NS_FAILED(rv)) return rv; + + prompter->Alert(nsnull, messageStr); + } } // if we have a host return NS_OK; diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index cda4debe1c0..ec399246f5a 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -43,6 +43,7 @@ pref("network.search.url","http://cgi.netscape.com/cgi-bin/url_search.cgi?search pref("keyword.URL", "http://keyword.netscape.com/keyword/"); pref("keyword.enabled", false); pref("general.useragent.locale", "chrome://navigator/locale/navigator.properties"); +pref("general.useragent.override", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:0.9.5+) Gecko/20011010 Netscape6/6.1b1"); pref("general.useragent.misc", "rv:0.9.6+"); pref("general.startup.browser", true); @@ -369,6 +370,9 @@ pref("network.http.accept.default", "text/xml, application/xml, application/xhtm pref("network.http.sendRefererHeader", 2); // 0=don't send any, 1=send only on clicks, 2=send on image requests as well +// Maximum number of consecutive redirects before aborting. +pref("network.http.redirection-limit", 10); + // Enable http compression: comment this out in case of problems with 1.1 pref("network.http.accept-encoding" ,"gzip, deflate, compress;q=0.9"); diff --git a/netwerk/base/public/netCore.h b/netwerk/base/public/netCore.h index 09f9d4b4300..d8ac33e35e8 100644 --- a/netwerk/base/public/netCore.h +++ b/netwerk/base/public/netCore.h @@ -68,6 +68,9 @@ #define NS_ERROR_PORT_ACCESS_NOT_ALLOWED \ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 19) +#define NS_ERROR_REDIRECT_LOOP \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 31) + #undef NS_NET #ifdef _IMPL_NS_NET #if defined(XP_PC) && !defined(XP_OS2) diff --git a/netwerk/protocol/http/public/nsIHttpChannel.idl b/netwerk/protocol/http/public/nsIHttpChannel.idl index a3bd3d3268b..b145ae17719 100644 --- a/netwerk/protocol/http/public/nsIHttpChannel.idl +++ b/netwerk/protocol/http/public/nsIHttpChannel.idl @@ -91,6 +91,13 @@ interface nsIHttpChannel : nsIChannel * by default. */ attribute boolean applyConversion; + + /** + * This attribute specifies the number of redirects this channel is allowed + * to make. If zero, the channel will fail to redirect and will generate + * a NS_ERROR_REDIRECT_LOOP failure status. + */ + attribute unsigned long redirectionLimit; }; /** diff --git a/netwerk/protocol/http/src/nsHttpChannel.cpp b/netwerk/protocol/http/src/nsHttpChannel.cpp index c86dcb4b436..edffbad1924 100644 --- a/netwerk/protocol/http/src/nsHttpChannel.cpp +++ b/netwerk/protocol/http/src/nsHttpChannel.cpp @@ -62,6 +62,7 @@ nsHttpChannel::nsHttpChannel() , mCacheAccess(0) , mPostID(0) , mRequestTime(0) + , mRedirectionLimit(nsHttpHandler::get()->RedirectionLimit()) , mIsPending(PR_FALSE) , mApplyConversion(PR_TRUE) , mFromCacheOnly(PR_FALSE) @@ -351,8 +352,18 @@ nsHttpChannel::HandleAsyncRedirect() // in processing the redirect. if (NS_SUCCEEDED(mStatus)) { rv = ProcessRedirection(mResponseHead->Status()); - if (NS_FAILED(rv)) + if (NS_FAILED(rv)) { + // If ProcessRedirection fails, then we have to send out the + // OnStart/OnStop notifications. + LOG(("ProcessRedirection failed [rv=%x]\n", rv)); mStatus = rv; + if (mListener) { + mListener->OnStartRequest(this, mListenerContext); + mListener->OnStopRequest(this, mListenerContext, mStatus); + mListener = 0; + mListenerContext = 0; + } + } } // close the cache entry... blow it away if we couldn't process @@ -463,7 +474,7 @@ nsHttpChannel::ApplyContentConversions() nsresult nsHttpChannel::ProcessResponse() { - nsresult rv = NS_OK; + nsresult rv; PRUint32 httpStatus = mResponseHead->Status(); LOG(("nsHttpChannel::ProcessResponse [this=%x httpStatus=%u]\n", @@ -478,6 +489,7 @@ nsHttpChannel::ProcessResponse() case 200: case 203: case 206: + // these can normally be cached rv = ProcessNormal(); break; case 300: @@ -485,19 +497,9 @@ nsHttpChannel::ProcessResponse() case 302: case 307: // these redirects can be cached (don't store the response body) - if (mCacheEntry) { - rv = InitCacheEntry(); - if (NS_SUCCEEDED(rv) && mCacheEntry) { - // XXX we must open an output stream to force the cache service to - // select a cache device for our entry -- bad cache service!! - rv = mCacheEntry->GetTransport(getter_AddRefs(mCacheTransport)); - if (NS_SUCCEEDED(rv)) { - nsCOMPtr out; - rv = mCacheTransport->OpenOutputStream(0, PRUint32(-1), 0, getter_AddRefs(out)); - } - } - CloseCacheEntry(rv); - } + if (mCacheEntry) + CloseCacheEntry(InitCacheEntry()); + rv = ProcessRedirection(httpStatus); if (NS_FAILED(rv)) { LOG(("ProcessRedirection failed [rv=%x]\n", rv)); @@ -508,6 +510,7 @@ nsHttpChannel::ProcessResponse() case 305: // these redirects cannot be cached CloseCacheEntry(NS_ERROR_ABORT); + rv = ProcessRedirection(httpStatus); if (NS_FAILED(rv)) { LOG(("ProcessRedirection failed [rv=%x]\n", rv)); @@ -951,6 +954,7 @@ nsHttpChannel::ReadFromCache() // server to validate at this time, so just mark the cache entry as // valid in order to allow others access to this cache entry. mCacheEntry->MarkValid(); + mCacheAccess = nsICache::ACCESS_READ; } // if this is a cached redirect, we must process the redirect asynchronously @@ -1140,7 +1144,15 @@ nsHttpChannel::ProcessRedirection(PRUint32 redirectType) if (!location) return NS_ERROR_FAILURE; - LOG(("redirecting to: %s\n", location)); + if (mRedirectionLimit == 0) { + LOG(("redirection limit reached!\n")); + // this error code is fatal, and should be conveyed to our listener. + Cancel(NS_ERROR_REDIRECT_LOOP); + return NS_ERROR_REDIRECT_LOOP; + } + + LOG(("redirecting to: %s [redirection-limit=%u]\n", + location, PRUint32(mRedirectionLimit))); nsresult rv; nsCOMPtr newChannel; @@ -1210,6 +1222,8 @@ nsHttpChannel::ProcessRedirection(PRUint32 redirectType) httpChannel->SetReferrer(mReferrer, mReferrerType); // convey the mApplyConversion flag (bug 91862) httpChannel->SetApplyConversion(mApplyConversion); + // convey the new redirection limit + httpChannel->SetRedirectionLimit(mRedirectionLimit - 1); } // call out to the event sink to notify it of this redirection. @@ -1694,16 +1708,16 @@ nsHttpChannel::GetCurrentPath(char **path) //----------------------------------------------------------------------------- NS_IMPL_THREADSAFE_ISUPPORTS10(nsHttpChannel, - nsIRequest, - nsIChannel, - nsIRequestObserver, - nsIStreamListener, - nsIHttpChannel, - nsIInterfaceRequestor, - nsIProgressEventSink, - nsICachingChannel, - nsIUploadChannel, - nsICacheListener) + nsIRequest, + nsIChannel, + nsIRequestObserver, + nsIStreamListener, + nsIHttpChannel, + nsIInterfaceRequestor, + nsIProgressEventSink, + nsICachingChannel, + nsIUploadChannel, + nsICacheListener) //----------------------------------------------------------------------------- // nsHttpChannel::nsIRequest @@ -2279,6 +2293,21 @@ nsHttpChannel::SetApplyConversion(PRBool value) return NS_OK; } +NS_IMETHODIMP +nsHttpChannel::GetRedirectionLimit(PRUint32 *value) +{ + NS_ENSURE_ARG_POINTER(value); + *value = PRUint32(mRedirectionLimit); + return NS_OK; +} + +NS_IMETHODIMP +nsHttpChannel::SetRedirectionLimit(PRUint32 value) +{ + mRedirectionLimit = CLAMP(value, 0, 0xff); + return NS_OK; +} + //----------------------------------------------------------------------------- // nsHttpChannel::nsIRequestObserver //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/http/src/nsHttpChannel.h b/netwerk/protocol/http/src/nsHttpChannel.h index f30a05da5eb..ecf1286c6ab 100644 --- a/netwerk/protocol/http/src/nsHttpChannel.h +++ b/netwerk/protocol/http/src/nsHttpChannel.h @@ -163,6 +163,9 @@ private: nsXPIDLString mProxyUser; nsXPIDLString mProxyPass; + // redirection specific data. + PRUint8 mRedirectionLimit; + PRPackedBool mIsPending; PRPackedBool mApplyConversion; PRPackedBool mFromCacheOnly; diff --git a/netwerk/protocol/http/src/nsHttpHandler.cpp b/netwerk/protocol/http/src/nsHttpHandler.cpp index 95406d6bb3a..bf8ab7a1876 100644 --- a/netwerk/protocol/http/src/nsHttpHandler.cpp +++ b/netwerk/protocol/http/src/nsHttpHandler.cpp @@ -111,6 +111,7 @@ nsHttpHandler::nsHttpHandler() , mMaxConnectionsPerServer(8) , mMaxPersistentConnectionsPerServer(2) , mMaxPersistentConnectionsPerProxy(4) + , mRedirectionLimit(10) , mLastUniqueID(NowInSeconds()) , mSessionStartTime(0) , mActiveConnections(0) @@ -1234,11 +1235,17 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref) } if (PREF_CHANGED(HTTP_PREF("sendRefererHeader"))) { - rv = prefs->GetIntPref(HTTP_PREF("sendRefererHeader"), (PRInt32 *) &val); + rv = prefs->GetIntPref(HTTP_PREF("sendRefererHeader"), &val); if (NS_SUCCEEDED(rv)) mReferrerLevel = (PRUint8) CLAMP(val, 0, 0xff); } + if (PREF_CHANGED(HTTP_PREF("redirection-limit"))) { + rv = prefs->GetIntPref(HTTP_PREF("redirection-limit"), &val); + if (NS_SUCCEEDED(rv)) + mRedirectionLimit = (PRUint8) CLAMP(val, 0, 0xff); + } + if (PREF_CHANGED(HTTP_PREF("version"))) { nsXPIDLCString httpVersion; prefs->GetCharPref(HTTP_PREF("version"), getter_Copies(httpVersion)); diff --git a/netwerk/protocol/http/src/nsHttpHandler.h b/netwerk/protocol/http/src/nsHttpHandler.h index 44a1e3a43eb..20f8990f400 100644 --- a/netwerk/protocol/http/src/nsHttpHandler.h +++ b/netwerk/protocol/http/src/nsHttpHandler.h @@ -86,6 +86,7 @@ public: const char *UserAgent(); nsHttpVersion DefaultVersion() { return mHttpVersion; } PRUint8 ReferrerLevel() { return mReferrerLevel; } + PRUint8 RedirectionLimit() { return mRedirectionLimit; } PRUint16 IdleTimeout() { return mIdleTimeout; } PRUint16 MaxRequestAttempts() { return mMaxRequestAttempts; } nsIIDNService *IDNConverter() { return mIDNConverter; } @@ -229,6 +230,8 @@ private: PRUint8 mMaxPersistentConnectionsPerServer; PRUint8 mMaxPersistentConnectionsPerProxy; + PRUint8 mRedirectionLimit; + nsCString mAccept; nsCString mAcceptLanguages; nsCString mAcceptEncodings;