Bug 1945855 - Show error page for 4xx/5xx responses with no content, whether the Content-Length header is missing or lying r=dveditz,necko-reviewers,valentin
Differential Revision: https://phabricator.services.mozilla.com/D240385
This commit is contained in:
@@ -25,6 +25,7 @@ function handleRequest(request, response) {
|
||||
|
||||
const status = parseInt(params.status);
|
||||
const message = params.message;
|
||||
const header = params.header;
|
||||
|
||||
// Set default if missing parameters
|
||||
if (!status || !message) {
|
||||
@@ -33,6 +34,15 @@ function handleRequest(request, response) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (header === "hide") {
|
||||
response.setStatusLine(request.httpVersion, status, message);
|
||||
return;
|
||||
}
|
||||
if (header === "lie") {
|
||||
response.setStatusLine(request.httpVersion, status, message);
|
||||
response.setHeader("Content-Length", "100", false);
|
||||
return;
|
||||
}
|
||||
response.setStatusLine(request.httpVersion, status, message);
|
||||
response.setHeader("Content-Length", "0", false);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@ async function test_blankPage(
|
||||
page,
|
||||
expectedL10nID,
|
||||
responseStatus,
|
||||
responseStatusText
|
||||
responseStatusText,
|
||||
header = "show" // show (zero content-length), hide (no content-length), or lie (non-empty content-length)
|
||||
) {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.http.blank_page_with_error_response.enabled", false]],
|
||||
@@ -27,7 +28,7 @@ async function test_blankPage(
|
||||
let pageLoaded;
|
||||
const uri = `${page}?status=${encodeURIComponent(
|
||||
responseStatus
|
||||
)}&message=${encodeURIComponent(responseStatusText)}`;
|
||||
)}&message=${encodeURIComponent(responseStatusText)}&header=${encodeURIComponent(header)}`;
|
||||
|
||||
// Simulating loading the page
|
||||
await BrowserTestUtils.openNewForegroundTab(
|
||||
@@ -89,6 +90,46 @@ add_task(async function test_blankPage_5xx() {
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_blankPage_withoutHeader_4xx() {
|
||||
await test_blankPage(
|
||||
BLANK_PAGE,
|
||||
"httpErrorPage-title",
|
||||
400,
|
||||
"Bad Request",
|
||||
"hide"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_blankPage_withoutHeader_5xx() {
|
||||
await test_blankPage(
|
||||
BLANK_PAGE,
|
||||
"serverError-title",
|
||||
503,
|
||||
"Service Unavailable",
|
||||
"hide"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_blankPage_lyingHeader_4xx() {
|
||||
await test_blankPage(
|
||||
BLANK_PAGE,
|
||||
"httpErrorPage-title",
|
||||
400,
|
||||
"Bad Request",
|
||||
"lie"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_blankPage_lyingHeader_5xx() {
|
||||
await test_blankPage(
|
||||
BLANK_PAGE,
|
||||
"serverError-title",
|
||||
503,
|
||||
"Service Unavailable",
|
||||
"lie"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_emptyPage_viewSource() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.http.blank_page_with_error_response.enabled", false]],
|
||||
@@ -96,7 +137,7 @@ add_task(async function test_emptyPage_viewSource() {
|
||||
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(
|
||||
gBrowser,
|
||||
`view-source:${BLANK_PAGE}?status=503&message=Service%20Unavailable`,
|
||||
`view-source:${BLANK_PAGE}?status=503&message=Service%20Unavailable&header=show`,
|
||||
true // wait for the load to complete
|
||||
);
|
||||
let browser = tab.linkedBrowser;
|
||||
|
||||
@@ -218,6 +218,7 @@ nsDocumentOpenInfo::OnDataAvailable(nsIRequest* request, nsIInputStream* inStr,
|
||||
// if we have retarged to the end stream listener, then forward the call....
|
||||
// otherwise, don't do anything
|
||||
|
||||
mReceivedData = true;
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if (m_targetStreamListener)
|
||||
@@ -240,10 +241,64 @@ nsDocumentOpenInfo::OnDataFinished(nsresult aStatus) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult nsDocumentOpenInfo::CheckContentLengthDiscrepancy(
|
||||
nsIRequest* request) {
|
||||
// Blank page with content length discrepancy is allowed
|
||||
if (mReceivedData ||
|
||||
mozilla::StaticPrefs::
|
||||
browser_http_blank_page_with_error_response_enabled()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request));
|
||||
if (!httpChannel) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint64_t decodedBodySize;
|
||||
if (NS_FAILED(httpChannel->GetDecodedBodySize(&decodedBodySize)) ||
|
||||
decodedBodySize != 0) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
if (NS_SUCCEEDED(httpChannel->GetURI(getter_AddRefs(uri))) &&
|
||||
uri->SchemeIs("view-source")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t responseCode = 0;
|
||||
if (NS_SUCCEEDED(httpChannel->GetResponseStatus(&responseCode))) {
|
||||
if (responseCode >= 500) {
|
||||
LOG(
|
||||
(" Returning NS_ERROR_NET_ERROR_RESPONSE from "
|
||||
"nsDocumentOpenInfo::CheckContentLengthDiscrepancy due to 5xx "
|
||||
"responses with no content"));
|
||||
return NS_ERROR_NET_ERROR_RESPONSE;
|
||||
}
|
||||
if (responseCode >= 400) {
|
||||
LOG(
|
||||
(" Returning NS_ERROR_NET_EMPTY_RESPONSE from "
|
||||
"nsDocumentOpenInfo::CheckContentLengthDiscrepancy due to 4xx "
|
||||
"responses with no content"));
|
||||
return NS_ERROR_NET_EMPTY_RESPONSE;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsDocumentOpenInfo::OnStopRequest(nsIRequest* request,
|
||||
nsresult aStatus) {
|
||||
LOG(("[0x%p] nsDocumentOpenInfo::OnStopRequest", this));
|
||||
|
||||
// Bug 1945855 - Show error page for 4xx/5xx responses with no content,
|
||||
// whether the Content-Length header is missing or lying
|
||||
nsresult rv = CheckContentLengthDiscrepancy(request);
|
||||
if (NS_FAILED(rv)) {
|
||||
aStatus = rv;
|
||||
}
|
||||
|
||||
if (m_targetStreamListener) {
|
||||
nsCOMPtr<nsIStreamListener> listener(m_targetStreamListener);
|
||||
|
||||
|
||||
@@ -80,6 +80,11 @@ class nsDocumentOpenInfo : public nsIThreadRetargetableStreamListener {
|
||||
*/
|
||||
nsresult Prepare();
|
||||
|
||||
// Check for a page with empty body + missing/any content-length.
|
||||
// Return NS_ERROR_NET_ERROR_RESPONSE for 5xx and NS_ERROR_NET_EMPTY_RESPONSE
|
||||
// for 4xx, respectively. Otherwise, return NS_OK.
|
||||
nsresult CheckContentLengthDiscrepancy(nsIRequest* request);
|
||||
|
||||
// Call this (from OnStartRequest) to attempt to find an nsIStreamListener to
|
||||
// take the data off our hands.
|
||||
nsresult DispatchContent(nsIRequest* request);
|
||||
@@ -213,6 +218,13 @@ class nsDocumentOpenInfo : public nsIThreadRetargetableStreamListener {
|
||||
* docshell
|
||||
*/
|
||||
bool mAllowListenerConversions = true;
|
||||
|
||||
/**
|
||||
* Tracks whether any data was received in OnDataAvailable.
|
||||
* Used to detect cases where the response has no Content-Length
|
||||
* header and an empty body.
|
||||
*/
|
||||
bool mReceivedData = false;
|
||||
};
|
||||
|
||||
#endif /* nsURILoader_h__ */
|
||||
|
||||
Reference in New Issue
Block a user