libdoh/
lib.rs

1mod constants;
2pub mod dns;
3mod dns_json;
4mod edns_ecs;
5mod errors;
6mod globals;
7pub mod odoh;
8#[cfg(feature = "tls")]
9mod tls;
10
11use std::net::{IpAddr, SocketAddr};
12use std::pin::Pin;
13use std::sync::Arc;
14use std::time::Duration;
15
16use base64::engine::Engine;
17use byteorder::{BigEndian, ByteOrder};
18use futures::prelude::*;
19use futures::task::{Context, Poll};
20use hyper::http;
21use hyper::server::conn::Http;
22use hyper::{Body, HeaderMap, Method, Request, Response, StatusCode};
23use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
24use tokio::net::{TcpListener, TcpSocket, UdpSocket};
25use tokio::runtime;
26
27use crate::constants::*;
28pub use crate::errors::*;
29pub use crate::globals::*;
30
31pub mod reexports {
32    pub use tokio;
33}
34
35const BASE64_URL_SAFE_NO_PAD: base64::engine::GeneralPurpose =
36    base64::engine::general_purpose::GeneralPurpose::new(
37        &base64::alphabet::URL_SAFE,
38        base64::engine::general_purpose::GeneralPurposeConfig::new()
39            .with_encode_padding(false)
40            .with_decode_padding_mode(base64::engine::DecodePaddingMode::Indifferent),
41    );
42
43#[derive(Clone, Debug)]
44struct DnsResponse {
45    packet: Vec<u8>,
46    ttl: u32,
47}
48
49#[derive(Clone, Debug)]
50enum DoHType {
51    Standard,
52    Oblivious,
53    Json,
54}
55
56impl DoHType {
57    fn as_str(&self) -> String {
58        match self {
59            DoHType::Standard => String::from("application/dns-message"),
60            DoHType::Oblivious => String::from("application/oblivious-dns-message"),
61            DoHType::Json => String::from("application/dns-json"),
62        }
63    }
64}
65
66#[derive(Clone, Debug)]
67pub struct DoH {
68    pub globals: Arc<Globals>,
69    pub remote_addr: Option<SocketAddr>,
70}
71
72#[allow(clippy::unnecessary_wraps)]
73fn http_error(status_code: StatusCode) -> Result<Response<Body>, http::Error> {
74    let response = Response::builder()
75        .status(status_code)
76        .body(Body::empty())
77        .unwrap();
78    Ok(response)
79}
80
81#[allow(clippy::unnecessary_wraps)]
82fn http_error_with_cache(status_code: StatusCode) -> Result<Response<Body>, http::Error> {
83    // Return error with very long cache time (1 year) to prevent crawler bots from retrying
84    let response = Response::builder()
85        .status(status_code)
86        .header(hyper::header::CACHE_CONTROL, "max-age=31536000, immutable")
87        .body(Body::empty())
88        .unwrap();
89    Ok(response)
90}
91
92#[derive(Clone, Debug)]
93pub struct LocalExecutor {
94    runtime_handle: runtime::Handle,
95}
96
97impl LocalExecutor {
98    fn new(runtime_handle: runtime::Handle) -> Self {
99        LocalExecutor { runtime_handle }
100    }
101}
102
103impl<F> hyper::rt::Executor<F> for LocalExecutor
104where
105    F: std::future::Future + Send + 'static,
106    F::Output: Send,
107{
108    fn execute(&self, fut: F) {
109        self.runtime_handle.spawn(fut);
110    }
111}
112
113#[allow(clippy::type_complexity)]
114impl hyper::service::Service<http::Request<Body>> for DoH {
115    type Error = http::Error;
116    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
117    type Response = Response<Body>;
118
119    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
120        Poll::Ready(Ok(()))
121    }
122
123    fn call(&mut self, req: Request<Body>) -> Self::Future {
124        let globals = &self.globals;
125        let self_inner = self.clone();
126        if req.uri().path() == globals.path {
127            match *req.method() {
128                Method::POST => Box::pin(async move { self_inner.serve_post(req).await }),
129                Method::GET => Box::pin(async move { self_inner.serve_get(req).await }),
130                _ => Box::pin(async { http_error(StatusCode::METHOD_NOT_ALLOWED) }),
131            }
132        } else if req.uri().path() == globals.odoh_configs_path {
133            match *req.method() {
134                Method::GET => Box::pin(async move { self_inner.serve_odoh_configs().await }),
135                _ => Box::pin(async { http_error(StatusCode::METHOD_NOT_ALLOWED) }),
136            }
137        } else {
138            Box::pin(async { http_error(StatusCode::NOT_FOUND) })
139        }
140    }
141}
142
143impl DoH {
144    async fn serve_get(&self, req: Request<Body>) -> Result<Response<Body>, http::Error> {
145        match Self::parse_content_type(&req) {
146            Ok(DoHType::Standard) => self.serve_doh_get(req).await,
147            Ok(DoHType::Oblivious) => self.serve_odoh_get(req).await,
148            Ok(DoHType::Json) => self.serve_json_get(req).await,
149            Err(response) => Ok(*response),
150        }
151    }
152
153    async fn serve_post(&self, req: Request<Body>) -> Result<Response<Body>, http::Error> {
154        match Self::parse_content_type(&req) {
155            Ok(DoHType::Standard) => self.serve_doh_post(req).await,
156            Ok(DoHType::Oblivious) => self.serve_odoh_post(req).await,
157            Ok(DoHType::Json) => http_error(StatusCode::METHOD_NOT_ALLOWED),
158            Err(response) => Ok(*response),
159        }
160    }
161
162    async fn serve_doh_query(
163        &self,
164        query: Vec<u8>,
165        client_ip: Option<IpAddr>,
166    ) -> Result<Response<Body>, http::Error> {
167        let resp = match self.proxy(query, client_ip).await {
168            Ok(resp) => {
169                self.build_response(resp.packet, resp.ttl, DoHType::Standard.as_str(), true)
170            }
171            Err(e) => return http_error(StatusCode::from(e)),
172        };
173        match resp {
174            Ok(resp) => Ok(resp),
175            Err(e) => http_error(StatusCode::from(e)),
176        }
177    }
178
179    fn query_from_query_string(&self, req: Request<Body>) -> Option<Vec<u8>> {
180        let http_query = req.uri().query().unwrap_or("");
181        let mut question_str = None;
182        for parts in http_query.split('&') {
183            let mut kv = parts.split('=');
184            if let Some(k) = kv.next() {
185                if k == DNS_QUERY_PARAM {
186                    question_str = kv.next();
187                }
188            }
189        }
190        if let Some(question_str) = question_str {
191            if question_str.len() > MAX_DNS_QUESTION_LEN * 4 / 3 {
192                return None;
193            }
194        }
195        let query = match question_str
196            .and_then(|question_str| BASE64_URL_SAFE_NO_PAD.decode(question_str).ok())
197        {
198            Some(query) => query,
199            _ => return None,
200        };
201        Some(query)
202    }
203
204    async fn serve_doh_get(&self, req: Request<Body>) -> Result<Response<Body>, http::Error> {
205        let client_ip = if self.globals.enable_ecs {
206            edns_ecs::extract_client_ip(req.headers(), self.remote_addr)
207        } else {
208            None
209        };
210
211        let query = match self.query_from_query_string(req) {
212            Some(query) => query,
213            _ => return http_error_with_cache(StatusCode::BAD_REQUEST),
214        };
215        self.serve_doh_query(query, client_ip).await
216    }
217
218    async fn serve_doh_post(&self, req: Request<Body>) -> Result<Response<Body>, http::Error> {
219        if self.globals.disable_post {
220            return http_error(StatusCode::METHOD_NOT_ALLOWED);
221        }
222
223        let client_ip = if self.globals.enable_ecs {
224            edns_ecs::extract_client_ip(req.headers(), self.remote_addr)
225        } else {
226            None
227        };
228
229        let query = match self.read_body(req.into_body()).await {
230            Ok(q) => q,
231            Err(e) => return http_error(StatusCode::from(e)),
232        };
233        self.serve_doh_query(query, client_ip).await
234    }
235
236    async fn serve_odoh(&self, encrypted_query: Vec<u8>) -> Result<Response<Body>, http::Error> {
237        let odoh_public_key = (*self.globals.odoh_rotator).clone().current_public_key();
238        let (query, context) = match (*odoh_public_key).clone().decrypt_query(encrypted_query) {
239            Ok((q, context)) => (q.to_vec(), context),
240            Err(e) => return http_error(StatusCode::from(e)),
241        };
242        let resp = match self.proxy(query, None).await {
243            Ok(resp) => resp,
244            Err(e) => return http_error(StatusCode::from(e)),
245        };
246        let encrypted_resp = match context.encrypt_response(resp.packet) {
247            Ok(resp) => self.build_response(resp, 0u32, DoHType::Oblivious.as_str(), false),
248            Err(e) => return http_error(StatusCode::from(e)),
249        };
250
251        match encrypted_resp {
252            Ok(resp) => Ok(resp),
253            Err(e) => http_error(StatusCode::from(e)),
254        }
255    }
256
257    async fn serve_odoh_get(&self, req: Request<Body>) -> Result<Response<Body>, http::Error> {
258        let encrypted_query = match self.query_from_query_string(req) {
259            Some(encrypted_query) => encrypted_query,
260            _ => return http_error_with_cache(StatusCode::BAD_REQUEST),
261        };
262        self.serve_odoh(encrypted_query).await
263    }
264
265    async fn serve_odoh_post(&self, req: Request<Body>) -> Result<Response<Body>, http::Error> {
266        if self.globals.disable_post && !self.globals.allow_odoh_post {
267            return http_error(StatusCode::METHOD_NOT_ALLOWED);
268        }
269        let encrypted_query = match self.read_body(req.into_body()).await {
270            Ok(q) => q,
271            Err(e) => return http_error(StatusCode::from(e)),
272        };
273        self.serve_odoh(encrypted_query).await
274    }
275
276    async fn serve_odoh_configs(&self) -> Result<Response<Body>, http::Error> {
277        let odoh_public_key = (*self.globals.odoh_rotator).clone().current_public_key();
278        let configs = (*odoh_public_key).clone().into_config();
279        match self.build_response(
280            configs,
281            ODOH_KEY_ROTATION_SECS,
282            "application/octet-stream".to_string(),
283            true,
284        ) {
285            Ok(resp) => Ok(resp),
286            Err(e) => http_error(StatusCode::from(e)),
287        }
288    }
289
290    async fn serve_json_get(&self, req: Request<Body>) -> Result<Response<Body>, http::Error> {
291        use serde_json::json;
292
293        // Parse query parameters
294        let query_params = req.uri().query().unwrap_or("");
295        let mut json_query = dns_json::DnsJsonQuery {
296            name: String::new(),
297            qtype: None,
298            cd: None,
299            ct: None,
300            do_: None,
301            edns_client_subnet: None,
302        };
303
304        // Parse query string
305        for parts in query_params.split('&') {
306            let mut kv = parts.split('=');
307            if let (Some(k), Some(v)) = (kv.next(), kv.next()) {
308                match k {
309                    "name" => {
310                        json_query.name = urlencoding::decode(v).unwrap_or_default().into_owned()
311                    }
312                    "type" => json_query.qtype = v.parse().ok(),
313                    "cd" => json_query.cd = Some(v == "1" || v == "true"),
314                    "ct" => json_query.ct = Some(v.to_string()),
315                    "do" => json_query.do_ = Some(v == "1" || v == "true"),
316                    "edns_client_subnet" => json_query.edns_client_subnet = Some(v.to_string()),
317                    _ => {}
318                }
319            }
320        }
321
322        // Validate query
323        if json_query.name.is_empty() {
324            let error_response = json!({
325                "Status": 400,
326                "Comment": "Missing 'name' parameter"
327            });
328            return Response::builder()
329                .status(StatusCode::BAD_REQUEST)
330                .header(hyper::header::CONTENT_TYPE, "application/dns-json")
331                .body(Body::from(error_response.to_string()))
332                .or_else(|_| http_error(StatusCode::INTERNAL_SERVER_ERROR));
333        }
334
335        // Build DNS query packet
336        let query_packet = match dns_json::build_dns_query(&json_query) {
337            Ok(packet) => packet,
338            Err(e) => {
339                let error_response = json!({
340                    "Status": 400,
341                    "Comment": format!("Invalid query: {}", e)
342                });
343                return Response::builder()
344                    .status(StatusCode::BAD_REQUEST)
345                    .header(hyper::header::CONTENT_TYPE, "application/dns-json")
346                    .body(Body::from(error_response.to_string()))
347                    .or_else(|_| http_error(StatusCode::INTERNAL_SERVER_ERROR));
348            }
349        };
350
351        // Extract client IP if ECS is enabled
352        let client_ip = if self.globals.enable_ecs {
353            edns_ecs::extract_client_ip(req.headers(), self.remote_addr)
354        } else {
355            None
356        };
357
358        // Send query and get response
359        let dns_response = match self.proxy(query_packet, client_ip).await {
360            Ok(resp) => resp,
361            Err(e) => return http_error(StatusCode::from(e)),
362        };
363
364        // Parse DNS response to JSON
365        match dns_json::parse_dns_to_json(&dns_response.packet) {
366            Ok(json_response) => {
367                let json_string = match serde_json::to_string(&json_response) {
368                    Ok(s) => s,
369                    Err(_) => return http_error(StatusCode::INTERNAL_SERVER_ERROR),
370                };
371
372                Response::builder()
373                    .status(StatusCode::OK)
374                    .header(hyper::header::CONTENT_TYPE, "application/dns-json")
375                    .header(
376                        hyper::header::CACHE_CONTROL,
377                        format!(
378                            "max-age={}, stale-if-error={}, stale-while-revalidate={}",
379                            dns_response.ttl, STALE_IF_ERROR_SECS, STALE_WHILE_REVALIDATE_SECS
380                        ),
381                    )
382                    .header(hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN, "*")
383                    .body(Body::from(json_string))
384                    .or_else(|_| http_error(StatusCode::INTERNAL_SERVER_ERROR))
385            }
386            Err(e) => {
387                let error_response = json!({
388                    "Status": 500,
389                    "Comment": format!("Failed to parse DNS response: {}", e)
390                });
391                Response::builder()
392                    .status(StatusCode::INTERNAL_SERVER_ERROR)
393                    .header(hyper::header::CONTENT_TYPE, "application/dns-json")
394                    .body(Body::from(error_response.to_string()))
395                    .or_else(|_| http_error(StatusCode::INTERNAL_SERVER_ERROR))
396            }
397        }
398    }
399
400    fn acceptable_content_type(
401        headers: &HeaderMap,
402        content_types: &[&'static str],
403    ) -> Option<&'static str> {
404        let accept = headers.get(hyper::header::ACCEPT);
405        let accept = accept?;
406        for part in accept.to_str().unwrap_or("").split(',').map(|s| s.trim()) {
407            if let Some(found) = part
408                .split(';')
409                .next()
410                .map(|s| s.trim().to_ascii_lowercase())
411            {
412                if let Some(&content_type) = content_types
413                    .iter()
414                    .find(|&&content_type| content_type == found)
415                {
416                    return Some(content_type);
417                }
418            }
419        }
420        None
421    }
422
423    fn parse_content_type(req: &Request<Body>) -> Result<DoHType, Box<Response<Body>>> {
424        const CT_DOH: &str = "application/dns-message";
425        const CT_ODOH: &str = "application/oblivious-dns-message";
426        const CT_JSON: &str = "application/dns-json";
427
428        let headers = req.headers();
429        let content_type = match headers.get(hyper::header::CONTENT_TYPE) {
430            None => {
431                let acceptable_content_type =
432                    Self::acceptable_content_type(headers, &[CT_DOH, CT_ODOH, CT_JSON]);
433                match acceptable_content_type {
434                    None => {
435                        // Return NOT_ACCEPTABLE with long cache time for crawler bots
436                        let response = Response::builder()
437                            .status(StatusCode::NOT_ACCEPTABLE)
438                            .header(hyper::header::CACHE_CONTROL, "max-age=31536000, immutable")
439                            .body(Body::empty())
440                            .unwrap();
441                        return Err(Box::new(response));
442                    }
443                    Some(content_type) => content_type,
444                }
445            }
446            Some(content_type) => match content_type.to_str() {
447                Err(_) => {
448                    // Return BAD_REQUEST with long cache time for invalid content type
449                    let response = Response::builder()
450                        .status(StatusCode::BAD_REQUEST)
451                        .header(hyper::header::CACHE_CONTROL, "max-age=31536000, immutable")
452                        .body(Body::empty())
453                        .unwrap();
454                    return Err(Box::new(response));
455                }
456                Ok(content_type) => content_type,
457            },
458        };
459
460        match content_type.to_ascii_lowercase().as_str() {
461            CT_DOH => Ok(DoHType::Standard),
462            CT_ODOH => Ok(DoHType::Oblivious),
463            CT_JSON => Ok(DoHType::Json),
464            _ => {
465                // Return UNSUPPORTED_MEDIA_TYPE with long cache time
466                let response = Response::builder()
467                    .status(StatusCode::UNSUPPORTED_MEDIA_TYPE)
468                    .header(hyper::header::CACHE_CONTROL, "max-age=31536000, immutable")
469                    .body(Body::empty())
470                    .unwrap();
471                Err(Box::new(response))
472            }
473        }
474    }
475
476    async fn read_body(&self, mut body: Body) -> Result<Vec<u8>, DoHError> {
477        let mut sum_size = 0;
478        let mut query = vec![];
479        while let Some(chunk) = body.next().await {
480            let chunk = chunk.map_err(|_| DoHError::TooLarge)?;
481            sum_size += chunk.len();
482            if sum_size >= MAX_DNS_QUESTION_LEN {
483                return Err(DoHError::TooLarge);
484            }
485            query.extend(chunk);
486        }
487        Ok(query)
488    }
489
490    async fn proxy(
491        &self,
492        query: Vec<u8>,
493        client_ip: Option<IpAddr>,
494    ) -> Result<DnsResponse, DoHError> {
495        let proxy_timeout = self.globals.timeout;
496        let timeout_res = tokio::time::timeout(proxy_timeout, self._proxy(query, client_ip)).await;
497        timeout_res.map_err(|_| DoHError::UpstreamTimeout)?
498    }
499
500    async fn _proxy(
501        &self,
502        mut query: Vec<u8>,
503        client_ip: Option<IpAddr>,
504    ) -> Result<DnsResponse, DoHError> {
505        if query.len() < MIN_DNS_PACKET_LEN {
506            return Err(DoHError::Incomplete);
507        }
508        let _ = dns::set_edns_max_payload_size(&mut query, MAX_DNS_RESPONSE_LEN as _);
509
510        // Add EDNS Client Subnet if enabled and we have a client IP
511        if self.globals.enable_ecs {
512            if let Some(client_ip) = client_ip {
513                if let Err(e) = edns_ecs::add_ecs_to_packet(
514                    &mut query,
515                    client_ip,
516                    self.globals.ecs_prefix_v4,
517                    self.globals.ecs_prefix_v6,
518                ) {
519                    eprintln!("Failed to add EDNS Client Subnet: {}", e);
520                }
521            }
522        }
523        let globals = &self.globals;
524        let mut packet = vec![0; MAX_DNS_RESPONSE_LEN];
525        let (min_ttl, max_ttl, err_ttl) = (globals.min_ttl, globals.max_ttl, globals.err_ttl);
526
527        // UDP
528        {
529            let socket = UdpSocket::bind(&globals.local_bind_address)
530                .await
531                .map_err(DoHError::Io)?;
532            let expected_server_address = globals.server_address;
533            socket
534                .send_to(&query, &globals.server_address)
535                .map_err(DoHError::Io)
536                .await?;
537            let (len, response_server_address) =
538                socket.recv_from(&mut packet).map_err(DoHError::Io).await?;
539            if len < MIN_DNS_PACKET_LEN || expected_server_address != response_server_address {
540                return Err(DoHError::UpstreamIssue);
541            }
542            packet.truncate(len);
543        }
544
545        // TCP
546        if dns::is_truncated(&packet) {
547            let clients_count = self.globals.clients_count.current();
548            if self.globals.max_clients >= UDP_TCP_RATIO
549                && clients_count >= self.globals.max_clients / UDP_TCP_RATIO
550            {
551                return Err(DoHError::TooManyTcpSessions);
552            }
553            let socket = match globals.server_address {
554                SocketAddr::V4(_) => TcpSocket::new_v4(),
555                SocketAddr::V6(_) => TcpSocket::new_v6(),
556            }
557            .map_err(DoHError::Io)?;
558            let mut ext_socket = socket
559                .connect(globals.server_address)
560                .await
561                .map_err(DoHError::Io)?;
562            ext_socket.set_nodelay(true).map_err(DoHError::Io)?;
563            let mut binlen = [0u8, 0];
564            BigEndian::write_u16(&mut binlen, query.len() as u16);
565            ext_socket.write_all(&binlen).await.map_err(DoHError::Io)?;
566            ext_socket.write_all(&query).await.map_err(DoHError::Io)?;
567            ext_socket.flush().await.map_err(DoHError::Io)?;
568            ext_socket
569                .read_exact(&mut binlen)
570                .await
571                .map_err(DoHError::Io)?;
572            let packet_len = BigEndian::read_u16(&binlen) as usize;
573            if !(MIN_DNS_PACKET_LEN..=MAX_DNS_RESPONSE_LEN).contains(&packet_len) {
574                return Err(DoHError::UpstreamIssue);
575            }
576            packet = vec![0u8; packet_len];
577            ext_socket
578                .read_exact(&mut packet)
579                .await
580                .map_err(DoHError::Io)?;
581        }
582
583        let ttl = if dns::is_recoverable_error(&packet) {
584            err_ttl
585        } else {
586            match dns::min_ttl(&packet, min_ttl, max_ttl, err_ttl) {
587                Err(_) => return Err(DoHError::UpstreamIssue),
588                Ok(ttl) => ttl,
589            }
590        };
591        dns::add_edns_padding(&mut packet)
592            .map_err(|_| DoHError::TooLarge)
593            .ok();
594        Ok(DnsResponse { packet, ttl })
595    }
596
597    fn build_response(
598        &self,
599        packet: Vec<u8>,
600        ttl: u32,
601        content_type: String,
602        cors: bool,
603    ) -> Result<Response<Body>, DoHError> {
604        let packet_len = packet.len();
605        let mut response_builder = Response::builder()
606            .header(hyper::header::CONTENT_LENGTH, packet_len)
607            .header(hyper::header::CONTENT_TYPE, content_type.as_str())
608            .header(
609                hyper::header::CACHE_CONTROL,
610                format!(
611                    "max-age={ttl}, stale-if-error={STALE_IF_ERROR_SECS}, \
612                     stale-while-revalidate={STALE_WHILE_REVALIDATE_SECS}"
613                )
614                .as_str(),
615            );
616        if cors {
617            response_builder =
618                response_builder.header(hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN, "*");
619        }
620        let response = response_builder
621            .body(Body::from(packet))
622            .map_err(|_| DoHError::InvalidData)?;
623        Ok(response)
624    }
625
626    async fn client_serve<I>(self, stream: I, server: Http<LocalExecutor>)
627    where
628        I: AsyncRead + AsyncWrite + Send + Unpin + 'static,
629    {
630        let clients_count = self.globals.clients_count.clone();
631        if clients_count.increment() > self.globals.max_clients {
632            clients_count.decrement();
633            return;
634        }
635        self.globals.runtime_handle.clone().spawn(async move {
636            tokio::time::timeout(
637                self.globals.timeout + Duration::from_secs(1),
638                server.serve_connection(stream, self),
639            )
640            .await
641            .ok();
642            clients_count.decrement();
643        });
644    }
645
646    async fn start_without_tls(
647        self,
648        listener: TcpListener,
649        server: Http<LocalExecutor>,
650    ) -> Result<(), DoHError> {
651        let listener_service = async {
652            while let Ok((stream, client_addr)) = listener.accept().await {
653                let mut doh = self.clone();
654                doh.remote_addr = Some(client_addr);
655                doh.client_serve(stream, server.clone()).await;
656            }
657            Ok(()) as Result<(), DoHError>
658        };
659        listener_service.await?;
660        Ok(())
661    }
662
663    pub async fn entrypoint(self) -> Result<(), DoHError> {
664        let listen_address = self.globals.listen_address;
665        let listener = TcpListener::bind(&listen_address)
666            .await
667            .map_err(DoHError::Io)?;
668        let path = &self.globals.path;
669
670        let tls_enabled: bool;
671        #[cfg(not(feature = "tls"))]
672        {
673            tls_enabled = false;
674        }
675        #[cfg(feature = "tls")]
676        {
677            tls_enabled =
678                self.globals.tls_cert_path.is_some() && self.globals.tls_cert_key_path.is_some();
679        }
680        if tls_enabled {
681            println!("Listening on https://{listen_address}{path}");
682        } else {
683            println!("Listening on http://{listen_address}{path}");
684        }
685
686        let mut server = Http::new();
687        server.http1_keep_alive(self.globals.keepalive);
688        server.http2_max_concurrent_streams(self.globals.max_concurrent_streams);
689        server.pipeline_flush(true);
690        let executor = LocalExecutor::new(self.globals.runtime_handle.clone());
691        let server = server.with_executor(executor);
692
693        #[cfg(feature = "tls")]
694        {
695            if tls_enabled {
696                self.start_with_tls(listener, server).await?;
697                return Ok(());
698            }
699        }
700        self.start_without_tls(listener, server).await?;
701        Ok(())
702    }
703}