Skip to content

Commit 8d8413f

Browse files
Wdestroierbkonyi
andauthored
Support custom hostname and TLS options (#2588)
* Support custom hostname and TLS options * Format webdev_server.dart file * TLS options E2E tests * Add copyright header and remove duplicated files * Format test/tls_test.dart --------- Co-authored-by: Ben Konyi <[email protected]>
1 parent 697f2f7 commit 8d8413f

File tree

5 files changed

+176
-3
lines changed

5 files changed

+176
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDR8FlEhQ8XZctL
3+
mGdx78Z5EInK2mcfU9Nq4oUKkPeiql7Sd3cd3prd+m5MYylL255wAZf8Fdupkko2
4+
cjQEsU24jUBWVG5q3YqZ+OidFYdxYnPbDhFtzL3Lr2zHGAi+MgqVENUF6ED2O5tU
5+
9U1crceXmXHb3E8Sg+zXX6TMVCS11OH9VQ1ZutM0fFMPZxmfU5aOfd5kuTmTiaOt
6+
OMyymroUPgzRW6UcbLfSdD7KuNFQgHDHiWkr2MtoZffgGM/cOfmCdgE85MdL6kyb
7+
7TUTbAlTSpJNWwygObce7DN7qwM8HocRWSAfH+y3ewbs2NtzG1Az+a/kUBjhvmjT
8+
WFiwDT+1AgMBAAECggEBALkJMXTeHh4OP2+ipVJb9r/P3tMnSornFEl5268jdNAv
9+
f6HbX+a4xCDwUHUNVWGh8XRhQzcRgOllofl5EPYt3AXUoac1hZi1KStqooOJbTZ3
10+
gwvIy33OXl5/gM2+Fj6k1oTqMAej3FXq1Y69InGUTX4F5b/V3u+/zWlKyHK7mxuT
11+
LIP3/iAxbpST5FB1G7ZPyz5mvzvEQFRiv9ubuMxYg3fzORULtbnnHNAM5jLG3IzZ
12+
9EM/1umaeZu1bg9rGVVgr0l/rWMJSg5u+Z6jcrt9+Oj7hYrMPGHKHXAPhC1gaq+k
13+
tlKHj/mhmJyN0jUXt71UgrsWF3MhrK2W2AlIfxXhIIECgYEA8oaIEOaeNmtDqlH5
14+
MmU0jd2EcMRnLrlfKs4fKObFoSMqELRB3yJ1u5Yt/wQCCSpN9bhkinR0l4Q7YsAy
15+
4sg5Q3VaA/rUWkVKkv2sD04LbBmpHr5vksPnIPKcfAY3csQ1F5pu9gY6zgzdxBvN
16+
nWEu8tqbUSdC5+0F+tB8IDW+/lUCgYEA3ZpVXmBLvgeZJ8rNjCCeKR5hXMYdFKqC
17+
Yd5KfasTKH7h8a4GWo/mtUsS5UPRRjhrP5mPjdUKBFhSaJtX+k9Hhot58RN20fWR
18+
4Ake4yV/cNFRykD1jsv9JJiPkggnVLVXN91+/EnKJozWv46q6nG44iX+q2w2i9FQ
19+
6lohgwQp2+ECgYEAwKJY+0uiiUkD0woPbJb0emZj5wophvRYgfB80YkTmt0KcYAr
20+
/icp6pjr6e3uDAedKrqOqWa8oQi3/sT45ibxTQKuQBEAkL8O79grzXBJJFDxgujy
21+
SFnwgLwTzXNGoZL1NM1Gq4XhOX8Aut72n7Xsi5tV2MzdmMgsgr8MiK0ICo0CgYBA
22+
h4yMauYjc/r5R2kLgQQNXTdk2JvnRK+q6Bww8/wkMq6AvfhDrtuztyTNdi4ekJdK
23+
ceEHoB3GniGBLJs13JgrabocpVpYUXYlEwLXijfOFmYGy1u2NViFq5dDIvSxCg1X
24+
yzwLI0GmcCSoq1bB5lO8Juw95skLdexmEdDoYfH+gQKBgDgaFlF9lYuKd0HhAxxm
25+
oJ8WBv26FWxR8/UsvUPqI8Mzmb8J1Q22tZEMEVr8bN/HkcL+8DVQ4uh2zx9+rqC0
26+
aPqJZNgePGpRDhZ/cwA8xsg+Xkou8rrFjpGRuVuG9FvMEexufkHoDc94ppONuwsR
27+
QzjTUxgXdLjtxT95ISwX83Nr
28+
-----END PRIVATE KEY-----
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIEgjCCAuqgAwIBAgIQBeTmjjlnyrCQUy06Xx/ybzANBgkqhkiG9w0BAQsFADCB
3+
pTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMT0wOwYDVQQLDDRMQVBU
4+
T1AtQUFSSFFPMkxcV2Rlc3RATEFQVE9QLUFBUkhRTzJMIChXYWduZXIgRC4gRi4p
5+
MUQwQgYDVQQDDDtta2NlcnQgTEFQVE9QLUFBUkhRTzJMXFdkZXN0QExBUFRPUC1B
6+
QVJIUU8yTCAoV2FnbmVyIEQuIEYuKTAeFw0yNTAyMTQxNDI2MjFaFw0yNzA1MTQx
7+
NDI2MjFaMGgxJzAlBgNVBAoTHm1rY2VydCBkZXZlbG9wbWVudCBjZXJ0aWZpY2F0
8+
ZTE9MDsGA1UECww0TEFQVE9QLUFBUkhRTzJMXFdkZXN0QExBUFRPUC1BQVJIUU8y
9+
TCAoV2FnbmVyIEQuIEYuKTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
10+
ANHwWUSFDxdly0uYZ3HvxnkQicraZx9T02rihQqQ96KqXtJ3dx3emt36bkxjKUvb
11+
nnABl/wV26mSSjZyNASxTbiNQFZUbmrdipn46J0Vh3Fic9sOEW3MvcuvbMcYCL4y
12+
CpUQ1QXoQPY7m1T1TVytx5eZcdvcTxKD7NdfpMxUJLXU4f1VDVm60zR8Uw9nGZ9T
13+
lo593mS5OZOJo604zLKauhQ+DNFbpRxst9J0Psq40VCAcMeJaSvYy2hl9+AYz9w5
14+
+YJ2ATzkx0vqTJvtNRNsCVNKkk1bDKA5tx7sM3urAzwehxFZIB8f7Ld7BuzY23Mb
15+
UDP5r+RQGOG+aNNYWLANP7UCAwEAAaNqMGgwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud
16+
JQQMMAoGCCsGAQUFBwMBMB8GA1UdIwQYMBaAFLmYo+uZjtNZbL0jyR7XVnK/FICG
17+
MCAGA1UdEQQZMBeCCWxvY2FsaG9zdIcEfwAAAYcEAAAAADANBgkqhkiG9w0BAQsF
18+
AAOCAYEAbUhcggR4p8n/Us+ov8zvI+hBIrDi7GevWQQKrU1Rp2gzHV6glhqGMQ1A
19+
BkGlj56L9O4P02awFZzB/55d2CsufzMD+d4aMKpdIJivwXByg6fJCtPOIprAiGeR
20+
GuE9Q9ceUUJVrTYiy5CZeKTIQp4ZqWu5/wBQ+yvbsvxnzq2ESDpGmCkY6/ToMWmu
21+
5cZPbZcyF7XBybSBdvaMGFsthRpawvQMsGHDZBaVhLn099Hjx2p35jNiM+HqCQXn
22+
NweLYMJx9FZtOYyNJoTj0w97ViaSubz7V9n7LVIxW1QXmDXAk+75Fwq3x+n/tfhe
23+
1BTLEOKvARvRVB6M1EE0z+zeNusHIu/TUMSmGv59OhsQdW5Lp1D0o3JK2mHu6tgP
24+
+v1XRMaTQq73oHqU0oXCtsEdPuoFBNREBDhBOGB5wtFjfniu1zD0cKSDbVslSD5B
25+
LKu9Yi1PzZTWVkaOWQolA6sYwWFLhQbHoyQoNjAJz58VNNtYIMjR6fZ0xTYiPQpJ
26+
4Jh3TJKU
27+
-----END CERTIFICATE-----

webdev/CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## 3.7.2-wip
22

3-
- Adds `--offline` flag [#2483](https://github.com/dart-lang/webdev/pull/2483)
3+
- Adds `--offline` flag [#2483](https://github.com/dart-lang/webdev/pull/2483).
4+
- Support the `--hostname` flag when the `--tls-cert-key` and `--tls-cert-chain` flags are present [#2588](https://github.com/dart-lang/webdev/pull/2588).
45

56
## 3.7.1
67

webdev/lib/src/serve/webdev_server.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,8 @@ class WebDevServer {
206206
final serverContext = SecurityContext()
207207
..useCertificateChain(tlsCertChain)
208208
..usePrivateKey(tlsCertKey);
209-
server =
210-
await HttpMultiServer.loopbackSecure(options.port, serverContext);
209+
server = await HttpMultiServer.bindSecure(
210+
hostname, options.port, serverContext);
211211
} else {
212212
server = await HttpMultiServer.bind(hostname, options.port);
213213
}

webdev/test/tls_test.dart

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:io';
6+
7+
import 'package:collection/collection.dart';
8+
import 'package:logging/logging.dart';
9+
import 'package:path/path.dart' as p;
10+
import 'package:test/test.dart';
11+
import 'package:test_descriptor/test_descriptor.dart' as d;
12+
import 'package:test_process/test_process.dart';
13+
14+
import 'package:webdev/src/logging.dart';
15+
import 'package:webdev/src/serve/utils.dart';
16+
17+
import 'test_utils.dart';
18+
19+
void main() {
20+
group('serve app with TLS options', () {
21+
// Change to true for debugging.
22+
final debug = false;
23+
24+
final testRunner = TestRunner();
25+
late String exampleDirectory;
26+
27+
setUpAll(() async {
28+
configureLogWriter(debug);
29+
await testRunner.setUpAll();
30+
exampleDirectory =
31+
p.absolute(p.join(p.current, '..', 'fixtures', '_experimentSound'));
32+
33+
final process = await TestProcess.start(
34+
'dart',
35+
['pub', 'upgrade'],
36+
workingDirectory: exampleDirectory,
37+
environment: getPubEnvironment(),
38+
);
39+
40+
await process.shouldExit(0);
41+
42+
await d
43+
.file('.dart_tool/package_config.json', isNotEmpty)
44+
.validate(exampleDirectory);
45+
await d.file('pubspec.lock', isNotEmpty).validate(exampleDirectory);
46+
});
47+
48+
test('listens on a loopback interface', () async {
49+
final port = await findUnusedPort();
50+
final args = [
51+
'serve',
52+
'web:$port',
53+
'--hostname=0.0.0.0',
54+
'--tls-cert-chain=localhost+2.pem',
55+
'--tls-cert-key=localhost+2-key.pem',
56+
];
57+
58+
final process =
59+
await testRunner.runWebDev(args, workingDirectory: exampleDirectory);
60+
await expectLater(process.stdout, emitsThrough(contains('Succeeded')));
61+
62+
final client = HttpClient()
63+
..badCertificateCallback = (_, __, ___) => true;
64+
try {
65+
final request =
66+
await client.getUrl(Uri.parse('https://localhost:$port'));
67+
final response = await request.close();
68+
expect(response.statusCode, equals(200));
69+
} finally {
70+
client.close(force: true);
71+
}
72+
73+
await process.kill();
74+
await process.shouldExit();
75+
});
76+
77+
test('listens on a non-loopback interface', () async {
78+
final port = await findUnusedPort();
79+
final args = [
80+
'serve',
81+
'web:$port',
82+
'--hostname=0.0.0.0',
83+
'--tls-cert-chain=localhost+2.pem',
84+
'--tls-cert-key=localhost+2-key.pem',
85+
];
86+
87+
final process =
88+
await testRunner.runWebDev(args, workingDirectory: exampleDirectory);
89+
await expectLater(process.stdout, emitsThrough(contains('Succeeded')));
90+
91+
final interfaces = await NetworkInterface.list(
92+
type: InternetAddressType.IPv4,
93+
includeLoopback: false,
94+
);
95+
final nonLoopback = interfaces.expand((i) => i.addresses).firstOrNull;
96+
97+
if (nonLoopback == null) {
98+
Logger.root.info(
99+
'No non-loopback IPv4 address available, skipping hostname test.');
100+
} else {
101+
final client = HttpClient()
102+
..badCertificateCallback = (_, __, ___) => true;
103+
try {
104+
final request = await client
105+
.getUrl(Uri.parse('https://${nonLoopback.address}:$port'));
106+
final response = await request.close();
107+
expect(response.statusCode, equals(200));
108+
} finally {
109+
client.close(force: true);
110+
}
111+
}
112+
113+
await process.kill();
114+
await process.shouldExit();
115+
});
116+
});
117+
}

0 commit comments

Comments
 (0)