mirror of
https://github.com/withastro/astro.git
synced 2024-12-30 22:03:56 -05:00
Call server island early so it can set headers (#12486)
Add changeset Proper fix changeup
This commit is contained in:
parent
a9ce785146
commit
dc3d842e4c
5 changed files with 45 additions and 4 deletions
5
.changeset/rotten-dodos-judge.md
Normal file
5
.changeset/rotten-dodos-judge.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Call server island early so it can set headers
|
|
@ -4,6 +4,7 @@ import {
|
|||
renderComponent,
|
||||
renderTemplate,
|
||||
} from '../../runtime/server/index.js';
|
||||
import { isAstroComponentFactory } from '../../runtime/server/render/astro/factory.js';
|
||||
import { createSlotValueFromString } from '../../runtime/server/render/slot.js';
|
||||
import type { ComponentInstance, ManifestData } from '../../types/astro.js';
|
||||
import type { RouteData, SSRManifest } from '../../types/public/internal.js';
|
||||
|
@ -121,17 +122,31 @@ export function createEndpoint(manifest: SSRManifest) {
|
|||
|
||||
const key = await manifest.key;
|
||||
const encryptedProps = data.encryptedProps;
|
||||
|
||||
const propString = await decryptString(key, encryptedProps);
|
||||
const props = JSON.parse(propString);
|
||||
|
||||
const componentModule = await imp();
|
||||
const Component = (componentModule as any)[data.componentExport];
|
||||
let Component = (componentModule as any)[data.componentExport];
|
||||
|
||||
const slots: ComponentSlots = {};
|
||||
for (const prop in data.slots) {
|
||||
slots[prop] = createSlotValueFromString(data.slots[prop]);
|
||||
}
|
||||
|
||||
// Wrap Astro components so we can set propagation to
|
||||
// `self` which is needed to force the runtime to wait
|
||||
// on the component before sending out the response headers.
|
||||
// This allows the island to set headers (cookies).
|
||||
if(isAstroComponentFactory(Component)) {
|
||||
const ServerIsland = Component;
|
||||
Component = function(this: typeof ServerIsland, ...args: Parameters<typeof ServerIsland>) {
|
||||
return ServerIsland.apply(this, args);
|
||||
};
|
||||
Object.assign(Component, ServerIsland);
|
||||
Component.propagation = 'self';
|
||||
}
|
||||
|
||||
return renderTemplate`${renderComponent(result, 'Component', Component, props, slots)}`;
|
||||
};
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import type * as vite from 'vite';
|
|||
import { normalizePath } from 'vite';
|
||||
import type { SSRManifest, SSRManifestI18n } from '../core/app/types.js';
|
||||
import { warnMissingAdapter } from '../core/dev/adapter-validation.js';
|
||||
import { createKey } from '../core/encryption.js';
|
||||
import { createKey, getEnvironmentKey, hasEnvironmentKey } from '../core/encryption.js';
|
||||
import { getViteErrorPayload } from '../core/errors/dev/index.js';
|
||||
import { AstroError, AstroErrorData } from '../core/errors/index.js';
|
||||
import { patchOverlay } from '../core/errors/overlay.js';
|
||||
|
@ -192,7 +192,7 @@ export function createDevelopmentManifest(settings: AstroSettings): SSRManifest
|
|||
checkOrigin:
|
||||
(settings.config.security?.checkOrigin && settings.buildOutput === 'server') ?? false,
|
||||
envGetSecretEnabled: false,
|
||||
key: createKey(),
|
||||
key: hasEnvironmentKey() ? getEnvironmentKey() : createKey(),
|
||||
middleware() {
|
||||
return {
|
||||
onRequest: NOOP_MIDDLEWARE_FN,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
---
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1));
|
||||
Astro.response.headers.set('X-Works', 'true');
|
||||
---
|
||||
<h2 id="island">I'm an island</h2>
|
||||
|
|
|
@ -19,11 +19,13 @@ describe('Server islands', () => {
|
|||
let devServer;
|
||||
|
||||
before(async () => {
|
||||
process.env.ASTRO_KEY = 'eKBaVEuI7YjfanEXHuJe/pwZKKt3LkAHeMxvTU7aR0M=';
|
||||
devServer = await fixture.startDevServer();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await devServer.stop();
|
||||
delete process.env.ASTRO_KEY;
|
||||
});
|
||||
|
||||
it('omits the islands HTML', async () => {
|
||||
|
@ -34,13 +36,31 @@ describe('Server islands', () => {
|
|||
const serverIslandEl = $('h2#island');
|
||||
assert.equal(serverIslandEl.length, 0);
|
||||
});
|
||||
|
||||
it('island can set headers', async () => {
|
||||
const res = await fixture.fetch('/_server-islands/Island', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
componentExport: 'default',
|
||||
encryptedProps: 'FC8337AF072BE5B1641501E1r8mLIhmIME1AV7UO9XmW9OLD',
|
||||
slots: {},
|
||||
})
|
||||
});
|
||||
const works = res.headers.get('X-Works');
|
||||
assert.equal(works, 'true', 'able to set header from server island');
|
||||
});
|
||||
});
|
||||
|
||||
describe('prod', () => {
|
||||
before(async () => {
|
||||
process.env.ASTRO_KEY = 'eKBaVEuI7YjfanEXHuJe/pwZKKt3LkAHeMxvTU7aR0M=';
|
||||
await fixture.build();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
delete process.env.ASTRO_KEY;
|
||||
});
|
||||
|
||||
it('omits the islands HTML', async () => {
|
||||
const app = await fixture.loadTestAdapterApp();
|
||||
const request = new Request('http://example.com/');
|
||||
|
|
Loading…
Reference in a new issue