Skip to content

Commit

Permalink
feat(serve-static): add precompressed option
Browse files Browse the repository at this point in the history
  • Loading branch information
yusukebe committed Sep 15, 2024
1 parent d0bbc92 commit 2ca2a4e
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 0 deletions.
31 changes: 31 additions & 0 deletions src/serve-static.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,18 @@ export type ServeStaticOptions<E extends Env = Env> = {
root?: string
path?: string
index?: string // default is 'index.html'
precompressed?: boolean
rewriteRequestPath?: (path: string) => string
onFound?: (path: string, c: Context<E>) => void | Promise<void>
onNotFound?: (path: string, c: Context<E>) => void | Promise<void>
}

const ENCODINGS = {
br: '.br',
zstd: '.zst',
gzip: '.gz',
} as const

const createStreamBody = (stream: ReadStream) => {
const body = new ReadableStream({
start(controller) {
Expand Down Expand Up @@ -95,6 +102,30 @@ export const serveStatic = (options: ServeStaticOptions = { root: '' }): Middlew
c.header('Content-Type', mimeType)
}

if (options.precompressed) {
const acceptEncodings =
c.req
.header('Accept-Encoding')
?.split(',')
.map((encoding) => encoding.trim())
.filter((encoding): encoding is keyof typeof ENCODINGS =>
Object.hasOwn(ENCODINGS, encoding)
)
.sort((a, b) => Object.keys(ENCODINGS).indexOf(a) - Object.keys(ENCODINGS).indexOf(b)) ??
[]

for (const encoding of acceptEncodings) {
const precompressedStats = getStats(path + ENCODINGS[encoding])
if (precompressedStats) {
c.header('Content-Encoding', encoding)
c.header('Vary', 'Accept-Encoding', { append: true })
stats = precompressedStats
path = path + ENCODINGS[encoding]
break
}
}
}

const size = stats.size

if (c.req.method == 'HEAD' || c.req.method == 'OPTIONS') {
Expand Down
1 change: 1 addition & 0 deletions test/assets/static-with-precompressed/hello.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello Not Compressed
1 change: 1 addition & 0 deletions test/assets/static-with-precompressed/hello.txt.br
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello br Compressed
1 change: 1 addition & 0 deletions test/assets/static-with-precompressed/hello.txt.zst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello zstd Compressed
46 changes: 46 additions & 0 deletions test/serve-static.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ describe('Serve Static Middleware', () => {
})
)

app.use(
'/static-with-precompressed/*',
serveStatic({
root: './test/assets',
precompressed: true,
})
)

const server = createAdaptorServer(app)

it('Should return index.html', async () => {
Expand Down Expand Up @@ -149,4 +157,42 @@ describe('Serve Static Middleware', () => {
expect(res.status).toBe(200)
expect(res.text).toBe('Extensionless')
})

it('Should return a pre-compressed zstd response - /static-with-precompressed/hello.txt', async () => {
// Check if it returns a normal response
let res = await request(server).get('/static-with-precompressed/hello.txt')
expect(res.status).toBe(200)
expect(res.headers['content-length']).toBe('20')
expect(res.text).toBe('Hello Not Compressed')

res = await request(server)
.get('/static-with-precompressed/hello.txt')
.set('Accept-Encoding', 'zstd')
expect(res.status).toBe(200)
expect(res.headers['content-length']).toBe('21')
expect(res.headers['content-encoding']).toBe('zstd')
expect(res.headers['vary']).toBe('Accept-Encoding')
expect(res.text).toBe('Hello zstd Compressed')
})

it('Should return a pre-compressed brotli response - /static-with-precompressed/hello.txt', async () => {
const res = await request(server)
.get('/static-with-precompressed/hello.txt')
.set('Accept-Encoding', 'wompwomp, gzip, br, deflate, zstd')
expect(res.status).toBe(200)
expect(res.headers['content-length']).toBe('19')
expect(res.headers['content-encoding']).toBe('br')
expect(res.headers['vary']).toBe('Accept-Encoding')
expect(res.text).toBe('Hello br Compressed')
})

it('Should not return a pre-compressed response - /static-with-precompressed/hello.txt', async () => {
const res = await request(server)
.get('/static-with-precompressed/hello.txt')
.set('Accept-Encoding', 'wompwomp, unknown')
expect(res.status).toBe(200)
expect(res.headers['content-encoding']).toBeUndefined()
expect(res.headers['vary']).toBeUndefined()
expect(res.text).toBe('Hello Not Compressed')
})
})

0 comments on commit 2ca2a4e

Please sign in to comment.