From 8808594a86d33cd91577390ed4e1887ade52654c Mon Sep 17 00:00:00 2001 From: Red Davies Date: Tue, 7 Jan 2025 08:06:46 -0500 Subject: [PATCH] Added SSL support so that jennet can now listen on SSL directly. --- README.md | 58 ++++++++++++++++++++++++++++++++++++++++++ examples/ssl/cert.pem | 20 +++++++++++++++ examples/ssl/key.pem | 27 ++++++++++++++++++++ examples/ssl/main.pony | 51 +++++++++++++++++++++++++++++++++++++ jennet/jennet.pony | 15 ++++++++++- 5 files changed, 170 insertions(+), 1 deletion(-) create mode 100755 examples/ssl/cert.pem create mode 100755 examples/ssl/key.pem create mode 100644 examples/ssl/main.pony diff --git a/README.md b/README.md index 53fc86b..b8c61eb 100644 --- a/README.md +++ b/README.md @@ -164,3 +164,61 @@ actor Main if server is None then env.out.print("bad routes!") end ``` + +### Serving over SSL + +Refer to the [SSLContext](https://ponylang.github.io/net_ssl/net_ssl-SSLContext/) documentation in [net_ssl](https://ponylang.github.io/net_ssl/) for SSL / TLS configuration. + +```pony +use "net" +use "files" +use "net_ssl" +use "http_server" +use "jennet" + +actor Main + new create(env: Env) => + let tcplauth: TCPListenAuth = TCPListenAuth(env.root) + let fileauth: FileAuth = FileAuth(env.root) + + try + let sslctx: SSLContext = + try + recover + SSLContext + .>set_cert( + FilePath(fileauth, "cert.pem"), + FilePath(fileauth, "key.pem") + )? + end + else + env.err.print("Unable to configure SSL") + error + end + + let server = + Jennet(tcplauth, env.out) + .> get("/", H) + .> sslctx(sslctx) + .serve(ServerConfig(where port' = "8443")) + + if server is None then + env.err.print("bad routes!") + error + end + else + env.err.print("Server unable to start") + end + +primitive H is RequestHandler + fun apply(ctx: Context): Context iso^ => + let body = "Hello".array() + ctx.respond( + StatusResponse( + StatusOK, + [("Content-Length", body.size().string())] + ), + body + ) + consume ctx +``` diff --git a/examples/ssl/cert.pem b/examples/ssl/cert.pem new file mode 100755 index 0000000..61b39de --- /dev/null +++ b/examples/ssl/cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDLzCCAhegAwIBAgIJAI/o78XcM9NqMA0GCSqGSIb3DQEBBQUAMBkxFzAVBgNV +BAoTDkZha2UgQXV0aG9yaXR5MB4XDTE1MDQxMTE3NTAwOVoXDTI1MDQwODE3NTAw +OVowGTEXMBUGA1UEChMORmFrZSBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQDMy0Xuax1Ej1iCOemYdiLwaJ/B8UsCH2ztJVPHyw6HSxwa +WNariJ/d24QLuF9YFx7Xq2oEJK7WJ+zQfnn+jnlQWKfDBFSagreSmOIKRYYkKya3 +JwqluKsXcqxXz//0PkL6gfRNrCsbPUVujTP3Ple5eRK1ilOxc0KaJwtRdYVEPWcU +rv0CvuU7qDKMzBh2Lt3tPC8J6VNqvK5bsLBdhcxFU+OjL65VEGjIc33pHMGkQitl +MpsFolqXYvMt/JPNdf3trPEg8eGHeK9+7AxmaRo4WLaFsYyV5Etwa8ox+p3gGL/I +OpE2PDC1SqJ2HdnLgXK1I/LGRlrNQssfew/rfqdZAgMBAAGjejB4MB0GA1UdDgQW +BBTUVbUqQanwnb1TzSj3VVOXHkPWsDBJBgNVHSMEQjBAgBTUVbUqQanwnb1TzSj3 +VVOXHkPWsKEdpBswGTEXMBUGA1UEChMORmFrZSBBdXRob3JpdHmCCQCP6O/F3DPT +ajAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQBlCZDD231UlS2FGOLK +yesxakCcCxwuqw6rbldj0zCNNRqlZ8lPDrbbr2ECUBKj6kYVJfU93YaYVYSmSTaB +41/GCfBWdNfPdP4q2kqpmKR5bCeiGYpFNajPLXTpmRvmIQ6NT1u86krIPYxle/02 +DgYatXMGanzcUCwrfSnkqZbyXBKRAxbhyJnLmkJ6HqIm5vs6hDw2KdtgwRw6koFJ ++0unhRHDKjZgFuCveaFFzFFkf4tlGmei3ykR3KTLxY3f1+GPunqD7HgebrE/vLP5 +Bgc1g/KSVGuyqKt9eCnSUlJypbPbKpPjnUrtx00HChpfnKpqiAgHFuZhaxBGUBZD +BccR +-----END CERTIFICATE----- diff --git a/examples/ssl/key.pem b/examples/ssl/key.pem new file mode 100755 index 0000000..9bb3416 --- /dev/null +++ b/examples/ssl/key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAzMtF7msdRI9YgjnpmHYi8GifwfFLAh9s7SVTx8sOh0scGljW +q4if3duEC7hfWBce16tqBCSu1ifs0H55/o55UFinwwRUmoK3kpjiCkWGJCsmtycK +pbirF3KsV8//9D5C+oH0TawrGz1Fbo0z9z5XuXkStYpTsXNCmicLUXWFRD1nFK79 +Ar7lO6gyjMwYdi7d7TwvCelTaryuW7CwXYXMRVPjoy+uVRBoyHN96RzBpEIrZTKb +BaJal2LzLfyTzXX97azxIPHhh3ivfuwMZmkaOFi2hbGMleRLcGvKMfqd4Bi/yDqR +NjwwtUqidh3Zy4FytSPyxkZazULLH3sP636nWQIDAQABAoIBADvxuAt8gPmjd8XO +i8ibs8ho53JOXaVGa4zSoz5U+nCxlI1FubhF6n13FqSTmBzhz55TR1nlHuQClbfF +fZH8RBg3iwtzRgxf/LnFVEyrYwNNOizcGaq+bh4T68xcTtBANTy8MzVVEt0LRzp/ +zFkvf2ADx20qCyti0HjsusxiONrrFBS6O7DTss9GFRN5lfsBP49FSl3/r0e+fUaO +/6ImRVJvmAU92+dQ4VOi4c+laeFDmhw1MQSpvHOz3Vrslin9NSR6TMQ7rH9I/QKL ++YQULz7fYINRwt8FBa/p94qjGIqlNz6UdTPGvaEMlVyYo1ziTDZlCHnAYWk3wiFW +s13Jct0CgYEA7PxGYpZCyzntd1wvOradxh7EbUrF68lhfDt6BgOBV8Ur3/4SvcwB +blc4nEf5/p/x9w8ICe4uyA0bsGwqGppIhptrPe0uLApidUrxNeqY5zsebriR/gE6 +mq5/cmScFTnywfAb/93qK5SMzeWKfocPozb5YqO8oJpPHPhVZYAj16MCgYEA3TnH +14szOoQSq8ul2tFdu7s9S+sfi1l7ejqiHjZVcMST+GyEk3ox5Ns24pwkRq80cQhp +1UYfapeeiYroW7nkVTqJUfZbilFO6eKCBfpzC7NGHMs86er3pLUrfHQKq6S5RSZr +W9CB7+Kd9OXMOIJ2yXcF+OyEzQH5ESwxoHvrJNMCgYEAz/o6Hw01wzqsP2Mkg8d+ +QUABLNVBS0NpjWh5F0v+ODPu5F4KvoyJ+PcM1IKKUD64UBTd/jEM1z5BvZu/e6dI +3PEKtccwDTBz5fIGLEYdOFj2xT6vyRX1A4j+ijrni+1WMYNyXyO2/dYZmBzJZW9M +dvWo+TsvMFcb9RWvhCtnyTcCgYAyK9t9r60TlUZivdHEeX0HiWRSZmWGBeoyA0An +F+1yoLJqQbojdDAClhCxffXgLfX3uI+/9aJEW1RyHxWpT9RP2/Guq++AxAXglyUj +0/PpcGPzPch3yHkXWpsdI3gUC0yVOSxZ60S8salfFAqnujbUY/Dvzjwj/lGNKneq +zM+8TwKBgHjQforDq00DhDAqzqZDDVd8OZ9PLp3wXL+TaidTMmU7Fob5spyQe0gn +LqVIyGFYoRbW57pLgSEXRh+Zqw1AAKmMYoc3/pKM60rgOQxMXEemFAJ8zuDQ5L6y +1dR3ak4ueRcSFpa6x8/STsgIuL38HMNXhStsJB9whHAFB3U6Wo2/ +-----END RSA PRIVATE KEY----- diff --git a/examples/ssl/main.pony b/examples/ssl/main.pony new file mode 100644 index 0000000..e0ddd1a --- /dev/null +++ b/examples/ssl/main.pony @@ -0,0 +1,51 @@ +use "net" +use "files" +use "net_ssl" +use "http_server" +use "../../jennet" + +actor Main + new create(env: Env) => + let tcplauth: TCPListenAuth = TCPListenAuth(env.root) + let fileauth: FileAuth = FileAuth(env.root) + + try + let sslctx: SSLContext = + try + recover + SSLContext + .>set_cert( + FilePath(fileauth, "cert.pem"), + FilePath(fileauth, "key.pem") + )? + end + else + env.err.print("Unable to configure SSL") + error + end + + let server = + Jennet(tcplauth, env.out) + .> get("/", H) + .> sslctx(sslctx) + .serve(ServerConfig(where port' = "8443")) + + if server is None then + env.err.print("bad routes!") + error + end + else + env.err.print("Server unable to start") + end + +primitive H is RequestHandler + fun apply(ctx: Context): Context iso^ => + let body = "Hello".array() + ctx.respond( + StatusResponse( + StatusOK, + [("Content-Length", body.size().string())] + ), + body + ) + consume ctx diff --git a/jennet/jennet.pony b/jennet/jennet.pony index af53a69..abe55e0 100644 --- a/jennet/jennet.pony +++ b/jennet/jennet.pony @@ -2,6 +2,7 @@ use "collections" use "files" use "http_server" use "net" +use "net_ssl" use "valbytes" class iso Jennet @@ -12,6 +13,7 @@ class iso Jennet let _routes: Array[_Route] iso = recover Array[_Route] end var _notfound: _HandlerGroup = _HandlerGroup(_DefaultNotFound) var _host: String = "Jennet" // TODO get host from server + var _sslctx: (SSLContext | None) = None new iso create( auth: TCPListenAuth val, @@ -26,6 +28,12 @@ class iso Jennet _out = out _auth = auth + fun ref sslctx(sslctx': SSLContext) => + _sslctx = sslctx' + """ + Provide a valid SSLContext to enable SSL. + """ + fun ref get( path: String, handler: RequestHandler, @@ -127,7 +135,12 @@ class iso Jennet let mux = try _Mux(_routes)? else return None end if dump_routes then _out.print(mux.debug()) end let router_factory = _RouterFactory(consume mux, _responder, _notfound) - Server(_auth, _ServerInfo(_out, _responder), router_factory, config) + match _sslctx + | None => + Server(_auth, _ServerInfo(_out, _responder), router_factory, config) + | let sslcontext: SSLContext => + Server(_auth, _ServerInfo(_out, _responder), router_factory, config, sslcontext) + end fun ref _add_route(method: String, path: String, handler: RequestHandler, middlewares: Array[Middleware] val)