Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: util getRequestIP #503

Merged
merged 13 commits into from
Aug 14, 2023
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ H3 has a concept of composable utilities that accept `event` (from `eventHandler
- `getRequestHost(event)`
- `getRequestProtocol(event)`
- `getRequestPath(event)`
- `getRequestIP(event, { xForwardedFor: boolean })`

#### Response

Expand Down
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export interface H3EventContext extends Record<string, any> {
matchedRoute?: RouteNode;
/* Cached session data */
sessions?: Record<string, Session>;
/* Trusted IP Address of client */
clientAddress?: string;
}

export type EventHandlerResponse<T = any> = T | Promise<T>;
Expand Down
29 changes: 29 additions & 0 deletions src/utils/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,32 @@
const path = getRequestPath(event);
return new URL(path, `${protocol}://${host}`);
}

export function getRequestIP(
event: H3Event,
opts: {
/**
* Use the X-Forwarded-For HTTP header set by proxies.
*
* Note: Make sure that this header can be trusted (your application running behind a CDN or reverse proxy) before enabling.
*/
xForwardedFor?: boolean;
} = {},
): string | undefined {
if (event.context.clientAddress) {
return event.context.clientAddress;
}

Check warning on line 166 in src/utils/request.ts

View check run for this annotation

Codecov / codecov/patch

src/utils/request.ts#L165-L166

Added lines #L165 - L166 were not covered by tests

if (opts.xForwardedFor) {
const xForwardedFor = getRequestHeader(event, "x-forwarded-for")
?.split(",")
?.pop();
if (xForwardedFor) {
return xForwardedFor;
}
}

if (event.node.req.socket.remoteAddress) {
return event.node.req.socket.remoteAddress;
harlan-zw marked this conversation as resolved.
Show resolved Hide resolved
}

Check warning on line 179 in src/utils/request.ts

View check run for this annotation

Codecov / codecov/patch

src/utils/request.ts#L176-L179

Added lines #L176 - L179 were not covered by tests
}
43 changes: 43 additions & 0 deletions test/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
getQuery,
getRequestURL,
readFormData,
getRequestIP,
} from "../src";

describe("", () => {
Expand Down Expand Up @@ -144,6 +145,48 @@ describe("", () => {
}
});

describe("getRequestIP", () => {
it("x-forwarded-for", async () => {
app.use(
"/",
eventHandler((event) => {
return getRequestIP(event, {
xForwardedFor: true,
});
}),
);
const req = request.get("/");
req.set("x-forwarded-for", "127.0.0.1");
harlan-zw marked this conversation as resolved.
Show resolved Hide resolved
expect((await req).text).toBe("127.0.0.1");
});
it("ports", async () => {
app.use(
"/",
eventHandler((event) => {
return getRequestIP(event, {
xForwardedFor: true,
});
}),
);
const req = request.get("/");
req.set("x-forwarded-for", "127.0.0.1:1234");
expect((await req).text).toBe("127.0.0.1:1234");
});
it("ipv6", async () => {
app.use(
"/",
eventHandler((event) => {
return getRequestIP(event, {
xForwardedFor: true,
});
}),
);
const req = request.get("/");
req.set("x-forwarded-for", "2001:0db8:85a3:0000:0000:8a2e:0370:7334");
expect((await req).text).toBe("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
});
});

describe("assertMethod", () => {
it("only allow head and post", async () => {
app.use(
Expand Down