Bug 1321528: Part 1 - Support suspending requests when processing response headers. r=honzab a=jcristau
MozReview-Commit-ID: 8ZtbvqLDksO
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -1891,17 +1891,16 @@ nsHttpChannel::ProcessAltService()
mUsername, mPrivateBrowsing, callbacks, proxyInfo,
mCaps & NS_HTTP_DISALLOW_SPDY,
originAttributes);
}
nsresult
nsHttpChannel::ProcessResponse()
{
- nsresult rv;
uint32_t httpStatus = mResponseHead->Status();
LOG(("nsHttpChannel::ProcessResponse [this=%p httpStatus=%u]\n",
this, httpStatus));
// do some telemetry
if (gHttpHandler->IsTelemetryEnabled()) {
// Gather data on whether the transaction and page (if this is
@@ -1984,27 +1983,58 @@ nsHttpChannel::ProcessResponse()
// Only allow 407 (authentication required) to continue
if (httpStatus != 407)
return ProcessFailedProxyConnect(httpStatus);
// If proxy CONNECT response needs to complete, wait to process connection
// for Strict-Transport-Security.
} else {
// Given a successful connection, process any STS or PKP data that's
// relevant.
- rv = ProcessSecurityHeaders();
+ DebugOnly<nsresult> rv = ProcessSecurityHeaders();
MOZ_ASSERT(NS_SUCCEEDED(rv), "ProcessSTSHeader failed, continuing load.");
}
MOZ_ASSERT(!mCachedContentIsValid);
ProcessSSLInformation();
// notify "http-on-examine-response" observers
gHttpHandler->OnExamineResponse(this);
+ return ContinueProcessResponse1();
+}
+
+void
+nsHttpChannel::AsyncContinueProcessResponse()
+{
+ nsresult rv;
+ rv = ContinueProcessResponse1();
+ if (NS_FAILED(rv)) {
+ // A synchronous failure here would normally be passed as the return
+ // value from OnStartRequest, which would in turn cancel the request.
+ // If we're continuing asynchronously, we need to cancel the request
+ // ourselves.
+ Unused << Cancel(rv);
+ }
+}
+
+nsresult
+nsHttpChannel::ContinueProcessResponse1()
+{
+ NS_PRECONDITION(!mCallOnResume, "How did that happen?");
+ nsresult rv;
+
+ if (mSuspendCount) {
+ LOG(("Waiting until resume to finish processing response [this=%p]\n", this));
+ mCallOnResume = &nsHttpChannel::AsyncContinueProcessResponse;
+ return NS_OK;
+ }
+
+ uint32_t httpStatus = mResponseHead->Status();
+
// Cookies and Alt-Service should not be handled on proxy failure either.
// This would be consolidated with ProcessSecurityHeaders but it should
// happen after OnExamineResponse.
if (!mTransaction->ProxyConnectFailed() && (httpStatus != 407)) {
nsAutoCString cookie;
if (NS_SUCCEEDED(mResponseHead->GetHeader(nsHttp::Set_Cookie, cookie))) {
SetCookie(cookie.get());
}
@@ -2033,32 +2063,32 @@ nsHttpChannel::ProcessResponse()
LOG((" continuation state has been reset"));
}
if (mAPIRedirectToURI && !mCanceled) {
MOZ_ASSERT(!mOnStartRequestCalled);
nsCOMPtr<nsIURI> redirectTo;
mAPIRedirectToURI.swap(redirectTo);
- PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse1);
+ PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse2);
rv = StartRedirectChannelToURI(redirectTo, nsIChannelEventSink::REDIRECT_TEMPORARY);
if (NS_SUCCEEDED(rv)) {
return NS_OK;
}
- PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse1);
- }
-
- // Hack: ContinueProcessResponse1 uses NS_OK to detect successful
+ PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse2);
+ }
+
+ // Hack: ContinueProcessResponse2 uses NS_OK to detect successful
// redirects, so we distinguish this codepath (a non-redirect that's
// processing normally) by passing in a bogus error code.
- return ContinueProcessResponse1(NS_BINDING_FAILED);
+ return ContinueProcessResponse2(NS_BINDING_FAILED);
}
nsresult
-nsHttpChannel::ContinueProcessResponse1(nsresult rv)
+nsHttpChannel::ContinueProcessResponse2(nsresult rv)
{
if (NS_SUCCEEDED(rv)) {
// redirectTo() has passed through, we don't want to go on with
// this channel. It will now be canceled by the redirect handling
// code that called this function.
return NS_OK;
}
@@ -2103,29 +2133,29 @@ nsHttpChannel::ContinueProcessResponse1(
case 307:
case 308:
case 303:
#if 0
case 305: // disabled as a security measure (see bug 187996).
#endif
// don't store the response body for redirects
MaybeInvalidateCacheEntryForSubsequentGet();
- PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse2);
+ PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse3);
rv = AsyncProcessRedirection(httpStatus);
if (NS_FAILED(rv)) {
- PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse2);
+ PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse3);
LOG(("AsyncProcessRedirection failed [rv=%x]\n", rv));
// don't cache failed redirect responses.
if (mCacheEntry)
mCacheEntry->AsyncDoom(nullptr);
if (DoNotRender3xxBody(rv)) {
mStatus = rv;
DoNotifyListener();
} else {
- rv = ContinueProcessResponse2(rv);
+ rv = ContinueProcessResponse3(rv);
}
}
break;
case 304:
if (!ShouldBypassProcessNotModified()) {
rv = ProcessNotModified();
if (NS_SUCCEEDED(rv)) {
successfulReval = true;
@@ -2223,33 +2253,33 @@ nsHttpChannel::ContinueProcessResponse1(
}
Telemetry::Accumulate(Telemetry::HTTP_09_INFO, v09Info);
}
}
return rv;
}
nsresult
-nsHttpChannel::ContinueProcessResponse2(nsresult rv)
+nsHttpChannel::ContinueProcessResponse3(nsresult rv)
{
bool doNotRender = DoNotRender3xxBody(rv);
if (rv == NS_ERROR_DOM_BAD_URI && mRedirectURI) {
bool isHTTP = false;
if (NS_FAILED(mRedirectURI->SchemeIs("http", &isHTTP)))
isHTTP = false;
if (!isHTTP && NS_FAILED(mRedirectURI->SchemeIs("https", &isHTTP)))
isHTTP = false;
if (!isHTTP) {
// This was a blocked attempt to redirect and subvert the system by
// redirecting to another protocol (perhaps javascript:)
// In that case we want to throw an error instead of displaying the
// non-redirected response body.
- LOG(("ContinueProcessResponse2 detected rejected Non-HTTP Redirection"));
+ LOG(("ContinueProcessResponse3 detected rejected Non-HTTP Redirection"));
doNotRender = true;
rv = NS_ERROR_CORRUPTED_CONTENT;
}
}
if (doNotRender) {
Cancel(rv);
DoNotifyListener();
@@ -2265,17 +2295,17 @@ nsHttpChannel::ContinueProcessResponse2(
if (mApplicationCacheForWrite) {
// Store response in the offline cache
InitOfflineCacheEntry();
CloseOfflineCacheEntry();
}
return NS_OK;
}
- LOG(("ContinueProcessResponse2 got failure result [rv=%x]\n", rv));
+ LOG(("ContinueProcessResponse3 got failure result [rv=%x]\n", rv));
if (mTransaction->ProxyConnectFailed()) {
return ProcessFailedProxyConnect(mRedirectType);
}
return ProcessNormal();
}
nsresult
nsHttpChannel::ProcessNormal()
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -284,18 +284,20 @@ private:
nsresult ContinueBeginConnectWithResult();
void ContinueBeginConnect();
nsresult Connect();
void SpeculativeConnect();
nsresult SetupTransaction();
void SetupTransactionRequestContext();
nsresult CallOnStartRequest();
nsresult ProcessResponse();
- nsresult ContinueProcessResponse1(nsresult);
+ void AsyncContinueProcessResponse();
+ nsresult ContinueProcessResponse1();
nsresult ContinueProcessResponse2(nsresult);
+ nsresult ContinueProcessResponse3(nsresult);
nsresult ProcessNormal();
nsresult ContinueProcessNormal(nsresult);
void ProcessAltService();
bool ShouldBypassProcessNotModified();
nsresult ProcessNotModified();
nsresult AsyncProcessRedirection(uint32_t httpStatus);
nsresult ContinueProcessRedirection(nsresult);
nsresult ContinueProcessRedirectionAfterFallback(nsresult);