Skip to content

Commit

Permalink
feat(etag): export RETAINED_304_HEADERS (#2763)
Browse files Browse the repository at this point in the history
* feat(etag): export `RETAINED_304_HEADERS`

* denoify
  • Loading branch information
yusukebe committed May 24, 2024
1 parent 04caa07 commit ca97310
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 24 deletions.
2 changes: 1 addition & 1 deletion deno_dist/middleware/etag/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type ETagOptions = {
* > would have been sent in an equivalent 200 OK response: Cache-Control,
* > Content-Location, Date, ETag, Expires, and Vary.
*/
const RETAINED_304_HEADERS = [
export const RETAINED_304_HEADERS = [
'cache-control',
'content-location',
'date',
Expand Down
80 changes: 58 additions & 22 deletions src/middleware/etag/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
import { Hono } from '../../hono'
import { etag } from '.'
import { etag, RETAINED_304_HEADERS } from '.'

describe('Etag Middleware', () => {
let app: Hono

beforeEach(() => {
app = new Hono()
it('Should return etag header', async () => {
const app = new Hono()
app.use('/etag/*', etag())
app.get('/etag/abc', (c) => {
return c.text('Hono is cool')
})
app.get('/etag/def', (c) => {
return c.json({ message: 'Hono is cool' })
})
})

it('Should return etag header', async () => {
let res = await app.request('http://localhost/etag/abc')
expect(res.headers.get('ETag')).not.toBeFalsy()
expect(res.headers.get('ETag')).toBe('"4e32298b1cb4edc595237405e5b696e105c2399a"')
Expand All @@ -26,18 +21,21 @@ describe('Etag Middleware', () => {
})

it('Should return etag header - binary', async () => {
app.use('/etag-binary/*', etag())
app.get('/etag-binary', async (c) => {
const app = new Hono()
app.use('/etag/*', etag())
app.get('/etag', async (c) => {
return c.body(new Uint8Array(1))
})

const res = await app.request('http://localhost/etag-binary')
const res = await app.request('http://localhost/etag')
expect(res.headers.get('ETag')).not.toBeFalsy()
const etagHeader = res.headers.get('ETag')
expect(etagHeader).toBe('"5ba93c9db0cff93f52b521d7420e43f6eda2784f"')
})

it('Should not be the same etag - arrayBuffer', async () => {
const app = new Hono()
app.use('/etag/*', etag())
app.get('/etag/ab1', (c) => {
return c.body(new ArrayBuffer(1))
})
Expand All @@ -52,6 +50,8 @@ describe('Etag Middleware', () => {
})

it('Should not be the same etag - Uint8Array', async () => {
const app = new Hono()
app.use('/etag/*', etag())
app.get('/etag/ui1', (c) => {
return c.body(new Uint8Array([1, 2, 3]))
})
Expand All @@ -66,17 +66,20 @@ describe('Etag Middleware', () => {
})

it('Should return etag header - weak', async () => {
app.use('/etag-weak/*', etag({ weak: true }))
app.get('/etag-weak/abc', (c) => {
const app = new Hono()
app.use('/etag/*', etag({ weak: true }))
app.get('/etag/abc', (c) => {
return c.text('Hono is cool')
})

const res = await app.request('http://localhost/etag-weak/abc')
const res = await app.request('http://localhost/etag/abc')
expect(res.headers.get('ETag')).not.toBeFalsy()
expect(res.headers.get('ETag')).toBe('W/"4e32298b1cb4edc595237405e5b696e105c2399a"')
})

it('Should handle conditional GETs', async () => {
const app = new Hono()
app.use('/etag/*', etag())
app.get('/etag/ghi', (c) =>
c.text('Hono is great', 200, {
'cache-control': 'public, max-age=120',
Expand All @@ -91,7 +94,7 @@ describe('Etag Middleware', () => {
let res = await app.request('http://localhost/etag/ghi')
expect(res.status).toBe(200)
expect(res.headers.get('ETag')).not.toBeFalsy()
const etag = res.headers.get('ETag') || ''
const etagHeaderValue = res.headers.get('ETag') || ''

// conditional GET with the wrong ETag:
res = await app.request('http://localhost/etag/ghi', {
Expand All @@ -104,11 +107,11 @@ describe('Etag Middleware', () => {
// conditional GET with matching ETag:
res = await app.request('http://localhost/etag/ghi', {
headers: {
'If-None-Match': etag,
'If-None-Match': etagHeaderValue,
},
})
expect(res.status).toBe(304)
expect(res.headers.get('Etag')).toBe(etag)
expect(res.headers.get('Etag')).toBe(etagHeaderValue)
expect(await res.text()).toBe('')
expect(res.headers.get('cache-control')).toBe('public, max-age=120')
expect(res.headers.get('date')).toBe('Mon, Feb 27 2023 12:08:36 GMT')
Expand All @@ -119,28 +122,61 @@ describe('Etag Middleware', () => {
// conditional GET with matching ETag among list:
res = await app.request('http://localhost/etag/ghi', {
headers: {
'If-None-Match': `"mismatch 1", ${etag}, "mismatch 2"`,
'If-None-Match': `"mismatch 1", ${etagHeaderValue}, "mismatch 2"`,
},
})
expect(res.status).toBe(304)
})

it('Should not return duplicate etag header values', async () => {
app.use('/etag2/*', etag())
app.use('/etag2/*', etag())
app.get('/etag2/abc', (c) => c.text('Hono is cool'))
const app = new Hono()
app.use('/etag/*', etag())
app.use('/etag/*', etag())
app.get('/etag/abc', (c) => c.text('Hono is cool'))

const res = await app.request('http://localhost/etag2/abc')
const res = await app.request('http://localhost/etag/abc')
expect(res.headers.get('ETag')).not.toBeFalsy()
expect(res.headers.get('ETag')).toBe('"4e32298b1cb4edc595237405e5b696e105c2399a"')
})

it('Should not override ETag headers from upstream', async () => {
const app = new Hono()
app.use('/etag/*', etag())
app.get('/etag/predefined', (c) =>
c.text('This response has an ETag', 200, { ETag: '"f-0194-d"' })
)

const res = await app.request('http://localhost/etag/predefined')
expect(res.headers.get('ETag')).toBe('"f-0194-d"')
})

it('Should retain the default and the specified headers', async () => {
const cacheControl = 'public, max-age=120'
const message = 'Hello!'
const app = new Hono()
app.use(
'/etag/*',
etag({
retainedHeaders: ['x-message-retain', ...RETAINED_304_HEADERS],
})
)
app.get('/etag', (c) => {
return c.text('Hono is cool', 200, {
'cache-control': cacheControl,
'x-message-retain': message,
'x-message': message,
})
})
const res = await app.request('/etag', {
headers: {
'If-None-Match': '"4e32298b1cb4edc595237405e5b696e105c2399a"',
},
})
expect(res.status).toBe(304)
expect(res.headers.get('ETag')).not.toBeFalsy()
expect(res.headers.get('ETag')).toBe('"4e32298b1cb4edc595237405e5b696e105c2399a"')
expect(res.headers.get('Cache-Control')).toBe(cacheControl)
expect(res.headers.get('x-message-retain')).toBe(message)
expect(res.headers.get('x-message')).toBeFalsy()
})
})
2 changes: 1 addition & 1 deletion src/middleware/etag/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type ETagOptions = {
* > would have been sent in an equivalent 200 OK response: Cache-Control,
* > Content-Location, Date, ETag, Expires, and Vary.
*/
const RETAINED_304_HEADERS = [
export const RETAINED_304_HEADERS = [
'cache-control',
'content-location',
'date',
Expand Down

0 comments on commit ca97310

Please sign in to comment.