Repository: deno
Issue: Support TLS SNI on listenTls and serve
Labels: feat,tls,ext/net

We'd like to support SNI on TLS connections for both raw TLS sockets and Deno.serve. The underlying TLS library (https://github.com/denoland/rustls-tokio-stream) supports SNI, but we need to expose this properly.

Here's the plan to make this work:

 - [ ] Plan the interface changes required. Likely interface changes are listed below. This method will be marked as `unstable` for the initial version
 - [ ] Implement a `TlsKeyResolver` in Rust with an async function for requesting key lookups and an async function for polling pending lookups: `let (cert, key) = await resolver.resolve(sni)` and `resolver.poll(|sni| async { return (cert, key) })` 
 - [ ] Wrap `Rc<TlsKeyResolver>` in a CPPGC struct and hook up the `poll` function in an async op. Call this new op from an async IIFE and delegate the polled request to the JS function: `async() => { while (sni = op_tls_poll_sni(resolver)) { let [cert, key] = await resFunc(); op_tls_poll_sni_result(resolver, sni, cert, key); } }`  
 - [ ] Provide Rc<TlsKeyResolver> to our TlsListener and call the `resolve` method in the SNI callback.
 - [ ] Plumb this infrastructure into `Deno.serve` (should be easy with the previous work)
 - [ ] (Optional) Implement a `Deno.TlsKey` for optimized TLS key management.

## Interface changes

- Add `TlsCertifiedKeyOptions` to `lib.deno_net.d.ts` with the cert and key properties from `ListenTlsOptions`. Remove these properties from `ListenTlsOptions` and inherit from `TlsServerKeyOptions`.

- Change `TlsCertifiedKeyOptions` so that key is `string | Deno.CertifiedKey | Deno.CertifiedKeyResolver`.

- Add `Deno.CertifiedKey` object. The object will not have any properties at this time -- it will just be a simple wrapper object:

```
const key = new Deno.CertifiedKey(Deno.readTextFileSync("certificate.cer"), Deno.readTextFileSync("private.key"));
```

- Add `Deno.CertifiedKeyResolver` object.  Add `resolveCertificate` as suggested by @lucacasonato to this object. Note that in the future we may wish to add a system mode to this certificate resolver. 

```
const resolver = new Deno.CertifiedKeyResolver({ resolveCertificate: (name) {
  const cert = await (await fetch(`http://certs.local/certs/${name}`)).text();
  const key = await (await fetch(`http://certs.local/keys/${name}`)).text();
  return new Deno.CertifiedKey(cert, key); 
});
```

```
+    /** A function to return a certificate for a connection, based on the name
+     * specified in the TLS ClientHello packet (SNI). If the function throws or
+     * returns a rejected promise, the connection with the client will be aborted.
+     *
+     * The results of this function are cached by name, if the returned
+     * certificate and key are valid. Entries in the cache may be discarded at any
+     * time. This function will never be invoked in parallel for the same name.
+     *
+     * Multiple names may return the same certificate and key. */
+    resolveCertificate?(name: string): Promise<Deno.CertifiedKey>;
+  }
```

References:

https://github.com/denoland/deno/issues/9109
https://github.com/denoland/deno/pull/9125
https://github.com/denoland/deno/pull/20237