зеркало из https://github.com/mozilla/gecko-dev.git
Bug 815299 - Part 1: Add an API for setting an empty request header on an HTTP channel; r=dragana
This commit is contained in:
Родитель
ca41adb6af
Коммит
90f77f08b9
|
@ -1326,6 +1326,29 @@ HttpBaseChannel::SetRequestHeader(const nsACString& aHeader,
|
|||
return mRequestHead.SetHeader(atom, flatValue, aMerge);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::SetEmptyRequestHeader(const nsACString& aHeader)
|
||||
{
|
||||
const nsCString &flatHeader = PromiseFlatCString(aHeader);
|
||||
|
||||
LOG(("HttpBaseChannel::SetEmptyRequestHeader [this=%p header=\"%s\"]\n",
|
||||
this, flatHeader.get()));
|
||||
|
||||
// Verify header names are valid HTTP tokens and header values are reasonably
|
||||
// close to whats allowed in RFC 2616.
|
||||
if (!nsHttp::IsValidToken(flatHeader)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsHttpAtom atom = nsHttp::ResolveAtom(flatHeader.get());
|
||||
if (!atom) {
|
||||
NS_WARNING("failed to resolve atom");
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return mRequestHead.SetEmptyHeader(atom);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::VisitRequestHeaders(nsIHttpHeaderVisitor *visitor)
|
||||
{
|
||||
|
|
|
@ -138,6 +138,7 @@ public:
|
|||
NS_IMETHOD GetRequestHeader(const nsACString& aHeader, nsACString& aValue) override;
|
||||
NS_IMETHOD SetRequestHeader(const nsACString& aHeader,
|
||||
const nsACString& aValue, bool aMerge) override;
|
||||
NS_IMETHOD SetEmptyRequestHeader(const nsACString& aHeader) override;
|
||||
NS_IMETHOD VisitRequestHeaders(nsIHttpHeaderVisitor *visitor) override;
|
||||
NS_IMETHOD GetResponseHeader(const nsACString &header, nsACString &value) override;
|
||||
NS_IMETHOD SetResponseHeader(const nsACString& header,
|
||||
|
|
|
@ -1769,6 +1769,25 @@ HttpChannelChild::SetRequestHeader(const nsACString& aHeader,
|
|||
tuple->mHeader = aHeader;
|
||||
tuple->mValue = aValue;
|
||||
tuple->mMerge = aMerge;
|
||||
tuple->mEmpty = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpChannelChild::SetEmptyRequestHeader(const nsACString& aHeader)
|
||||
{
|
||||
LOG(("HttpChannelChild::SetEmptyRequestHeader [this=%p]\n", this));
|
||||
nsresult rv = HttpBaseChannel::SetEmptyRequestHeader(aHeader);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
RequestHeaderTuple* tuple = mClientSetRequestHeaders.AppendElement();
|
||||
if (!tuple)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
tuple->mHeader = aHeader;
|
||||
tuple->mMerge = false;
|
||||
tuple->mEmpty = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -80,6 +80,7 @@ public:
|
|||
NS_IMETHOD SetRequestHeader(const nsACString& aHeader,
|
||||
const nsACString& aValue,
|
||||
bool aMerge) override;
|
||||
NS_IMETHOD SetEmptyRequestHeader(const nsACString& aHeader) override;
|
||||
NS_IMETHOD RedirectTo(nsIURI *newURI) override;
|
||||
// nsIHttpChannelInternal
|
||||
NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey) override;
|
||||
|
|
|
@ -361,9 +361,13 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
|
|||
mChannel->SetLoadFlags(loadFlags);
|
||||
|
||||
for (uint32_t i = 0; i < requestHeaders.Length(); i++) {
|
||||
mChannel->SetRequestHeader(requestHeaders[i].mHeader,
|
||||
requestHeaders[i].mValue,
|
||||
requestHeaders[i].mMerge);
|
||||
if (requestHeaders[i].mEmpty) {
|
||||
mChannel->SetEmptyRequestHeader(requestHeaders[i].mHeader);
|
||||
} else {
|
||||
mChannel->SetRequestHeader(requestHeaders[i].mHeader,
|
||||
requestHeaders[i].mValue,
|
||||
requestHeaders[i].mMerge);
|
||||
}
|
||||
}
|
||||
|
||||
mParentListener = new HttpChannelParentListener(this);
|
||||
|
@ -636,9 +640,13 @@ HttpChannelParent::RecvRedirect2Verify(const nsresult& result,
|
|||
newHttpChannel->RedirectTo(apiRedirectUri);
|
||||
|
||||
for (uint32_t i = 0; i < changedHeaders.Length(); i++) {
|
||||
newHttpChannel->SetRequestHeader(changedHeaders[i].mHeader,
|
||||
changedHeaders[i].mValue,
|
||||
changedHeaders[i].mMerge);
|
||||
if (changedHeaders[i].mEmpty) {
|
||||
newHttpChannel->SetEmptyRequestHeader(changedHeaders[i].mHeader);
|
||||
} else {
|
||||
newHttpChannel->SetRequestHeader(changedHeaders[i].mHeader,
|
||||
changedHeaders[i].mValue,
|
||||
changedHeaders[i].mMerge);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,6 +105,12 @@ NullHttpChannel::SetRequestHeader(const nsACString & aHeader, const nsACString &
|
|||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::SetEmptyRequestHeader(const nsACString & aHeader)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NullHttpChannel::VisitRequestHeaders(nsIHttpHeaderVisitor *aVisitor)
|
||||
{
|
||||
|
|
|
@ -24,11 +24,13 @@ struct RequestHeaderTuple {
|
|||
nsCString mHeader;
|
||||
nsCString mValue;
|
||||
bool mMerge;
|
||||
bool mEmpty;
|
||||
|
||||
bool operator ==(const RequestHeaderTuple &other) const {
|
||||
return mHeader.Equals(other.mHeader) &&
|
||||
mValue.Equals(other.mValue) &&
|
||||
mMerge == other.mMerge;
|
||||
mMerge == other.mMerge &&
|
||||
mEmpty == other.mEmpty;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -49,13 +51,15 @@ struct ParamTraits<mozilla::net::RequestHeaderTuple>
|
|||
WriteParam(aMsg, aParam.mHeader);
|
||||
WriteParam(aMsg, aParam.mValue);
|
||||
WriteParam(aMsg, aParam.mMerge);
|
||||
WriteParam(aMsg, aParam.mEmpty);
|
||||
}
|
||||
|
||||
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
|
||||
{
|
||||
if (!ReadParam(aMsg, aIter, &aResult->mHeader) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->mValue) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->mMerge))
|
||||
!ReadParam(aMsg, aIter, &aResult->mMerge) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->mEmpty))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
|
@ -51,6 +51,25 @@ nsHttpHeaderArray::SetHeader(nsHttpAtom header,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpHeaderArray::SetEmptyHeader(nsHttpAtom header)
|
||||
{
|
||||
nsEntry *entry = nullptr;
|
||||
|
||||
LookupEntry(header, &entry);
|
||||
|
||||
if (!entry) {
|
||||
entry = mHeaders.AppendElement(); // new nsEntry()
|
||||
if (!entry)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
entry->header = header;
|
||||
} else {
|
||||
entry->value.Truncate();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpHeaderArray::SetHeaderFromNet(nsHttpAtom header, const nsACString &value)
|
||||
{
|
||||
|
|
|
@ -30,6 +30,9 @@ public:
|
|||
nsresult SetHeader(nsHttpAtom header, const nsACString &value,
|
||||
bool merge = false);
|
||||
|
||||
// Used by internal setters to set an empty header
|
||||
nsresult SetEmptyHeader(nsHttpAtom header);
|
||||
|
||||
// Merges supported headers. For other duplicate values, determines if error
|
||||
// needs to be thrown or 1st value kept.
|
||||
nsresult SetHeaderFromNet(nsHttpAtom header, const nsACString &value);
|
||||
|
@ -169,18 +172,20 @@ nsHttpHeaderArray::MergeHeader(nsHttpAtom header,
|
|||
if (value.IsEmpty())
|
||||
return; // merge of empty header = no-op
|
||||
|
||||
// Append the new value to the existing value
|
||||
if (header == nsHttp::Set_Cookie ||
|
||||
header == nsHttp::WWW_Authenticate ||
|
||||
header == nsHttp::Proxy_Authenticate)
|
||||
{
|
||||
// Special case these headers and use a newline delimiter to
|
||||
// delimit the values from one another as commas may appear
|
||||
// in the values of these headers contrary to what the spec says.
|
||||
entry->value.Append('\n');
|
||||
} else {
|
||||
// Delimit each value from the others using a comma (per HTTP spec)
|
||||
entry->value.AppendLiteral(", ");
|
||||
if (!entry->value.IsEmpty()) {
|
||||
// Append the new value to the existing value
|
||||
if (header == nsHttp::Set_Cookie ||
|
||||
header == nsHttp::WWW_Authenticate ||
|
||||
header == nsHttp::Proxy_Authenticate)
|
||||
{
|
||||
// Special case these headers and use a newline delimiter to
|
||||
// delimit the values from one another as commas may appear
|
||||
// in the values of these headers contrary to what the spec says.
|
||||
entry->value.Append('\n');
|
||||
} else {
|
||||
// Delimit each value from the others using a comma (per HTTP spec)
|
||||
entry->value.AppendLiteral(", ");
|
||||
}
|
||||
}
|
||||
entry->value.Append(value);
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ public:
|
|||
return mHeaders.PeekHeader(h);
|
||||
}
|
||||
nsresult SetHeader(nsHttpAtom h, const nsACString &v, bool m=false) { return mHeaders.SetHeader(h, v, m); }
|
||||
nsresult SetEmptyHeader(nsHttpAtom h) { return mHeaders.SetEmptyHeader(h); }
|
||||
nsresult GetHeader(nsHttpAtom h, nsACString &v) const
|
||||
{
|
||||
return mHeaders.GetHeader(h, v);
|
||||
|
|
|
@ -14,7 +14,7 @@ interface nsIHttpHeaderVisitor;
|
|||
* the inspection of the resulting HTTP response status and headers when they
|
||||
* become available.
|
||||
*/
|
||||
[builtinclass, scriptable, uuid(fdc70825-8ae1-4f81-bd9e-f1fd3f6307ad)]
|
||||
[builtinclass, scriptable, uuid(182ee0b9-e3c1-4426-9db3-70814ea7ed24)]
|
||||
interface nsIHttpChannel : nsIChannel
|
||||
{
|
||||
/**************************************************************************
|
||||
|
@ -127,6 +127,23 @@ interface nsIHttpChannel : nsIChannel
|
|||
in ACString aValue,
|
||||
in boolean aMerge);
|
||||
|
||||
/**
|
||||
* Set a request header with empty value.
|
||||
*
|
||||
* This should be used with caution in the cases where the behavior of
|
||||
* setRequestHeader ignoring empty header values is undesirable.
|
||||
*
|
||||
* This method may only be called before the channel is opened.
|
||||
*
|
||||
* @param aHeader
|
||||
* The case-insensitive name of the request header to set (e.g.,
|
||||
* "Cookie").
|
||||
*
|
||||
* @throws NS_ERROR_IN_PROGRESS if called after the channel has been
|
||||
* opened.
|
||||
*/
|
||||
void setEmptyRequestHeader(in ACString aHeader);
|
||||
|
||||
/**
|
||||
* Call this method to visit all request headers. Calling setRequestHeader
|
||||
* while visiting request headers has undefined behavior. Don't do it!
|
||||
|
|
|
@ -747,6 +747,13 @@ nsViewSourceChannel::SetRequestHeader(const nsACString & aHeader,
|
|||
mHttpChannel->SetRequestHeader(aHeader, aValue, aMerge);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsViewSourceChannel::SetEmptyRequestHeader(const nsACString & aHeader)
|
||||
{
|
||||
return !mHttpChannel ? NS_ERROR_NULL_POINTER :
|
||||
mHttpChannel->SetEmptyRequestHeader(aHeader);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsViewSourceChannel::VisitRequestHeaders(nsIHttpHeaderVisitor *aVisitor)
|
||||
{
|
||||
|
|
|
@ -46,6 +46,24 @@ function setup_test() {
|
|||
setOK = channel.getRequestHeader("MergeMe");
|
||||
do_check_eq(setOK, "foo1, foo2, foo3");
|
||||
|
||||
channel.setEmptyRequestHeader("Empty");
|
||||
setOK = channel.getRequestHeader("Empty");
|
||||
do_check_eq(setOK, "");
|
||||
|
||||
channel.setRequestHeader("ReplaceWithEmpty", "initial value", true);
|
||||
setOK = channel.getRequestHeader("ReplaceWithEmpty");
|
||||
do_check_eq(setOK, "initial value");
|
||||
channel.setEmptyRequestHeader("ReplaceWithEmpty");
|
||||
setOK = channel.getRequestHeader("ReplaceWithEmpty");
|
||||
do_check_eq(setOK, "");
|
||||
|
||||
channel.setEmptyRequestHeader("MergeWithEmpty");
|
||||
setOK = channel.getRequestHeader("MergeWithEmpty");
|
||||
do_check_eq(setOK, "");
|
||||
channel.setRequestHeader("MergeWithEmpty", "foo", true);
|
||||
setOK = channel.getRequestHeader("MergeWithEmpty");
|
||||
do_check_eq(setOK, "foo");
|
||||
|
||||
var uri = ios.newURI("http://foo1.invalid:80", null, null);
|
||||
channel.referrer = uri;
|
||||
do_check_true(channel.referrer.equals(uri));
|
||||
|
@ -85,6 +103,12 @@ function serverHandler(metadata, response) {
|
|||
do_check_eq(setOK, "replaced");
|
||||
setOK = metadata.getHeader("MergeMe");
|
||||
do_check_eq(setOK, "foo1, foo2, foo3");
|
||||
setOK = metadata.getHeader("Empty");
|
||||
do_check_eq(setOK, "");
|
||||
setOK = metadata.getHeader("ReplaceWithEmpty");
|
||||
do_check_eq(setOK, "");
|
||||
setOK = metadata.getHeader("MergeWithEmpty");
|
||||
do_check_eq(setOK, "foo");
|
||||
setOK = metadata.getHeader("Referer");
|
||||
do_check_eq(setOK, "http://foo2.invalid:90/bar");
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче