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
}
Пример #2
0
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);
}
Пример #3
0
// 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;
}
Пример #6
0
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);
}
Пример #7
0
// 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;
}