viaduct/
backend.rs
1use crate::GLOBAL_SETTINGS;
6use crate::{info, trace};
7use ffi::FfiBackend;
8use once_cell::sync::OnceCell;
9mod ffi;
10
11pub fn note_backend(which: &str) {
12 static NOTE_BACKEND_ONCE: std::sync::Once = std::sync::Once::new();
16 let mut called = false;
17 NOTE_BACKEND_ONCE.call_once(|| {
18 info!("Using HTTP backend {}", which);
19 called = true;
20 });
21 if !called {
22 trace!("Using HTTP backend {}", which);
23 }
24}
25
26pub trait Backend: Send + Sync + 'static {
27 fn send(&self, request: crate::Request) -> Result<crate::Response, crate::Error>;
28}
29
30static BACKEND: OnceCell<&'static dyn Backend> = OnceCell::new();
31
32pub fn set_backend(b: &'static dyn Backend) -> Result<(), crate::Error> {
33 BACKEND
34 .set(b)
35 .map_err(|_| crate::error::Error::SetBackendError)
36}
37
38pub(crate) fn get_backend() -> &'static dyn Backend {
39 *BACKEND.get_or_init(|| Box::leak(Box::new(FfiBackend)))
40}
41
42pub fn send(request: crate::Request) -> Result<crate::Response, crate::Error> {
43 validate_request(&request)?;
44 get_backend().send(request)
45}
46
47pub fn validate_request(request: &crate::Request) -> Result<(), crate::Error> {
48 if request.url.scheme() != "https"
49 && match request.url.host() {
50 Some(url::Host::Domain(d)) => d != "localhost",
51 Some(url::Host::Ipv4(addr)) => !addr.is_loopback(),
52 Some(url::Host::Ipv6(addr)) => !addr.is_loopback(),
53 None => true,
54 }
55 && {
56 let settings = GLOBAL_SETTINGS.read();
57 settings
58 .addn_allowed_insecure_url
59 .as_ref()
60 .map(|url| url.host() != request.url.host() || url.scheme() != request.url.scheme())
61 .unwrap_or(true)
62 }
63 {
64 return Err(crate::Error::NonTlsUrl);
65 }
66 Ok(())
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
74 fn test_validate_request() {
75 let _https_request = crate::Request::new(
76 crate::Method::Get,
77 url::Url::parse("https://www.example.com").unwrap(),
78 );
79 assert!(validate_request(&_https_request).is_ok());
80
81 let _http_request = crate::Request::new(
82 crate::Method::Get,
83 url::Url::parse("http://www.example.com").unwrap(),
84 );
85 assert!(validate_request(&_http_request).is_err());
86
87 let _localhost_https_request = crate::Request::new(
88 crate::Method::Get,
89 url::Url::parse("https://127.0.0.1/index.html").unwrap(),
90 );
91 assert!(validate_request(&_localhost_https_request).is_ok());
92
93 let _localhost_https_request_2 = crate::Request::new(
94 crate::Method::Get,
95 url::Url::parse("https://localhost:4242/").unwrap(),
96 );
97 assert!(validate_request(&_localhost_https_request_2).is_ok());
98
99 let _localhost_http_request = crate::Request::new(
100 crate::Method::Get,
101 url::Url::parse("http://localhost:4242/").unwrap(),
102 );
103 assert!(validate_request(&_localhost_http_request).is_ok());
104
105 let localhost_request = crate::Request::new(
106 crate::Method::Get,
107 url::Url::parse("localhost:4242/").unwrap(),
108 );
109 assert!(validate_request(&localhost_request).is_err());
110
111 let localhost_request_shorthand_ipv6 =
112 crate::Request::new(crate::Method::Get, url::Url::parse("http://[::1]").unwrap());
113 assert!(validate_request(&localhost_request_shorthand_ipv6).is_ok());
114
115 let localhost_request_ipv6 = crate::Request::new(
116 crate::Method::Get,
117 url::Url::parse("http://[0:0:0:0:0:0:0:1]").unwrap(),
118 );
119 assert!(validate_request(&localhost_request_ipv6).is_ok());
120 }
121
122 #[test]
123 fn test_validate_request_addn_allowed_insecure_url() {
124 let request_root = crate::Request::new(
125 crate::Method::Get,
126 url::Url::parse("http://anything").unwrap(),
127 );
128 let request = crate::Request::new(
129 crate::Method::Get,
130 url::Url::parse("http://anything/path").unwrap(),
131 );
132 let request_ftp = crate::Request::new(
134 crate::Method::Get,
135 url::Url::parse("ftp://anything/path").unwrap(),
136 );
137 assert!(validate_request(&request_root).is_err());
138 assert!(validate_request(&request).is_err());
139 {
140 let mut settings = GLOBAL_SETTINGS.write();
141 settings.addn_allowed_insecure_url =
142 Some(url::Url::parse("http://something-else").unwrap());
143 }
144 assert!(validate_request(&request_root).is_err());
145 assert!(validate_request(&request).is_err());
146
147 {
148 let mut settings = GLOBAL_SETTINGS.write();
149 settings.addn_allowed_insecure_url = Some(url::Url::parse("http://anything").unwrap());
150 }
151 assert!(validate_request(&request_root).is_ok());
152 assert!(validate_request(&request).is_ok());
153 assert!(validate_request(&request_ftp).is_err());
154 }
155}