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,
|
renderComponent,
|
||||||
renderTemplate,
|
renderTemplate,
|
||||||
} from '../../runtime/server/index.js';
|
} from '../../runtime/server/index.js';
|
||||||
|
import { isAstroComponentFactory } from '../../runtime/server/render/astro/factory.js';
|
||||||
import { createSlotValueFromString } from '../../runtime/server/render/slot.js';
|
import { createSlotValueFromString } from '../../runtime/server/render/slot.js';
|
||||||
import type { ComponentInstance, ManifestData } from '../../types/astro.js';
|
import type { ComponentInstance, ManifestData } from '../../types/astro.js';
|
||||||
import type { RouteData, SSRManifest } from '../../types/public/internal.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 key = await manifest.key;
|
||||||
const encryptedProps = data.encryptedProps;
|
const encryptedProps = data.encryptedProps;
|
||||||
|
|
||||||
const propString = await decryptString(key, encryptedProps);
|
const propString = await decryptString(key, encryptedProps);
|
||||||
const props = JSON.parse(propString);
|
const props = JSON.parse(propString);
|
||||||
|
|
||||||
const componentModule = await imp();
|
const componentModule = await imp();
|
||||||
const Component = (componentModule as any)[data.componentExport];
|
let Component = (componentModule as any)[data.componentExport];
|
||||||
|
|
||||||
const slots: ComponentSlots = {};
|
const slots: ComponentSlots = {};
|
||||||
for (const prop in data.slots) {
|
for (const prop in data.slots) {
|
||||||
slots[prop] = createSlotValueFromString(data.slots[prop]);
|
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)}`;
|
return renderTemplate`${renderComponent(result, 'Component', Component, props, slots)}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import type * as vite from 'vite';
|
||||||
import { normalizePath } from 'vite';
|
import { normalizePath } from 'vite';
|
||||||
import type { SSRManifest, SSRManifestI18n } from '../core/app/types.js';
|
import type { SSRManifest, SSRManifestI18n } from '../core/app/types.js';
|
||||||
import { warnMissingAdapter } from '../core/dev/adapter-validation.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 { getViteErrorPayload } from '../core/errors/dev/index.js';
|
||||||
import { AstroError, AstroErrorData } from '../core/errors/index.js';
|
import { AstroError, AstroErrorData } from '../core/errors/index.js';
|
||||||
import { patchOverlay } from '../core/errors/overlay.js';
|
import { patchOverlay } from '../core/errors/overlay.js';
|
||||||
|
@ -192,7 +192,7 @@ export function createDevelopmentManifest(settings: AstroSettings): SSRManifest
|
||||||
checkOrigin:
|
checkOrigin:
|
||||||
(settings.config.security?.checkOrigin && settings.buildOutput === 'server') ?? false,
|
(settings.config.security?.checkOrigin && settings.buildOutput === 'server') ?? false,
|
||||||
envGetSecretEnabled: false,
|
envGetSecretEnabled: false,
|
||||||
key: createKey(),
|
key: hasEnvironmentKey() ? getEnvironmentKey() : createKey(),
|
||||||
middleware() {
|
middleware() {
|
||||||
return {
|
return {
|
||||||
onRequest: NOOP_MIDDLEWARE_FN,
|
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>
|
<h2 id="island">I'm an island</h2>
|
||||||
|
|
|
@ -19,11 +19,13 @@ describe('Server islands', () => {
|
||||||
let devServer;
|
let devServer;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
|
process.env.ASTRO_KEY = 'eKBaVEuI7YjfanEXHuJe/pwZKKt3LkAHeMxvTU7aR0M=';
|
||||||
devServer = await fixture.startDevServer();
|
devServer = await fixture.startDevServer();
|
||||||
});
|
});
|
||||||
|
|
||||||
after(async () => {
|
after(async () => {
|
||||||
await devServer.stop();
|
await devServer.stop();
|
||||||
|
delete process.env.ASTRO_KEY;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('omits the islands HTML', async () => {
|
it('omits the islands HTML', async () => {
|
||||||
|
@ -34,13 +36,31 @@ describe('Server islands', () => {
|
||||||
const serverIslandEl = $('h2#island');
|
const serverIslandEl = $('h2#island');
|
||||||
assert.equal(serverIslandEl.length, 0);
|
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', () => {
|
describe('prod', () => {
|
||||||
before(async () => {
|
before(async () => {
|
||||||
|
process.env.ASTRO_KEY = 'eKBaVEuI7YjfanEXHuJe/pwZKKt3LkAHeMxvTU7aR0M=';
|
||||||
await fixture.build();
|
await fixture.build();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
delete process.env.ASTRO_KEY;
|
||||||
|
});
|
||||||
|
|
||||||
it('omits the islands HTML', async () => {
|
it('omits the islands HTML', async () => {
|
||||||
const app = await fixture.loadTestAdapterApp();
|
const app = await fixture.loadTestAdapterApp();
|
||||||
const request = new Request('http://example.com/');
|
const request = new Request('http://example.com/');
|
||||||
|
|
Loading…
Reference in a new issue