Skip to content

Commit

Permalink
Call server island early so it can set headers (#12486)
Browse files Browse the repository at this point in the history
Add changeset

Proper fix

changeup
  • Loading branch information
matthewp authored Nov 21, 2024
1 parent a9ce785 commit dc3d842
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/rotten-dodos-judge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Call server island early so it can set headers
17 changes: 16 additions & 1 deletion packages/astro/src/core/server-islands/endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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)}`;
};

Expand Down
4 changes: 2 additions & 2 deletions packages/astro/src/vite-plugin-astro-server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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>
20 changes: 20 additions & 0 deletions packages/astro/test/server-islands.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand All @@ -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/');
Expand Down

0 comments on commit dc3d842

Please sign in to comment.