зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 5 changesets (bug 1305162) for permatimeout in e10s browser_action_keyword.js
Backed out changeset c633650caea6 (bug 1305162) Backed out changeset 1a23ce5a17c3 (bug 1305162) Backed out changeset d1095e03dfc6 (bug 1305162) Backed out changeset 1202edf6008d (bug 1305162) Backed out changeset de131f7c1fc1 (bug 1305162)
This commit is contained in:
Родитель
8b31ae6749
Коммит
c7973f5461
|
@ -41,15 +41,10 @@ add_task(function* () {
|
|||
data = yield createCurlData(requests.multipart, gNetwork);
|
||||
testIsMultipartRequest(data);
|
||||
testGetMultipartBoundary(data);
|
||||
testMultiPartHeaders(data);
|
||||
testRemoveBinaryDataFromMultipartText(data);
|
||||
|
||||
data = yield createCurlData(requests.multipartForm, gNetwork);
|
||||
testMultiPartHeaders(data);
|
||||
|
||||
testGetHeadersFromMultipartText({
|
||||
postDataText: "Content-Type: text/plain\r\n\r\n",
|
||||
});
|
||||
testGetHeadersFromMultipartText(data);
|
||||
|
||||
if (Services.appinfo.OS != "WINNT") {
|
||||
testEscapeStringPosix();
|
||||
|
@ -84,14 +79,6 @@ function testFindHeader(data) {
|
|||
"Should return null when a header is not found.");
|
||||
}
|
||||
|
||||
function testMultiPartHeaders(data) {
|
||||
let headers = data.headers;
|
||||
let contentType = CurlUtils.findHeader(headers, "Content-Type");
|
||||
|
||||
ok(contentType.startsWith("multipart/form-data; boundary="),
|
||||
"Multi-part content type header is present in headers array");
|
||||
}
|
||||
|
||||
function testWritePostDataTextParams(data) {
|
||||
let params = CurlUtils.writePostDataTextParams(data.postDataText);
|
||||
is(params, "param1=value1¶m2=value2¶m3=value3",
|
||||
|
|
|
@ -118,19 +118,14 @@ add_task(function* testFormSubmission() {
|
|||
|
||||
let client = hud.ui.webConsoleClient;
|
||||
const postData = yield client.getRequestPostData(request.actor);
|
||||
const requestHeaders = yield client.getRequestHeaders(request.actor);
|
||||
const responseContent = yield client.getResponseContent(request.actor);
|
||||
|
||||
let getHeader = name => {
|
||||
let header = requestHeaders.headers.find(h => h.name == name);
|
||||
return header && header.value;
|
||||
};
|
||||
|
||||
is(request.request.method, "POST", "Method is correct");
|
||||
is(getHeader("Content-Type"), "application/x-www-form-urlencoded",
|
||||
"Content-Type is correct");
|
||||
is(getHeader("Content-Length"), "20",
|
||||
"Content-length is correct");
|
||||
isnot(postData.postData.text
|
||||
.indexOf("Content-Type: application/x-www-form-urlencoded"), -1,
|
||||
"Content-Type is correct");
|
||||
isnot(postData.postData.text
|
||||
.indexOf("Content-Length: 20"), -1, "Content-length is correct");
|
||||
isnot(postData.postData.text
|
||||
.indexOf("name=foo+bar&age=144"), -1, "Form data is correct");
|
||||
is(responseContent.content.text.indexOf("<!DOCTYPE HTML>"), 0,
|
||||
|
|
|
@ -11,12 +11,6 @@ using struct mozilla::void_t
|
|||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
struct HeaderEntry
|
||||
{
|
||||
nsCString name;
|
||||
nsCString value;
|
||||
};
|
||||
|
||||
struct StringInputStreamParams
|
||||
{
|
||||
nsCString data;
|
||||
|
@ -92,8 +86,10 @@ struct BufferedInputStreamParams
|
|||
struct MIMEInputStreamParams
|
||||
{
|
||||
OptionalInputStreamParams optionalStream;
|
||||
HeaderEntry[] headers;
|
||||
nsCString headers;
|
||||
nsCString contentLength;
|
||||
bool startedReading;
|
||||
bool addContentLength;
|
||||
};
|
||||
|
||||
} // namespace ipc
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
* 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"
|
||||
|
||||
/**
|
||||
|
@ -20,10 +19,6 @@ interface nsIMIMEInputStream : nsIInputStream
|
|||
* 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;
|
||||
|
||||
|
@ -35,15 +30,6 @@ interface nsIMIMEInputStream : nsIInputStream
|
|||
*/
|
||||
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.
|
||||
|
|
|
@ -12,19 +12,18 @@
|
|||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsIHttpHeaderVisitor.h"
|
||||
#include "nsIMultiplexInputStream.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,
|
||||
|
@ -41,6 +40,8 @@ public:
|
|||
NS_DECL_NSISEEKABLESTREAM
|
||||
NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
|
||||
|
||||
nsresult Init();
|
||||
|
||||
private:
|
||||
|
||||
void InitStreams();
|
||||
|
@ -54,9 +55,15 @@ private:
|
|||
const char* aFromRawSegment, uint32_t aToOffset,
|
||||
uint32_t aCount, uint32_t *aWriteCount);
|
||||
|
||||
nsTArray<HeaderEntry> mHeaders;
|
||||
|
||||
nsCOMPtr<nsIInputStream> mStream;
|
||||
nsCString mHeaders;
|
||||
nsCOMPtr<nsIStringInputStream> mHeaderStream;
|
||||
|
||||
nsCString mContentLength;
|
||||
nsCOMPtr<nsIStringInputStream> mCLStream;
|
||||
|
||||
nsCOMPtr<nsIInputStream> mData;
|
||||
nsCOMPtr<nsIMultiplexInputStream> mStream;
|
||||
bool mAddContentLength;
|
||||
bool mStartedReading;
|
||||
};
|
||||
|
||||
|
@ -76,7 +83,8 @@ NS_IMPL_CI_INTERFACE_GETTER(nsMIMEInputStream,
|
|||
nsIInputStream,
|
||||
nsISeekableStream)
|
||||
|
||||
nsMIMEInputStream::nsMIMEInputStream() : mStartedReading(false)
|
||||
nsMIMEInputStream::nsMIMEInputStream() : mAddContentLength(false),
|
||||
mStartedReading(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -84,21 +92,40 @@ 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 = true;
|
||||
*aAddContentLength = mAddContentLength;
|
||||
return NS_OK;
|
||||
}
|
||||
NS_IMETHODIMP
|
||||
nsMIMEInputStream::SetAddContentLength(bool aAddContentLength)
|
||||
{
|
||||
NS_ENSURE_FALSE(mStartedReading, NS_ERROR_FAILURE);
|
||||
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;
|
||||
}
|
||||
mAddContentLength = aAddContentLength;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -106,39 +133,30 @@ 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");
|
||||
|
||||
HeaderEntry* entry = mHeaders.AppendElement();
|
||||
entry->name().Append(aName);
|
||||
entry->value().Append(aValue);
|
||||
// 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);
|
||||
|
||||
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);
|
||||
|
||||
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aStream);
|
||||
if (!seekable) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
mStream = aStream;
|
||||
mData = aStream;
|
||||
if (aStream)
|
||||
mStream->AppendStream(mData);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -146,7 +164,7 @@ NS_IMETHODIMP
|
|||
nsMIMEInputStream::GetData(nsIInputStream **aStream)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aStream);
|
||||
*aStream = mStream;
|
||||
*aStream = mData;
|
||||
NS_IF_ADDREF(*aStream);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -158,13 +176,28 @@ void nsMIMEInputStream::InitStreams()
|
|||
"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(); \
|
||||
}
|
||||
|
||||
|
@ -172,11 +205,8 @@ if (!mStartedReading) { \
|
|||
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))
|
||||
|
@ -254,11 +284,22 @@ nsMIMEInputStreamConstructor(nsISupports *outer, REFNSIID iid, void **result)
|
|||
if (outer)
|
||||
return NS_ERROR_NO_AGGREGATION;
|
||||
|
||||
RefPtr<nsMIMEInputStream> inst = new nsMIMEInputStream();
|
||||
nsMIMEInputStream *inst = new nsMIMEInputStream();
|
||||
if (!inst)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
return inst->QueryInterface(iid, result);
|
||||
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;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -267,9 +308,12 @@ nsMIMEInputStream::Serialize(InputStreamParams& aParams,
|
|||
{
|
||||
MIMEInputStreamParams params;
|
||||
|
||||
if (mStream) {
|
||||
if (mData) {
|
||||
nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mData);
|
||||
MOZ_ASSERT(stream);
|
||||
|
||||
InputStreamParams wrappedParams;
|
||||
SerializeInputStream(mStream, wrappedParams, aFileDescriptors);
|
||||
SerializeInputStream(stream, wrappedParams, aFileDescriptors);
|
||||
|
||||
NS_ASSERTION(wrappedParams.type() != InputStreamParams::T__None,
|
||||
"Wrapped stream failed to serialize!");
|
||||
|
@ -281,7 +325,9 @@ nsMIMEInputStream::Serialize(InputStreamParams& aParams,
|
|||
}
|
||||
|
||||
params.headers() = mHeaders;
|
||||
params.contentLength() = mContentLength;
|
||||
params.startedReading() = mStartedReading;
|
||||
params.addContentLength() = mAddContentLength;
|
||||
|
||||
aParams = params;
|
||||
}
|
||||
|
@ -300,10 +346,17 @@ nsMIMEInputStream::Deserialize(const InputStreamParams& aParams,
|
|||
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) {
|
||||
|
@ -311,13 +364,20 @@ nsMIMEInputStream::Deserialize(const InputStreamParams& aParams,
|
|||
return false;
|
||||
}
|
||||
|
||||
mStream = stream;
|
||||
mData = stream;
|
||||
|
||||
if (NS_FAILED(mStream->AppendStream(mData))) {
|
||||
NS_WARNING("Failed to append stream!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
NS_ASSERTION(wrappedParams.type() == OptionalInputStreamParams::Tvoid_t,
|
||||
"Unknown type for OptionalInputStreamParams!");
|
||||
}
|
||||
|
||||
mAddContentLength = params.addContentLength();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,9 +52,7 @@
|
|||
#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 "nsIDOMWindowUtils.h"
|
||||
|
@ -65,85 +63,6 @@
|
|||
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)
|
||||
|
@ -734,38 +653,22 @@ HttpBaseChannel::SetUploadStream(nsIInputStream *stream,
|
|||
|
||||
if (stream) {
|
||||
nsAutoCString method;
|
||||
bool hasHeaders = false;
|
||||
bool hasHeaders;
|
||||
|
||||
// This method and ExplicitSetUploadStream mean different things by "empty
|
||||
// content type string". This method means "no header", but
|
||||
// ExplicitSetUploadStream means "header with empty value". So we have to
|
||||
// massage the contentType argument into the form ExplicitSetUploadStream
|
||||
// expects.
|
||||
nsCOMPtr<nsIMIMEInputStream> mimeStream;
|
||||
nsCString contentType(contentTypeArg);
|
||||
if (contentType.IsEmpty()) {
|
||||
contentType.SetIsVoid(true);
|
||||
nsAutoCString contentType;
|
||||
if (contentTypeArg.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);
|
||||
|
||||
return ExplicitSetUploadStream(stream, contentType, contentLength,
|
||||
method, hasHeaders);
|
||||
}
|
||||
|
||||
hasHeaders = true;
|
||||
contentType.SetIsVoid(true);
|
||||
} else {
|
||||
method = NS_LITERAL_CSTRING("PUT");
|
||||
|
||||
MOZ_ASSERT(NS_FAILED(CallQueryInterface(stream, getter_AddRefs(mimeStream))),
|
||||
"nsIMIMEInputStream should not be set with an explicit content type");
|
||||
hasHeaders = false;
|
||||
contentType = contentTypeArg;
|
||||
}
|
||||
return ExplicitSetUploadStream(stream, contentType, contentLength,
|
||||
method, hasHeaders);
|
||||
|
@ -907,13 +810,6 @@ HttpBaseChannel::ExplicitSetUploadStream(nsIInputStream *aStream,
|
|||
// 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) {
|
||||
|
@ -2997,6 +2893,85 @@ HttpBaseChannel::ShouldRewriteRedirectToGET(uint32_t httpStatus,
|
|||
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,
|
||||
|
@ -3292,7 +3267,7 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI,
|
|||
nsIChannelEventSink::REDIRECT_STS_UPGRADE)) {
|
||||
// Copy non-origin related headers to the new channel.
|
||||
nsCOMPtr<nsIHttpHeaderVisitor> visitor =
|
||||
new AddHeadersToChannelVisitor(httpChannel);
|
||||
new SetupReplacementChannelHeaderVisitor(httpChannel);
|
||||
mRequestHead.VisitHeaders(visitor);
|
||||
}
|
||||
|
||||
|
|
|
@ -149,23 +149,15 @@ document.getElementById('form').submit();
|
|||
`;
|
||||
} 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)}'>
|
||||
`;
|
||||
}
|
||||
|
||||
|
@ -222,9 +214,8 @@ function frameScript() {
|
|||
if (frame) {
|
||||
var upload_stream = frame.contentDocument.getElementById("upload_stream");
|
||||
var post_data = frame.contentDocument.getElementById("post_data");
|
||||
var headers = frame.contentDocument.getElementById("upload_headers");
|
||||
if (upload_stream && post_data && headers) {
|
||||
sendAsyncMessage("Test:IFrameLoaded", [upload_stream.value, post_data.value, headers.value]);
|
||||
if (upload_stream && post_data) {
|
||||
sendAsyncMessage("Test:IFrameLoaded", [upload_stream.value, post_data.value]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -245,9 +236,9 @@ function loadTestTab(uri) {
|
|||
browser.messageManager.loadFrameScript("data:,(" + frameScript.toString() + ")();", true);
|
||||
|
||||
return new Promise(resolve => {
|
||||
function listener({ data: [hasUploadStream, postData, headers] }) {
|
||||
function listener({ data: [hasUploadStream, postData] }) {
|
||||
manager.removeMessageListener("Test:IFrameLoaded", listener);
|
||||
resolve([hasUploadStream, atob(postData), JSON.parse(headers)]);
|
||||
resolve([hasUploadStream, atob(postData)]);
|
||||
}
|
||||
|
||||
manager.addMessageListener("Test:IFrameLoaded", listener);
|
||||
|
@ -281,14 +272,13 @@ add_task(function*() {
|
|||
});
|
||||
|
||||
add_task(function*() {
|
||||
var [hasUploadStream, postData, headers] = yield loadTestTab(POST_FORM_URI);
|
||||
|
||||
var [hasUploadStream, postData] = yield loadTestTab(POST_FORM_URI);
|
||||
is(hasUploadStream, "yes", "post action should have uploadStream");
|
||||
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");
|
||||
is(postData,
|
||||
"Content-Type: text/plain\r\n" +
|
||||
"Content-Length: 9\r\n" +
|
||||
"\r\n" +
|
||||
"foo=bar\r\n", "POST data is received correctly");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
|
|
@ -69,13 +69,14 @@ add_task(function* test_setup() {
|
|||
function background() {
|
||||
const FILTERS = {urls: ["<all_urls>"]};
|
||||
|
||||
let requestBodySupported = true;
|
||||
|
||||
function onUpload(details) {
|
||||
let url = new URL(details.url);
|
||||
let upload = url.searchParams.get("upload");
|
||||
if (!upload) {
|
||||
if (!upload || !requestBodySupported) {
|
||||
return;
|
||||
}
|
||||
|
||||
let requestBody = details.requestBody;
|
||||
browser.test.log(`onBeforeRequest upload: ${details.url} ${JSON.stringify(details.requestBody)}`);
|
||||
browser.test.assertTrue(!!requestBody, `Intercepted upload ${details.url} #${details.requestId} ${upload} have a requestBody`);
|
||||
|
@ -109,8 +110,21 @@ function background() {
|
|||
onUpload(details);
|
||||
};
|
||||
|
||||
browser.webRequest.onBeforeRequest.addListener(
|
||||
onBeforeRequest, FILTERS, ["requestBody"]);
|
||||
try {
|
||||
browser.webRequest.onBeforeRequest.addListener(
|
||||
onBeforeRequest, FILTERS, ["requestBody"]);
|
||||
} catch (e) {
|
||||
browser.test.assertTrue(/\brequestBody\b/.test(e.message),
|
||||
"Request body is unsupported");
|
||||
|
||||
// requestBody is disabled in release builds
|
||||
if (!/\brequestBody\b/.test(e.message)) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
browser.webRequest.onBeforeRequest.addListener(
|
||||
onBeforeRequest, FILTERS);
|
||||
}
|
||||
}
|
||||
|
||||
add_task(function* test_xhr_forms() {
|
||||
|
|
|
@ -18,19 +18,8 @@ function makeURI(url) {
|
|||
return Services.io.newURI(url);
|
||||
}
|
||||
|
||||
function serializeInputStream(aStream) {
|
||||
let data = {
|
||||
content: NetUtil.readInputStreamToString(aStream, aStream.available()),
|
||||
};
|
||||
|
||||
if (aStream instanceof Ci.nsIMIMEInputStream) {
|
||||
data.headers = new Map();
|
||||
aStream.visitHeaders((name, value) => {
|
||||
data.headers.set(name, value);
|
||||
});
|
||||
}
|
||||
|
||||
return data;
|
||||
function readInputStreamToString(aStream) {
|
||||
return NetUtil.readInputStreamToString(aStream, aStream.available());
|
||||
}
|
||||
|
||||
function RemoteWebNavigation() {
|
||||
|
@ -92,8 +81,8 @@ RemoteWebNavigation.prototype = {
|
|||
flags: aLoadFlags,
|
||||
referrer: aReferrer ? aReferrer.spec : null,
|
||||
referrerPolicy: aReferrerPolicy,
|
||||
postData: aPostData ? serializeInputStream(aPostData) : null,
|
||||
headers: aHeaders ? serializeInputStream(aHeaders) : null,
|
||||
postData: aPostData ? readInputStreamToString(aPostData) : null,
|
||||
headers: aHeaders ? readInputStreamToString(aHeaders) : null,
|
||||
baseURI: aBaseURI ? aBaseURI.spec : null,
|
||||
triggeringPrincipal: aTriggeringPrincipal
|
||||
? Utils.serializePrincipal(aTriggeringPrincipal)
|
||||
|
|
|
@ -26,22 +26,10 @@ if (AppConstants.MOZ_CRASHREPORTER) {
|
|||
"nsICrashReporter");
|
||||
}
|
||||
|
||||
function makeInputStream(data) {
|
||||
function makeInputStream(aString) {
|
||||
let stream = Cc["@mozilla.org/io/string-input-stream;1"].
|
||||
createInstance(Ci.nsISupportsCString);
|
||||
stream.data = data.content;
|
||||
|
||||
if (data.headers) {
|
||||
let mimeStream = Cc["@mozilla.org/network/mime-input-stream;1"]
|
||||
.createInstance(Ci.nsIMIMEInputStream);
|
||||
|
||||
mimeStream.setData(stream);
|
||||
for (let [name, value] of data.headers) {
|
||||
mimeStream.addHeader(name, value);
|
||||
}
|
||||
return mimeStream;
|
||||
}
|
||||
|
||||
stream.data = aString;
|
||||
return stream; // XPConnect will QI this to nsIInputStream for us.
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ Cu.import("resource://gre/modules/Services.jsm");
|
|||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
|
||||
"resource://gre/modules/AppConstants.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
|
||||
"resource://gre/modules/BrowserUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionUtils",
|
||||
|
@ -855,8 +857,14 @@ HttpObserverManager = {
|
|||
};
|
||||
|
||||
var onBeforeRequest = {
|
||||
allowedOptions: ["blocking", "requestBody"],
|
||||
|
||||
get allowedOptions() {
|
||||
delete this.allowedOptions;
|
||||
this.allowedOptions = ["blocking"];
|
||||
if (!AppConstants.RELEASE_OR_BETA) {
|
||||
this.allowedOptions.push("requestBody");
|
||||
}
|
||||
return this.allowedOptions;
|
||||
},
|
||||
addListener(callback, filter = null, opt_extraInfoSpec = null) {
|
||||
let opts = parseExtra(opt_extraInfoSpec, this.allowedOptions);
|
||||
opts.filter = parseFilter(filter);
|
||||
|
|
|
@ -368,11 +368,31 @@ function parseFormData(stream, channel, lenient = false) {
|
|||
}
|
||||
|
||||
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.
|
||||
|
||||
headers = readString(createTextStream(stream),
|
||||
stream.available() - stream.data.available());
|
||||
|
||||
rewind(stream);
|
||||
stream = stream.data;
|
||||
}
|
||||
|
||||
let contentType = channel.getRequestHeader("Content-Type");
|
||||
let contentType;
|
||||
try {
|
||||
contentType = channel.getRequestHeader("Content-Type");
|
||||
} catch (e) {
|
||||
contentType = new Headers(headers).get("content-type");
|
||||
}
|
||||
|
||||
switch (Headers.getParam(contentType, "")) {
|
||||
case "multipart/form-data":
|
||||
|
@ -482,33 +502,29 @@ function* getRawDataChunked(outerStream, maxRead = WebRequestUpload.MAX_RAW_BYTE
|
|||
|
||||
WebRequestUpload = {
|
||||
createRequestBody(channel) {
|
||||
if (!(channel instanceof Ci.nsIUploadChannel) || !channel.uploadStream) {
|
||||
return null;
|
||||
}
|
||||
if (channel instanceof Ci.nsIUploadChannel && channel.uploadStream) {
|
||||
try {
|
||||
let stream = channel.uploadStream;
|
||||
|
||||
if (channel instanceof Ci.nsIUploadChannel2 && channel.uploadStreamHasHeaders) {
|
||||
return {error: "Upload streams with headers are unsupported"};
|
||||
}
|
||||
let formData = createFormData(stream, channel);
|
||||
if (formData) {
|
||||
return {formData};
|
||||
}
|
||||
|
||||
try {
|
||||
let stream = channel.uploadStream;
|
||||
|
||||
let formData = createFormData(stream, channel);
|
||||
if (formData) {
|
||||
return {formData};
|
||||
// If we failed to parse the stream as form data, return it as a
|
||||
// sequence of raw data chunks, along with a leniently-parsed form
|
||||
// data object, which ignores encoding errors.
|
||||
return {
|
||||
raw: Array.from(getRawDataChunked(stream)),
|
||||
lenientFormData: createFormData(stream, channel, true),
|
||||
};
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
return {error: e.message || String(e)};
|
||||
}
|
||||
|
||||
// If we failed to parse the stream as form data, return it as a
|
||||
// sequence of raw data chunks, along with a leniently-parsed form
|
||||
// data object, which ignores encoding errors.
|
||||
return {
|
||||
raw: Array.from(getRawDataChunked(stream)),
|
||||
lenientFormData: createFormData(stream, channel, true),
|
||||
};
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
return {error: e.message || String(e)};
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче