nsresult nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect(nsIChannelEventSink *sink, nsIChannel *oldChannel, nsIChannel *newChannel, PRUint32 flags) { LOG(("nsAsyncRedirectVerifyHelper::DelegateOnChannelRedirect() " "sink=%p expectedCBs=%u mResult=%x", sink, mExpectedCallbacks, mResult)); ++mExpectedCallbacks; if (IsOldChannelCanceled()) { LOG((" old channel has been canceled, cancel the redirect by " "emulating OnRedirectVerifyCallback...")); (void) OnRedirectVerifyCallback(NS_BINDING_ABORTED); return NS_BINDING_ABORTED; } nsresult rv = sink->AsyncOnChannelRedirect(oldChannel, newChannel, flags, this); LOG((" result=%x expectedCBs=%u", rv, mExpectedCallbacks)); // If the sink returns failure from this call the redirect is vetoed. We // emulate a callback from the sink in this case in order to perform all // the necessary logic. if (NS_FAILED(rv)) { LOG((" emulating OnRedirectVerifyCallback...")); (void) OnRedirectVerifyCallback(rv); } return rv; // Return the actual status since our caller may need it }
void HttpChannelChild::Redirect1Begin(const PRUint32& newChannelId, const IPC::URI& newURI, const PRUint32& redirectFlags, const nsHttpResponseHead& responseHead) { nsresult rv; nsCOMPtr<nsIIOService> ioService; rv = gHttpHandler->GetIOService(getter_AddRefs(ioService)); if (NS_FAILED(rv)) { // Veto redirect. nsHttpChannel decides to cancel or continue. OnRedirectVerifyCallback(rv); return; } nsCOMPtr<nsIURI> uri(newURI); nsCOMPtr<nsIChannel> newChannel; rv = ioService->NewChannelFromURI(uri, getter_AddRefs(newChannel)); if (NS_FAILED(rv)) { // Veto redirect. nsHttpChannel decides to cancel or continue. OnRedirectVerifyCallback(rv); return; } // We won't get OnStartRequest, set cookies here. mResponseHead = new nsHttpResponseHead(responseHead); SetCookie(mResponseHead->PeekHeader(nsHttp::Set_Cookie)); bool rewriteToGET = ShouldRewriteRedirectToGET(mResponseHead->Status(), mRequestHead.Method()); rv = SetupReplacementChannel(uri, newChannel, !rewriteToGET, false); if (NS_FAILED(rv)) { // Veto redirect. nsHttpChannel decides to cancel or continue. OnRedirectVerifyCallback(rv); return; } mRedirectChannelChild = do_QueryInterface(newChannel); if (mRedirectChannelChild) { mRedirectChannelChild->ConnectParent(newChannelId); } else { NS_ERROR("Redirecting to a protocol that doesn't support universal protocol redirect"); } rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags); if (NS_FAILED(rv)) OnRedirectVerifyCallback(rv); }
// This is called when the channel is redirected. NS_IMETHODIMP FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags, nsIAsyncVerifyRedirectCallback *aCallback) { NS_PRECONDITION(aNewChannel, "Redirect without a channel?"); nsresult rv; // Section 4.2, Step 4.6-4.7, enforcing a redirect count is done by Necko. // The pref used is "network.http.redirection-limit" which is set to 20 by // default. // // Step 4.8. We only unset this for spec compatibility. Any actions we take // on mRequest here do not affect what the channel does. mRequest->UnsetSameOriginDataURL(); // // Requests that require preflight are not permitted to redirect. // Fetch spec section 4.2 "HTTP Fetch", step 4.9 just uses the manual // redirect flag to decide whether to execute step 4.10 or not. We do not // represent it in our implementation. // The only thing we do is to check if the request requires a preflight (part // of step 4.9), in which case we abort. This part cannot be done by // nsCORSListenerProxy since it does not have access to mRequest. // which case. Step 4.10.3 is handled by OnRedirectVerifyCallback(), and all // the other steps are handled by nsCORSListenerProxy. if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) { rv = DoesNotRequirePreflight(aNewChannel); if (NS_FAILED(rv)) { NS_WARNING("FetchDriver::OnChannelRedirect: " "DoesNotRequirePreflight returned failure"); return rv; } } mRedirectCallback = aCallback; mOldRedirectChannel = aOldChannel; mNewRedirectChannel = aNewChannel; nsCOMPtr<nsIChannelEventSink> outer = do_GetInterface(mNotificationCallbacks); if (outer) { // The callee is supposed to call OnRedirectVerifyCallback() on success, // and nobody has to call it on failure, so we can just return after this // block. rv = outer->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, this); if (NS_FAILED(rv)) { aOldChannel->Cancel(rv); mRedirectCallback = nullptr; mOldRedirectChannel = nullptr; mNewRedirectChannel = nullptr; } return rv; } (void) OnRedirectVerifyCallback(NS_OK); return NS_OK; }
NS_IMETHODIMP nsIncrementalDownload::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel, uint32_t flags, nsIAsyncVerifyRedirectCallback *cb) { // In response to a redirect, we need to propagate the Range header. See bug // 311595. Any failure code returned from this function aborts the redirect. nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(oldChannel); NS_ENSURE_STATE(http); nsCOMPtr<nsIHttpChannel> newHttpChannel = do_QueryInterface(newChannel); NS_ENSURE_STATE(newHttpChannel); NS_NAMED_LITERAL_CSTRING(rangeHdr, "Range"); nsresult rv = ClearRequestHeader(newHttpChannel); if (NS_FAILED(rv)) return rv; // If we didn't have a Range header, then we must be doing a full download. nsAutoCString rangeVal; http->GetRequestHeader(rangeHdr, rangeVal); if (!rangeVal.IsEmpty()) { rv = newHttpChannel->SetRequestHeader(rangeHdr, rangeVal, false); NS_ENSURE_SUCCESS(rv, rv); } // A redirection changes the validator mPartialValidator.Truncate(); if (mCacheBust) { newHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Cache-Control"), NS_LITERAL_CSTRING("no-cache"), false); newHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Pragma"), NS_LITERAL_CSTRING("no-cache"), false); } // Prepare to receive callback mRedirectCallback = cb; mNewRedirectChannel = newChannel; // Give the observer a chance to see this redirect notification. nsCOMPtr<nsIChannelEventSink> sink = do_GetInterface(mObserver); if (sink) { rv = sink->AsyncOnChannelRedirect(oldChannel, newChannel, flags, this); if (NS_FAILED(rv)) { mRedirectCallback = nullptr; mNewRedirectChannel = nullptr; } return rv; } (void) OnRedirectVerifyCallback(NS_OK); return NS_OK; }
NS_IMETHODIMP nsCrossSiteListenerProxy::AsyncOnChannelRedirect(nsIChannel *aOldChannel, nsIChannel *aNewChannel, PRUint32 aFlags, nsIAsyncVerifyRedirectCallback *cb) { nsresult rv; if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) { rv = CheckRequestApproved(aOldChannel, PR_TRUE); if (NS_FAILED(rv)) { if (nsXMLHttpRequest::sAccessControlCache) { nsCOMPtr<nsIURI> oldURI; NS_GetFinalChannelURI(aOldChannel, getter_AddRefs(oldURI)); if (oldURI) { nsXMLHttpRequest::sAccessControlCache-> RemoveEntries(oldURI, mRequestingPrincipal); } } aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI); return NS_ERROR_DOM_BAD_URI; } } // Prepare to receive callback mRedirectCallback = cb; mOldRedirectChannel = aOldChannel; mNewRedirectChannel = aNewChannel; nsCOMPtr<nsIChannelEventSink> outer = do_GetInterface(mOuterNotificationCallbacks); if (outer) { rv = outer->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, this); if (NS_FAILED(rv)) { aOldChannel->Cancel(rv); // is this necessary...? mRedirectCallback = nsnull; mOldRedirectChannel = nsnull; mNewRedirectChannel = nsnull; } return rv; } (void) OnRedirectVerifyCallback(NS_OK); return NS_OK; }
void HttpChannelChild::Redirect1Begin(PHttpChannelChild* newChannel, const IPC::URI& newURI, const PRUint32& redirectFlags, const nsHttpResponseHead& responseHead) { HttpChannelChild* newHttpChannelChild = static_cast<HttpChannelChild*>(newChannel); nsCOMPtr<nsIURI> uri(newURI); nsresult rv = newHttpChannelChild->HttpBaseChannel::Init(uri, mCaps, mConnectionInfo->ProxyInfo()); if (NS_FAILED(rv)) { // Cancel the channel and veto the redirect. Cancel(rv); SendRedirect2Result(rv, mRedirectChannelChild->mRequestHeaders); return; } // We won't get OnStartRequest, set cookies here. mResponseHead = new nsHttpResponseHead(responseHead); SetCookie(mResponseHead->PeekHeader(nsHttp::Set_Cookie)); PRBool preserveMethod = (mResponseHead->Status() == 307); rv = SetupReplacementChannel(uri, newHttpChannelChild, preserveMethod); if (NS_FAILED(rv)) { // Cancel the channel and veto the redirect. Cancel(rv); SendRedirect2Result(rv, mRedirectChannelChild->mRequestHeaders); return; } mRedirectChannelChild = newHttpChannelChild; rv = gHttpHandler->AsyncOnChannelRedirect(this, newHttpChannelChild, redirectFlags); if (NS_FAILED(rv)) OnRedirectVerifyCallback(rv); }
// This is called when the channel is redirected. NS_IMETHODIMP FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags, nsIAsyncVerifyRedirectCallback *aCallback) { NS_PRECONDITION(aNewChannel, "Redirect without a channel?"); nsresult rv; // HTTP Fetch step 5, "redirect status", step 1 if (NS_WARN_IF(mRequest->GetRedirectMode() == RequestRedirect::Error)) { aOldChannel->Cancel(NS_BINDING_FAILED); return NS_BINDING_FAILED; } // HTTP Fetch step 5, "redirect status", steps 2 through 6 are automatically // handled by necko before calling AsyncOnChannelRedirect() with the new // nsIChannel. // HTTP Fetch step 5, "redirect status", steps 7 and 8 enforcing a redirect // count are done by Necko. The pref used is "network.http.redirection-limit" // which is set to 20 by default. // HTTP Fetch Step 9, "redirect status". We only unset this for spec // compatibility. Any actions we take on mRequest here do not affect what the //channel does. mRequest->UnsetSameOriginDataURL(); // HTTP Fetch step 5, "redirect status", step 10 requires us to halt the // redirect, but successfully return an opaqueredirect Response to the // initiating Fetch. if (mRequest->GetRedirectMode() == RequestRedirect::Manual) { // Ideally we would simply not cancel the old channel and allow it to // be processed as normal. Unfortunately this is quite fragile and // other redirect handlers can easily break it for certain use cases. // // For example, nsCORSListenerProxy cancels vetoed redirect channels. // The HTTP cache will also error on vetoed redirects when the // redirect has been previously cached. // // Therefore simulate the completion of the channel to produce the // opaqueredirect Response and then cancel the original channel. This // will result in OnStartRequest() getting called twice, but the second // time will be with an error response (from the Cancel) which will // be ignored. mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_OPAQUEREDIRECT); unused << OnStartRequest(aOldChannel, nullptr); unused << OnStopRequest(aOldChannel, nullptr, NS_OK); aOldChannel->Cancel(NS_BINDING_FAILED); return NS_BINDING_FAILED; } // The following steps are from HTTP Fetch step 5, "redirect status", step 11 // which requires the RequestRedirect to be "follow". MOZ_ASSERT(mRequest->GetRedirectMode() == RequestRedirect::Follow); // HTTP Fetch step 5, "redirect status", steps 11.1 and 11.2 block redirecting // to a URL with credentials in CORS mode. This is implemented in // nsCORSListenerProxy. mRedirectCallback = aCallback; mOldRedirectChannel = aOldChannel; mNewRedirectChannel = aNewChannel; nsCOMPtr<nsIChannelEventSink> outer = do_GetInterface(mNotificationCallbacks); if (outer) { // The callee is supposed to call OnRedirectVerifyCallback() on success, // and nobody has to call it on failure, so we can just return after this // block. rv = outer->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, this); if (NS_FAILED(rv)) { aOldChannel->Cancel(rv); mRedirectCallback = nullptr; mOldRedirectChannel = nullptr; mNewRedirectChannel = nullptr; } return rv; } (void) OnRedirectVerifyCallback(NS_OK); return NS_OK; }