1#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg))]
2#![doc = include_str!("../README.md")]
3#![allow(renamed_and_removed_lints)] #![allow(unknown_lints)] #![warn(missing_docs)]
7#![warn(noop_method_call)]
8#![warn(unreachable_pub)]
9#![warn(clippy::all)]
10#![deny(clippy::await_holding_lock)]
11#![deny(clippy::cargo_common_metadata)]
12#![deny(clippy::cast_lossless)]
13#![deny(clippy::checked_conversions)]
14#![warn(clippy::cognitive_complexity)]
15#![deny(clippy::debug_assert_with_mut_call)]
16#![deny(clippy::exhaustive_enums)]
17#![deny(clippy::exhaustive_structs)]
18#![deny(clippy::expl_impl_clone_on_copy)]
19#![deny(clippy::fallible_impl_from)]
20#![deny(clippy::implicit_clone)]
21#![deny(clippy::large_stack_arrays)]
22#![warn(clippy::manual_ok_or)]
23#![deny(clippy::missing_docs_in_private_items)]
24#![warn(clippy::needless_borrow)]
25#![warn(clippy::needless_pass_by_value)]
26#![warn(clippy::option_option)]
27#![deny(clippy::print_stderr)]
28#![deny(clippy::print_stdout)]
29#![warn(clippy::rc_buffer)]
30#![deny(clippy::ref_option_ref)]
31#![warn(clippy::semicolon_if_nothing_returned)]
32#![warn(clippy::trait_duplication_in_bounds)]
33#![deny(clippy::unchecked_duration_subtraction)]
34#![deny(clippy::unnecessary_wraps)]
35#![warn(clippy::unseparated_literal_suffix)]
36#![deny(clippy::unwrap_used)]
37#![deny(clippy::mod_module_files)]
38#![allow(clippy::let_unit_value)] #![allow(clippy::uninlined_format_args)]
40#![allow(clippy::significant_drop_in_scrutinee)] #![allow(clippy::result_large_err)] #![allow(clippy::needless_raw_string_hashes)] #![allow(clippy::needless_lifetimes)] #![allow(mismatched_lifetime_syntaxes)] use std::sync::{Arc, Mutex};
48
49use arti_client::{IntoTorAddr, TorClient};
50use ureq::{
51 http::{uri::Scheme, Uri},
52 tls::TlsProvider as UreqTlsProvider,
53 unversioned::{
54 resolver::{ArrayVec, ResolvedSocketAddrs, Resolver as UreqResolver},
55 transport::{Buffers, Connector as UreqConnector, LazyBuffers, NextTimeout, Transport},
56 },
57};
58
59use educe::Educe;
60use thiserror::Error;
61use tor_proto::stream::{DataReader, DataWriter};
62use tor_rtcompat::{Runtime, ToplevelBlockOn};
63
64#[cfg(feature = "rustls")]
65use ureq::unversioned::transport::RustlsConnector;
66
67#[cfg(feature = "native-tls")]
68use ureq::unversioned::transport::NativeTlsConnector;
69
70use futures::io::{AsyncReadExt, AsyncWriteExt};
71
72pub use arti_client;
74
75pub use tor_rtcompat;
77
78pub use ureq;
80
81pub fn default_agent() -> Result<ureq::Agent, Error> {
98 Ok(Connector::new()?.agent())
99}
100
101#[derive(Educe)]
162#[educe(Debug)]
163pub struct Connector<R: Runtime> {
164 #[educe(Debug(ignore))]
166 client: TorClient<R>,
167
168 tls_provider: UreqTlsProvider,
170}
171
172pub struct ConnectorBuilder<R: Runtime> {
190 client: Option<TorClient<R>>,
192
193 runtime: R,
201
202 tls_provider: Option<UreqTlsProvider>,
204}
205
206#[derive(Educe)]
209#[educe(Debug)]
210struct HttpTransport<R: Runtime> {
211 r: Arc<Mutex<DataReader>>,
214
215 w: Arc<Mutex<DataWriter>>, #[educe(Debug(ignore))]
220 buffer: LazyBuffers,
221
222 rt: R,
224}
225
226#[derive(Educe)]
250#[educe(Debug)]
251pub struct Resolver<R: Runtime> {
252 #[educe(Debug(ignore))]
256 client: TorClient<R>,
257}
258
259#[derive(Error, Debug)]
261#[non_exhaustive]
262pub enum Error {
263 #[error("unsupported URI scheme in {uri:?}")]
265 UnsupportedUriScheme {
266 uri: Uri,
268 },
269
270 #[error("Missing hostname in {uri:?}")]
272 MissingHostname {
273 uri: Uri,
275 },
276
277 #[error("Tor connection failed")]
279 Arti(#[from] arti_client::Error),
280
281 #[error("General I/O error")]
283 Io(#[from] std::io::Error),
284
285 #[error("TLS provider in config does not match the one in Connector.")]
287 TlsConfigMismatch,
288}
289
290impl tor_error::HasKind for Error {
292 #[rustfmt::skip]
293 fn kind(&self) -> tor_error::ErrorKind {
294 use tor_error::ErrorKind as EK;
295 match self {
296 Error::UnsupportedUriScheme{..} => EK::NotImplemented,
297 Error::MissingHostname{..} => EK::BadApiUsage,
298 Error::Arti(e) => e.kind(),
299 Error::Io(..) => EK::Other,
300 Error::TlsConfigMismatch => EK::BadApiUsage,
301 }
302 }
303}
304
305impl std::convert::From<Error> for ureq::Error {
307 fn from(err: Error) -> Self {
308 match err {
309 Error::MissingHostname { uri } => {
310 ureq::Error::BadUri(format!("Missing hostname in {uri:?}"))
311 }
312 Error::UnsupportedUriScheme { uri } => {
313 ureq::Error::BadUri(format!("Unsupported URI scheme in {uri:?}"))
314 }
315 Error::Arti(e) => ureq::Error::Io(std::io::Error::other(e)), Error::Io(e) => ureq::Error::Io(e),
317 Error::TlsConfigMismatch => {
318 ureq::Error::Tls("TLS provider in config does not match the one in Connector.")
319 }
320 }
321 }
322}
323
324impl<R: Runtime + ToplevelBlockOn> Transport for HttpTransport<R> {
334 fn buffers(&mut self) -> &mut dyn Buffers {
336 &mut self.buffer
337 }
338
339 fn transmit_output(&mut self, amount: usize, _timeout: NextTimeout) -> Result<(), ureq::Error> {
341 let mut writer = self.w.lock().expect("lock poisoned");
342
343 let buffer = self.buffer.output();
344 let data_to_write = &buffer[..amount];
345
346 self.rt.block_on(async {
347 writer.write_all(data_to_write).await?;
348 writer.flush().await?;
349 Ok(())
350 })
351 }
352
353 fn await_input(&mut self, _timeout: NextTimeout) -> Result<bool, ureq::Error> {
355 let mut reader = self.r.lock().expect("lock poisoned");
356
357 let buffers = self.buffer.input_append_buf();
358 let size = self.rt.block_on(reader.read(buffers))?;
359 self.buffer.input_appended(size);
360
361 Ok(size > 0)
362 }
363
364 fn is_open(&mut self) -> bool {
366 self.r.lock().is_ok_and(|guard| {
370 guard
371 .client_stream_ctrl()
372 .is_some_and(|ctrl| ctrl.is_connected())
373 })
374 }
375}
376
377impl<R: Runtime> ConnectorBuilder<R> {
378 pub fn new() -> Result<ConnectorBuilder<tor_rtcompat::PreferredRuntime>, Error> {
380 Ok(ConnectorBuilder {
381 client: None,
382 runtime: tor_rtcompat::PreferredRuntime::create()?,
383 tls_provider: None,
384 })
385 }
386
387 pub fn build(self) -> Result<Connector<R>, Error> {
389 let client = match self.client {
390 Some(client) => client,
391 None => TorClient::with_runtime(self.runtime).create_unbootstrapped()?,
392 };
393
394 let tls_provider = self.tls_provider.unwrap_or(get_default_tls_provider());
395
396 Ok(Connector {
397 client,
398 tls_provider,
399 })
400 }
401
402 pub fn with_runtime(runtime: R) -> Result<ConnectorBuilder<R>, Error> {
406 Ok(ConnectorBuilder {
407 client: None,
408 runtime,
409 tls_provider: None,
410 })
411 }
412
413 pub fn tor_client(mut self, client: TorClient<R>) -> ConnectorBuilder<R> {
420 self.runtime = client.runtime().clone();
421 self.client = Some(client);
422 self
423 }
424
425 pub fn tls_provider(mut self, tls_provider: UreqTlsProvider) -> Self {
427 self.tls_provider = Some(tls_provider);
428 self
429 }
430}
431
432impl<R: Runtime + ToplevelBlockOn> UreqResolver for Resolver<R> {
440 fn resolve(
442 &self,
443 uri: &Uri,
444 _config: &ureq::config::Config,
445 _timeout: NextTimeout,
446 ) -> Result<ResolvedSocketAddrs, ureq::Error> {
447 let (host, port) = uri_to_host_port(uri)?;
450 let ips = self
451 .client
452 .runtime()
453 .block_on(async { self.client.resolve(&host).await })
454 .map_err(Error::from)?;
455
456 let mut array_vec: ArrayVec<core::net::SocketAddr, 16> = ArrayVec::from_fn(|_| {
457 core::net::SocketAddr::new(core::net::IpAddr::V4(core::net::Ipv4Addr::UNSPECIFIED), 0)
458 });
459
460 for ip in ips {
461 let socket_addr = core::net::SocketAddr::new(ip, port);
462 array_vec.push(socket_addr);
463 }
464
465 Ok(array_vec)
466 }
467}
468
469impl<R: Runtime + ToplevelBlockOn> Connector<R> {
470 pub fn with_tor_client(client: TorClient<R>) -> Connector<R> {
472 Connector {
473 client,
474 tls_provider: get_default_tls_provider(),
475 }
476 }
477}
478
479impl<R: Runtime + ToplevelBlockOn> UreqConnector<()> for Connector<R> {
480 type Out = Box<dyn Transport>;
481
482 fn connect(
486 &self,
487 details: &ureq::unversioned::transport::ConnectionDetails,
488 _chained: Option<()>,
489 ) -> Result<Option<Self::Out>, ureq::Error> {
490 let (host, port) = uri_to_host_port(details.uri)?;
492
493 let addr = (host.as_str(), port)
495 .into_tor_addr()
496 .map_err(|e| Error::Arti(e.into()))?;
497
498 let stream = self
500 .client
501 .runtime()
502 .block_on(async { self.client.connect(addr).await })
503 .map_err(Error::from)?;
504
505 let (r, w) = stream.split();
507 Ok(Some(Box::new(HttpTransport {
508 r: Arc::new(Mutex::new(r)),
509 w: Arc::new(Mutex::new(w)),
510 buffer: LazyBuffers::new(2048, 2048),
511 rt: self.client.runtime().clone(),
512 })))
513 }
514}
515
516impl Connector<tor_rtcompat::PreferredRuntime> {
517 pub fn new() -> Result<Self, Error> {
525 Self::builder()?.build()
526 }
527}
528
529impl<R: Runtime + ToplevelBlockOn> Connector<R> {
530 pub fn resolver(&self) -> Resolver<R> {
532 Resolver {
533 client: self.client.clone(),
534 }
535 }
536
537 pub fn agent(self) -> ureq::Agent {
556 let resolver = self.resolver();
557
558 let ureq_config = ureq::config::Config::builder()
559 .tls_config(
560 ureq::tls::TlsConfig::builder()
561 .provider(self.tls_provider)
562 .build(),
563 )
564 .build();
565
566 ureq::Agent::with_parts(ureq_config, self.connector_chain(), resolver)
567 }
568
569 pub fn agent_with_ureq_config(
573 self,
574 config: ureq::config::Config,
575 ) -> Result<ureq::Agent, Error> {
576 let resolver = self.resolver();
577
578 if self.tls_provider != config.tls_config().provider() {
579 return Err(Error::TlsConfigMismatch);
580 }
581
582 Ok(ureq::Agent::with_parts(
583 config,
584 self.connector_chain(),
585 resolver,
586 ))
587 }
588
589 fn connector_chain(self) -> impl UreqConnector {
591 let chain = self;
592
593 #[cfg(feature = "rustls")]
594 let chain = chain.chain(RustlsConnector::default());
595
596 #[cfg(feature = "native-tls")]
597 let chain = chain.chain(NativeTlsConnector::default());
598
599 chain
600 }
601}
602
603pub fn get_default_tls_provider() -> UreqTlsProvider {
605 if cfg!(feature = "native-tls") {
606 UreqTlsProvider::NativeTls
607 } else {
608 UreqTlsProvider::Rustls
609 }
610}
611
612impl<R: Runtime> Connector<R> {
632 pub fn builder() -> Result<ConnectorBuilder<tor_rtcompat::PreferredRuntime>, Error> {
634 ConnectorBuilder::<R>::new()
635 }
636}
637
638fn uri_to_host_port(uri: &Uri) -> Result<(String, u16), Error> {
642 let host = uri
643 .host()
644 .ok_or_else(|| Error::MissingHostname { uri: uri.clone() })?;
645
646 let port = match uri.scheme() {
647 Some(scheme) if scheme == &Scheme::HTTPS => Ok(443),
648 Some(scheme) if scheme == &Scheme::HTTP => Ok(80),
649 Some(_) => Err(Error::UnsupportedUriScheme { uri: uri.clone() }),
650 None => Err(Error::UnsupportedUriScheme { uri: uri.clone() }),
651 }?;
652
653 Ok((host.to_owned(), port))
654}
655
656#[cfg(test)]
657mod arti_ureq_test {
658 #![allow(clippy::bool_assert_comparison)]
660 #![allow(clippy::clone_on_copy)]
661 #![allow(clippy::dbg_macro)]
662 #![allow(clippy::mixed_attributes_style)]
663 #![allow(clippy::print_stderr)]
664 #![allow(clippy::print_stdout)]
665 #![allow(clippy::single_char_pattern)]
666 #![allow(clippy::unwrap_used)]
667 #![allow(clippy::unchecked_duration_subtraction)]
668 #![allow(clippy::useless_vec)]
669 #![allow(clippy::needless_pass_by_value)]
670 use super::*;
673 use arti_client::config::TorClientConfigBuilder;
674 use std::str::FromStr;
675 use test_temp_dir::test_temp_dir;
676
677 const ARTI_TEST_LIVE_NETWORK: &str = "ARTI_TEST_LIVE_NETWORK";
678 const ARTI_TESTING_ON_LOCAL: &str = "ARTI_TESTING_ON_LOCAL";
679
680 fn assert_equal_types<T>(_: &T, _: &T) {}
683
684 fn test_live_network() -> bool {
687 let run_test = std::env::var(ARTI_TEST_LIVE_NETWORK).is_ok_and(|v| v == "1");
688 if !run_test {
689 println!("Skipping test, set {}=1 to run.", ARTI_TEST_LIVE_NETWORK);
690 }
691
692 run_test
693 }
694
695 fn testing_on_local() -> bool {
699 let run_test = std::env::var(ARTI_TESTING_ON_LOCAL).is_ok_and(|v| v == "1");
700 if !run_test {
701 println!("Skipping test, set {}=1 to run.", ARTI_TESTING_ON_LOCAL);
702 }
703
704 run_test
705 }
706
707 fn test_with_tor_client<R: Runtime>(rt: R, f: impl FnOnce(TorClient<R>)) {
709 let temp_dir = test_temp_dir!();
710 temp_dir.used_by(move |temp_dir| {
711 let arti_config = TorClientConfigBuilder::from_directories(
712 temp_dir.join("state"),
713 temp_dir.join("cache"),
714 )
715 .build()
716 .expect("Failed to build TorClientConfig");
717
718 let tor_client = arti_client::TorClient::with_runtime(rt)
719 .config(arti_config)
720 .create_unbootstrapped()
721 .expect("Error creating Tor Client.");
722
723 f(tor_client);
724 });
725 }
726
727 fn request_is_tor(agent: ureq::Agent, https: bool) -> bool {
730 let mut request = agent
731 .get(format!(
732 "http{}://check.torproject.org/api/ip",
733 if https { "s" } else { "" }
734 ))
735 .call()
736 .expect("Failed to make request.");
737 let response = request
738 .body_mut()
739 .read_to_string()
740 .expect("Failed to read body.");
741 let json_response: serde_json::Value =
742 serde_json::from_str(&response).expect("Failed to parse JSON.");
743 let is_tor = json_response
744 .get("IsTor")
745 .expect("Failed to retrieve IsTor property from response")
746 .as_bool()
747 .expect("Failed to convert IsTor to bool");
748
749 is_tor
750 }
751
752 #[test]
755 fn test_equal_types() {
756 assert_equal_types(&1, &i32::MIN);
757 assert_equal_types(&1, &i64::MIN);
758 assert_equal_types(&String::from("foo"), &String::with_capacity(1));
759 }
760
761 #[test]
764 #[cfg(all(feature = "rustls", not(feature = "native-tls")))]
765 fn articonnector_new_returns_default() {
766 if !testing_on_local() {
767 return;
768 }
769
770 let actual_connector = Connector::new().expect("Failed to create Connector.");
771 let expected_connector = Connector {
772 client: TorClient::with_runtime(
773 tor_rtcompat::PreferredRuntime::create().expect("Failed to create runtime."),
774 )
775 .create_unbootstrapped()
776 .expect("Error creating Tor Client."),
777 tls_provider: UreqTlsProvider::Rustls,
778 };
779
780 assert_equal_types(&expected_connector, &actual_connector);
781 assert_equal_types(
782 &actual_connector.client.runtime().clone(),
783 &tor_rtcompat::PreferredRuntime::create().expect("Failed to create runtime."),
784 );
785 assert_eq!(
786 &actual_connector.tls_provider,
787 &ureq::tls::TlsProvider::Rustls,
788 );
789 }
790
791 #[test]
794 #[cfg(all(feature = "rustls", not(feature = "native-tls")))]
795 fn articonnector_with_tor_client() {
796 if !testing_on_local() {
797 return;
798 }
799
800 let tor_client = TorClient::with_runtime(
801 tor_rtcompat::PreferredRuntime::create().expect("Failed to create runtime."),
802 )
803 .create_unbootstrapped()
804 .expect("Error creating Tor Client.");
805
806 let actual_connector = Connector::with_tor_client(tor_client);
807 let expected_connector = Connector {
808 client: TorClient::with_runtime(
809 tor_rtcompat::PreferredRuntime::create().expect("Failed to create runtime."),
810 )
811 .create_unbootstrapped()
812 .expect("Error creating Tor Client."),
813 tls_provider: UreqTlsProvider::Rustls,
814 };
815
816 assert_equal_types(&expected_connector, &actual_connector);
817 assert_equal_types(
818 &actual_connector.client.runtime().clone(),
819 &tor_rtcompat::PreferredRuntime::create().expect("Failed to create runtime."),
820 );
821 assert_eq!(
822 &actual_connector.tls_provider,
823 &ureq::tls::TlsProvider::Rustls,
824 );
825 }
826
827 #[test]
830 fn articonnectorbuilder_new_returns_default() {
831 if !testing_on_local() {
832 return;
833 }
834
835 let expected = Connector::new().expect("Failed to create Connector.");
836 let actual = Connector::<tor_rtcompat::PreferredRuntime>::builder()
837 .expect("Failed to create ConnectorBuilder.")
838 .build()
839 .expect("Failed to create Connector.");
840
841 assert_equal_types(&expected, &actual);
842 assert_equal_types(&expected.client.runtime(), &actual.client.runtime());
843 assert_eq!(&expected.tls_provider, &actual.tls_provider);
844 }
845
846 #[cfg(all(feature = "tokio", feature = "rustls"))]
849 #[test]
850 fn articonnectorbuilder_with_runtime() {
851 if !testing_on_local() {
852 return;
853 }
854
855 let arti_connector = ConnectorBuilder::with_runtime(
856 tor_rtcompat::tokio::TokioRustlsRuntime::create().expect("Failed to create runtime."),
857 )
858 .expect("Failed to create ConnectorBuilder.")
859 .build()
860 .expect("Failed to create Connector.");
861
862 assert_equal_types(
863 &arti_connector.client.runtime().clone(),
864 &tor_rtcompat::tokio::TokioRustlsRuntime::create().expect("Failed to create runtime."),
865 );
866
867 let arti_connector = ConnectorBuilder::with_runtime(
868 tor_rtcompat::PreferredRuntime::create().expect("Failed to create runtime."),
869 )
870 .expect("Failed to create ConnectorBuilder.")
871 .build()
872 .expect("Failed to create Connector.");
873
874 assert_equal_types(
875 &arti_connector.client.runtime().clone(),
876 &tor_rtcompat::PreferredRuntime::create().expect("Failed to create runtime."),
877 );
878 }
879
880 #[cfg(all(feature = "tokio", feature = "rustls"))]
882 #[test]
883 fn articonnectorbuilder_set_tor_client() {
884 let rt =
885 tor_rtcompat::tokio::TokioRustlsRuntime::create().expect("Failed to create runtime.");
886
887 test_with_tor_client(rt.clone(), move |tor_client| {
888 let arti_connector = ConnectorBuilder::with_runtime(rt)
889 .expect("Failed to create ConnectorBuilder.")
890 .tor_client(tor_client.clone().isolated_client())
891 .build()
892 .expect("Failed to create Connector.");
893
894 assert_equal_types(
895 &arti_connector.client.runtime().clone(),
896 &tor_rtcompat::tokio::TokioRustlsRuntime::create()
897 .expect("Failed to create runtime."),
898 );
899 });
900 }
901
902 #[test]
904 fn test_uri_to_host_port() {
905 let uri = Uri::from_str("http://torproject.org").expect("Error parsing uri.");
906 let (host, port) = uri_to_host_port(&uri).expect("Error parsing uri.");
907
908 assert_eq!(host, "torproject.org");
909 assert_eq!(port, 80);
910
911 let uri = Uri::from_str("https://torproject.org").expect("Error parsing uri.");
912 let (host, port) = uri_to_host_port(&uri).expect("Error parsing uri.");
913
914 assert_eq!(host, "torproject.org");
915 assert_eq!(port, 443);
916
917 let uri = Uri::from_str("https://www.torproject.org/test").expect("Error parsing uri.");
918 let (host, port) = uri_to_host_port(&uri).expect("Error parsing uri.");
919
920 assert_eq!(host, "www.torproject.org");
921 assert_eq!(port, 443);
922 }
923
924 #[test]
927 fn request_goes_over_tor() {
928 if !test_live_network() {
929 return;
930 }
931
932 let is_tor = request_is_tor(
933 default_agent().expect("Failed to retrieve default agent."),
934 true,
935 );
936
937 assert_eq!(is_tor, true);
938 }
939
940 #[test]
945 #[cfg(all(feature = "rustls", not(feature = "native-tls")))]
946 fn request_goes_over_tor_with_unsafe_check() {
947 if !test_live_network() {
948 return;
949 }
950
951 let is_tor = request_is_tor(ureq::Agent::new_with_defaults(), true);
952 assert_eq!(is_tor, false);
953
954 let is_tor = request_is_tor(
955 default_agent().expect("Failed to retrieve default agent."),
956 true,
957 );
958 assert_eq!(is_tor, true);
959 }
960
961 #[test]
964 fn request_with_bare_http() {
965 if !test_live_network() {
966 return;
967 }
968
969 let rt = tor_rtcompat::PreferredRuntime::create().expect("Failed to create runtime.");
970
971 test_with_tor_client(rt, |tor_client| {
972 let arti_connector = Connector::with_tor_client(tor_client);
973 let is_tor = request_is_tor(arti_connector.agent(), false);
974
975 assert_eq!(is_tor, true);
976 });
977 }
978
979 #[test]
981 fn test_get_default_tls_provider() {
982 #[cfg(feature = "native-tls")]
983 assert_eq!(get_default_tls_provider(), UreqTlsProvider::NativeTls);
984
985 #[cfg(not(feature = "native-tls"))]
986 assert_eq!(get_default_tls_provider(), UreqTlsProvider::Rustls);
987 }
988
989 #[test]
993 fn test_tor_client_with_get_default_tls_provider() {
994 if !testing_on_local() {
995 return;
996 }
997
998 let tor_client = TorClient::with_runtime(
999 tor_rtcompat::PreferredRuntime::create().expect("Failed to create runtime."),
1000 )
1001 .create_unbootstrapped()
1002 .expect("Error creating Tor Client.");
1003
1004 let arti_connector = Connector::<tor_rtcompat::PreferredRuntime>::builder()
1005 .expect("Failed to create ConnectorBuilder.")
1006 .tor_client(tor_client.clone().isolated_client())
1007 .tls_provider(get_default_tls_provider())
1008 .build()
1009 .expect("Failed to create Connector.");
1010
1011 #[cfg(feature = "native-tls")]
1012 assert_eq!(
1013 &arti_connector.tls_provider,
1014 &ureq::tls::TlsProvider::NativeTls,
1015 );
1016
1017 #[cfg(not(feature = "native-tls"))]
1018 assert_eq!(
1019 &arti_connector.tls_provider,
1020 &ureq::tls::TlsProvider::Rustls,
1021 );
1022 }
1023}