--- a/ipc/glue/InputStreamParams.ipdlh
+++ b/ipc/glue/InputStreamParams.ipdlh
@@ -6,16 +6,22 @@ include protocol PBlob;
include ProtocolTypes;
using struct mozilla::void_t
from "ipc/IPCMessageUtils.h";
namespace mozilla {
namespace ipc {
+struct HeaderEntry
+{
+ nsCString name;
+ nsCString value;
+};
+
struct StringInputStreamParams
{
nsCString data;
};
struct FileInputStreamParams
{
uint32_t fileDescriptorIndex;
@@ -81,16 +87,14 @@ struct BufferedInputStreamParams
{
OptionalInputStreamParams optionalStream;
uint32_t bufferSize;
};
struct MIMEInputStreamParams
{
OptionalInputStreamParams optionalStream;
- nsCString headers;
- nsCString contentLength;
+ HeaderEntry[] headers;
bool startedReading;
- bool addContentLength;
};
} // namespace ipc
} // namespace mozilla
--- a/netwerk/base/nsIMIMEInputStream.idl
+++ b/netwerk/base/nsIMIMEInputStream.idl
@@ -1,41 +1,55 @@
/* -*- Mode: C++; tab-width: 4; 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 "nsIHttpHeaderVisitor.idl"
#include "nsIInputStream.idl"
/**
* The MIME stream separates headers and a datastream. It also allows
* automatic creation of the content-length header.
*/
[scriptable, uuid(dcbce63c-1dd1-11b2-b94d-91f6d49a3161)]
interface nsIMIMEInputStream : nsIInputStream
{
/**
* When true a "Content-Length" header is automatically added to the
* stream. The value of the content-length is automatically calculated
* using the available() method on the data stream. The value is
* recalculated every time the stream is rewinded to the start.
* Not allowed to be changed once the stream has been started to be read.
+ *
+ * @deprecated A Content-Length header is automatically added when
+ * attaching the stream to a channel, so this setting no longer has any
+ * effect, and may not be set to false.
*/
attribute boolean addContentLength;
/**
* Adds an additional header to the stream on the form "name: value". May
* not be called once the stream has been started to be read.
* @param name name of the header
* @param value value of the header
*/
void addHeader(in string name, in string value);
/**
+ * Visits all headers which have been added via addHeader. Calling
+ * addHeader while visiting request headers has undefined behavior.
+ *
+ * @param aVisitor
+ * The header visitor instance.
+ */
+ void visitHeaders(in nsIHttpHeaderVisitor visitor);
+
+ /**
* Sets data-stream. May not be called once the stream has been started
* to be read.
* The cursor of the new stream should be located at the beginning of the
* stream if the implementation of the nsIMIMEInputStream also is used as
* an nsISeekableStream.
* @param stream stream containing the data for the stream
*/
void setData(in nsIInputStream stream);
--- a/netwerk/base/nsMIMEInputStream.cpp
+++ b/netwerk/base/nsMIMEInputStream.cpp
@@ -7,68 +7,61 @@
* The MIME stream separates headers and a datastream. It also allows
* automatic creation of the content-length header.
*/
#include "ipc/IPCMessageUtils.h"
#include "nsCOMPtr.h"
#include "nsComponentManagerUtils.h"
-#include "nsIMultiplexInputStream.h"
+#include "nsIHttpHeaderVisitor.h"
#include "nsIMIMEInputStream.h"
#include "nsISeekableStream.h"
-#include "nsIStringStream.h"
#include "nsString.h"
#include "nsMIMEInputStream.h"
#include "nsIClassInfoImpl.h"
#include "nsIIPCSerializableInputStream.h"
+#include "mozilla/Move.h"
#include "mozilla/ipc/InputStreamUtils.h"
using namespace mozilla::ipc;
using mozilla::Maybe;
+using mozilla::Move;
class nsMIMEInputStream : public nsIMIMEInputStream,
public nsISeekableStream,
public nsIIPCSerializableInputStream
{
virtual ~nsMIMEInputStream();
public:
nsMIMEInputStream();
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTSTREAM
NS_DECL_NSIMIMEINPUTSTREAM
NS_DECL_NSISEEKABLESTREAM
NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
- nsresult Init();
-
private:
void InitStreams();
struct MOZ_STACK_CLASS ReadSegmentsState {
nsCOMPtr<nsIInputStream> mThisStream;
nsWriteSegmentFun mWriter;
void* mClosure;
};
static nsresult ReadSegCb(nsIInputStream* aIn, void* aClosure,
const char* aFromRawSegment, uint32_t aToOffset,
uint32_t aCount, uint32_t *aWriteCount);
- nsCString mHeaders;
- nsCOMPtr<nsIStringInputStream> mHeaderStream;
-
- nsCString mContentLength;
- nsCOMPtr<nsIStringInputStream> mCLStream;
-
- nsCOMPtr<nsIInputStream> mData;
- nsCOMPtr<nsIMultiplexInputStream> mStream;
- bool mAddContentLength;
+ nsTArray<HeaderEntry> mHeaders;
+
+ nsCOMPtr<nsIInputStream> mStream;
bool mStartedReading;
};
NS_IMPL_ADDREF(nsMIMEInputStream)
NS_IMPL_RELEASE(nsMIMEInputStream)
NS_IMPL_CLASSINFO(nsMIMEInputStream, nullptr, nsIClassInfo::THREADSAFE,
NS_MIMEINPUTSTREAM_CID)
@@ -78,140 +71,117 @@ NS_IMPL_QUERY_INTERFACE_CI(nsMIMEInputSt
nsIInputStream,
nsISeekableStream,
nsIIPCSerializableInputStream)
NS_IMPL_CI_INTERFACE_GETTER(nsMIMEInputStream,
nsIMIMEInputStream,
nsIInputStream,
nsISeekableStream)
-nsMIMEInputStream::nsMIMEInputStream() : mAddContentLength(false),
- mStartedReading(false)
+nsMIMEInputStream::nsMIMEInputStream() : mStartedReading(false)
{
}
nsMIMEInputStream::~nsMIMEInputStream()
{
}
-nsresult nsMIMEInputStream::Init()
-{
- nsresult rv = NS_OK;
- mStream = do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1",
- &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-
- mHeaderStream = do_CreateInstance("@mozilla.org/io/string-input-stream;1",
- &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- mCLStream = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = mStream->AppendStream(mHeaderStream);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = mStream->AppendStream(mCLStream);
- NS_ENSURE_SUCCESS(rv, rv);
-
- return NS_OK;
-}
-
-
NS_IMETHODIMP
nsMIMEInputStream::GetAddContentLength(bool *aAddContentLength)
{
- *aAddContentLength = mAddContentLength;
+ *aAddContentLength = true;
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInputStream::SetAddContentLength(bool aAddContentLength)
{
NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
- mAddContentLength = aAddContentLength;
+ if (!aAddContentLength) {
+ // Content-Length is automatically added by the channel when setting the
+ // upload stream, so setting this to false has no practical effect.
+ return NS_ERROR_FAILURE;
+ }
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInputStream::AddHeader(const char *aName, const char *aValue)
{
NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
- mHeaders.Append(aName);
- mHeaders.AppendLiteral(": ");
- mHeaders.Append(aValue);
- mHeaders.AppendLiteral("\r\n");
- // Just in case someone somehow uses our stream, lets at least
- // let the stream have a valid pointer. The stream will be properly
- // initialized in nsMIMEInputStream::InitStreams
- mHeaderStream->ShareData(mHeaders.get(), 0);
+ HeaderEntry* entry = mHeaders.AppendElement();
+ entry->name().Append(aName);
+ entry->value().Append(aValue);
return NS_OK;
}
NS_IMETHODIMP
+nsMIMEInputStream::VisitHeaders(nsIHttpHeaderVisitor *visitor)
+{
+ nsresult rv;
+
+ for (auto& header : mHeaders) {
+ rv = visitor->VisitHeader(header.name(), header.value());
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
nsMIMEInputStream::SetData(nsIInputStream *aStream)
{
NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
- // Remove the old stream if there is one
- if (mData)
- mStream->RemoveStream(2);
- mData = aStream;
- if (aStream)
- mStream->AppendStream(mData);
+ nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aStream);
+ if (!seekable) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ mStream = aStream;
return NS_OK;
}
NS_IMETHODIMP
nsMIMEInputStream::GetData(nsIInputStream **aStream)
{
NS_ENSURE_ARG_POINTER(aStream);
- *aStream = mData;
+ *aStream = mStream;
NS_IF_ADDREF(*aStream);
return NS_OK;
}
// set up the internal streams
void nsMIMEInputStream::InitStreams()
{
NS_ASSERTION(!mStartedReading,
"Don't call initStreams twice without rewinding");
mStartedReading = true;
-
- // We'll use the content-length stream to add the final \r\n
- if (mAddContentLength) {
- uint64_t cl = 0;
- if (mData) {
- mData->Available(&cl);
- }
- mContentLength.AssignLiteral("Content-Length: ");
- mContentLength.AppendInt(cl);
- mContentLength.AppendLiteral("\r\n\r\n");
- }
- else {
- mContentLength.AssignLiteral("\r\n");
- }
- mCLStream->ShareData(mContentLength.get(), -1);
- mHeaderStream->ShareData(mHeaders.get(), -1);
}
#define INITSTREAMS \
if (!mStartedReading) { \
+ NS_ENSURE_TRUE(mStream, NS_ERROR_UNEXPECTED); \
InitStreams(); \
}
// Reset mStartedReading when Seek-ing to start
NS_IMETHODIMP
nsMIMEInputStream::Seek(int32_t whence, int64_t offset)
{
+ NS_ENSURE_TRUE(mStream, NS_ERROR_UNEXPECTED);
+
nsresult rv;
nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStream);
+
if (whence == NS_SEEK_SET && offset == 0) {
rv = stream->Seek(whence, offset);
if (NS_SUCCEEDED(rv))
mStartedReading = false;
}
else {
INITSTREAMS;
rv = stream->Seek(whence, offset);
@@ -279,60 +249,44 @@ NS_IMETHODIMP nsMIMEInputStream::SetEOF(
nsresult
nsMIMEInputStreamConstructor(nsISupports *outer, REFNSIID iid, void **result)
{
*result = nullptr;
if (outer)
return NS_ERROR_NO_AGGREGATION;
- nsMIMEInputStream *inst = new nsMIMEInputStream();
+ RefPtr<nsMIMEInputStream> inst = new nsMIMEInputStream();
if (!inst)
return NS_ERROR_OUT_OF_MEMORY;
- NS_ADDREF(inst);
-
- nsresult rv = inst->Init();
- if (NS_FAILED(rv)) {
- NS_RELEASE(inst);
- return rv;
- }
-
- rv = inst->QueryInterface(iid, result);
- NS_RELEASE(inst);
-
- return rv;
+ return inst->QueryInterface(iid, result);
}
void
nsMIMEInputStream::Serialize(InputStreamParams& aParams,
FileDescriptorArray& aFileDescriptors)
{
MIMEInputStreamParams params;
- if (mData) {
- nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mData);
- MOZ_ASSERT(stream);
-
+ if (mStream) {
InputStreamParams wrappedParams;
- SerializeInputStream(stream, wrappedParams, aFileDescriptors);
+ SerializeInputStream(mStream, wrappedParams, aFileDescriptors);
NS_ASSERTION(wrappedParams.type() != InputStreamParams::T__None,
"Wrapped stream failed to serialize!");
params.optionalStream() = wrappedParams;
}
else {
params.optionalStream() = mozilla::void_t();
}
params.headers() = mHeaders;
- params.contentLength() = mContentLength;
params.startedReading() = mStartedReading;
- params.addContentLength() = mAddContentLength;
aParams = params;
}
bool
nsMIMEInputStream::Deserialize(const InputStreamParams& aParams,
const FileDescriptorArray& aFileDescriptors)
{
@@ -341,48 +295,34 @@ nsMIMEInputStream::Deserialize(const Inp
return false;
}
const MIMEInputStreamParams& params =
aParams.get_MIMEInputStreamParams();
const OptionalInputStreamParams& wrappedParams = params.optionalStream();
mHeaders = params.headers();
- mContentLength = params.contentLength();
mStartedReading = params.startedReading();
- // nsMIMEInputStream::Init() already appended mHeaderStream & mCLStream
- mHeaderStream->ShareData(mHeaders.get(),
- mStartedReading ? mHeaders.Length() : 0);
- mCLStream->ShareData(mContentLength.get(),
- mStartedReading ? mContentLength.Length() : 0);
-
- nsCOMPtr<nsIInputStream> stream;
if (wrappedParams.type() == OptionalInputStreamParams::TInputStreamParams) {
+ nsCOMPtr<nsIInputStream> stream;
stream = DeserializeInputStream(wrappedParams.get_InputStreamParams(),
aFileDescriptors);
if (!stream) {
NS_WARNING("Failed to deserialize wrapped stream!");
return false;
}
- mData = stream;
-
- if (NS_FAILED(mStream->AppendStream(mData))) {
- NS_WARNING("Failed to append stream!");
- return false;
- }
+ mStream = stream;
}
else {
NS_ASSERTION(wrappedParams.type() == OptionalInputStreamParams::Tvoid_t,
"Unknown type for OptionalInputStreamParams!");
}
- mAddContentLength = params.addContentLength();
-
return true;
}
Maybe<uint64_t>
nsMIMEInputStream::ExpectedSerializedLength()
{
nsCOMPtr<nsIIPCSerializableInputStream> serializable = do_QueryInterface(mStream);
return serializable ? serializable->ExpectedSerializedLength() : Nothing();
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -47,25 +47,106 @@
#include "nsILoadGroupChild.h"
#include "mozilla/ConsoleReportCollector.h"
#include "LoadInfo.h"
#include "nsISSLSocketControl.h"
#include "mozilla/Telemetry.h"
#include "nsIURL.h"
#include "nsIConsoleService.h"
#include "mozilla/BinarySearch.h"
+#include "mozilla/DebugOnly.h"
#include "nsIHttpHeaderVisitor.h"
+#include "nsIMIMEInputStream.h"
#include "nsIXULRuntime.h"
#include "nsICacheInfoChannel.h"
#include <algorithm>
namespace mozilla {
namespace net {
+static
+bool IsHeaderBlacklistedForRedirectCopy(nsHttpAtom const& aHeader)
+{
+ // IMPORTANT: keep this list ASCII-code sorted
+ static nsHttpAtom const* blackList[] = {
+ &nsHttp::Accept,
+ &nsHttp::Accept_Encoding,
+ &nsHttp::Accept_Language,
+ &nsHttp::Authentication,
+ &nsHttp::Authorization,
+ &nsHttp::Connection,
+ &nsHttp::Content_Length,
+ &nsHttp::Cookie,
+ &nsHttp::Host,
+ &nsHttp::If,
+ &nsHttp::If_Match,
+ &nsHttp::If_Modified_Since,
+ &nsHttp::If_None_Match,
+ &nsHttp::If_None_Match_Any,
+ &nsHttp::If_Range,
+ &nsHttp::If_Unmodified_Since,
+ &nsHttp::Proxy_Authenticate,
+ &nsHttp::Proxy_Authorization,
+ &nsHttp::Range,
+ &nsHttp::TE,
+ &nsHttp::Transfer_Encoding,
+ &nsHttp::Upgrade,
+ &nsHttp::User_Agent,
+ &nsHttp::WWW_Authenticate
+ };
+
+ class HttpAtomComparator
+ {
+ nsHttpAtom const& mTarget;
+ public:
+ explicit HttpAtomComparator(nsHttpAtom const& aTarget)
+ : mTarget(aTarget) {}
+ int operator()(nsHttpAtom const* aVal) const {
+ if (mTarget == *aVal) {
+ return 0;
+ }
+ return strcmp(mTarget._val, aVal->_val);
+ }
+ };
+
+ size_t unused;
+ return BinarySearchIf(blackList, 0, ArrayLength(blackList),
+ HttpAtomComparator(aHeader), &unused);
+}
+
+class AddHeadersToChannelVisitor final : public nsIHttpHeaderVisitor
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ explicit AddHeadersToChannelVisitor(nsIHttpChannel *aChannel)
+ : mChannel(aChannel)
+ {
+ }
+
+ NS_IMETHOD VisitHeader(const nsACString& aHeader,
+ const nsACString& aValue) override
+ {
+ nsHttpAtom atom = nsHttp::ResolveAtom(aHeader);
+ if (!IsHeaderBlacklistedForRedirectCopy(atom)) {
+ mChannel->SetRequestHeader(aHeader, aValue, false);
+ }
+ return NS_OK;
+ }
+private:
+ ~AddHeadersToChannelVisitor()
+ {
+ }
+
+ nsCOMPtr<nsIHttpChannel> mChannel;
+};
+
+NS_IMPL_ISUPPORTS(AddHeadersToChannelVisitor, nsIHttpHeaderVisitor)
+
HttpBaseChannel::HttpBaseChannel()
: mStartPos(UINT64_MAX)
, mStatus(NS_OK)
, mLoadFlags(LOAD_NORMAL)
, mCaps(0)
, mClassOfService(0)
, mPriority(PRIORITY_NORMAL)
, mRedirectionLimit(gHttpHandler->RedirectionLimit())
@@ -640,24 +721,46 @@ HttpBaseChannel::SetUploadStream(nsIInpu
// plugins, |stream| may include headers, specifically Content-Type and
// Content-Length headers. in this case, |contentType| and |contentLength|
// would be unspecified. this is traditionally the case of a POST request,
// and so we select POST as the request method if contentType and
// contentLength are unspecified.
if (stream) {
nsAutoCString method;
- bool hasHeaders;
-
+ bool hasHeaders = false;
+
+ nsCOMPtr<nsIMIMEInputStream> mimeStream;
if (contentType.IsEmpty()) {
method = NS_LITERAL_CSTRING("POST");
+
+ // MIME streams are a special case, and include headers which need to be
+ // copied to the channel.
+ mimeStream = do_QueryInterface(stream);
+ if (mimeStream) {
+ // Copy non-origin related headers to the channel.
+ nsCOMPtr<nsIHttpHeaderVisitor> visitor =
+ new AddHeadersToChannelVisitor(this);
+ mimeStream->VisitHeaders(visitor);
+
+ // Pass a void string for the content type so that we don't override the
+ // content type set by the stream.
+ nsCString voidContentType;
+ voidContentType.SetIsVoid(true);
+
+ return ExplicitSetUploadStream(stream, voidContentType, contentLength,
+ method, hasHeaders);
+ }
+
hasHeaders = true;
} else {
method = NS_LITERAL_CSTRING("PUT");
- hasHeaders = false;
+
+ MOZ_ASSERT(NS_FAILED(CallQueryInterface(stream, getter_AddRefs(mimeStream))),
+ "nsIMIMEInputStream should not be set with an explicit content type");
}
return ExplicitSetUploadStream(stream, contentType, contentLength,
method, hasHeaders);
}
// if stream is null, ExplicitSetUploadStream returns error.
// So we need special case for GET method.
mUploadStreamHasHeaders = false;
@@ -789,16 +892,23 @@ HttpBaseChannel::ExplicitSetUploadStream
const nsACString &aContentType,
int64_t aContentLength,
const nsACString &aMethod,
bool aStreamHasHeaders)
{
// Ensure stream is set and method is valid
NS_ENSURE_TRUE(aStream, NS_ERROR_FAILURE);
+ {
+ DebugOnly<nsCOMPtr<nsIMIMEInputStream>> mimeStream;
+ MOZ_ASSERT(!aStreamHasHeaders ||
+ NS_FAILED(CallQueryInterface(aStream, getter_AddRefs(mimeStream.value))),
+ "nsIMIMEInputStream should not include headers");
+ }
+
if (aContentLength < 0 && !aStreamHasHeaders) {
nsresult rv = aStream->Available(reinterpret_cast<uint64_t*>(&aContentLength));
if (NS_FAILED(rv) || aContentLength < 0) {
NS_ERROR("unable to determine content length");
return NS_ERROR_FAILURE;
}
}
@@ -2811,95 +2921,16 @@ HttpBaseChannel::ShouldRewriteRedirectTo
// rewrite for 303 unless it was HEAD
if (httpStatus == 303)
return method != nsHttpRequestHead::kMethod_Head;
// otherwise, such as for 307, do not rewrite
return false;
}
-static
-bool IsHeaderBlacklistedForRedirectCopy(nsHttpAtom const& aHeader)
-{
- // IMPORTANT: keep this list ASCII-code sorted
- static nsHttpAtom const* blackList[] = {
- &nsHttp::Accept,
- &nsHttp::Accept_Encoding,
- &nsHttp::Accept_Language,
- &nsHttp::Authentication,
- &nsHttp::Authorization,
- &nsHttp::Connection,
- &nsHttp::Content_Length,
- &nsHttp::Cookie,
- &nsHttp::Host,
- &nsHttp::If,
- &nsHttp::If_Match,
- &nsHttp::If_Modified_Since,
- &nsHttp::If_None_Match,
- &nsHttp::If_None_Match_Any,
- &nsHttp::If_Range,
- &nsHttp::If_Unmodified_Since,
- &nsHttp::Proxy_Authenticate,
- &nsHttp::Proxy_Authorization,
- &nsHttp::Range,
- &nsHttp::TE,
- &nsHttp::Transfer_Encoding,
- &nsHttp::Upgrade,
- &nsHttp::User_Agent,
- &nsHttp::WWW_Authenticate
- };
-
- class HttpAtomComparator
- {
- nsHttpAtom const& mTarget;
- public:
- explicit HttpAtomComparator(nsHttpAtom const& aTarget)
- : mTarget(aTarget) {}
- int operator()(nsHttpAtom const* aVal) const {
- if (mTarget == *aVal) {
- return 0;
- }
- return strcmp(mTarget._val, aVal->_val);
- }
- };
-
- size_t unused;
- return BinarySearchIf(blackList, 0, ArrayLength(blackList),
- HttpAtomComparator(aHeader), &unused);
-}
-
-class SetupReplacementChannelHeaderVisitor final : public nsIHttpHeaderVisitor
-{
-public:
- NS_DECL_ISUPPORTS
-
- explicit SetupReplacementChannelHeaderVisitor(nsIHttpChannel *aChannel)
- : mChannel(aChannel)
- {
- }
-
- NS_IMETHOD VisitHeader(const nsACString& aHeader,
- const nsACString& aValue) override
- {
- nsHttpAtom atom = nsHttp::ResolveAtom(aHeader);
- if (!IsHeaderBlacklistedForRedirectCopy(atom)) {
- mChannel->SetRequestHeader(aHeader, aValue, false);
- }
- return NS_OK;
- }
-private:
- ~SetupReplacementChannelHeaderVisitor()
- {
- }
-
- nsCOMPtr<nsIHttpChannel> mChannel;
-};
-
-NS_IMPL_ISUPPORTS(SetupReplacementChannelHeaderVisitor, nsIHttpHeaderVisitor)
-
nsresult
HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI,
nsIChannel *newChannel,
bool preserveMethod,
uint32_t redirectFlags)
{
LOG(("HttpBaseChannel::SetupReplacementChannel "
"[this=%p newChannel=%p preserveMethod=%d]",
@@ -3178,17 +3209,17 @@ HttpBaseChannel::SetupReplacementChannel
if (cacheInfoChan) {
cacheInfoChan->PreferAlternativeDataType(mPreferredCachedAltDataType);
}
if (redirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL |
nsIChannelEventSink::REDIRECT_STS_UPGRADE)) {
// Copy non-origin related headers to the new channel.
nsCOMPtr<nsIHttpHeaderVisitor> visitor =
- new SetupReplacementChannelHeaderVisitor(httpChannel);
+ new AddHeadersToChannelVisitor(httpChannel);
mRequestHead.VisitHeaders(visitor);
}
// This channel has been redirected. Don't report timing info.
mTimingEnabled = false;
return NS_OK;
}
--- a/netwerk/test/browser/browser_nsIFormPOSTActionChannel.js
+++ b/netwerk/test/browser/browser_nsIFormPOSTActionChannel.js
@@ -144,25 +144,33 @@ CustomChannel.prototype = {
<script type="text/javascript">
<!--
document.getElementById('form').submit();
//-->
</script>
`;
} else if (this.uri.spec.startsWith(ACTION_BASE)) {
var postData = "";
+ var headers = {};
if (this._uploadStream) {
var bstream = Cc["@mozilla.org/binaryinputstream;1"]
.createInstance(Ci.nsIBinaryInputStream);
bstream.setInputStream(this._uploadStream);
postData = bstream.readBytes(bstream.available());
+
+ if (this._uploadStream instanceof Ci.nsIMIMEInputStream) {
+ this._uploadStream.visitHeaders((name, value) => {
+ headers[name] = value;
+ });
+ }
}
data += `
<input id="upload_stream" value="${this._uploadStream ? "yes" : "no"}">
<input id="post_data" value="${btoa(postData)}">
+<input id="upload_headers" value='${JSON.stringify(headers)}'>
`;
}
data += `
</body>
</html>
`;
@@ -209,18 +217,19 @@ document.getElementById('form').submit()
function frameScript() {
addMessageListener("Test:WaitForIFrame", function() {
var check = function() {
if (content) {
var frame = content.document.getElementById("frame");
if (frame) {
var upload_stream = frame.contentDocument.getElementById("upload_stream");
var post_data = frame.contentDocument.getElementById("post_data");
- if (upload_stream && post_data) {
- sendAsyncMessage("Test:IFrameLoaded", [upload_stream.value, post_data.value]);
+ var headers = frame.contentDocument.getElementById("upload_headers");
+ if (upload_stream && post_data && headers) {
+ sendAsyncMessage("Test:IFrameLoaded", [upload_stream.value, post_data.value, headers.value]);
return;
}
}
}
setTimeout(check, 100);
};
@@ -231,19 +240,19 @@ function frameScript() {
function loadTestTab(uri) {
gBrowser.selectedTab = gBrowser.addTab(uri);
var browser = gBrowser.selectedBrowser;
let manager = browser.messageManager;
browser.messageManager.loadFrameScript("data:,(" + frameScript.toString() + ")();", true);
return new Promise(resolve => {
- function listener({ data: [hasUploadStream, postData] }) {
+ function listener({ data: [hasUploadStream, postData, headers] }) {
manager.removeMessageListener("Test:IFrameLoaded", listener);
- resolve([hasUploadStream, atob(postData)]);
+ resolve([hasUploadStream, atob(postData), JSON.parse(headers)]);
}
manager.addMessageListener("Test:IFrameLoaded", listener);
manager.sendAsyncMessage("Test:WaitForIFrame");
});
}
add_task(function*() {
@@ -267,18 +276,19 @@ add_task(function*() {
add_task(function*() {
var [hasUploadStream, postData] = yield loadTestTab(UPLOAD_FORM_URI);
is(hasUploadStream, "no", "upload action should not have uploadStream");
gBrowser.removeCurrentTab();
});
add_task(function*() {
- var [hasUploadStream, postData] = yield loadTestTab(POST_FORM_URI);
+ var [hasUploadStream, postData, headers] = yield loadTestTab(POST_FORM_URI);
+
is(hasUploadStream, "yes", "post action should have uploadStream");
- is(postData,
- "Content-Type: text/plain\r\n" +
- "Content-Length: 9\r\n" +
- "\r\n" +
- "foo=bar\r\n", "POST data is received correctly");
+ is(postData, "foo=bar\r\n",
+ "POST data is received correctly");
+
+ is(headers["Content-Type"], "text/plain", "Content-Type header is correct");
+ is(headers["Content-Length"], undefined, "Content-Length header is correct");
gBrowser.removeCurrentTab();
});
--- a/toolkit/modules/addons/WebRequestUpload.jsm
+++ b/toolkit/modules/addons/WebRequestUpload.jsm
@@ -365,28 +365,30 @@ function parseFormData(stream, channel,
}
return formData;
}
try {
let headers;
if (stream instanceof Ci.nsIMIMEInputStream && stream.data) {
- // MIME input streams encode additional headers as a block at the
- // beginning of their stream. The actual request data comes from a
- // sub-stream, which is accessible via their `data` member. The
- // difference in available bytes between the outer stream and the
- // inner data stream tells us the size of that header block.
- //
- // Since we need to know at least the value of the Content-Type
- // header to properly parse the request body, we need to read and
- // parse the header block in order to extract it.
+ if (channel instanceof Ci.nsIUploadChannel2 && channel.uploadStreamHasHeaders) {
+ // MIME input streams encode additional headers as a block at the
+ // beginning of their stream. The actual request data comes from a
+ // sub-stream, which is accessible via their `data` member. The
+ // difference in available bytes between the outer stream and the
+ // inner data stream tells us the size of that header block.
+ //
+ // Since we need to know at least the value of the Content-Type
+ // header to properly parse the request body, we need to read and
+ // parse the header block in order to extract it.
- headers = readString(createTextStream(stream),
- stream.available() - stream.data.available());
+ headers = readString(createTextStream(stream),
+ stream.available() - stream.data.available());
+ }
rewind(stream);
stream = stream.data;
}
let contentType;
try {
contentType = channel.getRequestHeader("Content-Type");