Skip to content

Commit

Permalink
fix(routing): actions should redirect the original pathname (#12173) (#…
Browse files Browse the repository at this point in the history
…12235)

Co-authored-by: Bjorn Lu <bjornlu.dev@gmail.com>
  • Loading branch information
ematipico and bluwy authored Oct 18, 2024
1 parent d6f1704 commit a75bc5e
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 35 deletions.
5 changes: 5 additions & 0 deletions .changeset/tough-snakes-reflect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Fixes a bug where Astro Actions couldn't redirect to the correct pathname when there was a rewrite involved.
3 changes: 1 addition & 2 deletions packages/astro/e2e/actions-blog.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,7 @@ test.describe('Astro Actions - Blog', () => {
await expect(page).toHaveURL(astro.resolveUrl('/blog/'));
});

// TODO: fix regression #12201 and #12202
test.skip('Should redirect to the origin pathname when there is a rewrite', async ({
test('Should redirect to the origin pathname when there is a rewrite', async ({
page,
astro,
}) => {
Expand Down
6 changes: 6 additions & 0 deletions packages/astro/src/actions/runtime/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { yellow } from 'kleur/colors';
import type { APIContext, MiddlewareNext } from '../../@types/astro.js';
import { defineMiddleware } from '../../core/middleware/index.js';
import { getOriginPathname } from '../../core/routing/rewrite.js';
import { ACTION_QUERY_PARAMS } from '../consts.js';
import { formContentTypes, hasContentType } from './utils.js';
import { getAction } from './virtual/get-action.js';
Expand Down Expand Up @@ -135,6 +136,11 @@ async function redirectWithResult({
return context.redirect(referer);
}

const referer = getOriginPathname(context.request);
if (referer) {
return context.redirect(referer);
}

return context.redirect(context.url.pathname);
}

Expand Down
3 changes: 1 addition & 2 deletions packages/astro/src/core/app/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import fs from 'node:fs';
import type { IncomingMessage, ServerResponse } from 'node:http';
import { Http2ServerResponse } from 'node:http2';
import type { RouteData } from '../../@types/astro.js';
import { clientAddressSymbol } from '../constants.js';
import { deserializeManifest } from './common.js';
import { createOutgoingHttpHeaders } from './createOutgoingHttpHeaders.js';
import { App } from './index.js';
Expand All @@ -10,8 +11,6 @@ import type { SSRManifest, SerializedSSRManifest } from './types.js';

export { apply as applyPolyfills } from '../polyfill.js';

const clientAddressSymbol = Symbol.for('astro.clientAddress');

/**
* Allow the request body to be explicitly overridden. For example, this
* is used by the Express JSON middleware.
Expand Down
6 changes: 6 additions & 0 deletions packages/astro/src/core/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const REROUTE_DIRECTIVE_HEADER = 'X-Astro-Reroute';
* This metadata is used to determine the origin of a Response. If a rewrite has occurred, it should be prioritised over other logic.
*/
export const REWRITE_DIRECTIVE_HEADER_KEY = 'X-Astro-Rewrite';

export const REWRITE_DIRECTIVE_HEADER_VALUE = 'yes';

/**
Expand Down Expand Up @@ -68,6 +69,11 @@ export const clientAddressSymbol = Symbol.for('astro.clientAddress');
*/
export const clientLocalsSymbol = Symbol.for('astro.locals');

/**
* Use this symbol to set and retrieve the original pathname of a request. This is useful when working with redirects and rewrites
*/
export const originPathnameSymbol = Symbol.for('astro.originPathname');

/**
* The symbol used as a field on the response object to keep track of streaming.
*
Expand Down
35 changes: 4 additions & 31 deletions packages/astro/src/core/render-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { callMiddleware } from './middleware/callMiddleware.js';
import { sequence } from './middleware/index.js';
import { renderRedirect } from './redirects/render.js';
import { type Pipeline, Slots, getParams, getProps } from './render/index.js';
import { copyRequest, setOriginPathname } from './routing/rewrite.js';

export const apiContextRoutesSymbol = Symbol.for('context.routes');

Expand Down Expand Up @@ -83,6 +84,7 @@ export class RenderContext {
Pick<RenderContext, 'locals' | 'middleware' | 'status' | 'props' | 'partial'>
>): Promise<RenderContext> {
const pipelineMiddleware = await pipeline.getMiddleware();
setOriginPathname(request, pathname);
return new RenderContext(
pipeline,
locals,
Expand Down Expand Up @@ -156,7 +158,7 @@ export class RenderContext {
if (payload instanceof Request) {
this.request = payload;
} else {
this.request = this.#copyRequest(newUrl, this.request);
this.request = copyRequest(newUrl, this.request);
}
this.isRewriting = true;
this.url = new URL(this.request.url);
Expand Down Expand Up @@ -256,7 +258,7 @@ export class RenderContext {
if (reroutePayload instanceof Request) {
this.request = reroutePayload;
} else {
this.request = this.#copyRequest(newUrl, this.request);
this.request = copyRequest(newUrl, this.request);
}
this.url = new URL(this.request.url);
this.cookies = new AstroCookies(this.request);
Expand Down Expand Up @@ -570,33 +572,4 @@ export class RenderContext {
if (!i18n) return;
return (this.#preferredLocaleList ??= computePreferredLocaleList(request, i18n.locales));
}

/**
* Utility function that creates a new `Request` with a new URL from an old `Request`.
*
* @param newUrl The new `URL`
* @param oldRequest The old `Request`
*/
#copyRequest(newUrl: URL, oldRequest: Request): Request {
if (oldRequest.bodyUsed) {
throw new AstroError(AstroErrorData.RewriteWithBodyUsed);
}
return new Request(newUrl, {
method: oldRequest.method,
headers: oldRequest.headers,
body: oldRequest.body,
referrer: oldRequest.referrer,
referrerPolicy: oldRequest.referrerPolicy,
mode: oldRequest.mode,
credentials: oldRequest.credentials,
cache: oldRequest.cache,
redirect: oldRequest.redirect,
integrity: oldRequest.integrity,
signal: oldRequest.signal,
keepalive: oldRequest.keepalive,
// https://fetch.spec.whatwg.org/#dom-request-duplex
// @ts-expect-error It isn't part of the types, but undici accepts it and it allows to carry over the body to a new request
duplex: 'half',
});
}
}
43 changes: 43 additions & 0 deletions packages/astro/src/core/routing/rewrite.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { AstroConfig, RewritePayload, RouteData } from '../../@types/astro.js';
import { shouldAppendForwardSlash } from '../build/util.js';
import { originPathnameSymbol } from '../constants.js';
import { AstroError, AstroErrorData } from '../errors/index.js';
import { appendForwardSlash, removeTrailingForwardSlash } from '../path.js';
import { DEFAULT_404_ROUTE } from './astro-designed-error-pages.js';

Expand Down Expand Up @@ -70,3 +72,44 @@ export function findRouteToRewrite({
}
}
}

/**
* Utility function that creates a new `Request` with a new URL from an old `Request`.
*
* @param newUrl The new `URL`
* @param oldRequest The old `Request`
*/
export function copyRequest(newUrl: URL, oldRequest: Request): Request {
if (oldRequest.bodyUsed) {
throw new AstroError(AstroErrorData.RewriteWithBodyUsed);
}
return new Request(newUrl, {
method: oldRequest.method,
headers: oldRequest.headers,
body: oldRequest.body,
referrer: oldRequest.referrer,
referrerPolicy: oldRequest.referrerPolicy,
mode: oldRequest.mode,
credentials: oldRequest.credentials,
cache: oldRequest.cache,
redirect: oldRequest.redirect,
integrity: oldRequest.integrity,
signal: oldRequest.signal,
keepalive: oldRequest.keepalive,
// https://fetch.spec.whatwg.org/#dom-request-duplex
// @ts-expect-error It isn't part of the types, but undici accepts it and it allows to carry over the body to a new request
duplex: 'half',
});
}

export function setOriginPathname(request: Request, pathname: string): void {
Reflect.set(request, originPathnameSymbol, encodeURIComponent(pathname));
}

export function getOriginPathname(request: Request): string | undefined {
const origin = Reflect.get(request, originPathnameSymbol);
if (origin) {
return decodeURIComponent(origin);
}
return undefined;
}

0 comments on commit a75bc5e

Please sign in to comment.