void RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) { AutoCancel autoCancel(this, mRequestURL); if (!aValue.isObject()) { NS_WARNING("FetchEvent::RespondWith was passed a promise resolved to a non-Object value"); nsCString sourceSpec; uint32_t line = 0; uint32_t column = 0; nsString valueString; ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column, valueString); autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column, NS_LITERAL_CSTRING("InterceptedNonResponseWithURL"), mRequestURL, valueString); return; } RefPtr<Response> response; nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response); if (NS_FAILED(rv)) { nsCString sourceSpec; uint32_t line = 0; uint32_t column = 0; nsString valueString; ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column, valueString); autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column, NS_LITERAL_CSTRING("InterceptedNonResponseWithURL"), mRequestURL, valueString); return; } WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(worker); worker->AssertIsOnWorkerThread(); // Section "HTTP Fetch", step 3.3: // If one of the following conditions is true, return a network error: // * response's type is "error". // * request's mode is not "no-cors" and response's type is "opaque". // * request's redirect mode is not "manual" and response's type is // "opaqueredirect". // * request's redirect mode is not "follow" and response's url list // has more than one item. if (response->Type() == ResponseType::Error) { autoCancel.SetCancelMessage( NS_LITERAL_CSTRING("InterceptedErrorResponseWithURL"), mRequestURL); return; } MOZ_ASSERT_IF(mIsClientRequest, mRequestMode == RequestMode::Same_origin || mRequestMode == RequestMode::Navigate); if (response->Type() == ResponseType::Opaque && mRequestMode != RequestMode::No_cors) { uint32_t mode = static_cast<uint32_t>(mRequestMode); NS_ConvertASCIItoUTF16 modeString(RequestModeValues::strings[mode].value, RequestModeValues::strings[mode].length); autoCancel.SetCancelMessage( NS_LITERAL_CSTRING("BadOpaqueInterceptionRequestModeWithURL"), mRequestURL, modeString); return; } if (mRequestRedirectMode != RequestRedirect::Manual && response->Type() == ResponseType::Opaqueredirect) { autoCancel.SetCancelMessage( NS_LITERAL_CSTRING("BadOpaqueRedirectInterceptionWithURL"), mRequestURL); return; } if (mRequestRedirectMode != RequestRedirect::Follow && response->Redirected()) { autoCancel.SetCancelMessage( NS_LITERAL_CSTRING("BadRedirectModeInterceptionWithURL"), mRequestURL); return; } if (NS_WARN_IF(response->BodyUsed())) { autoCancel.SetCancelMessage( NS_LITERAL_CSTRING("InterceptedUsedResponseWithURL"), mRequestURL); return; } RefPtr<InternalResponse> ir = response->GetInternalResponse(); if (NS_WARN_IF(!ir)) { return; } // When an opaque response is encountered, we need the original channel's principal // to reflect the final URL. Non-opaque responses are either same-origin or CORS-enabled // cross-origin responses, which are treated as same-origin by consumers. nsCString responseURL; if (response->Type() == ResponseType::Opaque) { responseURL = ir->GetUnfilteredURL(); if (NS_WARN_IF(responseURL.IsEmpty())) { return; } } nsAutoPtr<RespondWithClosure> closure(new RespondWithClosure(mInterceptedChannel, mRegistration, ir, worker->GetChannelInfo(), mScriptSpec, responseURL, mRequestURL, mRespondWithScriptSpec, mRespondWithLineNumber, mRespondWithColumnNumber)); nsCOMPtr<nsIInputStream> body; ir->GetUnfilteredBody(getter_AddRefs(body)); // Errors and redirects may not have a body. if (body) { response->SetBodyUsed(); nsCOMPtr<nsIOutputStream> responseBody; rv = mInterceptedChannel->GetResponseBody(getter_AddRefs(responseBody)); if (NS_WARN_IF(NS_FAILED(rv))) { return; } const uint32_t kCopySegmentSize = 4096; // Depending on how the Response passed to .respondWith() was created, we may // get a non-buffered input stream. In addition, in some configurations the // destination channel's output stream can be unbuffered. We wrap the output // stream side here so that NS_AsyncCopy() works. Wrapping the output side // provides the most consistent operation since there are fewer stream types // we are writing to. The input stream can be a wide variety of concrete // objects which may or many not play well with NS_InputStreamIsBuffered(). if (!NS_OutputStreamIsBuffered(responseBody)) { nsCOMPtr<nsIOutputStream> buffered; rv = NS_NewBufferedOutputStream(getter_AddRefs(buffered), responseBody, kCopySegmentSize); if (NS_WARN_IF(NS_FAILED(rv))) { return; } responseBody = buffered; } nsCOMPtr<nsIEventTarget> stsThread = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv); if (NS_WARN_IF(!stsThread)) { return; } // XXXnsm, Fix for Bug 1141332 means that if we decide to make this // streaming at some point, we'll need a different solution to that bug. rv = NS_AsyncCopy(body, responseBody, stsThread, NS_ASYNCCOPY_VIA_WRITESEGMENTS, kCopySegmentSize, RespondWithCopyComplete, closure.forget()); if (NS_WARN_IF(NS_FAILED(rv))) { return; } } else { RespondWithCopyComplete(closure.forget(), NS_OK); } MOZ_ASSERT(!closure); autoCancel.Reset(); mRequestWasHandled = true; }
void RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) { AutoCancel autoCancel(this); if (!aValue.isObject()) { NS_WARNING("FetchEvent::RespondWith was passed a promise resolved to a non-Object value"); return; } RefPtr<Response> response; nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response); if (NS_FAILED(rv)) { return; } WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(worker); worker->AssertIsOnWorkerThread(); // Allow opaque response interception to be disabled until we can ensure the // security implications are not a complete disaster. if (response->Type() == ResponseType::Opaque && !worker->OpaqueInterceptionEnabled()) { autoCancel.SetCancelStatus(NS_ERROR_OPAQUE_INTERCEPTION_DISABLED); return; } // Section "HTTP Fetch", step 2.2: // If one of the following conditions is true, return a network error: // * response's type is "error". // * request's mode is not "no-cors" and response's type is "opaque". // * request is not a navigation request and response's type is // "opaqueredirect". if (response->Type() == ResponseType::Error) { autoCancel.SetCancelStatus(NS_ERROR_INTERCEPTED_ERROR_RESPONSE); return; } MOZ_ASSERT_IF(mIsClientRequest, mRequestMode == RequestMode::Same_origin); if (response->Type() == ResponseType::Opaque && mRequestMode != RequestMode::No_cors) { autoCancel.SetCancelStatus(NS_ERROR_BAD_OPAQUE_INTERCEPTION_REQUEST_MODE); return; } if (!mIsNavigationRequest && response->Type() == ResponseType::Opaqueredirect) { autoCancel.SetCancelStatus(NS_ERROR_BAD_OPAQUE_REDIRECT_INTERCEPTION); return; } if (NS_WARN_IF(response->BodyUsed())) { autoCancel.SetCancelStatus(NS_ERROR_INTERCEPTED_USED_RESPONSE); return; } RefPtr<InternalResponse> ir = response->GetInternalResponse(); if (NS_WARN_IF(!ir)) { return; } nsAutoPtr<RespondWithClosure> closure(new RespondWithClosure(mInterceptedChannel, ir, worker->GetChannelInfo(), mScriptSpec)); nsCOMPtr<nsIInputStream> body; ir->GetUnfilteredBody(getter_AddRefs(body)); // Errors and redirects may not have a body. if (body) { response->SetBodyUsed(); nsCOMPtr<nsIOutputStream> responseBody; rv = mInterceptedChannel->GetResponseBody(getter_AddRefs(responseBody)); if (NS_WARN_IF(NS_FAILED(rv))) { return; } nsCOMPtr<nsIEventTarget> stsThread = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv); if (NS_WARN_IF(!stsThread)) { return; } // XXXnsm, Fix for Bug 1141332 means that if we decide to make this // streaming at some point, we'll need a different solution to that bug. rv = NS_AsyncCopy(body, responseBody, stsThread, NS_ASYNCCOPY_VIA_READSEGMENTS, 4096, RespondWithCopyComplete, closure.forget()); if (NS_WARN_IF(NS_FAILED(rv))) { return; } } else { RespondWithCopyComplete(closure.forget(), NS_OK); } MOZ_ASSERT(!closure); autoCancel.Reset(); mRequestWasHandled = true; }
void RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) { AutoCancel autoCancel(this); if (!aValue.isObject()) { NS_WARNING("FetchEvent::RespondWith was passed a promise resolved to a non-Object value"); return; } nsRefPtr<Response> response; nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response); if (NS_FAILED(rv)) { return; } WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(worker); worker->AssertIsOnWorkerThread(); // Allow opaque response interception to be disabled until we can ensure the // security implications are not a complete disaster. if (response->Type() == ResponseType::Opaque && !worker->OpaqueInterceptionEnabled()) { return; } // Section 4.2, step 2.2 "If either response's type is "opaque" and request's // mode is not "no-cors" or response's type is error, return a network error." if (((response->Type() == ResponseType::Opaque) && (mRequestMode != RequestMode::No_cors)) || response->Type() == ResponseType::Error) { return; } if (NS_WARN_IF(response->BodyUsed())) { return; } nsRefPtr<InternalResponse> ir = response->GetInternalResponse(); if (NS_WARN_IF(!ir)) { return; } nsAutoPtr<RespondWithClosure> closure( new RespondWithClosure(mInterceptedChannel, ir, worker->GetChannelInfo())); nsCOMPtr<nsIInputStream> body; ir->GetInternalBody(getter_AddRefs(body)); // Errors and redirects may not have a body. if (body) { response->SetBodyUsed(); nsCOMPtr<nsIOutputStream> responseBody; rv = mInterceptedChannel->GetResponseBody(getter_AddRefs(responseBody)); if (NS_WARN_IF(NS_FAILED(rv))) { return; } nsCOMPtr<nsIEventTarget> stsThread = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv); if (NS_WARN_IF(!stsThread)) { return; } // XXXnsm, Fix for Bug 1141332 means that if we decide to make this // streaming at some point, we'll need a different solution to that bug. rv = NS_AsyncCopy(body, responseBody, stsThread, NS_ASYNCCOPY_VIA_READSEGMENTS, 4096, RespondWithCopyComplete, closure.forget()); if (NS_WARN_IF(NS_FAILED(rv))) { return; } } else { RespondWithCopyComplete(closure.forget(), NS_OK); } MOZ_ASSERT(!closure); autoCancel.Reset(); }
/*static*/ already_AddRefed<Response> Response::Constructor(const GlobalObject& aGlobal, const Optional<Nullable<fetch::ResponseBodyInit>>& aBody, const ResponseInit& aInit, ErrorResult& aRv) { nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); if (aInit.mStatus < 200 || aInit.mStatus > 599) { aRv.ThrowRangeError<MSG_INVALID_RESPONSE_STATUSCODE_ERROR>(); return nullptr; } // Check if the status text contains illegal characters nsACString::const_iterator start, end; aInit.mStatusText.BeginReading(start); aInit.mStatusText.EndReading(end); if (FindCharInReadable('\r', start, end)) { aRv.ThrowTypeError<MSG_RESPONSE_INVALID_STATUSTEXT_ERROR>(); return nullptr; } // Reset iterator since FindCharInReadable advances it. aInit.mStatusText.BeginReading(start); if (FindCharInReadable('\n', start, end)) { aRv.ThrowTypeError<MSG_RESPONSE_INVALID_STATUSTEXT_ERROR>(); return nullptr; } RefPtr<InternalResponse> internalResponse = new InternalResponse(aInit.mStatus, aInit.mStatusText); // Grab a valid channel info from the global so this response is 'valid' for // interception. if (NS_IsMainThread()) { ChannelInfo info; nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global); if (window) { nsIDocument* doc = window->GetExtantDoc(); MOZ_ASSERT(doc); info.InitFromDocument(doc); } else { info.InitFromChromeGlobal(global); } internalResponse->InitChannelInfo(info); } else { WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(worker); internalResponse->InitChannelInfo(worker->GetChannelInfo()); } RefPtr<Response> r = new Response(global, internalResponse, nullptr); if (aInit.mHeaders.WasPassed()) { internalResponse->Headers()->Clear(); // Instead of using Fill, create an object to allow the constructor to // unwrap the HeadersInit. RefPtr<Headers> headers = Headers::Create(global, aInit.mHeaders.Value(), aRv); if (aRv.Failed()) { return nullptr; } internalResponse->Headers()->Fill(*headers->GetInternalHeaders(), aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } } if (aBody.WasPassed() && !aBody.Value().IsNull()) { if (aInit.mStatus == 204 || aInit.mStatus == 205 || aInit.mStatus == 304) { aRv.ThrowTypeError<MSG_RESPONSE_NULL_STATUS_WITH_BODY>(); return nullptr; } nsCString contentTypeWithCharset; nsCOMPtr<nsIInputStream> bodyStream; int64_t bodySize = InternalResponse::UNKNOWN_BODY_SIZE; const fetch::ResponseBodyInit& body = aBody.Value().Value(); if (body.IsReadableStream()) { aRv.MightThrowJSException(); JSContext* cx = aGlobal.Context(); const ReadableStream& readableStream = body.GetAsReadableStream(); JS::Rooted<JSObject*> readableStreamObj(cx, readableStream.Obj()); bool disturbed; bool locked; if (!JS::ReadableStreamIsDisturbed(cx, readableStreamObj, &disturbed) || !JS::ReadableStreamIsLocked(cx, readableStreamObj, &locked)) { aRv.StealExceptionFromJSContext(cx); return nullptr; } if (disturbed || locked) { aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>(); return nullptr; } r->SetReadableStreamBody(cx, readableStreamObj); JS::ReadableStreamMode streamMode; if (!JS::ReadableStreamGetMode(cx, readableStreamObj, &streamMode)) { aRv.StealExceptionFromJSContext(cx); return nullptr; } if (streamMode == JS::ReadableStreamMode::ExternalSource) { // If this is a DOM generated ReadableStream, we can extract the // inputStream directly. void* underlyingSource = nullptr; if (!JS::ReadableStreamGetExternalUnderlyingSource(cx, readableStreamObj, &underlyingSource)) { aRv.StealExceptionFromJSContext(cx); return nullptr; } MOZ_ASSERT(underlyingSource); aRv = FetchStream::RetrieveInputStream(underlyingSource, getter_AddRefs(bodyStream)); // The releasing of the external source is needed in order to avoid an // extra stream lock. if (!JS::ReadableStreamReleaseExternalUnderlyingSource(cx, readableStreamObj)) { aRv.StealExceptionFromJSContext(cx); return nullptr; } if (NS_WARN_IF(aRv.Failed())) { return nullptr; } } else { // If this is a JS-created ReadableStream, let's create a // FetchStreamReader. aRv = FetchStreamReader::Create(aGlobal.Context(), global, getter_AddRefs(r->mFetchStreamReader), getter_AddRefs(bodyStream)); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } } } else { uint64_t size = 0; aRv = ExtractByteStreamFromBody(body, getter_AddRefs(bodyStream), contentTypeWithCharset, size); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } bodySize = size; } internalResponse->SetBody(bodyStream, bodySize); if (!contentTypeWithCharset.IsVoid() && !internalResponse->Headers()->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) { // Ignore Append() failing here. ErrorResult error; internalResponse->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), contentTypeWithCharset, error); error.SuppressException(); } if (aRv.Failed()) { return nullptr; } } r->SetMimeType(); return r.forget(); }
void RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) { AutoCancel autoCancel(this, mRequestURL); if (!aValue.isObject()) { NS_WARNING("FetchEvent::RespondWith was passed a promise resolved to a non-Object value"); nsCString sourceSpec; uint32_t line = 0; uint32_t column = 0; nsString valueString; ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column, valueString); autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column, NS_LITERAL_CSTRING("InterceptedNonResponseWithURL"), mRequestURL, valueString); return; } RefPtr<Response> response; nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response); if (NS_FAILED(rv)) { nsCString sourceSpec; uint32_t line = 0; uint32_t column = 0; nsString valueString; ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column, valueString); autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column, NS_LITERAL_CSTRING("InterceptedNonResponseWithURL"), mRequestURL, valueString); return; } WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(worker); worker->AssertIsOnWorkerThread(); // Allow opaque response interception to be disabled until we can ensure the // security implications are not a complete disaster. if (response->Type() == ResponseType::Opaque && !worker->OpaqueInterceptionEnabled()) { autoCancel.SetCancelMessage( NS_LITERAL_CSTRING("OpaqueInterceptionDisabledWithURL"), mRequestURL); return; } // Section "HTTP Fetch", step 2.2: // If one of the following conditions is true, return a network error: // * response's type is "error". // * request's mode is not "no-cors" and response's type is "opaque". // * request is not a navigation request and response's type is // "opaqueredirect". if (response->Type() == ResponseType::Error) { autoCancel.SetCancelMessage( NS_LITERAL_CSTRING("InterceptedErrorResponseWithURL"), mRequestURL); return; } MOZ_ASSERT_IF(mIsClientRequest, mRequestMode == RequestMode::Same_origin); if (response->Type() == ResponseType::Opaque && mRequestMode != RequestMode::No_cors) { uint32_t mode = static_cast<uint32_t>(mRequestMode); NS_ConvertASCIItoUTF16 modeString(RequestModeValues::strings[mode].value, RequestModeValues::strings[mode].length); autoCancel.SetCancelMessage( NS_LITERAL_CSTRING("BadOpaqueInterceptionRequestModeWithURL"), mRequestURL, modeString); return; } if (!mIsNavigationRequest && response->Type() == ResponseType::Opaqueredirect) { autoCancel.SetCancelMessage( NS_LITERAL_CSTRING("BadOpaqueRedirectInterceptionWithURL"), mRequestURL); return; } if (NS_WARN_IF(response->BodyUsed())) { autoCancel.SetCancelMessage( NS_LITERAL_CSTRING("InterceptedUsedResponseWithURL"), mRequestURL); return; } RefPtr<InternalResponse> ir = response->GetInternalResponse(); if (NS_WARN_IF(!ir)) { return; } // When an opaque response is encountered, we need the original channel's principal // to reflect the final URL. Non-opaque responses are either same-origin or CORS-enabled // cross-origin responses, which are treated as same-origin by consumers. nsCString responseURL; if (response->Type() == ResponseType::Opaque) { ir->GetUnfilteredUrl(responseURL); if (NS_WARN_IF(responseURL.IsEmpty())) { return; } } nsAutoPtr<RespondWithClosure> closure(new RespondWithClosure(mInterceptedChannel, ir, worker->GetChannelInfo(), mScriptSpec, responseURL, mRequestURL, mRespondWithScriptSpec, mRespondWithLineNumber, mRespondWithColumnNumber)); nsCOMPtr<nsIInputStream> body; ir->GetUnfilteredBody(getter_AddRefs(body)); // Errors and redirects may not have a body. if (body) { response->SetBodyUsed(); nsCOMPtr<nsIOutputStream> responseBody; rv = mInterceptedChannel->GetResponseBody(getter_AddRefs(responseBody)); if (NS_WARN_IF(NS_FAILED(rv))) { return; } nsCOMPtr<nsIEventTarget> stsThread = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv); if (NS_WARN_IF(!stsThread)) { return; } // XXXnsm, Fix for Bug 1141332 means that if we decide to make this // streaming at some point, we'll need a different solution to that bug. rv = NS_AsyncCopy(body, responseBody, stsThread, NS_ASYNCCOPY_VIA_READSEGMENTS, 4096, RespondWithCopyComplete, closure.forget()); if (NS_WARN_IF(NS_FAILED(rv))) { return; } } else { RespondWithCopyComplete(closure.forget(), NS_OK); } MOZ_ASSERT(!closure); autoCancel.Reset(); mRequestWasHandled = true; }