Skip to content

Commit

Permalink
Updating dependencies, adding .github directory, WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
avoidwork committed Oct 5, 2023
1 parent e9a003e commit 82e27d2
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 127 deletions.
87 changes: 55 additions & 32 deletions dist/tiny-etag.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,60 +7,86 @@
*/
'use strict';

var node_url = require('node:url');
var tinyLru = require('tiny-lru');
var MurmurHash3 = require('murmurhash3js');

const mmh3 = MurmurHash3.x86.hash32;

function clone (arg) {
return JSON.parse(JSON.stringify(arg, null, 0));
var node_crypto = require('node:crypto');

const BASE64 = "base64";
const SHA1 = "sha1";
const CACHE_CONTROL = "cache-control";
const CONTENT_LOCATION = "content-location";
const DATE = "date";
const ETAG = "etag";
const EXPIRES = "expires";
const VARY = "vary";
const TEXT_PLAIN = "text/plain";

const INT_DETAULT_CACHE = 1e3;
const INT_0 = 0;
const INT_200 = 200;
const INT_304 = 304;
const INT_1000 = 1000;

const GET = "GET";
const FINISH = "finish";
const RANGE = "range";
const IF_NONE_MATCH = "if-none-match";
const EMPTY = "";
const NO_CACHE = "no-cache";
const NO_STORE = "no-store";

const clone = typeof structuredClone === "function" ? structuredClone : arg => JSON.parse(JSON.stringify(arg));

function hash (arg = "") {
return node_crypto.createHash(SHA1).update(arg).digest(BASE64);
}

function keep (arg) {
return arg === "cache-control" || arg === "content-location" || arg === "date" || arg === "etag" || arg === "expires" || arg === "vary";
return arg === CACHE_CONTROL || arg === CONTENT_LOCATION || arg === DATE || arg === ETAG || arg === EXPIRES || arg === VARY;
}

function parse (arg) {
return new node_url.URL(typeof arg === "string" ? arg : `http://${arg.headers.host || `localhost:${arg.socket.server._connectionKey.replace(/.*::/, "")}`}${arg.url}`);
return new URL(typeof arg === "string" ? arg : `http://${arg.headers.host || `localhost:${arg.socket.server._connectionKey.replace(/.*::/, "")}`}${arg.url}`);
}

class ETag {
constructor (cacheSize, cacheTTL, seed, mimetype) {
constructor (cacheSize, cacheTTL, mimetype) {
this.cache = tinyLru.lru(cacheSize, cacheTTL);
this.mimetype = mimetype;
this.seed = seed;
}

create (arg) {
return `"${mmh3(arg, this.seed)}"`;
create (arg = EMPTY, mimetype = this.mimetype) {
return `"${this.hash(arg, mimetype)}"`;
}

hash (arg = EMPTY, mimetype = this.mimetype) {
return hash(`${arg}_${mimetype}`);
}

middleware (req, res, next) {
if (req.method === "GET") {
const uri = (req.parsed || parse(req)).href,
if (req.method === GET) {
const uri = (req.parsed || this.parse(req)).href,
key = this.hash(uri, req.headers.accept),
cached = this.cache.get(key);

res.on("finish", () => {
res.on(FINISH, () => {
const headers = res.getHeaders(),
status = res.statusCode;

if ((status === 200 || status === 304) && "etag" in headers && this.valid(headers)) {
if ((status === INT_200 || status === INT_304) && ETAG in headers && this.valid(headers)) {
this.register(key, {
etag: headers.etag,
headers: headers,
timestamp: cached ? cached.timestamp : Math.floor(new Date().getTime() / 1000)
timestamp: cached ? cached.timestamp : Math.floor(Date.now() / INT_1000)
});
}
});

if (cached !== void 0 && "etag" in cached && "range" in req.headers === false && req.headers["if-none-match"] === cached.etag) {
if (cached !== void 0 && ETAG in cached && RANGE in req.headers === false && req.headers[IF_NONE_MATCH] === cached.etag) {
const headers = clone(cached.headers);

headers.age = Math.floor(new Date().getTime() / 1000) - cached.timestamp;
res.removeHeader("cache-control");
res.send("", 304, headers);
headers.age = Math.floor(Date.now() / INT_1000) - cached.timestamp;
res.removeHeader(CACHE_CONTROL);
res.send(EMPTY, INT_304, headers);
} else {
next();
}
Expand All @@ -69,8 +95,8 @@ class ETag {
}
}

hash (arg = "", mimetype = "") {
return this.create(`${arg}_${mimetype || this.mimetype}`);
parse (arg) {
return parse(arg);
}

register (key, arg) {
Expand All @@ -87,23 +113,20 @@ class ETag {
return this;
}

unregister (key) {
this.cache.delete(key);
}

valid (headers) {
const header = headers["cache-control"] || "";
const header = headers[CACHE_CONTROL] || EMPTY;

return header.length === 0 || (header.includes("no-cache") === false && header.includes("no-store") === false); // eslint-disable-line no-extra-parens
return (header.includes(NO_CACHE) === false && header.includes(NO_STORE) === false) || header.length === INT_0; // eslint-disable-line no-extra-parens
}
}

function etag ({cacheSize = 1e3, cacheTTL = 0, seed = null, mimetype = "text/plain"} = {}) {
const obj = new ETag(cacheSize, cacheTTL, seed !== null ? seed : Math.floor(Math.random() * cacheSize) + 1, mimetype);
function etag ({cacheSize = INT_DETAULT_CACHE, cacheTTL = INT_0, mimetype = TEXT_PLAIN} = {}) {
const obj = new ETag(cacheSize, cacheTTL, mimetype);

obj.middleware = obj.middleware.bind(obj);

return obj;
}

exports.ETag = ETag;
exports.etag = etag;
83 changes: 51 additions & 32 deletions dist/tiny-etag.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,56 +5,79 @@
* @license BSD-3-Clause
* @version 4.0.0
*/
import {URL}from'node:url';import {lru}from'tiny-lru';import MurmurHash3 from'murmurhash3js';const mmh3 = MurmurHash3.x86.hash32;

function clone (arg) {
return JSON.parse(JSON.stringify(arg, null, 0));
import {lru}from'tiny-lru';import {createHash}from'node:crypto';const BASE64 = "base64";
const SHA1 = "sha1";
const CACHE_CONTROL = "cache-control";
const CONTENT_LOCATION = "content-location";
const DATE = "date";
const ETAG = "etag";
const EXPIRES = "expires";
const VARY = "vary";
const TEXT_PLAIN = "text/plain";

const INT_DETAULT_CACHE = 1e3;
const INT_0 = 0;
const INT_200 = 200;
const INT_304 = 304;
const INT_1000 = 1000;

const GET = "GET";
const FINISH = "finish";
const RANGE = "range";
const IF_NONE_MATCH = "if-none-match";
const EMPTY = "";
const NO_CACHE = "no-cache";
const NO_STORE = "no-store";const clone = typeof structuredClone === "function" ? structuredClone : arg => JSON.parse(JSON.stringify(arg));

function hash (arg = "") {
return createHash(SHA1).update(arg).digest(BASE64);
}

function keep (arg) {
return arg === "cache-control" || arg === "content-location" || arg === "date" || arg === "etag" || arg === "expires" || arg === "vary";
return arg === CACHE_CONTROL || arg === CONTENT_LOCATION || arg === DATE || arg === ETAG || arg === EXPIRES || arg === VARY;
}

function parse (arg) {
return new URL(typeof arg === "string" ? arg : `http://${arg.headers.host || `localhost:${arg.socket.server._connectionKey.replace(/.*::/, "")}`}${arg.url}`);
}

class ETag {
constructor (cacheSize, cacheTTL, seed, mimetype) {
}class ETag {
constructor (cacheSize, cacheTTL, mimetype) {
this.cache = lru(cacheSize, cacheTTL);
this.mimetype = mimetype;
this.seed = seed;
}

create (arg) {
return `"${mmh3(arg, this.seed)}"`;
create (arg = EMPTY, mimetype = this.mimetype) {
return `"${this.hash(arg, mimetype)}"`;
}

hash (arg = EMPTY, mimetype = this.mimetype) {
return hash(`${arg}_${mimetype}`);
}

middleware (req, res, next) {
if (req.method === "GET") {
const uri = (req.parsed || parse(req)).href,
if (req.method === GET) {
const uri = (req.parsed || this.parse(req)).href,
key = this.hash(uri, req.headers.accept),
cached = this.cache.get(key);

res.on("finish", () => {
res.on(FINISH, () => {
const headers = res.getHeaders(),
status = res.statusCode;

if ((status === 200 || status === 304) && "etag" in headers && this.valid(headers)) {
if ((status === INT_200 || status === INT_304) && ETAG in headers && this.valid(headers)) {
this.register(key, {
etag: headers.etag,
headers: headers,
timestamp: cached ? cached.timestamp : Math.floor(new Date().getTime() / 1000)
timestamp: cached ? cached.timestamp : Math.floor(Date.now() / INT_1000)
});
}
});

if (cached !== void 0 && "etag" in cached && "range" in req.headers === false && req.headers["if-none-match"] === cached.etag) {
if (cached !== void 0 && ETAG in cached && RANGE in req.headers === false && req.headers[IF_NONE_MATCH] === cached.etag) {
const headers = clone(cached.headers);

headers.age = Math.floor(new Date().getTime() / 1000) - cached.timestamp;
res.removeHeader("cache-control");
res.send("", 304, headers);
headers.age = Math.floor(Date.now() / INT_1000) - cached.timestamp;
res.removeHeader(CACHE_CONTROL);
res.send(EMPTY, INT_304, headers);
} else {
next();
}
Expand All @@ -63,8 +86,8 @@ class ETag {
}
}

hash (arg = "", mimetype = "") {
return this.create(`${arg}_${mimetype || this.mimetype}`);
parse (arg) {
return parse(arg);
}

register (key, arg) {
Expand All @@ -81,21 +104,17 @@ class ETag {
return this;
}

unregister (key) {
this.cache.delete(key);
}

valid (headers) {
const header = headers["cache-control"] || "";
const header = headers[CACHE_CONTROL] || EMPTY;

return header.length === 0 || (header.includes("no-cache") === false && header.includes("no-store") === false); // eslint-disable-line no-extra-parens
return (header.includes(NO_CACHE) === false && header.includes(NO_STORE) === false) || header.length === INT_0; // eslint-disable-line no-extra-parens
}
}

function etag ({cacheSize = 1e3, cacheTTL = 0, seed = null, mimetype = "text/plain"} = {}) {
const obj = new ETag(cacheSize, cacheTTL, seed !== null ? seed : Math.floor(Math.random() * cacheSize) + 1, mimetype);
function etag ({cacheSize = INT_DETAULT_CACHE, cacheTTL = INT_0, mimetype = TEXT_PLAIN} = {}) {
const obj = new ETag(cacheSize, cacheTTL, mimetype);

obj.middleware = obj.middleware.bind(obj);

return obj;
}export{etag};
}export{ETag,etag};
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"mocha": "^10.2.0",
"nyc": "^15.1.0",
"rollup": "^3.29.2",
"tiny-httptest": "^4.0.4",
"tiny-httptest": "^4.0.5",
"typescript": "^5.2.2",
"woodland": "^17.0.2"
}
Expand Down
25 changes: 25 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export const BASE64 = "base64";
export const SHA1 = "sha1";
export const CACHE_CONTROL = "cache-control";
export const CONTENT_LOCATION = "content-location";
export const DATE = "date";
export const ETAG = "etag";
export const EXPIRES = "expires";
export const VARY = "vary";
export const TEXT_PLAIN = "text/plain";

export const INT_DETAULT_CACHE = 1e3;
export const INT_0 = 0;
export const INT_200 = 200;
export const INT_304 = 304;
export const INT_1000 = 1000;

export const GET = "GET";
export const FINISH = "finish";
export const RANGE = "range";
export const IF_NONE_MATCH = "if-none-match";
export const EMPTY = "";
export const NO_CACHE = "no-cache";
export const NO_STORE = "no-store";
export const PUBLIC = "public";
export const CONTENT_TYPE = "content-type";
Loading

0 comments on commit 82e27d2

Please sign in to comment.