Skip to content

Commit

Permalink
fix(middleware): compute client address (#12222)
Browse files Browse the repository at this point in the history
* fix(middleware): compute client address

* add unit test
  • Loading branch information
ematipico authored Oct 14, 2024
1 parent d6f03e4 commit fb55695
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/slimy-kids-peel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Fixes an issue where the edge middleware couldn't correctly compute the client IP address when calling `ctx.clientAddress()`
12 changes: 9 additions & 3 deletions packages/astro/src/core/middleware/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import { ASTRO_VERSION, clientAddressSymbol, clientLocalsSymbol } from '../constants.js';
import { AstroCookies } from '../cookies/index.js';
import { AstroError, AstroErrorData } from '../errors/index.js';
import { getClientIpAddress } from '../routing/request.js';
import { sequence } from './sequence.js';

function defineMiddleware(fn: MiddlewareHandler) {
Expand Down Expand Up @@ -50,6 +51,7 @@ function createContext({
let preferredLocale: string | undefined = undefined;
let preferredLocaleList: string[] | undefined = undefined;
let currentLocale: string | undefined = undefined;
let clientIpAddress: string | undefined;
const url = new URL(request.url);
const route = url.pathname;

Expand Down Expand Up @@ -85,10 +87,14 @@ function createContext({
},
url,
get clientAddress() {
if (clientAddressSymbol in request) {
return Reflect.get(request, clientAddressSymbol) as string;
if (clientIpAddress) {
return clientIpAddress;
}
throw new AstroError(AstroErrorData.StaticClientAddressNotAvailable);
clientIpAddress = getClientIpAddress(request);
if (!clientIpAddress) {
throw new AstroError(AstroErrorData.StaticClientAddressNotAvailable);
}
return clientIpAddress;
},
get locals() {
let locals = Reflect.get(request, clientLocalsSymbol);
Expand Down
20 changes: 20 additions & 0 deletions packages/astro/src/core/routing/request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Utilities for extracting information from `Request`
*/

// Parses multiple header and returns first value if available.
export function getFirstForwardedValue(multiValueHeader?: string | string[] | null) {
return multiValueHeader
?.toString()
?.split(',')
.map((e) => e.trim())?.[0];
}

/**
* Returns the first value associated to the `x-forwarded-for` header.
*
* @param {Request} request
*/
export function getClientIpAddress(request: Request): string | undefined {
return getFirstForwardedValue(request.headers.get('x-forwarded-for'));
}
19 changes: 19 additions & 0 deletions packages/astro/test/units/routing/api-context.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import { createContext } from '../../../dist/core/middleware/index.js';

describe('createAPIContext', () => {
it('should return the clientAddress', () => {
const request = new Request('http://example.com', {
headers: {
'x-forwarded-for': '192.0.2.43, 172.16.58.3',
},
});

const context = createContext({
request,
});

assert.equal(context.clientAddress, '192.0.2.43');
});
});

0 comments on commit fb55695

Please sign in to comment.