Skip to content

Commit

Permalink
Add support for UNIX sockets
Browse files Browse the repository at this point in the history
Breaking change.

* Implement a new module `connection`, which abstracts `std::net` types into ones that can use
  `std::os::unix::net` types on Unix platforms.
* Change `ServerConfig::addr` to a new, abstracted type and add `http_unix` method.
  `http` and `https` methods are unchanged and should still work.
* `Request::remote_addr` now returns `Option<&SocketAddr>`. This is `Some` for TCP servers and
  `None` for UNIX servers (since UNIX remote sockets are almost always unnamed).
  • Loading branch information
ColonelThirtyTwo committed Mar 25, 2022
1 parent c5177b0 commit 789e1c1
Show file tree
Hide file tree
Showing 16 changed files with 324 additions and 53 deletions.
6 changes: 3 additions & 3 deletions benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use tiny_http::Method;
// TODO: obtain time
fn curl_bench() {
let server = tiny_http::Server::http("0.0.0.0:0").unwrap();
let port = server.server_addr().port();
let port = server.server_addr().to_ip().unwrap().port();
let num_requests = 10usize;

match Command::new("curl")
Expand All @@ -31,7 +31,7 @@ fn curl_bench() {
#[bench]
fn sequential_requests(bencher: &mut test::Bencher) {
let server = tiny_http::Server::http("0.0.0.0:0").unwrap();
let port = server.server_addr().port();
let port = server.server_addr().to_ip().unwrap().port();

let mut stream = std::net::TcpStream::connect(("127.0.0.1", port)).unwrap();

Expand All @@ -51,7 +51,7 @@ fn parallel_requests(bencher: &mut test::Bencher) {
fdlimit::raise_fd_limit();

let server = tiny_http::Server::http("0.0.0.0:0").unwrap();
let port = server.server_addr().port();
let port = server.server_addr().to_ip().unwrap().port();

bencher.iter(|| {
let mut streams = Vec::new();
Expand Down
4 changes: 2 additions & 2 deletions examples/php-cgi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ fn handle(rq: tiny_http::Request, script: &str) {
.env("PATH_INFO", "")
.env("PATH_TRANSLATED", "")
.env("QUERY_STRING", format!("{}", rq.url()))
.env("REMOTE_ADDR", format!("{}", rq.remote_addr()))
.env("REMOTE_ADDR", format!("{}", rq.remote_addr().unwrap()))
.env("REMOTE_HOST", "")
.env("REMOTE_IDENT", "")
.env("REMOTE_USER", "")
.env("REQUEST_METHOD", format!("{}", rq.method()))
.env("SCRIPT_NAME", script)
.env("SERVER_NAME", "tiny-http php-cgi example")
.env("SERVER_PORT", format!("{}", rq.remote_addr().port()))
.env("SERVER_PORT", format!("{}", rq.remote_addr().unwrap()))
.env("SERVER_PROTOCOL", "HTTP/1.1")
.env("SERVER_SOFTWARE", "tiny-http php-cgi example")
.output()
Expand Down
2 changes: 1 addition & 1 deletion examples/serve-root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fn get_content_type(path: &Path) -> &'static str {

fn main() {
let server = tiny_http::Server::http("0.0.0.0:8000").unwrap();
let port = server.server_addr().port();
let port = server.server_addr().to_ip().unwrap().port();
println!("Now listening on port {}", port);

loop {
Expand Down
2 changes: 1 addition & 1 deletion examples/websockets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ fn convert_key(input: &str) -> String {

fn main() {
let server = tiny_http::Server::http("0.0.0.0:0").unwrap();
let port = server.server_addr().port();
let port = server.server_addr().to_ip().unwrap().port();

println!("Server started");
println!(
Expand Down
2 changes: 1 addition & 1 deletion src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::Request;
/// and return Request objects.
pub struct ClientConnection {
// address of the client
remote_addr: IoResult<SocketAddr>,
remote_addr: IoResult<Option<SocketAddr>>,

// sequence of Readers to the stream, so that the data is not read in
// the wrong order
Expand Down
194 changes: 194 additions & 0 deletions src/connection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
//! Abstractions of Tcp and Unix socket types

#[cfg(unix)]
use std::os::unix::net as unix_net;
use std::{
net::{Shutdown, SocketAddr, TcpListener, TcpStream, ToSocketAddrs},
path::PathBuf,
};

/// Unified listener. Either a [`TcpListener`] or [`std::os::unix::net::UnixListener`]
pub enum Listener {
Tcp(TcpListener),
#[cfg(unix)]
Unix(unix_net::UnixListener),
}
impl Listener {
pub(crate) fn local_addr(&self) -> std::io::Result<ListenAddr> {
match self {
Self::Tcp(l) => l.local_addr().map(ListenAddr::from),
#[cfg(unix)]
Self::Unix(l) => l.local_addr().map(ListenAddr::from),
}
}

pub(crate) fn accept(&self) -> std::io::Result<(Connection, Option<SocketAddr>)> {
match self {
Self::Tcp(l) => l
.accept()
.map(|(conn, addr)| (Connection::from(conn), Some(addr))),
#[cfg(unix)]
Self::Unix(l) => l.accept().map(|(conn, _)| (Connection::from(conn), None)),
}
}
}
impl From<TcpListener> for Listener {
fn from(s: TcpListener) -> Self {
Self::Tcp(s)
}
}
#[cfg(unix)]
impl From<unix_net::UnixListener> for Listener {
fn from(s: unix_net::UnixListener) -> Self {
Self::Unix(s)
}
}

/// Unified connection. Either a [`TcpStream`] or [`std::os::unix::net::UnixStream`].
#[derive(Debug)]
pub(crate) enum Connection {
Tcp(TcpStream),
#[cfg(unix)]
Unix(unix_net::UnixStream),
}
impl std::io::Read for Connection {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
match self {
Self::Tcp(s) => s.read(buf),
#[cfg(unix)]
Self::Unix(s) => s.read(buf),
}
}
}
impl std::io::Write for Connection {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
match self {
Self::Tcp(s) => s.write(buf),
#[cfg(unix)]
Self::Unix(s) => s.write(buf),
}
}

fn flush(&mut self) -> std::io::Result<()> {
match self {
Self::Tcp(s) => s.flush(),
#[cfg(unix)]
Self::Unix(s) => s.flush(),
}
}
}
impl Connection {
/// Gets the peer's address. Some for TCP, None for Unix sockets.
pub(crate) fn peer_addr(&mut self) -> std::io::Result<Option<SocketAddr>> {
match self {
Self::Tcp(s) => s.peer_addr().map(Some),
#[cfg(unix)]
Self::Unix(_) => Ok(None),
}
}

pub(crate) fn shutdown(&self, how: Shutdown) -> std::io::Result<()> {
match self {
Self::Tcp(s) => s.shutdown(how),
#[cfg(unix)]
Self::Unix(s) => s.shutdown(how),
}
}

pub(crate) fn try_clone(&self) -> std::io::Result<Self> {
match self {
Self::Tcp(s) => s.try_clone().map(Self::from),
#[cfg(unix)]
Self::Unix(s) => s.try_clone().map(Self::from),
}
}
}
impl From<TcpStream> for Connection {
fn from(s: TcpStream) -> Self {
Self::Tcp(s)
}
}
#[cfg(unix)]
impl From<unix_net::UnixStream> for Connection {
fn from(s: unix_net::UnixStream) -> Self {
Self::Unix(s)
}
}

#[derive(Debug, Clone)]
pub enum ConfigListenAddr {
IP(Vec<SocketAddr>),
#[cfg(unix)]
// TODO: use SocketAddr when bind_addr is stabilized
Unix(std::path::PathBuf),
}
impl ConfigListenAddr {
pub fn from_socket_addrs<A: ToSocketAddrs>(addrs: A) -> std::io::Result<Self> {
addrs.to_socket_addrs().map(|it| Self::IP(it.collect()))
}

#[cfg(unix)]
pub fn unix_from_path<P: Into<PathBuf>>(path: P) -> Self {
Self::Unix(path.into())
}

pub(crate) fn bind(&self) -> std::io::Result<Listener> {
match self {
Self::IP(a) => TcpListener::bind(a.as_slice()).map(Listener::from),
#[cfg(unix)]
Self::Unix(a) => unix_net::UnixListener::bind(a).map(Listener::from),
}
}
}

/// Unified listen socket address. Either a [`SocketAddr`] or [`std::os::unix::net::SocketAddr`].
#[derive(Debug, Clone)]
pub enum ListenAddr {
IP(SocketAddr),
#[cfg(unix)]
Unix(unix_net::SocketAddr),
}
impl ListenAddr {
pub fn to_ip(self) -> Option<SocketAddr> {
match self {
Self::IP(s) => Some(s),
#[cfg(unix)]
Self::Unix(_) => None,
}
}

/// Gets the Unix socket address.
///
/// This is also available on non-Unix platforms, for ease of use, but always returns `None`.
#[cfg(unix)]
pub fn to_unix(self) -> Option<unix_net::SocketAddr> {
match self {
Self::IP(_) => None,
Self::Unix(s) => Some(s),
}
}
#[cfg(not(unix))]
pub fn to_unix(self) -> Option<SocketAddr> {
None
}
}
impl From<SocketAddr> for ListenAddr {
fn from(s: SocketAddr) -> Self {
Self::IP(s)
}
}
#[cfg(unix)]
impl From<unix_net::SocketAddr> for ListenAddr {
fn from(s: unix_net::SocketAddr) -> Self {
Self::Unix(s)
}
}
impl std::fmt::Display for ListenAddr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::IP(s) => s.fmt(f),
#[cfg(unix)]
Self::Unix(s) => std::fmt::Debug::fmt(s, f),
}
}
}

0 comments on commit 789e1c1

Please sign in to comment.