Skip to content

Commit 588c8a3

Browse files
harlan-zwpi0
andauthored
feat: util getRequestIP (#503)
Co-authored-by: Pooya Parsa <[email protected]>
1 parent ae26f8b commit 588c8a3

File tree

4 files changed

+75
-0
lines changed

4 files changed

+75
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ H3 has a concept of composable utilities that accept `event` (from `eventHandler
224224
- `getRequestHost(event)`
225225
- `getRequestProtocol(event)`
226226
- `getRequestPath(event)`
227+
- `getRequestIP(event, { xForwardedFor: boolean })`
227228

228229
#### Response
229230

src/types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ export interface H3EventContext extends Record<string, any> {
4444
matchedRoute?: RouteNode;
4545
/* Cached session data */
4646
sessions?: Record<string, Session>;
47+
/* Trusted IP Address of client */
48+
clientAddress?: string;
4749
}
4850

4951
export type EventHandlerResponse<T = any> = T | Promise<T>;

src/utils/request.ts

+29
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,32 @@ export function getRequestURL(
149149
const path = getRequestPath(event);
150150
return new URL(path, `${protocol}://${host}`);
151151
}
152+
153+
export function getRequestIP(
154+
event: H3Event,
155+
opts: {
156+
/**
157+
* Use the X-Forwarded-For HTTP header set by proxies.
158+
*
159+
* Note: Make sure that this header can be trusted (your application running behind a CDN or reverse proxy) before enabling.
160+
*/
161+
xForwardedFor?: boolean;
162+
} = {},
163+
): string | undefined {
164+
if (event.context.clientAddress) {
165+
return event.context.clientAddress;
166+
}
167+
168+
if (opts.xForwardedFor) {
169+
const xForwardedFor = getRequestHeader(event, "x-forwarded-for")
170+
?.split(",")
171+
?.pop();
172+
if (xForwardedFor) {
173+
return xForwardedFor;
174+
}
175+
}
176+
177+
if (event.node.req.socket.remoteAddress) {
178+
return event.node.req.socket.remoteAddress;
179+
}
180+
}

test/utils.test.ts

+43
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
getQuery,
1212
getRequestURL,
1313
readFormData,
14+
getRequestIP,
1415
} from "../src";
1516

1617
describe("", () => {
@@ -144,6 +145,48 @@ describe("", () => {
144145
}
145146
});
146147

148+
describe("getRequestIP", () => {
149+
it("x-forwarded-for", async () => {
150+
app.use(
151+
"/",
152+
eventHandler((event) => {
153+
return getRequestIP(event, {
154+
xForwardedFor: true,
155+
});
156+
}),
157+
);
158+
const req = request.get("/");
159+
req.set("x-forwarded-for", "127.0.0.1");
160+
expect((await req).text).toBe("127.0.0.1");
161+
});
162+
it("ports", async () => {
163+
app.use(
164+
"/",
165+
eventHandler((event) => {
166+
return getRequestIP(event, {
167+
xForwardedFor: true,
168+
});
169+
}),
170+
);
171+
const req = request.get("/");
172+
req.set("x-forwarded-for", "127.0.0.1:1234");
173+
expect((await req).text).toBe("127.0.0.1:1234");
174+
});
175+
it("ipv6", async () => {
176+
app.use(
177+
"/",
178+
eventHandler((event) => {
179+
return getRequestIP(event, {
180+
xForwardedFor: true,
181+
});
182+
}),
183+
);
184+
const req = request.get("/");
185+
req.set("x-forwarded-for", "2001:0db8:85a3:0000:0000:8a2e:0370:7334");
186+
expect((await req).text).toBe("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
187+
});
188+
});
189+
147190
describe("assertMethod", () => {
148191
it("only allow head and post", async () => {
149192
app.use(

0 commit comments

Comments
 (0)