Skip to content

Commit ed07e73

Browse files
committed
feat: ability to specify cache-control headers for v2 proxied paths
1 parent 03a2c35 commit ed07e73

File tree

4 files changed

+87
-21
lines changed

4 files changed

+87
-21
lines changed

.proxy-cache-control.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"paths": {
3+
// Examples of common paths -- default is to not set a cache-control value (null).
4+
// They can be set to cache with a lifetime e.g. "public, max-age=150"
5+
"^/v2/info(.*)": null,
6+
"^/v2/pox(.*)": null,
7+
"^/v2/contracts/call-read(.*)": null,
8+
"^/v2/map_entry(.*)": null,
9+
"^/v2(.*)": null
10+
}
11+
}

package-lock.json

Lines changed: 25 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
"bluebird": "^3.7.2",
107107
"bn.js": "^4.11.8",
108108
"c32check": "^1.1.2",
109+
"chokidar": "^3.5.1",
109110
"coinselect": "^3.1.12",
110111
"compression": "^1.7.4",
111112
"cors": "^2.8.5",
@@ -117,6 +118,7 @@
117118
"express-list-endpoints": "^5.0.0",
118119
"express-winston": "^4.0.3",
119120
"http-proxy-middleware": "^1.0.6",
121+
"jsonc-parser": "^3.0.0",
120122
"jsonrpc-lite": "^2.1.0",
121123
"node-fetch": "^2.6.0",
122124
"node-pg-migrate": "^4.2.3",

src/api/routes/core-node-rpc-proxy.ts

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ import * as express from 'express';
22
import * as cors from 'cors';
33
import { createProxyMiddleware, Options } from 'http-proxy-middleware';
44
import { logger, parsePort } from '../../helpers';
5-
import { Agent, IncomingMessage } from 'http';
5+
import { Agent } from 'http';
6+
import * as fs from 'fs';
67
import { addAsync } from '@awaitjs/express';
8+
import * as chokidar from 'chokidar';
9+
import * as jsoncParser from 'jsonc-parser';
710

811
export const V2_POX_MIN_AMOUNT_USTX_ENV_VAR = 'V2_POX_MIN_AMOUNT_USTX';
912

@@ -40,6 +43,40 @@ export function createCoreNodeRpcProxyRouter(): express.Router {
4043
maxTotalSockets: 400,
4144
});
4245

46+
const PROXY_CACHE_CONTROL_FILE = '.proxy-cache-control.json';
47+
const cacheControlFileWatcher = chokidar.watch(PROXY_CACHE_CONTROL_FILE);
48+
let pathCacheOptions = new Map<RegExp, string | null>();
49+
50+
const updatePathCacheOptions = () => {
51+
try {
52+
const configContent: { paths: Record<string, string> } = jsoncParser.parse(
53+
fs.readFileSync(PROXY_CACHE_CONTROL_FILE, 'utf8')
54+
);
55+
pathCacheOptions = new Map(
56+
Object.entries(configContent.paths).map(([k, v]) => [RegExp(k), v])
57+
);
58+
} catch (error) {
59+
pathCacheOptions.clear();
60+
logger.error(`Error reading changes from ${PROXY_CACHE_CONTROL_FILE}`, error);
61+
}
62+
};
63+
updatePathCacheOptions();
64+
cacheControlFileWatcher.on('all', (eventName, path, stats) => {
65+
updatePathCacheOptions();
66+
});
67+
68+
const getCacheControlHeader = (statusCode: number, url: string): string | null => {
69+
if (statusCode < 200 || statusCode > 299) {
70+
return null;
71+
}
72+
for (const [regexp, cacheControl] of pathCacheOptions.entries()) {
73+
if (cacheControl && regexp.test(url)) {
74+
return cacheControl;
75+
}
76+
}
77+
return null;
78+
};
79+
4380
router.getAsync('/pox', async (req, res, next) => {
4481
const overrideVal = getPoxOverride();
4582
if (!overrideVal) {
@@ -48,12 +85,16 @@ export function createCoreNodeRpcProxyRouter(): express.Router {
4885
}
4986
logger.info(`Overriding /v2/pox 'min_amount_ustx' with ${overrideVal}`);
5087
const poxRes = await fetch(`http://${stacksNodeRpcEndpoint}${req.originalUrl}`);
51-
res.setHeader('content-type', poxRes.headers.get('content-type') as string);
88+
res.header('content-type', poxRes.headers.get('content-type') as string);
5289
res.status(poxRes.status);
5390
const poxResString = await poxRes.text();
5491
try {
5592
const resJson: { min_amount_ustx: number } = JSON.parse(poxResString);
5693
resJson.min_amount_ustx = overrideVal;
94+
const header = getCacheControlHeader(res.statusCode, req.originalUrl);
95+
if (header) {
96+
res.header('Cache-Control', header);
97+
}
5798
res.json(resJson);
5899
} catch (error) {
59100
res.send(poxResString);
@@ -64,6 +105,12 @@ export function createCoreNodeRpcProxyRouter(): express.Router {
64105
agent: httpAgent,
65106
target: `http://${stacksNodeRpcEndpoint}`,
66107
changeOrigin: true,
108+
onProxyRes: (proxyRes, req, res) => {
109+
const header = getCacheControlHeader(res.statusCode, req.url);
110+
if (header) {
111+
proxyRes.headers['Cache-Control'] = header;
112+
}
113+
},
67114
onError: (error, req, res) => {
68115
const msg =
69116
(error as any).code === 'ECONNREFUSED'

0 commit comments

Comments
 (0)