Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,18 @@ jobs:
- host: windows-latest
build: yarn build --target i686-pc-windows-msvc
target: i686-pc-windows-msvc
- host: ubuntu-latest
- host: ubuntu-22.04
target: x86_64-unknown-linux-gnu
build: yarn build --target x86_64-unknown-linux-gnu --use-napi-cross
build: yarn build --target x86_64-unknown-linux-gnu
- host: ubuntu-latest
target: x86_64-unknown-linux-musl
build: yarn build --target x86_64-unknown-linux-musl -x
- host: macos-latest
target: aarch64-apple-darwin
build: yarn build --target aarch64-apple-darwin
- host: ubuntu-latest
- host: ubuntu-22.04
target: aarch64-unknown-linux-gnu
build: yarn build --target aarch64-unknown-linux-gnu --use-napi-cross
build: yarn build --target aarch64-unknown-linux-gnu
- host: ubuntu-latest
target: armv7-unknown-linux-gnueabihf
build: yarn build --target armv7-unknown-linux-gnueabihf --use-napi-cross
Expand Down Expand Up @@ -124,6 +124,24 @@ jobs:
run: ${{ matrix.settings.setup }}
if: ${{ matrix.settings.setup }}
shell: bash
- name: Install build essentials (x86_64 Linux)
if: ${{ matrix.settings.target == 'x86_64-unknown-linux-gnu' }}
run: |
sudo apt-get update
sudo apt install build-essential
echo "CC_x86_64_unknown_linux_gnu=gcc" >> $GITHUB_ENV
echo "CXX_x86_64_unknown_linux_gnu=g++" >> $GITHUB_ENV
echo "AR_x86_64_unknown_linux_gnu=ar" >> $GITHUB_ENV

- name: Install build essentials (aarch64 Linux)
if: ${{ matrix.settings.target == 'aarch64-unknown-linux-gnu' }}
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu binutils-aarch64-linux-gnu
echo "CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc" >> $GITHUB_ENV
echo "CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++" >> $GITHUB_ENV
echo "AR_aarch64_unknown_linux_gnu=aarch64-linux-gnu-ar" >> $GITHUB_ENV
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV
- name: Install dependencies
run: yarn install
- name: Build
Expand Down
14 changes: 8 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,35 +31,37 @@ mime_guess = "2.0.5"
napi-derive = "3.0.0"
percent-encoding = "2.3.2"
regex = "1.12.3"
rustls-acme = {version = "0.15.1", features = ["tokio"]}
serde_json = "1.0.148"
serde_qs = "1.0.0"
serde_urlencoded = "0.7.1"
tempfile = "3.24.0"
tokio-stream = {version = "0.1.18", features = ["net"]}
urlencoding = "2.1.3"

[dependencies.cookie]
features = ["percent-encode", "secure"]
version = "0.18.1"
features = ["percent-encode","secure"]

[dependencies.etag]
version = "4.0.0"
features = ["std"]
version = "4.0.0"

[dependencies.hyper]
version = "1.8.1"
features = ["full"]
version = "1.8.1"

[dependencies.hyper-util]
version = "0.1.19"
features = ["tokio"]
version = "0.1.19"

[dependencies.napi]
features = ["napi6", "async", "serde", "serde-json"]
version = "3.0.0"
features = ["napi6","async","serde","serde-json"]

[dependencies.tokio]
features = ["rt", "net", "rt-multi-thread", "macros"]
version = "1.48.0"
features = ["rt","net","rt-multi-thread","macros"]

[build-dependencies]
napi-build = "2"
Expand Down
7 changes: 7 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,7 @@ export declare class Server {
post(route: string, handler: JsHandlerFn): void
put(route: string, handler: JsHandlerFn): void
use(route: string | undefined | null, middleware: JsHandlerFn): void
acmeConfigMeta(config: AcmeConfigMeta): void
listen(addr: string): void
}

Expand Down Expand Up @@ -945,6 +946,12 @@ export declare class Version {
static http3(): Version
}

export interface AcmeConfigMeta {
domains: Array<string>
contactEmail: string
cacheDir: string
}

export interface ClearCookie {

}
Expand Down
10 changes: 10 additions & 0 deletions server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ const __dirname = process.cwd()
// Create app with router
const app = new Server()

// ============================================================================
// LETSENCRYPT: How to configure
// ============================================================================

// app.acmeConfigMeta({
// domains: ['"example.com'],
// contactEmail: '[email protected]',
// cacheDir: '/home/tomn/.local..',
// })

// ============================================================================
// ROUTE DEFINITIONS
// ============================================================================
Expand Down
93 changes: 74 additions & 19 deletions src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod get_next_id;
mod handle_http_request;

use env_logger::Builder as EnvLoggerBuilder;
use futures::prelude::*;
use hyper::Method as LibMethod;
use hyper::{server::conn::http1, service::service_fn};
use hyper_util::rt::tokio::{TokioIo, TokioTimer};
Expand All @@ -10,8 +11,11 @@ use matchit::{InsertError, Router};
use napi::bindgen_prelude::*;
use napi::threadsafe_function::{ThreadsafeCallContext, ThreadsafeFunction};
use napi_derive::napi;
use rustls_acme::AcmeConfig;
use rustls_acme::caches::DirCache;
use std::sync::Arc;
use tokio::net::TcpListener;
use tokio_stream::wrappers::TcpListenerStream;

use crate::request::Request;
use crate::response::Response;
Expand Down Expand Up @@ -59,11 +63,20 @@ pub struct MiddlewareMeta {
method: Option<LibMethod>,
}

#[napi(object)]
#[derive(Debug, Clone)]
pub struct AcmeConfigMeta {
pub domains: Vec<String>,
pub contact_email: String,
pub cache_dir: String,
}

/// HTTP Server that integrates with JavaScript handlers via Router
#[napi]
pub struct Server {
middlewares: Vec<MiddlewareMeta>,
router: Router<String>,
acme_config_meta: Option<AcmeConfigMeta>,
}

impl Server {
Expand Down Expand Up @@ -116,6 +129,7 @@ impl Server {
Ok(Self {
middlewares: Vec::new(),
router: Router::new(),
acme_config_meta: None,
})
}

Expand Down Expand Up @@ -144,10 +158,16 @@ impl Server {
self.register_middleware(route, middleware, env)
}

#[napi]
pub fn acme_config_meta(&mut self, config: AcmeConfigMeta) {
self.acme_config_meta = Some(config)
}

#[napi]
pub fn listen(&self, addr: String) -> Result<()> {
let router = Arc::new(self.router.clone());
let middlewares = Arc::new(self.middlewares.clone());
let acme_config_meta = self.acme_config_meta.clone();

EnvLoggerBuilder::new()
.filter_level(LevelFilter::max())
Expand All @@ -156,27 +176,62 @@ impl Server {
std::thread::spawn(move || {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async move {
let listener = TcpListener::bind(&addr).await.unwrap();
let tcp_listener = TcpListener::bind(&addr).await.unwrap();
log::debug!("Server listening on {}", addr);

loop {
let (socket, _) = listener.accept().await.unwrap();
let io = TokioIo::new(socket);

let router = router.clone();
let middlewares = middlewares.clone();

tokio::task::spawn(async move {
let _ = http1::Builder::new()
.timer(TokioTimer::new())
.serve_connection(
io,
service_fn(move |req| {
handle_http_request(req, router.clone(), middlewares.clone())
}),
)
.await;
});
match acme_config_meta {
Some(acme) => {
let tcp_stream = TcpListenerStream::new(tcp_listener);

let mut tls_incoming = AcmeConfig::new(acme.domains)
.contact_push(format!("mailto:{}", acme.contact_email))
.cache(DirCache::new(acme.cache_dir))
.tokio_incoming(tcp_stream, Vec::new());

while let Some(tls) = tls_incoming.next().await {
let tls = match tls {
Ok(t) => t,
Err(e) => {
log::error!("TLS accept error: {}", e);
continue;
}
};

let io = TokioIo::new(tls);
let router = router.clone();
let middlewares = middlewares.clone();

tokio::task::spawn(async move {
let _ = http1::Builder::new()
.timer(TokioTimer::new())
.serve_connection(
io,
service_fn(move |req| {
handle_http_request(req, router.clone(), middlewares.clone())
}),
)
.await;
});
}
}
None => loop {
let (socket, _) = tcp_listener.accept().await.unwrap();
let io = TokioIo::new(socket);
let router = router.clone();
let middlewares = middlewares.clone();

tokio::task::spawn(async move {
let _ = http1::Builder::new()
.timer(TokioTimer::new())
.serve_connection(
io,
service_fn(move |req| {
handle_http_request(req, router.clone(), middlewares.clone())
}),
)
.await;
});
},
}
});
});
Expand Down
Loading