mirror of
https://github.com/denoland/deno.git
synced 2024-12-26 00:59:24 -05:00
feat(ext/net): support cert, key options in listenTls (#13740)
This commit is contained in:
parent
f8b73ab97e
commit
3e8180c793
4 changed files with 113 additions and 19 deletions
|
@ -15,6 +15,9 @@ import { TextProtoReader } from "../../../test_util/std/textproto/mod.ts";
|
|||
|
||||
const encoder = new TextEncoder();
|
||||
const decoder = new TextDecoder();
|
||||
const cert = await Deno.readTextFile("cli/tests/testdata/tls/localhost.crt");
|
||||
const key = await Deno.readTextFile("cli/tests/testdata/tls/localhost.key");
|
||||
const caCerts = [await Deno.readTextFile("cli/tests/testdata/tls/RootCA.pem")];
|
||||
|
||||
async function sleep(msec: number) {
|
||||
await new Promise((res, _rej) => setTimeout(res, msec));
|
||||
|
@ -184,11 +187,60 @@ Deno.test(
|
|||
},
|
||||
);
|
||||
|
||||
const conn = await Deno.connectTls({
|
||||
hostname,
|
||||
port,
|
||||
caCerts: [Deno.readTextFileSync("cli/tests/testdata/tls/RootCA.pem")],
|
||||
});
|
||||
const conn = await Deno.connectTls({ hostname, port, caCerts });
|
||||
assert(conn.rid > 0);
|
||||
const w = new BufWriter(conn);
|
||||
const r = new BufReader(conn);
|
||||
const body = `GET / HTTP/1.1\r\nHost: ${hostname}:${port}\r\n\r\n`;
|
||||
const writeResult = await w.write(encoder.encode(body));
|
||||
assertEquals(body.length, writeResult);
|
||||
await w.flush();
|
||||
const tpr = new TextProtoReader(r);
|
||||
const statusLine = await tpr.readLine();
|
||||
assert(statusLine !== null, `line must be read: ${String(statusLine)}`);
|
||||
const m = statusLine.match(/^(.+?) (.+?) (.+?)$/);
|
||||
assert(m !== null, "must be matched");
|
||||
const [_, proto, status, ok] = m;
|
||||
assertEquals(proto, "HTTP/1.1");
|
||||
assertEquals(status, "200");
|
||||
assertEquals(ok, "OK");
|
||||
const headers = await tpr.readMIMEHeader();
|
||||
assert(headers !== null);
|
||||
const contentLength = parseInt(headers.get("content-length")!);
|
||||
const bodyBuf = new Uint8Array(contentLength);
|
||||
await r.readFull(bodyBuf);
|
||||
assertEquals(decoder.decode(bodyBuf), "Hello World\n");
|
||||
conn.close();
|
||||
listener.close();
|
||||
await resolvable;
|
||||
},
|
||||
);
|
||||
Deno.test(
|
||||
{ permissions: { read: false, net: true } },
|
||||
async function listenTlsWithCertAndKey() {
|
||||
const resolvable = deferred();
|
||||
const hostname = "localhost";
|
||||
const port = 3500;
|
||||
|
||||
const listener = Deno.listenTls({ hostname, port, cert, key });
|
||||
|
||||
const response = encoder.encode(
|
||||
"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World\n",
|
||||
);
|
||||
|
||||
listener.accept().then(
|
||||
async (conn) => {
|
||||
assert(conn.remoteAddr != null);
|
||||
assert(conn.localAddr != null);
|
||||
await conn.write(response);
|
||||
setTimeout(() => {
|
||||
conn.close();
|
||||
resolvable.resolve();
|
||||
}, 0);
|
||||
},
|
||||
);
|
||||
|
||||
const conn = await Deno.connectTls({ hostname, port, caCerts });
|
||||
assert(conn.rid > 0);
|
||||
const w = new BufWriter(conn);
|
||||
const r = new BufReader(conn);
|
||||
|
|
|
@ -65,7 +65,9 @@
|
|||
|
||||
function listenTls({
|
||||
port,
|
||||
cert,
|
||||
certFile,
|
||||
key,
|
||||
keyFile,
|
||||
hostname = "0.0.0.0",
|
||||
transport = "tcp",
|
||||
|
@ -73,7 +75,9 @@
|
|||
}) {
|
||||
const res = opListenTls({
|
||||
port,
|
||||
cert,
|
||||
certFile,
|
||||
key,
|
||||
keyFile,
|
||||
hostname,
|
||||
transport,
|
||||
|
|
18
ext/net/lib.deno_net.d.ts
vendored
18
ext/net/lib.deno_net.d.ts
vendored
|
@ -93,11 +93,21 @@ declare namespace Deno {
|
|||
): Listener;
|
||||
|
||||
export interface ListenTlsOptions extends ListenOptions {
|
||||
/** Server private key in PEM format */
|
||||
key?: string;
|
||||
/** Cert chain in PEM format */
|
||||
cert?: string;
|
||||
/** Path to a file containing a PEM formatted CA certificate. Requires
|
||||
* `--allow-read`. */
|
||||
certFile: string;
|
||||
/** Server public key file. Requires `--allow-read`.*/
|
||||
keyFile: string;
|
||||
* `--allow-read`.
|
||||
*
|
||||
* @deprecated This option is deprecated and will be removed in Deno 2.0.
|
||||
*/
|
||||
certFile?: string;
|
||||
/** Server private key file. Requires `--allow-read`.
|
||||
*
|
||||
* @deprecated This option is deprecated and will be removed in Deno 2.0.
|
||||
*/
|
||||
keyFile?: string;
|
||||
|
||||
transport?: "tcp";
|
||||
}
|
||||
|
|
|
@ -1004,8 +1004,12 @@ pub struct ListenTlsArgs {
|
|||
transport: String,
|
||||
hostname: String,
|
||||
port: u16,
|
||||
cert_file: String,
|
||||
key_file: String,
|
||||
cert: Option<String>,
|
||||
// TODO(kt3k): Remove this option at v2.0.
|
||||
cert_file: Option<String>,
|
||||
key: Option<String>,
|
||||
// TODO(kt3k): Remove this option at v2.0.
|
||||
key_file: Option<String>,
|
||||
alpn_protocols: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
|
@ -1020,23 +1024,47 @@ where
|
|||
assert_eq!(args.transport, "tcp");
|
||||
let hostname = &*args.hostname;
|
||||
let port = args.port;
|
||||
let cert_file = &*args.cert_file;
|
||||
let key_file = &*args.key_file;
|
||||
let cert_file = args.cert_file.as_deref();
|
||||
let key_file = args.key_file.as_deref();
|
||||
let cert = args.cert.as_deref();
|
||||
let key = args.key.as_deref();
|
||||
|
||||
{
|
||||
let permissions = state.borrow_mut::<NP>();
|
||||
permissions.check_net(&(hostname, Some(port)))?;
|
||||
permissions.check_read(Path::new(cert_file))?;
|
||||
permissions.check_read(Path::new(key_file))?;
|
||||
if let Some(path) = cert_file {
|
||||
permissions.check_read(Path::new(path))?;
|
||||
}
|
||||
if let Some(path) = key_file {
|
||||
permissions.check_read(Path::new(path))?;
|
||||
}
|
||||
}
|
||||
|
||||
let cert_chain = if cert_file.is_some() && cert.is_some() {
|
||||
return Err(generic_error("Both cert and certFile is specified. You can specify either one of them."));
|
||||
} else if let Some(path) = cert_file {
|
||||
load_certs_from_file(path)?
|
||||
} else if let Some(cert) = cert {
|
||||
load_certs(&mut BufReader::new(cert.as_bytes()))?
|
||||
} else {
|
||||
return Err(generic_error("`cert` is not specified."));
|
||||
};
|
||||
let key_der = if key_file.is_some() && key.is_some() {
|
||||
return Err(generic_error(
|
||||
"Both key and keyFile is specified. You can specify either one of them.",
|
||||
));
|
||||
} else if let Some(path) = key_file {
|
||||
load_private_keys_from_file(path)?.remove(0)
|
||||
} else if let Some(key) = key {
|
||||
load_private_keys(key.as_bytes())?.remove(0)
|
||||
} else {
|
||||
return Err(generic_error("`key` is not specified."));
|
||||
};
|
||||
|
||||
let mut tls_config = ServerConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(
|
||||
load_certs_from_file(cert_file)?,
|
||||
load_private_keys_from_file(key_file)?.remove(0),
|
||||
)
|
||||
.with_single_cert(cert_chain, key_der)
|
||||
.expect("invalid key or certificate");
|
||||
if let Some(alpn_protocols) = args.alpn_protocols {
|
||||
super::check_unstable(state, "Deno.listenTls#alpn_protocols");
|
||||
|
|
Loading…
Reference in a new issue