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; }
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; }
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; }
void FetchDriver::BeginResponse(InternalResponse* aResponse) { nsRefPtr<InternalResponse> r = BeginAndGetFilteredResponse(aResponse, nullptr); // Release the ref. }
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; }
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; }