Skip to content

Commit 05791fb

Browse files
eduardoboucaspieh
andauthored
feat: support header matching in edge functions (#361)
* feat; support header matching in edge functions * Update packages/edge-functions/dev/node/main.ts Co-authored-by: Michal Piechowiak <misiek.piechowiak@gmail.com> * refactor: simplify header check --------- Co-authored-by: Michal Piechowiak <misiek.piechowiak@gmail.com>
1 parent 5003228 commit 05791fb

File tree

3 files changed

+82
-0
lines changed

3 files changed

+82
-0
lines changed

packages/edge-functions/dev/node/main.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,59 @@ describe('`EdgeFunctionsHandler`', () => {
196196
await fixture.destroy()
197197
})
198198

199+
test('Runs an edge function with header conditions', async () => {
200+
const fixture = new Fixture()
201+
.withFile(
202+
'netlify.toml',
203+
`[build]
204+
publish = "public"
205+
`,
206+
)
207+
.withFile(
208+
'netlify/edge-functions/echo.mjs',
209+
`export default async (req, context) => new Response("Hello from edge function");
210+
211+
export const config = {
212+
path: "/echo",
213+
header: {
214+
"x-present": true,
215+
"x-absent": false,
216+
"x-match": "something"
217+
}
218+
};`,
219+
)
220+
221+
const directory = await fixture.create()
222+
const handler = new EdgeFunctionsHandler({
223+
configDeclarations: [],
224+
directories: [path.resolve(directory, 'netlify/edge-functions')],
225+
env: {},
226+
geolocation,
227+
logger: console,
228+
siteID: '123',
229+
siteName: 'test',
230+
})
231+
232+
const req1 = new Request('https://site.netlify/echo')
233+
req1.headers.set('x-nf-request-id', 'req-id')
234+
expect(await handler.match(req1)).toBeFalsy()
235+
236+
const req2 = new Request('https://site.netlify/echo')
237+
req2.headers.set('x-nf-request-id', 'req-id')
238+
req2.headers.set('x-present', '1')
239+
req2.headers.set('x-match', 'something good')
240+
expect(await handler.match(req2)).toBeTruthy()
241+
242+
const req3 = new Request('https://site.netlify/echo')
243+
req3.headers.set('x-nf-request-id', 'req-id')
244+
req3.headers.set('x-present', '1')
245+
req3.headers.set('x-absent', '1')
246+
req3.headers.set('x-match', 'something good')
247+
expect(await handler.match(req3)).toBeFalsy()
248+
249+
await fixture.destroy()
250+
})
251+
199252
test('Throws an error when the edge function has unparseable code', async () => {
200253
const fixture = new Fixture()
201254
.withFile(

packages/edge-functions/dev/node/main.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,32 @@ export class EdgeFunctionsHandler {
129129
return
130130
}
131131

132+
if (route.headers) {
133+
const headerMatches = Object.entries(route.headers).every(([headerName, headerMatch]) => {
134+
const requestHeaderValue = req.headers.get(headerName)
135+
136+
if (headerMatch?.matcher === 'exists') {
137+
return requestHeaderValue !== null
138+
}
139+
140+
if (headerMatch?.matcher === 'missing') {
141+
return requestHeaderValue === null
142+
}
143+
144+
if (requestHeaderValue && headerMatch?.matcher === 'regex') {
145+
const pattern = new RegExp(headerMatch.pattern)
146+
147+
return pattern.test(requestHeaderValue.split(', ').join(','))
148+
}
149+
150+
return false
151+
})
152+
153+
if (!headerMatches) {
154+
return
155+
}
156+
}
157+
132158
const isExcludedForFunction = manifest.function_config[route.function]?.excluded_patterns?.some((pattern) =>
133159
new RegExp(pattern).test(url.pathname),
134160
)

packages/edge-functions/src/lib/config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ type OnError = 'fail' | 'bypass' | Path
66

77
export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS'
88

9+
export type HeadersConfig = Record<string, boolean | string>
10+
911
type RateLimitAggregator = 'domain' | 'ip'
1012

1113
type RateLimitAction = 'rate_limit' | 'rewrite'
@@ -29,6 +31,7 @@ export interface Config {
2931
cache?: Cache
3032
excludedPath?: Path | Path[]
3133
excludedPattern?: string | string[]
34+
header?: HeadersConfig
3235
onError?: OnError
3336
path?: Path | Path[]
3437
pattern?: string | string[]

0 commit comments

Comments
 (0)