Bug 513086 - Make redirect API async, r=bz, sr=biesi

--HG--
rename : netwerk/test/unit/test_redirect_caching.js => netwerk/test/unit/test_redirect-caching_passing.js
This commit is contained in:
Honza Bambas 2010-07-28 20:33:06 +02:00
Родитель 95422562da
Коммит f56aa1a89b
33 изменённых файлов: 2083 добавлений и 146 удалений

Просмотреть файл

@ -72,6 +72,7 @@ XPIDLSRCS = \
nsIAuthPromptAdapterFactory.idl \ nsIAuthPromptAdapterFactory.idl \
nsIAuthPromptCallback.idl \ nsIAuthPromptCallback.idl \
nsIAsyncStreamCopier.idl \ nsIAsyncStreamCopier.idl \
nsIAsyncVerifyRedirectCallback.idl \
nsIBufferedStreams.idl \ nsIBufferedStreams.idl \
nsICancelable.idl \ nsICancelable.idl \
nsIChannelPolicy.idl \ nsIChannelPolicy.idl \
@ -149,6 +150,7 @@ EXPORTS = \
nsURIHashKey.h \ nsURIHashKey.h \
nsReadLine.h \ nsReadLine.h \
nsASocketHandler.h \ nsASocketHandler.h \
nsAsyncRedirectVerifyHelper.h \
$(NULL) $(NULL)
include $(topsrcdir)/config/rules.mk include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -0,0 +1,88 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org networking code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Honza Bambas <honzab@firemni.cz>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsAsyncRedirectVerifyHelper_h
#define nsAsyncRedirectVerifyHelper_h
#include "nsIRunnable.h"
#include "nsCOMPtr.h"
class nsIChannel;
/**
* This class simplifies call of OnChannelRedirect of IOService and
* the sink bound with the channel being redirected while the result of
* redirect decision is returned through the callback.
*/
class nsAsyncRedirectVerifyHelper : public nsIRunnable
{
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
public:
/**
* Initialize and runs the chain of OnChannelRedirect calls. OldChannel
* is QI'ed for nsIAsyncVerifyRedirectCallback. The result of the redirect
* decision is passed through this interface back to the oldChannel.
*
* @param oldChan
* channel being redirected, MUST implement
* nsIAsyncVerifyRedirectCallback
* @param newChan
* target of the redirect channel
* @param flags
* redirect flags
* @param synchronize
* set to TRUE if you want the Init method wait synchronously for
* all redirect callbacks
*/
nsresult Init(nsIChannel* oldChan,
nsIChannel* newChan,
PRUint32 flags,
PRBool synchronize = PR_FALSE);
protected:
nsCOMPtr<nsIChannel> mOldChan;
nsCOMPtr<nsIChannel> mNewChan;
PRUint32 mFlags;
PRBool mWaitingForRedirectCallback;
void Callback(nsresult result);
};
#endif

Просмотреть файл

@ -0,0 +1,52 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Honza Bambas <honzab@firemni.cz>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
[scriptable, uuid(8d171460-a716-41f1-92be-8c659db39b45)]
interface nsIAsyncVerifyRedirectCallback : nsISupports
{
/**
* Complement to nsIChannelEventSink asynchronous callback. The result of
* the redirect decision is passed through this callback.
*
* @param result
* Result of the redirect veto decision. If FAILED the redirect has been
* vetoed. If SUCCEEDED the redirect has been allowed by all consumers.
*/
void onRedirectVerifyCallback(in nsresult result);
};

Просмотреть файл

@ -54,6 +54,7 @@ endif
CPPSRCS = \ CPPSRCS = \
nsTransportUtils.cpp \ nsTransportUtils.cpp \
nsAsyncStreamCopier.cpp \ nsAsyncStreamCopier.cpp \
nsAsyncRedirectVerifyHelper.cpp \
nsAuthInformationHolder.cpp \ nsAuthInformationHolder.cpp \
nsBaseChannel.cpp \ nsBaseChannel.cpp \
nsBaseContentStream.cpp \ nsBaseContentStream.cpp \

Просмотреть файл

@ -0,0 +1,127 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org networking code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Honza Bambas <honzab@firemni.cz>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsAsyncRedirectVerifyHelper.h"
#include "nsThreadUtils.h"
#include "nsNetUtil.h"
#include "nsIOService.h"
#include "nsIChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsIAsyncVerifyRedirectCallback.h"
NS_IMPL_ISUPPORTS1(nsAsyncRedirectVerifyHelper, nsIRunnable)
nsresult
nsAsyncRedirectVerifyHelper::Init(nsIChannel* oldChan, nsIChannel* newChan,
PRUint32 flags, PRBool synchronize)
{
mOldChan = oldChan;
mNewChan = newChan;
mFlags = flags;
if (synchronize)
mWaitingForRedirectCallback = PR_TRUE;
nsresult rv;
rv = NS_DispatchToMainThread(this);
NS_ENSURE_SUCCESS(rv, rv);
if (synchronize) {
nsIThread *thread = NS_GetCurrentThread();
while (mWaitingForRedirectCallback) {
if (!NS_ProcessNextEvent(thread)) {
return NS_ERROR_UNEXPECTED;
}
}
}
return NS_OK;
}
void
nsAsyncRedirectVerifyHelper::Callback(nsresult result)
{
// TODO E10S OnRedirectCallback has to be called on the original process
nsCOMPtr<nsIAsyncVerifyRedirectCallback> callback(do_QueryInterface(mOldChan));
NS_ASSERTION(callback, "nsAsyncRedirectVerifyHelper: oldChannel doesn't"
" implement nsIAsyncVerifyRedirectCallback");
if (callback)
callback->OnRedirectVerifyCallback(result);
mWaitingForRedirectCallback = PR_FALSE;
}
NS_IMETHODIMP
nsAsyncRedirectVerifyHelper::Run()
{
/* If the channel got canceled after it fired AsyncOnChannelRedirect
* (bug 546606) and before we got here, mostly because docloader
* load has been canceled, we must completely ignore this notification
* and prevent any further notification.
*
* TODO Bug 546606, this must be checked before every single call!
*/
PRBool canceled;
nsCOMPtr<nsIHttpChannelInternal> oldChannelInternal =
do_QueryInterface(mOldChan);
if (oldChannelInternal) {
oldChannelInternal->GetCanceled(&canceled);
if (canceled) {
Callback(NS_BINDING_ABORTED);
return NS_OK;
}
}
// First, the global observer
NS_ASSERTION(gIOService, "Must have an IO service at this point");
nsresult rv = gIOService->OnChannelRedirect(mOldChan, mNewChan, mFlags);
if (NS_FAILED(rv)) {
Callback(rv);
return NS_OK;
}
// Now, the per-channel observers
nsCOMPtr<nsIChannelEventSink> sink;
NS_QueryNotificationCallbacks(mOldChan, sink);
if (sink)
rv = sink->OnChannelRedirect(mOldChan, mNewChan, mFlags);
Callback(rv);
return NS_OK;
}

Просмотреть файл

@ -47,6 +47,7 @@
#include "nsIStreamConverterService.h" #include "nsIStreamConverterService.h"
#include "nsIContentSniffer.h" #include "nsIContentSniffer.h"
#include "nsChannelClassifier.h" #include "nsChannelClassifier.h"
#include "nsAsyncRedirectVerifyHelper.h"
static PLDHashOperator static PLDHashOperator
CopyProperties(const nsAString &key, nsIVariant *data, void *closure) CopyProperties(const nsAString &key, nsIVariant *data, void *closure)
@ -115,49 +116,60 @@ nsBaseChannel::Redirect(nsIChannel *newChannel, PRUint32 redirectFlags,
// we support nsIHttpEventSink if we are an HTTP channel and if this is not // we support nsIHttpEventSink if we are an HTTP channel and if this is not
// an internal redirect. // an internal redirect.
// Global observers. These come first so that other observers don't see nsRefPtr<nsAsyncRedirectVerifyHelper> redirectCallbackHelper =
// redirects that get aborted for security reasons anyway. new nsAsyncRedirectVerifyHelper();
NS_ASSERTION(gIOService, "Must have an IO service");
nsresult rv = gIOService->OnChannelRedirect(this, newChannel, redirectFlags); PRBool checkRedirectSynchronously = !openNewChannel;
mRedirectChannel = newChannel;
mRedirectFlags = redirectFlags;
mOpenRedirectChannel = openNewChannel;
nsresult rv = redirectCallbackHelper->Init(this, newChannel, redirectFlags,
checkRedirectSynchronously);
if (NS_FAILED(rv)) if (NS_FAILED(rv))
return rv; return rv;
if (checkRedirectSynchronously && NS_FAILED(mStatus))
return mStatus;
return NS_OK;
}
nsresult
nsBaseChannel::ContinueRedirect()
{
// Backwards compat for non-internal redirects from a HTTP channel. // Backwards compat for non-internal redirects from a HTTP channel.
if (!(redirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) { // XXX Is our http channel implementation going to derive from nsBaseChannel?
// If not, this code can be removed.
if (!(mRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) {
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(); nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface();
if (httpChannel) { if (httpChannel) {
nsCOMPtr<nsIHttpEventSink> httpEventSink; nsCOMPtr<nsIHttpEventSink> httpEventSink;
GetCallback(httpEventSink); GetCallback(httpEventSink);
if (httpEventSink) { if (httpEventSink) {
rv = httpEventSink->OnRedirect(httpChannel, newChannel); nsresult rv = httpEventSink->OnRedirect(httpChannel, mRedirectChannel);
if (NS_FAILED(rv)) if (NS_FAILED(rv)) {
return rv; return rv;
}
} }
} }
} }
nsCOMPtr<nsIChannelEventSink> channelEventSink; // Make sure to do this _after_ making all the OnChannelRedirect calls
// Give our consumer a chance to observe/block this redirect. mRedirectChannel->SetOriginalURI(OriginalURI());
GetCallback(channelEventSink);
if (channelEventSink) {
rv = channelEventSink->OnChannelRedirect(this, newChannel, redirectFlags);
if (NS_FAILED(rv))
return rv;
}
// Make sure to do this _after_ making all the OnChannelRedirect calls
newChannel->SetOriginalURI(OriginalURI());
// If we fail to open the new channel, then we want to leave this channel // If we fail to open the new channel, then we want to leave this channel
// unaffected, so we defer tearing down our channel until we have succeeded // unaffected, so we defer tearing down our channel until we have succeeded
// with the redirect. // with the redirect.
if (openNewChannel) { if (mOpenRedirectChannel) {
rv = newChannel->AsyncOpen(mListener, mListenerContext); nsresult rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
if (NS_FAILED(rv)) if (NS_FAILED(rv))
return rv; return rv;
} }
mRedirectChannel = nsnull;
// close down this channel // close down this channel
Cancel(NS_BINDING_REDIRECTED); Cancel(NS_BINDING_REDIRECTED);
mListener = nsnull; mListener = nsnull;
@ -256,20 +268,30 @@ void
nsBaseChannel::HandleAsyncRedirect(nsIChannel* newChannel) nsBaseChannel::HandleAsyncRedirect(nsIChannel* newChannel)
{ {
NS_ASSERTION(!mPump, "Shouldn't have gotten here"); NS_ASSERTION(!mPump, "Shouldn't have gotten here");
PRBool doNotify = PR_TRUE;
nsresult rv = mStatus;
if (NS_SUCCEEDED(mStatus)) { if (NS_SUCCEEDED(mStatus)) {
nsresult rv = Redirect(newChannel, rv = Redirect(newChannel,
nsIChannelEventSink::REDIRECT_TEMPORARY, nsIChannelEventSink::REDIRECT_TEMPORARY,
PR_TRUE); PR_TRUE);
if (NS_FAILED(rv)) if (NS_SUCCEEDED(rv)) {
Cancel(rv); // OnRedirectVerifyCallback will be called asynchronously
else return;
doNotify = PR_FALSE; }
} }
ContinueHandleAsyncRedirect(rv);
}
void
nsBaseChannel::ContinueHandleAsyncRedirect(nsresult result)
{
mWaitingOnAsyncRedirect = PR_FALSE; mWaitingOnAsyncRedirect = PR_FALSE;
if (doNotify) { if (NS_FAILED(result))
Cancel(result);
if (NS_FAILED(result) && mListener) {
// Notify our consumer ourselves // Notify our consumer ourselves
mListener->OnStartRequest(this, mListenerContext); mListener->OnStartRequest(this, mListenerContext);
mListener->OnStopRequest(this, mListenerContext, mStatus); mListener->OnStopRequest(this, mListenerContext, mStatus);
@ -306,14 +328,15 @@ nsBaseChannel::ClassifyURI()
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// nsBaseChannel::nsISupports // nsBaseChannel::nsISupports
NS_IMPL_ISUPPORTS_INHERITED6(nsBaseChannel, NS_IMPL_ISUPPORTS_INHERITED7(nsBaseChannel,
nsHashPropertyBag, nsHashPropertyBag,
nsIRequest, nsIRequest,
nsIChannel, nsIChannel,
nsIInterfaceRequestor, nsIInterfaceRequestor,
nsITransportEventSink, nsITransportEventSink,
nsIRequestObserver, nsIRequestObserver,
nsIStreamListener) nsIStreamListener,
nsIAsyncVerifyRedirectCallback)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// nsBaseChannel::nsIRequest // nsBaseChannel::nsIRequest
@ -738,3 +761,21 @@ nsBaseChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
return rv; return rv;
} }
NS_IMETHODIMP
nsBaseChannel::OnRedirectVerifyCallback(nsresult result)
{
if (NS_SUCCEEDED(result))
result = ContinueRedirect();
if (NS_FAILED(result) && !mWaitingOnAsyncRedirect) {
if (NS_SUCCEEDED(mStatus))
mStatus = result;
return NS_OK;
}
if (mWaitingOnAsyncRedirect)
ContinueHandleAsyncRedirect(result);
return NS_OK;
}

Просмотреть файл

@ -52,6 +52,7 @@
#include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestor.h"
#include "nsIProgressEventSink.h" #include "nsIProgressEventSink.h"
#include "nsITransport.h" #include "nsITransport.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsThreadUtils.h" #include "nsThreadUtils.h"
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -70,6 +71,7 @@ class nsBaseChannel : public nsHashPropertyBag
, public nsIChannel , public nsIChannel
, public nsIInterfaceRequestor , public nsIInterfaceRequestor
, public nsITransportEventSink , public nsITransportEventSink
, public nsIAsyncVerifyRedirectCallback
, private nsIStreamListener , private nsIStreamListener
{ {
public: public:
@ -78,6 +80,7 @@ public:
NS_DECL_NSICHANNEL NS_DECL_NSICHANNEL
NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSITRANSPORTEVENTSINK NS_DECL_NSITRANSPORTEVENTSINK
NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
nsBaseChannel(); nsBaseChannel();
@ -246,6 +249,8 @@ private:
// Handle an async redirect callback. This will only be called if we // Handle an async redirect callback. This will only be called if we
// returned success from AsyncOpen while posting a redirect runnable. // returned success from AsyncOpen while posting a redirect runnable.
void HandleAsyncRedirect(nsIChannel* newChannel); void HandleAsyncRedirect(nsIChannel* newChannel);
void ContinueHandleAsyncRedirect(nsresult result);
nsresult ContinueRedirect();
// start URI classifier if requested // start URI classifier if requested
void ClassifyURI(); void ClassifyURI();
@ -281,6 +286,7 @@ private:
nsCOMPtr<nsISupports> mSecurityInfo; nsCOMPtr<nsISupports> mSecurityInfo;
nsCOMPtr<nsIStreamListener> mListener; nsCOMPtr<nsIStreamListener> mListener;
nsCOMPtr<nsISupports> mListenerContext; nsCOMPtr<nsISupports> mListenerContext;
nsCOMPtr<nsIChannel> mRedirectChannel;
nsCString mContentType; nsCString mContentType;
nsCString mContentCharset; nsCString mContentCharset;
PRUint32 mLoadFlags; PRUint32 mLoadFlags;
@ -289,6 +295,8 @@ private:
PRPackedBool mSynthProgressEvents; PRPackedBool mSynthProgressEvents;
PRPackedBool mWasOpened; PRPackedBool mWasOpened;
PRPackedBool mWaitingOnAsyncRedirect; PRPackedBool mWaitingOnAsyncRedirect;
PRPackedBool mOpenRedirectChannel;
PRUint32 mRedirectFlags;
}; };
#endif // !nsBaseChannel_h__ #endif // !nsBaseChannel_h__

Просмотреть файл

@ -55,6 +55,7 @@ HttpBaseChannel::HttpBaseChannel()
, mPriority(PRIORITY_NORMAL) , mPriority(PRIORITY_NORMAL)
, mCaps(0) , mCaps(0)
, mRedirectionLimit(gHttpHandler->RedirectionLimit()) , mRedirectionLimit(gHttpHandler->RedirectionLimit())
, mCanceled(PR_FALSE)
, mIsPending(PR_FALSE) , mIsPending(PR_FALSE)
, mWasOpened(PR_FALSE) , mWasOpened(PR_FALSE)
, mResponseHeadersModified(PR_FALSE) , mResponseHeadersModified(PR_FALSE)
@ -919,6 +920,13 @@ HttpBaseChannel::SetForceAllowThirdPartyCookie(PRBool aForce)
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
HttpBaseChannel::GetCanceled(PRBool *aCanceled)
{
*aCanceled = mCanceled;
return NS_OK;
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// HttpBaseChannel::nsISupportsPriority // HttpBaseChannel::nsISupportsPriority
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

Просмотреть файл

@ -161,6 +161,7 @@ public:
NS_IMETHOD SetCookie(const char *aCookieHeader); NS_IMETHOD SetCookie(const char *aCookieHeader);
NS_IMETHOD GetForceAllowThirdPartyCookie(PRBool *aForce); NS_IMETHOD GetForceAllowThirdPartyCookie(PRBool *aForce);
NS_IMETHOD SetForceAllowThirdPartyCookie(PRBool aForce); NS_IMETHOD SetForceAllowThirdPartyCookie(PRBool aForce);
NS_IMETHOD GetCanceled(PRBool *aCanceled);
// nsISupportsPriority // nsISupportsPriority
NS_IMETHOD GetPriority(PRInt32 *value); NS_IMETHOD GetPriority(PRInt32 *value);
@ -205,12 +206,13 @@ protected:
PRUint8 mCaps; PRUint8 mCaps;
PRUint8 mRedirectionLimit; PRUint8 mRedirectionLimit;
PRUint8 mCanceled : 1;
PRUint8 mIsPending : 1; PRUint8 mIsPending : 1;
PRUint8 mWasOpened : 1; PRUint8 mWasOpened : 1;
PRUint8 mResponseHeadersModified : 1; PRUint8 mResponseHeadersModified : 1;
PRUint8 mAllowPipelining : 1; PRUint8 mAllowPipelining : 1;
PRUint8 mForceAllowThirdPartyCookie : 1; PRUint8 mForceAllowThirdPartyCookie : 1;
PRUint32 mUploadStreamHasHeaders : 1; PRUint8 mUploadStreamHasHeaders : 1;
}; };

Просмотреть файл

@ -92,7 +92,6 @@ nsHttpChannel::nsHttpChannel()
, mApplyConversion(PR_TRUE) , mApplyConversion(PR_TRUE)
, mCachedContentIsValid(PR_FALSE) , mCachedContentIsValid(PR_FALSE)
, mCachedContentIsPartial(PR_FALSE) , mCachedContentIsPartial(PR_FALSE)
, mCanceled(PR_FALSE)
, mTransactionReplaced(PR_FALSE) , mTransactionReplaced(PR_FALSE)
, mAuthRetryPending(PR_FALSE) , mAuthRetryPending(PR_FALSE)
, mResuming(PR_FALSE) , mResuming(PR_FALSE)
@ -105,6 +104,8 @@ nsHttpChannel::nsHttpChannel()
, mLoadedFromApplicationCache(PR_FALSE) , mLoadedFromApplicationCache(PR_FALSE)
, mTracingEnabled(PR_TRUE) , mTracingEnabled(PR_TRUE)
, mCustomConditionalRequest(PR_FALSE) , mCustomConditionalRequest(PR_FALSE)
, mFallingBack(PR_FALSE)
, mWaitingForRedirectCallback(PR_FALSE)
, mRemoteChannel(PR_FALSE) , mRemoteChannel(PR_FALSE)
{ {
LOG(("Creating nsHttpChannel [this=%p]\n", this)); LOG(("Creating nsHttpChannel [this=%p]\n", this));
@ -299,6 +300,10 @@ nsHttpChannel::HandleAsyncNotifyListener()
void void
nsHttpChannel::DoNotifyListener() nsHttpChannel::DoNotifyListener()
{ {
// Make sure mIsPending is set to PR_FALSE. At this moment we are done from
// the point of view of our consumer and we have to report our self
// as not-pending.
mIsPending = PR_FALSE;
if (mListener) { if (mListener) {
mListener->OnStartRequest(this, mListenerContext); mListener->OnStartRequest(this, mListenerContext);
mListener->OnStopRequest(this, mListenerContext, mStatus); mListener->OnStopRequest(this, mListenerContext, mStatus);
@ -329,15 +334,28 @@ nsHttpChannel::HandleAsyncRedirect()
// channel could have been canceled, in which case there would be no point // channel could have been canceled, in which case there would be no point
// in processing the redirect. // in processing the redirect.
if (NS_SUCCEEDED(mStatus)) { if (NS_SUCCEEDED(mStatus)) {
rv = ProcessRedirection(mResponseHead->Status()); PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect);
rv = AsyncProcessRedirection(mResponseHead->Status());
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
// If ProcessRedirection fails, then we have to send out the PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect);
// OnStart/OnStop notifications. ContinueHandleAsyncRedirect(rv);
LOG(("ProcessRedirection failed [rv=%x]\n", rv));
mStatus = rv;
DoNotifyListener();
} }
} }
else {
ContinueHandleAsyncRedirect(NS_OK);
}
}
nsresult
nsHttpChannel::ContinueHandleAsyncRedirect(nsresult rv)
{
if (NS_FAILED(rv)) {
// If AsyncProcessRedirection fails, then we have to send out the
// OnStart/OnStop notifications.
LOG(("ContinueHandleAsyncRedirect got failure result [rv=%x]\n", rv));
mStatus = rv;
DoNotifyListener();
}
// close the cache entry. Blow it away if we couldn't process the redirect // close the cache entry. Blow it away if we couldn't process the redirect
// for some reason (the cache entry might be corrupt). // for some reason (the cache entry might be corrupt).
@ -351,6 +369,8 @@ nsHttpChannel::HandleAsyncRedirect()
if (mLoadGroup) if (mLoadGroup)
mLoadGroup->RemoveRequest(this, nsnull, mStatus); mLoadGroup->RemoveRequest(this, nsnull, mStatus);
return NS_OK;
} }
void void
@ -396,21 +416,34 @@ nsHttpChannel::HandleAsyncFallback()
// channel could have been canceled, in which case there would be no point // channel could have been canceled, in which case there would be no point
// in processing the fallback. // in processing the fallback.
if (!mCanceled) { if (!mCanceled) {
PRBool fallingBack; PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncFallback);
rv = ProcessFallback(&fallingBack); PRBool waitingForRedirectCallback;
if (NS_FAILED(rv) || !fallingBack) { rv = ProcessFallback(&waitingForRedirectCallback);
// If ProcessFallback fails, then we have to send out the if (waitingForRedirectCallback)
// OnStart/OnStop notifications. return;
LOG(("ProcessFallback failed [rv=%x, %d]\n", rv, fallingBack)); PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncFallback);
mStatus = NS_FAILED(rv) ? rv : NS_ERROR_DOCUMENT_NOT_CACHED; }
DoNotifyListener();
} ContinueHandleAsyncFallback(rv);
}
nsresult
nsHttpChannel::ContinueHandleAsyncFallback(nsresult rv)
{
if (!mCanceled && (NS_FAILED(rv) || !mFallingBack)) {
// If ProcessFallback fails, then we have to send out the
// OnStart/OnStop notifications.
LOG(("ProcessFallback failed [rv=%x, %d]\n", rv, mFallingBack));
mStatus = NS_FAILED(rv) ? rv : NS_ERROR_DOCUMENT_NOT_CACHED;
DoNotifyListener();
} }
mIsPending = PR_FALSE; mIsPending = PR_FALSE;
if (mLoadGroup) if (mLoadGroup)
mLoadGroup->RemoveRequest(this, nsnull, mStatus); mLoadGroup->RemoveRequest(this, nsnull, mStatus);
return rv;
} }
nsresult nsresult
@ -891,22 +924,12 @@ nsHttpChannel::ProcessResponse()
#endif #endif
// don't store the response body for redirects // don't store the response body for redirects
MaybeInvalidateCacheEntryForSubsequentGet(); MaybeInvalidateCacheEntryForSubsequentGet();
rv = ProcessRedirection(httpStatus); PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse);
if (NS_SUCCEEDED(rv)) { rv = AsyncProcessRedirection(httpStatus);
InitCacheEntry(); if (NS_FAILED(rv)) {
CloseCacheEntry(PR_FALSE); PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse);
LOG(("AsyncProcessRedirection failed [rv=%x]\n", rv));
if (mCacheForOfflineUse) { rv = ContinueProcessResponse(rv);
// Store response in the offline cache
InitOfflineCacheEntry();
CloseOfflineCacheEntry();
}
}
else {
LOG(("ProcessRedirection failed [rv=%x]\n", rv));
if (mTransaction->SSLConnectFailed())
return ProcessFailedSSLConnect(httpStatus);
rv = ProcessNormal();
} }
break; break;
case 304: case 304:
@ -953,6 +976,28 @@ nsHttpChannel::ProcessResponse()
return rv; return rv;
} }
nsresult
nsHttpChannel::ContinueProcessResponse(nsresult rv)
{
if (NS_SUCCEEDED(rv)) {
InitCacheEntry();
CloseCacheEntry(PR_FALSE);
if (mCacheForOfflineUse) {
// Store response in the offline cache
InitOfflineCacheEntry();
CloseOfflineCacheEntry();
}
return NS_OK;
}
LOG(("ContinueProcessResponse got failure result [rv=%x]\n", rv));
if (mTransaction->SSLConnectFailed()) {
return ProcessFailedSSLConnect(mRedirectType);
}
return ProcessNormal();
}
nsresult nsresult
nsHttpChannel::ProcessNormal() nsHttpChannel::ProcessNormal()
{ {
@ -963,18 +1008,34 @@ nsHttpChannel::ProcessNormal()
PRBool succeeded; PRBool succeeded;
rv = GetRequestSucceeded(&succeeded); rv = GetRequestSucceeded(&succeeded);
if (NS_SUCCEEDED(rv) && !succeeded) { if (NS_SUCCEEDED(rv) && !succeeded) {
PRBool fallingBack; PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessNormal);
rv = ProcessFallback(&fallingBack); PRBool waitingForRedirectCallback;
if (NS_FAILED(rv)) { rv = ProcessFallback(&waitingForRedirectCallback);
DoNotifyListener(); if (waitingForRedirectCallback) {
return rv; // The transaction has been suspended by ProcessFallback.
}
if (fallingBack) {
// Do not continue with normal processing, fallback is in
// progress now.
return NS_OK; return NS_OK;
} }
PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessNormal);
}
return ContinueProcessNormal(NS_OK);
}
nsresult
nsHttpChannel::ContinueProcessNormal(nsresult rv)
{
if (NS_FAILED(rv)) {
// Fill the failure status here, we have failed to fall back, thus we
// have to report our status as failed.
mStatus = rv;
DoNotifyListener();
return rv;
}
if (mFallingBack) {
// Do not continue with normal processing, fallback is in
// progress now.
return NS_OK;
} }
// if we're here, then any byte-range requests failed to result in a partial // if we're here, then any byte-range requests failed to result in a partial
@ -1086,7 +1147,7 @@ nsHttpChannel::ProxyFailover()
// XXXbz so where does this codepath remove us from the loadgroup, // XXXbz so where does this codepath remove us from the loadgroup,
// exactly? // exactly?
return DoReplaceWithProxy(pi); return AsyncDoReplaceWithProxy(pi);
} }
void void
@ -1107,21 +1168,41 @@ nsHttpChannel::HandleAsyncReplaceWithProxy()
nsCOMPtr<nsIProxyInfo> pi; nsCOMPtr<nsIProxyInfo> pi;
pi.swap(mTargetProxyInfo); pi.swap(mTargetProxyInfo);
if (!mCanceled) { if (!mCanceled) {
status = DoReplaceWithProxy(pi); PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncReplaceWithProxy);
if (mLoadGroup && NS_SUCCEEDED(status)) { status = AsyncDoReplaceWithProxy(pi);
mLoadGroup->RemoveRequest(this, nsnull, mStatus); if (NS_SUCCEEDED(status))
} return;
PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncReplaceWithProxy);
} }
if (NS_FAILED(status)) { if (NS_FAILED(status)) {
AsyncAbort(status); ContinueHandleAsyncReplaceWithProxy(status);
} }
} }
nsresult nsresult
nsHttpChannel::DoReplaceWithProxy(nsIProxyInfo* pi) nsHttpChannel::ContinueHandleAsyncReplaceWithProxy(nsresult status)
{ {
LOG(("nsHttpChannel::DoReplaceWithProxy [this=%p pi=%p]", this, pi)); if (mLoadGroup && NS_SUCCEEDED(status)) {
mLoadGroup->RemoveRequest(this, nsnull, mStatus);
}
else if (NS_FAILED(status)) {
AsyncAbort(status);
}
// Return NS_OK here, even it seems to be breaking the async function stack
// contract (i.e. passing the result code to a function bellow).
// ContinueHandleAsyncReplaceWithProxy will always be at the bottom of the
// stack. If we would return the failure code, the async function stack
// logic would cancel the channel synchronously, which is undesired after
// invoking AsyncAbort above.
return NS_OK;
}
nsresult
nsHttpChannel::AsyncDoReplaceWithProxy(nsIProxyInfo* pi)
{
LOG(("nsHttpChannel::AsyncDoReplaceWithProxy [this=%p pi=%p]", this, pi));
nsresult rv; nsresult rv;
nsCOMPtr<nsIChannel> newChannel; nsCOMPtr<nsIChannel> newChannel;
@ -1134,16 +1215,37 @@ nsHttpChannel::DoReplaceWithProxy(nsIProxyInfo* pi)
return rv; return rv;
// Inform consumers about this fake redirect // Inform consumers about this fake redirect
mRedirectChannel = newChannel;
PRUint32 flags = nsIChannelEventSink::REDIRECT_INTERNAL; PRUint32 flags = nsIChannelEventSink::REDIRECT_INTERNAL;
rv = gHttpHandler->OnChannelRedirect(this, newChannel, flags);
PushRedirectAsyncFunc(&nsHttpChannel::ContinueDoReplaceWithProxy);
rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, flags);
if (NS_SUCCEEDED(rv))
rv = WaitForRedirectCallback();
if (NS_FAILED(rv)) {
PopRedirectAsyncFunc(&nsHttpChannel::ContinueDoReplaceWithProxy);
mRedirectChannel = nsnull;
}
return rv;
}
nsresult
nsHttpChannel::ContinueDoReplaceWithProxy(nsresult rv)
{
if (NS_FAILED(rv)) if (NS_FAILED(rv))
return rv; return rv;
NS_PRECONDITION(mRedirectChannel, "No redirect channel?");
// Make sure to do this _after_ calling OnChannelRedirect // Make sure to do this _after_ calling OnChannelRedirect
newChannel->SetOriginalURI(mOriginalURI); mRedirectChannel->SetOriginalURI(mOriginalURI);
// open new channel // open new channel
rv = newChannel->AsyncOpen(mListener, mListenerContext); rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
mRedirectChannel = nsnull;
if (NS_FAILED(rv)) if (NS_FAILED(rv))
return rv; return rv;
@ -1405,12 +1507,13 @@ nsHttpChannel::ProcessNotModified()
} }
nsresult nsresult
nsHttpChannel::ProcessFallback(PRBool *fallingBack) nsHttpChannel::ProcessFallback(PRBool *waitingForRedirectCallback)
{ {
LOG(("nsHttpChannel::ProcessFallback [this=%p]\n", this)); LOG(("nsHttpChannel::ProcessFallback [this=%p]\n", this));
nsresult rv; nsresult rv;
*fallingBack = PR_FALSE; *waitingForRedirectCallback = PR_FALSE;
mFallingBack = PR_FALSE;
// At this point a load has failed (either due to network problems // At this point a load has failed (either due to network problems
// or an error returned on the server). Perform an application // or an error returned on the server). Perform an application
@ -1475,15 +1578,40 @@ nsHttpChannel::ProcessFallback(PRBool *fallingBack)
rv = newChannel->SetLoadFlags(newLoadFlags); rv = newChannel->SetLoadFlags(newLoadFlags);
// Inform consumers about this fake redirect // Inform consumers about this fake redirect
mRedirectChannel = newChannel;
PRUint32 redirectFlags = nsIChannelEventSink::REDIRECT_INTERNAL; PRUint32 redirectFlags = nsIChannelEventSink::REDIRECT_INTERNAL;
rv = gHttpHandler->OnChannelRedirect(this, newChannel, redirectFlags);
PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessFallback);
rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags);
if (NS_SUCCEEDED(rv))
rv = WaitForRedirectCallback();
if (NS_FAILED(rv)) {
PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessFallback);
mRedirectChannel = nsnull;
return rv;
}
// Indicate we are now waiting for the asynchronous redirect callback
// if all went OK.
*waitingForRedirectCallback = PR_TRUE;
return NS_OK;
}
nsresult
nsHttpChannel::ContinueProcessFallback(nsresult rv)
{
if (NS_FAILED(rv)) if (NS_FAILED(rv))
return rv; return rv;
NS_PRECONDITION(mRedirectChannel, "No redirect channel?");
// Make sure to do this _after_ calling OnChannelRedirect // Make sure to do this _after_ calling OnChannelRedirect
newChannel->SetOriginalURI(mOriginalURI); mRedirectChannel->SetOriginalURI(mOriginalURI);
rv = newChannel->AsyncOpen(mListener, mListenerContext); rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
mRedirectChannel = nsnull;
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// close down this channel // close down this channel
@ -1496,7 +1624,7 @@ nsHttpChannel::ProcessFallback(PRBool *fallingBack)
mCallbacks = nsnull; mCallbacks = nsnull;
mProgressSink = nsnull; mProgressSink = nsnull;
*fallingBack = PR_TRUE; mFallingBack = PR_TRUE;
return NS_OK; return NS_OK;
} }
@ -2768,9 +2896,9 @@ nsHttpChannel::SetupReplacementChannel(nsIURI *newURI,
} }
nsresult nsresult
nsHttpChannel::ProcessRedirection(PRUint32 redirectType) nsHttpChannel::AsyncProcessRedirection(PRUint32 redirectType)
{ {
LOG(("nsHttpChannel::ProcessRedirection [this=%p type=%u]\n", LOG(("nsHttpChannel::AsyncProcessRedirection [this=%p type=%u]\n",
this, redirectType)); this, redirectType));
const char *location = mResponseHead->PeekHeader(nsHttp::Location); const char *location = mResponseHead->PeekHeader(nsHttp::Location);
@ -2792,12 +2920,12 @@ nsHttpChannel::ProcessRedirection(PRUint32 redirectType)
return NS_ERROR_REDIRECT_LOOP; return NS_ERROR_REDIRECT_LOOP;
} }
mRedirectType = redirectType;
LOG(("redirecting to: %s [redirection-limit=%u]\n", LOG(("redirecting to: %s [redirection-limit=%u]\n",
location, PRUint32(mRedirectionLimit))); location, PRUint32(mRedirectionLimit)));
nsresult rv; nsresult rv;
nsCOMPtr<nsIChannel> newChannel;
nsCOMPtr<nsIURI> newURI;
// create a new URI using the location header and the current URL // create a new URI using the location header and the current URL
// as a base... // as a base...
@ -2811,36 +2939,49 @@ nsHttpChannel::ProcessRedirection(PRUint32 redirectType)
if (NS_FAILED(rv)) if (NS_FAILED(rv))
originCharset.Truncate(); originCharset.Truncate();
rv = ioService->NewURI(nsDependentCString(location), originCharset.get(), mURI, rv = ioService->NewURI(nsDependentCString(location),
getter_AddRefs(newURI)); originCharset.get(),
mURI,
getter_AddRefs(mRedirectURI));
if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv)) return rv;
if (mApplicationCache) { if (mApplicationCache) {
// if we are redirected to a different origin check if there is a fallback // if we are redirected to a different origin check if there is a fallback
// cache entry to fall back to. we don't care about file strict // cache entry to fall back to. we don't care about file strict
// checking, at least mURI is not a file URI. // checking, at least mURI is not a file URI.
if (!NS_SecurityCompareURIs(mURI, newURI, PR_FALSE)) { if (!NS_SecurityCompareURIs(mURI, mRedirectURI, PR_FALSE)) {
PRBool fallingBack; PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirectionAfterFallback);
rv = ProcessFallback(&fallingBack); PRBool waitingForRedirectCallback;
if (NS_SUCCEEDED(rv) && fallingBack) { rv = ProcessFallback(&waitingForRedirectCallback);
// do not continue with redirect processing, fallback is in if (waitingForRedirectCallback)
// progress now.
return NS_OK; return NS_OK;
} PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirectionAfterFallback);
} }
} }
return ContinueProcessRedirectionAfterFallback(NS_OK);
}
nsresult
nsHttpChannel::ContinueProcessRedirectionAfterFallback(nsresult rv)
{
if (NS_SUCCEEDED(rv) && mFallingBack) {
// do not continue with redirect processing, fallback is in
// progress now.
return NS_OK;
}
// Kill the current cache entry if we are redirecting // Kill the current cache entry if we are redirecting
// back to ourself. // back to ourself.
PRBool redirectingBackToSameURI = PR_FALSE; PRBool redirectingBackToSameURI = PR_FALSE;
if (mCacheEntry && (mCacheAccess & nsICache::ACCESS_WRITE) && if (mCacheEntry && (mCacheAccess & nsICache::ACCESS_WRITE) &&
NS_SUCCEEDED(mURI->Equals(newURI, &redirectingBackToSameURI)) && NS_SUCCEEDED(mURI->Equals(mRedirectURI, &redirectingBackToSameURI)) &&
redirectingBackToSameURI) redirectingBackToSameURI)
mCacheEntry->Doom(); mCacheEntry->Doom();
// move the reference of the old location to the new one if the new // move the reference of the old location to the new one if the new
// one has none. // one has none.
nsCOMPtr<nsIURL> newURL = do_QueryInterface(newURI); nsCOMPtr<nsIURL> newURL = do_QueryInterface(mRedirectURI);
if (newURL) { if (newURL) {
nsCAutoString ref; nsCAutoString ref;
rv = newURL->GetRef(ref); rv = newURL->GetRef(ref);
@ -2855,31 +2996,56 @@ nsHttpChannel::ProcessRedirection(PRUint32 redirectType)
} }
// if we need to re-send POST data then be sure to ask the user first. // if we need to re-send POST data then be sure to ask the user first.
PRBool preserveMethod = (redirectType == 307); PRBool preserveMethod = (mRedirectType == 307);
if (preserveMethod && mUploadStream) { if (preserveMethod && mUploadStream) {
rv = PromptTempRedirect(); rv = PromptTempRedirect();
if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv)) return rv;
} }
rv = ioService->NewChannelFromURI(newURI, getter_AddRefs(newChannel)); nsCOMPtr<nsIIOService> ioService;
rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv)) return rv;
rv = SetupReplacementChannel(newURI, newChannel, preserveMethod); nsCOMPtr<nsIChannel> newChannel;
rv = ioService->NewChannelFromURI(mRedirectURI, getter_AddRefs(newChannel));
if (NS_FAILED(rv)) return rv;
rv = SetupReplacementChannel(mRedirectURI, newChannel, preserveMethod);
if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv)) return rv;
PRUint32 redirectFlags; PRUint32 redirectFlags;
if (redirectType == 301) // Moved Permanently if (mRedirectType == 301) // Moved Permanently
redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT; redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT;
else else
redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY; redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY;
// verify that this is a legal redirect // verify that this is a legal redirect
rv = gHttpHandler->OnChannelRedirect(this, newChannel, redirectFlags); mRedirectChannel = newChannel;
PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection);
rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags);
if (NS_SUCCEEDED(rv))
rv = WaitForRedirectCallback();
if (NS_FAILED(rv)) {
PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection);
mRedirectChannel = nsnull;
}
return rv;
}
nsresult
nsHttpChannel::ContinueProcessRedirection(nsresult rv)
{
if (NS_FAILED(rv)) if (NS_FAILED(rv))
return rv; return rv;
NS_PRECONDITION(mRedirectChannel, "No redirect channel?");
// Make sure to do this _after_ calling OnChannelRedirect // Make sure to do this _after_ calling OnChannelRedirect
newChannel->SetOriginalURI(mOriginalURI); mRedirectChannel->SetOriginalURI(mOriginalURI);
// And now, the deprecated way // And now, the deprecated way
nsCOMPtr<nsIHttpEventSink> httpEventSink; nsCOMPtr<nsIHttpEventSink> httpEventSink;
@ -2887,15 +3053,19 @@ nsHttpChannel::ProcessRedirection(PRUint32 redirectType)
if (httpEventSink) { if (httpEventSink) {
// NOTE: nsIHttpEventSink is only used for compatibility with pre-1.8 // NOTE: nsIHttpEventSink is only used for compatibility with pre-1.8
// versions. // versions.
rv = httpEventSink->OnRedirect(this, newChannel); rv = httpEventSink->OnRedirect(this, mRedirectChannel);
if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv))
return rv;
} }
// XXX we used to talk directly with the script security manager, but that // XXX we used to talk directly with the script security manager, but that
// should really be handled by the event sink implementation. // should really be handled by the event sink implementation.
// begin loading the new channel // begin loading the new channel
rv = newChannel->AsyncOpen(mListener, mListenerContext); rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
if (NS_FAILED(rv)) return rv; mRedirectChannel = nsnull;
if (NS_FAILED(rv))
return rv;
// close down this channel // close down this channel
Cancel(NS_BINDING_REDIRECTED); Cancel(NS_BINDING_REDIRECTED);
@ -2978,6 +3148,7 @@ NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
NS_INTERFACE_MAP_ENTRY(nsITraceableChannel) NS_INTERFACE_MAP_ENTRY(nsITraceableChannel)
NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer) NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer)
NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel) NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel)
NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel) NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -2992,6 +3163,9 @@ nsHttpChannel::Cancel(nsresult status)
LOG((" ignoring; already canceled\n")); LOG((" ignoring; already canceled\n"));
return NS_OK; return NS_OK;
} }
if (mWaitingForRedirectCallback) {
LOG(("channel canceled during wait for redirect callback"));
}
mCanceled = PR_TRUE; mCanceled = PR_TRUE;
mStatus = status; mStatus = status;
if (mProxyRequest) if (mProxyRequest)
@ -3397,22 +3571,52 @@ nsHttpChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
// on proxy errors, try to failover // on proxy errors, try to failover
if (mConnectionInfo->ProxyInfo() && if (mConnectionInfo->ProxyInfo() &&
(mStatus == NS_ERROR_PROXY_CONNECTION_REFUSED || (mStatus == NS_ERROR_PROXY_CONNECTION_REFUSED ||
mStatus == NS_ERROR_UNKNOWN_PROXY_HOST || mStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
mStatus == NS_ERROR_NET_TIMEOUT)) { mStatus == NS_ERROR_NET_TIMEOUT)) {
PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest1);
if (NS_SUCCEEDED(ProxyFailover())) if (NS_SUCCEEDED(ProxyFailover()))
return NS_OK; return NS_OK;
PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest1);
} }
// on other request errors, try to fall back return ContinueOnStartRequest2(NS_OK);
PRBool fallingBack; }
if (NS_FAILED(mStatus) &&
NS_SUCCEEDED(ProcessFallback(&fallingBack)) &&
fallingBack) {
nsresult
nsHttpChannel::ContinueOnStartRequest1(nsresult result)
{
// Success indicates we passed ProxyFailover, in that case we must not continue
// with this code chain.
if (NS_SUCCEEDED(result))
return NS_OK; return NS_OK;
return ContinueOnStartRequest2(result);
}
nsresult
nsHttpChannel::ContinueOnStartRequest2(nsresult result)
{
// on other request errors, try to fall back
if (NS_FAILED(mStatus)) {
PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest3);
PRBool waitingForRedirectCallback;
nsresult rv = ProcessFallback(&waitingForRedirectCallback);
if (waitingForRedirectCallback)
return NS_OK;
PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest3);
} }
return ContinueOnStartRequest3(NS_OK);
}
nsresult
nsHttpChannel::ContinueOnStartRequest3(nsresult result)
{
if (mFallingBack)
return NS_OK;
return CallOnStartRequest(); return CallOnStartRequest();
} }
@ -4126,6 +4330,98 @@ nsHttpChannel::SetChooseApplicationCache(PRBool aChoose)
return NS_OK; return NS_OK;
} }
//-----------------------------------------------------------------------------
// nsHttpChannel::nsIAsyncVerifyRedirectCallback
//-----------------------------------------------------------------------------
nsresult
nsHttpChannel::WaitForRedirectCallback()
{
nsresult rv;
if (mTransactionPump) {
rv = mTransactionPump->Suspend();
NS_ENSURE_SUCCESS(rv, rv);
}
if (mCachePump) {
rv = mCachePump->Suspend();
if (NS_FAILED(rv) && mTransactionPump) {
nsresult resume = mTransactionPump->Resume();
NS_ASSERTION(NS_SUCCEEDED(resume),
"Failed to resume transaction pump");
}
NS_ENSURE_SUCCESS(rv, rv);
}
mWaitingForRedirectCallback = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
nsHttpChannel::OnRedirectVerifyCallback(nsresult result)
{
NS_ASSERTION(mWaitingForRedirectCallback,
"Someone forgot to call WaitForRedirectCallback() ?!");
mWaitingForRedirectCallback = PR_FALSE;
if (mCanceled && NS_SUCCEEDED(result))
result = NS_BINDING_ABORTED;
for (PRUint32 i = mRedirectFuncStack.Length(); i > 0;) {
--i;
// Pop the last function pushed to the stack
nsContinueRedirectionFunc func = mRedirectFuncStack[i];
mRedirectFuncStack.RemoveElementAt(mRedirectFuncStack.Length() - 1);
// Call it with the result we got from the callback or the deeper
// function call.
result = (this->*func)(result);
// If a new function has been pushed to the stack and placed us in the
// waiting state, we need to break the chain and wait for the callback
// again.
if (mWaitingForRedirectCallback)
break;
}
if (NS_FAILED(result) && !mCanceled) {
// First, cancel this channel if we are in failure state to set mStatus
// and let it be propagated to pumps.
Cancel(result);
}
if (!mWaitingForRedirectCallback) {
// We are not waiting for the callback. At this moment we must release
// reference to the redirect target channel, otherwise we may leak.
mRedirectChannel = nsnull;
}
// We always resume the pumps here. If all functions on stack have been
// called we need OnStopRequest to be triggered, and if we broke out of the
// loop above (and are thus waiting for a new callback) the suspension
// count must be balanced in the pumps.
if (mTransactionPump)
mTransactionPump->Resume();
if (mCachePump)
mCachePump->Resume();
return result;
}
void
nsHttpChannel::PushRedirectAsyncFunc(nsContinueRedirectionFunc func)
{
mRedirectFuncStack.AppendElement(func);
}
void
nsHttpChannel::PopRedirectAsyncFunc(nsContinueRedirectionFunc func)
{
NS_ASSERTION(func == mRedirectFuncStack[mRedirectFuncStack.Length() - 1],
"Trying to pop wrong method from redirect async stack!");
mRedirectFuncStack.TruncateLength(mRedirectFuncStack.Length() - 1);
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// nsHttpChannel::nsContentEncodings <public> // nsHttpChannel::nsContentEncodings <public>
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

Просмотреть файл

@ -48,6 +48,7 @@
#include "nsHttpTransaction.h" #include "nsHttpTransaction.h"
#include "nsInputStreamPump.h" #include "nsInputStreamPump.h"
#include "nsThreadUtils.h" #include "nsThreadUtils.h"
#include "nsTArray.h"
#include "nsIHttpEventSink.h" #include "nsIHttpEventSink.h"
#include "nsICachingChannel.h" #include "nsICachingChannel.h"
@ -64,6 +65,7 @@
#include "nsIHttpAuthenticableChannel.h" #include "nsIHttpAuthenticableChannel.h"
#include "nsITraceableChannel.h" #include "nsITraceableChannel.h"
#include "nsIHttpChannelAuthProvider.h" #include "nsIHttpChannelAuthProvider.h"
#include "nsIAsyncVerifyRedirectCallback.h"
class nsAHttpConnection; class nsAHttpConnection;
@ -84,6 +86,7 @@ class nsHttpChannel : public HttpBaseChannel
, public nsIHttpAuthenticableChannel , public nsIHttpAuthenticableChannel
, public nsITraceableChannel , public nsITraceableChannel
, public nsIApplicationCacheChannel , public nsIApplicationCacheChannel
, public nsIAsyncVerifyRedirectCallback
{ {
public: public:
NS_DECL_ISUPPORTS_INHERITED NS_DECL_ISUPPORTS_INHERITED
@ -100,6 +103,7 @@ public:
NS_DECL_NSITRACEABLECHANNEL NS_DECL_NSITRACEABLECHANNEL
NS_DECL_NSIAPPLICATIONCACHECONTAINER NS_DECL_NSIAPPLICATIONCACHECONTAINER
NS_DECL_NSIAPPLICATIONCACHECHANNEL NS_DECL_NSIAPPLICATIONCACHECHANNEL
NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
// nsIHttpAuthenticableChannel. We can't use // nsIHttpAuthenticableChannel. We can't use
// NS_DECL_NSIHTTPAUTHENTICABLECHANNEL because it duplicates cancel() and // NS_DECL_NSIHTTPAUTHENTICABLECHANNEL because it duplicates cancel() and
@ -160,6 +164,8 @@ public: /* internal necko use only */
} }
private: private:
typedef nsresult (nsHttpChannel::*nsContinueRedirectionFunc)(nsresult result);
// AsyncCall may be used to call a member function asynchronously. // AsyncCall may be used to call a member function asynchronously.
// retval isn't refcounted and is set only when event was successfully // retval isn't refcounted and is set only when event was successfully
// posted, the event is returned for the purpose of cancelling when needed // posted, the event is returned for the purpose of cancelling when needed
@ -176,25 +182,38 @@ private:
nsresult ApplyContentConversions(); nsresult ApplyContentConversions();
nsresult CallOnStartRequest(); nsresult CallOnStartRequest();
nsresult ProcessResponse(); nsresult ProcessResponse();
nsresult ContinueProcessResponse(nsresult);
nsresult ProcessNormal(); nsresult ProcessNormal();
nsresult ContinueProcessNormal(nsresult);
nsresult ProcessNotModified(); nsresult ProcessNotModified();
nsresult ProcessRedirection(PRUint32 httpStatus); nsresult AsyncProcessRedirection(PRUint32 httpStatus);
nsresult ContinueProcessRedirection(nsresult);
nsresult ContinueProcessRedirectionAfterFallback(nsresult);
PRBool ShouldSSLProxyResponseContinue(PRUint32 httpStatus); PRBool ShouldSSLProxyResponseContinue(PRUint32 httpStatus);
nsresult ProcessFailedSSLConnect(PRUint32 httpStatus); nsresult ProcessFailedSSLConnect(PRUint32 httpStatus);
nsresult ProcessFallback(PRBool *fallingBack); nsresult ProcessFallback(PRBool *waitingForRedirectCallback);
nsresult ContinueProcessFallback(nsresult);
PRBool ResponseWouldVary(); PRBool ResponseWouldVary();
nsresult ContinueOnStartRequest1(nsresult);
nsresult ContinueOnStartRequest2(nsresult);
nsresult ContinueOnStartRequest3(nsresult);
// redirection specific methods // redirection specific methods
void HandleAsyncRedirect(); void HandleAsyncRedirect();
nsresult ContinueHandleAsyncRedirect(nsresult);
void HandleAsyncNotModified(); void HandleAsyncNotModified();
void HandleAsyncFallback(); void HandleAsyncFallback();
nsresult ContinueHandleAsyncFallback(nsresult);
nsresult PromptTempRedirect(); nsresult PromptTempRedirect();
nsresult SetupReplacementChannel(nsIURI *, nsIChannel *, PRBool preserveMethod); nsresult SetupReplacementChannel(nsIURI *, nsIChannel *, PRBool preserveMethod);
// proxy specific methods // proxy specific methods
nsresult ProxyFailover(); nsresult ProxyFailover();
nsresult DoReplaceWithProxy(nsIProxyInfo *); nsresult AsyncDoReplaceWithProxy(nsIProxyInfo *);
nsresult ContinueDoReplaceWithProxy(nsresult);
void HandleAsyncReplaceWithProxy(); void HandleAsyncReplaceWithProxy();
nsresult ContinueHandleAsyncReplaceWithProxy(nsresult);
nsresult ResolveProxy(); nsresult ResolveProxy();
// cache specific methods // cache specific methods
@ -276,11 +295,14 @@ private:
// cache entry. // cache entry.
nsCString mFallbackKey; nsCString mFallbackKey;
nsCOMPtr<nsIURI> mRedirectURI;
nsCOMPtr<nsIChannel> mRedirectChannel;
PRUint32 mRedirectType;
// state flags // state flags
PRUint32 mApplyConversion : 1; PRUint32 mApplyConversion : 1;
PRUint32 mCachedContentIsValid : 1; PRUint32 mCachedContentIsValid : 1;
PRUint32 mCachedContentIsPartial : 1; PRUint32 mCachedContentIsPartial : 1;
PRUint32 mCanceled : 1;
PRUint32 mTransactionReplaced : 1; PRUint32 mTransactionReplaced : 1;
PRUint32 mAuthRetryPending : 1; PRUint32 mAuthRetryPending : 1;
PRUint32 mResuming : 1; PRUint32 mResuming : 1;
@ -300,6 +322,8 @@ private:
// headers. In such a case we must not override them in the cache code // headers. In such a case we must not override them in the cache code
// and also we want to pass possible 304 code response through. // and also we want to pass possible 304 code response through.
PRUint32 mCustomConditionalRequest : 1; PRUint32 mCustomConditionalRequest : 1;
PRUint32 mFallingBack : 1;
PRUint32 mWaitingForRedirectCallback : 1;
// True iff this channel is servicing a remote HttpChannelChild // True iff this channel is servicing a remote HttpChannelChild
PRUint32 mRemoteChannel : 1; PRUint32 mRemoteChannel : 1;
@ -326,6 +350,12 @@ private:
PRPackedBool mReady; PRPackedBool mReady;
}; };
nsTArray<nsContinueRedirectionFunc> mRedirectFuncStack;
nsresult WaitForRedirectCallback();
void PushRedirectAsyncFunc(nsContinueRedirectionFunc func);
void PopRedirectAsyncFunc(nsContinueRedirectionFunc func);
}; };
#endif // nsHttpChannel_h__ #endif // nsHttpChannel_h__

Просмотреть файл

@ -74,6 +74,7 @@
#include "nsQuickSort.h" #include "nsQuickSort.h"
#include "nsNetUtil.h" #include "nsNetUtil.h"
#include "nsIOService.h" #include "nsIOService.h"
#include "nsAsyncRedirectVerifyHelper.h"
#include "nsIXULAppInfo.h" #include "nsIXULAppInfo.h"
@ -530,22 +531,14 @@ nsHttpHandler::NotifyObservers(nsIHttpChannel *chan, const char *event)
} }
nsresult nsresult
nsHttpHandler::OnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan, nsHttpHandler::AsyncOnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan,
PRUint32 flags) PRUint32 flags)
{ {
// First, the global observer // TODO E10S This helper has to be initialized on the other process
NS_ASSERTION(gIOService, "Must have an IO service at this point"); nsRefPtr<nsAsyncRedirectVerifyHelper> redirectCallbackHelper =
nsresult rv = gIOService->OnChannelRedirect(oldChan, newChan, flags); new nsAsyncRedirectVerifyHelper();
if (NS_FAILED(rv))
return rv;
// Now, the per-channel observers return redirectCallbackHelper->Init(oldChan, newChan, flags);
nsCOMPtr<nsIChannelEventSink> sink;
NS_QueryNotificationCallbacks(oldChan, sink);
if (sink)
rv = sink->OnChannelRedirect(oldChan, newChan, flags);
return rv;
} }
/* static */ nsresult /* static */ nsresult

Просмотреть файл

@ -195,7 +195,7 @@ public:
// Called by channels before a redirect happens. This notifies both the // Called by channels before a redirect happens. This notifies both the
// channel's and the global redirect observers. // channel's and the global redirect observers.
nsresult OnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan, nsresult AsyncOnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan,
PRUint32 flags); PRUint32 flags);
// Called by the channel when the response is read from the cache without // Called by the channel when the response is read from the cache without

Просмотреть файл

@ -45,7 +45,7 @@ interface nsIProxyInfo;
* using any feature exposed by this interface, be aware that this interface * using any feature exposed by this interface, be aware that this interface
* will change and you will be broken. You have been warned. * will change and you will be broken. You have been warned.
*/ */
[scriptable, uuid(0eb66361-faaa-4e52-8c7e-6c25f11f8e3c)] [scriptable, uuid(91dbb42a-dffc-4f47-8b27-9579c0d92c3f)]
interface nsIHttpChannelInternal : nsISupports interface nsIHttpChannelInternal : nsISupports
{ {
/** /**
@ -83,4 +83,9 @@ interface nsIHttpChannelInternal : nsISupports
* wouldn't be. * wouldn't be.
*/ */
attribute boolean forceAllowThirdPartyCookie; attribute boolean forceAllowThirdPartyCookie;
/**
* Returns true iff the channel has been canceled.
*/
readonly attribute boolean canceled;
}; };

Просмотреть файл

@ -66,8 +66,14 @@ ChannelListener.prototype = {
this._got_onstartrequest = true; this._got_onstartrequest = true;
request.QueryInterface(Components.interfaces.nsIChannel); request.QueryInterface(Components.interfaces.nsIChannel);
this._contentLen = request.contentLength; try {
if (this._contentLen == -1) this._contentLen = request.contentLength;
}
catch (ex) {
if (!(this._flags & CL_EXPECT_FAILURE))
do_throw("Could not get contentLength");
}
if (this._contentLen == -1 && !(this._flags & CL_EXPECT_FAILURE))
do_throw("Content length is unknown in onStartRequest!"); do_throw("Content length is unknown in onStartRequest!");
} catch (ex) { } catch (ex) {
do_throw("Error in onStartRequest: " + ex); do_throw("Error in onStartRequest: " + ex);
@ -121,3 +127,31 @@ ChannelListener.prototype = {
} }
} }
}; };
var ES_ABORT_REDIRECT = 0x01;
function ChannelEventSink(flags)
{
this._flags = flags;
}
ChannelEventSink.prototype = {
QueryInterface: function(iid) {
if (iid.equals(Ci.nsIInterfaceRequestor) ||
iid.equals(Ci.nsISupports))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
getInterface: function(iid) {
if (iid.equals(Ci.nsIChannelEventSink))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
onChannelRedirect: function(oldChannel, newChannel, flags) {
if (this._flags & ES_ABORT_REDIRECT) {
throw Cr.NS_BINDING_ABORTED;
}
}
};

Просмотреть файл

@ -0,0 +1,100 @@
do_load_httpd_js();
var httpServer = null;
// Need to randomize, because apparently no one clears our cache
var randomPath = "/redirect/" + Math.random();
var randomURI = "http://localhost:4444" + randomPath;
var cacheUpdateObserver = null;
function make_channel(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel(url, "", null);
}
function make_uri(url) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newURI(url, null, null);
}
var responseBody = "Content body";
// start the test with loading this master entry referencing the manifest
function masterEntryHandler(metadata, response)
{
var masterEntryContent = "<html manifest='/manifest'></html>";
response.setHeader("Content-Type", "text/html");
response.bodyOutputStream.write(masterEntryContent, masterEntryContent.length);
}
// manifest defines fallback namespace from any /redirect path to /content
function manifestHandler(metadata, response)
{
var manifestContent = "CACHE MANIFEST\nFALLBACK:\nredirect /content\n";
response.setHeader("Content-Type", "text/cache-manifest");
response.bodyOutputStream.write(manifestContent, manifestContent.length);
}
// content handler correctly returns some plain text data
function contentHandler(metadata, response)
{
response.setHeader("Content-Type", "text/plain");
response.bodyOutputStream.write(responseBody, responseBody.length);
}
// finally check we got fallback content
function finish_test(request, buffer)
{
do_check_eq(buffer, "");
httpServer.stop(do_test_finished);
}
function run_test()
{
httpServer = new nsHttpServer();
httpServer.registerPathHandler("/masterEntry", masterEntryHandler);
httpServer.registerPathHandler("/manifest", manifestHandler);
httpServer.registerPathHandler("/content", contentHandler);
httpServer.start(4444);
var pm = Cc["@mozilla.org/permissionmanager;1"]
.getService(Ci.nsIPermissionManager);
var uri = make_uri("http://localhost:4444");
if (pm.testPermission(uri, "offline-app") != 0) {
dump("Previous test failed to clear offline-app permission! Expect failures.\n");
}
pm.add(uri, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
var ps = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefBranch);
dump(ps.getBoolPref("browser.cache.offline.enable"));
ps.setBoolPref("browser.cache.offline.enable", true);
ps.setComplexValue("browser.cache.offline.parent_directory", Ci.nsILocalFile, do_get_profile());
cacheUpdateObserver = {observe: function() {
dump("got offline-cache-update-completed\n");
// offline cache update completed.
// In this test the randomURI doesn't exists
var chan = make_channel(randomURI);
chan.loadFlags = (Ci.nsIRequest.INHIBIT_CACHING |
Ci.nsIRequest.LOAD_FROM_CACHE |
Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE);
chan.notificationCallbacks = new ChannelEventSink(ES_ABORT_REDIRECT);
var chanac = chan.QueryInterface(Ci.nsIApplicationCacheChannel);
chanac.chooseApplicationCache = true;
chan.asyncOpen(new ChannelListener(finish_test, null, CL_EXPECT_FAILURE), null);
}}
var os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
os.addObserver(cacheUpdateObserver, "offline-cache-update-completed", false);
var us = Cc["@mozilla.org/offlinecacheupdate-service;1"].
getService(Ci.nsIOfflineCacheUpdateService);
us.scheduleUpdate(make_uri("http://localhost:4444/manifest"),
make_uri("http://localhost:4444/masterEntry"));
do_test_pending();
}

Просмотреть файл

@ -0,0 +1,99 @@
do_load_httpd_js();
var httpServer = null;
// Need to randomize, because apparently no one clears our cache
var randomPath = "/redirect/" + Math.random();
var randomURI = "http://localhost:4444" + randomPath;
var cacheUpdateObserver = null;
function make_channel(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel(url, "", null);
}
function make_uri(url) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newURI(url, null, null);
}
var responseBody = "Content body";
// start the test with loading this master entry referencing the manifest
function masterEntryHandler(metadata, response)
{
var masterEntryContent = "<html manifest='/manifest'></html>";
response.setHeader("Content-Type", "text/html");
response.bodyOutputStream.write(masterEntryContent, masterEntryContent.length);
}
// manifest defines fallback namespace from any /redirect path to /content
function manifestHandler(metadata, response)
{
var manifestContent = "CACHE MANIFEST\nFALLBACK:\nredirect /content\n";
response.setHeader("Content-Type", "text/cache-manifest");
response.bodyOutputStream.write(manifestContent, manifestContent.length);
}
// content handler correctly returns some plain text data
function contentHandler(metadata, response)
{
response.setHeader("Content-Type", "text/plain");
response.bodyOutputStream.write(responseBody, responseBody.length);
}
// finally check we got fallback content
function finish_test(request, buffer)
{
do_check_eq(buffer, responseBody);
httpServer.stop(do_test_finished);
}
function run_test()
{
httpServer = new nsHttpServer();
httpServer.registerPathHandler("/masterEntry", masterEntryHandler);
httpServer.registerPathHandler("/manifest", manifestHandler);
httpServer.registerPathHandler("/content", contentHandler);
httpServer.start(4444);
var pm = Cc["@mozilla.org/permissionmanager;1"]
.getService(Ci.nsIPermissionManager);
var uri = make_uri("http://localhost:4444");
if (pm.testPermission(uri, "offline-app") != 0) {
dump("Previous test failed to clear offline-app permission! Expect failures.\n");
}
pm.add(uri, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
var ps = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefBranch);
dump(ps.getBoolPref("browser.cache.offline.enable"));
ps.setBoolPref("browser.cache.offline.enable", true);
ps.setComplexValue("browser.cache.offline.parent_directory", Ci.nsILocalFile, do_get_profile());
cacheUpdateObserver = {observe: function() {
dump("got offline-cache-update-completed\n");
// offline cache update completed.
// In this test the randomURI doesn't exists
var chan = make_channel(randomURI);
chan.loadFlags = (Ci.nsIRequest.INHIBIT_CACHING |
Ci.nsIRequest.LOAD_FROM_CACHE |
Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE);
var chanac = chan.QueryInterface(Ci.nsIApplicationCacheChannel);
chanac.chooseApplicationCache = true;
chan.asyncOpen(new ChannelListener(finish_test), null);
}}
var os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
os.addObserver(cacheUpdateObserver, "offline-cache-update-completed", false);
var us = Cc["@mozilla.org/offlinecacheupdate-service;1"].
getService(Ci.nsIOfflineCacheUpdateService);
us.scheduleUpdate(make_uri("http://localhost:4444/manifest"),
make_uri("http://localhost:4444/masterEntry"));
do_test_pending();
}

Просмотреть файл

@ -0,0 +1,104 @@
do_load_httpd_js();
var httpServer = null;
// Need to randomize, because apparently no one clears our cache
var randomPath = "/redirect/" + Math.random();
var randomURI = "http://localhost:4444" + randomPath;
var cacheUpdateObserver = null;
function make_channel(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel(url, "", null);
}
function make_uri(url) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newURI(url, null, null);
}
const responseBody = "Content body";
// start the test with loading this master entry referencing the manifest
function masterEntryHandler(metadata, response)
{
var masterEntryContent = "<html manifest='/manifest'></html>";
response.setHeader("Content-Type", "text/html");
response.bodyOutputStream.write(masterEntryContent, masterEntryContent.length);
}
// manifest defines fallback namespace from any /redirect path to /content
function manifestHandler(metadata, response)
{
var manifestContent = "CACHE MANIFEST\nFALLBACK:\nredirect /content\n";
response.setHeader("Content-Type", "text/cache-manifest");
response.bodyOutputStream.write(manifestContent, manifestContent.length);
}
// content handler correctly returns some plain text data
function contentHandler(metadata, response)
{
response.setHeader("Content-Type", "text/plain");
response.bodyOutputStream.write(responseBody, responseBody.length);
}
// redirect handler returns redirect
function redirectHandler(metadata, response)
{
response.setStatusLine(metadata.httpVersion, 301, "Moved");
response.setHeader("Location", "http://example.com/", false);
}
// finally check we got fallback content
function finish_test(request, buffer)
{
do_check_eq(buffer, "");
httpServer.stop(do_test_finished);
}
function run_test()
{
httpServer = new nsHttpServer();
httpServer.registerPathHandler("/masterEntry", masterEntryHandler);
httpServer.registerPathHandler("/manifest", manifestHandler);
httpServer.registerPathHandler("/content", contentHandler);
httpServer.registerPathHandler(randomPath, redirectHandler);
httpServer.start(4444);
var pm = Cc["@mozilla.org/permissionmanager;1"]
.getService(Ci.nsIPermissionManager);
var uri = make_uri("http://localhost:4444");
if (pm.testPermission(uri, "offline-app") != 0) {
dump("Previous test failed to clear offline-app permission! Expect failures.\n");
}
pm.add(uri, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
var ps = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefBranch);
dump(ps.getBoolPref("browser.cache.offline.enable"));
ps.setBoolPref("browser.cache.offline.enable", true);
ps.setComplexValue("browser.cache.offline.parent_directory", Ci.nsILocalFile, do_get_profile());
cacheUpdateObserver = {observe: function() {
dump("got offline-cache-update-completed\n");
// offline cache update completed.
var chan = make_channel(randomURI);
chan.notificationCallbacks = new ChannelEventSink(ES_ABORT_REDIRECT);
var chanac = chan.QueryInterface(Ci.nsIApplicationCacheChannel);
chanac.chooseApplicationCache = true;
chan.asyncOpen(new ChannelListener(finish_test, null, CL_EXPECT_FAILURE), null);
}}
var os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
os.addObserver(cacheUpdateObserver, "offline-cache-update-completed", false);
var us = Cc["@mozilla.org/offlinecacheupdate-service;1"].
getService(Ci.nsIOfflineCacheUpdateService);
us.scheduleUpdate(make_uri("http://localhost:4444/manifest"),
make_uri("http://localhost:4444/masterEntry"));
do_test_pending();
}

Просмотреть файл

@ -0,0 +1,103 @@
do_load_httpd_js();
var httpServer = null;
// Need to randomize, because apparently no one clears our cache
var randomPath = "/redirect/" + Math.random();
var randomURI = "http://localhost:4444" + randomPath;
var cacheUpdateObserver = null;
function make_channel(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel(url, "", null);
}
function make_uri(url) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newURI(url, null, null);
}
var responseBody = "Content body";
// start the test with loading this master entry referencing the manifest
function masterEntryHandler(metadata, response)
{
var masterEntryContent = "<html manifest='/manifest'></html>";
response.setHeader("Content-Type", "text/html");
response.bodyOutputStream.write(masterEntryContent, masterEntryContent.length);
}
// manifest defines fallback namespace from any /redirect path to /content
function manifestHandler(metadata, response)
{
var manifestContent = "CACHE MANIFEST\nFALLBACK:\nredirect /content\n";
response.setHeader("Content-Type", "text/cache-manifest");
response.bodyOutputStream.write(manifestContent, manifestContent.length);
}
// content handler correctly returns some plain text data
function contentHandler(metadata, response)
{
response.setHeader("Content-Type", "text/plain");
response.bodyOutputStream.write(responseBody, responseBody.length);
}
// redirect handler returns redirect
function redirectHandler(metadata, response)
{
response.setStatusLine(metadata.httpVersion, 301, "Moved");
response.setHeader("Location", "http://example.com/", false);
}
// finally check we got fallback content
function finish_test(request, buffer)
{
do_check_eq(buffer, responseBody);
httpServer.stop(do_test_finished);
}
function run_test()
{
httpServer = new nsHttpServer();
httpServer.registerPathHandler("/masterEntry", masterEntryHandler);
httpServer.registerPathHandler("/manifest", manifestHandler);
httpServer.registerPathHandler("/content", contentHandler);
httpServer.registerPathHandler(randomPath, redirectHandler);
httpServer.start(4444);
var pm = Cc["@mozilla.org/permissionmanager;1"]
.getService(Ci.nsIPermissionManager);
var uri = make_uri("http://localhost:4444");
if (pm.testPermission(uri, "offline-app") != 0) {
dump("Previous test failed to clear offline-app permission! Expect failures.\n");
}
pm.add(uri, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
var ps = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefBranch);
dump(ps.getBoolPref("browser.cache.offline.enable"));
ps.setBoolPref("browser.cache.offline.enable", true);
ps.setComplexValue("browser.cache.offline.parent_directory", Ci.nsILocalFile, do_get_profile());
cacheUpdateObserver = {observe: function() {
dump("got offline-cache-update-completed\n");
// offline cache update completed.
var chan = make_channel(randomURI);
var chanac = chan.QueryInterface(Ci.nsIApplicationCacheChannel);
chanac.chooseApplicationCache = true;
chan.asyncOpen(new ChannelListener(finish_test), null);
}}
var os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
os.addObserver(cacheUpdateObserver, "offline-cache-update-completed", false);
var us = Cc["@mozilla.org/offlinecacheupdate-service;1"].
getService(Ci.nsIOfflineCacheUpdateService);
us.scheduleUpdate(make_uri("http://localhost:4444/manifest"),
make_uri("http://localhost:4444/masterEntry"));
do_test_pending();
}

Просмотреть файл

@ -0,0 +1,107 @@
do_load_httpd_js();
var httpServer = null;
// Need to randomize, because apparently no one clears our cache
var randomPath = "/redirect/" + Math.random();
var randomURI = "http://localhost:4444" + randomPath;
var cacheUpdateObserver = null;
function make_channel(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel(url, "", null);
}
function make_uri(url) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newURI(url, null, null);
}
var responseBody = "Content body";
// start the test with loading this master entry referencing the manifest
function masterEntryHandler(metadata, response)
{
var masterEntryContent = "<html manifest='/manifest'></html>";
response.setHeader("Content-Type", "text/html");
response.bodyOutputStream.write(masterEntryContent, masterEntryContent.length);
}
// manifest defines fallback namespace from any /redirect path to /content
function manifestHandler(metadata, response)
{
var manifestContent = "CACHE MANIFEST\nFALLBACK:\nredirect /content\n";
response.setHeader("Content-Type", "text/cache-manifest");
response.bodyOutputStream.write(manifestContent, manifestContent.length);
}
// content handler correctly returns some plain text data
function contentHandler(metadata, response)
{
response.setHeader("Content-Type", "text/plain");
response.bodyOutputStream.write(responseBody, responseBody.length);
}
// redirect handler returns redirect
function redirectHandler(metadata, response)
{
response.setStatusLine(metadata.httpVersion, 301, "Moved");
response.setHeader("Location", "http://example.com/", false);
}
// finally check we got fallback content
function finish_test(request, buffer)
{
do_check_eq(buffer, "");
do_test_finished();
}
function run_test()
{
httpServer = new nsHttpServer();
httpServer.registerPathHandler("/masterEntry", masterEntryHandler);
httpServer.registerPathHandler("/manifest", manifestHandler);
httpServer.registerPathHandler("/content", contentHandler);
httpServer.start(4444);
var pm = Cc["@mozilla.org/permissionmanager;1"]
.getService(Ci.nsIPermissionManager);
var uri = make_uri("http://localhost:4444");
if (pm.testPermission(uri, "offline-app") != 0) {
dump("Previous test failed to clear offline-app permission! Expect failures.\n");
}
pm.add(uri, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
var ps = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefBranch);
dump(ps.getBoolPref("browser.cache.offline.enable"));
ps.setBoolPref("browser.cache.offline.enable", true);
ps.setComplexValue("browser.cache.offline.parent_directory", Ci.nsILocalFile, do_get_profile());
cacheUpdateObserver = {observe: function() {
dump("got offline-cache-update-completed\n");
// offline cache update completed.
httpServer.stop(function() {
// Now shut the server down to have an error in onStartRequest
var chan = make_channel(randomURI);
chan.notificationCallbacks = new ChannelEventSink(ES_ABORT_REDIRECT);
var chanac = chan.QueryInterface(Ci.nsIApplicationCacheChannel);
chanac.chooseApplicationCache = true;
chan.asyncOpen(new ChannelListener(finish_test, null, CL_EXPECT_FAILURE), null);
});
}}
var os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
os.addObserver(cacheUpdateObserver, "offline-cache-update-completed", false);
var us = Cc["@mozilla.org/offlinecacheupdate-service;1"].
getService(Ci.nsIOfflineCacheUpdateService);
us.scheduleUpdate(make_uri("http://localhost:4444/manifest"),
make_uri("http://localhost:4444/masterEntry"));
do_test_pending();
}

Просмотреть файл

@ -0,0 +1,106 @@
do_load_httpd_js();
var httpServer = null;
// Need to randomize, because apparently no one clears our cache
var randomPath = "/redirect/" + Math.random();
var randomURI = "http://localhost:4444" + randomPath;
var cacheUpdateObserver = null;
function make_channel(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel(url, "", null);
}
function make_uri(url) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newURI(url, null, null);
}
var responseBody = "Content body";
// start the test with loading this master entry referencing the manifest
function masterEntryHandler(metadata, response)
{
var masterEntryContent = "<html manifest='/manifest'></html>";
response.setHeader("Content-Type", "text/html");
response.bodyOutputStream.write(masterEntryContent, masterEntryContent.length);
}
// manifest defines fallback namespace from any /redirect path to /content
function manifestHandler(metadata, response)
{
var manifestContent = "CACHE MANIFEST\nFALLBACK:\nredirect /content\n";
response.setHeader("Content-Type", "text/cache-manifest");
response.bodyOutputStream.write(manifestContent, manifestContent.length);
}
// content handler correctly returns some plain text data
function contentHandler(metadata, response)
{
response.setHeader("Content-Type", "text/plain");
response.bodyOutputStream.write(responseBody, responseBody.length);
}
// redirect handler returns redirect
function redirectHandler(metadata, response)
{
response.setStatusLine(metadata.httpVersion, 301, "Moved");
response.setHeader("Location", "http://example.com/", false);
}
// finally check we got fallback content
function finish_test(request, buffer)
{
do_check_eq(buffer, responseBody);
do_test_finished();
}
function run_test()
{
httpServer = new nsHttpServer();
httpServer.registerPathHandler("/masterEntry", masterEntryHandler);
httpServer.registerPathHandler("/manifest", manifestHandler);
httpServer.registerPathHandler("/content", contentHandler);
httpServer.start(4444);
var pm = Cc["@mozilla.org/permissionmanager;1"]
.getService(Ci.nsIPermissionManager);
var uri = make_uri("http://localhost:4444");
if (pm.testPermission(uri, "offline-app") != 0) {
dump("Previous test failed to clear offline-app permission! Expect failures.\n");
}
pm.add(uri, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
var ps = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefBranch);
dump(ps.getBoolPref("browser.cache.offline.enable"));
ps.setBoolPref("browser.cache.offline.enable", true);
ps.setComplexValue("browser.cache.offline.parent_directory", Ci.nsILocalFile, do_get_profile());
cacheUpdateObserver = {observe: function() {
dump("got offline-cache-update-completed\n");
// offline cache update completed.
httpServer.stop(function() {
// Now shut the server down to have an error in onstartrequest
var chan = make_channel(randomURI);
var chanac = chan.QueryInterface(Ci.nsIApplicationCacheChannel);
chanac.chooseApplicationCache = true;
chan.asyncOpen(new ChannelListener(finish_test), null);
});
}}
var os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
os.addObserver(cacheUpdateObserver, "offline-cache-update-completed", false);
var us = Cc["@mozilla.org/offlinecacheupdate-service;1"].
getService(Ci.nsIOfflineCacheUpdateService);
us.scheduleUpdate(make_uri("http://localhost:4444/manifest"),
make_uri("http://localhost:4444/masterEntry"));
do_test_pending();
}

Просмотреть файл

@ -0,0 +1,103 @@
do_load_httpd_js();
var httpServer = null;
// Need to randomize, because apparently no one clears our cache
var randomPath = "/error/" + Math.random();
var randomURI = "http://localhost:4444" + randomPath;
var cacheUpdateObserver = null;
function make_channel(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel(url, "", null);
}
function make_uri(url) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newURI(url, null, null);
}
var responseBody = "Content body";
// start the test with loading this master entry referencing the manifest
function masterEntryHandler(metadata, response)
{
var masterEntryContent = "<html manifest='/manifest'></html>";
response.setHeader("Content-Type", "text/html");
response.bodyOutputStream.write(masterEntryContent, masterEntryContent.length);
}
// manifest defines fallback namespace from any /error path to /content
function manifestHandler(metadata, response)
{
var manifestContent = "CACHE MANIFEST\nFALLBACK:\nerror /content\n";
response.setHeader("Content-Type", "text/cache-manifest");
response.bodyOutputStream.write(manifestContent, manifestContent.length);
}
// content handler correctly returns some plain text data
function contentHandler(metadata, response)
{
response.setHeader("Content-Type", "text/plain");
response.bodyOutputStream.write(responseBody, responseBody.length);
}
// error handler returns error
function errorHandler(metadata, response)
{
response.setStatusLine(metadata.httpVersion, 404, "Bad request");
}
// finally check we got fallback content
function finish_test(request, buffer)
{
do_check_eq(buffer, "");
httpServer.stop(do_test_finished);
}
function run_test()
{
httpServer = new nsHttpServer();
httpServer.registerPathHandler("/masterEntry", masterEntryHandler);
httpServer.registerPathHandler("/manifest", manifestHandler);
httpServer.registerPathHandler("/content", contentHandler);
httpServer.registerPathHandler(randomPath, errorHandler);
httpServer.start(4444);
var pm = Cc["@mozilla.org/permissionmanager;1"]
.getService(Ci.nsIPermissionManager);
var uri = make_uri("http://localhost:4444");
if (pm.testPermission(uri, "offline-app") != 0) {
dump("Previous test failed to clear offline-app permission! Expect failures.\n");
}
pm.add(uri, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
var ps = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefBranch);
dump(ps.getBoolPref("browser.cache.offline.enable"));
ps.setBoolPref("browser.cache.offline.enable", true);
ps.setComplexValue("browser.cache.offline.parent_directory", Ci.nsILocalFile, do_get_profile());
cacheUpdateObserver = {observe: function() {
dump("got offline-cache-update-completed\n");
// offline cache update completed.
var chan = make_channel(randomURI);
chan.notificationCallbacks = new ChannelEventSink(ES_ABORT_REDIRECT);
var chanac = chan.QueryInterface(Ci.nsIApplicationCacheChannel);
chanac.chooseApplicationCache = true;
chan.asyncOpen(new ChannelListener(finish_test, null, CL_EXPECT_FAILURE), null);
}}
var os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
os.addObserver(cacheUpdateObserver, "offline-cache-update-completed", false);
var us = Cc["@mozilla.org/offlinecacheupdate-service;1"].
getService(Ci.nsIOfflineCacheUpdateService);
us.scheduleUpdate(make_uri("http://localhost:4444/manifest"),
make_uri("http://localhost:4444/masterEntry"));
do_test_pending();
}

Просмотреть файл

@ -0,0 +1,102 @@
do_load_httpd_js();
var httpServer = null;
// Need to randomize, because apparently no one clears our cache
var randomPath = "/error/" + Math.random();
var randomURI = "http://localhost:4444" + randomPath;
var cacheUpdateObserver = null;
function make_channel(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel(url, "", null);
}
function make_uri(url) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newURI(url, null, null);
}
var responseBody = "Content body";
// start the test with loading this master entry referencing the manifest
function masterEntryHandler(metadata, response)
{
var masterEntryContent = "<html manifest='/manifest'></html>";
response.setHeader("Content-Type", "text/html");
response.bodyOutputStream.write(masterEntryContent, masterEntryContent.length);
}
// manifest defines fallback namespace from any /error path to /content
function manifestHandler(metadata, response)
{
var manifestContent = "CACHE MANIFEST\nFALLBACK:\nerror /content\n";
response.setHeader("Content-Type", "text/cache-manifest");
response.bodyOutputStream.write(manifestContent, manifestContent.length);
}
// content handler correctly returns some plain text data
function contentHandler(metadata, response)
{
response.setHeader("Content-Type", "text/plain");
response.bodyOutputStream.write(responseBody, responseBody.length);
}
// error handler returns error
function errorHandler(metadata, response)
{
response.setStatusLine(metadata.httpVersion, 404, "Bad request");
}
// finally check we got fallback content
function finish_test(request, buffer)
{
do_check_eq(buffer, responseBody);
httpServer.stop(do_test_finished);
}
function run_test()
{
httpServer = new nsHttpServer();
httpServer.registerPathHandler("/masterEntry", masterEntryHandler);
httpServer.registerPathHandler("/manifest", manifestHandler);
httpServer.registerPathHandler("/content", contentHandler);
httpServer.registerPathHandler(randomPath, errorHandler);
httpServer.start(4444);
var pm = Cc["@mozilla.org/permissionmanager;1"]
.getService(Ci.nsIPermissionManager);
var uri = make_uri("http://localhost:4444");
if (pm.testPermission(uri, "offline-app") != 0) {
dump("Previous test failed to clear offline-app permission! Expect failures.\n");
}
pm.add(uri, "offline-app", Ci.nsIPermissionManager.ALLOW_ACTION);
var ps = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefBranch);
dump(ps.getBoolPref("browser.cache.offline.enable"));
ps.setBoolPref("browser.cache.offline.enable", true);
ps.setComplexValue("browser.cache.offline.parent_directory", Ci.nsILocalFile, do_get_profile());
cacheUpdateObserver = {observe: function() {
dump("got offline-cache-update-completed\n");
// offline cache update completed.
var chan = make_channel(randomURI);
var chanac = chan.QueryInterface(Ci.nsIApplicationCacheChannel);
chanac.chooseApplicationCache = true;
chan.asyncOpen(new ChannelListener(finish_test), null);
}}
var os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
os.addObserver(cacheUpdateObserver, "offline-cache-update-completed", false);
var us = Cc["@mozilla.org/offlinecacheupdate-service;1"].
getService(Ci.nsIOfflineCacheUpdateService);
us.scheduleUpdate(make_uri("http://localhost:4444/manifest"),
make_uri("http://localhost:4444/masterEntry"));
do_test_pending();
}

Просмотреть файл

@ -0,0 +1,62 @@
do_load_httpd_js();
var httpServer = null;
function make_channel(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel(url, "", null);
}
const responseBody = "response body";
function contentHandler(metadata, response)
{
response.setHeader("Content-Type", "text/plain");
response.bodyOutputStream.write(responseBody, responseBody.length);
}
function finish_test(request, buffer)
{
do_check_eq(buffer, "");
httpServer.stop(do_test_finished);
}
function run_test()
{
httpServer = new nsHttpServer();
httpServer.registerPathHandler("/content", contentHandler);
httpServer.start(4444);
var nc = new ChannelEventSink();
var on_modify_request_count = 0;
modifyrequestobserver = {observe: function() {
// We get 2 on-modify-request notifications:
// 1. when proxy service resolves the proxy settings from PAC function
// 2. when we try to fail over the first proxy (moving to the second one)
//
// In the second case we want to cancel the proxy engage, so, do not allow
// redirects from now.
if (++on_modify_request_count == 2)
nc._flags = ES_ABORT_REDIRECT;
}}
var os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
os.addObserver(modifyrequestobserver, "http-on-modify-request", false);
var prefserv = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService);
var prefs = prefserv.getBranch("network.proxy.");
prefs.setIntPref("type", 2);
prefs.setCharPref("autoconfig_url", "data:text/plain," +
"function FindProxyForURL(url, host) {return 'PROXY a_non_existent_domain_x7x6c572v:80; PROXY localhost:4444';}"
);
var chan = make_channel("http://localhost:4444/content");
chan.notificationCallbacks = nc;
chan.asyncOpen(new ChannelListener(finish_test, null, CL_EXPECT_FAILURE), null);
do_test_pending();
}

Просмотреть файл

@ -0,0 +1,42 @@
do_load_httpd_js();
var httpServer = null;
function make_channel(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel(url, "", null);
}
const responseBody = "response body";
function contentHandler(metadata, response)
{
response.setHeader("Content-Type", "text/plain");
response.bodyOutputStream.write(responseBody, responseBody.length);
}
function finish_test(request, buffer)
{
do_check_eq(buffer, responseBody);
httpServer.stop(do_test_finished);
}
function run_test()
{
httpServer = new nsHttpServer();
httpServer.registerPathHandler("/content", contentHandler);
httpServer.start(4444);
var prefserv = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService);
var prefs = prefserv.getBranch("network.proxy.");
prefs.setIntPref("type", 2);
prefs.setCharPref("autoconfig_url", "data:text/plain," +
"function FindProxyForURL(url, host) {return 'PROXY a_non_existent_domain_x7x6c572v:80; PROXY localhost:4444';}"
);
var chan = make_channel("http://localhost:4444/content");
chan.asyncOpen(new ChannelListener(finish_test, null), null);
do_test_pending();
}

Просмотреть файл

@ -0,0 +1,43 @@
do_load_httpd_js();
var httpServer = null;
function make_channel(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel(url, "", null);
}
const responseBody = "response body";
function contentHandler(metadata, response)
{
response.setHeader("Content-Type", "text/plain");
response.bodyOutputStream.write(responseBody, responseBody.length);
}
function finish_test(request, buffer)
{
do_check_eq(buffer, "");
httpServer.stop(do_test_finished);
}
function run_test()
{
httpServer = new nsHttpServer();
httpServer.registerPathHandler("/content", contentHandler);
httpServer.start(4444);
var prefserv = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService);
var prefs = prefserv.getBranch("network.proxy.");
prefs.setIntPref("type", 2);
prefs.setCharPref("autoconfig_url", "data:text/plain," +
"function FindProxyForURL(url, host) {return 'PROXY localhost:4444';}"
);
var chan = make_channel("http://localhost:4444/content");
chan.notificationCallbacks = new ChannelEventSink(ES_ABORT_REDIRECT);
chan.asyncOpen(new ChannelListener(finish_test, null, CL_EXPECT_FAILURE), null);
do_test_pending();
}

Просмотреть файл

@ -0,0 +1,42 @@
do_load_httpd_js();
var httpServer = null;
function make_channel(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel(url, "", null);
}
const responseBody = "response body";
function contentHandler(metadata, response)
{
response.setHeader("Content-Type", "text/plain");
response.bodyOutputStream.write(responseBody, responseBody.length);
}
function finish_test(request, buffer)
{
do_check_eq(buffer, responseBody);
httpServer.stop(do_test_finished);
}
function run_test()
{
httpServer = new nsHttpServer();
httpServer.registerPathHandler("/content", contentHandler);
httpServer.start(4444);
var prefserv = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService);
var prefs = prefserv.getBranch("network.proxy.");
prefs.setIntPref("type", 2);
prefs.setCharPref("autoconfig_url", "data:text/plain," +
"function FindProxyForURL(url, host) {return 'PROXY localhost:4444';}"
);
var chan = make_channel("http://localhost:4444/content");
chan.asyncOpen(new ChannelListener(finish_test, null), null);
do_test_pending();
}

Просмотреть файл

@ -0,0 +1,62 @@
do_load_httpd_js();
var httpServer = null;
// Need to randomize, because apparently no one clears our cache
var randomPath = "/redirect/" + Math.random();
var randomURI = "http://localhost:4444" + randomPath;
function make_channel(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel(url, "", null);
}
const responseBody = "response body";
function redirectHandler(metadata, response)
{
response.setStatusLine(metadata.httpVersion, 301, "Moved");
response.setHeader("Location", "http://localhost:4444/content", false);
response.setHeader("Cache-control", "max-age=1000", false);
return;
}
function contentHandler(metadata, response)
{
response.setHeader("Content-Type", "text/plain");
response.bodyOutputStream.write(responseBody, responseBody.length);
}
function firstTimeThrough(request, buffer)
{
do_check_eq(buffer, responseBody);
var chan = make_channel(randomURI);
chan.asyncOpen(new ChannelListener(secondTimeThrough, null), null);
}
function secondTimeThrough(request, buffer)
{
do_check_eq(buffer, responseBody);
var chan = make_channel(randomURI);
chan.loadFlags |= Ci.nsIRequest.LOAD_FROM_CACHE;
chan.notificationCallbacks = new ChannelEventSink(ES_ABORT_REDIRECT);
chan.asyncOpen(new ChannelListener(finish_test, null, CL_EXPECT_FAILURE), null);
}
function finish_test(request, buffer)
{
do_check_eq(buffer, "");
httpServer.stop(do_test_finished);
}
function run_test()
{
httpServer = new nsHttpServer();
httpServer.registerPathHandler(randomPath, redirectHandler);
httpServer.registerPathHandler("/content", contentHandler);
httpServer.start(4444);
var chan = make_channel(randomURI);
chan.asyncOpen(new ChannelListener(firstTimeThrough, null), null);
do_test_pending();
}

Просмотреть файл

@ -0,0 +1,43 @@
do_load_httpd_js();
var httpServer = null;
// Need to randomize, because apparently no one clears our cache
var randomPath = "/redirect/" + Math.random();
var randomURI = "http://localhost:4444" + randomPath;
function make_channel(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel(url, "", null);
}
function redirectHandler(metadata, response)
{
response.setStatusLine(metadata.httpVersion, 301, "Moved");
response.setHeader("Location", "httpx://localhost:4444/content", false);
response.setHeader("Cache-control", "max-age=1000", false);
}
function firstTimeThrough(request, buffer)
{
var chan = make_channel(randomURI);
chan.loadFlags |= Ci.nsIRequest.LOAD_FROM_CACHE;
chan.asyncOpen(new ChannelListener(finish_test, null, CL_EXPECT_FAILURE), null);
}
function finish_test(request, buffer)
{
do_check_eq(buffer, "");
httpServer.stop(do_test_finished);
}
function run_test()
{
httpServer = new nsHttpServer();
httpServer.registerPathHandler(randomPath, redirectHandler);
httpServer.start(4444);
var chan = make_channel(randomURI);
chan.asyncOpen(new ChannelListener(firstTimeThrough), null);
do_test_pending();
}

Просмотреть файл

@ -0,0 +1,45 @@
do_load_httpd_js();
var httpServer = null;
// Need to randomize, because apparently no one clears our cache
var randomPath = "/redirect/" + Math.random();
var randomURI = "http://localhost:4444" + randomPath;
function make_channel(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel(url, "", null);
}
const responseBody = "response body";
function redirectHandler(metadata, response)
{
response.setStatusLine(metadata.httpVersion, 301, "Moved");
response.setHeader("Location", "http://localhost:4444/content", false);
}
function contentHandler(metadata, response)
{
response.setHeader("Content-Type", "text/plain");
response.bodyOutputStream.write(responseBody, responseBody.length);
}
function finish_test(request, buffer)
{
do_check_eq(buffer, "");
httpServer.stop(do_test_finished);
}
function run_test()
{
httpServer = new nsHttpServer();
httpServer.registerPathHandler(randomPath, redirectHandler);
httpServer.registerPathHandler("/content", contentHandler);
httpServer.start(4444);
var chan = make_channel(randomURI);
chan.notificationCallbacks = new ChannelEventSink(ES_ABORT_REDIRECT);
chan.asyncOpen(new ChannelListener(finish_test, null), null);
do_test_pending();
}

Просмотреть файл

@ -0,0 +1,36 @@
do_load_httpd_js();
var httpServer = null;
// Need to randomize, because apparently no one clears our cache
var randomPath = "/redirect/" + Math.random();
var randomURI = "http://localhost:4444" + randomPath;
function make_channel(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel(url, "", null);
}
function redirectHandler(metadata, response)
{
response.setStatusLine(metadata.httpVersion, 301, "Moved");
response.setHeader("Location", "httpx://localhost:4444/content", false);
response.setHeader("Cache-Control", "no-cache", false);
}
function finish_test(request, buffer)
{
do_check_eq(buffer, "");
httpServer.stop(do_test_finished);
}
function run_test()
{
httpServer = new nsHttpServer();
httpServer.registerPathHandler(randomPath, redirectHandler);
httpServer.start(4444);
var chan = make_channel(randomURI);
chan.asyncOpen(new ChannelListener(finish_test), null);
do_test_pending();
}

Просмотреть файл

@ -0,0 +1,51 @@
do_load_httpd_js();
var httpServer = null;
// Need to randomize, because apparently no one clears our cache
var randomPath = "/redirect/" + Math.random();
var randomURI = "http://localhost:4444" + randomPath;
function make_channel(url, callback, ctx) {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newChannel(url, "", null);
}
const responseBody = "response body";
function redirectHandler(metadata, response)
{
response.setStatusLine(metadata.httpVersion, 301, "Moved");
response.setHeader("Location", "http://localhost:4444/content", false);
}
function contentHandler(metadata, response)
{
response.setHeader("Content-Type", "text/plain");
response.bodyOutputStream.write(responseBody, responseBody.length);
}
function firstTimeThrough(request, buffer)
{
do_check_eq(buffer, responseBody);
var chan = make_channel(randomURI);
chan.asyncOpen(new ChannelListener(finish_test, null), null);
}
function finish_test(request, buffer)
{
do_check_eq(buffer, responseBody);
httpServer.stop(do_test_finished);
}
function run_test()
{
httpServer = new nsHttpServer();
httpServer.registerPathHandler(randomPath, redirectHandler);
httpServer.registerPathHandler("/content", contentHandler);
httpServer.start(4444);
var chan = make_channel(randomURI);
chan.asyncOpen(new ChannelListener(firstTimeThrough, null), null);
do_test_pending();
}