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

Add support for HTTPS #65

Merged
merged 5 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
34 changes: 26 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
[![CI](https://github.com/kevmoo/dhttpd/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/kevmoo/dhttpd/actions/workflows/ci.yml)
[![package publisher](https://img.shields.io/pub/publisher/dhttpd.svg)](https://pub.dev/packages/dhttpd/publisher)

A simple HTTP server that can serve up any directory, built with Dart.
A simple HTTP(S) server that can serve up any directory, built with Dart.
maks marked this conversation as resolved.
Show resolved Hide resolved
Inspired by `python -m SimpleHTTPServer`.

## Install
Expand Down Expand Up @@ -39,17 +39,35 @@ $ dart run build_runner build -o build
$ dhttpd --path build/web/ # Serves app at http://localhost:8080
```

### HTTPS

If you want to use HTTPS you will need to pass in the path of the ssl certificate and the ssl key file as well as the password string, if a password is set on the key:

```
$ dart bin/dhttpd.dart --sslcert=sample/server_chain.pem --sslkey=sample/server_key.pem --sslkeypassword=dartdart
Server HTTPS started on port 8080
```

## Configure

```console
$ dhttpd --help
-p, --port=<port> The port to listen on.
(defaults to "8080")
--path=<path> The path to serve. If not set, the current directory is used.
--headers=<headers> HTTP headers to apply to each response. header=value;header2=value
--host=<host> The hostname to listen on.
(defaults to "localhost")
-h, --help Displays the help.
-p, --port=<port> The port to listen on.
(defaults to "8080")
--path=<path> The path to serve. If not set, the current directory is used.
--headers=<headers> HTTP headers to apply to each response. header=value;header2=value
--host=<host> The hostname to listen on.
(defaults to "localhost")
--sslcert=<sslcert> The SSL certificate to use.
If set along with sslkey, https will be used.
See the dart documentation about SecurityContext.useCertificateChain for more.
--sslkey=<sslkey> The key of the SSL certificate to use.
If set along with sslcert, https will be used.
See the dart documentation about SecurityContext.usePrivateKey for more.
--sslkeypassword=<sslkeypassword> The password for the key of the SSL certificate to use.
Required if the ssl key being used has a password set.
See the dart documentation about SecurityContext.usePrivateKey for more.
-h, --help Displays the help.
```

[path]: https://dart.dev/tools/pub/cmd/pub-global#running-a-script-from-your-path
6 changes: 5 additions & 1 deletion bin/dhttpd.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@ Future<void> main(List<String> args) async {
headers:
options.headers != null ? _parseKeyValuePairs(options.headers!) : null,
address: options.host,
sslCert: options.sslcert,
sslKey: options.sslkey,
sslPassword: options.sslkeypassword,
);

print('Server started on port ${options.port}');
print(
'Server HTTP${Dhttpd.isSSL ? 'S' : ''} started on port ${options.port}');
}

Map<String, String> _parseKeyValuePairs(String str) => <String, String>{
Expand Down
19 changes: 18 additions & 1 deletion lib/dhttpd.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'src/options.dart';
class Dhttpd {
final HttpServer _server;
final String path;
static bool _ssl = false;

Dhttpd._(this._server, this.path);

Expand All @@ -19,6 +20,8 @@ class Dhttpd {

String get urlBase => 'http://$host:$port/';

static bool get isSSL => _ssl;
maks marked this conversation as resolved.
Show resolved Hide resolved

/// [address] can either be a [String] or an
/// [InternetAddress]. If [address] is a [String], [start] will
/// perform a [InternetAddress.lookup] and use the first value in the
Expand All @@ -34,15 +37,29 @@ class Dhttpd {
int port = defaultPort,
Object address = defaultHost,
Map<String, String>? headers,
String? sslCert,
String? sslKey,
String? sslPassword,
}) async {
path ??= Directory.current.path;

SecurityContext? securityContext;
if (sslCert != null && sslKey != null) {
securityContext = SecurityContext()
..useCertificateChain(sslCert)
..usePrivateKey(sslKey, password: sslPassword);
}
if (securityContext != null) {
_ssl = true;
}

final pipeline = const Pipeline()
.addMiddleware(logRequests())
.addMiddleware(_headersMiddleware(headers))
.addHandler(createStaticHandler(path, defaultDocument: 'index.html'));

final server = await io.serve(pipeline, address, port);
final server = await io.serve(pipeline, address, port,
securityContext: securityContext);
return Dhttpd._(server, path);
}

Expand Down
27 changes: 27 additions & 0 deletions lib/src/options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,30 @@ class Options {
help: 'The hostname to listen on.')
final String host;

@CliOption(
valueHelp: 'sslcert',
help: 'The SSL certificate to use.'
'\r\nIf set along with sslkey, https will be used.'
'\r\nSee the dart documentation about '
'SecurityContext.useCertificateChain for more.')
final String? sslcert;

@CliOption(
valueHelp: 'sslkey',
help: 'The key of the SSL certificate to use.'
'\r\nIf set along with sslcert, https will be used.'
'\r\nSee the dart documentation about '
'SecurityContext.usePrivateKey for more.')
final String? sslkey;

@CliOption(
valueHelp: 'sslkeypassword',
help: 'The password for the key of the SSL certificate to use.'
maks marked this conversation as resolved.
Show resolved Hide resolved
'\r\nRequired if the ssl key being used has a password set.'
'\r\nSee the dart documentation about '
'SecurityContext.usePrivateKey for more.')
final String? sslkeypassword;

@CliOption(abbr: 'h', negatable: false, help: 'Displays the help.')
final bool help;

Expand All @@ -42,6 +66,9 @@ class Options {
this.path,
this.headers,
required this.host,
this.sslcert,
this.sslkey,
this.sslkeypassword,
required this.help,
});
}
25 changes: 25 additions & 0 deletions lib/src/options.g.dart

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

59 changes: 59 additions & 0 deletions sample/server_chain.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
-----BEGIN CERTIFICATE-----
MIIDZDCCAkygAwIBAgIBATANBgkqhkiG9w0BAQsFADAgMR4wHAYDVQQDDBVpbnRl
cm1lZGlhdGVhdXRob3JpdHkwHhcNMTUxMDI3MTAyNjM1WhcNMjUxMDI0MTAyNjM1
WjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQCkg/Qr8RQeLTOSgCkyiEX2ztgkgscX8hKGHEHdvlkmVK3JVEIIwkvu
/Y9LtHZUia3nPAgqEEbexzTENZjSCcC0V6I2XW/e5tIE3rO0KLZyhtZhN/2SfJ6p
KbOh0HLr1VtkKJGp1tzUmHW/aZI32pK60ZJ/N917NLPCJpCaL8+wHo3+w3oNqln6
oJsfgxy9SUM8Bsc9WMYKMUdqLO1QKs1A5YwqZuO7Mwj+4LY2QDixC7Ua7V9YAPo2
1SBeLvMCHbYxSPCuxcZ/kDkgax/DF9u7aZnGhMImkwBka0OQFvpfjKtTIuoobTpe
PAG7MQYXk4RjnjdyEX/9XAQzvNo1CDObAgMBAAGjgbQwgbEwPAYDVR0RBDUwM4IJ
bG9jYWxob3N0ggkxMjcuMC4wLjGCAzo6MYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAA
ATAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSvhJo6taTggJQBukEvMo/PDk8tKTAf
BgNVHSMEGDAWgBS98L4T5RaIToE3DkBRsoeWPil0eDAOBgNVHQ8BAf8EBAMCA6gw
EwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggEBAHLOt0mL2S4A
B7vN7KsfQeGlVgZUVlEjem6kqBh4fIzl4CsQuOO8oJ0FlO1z5JAIo98hZinymJx1
phBVpyGIKakT/etMH0op5evLe9dD36VA3IM/FEv5ibk35iGnPokiJXIAcdHd1zam
YaTHRAnZET5S03+7BgRTKoRuszhbvuFz/vKXaIAnVNOF4Gf2NUJ/Ax7ssJtRkN+5
UVxe8TZVxzgiRv1uF6NTr+J8PDepkHCbJ6zEQNudcFKAuC56DN1vUe06gRDrNbVq
2JHEh4pRfMpdsPCrS5YHBjVq/XHtFHgwDR6g0WTwSUJvDeM4OPQY5f61FB0JbFza
PkLkXmoIod8=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDLjCCAhagAwIBAgIBAjANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1yb290
YXV0aG9yaXR5MB4XDTE1MTAyNzEwMjYzNVoXDTI1MTAyNDEwMjYzNVowIDEeMBwG
A1UEAwwVaW50ZXJtZWRpYXRlYXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA6GndRFiXk+2q+Ig7ZOWKKGta+is8137qyXz+eVFs5sA0ajMN
ZBAMWS0TIXw/Yks+y6fEcV/tfv91k1eUN4YXPcoxTdDF97d2hO9wxumeYOMnQeDy
VZVDKQBZ+jFMeI+VkNpMEdmsLErpZDGob/1dC8tLEuR6RuRR8X6IDGMPOCMw1jLK
V1bQjPtzqKadTscfjLuKxuLgspJdTrzsu6hdcl1mm8K6CjTY2HNXWxs1yYmwfuQ2
Z4/8sOMNqFqLjN+ChD7pksTMq7IosqGiJzi2bpd5f44ek/k822Y0ATncJHk4h1Z+
kZBnW6kgcLna1gDri9heRwSZ+M8T8nlHgIMZIQIDAQABo3sweTASBgNVHRMBAf8E
CDAGAQH/AgEAMB0GA1UdDgQWBBS98L4T5RaIToE3DkBRsoeWPil0eDAfBgNVHSME
GDAWgBRxD5DQHTmtpDFKDOiMf5FAi6vfbzAOBgNVHQ8BAf8EBAMCAgQwEwYDVR0l
BAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggEBAD+4KpUeV5mUPw5IG/7w
eOXnUpeS96XFGuS1JuFo/TbgntPWSPyo+rD4GrPIkUXyoHaMCDd2UBEjyGbBIKlB
NZA3RJOAEp7DTkLNK4RFn/OEcLwG0J5brL7kaLRO4vwvItVIdZ2XIqzypRQTc0MG
MmF08zycnSlaN01ryM67AsMhwdHqVa+uXQPo8R8sdFGnZ33yywTYD73FeImXilQ2
rDnFUVqmrW1fjl0Fi4rV5XI0EQiPrzKvRtmF8ZqjGATPOsRd64cwQX6V+P5hNeIR
9pba6td7AbNGausHfacRYMyoGJWWWkFPd+7jWOCPqW7Fk1tmBgdB8GzXa3inWIRM
RUE=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIC+zCCAeOgAwIBAgIBATANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1yb290
YXV0aG9yaXR5MB4XDTE1MTAyNzEwMjYzNFoXDTI1MTAyNDEwMjYzNFowGDEWMBQG
A1UEAwwNcm9vdGF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAMl+dcraUM/E7E6zl7+7hK9oUJYXJLnfiMtP/TRFVbH4+2aEN8vXzPbzKdR3
FfaHczXQTwnTCaYA4u4uSDvSOsFFEfxEwYORsdKmQEM8nGpVX2NVvKsMcGIhh8kh
ZwJfkMIOcAxmGIHGdMhF8VghonJ8uGiuqktxdfpARq0g3fqIjDHsF9/LpfshUfk9
wsRyTF0yr90U/dsfnE+u8l7GvVl8j2Zegp0sagAGtLaNv7tP17AibqEGg2yDBrBN
9r9ihe4CqMjx+Q2kQ2S9Gz2V2ReO/n6vm2VQxsPRB/lV/9jh7cUcS0/9mggLYrDy
cq1v7rLLQrWuxMz1E3gOhyCYJ38CAwEAAaNQME4wHQYDVR0OBBYEFHEPkNAdOa2k
MUoM6Ix/kUCLq99vMB8GA1UdIwQYMBaAFHEPkNAdOa2kMUoM6Ix/kUCLq99vMAwG
A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBABrhjnWC6b+z9Kw73C/niOwo
9sPdufjS6tb0sCwDjt3mjvE4NdNWt+/+ZOugW6dqtvqhtqZM1q0u9pJkNwIrqgFD
ZHcfNaf31G6Z2YE+Io7woTVw6fFobg/EFo+a/qwbvWL26McmiRL5yiSBjVjpX4a5
kdZ+aPQUCBaLrTWwlCDqzSVIULWUQvveRWbToMFKPNID58NtEpymAx3Pgir7YjV9
UnlU2l5vZrh1PTCqZxvC/IdRESUfW80LdHaeyizRUP+6vKxGgSz2MRuYINjbd6GO
hGiCpWlwziW2xLV1l2qSRLko2kIafLZP18N0ThM9zKbU5ps9NgFOf//wqSGtLaE=
-----END CERTIFICATE-----
29 changes: 29 additions & 0 deletions sample/server_key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIE4zAcBgoqhkiG9w0BDAEBMA4ECBMCjlg8JYZ4AgIIAASCBMFd9cBoZ5xcTock
AVQcg/HzYJtMceKn1gtMDdC7mmXuyN0shoxhG4BpQInHkFARL+nenesXFxEm4X5e
L603Pcgw72/ratxVpTW7hPMjiLTEBqza0GjQm7Sarbdy+Vzdp/6XFrAcPfFl1juY
oyYzbozPsvFHz3Re44y1KmI4HAzU/qkjJUbNTTiPPVI2cDP6iYN2XXxBb1wwp8jR
iqdZqFG7lU/wvPEbD7BVPpmJBHWNG681zb4ea5Zn4hW8UaxpiIBiaH0/IWc2SVZd
RliAFo3NEsGxCcsnBo/n00oudGbOJxdOp7FbH5hJpeqX2WhCyJRxIeHOWmeuMAet
03HFriiEmJ99m2nEJN1x0A3QUUM7ji6vZAb4qb1dyq7LlX4M2aaqixRnaTcQkapf
DOxX35DEBXSKrDpyWp6Rx4wNpUyi1TKyhaVnYgD3Gn0VfC/2w86gSFlrf9PMYGM0
PvFxTDzTyjOuPBRa728gZOGXgDOL7qvdInU/opVew7kFeRQHXxHzFCLK5dD+Vrig
5fS3m0++f55ODkxqHXB8gbXbd3GMmsW6MrGpU7VsCNtbVPdSMW0FalovEB0M+2lj
1VfuvL+0F5huTe+BgZAt6xgET/CIcZXdNMRPVhraqUjqWtI9Rdk4STPCpU1rDkjG
YDl/fo4W2T6qQWFUpiC9IvVVGkVxaqfZZ4Qu+V5xPUi6vk95QiTNkN1t+m+sCCgS
Llkea8Um0aHMy33Lj3NsfL0LMrnpniqcAks8BvcgIZwk1VRqcj7BQVCygJSYrmAR
DBhMpjWlXuSggnyVPuduZDtnTN+8lCHLOKL3a3bDb6ySaKX49Km6GutDLfpDtEA0
3mQvmEG4XVm7zy+AlN72qFbtSLDRi/D/uQh2q/ZrFQLOBQBQB56TvEbKouLimUDM
ascQA3aUyhOE7e+d02NOFIFTozwc/C//CIFeA+ZEwxyfha/3Bor6Jez7PC/eHNxZ
w7YMXzPW9NhcCcerhYGebuCJxLwzqJ+IGdukjKsGV2ytWDoB2xZiJNu096j4RKcq
YSJoen0R7IH8N4eDujXR8m9kAl724Uqs1OoAs4VNICvzTutbsgVZ6Z+NMOcfnPw9
jZkFhot16w8znD+OmhBR7/bzOLpaeUhk7EhNq5M6U0NNWx3WwkDlvU/jx+6/EQe3
iLEHptH2HYBF1xscaKGbtKNtuQsfdzgWpOX0qK2YbK3yCKvL/xIm1DQmDZDKkWdW
VNh8oGV1H96CivWlvxhAgXKz9F/83CjMw8YXRk7RJvWR4vtNvXFAvGkFIYCN9Jv9
p+1ukaYoxSLGBik907I6gWSHqumJiCprUyAX/bVfZfNiYh4hzeA3lhwxZSax3JG4
7QFPvyepOmF/3AAzS/Pusx6jOZnuCMCkfQi6Wpem1o3s4x+fP7kz00Xuj01ErucM
S10ixfIh84kXBN3dTRDtDdeCyoMsBKO0W5jDBBlWL02YfdF6Opo1Q4cPh2DYgXMh
XEszNZSK5LB0y+f3A6Kdx/hkZzHVvMONA70OyrkoZzGyWENhcB0c7ntTJyPPD2qM
s0HRA2VwF/0ypU3OKERM1Ua5NSkTgvnnVTlV9GO90Tkn5v4fxdl8NzIuJLyGguTP
Xc0tRM34Lg==
-----END ENCRYPTED PRIVATE KEY-----
Loading