Skip to content

Instantly share code, notes, and snippets.

@jdm
Created September 25, 2013 22:46
Show Gist options
  • Select an option

  • Save jdm/6707215 to your computer and use it in GitHub Desktop.

Select an option

Save jdm/6707215 to your computer and use it in GitHub Desktop.
# 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