Skip to content

Commit af7cd01

Browse files
authored
feat(etag): export RETAINED_304_HEADERS (#2763)
* feat(etag): export `RETAINED_304_HEADERS` * denoify
1 parent 9deb12f commit af7cd01

File tree

3 files changed

+60
-24
lines changed

3 files changed

+60
-24
lines changed

deno_dist/middleware/etag/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ type ETagOptions = {
1212
* > would have been sent in an equivalent 200 OK response: Cache-Control,
1313
* > Content-Location, Date, ETag, Expires, and Vary.
1414
*/
15-
const RETAINED_304_HEADERS = [
15+
export const RETAINED_304_HEADERS = [
1616
'cache-control',
1717
'content-location',
1818
'date',

src/middleware/etag/index.test.ts

+58-22
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
11
import { Hono } from '../../hono'
2-
import { etag } from '.'
2+
import { etag, RETAINED_304_HEADERS } from '.'
33

44
describe('Etag Middleware', () => {
5-
let app: Hono
6-
7-
beforeEach(() => {
8-
app = new Hono()
5+
it('Should return etag header', async () => {
6+
const app = new Hono()
97
app.use('/etag/*', etag())
108
app.get('/etag/abc', (c) => {
119
return c.text('Hono is cool')
1210
})
1311
app.get('/etag/def', (c) => {
1412
return c.json({ message: 'Hono is cool' })
1513
})
16-
})
17-
18-
it('Should return etag header', async () => {
1914
let res = await app.request('http://localhost/etag/abc')
2015
expect(res.headers.get('ETag')).not.toBeFalsy()
2116
expect(res.headers.get('ETag')).toBe('"4e32298b1cb4edc595237405e5b696e105c2399a"')
@@ -26,18 +21,21 @@ describe('Etag Middleware', () => {
2621
})
2722

2823
it('Should return etag header - binary', async () => {
29-
app.use('/etag-binary/*', etag())
30-
app.get('/etag-binary', async (c) => {
24+
const app = new Hono()
25+
app.use('/etag/*', etag())
26+
app.get('/etag', async (c) => {
3127
return c.body(new Uint8Array(1))
3228
})
3329

34-
const res = await app.request('http://localhost/etag-binary')
30+
const res = await app.request('http://localhost/etag')
3531
expect(res.headers.get('ETag')).not.toBeFalsy()
3632
const etagHeader = res.headers.get('ETag')
3733
expect(etagHeader).toBe('"5ba93c9db0cff93f52b521d7420e43f6eda2784f"')
3834
})
3935

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

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

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

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

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

9699
// conditional GET with the wrong ETag:
97100
res = await app.request('http://localhost/etag/ghi', {
@@ -104,11 +107,11 @@ describe('Etag Middleware', () => {
104107
// conditional GET with matching ETag:
105108
res = await app.request('http://localhost/etag/ghi', {
106109
headers: {
107-
'If-None-Match': etag,
110+
'If-None-Match': etagHeaderValue,
108111
},
109112
})
110113
expect(res.status).toBe(304)
111-
expect(res.headers.get('Etag')).toBe(etag)
114+
expect(res.headers.get('Etag')).toBe(etagHeaderValue)
112115
expect(await res.text()).toBe('')
113116
expect(res.headers.get('cache-control')).toBe('public, max-age=120')
114117
expect(res.headers.get('date')).toBe('Mon, Feb 27 2023 12:08:36 GMT')
@@ -119,28 +122,61 @@ describe('Etag Middleware', () => {
119122
// conditional GET with matching ETag among list:
120123
res = await app.request('http://localhost/etag/ghi', {
121124
headers: {
122-
'If-None-Match': `"mismatch 1", ${etag}, "mismatch 2"`,
125+
'If-None-Match': `"mismatch 1", ${etagHeaderValue}, "mismatch 2"`,
123126
},
124127
})
125128
expect(res.status).toBe(304)
126129
})
127130

128131
it('Should not return duplicate etag header values', async () => {
129-
app.use('/etag2/*', etag())
130-
app.use('/etag2/*', etag())
131-
app.get('/etag2/abc', (c) => c.text('Hono is cool'))
132+
const app = new Hono()
133+
app.use('/etag/*', etag())
134+
app.use('/etag/*', etag())
135+
app.get('/etag/abc', (c) => c.text('Hono is cool'))
132136

133-
const res = await app.request('http://localhost/etag2/abc')
137+
const res = await app.request('http://localhost/etag/abc')
134138
expect(res.headers.get('ETag')).not.toBeFalsy()
135139
expect(res.headers.get('ETag')).toBe('"4e32298b1cb4edc595237405e5b696e105c2399a"')
136140
})
137141

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

143149
const res = await app.request('http://localhost/etag/predefined')
144150
expect(res.headers.get('ETag')).toBe('"f-0194-d"')
145151
})
152+
153+
it('Should retain the default and the specified headers', async () => {
154+
const cacheControl = 'public, max-age=120'
155+
const message = 'Hello!'
156+
const app = new Hono()
157+
app.use(
158+
'/etag/*',
159+
etag({
160+
retainedHeaders: ['x-message-retain', ...RETAINED_304_HEADERS],
161+
})
162+
)
163+
app.get('/etag', (c) => {
164+
return c.text('Hono is cool', 200, {
165+
'cache-control': cacheControl,
166+
'x-message-retain': message,
167+
'x-message': message,
168+
})
169+
})
170+
const res = await app.request('/etag', {
171+
headers: {
172+
'If-None-Match': '"4e32298b1cb4edc595237405e5b696e105c2399a"',
173+
},
174+
})
175+
expect(res.status).toBe(304)
176+
expect(res.headers.get('ETag')).not.toBeFalsy()
177+
expect(res.headers.get('ETag')).toBe('"4e32298b1cb4edc595237405e5b696e105c2399a"')
178+
expect(res.headers.get('Cache-Control')).toBe(cacheControl)
179+
expect(res.headers.get('x-message-retain')).toBe(message)
180+
expect(res.headers.get('x-message')).toBeFalsy()
181+
})
146182
})

src/middleware/etag/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ type ETagOptions = {
1212
* > would have been sent in an equivalent 200 OK response: Cache-Control,
1313
* > Content-Location, Date, ETag, Expires, and Vary.
1414
*/
15-
const RETAINED_304_HEADERS = [
15+
export const RETAINED_304_HEADERS = [
1616
'cache-control',
1717
'content-location',
1818
'date',

0 commit comments

Comments
 (0)