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

expose event.clientAddress #4289

Merged
merged 16 commits into from
Mar 14, 2022
7 changes: 6 additions & 1 deletion packages/adapter-netlify/src/handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ export function init(manifest) {
const server = new Server(manifest);

return async (event, context) => {
const rendered = await server.respond(to_request(event), { platform: { context } });
const rendered = await server.respond(to_request(event), {
platform: { context },
getClientAddress() {
return event.headers['x-nf-client-connection-ip'];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ascorbic I wanted to confirm with you if we're doing this correctly because it seems to be undocumented, but is what is suggested in this support post: https://answers.netlify.com/t/is-the-client-ip-header-going-to-be-supported-long-term/11203

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ascorbic I wanted to confirm with you if we're doing this correctly because it seems to be undocumented, but is what is suggested in this support post: https://answers.netlify.com/t/is-the-client-ip-header-going-to-be-supported-long-term/11203

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@benmccann Yes, that's correct. We recently cleaned up the internal headers sent to functions so that all remaining ones are now officially supported. https://answers.netlify.com/t/upcoming-change-stripping-exposed-netlify-headers-from-function-and-proxy-requests/52665

}
});

const partial_response = {
statusCode: rendered.status,
Expand Down
7 changes: 7 additions & 0 deletions packages/kit/src/core/build/prerender/prerender.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ export async function prerender({ config, entries, files, log }) {
const dependencies = new Map();

const response = await server.respond(new Request(`http://sveltekit-prerender${encoded}`), {
getClientAddress,
prerender: {
default: config.kit.prerender.default,
dependencies
Expand Down Expand Up @@ -268,6 +269,7 @@ export async function prerender({ config, entries, files, log }) {
}

const rendered = await server.respond(new Request('http://sveltekit-prerender/[fallback]'), {
getClientAddress,
prerender: {
fallback: true,
default: false,
Expand All @@ -281,3 +283,8 @@ export async function prerender({ config, entries, files, log }) {

return prerendered;
}

/** @return {string} */
function getClientAddress() {
throw new Error('Cannot read clientAddress during prerendering');
}
116 changes: 63 additions & 53 deletions packages/kit/src/core/dev/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,62 +259,72 @@ export async function create_plugin(config, cwd) {

const template = load_template(cwd, config);

const rendered = await respond(request, {
amp: config.kit.amp,
csp: config.kit.csp,
dev: true,
floc: config.kit.floc,
get_stack: (error) => {
return fix_stack_trace(error);
},
handle_error: (error, event) => {
hooks.handleError({
error: new Proxy(error, {
get: (target, property) => {
if (property === 'stack') {
return fix_stack_trace(error);
const rendered = await respond(
request,
{
amp: config.kit.amp,
csp: config.kit.csp,
dev: true,
floc: config.kit.floc,
get_stack: (error) => {
return fix_stack_trace(error);
},
handle_error: (error, event) => {
hooks.handleError({
error: new Proxy(error, {
get: (target, property) => {
if (property === 'stack') {
return fix_stack_trace(error);
}

return Reflect.get(target, property, target);
}

return Reflect.get(target, property, target);
}),
event,

// TODO remove for 1.0
// @ts-expect-error
get request() {
throw new Error(
'request in handleError has been replaced with event. See https://github.com/sveltejs/kit/pull/3384 for details'
);
}
}),
event,

// TODO remove for 1.0
// @ts-expect-error
get request() {
throw new Error(
'request in handleError has been replaced with event. See https://github.com/sveltejs/kit/pull/3384 for details'
);
}
});
},
hooks,
hydrate: config.kit.browser.hydrate,
manifest,
method_override: config.kit.methodOverride,
paths: {
base: config.kit.paths.base,
assets
},
prefix: '',
prerender: config.kit.prerender.enabled,
read: (file) => fs.readFileSync(path.join(config.kit.files.assets, file)),
root,
router: config.kit.browser.router,
template: ({ head, body, assets, nonce }) => {
return (
template
.replace(/%svelte\.assets%/g, assets)
.replace(/%svelte\.nonce%/g, nonce)
// head and body must be replaced last, in case someone tries to sneak in %svelte.assets% etc
.replace('%svelte.head%', () => head)
.replace('%svelte.body%', () => body)
);
});
},
hooks,
hydrate: config.kit.browser.hydrate,
manifest,
method_override: config.kit.methodOverride,
paths: {
base: config.kit.paths.base,
assets
},
prefix: '',
prerender: config.kit.prerender.enabled,
read: (file) => fs.readFileSync(path.join(config.kit.files.assets, file)),
root,
router: config.kit.browser.router,
template: ({ head, body, assets, nonce }) => {
return (
template
.replace(/%svelte\.assets%/g, assets)
.replace(/%svelte\.nonce%/g, nonce)
// head and body must be replaced last, in case someone tries to sneak in %svelte.assets% etc
.replace('%svelte.head%', () => head)
.replace('%svelte.body%', () => body)
);
},
template_contains_nonce: template.includes('%svelte.nonce%'),
trailing_slash: config.kit.trailingSlash
},
template_contains_nonce: template.includes('%svelte.nonce%'),
trailing_slash: config.kit.trailingSlash
});
{
getClientAddress: () => {
const { remoteAddress } = req.socket;
if (remoteAddress) return remoteAddress;
throw new Error('Could not determine clientAddress');
Conduitry marked this conversation as resolved.
Show resolved Hide resolved
}
}
);

if (rendered) {
setResponse(res, rendered);
Expand Down
11 changes: 10 additions & 1 deletion packages/kit/src/core/preview/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,16 @@ export async function preview({ port, host, config, https: use_https = false })
return res.end(err.reason || 'Invalid request body');
}

setResponse(res, await server.respond(request));
setResponse(
res,
await server.respond(request, {
getClientAddress: () => {
const { remoteAddress } = req.socket;
if (remoteAddress) return remoteAddress;
throw new Error('Could not determine clientAddress');
Conduitry marked this conversation as resolved.
Show resolved Hide resolved
}
})
);
}
]);

Expand Down
13 changes: 8 additions & 5 deletions packages/kit/src/runtime/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const DATA_SUFFIX = '/__data.json';
const default_transform = ({ html }) => html;

/** @type {import('types').Respond} */
export async function respond(request, options, state = {}) {
export async function respond(request, options, state) {
const url = new URL(request.url);

const normalized = normalize_path(url.pathname, options.trailing_slash);
Expand Down Expand Up @@ -53,11 +53,14 @@ export async function respond(request, options, state = {}) {

/** @type {import('types').RequestEvent} */
const event = {
request,
url,
params: {},
get clientAddress() {
return (event.clientAddress = state.getClientAddress());
},
locals: {},
platform: state.platform
params: {},
platform: state.platform,
request,
url
};

// TODO remove this for 1.0
Expand Down
1 change: 1 addition & 0 deletions packages/kit/src/runtime/server/page/load_node.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ export async function load_node({

response = await respond(new Request(new URL(requested, event.url).href, opts), options, {
fetched: requested,
getClientAddress: state.getClientAddress,
initiator: route
});

Expand Down
7 changes: 4 additions & 3 deletions packages/kit/types/internal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export interface Hooks {
export class InternalServer extends Server {
respond(
request: Request,
options?: RequestOptions & {
options: RequestOptions & {
prerender?: PrerenderOptions;
}
): Promise<Response>;
Expand Down Expand Up @@ -164,7 +164,7 @@ export type RecursiveRequired<T> = {
export type RequiredResolveOptions = Required<ResolveOptions>;

export interface Respond {
(request: Request, options: SSROptions, state?: SSRState): Promise<Response>;
(request: Request, options: SSROptions, state: SSRState): Promise<Response>;
}

export type RouteData = PageData | EndpointData;
Expand Down Expand Up @@ -302,11 +302,12 @@ export interface SSRPagePart {
export type SSRRoute = SSREndpoint | SSRPage;

export interface SSRState {
fallback?: string;
fetched?: string;
getClientAddress: () => string;
initiator?: SSRPage | null;
platform?: any;
prerender?: PrerenderOptions;
fallback?: string;
}

export type StrictBody = string | Uint8Array;
Expand Down
8 changes: 5 additions & 3 deletions packages/kit/types/private.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,14 +234,16 @@ export interface PrerenderErrorHandler {
export type PrerenderOnErrorValue = 'fail' | 'continue' | PrerenderErrorHandler;

export interface RequestEvent<Params = Record<string, string>> {
request: Request;
url: URL;
params: Params;
clientAddress: string;
locals: App.Locals;
params: Params;
platform: Readonly<App.Platform>;
request: Request;
url: URL;
}

export interface RequestOptions {
getClientAddress: () => string;
platform?: App.Platform;
}

Expand Down