Skip to content

Instantly share code, notes, and snippets.

@coolov
Created June 15, 2021 21:42
Show Gist options
  • Save coolov/a0acc937600a77918bdd62bca41482b7 to your computer and use it in GitHub Desktop.
Save coolov/a0acc937600a77918bdd62bca41482b7 to your computer and use it in GitHub Desktop.
http-cache-node.ts
import Database, { Statement, Options } from "better-sqlite3";
import { Request, Response } from "node-fetch";
interface Record {
url: string;
status: number;
statusText: string;
body: Buffer;
headers: string;
insertTime: number;
cacheControl?: string;
lastModified?: string;
etag?: string;
}
interface Headers {
[key: string]: any;
}
class DB {
insertStmt: Statement;
readStmt: Statement;
constructor(name: string, options?: Options) {
const db = new Database(name, options);
// bootstrap the database
db.exec(`
CREATE TABLE IF NOT EXISTS httpCache (
url TEXT PRIMARY KEY,
status INTEGER,
statusText TEXT,
headers TEXT,
insertTime INTEGER,
cacheControl TEXT,
lastModified TEXT,
etag TEXT,
body BLOB
);
`);
// prepare statements
this.insertStmt = db.prepare(
`INSERT OR IGNORE INTO httpCache (
url,
status,
statusText,
headers,
cacheControl,
lastModified,
etag,
body,
insertTime
) VALUES (
@url,
@status,
@statusText,
@headers,
@cacheControl,
@lastModified,
@etag,
@body,
@insertTime
)`
);
this.readStmt = db.prepare(
`SELECT
headers,
status,
statusText,
body
FROM
httpCache
WHERE url = @url
`
);
}
add(record: Record) {
return this.insertStmt.run(record);
}
get(url: string): Record {
const result = this.readStmt.get({ url });
return result;
}
}
export default class HTTPCache {
db: DB;
constructor(name: string, options?: Options) {
this.db = new DB(name, options);
}
async add(response: Response) {
const body = await response.buffer();
const { statusText, url, status } = response;
const headers: Headers = {};
// i bet there is an easier way
for (const [key, value] of response.headers.entries()) {
headers[key] = value;
}
// store cache directives in seperate fields for easy access
const cacheControl = headers["cache-control"];
const lastModified = headers["last-modified"];
const etag = headers["etag"];
const insertTime = new Date().getTime();
this.db.add({
statusText,
url,
status,
headers: JSON.stringify(headers),
body,
insertTime,
cacheControl,
lastModified,
etag,
});
}
async match(request: Request): Promise<Response | undefined> {
const url = request.url;
const response = this.db.get(url);
if (response !== undefined) {
const { body, headers, status, statusText } = response;
return new Response(body, {
status,
statusText,
headers: JSON.parse(headers),
});
}
return undefined;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment