-
Notifications
You must be signed in to change notification settings - Fork 321
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
931 additions
and
228 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#[cfg(unix)] | ||
#[async_std::main] | ||
async fn main() -> Result<(), std::io::Error> { | ||
use std::{env, net::TcpListener, os::unix::io::FromRawFd}; | ||
tide::log::start(); | ||
let mut app = tide::new(); | ||
app.at("/").get(|_| async { Ok(CHANGE_THIS_TEXT) }); | ||
|
||
const CHANGE_THIS_TEXT: &str = "hello world!"; | ||
|
||
const DOCS: &str = " | ||
To run this example: | ||
$ cargo install catflap cargo-watch | ||
$ catflap -- cargo watch -x \"run --example catflap\" | ||
and then edit this file"; | ||
|
||
if let Some(fd) = env::var("LISTEN_FD").ok().and_then(|fd| fd.parse().ok()) { | ||
app.listen(unsafe { TcpListener::from_raw_fd(fd) }).await?; | ||
} else { | ||
println!("{} ({})", DOCS, file!()); | ||
} | ||
Ok(()) | ||
} | ||
|
||
#[cfg(not(unix))] | ||
fn main() { | ||
panic!("this example only runs on cfg(unix) systems"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
#[async_std::main] | ||
async fn main() -> Result<(), std::io::Error> { | ||
tide::log::start(); | ||
let mut app = tide::new(); | ||
|
||
app.at("/").get(|request: tide::Request<_>| async move { | ||
Ok(format!( | ||
"Hi! You reached this app through: {}", | ||
request.local_addr().unwrap_or("an unknown port") | ||
)) | ||
}); | ||
|
||
app.listen(vec!["localhost:8000", "localhost:8081"]).await?; | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
//! Types that represent HTTP transports and binding | ||
|
||
mod multi_listener; | ||
mod parsed_listener; | ||
mod tcp_listener; | ||
mod to_listener; | ||
#[cfg(unix)] | ||
mod unix_listener; | ||
|
||
use crate::utils::BoxFuture; | ||
use crate::Server; | ||
use async_std::io; | ||
|
||
pub use multi_listener::MultiListener; | ||
pub use to_listener::ToListener; | ||
|
||
pub(crate) use parsed_listener::ParsedListener; | ||
pub(crate) use tcp_listener::TcpListener; | ||
#[cfg(unix)] | ||
pub(crate) use unix_listener::UnixListener; | ||
|
||
/// The Listener trait represents an implementation of http transport | ||
/// for a tide application. In order to provide a Listener to tide, | ||
/// you will also need to implement at least one [`ToListener`](crate::listener::ToListener) that | ||
/// outputs your Listener type. | ||
pub trait Listener<State: 'static>: | ||
std::fmt::Debug + std::fmt::Display + Send + Sync + 'static | ||
{ | ||
/// This is the primary entrypoint for the Listener trait. listen | ||
/// is called exactly once, and is expected to spawn tasks for | ||
/// each incoming connection. | ||
fn listen<'a>(&'a self, app: Server<State>) -> BoxFuture<'a, io::Result<()>>; | ||
|
||
/// Connect provides an opportunity to resolve any addresses, and | ||
/// mutate self. This is called before | ||
/// [listen](crate::listener::Listener::listen). | ||
fn connect<'a>(&'a mut self) -> BoxFuture<'a, io::Result<()>>; | ||
} | ||
|
||
pub(crate) fn is_transient_error(e: &io::Error) -> bool { | ||
e.kind() == io::ErrorKind::ConnectionRefused | ||
|| e.kind() == io::ErrorKind::ConnectionAborted | ||
|| e.kind() == io::ErrorKind::ConnectionReset | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
use crate::listener::{Listener, ToListener}; | ||
use crate::utils::BoxFuture; | ||
use crate::Server; | ||
|
||
use std::fmt::{self, Debug, Display, Formatter}; | ||
|
||
use async_std::io; | ||
use async_std::prelude::*; | ||
|
||
/// MultiListener allows tide to listen on any number of transports | ||
/// simultaneously (such as tcp ports, unix sockets, or tls). | ||
/// | ||
/// # Example: | ||
/// ```rust | ||
/// fn main() -> Result<(), std::io::Error> { | ||
/// async_std::task::block_on(async { | ||
/// tide::log::start(); | ||
/// let mut app = tide::new(); | ||
/// app.at("/").get(|_| async { Ok("Hello, world!") }); | ||
/// | ||
/// let mut multi = tide::listener::MultiListener::new(); | ||
/// multi.add("127.0.0.1:8000")?; | ||
/// multi.add(async_std::net::TcpListener::bind("127.0.0.1:8001").await?)?; | ||
/// # if cfg!(unix) { | ||
/// multi.add("unix://unix.socket")?; | ||
/// # } | ||
/// | ||
/// # if false { | ||
/// app.listen(multi).await?; | ||
/// # } | ||
/// Ok(()) | ||
/// }) | ||
///} | ||
///``` | ||
|
||
pub struct MultiListener<State>(Vec<Box<dyn Listener<State>>>); | ||
|
||
impl<State: Send + Sync + 'static> MultiListener<State> { | ||
/// creates a new MultiListener | ||
pub fn new() -> Self { | ||
Self(vec![]) | ||
} | ||
|
||
/// Adds any [`ToListener`](crate::listener::ToListener) to this | ||
/// MultiListener. An error result represents a failure to convert | ||
/// the [`ToListener`](crate::listener::ToListener) into a | ||
/// [`Listener`](crate::listener::Listener). | ||
/// | ||
/// ```rust | ||
/// # fn main() -> std::io::Result<()> { | ||
/// let mut multi = tide::listener::MultiListener::new(); | ||
/// multi.add("127.0.0.1:8000")?; | ||
/// multi.add(("localhost", 8001))?; | ||
/// multi.add(std::net::TcpListener::bind(("localhost", 8002))?)?; | ||
/// # std::mem::drop(tide::new().listen(multi)); // for the State generic | ||
/// # Ok(()) } | ||
/// ``` | ||
pub fn add<TL: ToListener<State>>(&mut self, listener: TL) -> io::Result<()> { | ||
self.0.push(Box::new(listener.to_listener()?)); | ||
Ok(()) | ||
} | ||
|
||
/// `MultiListener::with` allows for chained construction of a MultiListener: | ||
/// ```rust,no_run | ||
/// # use tide::listener::MultiListener; | ||
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async move { | ||
/// # let app = tide::new(); | ||
/// app.listen( | ||
/// MultiListener::new() | ||
/// .with("127.0.0.1:8080") | ||
/// .with(async_std::net::TcpListener::bind("127.0.0.1:8081").await?), | ||
/// ).await?; | ||
/// # Ok(()) }) } | ||
pub fn with<TL: ToListener<State>>(mut self, listener: TL) -> Self { | ||
self.add(listener).expect("Unable to add listener"); | ||
self | ||
} | ||
|
||
/// from_iter allows for the construction of a new MultiListener | ||
/// from collections of [`ToListener`](ToListener)s. | ||
/// ```rust | ||
/// # use tide::listener::MultiListener; | ||
/// # fn main() -> std::io::Result<()> { | ||
/// let mut multi = MultiListener::from_iter(vec!["127.0.0.1:8000", "tcp://localhost:8001"])?; | ||
/// if cfg!(unix) { | ||
/// multi.add("unix:///var/run/tide/socket")?; | ||
/// } | ||
/// # std::mem::drop(tide::new().listen(multi)); // for the State generic | ||
/// # Ok(()) } | ||
/// ``` | ||
pub fn from_iter<TL: ToListener<State>>(vec: impl IntoIterator<Item = TL>) -> io::Result<Self> { | ||
let mut multi = Self::new(); | ||
for listener in vec { | ||
multi.add(listener)?; | ||
} | ||
Ok(multi) | ||
} | ||
} | ||
|
||
impl<State: Send + Sync + 'static> Listener<State> for MultiListener<State> { | ||
fn connect<'a>(&'a mut self) -> BoxFuture<'a, io::Result<()>> { | ||
Box::pin(async move { | ||
for listener in self.0.iter_mut() { | ||
listener.connect().await?; | ||
} | ||
Ok(()) | ||
}) | ||
} | ||
|
||
fn listen<'a>(&'a self, app: Server<State>) -> BoxFuture<'a, io::Result<()>> { | ||
let mut fut: Option<BoxFuture<'a, io::Result<()>>> = None; | ||
|
||
for listener in self.0.iter() { | ||
let app = app.clone(); | ||
let listened = listener.listen(app); | ||
if let Some(f) = fut { | ||
fut = Some(Box::pin(f.race(listened))); | ||
} else { | ||
fut = Some(Box::pin(listened)); | ||
} | ||
} | ||
|
||
fut.expect("at least one listener must be provided to a MultiListener") | ||
} | ||
} | ||
|
||
impl<State> Debug for MultiListener<State> { | ||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { | ||
write!(f, "{:?}", self.0) | ||
} | ||
} | ||
|
||
impl<State> Display for MultiListener<State> { | ||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { | ||
let string = self | ||
.0 | ||
.iter() | ||
.map(|l| l.to_string()) | ||
.collect::<Vec<_>>() | ||
.join(", "); | ||
|
||
writeln!(f, "{}", string) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
#[cfg(unix)] | ||
use super::UnixListener; | ||
use super::{Listener, TcpListener}; | ||
use crate::{utils::BoxFuture, Server}; | ||
use async_std::io; | ||
use std::fmt::{self, Display, Formatter}; | ||
|
||
#[derive(Debug)] | ||
pub enum ParsedListener { | ||
#[cfg(unix)] | ||
Unix(UnixListener), | ||
Tcp(TcpListener), | ||
} | ||
|
||
impl Display for ParsedListener { | ||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { | ||
match self { | ||
#[cfg(unix)] | ||
Self::Unix(u) => write!(f, "{}", u), | ||
Self::Tcp(t) => write!(f, "{}", t), | ||
} | ||
} | ||
} | ||
|
||
impl<State: Send + Sync + 'static> Listener<State> for ParsedListener { | ||
fn connect<'a>(&'a mut self) -> BoxFuture<'a, io::Result<()>> { | ||
match self { | ||
#[cfg(unix)] | ||
Self::Unix(u) => Listener::<State>::connect(u), | ||
Self::Tcp(t) => Listener::<State>::connect(t), | ||
} | ||
} | ||
|
||
fn listen<'a>(&'a self, app: Server<State>) -> BoxFuture<'a, io::Result<()>> { | ||
match self { | ||
#[cfg(unix)] | ||
Self::Unix(u) => u.listen(app), | ||
Self::Tcp(t) => t.listen(app), | ||
} | ||
} | ||
} |
Oops, something went wrong.