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)] use std::sync::{Arc, Mutex};
47
48use arti_client::{IntoTorAddr, TorClient};
49use ureq::{
50 http::{uri::Scheme, Uri},
51 tls::TlsProvider as UreqTlsProvider,
52 unversioned::{
53 resolver::{ArrayVec, ResolvedSocketAddrs, Resolver as UreqResolver},
54 transport::{Buffers, Connector as UreqConnector, LazyBuffers, NextTimeout, Transport},
55 },
56};
57
58use educe::Educe;
59use thiserror::Error;
60use tor_proto::stream::{DataReader, DataWriter};
61use tor_rtcompat::{Runtime, ToplevelBlockOn};
62
63#[cfg(feature = "rustls")]
64use ureq::unversioned::transport::RustlsConnector;
65
66#[cfg(feature = "native-tls")]
67use ureq::unversioned::transport::NativeTlsConnector;
68
69use futures::io::{AsyncReadExt, AsyncWriteExt};
70
71pub use arti_client;
73
74pub use tor_rtcompat;
76
77pub use ureq;
79
80pub fn default_agent() -> Result<ureq::Agent, Error> {
97 Ok(Connector::new()?.agent())
98}
99
100#[derive(Educe)]
161#[educe(Debug)]
162pub struct Connector<R: Runtime> {
163 #[educe(Debug(ignore))]
165 client: TorClient<R>,
166
167 tls_provider: UreqTlsProvider,
169}
170
171pub struct ConnectorBuilder<R: Runtime> {
189 client: Option<TorClient<R>>,
191
192 runtime: R,
200
201 tls_provider: Option<UreqTlsProvider>,
203}
204
205#[derive(Educe)]
208#[educe(Debug)]
209struct HttpTransport<R: Runtime> {
210 r: Arc<Mutex<DataReader>>,
213
214 w: Arc<Mutex<DataWriter>>, #[educe(Debug(ignore))]
219 buffer: LazyBuffers,
220
221 rt: R,
223}
224
225#[derive(Educe)]
249#[educe(Debug)]
250pub struct Resolver<R: Runtime> {
251 #[educe(Debug(ignore))]
255 client: TorClient<R>,
256}
257
258#[derive(Error, Debug)]
260#[non_exhaustive]
261pub enum Error {
262 #[error("unsupported URI scheme in {uri:?}")]
264 UnsupportedUriScheme {
265 uri: Uri,
267 },
268
269 #[error("Missing hostname in {uri:?}")]
271 MissingHostname {
272 uri: Uri,
274 },
275
276 #[error("Tor connection failed")]
278 Arti(#[from] arti_client::Error),
279
280 #[error("General I/O error")]
282 Io(#[from] std::io::Error),
283
284 #[error("TLS provider in config does not match the one in Connector.")]
286 TlsConfigMismatch,
287}
288
289impl tor_error::HasKind for Error {
291 #[rustfmt::skip]
292 fn kind(&self) -> tor_error::ErrorKind {
293 use tor_error::ErrorKind as EK;
294 match self {
295 Error::UnsupportedUriScheme{..} => EK::NotImplemented,
296 Error::MissingHostname{..} => EK::BadApiUsage,
297 Error::Arti(e) => e.kind(),
298 Error::Io(..) => EK::Other,
299 Error::TlsConfigMismatch => EK::BadApiUsage,
300 }
301 }
302}
303
304impl std::convert::From<Error> for ureq::Error {
306 fn from(err: Error) -> Self {
307 match err {
308 Error::MissingHostname { uri } => {
309 ureq::Error::BadUri(format!("Missing hostname in {uri:?}"))
310 }
311 Error::UnsupportedUriScheme { uri } => {
312 ureq::Error::BadUri(format!("Unsupported URI scheme in {uri:?}"))
313 }
314 Error::Arti(e) => ureq::Error::Io(std::io::Error::new(std::io::ErrorKind::Other, e)), Error::Io(e) => ureq::Error::Io(e),
316 Error::TlsConfigMismatch => {
317 ureq::Error::Tls("TLS provider in config does not match the one in Connector.")
318 }
319 }
320 }
321}
322
323impl<R: Runtime + ToplevelBlockOn> Transport for HttpTransport<R> {
333 fn buffers(&mut self) -> &mut dyn Buffers {
335 &mut self.buffer
336 }
337
338 fn transmit_output(&mut self, amount: usize, _timeout: NextTimeout) -> Result<(), ureq::Error> {
340 let mut writer = self.w.lock().expect("lock poisoned");
341
342 let buffer = self.buffer.output();
343 let data_to_write = &buffer[..amount];
344
345 self.rt.block_on(async {
346 writer.write_all(data_to_write).await?;
347 writer.flush().await?;
348 Ok(())
349 })
350 }
351
352 fn await_input(&mut self, _timeout: NextTimeout) -> Result<bool, ureq::Error> {
354 if self.buffers().can_use_input() {
355 return Ok(true);
356 }
357
358 let mut reader = self.r.lock().expect("lock poisoned");
359
360 let buffers = self.buffer.input_append_buf();
361 let size = self.rt.block_on(reader.read(buffers))?;
362 self.buffer.input_appended(size);
363
364 Ok(size > 0)
365 }
366
367 fn is_open(&mut self) -> bool {
369 self.r.lock().is_ok_and(|guard| {
373 guard
374 .client_stream_ctrl()
375 .is_some_and(|ctrl| ctrl.is_connected())
376 })
377 }
378}
379
380impl<R: Runtime> ConnectorBuilder<R> {
381 pub fn new() -> Result<ConnectorBuilder<tor_rtcompat::PreferredRuntime>, Error> {
383 Ok(ConnectorBuilder {
384 client: None,
385 runtime: tor_rtcompat::PreferredRuntime::create()?,
386 tls_provider: None,
387 })
388 }
389
390 pub fn build(self) -> Result<Connector<R>, Error> {
392 let client = match self.client {
393 Some(client) => client,
394 None => TorClient::with_runtime(self.runtime).create_unbootstrapped()?,
395 };
396
397 let tls_provider = self.tls_provider.unwrap_or(get_default_tls_provider());
398
399 Ok(Connector {
400 client,
401 tls_provider,
402 })
403 }
404
405 pub fn with_runtime(runtime: R) -> Result<ConnectorBuilder<R>, Error> {
409 Ok(ConnectorBuilder {
410 client: None,
411 runtime,
412 tls_provider: None,
413 })
414 }
415
416 pub fn tor_client(mut self, client: TorClient<R>) -> ConnectorBuilder<R> {
423 self.runtime = client.runtime().clone();
424 self.client = Some(client);
425 self
426 }
427
428 pub fn tls_provider(mut self, tls_provider: UreqTlsProvider) -> Self {
430 self.tls_provider = Some(tls_provider);
431 self
432 }
433}
434
435impl<R: Runtime + ToplevelBlockOn> UreqResolver for Resolver<R> {
443 fn resolve(
445 &self,
446 uri: &Uri,
447 _config: &ureq::config::Config,
448 _timeout: NextTimeout,
449 ) -> Result<ResolvedSocketAddrs, ureq::Error> {
450 let (host, port) = uri_to_host_port(uri)?;
453 let ips = self
454 .client
455 .runtime()
456 .block_on(async { self.client.resolve(&host).await })
457 .map_err(Error::from)?;
458
459 let mut array_vec: ArrayVec<core::net::SocketAddr, 16> = ArrayVec::from_fn(|_| {
460 core::net::SocketAddr::new(core::net::IpAddr::V4(core::net::Ipv4Addr::UNSPECIFIED), 0)
461 });
462
463 for ip in ips {
464 let socket_addr = core::net::SocketAddr::new(ip, port);
465 array_vec.push(socket_addr);
466 }
467
468 Ok(array_vec)
469 }
470}
471
472impl<R: Runtime + ToplevelBlockOn> Connector<R> {
473 pub fn with_tor_client(client: TorClient<R>) -> Connector<R> {
475 Connector {
476 client,
477 tls_provider: get_default_tls_provider(),
478 }
479 }
480}
481
482impl<R: Runtime + ToplevelBlockOn> UreqConnector<()> for Connector<R> {
483 type Out = Box<dyn Transport>;
484
485 fn connect(
489 &self,
490 details: &ureq::unversioned::transport::ConnectionDetails,
491 _chained: Option<()>,
492 ) -> Result<Option<Self::Out>, ureq::Error> {
493 let (host, port) = uri_to_host_port(details.uri)?;
495
496 let addr = (host.as_str(), port)
498 .into_tor_addr()
499 .map_err(|e| Error::Arti(e.into()))?;
500
501 let stream = self
503 .client
504 .runtime()
505 .block_on(async { self.client.connect(addr).await })
506 .map_err(Error::from)?;
507
508 let (r, w) = stream.split();
510 Ok(Some(Box::new(HttpTransport {
511 r: Arc::new(Mutex::new(r)),
512 w: Arc::new(Mutex::new(w)),
513 buffer: LazyBuffers::new(2048, 2048),
514 rt: self.client.runtime().clone(),
515 })))
516 }
517}
518
519impl Connector<tor_rtcompat::PreferredRuntime> {
520 pub fn new() -> Result<Self, Error> {
528 Self::builder()?.build()
529 }
530}
531
532impl<R: Runtime + ToplevelBlockOn> Connector<R> {
533 pub fn resolver(&self) -> Resolver<R> {
535 Resolver {
536 client: self.client.clone(),
537 }
538 }
539
540 pub fn agent(self) -> ureq::Agent {
559 let resolver = self.resolver();
560
561 let ureq_config = ureq::config::Config::builder()
562 .tls_config(
563 ureq::tls::TlsConfig::builder()
564 .provider(self.tls_provider)
565 .build(),
566 )
567 .build();
568
569 ureq::Agent::with_parts(ureq_config, self.connector_chain(), resolver)
570 }
571
572 pub fn agent_with_ureq_config(
576 self,
577 config: ureq::config::Config,
578 ) -> Result<ureq::Agent, Error> {
579 let resolver = self.resolver();
580
581 if self.tls_provider != config.tls_config().provider() {
582 return Err(Error::TlsConfigMismatch);
583 }
584
585 Ok(ureq::Agent::with_parts(
586 config,
587 self.connector_chain(),
588 resolver,
589 ))
590 }
591
592 fn connector_chain(self) -> impl UreqConnector {
594 let chain = self;
595
596 #[cfg(feature = "rustls")]
597 let chain = chain.chain(RustlsConnector::default());
598
599 #[cfg(feature = "native-tls")]
600 let chain = chain.chain(NativeTlsConnector::default());
601
602 chain
603 }
604}
605
606pub fn get_default_tls_provider() -> UreqTlsProvider {
608 if cfg!(feature = "native-tls") {
609 UreqTlsProvider::NativeTls
610 } else {
611 UreqTlsProvider::Rustls
612 }
613}
614
615impl<R: Runtime> Connector<R> {
635 pub fn builder() -> Result<ConnectorBuilder<tor_rtcompat::PreferredRuntime>, Error> {
637 ConnectorBuilder::<R>::new()
638 }
639}
640
641fn uri_to_host_port(uri: &Uri) -> Result<(String, u16), Error> {
645 let host = uri
646 .host()
647 .ok_or_else(|| Error::MissingHostname { uri: uri.clone() })?;
648
649 let port = match uri.scheme() {
650 Some(scheme) if scheme == &Scheme::HTTPS => Ok(443),
651 Some(scheme) if scheme == &Scheme::HTTP => Ok(80),
652 Some(_) => Err(Error::UnsupportedUriScheme { uri: uri.clone() }),
653 None => Err(Error::UnsupportedUriScheme { uri: uri.clone() }),
654 }?;
655
656 Ok((host.to_owned(), port))
657}
658
659#[cfg(test)]
660mod arti_ureq_test {
661 #![allow(clippy::bool_assert_comparison)]
663 #![allow(clippy::clone_on_copy)]
664 #![allow(clippy::dbg_macro)]
665 #![allow(clippy::mixed_attributes_style)]
666 #![allow(clippy::print_stderr)]
667 #![allow(clippy::print_stdout)]
668 #![allow(clippy::single_char_pattern)]
669 #![allow(clippy::unwrap_used)]
670 #![allow(clippy::unchecked_duration_subtraction)]
671 #![allow(clippy::useless_vec)]
672 #![allow(clippy::needless_pass_by_value)]
673 use super::*;
676 use arti_client::config::TorClientConfigBuilder;
677 use std::str::FromStr;
678 use test_temp_dir::test_temp_dir;
679
680 const ARTI_TEST_LIVE_NETWORK: &str = "ARTI_TEST_LIVE_NETWORK";
681 const ARTI_TESTING_ON_LOCAL: &str = "ARTI_TESTING_ON_LOCAL";
682
683 fn assert_equal_types<T>(_: &T, _: &T) {}
686
687 fn test_live_network() -> bool {
690 let run_test = std::env::var(ARTI_TEST_LIVE_NETWORK).is_ok_and(|v| v == "1");
691 if !run_test {
692 println!("Skipping test, set {}=1 to run.", ARTI_TEST_LIVE_NETWORK);
693 }
694
695 run_test
696 }
697
698 fn testing_on_local() -> bool {
702 let run_test = std::env::var(ARTI_TESTING_ON_LOCAL).is_ok_and(|v| v == "1");
703 if !run_test {
704 println!("Skipping test, set {}=1 to run.", ARTI_TESTING_ON_LOCAL);
705 }
706
707 run_test
708 }
709
710 fn test_with_tor_client<R: Runtime>(rt: R, f: impl FnOnce(TorClient<R>)) {
712 let temp_dir = test_temp_dir!();
713 temp_dir.used_by(move |temp_dir| {
714 let arti_config = TorClientConfigBuilder::from_directories(
715 temp_dir.join("state"),
716 temp_dir.join("cache"),
717 )
718 .build()
719 .expect("Failed to build TorClientConfig");
720
721 let tor_client = arti_client::TorClient::with_runtime(rt)
722 .config(arti_config)
723 .create_unbootstrapped()
724 .expect("Error creating Tor Client.");
725
726 f(tor_client);
727 });
728 }
729
730 fn request_is_tor(agent: ureq::Agent, https: bool) -> bool {
733 let mut request = agent
734 .get(format!(
735 "http{}://check.torproject.org/api/ip",
736 if https { "s" } else { "" }
737 ))
738 .call()
739 .expect("Failed to make request.");
740 let response = request
741 .body_mut()
742 .read_to_string()
743 .expect("Failed to read body.");
744 let json_response: serde_json::Value =
745 serde_json::from_str(&response).expect("Failed to parse JSON.");
746 let is_tor = json_response
747 .get("IsTor")
748 .expect("Failed to retrieve IsTor property from response")
749 .as_bool()
750 .expect("Failed to convert IsTor to bool");
751
752 is_tor
753 }
754
755 #[test]
758 fn test_equal_types() {
759 assert_equal_types(&1, &i32::MIN);
760 assert_equal_types(&1, &i64::MIN);
761 assert_equal_types(&String::from("foo"), &String::with_capacity(1));
762 }
763
764 #[test]
767 #[cfg(all(feature = "rustls", not(feature = "native-tls")))]
768 fn articonnector_new_returns_default() {
769 if !testing_on_local() {
770 return;
771 }
772
773 let actual_connector = Connector::new().expect("Failed to create Connector.");
774 let expected_connector = Connector {
775 client: TorClient::with_runtime(
776 tor_rtcompat::PreferredRuntime::create().expect("Failed to create runtime."),
777 )
778 .create_unbootstrapped()
779 .expect("Error creating Tor Client."),
780 tls_provider: UreqTlsProvider::Rustls,
781 };
782
783 assert_equal_types(&expected_connector, &actual_connector);
784 assert_equal_types(
785 &actual_connector.client.runtime().clone(),
786 &tor_rtcompat::PreferredRuntime::create().expect("Failed to create runtime."),
787 );
788 assert_eq!(
789 &actual_connector.tls_provider,
790 &ureq::tls::TlsProvider::Rustls,
791 );
792 }
793
794 #[test]
797 #[cfg(all(feature = "rustls", not(feature = "native-tls")))]
798 fn articonnector_with_tor_client() {
799 if !testing_on_local() {
800 return;
801 }
802
803 let tor_client = TorClient::with_runtime(
804 tor_rtcompat::PreferredRuntime::create().expect("Failed to create runtime."),
805 )
806 .create_unbootstrapped()
807 .expect("Error creating Tor Client.");
808
809 let actual_connector = Connector::with_tor_client(tor_client);
810 let expected_connector = Connector {
811 client: TorClient::with_runtime(
812 tor_rtcompat::PreferredRuntime::create().expect("Failed to create runtime."),
813 )
814 .create_unbootstrapped()
815 .expect("Error creating Tor Client."),
816 tls_provider: UreqTlsProvider::Rustls,
817 };
818
819 assert_equal_types(&expected_connector, &actual_connector);
820 assert_equal_types(
821 &actual_connector.client.runtime().clone(),
822 &tor_rtcompat::PreferredRuntime::create().expect("Failed to create runtime."),
823 );
824 assert_eq!(
825 &actual_connector.tls_provider,
826 &ureq::tls::TlsProvider::Rustls,
827 );
828 }
829
830 #[test]
833 fn articonnectorbuilder_new_returns_default() {
834 if !testing_on_local() {
835 return;
836 }
837
838 let expected = Connector::new().expect("Failed to create Connector.");
839 let actual = Connector::<tor_rtcompat::PreferredRuntime>::builder()
840 .expect("Failed to create ConnectorBuilder.")
841 .build()
842 .expect("Failed to create Connector.");
843
844 assert_equal_types(&expected, &actual);
845 assert_equal_types(&expected.client.runtime(), &actual.client.runtime());
846 assert_eq!(&expected.tls_provider, &actual.tls_provider);
847 }
848
849 #[cfg(all(feature = "tokio", feature = "rustls"))]
852 #[test]
853 fn articonnectorbuilder_with_runtime() {
854 if !testing_on_local() {
855 return;
856 }
857
858 let arti_connector = ConnectorBuilder::with_runtime(
859 tor_rtcompat::tokio::TokioRustlsRuntime::create().expect("Failed to create runtime."),
860 )
861 .expect("Failed to create ConnectorBuilder.")
862 .build()
863 .expect("Failed to create Connector.");
864
865 assert_equal_types(
866 &arti_connector.client.runtime().clone(),
867 &tor_rtcompat::tokio::TokioRustlsRuntime::create().expect("Failed to create runtime."),
868 );
869
870 let arti_connector = ConnectorBuilder::with_runtime(
871 tor_rtcompat::PreferredRuntime::create().expect("Failed to create runtime."),
872 )
873 .expect("Failed to create ConnectorBuilder.")
874 .build()
875 .expect("Failed to create Connector.");
876
877 assert_equal_types(
878 &arti_connector.client.runtime().clone(),
879 &tor_rtcompat::PreferredRuntime::create().expect("Failed to create runtime."),
880 );
881 }
882
883 #[cfg(all(feature = "tokio", feature = "rustls"))]
885 #[test]
886 fn articonnectorbuilder_set_tor_client() {
887 let rt =
888 tor_rtcompat::tokio::TokioRustlsRuntime::create().expect("Failed to create runtime.");
889
890 test_with_tor_client(rt.clone(), move |tor_client| {
891 let arti_connector = ConnectorBuilder::with_runtime(rt)
892 .expect("Failed to create ConnectorBuilder.")
893 .tor_client(tor_client.clone().isolated_client())
894 .build()
895 .expect("Failed to create Connector.");
896
897 assert_equal_types(
898 &arti_connector.client.runtime().clone(),
899 &tor_rtcompat::tokio::TokioRustlsRuntime::create()
900 .expect("Failed to create runtime."),
901 );
902 });
903 }
904
905 #[test]
907 fn test_uri_to_host_port() {
908 let uri = Uri::from_str("http://torproject.org").expect("Error parsing uri.");
909 let (host, port) = uri_to_host_port(&uri).expect("Error parsing uri.");
910
911 assert_eq!(host, "torproject.org");
912 assert_eq!(port, 80);
913
914 let uri = Uri::from_str("https://torproject.org").expect("Error parsing uri.");
915 let (host, port) = uri_to_host_port(&uri).expect("Error parsing uri.");
916
917 assert_eq!(host, "torproject.org");
918 assert_eq!(port, 443);
919
920 let uri = Uri::from_str("https://www.torproject.org/test").expect("Error parsing uri.");
921 let (host, port) = uri_to_host_port(&uri).expect("Error parsing uri.");
922
923 assert_eq!(host, "www.torproject.org");
924 assert_eq!(port, 443);
925 }
926
927 #[test]
930 fn request_goes_over_tor() {
931 if !test_live_network() {
932 return;
933 }
934
935 let is_tor = request_is_tor(
936 default_agent().expect("Failed to retrieve default agent."),
937 true,
938 );
939
940 assert_eq!(is_tor, true);
941 }
942
943 #[test]
948 #[cfg(all(feature = "rustls", not(feature = "native-tls")))]
949 fn request_goes_over_tor_with_unsafe_check() {
950 if !test_live_network() {
951 return;
952 }
953
954 let is_tor = request_is_tor(ureq::Agent::new_with_defaults(), true);
955 assert_eq!(is_tor, false);
956
957 let is_tor = request_is_tor(
958 default_agent().expect("Failed to retrieve default agent."),
959 true,
960 );
961 assert_eq!(is_tor, true);
962 }
963
964 #[test]
967 fn request_with_bare_http() {
968 if !test_live_network() {
969 return;
970 }
971
972 let rt = tor_rtcompat::PreferredRuntime::create().expect("Failed to create runtime.");
973
974 test_with_tor_client(rt, |tor_client| {
975 let arti_connector = Connector::with_tor_client(tor_client);
976 let is_tor = request_is_tor(arti_connector.agent(), false);
977
978 assert_eq!(is_tor, true);
979 });
980 }
981
982 #[test]
984 fn test_get_default_tls_provider() {
985 #[cfg(feature = "native-tls")]
986 assert_eq!(get_default_tls_provider(), UreqTlsProvider::NativeTls);
987
988 #[cfg(not(feature = "native-tls"))]
989 assert_eq!(get_default_tls_provider(), UreqTlsProvider::Rustls);
990 }
991
992 #[test]
996 fn test_tor_client_with_get_default_tls_provider() {
997 if !testing_on_local() {
998 return;
999 }
1000
1001 let tor_client = TorClient::with_runtime(
1002 tor_rtcompat::PreferredRuntime::create().expect("Failed to create runtime."),
1003 )
1004 .create_unbootstrapped()
1005 .expect("Error creating Tor Client.");
1006
1007 let arti_connector = Connector::<tor_rtcompat::PreferredRuntime>::builder()
1008 .expect("Failed to create ConnectorBuilder.")
1009 .tor_client(tor_client.clone().isolated_client())
1010 .tls_provider(get_default_tls_provider())
1011 .build()
1012 .expect("Failed to create Connector.");
1013
1014 #[cfg(feature = "native-tls")]
1015 assert_eq!(
1016 &arti_connector.tls_provider,
1017 &ureq::tls::TlsProvider::NativeTls,
1018 );
1019
1020 #[cfg(not(feature = "native-tls"))]
1021 assert_eq!(
1022 &arti_connector.tls_provider,
1023 &ureq::tls::TlsProvider::Rustls,
1024 );
1025 }
1026}