diff options
author | Moonchild <mcwerewolf@gmail.com> | 2018-09-27 00:59:51 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-09-27 00:59:51 +0200 |
commit | b92dbaf6a1da5e06e9c0eb818288c0db1b6e45e2 (patch) | |
tree | 61462bdb3c34813d3cbd32c87628854a48392cea | |
parent | 63875408947b0e5551f41e4de1e0ca44dd970c36 (diff) | |
parent | c5c9445e3adf6b65c98f6810551d7c3d64133134 (diff) | |
download | uxp-b92dbaf6a1da5e06e9c0eb818288c0db1b6e45e2.tar.gz |
Merge pull request #791 from g4jc/session_supercookie
Issue #792 - backport mozbug 1334776 - CVE-2017-7797 Header name interning leaks across origins
21 files changed, 238 insertions, 101 deletions
diff --git a/devtools/client/netmonitor/filter-predicates.js b/devtools/client/netmonitor/filter-predicates.js index 9c8e49c622..75ee422aad 100644 --- a/devtools/client/netmonitor/filter-predicates.js +++ b/devtools/client/netmonitor/filter-predicates.js @@ -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"); }); } diff --git a/devtools/client/netmonitor/test/browser_net_copy_headers.js b/devtools/client/netmonitor/test/browser_net_copy_headers.js index 36ce2fb347..bb582c8e13 100644 --- a/devtools/client/netmonitor/test/browser_net_copy_headers.js +++ b/devtools/client/netmonitor/test/browser_net_copy_headers.js @@ -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."); diff --git a/devtools/client/netmonitor/test/browser_net_timing-division.js b/devtools/client/netmonitor/test/browser_net_timing-division.js index 0114ba2355..ff2379dc28 100644 --- a/devtools/client/netmonitor/test/browser_net_timing-division.js +++ b/devtools/client/netmonitor/test/browser_net_timing-division.js @@ -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."); diff --git a/devtools/client/shared/AppCacheUtils.jsm b/devtools/client/shared/AppCacheUtils.jsm index a2beca993c..9fd4d0541e 100644 --- a/devtools/client/shared/AppCacheUtils.jsm +++ b/devtools/client/shared/AppCacheUtils.jsm @@ -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); diff --git a/devtools/client/shared/curl.js b/devtools/client/shared/curl.js index 420fe6aa5c..6d33ad9717 100644 --- a/devtools/client/shared/curl.js +++ b/devtools/client/shared/curl.js @@ -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"); diff --git a/devtools/client/webconsole/net/test/mochitest/browser_net_headers.js b/devtools/client/webconsole/net/test/mochitest/browser_net_headers.js index 4a47074ee1..14cde846c2 100644 --- a/devtools/client/webconsole/net/test/mochitest/browser_net_headers.js +++ b/devtools/client/webconsole/net/test/mochitest/browser_net_headers.js @@ -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; diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_630733_response_redirect_headers.js b/devtools/client/webconsole/test/browser_webconsole_bug_630733_response_redirect_headers.js index 5097499538..da4bdcf122 100644 --- a/devtools/client/webconsole/test/browser_webconsole_bug_630733_response_redirect_headers.js +++ b/devtools/client/webconsole/test/browser_webconsole_bug_630733_response_redirect_headers.js @@ -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; } } diff --git a/devtools/shared/webconsole/test/test_network_get.html b/devtools/shared/webconsole/test/test_network_get.html index 710c9b0d75..c2313be12a 100644 --- a/devtools/shared/webconsole/test/test_network_get.html +++ b/devtools/shared/webconsole/test/test_network_get.html @@ -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); diff --git a/devtools/shared/webconsole/test/test_network_longstring.html b/devtools/shared/webconsole/test/test_network_longstring.html index d551368969..9e6ea7771b 100644 --- a/devtools/shared/webconsole/test/test_network_longstring.html +++ b/devtools/shared/webconsole/test/test_network_longstring.html @@ -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", diff --git a/devtools/shared/webconsole/test/test_network_post.html b/devtools/shared/webconsole/test/test_network_post.html index d96b9b0b7c..a0b8edb648 100644 --- a/devtools/shared/webconsole/test/test_network_post.html +++ b/devtools/shared/webconsole/test/test_network_post.html @@ -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); diff --git a/dom/security/test/cors/test_CrossSiteXHR.html b/dom/security/test/cors/test_CrossSiteXHR.html index b3cda3b871..d9aef5c60c 100644 --- a/dom/security/test/cors/test_CrossSiteXHR.html +++ b/dom/security/test/cors/test_CrossSiteXHR.html @@ -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()); } diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index c4e764d26d..03123ceb08 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -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 diff --git a/netwerk/protocol/http/PHttpChannelParams.h b/netwerk/protocol/http/PHttpChannelParams.h index 4df5c7832e..b04f57306c 100644 --- a/netwerk/protocol/http/PHttpChannelParams.h +++ b/netwerk/protocol/http/PHttpChannelParams.h @@ -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; diff --git a/netwerk/protocol/http/nsHttpHeaderArray.cpp b/netwerk/protocol/http/nsHttpHeaderArray.cpp index 670300dea5..1030bc91ee 100644 --- a/netwerk/protocol/http/nsHttpHeaderArray.cpp +++ b/netwerk/protocol/http/nsHttpHeaderArray.cpp @@ -18,12 +18,37 @@ 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) || (variety == eVarietyRequestOverride), @@ -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(); } diff --git a/netwerk/protocol/http/nsHttpHeaderArray.h b/netwerk/protocol/http/nsHttpHeaderArray.h index 91b91f04aa..3ffdfa8146 100644 --- a/netwerk/protocol/http/nsHttpHeaderArray.h +++ b/netwerk/protocol/http/nsHttpHeaderArray.h @@ -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; } diff --git a/netwerk/protocol/http/nsHttpRequestHead.cpp b/netwerk/protocol/http/nsHttpRequestHead.cpp index 094a794570..b366a8d542 100644 --- a/netwerk/protocol/http/nsHttpRequestHead.cpp +++ b/netwerk/protocol/http/nsHttpRequestHead.cpp @@ -131,6 +131,20 @@ nsHttpRequestHead::Origin(nsACString &aOrigin) } 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') { diff --git a/netwerk/protocol/http/nsHttpRequestHead.h b/netwerk/protocol/http/nsHttpRequestHead.h index 4159680834..b7020d33a0 100644 --- a/netwerk/protocol/http/nsHttpRequestHead.h +++ b/netwerk/protocol/http/nsHttpRequestHead.h @@ -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); diff --git a/netwerk/protocol/http/nsHttpResponseHead.cpp b/netwerk/protocol/http/nsHttpResponseHead.cpp index 6d384c4887..fa6430b823 100644 --- a/netwerk/protocol/http/nsHttpResponseHead.cpp +++ b/netwerk/protocol/http/nsHttpResponseHead.cpp @@ -137,6 +137,26 @@ nsHttpResponseHead::Immutable() } 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, bool merge) @@ -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)); } } diff --git a/netwerk/protocol/http/nsHttpResponseHead.h b/netwerk/protocol/http/nsHttpResponseHead.h index 0a912f4b40..8b51386ea2 100644 --- a/netwerk/protocol/http/nsHttpResponseHead.h +++ b/netwerk/protocol/http/nsHttpResponseHead.h @@ -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 *); diff --git a/netwerk/test/unit/test_bug1064258.js b/netwerk/test/unit/test_bug1064258.js index 3f76837ae9..da982c2d6f 100644 --- a/netwerk/test/unit/test_bug1064258.js +++ b/netwerk/test/unit/test_bug1064258.js @@ -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"); diff --git a/netwerk/test/unit/test_original_sent_received_head.js b/netwerk/test/unit/test_original_sent_received_head.js index c4d02d5d22..d668abf590 100644 --- a/netwerk/test/unit/test_original_sent_received_head.js +++ b/netwerk/test/unit/test_original_sent_received_head.js @@ -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, ""); |