1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//! Regression: with ring and aws-lc-rs both linked (atproto-crates' reqwest brings aws-lc-rs), rustls cannot pick
//! a process default and every wss:// subscribe panicked until atlibs_host::tls(). This test never calls tls():
//! ws::subscribe (com.atproto.sync.subscribeRepos, echo's `watch`) must. The only test in this binary, so the
//! process default is untouched by anything else. Fixture: tests/fixtures/localhost.pem (self-signed, not a CA).
use std::sync::Arc;
use std::time::Duration;
use atlibs_host::Signal;
use atlibs_host::ws::{encode_cbor, subscribe, ws_url};
use futures_util::SinkExt;
use rustls::pki_types::{CertificateDer, PrivateKeyDer, pem::PemObject};
use serde_json::json;
use tokio_tungstenite::tungstenite::Message;
#[tokio::test]
async fn wss_subscribe_with_two_crypto_providers_linked() {
let fixtures = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures");
let cert = format!("{fixtures}/localhost.pem");
// SAFETY: the only test in this binary, and nothing else reads the environment concurrently.
unsafe { std::env::set_var("ATLIBS_EXTRA_CA", &cert) };
// the server names its provider, so only the client side depends on the process default
let config = rustls::ServerConfig::builder_with_provider(Arc::new(rustls::crypto::ring::default_provider()))
.with_safe_default_protocol_versions()
.unwrap()
.with_no_client_auth()
.with_single_cert(
CertificateDer::pem_file_iter(&cert).unwrap().map(Result::unwrap).collect(),
PrivateKeyDer::from_pem_file(format!("{fixtures}/localhost.key.pem")).unwrap(),
)
.unwrap();
let acceptor = tokio_rustls::TlsAcceptor::from(Arc::new(config));
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let port = listener.local_addr().unwrap().port();
tokio::spawn(async move {
let (tcp, _) = listener.accept().await.unwrap();
let mut ws = tokio_tungstenite::accept_async(acceptor.accept(tcp).await.unwrap()).await.unwrap();
let frame = encode_cbor(&[json!({ "op": 1, "t": "#commit" }), json!({ "seq": 1 })]).unwrap();
ws.send(Message::binary(frame)).await.unwrap();
ws.close(None).await.unwrap();
});
let nsid = "com.atproto.sync.subscribeRepos";
let url = ws_url(&format!("https://127.0.0.1:{port}"), nsid, None).unwrap();
assert!(url.starts_with("wss://"));
let mut sub = subscribe(url, nsid.into(), None, None, vec![], Signal::new());
let first = tokio::time::timeout(Duration::from_secs(10), sub.messages.recv()).await.unwrap();
assert_eq!(
first,
Some(Ok(json!({ "$type": "com.atproto.sync.subscribeRepos#commit", "seq": 1 }))),
"no message: the subscribe task died (a CryptoProvider panic above means tls() was not called)"
);
}