Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: support HTTP/2 in astro dev #11284

Merged
merged 4 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/six-fans-kiss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'astro': patch
---

Fixes an issue that would break `Astro.request.url` and `Astro.request.headers` in `astro dev` if HTTP/2 was enabled.

HTTP/2 is now enabled by default in `astro dev` if `https` is configured in the Vite config.
1 change: 1 addition & 0 deletions packages/astro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@
"rollup": "^4.18.0",
"sass": "^1.77.5",
"srcset-parse": "^1.1.0",
"undici": "^6.19.2",
"unified": "^11.0.4"
},
"engines": {
Expand Down
4 changes: 0 additions & 4 deletions packages/astro/src/core/create-vite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,6 @@ export async function createVite(
process.env.NODE_ENV === 'test' || process.env.NODE_ENV === 'production'
? false
: undefined, // disable HMR for test
// handle Vite URLs
proxy: {
// add proxies here
},
watch: {
// Prevent watching during the build to speed it up
ignored: mode === 'build' ? ['**'] : undefined,
Expand Down
5 changes: 4 additions & 1 deletion packages/astro/src/core/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ export function createRequest({
? undefined
: headers instanceof Headers
? headers
: new Headers(Object.entries(headers as Record<string, any>));
: new Headers(
// Filter out H2 pseudo-headers, as these can't be created with Headers
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are H2 pseudo-headers?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They're fake headers that are added to all HTTP/2 requests. They're only allowed to be generated internally though: it's an error to add one to Headers yourself. https://httpwg.org/specs/rfc7540.html#HttpRequest

Object.entries(headers as Record<string, any>).filter(([name]) => !name.startsWith(':'))
);

if (typeof url === 'string') url = new URL(url);

Expand Down
4 changes: 3 additions & 1 deletion packages/astro/src/vite-plugin-astro-server/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ export async function handleRequest({
incomingResponse,
}: HandleRequest) {
const { config, loader } = pipeline;
const origin = `${loader.isHttps() ? 'https' : 'http'}://${incomingRequest.headers.host}`;
const origin = `${loader.isHttps() ? 'https' : 'http'}://${
incomingRequest.headers[':authority'] ?? incomingRequest.headers.host
}`;

const url = new URL(origin + incomingRequest.url);
let pathname: string;
Expand Down
37 changes: 37 additions & 0 deletions packages/astro/test/astro-dev-http2.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
import * as cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';

describe('Astro HTTP/2 support', () => {
let fixture;
let devServer;

before(async () => {
fixture = await loadFixture({
root: './fixtures/astro-dev-http2/',
});
devServer = await fixture.startDevServer();
});

after(async () => {
await devServer.stop();
});

describe('dev', () => {
it('returns custom headers for valid URLs', async () => {
const result = await fixture.fetch('/');
assert.equal(result.status, 200);
const html = await result.text();
console.log(result.headers);
const $ = cheerio.load(html);
const urlString = $('main').text();
assert.equal(Boolean(urlString), true);
const url = new URL(urlString);
// Not asserting host because of all the ways localhost can be represented
assert.equal(url.protocol, 'https:');
assert.equal(url.port, '4321');
assert.equal($('p').text(), '2.0');
});
});
});
24 changes: 24 additions & 0 deletions packages/astro/test/fixtures/astro-dev-http2/.cert/cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIIEHDCCAoSgAwIBAgIRAIWPzjvpgZGzQqe1TFcdGmAwDQYJKoZIhvcNAQELBQAw
ZTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMR0wGwYDVQQLDBRhbGV4
QERFU0tUT1AtMFM5OFUzMDEkMCIGA1UEAwwbbWtjZXJ0IGFsZXhAREVTS1RPUC0w
Uzk4VTMwMB4XDTI0MDIyNjAwNDkyOVoXDTI2MDUyNTIzNDkyOVowSDEnMCUGA1UE
ChMebWtjZXJ0IGRldmVsb3BtZW50IGNlcnRpZmljYXRlMR0wGwYDVQQLDBRhbGV4
QERFU0tUT1AtMFM5OFUzMDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AMRj8/YGXRRWkpIxdaWbL+v2RZoI7iGOyJliC2Iudag5/irpiBAFgZAqpjSb1i1E
OKkXRQlCx21jQWe/jEFGqEPIqjeLPrXHATKU+6prOH2FV2qF7PDd0gi1gkR0cxX7
dUA9kUeqm1HHkogUuhRjg5uCklyCraN49yz6QU6U7uiTo4ZM9mjfig0EfG2W1DBp
G0bKkEhgkSKw3v1mvGVYN5yAv6unLjDVJGwLKqTTpDpsahG47h+ZPHj7wjSOQiDB
tXR+HNLJdSe59+GQ8D5/M7hRG6rZ+8GzaNjQWRl8BK6Ls0k1qtMgcEFeNDLEWTj1
16yNmd4/IX4irMmSA+F7PgUCAwEAAaNkMGIwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
JQQMMAoGCCsGAQUFBwMBMB8GA1UdIwQYMBaAFE+Vk1SZFKjFDIsieTVT/860OBs6
MBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAAATANBgkqhkiG9w0BAQsFAAOCAYEA
szcc7pdwdYu3uIN8f8LHFABDhxiPDLMqmyEJXym5z8c44Mtl0mfGnKs0uIzl/XtL
F1aLH8yHubZ1LJkIczAypcryekmk+VzTsNdv1aKelhsZ9QJUxg/NsrMXe5DZ9Eeu
KxlJBJKo+oRpDsxRYo1l5FvIljcVvOUTaKR3UtY6FU2xdDMNMtoJDbaJCPAg143H
ZnSa7xEv7fGDTcn9oKFc1fc1BnCy4qCHkxF8pIeXbXEZ/q1fqNNtp87/PigPT7YO
ppcYvEsj4Y+6yuDfIrWAZNcbtiOfFUyPXy1KN+/VxZhcZ/MuAbl4EiESDFbE5j8U
whIHlRXUY6B09PL9NVNGyjNDH3NMQkSKVFA2KVeaFeDIROjPeMkrKY56lWVpEiru
HVLuFVpM27uJEKgSNeAYttfOEvsvr20Otmt29Uu59qfX+w0crQUEElcUJB4DgRH+
NVoYZLYMm88B5aVUsmwkkrJJrAJ+M6UnzIhdN2alJxXojW38jDWzn+dWbc0a2hIB
-----END CERTIFICATE-----
28 changes: 28 additions & 0 deletions packages/astro/test/fixtures/astro-dev-http2/.cert/key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEY/P2Bl0UVpKS
MXWlmy/r9kWaCO4hjsiZYgtiLnWoOf4q6YgQBYGQKqY0m9YtRDipF0UJQsdtY0Fn
v4xBRqhDyKo3iz61xwEylPuqazh9hVdqhezw3dIItYJEdHMV+3VAPZFHqptRx5KI
FLoUY4ObgpJcgq2jePcs+kFOlO7ok6OGTPZo34oNBHxtltQwaRtGypBIYJEisN79
ZrxlWDecgL+rpy4w1SRsCyqk06Q6bGoRuO4fmTx4+8I0jkIgwbV0fhzSyXUnuffh
kPA+fzO4URuq2fvBs2jY0FkZfASui7NJNarTIHBBXjQyxFk49desjZnePyF+IqzJ
kgPhez4FAgMBAAECggEBAL+70d89ATys9LYT4YcIBnY5XmRvGYXbr47IANMfBrFx
xOpCSxtRNNf6O4AbMLPK6gJzfGv5LVhnUeCnSpgkEnzy+PP3VwcDPfETMMyFl4Y8
W0bdb6EM/1SPWJnaks1ATY2lTiQItVDXJgEDM1Raf4+gn6H/1uRFYhQgUwgUMVcP
rb/6qk+vFJXGuyx1R1DoIoqz+8iBfcsddUplx8ulJv74/Lgn5B/5sZ6I6cnW8lva
FskuY1DL2RUywuV2J2fnFGNuGPPvc5elE0ORYLjlUGDMHQq+a9UxXsfCC4gHoks1
P4vYZjzOVGRA3o+F8khuZLeCebMsKnoyzmkeRGejlzUCgYEA8d+TtwU3qlFUl3sa
TGJm+tD5sgLaZHDssMhkkTtTVzYIllFSyvT8UI7/9ZwYinq0JOnGN0Cb7TsH4AGQ
jQzHfiudibvODK4HL3rVkiOwj+kdjH+oTMlCTGsCj9uZhEajzKpXgpSlYkpqhDR2
zCdMdFXCE+SpJCJaTI+jcbup6P8CgYEAz9xUBQIk8CdySkgIB0gmpIgtvS1EwYod
YvYu+coQ1lEtALetDdbx5VfasWd1A18sIFlkfZZMKr5+1QXVuKOFhx2n49XIev/6
t+Hgx8aToIpB0tz/3CTea5HGK8FvX6t5QKDL6XqDrRGO1FVdMSWl9WLfmpTynJdj
sNHr7JFwNPsCgYAz5OE/ekoYK7z3hzz8OHyZwa5hCAWtWSEfSM9y7YSTCI/NGIOn
8eoUqqm2G5iUVYFDDjkt75nEy06EPDG0YZKHunnhbD7oL4pxIGykHy4poj1pwJXu
a5vi4264SMhmPfW02rNN2/Cj5w11cgAvCxt3NlMei4fSreAr3wGVTEtHJwKBgEBw
QIfQ81yUDgVjMUH4pyoooW1dRExvoc6VHVkIwJGAVuA7EOYSdakwxDZtKURjU82v
iMy6NGCn76/ggDIeV33cvriOBPnEs5gf6Uxljkydr+xL4PIBaAaXCYV1ES7qfMuB
TdXSylFz+QBwelSLJFjfTwygElpjQF+HpIkRSWTTAoGBAJIqeQ4edg4weut00+32
A8rQEpiz5SByPYNUPCI5BReKd+/Dw2QdXnfJNVg7/NFfuUdvPVkmptsisYdfWhlp
y+KFAwdbgDUU+ruPuv5fHU4sA85Uuxazr/YXZIB6wmsQKt8cNezqNhjY6UovTOdZ
7qnkPUO8VGcnrRZiav8WIevg
-----END PRIVATE KEY-----
26 changes: 26 additions & 0 deletions packages/astro/test/fixtures/astro-dev-http2/astro.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { defineConfig } from "astro/config";
import { readFileSync } from "fs";
// https://astro.build/config
export default defineConfig({
output: "hybrid",
vite: {
server: {
https: {
key: readFileSync(new URL(".cert/key.pem", import.meta.url)),
cert: readFileSync(new URL(".cert/cert.pem", import.meta.url)),
},
},
plugins: [
{
name: 'http-version-plugin',
// This plugin allows tests to track the version of HTTP used in the request
configureServer: (server) => {
server.middlewares.use((req, res, next) => {
req.headers['x-http-version'] = req.httpVersion;
next();
});
}
},
],
},
});
8 changes: 8 additions & 0 deletions packages/astro/test/fixtures/astro-dev-http2/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "@test/astro-dev-http2",
"version": "0.0.1",
"private": true,
"dependencies": {
"astro": "workspace:*"
}
}
17 changes: 17 additions & 0 deletions packages/astro/test/fixtures/astro-dev-http2/src/pages/index.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
const url = Astro.request.url
const httpVersion = Astro.request.headers.get('x-http-version')
export const prerender = false
---

<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
</head>
<body>
<main>{url}</main>
<p>{httpVersion}</p>
</body>
</html>
19 changes: 18 additions & 1 deletion packages/astro/test/test-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { fileURLToPath } from 'node:url';
import { execa } from 'execa';
import fastGlob from 'fast-glob';
import stripAnsi from 'strip-ansi';
import { Agent } from 'undici';
import { check } from '../dist/cli/check/index.js';
import build from '../dist/core/build/index.js';
import { RESOLVED_SPLIT_MODULE_ID } from '../dist/core/build/plugins/plugin-ssr.js';
Expand Down Expand Up @@ -122,8 +123,13 @@ export async function loadFixture(inlineConfig) {
// Load the config.
const { astroConfig: config } = await resolveConfig(inlineConfig, 'dev');

const protocol = config.vite?.server?.https ? 'https' : 'http';

const resolveUrl = (url) =>
`http://${config.server.host || 'localhost'}:${config.server.port}${url.replace(/^\/?/, '/')}`;
`${protocol}://${config.server.host || 'localhost'}:${config.server.port}${url.replace(
/^\/?/,
'/'
)}`;

// A map of files that have been edited.
let fileEdits = new Map();
Expand Down Expand Up @@ -171,6 +177,17 @@ export async function loadFixture(inlineConfig) {
config,
resolveUrl,
fetch: async (url, init) => {
if (config.vite?.server?.https) {
init = {
dispatcher: new Agent({
connect: {
rejectUnauthorized: false,
},
allowH2: true,
}),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this for? I have no knowledge of this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This tells undici fetch to use a custom dispatcher. This lets you customise some of the HTTP options for the request. rejectUnauthorized lets you ignore invalid server certificates, which is needed here as the dev server will always use self-signed certs. allowH2 enables HTTP/2 requests in fetch, if the server supports it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add comments for both of these

...init,
};
}
const resolvedUrl = resolveUrl(url);
try {
return await fetch(resolvedUrl, init);
Expand Down
19 changes: 14 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading