コード例 #1
0
ファイル: FetchDriver.cpp プロジェクト: fatman2021/gecko-dev
nsresult
FetchDriver::ContinueFetch(bool aCORSFlag)
{
  workers::AssertIsOnMainThread();

  nsAutoCString url;
  mRequest->GetURL(url);
  nsCOMPtr<nsIURI> requestURI;
  nsresult rv = NS_NewURI(getter_AddRefs(requestURI), url,
                          nullptr, nullptr);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return FailWithNetworkError();
  }

  // Begin Step 4 of the Fetch algorithm
  // https://fetch.spec.whatwg.org/#fetching

  // FIXME(nsm): Bug 1039846: Add CSP checks

  nsAutoCString scheme;
  rv = requestURI->GetScheme(scheme);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return FailWithNetworkError();
  }

  rv = mPrincipal->CheckMayLoad(requestURI, false /* report */, false /* allowIfInheritsPrincipal */);
  if ((!aCORSFlag && NS_SUCCEEDED(rv)) ||
      (scheme.EqualsLiteral("data") && mRequest->SameOriginDataURL()) ||
      scheme.EqualsLiteral("about")) {
    return BasicFetch();
  }

  if (mRequest->Mode() == RequestMode::Same_origin) {
    return FailWithNetworkError();
  }

  if (mRequest->Mode() == RequestMode::No_cors) {
    mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_OPAQUE);
    return BasicFetch();
  }

  if (!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("https")) {
    return FailWithNetworkError();
  }

  bool corsPreflight = false;
  if (mRequest->Mode() == RequestMode::Cors_with_forced_preflight ||
      (mRequest->UnsafeRequest() && (!mRequest->HasSimpleMethod() || !mRequest->Headers()->HasOnlySimpleHeaders()))) {
    corsPreflight = true;
  }

  mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_CORS);
  return HttpFetch(true /* aCORSFlag */, corsPreflight);
}
コード例 #2
0
ファイル: FetchDriver.cpp プロジェクト: JasonJinCn/gecko-dev
nsresult
FetchDriver::ContinueFetch(bool aCORSFlag)
{
  workers::AssertIsOnMainThread();

  MainFetchOp nextOp = SetTaintingAndGetNextOp(aCORSFlag);

  if (nextOp.mType == NETWORK_ERROR) {
    return FailWithNetworkError();
  }

  if (nextOp.mType == BASIC_FETCH) {
    return BasicFetch();
  }

  if (nextOp.mType == HTTP_FETCH) {
    return HttpFetch(nextOp.mCORSFlag, nextOp.mCORSPreflightFlag);
  }

  MOZ_ASSERT_UNREACHABLE("Unexpected main fetch operation!");
  return FailWithNetworkError();
 }
コード例 #3
0
ファイル: FetchDriver.cpp プロジェクト: artines1/gecko-dev
nsresult
FetchDriver::Fetch(AbortSignal* aSignal, FetchDriverObserver* aObserver)
{
  AssertIsOnMainThread();
#ifdef DEBUG
  MOZ_ASSERT(!mFetchCalled);
  mFetchCalled = true;
#endif

  mObserver = aObserver;

  Telemetry::Accumulate(Telemetry::SERVICE_WORKER_REQUEST_PASSTHROUGH,
                        mRequest->WasCreatedByFetchEvent());

  // FIXME(nsm): Deal with HSTS.

  MOZ_RELEASE_ASSERT(!mRequest->IsSynchronous(),
                     "Synchronous fetch not supported");


  UniquePtr<mozilla::ipc::PrincipalInfo> principalInfo(new mozilla::ipc::PrincipalInfo());
  nsresult rv = PrincipalToPrincipalInfo(mPrincipal, principalInfo.get());
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  mRequest->SetPrincipalInfo(std::move(principalInfo));

  // If the signal is aborted, it's time to inform the observer and terminate
  // the operation.
  if (aSignal) {
    if (aSignal->Aborted()) {
      Abort();
      return NS_OK;
    }

    Follow(aSignal);
  }

  rv = HttpFetch(mRequest->GetPreferredAlternativeDataType());
  if (NS_FAILED(rv)) {
    FailWithNetworkError(rv);
  }

  // Any failure is handled by FailWithNetworkError notifying the aObserver.
  return NS_OK;
}
コード例 #4
0
ファイル: FetchDriver.cpp プロジェクト: ShakoHo/gecko-dev
NS_IMETHODIMP
FetchDriver::OnStopRequest(nsIRequest* aRequest,
                           nsISupports* aContext,
                           nsresult aStatusCode)
{
  workers::AssertIsOnMainThread();
  if (mPipeOutputStream) {
    mPipeOutputStream->Close();
  }

  if (NS_FAILED(aStatusCode)) {
    FailWithNetworkError();
    return aStatusCode;
  }

  ContinueHttpFetchAfterNetworkFetch();
  return NS_OK;
}
コード例 #5
0
ファイル: FetchDriver.cpp プロジェクト: alphan102/gecko-dev
NS_IMETHODIMP
FetchDriver::OnStopRequest(nsIRequest* aRequest,
                           nsISupports* aContext,
                           nsresult aStatusCode)
{
  workers::AssertIsOnMainThread();
  if (NS_FAILED(aStatusCode)) {
    nsCOMPtr<nsIAsyncOutputStream> outputStream = do_QueryInterface(mPipeOutputStream);
    if (outputStream) {
      outputStream->CloseWithStatus(NS_BINDING_FAILED);
    }

    // We proceed as usual here, since we've already created a successful response
    // from OnStartRequest.
  } else {
    MOZ_ASSERT(mResponse);
    MOZ_ASSERT(!mResponse->IsError());

    // From "Main Fetch" step 17: SRI-part3.
    if (mResponse->Type() != ResponseType::Error &&
        !mRequest->GetIntegrity().IsEmpty()) {
      MOZ_ASSERT(mSRIDataVerifier);

      nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);

      nsIConsoleReportCollector* aReporter = nullptr;
      if (mObserver) {
        aReporter = mObserver->GetReporter();
      }

      nsAutoCString sourceUri;
      if (mDocument && mDocument->GetDocumentURI()) {
        mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
      } else if (!mWorkerScript.IsEmpty()) {
        sourceUri.Assign(mWorkerScript);
      }
      nsresult rv = mSRIDataVerifier->Verify(mSRIMetadata, channel, sourceUri,
                                             aReporter);
      if (NS_FAILED(rv)) {
        FailWithNetworkError();
        // Cancel request.
        return rv;
      }
    }

    if (mPipeOutputStream) {
      mPipeOutputStream->Close();
    }
  }

  if (mObserver) {
    if (mResponse->Type() != ResponseType::Error &&
        !mRequest->GetIntegrity().IsEmpty()) {
      //From "Main Fetch" step 23: Process response.
      MOZ_ASSERT(mResponse);
      mObserver->OnResponseAvailable(mResponse);
      #ifdef DEBUG
        mResponseAvailableCalled = true;
      #endif
    }

    mObserver->OnResponseEnd();
    mObserver = nullptr;
  }

  return NS_OK;
}
コード例 #6
0
ファイル: FetchDriver.cpp プロジェクト: alphan102/gecko-dev
NS_IMETHODIMP
FetchDriver::OnStartRequest(nsIRequest* aRequest,
                            nsISupports* aContext)
{
  workers::AssertIsOnMainThread();

  // Note, this can be called multiple times if we are doing an opaqueredirect.
  // In that case we will get a simulated OnStartRequest() and then the real
  // channel will call in with an errored OnStartRequest().

  nsresult rv;
  aRequest->GetStatus(&rv);
  if (NS_FAILED(rv)) {
    FailWithNetworkError();
    return rv;
  }

  // We should only get to the following code once.
  MOZ_ASSERT(!mPipeOutputStream);
  MOZ_ASSERT(mObserver);

  RefPtr<InternalResponse> response;
  nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);

  // On a successful redirect we perform the following substeps of HTTP Fetch,
  // step 5, "redirect status", step 11.

  bool foundOpaqueRedirect = false;

  int64_t contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
  rv = channel->GetContentLength(&contentLength);
  MOZ_ASSERT_IF(NS_FAILED(rv), contentLength == InternalResponse::UNKNOWN_BODY_SIZE);

  if (httpChannel) {
    uint32_t responseStatus;
    httpChannel->GetResponseStatus(&responseStatus);

    if (mozilla::net::nsHttpChannel::IsRedirectStatus(responseStatus)) {
      if (mRequest->GetRedirectMode() == RequestRedirect::Error) {
        FailWithNetworkError();
        return NS_BINDING_FAILED;
      }
      if (mRequest->GetRedirectMode() == RequestRedirect::Manual) {
        foundOpaqueRedirect = true;
      }
    }

    nsAutoCString statusText;
    httpChannel->GetResponseStatusText(statusText);

    response = new InternalResponse(responseStatus, statusText);

    RefPtr<FillResponseHeaders> visitor = new FillResponseHeaders(response);
    rv = httpChannel->VisitResponseHeaders(visitor);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      NS_WARNING("Failed to visit all headers.");
    }

    // If Content-Encoding or Transfer-Encoding headers are set, then the actual
    // Content-Length (which refer to the decoded data) is obscured behind the encodings.
    ErrorResult result;
    if (response->Headers()->Has(NS_LITERAL_CSTRING("content-encoding"), result) ||
        response->Headers()->Has(NS_LITERAL_CSTRING("transfer-encoding"), result)) {
      NS_WARNING("Cannot know response Content-Length due to presence of Content-Encoding "
                 "or Transfer-Encoding headers.");
      contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
    }
    MOZ_ASSERT(!result.Failed());
  } else {
    response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));

    ErrorResult result;
    nsAutoCString contentType;
    rv = channel->GetContentType(contentType);
    if (NS_SUCCEEDED(rv) && !contentType.IsEmpty()) {
      nsAutoCString contentCharset;
      channel->GetContentCharset(contentCharset);
      if (NS_SUCCEEDED(rv) && !contentCharset.IsEmpty()) {
        contentType += NS_LITERAL_CSTRING(";charset=") + contentCharset;
      }

      response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"),
                                  contentType,
                                  result);
      MOZ_ASSERT(!result.Failed());
    }

    if (contentLength > 0) {
      nsAutoCString contentLenStr;
      contentLenStr.AppendInt(contentLength);
      response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"),
                                  contentLenStr,
                                  result);
      MOZ_ASSERT(!result.Failed());
    }
  }

  // We open a pipe so that we can immediately set the pipe's read end as the
  // response's body. Setting the segment size to UINT32_MAX means that the
  // pipe has infinite space. The nsIChannel will continue to buffer data in
  // xpcom events even if we block on a fixed size pipe.  It might be possible
  // to suspend the channel and then resume when there is space available, but
  // for now use an infinite pipe to avoid blocking.
  nsCOMPtr<nsIInputStream> pipeInputStream;
  rv = NS_NewPipe(getter_AddRefs(pipeInputStream),
                  getter_AddRefs(mPipeOutputStream),
                  0, /* default segment size */
                  UINT32_MAX /* infinite pipe */,
                  true /* non-blocking input, otherwise you deadlock */,
                  false /* blocking output, since the pipe is 'in'finite */ );
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    // Cancel request.
    return rv;
  }
  response->SetBody(pipeInputStream, contentLength);

  response->InitChannelInfo(channel);

  nsCOMPtr<nsIURI> channelURI;
  rv = channel->GetURI(getter_AddRefs(channelURI));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    // Cancel request.
    return rv;
  }

  nsCOMPtr<nsILoadInfo> loadInfo;
  rv = channel->GetLoadInfo(getter_AddRefs(loadInfo));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    return rv;
  }

  // Propagate any tainting from the channel back to our response here.  This
  // step is not reflected in the spec because the spec is written such that
  // FetchEvent.respondWith() just passes the already-tainted Response back to
  // the outer fetch().  In gecko, however, we serialize the Response through
  // the channel and must regenerate the tainting from the channel in the
  // interception case.
  mRequest->MaybeIncreaseResponseTainting(loadInfo->GetTainting());

  // Resolves fetch() promise which may trigger code running in a worker.  Make
  // sure the Response is fully initialized before calling this.
  mResponse = BeginAndGetFilteredResponse(response, foundOpaqueRedirect);

  // From "Main Fetch" step 17: SRI-part1.
  if (mResponse->Type() != ResponseType::Error &&
      !mRequest->GetIntegrity().IsEmpty() &&
      mSRIMetadata.IsEmpty()) {
    nsIConsoleReportCollector* aReporter = nullptr;
    if (mObserver) {
      aReporter = mObserver->GetReporter();
    }

    nsAutoCString sourceUri;
    if (mDocument && mDocument->GetDocumentURI()) {
      mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
    } else if (!mWorkerScript.IsEmpty()) {
      sourceUri.Assign(mWorkerScript);
    }
    SRICheck::IntegrityMetadata(mRequest->GetIntegrity(), sourceUri,
                                aReporter, &mSRIMetadata);
    mSRIDataVerifier = new SRICheckDataVerifier(mSRIMetadata, sourceUri,
                                                aReporter);

    // Do not retarget off main thread when using SRI API.
    return NS_OK;
  }

  nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    // Cancel request.
    return rv;
  }

  // Try to retarget off main thread.
  if (nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(aRequest)) {
    Unused << NS_WARN_IF(NS_FAILED(rr->RetargetDeliveryTo(sts)));
  }
  return NS_OK;
}
コード例 #7
0
ファイル: FetchDriver.cpp プロジェクト: artines1/gecko-dev
NS_IMETHODIMP
FetchDriver::OnStartRequest(nsIRequest* aRequest,
                            nsISupports* aContext)
{
  AssertIsOnMainThread();

  // Note, this can be called multiple times if we are doing an opaqueredirect.
  // In that case we will get a simulated OnStartRequest() and then the real
  // channel will call in with an errored OnStartRequest().

  if (!mChannel) {
    MOZ_ASSERT(!mObserver);
    return NS_BINDING_ABORTED;
  }

  nsresult rv;
  aRequest->GetStatus(&rv);
  if (NS_FAILED(rv)) {
    FailWithNetworkError(rv);
    return rv;
  }

  // We should only get to the following code once.
  MOZ_ASSERT(!mPipeOutputStream);
  MOZ_ASSERT(mObserver);

  mNeedToObserveOnDataAvailable = mObserver->NeedOnDataAvailable();

  RefPtr<InternalResponse> response;
  nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);

  // On a successful redirect we perform the following substeps of HTTP Fetch,
  // step 5, "redirect status", step 11.

  bool foundOpaqueRedirect = false;

  int64_t contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
  rv = channel->GetContentLength(&contentLength);
  MOZ_ASSERT_IF(NS_FAILED(rv), contentLength == InternalResponse::UNKNOWN_BODY_SIZE);

  if (httpChannel) {
    uint32_t responseStatus;
    rv = httpChannel->GetResponseStatus(&responseStatus);
    MOZ_ASSERT(NS_SUCCEEDED(rv));

    if (mozilla::net::nsHttpChannel::IsRedirectStatus(responseStatus)) {
      if (mRequest->GetRedirectMode() == RequestRedirect::Error) {
        FailWithNetworkError(NS_BINDING_ABORTED);
        return NS_BINDING_FAILED;
      }
      if (mRequest->GetRedirectMode() == RequestRedirect::Manual) {
        foundOpaqueRedirect = true;
      }
    }

    nsAutoCString statusText;
    rv = httpChannel->GetResponseStatusText(statusText);
    MOZ_ASSERT(NS_SUCCEEDED(rv));

    response = new InternalResponse(responseStatus, statusText);

    response->Headers()->FillResponseHeaders(httpChannel);

    // If Content-Encoding or Transfer-Encoding headers are set, then the actual
    // Content-Length (which refer to the decoded data) is obscured behind the encodings.
    ErrorResult result;
    if (response->Headers()->Has(NS_LITERAL_CSTRING("content-encoding"), result) ||
        response->Headers()->Has(NS_LITERAL_CSTRING("transfer-encoding"), result)) {
      // We cannot trust the content-length when content-encoding or
      // transfer-encoding are set.  There are many servers which just
      // get this wrong.
      contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
    }
    MOZ_ASSERT(!result.Failed());
  } else {
    response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));

    ErrorResult result;
    nsAutoCString contentType;
    rv = channel->GetContentType(contentType);
    if (NS_SUCCEEDED(rv) && !contentType.IsEmpty()) {
      nsAutoCString contentCharset;
      channel->GetContentCharset(contentCharset);
      if (NS_SUCCEEDED(rv) && !contentCharset.IsEmpty()) {
        contentType += NS_LITERAL_CSTRING(";charset=") + contentCharset;
      }

      response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"),
                                  contentType,
                                  result);
      MOZ_ASSERT(!result.Failed());
    }

    if (contentLength > 0) {
      nsAutoCString contentLenStr;
      contentLenStr.AppendInt(contentLength);
      response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"),
                                  contentLenStr,
                                  result);
      MOZ_ASSERT(!result.Failed());
    }
  }

  nsCOMPtr<nsICacheInfoChannel> cic = do_QueryInterface(aRequest);
  if (cic && mAltDataListener) {
    // Skip the case that mAltDataListener->Status() equals to FALLBACK, that means
    // the opened channel for alternative data loading is reused for loading the
    // main data.
    if (mAltDataListener->Status() != AlternativeDataStreamListener::FALLBACK) {
      // Verify the cache ID is the same with from alternative data cache.
      // If the cache ID is different, droping the alternative data loading,
      // otherwise setup the response's alternative body and cacheInfoChannel.
      uint64_t cacheEntryId = 0;
      if (NS_SUCCEEDED(cic->GetCacheEntryId(&cacheEntryId)) &&
          cacheEntryId != mAltDataListener->GetAlternativeDataCacheEntryId()) {
        mAltDataListener->Cancel();
      } else {
        // AlternativeDataStreamListener::OnStartRequest had already been called,
        // the alternative data input stream and cacheInfo channel must be created.
        nsCOMPtr<nsICacheInfoChannel> cacheInfo = mAltDataListener->GetCacheInfoChannel();
        nsCOMPtr<nsIInputStream> altInputStream = mAltDataListener->GetAlternativeInputStream();
        MOZ_ASSERT(altInputStream && cacheInfo);
        response->SetAlternativeBody(altInputStream);
        nsMainThreadPtrHandle<nsICacheInfoChannel> handle(
          new nsMainThreadPtrHolder<nsICacheInfoChannel>("nsICacheInfoChannel",
                                                         cacheInfo,
                                                         false));
        response->SetCacheInfoChannel(handle);
      }
    } else if (!mAltDataListener->GetAlternativeDataType().IsEmpty()) {
      // If the status is FALLBACK and the mAltDataListener::mAlternativeDataType
      // is not empty, that means the data need to be saved into cache, setup the
      // response's nsICacheInfoChannel for caching the data after loading.
      nsMainThreadPtrHandle<nsICacheInfoChannel> handle(
        new nsMainThreadPtrHolder<nsICacheInfoChannel>("nsICacheInfoChannel",
                                                       cic,
                                                       false));
      response->SetCacheInfoChannel(handle);
    }
  }

  // We open a pipe so that we can immediately set the pipe's read end as the
  // response's body. Setting the segment size to UINT32_MAX means that the
  // pipe has infinite space. The nsIChannel will continue to buffer data in
  // xpcom events even if we block on a fixed size pipe.  It might be possible
  // to suspend the channel and then resume when there is space available, but
  // for now use an infinite pipe to avoid blocking.
  nsCOMPtr<nsIInputStream> pipeInputStream;
  rv = NS_NewPipe(getter_AddRefs(pipeInputStream),
                  getter_AddRefs(mPipeOutputStream),
                  0, /* default segment size */
                  UINT32_MAX /* infinite pipe */,
                  true /* non-blocking input, otherwise you deadlock */,
                  false /* blocking output, since the pipe is 'in'finite */ );
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError(rv);
    // Cancel request.
    return rv;
  }
  response->SetBody(pipeInputStream, contentLength);

  response->InitChannelInfo(channel);

  nsCOMPtr<nsIURI> channelURI;
  rv = channel->GetURI(getter_AddRefs(channelURI));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError(rv);
    // Cancel request.
    return rv;
  }

  nsCOMPtr<nsILoadInfo> loadInfo;
  rv = channel->GetLoadInfo(getter_AddRefs(loadInfo));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError(rv);
    return rv;
  }

  // Propagate any tainting from the channel back to our response here.  This
  // step is not reflected in the spec because the spec is written such that
  // FetchEvent.respondWith() just passes the already-tainted Response back to
  // the outer fetch().  In gecko, however, we serialize the Response through
  // the channel and must regenerate the tainting from the channel in the
  // interception case.
  mRequest->MaybeIncreaseResponseTainting(loadInfo->GetTainting());

  // Resolves fetch() promise which may trigger code running in a worker.  Make
  // sure the Response is fully initialized before calling this.
  mResponse = BeginAndGetFilteredResponse(response, foundOpaqueRedirect);
  if (NS_WARN_IF(!mResponse)) {
    // Fail to generate a paddingInfo for opaque response.
    MOZ_DIAGNOSTIC_ASSERT(mResponse->Type() == ResponseType::Opaque);
    FailWithNetworkError(NS_ERROR_UNEXPECTED);
    return rv;
  }

  // From "Main Fetch" step 19: SRI-part1.
  if (ShouldCheckSRI(mRequest, mResponse) && mSRIMetadata.IsEmpty()) {
    nsIConsoleReportCollector* reporter = nullptr;
    if (mObserver) {
      reporter = mObserver->GetReporter();
    }

    nsAutoCString sourceUri;
    if (mDocument && mDocument->GetDocumentURI()) {
      mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
    } else if (!mWorkerScript.IsEmpty()) {
      sourceUri.Assign(mWorkerScript);
    }
    SRICheck::IntegrityMetadata(mRequest->GetIntegrity(), sourceUri,
                                reporter, &mSRIMetadata);
    mSRIDataVerifier = new SRICheckDataVerifier(mSRIMetadata, sourceUri,
                                                reporter);

    // Do not retarget off main thread when using SRI API.
    return NS_OK;
  }

  nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError(rv);
    // Cancel request.
    return rv;
  }

  // Try to retarget off main thread.
  if (nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(aRequest)) {
    Unused << NS_WARN_IF(NS_FAILED(rr->RetargetDeliveryTo(sts)));
  }
  return NS_OK;
}
コード例 #8
0
ファイル: FetchDriver.cpp プロジェクト: artines1/gecko-dev
NS_IMETHODIMP
FetchDriver::OnStopRequest(nsIRequest* aRequest,
                           nsISupports* aContext,
                           nsresult aStatusCode)
{
  AssertIsOnMainThread();

  MOZ_DIAGNOSTIC_ASSERT(!mOnStopRequestCalled);
  mOnStopRequestCalled = true;

  // main data loading is going to finish, breaking the reference cycle.
  RefPtr<AlternativeDataStreamListener> altDataListener = mAltDataListener.forget();

  // We need to check mObserver, which is nulled by FailWithNetworkError(),
  // because in the case of "error" redirect mode, aStatusCode may be NS_OK but
  // mResponse will definitely be null so we must not take the else branch.
  if (NS_FAILED(aStatusCode) || !mObserver) {
    nsCOMPtr<nsIAsyncOutputStream> outputStream = do_QueryInterface(mPipeOutputStream);
    if (outputStream) {
      outputStream->CloseWithStatus(NS_BINDING_FAILED);
    }
    if (altDataListener) {
      altDataListener->Cancel();
    }

    // We proceed as usual here, since we've already created a successful response
    // from OnStartRequest.
  } else {
    MOZ_ASSERT(mResponse);
    MOZ_ASSERT(!mResponse->IsError());

    // From "Main Fetch" step 19: SRI-part3.
    if (ShouldCheckSRI(mRequest, mResponse)) {
      MOZ_ASSERT(mSRIDataVerifier);

      nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);

      nsIConsoleReportCollector* reporter = nullptr;
      if (mObserver) {
        reporter = mObserver->GetReporter();
      }

      nsAutoCString sourceUri;
      if (mDocument && mDocument->GetDocumentURI()) {
        mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
      } else if (!mWorkerScript.IsEmpty()) {
        sourceUri.Assign(mWorkerScript);
      }
      nsresult rv = mSRIDataVerifier->Verify(mSRIMetadata, channel, sourceUri,
                                             reporter);
      if (NS_FAILED(rv)) {
        if (altDataListener) {
          altDataListener->Cancel();
        }
        FailWithNetworkError(rv);
        // Cancel request.
        return rv;
      }
    }

    if (mPipeOutputStream) {
      mPipeOutputStream->Close();
    }
  }

  return FinishOnStopRequest(altDataListener);
}
コード例 #9
0
ファイル: FetchDriver.cpp プロジェクト: JasonJinCn/gecko-dev
NS_IMETHODIMP
FetchDriver::OnStartRequest(nsIRequest* aRequest,
                            nsISupports* aContext)
{
  workers::AssertIsOnMainThread();

  // Note, this can be called multiple times if we are doing an opaqueredirect.
  // In that case we will get a simulated OnStartRequest() and then the real
  // channel will call in with an errored OnStartRequest().

  nsresult rv;
  aRequest->GetStatus(&rv);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    return rv;
  }

  // We should only get to the following code once.
  MOZ_ASSERT(!mPipeOutputStream);
  MOZ_ASSERT(mObserver);

  nsRefPtr<InternalResponse> response;
  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
  if (httpChannel) {
    uint32_t responseStatus;
    httpChannel->GetResponseStatus(&responseStatus);

    nsAutoCString statusText;
    httpChannel->GetResponseStatusText(statusText);

    response = new InternalResponse(responseStatus, statusText);

    nsRefPtr<FillResponseHeaders> visitor = new FillResponseHeaders(response);
    rv = httpChannel->VisitResponseHeaders(visitor);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      NS_WARNING("Failed to visit all headers.");
    }
  } else {
    nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aRequest);
    // If it is not an http channel, it has to be a jar one.
    MOZ_ASSERT(jarChannel);

    // We simulate the http protocol for jar/app requests
    uint32_t responseStatus = 200;
    nsAutoCString statusText;
    response = new InternalResponse(responseStatus, NS_LITERAL_CSTRING("OK"));
    ErrorResult result;
    nsAutoCString contentType;
    jarChannel->GetContentType(contentType);
    response->Headers()->Append(NS_LITERAL_CSTRING("content-type"),
                                contentType,
                                result);
    MOZ_ASSERT(!result.Failed());
  }

  // We open a pipe so that we can immediately set the pipe's read end as the
  // response's body. Setting the segment size to UINT32_MAX means that the
  // pipe has infinite space. The nsIChannel will continue to buffer data in
  // xpcom events even if we block on a fixed size pipe.  It might be possible
  // to suspend the channel and then resume when there is space available, but
  // for now use an infinite pipe to avoid blocking.
  nsCOMPtr<nsIInputStream> pipeInputStream;
  rv = NS_NewPipe(getter_AddRefs(pipeInputStream),
                  getter_AddRefs(mPipeOutputStream),
                  0, /* default segment size */
                  UINT32_MAX /* infinite pipe */,
                  true /* non-blocking input, otherwise you deadlock */,
                  false /* blocking output, since the pipe is 'in'finite */ );
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    // Cancel request.
    return rv;
  }
  response->SetBody(pipeInputStream);

  nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
  response->InitChannelInfo(channel);

  nsCOMPtr<nsIURI> channelURI;
  rv = channel->GetURI(getter_AddRefs(channelURI));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    // Cancel request.
    return rv;
  }

  // Resolves fetch() promise which may trigger code running in a worker.  Make
  // sure the Response is fully initialized before calling this.
  mResponse = BeginAndGetFilteredResponse(response, channelURI);

  nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    // Cancel request.
    return rv;
  }

  // Try to retarget off main thread.
  if (nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(aRequest)) {
    NS_WARN_IF(NS_FAILED(rr->RetargetDeliveryTo(sts)));
  }
  return NS_OK;
}
コード例 #10
0
ファイル: FetchDriver.cpp プロジェクト: JasonJinCn/gecko-dev
// This function implements the "HTTP Fetch" algorithm from the Fetch spec.
// Functionality is often split between here, the CORS listener proxy and the
// Necko HTTP implementation.
nsresult
FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthenticationFlag)
{
  // Step 1. "Let response be null."
  mResponse = nullptr;
  nsresult rv;

  // We need to track the CORS flag through redirects.  Since there is no way
  // for us to go from CORS mode to non-CORS mode, we just need to remember
  // if it has ever been set.
  mCORSFlagEverSet = mCORSFlagEverSet || aCORSFlag;

  nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    return rv;
  }

  nsAutoCString url;
  mRequest->GetURL(url);
  nsCOMPtr<nsIURI> uri;
  rv = NS_NewURI(getter_AddRefs(uri),
                          url,
                          nullptr,
                          nullptr,
                          ios);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    return rv;
  }

  // Step 2 deals with letting ServiceWorkers intercept requests. This is
  // handled by Necko after the channel is opened.
  // FIXME(nsm): Bug 1119026: The channel's skip service worker flag should be
  // set based on the Request's flag.

  // Step 3.1 "If the CORS preflight flag is set and one of these conditions is
  // true..." is handled by the CORS proxy.
  //
  // Step 3.2 "Set request's skip service worker flag." This isn't required
  // since Necko will fall back to the network if the ServiceWorker does not
  // respond with a valid Response.
  //
  // NS_StartCORSPreflight() will automatically kick off the original request
  // if it succeeds, so we need to have everything setup for the original
  // request too.

  // Step 3.3 "Let credentials flag be set if one of
  //  - request's credentials mode is "include"
  //  - request's credentials mode is "same-origin" and either the CORS flag
  //    is unset or response tainting is "opaque"
  // is true, and unset otherwise."
  bool useCredentials = false;
  if (mRequest->GetCredentialsMode() == RequestCredentials::Include ||
      (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin && !aCORSFlag &&
       mRequest->GetResponseTainting() != InternalRequest::RESPONSETAINT_OPAQUE)) {
    useCredentials = true;
  }

  // This is effectivetly the opposite of the use credentials flag in "HTTP
  // network or cache fetch" in the spec and decides whether to transmit
  // cookies and other identifying information. LOAD_ANONYMOUS also prevents
  // new cookies sent by the server from being stored.  This value will
  // propagate across redirects, which is what we want.
  const nsLoadFlags credentialsFlag = useCredentials ? 0 : nsIRequest::LOAD_ANONYMOUS;

  // Set skip serviceworker flag.
  // While the spec also gates on the client being a ServiceWorker, we can't
  // infer that here. Instead we rely on callers to set the flag correctly.
  const nsLoadFlags bypassFlag = mRequest->SkipServiceWorker() ?
                                 nsIChannel::LOAD_BYPASS_SERVICE_WORKER : 0;

  // From here on we create a channel and set its properties with the
  // information from the InternalRequest. This is an implementation detail.
  MOZ_ASSERT(mLoadGroup);
  nsCOMPtr<nsIChannel> chan;
  rv = NS_NewChannel(getter_AddRefs(chan),
                     uri,
                     mPrincipal,
                     nsILoadInfo::SEC_NORMAL,
                     mRequest->ContentPolicyType(),
                     mLoadGroup,
                     nullptr, /* aCallbacks */
                     nsIRequest::LOAD_NORMAL | credentialsFlag | bypassFlag,
                     ios);
  mLoadGroup = nullptr;
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    return rv;
  }

  // Insert ourselves into the notification callbacks chain so we can handle
  // cross-origin redirects.
  chan->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
  chan->SetNotificationCallbacks(this);

  // FIXME(nsm): Bug 1120715.
  // Step 3.4 "If request's cache mode is default and request's header list
  // contains a header named `If-Modified-Since`, `If-None-Match`,
  // `If-Unmodified-Since`, `If-Match`, or `If-Range`, set request's cache mode
  // to no-store."

  // Step 3.5 begins "HTTP network or cache fetch".
  // HTTP network or cache fetch
  // ---------------------------
  // Step 1 "Let HTTPRequest..." The channel is the HTTPRequest.
  nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan);
  if (httpChan) {
    // Copy the method.
    nsAutoCString method;
    mRequest->GetMethod(method);
    rv = httpChan->SetRequestMethod(method);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      FailWithNetworkError();
      return rv;
    }

    // Set the same headers.
    nsAutoTArray<InternalHeaders::Entry, 5> headers;
    mRequest->Headers()->GetEntries(headers);
    for (uint32_t i = 0; i < headers.Length(); ++i) {
      if (headers[i].mValue.IsEmpty()) {
        httpChan->SetEmptyRequestHeader(headers[i].mName);
      } else {
        httpChan->SetRequestHeader(headers[i].mName, headers[i].mValue, false /* merge */);
      }
    }

    // Step 2. Set the referrer.
    nsAutoString referrer;
    mRequest->GetReferrer(referrer);
    if (referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
      rv = nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal,
                                                         mDocument,
                                                         httpChan);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return FailWithNetworkError();
      }
    } else if (referrer.IsEmpty()) {
      rv = httpChan->SetReferrerWithPolicy(nullptr, net::RP_No_Referrer);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return FailWithNetworkError();
      }
    } else {
      // From "Determine request's Referrer" step 3
      // "If request's referrer is a URL, let referrerSource be request's
      // referrer."
      //
      // XXXnsm - We never actually hit this from a fetch() call since both
      // fetch and Request() create a new internal request whose referrer is
      // always set to about:client. Should we just crash here instead until
      // someone tries to use FetchDriver for non-fetch() APIs?
      nsCOMPtr<nsIURI> referrerURI;
      rv = NS_NewURI(getter_AddRefs(referrerURI), referrer, nullptr, nullptr);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return FailWithNetworkError();
      }

      rv =
        httpChan->SetReferrerWithPolicy(referrerURI,
                                        mDocument ? mDocument->GetReferrerPolicy() :
                                                    net::RP_Default);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return FailWithNetworkError();
      }
    }

    // Step 3 "If HTTPRequest's force Origin header flag is set..."
    if (mRequest->ForceOriginHeader()) {
      nsAutoString origin;
      rv = nsContentUtils::GetUTFOrigin(mPrincipal, origin);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return FailWithNetworkError();
      }
      httpChan->SetRequestHeader(NS_LITERAL_CSTRING("origin"),
                                 NS_ConvertUTF16toUTF8(origin),
                                 false /* merge */);
    }
    // Bug 1120722 - Authorization will be handled later.
    // Auth may require prompting, we don't support it yet.
    // The next patch in this same bug prevents this from aborting the request.
    // Credentials checks for CORS are handled by nsCORSListenerProxy,

    nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(httpChan);

    // Conversion between enumerations is safe due to static asserts in
    // dom/workers/ServiceWorkerManager.cpp
    internalChan->SetCorsMode(static_cast<uint32_t>(mRequest->Mode()));
    internalChan->SetRedirectMode(static_cast<uint32_t>(mRequest->GetRedirectMode()));
  }

  // Step 5. Proxy authentication will be handled by Necko.
  // FIXME(nsm): Bug 1120715.
  // Step 7-10. "If request's cache mode is neither no-store nor reload..."

  // Continue setting up 'HTTPRequest'. Content-Type and body data.
  nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(chan);
  if (uploadChan) {
    nsAutoCString contentType;
    ErrorResult result;
    mRequest->Headers()->Get(NS_LITERAL_CSTRING("content-type"), contentType, result);
    // This is an error because the Request constructor explicitly extracts and
    // sets a content-type per spec.
    if (result.Failed()) {
      return FailWithNetworkError();
    }

    nsCOMPtr<nsIInputStream> bodyStream;
    mRequest->GetBody(getter_AddRefs(bodyStream));
    if (bodyStream) {
      nsAutoCString method;
      mRequest->GetMethod(method);
      rv = uploadChan->ExplicitSetUploadStream(bodyStream, contentType, -1, method, false /* aStreamHasHeaders */);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return FailWithNetworkError();
      }
    }
  }

  nsCOMPtr<nsIStreamListener> listener = this;

  MOZ_ASSERT_IF(aCORSFlag, mRequest->Mode() == RequestMode::Cors);

  // Only use nsCORSListenerProxy if we are in CORS mode.  Otherwise it
  // will overwrite the CorsMode flag unconditionally to "cors" or
  // "cors-with-forced-preflight".
  if (mRequest->Mode() == RequestMode::Cors) {
    // Passing false for the credentials flag to nsCORSListenerProxy is semantically
    // the same as the "same-origin" RequestCredentials value.  We implement further
    // blocking of credentials for "omit" by setting LOAD_ANONYMOUS manually above.
    bool corsCredentials =
      mRequest->GetCredentialsMode() == RequestCredentials::Include;

    // Set up a CORS proxy that will handle the various requirements of the CORS
    // protocol. It handles the preflight cache and CORS response headers.
    // If the request is allowed, it will start our original request
    // and our observer will be notified. On failure, our observer is notified
    // directly.
    nsRefPtr<nsCORSListenerProxy> corsListener =
      new nsCORSListenerProxy(this, mPrincipal, corsCredentials);
    rv = corsListener->Init(chan, DataURIHandling::Allow);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return FailWithNetworkError();
    }
    listener = corsListener.forget();
  }

  // If preflight is required, start a "CORS preflight fetch"
  // https://fetch.spec.whatwg.org/#cors-preflight-fetch-0. All the
  // implementation is handled by NS_StartCORSPreflight, we just set up the
  // unsafeHeaders so they can be verified against the response's
  // "Access-Control-Allow-Headers" header.
  if (aCORSPreflightFlag) {
    MOZ_ASSERT(mRequest->Mode() != RequestMode::No_cors,
               "FetchDriver::ContinueFetch() should ensure that the request is not no-cors");
    MOZ_ASSERT(httpChan, "CORS preflight can only be used with HTTP channels");
    nsAutoTArray<nsCString, 5> unsafeHeaders;
    mRequest->Headers()->GetUnsafeHeaders(unsafeHeaders);

    nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(httpChan);
    rv = internalChan->SetCorsPreflightParameters(unsafeHeaders, useCredentials, mPrincipal);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return FailWithNetworkError();
    }
  }

  rv = chan->AsyncOpen(listener, nullptr);

  if (NS_WARN_IF(NS_FAILED(rv))) {
    return FailWithNetworkError();
  }

  // Step 4 onwards of "HTTP Fetch" is handled internally by Necko.
  return NS_OK;
}
コード例 #11
0
ファイル: FetchDriver.cpp プロジェクト: JasonJinCn/gecko-dev
nsresult
FetchDriver::BasicFetch()
{
  nsAutoCString url;
  mRequest->GetURL(url);
  nsCOMPtr<nsIURI> uri;
  nsresult rv = NS_NewURI(getter_AddRefs(uri),
                 url,
                 nullptr,
                 nullptr);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    return rv;
  }

  nsAutoCString scheme;
  rv = uri->GetScheme(scheme);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    return rv;
  }

  if (scheme.LowerCaseEqualsLiteral("about")) {
    if (url.EqualsLiteral("about:blank")) {
      nsRefPtr<InternalResponse> response =
        new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
      ErrorResult result;
      response->Headers()->Append(NS_LITERAL_CSTRING("content-type"),
                                  NS_LITERAL_CSTRING("text/html;charset=utf-8"),
                                  result);
      MOZ_ASSERT(!result.Failed());
      nsCOMPtr<nsIInputStream> body;
      rv = NS_NewCStringInputStream(getter_AddRefs(body), EmptyCString());
      if (NS_WARN_IF(NS_FAILED(rv))) {
        FailWithNetworkError();
        return rv;
      }

      response->SetBody(body);
      BeginResponse(response);
      return SucceedWithResponse();
    }
    return FailWithNetworkError();
  }

  if (scheme.LowerCaseEqualsLiteral("blob")) {
    nsRefPtr<BlobImpl> blobImpl;
    rv = NS_GetBlobForBlobURI(uri, getter_AddRefs(blobImpl));
    BlobImpl* blob = static_cast<BlobImpl*>(blobImpl.get());
    if (NS_WARN_IF(NS_FAILED(rv))) {
      FailWithNetworkError();
      return rv;
    }

    nsRefPtr<InternalResponse> response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
    ErrorResult result;
    uint64_t size = blob->GetSize(result);
    if (NS_WARN_IF(result.Failed())) {
      FailWithNetworkError();
      return result.StealNSResult();
    }

    nsAutoString sizeStr;
    sizeStr.AppendInt(size);
    response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"), NS_ConvertUTF16toUTF8(sizeStr), result);
    if (NS_WARN_IF(result.Failed())) {
      FailWithNetworkError();
      return result.StealNSResult();
    }

    nsAutoString type;
    blob->GetType(type);
    response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), NS_ConvertUTF16toUTF8(type), result);
    if (NS_WARN_IF(result.Failed())) {
      FailWithNetworkError();
      return result.StealNSResult();
    }

    nsCOMPtr<nsIInputStream> stream;
    blob->GetInternalStream(getter_AddRefs(stream), result);
    if (NS_WARN_IF(result.Failed())) {
      FailWithNetworkError();
      return result.StealNSResult();
    }

    response->SetBody(stream);
    BeginResponse(response);
    return SucceedWithResponse();
  }

  if (scheme.LowerCaseEqualsLiteral("data")) {
    nsAutoCString method;
    mRequest->GetMethod(method);
    if (method.LowerCaseEqualsASCII("get")) {
      nsresult rv;
      nsCOMPtr<nsIProtocolHandler> dataHandler =
        do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "data", &rv);

      if (NS_WARN_IF(NS_FAILED(rv))) {
        return FailWithNetworkError();
      }

      nsCOMPtr<nsIChannel> channel;
      rv = dataHandler->NewChannel(uri, getter_AddRefs(channel));
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return FailWithNetworkError();
      }

      nsCOMPtr<nsIInputStream> stream;
      rv = channel->Open(getter_AddRefs(stream));
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return FailWithNetworkError();
      }

      // nsDataChannel will parse the data URI when it is Open()ed and set the
      // correct content type and charset.
      nsAutoCString contentType;
      if (NS_SUCCEEDED(channel->GetContentType(contentType))) {
        nsAutoCString charset;
        if (NS_SUCCEEDED(channel->GetContentCharset(charset)) && !charset.IsEmpty()) {
          contentType.AppendLiteral(";charset=");
          contentType.Append(charset);
        }
      } else {
        NS_WARNING("Could not get content type from data channel");
      }

      nsRefPtr<InternalResponse> response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
      ErrorResult result;
      response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), contentType, result);
      if (NS_WARN_IF(result.Failed())) {
        FailWithNetworkError();
        return result.StealNSResult();
      }

      response->SetBody(stream);
      BeginResponse(response);
      return SucceedWithResponse();
    }

    return FailWithNetworkError();
  }

  if (scheme.LowerCaseEqualsLiteral("http") ||
      scheme.LowerCaseEqualsLiteral("https") ||
      scheme.LowerCaseEqualsLiteral("app")) {
    return HttpFetch();
  }

  return FailWithNetworkError();
}
コード例 #12
0
NS_IMETHODIMP
FetchDriver::OnStartRequest(nsIRequest* aRequest,
                            nsISupports* aContext)
{
  workers::AssertIsOnMainThread();

  // Note, this can be called multiple times if we are doing an opaqueredirect.
  // In that case we will get a simulated OnStartRequest() and then the real
  // channel will call in with an errored OnStartRequest().

  nsresult rv;
  aRequest->GetStatus(&rv);
  if (NS_FAILED(rv)) {
    FailWithNetworkError();
    return rv;
  }

  // We should only get to the following code once.
  MOZ_ASSERT(!mPipeOutputStream);
  MOZ_ASSERT(mObserver);

  RefPtr<InternalResponse> response;
  nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);

  // On a successful redirect we perform the following substeps of HTTP Fetch,
  // step 5, "redirect status", step 11.

  // Step 11.5 "Append locationURL to request's url list." so that when we set the
  // Response's URL from the Request's URL in Main Fetch, step 15, we get the
  // final value. Note, we still use a single URL value instead of a list.
  // Because of that we only need to do this after the request finishes.
  nsCOMPtr<nsIURI> newURI;
  rv = NS_GetFinalChannelURI(channel, getter_AddRefs(newURI));
  if (NS_FAILED(rv)) {
    FailWithNetworkError();
    return rv;
  }
  nsAutoCString newUrl;
  newURI->GetSpec(newUrl);
  mRequest->SetURL(newUrl);

  bool foundOpaqueRedirect = false;

  if (httpChannel) {
    uint32_t responseStatus;
    httpChannel->GetResponseStatus(&responseStatus);

    if (mozilla::net::nsHttpChannel::IsRedirectStatus(responseStatus)) {
      if (mRequest->GetRedirectMode() == RequestRedirect::Error) {
        FailWithNetworkError();
        return NS_BINDING_FAILED;
      }
      if (mRequest->GetRedirectMode() == RequestRedirect::Manual) {
        foundOpaqueRedirect = true;
      }
    }

    nsAutoCString statusText;
    httpChannel->GetResponseStatusText(statusText);

    response = new InternalResponse(responseStatus, statusText);

    RefPtr<FillResponseHeaders> visitor = new FillResponseHeaders(response);
    rv = httpChannel->VisitResponseHeaders(visitor);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      NS_WARNING("Failed to visit all headers.");
    }
  } else {
    response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));

    ErrorResult result;
    nsAutoCString contentType;
    rv = channel->GetContentType(contentType);
    if (NS_SUCCEEDED(rv) && !contentType.IsEmpty()) {
      nsAutoCString contentCharset;
      channel->GetContentCharset(contentCharset);
      if (NS_SUCCEEDED(rv) && !contentCharset.IsEmpty()) {
        contentType += NS_LITERAL_CSTRING(";charset=") + contentCharset;
      }

      response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"),
                                  contentType,
                                  result);
      MOZ_ASSERT(!result.Failed());
    }

    int64_t contentLength;
    rv = channel->GetContentLength(&contentLength);
    if (NS_SUCCEEDED(rv) && contentLength) {
      nsAutoCString contentLenStr;
      contentLenStr.AppendInt(contentLength);
      response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"),
                                  contentLenStr,
                                  result);
      MOZ_ASSERT(!result.Failed());
    }
  }

  // We open a pipe so that we can immediately set the pipe's read end as the
  // response's body. Setting the segment size to UINT32_MAX means that the
  // pipe has infinite space. The nsIChannel will continue to buffer data in
  // xpcom events even if we block on a fixed size pipe.  It might be possible
  // to suspend the channel and then resume when there is space available, but
  // for now use an infinite pipe to avoid blocking.
  nsCOMPtr<nsIInputStream> pipeInputStream;
  rv = NS_NewPipe(getter_AddRefs(pipeInputStream),
                  getter_AddRefs(mPipeOutputStream),
                  0, /* default segment size */
                  UINT32_MAX /* infinite pipe */,
                  true /* non-blocking input, otherwise you deadlock */,
                  false /* blocking output, since the pipe is 'in'finite */ );
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    // Cancel request.
    return rv;
  }
  response->SetBody(pipeInputStream);

  response->InitChannelInfo(channel);

  nsCOMPtr<nsIURI> channelURI;
  rv = channel->GetURI(getter_AddRefs(channelURI));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    // Cancel request.
    return rv;
  }

  nsCOMPtr<nsILoadInfo> loadInfo;
  rv = channel->GetLoadInfo(getter_AddRefs(loadInfo));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    return rv;
  }

  // Propagate any tainting from the channel back to our response here.  This
  // step is not reflected in the spec because the spec is written such that
  // FetchEvent.respondWith() just passes the already-tainted Response back to
  // the outer fetch().  In gecko, however, we serialize the Response through
  // the channel and must regenerate the tainting from the channel in the
  // interception case.
  mRequest->MaybeIncreaseResponseTainting(loadInfo->GetTainting());

  // Resolves fetch() promise which may trigger code running in a worker.  Make
  // sure the Response is fully initialized before calling this.
  mResponse = BeginAndGetFilteredResponse(response, channelURI,
                                          foundOpaqueRedirect);

  nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    // Cancel request.
    return rv;
  }

  // Try to retarget off main thread.
  if (nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(aRequest)) {
    NS_WARN_IF(NS_FAILED(rr->RetargetDeliveryTo(sts)));
  }
  return NS_OK;
}
コード例 #13
0
ファイル: FetchDriver.cpp プロジェクト: ShakoHo/gecko-dev
nsresult
FetchDriver::ContinueFetch(bool aCORSFlag)
{
  workers::AssertIsOnMainThread();

  nsAutoCString url;
  mRequest->GetURL(url);
  nsCOMPtr<nsIURI> requestURI;
  nsresult rv = NS_NewURI(getter_AddRefs(requestURI), url,
                          nullptr, nullptr);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return FailWithNetworkError();
  }

  // CSP/mixed content checks.
  int16_t shouldLoad;
  rv = NS_CheckContentLoadPolicy(mRequest->ContentPolicyType(),
                                 requestURI,
                                 mPrincipal,
                                 mDocument,
                                 // FIXME(nsm): Should MIME be extracted from
                                 // Content-Type header?
                                 EmptyCString(), /* mime guess */
                                 nullptr, /* extra */
                                 &shouldLoad,
                                 nsContentUtils::GetContentPolicy(),
                                 nsContentUtils::GetSecurityManager());
  if (NS_WARN_IF(NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad))) {
    // Disallowed by content policy.
    return FailWithNetworkError();
  }

  // Begin Step 4 of the Fetch algorithm
  // https://fetch.spec.whatwg.org/#fetching

  nsAutoCString scheme;
  rv = requestURI->GetScheme(scheme);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return FailWithNetworkError();
  }

  rv = mPrincipal->CheckMayLoad(requestURI, false /* report */, false /* allowIfInheritsPrincipal */);
  if ((!aCORSFlag && NS_SUCCEEDED(rv)) ||
      (scheme.EqualsLiteral("data") && mRequest->SameOriginDataURL()) ||
      scheme.EqualsLiteral("about")) {
    return BasicFetch();
  }

  if (mRequest->Mode() == RequestMode::Same_origin) {
    return FailWithNetworkError();
  }

  if (mRequest->Mode() == RequestMode::No_cors) {
    mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_OPAQUE);
    return BasicFetch();
  }

  if (!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("https")) {
    return FailWithNetworkError();
  }

  bool corsPreflight = false;
  if (mRequest->Mode() == RequestMode::Cors_with_forced_preflight ||
      (mRequest->UnsafeRequest() && (!mRequest->HasSimpleMethod() || !mRequest->Headers()->HasOnlySimpleHeaders()))) {
    corsPreflight = true;
  }

  mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_CORS);
  return HttpFetch(true /* aCORSFlag */, corsPreflight);
}
コード例 #14
0
ファイル: FetchDriver.cpp プロジェクト: ShakoHo/gecko-dev
// This function implements the "HTTP Fetch" algorithm from the Fetch spec.
// Functionality is often split between here, the CORS listener proxy and the
// Necko HTTP implementation.
nsresult
FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthenticationFlag)
{
  // Step 1. "Let response be null."
  mResponse = nullptr;
  nsresult rv;

  nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    return rv;
  }

  nsAutoCString url;
  mRequest->GetURL(url);
  nsCOMPtr<nsIURI> uri;
  rv = NS_NewURI(getter_AddRefs(uri),
                          url,
                          nullptr,
                          nullptr,
                          ios);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    return rv;
  }

  // Step 2 deals with letting ServiceWorkers intercept requests. This is
  // handled by Necko after the channel is opened.
  // FIXME(nsm): Bug 1119026: The channel's skip service worker flag should be
  // set based on the Request's flag.

  // Step 3.1 "If the CORS preflight flag is set and one of these conditions is
  // true..." is handled by the CORS proxy.
  //
  // Step 3.2 "Set request's skip service worker flag." This isn't required
  // since Necko will fall back to the network if the ServiceWorker does not
  // respond with a valid Response.
  //
  // NS_StartCORSPreflight() will automatically kick off the original request
  // if it succeeds, so we need to have everything setup for the original
  // request too.

  // Step 3.3 "Let credentials flag be set if either request's credentials mode
  // is include, or request's credentials mode is same-origin and the CORS flag
  // is unset, and unset otherwise."
  bool useCredentials = false;
  if (mRequest->GetCredentialsMode() == RequestCredentials::Include ||
      (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin && !aCORSFlag)) {
    useCredentials = true;
  }

  // This is effectivetly the opposite of the use credentials flag in "HTTP
  // network or cache fetch" in the spec and decides whether to transmit
  // cookies and other identifying information. LOAD_ANONYMOUS also prevents
  // new cookies sent by the server from being stored.
  const nsLoadFlags credentialsFlag = useCredentials ? 0 : nsIRequest::LOAD_ANONYMOUS;

  // From here on we create a channel and set its properties with the
  // information from the InternalRequest. This is an implementation detail.
  MOZ_ASSERT(mLoadGroup);
  nsCOMPtr<nsIChannel> chan;
  rv = NS_NewChannel(getter_AddRefs(chan),
                     uri,
                     mPrincipal,
                     nsILoadInfo::SEC_NORMAL,
                     mRequest->ContentPolicyType(),
                     mLoadGroup,
                     nullptr, /* aCallbacks */
                     nsIRequest::LOAD_NORMAL | credentialsFlag,
                     ios);
  mLoadGroup = nullptr;
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    return rv;
  }

  // Insert ourselves into the notification callbacks chain so we can handle
  // cross-origin redirects.
  chan->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
  chan->SetNotificationCallbacks(this);

  // FIXME(nsm): Bug 1120715.
  // Step 3.4 "If request's cache mode is default and request's header list
  // contains a header named `If-Modified-Since`, `If-None-Match`,
  // `If-Unmodified-Since`, `If-Match`, or `If-Range`, set request's cache mode
  // to no-store."

  // Step 3.5 begins "HTTP network or cache fetch".
  // HTTP network or cache fetch
  // ---------------------------
  // Step 1 "Let HTTPRequest..." The channel is the HTTPRequest.
  nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan);
  if (httpChan) {
    // Copy the method.
    nsAutoCString method;
    mRequest->GetMethod(method);
    rv = httpChan->SetRequestMethod(method);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      FailWithNetworkError();
      return rv;
    }

    // Set the same headers.
    nsAutoTArray<InternalHeaders::Entry, 5> headers;
    mRequest->Headers()->GetEntries(headers);
    for (uint32_t i = 0; i < headers.Length(); ++i) {
      httpChan->SetRequestHeader(headers[i].mName, headers[i].mValue, false /* merge */);
    }

    // Step 2. Set the referrer.
    nsAutoString referrer;
    mRequest->GetReferrer(referrer);
    // The referrer should have already been resolved to a URL by the caller.
    MOZ_ASSERT(!referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR));
    if (!referrer.IsEmpty()) {
      nsCOMPtr<nsIURI> refURI;
      rv = NS_NewURI(getter_AddRefs(refURI), referrer, nullptr, nullptr);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return FailWithNetworkError();
      }

      net::ReferrerPolicy referrerPolicy = net::RP_Default;
      if (mDocument) {
        referrerPolicy = mDocument->GetReferrerPolicy();
      }

      rv = httpChan->SetReferrerWithPolicy(refURI, referrerPolicy);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return FailWithNetworkError();
      }
    }

    // Step 3 "If HTTPRequest's force Origin header flag is set..."
    if (mRequest->ForceOriginHeader()) {
      nsAutoString origin;
      rv = nsContentUtils::GetUTFOrigin(mPrincipal, origin);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return FailWithNetworkError();
      }
      httpChan->SetRequestHeader(NS_LITERAL_CSTRING("origin"),
                                 NS_ConvertUTF16toUTF8(origin),
                                 false /* merge */);
    }
    // Bug 1120722 - Authorization will be handled later.
    // Auth may require prompting, we don't support it yet.
    // The next patch in this same bug prevents this from aborting the request.
    // Credentials checks for CORS are handled by nsCORSListenerProxy,
  }

  // Step 5. Proxy authentication will be handled by Necko.
  // FIXME(nsm): Bug 1120715.
  // Step 7-10. "If request's cache mode is neither no-store nor reload..."

  // Continue setting up 'HTTPRequest'. Content-Type and body data.
  nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(chan);
  if (uploadChan) {
    nsAutoCString contentType;
    ErrorResult result;
    mRequest->Headers()->Get(NS_LITERAL_CSTRING("content-type"), contentType, result);
    // This is an error because the Request constructor explicitly extracts and
    // sets a content-type per spec.
    if (result.Failed()) {
      return FailWithNetworkError();
    }

    nsCOMPtr<nsIInputStream> bodyStream;
    mRequest->GetBody(getter_AddRefs(bodyStream));
    if (bodyStream) {
      nsAutoCString method;
      mRequest->GetMethod(method);
      rv = uploadChan->ExplicitSetUploadStream(bodyStream, contentType, -1, method, false /* aStreamHasHeaders */);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return FailWithNetworkError();
      }
    }
  }

  // Set skip serviceworker flag.
  // While the spec also gates on the client being a ServiceWorker, we can't
  // infer that here. Instead we rely on callers to set the flag correctly.
  if (mRequest->SkipServiceWorker()) {
    if (httpChan) {
      nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(httpChan);
      internalChan->ForceNoIntercept();
    }
  }

  nsCOMPtr<nsIStreamListener> listener = this;

  // Unless the cors mode is explicitly no-cors, we set up a cors proxy even in
  // the same-origin case, since the proxy does not enforce cors header checks
  // in the same-origin case.
  if (mRequest->Mode() != RequestMode::No_cors) {
    // Set up a CORS proxy that will handle the various requirements of the CORS
    // protocol. It handles the preflight cache and CORS response headers.
    // If the request is allowed, it will start our original request
    // and our observer will be notified. On failure, our observer is notified
    // directly.
    nsRefPtr<nsCORSListenerProxy> corsListener =
      new nsCORSListenerProxy(this, mPrincipal, useCredentials);
    rv = corsListener->Init(chan, DataURIHandling::Allow);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return FailWithNetworkError();
    }
    listener = corsListener.forget();
  }

  // If preflight is required, start a "CORS preflight fetch"
  // https://fetch.spec.whatwg.org/#cors-preflight-fetch-0. All the
  // implementation is handled by NS_StartCORSPreflight, we just set up the
  // unsafeHeaders so they can be verified against the response's
  // "Access-Control-Allow-Headers" header.
  if (aCORSPreflightFlag) {
    nsCOMPtr<nsIChannel> preflightChannel;
    nsAutoTArray<nsCString, 5> unsafeHeaders;
    mRequest->Headers()->GetUnsafeHeaders(unsafeHeaders);

    rv = NS_StartCORSPreflight(chan, listener, mPrincipal,
                               useCredentials,
                               unsafeHeaders,
                               getter_AddRefs(preflightChannel));
  } else {
    rv = chan->AsyncOpen(listener, nullptr);
  }

  if (NS_WARN_IF(NS_FAILED(rv))) {
    return FailWithNetworkError();
  }

  // Step 4 onwards of "HTTP Fetch" is handled internally by Necko.
  return NS_OK;
}
コード例 #15
0
ファイル: FetchDriver.cpp プロジェクト: ShakoHo/gecko-dev
nsresult
FetchDriver::BasicFetch()
{
  nsAutoCString url;
  mRequest->GetURL(url);
  nsCOMPtr<nsIURI> uri;
  nsresult rv = NS_NewURI(getter_AddRefs(uri),
                 url,
                 nullptr,
                 nullptr);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    return rv;
  }

  nsAutoCString scheme;
  rv = uri->GetScheme(scheme);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    return rv;
  }

  if (scheme.LowerCaseEqualsLiteral("about")) {
    if (url.EqualsLiteral("about:blank")) {
      nsRefPtr<InternalResponse> response =
        new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
      ErrorResult result;
      response->Headers()->Append(NS_LITERAL_CSTRING("content-type"),
                                  NS_LITERAL_CSTRING("text/html;charset=utf-8"),
                                  result);
      MOZ_ASSERT(!result.Failed());
      nsCOMPtr<nsIInputStream> body;
      rv = NS_NewCStringInputStream(getter_AddRefs(body), EmptyCString());
      if (NS_WARN_IF(NS_FAILED(rv))) {
        FailWithNetworkError();
        return rv;
      }

      response->SetBody(body);
      BeginResponse(response);
      return SucceedWithResponse();
    }
    return FailWithNetworkError();
  }

  if (scheme.LowerCaseEqualsLiteral("blob")) {
    nsRefPtr<BlobImpl> blobImpl;
    rv = NS_GetBlobForBlobURI(uri, getter_AddRefs(blobImpl));
    BlobImpl* blob = static_cast<BlobImpl*>(blobImpl.get());
    if (NS_WARN_IF(NS_FAILED(rv))) {
      FailWithNetworkError();
      return rv;
    }

    nsRefPtr<InternalResponse> response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
    ErrorResult result;
    uint64_t size = blob->GetSize(result);
    if (NS_WARN_IF(result.Failed())) {
      FailWithNetworkError();
      return result.StealNSResult();
    }

    nsAutoString sizeStr;
    sizeStr.AppendInt(size);
    response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"), NS_ConvertUTF16toUTF8(sizeStr), result);
    if (NS_WARN_IF(result.Failed())) {
      FailWithNetworkError();
      return result.StealNSResult();
    }

    nsAutoString type;
    blob->GetType(type);
    response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), NS_ConvertUTF16toUTF8(type), result);
    if (NS_WARN_IF(result.Failed())) {
      FailWithNetworkError();
      return result.StealNSResult();
    }

    nsCOMPtr<nsIInputStream> stream;
    blob->GetInternalStream(getter_AddRefs(stream), result);
    if (NS_WARN_IF(result.Failed())) {
      FailWithNetworkError();
      return result.StealNSResult();
    }

    response->SetBody(stream);
    BeginResponse(response);
    return SucceedWithResponse();
  }

  if (scheme.LowerCaseEqualsLiteral("data")) {
    nsAutoCString method;
    mRequest->GetMethod(method);
    if (method.LowerCaseEqualsASCII("get")) {
      // Use nsDataHandler directly so that we can extract the content type.
      // XXX(nsm): Is there a way to acquire the charset without such tight
      // coupling with the DataHandler? nsIProtocolHandler does not provide
      // anything similar.
      nsAutoCString contentType, contentCharset, dataBuffer, hashRef;
      bool isBase64;
      rv = nsDataHandler::ParseURI(url,
                                   contentType,
                                   contentCharset,
                                   isBase64,
                                   dataBuffer,
                                   hashRef);
      if (NS_SUCCEEDED(rv)) {
        ErrorResult result;
        nsRefPtr<InternalResponse> response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
        if (!contentCharset.IsEmpty()) {
          contentType.Append(";charset=");
          contentType.Append(contentCharset);
        }

        response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), contentType, result);
        if (!result.Failed()) {
          nsCOMPtr<nsIInputStream> stream;
          rv = NS_NewCStringInputStream(getter_AddRefs(stream), dataBuffer);
          if (NS_SUCCEEDED(rv)) {
            response->SetBody(stream);
            BeginResponse(response);
            return SucceedWithResponse();
          }
        }
      }
    }

    return FailWithNetworkError();
  }

  if (scheme.LowerCaseEqualsLiteral("http") ||
      scheme.LowerCaseEqualsLiteral("https") ||
      scheme.LowerCaseEqualsLiteral("app")) {
    return HttpFetch();
  }

  return FailWithNetworkError();
}
コード例 #16
0
ファイル: FetchDriver.cpp プロジェクト: fatman2021/gecko-dev
NS_IMETHODIMP
FetchDriver::OnStartRequest(nsIRequest* aRequest,
                            nsISupports* aContext)
{
  workers::AssertIsOnMainThread();
  MOZ_ASSERT(!mPipeOutputStream);
  MOZ_ASSERT(mObserver);
  nsresult rv;
  aRequest->GetStatus(&rv);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    return rv;
  }

  nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest);
  // For now we only support HTTP.
  MOZ_ASSERT(channel);

  aRequest->GetStatus(&rv);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    return rv;
  }

  uint32_t responseStatus;
  channel->GetResponseStatus(&responseStatus);

  nsAutoCString statusText;
  channel->GetResponseStatusText(statusText);

  nsRefPtr<InternalResponse> response = new InternalResponse(responseStatus, statusText);

  nsRefPtr<FillResponseHeaders> visitor = new FillResponseHeaders(response);
  rv = channel->VisitResponseHeaders(visitor);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    NS_WARNING("Failed to visit all headers.");
  }

  mResponse = BeginAndGetFilteredResponse(response);

  // We open a pipe so that we can immediately set the pipe's read end as the
  // response's body. Setting the segment size to UINT32_MAX means that the
  // pipe has infinite space. The nsIChannel will continue to buffer data in
  // xpcom events even if we block on a fixed size pipe.  It might be possible
  // to suspend the channel and then resume when there is space available, but
  // for now use an infinite pipe to avoid blocking.
  nsCOMPtr<nsIInputStream> pipeInputStream;
  rv = NS_NewPipe(getter_AddRefs(pipeInputStream),
                  getter_AddRefs(mPipeOutputStream),
                  0, /* default segment size */
                  UINT32_MAX /* infinite pipe */);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    // Cancel request.
    return rv;
  }

  mResponse->SetBody(pipeInputStream);

  nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    FailWithNetworkError();
    // Cancel request.
    return rv;
  }

  // Try to retarget off main thread.
  nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(aRequest);
  if (rr) {
    rr->RetargetDeliveryTo(sts);
  }
  return NS_OK;
}