@schoolofmotion/rocket-booster
Version:
Serverless reverse proxy and load balancing library built for Cloudflare Workers.
92 lines (82 loc) • 2.1 kB
text/typescript
import { useFirewall } from './firewall';
import { useHeaders } from './headers';
import { useLoadBalancing } from './load-balancing';
import { useUpstream } from './upstream';
import { useCORS } from './cors';
import { useRewrite } from './rewrite';
import { WorkersKV } from './storage';
import { createResponse, getHostname } from './utils';
import { usePipeline } from './middleware';
import { Proxy, Options, Route } from '../types/proxy';
import { Context } from '../types/middleware';
const filter = (
request: Request,
routes: Route[],
): Options | null => {
const url = new URL(request.url);
for (const { pattern, options } of routes) {
if (
options.methods === undefined
|| options.methods.includes(request.method)
) {
const regex = new RegExp(`^${pattern.replace(/\*/g, '.*')}`);
if (regex.test(url.pathname)) {
return options;
}
}
}
return null;
};
export default function useProxy(
globalOptions?: Partial<Options>,
): Proxy {
const pipeline = usePipeline(
useFirewall,
useLoadBalancing,
useHeaders,
useCORS,
useRewrite,
useUpstream,
);
const routes: Route[] = [];
const use = (
pattern: string,
options: Options,
) => {
routes.push({
pattern,
options: {
...globalOptions,
...options,
},
});
};
const apply = async (
request: Request,
): Promise<Response> => {
const options = filter(request, routes);
if (options === null) {
return createResponse('Failed to find a route that matches the path and method of the current request', 500);
}
const context: Context = {
request,
options,
hostname: getHostname(request),
response: new Response('Unhandled response'),
storage: new WorkersKV(),
upstream: null,
};
try {
await pipeline.execute(context);
} catch (error) {
if (error instanceof Error) {
context.response = createResponse(error.message, 500);
}
}
return context.response;
};
return {
use,
apply,
};
}