Created
September 25, 2013 22:46
-
-
Save jdm/6707215 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # HG changeset patch | |
| # Parent 59beb1868522e2681b8721e29613d0521fe7e6c2 | |
| # User Josh Matthews <[email protected]> | |
| diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp | |
| --- a/docshell/base/nsDocShell.cpp | |
| +++ b/docshell/base/nsDocShell.cpp | |
| @@ -16,16 +16,17 @@ | |
| #include "mozilla/dom/TabChild.h" | |
| #include "mozilla/Preferences.h" | |
| #include "mozilla/Services.h" | |
| #include "mozilla/StartupTimeline.h" | |
| #include "mozilla/Telemetry.h" | |
| #include "mozilla/unused.h" | |
| #include "mozilla/Util.h" | |
| #include "mozilla/VisualEventTracer.h" | |
| +#include "mozilla/net/AlternateSourceChannel.h" | |
| #ifdef MOZ_LOGGING | |
| // so we can get logging even in release builds (but only for some things) | |
| #define FORCE_PR_LOG 1 | |
| #endif | |
| #include "nsIContent.h" | |
| #include "nsIDocument.h" | |
| @@ -191,16 +192,17 @@ static NS_DEFINE_CID(kAppShellCID, NS_AP | |
| #if defined(DEBUG_bryner) || defined(DEBUG_chb) | |
| //#define DEBUG_DOCSHELL_FOCUS | |
| #define DEBUG_PAGE_CACHE | |
| #endif | |
| using namespace mozilla; | |
| using namespace mozilla::dom; | |
| +using namespace mozilla::net; | |
| // Number of documents currently loading | |
| static int32_t gNumberOfDocumentsLoading = 0; | |
| // Global count of existing docshells. | |
| static int32_t gDocShellCount = 0; | |
| // Global count of docshells with the private attribute set | |
| @@ -9747,16 +9749,22 @@ nsDocShell::DoURILoad(nsIURI * aURI, | |
| } | |
| } | |
| nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(channel)); | |
| if (timedChannel) { | |
| timedChannel->SetTimingEnabled(true); | |
| } | |
| + //TODO(jdm): Check document for presence of navigation controller | |
| + if (false) { | |
| + channel = new AlternateSourceChannel(channel); | |
| + //TODO(jdm): inform navigation controller about channel | |
| + } | |
| + | |
| rv = DoChannelLoad(channel, uriLoader, aBypassClassifier); | |
| // | |
| // If the channel load failed, we failed and nsIWebProgress just ain't | |
| // gonna happen. | |
| // | |
| if (NS_SUCCEEDED(rv)) { | |
| if (aDocShell) { | |
| diff --git a/netwerk/base/public/moz.build b/netwerk/base/public/moz.build | |
| --- a/netwerk/base/public/moz.build | |
| +++ b/netwerk/base/public/moz.build | |
| @@ -1,16 +1,17 @@ | |
| # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- | |
| # vim: set filetype=python: | |
| # This Source Code Form is subject to the terms of the Mozilla Public | |
| # License, v. 2.0. If a copy of the MPL was not distributed with this | |
| # file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
| XPIDL_SOURCES += [ | |
| 'mozIThirdPartyUtil.idl', | |
| + 'nsIAlternateSourceChannel.idl', | |
| 'nsIApplicationCache.idl', | |
| 'nsIApplicationCacheChannel.idl', | |
| 'nsIApplicationCacheContainer.idl', | |
| 'nsIApplicationCacheService.idl', | |
| 'nsIArrayBufferInputStream.idl', | |
| 'nsIAsyncStreamCopier.idl', | |
| 'nsIAsyncVerifyRedirectCallback.idl', | |
| 'nsIAuthInformation.idl', | |
| diff --git a/netwerk/base/public/nsIAlternateSourceChannel.idl b/netwerk/base/public/nsIAlternateSourceChannel.idl | |
| new file mode 100644 | |
| --- /dev/null | |
| +++ b/netwerk/base/public/nsIAlternateSourceChannel.idl | |
| @@ -0,0 +1,16 @@ | |
| +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | |
| +/* This Source Code Form is subject to the terms of the Mozilla Public | |
| + * License, v. 2.0. If a copy of the MPL was not distributed with this | |
| + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
| + | |
| +#include "nsISupports.idl" | |
| + | |
| +interface nsIChannel; | |
| +interface nsIInputStream; | |
| + | |
| +[scriptable, uuid(9bd368b6-3893-4fd7-b7e7-40a9cd45e684)] | |
| +interface nsIAlternateSourceChannel : nsISupports { | |
| + void forwardToOriginalChannel(); | |
| + void initiateAlternateResponse(in nsIInputStream body); | |
| + readonly attribute nsIChannel wrappedChannel; | |
| +}; | |
| diff --git a/netwerk/base/public/nsINetUtil.idl b/netwerk/base/public/nsINetUtil.idl | |
| --- a/netwerk/base/public/nsINetUtil.idl | |
| +++ b/netwerk/base/public/nsINetUtil.idl | |
| @@ -1,15 +1,16 @@ | |
| /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | |
| /* This Source Code Form is subject to the terms of the Mozilla Public | |
| * License, v. 2.0. If a copy of the MPL was not distributed with this | |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
| #include "nsISupports.idl" | |
| +interface nsIChannel; | |
| interface nsIURI; | |
| interface nsIPrefBranch; | |
| /** | |
| * nsINetUtil provides various network-related utility methods. | |
| */ | |
| [scriptable, uuid(ca68c485-9db3-4c12-82a6-4fab7948e2d5)] | |
| interface nsINetUtil : nsISupports | |
| @@ -187,9 +188,11 @@ interface nsINetUtil : nsISupports | |
| * @return whether a charset parameter was found. This can be false even in | |
| * cases when parseContentType would claim to have a charset, if the type | |
| * that won out does not have a charset parameter specified. | |
| */ | |
| boolean extractCharsetFromContentType(in AUTF8String aTypeHeader, | |
| out AUTF8String aCharset, | |
| out long aCharsetStart, | |
| out long aCharsetEnd); | |
| + | |
| + nsIChannel createAlternateSourceChannel(in nsIChannel aChannel); | |
| }; | |
| diff --git a/netwerk/base/src/AlternateSourceChannel.cpp b/netwerk/base/src/AlternateSourceChannel.cpp | |
| new file mode 100644 | |
| --- /dev/null | |
| +++ b/netwerk/base/src/AlternateSourceChannel.cpp | |
| @@ -0,0 +1,403 @@ | |
| +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | |
| +/* This Source Code Form is subject to the terms of the Mozilla Public | |
| + * License, v. 2.0. If a copy of the MPL was not distributed with this | |
| + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
| + | |
| +#include "nsIHttpChannelInternal.h" | |
| +#include "nsInputStreamPump.h" | |
| +#include "nsIOService.h" | |
| +#include "AlternateSourceChannel.h" | |
| +#include "nsNetUtil.h" | |
| +#include "nsSocketTransportService2.h" | |
| +#include "nsHttpTransaction.h" | |
| +#include "nsThreadUtils.h" | |
| + | |
| +//XXXjdm should probably make QI forward to mWrappedChannel for non-nsIChannel/nsISupports | |
| + | |
| +class nsIInterfaceRequestor; | |
| + | |
| +namespace mozilla { | |
| +namespace net { | |
| + | |
| +NS_IMPL_ISUPPORTS3(AlternateSourceChannel, | |
| + nsIChannel, | |
| + nsIAlternateSourceChannel, | |
| + nsIStreamListener) | |
| + | |
| +AlternateSourceChannel::AlternateSourceChannel(nsIChannel* aWrappedChannel) | |
| +: mWrappedChannel(aWrappedChannel) | |
| +, mStatus(NS_OK) | |
| +, mSuspendCount(0) | |
| +, mContentLength(-1) | |
| +, mForwardToWrapped(false) | |
| +, mCanceled(false) | |
| +, mPending(false) | |
| +, mWasOpened(false) | |
| +{ | |
| +} | |
| + | |
| +AlternateSourceChannel::~AlternateSourceChannel() | |
| +{ | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetWrappedChannel(nsIChannel** aChannel) | |
| +{ | |
| + NS_ADDREF(*aChannel = mWrappedChannel); | |
| + return NS_OK; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::ForwardToOriginalChannel() | |
| +{ | |
| + if (mForwardToWrapped) { | |
| + return NS_OK; | |
| + } | |
| + | |
| + mForwardToWrapped = true; | |
| + nsCOMPtr<nsIHttpChannelInternal> httpChan = do_QueryInterface(mWrappedChannel); | |
| + if (httpChan) { | |
| + httpChan->AsyncOpenFinish(); | |
| + } else { | |
| + mWrappedChannel->AsyncOpen(mListener, mContext); | |
| + mListener = nullptr; | |
| + mContext = nullptr; | |
| + } | |
| + return NS_OK; | |
| +} | |
| + | |
| +class WriteSegmentsRunnable : public nsRunnable { | |
| +public: | |
| + WriteSegmentsRunnable(nsHttpTransaction* aTransaction, | |
| + AlternateSourceChannel* aWriter) | |
| + : mTransaction(aTransaction) | |
| + , mWriter(aWriter) | |
| + { | |
| + } | |
| + | |
| + NS_IMETHOD Run() | |
| + { | |
| + MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); | |
| + | |
| + uint32_t n; | |
| + nsresult rv = mTransaction->WriteSegments(mWriter, nsIOService::gDefaultSegmentSize, &n); | |
| + if (NS_FAILED(rv)) { | |
| + nsresult reason = rv == NS_BASE_STREAM_CLOSED ? NS_OK : rv; | |
| + mTransaction->Close(reason); | |
| + return NS_OK; | |
| + } | |
| + rv = NS_DispatchToCurrentThread(this); | |
| + NS_ENSURE_SUCCESS(rv, rv); | |
| + return NS_OK; | |
| + } | |
| + | |
| +private: | |
| + nsRefPtr<nsHttpTransaction> mTransaction; | |
| + nsRefPtr<AlternateSourceChannel> mWriter; | |
| +}; | |
| + | |
| +nsresult | |
| +AlternateSourceChannel::OnWriteSegment(char* aBuf, uint32_t aCount, uint32_t* aCountWritten) | |
| +{ | |
| + if (aCount == 0) { | |
| + // some WriteSegments implementations will erroneously call the reader | |
| + // to provide 0 bytes worth of data. we must protect against this case | |
| + // or else we'd end up closing the socket prematurely. | |
| + NS_ERROR("bad WriteSegments implementation"); | |
| + return NS_ERROR_FAILURE; // stop iterating | |
| + } | |
| + nsresult rv = mBody->Read(aBuf, aCount, aCountWritten); | |
| + NS_ENSURE_SUCCESS(rv, rv); | |
| + return NS_OK; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::InitiateAlternateResponse(nsIInputStream* aBody) | |
| +{ | |
| + mBody = aBody; | |
| + | |
| + nsCOMPtr<nsIHttpChannelInternal> httpChan = do_QueryInterface(mWrappedChannel); | |
| + if (httpChan) { | |
| + nsRefPtr<nsHttpTransaction> transaction; | |
| + nsresult rv = httpChan->GetConnectionlessTransaction(getter_AddRefs(transaction)); | |
| + NS_ENSURE_SUCCESS(rv, rv); | |
| + | |
| + nsCOMPtr<nsIRunnable> event = new WriteSegmentsRunnable(transaction, this); | |
| + rv = gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL); | |
| + NS_ENSURE_SUCCESS(rv, rv); | |
| + } else { | |
| + nsresult rv = nsInputStreamPump::Create(getter_AddRefs(mPump), aBody); | |
| + NS_ENSURE_SUCCESS(rv, rv); | |
| + rv = mPump->AsyncRead(this, nullptr); | |
| + NS_ENSURE_SUCCESS(rv, rv); | |
| + } | |
| + | |
| + return NS_OK; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetOriginalURI(nsIURI** aOriginalURI) | |
| +{ | |
| + return mWrappedChannel->GetOriginalURI(aOriginalURI); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::SetOriginalURI(nsIURI* aOriginalURI) | |
| +{ | |
| + return mWrappedChannel->SetOriginalURI(aOriginalURI); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetURI(nsIURI** aURI) | |
| +{ | |
| + return mWrappedChannel->GetURI(aURI); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetOwner(nsISupports** aOwner) | |
| +{ | |
| + return mWrappedChannel->GetOwner(aOwner); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::SetOwner(nsISupports* aOwner) | |
| +{ | |
| + return mWrappedChannel->SetOwner(aOwner); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) | |
| +{ | |
| + return mWrappedChannel->GetNotificationCallbacks(aCallbacks); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) | |
| +{ | |
| + return mWrappedChannel->SetNotificationCallbacks(aCallbacks); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetSecurityInfo(nsISupports** aSecurityInfo) | |
| +{ | |
| + return mWrappedChannel->GetSecurityInfo(aSecurityInfo); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetContentType(nsACString& aContentType) | |
| +{ | |
| + return mWrappedChannel->GetContentType(aContentType); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::SetContentType(const nsACString& aContentType) | |
| +{ | |
| + return mWrappedChannel->SetContentType(aContentType); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetContentCharset(nsACString& aContentCharset) | |
| +{ | |
| + return mWrappedChannel->GetContentCharset(aContentCharset); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::SetContentCharset(const nsACString& aContentCharset) | |
| +{ | |
| + return mWrappedChannel->SetContentCharset(aContentCharset); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetContentLength(int64_t* aContentLength) | |
| +{ | |
| + return mWrappedChannel->GetContentLength(aContentLength); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::SetContentLength(int64_t aContentLength) | |
| +{ | |
| + return mWrappedChannel->SetContentLength(aContentLength); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::Open(nsIInputStream** aStream) | |
| +{ | |
| + return NS_ERROR_NOT_AVAILABLE; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::AsyncOpen(nsIStreamListener* aListener, nsISupports* aContext) | |
| +{ | |
| + NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_FAILURE); | |
| + | |
| + if (mForwardToWrapped) { | |
| + return mWrappedChannel->AsyncOpen(aListener, aContext); | |
| + } | |
| + | |
| + mWasOpened = true; | |
| + | |
| + nsCOMPtr<nsIHttpChannelInternal> httpChan = do_QueryInterface(mWrappedChannel); | |
| + if (httpChan) { | |
| + httpChan->AsyncOpenNetworkless(aListener, aContext); | |
| + } else { | |
| + mListener = aListener; | |
| + mContext = aContext; | |
| + } | |
| + return NS_OK; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetContentDisposition(uint32_t* aContentDisposition) | |
| +{ | |
| + return mWrappedChannel->GetContentDisposition(aContentDisposition); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::SetContentDisposition(uint32_t aContentDisposition) | |
| +{ | |
| + return mWrappedChannel->SetContentDisposition(aContentDisposition); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetContentDispositionFilename(nsAString& aFilename) | |
| +{ | |
| + return mWrappedChannel->GetContentDispositionFilename(aFilename); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::SetContentDispositionFilename(const nsAString& aFilename) | |
| +{ | |
| + return mWrappedChannel->SetContentDispositionFilename(aFilename); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetContentDispositionHeader(nsACString& aHeader) | |
| +{ | |
| + return mWrappedChannel->GetContentDispositionHeader(aHeader); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetName(nsACString& aName) | |
| +{ | |
| + return mWrappedChannel->GetName(aName); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::IsPending(bool* aPending) | |
| +{ | |
| + return mWrappedChannel->IsPending(aPending); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetStatus(nsresult* aStatus) | |
| +{ | |
| + return mWrappedChannel->GetStatus(aStatus); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::Cancel(nsresult aStatus) | |
| +{ | |
| + mCanceled = true; | |
| + mStatus = aStatus; | |
| + return mWrappedChannel->Cancel(aStatus); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::Suspend() | |
| +{ | |
| + if (mPump) { | |
| + mPump->Suspend(); | |
| + } | |
| + return mWrappedChannel->Suspend(); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::Resume() | |
| +{ | |
| + if (mPump) { | |
| + mPump->Resume(); | |
| + } | |
| + return mWrappedChannel->Resume(); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) | |
| +{ | |
| + return mWrappedChannel->GetLoadGroup(aLoadGroup); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) | |
| +{ | |
| + return mWrappedChannel->SetLoadGroup(aLoadGroup); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) | |
| +{ | |
| + return mWrappedChannel->GetLoadFlags(aLoadFlags); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::SetLoadFlags(nsLoadFlags aLoadFlags) | |
| +{ | |
| + return mWrappedChannel->SetLoadFlags(aLoadFlags); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) | |
| +{ | |
| + MOZ_ASSERT(!mForwardToWrapped); | |
| + | |
| + nsresult rv = NS_OK; | |
| + if (mWrappedChannel) { | |
| + nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(mWrappedChannel); | |
| + rv = listener->OnStartRequest(this, mContext); | |
| + } | |
| + return rv; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext, | |
| + nsIInputStream *aInputStream, uint64_t aOffset, | |
| + uint32_t aCount) | |
| +{ | |
| + MOZ_ASSERT(!mCanceled); | |
| + MOZ_ASSERT(!mForwardToWrapped); | |
| + | |
| + nsresult rv = NS_OK; | |
| + if (mWrappedChannel) { | |
| + nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(mWrappedChannel); | |
| + rv = listener->OnDataAvailable(this, mContext, aInputStream, aOffset, aCount); | |
| + } | |
| + return rv; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +AlternateSourceChannel::OnStopRequest(nsIRequest* aRequest, | |
| + nsISupports* aContext, | |
| + nsresult aStatusCode) | |
| +{ | |
| + MOZ_ASSERT(!mForwardToWrapped); | |
| + | |
| + mStatus = aStatusCode; | |
| + if (!mCanceled && NS_FAILED(mStatus)) { | |
| + mCanceled = true; | |
| + } | |
| + mPending = false; | |
| + | |
| + nsresult rv = NS_OK; | |
| + if (mWrappedChannel) { | |
| + nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(mWrappedChannel); | |
| + rv = listener->OnStopRequest(this, mContext, aStatusCode); | |
| + } | |
| + | |
| + mListener = nullptr; | |
| + mContext = nullptr; | |
| + mBody = nullptr; | |
| + mPump = nullptr; | |
| + return rv; | |
| +} | |
| + | |
| +} // namespace net | |
| +} // namespace mozilla | |
| diff --git a/netwerk/base/src/AlternateSourceChannel.h b/netwerk/base/src/AlternateSourceChannel.h | |
| new file mode 100644 | |
| --- /dev/null | |
| +++ b/netwerk/base/src/AlternateSourceChannel.h | |
| @@ -0,0 +1,56 @@ | |
| +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | |
| +/* This Source Code Form is subject to the terms of the Mozilla Public | |
| + * License, v. 2.0. If a copy of the MPL was not distributed with this | |
| + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
| + | |
| +#ifndef mozilla_net_AlternateSourceChannel_h | |
| +#define mozilla_net_AlternateSourceChannel_h | |
| + | |
| +#include "nsAHttpTransaction.h" | |
| +#include "nsIAlternateSourceChannel.h" | |
| +#include "nsIChannel.h" | |
| +#include "nsIStreamListener.h" | |
| +#include "nsCOMPtr.h" | |
| +#include "nsAutoPtr.h" | |
| +#include "nsString.h" | |
| + | |
| +class nsInputStreamPump; | |
| + | |
| +namespace mozilla { | |
| +namespace net { | |
| + | |
| +class AlternateSourceChannel : public nsIChannel | |
| + , public nsIAlternateSourceChannel | |
| + , public nsIStreamListener | |
| + , public nsAHttpSegmentWriter { | |
| +public: | |
| + AlternateSourceChannel(nsIChannel* aWrappedChannel); | |
| + virtual ~AlternateSourceChannel(); | |
| + | |
| + NS_DECL_THREADSAFE_ISUPPORTS | |
| + NS_DECL_NSIALTERNATESOURCECHANNEL | |
| + NS_DECL_NSICHANNEL | |
| + NS_DECL_NSIREQUEST | |
| + NS_DECL_NSISTREAMLISTENER | |
| + NS_DECL_NSIREQUESTOBSERVER | |
| + NS_DECL_NSAHTTPSEGMENTWRITER | |
| + | |
| +private: | |
| + nsCOMPtr<nsIChannel> mWrappedChannel; | |
| + nsCOMPtr<nsIStreamListener> mListener; | |
| + nsCOMPtr<nsISupports> mContext; | |
| + nsCOMPtr<nsIInputStream> mBody; | |
| + nsRefPtr<nsInputStreamPump> mPump; | |
| + nsresult mStatus; | |
| + uint32_t mSuspendCount; | |
| + int64_t mContentLength; | |
| + bool mForwardToWrapped; | |
| + bool mCanceled; | |
| + bool mPending; | |
| + bool mWasOpened; | |
| +}; | |
| + | |
| +} // namespace net | |
| +} // namespace mozilla | |
| + | |
| +#endif // mozilla_net_AlternateSourceChannel_h | |
| diff --git a/netwerk/base/src/moz.build b/netwerk/base/src/moz.build | |
| --- a/netwerk/base/src/moz.build | |
| +++ b/netwerk/base/src/moz.build | |
| @@ -9,21 +9,23 @@ MODULE = 'necko' | |
| EXPORTS += [ | |
| 'nsFileStreams.h', | |
| 'nsMIMEInputStream.h', | |
| 'nsTemporaryFileInputStream.h', | |
| 'nsURLHelper.h', | |
| ] | |
| EXPORTS.mozilla.net += [ | |
| + 'AlternateSourceChannel.h', | |
| 'Dashboard.h', | |
| 'DashboardTypes.h', | |
| ] | |
| CPP_SOURCES += [ | |
| + 'AlternateSourceChannel.cpp', | |
| 'ArrayBufferInputStream.cpp', | |
| 'BackgroundFileSaver.cpp', | |
| 'Dashboard.cpp', | |
| 'EventTokenBucket.cpp', | |
| 'NetworkActivityMonitor.cpp', | |
| 'ProxyAutoConfig.cpp', | |
| 'RedirectChannelRegistrar.cpp', | |
| 'Tickler.cpp', | |
| diff --git a/netwerk/base/src/nsIOService.cpp b/netwerk/base/src/nsIOService.cpp | |
| --- a/netwerk/base/src/nsIOService.cpp | |
| +++ b/netwerk/base/src/nsIOService.cpp | |
| @@ -39,22 +39,24 @@ | |
| #include "nsIPermissionManager.h" | |
| #include "nsTArray.h" | |
| #include "nsIConsoleService.h" | |
| #include "nsIUploadChannel2.h" | |
| #include "nsXULAppAPI.h" | |
| #include "nsIProxiedChannel.h" | |
| #include "nsIProtocolProxyCallback.h" | |
| #include "nsICancelable.h" | |
| +#include "AlternateSourceChannel.h" | |
| #if defined(XP_WIN) | |
| #include "nsNativeConnectionHelper.h" | |
| #endif | |
| using namespace mozilla; | |
| +using mozilla::net::AlternateSourceChannel; | |
| #define PORT_PREF_PREFIX "network.security.ports." | |
| #define PORT_PREF(x) PORT_PREF_PREFIX x | |
| #define AUTODIAL_PREF "network.autodial-helper.enabled" | |
| #define MANAGE_OFFLINE_STATUS_PREF "network.manage-offline-status" | |
| // Nb: these have been misnomers since bug 715770 removed the buffer cache. | |
| // "network.segment.count" and "network.segment.size" would be better names, | |
| @@ -1173,16 +1175,25 @@ nsIOService::ExtractCharsetFromContentTy | |
| net_ParseContentType(aTypeHeader, ignored, aCharset, aHadCharset, | |
| aCharsetStart, aCharsetEnd); | |
| if (*aHadCharset && *aCharsetStart == *aCharsetEnd) { | |
| *aHadCharset = false; | |
| } | |
| return NS_OK; | |
| } | |
| +NS_IMETHODIMP | |
| +nsIOService::CreateAlternateSourceChannel(nsIChannel* aChannel, | |
| + nsIChannel** aWrappedChannel) | |
| +{ | |
| + nsCOMPtr<nsIChannel> wrapper = new AlternateSourceChannel(aChannel); | |
| + wrapper.forget(aWrappedChannel); | |
| + return NS_OK; | |
| +} | |
| + | |
| // nsISpeculativeConnect | |
| class IOServiceProxyCallback MOZ_FINAL : public nsIProtocolProxyCallback | |
| { | |
| public: | |
| NS_DECL_ISUPPORTS | |
| NS_DECL_NSIPROTOCOLPROXYCALLBACK | |
| IOServiceProxyCallback(nsIInterfaceRequestor *aCallbacks, | |
| diff --git a/netwerk/base/src/nsInputStreamPump.h b/netwerk/base/src/nsInputStreamPump.h | |
| --- a/netwerk/base/src/nsInputStreamPump.h | |
| +++ b/netwerk/base/src/nsInputStreamPump.h | |
| @@ -56,17 +56,16 @@ public: | |
| */ | |
| NS_HIDDEN_(nsresult) PeekStream(PeekSegmentFun callback, void *closure); | |
| /** | |
| * Dispatched (to the main thread) by OnStateStop if it's called off main | |
| * thread. Updates mState based on return value of OnStateStop. | |
| */ | |
| nsresult CallOnStateStop(); | |
| - | |
| protected: | |
| enum { | |
| STATE_IDLE, | |
| STATE_START, | |
| STATE_TRANSFER, | |
| STATE_STOP | |
| }; | |
| diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp | |
| --- a/netwerk/protocol/http/HttpChannelChild.cpp | |
| +++ b/netwerk/protocol/http/HttpChannelChild.cpp | |
| @@ -969,16 +969,33 @@ NS_IMETHODIMP | |
| HttpChannelChild::GetSecurityInfo(nsISupports **aSecurityInfo) | |
| { | |
| NS_ENSURE_ARG_POINTER(aSecurityInfo); | |
| NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo); | |
| return NS_OK; | |
| } | |
| NS_IMETHODIMP | |
| +HttpChannelChild::AsyncOpenFinish() | |
| +{ | |
| + return NS_ERROR_FAILURE; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +HttpChannelChild::AsyncOpenNetworkless(nsIStreamListener *listener, nsISupports *aContext) | |
| +{ | |
| + return NS_ERROR_FAILURE; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +HttpChannelChild::GetConnectionlessTransaction(nsHttpTransaction** aTransaction) { | |
| + return NS_ERROR_NOT_AVAILABLE; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) | |
| { | |
| LOG(("HttpChannelChild::AsyncOpen [this=%p uri=%s]\n", this, mSpec.get())); | |
| if (mCanceled) | |
| return mStatus; | |
| NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE); | |
| diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h | |
| --- a/netwerk/protocol/http/HttpChannelChild.h | |
| +++ b/netwerk/protocol/http/HttpChannelChild.h | |
| @@ -72,16 +72,19 @@ public: | |
| bool aMerge); | |
| NS_IMETHOD RedirectTo(nsIURI *newURI); | |
| // nsIHttpChannelInternal | |
| NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey); | |
| NS_IMETHOD GetLocalAddress(nsACString& addr); | |
| NS_IMETHOD GetLocalPort(int32_t* port); | |
| NS_IMETHOD GetRemoteAddress(nsACString& addr); | |
| NS_IMETHOD GetRemotePort(int32_t* port); | |
| + NS_IMETHOD AsyncOpenNetworkless(nsIStreamListener *listener, nsISupports *aContext); | |
| + NS_IMETHOD AsyncOpenFinish(); | |
| + NS_IMETHOD GetConnectionlessTransaction(nsHttpTransaction** aTransaction); | |
| // nsISupportsPriority | |
| NS_IMETHOD SetPriority(int32_t value); | |
| // nsIResumableChannel | |
| NS_IMETHOD ResumeAt(uint64_t startPos, const nsACString& entityID); | |
| // IPDL holds a reference while the PHttpChannel protocol is live (starting at | |
| // AsyncOpen, and ending at either OnStopRequest or any IPDL error, either of | |
| // which call NeckoChild::DeallocPHttpChannelChild()). | |
| diff --git a/netwerk/protocol/http/moz.build b/netwerk/protocol/http/moz.build | |
| --- a/netwerk/protocol/http/moz.build | |
| +++ b/netwerk/protocol/http/moz.build | |
| @@ -18,20 +18,24 @@ XPIDL_SOURCES += [ | |
| 'nsIHttpProtocolHandler.idl', | |
| ] | |
| XPIDL_MODULE = 'necko_http' | |
| MODULE = 'necko' | |
| EXPORTS += [ | |
| + 'TimingStruct.h', | |
| + 'nsAHttpConnection.h', | |
| + 'nsAHttpTransaction.h', | |
| 'nsHttp.h', | |
| 'nsHttpAtomList.h', | |
| 'nsHttpHeaderArray.h', | |
| 'nsHttpResponseHead.h', | |
| + 'nsHttpTransaction.h', | |
| ] | |
| EXPORTS.mozilla.net += [ | |
| 'HttpBaseChannel.h', | |
| 'HttpChannelChild.h', | |
| 'HttpChannelParent.h', | |
| 'HttpInfo.h', | |
| 'PHttpChannelParams.h', | |
| diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp | |
| --- a/netwerk/protocol/http/nsHttpChannel.cpp | |
| +++ b/netwerk/protocol/http/nsHttpChannel.cpp | |
| @@ -315,16 +315,17 @@ nsHttpChannel::nsHttpChannel() | |
| , mProxyAuthPending(false) | |
| , mResuming(false) | |
| , mInitedCacheEntry(false) | |
| , mFallbackChannel(false) | |
| , mCustomConditionalRequest(false) | |
| , mFallingBack(false) | |
| , mWaitingForRedirectCallback(false) | |
| , mRequestTimeInitialized(false) | |
| + , mDelayTransactionIndefinitely(false) | |
| , mDidReval(false) | |
| { | |
| LOG(("Creating nsHttpChannel [this=%p]\n", this)); | |
| mChannelCreationTime = PR_Now(); | |
| mChannelCreationTimestamp = TimeStamp::Now(); | |
| } | |
| nsHttpChannel::~nsHttpChannel() | |
| @@ -402,29 +403,33 @@ nsHttpChannel::Connect() | |
| return NS_ERROR_UNKNOWN_HOST; | |
| // Finalize ConnectionInfo flags before SpeculativeConnect | |
| mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0); | |
| mConnectionInfo->SetPrivate(mPrivateBrowsing); | |
| // Consider opening a TCP connection right away | |
| RetrieveSSLOptions(); | |
| - SpeculativeConnect(); | |
| + if (!mDelayTransactionIndefinitely) { | |
| + SpeculativeConnect(); | |
| + } | |
| // Don't allow resuming when cache must be used | |
| if (mResuming && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) { | |
| LOG(("Resuming from cache is not supported yet")); | |
| return NS_ERROR_DOCUMENT_NOT_CACHED; | |
| } | |
| if (ShouldSkipCache()) | |
| return ContinueConnect(); | |
| // open a cache entry for this channel... | |
| - rv = OpenCacheEntry(usingSSL); | |
| + if (!mDelayTransactionIndefinitely) { | |
| + rv = OpenCacheEntry(usingSSL); | |
| + } | |
| // do not continue if asyncOpenCacheEntry is in progress | |
| if (mOnCacheEntryAvailableCallback) { | |
| MOZ_ASSERT(NS_SUCCEEDED(rv), "Unexpected state"); | |
| return NS_OK; | |
| } | |
| if (NS_FAILED(rv)) { | |
| @@ -496,18 +501,20 @@ nsHttpChannel::ContinueConnect() | |
| if (mLoadFlags & LOAD_NO_NETWORK_IO) { | |
| return NS_ERROR_DOCUMENT_NOT_CACHED; | |
| } | |
| // hit the net... | |
| nsresult rv = SetupTransaction(); | |
| if (NS_FAILED(rv)) return rv; | |
| - rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority); | |
| - if (NS_FAILED(rv)) return rv; | |
| + if (!mDelayTransactionIndefinitely) { | |
| + rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority); | |
| + if (NS_FAILED(rv)) return rv; | |
| + } | |
| rv = mTransactionPump->AsyncRead(this, nullptr); | |
| if (NS_FAILED(rv)) return rv; | |
| uint32_t suspendCount = mSuspendCount; | |
| while (suspendCount--) | |
| mTransactionPump->Suspend(); | |
| @@ -4552,20 +4559,46 @@ nsHttpChannel::AsyncOpen(nsIStreamListen | |
| // the only time we would already know the proxy information at this | |
| // point would be if we were proxying a non-http protocol like ftp | |
| if (!mProxyInfo && NS_SUCCEEDED(ResolveProxy())) | |
| return NS_OK; | |
| rv = BeginConnect(); | |
| if (NS_FAILED(rv)) | |
| ReleaseListeners(); | |
| - | |
| return rv; | |
| } | |
| +NS_IMETHODIMP | |
| +nsHttpChannel::GetConnectionlessTransaction(nsHttpTransaction** aTransaction) { | |
| + mTransaction->MarkConnectionless(); | |
| + NS_ADDREF(*aTransaction = mTransaction); | |
| + return NS_OK; | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +nsHttpChannel::AsyncOpenNetworkless(nsIStreamListener *listener, nsISupports *context) | |
| +{ | |
| + mDelayTransactionIndefinitely = true; | |
| + return AsyncOpen(listener, context); | |
| +} | |
| + | |
| +NS_IMETHODIMP | |
| +nsHttpChannel::AsyncOpenFinish() | |
| +{ | |
| + mDelayTransactionIndefinitely = false; | |
| + //TODO: open cache entry? | |
| + //TODO: speculatively connect? | |
| + | |
| + nsresult rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority); | |
| + NS_ENSURE_SUCCESS(rv, rv); | |
| + | |
| + return NS_OK; | |
| +} | |
| + | |
| nsresult | |
| nsHttpChannel::BeginConnect() | |
| { | |
| LOG(("nsHttpChannel::BeginConnect [this=%p]\n", this)); | |
| nsresult rv; | |
| // Construct connection info object | |
| nsAutoCString host; | |
| @@ -5857,18 +5890,20 @@ nsHttpChannel::DoAuthRetry(nsAHttpConnec | |
| // rewind the upload stream | |
| if (mUploadStream) { | |
| nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream); | |
| if (seekable) | |
| seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); | |
| } | |
| - rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority); | |
| - if (NS_FAILED(rv)) return rv; | |
| + if (!mDelayTransactionIndefinitely) { | |
| + rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority); | |
| + if (NS_FAILED(rv)) return rv; | |
| + } | |
| rv = mTransactionPump->AsyncRead(this, nullptr); | |
| if (NS_FAILED(rv)) return rv; | |
| uint32_t suspendCount = mSuspendCount; | |
| while (suspendCount--) | |
| mTransactionPump->Suspend(); | |
| diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h | |
| --- a/netwerk/protocol/http/nsHttpChannel.h | |
| +++ b/netwerk/protocol/http/nsHttpChannel.h | |
| @@ -111,16 +111,19 @@ public: | |
| NS_IMETHOD Cancel(nsresult status); | |
| NS_IMETHOD Suspend(); | |
| NS_IMETHOD Resume(); | |
| // nsIChannel | |
| NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo); | |
| NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext); | |
| // nsIHttpChannelInternal | |
| NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey); | |
| + NS_IMETHOD AsyncOpenNetworkless(nsIStreamListener *listener, nsISupports *aContext); | |
| + NS_IMETHOD AsyncOpenFinish(); | |
| + NS_IMETHOD GetConnectionlessTransaction(nsHttpTransaction** aTransaction); | |
| // nsISupportsPriority | |
| NS_IMETHOD SetPriority(int32_t value); | |
| // nsIResumableChannel | |
| NS_IMETHOD ResumeAt(uint64_t startPos, const nsACString& entityID); | |
| NS_IMETHOD SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks); | |
| NS_IMETHOD SetLoadGroup(nsILoadGroup *aLoadGroup); | |
| @@ -355,16 +358,17 @@ private: | |
| // 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. | |
| uint32_t mCustomConditionalRequest : 1; | |
| uint32_t mFallingBack : 1; | |
| uint32_t mWaitingForRedirectCallback : 1; | |
| // True if mRequestTime has been set. In such a case it is safe to update | |
| // the cache entry's expiration time. Otherwise, it is not(see bug 567360). | |
| uint32_t mRequestTimeInitialized : 1; | |
| + uint32_t mDelayTransactionIndefinitely : 1; | |
| nsTArray<nsContinueRedirectionFunc> mRedirectFuncStack; | |
| PRTime mChannelCreationTime; | |
| mozilla::TimeStamp mChannelCreationTimestamp; | |
| mozilla::TimeStamp mAsyncOpenTime; | |
| mozilla::TimeStamp mCacheReadStart; | |
| mozilla::TimeStamp mCacheReadEnd; | |
| diff --git a/netwerk/protocol/http/nsHttpHeaderArray.h b/netwerk/protocol/http/nsHttpHeaderArray.h | |
| --- a/netwerk/protocol/http/nsHttpHeaderArray.h | |
| +++ b/netwerk/protocol/http/nsHttpHeaderArray.h | |
| @@ -9,16 +9,20 @@ | |
| #include "nsHttp.h" | |
| #include "nsTArray.h" | |
| #include "nsIHttpChannel.h" | |
| #include "nsIHttpHeaderVisitor.h" | |
| #include "nsCOMPtr.h" | |
| #include "nsString.h" | |
| +namespace IPC { | |
| +template<typename T> struct ParamTraits; | |
| +} | |
| + | |
| class nsHttpHeaderArray | |
| { | |
| public: | |
| const char *PeekHeader(nsHttpAtom header) const; | |
| // Used by internal setters: to set header from network use SetHeaderFromNet | |
| nsresult SetHeader(nsHttpAtom header, const nsACString &value, | |
| bool merge = false); | |
| diff --git a/netwerk/protocol/http/nsHttpTransaction.cpp b/netwerk/protocol/http/nsHttpTransaction.cpp | |
| --- a/netwerk/protocol/http/nsHttpTransaction.cpp | |
| +++ b/netwerk/protocol/http/nsHttpTransaction.cpp | |
| @@ -108,16 +108,17 @@ nsHttpTransaction::nsHttpTransaction() | |
| , mSentData(false) | |
| , mReceivedData(false) | |
| , mStatusEventPending(false) | |
| , mHasRequestBody(false) | |
| , mProxyConnectFailed(false) | |
| , mHttpResponseMatched(false) | |
| , mPreserveStream(false) | |
| , mDispatchedAsBlocking(false) | |
| + , mConnectionless(false) | |
| , mReportedStart(false) | |
| , mReportedResponseHeader(false) | |
| , mForTakeResponseHead(nullptr) | |
| , mResponseHeadTaken(false) | |
| , mSubmittedRatePacing(false) | |
| , mPassedRatePacing(false) | |
| , mSynchronousRatePaceRequest(false) | |
| { | |
| @@ -403,16 +404,17 @@ nsHttpTransaction::TakeSubTransactions( | |
| //---------------------------------------------------------------------------- | |
| // nsHttpTransaction::nsAHttpTransaction | |
| //---------------------------------------------------------------------------- | |
| void | |
| nsHttpTransaction::SetConnection(nsAHttpConnection *conn) | |
| { | |
| + MOZ_ASSERT(!mConnectionless); | |
| NS_IF_RELEASE(mConnection); | |
| NS_IF_ADDREF(mConnection = conn); | |
| if (conn) { | |
| MOZ_EVENT_TRACER_EXEC(static_cast<nsAHttpTransaction*>(this), | |
| "net::http::transaction"); | |
| } | |
| } | |
| @@ -1254,17 +1256,17 @@ nsHttpTransaction::HandleContentStart() | |
| } | |
| #endif | |
| // Save http version, mResponseHead isn't available anymore after | |
| // TakeResponseHead() is called | |
| mHttpVersion = mResponseHead->Version(); | |
| // notify the connection, give it a chance to cause a reset. | |
| bool reset = false; | |
| - if (!mRestartInProgressVerifier.IsSetup()) | |
| + if (!mRestartInProgressVerifier.IsSetup() && mConnection) | |
| mConnection->OnHeadersAvailable(this, mRequestHead, mResponseHead, &reset); | |
| // looks like we should ignore this response, resetting... | |
| if (reset) { | |
| LOG(("resetting transaction's response head\n")); | |
| mHaveAllHeaders = false; | |
| mHaveStatusLine = false; | |
| mReceivedData = false; | |
| @@ -1283,25 +1285,29 @@ nsHttpTransaction::HandleContentStart() | |
| case 205: | |
| case 304: | |
| mNoContent = true; | |
| LOG(("this response should not contain a body.\n")); | |
| break; | |
| } | |
| if (mResponseHead->Status() == 200 && | |
| - mConnection->IsProxyConnectInProgress()) { | |
| + mConnection && mConnection->IsProxyConnectInProgress()) { | |
| // successful CONNECTs do not have response bodies | |
| mNoContent = true; | |
| } | |
| - mConnection->SetLastTransactionExpectedNoContent(mNoContent); | |
| - if (mInvalidResponseBytesRead) | |
| + if (mConnection) { | |
| + mConnection->SetLastTransactionExpectedNoContent(mNoContent); | |
| + } | |
| + if (mInvalidResponseBytesRead) { | |
| + MOZ_ASSERT(mConnection); | |
| gHttpHandler->ConnMgr()->PipelineFeedbackInfo( | |
| mConnInfo, nsHttpConnectionMgr::BadInsufficientFraming, | |
| nullptr, mClassification); | |
| + } | |
| if (mNoContent) | |
| mContentLength = 0; | |
| else { | |
| // grab the content-length from the response headers | |
| mContentLength = mResponseHead->ContentLength(); | |
| if ((mClassification != CLASS_SOLO) && | |
| @@ -1354,17 +1360,17 @@ nsHttpTransaction::HandleContent(char *b | |
| { | |
| nsresult rv; | |
| LOG(("nsHttpTransaction::HandleContent [this=%p count=%u]\n", this, count)); | |
| *contentRead = 0; | |
| *contentRemaining = 0; | |
| - MOZ_ASSERT(mConnection); | |
| + MOZ_ASSERT(mConnection || mConnectionless); | |
| if (!mDidContentStart) { | |
| rv = HandleContentStart(); | |
| if (NS_FAILED(rv)) return rv; | |
| // Do not write content to the pipe if we haven't started streaming yet | |
| if (!mDidContentStart) | |
| return NS_OK; | |
| } | |
| @@ -1375,17 +1381,17 @@ nsHttpTransaction::HandleContent(char *b | |
| rv = mChunkedDecoder->HandleChunkedContent(buf, count, contentRead, contentRemaining); | |
| if (NS_FAILED(rv)) return rv; | |
| } | |
| else if (mContentLength >= int64_t(0)) { | |
| // HTTP/1.0 servers have been known to send erroneous Content-Length | |
| // headers. So, unless the connection is persistent, we must make | |
| // allowances for a possibly invalid Content-Length header. Thus, if | |
| // NOT persistent, we simply accept everything in |buf|. | |
| - if (mConnection->IsPersistent() || mPreserveStream || | |
| + if ((mConnection && mConnection->IsPersistent()) || mPreserveStream || | |
| mHttpVersion >= NS_HTTP_VERSION_1_1) { | |
| int64_t remaining = mContentLength - mContentRead; | |
| *contentRead = uint32_t(std::min<int64_t>(count, remaining)); | |
| *contentRemaining = count - *contentRead; | |
| } | |
| else { | |
| *contentRead = count; | |
| // mContentLength might need to be increased... | |
| diff --git a/netwerk/protocol/http/nsHttpTransaction.h b/netwerk/protocol/http/nsHttpTransaction.h | |
| --- a/netwerk/protocol/http/nsHttpTransaction.h | |
| +++ b/netwerk/protocol/http/nsHttpTransaction.h | |
| @@ -115,16 +115,18 @@ public: | |
| bool UsesPipelining() const { return mCaps & NS_HTTP_ALLOW_PIPELINING; } | |
| // overload of nsAHttpTransaction::LoadGroupConnectionInfo() | |
| nsILoadGroupConnectionInfo *LoadGroupConnectionInfo() { return mLoadGroupCI.get(); } | |
| void SetLoadGroupConnectionInfo(nsILoadGroupConnectionInfo *aLoadGroupCI) { mLoadGroupCI = aLoadGroupCI; } | |
| void DispatchedAsBlocking(); | |
| void RemoveDispatchedAsBlocking(); | |
| + void MarkConnectionless() { MOZ_ASSERT(!mConnection); mConnectionless = true; } | |
| + | |
| private: | |
| nsresult Restart(); | |
| nsresult RestartInProgress(); | |
| char *LocateHttpStart(char *buf, uint32_t len, | |
| bool aAllowPartialMatch); | |
| nsresult ParseLine(char *line); | |
| nsresult ParseLineSegment(char *seg, uint32_t len); | |
| nsresult ParseHead(char *, uint32_t count, uint32_t *countRead); | |
| @@ -229,16 +231,17 @@ private: | |
| bool mSentData; | |
| bool mReceivedData; | |
| bool mStatusEventPending; | |
| bool mHasRequestBody; | |
| bool mProxyConnectFailed; | |
| bool mHttpResponseMatched; | |
| bool mPreserveStream; | |
| bool mDispatchedAsBlocking; | |
| + bool mConnectionless; | |
| // mClosed := transaction has been explicitly closed | |
| // mTransactionDone := transaction ran to completion or was interrupted | |
| // mResponseComplete := transaction ran to completion | |
| // For Restart-In-Progress Functionality | |
| bool mReportedStart; | |
| bool mReportedResponseHeader; | |
| diff --git a/netwerk/protocol/http/nsIHttpChannelInternal.idl b/netwerk/protocol/http/nsIHttpChannelInternal.idl | |
| --- a/netwerk/protocol/http/nsIHttpChannelInternal.idl | |
| +++ b/netwerk/protocol/http/nsIHttpChannelInternal.idl | |
| @@ -5,26 +5,29 @@ | |
| #include "nsISupports.idl" | |
| %{C++ | |
| #include "nsTArray.h" | |
| #include "nsCOMArray.h" | |
| class nsCString; | |
| +class nsHttpTransaction; | |
| %} | |
| [ptr] native StringArray(nsTArray<nsCString>); | |
| [ref] native securityMessagesArray(nsCOMArray<nsISecurityConsoleMessage>); | |
| +[ptr] native nsHttpTransactionPtr(nsHttpTransaction); | |
| interface nsISocketTransport; | |
| interface nsIAsyncInputStream; | |
| interface nsIAsyncOutputStream; | |
| interface nsIURI; | |
| interface nsIProxyInfo; | |
| interface nsISecurityConsoleMessage; | |
| +interface nsIStreamListener; | |
| /** | |
| * The callback interface for nsIHttpChannelInternal::HTTPUpgrade() | |
| */ | |
| [scriptable, uuid(4b967b6d-cd1c-49ae-a457-23ff76f5a2e8)] | |
| interface nsIHttpUpgradeListener : nsISupports | |
| { | |
| @@ -33,17 +36,17 @@ interface nsIHttpUpgradeListener : nsISu | |
| in nsIAsyncOutputStream aSocketOut); | |
| }; | |
| /** | |
| * Dumping ground for http. This interface will never be frozen. If you are | |
| * using any feature exposed by this interface, be aware that this interface | |
| * will change and you will be broken. You have been warned. | |
| */ | |
| -[scriptable, uuid(5b4b2632-cee4-11e2-8e84-c7506188709b)] | |
| +[scriptable, uuid(ee847842-bea9-4df4-817e-e5449085223a)] | |
| interface nsIHttpChannelInternal : nsISupports | |
| { | |
| /** | |
| * An http channel can own a reference to the document URI | |
| */ | |
| attribute nsIURI documentURI; | |
| /** | |
| @@ -177,9 +180,12 @@ interface nsIHttpChannelInternal : nsISu | |
| /** | |
| * If set, this channel will load in parallel with the rest of the load | |
| * group even if a blocking subset of the group would normally be given | |
| * exclusivity. Default false. | |
| */ | |
| attribute boolean loadUnblocked; | |
| + readonly attribute nsHttpTransactionPtr connectionlessTransaction; | |
| + void asyncOpenNetworkless(in nsIStreamListener listener, in nsISupports context); | |
| + void asyncOpenFinish(); | |
| }; | |
| diff --git a/netwerk/test/unit/test_alternate_source_channel.js b/netwerk/test/unit/test_alternate_source_channel.js | |
| new file mode 100644 | |
| --- /dev/null | |
| +++ b/netwerk/test/unit/test_alternate_source_channel.js | |
| @@ -0,0 +1,75 @@ | |
| +"use strict"; | |
| +// https://bugzilla.mozilla.org/show_bug.cgi?id=761228 | |
| + | |
| +const Cc = Components.classes; | |
| +const Ci = Components.interfaces; | |
| +const Cu = Components.utils; | |
| +const Cr = Components.results; | |
| + | |
| +Cu.import("resource://testing-common/httpd.js"); | |
| + | |
| +XPCOMUtils.defineLazyGetter(this, "URL", function() { | |
| + return "http://localhost:" + httpServer.identity.primaryPort; | |
| +}); | |
| + | |
| +var httpServer = null; | |
| + | |
| +function make_uri(url) { | |
| + var ios = Cc["@mozilla.org/network/io-service;1"]. | |
| + getService(Ci.nsIIOService); | |
| + return ios.newURI(url, null, null); | |
| +} | |
| + | |
| +function make_channel(url) { | |
| + var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); | |
| + var chan = ios.newChannel(url, null, null).QueryInterface(Ci.nsIHttpChannel); | |
| + return chan; | |
| +} | |
| + | |
| +const REMOTE_BODY = "http handler body"; | |
| +const NON_REMOTE_BODY = "synthesized body"; | |
| +const NON_REMOTE_RESPONSE = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: " + NON_REMOTE_BODY.length + "\r\n\r\n" + NON_REMOTE_BODY; | |
| + | |
| +function bodyHandler(metadata, response) { | |
| + response.setHeader('Content-Type', 'text/plain'); | |
| + response.write(REMOTE_BODY); | |
| +} | |
| + | |
| +function run_test() { | |
| + httpServer = new HttpServer(); | |
| + httpServer.registerPathHandler('/body', bodyHandler); | |
| + httpServer.start(-1); | |
| + | |
| + run_next_test(); | |
| +} | |
| + | |
| +add_test(function() { | |
| + var chan = make_channel(URL + '/body'); | |
| + var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); | |
| + chan = ios.QueryInterface(Ci.nsINetUtil).createAlternateSourceChannel(chan); | |
| + chan.QueryInterface(Ci.nsIAlternateSourceChannel); | |
| + chan.asyncOpen(new ChannelListener(handle_synthesized_response, null), null); | |
| + var synthesized = Cc["@mozilla.org/io/string-input-stream;1"] | |
| + .createInstance(Ci.nsIStringInputStream); | |
| + synthesized.data = NON_REMOTE_RESPONSE; | |
| + chan.initiateAlternateResponse(synthesized); | |
| +}); | |
| + | |
| +function handle_synthesized_response(request, buffer) { | |
| + do_check_eq(buffer, NON_REMOTE_BODY); | |
| + run_next_test(); | |
| +} | |
| + | |
| +add_test(function() { | |
| + var chan = make_channel(URL + '/body'); | |
| + var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); | |
| + chan = ios.QueryInterface(Ci.nsINetUtil).createAlternateSourceChannel(chan); | |
| + chan.QueryInterface(Ci.nsIAlternateSourceChannel); | |
| + chan.asyncOpen(new ChannelListener(handle_remote_response, null), null); | |
| + do_execute_soon(function() { chan.forwardToOriginalChannel(); }); | |
| +}); | |
| + | |
| +function handle_remote_response(request, buffer) { | |
| + do_check_eq(buffer, REMOTE_BODY); | |
| + httpServer.stop(run_next_test); | |
| +} | |
| diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini | |
| --- a/netwerk/test/unit/xpcshell.ini | |
| +++ b/netwerk/test/unit/xpcshell.ini | |
| @@ -11,16 +11,17 @@ skip-if = os == "android" | |
| [test_304_responses.js] | |
| # Bug 675039: test hangs on Android-armv6 | |
| skip-if = os == "android" | |
| [test_cacheForOfflineUse_no-store.js] | |
| [test_307_redirect.js] | |
| [test_NetUtil.js] | |
| [test_URIs.js] | |
| [test_aboutblank.js] | |
| +[test_alternate_source_channel.js] | |
| [test_assoc.js] | |
| [test_auth_jar.js] | |
| [test_auth_proxy.js] | |
| [test_authentication.js] | |
| [test_authpromptwrapper.js] | |
| [test_backgroundfilesaver.js] | |
| [test_bug203271.js] | |
| # Bug 675039: intermittent fail on Android-armv6 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment