@@ -146,7 +146,6 @@ kj::Promise<DeferredProxy<void>> ServiceWorkerGlobalScope::request(kj::HttpMetho
146146 Worker::Lock& lock,
147147 kj::Maybe<ExportedHandler&> exportedHandler,
148148 kj::Maybe<jsg::Ref<AbortSignal>> abortSignal) {
149- throwIfInvalidHeaderValue (headers);
150149 TRACE_EVENT (" workerd" , " ServiceWorkerGlobalScope::request()" );
151150 // To construct a ReadableStream object, we're supposed to pass in an Own<AsyncInputStream>, so
152151 // that it can drop the reference whenever it gets GC'ed. But in this case the stream's lifetime
@@ -167,10 +166,9 @@ kj::Promise<DeferredProxy<void>> ServiceWorkerGlobalScope::request(kj::HttpMetho
167166 CfProperty cf (cfBlobJson);
168167
169168 auto jsHeaders = js.alloc <Headers>(js, headers, Headers::Guard::REQUEST);
170- // We do not automatically decode gzipped request bodies because the fetch() standard doesn't
171- // specify any automatic encoding of requests. https://github.com/whatwg/fetch/issues/589
172- auto b = newSystemStream (kj::addRef (*ownRequestBody), StreamEncoding::IDENTITY);
173- auto jsStream = js.alloc <ReadableStream>(ioContext, kj::mv (b));
169+
170+ // We only create the body stream if there is a body to read.
171+ kj::Maybe<jsg::Ref<ReadableStream>> maybeJsStream = kj::none;
174172
175173 // If the request has "no body", we want `request.body` to be null. But, this is not the same
176174 // thing as the request having a body that happens to be empty. Unfortunately, KJ HTTP gives us
@@ -196,7 +194,12 @@ kj::Promise<DeferredProxy<void>> ServiceWorkerGlobalScope::request(kj::HttpMetho
196194 if (headers.get (kj::HttpHeaderId::CONTENT_LENGTH) != kj::none ||
197195 headers.get (kj::HttpHeaderId::TRANSFER_ENCODING) != kj::none ||
198196 requestBody.tryGetLength ().orDefault (1 ) > 0 ) {
197+ // We do not automatically decode gzipped request bodies because the fetch() standard doesn't
198+ // specify any automatic encoding of requests. https://github.com/whatwg/fetch/issues/589
199+ auto b = newSystemStream (kj::addRef (*ownRequestBody), StreamEncoding::IDENTITY);
200+ auto jsStream = js.alloc <ReadableStream>(ioContext, kj::mv (b));
199201 body = Body::ExtractedBody (jsStream.addRef ());
202+ maybeJsStream = kj::mv (jsStream);
200203 }
201204
202205 // If the request doesn't specify "Content-Length" or "Transfer-Encoding", set "Content-Length"
@@ -269,21 +272,25 @@ kj::Promise<DeferredProxy<void>> ServiceWorkerGlobalScope::request(kj::HttpMetho
269272 " the eventual Response) as the argument." );
270273 }
271274
272- if (jsStream->isDisturbed ()) {
273- lock.logUncaughtException (
274- " Script consumed request body but didn't call respondWith(). Can't forward request." );
275- return addNoopDeferredProxy (
276- response.sendError (500 , " Internal Server Error" , ioContext.getHeaderTable ()));
277- } else {
278- auto client = ioContext.getHttpClient (IoContext::NEXT_CLIENT_CHANNEL, false ,
279- cfBlobJson.map ([](kj::StringPtr s) { return kj::str (s); }), " fetch_default" _kjc);
280- auto adapter = kj::newHttpService (*client);
281- auto promise = adapter->request (method, url, headers, requestBody, response);
282- // Default handling doesn't rely on the IoContext at all so we can return it as a
283- // deferred proxy task.
284- return DeferredProxy<void >{promise.attach (kj::mv (adapter), kj::mv (client))};
275+ KJ_IF_SOME (jsStream, maybeJsStream) {
276+ if (jsStream->isDisturbed ()) {
277+ lock.logUncaughtException (
278+ " Script consumed request body but didn't call respondWith(). Can't forward request." );
279+ return addNoopDeferredProxy (
280+ response.sendError (500 , " Internal Server Error" , ioContext.getHeaderTable ()));
281+ }
282+ maybeJsStream = kj::none; // Release our reference; we don't need it anymore.
285283 }
284+
285+ auto client = ioContext.getHttpClient (IoContext::NEXT_CLIENT_CHANNEL, false ,
286+ cfBlobJson.map ([](kj::StringPtr s) { return kj::str (s); }), " fetch_default" _kjc);
287+ auto adapter = kj::newHttpService (*client);
288+ auto promise = adapter->request (method, url, headers, requestBody, response);
289+ // Default handling doesn't rely on the IoContext at all so we can return it as a
290+ // deferred proxy task.
291+ return DeferredProxy<void >{promise.attach (kj::mv (adapter), kj::mv (client))};
286292 } else KJ_IF_SOME (promise, event->getResponsePromise (lock)) {
293+ maybeJsStream = kj::none; // Release reference to request body stream. We don't need it.
287294 auto body2 = kj::addRef (*ownRequestBody);
288295
289296 // HACK: If the client disconnects, the `response` reference is no longer valid. But our
0 commit comments