Skip to content

Commit 640d431

Browse files
committed
fix(tls): flush send buffer in the background after closing TLS stream (denoland#10146)
In denoland#9118, TLS streams were split into a "read half" and a "write half" using tokio::io::split() to allow concurrent Conn#read() and Conn#write() calls without one blocking the other. However, this introduced a bug: outgoing data gets discarded when the TLS stream is gracefully closed, because the read half is closed too early, before all TLS control data has been received. Fixes: denoland#9692 Fixes: denoland#10049 Fixes: denoland#10296 Fixes: denoland/std#750
1 parent 36c5461 commit 640d431

File tree

7 files changed

+1440
-261
lines changed

7 files changed

+1440
-261
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/tests/integration_tests.rs

+95-70
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ use deno_core::serde_json;
55
use deno_core::url;
66
use deno_runtime::deno_fetch::reqwest;
77
use deno_runtime::deno_websocket::tokio_tungstenite;
8-
use rustls::Session;
8+
use deno_runtime::ops::tls::rustls;
9+
use deno_runtime::ops::tls::webpki;
10+
use deno_runtime::ops::tls::TlsStream;
911
use std::fs;
1012
use std::io::BufReader;
1113
use std::io::Cursor;
@@ -14,8 +16,7 @@ use std::process::Command;
1416
use std::sync::Arc;
1517
use tempfile::TempDir;
1618
use test_util as util;
17-
use tokio_rustls::rustls;
18-
use tokio_rustls::webpki;
19+
use tokio::task::LocalSet;
1920

2021
#[test]
2122
fn js_unit_tests_lint() {
@@ -6134,79 +6135,103 @@ console.log("finish");
61346135

61356136
#[tokio::test]
61366137
async fn listen_tls_alpn() {
6137-
let child = util::deno_cmd()
6138-
.current_dir(util::root_path())
6139-
.arg("run")
6140-
.arg("--unstable")
6141-
.arg("--quiet")
6142-
.arg("--allow-net")
6143-
.arg("--allow-read")
6144-
.arg("./cli/tests/listen_tls_alpn.ts")
6145-
.arg("4504")
6146-
.stdout(std::process::Stdio::piped())
6147-
.spawn()
6148-
.unwrap();
6149-
let mut stdout = child.stdout.unwrap();
6150-
let mut buffer = [0; 5];
6151-
let read = stdout.read(&mut buffer).unwrap();
6152-
assert_eq!(read, 5);
6153-
let msg = std::str::from_utf8(&buffer).unwrap();
6154-
assert_eq!(msg, "READY");
6155-
6156-
let mut cfg = rustls::ClientConfig::new();
6157-
let reader =
6158-
&mut BufReader::new(Cursor::new(include_bytes!("./tls/RootCA.crt")));
6159-
cfg.root_store.add_pem_file(reader).unwrap();
6160-
cfg.alpn_protocols.push("foobar".as_bytes().to_vec());
6161-
6162-
let tls_connector = tokio_rustls::TlsConnector::from(Arc::new(cfg));
6163-
let hostname = webpki::DNSNameRef::try_from_ascii_str("localhost").unwrap();
6164-
let stream = tokio::net::TcpStream::connect("localhost:4504")
6165-
.await
6166-
.unwrap();
6138+
// TLS streams require the presence of an ambient local task set to gracefully
6139+
// close dropped connections in the background.
6140+
LocalSet::new()
6141+
.run_until(async {
6142+
let mut child = util::deno_cmd()
6143+
.current_dir(util::root_path())
6144+
.arg("run")
6145+
.arg("--unstable")
6146+
.arg("--quiet")
6147+
.arg("--allow-net")
6148+
.arg("--allow-read")
6149+
.arg("./cli/tests/listen_tls_alpn.ts")
6150+
.arg("4504")
6151+
.stdout(std::process::Stdio::piped())
6152+
.spawn()
6153+
.unwrap();
6154+
let stdout = child.stdout.as_mut().unwrap();
6155+
let mut buffer = [0; 5];
6156+
let read = stdout.read(&mut buffer).unwrap();
6157+
assert_eq!(read, 5);
6158+
let msg = std::str::from_utf8(&buffer).unwrap();
6159+
assert_eq!(msg, "READY");
6160+
6161+
let mut cfg = rustls::ClientConfig::new();
6162+
let reader =
6163+
&mut BufReader::new(Cursor::new(include_bytes!("./tls/RootCA.crt")));
6164+
cfg.root_store.add_pem_file(reader).unwrap();
6165+
cfg.alpn_protocols.push("foobar".as_bytes().to_vec());
6166+
let cfg = Arc::new(cfg);
6167+
6168+
let hostname =
6169+
webpki::DNSNameRef::try_from_ascii_str("localhost").unwrap();
6170+
6171+
let tcp_stream = tokio::net::TcpStream::connect("localhost:4504")
6172+
.await
6173+
.unwrap();
6174+
let mut tls_stream =
6175+
TlsStream::new_client_side(tcp_stream, &cfg, hostname);
6176+
tls_stream.handshake().await.unwrap();
6177+
let (_, session) = tls_stream.get_ref();
61676178

6168-
let tls_stream = tls_connector.connect(hostname, stream).await.unwrap();
6169-
let (_, session) = tls_stream.get_ref();
6179+
let alpn = session.get_alpn_protocol().unwrap();
6180+
assert_eq!(std::str::from_utf8(alpn).unwrap(), "foobar");
61706181

6171-
let alpn = session.get_alpn_protocol().unwrap();
6172-
assert_eq!(std::str::from_utf8(alpn).unwrap(), "foobar");
6182+
child.kill().unwrap();
6183+
child.wait().unwrap();
6184+
})
6185+
.await;
61736186
}
61746187

61756188
#[tokio::test]
61766189
async fn listen_tls_alpn_fail() {
6177-
let child = util::deno_cmd()
6178-
.current_dir(util::root_path())
6179-
.arg("run")
6180-
.arg("--unstable")
6181-
.arg("--quiet")
6182-
.arg("--allow-net")
6183-
.arg("--allow-read")
6184-
.arg("./cli/tests/listen_tls_alpn.ts")
6185-
.arg("4505")
6186-
.stdout(std::process::Stdio::piped())
6187-
.spawn()
6188-
.unwrap();
6189-
let mut stdout = child.stdout.unwrap();
6190-
let mut buffer = [0; 5];
6191-
let read = stdout.read(&mut buffer).unwrap();
6192-
assert_eq!(read, 5);
6193-
let msg = std::str::from_utf8(&buffer).unwrap();
6194-
assert_eq!(msg, "READY");
6195-
6196-
let mut cfg = rustls::ClientConfig::new();
6197-
let reader =
6198-
&mut BufReader::new(Cursor::new(include_bytes!("./tls/RootCA.crt")));
6199-
cfg.root_store.add_pem_file(reader).unwrap();
6200-
cfg.alpn_protocols.push("boofar".as_bytes().to_vec());
6201-
6202-
let tls_connector = tokio_rustls::TlsConnector::from(Arc::new(cfg));
6203-
let hostname = webpki::DNSNameRef::try_from_ascii_str("localhost").unwrap();
6204-
let stream = tokio::net::TcpStream::connect("localhost:4505")
6205-
.await
6206-
.unwrap();
6190+
// TLS streams require the presence of an ambient local task set to gracefully
6191+
// close dropped connections in the background.
6192+
LocalSet::new()
6193+
.run_until(async {
6194+
let mut child = util::deno_cmd()
6195+
.current_dir(util::root_path())
6196+
.arg("run")
6197+
.arg("--unstable")
6198+
.arg("--quiet")
6199+
.arg("--allow-net")
6200+
.arg("--allow-read")
6201+
.arg("./cli/tests/listen_tls_alpn.ts")
6202+
.arg("4505")
6203+
.stdout(std::process::Stdio::piped())
6204+
.spawn()
6205+
.unwrap();
6206+
let stdout = child.stdout.as_mut().unwrap();
6207+
let mut buffer = [0; 5];
6208+
let read = stdout.read(&mut buffer).unwrap();
6209+
assert_eq!(read, 5);
6210+
let msg = std::str::from_utf8(&buffer).unwrap();
6211+
assert_eq!(msg, "READY");
6212+
6213+
let mut cfg = rustls::ClientConfig::new();
6214+
let reader =
6215+
&mut BufReader::new(Cursor::new(include_bytes!("./tls/RootCA.crt")));
6216+
cfg.root_store.add_pem_file(reader).unwrap();
6217+
cfg.alpn_protocols.push("boofar".as_bytes().to_vec());
6218+
let cfg = Arc::new(cfg);
6219+
6220+
let hostname =
6221+
webpki::DNSNameRef::try_from_ascii_str("localhost").unwrap();
6222+
6223+
let tcp_stream = tokio::net::TcpStream::connect("localhost:4505")
6224+
.await
6225+
.unwrap();
6226+
let mut tls_stream =
6227+
TlsStream::new_client_side(tcp_stream, &cfg, hostname);
6228+
tls_stream.handshake().await.unwrap();
6229+
let (_, session) = tls_stream.get_ref();
62076230

6208-
let tls_stream = tls_connector.connect(hostname, stream).await.unwrap();
6209-
let (_, session) = tls_stream.get_ref();
6231+
assert!(session.get_alpn_protocol().is_none());
62106232

6211-
assert!(session.get_alpn_protocol().is_none());
6233+
child.kill().unwrap();
6234+
child.wait().unwrap();
6235+
})
6236+
.await;
62126237
}

0 commit comments

Comments
 (0)