Something went wrong. Try again.
A lightweight reverse proxy built in Bun and typescript
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134/** * Compression service that handles gzip and brotli compression for HTTP responses * to reduce bandwidth usage and improve response times. */import { gzipSync, brotliCompressSync } from "zlib";import http from "http";
export class Compression { private enabled: boolean; private compressibleTypes: string[];
constructor(enabled: boolean = false) { this.enabled = enabled; this.compressibleTypes = [ "text/html", "text/css", "text/javascript", "text/xml", "text/plain", "text/json", "application/json", "application/javascript", "application/xml", "application/rss+xml", "application/atom+xml", "image/svg+xml" ]; }
shouldCompress(contentType: string, size?: number): boolean { if (!this.enabled) return false; if (size && size < 1024) return false; return this.compressibleTypes.some(type => contentType.toLowerCase().includes(type) ); }
compress(data: Buffer, encoding: string): Buffer { if (!this.enabled) return data;
try { switch (encoding) { case "br": return brotliCompressSync(data); case "gzip": return gzipSync(data); default: return data; } } catch (error) { console.error("Compression failed:", error); return data; } }
selectEncoding(acceptEncoding: string): string | null { if (!this.enabled) return null;
const encodings = acceptEncoding.toLowerCase(); if (encodings.includes("br")) { return "br"; } if (encodings.includes("gzip")) { return "gzip"; } return null; }
processResponse( data: Buffer, contentType: string, acceptEncoding: string ): { data: Buffer; encoding?: string } { if (!this.shouldCompress(contentType, data.length)) { return { data }; }
const encoding = this.selectEncoding(acceptEncoding); if (!encoding) { return { data }; }
const compressed = this.compress(data, encoding); if (compressed.length < data.length) { return { data: compressed, encoding }; } return { data }; }
setCompressionHeaders( res: http.ServerResponse, encoding?: string, originalSize?: number, compressedSize?: number ): void { if (encoding) { res.setHeader("Content-Encoding", encoding); res.setHeader("Vary", "Accept-Encoding"); if (originalSize && compressedSize) { const ratio = ((originalSize - compressedSize) / originalSize * 100).toFixed(1); res.setHeader("X-Compression-Ratio", `${ratio}%`); } } }
addCompressibleType(type: string): void { if (!this.compressibleTypes.includes(type)) { this.compressibleTypes.push(type); } }
removeCompressibleType(type: string): void { const index = this.compressibleTypes.indexOf(type); if (index > -1) { this.compressibleTypes.splice(index, 1); } }
getStats() { return { enabled: this.enabled, compressibleTypes: this.compressibleTypes.length, supportedEncodings: ["gzip", "br"] }; }}