mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-15 08:53:07 +09:00
backport mozbug 1334776 - CVE-2017-7797 Header name interning leaks across origins
Potential attack: session supercookie. [Moz Notes](https://bugzilla.mozilla.org/show_bug.cgi?id=1334776#c5): "The problem is that for unknown header names we store the first one we see and then later we case-insensitively match against that name *globally*. That means you can track if a user agent has already seen a certain header name used (by using a different casing and observing whether it gets normalized). This would allow you to see if a user has used a sensitive service that uses custom header names, or allows you to track a user across sites, by teaching the browser about a certain header case once and then observing if different casings get normalized to that. What we should do instead is only store the casing for a header name for each header list and not globally. That way it only leaks where it's expected (and necessary) to leak." [Moz fix note](https://bugzilla.mozilla.org/show_bug.cgi?id=1334776#c8): "nsHttpAtom now holds the old nsHttpAtom and a string that is case sensitive (only for not standard headers). So nsHttpAtom holds a pointer to a header name. (header names are store on a static structure). This is how it used to be. I left that part the same but added a nsCString which holds a string that was used to resoled the header name. So when we parse headers we call ResolveHeader with a char*. If it is a new header name the char* will be stored in a HttpHeapAtom, nsHttpAtom::_val will point to HttpHeapAtom::value and the same strings will be stored in mLocalCaseSensitiveHeader. For the first resolve request they will be the same but for the following maybe not. At the end this nsHttpAtom will be stored in nsHttpHeaderArray. For all operation we will used the old char* except when we are returning it to a script using VisitHeaders."
This commit is contained in:
parent
de28886722
commit
30797d4da8
21 changed files with 238 additions and 101 deletions
|
|
@ -72,7 +72,7 @@ function isWS({ requestHeaders, responseHeaders }) {
|
|||
|
||||
// Find the 'upgrade' header.
|
||||
let upgradeHeader = requestHeaders.headers.find(header => {
|
||||
return (header.name == "Upgrade");
|
||||
return (header.name.toLowerCase() == "upgrade");
|
||||
});
|
||||
|
||||
// If no header found on request, check response - mainly to get
|
||||
|
|
@ -81,7 +81,7 @@ function isWS({ requestHeaders, responseHeaders }) {
|
|||
if (!upgradeHeader && responseHeaders &&
|
||||
Array.isArray(responseHeaders.headers)) {
|
||||
upgradeHeader = responseHeaders.headers.find(header => {
|
||||
return (header.name == "Upgrade");
|
||||
return (header.name.toLowerCase() == "upgrade");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,12 +49,12 @@ add_task(function* () {
|
|||
|
||||
const EXPECTED_RESPONSE_HEADERS = [
|
||||
`${httpVersion} ${status} ${statusText}`,
|
||||
"Last-Modified: Sun, 3 May 2015 11:11:11 GMT",
|
||||
"Content-Type: text/html",
|
||||
"Content-Length: 465",
|
||||
"Connection: close",
|
||||
"Server: httpd.js",
|
||||
"Date: Sun, 3 May 2015 11:11:11 GMT"
|
||||
"last-modified: Sun, 3 May 2015 11:11:11 GMT",
|
||||
"content-type: text/html",
|
||||
"content-length: 465",
|
||||
"connection: close",
|
||||
"server: httpd.js",
|
||||
"date: Sun, 3 May 2015 11:11:11 GMT"
|
||||
].join("\n");
|
||||
|
||||
yield waitForClipboardPromise(function setup() {
|
||||
|
|
@ -62,8 +62,8 @@ add_task(function* () {
|
|||
}, function validate(result) {
|
||||
// Fake the "Last-Modified" and "Date" headers because they will vary:
|
||||
result = String(result)
|
||||
.replace(/Last-Modified: [^\n]+ GMT/, "Last-Modified: Sun, 3 May 2015 11:11:11 GMT")
|
||||
.replace(/Date: [^\n]+ GMT/, "Date: Sun, 3 May 2015 11:11:11 GMT");
|
||||
.replace(/last-modified: [^\n]+ GMT/, "last-modified: Sun, 3 May 2015 11:11:11 GMT")
|
||||
.replace(/date: [^\n]+ GMT/, "date: Sun, 3 May 2015 11:11:11 GMT");
|
||||
return result === EXPECTED_RESPONSE_HEADERS;
|
||||
});
|
||||
info("Clipboard contains the currently selected item's response headers.");
|
||||
|
|
|
|||
|
|
@ -48,9 +48,9 @@ add_task(function* () {
|
|||
let lastRequest = RequestsMenu.getItemAtIndex(1);
|
||||
|
||||
info("First request happened at: " +
|
||||
firstRequest.attachment.responseHeaders.headers.find(e => e.name == "Date").value);
|
||||
firstRequest.attachment.responseHeaders.headers.find(e => e.name == "date").value);
|
||||
info("Last request happened at: " +
|
||||
lastRequest.attachment.responseHeaders.headers.find(e => e.name == "Date").value);
|
||||
lastRequest.attachment.responseHeaders.headers.find(e => e.name == "date").value);
|
||||
|
||||
ok(secDivs.length,
|
||||
"There should be at least one division on the seconds time scale.");
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ AppCacheUtils.prototype = {
|
|||
_parseManifest: function ACU__parseManifest(uriInfo) {
|
||||
let deferred = defer();
|
||||
let manifestName = uriInfo.name;
|
||||
let manifestLastModified = new Date(uriInfo.responseHeaders["Last-Modified"]);
|
||||
let manifestLastModified = new Date(uriInfo.responseHeaders["last-modified"]);
|
||||
|
||||
if (uriInfo.charset.toLowerCase() != "utf-8") {
|
||||
this._addError(0, "notUTF8", uriInfo.charset);
|
||||
|
|
@ -158,7 +158,7 @@ AppCacheUtils.prototype = {
|
|||
// Check that the resource was not modified after the manifest was last
|
||||
// modified. If it was then the manifest file should be refreshed.
|
||||
let resourceLastModified =
|
||||
new Date(uriInfo.responseHeaders["Last-Modified"]);
|
||||
new Date(uriInfo.responseHeaders["last-modified"]);
|
||||
|
||||
if (manifestLastModified < resourceLastModified) {
|
||||
this._addError(parsedUri.line, "fileChangedButNotManifest",
|
||||
|
|
@ -230,12 +230,12 @@ AppCacheUtils.prototype = {
|
|||
|
||||
result.requestHeaders = {};
|
||||
request.visitRequestHeaders(function (header, value) {
|
||||
result.requestHeaders[header] = value;
|
||||
result.responseHeaders[header.toLowerCase()] = value;
|
||||
});
|
||||
|
||||
result.responseHeaders = {};
|
||||
request.visitResponseHeaders(function (header, value) {
|
||||
result.responseHeaders[header] = value;
|
||||
result.responseHeaders[header.toLowerCase()] = value;
|
||||
});
|
||||
|
||||
deferred.resolve(result);
|
||||
|
|
|
|||
|
|
@ -81,14 +81,14 @@ const Curl = {
|
|||
postDataText = data.postDataText;
|
||||
postData.push("--data");
|
||||
postData.push(escapeString(utils.writePostDataTextParams(postDataText)));
|
||||
ignoredHeaders.add("Content-Length");
|
||||
ignoredHeaders.add("content-length");
|
||||
} else if (multipartRequest) {
|
||||
postDataText = data.postDataText;
|
||||
postData.push("--data-binary");
|
||||
let boundary = utils.getMultipartBoundary(data);
|
||||
let text = utils.removeBinaryDataFromMultipartText(postDataText, boundary);
|
||||
postData.push(escapeString(text));
|
||||
ignoredHeaders.add("Content-Length");
|
||||
ignoredHeaders.add("content-length");
|
||||
}
|
||||
|
||||
// Add method.
|
||||
|
|
@ -125,11 +125,11 @@ const Curl = {
|
|||
}
|
||||
for (let i = 0; i < headers.length; i++) {
|
||||
let header = headers[i];
|
||||
if (header.name === "Accept-Encoding") {
|
||||
if (header.name.toLowerCase() === "accept-encoding") {
|
||||
command.push("--compressed");
|
||||
continue;
|
||||
}
|
||||
if (ignoredHeaders.has(header.name)) {
|
||||
if (ignoredHeaders.has(header.name.toLowerCase())) {
|
||||
continue;
|
||||
}
|
||||
command.push("-H");
|
||||
|
|
|
|||
|
|
@ -26,11 +26,11 @@ add_task(function* () {
|
|||
// Select "Headers" tab
|
||||
let tabBody = yield selectNetInfoTab(hud, netInfoBody, "headers");
|
||||
let paramName = tabBody.querySelector(
|
||||
".netInfoParamName > span[title='Content-Type']");
|
||||
".netInfoParamName > span[title='content-type']");
|
||||
|
||||
// Verify "Content-Type" header (name and value)
|
||||
ok(paramName, "Header name must exist");
|
||||
is(paramName.textContent, "Content-Type",
|
||||
is(paramName.textContent, "content-type",
|
||||
"The header name must have proper value");
|
||||
|
||||
let paramValue = paramName.parentNode.nextSibling;
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ function getContent() {
|
|||
function performTest() {
|
||||
function readHeader(name) {
|
||||
for (let header of headers) {
|
||||
if (header.name == name) {
|
||||
if (header.name.toLowerCase() == name.toLowerCase()) {
|
||||
return header.value;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -196,13 +196,13 @@ function onResponseHeaders(aState, aResponse)
|
|||
ok(!!aResponse.rawHeaders, "response rawHeaders available");
|
||||
|
||||
checkHeadersOrCookies(aResponse.headers, {
|
||||
"Content-Type": /^application\/(json|octet-stream)$/,
|
||||
"Content-Length": /^\d+$/,
|
||||
"content-type": /^application\/(json|octet-stream)$/,
|
||||
"content-length": /^\d+$/,
|
||||
});
|
||||
|
||||
checkRawHeaders(aResponse.rawHeaders, {
|
||||
"Content-Type": /^application\/(json|octet-stream)$/,
|
||||
"Content-Length": /^\d+$/,
|
||||
"content-type": /^application\/(json|octet-stream)$/,
|
||||
"content-length": /^\d+$/,
|
||||
});
|
||||
|
||||
onResponseCookies = onResponseCookies.bind(null, aState);
|
||||
|
|
|
|||
|
|
@ -212,8 +212,8 @@ function onResponseHeaders(aState, aResponse)
|
|||
ok(aResponse.headersSize > 0, "response headersSize > 0");
|
||||
|
||||
checkHeadersOrCookies(aResponse.headers, {
|
||||
"Content-Type": /^application\/(json|octet-stream)$/,
|
||||
"Content-Length": /^\d+$/,
|
||||
"content-type": /^application\/(json|octet-stream)$/,
|
||||
"content-length": /^\d+$/,
|
||||
"x-very-short": "hello world",
|
||||
"x-very-long": {
|
||||
"type": "longString",
|
||||
|
|
|
|||
|
|
@ -204,13 +204,13 @@ function onResponseHeaders(aState, aResponse)
|
|||
ok(!!aResponse.rawHeaders, "response rawHeaders available");
|
||||
|
||||
checkHeadersOrCookies(aResponse.headers, {
|
||||
"Content-Type": /^application\/(json|octet-stream)$/,
|
||||
"Content-Length": /^\d+$/,
|
||||
"content-type": /^application\/(json|octet-stream)$/,
|
||||
"content-length": /^\d+$/,
|
||||
});
|
||||
|
||||
checkRawHeaders(aResponse.rawHeaders, {
|
||||
"Content-Type": /^application\/(json|octet-stream)$/,
|
||||
"Content-Length": /^\d+$/,
|
||||
"content-type": /^application\/(json|octet-stream)$/,
|
||||
"content-length": /^\d+$/,
|
||||
});
|
||||
|
||||
onResponseCookies = onResponseCookies.bind(null, aState);
|
||||
|
|
|
|||
|
|
@ -743,7 +743,7 @@ function runTest() {
|
|||
is(res.responseHeaders[header], test.responseHeaders[header],
|
||||
"|xhr.getResponseHeader()|wrong response header (" + header + ") in test for " +
|
||||
test.toSource());
|
||||
is(res.allResponseHeaders[header], test.responseHeaders[header],
|
||||
is(res.allResponseHeaders[header.toLowerCase()], test.responseHeaders[header],
|
||||
"|xhr.getAllResponseHeaderss()|wrong response header (" + header + ") in test for " +
|
||||
test.toSource());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1665,13 +1665,7 @@ HttpBaseChannel::SetRequestHeader(const nsACString& aHeader,
|
|||
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.SetHeader(atom, flatValue, aMerge);
|
||||
return mRequestHead.SetHeader(aHeader, flatValue, aMerge);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
@ -1688,13 +1682,7 @@ HttpBaseChannel::SetEmptyRequestHeader(const nsACString& aHeader)
|
|||
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);
|
||||
return mRequestHead.SetEmptyHeader(aHeader);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
@ -1750,7 +1738,7 @@ HttpBaseChannel::SetResponseHeader(const nsACString& header,
|
|||
|
||||
mResponseHeadersModified = true;
|
||||
|
||||
return mResponseHead->SetHeader(atom, value, merge);
|
||||
return mResponseHead->SetHeader(header, value, merge);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
|||
|
|
@ -98,7 +98,11 @@ struct ParamTraits<mozilla::net::nsHttpHeaderArray::nsEntry>
|
|||
|
||||
static void Write(Message* aMsg, const paramType& aParam)
|
||||
{
|
||||
WriteParam(aMsg, aParam.header);
|
||||
if (aParam.headerNameOriginal.IsEmpty()) {
|
||||
WriteParam(aMsg, aParam.header);
|
||||
} else {
|
||||
WriteParam(aMsg, aParam.headerNameOriginal);
|
||||
}
|
||||
WriteParam(aMsg, aParam.value);
|
||||
switch (aParam.variety) {
|
||||
case mozilla::net::nsHttpHeaderArray::eVarietyUnknown:
|
||||
|
|
@ -124,11 +128,18 @@ struct ParamTraits<mozilla::net::nsHttpHeaderArray::nsEntry>
|
|||
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
|
||||
{
|
||||
uint8_t variety;
|
||||
if (!ReadParam(aMsg, aIter, &aResult->header) ||
|
||||
nsAutoCString header;
|
||||
if (!ReadParam(aMsg, aIter, &header) ||
|
||||
!ReadParam(aMsg, aIter, &aResult->value) ||
|
||||
!ReadParam(aMsg, aIter, &variety))
|
||||
return false;
|
||||
|
||||
mozilla::net::nsHttpAtom atom = mozilla::net::nsHttp::ResolveAtom(header);
|
||||
aResult->header = atom;
|
||||
if (!header.Equals(atom.get())) {
|
||||
aResult->headerNameOriginal = header;
|
||||
}
|
||||
|
||||
switch (variety) {
|
||||
case 0:
|
||||
aResult->variety = mozilla::net::nsHttpHeaderArray::eVarietyUnknown;
|
||||
|
|
|
|||
|
|
@ -18,11 +18,36 @@ namespace net {
|
|||
//-----------------------------------------------------------------------------
|
||||
// nsHttpHeaderArray <public>
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsresult
|
||||
nsHttpHeaderArray::SetHeader(const nsACString &headerName,
|
||||
const nsACString &value,
|
||||
bool merge,
|
||||
nsHttpHeaderArray::HeaderVariety variety)
|
||||
{
|
||||
nsHttpAtom header = nsHttp::ResolveAtom(PromiseFlatCString(headerName).get());
|
||||
if (!header) {
|
||||
NS_WARNING("failed to resolve atom");
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
return SetHeader(header, headerName, value, merge, variety);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpHeaderArray::SetHeader(nsHttpAtom header,
|
||||
const nsACString &value,
|
||||
bool merge,
|
||||
nsHttpHeaderArray::HeaderVariety variety)
|
||||
{
|
||||
return SetHeader(header, EmptyCString(), value, merge, variety);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpHeaderArray::SetHeader(nsHttpAtom header,
|
||||
const nsACString &headerName,
|
||||
const nsACString &value,
|
||||
bool merge,
|
||||
nsHttpHeaderArray::HeaderVariety variety)
|
||||
{
|
||||
MOZ_ASSERT((variety == eVarietyResponse) ||
|
||||
(variety == eVarietyRequestDefault) ||
|
||||
|
|
@ -51,7 +76,7 @@ nsHttpHeaderArray::SetHeader(nsHttpAtom header,
|
|||
MOZ_ASSERT(!entry || variety != eVarietyRequestDefault,
|
||||
"Cannot set default entry which overrides existing entry!");
|
||||
if (!entry) {
|
||||
return SetHeader_internal(header, value, variety);
|
||||
return SetHeader_internal(header, headerName, value, variety);
|
||||
} else if (merge && !IsSingletonHeader(header)) {
|
||||
return MergeHeader(header, entry, value, variety);
|
||||
} else {
|
||||
|
|
@ -59,7 +84,7 @@ nsHttpHeaderArray::SetHeader(nsHttpAtom header,
|
|||
if (entry->variety == eVarietyResponseNetOriginalAndResponse) {
|
||||
MOZ_ASSERT(variety == eVarietyResponse);
|
||||
entry->variety = eVarietyResponseNetOriginal;
|
||||
return SetHeader_internal(header, value, variety);
|
||||
return SetHeader_internal(header, headerName, value, variety);
|
||||
} else {
|
||||
entry->value = value;
|
||||
entry->variety = variety;
|
||||
|
|
@ -71,6 +96,7 @@ nsHttpHeaderArray::SetHeader(nsHttpAtom header,
|
|||
|
||||
nsresult
|
||||
nsHttpHeaderArray::SetHeader_internal(nsHttpAtom header,
|
||||
const nsACString &headerName,
|
||||
const nsACString &value,
|
||||
nsHttpHeaderArray::HeaderVariety variety)
|
||||
{
|
||||
|
|
@ -79,14 +105,26 @@ nsHttpHeaderArray::SetHeader_internal(nsHttpAtom header,
|
|||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
entry->header = header;
|
||||
// Only save original form of a header if it is different than the header
|
||||
// atom string.
|
||||
if (!headerName.Equals(header.get())) {
|
||||
entry->headerNameOriginal = headerName;
|
||||
}
|
||||
entry->value = value;
|
||||
entry->variety = variety;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpHeaderArray::SetEmptyHeader(nsHttpAtom header, HeaderVariety variety)
|
||||
nsHttpHeaderArray::SetEmptyHeader(const nsACString &headerName,
|
||||
HeaderVariety variety)
|
||||
{
|
||||
nsHttpAtom header = nsHttp::ResolveAtom(PromiseFlatCString(headerName).get());
|
||||
if (!header) {
|
||||
NS_WARNING("failed to resolve atom");
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
MOZ_ASSERT((variety == eVarietyResponse) ||
|
||||
(variety == eVarietyRequestDefault) ||
|
||||
(variety == eVarietyRequestOverride),
|
||||
|
|
@ -104,11 +142,12 @@ nsHttpHeaderArray::SetEmptyHeader(nsHttpAtom header, HeaderVariety variety)
|
|||
entry->variety = eVarietyResponseNetOriginal;
|
||||
}
|
||||
|
||||
return SetHeader_internal(header, EmptyCString(), variety);
|
||||
return SetHeader_internal(header, headerName, EmptyCString(), variety);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpHeaderArray::SetHeaderFromNet(nsHttpAtom header,
|
||||
const nsACString &headerNameOriginal,
|
||||
const nsACString &value,
|
||||
bool response)
|
||||
{
|
||||
|
|
@ -125,7 +164,7 @@ nsHttpHeaderArray::SetHeaderFromNet(nsHttpAtom header,
|
|||
LOG(("Ignoring Empty Header: %s\n", header.get()));
|
||||
if (response) {
|
||||
// Set header as original but not as response header.
|
||||
return SetHeader_internal(header, value,
|
||||
return SetHeader_internal(header, headerNameOriginal, value,
|
||||
eVarietyResponseNetOriginal);
|
||||
}
|
||||
return NS_OK; // ignore empty headers by default
|
||||
|
|
@ -135,7 +174,7 @@ nsHttpHeaderArray::SetHeaderFromNet(nsHttpAtom header,
|
|||
if (response) {
|
||||
variety = eVarietyResponseNetOriginalAndResponse;
|
||||
}
|
||||
return SetHeader_internal(header, value, variety);
|
||||
return SetHeader_internal(header, headerNameOriginal, value, variety);
|
||||
|
||||
} else if (!IsSingletonHeader(header)) {
|
||||
HeaderVariety variety = eVarietyRequestOverride;
|
||||
|
|
@ -147,7 +186,7 @@ nsHttpHeaderArray::SetHeaderFromNet(nsHttpAtom header,
|
|||
return rv;
|
||||
}
|
||||
if (response) {
|
||||
rv = SetHeader_internal(header, value,
|
||||
rv = SetHeader_internal(header, headerNameOriginal, value,
|
||||
eVarietyResponseNetOriginal);
|
||||
}
|
||||
return rv;
|
||||
|
|
@ -164,7 +203,7 @@ nsHttpHeaderArray::SetHeaderFromNet(nsHttpAtom header,
|
|||
|
||||
}
|
||||
if (response) {
|
||||
return SetHeader_internal(header, value,
|
||||
return SetHeader_internal(header, headerNameOriginal, value,
|
||||
eVarietyResponseNetOriginal);
|
||||
}
|
||||
}
|
||||
|
|
@ -174,6 +213,7 @@ nsHttpHeaderArray::SetHeaderFromNet(nsHttpAtom header,
|
|||
|
||||
nsresult
|
||||
nsHttpHeaderArray::SetResponseHeaderFromCache(nsHttpAtom header,
|
||||
const nsACString &headerNameOriginal,
|
||||
const nsACString &value,
|
||||
nsHttpHeaderArray::HeaderVariety variety)
|
||||
{
|
||||
|
|
@ -183,7 +223,7 @@ nsHttpHeaderArray::SetResponseHeaderFromCache(nsHttpAtom header,
|
|||
"eVarietyResponseNetOriginal");
|
||||
|
||||
if (variety == eVarietyResponseNetOriginal) {
|
||||
return SetHeader_internal(header, value,
|
||||
return SetHeader_internal(header, headerNameOriginal, value,
|
||||
eVarietyResponseNetOriginal);
|
||||
} else {
|
||||
nsTArray<nsEntry>::index_type index = 0;
|
||||
|
|
@ -203,7 +243,8 @@ nsHttpHeaderArray::SetResponseHeaderFromCache(nsHttpAtom header,
|
|||
}
|
||||
} while (index != mHeaders.NoIndex);
|
||||
// If we are here, we have not found an entry so add a new one.
|
||||
return SetHeader_internal(header, value, eVarietyResponse);
|
||||
return SetHeader_internal(header, headerNameOriginal, value,
|
||||
eVarietyResponse);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -260,8 +301,16 @@ nsHttpHeaderArray::GetOriginalHeader(nsHttpAtom aHeader,
|
|||
if (entry.variety == eVarietyResponse) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsAutoCString hdr;
|
||||
if (entry.headerNameOriginal.IsEmpty()) {
|
||||
hdr = nsDependentCString(entry.header);
|
||||
} else {
|
||||
hdr = entry.headerNameOriginal;
|
||||
}
|
||||
|
||||
rv = NS_OK;
|
||||
if (NS_FAILED(aVisitor->VisitHeader(nsDependentCString(entry.header),
|
||||
if (NS_FAILED(aVisitor->VisitHeader(hdr,
|
||||
entry.value))) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -298,8 +347,14 @@ nsHttpHeaderArray::VisitHeaders(nsIHttpHeaderVisitor *visitor, nsHttpHeaderArray
|
|||
} else if (filter == eFilterResponseOriginal && entry.variety == eVarietyResponse) {
|
||||
continue;
|
||||
}
|
||||
rv = visitor->VisitHeader(
|
||||
nsDependentCString(entry.header), entry.value);
|
||||
|
||||
nsAutoCString hdr;
|
||||
if (entry.headerNameOriginal.IsEmpty()) {
|
||||
hdr = nsDependentCString(entry.header);
|
||||
} else {
|
||||
hdr = entry.headerNameOriginal;
|
||||
}
|
||||
rv = visitor->VisitHeader(hdr, entry.value);
|
||||
if NS_FAILED(rv) {
|
||||
return rv;
|
||||
}
|
||||
|
|
@ -310,6 +365,7 @@ nsHttpHeaderArray::VisitHeaders(nsIHttpHeaderVisitor *visitor, nsHttpHeaderArray
|
|||
/*static*/ nsresult
|
||||
nsHttpHeaderArray::ParseHeaderLine(const nsACString& line,
|
||||
nsHttpAtom *hdr,
|
||||
nsACString *headerName,
|
||||
nsACString *val)
|
||||
{
|
||||
//
|
||||
|
|
@ -360,6 +416,7 @@ nsHttpHeaderArray::ParseHeaderLine(const nsACString& line,
|
|||
// assign return values
|
||||
if (hdr) *hdr = atom;
|
||||
if (val) val->Assign(p, p2 - p + 1);
|
||||
if (headerName) headerName->Assign(sub);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
@ -397,7 +454,11 @@ nsHttpHeaderArray::Flatten(nsACString &buf, bool pruneProxyHeaders,
|
|||
continue;
|
||||
}
|
||||
|
||||
buf.Append(entry.header);
|
||||
if (entry.headerNameOriginal.IsEmpty()) {
|
||||
buf.Append(entry.header);
|
||||
} else {
|
||||
buf.Append(entry.headerNameOriginal);
|
||||
}
|
||||
buf.AppendLiteral(": ");
|
||||
buf.Append(entry.value);
|
||||
buf.AppendLiteral("\r\n");
|
||||
|
|
@ -415,7 +476,11 @@ nsHttpHeaderArray::FlattenOriginalHeader(nsACString &buf)
|
|||
continue;
|
||||
}
|
||||
|
||||
buf.Append(entry.header);
|
||||
if (entry.headerNameOriginal.IsEmpty()) {
|
||||
buf.Append(entry.header);
|
||||
} else {
|
||||
buf.Append(entry.headerNameOriginal);
|
||||
}
|
||||
buf.AppendLiteral(": ");
|
||||
buf.Append(entry.value);
|
||||
buf.AppendLiteral("\r\n");
|
||||
|
|
@ -423,11 +488,13 @@ nsHttpHeaderArray::FlattenOriginalHeader(nsACString &buf)
|
|||
}
|
||||
|
||||
const char *
|
||||
nsHttpHeaderArray::PeekHeaderAt(uint32_t index, nsHttpAtom &header) const
|
||||
nsHttpHeaderArray::PeekHeaderAt(uint32_t index, nsHttpAtom &header,
|
||||
nsACString &headerNameOriginal) const
|
||||
{
|
||||
const nsEntry &entry = mHeaders[index];
|
||||
|
||||
header = entry.header;
|
||||
headerNameOriginal = entry.headerNameOriginal;
|
||||
return entry.value.get();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,19 +49,30 @@ public:
|
|||
};
|
||||
|
||||
// Used by internal setters: to set header from network use SetHeaderFromNet
|
||||
nsresult SetHeader(const nsACString &headerName,
|
||||
const nsACString &value,
|
||||
bool merge, HeaderVariety variety);
|
||||
nsresult SetHeader(nsHttpAtom header, const nsACString &value,
|
||||
bool merge, HeaderVariety variety);
|
||||
nsresult SetHeader(nsHttpAtom header,
|
||||
const nsACString &headerName,
|
||||
const nsACString &value,
|
||||
bool merge, HeaderVariety variety);
|
||||
|
||||
// Used by internal setters to set an empty header
|
||||
nsresult SetEmptyHeader(nsHttpAtom header, HeaderVariety variety);
|
||||
nsresult SetEmptyHeader(const nsACString &headerName, HeaderVariety variety);
|
||||
|
||||
// Merges supported headers. For other duplicate values, determines if error
|
||||
// needs to be thrown or 1st value kept.
|
||||
// For the response header we keep the original headers as well.
|
||||
nsresult SetHeaderFromNet(nsHttpAtom header, const nsACString &value,
|
||||
nsresult SetHeaderFromNet(nsHttpAtom header,
|
||||
const nsACString &headerNameOriginal,
|
||||
const nsACString &value,
|
||||
bool response);
|
||||
|
||||
nsresult SetResponseHeaderFromCache(nsHttpAtom header, const nsACString &value,
|
||||
nsresult SetResponseHeaderFromCache(nsHttpAtom header,
|
||||
const nsACString &headerNameOriginal,
|
||||
const nsACString &value,
|
||||
HeaderVariety variety);
|
||||
|
||||
nsresult GetHeader(nsHttpAtom header, nsACString &value) const;
|
||||
|
|
@ -97,15 +108,17 @@ public:
|
|||
// parse a header line, return the header atom and a pointer to the
|
||||
// header value (the substring of the header line -- do not free).
|
||||
static nsresult ParseHeaderLine(const nsACString& line,
|
||||
nsHttpAtom *header=nullptr,
|
||||
nsACString* value=nullptr);
|
||||
nsHttpAtom *header = nullptr,
|
||||
nsACString *headerNameOriginal = nullptr,
|
||||
nsACString *value = nullptr);
|
||||
|
||||
void Flatten(nsACString &, bool pruneProxyHeaders, bool pruneTransients);
|
||||
void FlattenOriginalHeader(nsACString &);
|
||||
|
||||
uint32_t Count() const { return mHeaders.Length(); }
|
||||
|
||||
const char *PeekHeaderAt(uint32_t i, nsHttpAtom &header) const;
|
||||
const char *PeekHeaderAt(uint32_t i, nsHttpAtom &header,
|
||||
nsACString &headerNameOriginal) const;
|
||||
|
||||
void Clear();
|
||||
|
||||
|
|
@ -113,6 +126,7 @@ public:
|
|||
struct nsEntry
|
||||
{
|
||||
nsHttpAtom header;
|
||||
nsCString headerNameOriginal;
|
||||
nsCString value;
|
||||
HeaderVariety variety = eVarietyUnknown;
|
||||
|
||||
|
|
@ -140,7 +154,9 @@ private:
|
|||
int32_t LookupEntry(nsHttpAtom header, nsEntry **);
|
||||
nsresult MergeHeader(nsHttpAtom header, nsEntry *entry,
|
||||
const nsACString &value, HeaderVariety variety);
|
||||
nsresult SetHeader_internal(nsHttpAtom header, const nsACString &value,
|
||||
nsresult SetHeader_internal(nsHttpAtom header,
|
||||
const nsACString &headerName,
|
||||
const nsACString &value,
|
||||
HeaderVariety variety);
|
||||
|
||||
// Header cannot be merged: only one value possible
|
||||
|
|
@ -257,7 +273,11 @@ nsHttpHeaderArray::MergeHeader(nsHttpAtom header,
|
|||
if (entry->variety == eVarietyResponseNetOriginalAndResponse) {
|
||||
MOZ_ASSERT(variety == eVarietyResponse);
|
||||
entry->variety = eVarietyResponseNetOriginal;
|
||||
nsresult rv = SetHeader_internal(header, newValue, eVarietyResponse);
|
||||
// Copy entry->headerNameOriginal because in SetHeader_internal we are going
|
||||
// to a new one and a realocation can happen.
|
||||
nsCString headerNameOriginal = entry->headerNameOriginal;
|
||||
nsresult rv = SetHeader_internal(header, headerNameOriginal,
|
||||
newValue, eVarietyResponse);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,6 +130,20 @@ nsHttpRequestHead::Origin(nsACString &aOrigin)
|
|||
aOrigin = mOrigin;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpRequestHead::SetHeader(const nsACString &h, const nsACString &v,
|
||||
bool m /*= false*/)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
|
||||
if (mInVisitHeaders) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return mHeaders.SetHeader(h, v, m,
|
||||
nsHttpHeaderArray::eVarietyRequestOverride);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpRequestHead::SetHeader(nsHttpAtom h, const nsACString &v,
|
||||
bool m /*= false*/)
|
||||
|
|
@ -158,7 +172,7 @@ nsHttpRequestHead::SetHeader(nsHttpAtom h, const nsACString &v, bool m,
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsHttpRequestHead::SetEmptyHeader(nsHttpAtom h)
|
||||
nsHttpRequestHead::SetEmptyHeader(const nsACString &h)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
|
||||
|
|
@ -253,6 +267,7 @@ nsHttpRequestHead::ParseHeaderSet(const char *buffer)
|
|||
{
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
nsHttpAtom hdr;
|
||||
nsAutoCString headerNameOriginal;
|
||||
nsAutoCString val;
|
||||
while (buffer) {
|
||||
const char *eof = strchr(buffer, '\r');
|
||||
|
|
@ -262,9 +277,13 @@ nsHttpRequestHead::ParseHeaderSet(const char *buffer)
|
|||
if (NS_SUCCEEDED(nsHttpHeaderArray::ParseHeaderLine(
|
||||
nsDependentCSubstring(buffer, eof - buffer),
|
||||
&hdr,
|
||||
&headerNameOriginal,
|
||||
&val))) {
|
||||
|
||||
mHeaders.SetHeaderFromNet(hdr, val, false);
|
||||
mHeaders.SetHeaderFromNet(hdr,
|
||||
headerNameOriginal,
|
||||
val,
|
||||
false);
|
||||
}
|
||||
buffer = eof + 1;
|
||||
if (*buffer == '\n') {
|
||||
|
|
|
|||
|
|
@ -57,10 +57,12 @@ public:
|
|||
int32_t port);
|
||||
void Origin(nsACString &aOrigin);
|
||||
|
||||
nsresult SetHeader(const nsACString &h, const nsACString &v,
|
||||
bool m=false);
|
||||
nsresult SetHeader(nsHttpAtom h, const nsACString &v, bool m=false);
|
||||
nsresult SetHeader(nsHttpAtom h, const nsACString &v, bool m,
|
||||
nsHttpHeaderArray::HeaderVariety variety);
|
||||
nsresult SetEmptyHeader(nsHttpAtom h);
|
||||
nsresult SetEmptyHeader(const nsACString &h);
|
||||
nsresult GetHeader(nsHttpAtom h, nsACString &v);
|
||||
|
||||
nsresult ClearHeader(nsHttpAtom h);
|
||||
|
|
|
|||
|
|
@ -136,6 +136,26 @@ nsHttpResponseHead::Immutable()
|
|||
return mCacheControlImmutable;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpResponseHead::SetHeader(const nsACString &hdr,
|
||||
const nsACString &val,
|
||||
bool merge)
|
||||
{
|
||||
ReentrantMonitorAutoEnter monitor(mReentrantMonitor);
|
||||
|
||||
if (mInVisitHeaders) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsHttpAtom atom = nsHttp::ResolveAtom(PromiseFlatCString(hdr).get());
|
||||
if (!atom) {
|
||||
NS_WARNING("failed to resolve atom");
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return SetHeader_locked(atom, hdr, val, merge);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpResponseHead::SetHeader(nsHttpAtom hdr,
|
||||
const nsACString &val,
|
||||
|
|
@ -147,24 +167,25 @@ nsHttpResponseHead::SetHeader(nsHttpAtom hdr,
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return SetHeader_locked(hdr, val, merge);
|
||||
return SetHeader_locked(hdr, EmptyCString(), val, merge);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpResponseHead::SetHeader_locked(nsHttpAtom hdr,
|
||||
nsHttpResponseHead::SetHeader_locked(nsHttpAtom atom,
|
||||
const nsACString &hdr,
|
||||
const nsACString &val,
|
||||
bool merge)
|
||||
{
|
||||
nsresult rv = mHeaders.SetHeader(hdr, val, merge,
|
||||
nsresult rv = mHeaders.SetHeader(atom, hdr, val, merge,
|
||||
nsHttpHeaderArray::eVarietyResponse);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// respond to changes in these headers. we need to reparse the entire
|
||||
// header since the change may have merged in additional values.
|
||||
if (hdr == nsHttp::Cache_Control)
|
||||
ParseCacheControl(mHeaders.PeekHeader(hdr));
|
||||
else if (hdr == nsHttp::Pragma)
|
||||
ParsePragma(mHeaders.PeekHeader(hdr));
|
||||
if (atom == nsHttp::Cache_Control)
|
||||
ParseCacheControl(mHeaders.PeekHeader(atom));
|
||||
else if (atom == nsHttp::Pragma)
|
||||
ParsePragma(mHeaders.PeekHeader(atom));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
@ -316,6 +337,7 @@ nsHttpResponseHead::ParseCachedOriginalHeaders(char *block)
|
|||
|
||||
char *p = block;
|
||||
nsHttpAtom hdr = {0};
|
||||
nsAutoCString headerNameOriginal;
|
||||
nsAutoCString val;
|
||||
nsresult rv;
|
||||
|
||||
|
|
@ -331,12 +353,13 @@ nsHttpResponseHead::ParseCachedOriginalHeaders(char *block)
|
|||
|
||||
*p = 0;
|
||||
if (NS_FAILED(nsHttpHeaderArray::ParseHeaderLine(
|
||||
nsDependentCString(block, p - block), &hdr, &val))) {
|
||||
nsDependentCString(block, p - block), &hdr, &headerNameOriginal, &val))) {
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
rv = mHeaders.SetResponseHeaderFromCache(hdr,
|
||||
headerNameOriginal,
|
||||
val,
|
||||
nsHttpHeaderArray::eVarietyResponseNetOriginal);
|
||||
|
||||
|
|
@ -567,18 +590,21 @@ nsresult
|
|||
nsHttpResponseHead::ParseHeaderLine_locked(const nsACString &line, bool originalFromNetHeaders)
|
||||
{
|
||||
nsHttpAtom hdr = {0};
|
||||
nsAutoCString headerNameOriginal;
|
||||
nsAutoCString val;
|
||||
|
||||
if (NS_FAILED(nsHttpHeaderArray::ParseHeaderLine(line, &hdr, &val))) {
|
||||
if (NS_FAILED(nsHttpHeaderArray::ParseHeaderLine(line, &hdr, &headerNameOriginal, &val))) {
|
||||
return NS_OK;
|
||||
}
|
||||
nsresult rv;
|
||||
if (originalFromNetHeaders) {
|
||||
rv = mHeaders.SetHeaderFromNet(hdr,
|
||||
headerNameOriginal,
|
||||
val,
|
||||
true);
|
||||
} else {
|
||||
rv = mHeaders.SetResponseHeaderFromCache(hdr,
|
||||
headerNameOriginal,
|
||||
val,
|
||||
nsHttpHeaderArray::eVarietyResponse);
|
||||
}
|
||||
|
|
@ -856,7 +882,8 @@ nsHttpResponseHead::UpdateHeaders(nsHttpResponseHead *aOther)
|
|||
uint32_t i, count = aOther->mHeaders.Count();
|
||||
for (i=0; i<count; ++i) {
|
||||
nsHttpAtom header;
|
||||
const char *val = aOther->mHeaders.PeekHeaderAt(i, header);
|
||||
nsAutoCString headerNameOriginal;
|
||||
const char *val = aOther->mHeaders.PeekHeaderAt(i, header, headerNameOriginal);
|
||||
|
||||
if (!val) {
|
||||
continue;
|
||||
|
|
@ -890,7 +917,8 @@ nsHttpResponseHead::UpdateHeaders(nsHttpResponseHead *aOther)
|
|||
LOG(("new response header [%s: %s]\n", header.get(), val));
|
||||
|
||||
// overwrite the current header value with the new value...
|
||||
SetHeader_locked(header, nsDependentCString(val));
|
||||
SetHeader_locked(header, headerNameOriginal,
|
||||
nsDependentCString(val));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ public:
|
|||
*/
|
||||
int64_t TotalEntitySize();
|
||||
|
||||
nsresult SetHeader(const nsACString &h, const nsACString &v,
|
||||
bool m=false);
|
||||
nsresult SetHeader(nsHttpAtom h, const nsACString &v, bool m=false);
|
||||
nsresult GetHeader(nsHttpAtom h, nsACString &v);
|
||||
void ClearHeader(nsHttpAtom h);
|
||||
|
|
@ -137,8 +139,8 @@ public:
|
|||
bool HasContentType();
|
||||
bool HasContentCharset();
|
||||
private:
|
||||
nsresult SetHeader_locked(nsHttpAtom h, const nsACString &v,
|
||||
bool m=false);
|
||||
nsresult SetHeader_locked(nsHttpAtom atom, const nsACString &h,
|
||||
const nsACString &v, bool m=false);
|
||||
void AssignDefaultStatusText();
|
||||
void ParseVersion(const char *);
|
||||
void ParseCacheControl(const char *);
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ function cacheCheck2(status, entry)
|
|||
do_check_eq(entry.dataSize, 0);
|
||||
try {
|
||||
do_check_neq(entry.getMetaDataElement("response-head"), null);
|
||||
do_check_true(entry.getMetaDataElement("response-head").match('Etag: testetag'));
|
||||
do_check_true(entry.getMetaDataElement("response-head").match('etag: testetag'));
|
||||
}
|
||||
catch (ex) {
|
||||
do_throw("Missing response head");
|
||||
|
|
|
|||
|
|
@ -114,11 +114,11 @@ function checkResponse(request, data, context) {
|
|||
var locationHeaderFound = 0;
|
||||
request.visitResponseHeaders({
|
||||
visitHeader: function visit(aName, aValue) {
|
||||
if (aName == "Link") {
|
||||
if (aName == "link") {
|
||||
linkHeaderFound++;
|
||||
do_check_eq(aValue, "value1, value2");
|
||||
}
|
||||
if (aName == "Location") {
|
||||
if (aName == "location") {
|
||||
locationHeaderFound++;
|
||||
do_check_eq(aValue, "loc");
|
||||
}
|
||||
|
|
@ -132,7 +132,7 @@ function checkResponse(request, data, context) {
|
|||
var locationOrgHeaderFound = 0;
|
||||
request.visitOriginalResponseHeaders({
|
||||
visitHeader: function visitOrg(aName, aValue) {
|
||||
if (aName == "Link") {
|
||||
if (aName == "link") {
|
||||
if (linkOrgHeaderFound == 0) {
|
||||
do_check_eq(aValue, "");
|
||||
} else if (linkOrgHeaderFound == 1 ) {
|
||||
|
|
@ -142,7 +142,7 @@ function checkResponse(request, data, context) {
|
|||
}
|
||||
linkOrgHeaderFound++;
|
||||
}
|
||||
if (aName == "Location") {
|
||||
if (aName == "location") {
|
||||
locationOrgHeaderFound++;
|
||||
do_check_eq(aValue, "loc");
|
||||
}
|
||||
|
|
@ -160,10 +160,10 @@ function checkResponse(request, data, context) {
|
|||
var locationHeaderFound2 = 0;
|
||||
request.visitResponseHeaders({
|
||||
visitHeader: function visit(aName, aValue) {
|
||||
if (aName == "Link") {
|
||||
if (aName == "link") {
|
||||
linkHeaderFound2 = true;
|
||||
}
|
||||
if (aName == "Location") {
|
||||
if (aName == "location") {
|
||||
locationHeaderFound2 = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -176,7 +176,7 @@ function checkResponse(request, data, context) {
|
|||
var locationOrgHeaderFound2 = 0;
|
||||
request.visitOriginalResponseHeaders({
|
||||
visitHeader: function visitOrg(aName, aValue) {
|
||||
if (aName == "Link") {
|
||||
if (aName == "link") {
|
||||
if (linkOrgHeaderFound2 == 0) {
|
||||
do_check_eq(aValue, "");
|
||||
} else if (linkOrgHeaderFound2 == 1 ) {
|
||||
|
|
@ -186,7 +186,7 @@ function checkResponse(request, data, context) {
|
|||
}
|
||||
linkOrgHeaderFound2++;
|
||||
}
|
||||
if (aName == "Location") {
|
||||
if (aName == "location") {
|
||||
locationOrgHeaderFound2++;
|
||||
do_check_eq(aValue, "loc");
|
||||
}
|
||||
|
|
@ -199,7 +199,7 @@ function checkResponse(request, data, context) {
|
|||
|
||||
if (dbg) { print("============== Test GetResponseHeader"); }
|
||||
var linkOrgHeaderFound3 = 0;
|
||||
request.getOriginalResponseHeader("Link",{
|
||||
request.getOriginalResponseHeader("link",{
|
||||
visitHeader: function visitOrg(aName, aValue) {
|
||||
if (linkOrgHeaderFound3 == 0) {
|
||||
do_check_eq(aValue, "");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue