Last active
August 17, 2023 01:32
-
-
Save olragon/011b3a45f63924e267aa50f287b394a4 to your computer and use it in GitHub Desktop.
Elysia's plugin for response compression, support gzip & deflare
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// fork of elysia-compression | |
// changes: | |
// 1/ use gzip, deflate from node:zlib, | |
// 2/ use async version, | |
// 3/ check accept-encoding header, | |
// 4/ prevent unexpected download because of application/octet-stream | |
import {Context, Elysia} from "elysia"; | |
import {gzip, deflate, ZlibOptions} from "zlib"; | |
export interface CompressionOptions { | |
type: 'gzip' | 'deflate' // default gzip | |
force?: boolean | |
options?: ZlibOptions | |
} | |
export const compression = | |
(options: CompressionOptions = { type: 'gzip' }) => | |
(app: Elysia) => { | |
return app.onAfterHandle(async (context, response) => { | |
return handleCompression(options)(context, response); | |
}); | |
} | |
export async function toBuffer(res: unknown) { | |
if (res instanceof Buffer) { | |
return res; | |
} | |
if (res instanceof Blob || res instanceof Response) { | |
return new Buffer(await res.arrayBuffer()); | |
} | |
return Buffer.from( | |
(typeof res === 'object' | |
? JSON.stringify(res) | |
: res?.toString() ?? String(res)) | |
); | |
} | |
export function getCompressors() { | |
return { | |
async gzip(buffer: Buffer, options?: ZlibOptions): Promise<Buffer> { | |
return new Promise((resolve, reject) => { | |
gzip(buffer, {...options}, (error, result) => { | |
if (error) return reject(error); | |
resolve(result); | |
}); | |
}); | |
}, | |
async deflate(buffer: Buffer, options?: ZlibOptions): Promise<Buffer> { | |
return new Promise((resolve, reject) => { | |
deflate(buffer, {...options}, (error, result) => { | |
if (error) return reject(error); | |
resolve(result); | |
}); | |
}); | |
} | |
}; | |
} | |
export function handleCompression({ type = 'gzip', force = false, options }: CompressionOptions) { | |
const compressors = getCompressors(); | |
if (!compressors[type]) { | |
throw new Error(`Unknown compression type: ${type}`); | |
} | |
return async function onAfterHandle<C extends Context, R extends unknown>({ set, request }: C, response: R) { | |
// do not compress if response is empty, eg: redirect | |
if (!response) { | |
return response; | |
} | |
// do not compress if client does not support | |
if (!force && !request.headers.get('accept-encoding')?.includes(type)) { | |
return response; | |
} | |
// keep original content-type otherwise it will be application/octet-stream | |
const contentType = (response instanceof Response | |
? response.headers.get('Content-Type') | |
: set.headers['Content-Type']) || 'text/plain'; | |
const buffer = await toBuffer(response); | |
const compressed = await compressors[type](buffer, options); | |
set.headers['Content-Encoding'] = type; | |
const resp = new Response(compressed, set); | |
// set content-type to original value | |
resp.headers.set('Content-Type', contentType); | |
return resp; | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Elysia } from "elysia"; | |
import { compression } from "./elysia-compression" | |
const app = new Elysia() | |
.use(compression()) | |
.get('/', async () => "Compressed!") | |
.listen(process.env.PORT || process.env.APP_PORT || 3000); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment