Skip to content
File

Blob: src/workerd/server/fallback-service.h

cpp110 lines
1#pragma once
2 
3#include <workerd/server/workerd.capnp.h>
4 
5#include <kj/common.h>
6#include <kj/map.h>
7#include <kj/mutex.h>
8#include <kj/one-of.h>
9#include <kj/string.h>
10#include <kj/thread.h>
11 
12namespace workerd::fallback {
13 
14// The fallback service is a mechanism used only in workerd local development.
15// It is used to use an external http service to resolve module specifiers
16// dynamically if the module is not found in the static bundles. A worker
17// must be configured to use the fallback service and workerd must be started
18// with the --experimental CLI flag.
19//
20// There are two versions of the fallback service protocol:
21//
22// V1: The request is sent to the fallback service as a GET request using
23// query strings to pass the details. The specifier and referrer are treated
24// as strings. Import attributes are not included.
25//
26// V2: The request is sent to the fallback service as a POST request using
27// JSON to pass the details. The specifier and referrer are treated as URLs.
28// Import attributes are included.
29//
30// The fallback service may return either a JSON string describing the module
31// configuration, a 301 redirect to a different module specifier, or an error.
32enum class ImportType {
33 // The import is a static or dynamic import
34 IMPORT,
35 // The import is a CommonJs-style require()
36 REQUIRE,
37 // The import originated from inside the runtime
38 INTERNAL,
39};
40 
41enum class Version {
42 // With V1 of the fallback service, the request is sent to the fallback
43 // service as a GET request using query strings to pass the details.
44 // The specifier and referrer are treated as strings. Import attributes
45 // are not included.
46 V1,
47 // With V2 of the fallback service, the request is sent to the fallback
48 // service as a POST request using JSON to pass the details. The specifier
49 // and referrer are treated as URLs. Import attributes are included.
50 V2,
51};
52 
53using ModuleOrRedirect =
54 kj::Maybe<kj::OneOf<kj::String, kj::Own<server::config::Worker::Module::Reader>>>;
55 
56// A persistent client for the fallback service that uses a single background
57// thread with a long-lived HTTP client for all module resolution requests.
58// This avoids creating a new OS thread, DNS lookup, and TCP connection for
59// each request, which can exhaust ephemeral ports when many modules are
60// resolved concurrently (e.g. running many test files with vitest-pool-workers).
61//
62// IMPORTANT: This class supports only one caller at a time. tryResolve() will
63// assert if called concurrently. Module resolution in workerd is single-threaded
64// per isolate/registry so this is safe in practice.
65class FallbackServiceClient {
66 public:
67 explicit FallbackServiceClient(kj::String address);
68 ~FallbackServiceClient() noexcept(false);
69 
70 KJ_DISALLOW_COPY_AND_MOVE(FallbackServiceClient);
71 
72 ModuleOrRedirect tryResolve(Version version,
73 ImportType type,
74 kj::StringPtr specifier,
75 kj::StringPtr rawSpecifier,
76 kj::StringPtr referrer,
77 const kj::HashMap<kj::StringPtr, kj::StringPtr>& attributes);
78 
79 private:
80 // Shared state between the calling thread and the background thread.
81 // Access is serialized through kj::MutexGuarded.
82 struct SharedState {
83 // Request fields - valid only when hasRequest is true.
84 // These contain non-owning references into the caller's stack frame,
85 // which is safe because the caller blocks until responseReady is set.
86 Version version = Version::V1;
87 ImportType type = ImportType::IMPORT;
88 kj::StringPtr specifier;
89 kj::StringPtr rawSpecifier;
90 kj::StringPtr referrer;
91 const kj::HashMap<kj::StringPtr, kj::StringPtr>* attributes = nullptr;
92 bool hasRequest = false;
93 
94 // Response field - valid only when responseReady is true.
95 ModuleOrRedirect response;
96 bool responseReady = false;
97 
98 // Set to true to signal the background thread to exit.
99 bool shutdown = false;
100 };
101 
102 kj::String ownedAddress;
103 kj::MutexGuarded<SharedState> state;
104 kj::Thread thread;
105 
106 void threadMain();
107};
108 
109} // namespace workerd::fallback