localtunnel_server/
lib.rs

1//! Start a localtunnel server,
2//! request a proxy endpoint at `domain.tld/<your-endpoint>`,
3//! user's request then proxied via `<your-endpoint>.domain.tld`.
4
5#[macro_use]
6extern crate lazy_static;
7
8use std::time::Duration;
9use std::{net::SocketAddr, sync::Arc};
10
11use actix_web::{web, App, HttpServer};
12use anyhow::Result;
13use dotenv::dotenv;
14use hyper::{server::conn::http1, service::service_fn};
15use tokio::{net::TcpListener, sync::Mutex, time::timeout};
16
17use crate::api::{api_status, request_endpoint};
18use crate::config::Config;
19use crate::proxy::proxy_handler;
20use crate::state::{ClientManager, State};
21
22mod api;
23mod auth;
24mod config;
25mod error;
26mod proxy;
27mod state;
28
29/// The interval between cleanup checks
30const CLEANUP_CHECK_INTERVAL: Duration = Duration::from_secs(60);
31
32lazy_static! {
33    static ref CONFIG: Config = {
34        dotenv().ok();
35        envy::from_env::<Config>().unwrap_or_default()
36    };
37}
38
39pub struct ServerConfig {
40    pub domain: String,
41    pub api_port: u16,
42    pub secure: bool,
43    pub max_sockets: u8,
44    pub proxy_port: u16,
45    pub require_auth: bool,
46}
47
48/// Start the proxy use low level api from hyper.
49/// Proxy endpoint request is served via actix-web.
50pub async fn start(config: ServerConfig) -> Result<()> {
51    let ServerConfig {
52        domain,
53        api_port,
54        secure,
55        max_sockets,
56        proxy_port,
57        require_auth,
58    } = config;
59    log::info!("Api server listens at {} {}", &domain, api_port);
60    log::info!(
61        "Start proxy server at {} {}, options: {} {}, require auth: {}",
62        &domain,
63        proxy_port,
64        secure,
65        max_sockets,
66        require_auth
67    );
68
69    let manager = Arc::new(Mutex::new(ClientManager::new(max_sockets)));
70    let api_state = web::Data::new(State {
71        manager: manager.clone(),
72        max_sockets,
73        require_auth,
74        secure,
75        domain,
76    });
77
78    let proxy_addr: SocketAddr = ([0, 0, 0, 0], proxy_port).into();
79    let listener = TcpListener::bind(proxy_addr).await?;
80    tokio::spawn(async move {
81        loop {
82            match timeout(CLEANUP_CHECK_INTERVAL, listener.accept()).await {
83                Ok(Ok((stream, _))) => {
84                    log::info!("Accepted a new proxy request");
85
86                    let proxy_manager = manager.clone();
87                    let service = service_fn(move |req| proxy_handler(req, proxy_manager.clone()));
88
89                    tokio::spawn(async move {
90                        if let Err(err) = http1::Builder::new()
91                            .serve_connection(hyper_util::rt::TokioIo::new(stream), service)
92                            .with_upgrades()
93                            .await
94                        {
95                            log::error!("Failed to serve connection: {:?}", err);
96                        }
97                    });
98                }
99                Ok(Err(e)) => log::error!("Failed to accept the request: {:?}", e),
100                Err(_) => {
101                    // timeout, cleanup old connections
102                    let mut manager = manager.lock().await;
103                    manager.cleanup().await;
104                }
105            }
106        }
107    });
108
109    HttpServer::new(move || {
110        App::new()
111            .app_data(api_state.clone())
112            .service(api_status)
113            .service(request_endpoint)
114    })
115    .bind(("0.0.0.0", api_port))?
116    .run()
117    .await?;
118
119    Ok(())
120}