From 1ac2b56efe1230c5492885751e005f40437daad1 Mon Sep 17 00:00:00 2001 From: Erik Hollensbe Date: Wed, 27 Apr 2022 07:31:26 -0700 Subject: [PATCH] Add `unix` feature This adds several components: - a unix compile-time crate feature which allows for a `serve_unix` method which serves over a unix socket - an example that serves over a unix socket Signed-off-by: Erik Hollensbe --- Cargo.toml | 1 + examples/hello-world-unix.rs | 30 ++++++++++++++++++++++++++++++ src/app.rs | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 examples/hello-world-unix.rs diff --git a/Cargo.toml b/Cargo.toml index ea7acf1..bc0805e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,3 +25,4 @@ env_logger = "^0.9" default = ["logging"] logging = ["log"] tls = ["tokio-rustls", "webpki"] +unix = [] diff --git a/examples/hello-world-unix.rs b/examples/hello-world-unix.rs new file mode 100644 index 0000000..2ef0497 --- /dev/null +++ b/examples/hello-world-unix.rs @@ -0,0 +1,30 @@ +use std::path::PathBuf; + +use ratpack::prelude::*; + +async fn hello( + req: Request, + _resp: Option>, + params: Params, + _app: App<(), NoState>, + _state: NoState, +) -> HTTPResult { + let name = params.get("name").unwrap(); + let bytes = Body::from(format!("hello, {}!\n", name)); + + return Ok(( + req, + Some(Response::builder().status(200).body(bytes).unwrap()), + NoState {}, + )); +} + +#[tokio::main] +async fn main() -> Result<(), ServerError> { + let mut app = App::new(); + app.get("/:name", compose_handler!(hello)); + + app.serve_unix(PathBuf::from("/tmp/server.sock")).await?; + + Ok(()) +} diff --git a/src/app.rs b/src/app.rs index 17f063c..4790383 100644 --- a/src/app.rs +++ b/src/app.rs @@ -4,6 +4,11 @@ use http::{HeaderMap, Method, Request, Response, StatusCode}; use hyper::{server::conn::Http, service::service_fn, Body}; use tokio::{net::TcpListener, sync::Mutex}; +#[cfg(feature = "unix")] +use std::path::PathBuf; +#[cfg(feature = "unix")] +use tokio::net::UnixListener; + use crate::{handler::Handler, router::Router, Error, ServerError, TransientState}; /// App is used to define application-level functionality and initialize the server. Routes are @@ -182,6 +187,33 @@ impl App< } } + #[cfg(feature = "unix")] + pub async fn serve_unix(self, filename: PathBuf) -> Result<(), ServerError> { + let unix_listener = UnixListener::bind(filename)?; + loop { + let (stream, _) = unix_listener.accept().await?; + + let s = self.clone(); + let sfn = service_fn(move |req: Request| { + let s = s.clone(); + async move { s.clone().dispatch(req).await } + }); + + tokio::task::spawn(async move { + if let Err(http_err) = Http::new() + .http1_keep_alive(true) + .serve_connection(stream, sfn) + .await + { + #[cfg(feature = "logging")] + log::error!("Error while serving HTTP connection: {}", http_err); + #[cfg(not(feature = "logging"))] + eprintln!("Error while serving HTTP connection: {}", http_err); + } + }); + } + } + /// Start a TCP/HTTP server with tokio. Performs dispatch on an as-needed basis. This is a more /// common path for users to start a server. pub async fn serve(self, addr: &str) -> Result<(), ServerError> {