Skip to content
File

Blob: src/workerd/api/http.h

cpp1232 lines
1// Copyright (c) 2017-2022 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4 
5#pragma once
6 
7#include "basics.h"
8#include "blob.h"
9#include "cf-property.h"
10#include "form-data.h"
11#include "headers.h"
12#include "queue.h"
13#include "web-socket.h"
14#include "worker-rpc.h"
15 
16#include <workerd/api/streams/readable.h>
17#include <workerd/api/url-standard.h>
18#include <workerd/api/url.h>
19#include <workerd/io/compatibility-date.capnp.h>
20#include <workerd/jsg/async-context.h>
21#include <workerd/jsg/jsg.h>
22 
23#include <kj/compat/http.h>
24 
25namespace workerd::api {
26 
27// Base class for Request and Response. In JavaScript, this class is a mixin, meaning no one will
28// be instantiating objects of this type -- it exists solely to house body-related functionality
29// common to both Requests and Responses.
30class Body: public jsg::Object {
31 public:
32 // The types of objects from which a Body can be created.
33 //
34 // If the object is a ReadableStream, Body will adopt it directly; otherwise the object is some
35 // sort of buffer-like source. In this case, Body will store its own ReadableStream that wraps the
36 // source, and it keeps a reference to the source object around. This allows Requests and
37 // Responses created from Strings, ArrayBuffers, FormDatas, Blobs, or URLSearchParams to be
38 // retransmitted.
39 //
40 // For an example of where this is important, consider a POST Request in redirect-follow mode and
41 // containing a body: if passed to a fetch() call that results in a 307 or 308 response, fetch()
42 // will re-POST to the new URL. If the body was constructed from a ReadableStream, this re-POST
43 // will fail, because there is no body source left. On the other hand, if the body was constructed
44 // from any of the other source types, Body can create a new ReadableStream from the source, and
45 // the POST will successfully retransmit.
46 using Initializer = kj::OneOf<jsg::Ref<ReadableStream>,
47 kj::String,
48 jsg::JsRef<jsg::JsBufferSource>,
49 jsg::Ref<Blob>,
50 jsg::Ref<FormData>,
51 jsg::Ref<URLSearchParams>,
52 jsg::Ref<url::URLSearchParams>,
53 jsg::AsyncGeneratorIgnoringStrings<jsg::Value>>;
54 
55 struct RefcountedBytes final: public kj::Refcounted {
56 kj::Array<kj::byte> bytes;
57 RefcountedBytes(kj::Array<kj::byte>&& bytes): bytes(kj::mv(bytes)) {}
58 JSG_MEMORY_INFO(RefcountedBytes) {
59 tracker.trackFieldWithSize("bytes", bytes.size());
60 }
61 };
62 
63 // The Fetch spec calls this type the body's "source", even though it really is a buffer. I end
64 // talking about things like "a buffer-backed body", whereas in standardese I should say
65 // "a body with a non-null source".
66 //
67 // I find that confusing, so let's just call it what it is: a Body::Buffer.
68 struct Buffer {
69 // In order to reconstruct buffer-backed ReadableStreams without gratuitous array copying, we
70 // need to be able to tie the lifetime of the source buffer to the lifetime of the
71 // ReadableStream's native stream, AND the lifetime of the Body itself. Thus we need
72 // refcounting.
73 //
74 // NOTE: ownBytes may contain a v8::Global reference, hence instances of `Buffer` must exist
75 // only within the V8 heap space.
76 kj::OneOf<kj::Own<RefcountedBytes>, jsg::Ref<Blob>, jsg::JsRef<jsg::JsBufferSource>> ownBytes;
77 // TODO(cleanup): When we integrate with V8's garbage collection APIs, we need to account for
78 // that here.
79 
80 // Bodies constructed from buffers rather than ReadableStreams can be retransmitted if necessary
81 // (e.g. for redirects, authentication). In these cases, we need to keep an ArrayPtr view onto
82 // the Array source itself, because the source may be a string, and thus have a trailing nul
83 // byte.
84 kj::ArrayPtr<const kj::byte> view;
85 
86 Buffer() = default;
87 Buffer(jsg::Lock& js, jsg::JsBufferSource& source)
88 : ownBytes(source.addRef(js)),
89 view(source.asArrayPtr()) {
90 // If the BufferSource is resizable, then it should have been copied into
91 // a kj::Array and passed in to that constructor.
92 KJ_ASSERT(!source.isResizable());
93 }
94 Buffer(kj::Array<kj::byte> array)
95 : ownBytes(kj::refcounted<RefcountedBytes>(kj::mv(array))),
96 view(ownBytes.get<kj::Own<RefcountedBytes>>()->bytes) {}
97 Buffer(kj::String string)
98 : ownBytes(kj::refcounted<RefcountedBytes>(string.releaseArray().releaseAsBytes())),
99 view([this] {
100 auto bytesIncludingNull = ownBytes.get<kj::Own<RefcountedBytes>>()->bytes.asPtr();
101 return bytesIncludingNull.first(bytesIncludingNull.size() - 1);
102 }()) {}
103 Buffer(jsg::Ref<Blob> blob)
104 : ownBytes(kj::mv(blob)),
105 view(ownBytes.get<jsg::Ref<Blob>>()->getData()) {}
106 
107 Buffer clone(jsg::Lock& js);
108 
109 JSG_MEMORY_INFO(Buffer) {
110 KJ_SWITCH_ONEOF(ownBytes) {
111 KJ_CASE_ONEOF(ref, jsg::JsRef<jsg::JsBufferSource>) {
112 tracker.trackField("ref", ref);
113 }
114 KJ_CASE_ONEOF(bytes, kj::Own<RefcountedBytes>) {
115 tracker.trackField("bytes", bytes);
116 }
117 KJ_CASE_ONEOF(blob, jsg::Ref<Blob>) {
118 tracker.trackField("blob", blob);
119 }
120 }
121 }
122 };
123 
124 struct Impl {
125 jsg::Ref<ReadableStream> stream;
126 kj::Maybe<Buffer> buffer;
127 JSG_MEMORY_INFO(Impl) {
128 tracker.trackField("stream", stream);
129 tracker.trackField("buffer", buffer);
130 }
131 };
132 
133 struct ExtractedBody {
134 ExtractedBody(jsg::Ref<ReadableStream> stream,
135 kj::Maybe<Buffer> source = kj::none,
136 kj::Maybe<kj::String> contentType = kj::none);
137 
138 Impl impl;
139 kj::Maybe<kj::String> contentType;
140 };
141 
142 // Implements the "extract a body" algorithm from the Fetch spec.
143 // https://fetch.spec.whatwg.org/#concept-bodyinit-extract
144 static ExtractedBody extractBody(jsg::Lock& js, Initializer init);
145 
146 explicit Body(jsg::Lock& js, kj::Maybe<ExtractedBody> init, Headers& headers);
147 
148 kj::Maybe<Buffer> getBodyBuffer(jsg::Lock& js);
149 
150 // The following body rewind/nullification functions are helpers for implementing fetch() redirect
151 // handling.
152 
153 // True if this body is null or buffer-backed, false if this body is a ReadableStream.
154 bool canRewindBody();
155 
156 // Reconstruct this body from its backing buffer. Precondition: `canRewindBody() == true`.
157 void rewindBody(jsg::Lock& js);
158 
159 // Convert this body into a null body.
160 void nullifyBody();
161 
162 // ---------------------------------------------------------------------------
163 // JS API
164 
165 kj::Maybe<jsg::Ref<ReadableStream>> getBody();
166 bool getBodyUsed();
167 jsg::Promise<jsg::BufferSource> arrayBuffer(jsg::Lock& js);
168 jsg::Promise<jsg::BufferSource> bytes(jsg::Lock& js);
169 jsg::Promise<kj::String> text(jsg::Lock& js);
170 jsg::Promise<jsg::Ref<FormData>> formData(jsg::Lock& js);
171 jsg::Promise<jsg::Value> json(jsg::Lock& js);
172 jsg::Promise<jsg::Ref<Blob>> blob(jsg::Lock& js);
173 
174 JSG_RESOURCE_TYPE(Body, CompatibilityFlags::Reader flags) {
175 if (flags.getJsgPropertyOnPrototypeTemplate()) {
176 JSG_READONLY_PROTOTYPE_PROPERTY(body, getBody);
177 JSG_READONLY_PROTOTYPE_PROPERTY(bodyUsed, getBodyUsed);
178 } else {
179 JSG_READONLY_INSTANCE_PROPERTY(body, getBody);
180 JSG_READONLY_INSTANCE_PROPERTY(bodyUsed, getBodyUsed);
181 }
182 JSG_METHOD(arrayBuffer);
183 JSG_METHOD(bytes);
184 JSG_METHOD(text);
185 JSG_METHOD(json);
186 JSG_METHOD(formData);
187 JSG_METHOD(blob);
188 
189 if (flags.getFetchIterableTypeSupport()) {
190 JSG_TS_DEFINE(type BodyInit = ReadableStream<Uint8Array> | string | ArrayBuffer | ArrayBufferView | Blob | URLSearchParams | FormData | Iterable<ArrayBuffer|ArrayBufferView> | AsyncIterable<ArrayBuffer|ArrayBufferView>);
191 } else {
192 JSG_TS_DEFINE(type BodyInit = ReadableStream<Uint8Array> | string | ArrayBuffer | ArrayBufferView | Blob | URLSearchParams | FormData);
193 }
194 // All type aliases get inlined when exporting RTTI, but this type alias is included by
195 // the official TypeScript types, so users might be depending on it.
196 JSG_TS_OVERRIDE({
197 json<T>(): Promise<T>;
198 bytes(): Promise<Uint8Array>;
199 arrayBuffer(): Promise<ArrayBuffer>;
200 });
201 // Allow JSON body type to be specified
202 }
203 
204 void visitForMemoryInfo(jsg::MemoryTracker& tracker) const {
205 tracker.trackField("impl", impl);
206 }
207 
208 protected:
209 // Helper to implement Request/Response::clone().
210 kj::Maybe<ExtractedBody> clone(jsg::Lock& js);
211 
212 private:
213 kj::Maybe<Impl> impl;
214 
215 // HACK: This `headersRef` variable refers to a Headers object in the Request/Response subclass.
216 // As such, it will briefly dangle during object destruction. While unlikely to be an issue,
217 // it's worth being aware of.
218 Headers& headersRef;
219 
220 void visitForGc(jsg::GcVisitor& visitor) {
221 KJ_IF_SOME(i, impl) {
222 visitor.visit(i.stream);
223 }
224 }
225};
226 
227// Controls how response bodies are encoded/decoded according to Content-Encoding headers
228enum class Response_BodyEncoding {
229 AUTO, // Automatically encode/decode based on Content-Encoding headers
230 MANUAL // Treat Content-Encoding headers as opaque (no automatic encoding/decoding)
231};
232 
233class Request;
234class Response;
235struct RequestInitializerDict;
236 
237class Socket;
238struct SocketOptions;
239struct SocketAddress;
240using AnySocketAddress = kj::OneOf<SocketAddress, kj::String>;
241 
242// Represents a client to a remote "web service".
243//
244// Originally, this meant an HTTP service, and `Fetcher` had just one method, `fetch()`, hence the
245// name. However, `Fetcher` is really the JavaScript type for a `WorkerInterface`, and is used in
246// particular to represent service bindings as well as Durable Object stubs. As such, as Workers
247// have grown new ways to talk to other Workers, `Fetcher` has added methods other than `fetch()`.
248//
249// TODO(cleanup): This probably doesn't belong in `http.h` anymore. And perhaps it should be
250// renamed, though I haven't heard any great suggestions for what the name should be.
251class Fetcher: public JsRpcClientProvider {
252 public:
253 // Should we use a fake https base url if we lack a scheme+authority?
254 enum class RequiresHostAndProtocol { YES, NO };
255 
256 // `channel` is what to pass to IoContext::getSubrequestChannel() to get a WorkerInterface
257 // representing this Fetcher. Note that different requests potentially have different client
258 // objects because a WorkerInterface is a KJ I/O object and therefore tied to a thread.
259 // Abstractly, within a worker instance, the same channel always refers to the same Fetcher, even
260 // though the WorkerInterface object changes from request to request.
261 //
262 // If `requiresHost` is false, then requests using this Fetcher are allowed to specify a
263 // URL that has no protocol or host.
264 //
265 // See pipeline.capnp or request-context.h for an explanation of `isInHouse`.
266 explicit Fetcher(uint channel, RequiresHostAndProtocol requiresHost, bool isInHouse = false)
267 : channelOrClientFactory(channel),
268 requiresHost(requiresHost),
269 isInHouse(isInHouse) {}
270 
271 // Create a Fetcher bound to an IoChannelFactory::SubrequestChannel object rather than a numeric
272 // channel. This Fetcher will inherently be bound to the current I/O context.
273 explicit Fetcher(IoOwn<IoChannelFactory::SubrequestChannel> subrequestChannel,
274 RequiresHostAndProtocol requiresHost = RequiresHostAndProtocol::YES,
275 bool isInHouse = false)
276 : channelOrClientFactory(kj::mv(subrequestChannel)),
277 requiresHost(requiresHost),
278 isInHouse(isInHouse) {}
279 
280 // Used by Fetchers that use ad-hoc, single-use WorkerInterface instances, such as ones
281 // created for Actors.
282 //
283 // Implementations that create HTTP connections should route through
284 // IoContext::getSubrequestNoChecks() (or getSubrequest()) internally, to ensure external memory
285 // adjustment and other subrequest accounting are applied.
286 //
287 // TODO(cleanup): Consider removing this in favor of `IoChannelFactory::SubrequestChannel`, which
288 // is almost the same thing.
289 class OutgoingFactory {
290 public:
291 virtual kj::Own<WorkerInterface> newSingleUseClient(kj::Maybe<kj::String> cfStr) = 0;
292 
293 // Get a `SubrequestChannel` representing this Fetcher. This is used especially when the
294 // Fetcher is being passed to another isolate.
295 virtual kj::Own<IoChannelFactory::SubrequestChannel> getSubrequestChannel() {
296 // TODO(soon): Update all implementations and remove this default implementation.
297 KJ_UNIMPLEMENTED("this Fetcher doesn't yet implement getSubrequestChannel()");
298 }
299 };
300 
301 // Used by Fetchers that obtain their HttpClient in a custom way, but which aren't tied
302 // to a specific I/O context. The factory object moves with the isolate across threads and
303 // contexts, and must work from any context.
304 //
305 // Same as OutgoingFactory: implementations that create HTTP connections should route through
306 // IoContext::getSubrequestNoChecks() internally.
307 class CrossContextOutgoingFactory {
308 public:
309 virtual kj::Own<WorkerInterface> newSingleUseClient(
310 IoContext& context, kj::Maybe<kj::String> cfStr) = 0;
311 
312 virtual kj::Own<IoChannelFactory::SubrequestChannel> getSubrequestChannel(IoContext& context) {
313 // TODO(soon): Update all implementations and remove this default implementation.
314 KJ_UNIMPLEMENTED("this Fetcher doesn't yet implement getSubrequestChannel()");
315 }
316 };
317 
318 // `outgoingFactory` is used for Fetchers that use ad-hoc WorkerInterface instances, such as ones
319 // created for Actors.
320 Fetcher(IoOwn<OutgoingFactory> outgoingFactory,
321 RequiresHostAndProtocol requiresHost,
322 bool isInHouse = false)
323 : channelOrClientFactory(kj::mv(outgoingFactory)),
324 requiresHost(requiresHost),
325 isInHouse(isInHouse) {}
326 
327 // `outgoingFactory` is used for Fetchers that use ad-hoc WorkerInterface instances, but doesn't
328 // require an IoContext
329 Fetcher(kj::Own<CrossContextOutgoingFactory> outgoingFactory,
330 RequiresHostAndProtocol requiresHost,
331 bool isInHouse = false)
332 : channelOrClientFactory(kj::mv(outgoingFactory)),
333 requiresHost(requiresHost),
334 isInHouse(isInHouse) {}
335 
336 // Returns an `WorkerInterface` that is only valid for the lifetime of the current
337 // `IoContext`.
338 kj::Own<WorkerInterface> getClient(
339 IoContext& ioContext, kj::Maybe<kj::String> cfStr, kj::ConstString operationName);
340 
341 // Result of getClient call that includes optional trace context
342 struct ClientWithTracing {
343 kj::Own<WorkerInterface> client;
344 kj::Maybe<TraceContext> traceContext;
345 };
346 
347 // Get client and optionally create trace context, all in one call
348 ClientWithTracing getClientWithTracing(
349 IoContext& ioContext, kj::Maybe<kj::String> cfStr, kj::ConstString operationName);
350 
351 // Get a SubrequestChannel representing this Fetcher.
352 kj::Own<IoChannelFactory::SubrequestChannel> getSubrequestChannel(IoContext& ioContext);
353 
354 // Wraps kj::Url::parse to take into account whether the Fetcher requires a host to be
355 // specified on URLs, Fetcher-specific URL decoding options, and error handling.
356 kj::Url parseUrl(jsg::Lock& js, kj::StringPtr url);
357 
358 jsg::Ref<Socket> connect(
359 jsg::Lock& js, AnySocketAddress address, jsg::Optional<SocketOptions> options);
360 
361 jsg::Promise<jsg::Ref<Response>> fetch(jsg::Lock& js,
362 kj::OneOf<jsg::Ref<Request>, kj::String> requestOrUrl,
363 jsg::Optional<kj::OneOf<RequestInitializerDict, jsg::Ref<Request>>> requestInit);
364 
365 using GetResult = kj::OneOf<jsg::Ref<ReadableStream>, jsg::BufferSource, kj::String, jsg::Value>;
366 
367 jsg::Promise<GetResult> get(jsg::Lock& js, kj::String url, jsg::Optional<kj::String> type);
368 
369 // Optional parameter for passing options into a Fetcher::put. Initially
370 // intended for supporting expiration times in KV bindings.
371 struct PutOptions {
372 jsg::Optional<int> expiration;
373 jsg::Optional<int> expirationTtl;
374 
375 JSG_STRUCT(expiration, expirationTtl);
376 };
377 
378 jsg::Promise<void> put(
379 jsg::Lock& js, kj::String url, Body::Initializer body, jsg::Optional<PutOptions> options);
380 
381 jsg::Promise<void> delete_(jsg::Lock& js, kj::String url);
382 
383 // Representation of a queue message for use when invoking the queue() event handler on another
384 // worker via a service binding.
385 struct ServiceBindingQueueMessage {
386 kj::String id;
387 kj::Date timestamp;
388 jsg::Optional<jsg::Value> body;
389 jsg::Optional<kj::Array<kj::byte>> serializedBody;
390 uint16_t attempts;
391 
392 JSG_STRUCT(id, timestamp, body, serializedBody, attempts);
393 JSG_STRUCT_TS_OVERRIDE(type ServiceBindingQueueMessage<Body = unknown> = {
394 id: string;
395 timestamp: Date;
396 attempts: number;
397 } & (
398 | { body: Body }
399 | { serializedBody: ArrayBuffer | ArrayBufferView }
400 ));
401 };
402 
403 struct QueueResult {
404 kj::String outcome;
405 bool ackAll;
406 QueueRetryBatch retryBatch;
407 kj::Array<kj::String> explicitAcks;
408 kj::Array<QueueRetryMessage> retryMessages;
409 JSG_STRUCT(outcome, ackAll, retryBatch, explicitAcks, retryMessages);
410 };
411 
412 jsg::Promise<QueueResult> queue(jsg::Lock& js,
413 kj::String queueName,
414 kj::Array<ServiceBindingQueueMessage> messages,
415 jsg::Optional<MessageBatchMetadata> metadata);
416 
417 struct ScheduledOptions {
418 jsg::Optional<kj::Date> scheduledTime;
419 jsg::Optional<kj::String> cron;
420 
421 JSG_STRUCT(scheduledTime, cron);
422 };
423 
424 struct ScheduledResult {
425 kj::String outcome;
426 bool noRetry;
427 
428 JSG_STRUCT(outcome, noRetry);
429 };
430 
431 jsg::Promise<ScheduledResult> scheduled(jsg::Lock& js, jsg::Optional<ScheduledOptions> options);
432 
433 kj::Maybe<jsg::Ref<JsRpcProperty>> getRpcMethod(jsg::Lock& js, kj::String name);
434 // Internal method for use from bindings code. It skips compatibility flags checks.
435 kj::Maybe<jsg::Ref<JsRpcProperty>> getRpcMethodInternal(jsg::Lock& js, kj::String name);
436 kj::Maybe<jsg::Ref<JsRpcProperty>> getRpcMethodForTestOnly(jsg::Lock& js, kj::String name) {
437 return getRpcMethod(js, kj::mv(name));
438 }
439 
440 rpc::JsRpcTarget::Client getClientForOneCall(
441 jsg::Lock& js, kj::Vector<kj::StringPtr>& path) override;
442 
443 JSG_RESOURCE_TYPE(Fetcher, CompatibilityFlags::Reader flags) {
444 // WARNING: New JSG_METHODs on Fetcher must be gated via compatibility flag to prevent
445 // conflicts with JS RPC methods (implemented via the wildcard property). Ideally, we do not
446 // add any new methods here, and instead rely on RPC for all future needs.
447 //
448 // Similarly, subclasses of `Fetcher` (notably, `DurableObject`) must follow the same rule,
449 // as any methods added to them will shadow RPC methods of the same name.
450 
451 JSG_METHOD(fetch);
452 JSG_METHOD(connect);
453 
454 if (flags.getServiceBindingExtraHandlers()) {
455 JSG_METHOD(queue);
456 JSG_METHOD(scheduled);
457 
458 JSG_TS_OVERRIDE(type Fetcher<
459 T extends Rpc.EntrypointBranded | undefined = undefined,
460 Reserved extends string = never
461 > = (
462 T extends Rpc.EntrypointBranded
463 ? Rpc.Provider<T, Reserved | "fetch" | "connect" | "queue" | "scheduled">
464 : unknown
465 ) & {
466 fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
467 connect(address: SocketAddress | string, options?: SocketOptions): Socket;
468 queue(queueName: string, messages: ServiceBindingQueueMessage[], metadata?: MessageBatchMetadata): Promise<FetcherQueueResult>;
469 scheduled(options?: FetcherScheduledOptions): Promise<FetcherScheduledResult>;
470 });
471 } else {
472 JSG_TS_OVERRIDE(type Fetcher<
473 T extends Rpc.EntrypointBranded | undefined = undefined,
474 Reserved extends string = never
475 > = (
476 T extends Rpc.EntrypointBranded
477 ? Rpc.Provider<T, Reserved | "fetch" | "connect">
478 : unknown
479 ) & {
480 fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
481 connect(address: SocketAddress | string, options?: SocketOptions): Socket;
482 });
483 }
484 JSG_TS_DEFINE(
485 type Service<
486 T extends
487 | (new (...args: any[]) => Rpc.WorkerEntrypointBranded)
488 | Rpc.WorkerEntrypointBranded
489 | ExportedHandler<any, any, any>
490 | undefined = undefined,
491 > = T extends new (...args: any[]) => Rpc.WorkerEntrypointBranded ? Fetcher<InstanceType<T>>
492 : T extends Rpc.WorkerEntrypointBranded ? Fetcher<T>
493 : T extends Exclude<Rpc.EntrypointBranded, Rpc.WorkerEntrypointBranded> ? never
494 : Fetcher<undefined>
495 );
496 
497 if (!flags.getFetcherNoGetPutDelete()) {
498 // These helpers just map to `fetch()` with the corresponding HTTP method. They were never
499 // documented and probably never should have been defined. We are removing them to make room
500 // for RPC.
501 JSG_METHOD(get);
502 JSG_METHOD(put);
503 JSG_METHOD_NAMED(delete, delete_);
504 }
505 
506 JSG_WILDCARD_PROPERTY(getRpcMethod);
507 
508 if (flags.getWorkerdExperimental()) {
509 // We export a copy of getRpcMethod for use in tests only which allows the caller to provide
510 // an arbitrary string as the method name. This allows invoking methods that would normally
511 // be shadowed by non-wildcard methods.
512 JSG_METHOD(getRpcMethodForTestOnly);
513 }
514 }
515 
516 void serialize(jsg::Lock& js, jsg::Serializer& serializer);
517 static jsg::Ref<Fetcher> deserialize(
518 jsg::Lock& js, rpc::SerializationTag tag, jsg::Deserializer& deserializer);
519 
520 JSG_SERIALIZABLE(rpc::SerializationTag::SERVICE_STUB);
521 
522 private:
523 kj::OneOf<uint,
524 IoOwn<IoChannelFactory::SubrequestChannel>,
525 kj::Own<CrossContextOutgoingFactory>,
526 IoOwn<OutgoingFactory>>
527 channelOrClientFactory;
528 RequiresHostAndProtocol requiresHost;
529 bool isInHouse;
530};
531 
532// Type of the second parameter to Request's constructor. Also the type of the second parameter
533// to fetch().
534//
535// When adding new properties to this struct, don't forget to update Request::serialize().
536struct RequestInitializerDict {
537 jsg::Optional<kj::String> method;
538 jsg::Optional<Headers::Initializer> headers;
539 
540 // The script author may specify an empty body either implicitly, by allowing this property to
541 // be undefined, or explicitly, by setting this property to null. To support both cases, this
542 // body initializer must be Optional<Maybe<Body::Initializer>>.
543 jsg::Optional<kj::Maybe<Body::Initializer>> body;
544 
545 // follow, error, manual (default follow)
546 jsg::Optional<kj::String> redirect;
547 
548 jsg::Optional<kj::Maybe<jsg::Ref<Fetcher>>> fetcher;
549 
550 // Cloudflare-specific feature flags.
551 jsg::Optional<jsg::V8Ref<v8::Object>> cf;
552 // TODO(someday): We should generalize this concept to sending control information to
553 // downstream workers in the pipeline. That is, when multiple workers apply to the same
554 // request (with the first worker's subrequests being passed to the next worker), then
555 // first worker should be able to set flags on the request that the second worker can see.
556 // Perhaps we should say that any field you set on a Request object will be JSON-serialized
557 // and passed on to the next worker? Then `cf` is just one such field: it's not special,
558 // it's only named `cf` because the consumer is Cloudflare code.
559 
560 // The fetch standard defines additional properties that are really only relevant in browser
561 // implementations that implement CORS. The WinterTC has determined that for non-browser
562 // environments, these should be silently ignoredif the runtime has no use for them.
563 // * mode
564 // * credentials
565 // * referrer
566 // * referrerPolicy
567 // * keepalive
568 // * window
569 
570 // In browsers this controls the local browser cache. For Cloudflare Workers it could control the
571 // Cloudflare edge cache. While the standard defines a number of values for this property, our
572 // implementation supports only three: undefined (identifying the default caching behavior that
573 // has been implemented by the runtime), "no-store", and "no-cache".
574 jsg::Optional<kj::String> cache;
575 
576 // Subresource integrity (check response against a given hash).
577 // We do not implement integrity checking, however, we will accept either an undefined
578 // or empty string value for the property. If any other value is given we will throw.
579 jsg::Optional<kj::String> integrity;
580 
581 // The spec declares this optional, but is unclear on whether it is nullable. The spec is also
582 // unclear on whether the `Request.signal` property is nullable. If `Request.signal` is nullable,
583 // then we definitely have to accept `null` as an input here, otherwise
584 // `new Request(url, {...request})` will fail when `request.signal` is null. However, it's also
585 // possible that neither property should be nullable. Indeed, it appears that Chrome always
586 // constructs a dummy signal even if none was provided, and uses that. But Chrome is also happy
587 // to accept `null` as an input, so if we're doing what Chrome does, then we should accept
588 // `null`.
589 jsg::Optional<kj::Maybe<jsg::Ref<AbortSignal>>> signal;
590 
591 // Controls whether the response body is automatically decoded according to Content-Encoding
592 // headers. Default behavior is "automatic" which means bodies are decoded. Setting this to
593 // "manual" means the raw compressed bytes are returned.
594 jsg::Optional<kj::String> encodeResponseBody;
595 
596 // The duplex option controls whether or not a fetch is expected to send the entire request
597 // before processing the response. The default value ("half"), which is currently the only
598 // option supported by the standard, dictates that the request is fully sent before handling
599 // the response. There are currently a proposal to add a "full" option which is the model
600 // we support. Once "full" is added, we need to update this to accept either undefined or
601 // "full", and possibly decide if we want to support the "half" option. For now we'll just
602 // ignore this option. Enabling this option later might require a compatibility flag.
603 // jsg::Optional<kj::String> duplex;
604 // TODO(conform): Might support later?
605 
606 // Specifies the relative priority of the request. We currently do not make use of this
607 // information. Per the spec, the only values acceptable for the priority option are
608 // "high", "low", and "auto", with "auto" being considered the default. For now we'll just
609 // ignore this option. Enabling this option later might require a compatibility flag.
610 // jsg::Optional<kj::String> priority;
611 // TODO(conform): Might support later?
612 
613 JSG_STRUCT(
614 method, headers, body, redirect, fetcher, cf, cache, integrity, signal, encodeResponseBody);
615 JSG_STRUCT_TS_OVERRIDE_DYNAMIC(CompatibilityFlags::Reader flags) {
616 if (flags.getCacheOptionEnabled()) {
617 if (flags.getCacheReload()) {
618 JSG_TS_OVERRIDE(RequestInit<Cf = CfProperties> {
619 headers?: HeadersInit;
620 body?: BodyInit | null;
621 cache?: 'no-store' | 'no-cache' | 'reload';
622 cf?: Cf;
623 encodeResponseBody?: "automatic" | "manual";
624 });
625 
626 } else if (flags.getCacheNoCache()) {
627 JSG_TS_OVERRIDE(RequestInit<Cf = CfProperties> {
628 headers?: HeadersInit;
629 body?: BodyInit | null;
630 cache?: 'no-store' | 'no-cache';
631 cf?: Cf;
632 encodeResponseBody?: "automatic" | "manual";
633 });
634 } else {
635 JSG_TS_OVERRIDE(RequestInit<Cf = CfProperties> {
636 headers?: HeadersInit;
637 body?: BodyInit | null;
638 cache?: 'no-store';
639 cf?: Cf;
640 encodeResponseBody?: "automatic" | "manual";
641 });
642 }
643 } else {
644 JSG_TS_OVERRIDE(RequestInit<Cf = CfProperties> {
645 headers?: HeadersInit;
646 body?: BodyInit | null;
647 cache?: never;
648 cf?: Cf;
649 encodeResponseBody?: "automatic" | "manual";
650 });
651 }
652 }
653 
654 // This method is called within tryUnwrap() when the type is unpacked from v8.
655 // See jsg Readme for more details.
656 void validate(jsg::Lock&);
657};
658 
659class Request final: public Body {
660 public:
661 enum class Redirect {
662 FOLLOW,
663 MANUAL
664 // Note: error mode doesn't make sense for us.
665 };
666 static kj::Maybe<Redirect> tryParseRedirect(kj::StringPtr redirect);
667 
668 enum class CacheMode {
669 // CacheMode::NONE is set when cache is undefined. It represents the default cache
670 // mode that workers has supported.
671 NONE,
672 NOSTORE,
673 NOCACHE,
674 RELOAD,
675 };
676 
677 Request(jsg::Lock& js,
678 kj::HttpMethod method,
679 kj::StringPtr url,
680 Redirect redirect,
681 jsg::Ref<Headers> headers,
682 kj::Maybe<jsg::Ref<Fetcher>> fetcher,
683 kj::Maybe<jsg::Ref<AbortSignal>> signal,
684 CfProperty&& cf,
685 kj::Maybe<Body::ExtractedBody> body,
686 kj::Maybe<jsg::Ref<AbortSignal>> thisSignal,
687 CacheMode cacheMode = CacheMode::NONE,
688 Response_BodyEncoding responseBodyEncoding = Response_BodyEncoding::AUTO)
689 : Body(js, kj::mv(body), *headers),
690 method(method),
691 url(kj::str(url)),
692 redirect(redirect),
693 headers(kj::mv(headers)),
694 fetcher(kj::mv(fetcher)),
695 cacheMode(cacheMode),
696 cf(kj::mv(cf)),
697 responseBodyEncoding(responseBodyEncoding) {
698 KJ_IF_SOME(s, signal) {
699 // If the AbortSignal will never abort, assigning it to thisSignal instead ensures
700 // that the cancel machinery is not used but the request.signal accessor will still
701 // do the right thing.
702 if (s->getNeverAborts()) {
703 this->thisSignal = s.addRef();
704 } else {
705 this->signal = s.addRef();
706 }
707 }
708 }
709 // TODO(conform): Technically, the request's URL should be parsed immediately upon Request
710 // construction, and any errors encountered should be thrown. Instead, we defer parsing until
711 // fetch()-time. This sidesteps an awkward issue: The request URL should be parsed relative to
712 // the service worker script's URL (e.g. https://capnproto.org/sw.js), but edge worker scripts
713 // don't have a script URL, so we have no choice but to parse it as an absolute URL. This means
714 // constructs like `new Request("")` should actually throw TypeError, but constructing Requests
715 // with empty URLs is useful in testing.
716 
717 kj::HttpMethod getMethodEnum() {
718 return method;
719 }
720 void setMethodEnum(kj::HttpMethod newMethod) {
721 method = newMethod;
722 }
723 Redirect getRedirectEnum() {
724 return redirect;
725 }
726 void shallowCopyHeadersTo(kj::HttpHeaders& out);
727 kj::Maybe<kj::String> serializeCfBlobJson(jsg::Lock& js);
728 
729 // ---------------------------------------------------------------------------
730 // JS API
731 
732 using InitializerDict = RequestInitializerDict;
733 
734 using Info = kj::OneOf<jsg::Ref<Request>, kj::String>;
735 using Initializer = kj::OneOf<InitializerDict, jsg::Ref<Request>>;
736 
737 // Wrapper around Request::constructor that calls it only if necessary, and returns a
738 // jsg::Ref<Request>.
739 //
740 // C++ API, but declared down here because we need the InitializerDict type.
741 static jsg::Ref<Request> coerce(
742 jsg::Lock& js, Request::Info input, jsg::Optional<Request::Initializer> init);
743 
744 static jsg::Ref<Request> constructor(
745 jsg::Lock& js, Request::Info input, jsg::Optional<Request::Initializer> init);
746 
747 jsg::Ref<Request> clone(jsg::Lock& js);
748 
749 kj::StringPtr getMethod();
750 kj::StringPtr getUrl();
751 jsg::Ref<Headers> getHeaders(jsg::Lock& js);
752 kj::StringPtr getRedirect();
753 kj::Maybe<jsg::Ref<Fetcher>> getFetcher();
754 
755 // getSignal() is the one that we used internally to determine if there's actually
756 // an AbortSignal that can be triggered to cancel things. The getThisSignal() is
757 // used only on the JavaScript side to conform to the spec, which requires
758 // request.signal to always return an AbortSignal even if one is not actively
759 // used on this request.
760 kj::Maybe<jsg::Ref<AbortSignal>> getSignal();
761 jsg::Ref<AbortSignal> getThisSignal(jsg::Lock& js);
762 
763 // Clear the request's signal if the 'ignoreForSubrequests' flag is set. This happens when
764 // a request from an incoming fetch is passed-through to another fetch. We want to avoid
765 // aborting the subrequest in that case.
766 void clearSignalIfIgnoredForSubrequest(jsg::Lock& js);
767 
768 // Returns the `cf` field containing Cloudflare feature flags.
769 jsg::Optional<jsg::JsObject> getCf(jsg::Lock& js);
770 
771 // The duplex option controls whether or not a fetch is expected to send the entire request
772 // before processing the response. The default value ("half"), which is currently the only
773 // option supported by the standard, dictates that the request is fully sent before handling
774 // the response. There are currently a proposal to add a "full" option which is the model
775 // we support. Once "full" is added, we need to update this to accept either undefined or
776 // "full", and possibly decide if we want to support the "half" option.
777 // jsg::JsValue getDuplex(jsg::Lock& js) { return js.undefined(); }
778 // TODO(conform): Might implement?
779 
780 // These relate to CORS support, which we do not implement. WinterTC has determined that
781 // non-browser implementations that do not implement CORS support should ignore these
782 // entirely as if they were not defined.
783 // * destination
784 // * mode
785 // * credentials
786 // * referrer
787 // * referrerPolicy
788 // * isReloadNavigation
789 // * isHistoryNavigation
790 // * keepalive (see below)
791 
792 // We do not implement support for the keepalive option but we do want to at least provide
793 // the standard property, hard-coded to always be false. WinterTC actually recommends that
794 // this one just be left undefined but we already had this returning false always and it
795 // would require a compat flag to remove. Just keep it as it's harmless.
796 bool getKeepalive() {
797 return false;
798 }
799 
800 // The cache mode determines how HTTP cache is used with the request.
801 jsg::Optional<kj::StringPtr> getCache(jsg::Lock& js);
802 CacheMode getCacheMode();
803 
804 // We do not implement integrity checking at all. However, the spec says that
805 // the default value should be an empty string. When the Request object is
806 // created we verify that the given value is undefined or empty.
807 kj::String getIntegrity() {
808 return kj::String();
809 }
810 
811 // Get the response body encoding setting for this request
812 Response_BodyEncoding getResponseBodyEncoding() {
813 return responseBodyEncoding;
814 }
815 
816 JSG_RESOURCE_TYPE(Request, CompatibilityFlags::Reader flags) {
817 JSG_INHERIT(Body);
818 
819 JSG_METHOD(clone);
820 
821 JSG_TS_DEFINE(type RequestInfo<CfHostMetadata = unknown, Cf = CfProperties<CfHostMetadata>> = Request<CfHostMetadata, Cf> | string);
822 // All type aliases get inlined when exporting RTTI, but this type alias is included by
823 // the official TypeScript types, so users might be depending on it.
824 
825 if (flags.getJsgPropertyOnPrototypeTemplate()) {
826 JSG_READONLY_PROTOTYPE_PROPERTY(method, getMethod);
827 JSG_READONLY_PROTOTYPE_PROPERTY(url, getUrl);
828 JSG_READONLY_PROTOTYPE_PROPERTY(headers, getHeaders);
829 JSG_READONLY_PROTOTYPE_PROPERTY(redirect, getRedirect);
830 JSG_READONLY_PROTOTYPE_PROPERTY(fetcher, getFetcher);
831 JSG_READONLY_PROTOTYPE_PROPERTY(signal, getThisSignal);
832 JSG_READONLY_PROTOTYPE_PROPERTY(cf, getCf);
833 
834 // TODO(conform): These are standard properties that we do not implement (see descriptions
835 // above).
836 // JSG_READONLY_PROTOTYPE_PROPERTY(duplex, getDuplex);
837 JSG_READONLY_PROTOTYPE_PROPERTY(integrity, getIntegrity);
838 JSG_READONLY_PROTOTYPE_PROPERTY(keepalive, getKeepalive);
839 if (flags.getCacheOptionEnabled()) {
840 JSG_READONLY_PROTOTYPE_PROPERTY(cache, getCache);
841 if (flags.getCacheReload()) {
842 JSG_TS_OVERRIDE(<CfHostMetadata = unknown, Cf = CfProperties<CfHostMetadata>> {
843 constructor(input: RequestInfo<CfProperties> | URL, init?: RequestInit<Cf>);
844 clone(): Request<CfHostMetadata, Cf>;
845 cache?: "no-store" | "no-cache" | "reload";
846 cf?: Cf;
847 });
848 } else if (flags.getCacheNoCache()) {
849 JSG_TS_OVERRIDE(<CfHostMetadata = unknown, Cf = CfProperties<CfHostMetadata>> {
850 constructor(input: RequestInfo<CfProperties> | URL, init?: RequestInit<Cf>);
851 clone(): Request<CfHostMetadata, Cf>;
852 cache?: "no-store" | "no-cache";
853 cf?: Cf;
854 });
855 } else {
856 JSG_TS_OVERRIDE(<CfHostMetadata = unknown, Cf = CfProperties<CfHostMetadata>> {
857 constructor(input: RequestInfo<CfProperties> | URL, init?: RequestInit<Cf>);
858 clone(): Request<CfHostMetadata, Cf>;
859 cache?: "no-store";
860 cf?: Cf;
861 });
862 }
863 } else {
864 JSG_TS_OVERRIDE(<CfHostMetadata = unknown, Cf = CfProperties<CfHostMetadata>> {
865 constructor(input: RequestInfo<CfProperties> | URL, init?: RequestInit<Cf>);
866 clone(): Request<CfHostMetadata, Cf>;
867 cf?: Cf;
868 });
869 }
870 
871 // Use `RequestInfo` and `RequestInit` type aliases in constructor instead of inlining.
872 // `CfProperties` is defined in `/types/defines/cf.d.ts`. We only really need a single `Cf`
873 // type parameter here, but it would be a breaking type change to remove `CfHostMetadata`.
874 } else {
875 JSG_READONLY_INSTANCE_PROPERTY(method, getMethod);
876 JSG_READONLY_INSTANCE_PROPERTY(url, getUrl);
877 JSG_READONLY_INSTANCE_PROPERTY(headers, getHeaders);
878 JSG_READONLY_INSTANCE_PROPERTY(redirect, getRedirect);
879 JSG_READONLY_INSTANCE_PROPERTY(fetcher, getFetcher);
880 JSG_READONLY_INSTANCE_PROPERTY(signal, getThisSignal);
881 JSG_READONLY_INSTANCE_PROPERTY(cf, getCf);
882 
883 // TODO(conform): These are standard properties that we do not implement (see descriptions
884 // above).
885 // JSG_READONLY_INSTANCE_PROPERTY(duplex, getDuplex);
886 JSG_READONLY_INSTANCE_PROPERTY(integrity, getIntegrity);
887 JSG_READONLY_INSTANCE_PROPERTY(keepalive, getKeepalive);
888 
889 JSG_TS_OVERRIDE(<CfHostMetadata = unknown, Cf = CfProperties<CfHostMetadata>> {
890 constructor(input: RequestInfo<CfProperties> | URL, init?: RequestInit<Cf>);
891 clone(): Request<CfHostMetadata, Cf>;
892 readonly cf?: Cf;
893 });
894 }
895 }
896 
897 void serialize(jsg::Lock& js,
898 jsg::Serializer& serializer,
899 const jsg::TypeHandler<RequestInitializerDict>& initDictHandler);
900 static jsg::Ref<Request> deserialize(jsg::Lock& js,
901 rpc::SerializationTag tag,
902 jsg::Deserializer& deserializer,
903 const jsg::TypeHandler<RequestInitializerDict>& initDictHandler);
904 
905 JSG_SERIALIZABLE(rpc::SerializationTag::REQUEST);
906 
907 void visitForMemoryInfo(jsg::MemoryTracker& tracker) const {
908 tracker.trackField("url", url);
909 tracker.trackField("headers", headers);
910 tracker.trackField("fetcher", fetcher);
911 tracker.trackField("signal", signal);
912 tracker.trackField("thisSignal", thisSignal);
913 tracker.trackField("cf", cf);
914 }
915 
916 private:
917 kj::HttpMethod method;
918 kj::String url;
919 Redirect redirect;
920 jsg::Ref<Headers> headers;
921 kj::Maybe<jsg::Ref<Fetcher>> fetcher;
922 kj::Maybe<jsg::Ref<AbortSignal>> signal;
923 
924 CacheMode cacheMode = CacheMode::NONE;
925 
926 // The fetch spec definition of Request has a distinction between the "signal" (which is
927 // an optional AbortSignal passed in with the options), and "this' signal", which is an
928 // AbortSignal that is always available via the request.signal accessor. When signal is
929 // used explicitly, thisSignal will not be.
930 kj::Maybe<jsg::Ref<AbortSignal>> thisSignal;
931 
932 CfProperty cf;
933 
934 // Controls how to handle Content-Encoding headers in the response
935 Response_BodyEncoding responseBodyEncoding = Response_BodyEncoding::AUTO;
936 
937 void visitForGc(jsg::GcVisitor& visitor) {
938 visitor.visit(headers, fetcher, signal, thisSignal, cf);
939 }
940};
941 
942class Response final: public Body {
943 public:
944 // Alias to the global Response_BodyEncoding enum for backward compatibility
945 using BodyEncoding = Response_BodyEncoding;
946 
947 Response(jsg::Lock& js,
948 int statusCode,
949 kj::Maybe<kj::String> statusText,
950 jsg::Ref<Headers> headers,
951 CfProperty&& cf,
952 kj::Maybe<Body::ExtractedBody> body,
953 kj::Array<kj::String> urlList = {},
954 kj::Maybe<jsg::Ref<WebSocket>> webSocket = kj::none,
955 BodyEncoding bodyEncoding = BodyEncoding::AUTO);
956 
957 // ---------------------------------------------------------------------------
958 // JS API
959 
960 struct InitializerDict {
961 jsg::Optional<int> status;
962 jsg::Optional<kj::String> statusText;
963 jsg::Optional<Headers::Initializer> headers;
964 
965 // Cloudflare-specific feature flags.
966 jsg::Optional<jsg::V8Ref<v8::Object>> cf;
967 
968 jsg::Optional<kj::Maybe<jsg::Ref<WebSocket>>> webSocket;
969 
970 jsg::Optional<kj::String> encodeBody;
971 
972 JSG_STRUCT(status, statusText, headers, cf, webSocket, encodeBody);
973 JSG_STRUCT_TS_OVERRIDE(ResponseInit {
974 headers?: HeadersInit;
975 encodeBody?: "automatic" | "manual";
976 });
977 };
978 
979 using Initializer = kj::OneOf<InitializerDict, jsg::Ref<Response>>;
980 
981 // Response's constructor has two arguments: an optional, nullable body that defaults to null, and
982 // an optional initializer property bag. Tragically, the only way to express the "optional,
983 // nullable body that defaults to null" is with an Optional<Maybe<Body::Initializer>>. The reason
984 // for this is because:
985 //
986 // - We need to be able to call `new Response()`, meaning the body initializer MUST be Optional.
987 // - We need to be able to call `new Response(null)`, but `null` cannot implicitly convert to
988 // an Optional, so we need an inner Maybe to inhibit string coercion to Body::Initializer.
989 static jsg::Ref<Response> constructor(jsg::Lock& js,
990 jsg::Optional<kj::Maybe<Body::Initializer>> bodyInit,
991 jsg::Optional<Initializer> maybeInit);
992 
993 // Constructs a redirection response. `status` must be a redirect status if given, otherwise it
994 // defaults to 302 (technically a non-conformity, but both Chrome and Firefox use this default).
995 //
996 // It's worth noting a couple property quirks of Responses constructed using this method:
997 // 1. `url` will be the empty string, because the response didn't actually come from any
998 // particular URL.
999 // 2. `redirected` will equal false, for the same reason as (1).
1000 // 3. `body` will be empty -- we don't even provide a default courtesy body. If you need one,
1001 // you'll need to use the regular constructor, which is more flexible.
1002 //
1003 // These behaviors surprised me, but they match both the spec and Chrome/Firefox behavior.
1004 static jsg::Ref<Response> redirect(jsg::Lock& js, kj::String url, jsg::Optional<int> status);
1005 
1006 // Constructs a `network error` response.
1007 //
1008 // A network error is a response whose status is always 0, status message is always the empty
1009 // byte sequence, header list is always empty, body is always null, and trailer is always empty.
1010 static jsg::Ref<Response> error(jsg::Lock& js);
1011 
1012 jsg::Ref<Response> clone(jsg::Lock& js);
1013 
1014 static jsg::Ref<Response> json_(
1015 jsg::Lock& js, jsg::JsValue any, jsg::Optional<Initializer> maybeInit);
1016 
1017 struct SendOptions {
1018 bool allowWebSocket = false;
1019 };
1020 
1021 // Helper not exposed to JavaScript.
1022 kj::Promise<DeferredProxy<void>> send(jsg::Lock& js,
1023 kj::HttpService::Response& outer,
1024 SendOptions options,
1025 kj::Maybe<const kj::HttpHeaders&> maybeReqHeaders);
1026 
1027 int getStatus();
1028 kj::StringPtr getStatusText();
1029 jsg::Ref<Headers> getHeaders(jsg::Lock& js);
1030 
1031 bool getOk();
1032 bool getRedirected();
1033 kj::StringPtr getUrl();
1034 
1035 kj::Maybe<jsg::Ref<WebSocket>> getWebSocket(jsg::Lock& js);
1036 
1037 // Returns the `cf` field containing Cloudflare feature flags.
1038 jsg::Optional<jsg::JsObject> getCf(jsg::Lock& js);
1039 
1040 // This relates to CORS, which doesn't apply on the edge -- see Request::Initializer::mode.
1041 // In discussing with other runtime implementations that do not implement CORS, it was
1042 // determined that only the `'default'` and `'error'` properties should be implemented.
1043 kj::StringPtr getType() {
1044 if (statusCode == 0) return "error"_kj;
1045 return "default"_kj;
1046 }
1047 
1048 JSG_RESOURCE_TYPE(Response, CompatibilityFlags::Reader flags) {
1049 JSG_INHERIT(Body);
1050 
1051 JSG_STATIC_METHOD(error);
1052 JSG_STATIC_METHOD(redirect);
1053 JSG_STATIC_METHOD_NAMED(json, json_);
1054 JSG_METHOD(clone);
1055 
1056 if (flags.getJsgPropertyOnPrototypeTemplate()) {
1057 JSG_READONLY_PROTOTYPE_PROPERTY(status, getStatus);
1058 JSG_READONLY_PROTOTYPE_PROPERTY(statusText, getStatusText);
1059 JSG_READONLY_PROTOTYPE_PROPERTY(headers, getHeaders);
1060 
1061 JSG_READONLY_PROTOTYPE_PROPERTY(ok, getOk);
1062 JSG_READONLY_PROTOTYPE_PROPERTY(redirected, getRedirected);
1063 JSG_READONLY_PROTOTYPE_PROPERTY(url, getUrl);
1064 
1065 JSG_READONLY_PROTOTYPE_PROPERTY(webSocket, getWebSocket);
1066 
1067 JSG_READONLY_PROTOTYPE_PROPERTY(cf, getCf);
1068 
1069 JSG_READONLY_PROTOTYPE_PROPERTY(type, getType);
1070 } else {
1071 JSG_READONLY_INSTANCE_PROPERTY(status, getStatus);
1072 JSG_READONLY_INSTANCE_PROPERTY(statusText, getStatusText);
1073 JSG_READONLY_INSTANCE_PROPERTY(headers, getHeaders);
1074 
1075 JSG_READONLY_INSTANCE_PROPERTY(ok, getOk);
1076 JSG_READONLY_INSTANCE_PROPERTY(redirected, getRedirected);
1077 JSG_READONLY_INSTANCE_PROPERTY(url, getUrl);
1078 
1079 JSG_READONLY_INSTANCE_PROPERTY(webSocket, getWebSocket);
1080 
1081 JSG_READONLY_INSTANCE_PROPERTY(cf, getCf);
1082 
1083 JSG_READONLY_INSTANCE_PROPERTY(type, getType);
1084 }
1085 
1086 JSG_TS_OVERRIDE({
1087 constructor(body?: BodyInit | null, init?: ResponseInit);
1088 type: 'default' | 'error';
1089 });
1090 // Use `BodyInit` and `ResponseInit` type aliases in constructor instead of inlining
1091 }
1092 
1093 void serialize(jsg::Lock& js,
1094 jsg::Serializer& serializer,
1095 const jsg::TypeHandler<InitializerDict>& initDictHandler,
1096 const jsg::TypeHandler<kj::Maybe<jsg::Ref<ReadableStream>>>& streamHandler);
1097 static jsg::Ref<Response> deserialize(jsg::Lock& js,
1098 rpc::SerializationTag tag,
1099 jsg::Deserializer& deserializer,
1100 const jsg::TypeHandler<InitializerDict>& initDictHandler,
1101 const jsg::TypeHandler<kj::Maybe<jsg::Ref<ReadableStream>>>& streamHandler);
1102 
1103 JSG_SERIALIZABLE(rpc::SerializationTag::RESPONSE);
1104 
1105 void visitForMemoryInfo(jsg::MemoryTracker& tracker) const {
1106 tracker.trackField("statusText", statusText);
1107 tracker.trackField("headers", headers);
1108 tracker.trackField("webSocket", webSocket);
1109 tracker.trackField("cf", cf);
1110 for (const auto& url: urlList) {
1111 tracker.trackField("urlList", url);
1112 }
1113 tracker.trackField("asyncContext", asyncContext);
1114 }
1115 
1116 private:
1117 int statusCode;
1118 // If the statusText is empty, we will derive it from the statusCode. If there's no
1119 // match, it will be empty.
1120 kj::Maybe<kj::String> statusText;
1121 jsg::Ref<Headers> headers;
1122 CfProperty cf;
1123 
1124 // The URL list, per the Fetch spec. Only Responses actually created by fetch() have a non-empty
1125 // URL list; for responses created from JavaScript this is empty. The list is filled in with the
1126 // sequence of URLs that fetch() requested. In redirect manual mode, this will be one element,
1127 // and just be a copy of the corresponding request's URL; in redirect follow mode the length of
1128 // the list will be one plus the number of redirects followed.
1129 //
1130 // The last URL is typically the only one that the user will care about, and is the one exposed
1131 // by getUrl().
1132 kj::Array<kj::String> urlList;
1133 
1134 // If this response represents a successful WebSocket handshake, this is the socket, and the body
1135 // is empty.
1136 kj::Maybe<jsg::Ref<WebSocket>> webSocket;
1137 
1138 // If this response is already encoded and the user don't want to encode the
1139 // body twice, they can specify encodeBody: "manual".
1140 Response::BodyEncoding bodyEncoding;
1141 
1142 // Capturing the AsyncContextFrame when the Response is created is necessary because there's
1143 // a natural separation that occurs between the moment the Response is created and when we
1144 // actually start consuming it. If a JS-backed ReadableStream is used, we end up losing the
1145 // appropriate async context in the promise read loop since that is kicked off later.
1146 kj::Maybe<jsg::Ref<jsg::AsyncContextFrame>> asyncContext;
1147 
1148 void visitForGc(jsg::GcVisitor& visitor) {
1149 visitor.visit(headers, webSocket, cf, asyncContext);
1150 }
1151};
1152 
1153class FetchEvent final: public ExtendableEvent {
1154 public:
1155 FetchEvent(jsg::Ref<Request> request)
1156 : ExtendableEvent("fetch"),
1157 request(kj::mv(request)),
1158 state(AwaitingRespondWith()) {}
1159 
1160 kj::Maybe<jsg::Promise<jsg::Ref<Response>>> getResponsePromise(jsg::Lock& js);
1161 
1162 // TODO(soon): constructor
1163 static jsg::Ref<FetchEvent> constructor(kj::String type) = delete;
1164 
1165 jsg::Ref<Request> getRequest();
1166 void respondWith(jsg::Lock& js, jsg::Promise<jsg::Ref<Response>> promise);
1167 
1168 void passThroughOnException();
1169 
1170 // TODO(someday): Do any other FetchEvent members make sense on the edge?
1171 
1172 JSG_RESOURCE_TYPE(FetchEvent) {
1173 JSG_INHERIT(ExtendableEvent);
1174 
1175 JSG_READONLY_INSTANCE_PROPERTY(request, getRequest);
1176 JSG_METHOD(respondWith);
1177 JSG_METHOD(passThroughOnException);
1178 }
1179 
1180 void visitForMemoryInfo(jsg::MemoryTracker& tracker) const {
1181 tracker.trackField("request", request);
1182 KJ_IF_SOME(respondWithCalled, state.tryGet<RespondWithCalled>()) {
1183 tracker.trackField("promise", respondWithCalled.promise);
1184 }
1185 }
1186 
1187 private:
1188 jsg::Ref<Request> request;
1189 
1190 struct AwaitingRespondWith {};
1191 struct RespondWithCalled {
1192 jsg::Promise<jsg::Ref<Response>> promise;
1193 };
1194 struct ResponseSent {};
1195 
1196 kj::OneOf<AwaitingRespondWith, RespondWithCalled, ResponseSent> state;
1197 
1198 void visitForGc(jsg::GcVisitor& visitor) {
1199 visitor.visit(request);
1200 KJ_IF_SOME(respondWithCalled, state.tryGet<RespondWithCalled>()) {
1201 visitor.visit(respondWithCalled.promise);
1202 }
1203 }
1204};
1205 
1206jsg::Promise<jsg::Ref<Response>> fetchImpl(jsg::Lock& js,
1207 kj::Maybe<jsg::Ref<Fetcher>> fetcher, // if null, use fetcher from request object
1208 Request::Info requestOrUrl,
1209 jsg::Optional<Request::Initializer> requestInit);
1210 
1211jsg::Ref<Response> makeHttpResponse(jsg::Lock& js,
1212 kj::HttpMethod method,
1213 kj::Vector<kj::Url> urlList,
1214 uint statusCode,
1215 kj::StringPtr statusText,
1216 const kj::HttpHeaders& headers,
1217 kj::Own<kj::AsyncInputStream> body,
1218 kj::Maybe<jsg::Ref<WebSocket>> webSocket,
1219 Response::BodyEncoding bodyEncoding = Response::BodyEncoding::AUTO,
1220 kj::Maybe<jsg::Ref<AbortSignal>> signal = kj::none);
1221 
1222#define EW_HTTP_ISOLATE_TYPES \
1223 api::FetchEvent, api::Headers, api::Headers::EntryIterator, api::Headers::EntryIterator::Next, \
1224 api::Headers::KeyIterator, api::Headers::KeyIterator::Next, api::Headers::ValueIterator, \
1225 api::Headers::ValueIterator::Next, api::Body, api::Response, api::Response::InitializerDict, \
1226 api::Request, api::Request::InitializerDict, api::Fetcher, api::Fetcher::PutOptions, \
1227 api::Fetcher::ScheduledOptions, api::Fetcher::ScheduledResult, api::Fetcher::QueueResult, \
1228 api::Fetcher::ServiceBindingQueueMessage
1229 
1230// The list of http.h types that are added to worker.c++'s JSG_DECLARE_ISOLATE_TYPE
1231} // namespace workerd::api