Skip to content

Commit

Permalink
feat: check compressible mime type
Browse files Browse the repository at this point in the history
  • Loading branch information
Gusb3ll committed Oct 1, 2023
1 parent 4eb7cc2 commit 234ace2
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 47 deletions.
9 changes: 0 additions & 9 deletions CHANGELOG.md

This file was deleted.

Binary file modified bun.lockb
Binary file not shown.
6 changes: 4 additions & 2 deletions example/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { Elysia } from 'elysia'
import { compression } from '../src/index'

const app = new Elysia()
.use(compression())
.all('/', () => ({ value: 'Hello, World!' }))
.use(compression({ type: 'deflate' }))
.get('/', () => 'Hello, world!')
.get('/json', () => ({ hello: 'world' }))
.get('/pic', () => Bun.file('./tests/mei.jpg'))
.listen(4000)

console.log(
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "elysia-compression",
"version": "0.0.5",
"version": "0.0.6",
"description": "Compression plugin for Elysia",
"author": {
"name": "Gusb3ll",
Expand Down Expand Up @@ -32,7 +32,11 @@
"release": "npm run build && npm publish --access public",
"fmt": "prettier --write ."
},
"dependencies": {
"compressible": "2.0.18"
},
"devDependencies": {
"@types/compressible": "2.0.0",
"bun-types": "1.0.3",
"elysia": "0.7.15",
"eslint": "8.50.0",
Expand Down
116 changes: 81 additions & 35 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Elysia } from 'elysia'
import compressible from 'compressible'
import { Elysia, mapResponse } from 'elysia'
import { gzipSync, deflateSync, type ZlibCompressionOptions } from 'bun'
// import { brotliCompressSync, BrotliOptions } from 'zlib'

Expand All @@ -23,6 +24,14 @@ export type CompressionOptions = {
encoding?: BufferEncoding
}

const shouldCompress = (res: any) => {
const type = res.headers.get('Content-Type')
if (!type) {
return false
}
return compressible(type) ?? false
}

const toBuffer = (data: unknown, encoding: BufferEncoding) =>
Buffer.from(
typeof data === 'object'
Expand All @@ -31,40 +40,77 @@ const toBuffer = (data: unknown, encoding: BufferEncoding) =>
encoding,
)

export const compression =
(
{ type = 'gzip', options = {}, encoding = 'utf-8' }: CompressionOptions = {
type: 'gzip',
encoding: 'utf-8',
},
) =>
(app: Elysia) => {
switch (type) {
case 'gzip':
return app.onAfterHandle(ctx => {
ctx.set.headers['Content-Encoding'] = 'gzip'
const compressed = gzipSync(
toBuffer(ctx.response, encoding),
options as ZlibCompressionOptions,
)
ctx.response = new Response(compressed, ctx as any)
export const compression = (
{ type = 'gzip', options = {}, encoding = 'utf-8' }: CompressionOptions = {
type: 'gzip',
encoding: 'utf-8',
},
) => {
const app = new Elysia({
name: 'elysia-compression',
})

switch (type) {
case 'gzip':
return app.onAfterHandle(ctx => {
ctx.set.headers['Content-Encoding'] = 'gzip'
const res = mapResponse(ctx.response, {
status: 200,
headers: {},
})
if (!res.headers.get('Content-Type')) {
res.headers.set('Content-Type', 'text/plain')
}
if (!shouldCompress(res)) {
delete ctx.set.headers['Content-Encoding']
return ctx.response
}

const compressed = gzipSync(
toBuffer(ctx.response, encoding),
options as ZlibCompressionOptions,
)
const response = new Response(compressed, {
headers: res.headers,
})

ctx.response = response
})

case 'deflate':
return app.onAfterHandle(ctx => {
ctx.set.headers['Content-Encoding'] = 'deflate'
const res = mapResponse(ctx.response, {
status: 200,
headers: {},
})
case 'deflate':
return app.onAfterHandle(ctx => {
ctx.set.headers['Content-Encoding'] = 'deflate'
const compressed = deflateSync(
toBuffer(ctx.response, encoding),
options as ZlibCompressionOptions,
)
ctx.response = new Response(compressed, ctx as any)
if (!res.headers.get('Content-Type')) {
res.headers.set('Content-Type', 'text/plain')
}
if (!shouldCompress(res)) {
delete ctx.set.headers['Content-Encoding']
return ctx.response
}

const compressed = deflateSync(
toBuffer(ctx.response, encoding),
options as ZlibCompressionOptions,
)
const response = new Response(compressed, {
headers: res.headers,
})
// case 'brotli':
// return app.onAfterHandle((ctx) => {
// ctx.set.headers['Content-Encoding'] = 'br';
// const compressed = brotliCompressSync(toBuffer(ctx.response, encoding), options as BrotliOptions);
// ctx.response = new Response(compressed, ctx as any);
// })
default:
throw new Error('Invalid compression type.')
}

ctx.response = response
})

// case 'brotli':
// return app.onAfterHandle((ctx) => {
// ctx.set.headers['Content-Encoding'] = 'br';
// const compressed = brotliCompressSync(toBuffer(ctx.response, encoding), options as BrotliOptions);
// ctx.response = new Response(compressed, ctx as any);
// })

default:
throw new Error('Invalid compression type.')
}
}
69 changes: 69 additions & 0 deletions tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,73 @@ describe('Compression', () => {
expect(res.headers.get('Content-Encoding')).toBe('deflate')
expect(res.headers.get('x-powered-by')).toBe('Elysia')
})

it('return correct plain/text', async () => {
const app = new Elysia()
.use(compression({ type: 'deflate' }))
.get('/', () => response)

const res = await app.handle(req())

expect(res.headers.get('Content-Type')).toBe('text/plain')
})

it('return correct application/json', async () => {
const app = new Elysia()
.use(compression({ type: 'deflate' }))
.get('/', () => ({ hello: 'world' }))

const res = await app.handle(req())

expect(res.headers.get('Content-Type')).toBe('application/json')
})

it('return correct application/json', async () => {
const app = new Elysia()
.use(compression({ type: 'deflate' }))
.get('/', () => ({ hello: 'world' }))

const res = await app.handle(req())

expect(res.headers.get('Content-Type')).toBe('application/json')
})

it('return correct image type', async () => {
const app = new Elysia()
.use(compression({ type: 'deflate' }))
.get('/', () => Bun.file('tests/mei.jpg'))

const res = await app.handle(req())

expect(res.headers.get('Content-Type')).toBe('image/jpeg')
})
it('handle gzip compression', async () => {
const app = new Elysia().use(compression()).get('/', () => response)
const res = await app.handle(req())

expect(res.headers.get('Content-Encoding')).toBe('gzip')
})

it('handle deflate compression', async () => {
const app = new Elysia()
.use(compression({ type: 'deflate' }))
.get('/', () => response)
const res = await app.handle(req())

expect(res.headers.get('Content-Encoding')).toBe('deflate')
})

it('accept additional headers', async () => {
const app = new Elysia()
.use(compression({ type: 'deflate' }))
.get('/', ({ set }) => {
set.headers['x-powered-by'] = 'Elysia'

return response
})
const res = await app.handle(req())

expect(res.headers.get('Content-Encoding')).toBe('deflate')
expect(res.headers.get('x-powered-by')).toBe('Elysia')
})
})
Binary file added tests/mei.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 234ace2

Please sign in to comment.