Skip to content

Commit

Permalink
more
Browse files Browse the repository at this point in the history
Signed-off-by: flakey5 <[email protected]>
  • Loading branch information
flakey5 committed Jun 22, 2024
1 parent 7fc1b5e commit fe29bd5
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 31 deletions.
3 changes: 2 additions & 1 deletion src/middleware/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { IRequest } from 'itty-router';
import { Context } from '../context';

export type MiddlewareNext = () => Promise<Response>;

export interface Middleware {
handle(
request: Request,
request: IRequest,
ctx: Context,
next: MiddlewareNext
): Promise<Response>;
Expand Down
31 changes: 23 additions & 8 deletions src/middleware/originMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
import { Context } from 'toucan-js/dist/types';
import { Context } from '../context';
import responses from '../responses';
import { parseUrl } from '../utils/request';
import { Middleware } from './middleware';

export class R2Middleware implements Middleware {
handle(
request: Request,
ctx: Context,
next?: (request: Request) => Promise<Response>
): Promise<Response> {
throw new Error();
export class OriginMiddleware implements Middleware {
handle(request: Request, ctx: Context): Promise<Response> {
const url = parseUrl(request);
if (url === undefined) {
return Promise.resolve(responses.badRequest());
}

const res = fetch(ctx.env.ORIGIN_HOST + url.pathname, {
method: request.method,
headers: {
'user-agent': 'release-cloudflare-worker',
'if-match': request.headers.get('if-match') ?? '',
'if-none-match': request.headers.get('if-none-match') ?? '',
'if-modified-since': request.headers.get('if-modified-since') ?? '',
'if-unmodified-since': request.headers.get('if-unmodified-since') ?? '',
range: request.headers.get('if-match') ?? '',
},
});

return res;
}
}
75 changes: 61 additions & 14 deletions src/middleware/r2Middleware.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import { IRequest } from 'itty-router';
import { CACHE_HEADERS } from '../constants/cache';
import { Context } from '../context';
import { R2Provider } from '../providers/r2Provider';
import responses from '../responses';
import { isDirectoryPath } from '../utils/path';
import {
enforceDirectoryPathRestrictions,
isDirectoryPath,
} from '../utils/path';
import { parseUrl } from '../utils/request';
import { Middleware, MiddlewareNext } from './middleware';

// @ts-expect-error ctx
const r2Provider = new R2Provider();
import { Middleware } from './middleware';

export class R2Middleware implements Middleware {
handle(
request: Request,
ctx: Context,
): Promise<Response> {
handle(request: IRequest, ctx: Context): Promise<Response> {
switch (request.method) {
case 'HEAD':
return head(request);
return head(request, ctx);
default:
throw new Error(
`${request.method} ${request.url} unexpectedly mapped to R2Middleware`
Expand All @@ -24,18 +23,50 @@ export class R2Middleware implements Middleware {
}
}

async function head(request: Request): Promise<Response> {
async function head(request: IRequest, ctx: Context): Promise<Response> {
const url = parseUrl(request);
if (url === undefined) {
return responses.badRequest();
}

const isPathADirectory = isDirectoryPath(url.pathname);
const provider = new R2Provider({ ctx });

const path = getR2Path(request, url);
const isPathADirectory = isDirectoryPath(path);

if (isPathADirectory) {
throw 'todo';
const errorResponse = enforceDirectoryPathRestrictions(
path,
request,
ctx.env
);
if (errorResponse !== undefined) {
return errorResponse;
}

const result = await provider.readDirectory(path);
if (result === undefined) {
return responses.directoryNotFound(request);
}

if ('body' in result) throw new Error('todo remove this possibility');

let lastModified: Date | undefined = undefined;
for (const file of result.files) {
if (lastModified === undefined || file.lastModified > lastModified) {
lastModified = file.lastModified;
}
}

return new Response(undefined, {
headers: {
'last-modified': (lastModified ?? new Date()).toUTCString(),
'content-type': 'text/html',
'cache-control': CACHE_HEADERS.success,
},
});
} else {
const result = await r2Provider.headFile(url.pathname);
const result = await provider.headFile(path);
if (result === undefined) {
return responses.fileNotFound(request);
}
Expand All @@ -46,3 +77,19 @@ async function head(request: Request): Promise<Response> {
});
}
}

function getR2Path(request: IRequest, url: URL): string {
if (!('path' in request.params)) {
// /metrics probably, no need to modify
return url.pathname;
}

const { path } = request.params;
if (url.pathname.startsWith('/dist')) {
return `nodejs/releases/${path}`;
} else if (url.pathname.startsWith('/download')) {
return `nodejs/${path}`;
} else {
throw new Error('todo what other cases');
}
}
27 changes: 27 additions & 0 deletions src/routes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Middleware } from '../middleware/middleware';
import { OriginMiddleware } from '../middleware/originMiddleware';
import { R2Middleware } from '../middleware/r2Middleware';
import { Router } from './router';

export function registerRoutes(router: Router) {

Check failure on line 6 in src/routes/index.ts

View workflow job for this annotation

GitHub Actions / Check Linting and Formatting

Missing return type on function
router.get('/robots.txt', []);

const tmp: Middleware = {

Check failure on line 9 in src/routes/index.ts

View workflow job for this annotation

GitHub Actions / Check Linting and Formatting

'tmp' is assigned a value but never used. Allowed unused vars must match /^_/u
handle: async request => {
return Response.json(request.params);
},
};
const r2Middleware = new R2Middleware();
const originMiddleware = new OriginMiddleware();

router.head('/metrics*', [r2Middleware, originMiddleware]);
router.get('/metrics*', [/*r2Middleware, */ originMiddleware]);

router.head('/dist:path+', [r2Middleware, originMiddleware]);
router.get('/dist:path+', [r2Middleware, originMiddleware]);

router.head('/download:path+', [r2Middleware, originMiddleware]);
router.get('/download:path+', [r2Middleware, originMiddleware]);
}

export * from './router';
16 changes: 9 additions & 7 deletions src/routes/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { Context } from '../context';
export class Router {
private itty = AutoRouter<IRequest, [Context]>();

handle(request: Request): Promise<Response> {
return this.itty.fetch(request);
handle(request: Request, ctx: Context): Promise<Response> {
return this.itty.fetch(request, ctx);
}

head(endpoint: string, middlewares: Middleware[]): void {
Expand All @@ -26,17 +26,18 @@ export class Router {
}
}

type MiddlewareChain = (request: Request, ctx: Context) => Promise<Response>;
type MiddlewareChain = (request: IRequest, ctx: Context) => Promise<Response>;

function buildMiddlewareChain(middlewares: Middleware[]): MiddlewareChain {
let root: MiddlewareChain = () => {
throw new Error('reached the end of middleware chain');
};

for (let i = middlewares.length - 1; i >= 0; i--) {
// TODO fix the error handler is broken?
// const middleware = errorHandled(middlewares[i]);
const middleware = middlewares[i];
root = (request, ctx) => {

Check failure on line 40 in src/routes/router.ts

View workflow job for this annotation

GitHub Actions / Check Linting and Formatting

Missing return type on function
const middleware = errorHandled(middlewares[i]);

return middleware.handle(request, ctx, () => {
return root(request, ctx);
});
Expand All @@ -48,11 +49,12 @@ function buildMiddlewareChain(middlewares: Middleware[]): MiddlewareChain {

function errorHandled(middleware: Middleware): Middleware {

Check failure on line 50 in src/routes/router.ts

View workflow job for this annotation

GitHub Actions / Check Linting and Formatting

'errorHandled' is defined but never used. Allowed unused vars must match /^_/u
const wrapper: Middleware = {
handle(request: Request, ctx: Context, next) {
handle(request: IRequest, ctx: Context, next) {
try {
return middleware.handle(request, ctx, next);
} catch (err) {
// TODO send error to sentry
// TODO: add some error context
ctx.sentry.captureException(err);
return next();
}
},
Expand Down
20 changes: 20 additions & 0 deletions src/utils/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
URL_TO_BUCKET_PATH_MAP,
} from '../constants/r2Prefixes';
import { Env } from '../env';
import responses from '../responses';

/**
* Maps a path in a url to the path to the resource
Expand Down Expand Up @@ -201,3 +202,22 @@ export function isExtensionless(path: string): boolean {
export function isDirectoryPath(path: string): boolean {
return hasTrailingSlash(path) || isExtensionless(path);
}

export function enforceDirectoryPathRestrictions(
path: string,
request: Pick<Request, 'method'>,
env: Pick<Env, 'DIRECTORY_LISTING'>
): Response | undefined {
if (env.DIRECTORY_LISTING === 'off') {
return responses.fileNotFound(request);
}

if (!hasTrailingSlash(path)) {
// We always want to have trailing slashes for directory listings
path += '/';

return Response.redirect(path, 301);
}

return undefined;
}
4 changes: 3 additions & 1 deletion src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Toucan } from 'toucan-js';
import responses from './responses';
import { Context } from './context';
import { Router } from './routes/router';
import { registerRoutes } from './routes';

interface Worker {
/**
Expand All @@ -13,6 +14,7 @@ interface Worker {
}

const router: Router = new Router();
registerRoutes(router);
// TODO: routeRegistry.register();

const cloudflareWorker: Worker = {
Expand All @@ -34,7 +36,7 @@ const cloudflareWorker: Worker = {
execution: ctx,
};
// TODO: how to we pass context
return await router.handle(request);
return await router.handle(request, context);
} catch (e) {
// Send to sentry, if it's disabled this will just noop
sentry.captureException(e);
Expand Down

0 comments on commit fe29bd5

Please sign in to comment.