mirror of
https://github.com/denoland/deno.git
synced 2024-12-25 00:29:09 -05:00
fix: support --cert flag for tls connect APIs (#11484)
This commit is contained in:
parent
7d151efc68
commit
78fc9a4c60
9 changed files with 175 additions and 4 deletions
|
@ -1785,3 +1785,13 @@ mod permissions {
|
||||||
exit_code: 1,
|
exit_code: 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
itest!(tls_starttls {
|
||||||
|
args: "run --quiet --reload --allow-net --allow-read --unstable --cert tls/RootCA.pem tls_starttls.js",
|
||||||
|
output: "tls.out",
|
||||||
|
});
|
||||||
|
|
||||||
|
itest!(tls_connecttls {
|
||||||
|
args: "run --quiet --reload --allow-net --allow-read --cert tls/RootCA.pem tls_connecttls.js",
|
||||||
|
output: "tls.out",
|
||||||
|
});
|
||||||
|
|
1
cli/tests/tls.out
Normal file
1
cli/tests/tls.out
Normal file
|
@ -0,0 +1 @@
|
||||||
|
DONE
|
67
cli/tests/tls_connecttls.js
Normal file
67
cli/tests/tls_connecttls.js
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import { deferred } from "../../test_util/std/async/deferred.ts";
|
||||||
|
import { assert, assertEquals } from "../../test_util/std/testing/asserts.ts";
|
||||||
|
import { BufReader, BufWriter } from "../../test_util/std/io/bufio.ts";
|
||||||
|
import { TextProtoReader } from "../../test_util/std/textproto/mod.ts";
|
||||||
|
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
|
||||||
|
const resolvable = deferred();
|
||||||
|
const hostname = "localhost";
|
||||||
|
const port = 3505;
|
||||||
|
|
||||||
|
const listener = Deno.listenTls({
|
||||||
|
hostname,
|
||||||
|
port,
|
||||||
|
certFile: "./tls/localhost.crt",
|
||||||
|
keyFile: "./tls/localhost.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);
|
||||||
|
// TODO(bartlomieju): this might be a bug
|
||||||
|
setTimeout(() => {
|
||||||
|
conn.close();
|
||||||
|
resolvable.resolve();
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const conn = await Deno.connectTls({
|
||||||
|
hostname,
|
||||||
|
port,
|
||||||
|
});
|
||||||
|
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;
|
||||||
|
|
||||||
|
console.log("DONE");
|
65
cli/tests/tls_starttls.js
Normal file
65
cli/tests/tls_starttls.js
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import { deferred } from "../../test_util/std/async/deferred.ts";
|
||||||
|
import { assert, assertEquals } from "../../test_util/std/testing/asserts.ts";
|
||||||
|
import { BufReader, BufWriter } from "../../test_util/std/io/bufio.ts";
|
||||||
|
import { TextProtoReader } from "../../test_util/std/textproto/mod.ts";
|
||||||
|
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
|
||||||
|
const resolvable = deferred();
|
||||||
|
const hostname = "localhost";
|
||||||
|
const port = 3504;
|
||||||
|
|
||||||
|
const listener = Deno.listenTls({
|
||||||
|
hostname,
|
||||||
|
port,
|
||||||
|
certFile: "./tls/localhost.crt",
|
||||||
|
keyFile: "./tls/localhost.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);
|
||||||
|
// TODO(bartlomieju): this might be a bug
|
||||||
|
setTimeout(() => {
|
||||||
|
conn.close();
|
||||||
|
resolvable.resolve();
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let conn = await Deno.connect({ hostname, port });
|
||||||
|
conn = await Deno.startTls(conn, { hostname });
|
||||||
|
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;
|
||||||
|
|
||||||
|
console.log("DONE");
|
|
@ -88,12 +88,22 @@ pub fn get_unstable_declaration() -> PathBuf {
|
||||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_net.unstable.d.ts")
|
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_net.unstable.d.ts")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init<P: NetPermissions + 'static>(unstable: bool) -> Extension {
|
#[derive(Clone)]
|
||||||
|
pub struct DefaultTlsOptions {
|
||||||
|
pub ca_data: Option<Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init<P: NetPermissions + 'static>(
|
||||||
|
ca_data: Option<Vec<u8>>,
|
||||||
|
unstable: bool,
|
||||||
|
) -> Extension {
|
||||||
let mut ops_to_register = vec![];
|
let mut ops_to_register = vec![];
|
||||||
ops_to_register.extend(io::init());
|
ops_to_register.extend(io::init());
|
||||||
ops_to_register.extend(ops::init::<P>());
|
ops_to_register.extend(ops::init::<P>());
|
||||||
ops_to_register.extend(ops_tls::init::<P>());
|
ops_to_register.extend(ops_tls::init::<P>());
|
||||||
|
|
||||||
|
let default_tls_options = DefaultTlsOptions { ca_data };
|
||||||
|
|
||||||
Extension::builder()
|
Extension::builder()
|
||||||
.js(include_js_files!(
|
.js(include_js_files!(
|
||||||
prefix "deno:extensions/net",
|
prefix "deno:extensions/net",
|
||||||
|
@ -103,6 +113,7 @@ pub fn init<P: NetPermissions + 'static>(unstable: bool) -> Extension {
|
||||||
))
|
))
|
||||||
.ops(ops_to_register)
|
.ops(ops_to_register)
|
||||||
.state(move |state| {
|
.state(move |state| {
|
||||||
|
state.put(default_tls_options.clone());
|
||||||
state.put(UnstableChecker { unstable });
|
state.put(UnstableChecker { unstable });
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|
|
@ -10,6 +10,7 @@ use crate::ops::OpAddr;
|
||||||
use crate::ops::OpConn;
|
use crate::ops::OpConn;
|
||||||
use crate::resolve_addr::resolve_addr;
|
use crate::resolve_addr::resolve_addr;
|
||||||
use crate::resolve_addr::resolve_addr_sync;
|
use crate::resolve_addr::resolve_addr_sync;
|
||||||
|
use crate::DefaultTlsOptions;
|
||||||
use crate::NetPermissions;
|
use crate::NetPermissions;
|
||||||
use deno_core::error::bad_resource;
|
use deno_core::error::bad_resource;
|
||||||
use deno_core::error::bad_resource_id;
|
use deno_core::error::bad_resource_id;
|
||||||
|
@ -60,6 +61,7 @@ use std::convert::From;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
|
use std::io::Cursor;
|
||||||
use std::io::ErrorKind;
|
use std::io::ErrorKind;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
|
@ -702,6 +704,7 @@ where
|
||||||
};
|
};
|
||||||
let cert_file = args.cert_file.as_deref();
|
let cert_file = args.cert_file.as_deref();
|
||||||
|
|
||||||
|
let default_tls_options;
|
||||||
{
|
{
|
||||||
super::check_unstable2(&state, "Deno.startTls");
|
super::check_unstable2(&state, "Deno.startTls");
|
||||||
let mut s = state.borrow_mut();
|
let mut s = state.borrow_mut();
|
||||||
|
@ -710,6 +713,7 @@ where
|
||||||
if let Some(path) = cert_file {
|
if let Some(path) = cert_file {
|
||||||
permissions.check_read(Path::new(path))?;
|
permissions.check_read(Path::new(path))?;
|
||||||
}
|
}
|
||||||
|
default_tls_options = s.borrow::<DefaultTlsOptions>().clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
let hostname_dns = DNSNameRef::try_from_ascii_str(hostname)
|
let hostname_dns = DNSNameRef::try_from_ascii_str(hostname)
|
||||||
|
@ -733,6 +737,10 @@ where
|
||||||
tls_config
|
tls_config
|
||||||
.root_store
|
.root_store
|
||||||
.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
|
.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
|
||||||
|
if let Some(ca_data) = default_tls_options.ca_data {
|
||||||
|
let reader = &mut Cursor::new(ca_data);
|
||||||
|
tls_config.root_store.add_pem_file(reader).unwrap();
|
||||||
|
};
|
||||||
if let Some(path) = cert_file {
|
if let Some(path) = cert_file {
|
||||||
let key_file = File::open(path)?;
|
let key_file = File::open(path)?;
|
||||||
let reader = &mut BufReader::new(key_file);
|
let reader = &mut BufReader::new(key_file);
|
||||||
|
@ -779,6 +787,7 @@ where
|
||||||
let port = args.port;
|
let port = args.port;
|
||||||
let cert_file = args.cert_file.as_deref();
|
let cert_file = args.cert_file.as_deref();
|
||||||
|
|
||||||
|
let default_tls_options;
|
||||||
{
|
{
|
||||||
let mut s = state.borrow_mut();
|
let mut s = state.borrow_mut();
|
||||||
let permissions = s.borrow_mut::<NP>();
|
let permissions = s.borrow_mut::<NP>();
|
||||||
|
@ -786,6 +795,7 @@ where
|
||||||
if let Some(path) = cert_file {
|
if let Some(path) = cert_file {
|
||||||
permissions.check_read(Path::new(path))?;
|
permissions.check_read(Path::new(path))?;
|
||||||
}
|
}
|
||||||
|
default_tls_options = s.borrow::<DefaultTlsOptions>().clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
let hostname_dns = DNSNameRef::try_from_ascii_str(hostname)
|
let hostname_dns = DNSNameRef::try_from_ascii_str(hostname)
|
||||||
|
@ -804,6 +814,10 @@ where
|
||||||
tls_config
|
tls_config
|
||||||
.root_store
|
.root_store
|
||||||
.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
|
.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
|
||||||
|
if let Some(ca_data) = default_tls_options.ca_data {
|
||||||
|
let reader = &mut Cursor::new(ca_data);
|
||||||
|
tls_config.root_store.add_pem_file(reader).unwrap();
|
||||||
|
};
|
||||||
if let Some(path) = cert_file {
|
if let Some(path) = cert_file {
|
||||||
let key_file = File::open(path)?;
|
let key_file = File::open(path)?;
|
||||||
let reader = &mut BufReader::new(key_file);
|
let reader = &mut BufReader::new(key_file);
|
||||||
|
|
|
@ -59,7 +59,7 @@ fn create_runtime_snapshot(snapshot_path: &Path, files: Vec<PathBuf>) {
|
||||||
deno_broadcast_channel::InMemoryBroadcastChannel::default(),
|
deno_broadcast_channel::InMemoryBroadcastChannel::default(),
|
||||||
false, // No --unstable.
|
false, // No --unstable.
|
||||||
),
|
),
|
||||||
deno_net::init::<deno_net::NoNetPermissions>(false), // No --unstable.
|
deno_net::init::<deno_net::NoNetPermissions>(None, false), // No --unstable.
|
||||||
deno_http::init(),
|
deno_http::init(),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -332,7 +332,10 @@ impl WebWorker {
|
||||||
vec![
|
vec![
|
||||||
ops::fs_events::init(),
|
ops::fs_events::init(),
|
||||||
ops::fs::init(),
|
ops::fs::init(),
|
||||||
deno_net::init::<Permissions>(options.unstable),
|
deno_net::init::<Permissions>(
|
||||||
|
options.ca_data.clone(),
|
||||||
|
options.unstable,
|
||||||
|
),
|
||||||
ops::os::init(),
|
ops::os::init(),
|
||||||
ops::permissions::init(),
|
ops::permissions::init(),
|
||||||
ops::plugin::init(),
|
ops::plugin::init(),
|
||||||
|
|
|
@ -123,7 +123,7 @@ impl MainWorker {
|
||||||
ops::fs::init(),
|
ops::fs::init(),
|
||||||
ops::io::init(),
|
ops::io::init(),
|
||||||
ops::io::init_stdio(),
|
ops::io::init_stdio(),
|
||||||
deno_net::init::<Permissions>(options.unstable),
|
deno_net::init::<Permissions>(options.ca_data.clone(), options.unstable),
|
||||||
ops::os::init(),
|
ops::os::init(),
|
||||||
ops::permissions::init(),
|
ops::permissions::init(),
|
||||||
ops::plugin::init(),
|
ops::plugin::init(),
|
||||||
|
|
Loading…
Reference in a new issue