Skip to content

Commit 299ad49

Browse files
committed
Fix Astro.url.protocol when using the @astrojs/node SSR adapter with HTTPS
1 parent 088f519 commit 299ad49

7 files changed

Lines changed: 116 additions & 2 deletions

File tree

.changeset/odd-rats-drop.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'astro': patch
3+
'@astrojs/node': patch
4+
---
5+
6+
Fix `Astro.url.protocol` when using the @astrojs/node SSR adapter with HTTPS

packages/astro/src/core/app/node.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,18 @@ import type { SerializedSSRManifest, SSRManifest } from './types';
33

44
import * as fs from 'fs';
55
import { IncomingMessage } from 'http';
6+
import { TLSSocket } from 'tls';
67
import { deserializeManifest } from './common.js';
78
import { App, MatchOptions } from './index.js';
89

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

1112
function createRequestFromNodeRequest(req: IncomingMessage, body?: Uint8Array): Request {
12-
let url = `http://${req.headers.host}${req.url}`;
13+
const protocol =
14+
req.socket instanceof TLSSocket || req.headers['x-forwarded-proto'] === 'https'
15+
? 'https'
16+
: 'http';
17+
let url = `${protocol}://${req.headers.host}${req.url}`;
1318
let rawHeaders = req.headers as Record<string, any>;
1419
const entries = Object.entries(rawHeaders);
1520
const method = req.method || 'GET';

packages/integrations/node/src/standalone.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { NodeApp } from 'astro/app/node';
2+
import https from 'https';
23
import path from 'path';
34
import { fileURLToPath } from 'url';
45
import { createServer } from './http-server.js';
@@ -53,8 +54,9 @@ export default function startServer(app: NodeApp, options: Options) {
5354
handler
5455
);
5556

57+
const protocol = server.server instanceof https.Server ? 'https' : 'http';
5658
// eslint-disable-next-line no-console
57-
console.log(`Server listening on http://${host}:${port}`);
59+
console.log(`Server listening on ${protocol}://${host}:${port}`);
5860

5961
return server.closed();
6062
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "@test/url-protocol",
3+
"version": "0.0.0",
4+
"private": true,
5+
"dependencies": {
6+
"astro": "workspace:*",
7+
"@astrojs/node": "workspace:*"
8+
}
9+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
---
3+
4+
<html lang="en">
5+
<head>
6+
<title>url-protocol</title>
7+
</head>
8+
<body>
9+
{Astro.url.protocol}
10+
</body>
11+
</html>
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { TLSSocket } from 'tls';
2+
import nodejs from '../dist/index.js';
3+
import { loadFixture, createRequestAndResponse } from './test-utils.js';
4+
import { expect } from 'chai';
5+
6+
describe('URL protocol', () => {
7+
/** @type {import('./test-utils').Fixture} */
8+
let fixture;
9+
10+
before(async () => {
11+
fixture = await loadFixture({
12+
root: './fixtures/url-protocol/',
13+
output: 'server',
14+
adapter: nodejs({ mode: 'standalone' }),
15+
});
16+
await fixture.build();
17+
});
18+
19+
it('return http when non-secure', async () => {
20+
const { handler } = await import('./fixtures/url-protocol/dist/server/entry.mjs');
21+
let { req, res, text } = createRequestAndResponse({
22+
url: '/',
23+
});
24+
25+
handler(req, res);
26+
req.send();
27+
28+
const html = await text();
29+
expect(html).to.include('http:');
30+
});
31+
32+
it('return https when secure', async () => {
33+
const { handler } = await import('./fixtures/url-protocol/dist/server/entry.mjs');
34+
let { req, res, text } = createRequestAndResponse({
35+
socket: new TLSSocket(),
36+
url: '/',
37+
});
38+
39+
handler(req, res);
40+
req.send();
41+
42+
const html = await text();
43+
expect(html).to.include('https:');
44+
});
45+
46+
it('return http when the X-Forwarded-Proto header is set to http', async () => {
47+
const { handler } = await import('./fixtures/url-protocol/dist/server/entry.mjs');
48+
let { req, res, text } = createRequestAndResponse({
49+
headers: { 'X-Forwarded-Proto': 'http' },
50+
url: '/',
51+
});
52+
53+
handler(req, res);
54+
req.send();
55+
56+
const html = await text();
57+
expect(html).to.include('http:');
58+
});
59+
60+
it('return https when the X-Forwarded-Proto header is set to https', async () => {
61+
const { handler } = await import('./fixtures/url-protocol/dist/server/entry.mjs');
62+
let { req, res, text } = createRequestAndResponse({
63+
headers: { 'X-Forwarded-Proto': 'https' },
64+
url: '/',
65+
});
66+
67+
handler(req, res);
68+
req.send();
69+
70+
const html = await text();
71+
expect(html).to.include('https:');
72+
});
73+
});

pnpm-lock.yaml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)