Skip to content

Commit d56c9d6

Browse files
CopilotL3P3
andcommitted
Add HTTP cache headers for static files with ETag and Last-Modified support
Co-authored-by: L3P3 <4629449+L3P3@users.noreply.github.com>
1 parent 36cf561 commit d56c9d6

2 files changed

Lines changed: 51 additions & 11 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "rtjscomp",
3-
"version": "0.9.12",
3+
"version": "0.9.13",
44
"description": "php-like server but with javascript",
55
"repository": {
66
"type": "git",

rtjscomp.js

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1526,8 +1526,41 @@ const request_handle = async (request, response, https) => {
15261526
}
15271527
}
15281528

1529+
// Generate ETag from modification time and file size
1530+
const mtime_ms = file_stat.mtimeMs;
1531+
const etag = `"${mtime_ms.toString(36)}-${file_stat.size.toString(36)}"`;
1532+
const last_modified = new Date(mtime_ms).toUTCString();
1533+
1534+
// Check If-None-Match (ETag validation)
1535+
if ('if-none-match' in request_headers) {
1536+
const if_none_match = request_headers['if-none-match'];
1537+
if (if_none_match === etag || if_none_match === '*') {
1538+
response.setHeader('ETag', etag);
1539+
response.setHeader('Cache-Control', 'public, max-age=31536000');
1540+
throw 304;
1541+
}
1542+
}
1543+
1544+
// Check If-Modified-Since (time-based validation)
1545+
if (
1546+
'if-modified-since' in request_headers &&
1547+
!('if-none-match' in request_headers)
1548+
) {
1549+
const if_modified_since = new Date(request_headers['if-modified-since']);
1550+
// Round mtime down to seconds for comparison (HTTP dates don't have millisecond precision)
1551+
const mtime_seconds = Math.floor(mtime_ms / 1000) * 1000;
1552+
if (!isNaN(if_modified_since) && mtime_seconds <= if_modified_since.getTime()) {
1553+
response.setHeader('ETag', etag);
1554+
response.setHeader('Last-Modified', last_modified);
1555+
response.setHeader('Cache-Control', 'public, max-age=31536000');
1556+
throw 304;
1557+
}
1558+
}
1559+
15291560
if (spam_enabled) spam('static_send', [path, file_compression]);
1530-
response.setHeader('Cache-Control', 'public, max-age=600');
1561+
response.setHeader('Cache-Control', 'public, max-age=31536000');
1562+
response.setHeader('ETag', etag);
1563+
response.setHeader('Last-Modified', last_modified);
15311564
if (compression_enabled_type) {
15321565
response.setHeader('Vary', 'Accept-Encoding');
15331566
}
@@ -1637,15 +1670,22 @@ const request_handle = async (request, response, https) => {
16371670
}
16381671

16391672
if (!response.headersSent) {
1640-
response.writeHead(err, {
1641-
'Content-Type': 'text/html',
1642-
'Cache-Control': 'no-cache, no-store',
1643-
});
1644-
response.end(`<!DOCTYPE html><html><body><h1>HTTP ${
1645-
err
1646-
}: ${
1647-
http.STATUS_CODES[err] || 'Error'
1648-
}</h1></body></html>`);
1673+
if (err === 304) {
1674+
// 304 Not Modified - headers already set, just send status
1675+
response.statusCode = 304;
1676+
response.end();
1677+
}
1678+
else {
1679+
response.writeHead(err, {
1680+
'Content-Type': 'text/html',
1681+
'Cache-Control': 'no-cache, no-store',
1682+
});
1683+
response.end(`<!DOCTYPE html><html><body><h1>HTTP ${
1684+
err
1685+
}: ${
1686+
http.STATUS_CODES[err] || 'Error'
1687+
}</h1></body></html>`);
1688+
}
16491689
}
16501690
if ('content-length' in request_headers) {
16511691
request.socket.destroy();

0 commit comments

Comments
 (0)