From 18aca9b5cd7a2154ad079493415bccab9121ef9f Mon Sep 17 00:00:00 2001 From: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> Date: Fri, 20 Oct 2023 15:37:24 -0600 Subject: [PATCH 1/2] =?UTF-8?q?fix(server):=20=F0=9F=A9=B9=20Using=20sessi?= =?UTF-8?q?onID=20as=20a=20fallback=20to=20requests=20where=20referer=20is?= =?UTF-8?q?=20missing.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> --- package-lock.json | 77 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 ++ src/heliaServer.ts | 52 ++++++++++++++++++++++--------- src/index.ts | 13 +++++--- 4 files changed, 126 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index 18b7285..6cc2703 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@helia/unixfs": "1.x", "express": "4.x", "express-prom-bundle": "6.x", + "express-session": "1.17.3", "file-type": "18.x", "helia": "2.x", "lru-cache": "10.x", @@ -20,6 +21,7 @@ }, "devDependencies": { "@types/express": "4.x", + "@types/express-session": "1.17.9", "@types/mime-types": "2.x", "@types/node": "20.x", "aegir": "40.x", @@ -3828,6 +3830,15 @@ "@types/send": "*" } }, + "node_modules/@types/express-session": { + "version": "1.17.9", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.9.tgz", + "integrity": "sha512-yIqficLlTPdloeEPhOVenpOUWILkdaXHUWhTOqFGx9JoSuTgeatNjb97k8VvJehbTk0kUSUAHy5r27PXMga89Q==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.2.tgz", @@ -8846,6 +8857,45 @@ "prom-client": ">=12.0.0" } }, + "node_modules/express-session": { + "version": "1.17.3", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz", + "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==", + "dependencies": { + "cookie": "0.4.2", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express-session/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express-session/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express-session/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -18745,6 +18795,14 @@ "node": ">= 0.8" } }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -20592,6 +20650,14 @@ "integrity": "sha512-GXpfrYVPwx3K7RQ6aYT8KPS8XViSXUVJT1ONhoKPE9VAleW42YE+U+8VEyGWt41EnEQW7gwecYJriTI0pKoecQ==", "dev": true }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -24408,6 +24474,17 @@ "node": ">=0.8.0" } }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/uint8-varint": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/uint8-varint/-/uint8-varint-2.0.1.tgz", diff --git a/package.json b/package.json index b392e63..603271f 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "homepage": "https://github.com/whizzzkid/helia-docker#readme", "devDependencies": { "@types/express": "4.x", + "@types/express-session": "1.17.9", "@types/mime-types": "2.x", "@types/node": "20.x", "aegir": "40.x", @@ -48,6 +49,7 @@ "@helia/unixfs": "1.x", "express": "4.x", "express-prom-bundle": "6.x", + "express-session": "1.17.3", "file-type": "18.x", "helia": "2.x", "lru-cache": "10.x", diff --git a/src/heliaServer.ts b/src/heliaServer.ts index c8eb047..c2edcf4 100644 --- a/src/heliaServer.ts +++ b/src/heliaServer.ts @@ -59,23 +59,36 @@ export class HeliaServer { ] } + /** + * Computes referer path for the request. + */ + private fetchRefererPath ({ request }: IRouteHandler): string { + let refererPath = new URL(request.headers.referer ?? 'http://ipfs.io').pathname + if (refererPath === '/') { + refererPath = request.sessionID + } + + if (refererPath !== undefined) { + return refererPath + } + + throw new Error('Error calculating referer') + } + /** * Handles redirecting to the relative path */ private async redirectRelative ({ request, response }: IRouteHandler): Promise { try { - const referrerPath = new URL(request.headers.referer ?? '').pathname - if (referrerPath !== undefined) { - this.log('Referer found:', referrerPath) - let relativeRedirectPath = `${referrerPath}${request.path}` - const { namespace, address } = this.heliaFetch.parsePath(referrerPath) - if (namespace === 'ipns') { - relativeRedirectPath = `/${namespace}/${address}${request.path}` - } - // absolute redirect - this.log('Redirecting to relative to referer:', referrerPath) - response.redirect(relativeRedirectPath) + const refererPath = this.fetchRefererPath({ request, response }) + let relativeRedirectPath = `${refererPath}${request.path}` + const { namespace, address } = this.heliaFetch.parsePath(refererPath) + if (namespace === 'ipns') { + relativeRedirectPath = `/${namespace}/${address}${request.path}` } + // absolute redirect + this.log('Redirecting to relative to referer:', refererPath) + response.redirect(relativeRedirectPath) } catch (error) { this.log('Error redirecting to relative path:', error) response.status(500).end() @@ -113,9 +126,8 @@ export class HeliaServer { address: reqDomain } = this.heliaFetch.parsePath(request.path) - if (request.headers.referer !== undefined) { - this.log('Referer found:', request.headers.referer) - const refererPath = new URL(request.headers.referer).pathname + try { + const refererPath = this.fetchRefererPath({ request, response }) const { namespace: refNamespace, address: refDomain @@ -131,6 +143,8 @@ export class HeliaServer { return true } } + } catch (error) { + this.log('Error checking for additional redirection:', error) } return false } @@ -190,4 +204,14 @@ export class HeliaServer { await this.heliaFetch.node?.gc() response.status(200).end() } + + /** + * Generates a session ID for the request. + * This is a very ghetto way of identifying what the root ipns path for a request is. + * Overloading the sessionID the first request allows us to use the same session for all subsequent requests. Mostly! + * In many cases it won't work, but the browser won't care for those either. + */ + public sessionId (request: Request): string { + return request.sessionID ?? request.path + } } diff --git a/src/index.ts b/src/index.ts index 02e923c..2d3a233 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,21 +1,26 @@ import debug from 'debug' import express from 'express' import promBundle from 'express-prom-bundle' +import session from 'express-session' import { HeliaServer, type IRouteEntry } from './heliaServer.js' const logger = debug('helia-server') -const app = express() const promMetricsMiddleware = promBundle({ includeMethod: true }) +const heliaServer = new HeliaServer(logger) +await heliaServer.isReady + // Constants const PORT = (process?.env?.PORT ?? 8080) as number const HOST = process?.env?.HOST ?? '0.0.0.0' // Add the prometheus middleware +const app = express() app.use(promMetricsMiddleware) - -const heliaServer = new HeliaServer(logger) -await heliaServer.isReady +app.use(session({ + genid: heliaServer.sessionId, + secret: 'very secret value' +})) // Add the routes app.get('/', (req, res) => { From 693001e1f5149028f450596270e8b8a8cc999efa Mon Sep 17 00:00:00 2001 From: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> Date: Mon, 23 Oct 2023 02:26:44 -0600 Subject: [PATCH 2/2] fix(server): PR review Signed-off-by: Nishant Arora <1895906+whizzzkid@users.noreply.github.com> --- src/heliaServer.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/heliaServer.ts b/src/heliaServer.ts index c2edcf4..04c190e 100644 --- a/src/heliaServer.ts +++ b/src/heliaServer.ts @@ -62,8 +62,9 @@ export class HeliaServer { /** * Computes referer path for the request. */ - private fetchRefererPath ({ request }: IRouteHandler): string { - let refererPath = new URL(request.headers.referer ?? 'http://ipfs.io').pathname + private getRefererFromRouteHandler ({ request }: IRouteHandler): string { + // this defaults to hostname because we want '/' to be the default referer path. + let refererPath = new URL(request.headers.referer ?? request.hostname).pathname if (refererPath === '/') { refererPath = request.sessionID } @@ -80,7 +81,7 @@ export class HeliaServer { */ private async redirectRelative ({ request, response }: IRouteHandler): Promise { try { - const refererPath = this.fetchRefererPath({ request, response }) + const refererPath = this.getRefererFromRouteHandler({ request, response }) let relativeRedirectPath = `${refererPath}${request.path}` const { namespace, address } = this.heliaFetch.parsePath(refererPath) if (namespace === 'ipns') { @@ -127,7 +128,7 @@ export class HeliaServer { } = this.heliaFetch.parsePath(request.path) try { - const refererPath = this.fetchRefererPath({ request, response }) + const refererPath = this.getRefererFromRouteHandler({ request, response }) const { namespace: refNamespace, address: refDomain