@@ -22,12 +22,7 @@ import {
2222 WORKER_BINDING_SERVICE_LOOPBACK ,
2323 kProxyNodeBinding ,
2424} from "../shared" ;
25- import {
26- HEADER_SITES ,
27- KV_PLUGIN_NAME ,
28- MAX_LIST_KEYS ,
29- PARAM_URL_ENCODED ,
30- } from "./constants" ;
25+ import { HEADER_SITES , KV_PLUGIN_NAME , MAX_LIST_KEYS } from "./constants" ;
3126import {
3227 KVGatewayGetOptions ,
3328 KVGatewayGetResult ,
@@ -43,7 +38,7 @@ async function* listKeysInDirectoryInner(
4338) : AsyncGenerator < string > {
4439 const fileEntries = await fs . readdir ( currentPath , { withFileTypes : true } ) ;
4540 for ( const fileEntry of fileEntries ) {
46- const filePath = path . join ( currentPath , fileEntry . name ) ;
41+ const filePath = path . posix . join ( currentPath , fileEntry . name ) ;
4742 if ( fileEntry . isDirectory ( ) ) {
4843 yield * listKeysInDirectoryInner ( rootPath , filePath ) ;
4944 } else {
@@ -70,6 +65,20 @@ export interface SiteMatcherRegExps {
7065// Cache glob RegExps between `getBindings` and `getServices` calls
7166const sitesRegExpsCache = new WeakMap < SitesOptions , SiteMatcherRegExps > ( ) ;
7267
68+ function serialiseSiteRegExps ( exps : SiteMatcherRegExps ) {
69+ return {
70+ include : exps . include && serialiseRegExps ( exps . include ) ,
71+ exclude : exps . exclude && serialiseRegExps ( exps . exclude ) ,
72+ } ;
73+ }
74+
75+ function deserialiseSiteRegExps ( exps : ReturnType < typeof serialiseSiteRegExps > ) {
76+ return {
77+ include : exps . include && deserialiseRegExps ( exps . include ) ,
78+ exclude : exps . exclude && deserialiseRegExps ( exps . exclude ) ,
79+ } ;
80+ }
81+
7382function testSiteRegExps ( regExps : SiteMatcherRegExps , key : string ) : boolean {
7483 return (
7584 // Either include globs undefined, or name matches them
@@ -104,77 +113,25 @@ const SERVICE_NAMESPACE_SITE = `${KV_PLUGIN_NAME}:site`;
104113
105114const BINDING_KV_NAMESPACE_SITE = "__STATIC_CONTENT" ;
106115const BINDING_JSON_SITE_MANIFEST = "__STATIC_CONTENT_MANIFEST" ;
107- const BINDING_JSON_SITE_FILTER = "MINIFLARE_SITE_FILTER" ;
116+ const BINDING_TEXT_SITE_FILTER = "MINIFLARE_SITE_FILTER" ;
108117
109118const SCRIPT_SITE = `
110- // Inject key encoding/decoding functions
111- const SITES_NO_CACHE_PREFIX = "${ SITES_NO_CACHE_PREFIX } ";
112- const encodeSitesKey = ${ encodeSitesKey . toString ( ) } ;
113- const decodeSitesKey = ${ decodeSitesKey . toString ( ) } ;
114-
115- // Inject glob matching RegExp functions
116- const deserialiseRegExps = ${ deserialiseRegExps . toString ( ) } ;
117- const testRegExps = ${ testRegExps . toString ( ) } ;
118- const testSiteRegExps = ${ testSiteRegExps . toString ( ) } ;
119-
120- // Deserialise glob matching RegExps
121- const serialisedSiteRegExps = ${ BINDING_JSON_SITE_FILTER } ;
122- const siteRegExps = {
123- include: serialisedSiteRegExps.include && deserialiseRegExps(serialisedSiteRegExps.include),
124- exclude: serialisedSiteRegExps.exclude && deserialiseRegExps(serialisedSiteRegExps.exclude),
125- };
126-
127- async function handleRequest(request) {
119+ function handleRequest(request) {
128120 // Only permit reads
129121 if (request.method !== "GET") {
130122 const message = \`Cannot \${request.method.toLowerCase()}() with read-only Workers Sites namespace\`;
131123 return new Response(message, { status: 405, statusText: message });
132124 }
133-
134- // Decode key (empty if listing)
135- const url = new URL(request.url);
136- let key = url.pathname.substring(1); // Strip leading "/"
137- if (url.searchParams.get("${ PARAM_URL_ENCODED } ")?.toLowerCase() === "true") {
138- key = decodeURIComponent(key);
139- }
140-
141- // Strip SITES_NO_CACHE_PREFIX
142- key = decodeSitesKey(key);
143125
144- // If not listing keys, check key is included, returning not found if not
145- if (key !== "" && !testSiteRegExps(siteRegExps, key)) {
146- return new Response("Not Found", { status: 404, statusText: "Not Found" })
147- }
148-
149- // Re-encode key
150- key = encodeURIComponent(key);
151- url.pathname = \`/${ KV_PLUGIN_NAME } /${ BINDING_KV_NAMESPACE_SITE } /\${key}\`;
152- url.searchParams.set("${ PARAM_URL_ENCODED } ", "true"); // Always URL encoded now
126+ const url = new URL(request.url);
127+ url.pathname = \`/${ KV_PLUGIN_NAME } /${ BINDING_KV_NAMESPACE_SITE } /\${url.pathname}\`;
153128
154- // Send request to loopback server
155129 request = new Request(url, request);
156130 request.headers.set("${ HEADER_PERSIST } ", ${ BINDING_TEXT_PERSIST } );
157131 // Add magic header to indicate namespace should be ignored, and persist
158132 // should be used as the root without any additional namespace
159- request.headers.set("${ HEADER_SITES } ", "true");
160- const response = await ${ CoreBindings . SERVICE_LOOPBACK } .fetch(request);
161-
162- // If listing keys, only return included keys, and add SITES_NO_CACHE_PREFIX
163- // to all result keys
164- if (key === "" && response.ok) {
165- const { keys, list_complete, cursor } = await response.json();
166- return Response.json({
167- keys: keys.filter((key) => {
168- if (!testSiteRegExps(siteRegExps, key)) return false;
169- key.name = encodeSitesKey(key.name);
170- return true;
171- }),
172- list_complete,
173- cursor,
174- });
175- }
176-
177- return response;
133+ request.headers.set("${ HEADER_SITES } ", ${ BINDING_TEXT_SITE_FILTER } );
134+ return ${ CoreBindings . SERVICE_LOOPBACK } .fetch(request);
178135}
179136
180137addEventListener("fetch", (event) => event.respondWith(handleRequest(event.request)));
@@ -252,10 +209,7 @@ export function getSitesService(options: SitesOptions): Service {
252209 const siteRegExps = sitesRegExpsCache . get ( options ) ;
253210 assert ( siteRegExps !== undefined ) ;
254211 // Ensure `siteRegExps` is JSON-serialisable
255- const serialisedSiteRegExps = {
256- include : siteRegExps . include && serialiseRegExps ( siteRegExps . include ) ,
257- exclude : siteRegExps . exclude && serialiseRegExps ( siteRegExps . exclude ) ,
258- } ;
212+ const serialisedSiteRegExps = serialiseSiteRegExps ( siteRegExps ) ;
259213
260214 // Use unsanitised file storage to ensure file names containing e.g. dots
261215 // resolve correctly.
@@ -273,8 +227,8 @@ export function getSitesService(options: SitesOptions): Service {
273227 text : JSON . stringify ( persist ) ,
274228 } ,
275229 {
276- name : BINDING_JSON_SITE_FILTER ,
277- json : JSON . stringify ( serialisedSiteRegExps ) ,
230+ name : BINDING_TEXT_SITE_FILTER ,
231+ text : JSON . stringify ( serialisedSiteRegExps ) ,
278232 } ,
279233 ] ,
280234 } ,
@@ -288,13 +242,19 @@ export function getSitesService(options: SitesOptions): Service {
288242
289243export async function sitesGatewayGet (
290244 persist : Persistence ,
245+ serialisedSiteRegExps : string ,
291246 key : string ,
292247 opts ?: KVGatewayGetOptions
293248) : Promise < KVGatewayGetResult | undefined > {
294249 // `persist` is a resolved path set in `getSitesService()`
295250 assert ( typeof persist === "string" ) ;
251+ const siteRegExps = deserialiseSiteRegExps ( JSON . parse ( serialisedSiteRegExps ) ) ;
296252
297253 validateGetOptions ( key , opts ) ;
254+
255+ key = decodeSitesKey ( key ) ;
256+ if ( ! testSiteRegExps ( siteRegExps , key ) ) return ;
257+
298258 const filePath = path . join ( persist , key ) ;
299259 if ( ! filePath . startsWith ( persist ) ) return ;
300260 try {
@@ -314,17 +274,21 @@ export async function sitesGatewayGet(
314274
315275export async function sitesGatewayList (
316276 persist : Persistence ,
277+ serialisedSiteRegExps : string ,
317278 opts : KVGatewayListOptions = { }
318279) : Promise < KVGatewayListResult > {
319280 // `persist` is a resolved path set in `getSitesService()`
320281 assert ( typeof persist === "string" ) ;
282+ const siteRegExps = deserialiseSiteRegExps ( JSON . parse ( serialisedSiteRegExps ) ) ;
321283
322284 validateListOptions ( opts ) ;
323285 const { limit = MAX_LIST_KEYS , prefix, cursor } = opts ;
324286
325287 // Get sorted array of all keys matching prefix
326288 let keys : KVGatewayListResult [ "keys" ] = [ ] ;
327- for await ( const name of listKeysInDirectory ( persist ) ) {
289+ for await ( let name of listKeysInDirectory ( persist ) ) {
290+ if ( ! testSiteRegExps ( siteRegExps , name ) ) continue ;
291+ name = encodeSitesKey ( name ) ;
328292 if ( prefix === undefined || name . startsWith ( prefix ) ) keys . push ( { name } ) ;
329293 }
330294 keys . sort ( ( a , b ) => lexicographicCompare ( a . name , b . name ) ) ;
0 commit comments