├── tower-web-macros ├── src │ ├── derive │ │ ├── test │ │ │ ├── extract.rs │ │ │ └── response.rs │ │ └── test.rs │ ├── resource │ │ ├── test.rs │ │ ├── test │ │ │ └── parse.rs │ │ ├── catch.rs │ │ ├── arg.rs │ │ ├── route.rs │ │ └── ty_tree.rs │ ├── header.rs │ ├── resource.rs │ ├── derive.rs │ └── lib.rs ├── README.md ├── .gitignore ├── Cargo.toml └── LICENSE ├── examples ├── async-await │ ├── rust-toolchain │ ├── .cargo │ │ └── config │ ├── Cargo.toml │ └── src │ │ └── hyper.rs ├── rustls │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── catch.rs ├── derive_extract.rs ├── middleware.rs ├── static_file.rs ├── README.md ├── html_handlebars.rs └── json.rs ├── tests ├── templates │ └── hbs │ │ ├── bar.hbs │ │ └── foo.hbs ├── serializer.rs ├── multi_resource.rs ├── methods.rs ├── empty.rs ├── deflate_stream.rs ├── catch.rs ├── resource.rs ├── params.rs ├── content_type.rs ├── html_handlebars.rs └── support.rs ├── .gitignore ├── test_suite ├── tests │ └── derive_rsponse.rs └── Cargo.toml ├── templates └── examples │ └── hello_world.hbs ├── src ├── util │ ├── sealed.rs │ ├── chain.rs │ ├── never.rs │ ├── buf_stream │ │ ├── bytes.rs │ │ ├── file.rs │ │ ├── either.rs │ │ ├── std.rs │ │ ├── empty.rs │ │ ├── str.rs │ │ ├── chain.rs │ │ ├── collect.rs │ │ ├── from.rs │ │ ├── size_hint.rs │ │ ├── buf_stream.rs │ │ └── deflate.rs │ ├── mime_types.rs │ ├── buf_stream.rs │ ├── http.rs │ └── http │ │ ├── future.rs │ │ ├── middleware.rs │ │ ├── new_service.rs │ │ └── service.rs ├── middleware │ ├── deflate.rs │ ├── log.rs │ ├── cors.rs │ ├── cors │ │ └── middleware.rs │ ├── identity.rs │ ├── log │ │ ├── middleware.rs │ │ └── service.rs │ ├── chain.rs │ ├── deflate │ │ ├── middleware.rs │ │ └── service.rs │ └── middleware.rs ├── view.rs ├── error.rs ├── util.rs ├── response │ ├── option.rs │ ├── vec.rs │ ├── either.rs │ ├── file.rs │ ├── content_type.rs │ ├── str.rs │ ├── json.rs │ ├── serde.rs │ ├── serializer_context.rs │ ├── response.rs │ ├── serializer.rs │ ├── default_serializer.rs │ └── context.rs ├── service.rs ├── routing │ ├── captures.rs │ ├── builder.rs │ ├── route_match.rs │ ├── set.rs │ └── route.rs ├── routing.rs ├── codegen │ ├── async_await.rs │ └── callsite.rs ├── codegen.rs ├── error │ ├── never.rs │ └── map.rs ├── extract │ ├── http.rs │ ├── option.rs │ ├── context.rs │ ├── immediate.rs │ ├── error.rs │ ├── http_date_time.rs │ ├── num.rs │ ├── osstring.rs │ ├── pathbuf.rs │ ├── str.rs │ └── bytes.rs ├── middleware.rs ├── response.rs ├── net.rs ├── service │ ├── web.rs │ └── new_service.rs ├── config.rs └── extract.rs ├── .travis.yml ├── LICENSE ├── Cargo.toml ├── CHANGELOG.md └── README.md /tower-web-macros/src/derive/test/extract.rs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/async-await/rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly-2019-05-08 -------------------------------------------------------------------------------- /tower-web-macros/README.md: -------------------------------------------------------------------------------- 1 | # Tower Web Macros 2 | -------------------------------------------------------------------------------- /tower-web-macros/src/resource/test.rs: -------------------------------------------------------------------------------- 1 | mod parse; 2 | -------------------------------------------------------------------------------- /tower-web-macros/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /tower-web-macros/src/derive/test.rs: -------------------------------------------------------------------------------- 1 | mod extract; 2 | mod response; 3 | -------------------------------------------------------------------------------- /examples/async-await/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target-dir = "../../target" 3 | -------------------------------------------------------------------------------- /tests/templates/hbs/bar.hbs: -------------------------------------------------------------------------------- 1 | Bar - {{ title }} 2 | -------------------------------------------------------------------------------- /tests/templates/hbs/foo.hbs: -------------------------------------------------------------------------------- 1 | Foo - {{ title }} 2 | -------------------------------------------------------------------------------- /tests/serializer.rs: -------------------------------------------------------------------------------- 1 | // TODO: Cannot currently implement tests as `Serializer` is sealed 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | Cargo.lock 5 | **/local.cert.pem 6 | **/local.key.pem 7 | **/target/ 8 | -------------------------------------------------------------------------------- /test_suite/tests/derive_rsponse.rs: -------------------------------------------------------------------------------- 1 | use tower_web::Response; 2 | 3 | #[derive(Response)] 4 | pub struct Routes {} 5 | 6 | -------------------------------------------------------------------------------- /templates/examples/hello_world.hbs: -------------------------------------------------------------------------------- 1 | 2 | {{ title }} 3 | 4 |

Winning

5 | 6 | 7 | -------------------------------------------------------------------------------- /src/util/sealed.rs: -------------------------------------------------------------------------------- 1 | /// Private trait to this crate to prevent traits from being implemented in 2 | /// downstream crates. 3 | pub trait Sealed {} 4 | 5 | impl Sealed for () {} 6 | impl Sealed for (T, U) {} 7 | -------------------------------------------------------------------------------- /tower-web-macros/src/header.rs: -------------------------------------------------------------------------------- 1 | use http::header::HeaderName; 2 | 3 | pub(crate) fn arg_to_header_name(arg: &str) -> HeaderName { 4 | let header = arg.replace("_", "-").to_lowercase(); 5 | header.parse().unwrap() 6 | } 7 | -------------------------------------------------------------------------------- /src/middleware/deflate.rs: -------------------------------------------------------------------------------- 1 | //! Middleware that deflates HTTP response bodies 2 | 3 | mod middleware; 4 | mod service; 5 | 6 | pub use self::middleware::DeflateMiddleware; 7 | pub use self::service::{DeflateService, ResponseFuture}; 8 | -------------------------------------------------------------------------------- /src/middleware/log.rs: -------------------------------------------------------------------------------- 1 | //! Middleware that creates a log entry for each HTTP request. 2 | 3 | mod middleware; 4 | mod service; 5 | 6 | pub use self::middleware::LogMiddleware; 7 | pub use self::service::{LogService, ResponseFuture}; 8 | -------------------------------------------------------------------------------- /src/view.rs: -------------------------------------------------------------------------------- 1 | //! Render content using templates 2 | //! 3 | //! Currently, Handlebars is the only supported template engine. In time, the 4 | //! API will be opened up to third party crates. 5 | 6 | mod handlebars; 7 | 8 | pub use self::handlebars::Handlebars; 9 | -------------------------------------------------------------------------------- /test_suite/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test_suite" 3 | version = "0.1.0" 4 | authors = ["Carl Lerche "] 5 | edition = "2018" 6 | publish = false 7 | 8 | [workspace] 9 | 10 | [dependencies] 11 | tower-web = { path = "../" } 12 | serde = "1" 13 | -------------------------------------------------------------------------------- /examples/rustls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustls" 3 | version = "0.1.0" 4 | 5 | # Used to escape the parent workspace. 6 | [workspace] 7 | 8 | [dependencies] 9 | tokio = "0.1.10" 10 | tower-web = { path = "../..", features = ["rustls"] } 11 | tokio-rustls = "0.8.0" 12 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | //! Error types and traits. 2 | 3 | mod catch; 4 | mod error; 5 | mod map; 6 | mod never; 7 | 8 | pub use self::catch::{Catch, IntoCatch, DefaultCatch, FnCatch}; 9 | pub use self::error::{Error, Builder, ErrorKind}; 10 | pub use self::map::Map; 11 | pub(crate) use self::never::Never; 12 | -------------------------------------------------------------------------------- /src/middleware/cors.rs: -------------------------------------------------------------------------------- 1 | //! CORS middleware. 2 | 3 | mod builder; 4 | mod config; 5 | mod middleware; 6 | mod service; 7 | 8 | pub use self::builder::CorsBuilder; 9 | pub use self::config::AllowedOrigins; 10 | pub use self::middleware::CorsMiddleware; 11 | pub use self::service::CorsService; 12 | 13 | use self::config::{Config, CorsResource}; 14 | -------------------------------------------------------------------------------- /src/util/chain.rs: -------------------------------------------------------------------------------- 1 | /// Combine two values 2 | /// 3 | /// This trait is used to represent types that can be chained, including 4 | /// middleware and resource values. 5 | pub trait Chain { 6 | /// The combined type 7 | type Output; 8 | 9 | /// Combine `self` with `other`. 10 | fn chain(self, other: U) -> Self::Output; 11 | } 12 | -------------------------------------------------------------------------------- /src/util/never.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::fmt; 3 | 4 | #[derive(Debug)] 5 | pub enum Never {} 6 | 7 | impl fmt::Display for Never { 8 | fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { 9 | match *self {} 10 | } 11 | } 12 | 13 | impl Error for Never { 14 | fn description(&self) -> &str { 15 | match *self {} 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | //! Utility types and traits. 2 | 3 | pub mod buf_stream; 4 | pub mod http; 5 | 6 | #[doc(hidden)] 7 | pub mod tuple; 8 | 9 | mod chain; 10 | #[doc(hidden)] 11 | pub mod mime_types; 12 | mod never; 13 | mod sealed; 14 | 15 | pub use self::buf_stream::BufStream; 16 | pub use self::chain::Chain; 17 | 18 | pub(crate) use self::never::Never; 19 | pub(crate) use self::sealed::Sealed; 20 | -------------------------------------------------------------------------------- /examples/async-await/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples-async-await" 3 | edition = "2018" 4 | version = "0.1.0" 5 | authors = ["Carl Lerche "] 6 | 7 | # Used to escape the parent workspace. This crate uses edition 2018 and the 8 | # parent does not. 9 | [workspace] 10 | 11 | [[bin]] 12 | name = "hyper" 13 | path = "src/hyper.rs" 14 | 15 | [dependencies] 16 | tower-web = { version = "0.3.7", path = "../..", features = ["async-await-preview"] } 17 | tokio = "0.1.10" 18 | hyper = "0.12.10" 19 | futures = "0.1.18" 20 | -------------------------------------------------------------------------------- /src/response/option.rs: -------------------------------------------------------------------------------- 1 | use super::{Context, Response, Serializer}; 2 | use http::status::StatusCode; 3 | 4 | use http; 5 | 6 | impl Response for Option { 7 | type Buf = T::Buf; 8 | type Body = T::Body; 9 | 10 | fn into_http(self, context: &Context<'_, S>) -> Result, crate::Error> { 11 | match self { 12 | Some(inner) => Response::into_http(inner, context), 13 | None => Err(StatusCode::NOT_FOUND.into()), 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/response/vec.rs: -------------------------------------------------------------------------------- 1 | use crate::response::{Response, Serializer, Context, SerdeResponse}; 2 | 3 | use http; 4 | use serde; 5 | 6 | impl Response for Vec 7 | where 8 | T: serde::Serialize, 9 | { 10 | type Buf = as Response>::Buf; 11 | type Body = as Response>::Body; 12 | 13 | fn into_http(self, context: &Context<'_, S>) -> Result, crate::Error> 14 | where 15 | S: Serializer, 16 | { 17 | Response::into_http(SerdeResponse::new(self), context) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/util/buf_stream/bytes.rs: -------------------------------------------------------------------------------- 1 | use super::BufStream; 2 | 3 | use bytes::Bytes; 4 | use futures::Poll; 5 | 6 | use std::io; 7 | 8 | impl BufStream for Bytes { 9 | type Item = io::Cursor; 10 | type Error = (); 11 | 12 | fn poll(&mut self) -> Poll, Self::Error> { 13 | use std::mem; 14 | 15 | if self.is_empty() { 16 | return Ok(None.into()); 17 | } 18 | 19 | let bytes = mem::replace(self, Bytes::new()); 20 | let buf = io::Cursor::new(bytes); 21 | 22 | Ok(Some(buf).into()) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/service.rs: -------------------------------------------------------------------------------- 1 | //! Define the web service as a set of routes, resources, middlewares, serializers, ... 2 | //! 3 | //! [`ServiceBuilder`] combines all the various components (routes, resources, 4 | //! middlewares, serializers, deserializers, catch handlers, ...) and turns it 5 | //! into an HTTP service. 6 | //! 7 | //! [`ServiceBuilder`]: struct.ServiceBuilder.html 8 | 9 | mod builder; 10 | mod new_service; 11 | // TODO: Rename this `service`? 12 | mod web; 13 | 14 | pub use self::builder::ServiceBuilder; 15 | pub use self::new_service::NewWebService; 16 | pub use self::web::WebService; 17 | -------------------------------------------------------------------------------- /tower-web-macros/src/resource/test/parse.rs: -------------------------------------------------------------------------------- 1 | use quote::quote; 2 | 3 | macro_rules! expand { 4 | ($($tt:tt)*) => {{ 5 | crate::resource::expand_derive_resource(quote!($($tt)*)) 6 | }} 7 | } 8 | 9 | #[test] 10 | #[should_panic(expected = "duplicate routes with method")] 11 | fn duplicate_routes(){ 12 | expand! { 13 | impl Test{ 14 | #[get("/foo")] 15 | #[content_type("plain")] 16 | fn foo(&self){} 17 | 18 | #[get("/foo")] 19 | #[content_type("plain")] 20 | fn bar(&self){} 21 | } 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /src/routing/captures.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub(crate) struct Captures { 3 | /// Captures extracted from the request 4 | captures: Vec<(usize, usize)>, 5 | } 6 | 7 | impl Captures { 8 | pub(crate) fn new(captures: Vec<(usize, usize)>) -> Captures { 9 | Captures { captures } 10 | } 11 | 12 | #[cfg(test)] 13 | pub(crate) fn len(&self) -> usize { 14 | self.captures.len() 15 | } 16 | 17 | /// Get a capture 18 | pub fn get<'a>(&self, index: usize, src: &'a str) -> &'a str { 19 | let (pos, len) = self.captures[index]; 20 | &src[pos..(pos + len)] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tower-web-macros/src/resource.rs: -------------------------------------------------------------------------------- 1 | mod arg; 2 | mod attr; 3 | mod catch; 4 | mod parse; 5 | mod signature; 6 | mod resource; 7 | mod route; 8 | mod ty_tree; 9 | #[cfg(test)] 10 | mod test; 11 | 12 | use self::arg::Arg; 13 | use self::attr::Attributes; 14 | use self::catch::Catch; 15 | use self::parse::*; 16 | use self::signature::Signature; 17 | use self::resource::*; 18 | use self::route::*; 19 | use self::ty_tree::TyTree; 20 | 21 | use proc_macro2::TokenStream; 22 | 23 | /// Implement a Web Service 24 | pub fn expand_derive_resource(input: TokenStream) -> TokenStream { 25 | Parse::parse(input) 26 | .generate() 27 | } 28 | -------------------------------------------------------------------------------- /tower-web-macros/src/derive.rs: -------------------------------------------------------------------------------- 1 | mod attr; 2 | mod extract; 3 | mod response; 4 | #[cfg(test)] 5 | mod test; 6 | 7 | use self::attr::Attribute; 8 | use self::extract::Extract; 9 | use self::response::Response; 10 | 11 | use proc_macro2::TokenStream; 12 | use syn::DeriveInput; 13 | 14 | pub fn expand_derive_extract(input: DeriveInput) -> Result { 15 | Extract::from_ast(input) 16 | .and_then(|extract| extract.gen()) 17 | } 18 | 19 | pub fn expand_derive_response(input: DeriveInput) -> Result { 20 | Response::from_ast(input) 21 | .and_then(|response| response.gen()) 22 | } 23 | -------------------------------------------------------------------------------- /src/util/mime_types.rs: -------------------------------------------------------------------------------- 1 | use bytes::BytesMut; 2 | use http::header::HeaderValue; 3 | use mime_guess; 4 | use std::collections::HashMap; 5 | use lazy_static::lazy_static; 6 | 7 | lazy_static! { 8 | pub static ref BY_EXTENSION: HashMap<&'static str, HeaderValue> = { 9 | let mut map = HashMap::new(); 10 | if let Some(extensions) = mime_guess::get_extensions("*", "*") { 11 | for extension in extensions { 12 | map.insert(*extension, HeaderValue::from_shared(BytesMut::from(format!("{}", mime_guess::get_mime_type(extension)).into_bytes()).freeze()).unwrap()); 13 | } 14 | } 15 | map 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /src/routing.rs: -------------------------------------------------------------------------------- 1 | //! Map HTTP requests to Resource methods. 2 | //! 3 | //! Currently, this module is intended to be used by the `impl_web!` macro and 4 | //! not the end user. 5 | 6 | mod builder; 7 | mod captures; 8 | mod path; 9 | mod resource; 10 | mod route; 11 | mod route_match; 12 | mod service; 13 | mod set; 14 | 15 | pub use self::builder::Builder; 16 | pub use self::resource::{Resource, ResourceFuture, IntoResource, Unit}; 17 | pub use self::route::Route; 18 | pub use self::route_match::RouteMatch; 19 | pub use self::service::RoutedService; 20 | pub use self::set::RouteSet; 21 | 22 | pub(crate) use self::captures::Captures; 23 | pub(crate) use self::path::Path; 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | 4 | env: 5 | - PKGS='-p tower-web -p tower-web-macros' 6 | 7 | matrix: 8 | include: 9 | - rust: stable 10 | - rust: nightly 11 | # minimum rustc version 12 | - rust: 1.36.0 13 | script: cargo build 14 | 15 | allow_failures: 16 | - rust: nightly 17 | 18 | script: 19 | - cargo check --no-default-features $PKGS 20 | - cargo test --lib --no-default-features $PKGS 21 | - cargo test --tests --no-default-features $PKGS 22 | - cargo test $PKGS 23 | - if [ "$TRAVIS_RUST_VERSION" == "stable" ]; then cd test_suite && cargo test; fi 24 | 25 | notifications: 26 | email: 27 | on_success: never 28 | -------------------------------------------------------------------------------- /tower-web-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tower-web-macros" 3 | version = "0.3.5" 4 | authors = ["Carl Lerche "] 5 | readme = "README.md" 6 | license = "MIT" 7 | repository = "https://github.com/carllerche/tower-web" 8 | homepage = "https://github.com/carllerche/tower-web" 9 | documentation = "http://rust-doc.s3-website-us-east-1.amazonaws.com/tower-web/v0.3.5/tower_web/" 10 | description = """ 11 | Macros for tower-web 12 | """ 13 | edition = "2018" 14 | 15 | [lib] 16 | proc-macro = true 17 | 18 | [dependencies] 19 | http = "0.1.7" 20 | proc-macro2 = "1.0.6" 21 | quote = "1.0.2" 22 | syn = { version = "1.0.11", features = ["full", "fold", "extra-traits"] } 23 | -------------------------------------------------------------------------------- /src/util/buf_stream/file.rs: -------------------------------------------------------------------------------- 1 | use super::BufStream; 2 | 3 | use bytes::BytesMut; 4 | use futures::{Async, Poll, try_ready}; 5 | use tokio_fs::File; 6 | use tokio_io::AsyncRead; 7 | 8 | use std::io; 9 | 10 | impl BufStream for File { 11 | type Item = io::Cursor; 12 | type Error = io::Error; 13 | 14 | fn poll(&mut self) -> Poll, Self::Error> { 15 | let mut v = BytesMut::with_capacity(8 * 1024); 16 | 17 | let len = try_ready!(self.read_buf(&mut v)); 18 | 19 | if len == 0 { 20 | Ok(Async::Ready(None)) 21 | } else { 22 | Ok(Async::Ready(Some(io::Cursor::new(v)))) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/codegen/async_await.rs: -------------------------------------------------------------------------------- 1 | use futures::Future; 2 | 3 | use std::future::Future as StdFuture; 4 | 5 | /// Converts a `std::future::Future` to a boxed stable future. 6 | /// 7 | /// This bridges async/await with stable futures. 8 | pub fn async_to_box_future_send( 9 | future: T, 10 | ) -> Box + Send> 11 | where 12 | T: StdFuture + Send + 'static, 13 | { 14 | use tokio_async_await::compat::backward; 15 | 16 | let future = backward::Compat::new(map_ok(future)); 17 | Box::new(future) 18 | } 19 | 20 | async fn map_ok(future: T) -> Result 21 | where 22 | T: StdFuture, 23 | { 24 | Ok(await!(future)) 25 | } 26 | -------------------------------------------------------------------------------- /src/util/buf_stream/either.rs: -------------------------------------------------------------------------------- 1 | use super::{BufStream, SizeHint}; 2 | use futures::{future::Either, Poll}; 3 | 4 | impl BufStream for Either 5 | where 6 | A: BufStream, 7 | B: BufStream, 8 | { 9 | type Item = A::Item; 10 | type Error = A::Error; 11 | 12 | fn poll(&mut self) -> Poll, Self::Error> { 13 | match self { 14 | Either::A(a) => a.poll(), 15 | Either::B(b) => b.poll(), 16 | } 17 | } 18 | 19 | fn size_hint(&self) -> SizeHint { 20 | match self { 21 | Either::A(a) => a.size_hint(), 22 | Either::B(b) => b.size_hint(), 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/codegen.rs: -------------------------------------------------------------------------------- 1 | #![doc(hidden)] 2 | 3 | mod callsite; 4 | pub use self::callsite::CallSite; 5 | pub(crate) use self::callsite::Source; 6 | 7 | pub mod bytes { 8 | //! Types provided by the `bytes` crate 9 | 10 | pub use bytes::*; 11 | } 12 | 13 | pub mod tower { 14 | //! Types provided by the `tower` crate 15 | 16 | pub use tower_service::Service; 17 | } 18 | 19 | pub mod http { 20 | //! Types provided by the `http` crate. 21 | pub use http::*; 22 | } 23 | 24 | pub mod futures { 25 | //! Types provided by the `futures` crate. 26 | pub use futures::*; 27 | } 28 | 29 | pub mod serde { 30 | pub use serde::*; 31 | } 32 | 33 | #[cfg(feature = "async-await-preview")] 34 | pub mod async_await; 35 | -------------------------------------------------------------------------------- /src/response/either.rs: -------------------------------------------------------------------------------- 1 | use super::{Context, Response, Serializer}; 2 | use futures::future::Either; 3 | use http; 4 | use crate::util::tuple::Either2; 5 | use crate::util::BufStream; 6 | 7 | impl Response for Either 8 | where 9 | A: Response, 10 | B: Response, 11 | { 12 | type Buf = ::Item; 13 | type Body = Either2<::Body, ::Body>; 14 | 15 | fn into_http(self, context: &Context<'_, S>) -> Result, crate::Error> { 16 | match self { 17 | Either::A(a) => Either2::A::(a).into_http(context), 18 | Either::B(b) => Either2::B::(b).into_http(context), 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/util/buf_stream/std.rs: -------------------------------------------------------------------------------- 1 | use crate::util::BufStream; 2 | 3 | use bytes::Buf; 4 | use futures::{Poll, Stream}; 5 | 6 | /// Wraps a `futures::Stream` that yields `Buf` values and provides a 7 | /// `BufStream` implementation. 8 | #[derive(Debug)] 9 | pub struct StdStream(T); 10 | 11 | impl StdStream { 12 | /// Create a new `StdStream` containing `stream`. 13 | pub fn new(stream: T) -> StdStream { 14 | StdStream(stream) 15 | } 16 | } 17 | 18 | impl BufStream for StdStream 19 | where 20 | T: Stream, 21 | T::Item: Buf, 22 | { 23 | type Item = T::Item; 24 | type Error = T::Error; 25 | 26 | fn poll(&mut self) -> Poll, Self::Error> { 27 | self.0.poll() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/error/never.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | 3 | use std::error; 4 | use std::fmt; 5 | 6 | // A crate-private type until we can use !. 7 | // 8 | // Being crate-private, we should be able to swap the type out in a 9 | // backwards compatible way. 10 | pub enum Never {} 11 | 12 | impl From for Error { 13 | fn from(never: Never) -> Error { 14 | match never {} 15 | } 16 | } 17 | 18 | impl fmt::Debug for Never { 19 | fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { 20 | match *self {} 21 | } 22 | } 23 | 24 | impl fmt::Display for Never { 25 | fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { 26 | match *self {} 27 | } 28 | } 29 | 30 | impl error::Error for Never { 31 | fn description(&self) -> &str { 32 | match *self {} 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/catch.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate tower_web; 3 | 4 | use futures::{Future, IntoFuture}; 5 | use tower_web::ServiceBuilder; 6 | 7 | use std::io; 8 | 9 | #[derive(Clone, Debug)] 10 | struct Buggy; 11 | 12 | impl_web! { 13 | impl Buggy { 14 | #[get("/")] 15 | fn index(&self) -> impl Future { 16 | Err(()).into_future() 17 | } 18 | 19 | #[catch] 20 | fn catch_error(&self) -> impl Future { 21 | Ok("hello".to_string()).into_future() 22 | } 23 | } 24 | } 25 | 26 | pub fn main() { 27 | let addr = "127.0.0.1:8080".parse().expect("Invalid address"); 28 | println!("Listening on http://{}", addr); 29 | 30 | ServiceBuilder::new() 31 | .resource(Buggy) 32 | .run(&addr) 33 | .unwrap(); 34 | } 35 | -------------------------------------------------------------------------------- /src/routing/builder.rs: -------------------------------------------------------------------------------- 1 | use super::{Route, RouteSet}; 2 | 3 | use std::mem; 4 | 5 | /// Build a set of routes 6 | #[derive(Debug)] 7 | pub struct Builder { 8 | routes: RouteSet, 9 | } 10 | 11 | impl Builder { 12 | /// Create a new `Builder` instance 13 | pub fn new() -> Self { 14 | Builder { 15 | routes: RouteSet::new(), 16 | } 17 | } 18 | 19 | /// Insert a route into the route set 20 | pub fn insert(&mut self, route: Route) -> &mut Self { 21 | self.routes.insert(route); 22 | self 23 | } 24 | 25 | pub(crate) fn insert_all(&mut self, set: RouteSet) -> &mut Self { 26 | self.routes.insert_all(set); 27 | self 28 | } 29 | 30 | /// Finalize the route set 31 | pub fn build(&mut self) -> RouteSet { 32 | mem::replace(&mut self.routes, RouteSet::new()) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/response/file.rs: -------------------------------------------------------------------------------- 1 | use super::{Context, Response, Serializer}; 2 | use crate::error; 3 | 4 | use bytes::BytesMut; 5 | use http::{self, header}; 6 | use tokio_fs::File; 7 | 8 | use std::io; 9 | 10 | const OCTET_STREAM: &'static str = "application/octet-stream"; 11 | 12 | impl Response for File { 13 | type Buf = io::Cursor; 14 | type Body = error::Map; 15 | 16 | fn into_http(self, context: &Context<'_, S>) -> Result, crate::Error> { 17 | let content_type = context.content_type_header() 18 | .map(|header| header.clone()) 19 | .unwrap_or_else(|| header::HeaderValue::from_static(OCTET_STREAM)); 20 | 21 | Ok(http::Response::builder() 22 | .status(200) 23 | .header(header::CONTENT_TYPE, content_type) 24 | .body(error::Map::new(self)) 25 | .unwrap()) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/extract/http.rs: -------------------------------------------------------------------------------- 1 | //! Permit the extraction of the underlying `http::Request` without the body. 2 | 3 | use http::Request; 4 | 5 | use crate::extract::{Context, Error, Extract, Immediate}; 6 | use crate::util::BufStream; 7 | 8 | impl Extract for Request<()> { 9 | type Future = Immediate; 10 | 11 | fn extract(ctx: &Context<'_>) -> Self::Future { 12 | let request = Request::builder() 13 | .version(ctx.request().version()) 14 | .method(ctx.request().method()) 15 | .uri(ctx.request().uri()) 16 | .body(()) 17 | .map_err(|e| Error::invalid_argument(&e)) 18 | .map(|mut request| { 19 | request 20 | .headers_mut() 21 | .extend(ctx.request().headers().clone()); 22 | request 23 | }); 24 | 25 | Immediate::result(request) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/middleware/cors/middleware.rs: -------------------------------------------------------------------------------- 1 | use super::{Config, CorsService}; 2 | use crate::middleware::Middleware; 3 | 4 | use http; 5 | use crate::util::http::HttpService; 6 | 7 | use std::sync::Arc; 8 | 9 | /// Middleware providing an implementation of the CORS specification. 10 | #[derive(Debug)] 11 | pub struct CorsMiddleware { 12 | config: Arc, 13 | } 14 | 15 | impl CorsMiddleware { 16 | pub(super) fn new(config: Config) -> CorsMiddleware { 17 | let config = Arc::new(config); 18 | CorsMiddleware { config } 19 | } 20 | } 21 | 22 | impl Middleware for CorsMiddleware 23 | where 24 | S: HttpService, 25 | { 26 | type Request = http::Request; 27 | type Response = http::Response>; 28 | type Error = S::Error; 29 | type Service = CorsService; 30 | 31 | fn wrap(&self, service: S) -> Self::Service { 32 | CorsService::new(service, self.config.clone()) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/util/buf_stream.rs: -------------------------------------------------------------------------------- 1 | //! Asynchronous stream of bytes. 2 | //! 3 | //! This module contains the `BufStream` trait and a number of combinators for 4 | //! this trait. The trait is similar to `Stream` in the `futures` library, but 5 | //! instead of yielding arbitrary values, it only yields types that implement 6 | //! `Buf` (i.e, byte collections). 7 | //! 8 | //! Having a dedicated trait for this case enables greater functionality and 9 | //! ease of use. 10 | //! 11 | //! This module will eventually be moved into Tokio. 12 | 13 | mod buf_stream; 14 | mod bytes; 15 | mod chain; 16 | mod collect; 17 | pub mod deflate; 18 | mod either; 19 | mod empty; 20 | mod file; 21 | mod from; 22 | pub mod size_hint; 23 | mod std; 24 | mod str; 25 | 26 | pub use self::buf_stream::BufStream; 27 | pub use self::chain::Chain; 28 | pub use self::collect::Collect; 29 | pub use self::empty::{empty, Empty}; 30 | pub use self::from::FromBufStream; 31 | pub use self::size_hint::SizeHint; 32 | pub use self::std::StdStream; 33 | -------------------------------------------------------------------------------- /src/util/buf_stream/empty.rs: -------------------------------------------------------------------------------- 1 | use bytes::Buf; 2 | use futures::{Async, Poll}; 3 | use std::marker::PhantomData; 4 | use crate::util::buf_stream::BufStream; 5 | 6 | /// A `BufStream` that contains no data. 7 | #[derive(Debug, Copy, Clone)] 8 | pub struct Empty(PhantomData<(Item, Error)>); 9 | 10 | /// Create a new `Empty` instance. 11 | pub fn empty() -> Empty { 12 | Default::default() 13 | } 14 | 15 | impl Empty { 16 | /// Create a new `Empty` instance. 17 | pub fn new() -> Self { 18 | Default::default() 19 | } 20 | } 21 | 22 | impl Default for Empty { 23 | fn default() -> Self { 24 | Empty(Default::default()) 25 | } 26 | } 27 | 28 | impl BufStream for Empty 29 | where 30 | Item: Buf, 31 | { 32 | type Item = Item; 33 | type Error = Error; 34 | 35 | fn poll(&mut self) -> Poll, Self::Error> { 36 | Ok(Async::Ready(None)) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/util/buf_stream/str.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Never; 2 | use super::BufStream; 3 | 4 | use futures::Poll; 5 | 6 | use std::io; 7 | use std::mem; 8 | 9 | impl BufStream for String { 10 | type Item = io::Cursor>; 11 | type Error = Never; 12 | 13 | fn poll(&mut self) -> Poll, Self::Error> { 14 | if self.is_empty() { 15 | return Ok(None.into()); 16 | } 17 | 18 | let bytes = mem::replace(self, String::new()).into_bytes(); 19 | let buf = io::Cursor::new(bytes); 20 | 21 | Ok(Some(buf).into()) 22 | } 23 | } 24 | 25 | impl BufStream for &'static str { 26 | type Item = io::Cursor<&'static [u8]>; 27 | type Error = Never; 28 | 29 | fn poll(&mut self) -> Poll, Self::Error> { 30 | if self.is_empty() { 31 | return Ok(None.into()); 32 | } 33 | 34 | let bytes = mem::replace(self, "").as_bytes(); 35 | let buf = io::Cursor::new(bytes); 36 | 37 | Ok(Some(buf).into()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/middleware/identity.rs: -------------------------------------------------------------------------------- 1 | use super::{Middleware, Chain}; 2 | 3 | use tower_service::Service; 4 | 5 | /// A no-op middleware. 6 | /// 7 | /// When wrapping a `Service`, the `Identity` middleware returns the provided 8 | /// service without modifying it. 9 | #[derive(Debug, Default, Clone)] 10 | pub struct Identity { 11 | _p: (), 12 | } 13 | 14 | impl Identity { 15 | /// Create a new `Identity` value 16 | pub fn new() -> Identity { 17 | Identity { _p: () } 18 | } 19 | } 20 | 21 | /// Decorates a `Service`, transforming either the request or the response. 22 | impl Middleware for Identity { 23 | type Request = S::Request; 24 | type Response = S::Response; 25 | type Error = S::Error; 26 | type Service = S; 27 | 28 | fn wrap(&self, inner: S) -> Self::Service { 29 | inner 30 | } 31 | } 32 | 33 | impl crate::util::Chain for Identity { 34 | type Output = Chain; 35 | 36 | fn chain(self, other: T) -> Self::Output { 37 | Chain::new(self, other) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/middleware/log/middleware.rs: -------------------------------------------------------------------------------- 1 | use super::LogService; 2 | use crate::middleware::Middleware; 3 | 4 | use http; 5 | use tower_service::Service; 6 | 7 | /// Decorate a service by logging all received requests. 8 | #[derive(Debug)] 9 | pub struct LogMiddleware { 10 | target: &'static str, 11 | } 12 | 13 | impl LogMiddleware { 14 | /// Create a new `LogMiddleware` instance configured to use `target`. 15 | pub fn new(target: &'static str) -> LogMiddleware { 16 | LogMiddleware { target } 17 | } 18 | } 19 | 20 | impl Middleware for LogMiddleware 21 | where S: Service, 22 | Response = http::Response>, 23 | S::Error: ::std::error::Error, 24 | { 25 | type Request = http::Request; 26 | type Response = http::Response; 27 | type Error = S::Error; 28 | type Service = LogService; 29 | 30 | fn wrap(&self, service: S) -> Self::Service { 31 | LogService::new(service, self.target) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tower-web-macros/src/resource/catch.rs: -------------------------------------------------------------------------------- 1 | use crate::resource::{Attributes, Signature}; 2 | 3 | use proc_macro2::TokenStream; 4 | use quote::quote; 5 | 6 | #[derive(Debug)] 7 | pub(crate) struct Catch { 8 | index: usize, 9 | 10 | sig: Signature, 11 | 12 | attributes: Attributes, 13 | } 14 | 15 | impl Catch { 16 | pub fn new(index: usize, sig: Signature, attributes: Attributes) -> Catch { 17 | // TODO: Handle args 18 | assert!(sig.args().is_empty(), "catch arguments unimplemented"); 19 | 20 | Catch { 21 | index, 22 | sig, 23 | attributes, 24 | } 25 | } 26 | 27 | /// The response future type 28 | pub fn future_ty(&self) -> TokenStream { 29 | self.sig.future_ty() 30 | } 31 | 32 | pub fn dispatch(&self) -> TokenStream { 33 | let args = self.sig.args().iter().map(|_arg| { 34 | panic!("unimplemented: catch handlers cannot take arguments"); 35 | }); 36 | 37 | self.sig.dispatch( 38 | quote!(self.inner), 39 | args) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/middleware.rs: -------------------------------------------------------------------------------- 1 | //! Middleware traits and implementations. 2 | //! 3 | //! A middleware decorates an service and provides additional functionality. 4 | //! This additional functionality may include, but is not limited to: 5 | //! 6 | //! * Rejecting the request. 7 | //! * Taking an action based on the request. 8 | //! * Mutating the request before passing it along to the application. 9 | //! * Mutating the response returned by the application. 10 | //! 11 | //! A middleware implements the [`Middleware`] trait. 12 | //! 13 | //! Currently, the following middleware implementations are provided: 14 | //! 15 | //! * [access logging][log] 16 | //! 17 | //! More will come. 18 | //! 19 | //! **Note**: This module will eventually be extracted out of `tower-web` into 20 | //! `tower` and `tower-http`. 21 | //! 22 | //! [`Middleware`]: trait.Middleware.html 23 | //! [log]: log/index.html 24 | 25 | pub mod cors; 26 | pub mod deflate; 27 | pub mod log; 28 | 29 | mod chain; 30 | mod identity; 31 | mod middleware; 32 | 33 | pub use self::chain::Chain; 34 | pub use self::identity::Identity; 35 | pub use self::middleware::Middleware; 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Carl Lerche 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /src/routing/route_match.rs: -------------------------------------------------------------------------------- 1 | use crate::config::Config; 2 | use crate::routing::Captures; 3 | 4 | use http::Request; 5 | 6 | /// Data captured from an HTTP request when it matches a route. 7 | /// 8 | /// Primarily, this stores the path captures. 9 | /// 10 | /// This type is not intended to be used directly. 11 | #[derive(Debug)] 12 | pub struct RouteMatch<'a> { 13 | /// The matched HTTP request head 14 | request: &'a Request<()>, 15 | 16 | /// Route captures 17 | captures: Captures, 18 | 19 | /// Config 20 | config: &'a Config, 21 | } 22 | 23 | impl<'a> RouteMatch<'a> { 24 | /// Create a new `RouteMatch` 25 | pub(crate) fn new(request: &'a Request<()>, captures: Captures, config: &'a Config) -> Self { 26 | RouteMatch { 27 | request, 28 | captures, 29 | config, 30 | } 31 | } 32 | 33 | pub(crate) fn request(&self) -> &Request<()> { 34 | self.request 35 | } 36 | 37 | pub(crate) fn captures(&self) -> &Captures { 38 | &self.captures 39 | } 40 | 41 | pub(crate) fn config(&self) -> &Config { 42 | &self.config 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tower-web-macros/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Carl Lerche 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /src/util/http.rs: -------------------------------------------------------------------------------- 1 | //! HTTP service, middleware, and future utilities 2 | //! 3 | //! This module provides specialized versions of the various tower traits that 4 | //! are easier to work with in the context of HTTP requests and responses. 5 | //! 6 | //! These traits can be used as aliases of sorts. Ideally, Rust will add trait 7 | //! alias support in the language and these traits can go away. 8 | //! 9 | //! * [`HttpService`]: A `Service` of `http::Request` to `http::Response`. 10 | //! * [`HttpMiddleware`]: Middleware for `HttpService`. 11 | //! * [`HttpFuture`]: A future yielding an http::Response, i.e. an `HttpService` 12 | //! response future. 13 | //! 14 | //! These types will (probably) be moved into tower-http. 15 | //! 16 | //! [`HttpService`]: trait.HttpService.html 17 | //! [`HttpMiddleware`]: trait.HttpMiddleware.html 18 | //! [`HttpFuture`]: trait.HttpFuture.html 19 | 20 | mod future; 21 | mod middleware; 22 | mod new_service; 23 | mod service; 24 | 25 | pub use self::future::{HttpFuture, LiftFuture}; 26 | pub use self::middleware::HttpMiddleware; 27 | pub use self::new_service::NewHttpService; 28 | pub use self::service::{HttpService, LiftService}; 29 | 30 | pub(crate) use self::future::sealed::Sealed as SealedFuture; 31 | -------------------------------------------------------------------------------- /src/middleware/chain.rs: -------------------------------------------------------------------------------- 1 | use super::Middleware; 2 | 3 | use tower_service::Service; 4 | 5 | /// Two middlewares chained together. 6 | /// 7 | /// This type is produced by `Middleware::chain`. 8 | #[derive(Debug)] 9 | pub struct Chain 10 | { 11 | inner: Inner, 12 | outer: Outer, 13 | } 14 | 15 | impl Chain { 16 | /// Create a new `Chain`. 17 | pub fn new(inner: Inner, outer: Outer) -> Self { 18 | Chain { 19 | inner, 20 | outer, 21 | } 22 | } 23 | } 24 | 25 | impl Middleware for Chain 26 | where S: Service, 27 | Inner: Middleware, 28 | Outer: Middleware, 29 | { 30 | type Request = Outer::Request; 31 | type Response = Outer::Response; 32 | type Error = Outer::Error; 33 | type Service = Outer::Service; 34 | 35 | fn wrap(&self, service: S) -> Self::Service { 36 | self.outer.wrap( 37 | self.inner.wrap(service)) 38 | } 39 | } 40 | 41 | impl crate::util::Chain for Chain { 42 | type Output = Chain; 43 | 44 | fn chain(self, other: T) -> Self::Output { 45 | Chain::new(self, other) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/middleware/deflate/middleware.rs: -------------------------------------------------------------------------------- 1 | use super::DeflateService; 2 | use crate::middleware::Middleware; 3 | use crate::util::buf_stream::BufStream; 4 | use crate::util::buf_stream::deflate::CompressStream; 5 | 6 | use flate2::Compression; 7 | use http; 8 | use tower_service::Service; 9 | 10 | /// Deflate all response bodies 11 | #[derive(Debug)] 12 | pub struct DeflateMiddleware { 13 | level: Compression, 14 | } 15 | 16 | impl DeflateMiddleware { 17 | /// Create a new `DeflateMiddleware` instance 18 | pub fn new(level: Compression) -> DeflateMiddleware { 19 | DeflateMiddleware { level } 20 | } 21 | } 22 | 23 | impl Middleware for DeflateMiddleware 24 | where S: Service, 25 | Response = http::Response>, 26 | RequestBody: BufStream, 27 | ResponseBody: BufStream, 28 | S::Error: ::std::error::Error, 29 | { 30 | type Request = http::Request; 31 | type Response = http::Response>; 32 | type Error = S::Error; 33 | type Service = DeflateService; 34 | 35 | fn wrap(&self, service: S) -> Self::Service { 36 | DeflateService::new(service, self.level) 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/routing/set.rs: -------------------------------------------------------------------------------- 1 | use super::{Route, Captures}; 2 | 3 | use http::Request; 4 | 5 | /// A set of routes 6 | /// 7 | /// Matches an HTTP request with a route, which in turn maps to a resource 8 | /// method. 9 | #[derive(Debug)] 10 | pub struct RouteSet { 11 | routes: Vec>, 12 | } 13 | 14 | // ===== impl RouteSet ===== 15 | 16 | impl RouteSet { 17 | /// Create a new, empty, `RouteSet`. 18 | pub fn new() -> RouteSet { 19 | RouteSet { routes: vec![] } 20 | } 21 | 22 | pub(crate) fn map(self, f: F) -> RouteSet 23 | where F: Fn(T) -> U, 24 | { 25 | RouteSet { 26 | routes: self.routes.into_iter().map(|r| r.map(&f)).collect() 27 | } 28 | } 29 | 30 | pub(crate) fn insert(&mut self, route: Route) { 31 | self.routes.push(route); 32 | } 33 | 34 | pub(crate) fn insert_all(&mut self, set: RouteSet) { 35 | self.routes.extend(set.routes); 36 | } 37 | } 38 | 39 | impl RouteSet 40 | where 41 | T: Clone, 42 | { 43 | /// Match a request against a route set 44 | pub(crate) fn test(&self, request: &Request<()>) -> Option<(T, Captures)> { 45 | self.routes 46 | .iter() 47 | .flat_map(|route| route.test(request)) 48 | .next() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/extract/option.rs: -------------------------------------------------------------------------------- 1 | //! Types used to extract `Option` values from an HTTP request. 2 | 3 | use crate::extract::{Extract, ExtractFuture, Error, Context}; 4 | use crate::util::BufStream; 5 | 6 | use futures::{Async, Poll}; 7 | 8 | /// Extract an `Option` value from an HTTP request. 9 | #[derive(Debug)] 10 | pub struct ExtractOptionFuture { 11 | inner: T, 12 | none: bool, 13 | } 14 | 15 | impl Extract for Option 16 | where T: Extract, 17 | { 18 | type Future = ExtractOptionFuture; 19 | 20 | fn extract(ctx: &Context<'_>) -> Self::Future { 21 | ExtractOptionFuture { 22 | inner: T::extract(ctx), 23 | none: false, 24 | } 25 | } 26 | } 27 | 28 | impl ExtractFuture for ExtractOptionFuture 29 | where T: ExtractFuture, 30 | { 31 | type Item = Option; 32 | 33 | fn poll(&mut self) -> Poll<(), Error> { 34 | match self.inner.poll() { 35 | Err(ref e) if e.is_missing_argument() => { 36 | self.none = true; 37 | Ok(Async::Ready(())) 38 | } 39 | res => res, 40 | } 41 | } 42 | 43 | fn extract(self) -> Self::Item { 44 | if self.none { 45 | None 46 | } else { 47 | Some(self.inner.extract()) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/response/content_type.rs: -------------------------------------------------------------------------------- 1 | use http::header::HeaderValue; 2 | 3 | /// Content type of a response 4 | /// 5 | /// Instances of `ContentType` are returned by [`Serializer::lookup`]. This type 6 | /// is not intended to be used by the end user besides using it as an argument 7 | /// to [`Context::new`]. 8 | /// 9 | /// [`Serializer::lookup`]: trait.Serializer.html#method.lookup 10 | /// [`Context::new`]: struct.Context.html 11 | #[derive(Debug)] 12 | pub struct ContentType { 13 | /// The HTTP header representing the content-type 14 | header: HeaderValue, 15 | 16 | /// Used by `Serializer` to match the content type with a specific 17 | /// serializer. 18 | format: T, 19 | } 20 | 21 | impl ContentType { 22 | pub(crate) fn new(header: HeaderValue, format: T) -> Self { 23 | ContentType { 24 | header, 25 | format, 26 | } 27 | } 28 | 29 | #[doc(hidden)] 30 | pub fn header(&self) -> &HeaderValue { 31 | &self.header 32 | } 33 | 34 | #[doc(hidden)] 35 | pub fn format(&self) -> &T { 36 | &self.format 37 | } 38 | 39 | pub(crate) fn map(self, f: F) -> ContentType 40 | where F: FnOnce(T) -> U 41 | { 42 | ContentType { 43 | header: self.header, 44 | format: f(self.format), 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/response/str.rs: -------------------------------------------------------------------------------- 1 | use super::{Context, Response, Serializer}; 2 | use crate::error; 3 | 4 | use http; 5 | use http::header::{self, HeaderValue}; 6 | 7 | use std::io; 8 | 9 | impl Response for String { 10 | type Buf = io::Cursor>; 11 | type Body = error::Map; 12 | 13 | fn into_http(self, context: &Context<'_, S>) -> Result, crate::Error> { 14 | respond(self, context) 15 | } 16 | } 17 | 18 | impl Response for &'static str { 19 | type Buf = io::Cursor<&'static [u8]>; 20 | type Body = error::Map<&'static str>; 21 | 22 | fn into_http(self, context: &Context<'_, S>) -> Result, crate::Error> { 23 | respond(self, context) 24 | } 25 | } 26 | 27 | fn respond(value: T, context: &Context<'_, S>) 28 | -> Result>, crate::Error> 29 | { 30 | let content_type = context.content_type_header() 31 | .map(|content_type| content_type.clone()) 32 | .unwrap_or_else(|| HeaderValue::from_static("text/plain")); 33 | 34 | let response = http::Response::builder() 35 | // Customize response 36 | .status(200) 37 | .header(header::CONTENT_TYPE, content_type) 38 | .body(error::Map::new(value)) 39 | .unwrap(); 40 | 41 | Ok(response) 42 | } 43 | -------------------------------------------------------------------------------- /src/codegen/callsite.rs: -------------------------------------------------------------------------------- 1 | use self::Source::*; 2 | 3 | use http::header::HeaderName; 4 | 5 | #[derive(Debug)] 6 | pub struct CallSite { 7 | /// Where to extract the argument when the type does not provide the 8 | /// information. 9 | source: Source, 10 | } 11 | 12 | #[derive(Debug, Clone)] 13 | pub(crate) enum Source { 14 | Capture(usize), 15 | Header(HeaderName), 16 | QueryString, 17 | Body, 18 | Unknown, 19 | } 20 | 21 | impl CallSite { 22 | pub fn new_capture(index: usize) -> CallSite { 23 | CallSite { source: Capture(index) } 24 | } 25 | 26 | pub fn new_header(name: &'static str) -> CallSite { 27 | CallSite { source: Header(HeaderName::from_static(name)) } 28 | } 29 | 30 | pub fn new_query_string() -> CallSite { 31 | CallSite { source: QueryString } 32 | } 33 | 34 | pub fn new_body() -> CallSite { 35 | CallSite { source: Body } 36 | } 37 | 38 | /// Cannot infer where to extract the argument based on the call site. 39 | pub fn new_unknown() -> CallSite { 40 | CallSite { source: Unknown } 41 | } 42 | 43 | pub(crate) fn source(&self) -> &Source { 44 | &self.source 45 | } 46 | 47 | pub(crate) fn requires_body(&self) -> bool { 48 | match self.source() { 49 | Body => true, 50 | _ => false, 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/util/buf_stream/chain.rs: -------------------------------------------------------------------------------- 1 | use super::{BufStream, SizeHint}; 2 | use crate::util::tuple::Either2 as Either; 3 | 4 | use futures::{Poll, try_ready}; 5 | 6 | /// A buf stream that sequences two buf streams together. 7 | /// 8 | /// `Chain` values are produced by the `chain` function on `BufStream`. 9 | #[derive(Debug)] 10 | pub struct Chain { 11 | left: Option, 12 | right: U, 13 | } 14 | 15 | impl Chain { 16 | pub(crate) fn new(left: T, right: U) -> Chain { 17 | Chain { 18 | left: Some(left), 19 | right, 20 | } 21 | } 22 | } 23 | 24 | impl BufStream for Chain 25 | where 26 | T: BufStream, 27 | U: BufStream, 28 | { 29 | type Item = Either; 30 | type Error = T::Error; 31 | 32 | fn poll(&mut self) -> Poll, Self::Error> { 33 | if let Some(ref mut stream) = self.left { 34 | let res = try_ready!(stream.poll()); 35 | 36 | if res.is_some() { 37 | return Ok(res.map(Either::A).into()); 38 | } 39 | } 40 | 41 | self.left = None; 42 | 43 | let res = try_ready!(self.right.poll()); 44 | Ok(res.map(Either::B).into()) 45 | } 46 | 47 | fn size_hint(&self) -> SizeHint { 48 | // TODO: Implement 49 | SizeHint::default() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/response/json.rs: -------------------------------------------------------------------------------- 1 | use crate::error; 2 | use crate::response::{Context, Response, Serializer}; 3 | use crate::util::BufStream; 4 | 5 | use bytes::Bytes; 6 | use http; 7 | use http::header::{self, HeaderValue}; 8 | use serde_json::{self, Value}; 9 | 10 | impl Response for Value { 11 | type Buf = ::Item; 12 | type Body = error::Map; 13 | 14 | fn into_http(self, context: &Context<'_, S>) -> Result, crate::Error> 15 | where 16 | S: Serializer, 17 | { 18 | // TODO: Improve error handling 19 | let body = serde_json::to_vec(&self).unwrap(); 20 | 21 | // TODO: Improve and handle errors 22 | let body = error::Map::new(Bytes::from(body)); 23 | 24 | let mut response = http::Response::builder() 25 | // Customize response 26 | .status(200) 27 | .body(body) 28 | .unwrap(); 29 | 30 | response 31 | .headers_mut() 32 | .entry(header::CONTENT_TYPE) 33 | .unwrap() 34 | .or_insert_with(|| { 35 | context.content_type_header() 36 | .map(|content_type| content_type.clone()) 37 | .unwrap_or_else(|| { 38 | HeaderValue::from_static("application/json") 39 | }) 40 | }); 41 | 42 | Ok(response) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tower-web-macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "512"] 2 | #[deny(rust_2018_idioms)] 3 | 4 | extern crate proc_macro; 5 | use proc_macro::TokenStream; 6 | 7 | use proc_macro2; 8 | use syn; 9 | use quote::quote; 10 | 11 | mod derive; 12 | mod header; 13 | mod resource; 14 | 15 | const MAX_VARIANTS: usize = 12; 16 | 17 | /// Implement a Web Service 18 | #[proc_macro] 19 | pub fn derive_resource(input: TokenStream) -> TokenStream { 20 | // Parse the input to a proc_macro2 token stream 21 | let input = syn::parse_macro_input!(input); 22 | 23 | // Generate the output 24 | resource::expand_derive_resource(input) 25 | // Convert it back to a proc_macro token stream 26 | .into() 27 | } 28 | 29 | #[proc_macro_derive(Extract, attributes(web, serde))] 30 | pub fn derive_extract(input: TokenStream) -> TokenStream { 31 | let input = syn::parse_macro_input!(input); 32 | 33 | derive::expand_derive_extract(input) 34 | .unwrap_or_else(compile_error) 35 | .into() 36 | } 37 | 38 | #[proc_macro_derive(Response, attributes(web))] 39 | pub fn derive_response(input: TokenStream) -> TokenStream { 40 | let input = syn::parse_macro_input!(input); 41 | 42 | derive::expand_derive_response(input) 43 | .unwrap_or_else(compile_error) 44 | .into() 45 | } 46 | 47 | fn compile_error(message: String) -> proc_macro2::TokenStream { 48 | quote! { 49 | compile_error!(#message); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/multi_resource.rs: -------------------------------------------------------------------------------- 1 | use tower_web::impl_web; 2 | 3 | mod support; 4 | use crate::support::*; 5 | 6 | #[derive(Clone, Debug)] 7 | struct One; 8 | 9 | #[derive(Clone, Debug)] 10 | struct Two; 11 | 12 | #[derive(Clone, Debug)] 13 | struct Three; 14 | 15 | #[derive(Clone, Debug)] 16 | struct Four; 17 | 18 | impl_web! { 19 | impl One { 20 | #[get("/one")] 21 | fn action(&self) -> Result<&'static str, ()> { 22 | Ok("/one") 23 | } 24 | } 25 | 26 | impl Two { 27 | #[get("/two")] 28 | fn action(&self) -> Result<&'static str, ()> { 29 | Ok("/two") 30 | } 31 | } 32 | 33 | impl Three { 34 | #[get("/three")] 35 | fn action(&self) -> Result<&'static str, ()> { 36 | Ok("/three") 37 | } 38 | } 39 | 40 | impl Four { 41 | #[get("/four")] 42 | fn action(&self) -> Result<&'static str, ()> { 43 | Ok("/four") 44 | } 45 | } 46 | } 47 | 48 | #[test] 49 | fn multi_resource() { 50 | use tower_service::NewService; 51 | 52 | let mut web = ::tower_web::ServiceBuilder::new() 53 | .resource(One) 54 | .resource(Two) 55 | .resource(Three) 56 | .resource(Four) 57 | .build_new_service() 58 | .new_service() 59 | .wait().unwrap(); 60 | 61 | for path in &["/one", "/two", "/three", "/four"] { 62 | let response = web.call_unwrap(get!(*path)); 63 | assert_ok!(response); 64 | assert_body!(response, *path); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/util/buf_stream/collect.rs: -------------------------------------------------------------------------------- 1 | use super::{BufStream, FromBufStream}; 2 | 3 | use futures::{Future, Poll, try_ready}; 4 | 5 | /// Consumes a buf stream, collecting the data into a single byte container. 6 | /// 7 | /// `Collect` values are produced by `BufStream::collect`. 8 | #[derive(Debug)] 9 | pub struct Collect { 10 | stream: T, 11 | builder: Option, 12 | } 13 | 14 | impl Collect 15 | where 16 | T: BufStream, 17 | U: FromBufStream, 18 | { 19 | pub(crate) fn new(stream: T) -> Collect { 20 | let builder = U::builder(&stream.size_hint()); 21 | 22 | Collect { 23 | stream, 24 | builder: Some(builder), 25 | } 26 | } 27 | } 28 | 29 | impl Future for Collect 30 | where 31 | T: BufStream, 32 | U: FromBufStream, 33 | { 34 | type Item = U; 35 | type Error = T::Error; 36 | 37 | fn poll(&mut self) -> Poll { 38 | loop { 39 | match try_ready!(self.stream.poll()) { 40 | Some(mut buf) => { 41 | let builder = self.builder.as_mut().expect("cannot poll after done"); 42 | 43 | U::extend(builder, &mut buf); 44 | } 45 | None => { 46 | let builder = self.builder.take().expect("cannot poll after done"); 47 | let value = U::build(builder); 48 | return Ok(value.into()); 49 | } 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/derive_extract.rs: -------------------------------------------------------------------------------- 1 | /// Web service with a custom type argument. 2 | /// 3 | /// ## Overview 4 | /// 5 | /// Custom types can be used as arguments to web handler functions. 6 | /// `[derive(Extract)]`. 7 | /// 8 | /// ## Usage 9 | /// 10 | /// Run the example: 11 | /// 12 | /// cargo run --example derive_extract 13 | /// 14 | /// Then send a request: 15 | /// 16 | /// curl -v http://localhost:8080/query-string?foo=123 17 | 18 | #[macro_use] 19 | extern crate tower_web; 20 | 21 | use tower_web::ServiceBuilder; 22 | 23 | /// This type will be the web service implementation. 24 | #[derive(Clone, Debug)] 25 | pub struct ArgResource; 26 | 27 | #[derive(Debug, Extract)] 28 | struct Foo { 29 | /// A `foo` component must be provided and it must be a numeric type. 30 | foo: u32, 31 | 32 | /// A `bar` component is always optional 33 | bar: Option, 34 | } 35 | 36 | impl_web! { 37 | impl ArgResource { 38 | 39 | // By convention, arguments named `query_string` will be populated using 40 | // the HTTP request query string. 41 | #[get("/query-string")] 42 | fn hello_query_string(&self, query_string: Option) -> Result { 43 | Ok(format!("We received the query {:?}", query_string)) 44 | } 45 | } 46 | } 47 | 48 | pub fn main() { 49 | let addr = "127.0.0.1:8080".parse().expect("Invalid address"); 50 | println!("Listening on http://{}", addr); 51 | 52 | ServiceBuilder::new() 53 | .resource(ArgResource) 54 | .run(&addr) 55 | .unwrap(); 56 | } 57 | -------------------------------------------------------------------------------- /src/extract/context.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::CallSite; 2 | use crate::config::Config; 3 | use crate::routing::{Captures, RouteMatch}; 4 | 5 | use http::Request; 6 | 7 | /// Context available when extracting data from the HTTP request. 8 | /// 9 | /// Primarily, `Context` includes a reference to the HTTP request in question. 10 | #[derive(Debug)] 11 | pub struct Context<'a> { 12 | /// Reference too the callsite 13 | callsite: &'a CallSite, 14 | 15 | /// Reference to the HTTP request 16 | request: &'a Request<()>, 17 | 18 | captures: &'a Captures, 19 | 20 | config: &'a Config, 21 | } 22 | 23 | impl<'a> Context<'a> { 24 | // Used as part of codegen, but not part of the public API. 25 | #[doc(hidden)] 26 | pub fn new(route_match: &'a RouteMatch<'_>, callsite: &'a CallSite) -> Context<'a> { 27 | let request = route_match.request(); 28 | let captures = route_match.captures(); 29 | let config = route_match.config(); 30 | 31 | Context { 32 | callsite, 33 | request, 34 | captures, 35 | config, 36 | } 37 | } 38 | 39 | pub(crate) fn callsite(&self) -> &CallSite { 40 | self.callsite 41 | } 42 | 43 | pub(crate) fn captures(&self) -> &Captures { 44 | self.captures 45 | } 46 | 47 | /// Returns a reference to the HTTP request from which the data should be 48 | /// extracted. 49 | pub fn request(&self) -> &Request<()> { 50 | &self.request 51 | } 52 | 53 | /// Returns the stored configuration value of type `T`. 54 | pub fn config(&self) -> Option<&T> { self.config.get::() } 55 | } 56 | -------------------------------------------------------------------------------- /src/response.rs: -------------------------------------------------------------------------------- 1 | //! Types and traits for responding to HTTP requests. 2 | //! 3 | //! The [`Response`] trait is implemented by types that can be converted to an 4 | //! HTTP response. Resource methods must return futures that yield types 5 | //! implementing [`Response`]. 6 | //! 7 | //! Currently, [`Response`] implementations are provided for the following 8 | //! types: 9 | //! 10 | //! * [`String`](https://doc.rust-lang.org/std/string/struct.String.html) 11 | //! * [`&'static str`](https://doc.rust-lang.org/std/primitive.str.html) 12 | //! * [`http::Response`](https://docs.rs/http/0.1/http/response/struct.Response.html) 13 | //! * [`serde_json::Value`](https://docs.rs/serde_json/1/serde_json/enum.Value.html) 14 | //! * [`tokio::fs::File`](https://docs.rs/tokio-fs/0.1/tokio_fs/file/struct.File.html) 15 | //! 16 | //! More implementations can be added by submitting a PR. 17 | //! 18 | //! Also, [`Response`] can be implemented for custom types by using the 19 | //! [`derive(Response)`] proc macro. See [library level][lib] documentation for 20 | //! more details. 21 | //! 22 | //! [`Response`]: trait.Response.html 23 | //! [lib]: ../index.html 24 | 25 | mod content_type; 26 | mod context; 27 | mod default_serializer; 28 | mod either; 29 | mod file; 30 | mod json; 31 | mod option; 32 | mod response; 33 | mod serde; 34 | mod serializer; 35 | mod serializer_context; 36 | mod str; 37 | mod vec; 38 | 39 | pub use self::content_type::ContentType; 40 | pub use self::context::Context; 41 | pub use self::default_serializer::DefaultSerializer; 42 | pub use self::response::Response; 43 | pub use self::serde::SerdeResponse; 44 | pub use self::serializer::Serializer; 45 | pub use self::serializer_context::SerializerContext; 46 | -------------------------------------------------------------------------------- /examples/middleware.rs: -------------------------------------------------------------------------------- 1 | /// Add middleware to service 2 | /// 3 | /// ## Overview 4 | /// 5 | /// Middleware decorates a service, adding additional functionality. It is a 6 | /// concept common to most web frameworks. 7 | /// 8 | /// Tower Web uses the Tower stack for middleware (hence the name). This example 9 | /// decorates the application with the LogMiddleware. This middleware logs 10 | /// information for each request. 11 | /// 12 | /// ## Usage 13 | /// 14 | /// Run the example: 15 | /// 16 | /// RUST_LOG="hello_world=info" cargo run --example middleware 17 | /// 18 | /// Then send a request: 19 | /// 20 | /// curl -v http://localhost:8080/ 21 | 22 | extern crate env_logger; 23 | #[macro_use] 24 | extern crate tower_web; 25 | 26 | use tower_web::ServiceBuilder; 27 | use tower_web::middleware::deflate::DeflateMiddleware; 28 | use tower_web::middleware::log::LogMiddleware; 29 | 30 | use flate2::Compression; 31 | 32 | #[derive(Clone, Debug)] 33 | pub struct HelloWorld; 34 | 35 | impl_web! { 36 | impl HelloWorld { 37 | #[get("/")] 38 | fn hello_world(&self) -> Result<&'static str, ()> { 39 | Ok("hello world") 40 | } 41 | } 42 | } 43 | 44 | pub fn main() { 45 | let _ = env_logger::try_init(); 46 | 47 | let addr = "127.0.0.1:8080".parse().expect("Invalid address"); 48 | println!("Listening on http://{}", addr); 49 | 50 | ServiceBuilder::new() 51 | .resource(HelloWorld) 52 | // Add middleware, in this case access logging 53 | .middleware(LogMiddleware::new("hello_world::web")) 54 | .middleware(DeflateMiddleware::new(Compression::best())) 55 | // We run the service 56 | .run(&addr) 57 | .unwrap(); 58 | } 59 | -------------------------------------------------------------------------------- /src/response/serde.rs: -------------------------------------------------------------------------------- 1 | use crate::error; 2 | use crate::response::{Response, Serializer, Context}; 3 | use crate::util::BufStream; 4 | 5 | use bytes::Bytes; 6 | use http::{self, header}; 7 | use serde; 8 | 9 | /// Use a Serde value as an HTTP response 10 | /// 11 | /// Takes a `T: serde::Serialize` and implements `Response` for it. 12 | #[derive(Debug)] 13 | pub struct SerdeResponse(T); 14 | 15 | impl SerdeResponse { 16 | /// Create a new `SerdeResponse` using the given value. 17 | pub fn new(value: T) -> SerdeResponse { 18 | SerdeResponse(value) 19 | } 20 | } 21 | 22 | impl Response for SerdeResponse 23 | where 24 | T: serde::Serialize, 25 | { 26 | type Buf = ::Item; 27 | type Body = error::Map; 28 | 29 | fn into_http(self, context: &Context<'_, S>) -> Result, crate::Error> 30 | where 31 | S: Serializer, 32 | { 33 | let content_type = context.content_type_header() 34 | .expect("no content type specified for response"); 35 | 36 | let serialize_context = context.serializer_context(); 37 | 38 | let serialized = context.serialize(&self.0, &serialize_context) 39 | // TODO: Improve and handle errors 40 | .unwrap(); 41 | 42 | let body = error::Map::new(serialized); 43 | 44 | let mut response = http::Response::builder() 45 | // Customize response 46 | .status(200) 47 | .body(body) 48 | .unwrap(); 49 | 50 | response 51 | .headers_mut() 52 | .entry(header::CONTENT_TYPE) 53 | .unwrap() 54 | .or_insert_with(|| content_type.clone()); 55 | 56 | Ok(response) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/util/http/future.rs: -------------------------------------------------------------------------------- 1 | use futures::{Future, Poll}; 2 | use http; 3 | 4 | /// HTTP response future trait 5 | /// 6 | /// A trait "alias" for `Future` where the yielded item is an `http::Response`. 7 | /// 8 | /// Using `HttpFuture` in where bounds is easier than trying to use `Future` 9 | /// directly. 10 | pub trait HttpFuture: sealed::Sealed { 11 | /// The HTTP response body 12 | type Body; 13 | 14 | /// Attempt to resolve the future to a final value, registering the current 15 | /// task for wakeup if the value is not yet available. 16 | fn poll_http(&mut self) -> Poll, crate::Error>; 17 | 18 | /// Wraps `self` with `LiftFuture`. This provides an implementation of 19 | /// `Future` for `Self`. 20 | fn lift(self) -> LiftFuture 21 | where Self: Sized, 22 | { 23 | LiftFuture { inner: self } 24 | } 25 | } 26 | 27 | /// Contains an `HttpFuture` providing an implementation of `Future`. 28 | #[derive(Debug)] 29 | pub struct LiftFuture { 30 | inner: T, 31 | } 32 | 33 | impl HttpFuture for T 34 | where T: Future, Error = crate::Error> 35 | { 36 | type Body = B; 37 | 38 | fn poll_http(&mut self) -> Poll, crate::Error> { 39 | Future::poll(self) 40 | } 41 | } 42 | 43 | impl sealed::Sealed for T 44 | where T: Future, Error = crate::Error> 45 | { 46 | } 47 | 48 | impl Future for LiftFuture { 49 | type Item = http::Response; 50 | type Error = crate::Error; 51 | 52 | fn poll(&mut self) -> Poll { 53 | self.inner.poll_http() 54 | } 55 | } 56 | 57 | /// Must be made crate public for `Either{N}` implementations. 58 | pub(crate) mod sealed { 59 | pub trait Sealed {} 60 | } 61 | -------------------------------------------------------------------------------- /tests/methods.rs: -------------------------------------------------------------------------------- 1 | use http; 2 | use tower_web::impl_web; 3 | 4 | mod support; 5 | use crate::support::*; 6 | 7 | use http::request; 8 | 9 | #[derive(Clone, Debug)] 10 | struct TestMethods; 11 | 12 | impl_web! { 13 | impl TestMethods { 14 | #[get("/")] 15 | #[content_type("plain")] 16 | fn sync_get_str(&self) -> Result<&'static str, ()> { 17 | Ok("GET: hello world") 18 | } 19 | 20 | #[post("/")] 21 | #[content_type("plain")] 22 | fn sync_post_str(&self) -> Result<&'static str, ()> { 23 | Ok("POST: hello world") 24 | } 25 | 26 | #[put("/")] 27 | #[content_type("plain")] 28 | fn sync_put_str(&self) -> Result<&'static str, ()> { 29 | Ok("PUT: hello world") 30 | } 31 | 32 | #[patch("/")] 33 | #[content_type("plain")] 34 | fn sync_patch_str(&self) -> Result<&'static str, ()> { 35 | Ok("PATCH: hello world") 36 | } 37 | 38 | #[delete("/")] 39 | #[content_type("plain")] 40 | fn sync_delete_str(&self) -> Result<&'static str, ()> { 41 | Ok("DELETE: hello world") 42 | } 43 | } 44 | } 45 | 46 | #[test] 47 | fn sync_method_str() { 48 | let mut web = service(TestMethods); 49 | 50 | let methods = [ 51 | "GET", 52 | "POST", 53 | "PUT", 54 | "PATCH", 55 | "DELETE", 56 | ]; 57 | 58 | for &method in &methods { 59 | let request = request::Builder::new() 60 | .method(method) 61 | .uri("/") 62 | .body("".to_string()) 63 | .unwrap(); 64 | 65 | let response = web.call_unwrap(request); 66 | assert_ok!(response); 67 | assert_eq!(response.headers()["content-type"], "text/plain"); 68 | assert_body!(response, format!("{}: hello world", method)); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/net.rs: -------------------------------------------------------------------------------- 1 | //! Networking types and trait 2 | 3 | use futures::{Stream, Poll}; 4 | use tokio::net::TcpStream; 5 | use tokio_io::{AsyncRead, AsyncWrite}; 6 | 7 | use std::io; 8 | use std::net::SocketAddr; 9 | 10 | #[cfg(feature = "rustls")] 11 | use tokio_rustls::{TlsStream, rustls::ServerSession}; 12 | 13 | /// A stream between a local and remote target. 14 | pub trait Connection: AsyncRead + AsyncWrite { 15 | /// Returns the socket address of the remote peer of this connection. 16 | fn peer_addr(&self) -> Option; 17 | } 18 | 19 | /// An asynchronous stream of connections. 20 | pub trait ConnectionStream { 21 | /// Connection type yielded each iteration. 22 | type Item: Connection; 23 | 24 | /// Attempt to resolve the next connection, registering the current task for 25 | /// wakeup if one is not yet available. 26 | fn poll_next(&mut self) -> Poll, io::Error>; 27 | } 28 | 29 | impl Connection for TcpStream { 30 | fn peer_addr(&self) -> Option { 31 | TcpStream::peer_addr(self).ok() 32 | } 33 | } 34 | 35 | #[cfg(feature = "rustls")] 36 | impl Connection for TlsStream { 37 | fn peer_addr(&self) -> Option { 38 | TcpStream::peer_addr(self.get_ref().0).ok() 39 | } 40 | } 41 | 42 | impl ConnectionStream for T 43 | where 44 | T: Stream, 45 | T::Item: Connection, 46 | { 47 | type Item = ::Item; 48 | 49 | fn poll_next(&mut self) -> Poll, io::Error> { 50 | self.poll() 51 | } 52 | } 53 | 54 | #[derive(Debug)] 55 | pub(crate) struct Lift(pub(crate) T); 56 | 57 | impl Stream for Lift { 58 | type Item = ::Item; 59 | type Error = io::Error; 60 | 61 | fn poll(&mut self) -> Poll, Self::Error> { 62 | self.0.poll_next() 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/routing/route.rs: -------------------------------------------------------------------------------- 1 | use super::{Captures, Path}; 2 | 3 | use http::{Method, Request}; 4 | 5 | /// Matches an HTTP request with a resource method. 6 | #[derive(Debug)] 7 | pub struct Route { 8 | /// Where to route the request 9 | destination: T, 10 | 11 | /// HTTP method used to match the route 12 | method: Method, 13 | 14 | /// Path used to match the route 15 | path: Path, 16 | } 17 | 18 | impl Route { 19 | /// Create a new route 20 | pub fn new(destination: T) -> Self { 21 | let method = Method::default(); 22 | let path = Path::new("/"); 23 | 24 | Route { 25 | destination, 26 | method, 27 | path, 28 | } 29 | } 30 | 31 | /// Set the HTTP request method matched by this route. 32 | pub fn method(mut self, value: Method) -> Self { 33 | self.method = value; 34 | self 35 | } 36 | 37 | /// Set the HTTP request path matched by this route. 38 | pub fn path(mut self, path: &str) -> Self { 39 | self.path = Path::new(path); 40 | self 41 | } 42 | 43 | pub(crate) fn map(self, f: F) -> Route 44 | where 45 | F: Fn(T) -> U, 46 | { 47 | let destination = f(self.destination); 48 | 49 | Route { 50 | destination, 51 | method: self.method, 52 | path: self.path, 53 | } 54 | } 55 | } 56 | 57 | impl Route 58 | where 59 | T: Clone, 60 | { 61 | /// Try to match a request against this route. 62 | pub(crate) fn test<'a>( 63 | &'a self, 64 | request: &Request<()>, 65 | ) -> Option<(T, Captures)> { 66 | 67 | if *request.method() != self.method { 68 | return None; 69 | } 70 | 71 | self.path.test(request.uri().path()) 72 | .map(|captures| { 73 | (self.destination.clone(), captures) 74 | }) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/util/http/middleware.rs: -------------------------------------------------------------------------------- 1 | use crate::middleware::Middleware; 2 | use crate::util::buf_stream::BufStream; 3 | use crate::util::http::HttpService; 4 | 5 | use http::{Request, Response}; 6 | 7 | /// HTTP middleware trait 8 | /// 9 | /// A trait "alias" for `Middleware` where the yielded service is an 10 | /// `HttpService`. 11 | /// 12 | /// Using `HttpMiddleware` in where bounds is easier than trying to use `Middleware` 13 | /// directly. 14 | pub trait HttpMiddleware: sealed::Sealed { 15 | /// The HTTP request body handled by the wrapped service. 16 | type RequestBody: BufStream; 17 | 18 | /// The HTTP response body returned by the wrapped service. 19 | type ResponseBody: BufStream; 20 | 21 | /// The wrapped service's error type. 22 | type Error; 23 | 24 | /// The wrapped service. 25 | type Service: HttpService; 28 | 29 | /// Wrap the given service with the middleware, returning a new servicee 30 | /// that has been decorated with the middleware. 31 | fn wrap_http(&self, inner: S) -> Self::Service; 32 | } 33 | 34 | impl HttpMiddleware for T 35 | where T: Middleware, 36 | Response = Response>, 37 | B1: BufStream, 38 | B2: BufStream, 39 | { 40 | type RequestBody = B1; 41 | type ResponseBody = B2; 42 | type Error = T::Error; 43 | type Service = T::Service; 44 | 45 | fn wrap_http(&self, inner: S) -> Self::Service { 46 | Middleware::wrap(self, inner) 47 | } 48 | } 49 | 50 | impl sealed::Sealed for T 51 | where T: Middleware, 52 | Response = Response>, 53 | B1: BufStream, 54 | B2: BufStream, 55 | {} 56 | 57 | mod sealed { 58 | pub trait Sealed {} 59 | } 60 | -------------------------------------------------------------------------------- /tower-web-macros/src/resource/arg.rs: -------------------------------------------------------------------------------- 1 | use syn; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | 5 | #[derive(Debug)] 6 | pub(crate) struct Arg { 7 | pub index: usize, 8 | 9 | /// Argument identifier, i.e., the variable name. 10 | pub ident: Option, 11 | 12 | /// The index of the path binding the identifier matches. 13 | pub capture: Option, 14 | 15 | /// The argument type 16 | pub ty: Box, 17 | } 18 | 19 | impl Arg { 20 | /// Create a new, regular, argument. 21 | pub fn new(index: usize, ident: String, capture: Option, ty: Box) -> Arg { 22 | Arg { 23 | index, 24 | ident: Some(ident), 25 | capture, 26 | ty, 27 | } 28 | } 29 | 30 | /// The argument is formatted in a way that cannot be interpretted. 31 | pub fn ty_only(index: usize, ty: Box::) -> Arg { 32 | Arg { 33 | index, 34 | ty, 35 | ident: None, 36 | capture: None, 37 | } 38 | } 39 | 40 | /// Generate a call site for the argument 41 | pub fn new_callsite(&self) -> TokenStream { 42 | if let Some(idx) = self.capture { 43 | quote! { __tw::codegen::CallSite::new_capture(#idx) } 44 | } else if let Some(ref ident) = self.ident { 45 | match &ident[..] { 46 | "query_string" => quote! { __tw::codegen::CallSite::new_query_string() }, 47 | "body" => quote! { __tw::codegen::CallSite::new_body() }, 48 | header => { 49 | let header = crate::header::arg_to_header_name(header); 50 | let header = header.as_str(); 51 | 52 | quote! { __tw::codegen::CallSite::new_header(#header) } 53 | } 54 | } 55 | } else { 56 | quote! { __tw::codegen::CallSite::new_unknown() } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/util/buf_stream/from.rs: -------------------------------------------------------------------------------- 1 | use super::SizeHint; 2 | 3 | use bytes::{Buf, BufMut, Bytes, BytesMut}; 4 | 5 | /// Conversion from a `BufStream`. 6 | /// 7 | /// By implementing `FromBufStream` for a type, you define how it will be 8 | /// created from a buf stream. This is common for types which describe byte 9 | /// storage of some kind. 10 | /// 11 | /// `FromBufStream` is rarely called explicitly, and it is instead used through 12 | /// `BufStream`'s `collect` method. 13 | pub trait FromBufStream { 14 | /// Type that is used to build `Self` while the `BufStream` is being 15 | /// consumed. 16 | type Builder; 17 | 18 | /// Create a new, empty, builder. The provided `hint` can be used to inform 19 | /// reserving capacity. 20 | fn builder(hint: &SizeHint) -> Self::Builder; 21 | 22 | /// Extend the builder with the `Buf`. 23 | /// 24 | /// This method is called whenever a new `Buf` value is obtained from the 25 | /// buf stream. 26 | fn extend(builder: &mut Self::Builder, buf: &mut T); 27 | 28 | /// Finalize the building of `Self`. 29 | /// 30 | /// Called once the buf stream is fully consumed. 31 | fn build(builder: Self::Builder) -> Self; 32 | } 33 | 34 | impl FromBufStream for Vec { 35 | type Builder = Vec; 36 | 37 | fn builder(hint: &SizeHint) -> Vec { 38 | Vec::with_capacity(hint.lower()) 39 | } 40 | 41 | fn extend(builder: &mut Self, buf: &mut T) { 42 | builder.put(buf); 43 | } 44 | 45 | fn build(builder: Self) -> Self { 46 | builder 47 | } 48 | } 49 | 50 | impl FromBufStream for Bytes { 51 | type Builder = BytesMut; 52 | 53 | fn builder(hint: &SizeHint) -> BytesMut { 54 | BytesMut::with_capacity(hint.lower()) 55 | } 56 | 57 | fn extend(builder: &mut Self::Builder, buf: &mut T) { 58 | builder.put(buf); 59 | } 60 | 61 | fn build(builder: Self::Builder) -> Self { 62 | builder.freeze() 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/extract/immediate.rs: -------------------------------------------------------------------------------- 1 | use crate::extract::{ExtractFuture, Error}; 2 | 3 | use futures::{Poll}; 4 | 5 | /// Implements `ExtractFuture` such that the result is immediately available. 6 | /// 7 | /// This type is useful when implementing `Extract` for types that do not 8 | /// require any asynchronous processing. For example, extracting an HTTP header 9 | /// value from an HTTP request can complete immediately as all the information 10 | /// is present. 11 | #[derive(Debug)] 12 | pub struct Immediate { 13 | inner: Result>, 14 | } 15 | 16 | impl Immediate { 17 | /// Create a new `Immediate` instance from a `Result` value. 18 | /// 19 | /// When polling the returned `Immediate` instance, it will yield `result`. 20 | pub fn result(result: Result) -> Immediate { 21 | Immediate { 22 | inner: result.map_err(Some), 23 | } 24 | } 25 | 26 | /// Create a new `Immediate` instance that is in the success state. 27 | /// 28 | /// When polling the returned `Immediate` instance, it will yield `value`. 29 | pub fn ok(value: T) -> Immediate { 30 | Immediate::result(Ok(value)) 31 | } 32 | 33 | /// Create a new `Immediate` instance that is in the error state. 34 | /// 35 | /// When polling the returned `Immediate` instance, it will yield `error`. 36 | pub fn err(error: Error) -> Immediate { 37 | Immediate::result(Err(error)) 38 | } 39 | } 40 | 41 | impl ExtractFuture for Immediate { 42 | type Item = T; 43 | 44 | fn poll(&mut self) -> Poll<(), Error> { 45 | match self.inner { 46 | Ok(_) => Ok(().into()), 47 | Err(ref mut err) => { 48 | Err(err.take().unwrap()) 49 | } 50 | } 51 | } 52 | 53 | fn extract(self) -> T { 54 | self.inner.unwrap() 55 | } 56 | } 57 | 58 | impl From> for Immediate 59 | where E: Into, 60 | { 61 | fn from(src: Result) -> Self { 62 | let inner = src.map_err(|e| Some(e.into())); 63 | Immediate { inner } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/util/http/new_service.rs: -------------------------------------------------------------------------------- 1 | use super::HttpService; 2 | use crate::util::buf_stream::BufStream; 3 | 4 | use futures::Future; 5 | use http::{Request, Response}; 6 | use tower_service::NewService; 7 | 8 | /// Creates `HttpService` values. 9 | /// 10 | /// This is not intended to be implemented directly. Instead, it is a trait 11 | /// alias of sorts, aliasing `tower_service::NewService` trait with 12 | /// `http::Request` and `http::Response` types. 13 | pub trait NewHttpService: sealed::Sealed { 14 | /// The HTTP request body handled by the service. 15 | type RequestBody: BufStream; 16 | 17 | /// The HTTP response body returned by the service. 18 | type ResponseBody: BufStream; 19 | 20 | /// Errors produced by the service 21 | type Error; 22 | 23 | /// The `Service` value created by this factory 24 | type Service: HttpService; 27 | 28 | /// Errors produced while building a service. 29 | type InitError; 30 | 31 | /// The future of the `Service` instance. 32 | type Future: Future; 33 | 34 | /// Create and return a new service value asynchronously. 35 | fn new_http_service(&self) -> Self::Future; 36 | } 37 | 38 | impl NewHttpService for T 39 | where T: NewService, 40 | Response = Response>, 41 | B1: BufStream, 42 | B2: BufStream 43 | { 44 | type RequestBody = B1; 45 | type ResponseBody = B2; 46 | type Error = T::Error; 47 | type Service = T::Service; 48 | type InitError = T::InitError; 49 | type Future = T::Future; 50 | 51 | fn new_http_service(&self) -> Self::Future { 52 | NewService::new_service(self) 53 | } 54 | } 55 | 56 | impl sealed::Sealed for T 57 | where T: NewService, 58 | Response = Response>, 59 | B1: BufStream, 60 | B2: BufStream 61 | {} 62 | 63 | mod sealed { 64 | pub trait Sealed {} 65 | } 66 | -------------------------------------------------------------------------------- /src/error/map.rs: -------------------------------------------------------------------------------- 1 | use http::status::StatusCode; 2 | use crate::util::BufStream; 3 | 4 | use futures::{Future, Poll}; 5 | 6 | /// Map an arbitrary error type to [`Error`] 7 | /// 8 | /// The conversion is done by treating all errors as "internal server errors". 9 | /// 10 | /// [`Error`]: struct.Error.html 11 | #[derive(Debug)] 12 | pub struct Map { 13 | inner: State, 14 | } 15 | 16 | #[derive(Debug)] 17 | enum State { 18 | Inner(T), 19 | Immediate(Option), 20 | } 21 | 22 | impl Map { 23 | /// Create a new `Map` instance backed by `inner`. 24 | /// 25 | /// The returned value will map all errors generated by `inner` into 26 | /// [`Error`] by treating them as "internal server errors". 27 | /// 28 | /// [`Error`]: struct.Error.html 29 | pub fn new(inner: T) -> Map { 30 | Map { 31 | inner: State::Inner(inner), 32 | } 33 | } 34 | 35 | /// Create a neew `Map` instance that is in the error state. 36 | /// 37 | /// The instance will yield `error` immediately when it is used. 38 | pub fn immediate(error: crate::Error) -> Map { 39 | Map { 40 | inner: State::Immediate(Some(error)), 41 | } 42 | } 43 | } 44 | 45 | impl Future for Map { 46 | type Item = T::Item; 47 | type Error = crate::Error; 48 | 49 | fn poll(&mut self) -> Poll { 50 | use self::State::*; 51 | 52 | match self.inner { 53 | Inner(ref mut f) => f.poll().map_err(|_| crate::Error::from(StatusCode::INTERNAL_SERVER_ERROR)), 54 | Immediate(ref mut e) => Err(e.take().unwrap()), 55 | } 56 | } 57 | } 58 | 59 | impl BufStream for Map { 60 | type Item = T::Item; 61 | type Error = crate::Error; 62 | 63 | fn poll(&mut self) -> Poll, Self::Error> { 64 | use self::State::*; 65 | 66 | match self.inner { 67 | Inner(ref mut f) => f.poll().map_err(|_| crate::Error::from(StatusCode::INTERNAL_SERVER_ERROR)), 68 | Immediate(ref mut e) => Err(e.take().unwrap()), 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/response/serializer_context.rs: -------------------------------------------------------------------------------- 1 | use http; 2 | 3 | /// Context passed to `Serializer::serialize` 4 | /// 5 | /// `SerializerContext` contains context obtained when deriving `Response`. 6 | #[derive(Debug)] 7 | pub struct SerializerContext<'a> { 8 | request: &'a http::Request<()>, 9 | resource_mod: Option<&'a str>, 10 | resource_name: Option<&'a str>, 11 | handler_name: Option<&'a str>, 12 | template: Option<&'a str>, 13 | } 14 | 15 | impl<'a> SerializerContext<'a> { 16 | pub(crate) fn new(request: &'a http::Request<()>) -> SerializerContext<'a> { 17 | SerializerContext { 18 | request, 19 | resource_mod: None, 20 | resource_name: None, 21 | handler_name: None, 22 | template: None, 23 | } 24 | } 25 | 26 | /// Returns a reference to the original request 27 | pub fn request(&self) -> &http::Request<()> { 28 | self.request 29 | } 30 | 31 | /// Returns the module in which the `impl_web!` was used to implement the resource. 32 | pub fn resource_mod(&self) -> Option<&str> { 33 | self.resource_mod 34 | } 35 | 36 | pub(crate) fn set_resource_mod(&mut self, value: Option<&'a str>) { 37 | self.resource_mod = value; 38 | } 39 | 40 | /// Returns the name of the resource handling the request. 41 | pub fn resource_name(&self) -> Option<&str> { 42 | self.resource_name 43 | } 44 | 45 | pub(crate) fn set_resource_name(&mut self, value: Option<&'a str>) { 46 | self.resource_name = value; 47 | } 48 | 49 | /// Returns the name of the function handling the request. 50 | pub fn handler_name(&self) -> Option<&str> { 51 | self.handler_name 52 | } 53 | 54 | pub(crate) fn set_handler_name(&mut self, value: Option<&'a str>) { 55 | self.handler_name = value; 56 | } 57 | 58 | /// Returns the `template` value set for the response. 59 | pub fn template(&self) -> Option<&str> { 60 | self.template 61 | } 62 | 63 | #[doc(hidden)] 64 | pub fn set_template(&mut self, value: &'a str) { 65 | self.template = Some(value); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/service/web.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Catch; 2 | use crate::routing::{Resource, RoutedService}; 3 | use crate::util::http::{HttpMiddleware, HttpService}; 4 | 5 | use futures::Poll; 6 | use http; 7 | use tower_service::Service; 8 | 9 | use std::fmt; 10 | 11 | /// The service defined by `ServiceBuilder`. 12 | /// 13 | /// `WebService` contains the resources, routes, middleware, catch handlers, ... 14 | /// that were defined by the builder. It implements `tower_service::Service`, 15 | /// which exposes an HTTP request / response API. 16 | pub struct WebService 17 | where 18 | T: Resource, 19 | U: Catch, 20 | M: HttpMiddleware>, 21 | { 22 | /// The routed service wrapped with middleware 23 | inner: M::Service, 24 | } 25 | 26 | impl WebService 27 | where 28 | T: Resource, 29 | U: Catch, 30 | M: HttpMiddleware>, 31 | { 32 | pub(crate) fn new(inner: M::Service) -> WebService { 33 | WebService { inner } 34 | } 35 | } 36 | 37 | impl Service for WebService 38 | where 39 | T: Resource, 40 | U: Catch, 41 | M: HttpMiddleware>, 42 | { 43 | type Request = http::Request; 44 | type Response = http::Response; 45 | type Error = M::Error; 46 | type Future = ::Future; 47 | 48 | fn poll_ready(&mut self) -> Poll<(), Self::Error> { 49 | self.inner.poll_http_ready() 50 | } 51 | 52 | fn call(&mut self, request: Self::Request) -> Self::Future { 53 | self.inner.call_http(request) 54 | } 55 | } 56 | 57 | impl fmt::Debug for WebService 58 | where T: Resource + fmt::Debug, 59 | U: Catch + fmt::Debug, 60 | M: HttpMiddleware> + fmt::Debug, 61 | M::Service: fmt::Debug, 62 | M::RequestBody: fmt::Debug, 63 | M::ResponseBody: fmt::Debug, 64 | M::Error: fmt::Debug, 65 | { 66 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 67 | fmt.debug_struct("WebService") 68 | .field("inner", &self.inner) 69 | .finish() 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/response/response.rs: -------------------------------------------------------------------------------- 1 | use crate::error; 2 | use crate::response::{Context, Serializer}; 3 | use crate::util::BufStream; 4 | 5 | use bytes::Buf; 6 | use http; 7 | 8 | /// Types that can be returned from resources as responses to HTTP requests. 9 | /// 10 | /// Implementations of `Response` are responsible for encoding the value using 11 | /// the appropriate content type. The content type may be specific to the type 12 | /// in question, for example `serde_json::Value` implies a content type of 13 | /// `application/json`. 14 | /// 15 | /// Alternatively, the provided `context` may be used to encode the response in 16 | /// most suitable content-type based on the request context. The content-type is 17 | /// picked using the following factors: 18 | /// 19 | /// * The HTTP request's `Accept` header value (not yet implemented). 20 | /// * Any content type specified by the resource using annotations. 21 | /// * Serialization formats that the application made available to the resource. 22 | /// 23 | /// Implementations of `Response` are able to asynchronously stream the response 24 | /// body if needed. This is done by setting the HTTP response body to a 25 | /// `BufStream` type that supports streaming. 26 | pub trait Response { 27 | /// Data chunk type. 28 | type Buf: Buf; 29 | 30 | /// The HTTP response body type. 31 | type Body: BufStream; 32 | 33 | /// Convert the value into a response future 34 | fn into_http(self, context: &Context<'_, S>) -> Result, crate::Error>; 35 | } 36 | 37 | impl Response for http::Response 38 | where T: BufStream, 39 | { 40 | type Buf = T::Item; 41 | type Body = error::Map; 42 | 43 | fn into_http(self, _: &Context<'_, S>) -> Result, crate::Error> { 44 | Ok(self.map(error::Map::new)) 45 | } 46 | } 47 | 48 | impl Response for Result 49 | where R: Response, 50 | E: Into, 51 | { 52 | type Buf = R::Buf; 53 | type Body = R::Body; 54 | 55 | fn into_http(self, context: &Context<'_, S>) -> Result, crate::Error> { 56 | self.map_err(|err| err.into()).and_then(|resp| resp.into_http(context)) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tower-web" 3 | # When releasing to crates.io: 4 | # - Update html_root_url. 5 | # - Update CHANGELOG.md. 6 | # - Update documentation URL 7 | # - Cargo.toml 8 | # - Readme.md 9 | # - Create "v0.3.x" git tag. 10 | # - Push documentation 11 | version = "0.3.7" 12 | license = "MIT" 13 | authors = ["Carl Lerche "] 14 | readme = "README.md" 15 | repository = "https://github.com/carllerche/tower-web" 16 | homepage = "https://github.com/carllerche/tower-web" 17 | documentation = "https://docs.rs/tower-web/0.3.7/tower_web/" 18 | description = """ 19 | Web framework with a focus on removing boilerplate 20 | """ 21 | categories = ["asynchronous", "web-programming::http-server"] 22 | edition = "2018" 23 | 24 | [workspace] 25 | 26 | members = [ 27 | "./", 28 | "tower-web-macros", 29 | ] 30 | 31 | [features] 32 | # This feature comes with no promise of stability. Things will 33 | # break with each patch release. Use at your own risk. 34 | async-await-preview = [ 35 | "tokio/async-await-preview", 36 | "tokio-async-await/async-await-preview", 37 | "futures/nightly", 38 | ] 39 | rustls = ["tokio-rustls"] 40 | default = ["handlebars"] 41 | 42 | [dependencies] 43 | bytes = "0.4.7" 44 | futures = "0.1.21" 45 | headers = "0.2.0" 46 | http = "0.1.7" 47 | hyper = "0.12.1" 48 | lazy_static = "1" 49 | log = "0.4.1" 50 | mime = "0.3.13" 51 | mime_guess = "1" 52 | percent-encoding = "1.0.1" 53 | tokio = "0.1.6" 54 | tokio-fs = "0.1.2" 55 | tokio-io = "0.1.7" 56 | tower-service = "0.1.0" 57 | void = "1.0.2" 58 | 59 | # Parsing params 60 | atoi = "= 0.2.3" 61 | checked = "0.5.0" 62 | chrono = "0.4.4" 63 | 64 | # Serializing responses, deserializing requests 65 | serde = { version = "1.0.70", features = ["derive"] } 66 | serde_json = "1.0.24" 67 | serde_plain = "0.3.0" 68 | serde_urlencoded = "0.5.1" 69 | 70 | # Code gen 71 | tower-web-macros = { version = "0.3.2", path = "tower-web-macros" } 72 | 73 | # Deflate middleware 74 | flate2 = "1.0.2" 75 | 76 | # rustls support 77 | tokio-rustls = { version = "0.8.0", optional = true } 78 | 79 | # Handlebars support 80 | handlebars = { version = "~1.0.3", optional = true } 81 | 82 | # async/await support 83 | tokio-async-await = { version = "0.1.4", optional = true } 84 | 85 | [dev-dependencies] 86 | env_logger = "0.5.12" 87 | rand = "0.5.5" 88 | -------------------------------------------------------------------------------- /src/service/new_service.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Catch; 2 | use crate::routing::{Resource, RoutedService}; 3 | use crate::service::WebService; 4 | use crate::util::Never; 5 | use crate::util::http::{HttpMiddleware}; 6 | 7 | use futures::future::{self, FutureResult}; 8 | use http; 9 | use tower_service::NewService; 10 | 11 | use std::fmt; 12 | 13 | /// Creates new `WebService` values. 14 | /// 15 | /// Instances of this type are created by `ServiceBuilder`. A `NewWebService` 16 | /// instance is used to generate a `WebService` instance per connection. 17 | pub struct NewWebService 18 | where 19 | T: Resource, 20 | { 21 | /// The routed service. This service implements `Clone`. 22 | service: RoutedService, 23 | 24 | /// Middleware to wrap the routed service with 25 | middleware: M, 26 | } 27 | 28 | impl NewWebService 29 | where 30 | T: Resource, 31 | U: Catch, 32 | M: HttpMiddleware>, 33 | { 34 | /// Create a new `NewWebService` instance. 35 | pub(crate) fn new(service: RoutedService, middleware: M) -> Self { 36 | NewWebService { 37 | service, 38 | middleware, 39 | } 40 | } 41 | } 42 | 43 | impl NewService for NewWebService 44 | where 45 | T: Resource, 46 | U: Catch, 47 | M: HttpMiddleware>, 48 | { 49 | type Request = http::Request; 50 | type Response = http::Response; 51 | type Error = M::Error; 52 | type Service = WebService; 53 | type InitError = Never; 54 | type Future = FutureResult; 55 | 56 | fn new_service(&self) -> Self::Future { 57 | let service = self.middleware.wrap_http(self.service.clone()); 58 | 59 | future::ok(WebService::new(service)) 60 | } 61 | } 62 | 63 | impl fmt::Debug for NewWebService 64 | where 65 | T: Resource + fmt::Debug, 66 | T::Destination: fmt::Debug, 67 | U: fmt::Debug, 68 | M: fmt::Debug, 69 | { 70 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 71 | fmt.debug_struct("NewService") 72 | .field("service", &self.service) 73 | .field("middleware", &self.middleware) 74 | .finish() 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/extract/error.rs: -------------------------------------------------------------------------------- 1 | use self::Kind::*; 2 | 3 | use http::status::StatusCode; 4 | 5 | /// Errors that can happen while extracting data from an HTTP request. 6 | #[derive(Debug)] 7 | pub struct Error { 8 | kind: Kind, 9 | inner: crate::Error, 10 | } 11 | 12 | #[derive(Debug)] 13 | enum Kind { 14 | Missing, 15 | Invalid, 16 | Web, 17 | } 18 | 19 | impl Error { 20 | /// The data is missing from the HTTP request. 21 | pub fn missing_argument() -> Error { 22 | Self::missing(crate::Error::from(StatusCode::BAD_REQUEST)) 23 | } 24 | 25 | /// The data is missing from the HTTP request. 26 | pub fn missing(inner: crate::Error) -> Error { 27 | Error { 28 | kind: Missing, 29 | inner, 30 | } 31 | } 32 | 33 | /// Returns `true` when the error represents missing data from the HTTP 34 | /// request. 35 | pub fn is_missing_argument(&self) -> bool { 36 | match self.kind { 37 | Missing => true, 38 | _ => false, 39 | } 40 | } 41 | 42 | /// The data is in an invalid format and cannot be extracted. 43 | pub fn invalid_argument(reason: &T) -> Error { 44 | let mut inner = crate::Error::from(StatusCode::BAD_REQUEST); 45 | inner.set_detail(&reason.to_string()); 46 | Self::invalid(inner) 47 | } 48 | 49 | /// The data is in an invalid format and cannot be extracted. 50 | pub fn invalid(inner: crate::Error) -> Error { 51 | Error { 52 | kind: Invalid, 53 | inner, 54 | } 55 | } 56 | 57 | /// Returns `true` when the data is in an invalid format and cannot be 58 | /// extracted. 59 | pub fn is_invalid_argument(&self) -> bool { 60 | match self.kind { 61 | Invalid => true, 62 | _ => false, 63 | } 64 | } 65 | 66 | pub(crate) fn internal_error() -> Error { 67 | crate::Error::from(StatusCode::BAD_REQUEST).into() 68 | } 69 | } 70 | 71 | impl From for crate::Error { 72 | fn from(err: Error) -> Self { 73 | err.inner 74 | } 75 | } 76 | 77 | impl From for Error { 78 | fn from(inner: crate::Error) -> Self { 79 | Error { 80 | kind: Web, 81 | inner, 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | //! Application level configuration. 2 | //! 3 | //! Provides infrastructure for application level configuration. Configuration 4 | //! values may be set and retrieved by type. 5 | 6 | use std::any::{Any, TypeId}; 7 | use std::sync::Arc; 8 | use std::collections::HashMap; 9 | use std::hash::{BuildHasherDefault, Hasher}; 10 | use std::fmt; 11 | 12 | type AnyMap = HashMap, BuildHasherDefault>; 13 | 14 | #[derive(Debug, Default)] 15 | struct IdHasher(u64); 16 | 17 | impl Hasher for IdHasher { 18 | fn write(&mut self, _: &[u8]) { 19 | unreachable!("TypeId calls write_u64"); 20 | } 21 | 22 | #[inline] 23 | fn write_u64(&mut self, id: u64) { 24 | self.0 = id; 25 | } 26 | 27 | #[inline] 28 | fn finish(&self) -> u64 { 29 | self.0 30 | } 31 | } 32 | 33 | pub(crate) struct ConfigBuilder { 34 | inner: AnyMap, 35 | } 36 | 37 | impl ConfigBuilder { 38 | pub(crate) fn new() -> Self { 39 | Self { inner: AnyMap::default() } 40 | } 41 | 42 | pub(crate) fn insert(mut self, val: T) -> Self { 43 | self.inner.insert(TypeId::of::(), Box::new(val)); 44 | self 45 | } 46 | 47 | pub(crate) fn into_config(self) -> Config { 48 | Config { inner: Arc::new(self.inner) } 49 | } 50 | } 51 | 52 | impl fmt::Debug for ConfigBuilder { 53 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 54 | f.debug_struct("ConfigBuilder") 55 | .finish() 56 | } 57 | } 58 | 59 | /// A type of application level configuration. 60 | #[derive(Clone)] 61 | pub struct Config { 62 | inner: Arc, 63 | } 64 | 65 | impl Config { 66 | /// Get the configuration value of the specified type. 67 | /// 68 | /// If a configuration value of type `T` is stored in `Config`, it is 69 | /// returned. Otherwise, `None` is returned. 70 | pub fn get(&self) -> Option<&T> { 71 | self.inner 72 | .get(&TypeId::of::()) 73 | .and_then(|boxed| { 74 | (&**boxed as &dyn Any).downcast_ref() 75 | }) 76 | } 77 | } 78 | 79 | impl fmt::Debug for Config { 80 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 81 | f.debug_struct("Config") 82 | .finish() 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/middleware/deflate/service.rs: -------------------------------------------------------------------------------- 1 | use flate2::Compression; 2 | use futures::{Future, Poll, try_ready}; 3 | use http; 4 | use http::header::{CONTENT_ENCODING, HeaderValue}; 5 | use tower_service::Service; 6 | use crate::util::buf_stream::BufStream; 7 | use crate::util::buf_stream::deflate::CompressStream; 8 | 9 | /// Deflates the inner service's response bodies. 10 | #[derive(Debug)] 11 | pub struct DeflateService { 12 | inner: S, 13 | level: Compression, 14 | } 15 | 16 | /// Deflate the response body. 17 | #[derive(Debug)] 18 | pub struct ResponseFuture { 19 | inner: T, 20 | level: Compression, 21 | } 22 | 23 | impl DeflateService { 24 | pub(super) fn new(inner: S, level: Compression) -> DeflateService { 25 | DeflateService { 26 | inner, 27 | level, 28 | } 29 | } 30 | } 31 | 32 | impl Service for DeflateService 33 | where S: Service, 34 | Response = http::Response>, 35 | ResponseBody: BufStream, 36 | S::Error: ::std::error::Error, 37 | { 38 | type Request = http::Request; 39 | type Response = http::Response>; 40 | type Error = S::Error; 41 | type Future = ResponseFuture; 42 | 43 | fn poll_ready(&mut self) -> Poll<(), Self::Error> { 44 | self.inner.poll_ready() 45 | } 46 | 47 | fn call(&mut self, request: Self::Request) -> Self::Future { 48 | ResponseFuture { 49 | inner: self.inner.call(request), 50 | level: self.level, 51 | } 52 | } 53 | } 54 | 55 | impl Future for ResponseFuture 56 | where 57 | T: Future>, 58 | B: BufStream, 59 | T::Error: ::std::error::Error, 60 | { 61 | type Item = http::Response>; 62 | type Error = T::Error; 63 | 64 | fn poll(&mut self) -> Poll { 65 | let mut response = try_ready!(self.inner.poll()) 66 | .map(|body| CompressStream::new(body, self.level)); 67 | 68 | let content_encoding = HeaderValue::from_static("deflate"); 69 | 70 | // Set content-encoding 71 | response.headers_mut() 72 | .insert(CONTENT_ENCODING, content_encoding); 73 | 74 | Ok(response.into()) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/extract/http_date_time.rs: -------------------------------------------------------------------------------- 1 | //! HTTP combined date and time value. 2 | 3 | use chrono::{DateTime, Timelike, Utc}; 4 | use crate::extract::{Context, Error, Extract, Immediate}; 5 | use http::{self, header}; 6 | use std::time::SystemTime; 7 | use crate::util::buf_stream::BufStream; 8 | 9 | /// HTTP combined date and time value 10 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] 11 | pub struct HttpDateTime(DateTime); 12 | 13 | impl HttpDateTime { 14 | fn normalize(dt: DateTime) -> Self { 15 | // We don't care about anything smaller than a second 16 | let dt = dt 17 | .with_nanosecond(0) 18 | .expect("Unable to normalize HttpDateTime"); 19 | HttpDateTime(dt) 20 | } 21 | } 22 | 23 | impl From for HttpDateTime { 24 | fn from(t: SystemTime) -> Self { 25 | HttpDateTime::normalize(t.into()) 26 | } 27 | } 28 | 29 | impl http::HttpTryFrom for header::HeaderValue { 30 | type Error = header::InvalidHeaderValue; 31 | 32 | fn try_from(t: HttpDateTime) -> Result { 33 | let s = t.0.to_rfc2822(); 34 | http::HttpTryFrom::try_from(s.as_str()) 35 | } 36 | } 37 | 38 | impl Extract for HttpDateTime { 39 | type Future = Immediate; 40 | 41 | fn extract(ctx: &Context<'_>) -> Self::Future { 42 | use crate::codegen::Source::*; 43 | 44 | match ctx.callsite().source() { 45 | Header(header_name) => { 46 | let value = match ctx.request().headers().get(header_name) { 47 | Some(value) => value, 48 | None => return Immediate::err(Error::missing_argument()), 49 | }; 50 | 51 | let value = match value.to_str() { 52 | Ok(s) => s, 53 | Err(_) => return Immediate::err(Error::invalid_argument(&"invalid UTF-8 string")), 54 | }; 55 | 56 | match DateTime::parse_from_rfc2822(&value) { 57 | Ok(dt) => Immediate::ok(HttpDateTime::normalize(dt.with_timezone(&Utc))), 58 | Err(e) => Immediate::err(Error::invalid_argument(&e)), 59 | } 60 | } 61 | _ => unimplemented!("A HttpDateTime can only be extracted from the headers for now"), 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/extract/num.rs: -------------------------------------------------------------------------------- 1 | use crate::extract::{Extract, Error, Context, Immediate}; 2 | use crate::util::BufStream; 3 | 4 | use atoi::atoi; 5 | use checked::Checked; 6 | 7 | use std::error::Error as E; 8 | use std::str::FromStr; 9 | 10 | macro_rules! num_extract_impls { 11 | ($($num:ident),+) => { 12 | $( 13 | impl Extract for $num { 14 | type Future = Immediate<$num>; 15 | 16 | fn extract(ctx: &Context<'_>) -> Self::Future { 17 | use crate::codegen::Source::*; 18 | 19 | match ctx.callsite().source() { 20 | Capture(idx) => { 21 | let path = ctx.request().uri().path(); 22 | let capture = ctx.captures().get(*idx, path); 23 | 24 | $num::from_str(capture).map_err(|err| { 25 | Error::invalid_argument(&err.description()) 26 | }).into() 27 | } 28 | Header(header_name) => { 29 | let value = match ctx.request().headers().get(header_name) { 30 | Some(value) => value, 31 | None => { 32 | return Immediate::err(Error::missing_argument()); 33 | } 34 | }; 35 | 36 | match atoi(value.as_bytes()) { 37 | Some(Checked(Some(s))) => Immediate::ok(s), 38 | _ => Immediate::err(Error::invalid_argument(&"invalid integer")), 39 | } 40 | } 41 | QueryString => { 42 | unimplemented!(); 43 | } 44 | Body => { 45 | unimplemented!(); 46 | } 47 | Unknown => { 48 | unimplemented!(); 49 | } 50 | } 51 | } 52 | } 53 | )+ 54 | } 55 | } 56 | 57 | // i128,u128 don't work because "no implementation for `checked::Checked * checked::Checked" 58 | num_extract_impls!(u8, u16, u32, u64, i8, i16, i32, i64); -------------------------------------------------------------------------------- /tests/empty.rs: -------------------------------------------------------------------------------- 1 | mod empty_impl { 2 | use tower_web::impl_web; 3 | 4 | #[derive(Clone, Debug)] 5 | struct Empty; 6 | 7 | impl_web! { 8 | impl Empty { 9 | } 10 | } 11 | 12 | #[test] 13 | fn use_type() { 14 | let _v = Empty; 15 | } 16 | } 17 | 18 | mod no_routes { 19 | use tower_web::impl_web; 20 | 21 | #[derive(Clone)] 22 | struct Empty; 23 | 24 | impl_web! { 25 | impl Empty { 26 | fn foo(&self) { 27 | } 28 | } 29 | } 30 | 31 | #[test] 32 | fn use_type() { 33 | let v = Empty; 34 | v.foo(); 35 | } 36 | } 37 | 38 | mod other_attr { 39 | use tower_web::impl_web; 40 | 41 | #[derive(Clone)] 42 | struct Empty; 43 | 44 | impl_web! { 45 | impl Empty { 46 | #[inline] 47 | fn foo(&self) { 48 | } 49 | } 50 | } 51 | 52 | #[test] 53 | fn use_type() { 54 | let v = Empty; 55 | v.foo(); 56 | } 57 | } 58 | 59 | mod one_route { 60 | use tower_web::impl_web; 61 | 62 | #[derive(Clone)] 63 | struct OneRoute; 64 | 65 | impl_web! { 66 | impl OneRoute { 67 | #[get("/")] 68 | fn foo(&self) -> Result { 69 | Ok("foo".to_string()) 70 | } 71 | } 72 | } 73 | 74 | #[test] 75 | fn use_type() { 76 | let v = OneRoute; 77 | assert_eq!("foo", v.foo().unwrap()); 78 | } 79 | } 80 | 81 | /* 82 | #[derive(Clone)] 83 | struct Empty; 84 | 85 | impl_web! { 86 | impl Empty { 87 | 88 | // These should not parse due to an invalid fn sig 89 | 90 | /// @GET("/") 91 | fn foo() { 92 | } 93 | 94 | /// @GET("/:id") 95 | fn foo(id: u32) { 96 | } 97 | 98 | /// @GET("/") 99 | fn foo(self) { 100 | } 101 | 102 | /// @GET("/:id") 103 | fn foo(self, id: u32) { 104 | } 105 | 106 | /// @GET("/") 107 | fn foo(self: Box) { 108 | } 109 | 110 | /// @GET("/:id") 111 | fn foo(self: Box, id: u32) { 112 | } 113 | } 114 | } 115 | */ 116 | 117 | // Additional tests: 118 | // * Passing arg that does not impl `Extract` 119 | // * No function generics 120 | -------------------------------------------------------------------------------- /tests/deflate_stream.rs: -------------------------------------------------------------------------------- 1 | use tower_web::util::buf_stream::BufStream; 2 | use tower_web::util::buf_stream::deflate::CompressStream; 3 | 4 | use bytes::Bytes; 5 | use futures::{Poll, Future}; 6 | use flate2::Compression; 7 | use flate2::read::DeflateDecoder; 8 | use rand::{thread_rng, Rng}; 9 | use rand::distributions::{Distribution, Standard}; 10 | 11 | use std::cmp; 12 | use std::io; 13 | use std::io::prelude::*; 14 | 15 | macro_rules! assert_round_trip { 16 | ($stream:expr, $expect:expr) => {{ 17 | let data: Vec = $stream.collect().wait().unwrap(); 18 | let mut decoder = DeflateDecoder::new(&data[..]); 19 | let mut actual = vec![]; 20 | decoder.read_to_end(&mut actual).unwrap(); 21 | assert_eq!(&actual[..], &$expect[..]); 22 | }} 23 | } 24 | 25 | #[test] 26 | fn single_chunk() { 27 | let data: Vec = Standard.sample_iter(&mut thread_rng()) 28 | .take(16 * 1024) 29 | .collect(); 30 | 31 | let deflate = CompressStream::new( 32 | Mock::single_chunk(data.clone()), 33 | Compression::fast()); 34 | 35 | assert_round_trip!(deflate, data); 36 | } 37 | 38 | #[test] 39 | fn multi_chunk() { 40 | let data: Vec = Standard.sample_iter(&mut thread_rng()) 41 | .take(16 * 1024) 42 | .collect(); 43 | 44 | let deflate = CompressStream::new( 45 | Mock::rand_chunks(data.clone()), 46 | Compression::fast()); 47 | 48 | assert_round_trip!(deflate, data); 49 | } 50 | 51 | struct Mock { 52 | chunks: Vec, 53 | } 54 | 55 | impl Mock { 56 | pub fn single_chunk(data: Vec) -> Mock { 57 | Mock { chunks: vec![data.into()] } 58 | } 59 | 60 | pub fn rand_chunks(data: Vec) -> Mock { 61 | let mut data = Bytes::from(data); 62 | let max = data.len() / 4; 63 | 64 | let mut rng = thread_rng(); 65 | let mut chunks = vec![]; 66 | 67 | while !data.is_empty() { 68 | let n = cmp::min(rng.gen_range(1, max), data.len()); 69 | chunks.push(data.split_to(n)); 70 | } 71 | 72 | Mock { chunks } 73 | } 74 | } 75 | 76 | impl BufStream for Mock { 77 | type Item = io::Cursor; 78 | type Error = (); 79 | 80 | fn poll(&mut self) -> Poll, Self::Error> { 81 | if self.chunks.is_empty() { 82 | return Ok(None.into()); 83 | } 84 | 85 | let chunk = self.chunks.remove(0); 86 | let buf = io::Cursor::new(chunk); 87 | 88 | Ok(Some(buf).into()) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/response/serializer.rs: -------------------------------------------------------------------------------- 1 | use crate::response::{ContentType, SerializerContext}; 2 | use crate::util::tuple::Either2; 3 | 4 | use bytes::Bytes; 5 | use serde::Serialize; 6 | use void::Void; 7 | 8 | /// Serialize an HTTP response body 9 | /// 10 | /// `Serializer` values use one or more [Serde serializers][serde] to perform 11 | /// the actual serialization. 12 | /// 13 | /// The `Serializer` values are also responsible for mapping content-type values 14 | /// to Serde serializers. 15 | /// 16 | /// [serde]: https://docs.rs/serde/1.0.71/serde/trait.Serializer.html 17 | pub trait Serializer: Clone + Send + Sync + 'static + crate::util::Sealed { 18 | /// A token used by `Serializer` implementations to identify the specific 19 | /// serialization format to use when encoding a value. 20 | type Format: Clone + Send + Sync + 'static; 21 | 22 | /// Lookup a serializer and `HeaderValue` for the given `Content-Type` 23 | /// string. 24 | fn lookup(&self, name: &str) -> Option>; 25 | 26 | /// Serialize the value using the specified format. 27 | fn serialize(&self, value: &T, format: &Self::Format, context: &SerializerContext<'_>) 28 | -> Result 29 | where 30 | T: Serialize; 31 | } 32 | 33 | impl Serializer for () { 34 | type Format = Void; 35 | 36 | fn lookup(&self, _: &str) -> Option> { 37 | None 38 | } 39 | 40 | fn serialize(&self, _: &T, _: &Self::Format, _: &SerializerContext<'_>) 41 | -> Result 42 | where 43 | T: Serialize 44 | { 45 | unreachable!(); 46 | } 47 | } 48 | 49 | impl Serializer for (T, U) 50 | where 51 | T: Serializer, 52 | U: Serializer, 53 | { 54 | type Format = Either2; 55 | 56 | fn lookup(&self, name: &str) -> Option> { 57 | if let Some(content_type) = self.0.lookup(name) { 58 | return Some(content_type.map(Either2::A)); 59 | } 60 | 61 | self.1.lookup(name) 62 | .map(|content_type| content_type.map(Either2::B)) 63 | } 64 | 65 | fn serialize(&self, value: &V, format: &Self::Format, context: &SerializerContext<'_>) 66 | -> Result 67 | where 68 | V: Serialize 69 | { 70 | match *format { 71 | Either2::A(ref format) => self.0.serialize(value, format, context), 72 | Either2::B(ref format) => self.1.serialize(value, format, context), 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/extract/osstring.rs: -------------------------------------------------------------------------------- 1 | use crate::extract::{Context, Error, Extract, Immediate}; 2 | use percent_encoding; 3 | use std::borrow::Cow; 4 | use std::ffi::{OsStr, OsString}; 5 | use std::str; 6 | use crate::util::buf_stream::BufStream; 7 | 8 | fn osstr_from_bytes(bytes: &[u8]) -> Result<&OsStr, Error> { 9 | // NOTE: this is too conservative, as we are rejecting valid paths on Unix 10 | str::from_utf8(bytes) 11 | .map_err(|e| Error::invalid_argument(&e)) 12 | .map(|s| OsStr::new(s)) 13 | } 14 | 15 | fn decode(s: &str) -> Result { 16 | let percent_decoded = Cow::from(percent_encoding::percent_decode(s.as_bytes())); 17 | Ok(osstr_from_bytes(percent_decoded.as_ref())?.to_os_string()) 18 | } 19 | 20 | impl Extract for OsString { 21 | type Future = Immediate; 22 | 23 | fn extract(ctx: &Context<'_>) -> Self::Future { 24 | use crate::codegen::Source::*; 25 | 26 | match ctx.callsite().source() { 27 | Capture(idx) => { 28 | let path = ctx.request().uri().path(); 29 | let value = ctx.captures().get(*idx, path); 30 | 31 | Immediate::result(decode(value)) 32 | } 33 | Header(header_name) => { 34 | let value = match ctx.request().headers().get(header_name) { 35 | Some(value) => value, 36 | None => { 37 | return Immediate::err(Error::missing_argument()); 38 | } 39 | }; 40 | 41 | let r = value 42 | .to_str() 43 | .map(OsString::from) 44 | .map_err(|e| Error::invalid_argument(&e)); 45 | Immediate::result(r) 46 | } 47 | QueryString => { 48 | let query = ctx.request().uri().query().unwrap_or(""); 49 | 50 | Immediate::result(decode(query)) 51 | } 52 | Body => { 53 | unimplemented!(); 54 | } 55 | Unknown => { 56 | unimplemented!(); 57 | } 58 | } 59 | } 60 | } 61 | 62 | #[cfg(test)] 63 | mod test { 64 | use super::*; 65 | use std::path::Path; 66 | 67 | #[test] 68 | fn extract() { 69 | assert_eq!(Path::new("hello, world"), decode("hello,%20world").unwrap()); 70 | } 71 | 72 | #[test] 73 | fn disallows_path_traversal() { 74 | assert_eq!(decode("foo").unwrap(), OsString::from("foo")); 75 | assert_eq!(decode("foo%20bar").unwrap(), OsString::from("foo bar")); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.3.7 (April 10, 2019) 2 | 3 | ### Fixed 4 | - fix panics with non-ASCII characters in routes (#206). 5 | - fix build on newer rustc versions (#205). 6 | 7 | ### Changed 8 | - duplicate routes are now detected at compile-time (#195). 9 | 10 | # 0.3.6 (March 13, 2019) 11 | 12 | ### Fixed 13 | - fix build on newer rustc versions (#193). 14 | 15 | ### Added 16 | - `Extract` implementation for `serde_json::Value` (#191). 17 | 18 | # 0.3.5 (February 25, 2019) 19 | 20 | ### Added 21 | - Try to detect response content-type (#187). 22 | 23 | # 0.3.4 (January 25, 2019) 24 | 25 | ### Added 26 | - Support extracting a string from a body (#158). 27 | - `rustls` optional support (#160). 28 | - Log 4xx responses (#164). 29 | - `Response` implementation for `Result` (#163). 30 | - Support handlers with large numbers of arguments (#170). 31 | - RFC7807: Problem details for HTTP APIs (#171). 32 | 33 | ### Fixed 34 | - Fix build on older Rust versions (#169, #172). 35 | - Parse `Content-Type` header correctly (#179). 36 | 37 | # 0.3.3 (November 17, 2018) 38 | 39 | * Allow template directory to be specified with env var (#139). 40 | * Implement `Response` for `Option` and `Vec` (#150). 41 | * Use 8 KiB as default chunk size when streaming files (#152). 42 | * Misc codegen tweaks (#155, #151). 43 | 44 | # 0.3.2 (October 18, 2018) 45 | 46 | * Support generics on response types (#144) 47 | * Support generics on resource types (#143) 48 | * Percent-decode Strings and PathBufs (#108) 49 | 50 | # 0.3.1 (October 10, 2018) 51 | 52 | * Fix panic when content-type not provided (#123). 53 | * Implement `Extract` for all numeric types (#131). 54 | * Ignore attributes for other derives (#130). 55 | * Avoid clone when logging disabled (#126). 56 | * Add non-blocking `serve` method to run server (#76). 57 | 58 | # 0.3.0 (September 28, 2018) 59 | 60 | * Add experimental async/await support (#119). 61 | * Add template support (#115). 62 | * Fix potential int overflow when extracting numbers (#110). 63 | 64 | # 0.2.2 (September 7, 2018) 65 | 66 | * Add #[web(either)] to delegate Response to enum variants (#97) 67 | * Add deflate middleware (#101) 68 | * Add support for service level configuration (#98) 69 | 70 | # 0.2.1 (August 30, 2018) 71 | 72 | * Add CORS middleware (#61) 73 | * Support for application/x-www-form-urlencoded (#84). 74 | 75 | # 0.2.0 (August 14, 2018) 76 | 77 | * Enable true attributes on stable Rust (#59). 78 | * Rename HTTP trait alias functions (#64). 79 | 80 | # 0.1.2 (August 9, 2018) 81 | 82 | * Switch docs to S3. 83 | 84 | # 0.1.1 (August 9, 2018) 85 | 86 | * Allow warnings to make docs.rs happy. 87 | 88 | # 0.1.0 (August 9, 2018) 89 | 90 | * Initial release 91 | -------------------------------------------------------------------------------- /src/extract/pathbuf.rs: -------------------------------------------------------------------------------- 1 | use crate::extract::{Context, Error, Extract, Immediate}; 2 | use std::ffi::{OsStr, OsString}; 3 | use std::path::{self, Path, PathBuf}; 4 | use crate::util::buf_stream::BufStream; 5 | 6 | // https://www.owasp.org/index.php/Path_Traversal 7 | fn check_for_path_traversal(path: &Path) -> Result<(), Error> { 8 | use self::path::Component::*; 9 | 10 | let path_traversal_error = || Error::invalid_argument(&"Path traversal detected"); 11 | 12 | let mut depth = 0u32; 13 | for c in path.components() { 14 | match c { 15 | Prefix(_) | RootDir => { 16 | // Escaping to the root is immediately a failure 17 | Err(path_traversal_error())? 18 | } 19 | CurDir => { 20 | // no-op 21 | } 22 | ParentDir => { 23 | depth = match depth.checked_sub(1) { 24 | Some(v) => v, 25 | None => Err(path_traversal_error())?, 26 | } 27 | } 28 | Normal(_) => { 29 | depth += 1; 30 | } 31 | } 32 | } 33 | 34 | Ok(()) 35 | } 36 | 37 | fn decode(s: &OsStr) -> Result { 38 | let path = PathBuf::from(s); 39 | check_for_path_traversal(&path)?; 40 | Ok(path) 41 | } 42 | 43 | impl Extract for PathBuf { 44 | type Future = Immediate; 45 | 46 | fn extract(ctx: &Context<'_>) -> Self::Future { 47 | use crate::extract::ExtractFuture; 48 | 49 | let s = >::extract(ctx).extract(); 50 | Immediate::result(decode(&s)) 51 | } 52 | } 53 | 54 | #[cfg(test)] 55 | mod test { 56 | use super::*; 57 | use std::path::Path; 58 | 59 | #[test] 60 | fn extract() { 61 | assert_eq!( 62 | decode(OsStr::new("hello, world")).unwrap(), 63 | Path::new("hello, world") 64 | ); 65 | } 66 | 67 | #[test] 68 | fn disallows_path_traversal() { 69 | assert!(decode(OsStr::new("/")).unwrap_err().is_invalid_argument()); 70 | assert!(decode(OsStr::new("..")).unwrap_err().is_invalid_argument()); 71 | assert_eq!(decode(OsStr::new("a/..")).unwrap(), Path::new("a/..")); 72 | assert!( 73 | decode(OsStr::new("../a")) 74 | .unwrap_err() 75 | .is_invalid_argument() 76 | ); 77 | assert!( 78 | decode(OsStr::new("../a/b")) 79 | .unwrap_err() 80 | .is_invalid_argument() 81 | ); 82 | assert_eq!(decode(OsStr::new("a/../b")).unwrap(), Path::new("a/../b")); 83 | assert_eq!(decode(OsStr::new("a/b/..")).unwrap(), Path::new("a/b/..")); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /examples/static_file.rs: -------------------------------------------------------------------------------- 1 | /// Web service that responds using static files from disk. 2 | /// 3 | /// ## Overview 4 | /// 5 | /// Resources may respond with any type that implements the `Response` trait. 6 | /// Tower Web provides an implementation of `Response` for `tokio::fs::File`. 7 | /// So, to use a static file as an HTTP response, the resource return type is 8 | /// set to `tokio::fs::File`. 9 | /// 10 | /// ## Usage 11 | /// 12 | /// Run the example: 13 | /// 14 | /// cargo run --example static_file 15 | /// 16 | /// Then send a request: 17 | /// 18 | /// curl -v http://localhost:8080/ 19 | 20 | #[macro_use] 21 | extern crate tower_web; 22 | 23 | 24 | use std::{io, path::PathBuf}; 25 | use tokio::{fs::File, prelude::Future}; 26 | use tower_web::ServiceBuilder; 27 | 28 | #[derive(Clone, Debug)] 29 | pub struct SelfServing; 30 | 31 | impl_web! { 32 | impl SelfServing { 33 | #[get("/")] 34 | #[content_type("plain")] 35 | fn index(&self) -> impl Future { 36 | let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 37 | path.push(file!()); 38 | File::open(path) 39 | } 40 | 41 | // This is an example of what **not** to do. 42 | // 43 | // While a glob can be extracted from the path as a `String`, this will 44 | // ofer no protection against path traversal attacks. 45 | // 46 | // See below for the correct way to do it. 47 | // 48 | #[get("/unsafe-files/*relative_path")] 49 | #[content_type("plain")] 50 | fn unsafe_files(&self, relative_path: String) -> impl Future { 51 | let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 52 | // String does no checks for path traversal; do not use this in production! 53 | path.push(relative_path); 54 | File::open(path) 55 | } 56 | 57 | // Tower Web extracts to `PathBuf` by ensuring that "../" is safely 58 | // rejected. This prevents an attacker from accessing files outside of 59 | // the "public" directory. 60 | // 61 | #[get("/files/*relative_path")] 62 | #[content_type("plain")] 63 | fn files(&self, relative_path: PathBuf) -> impl Future { 64 | let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 65 | path.push(relative_path); 66 | File::open(path) 67 | } 68 | } 69 | } 70 | 71 | pub fn main() { 72 | let addr = "127.0.0.1:8080".parse().expect("Invalid address"); 73 | println!("Listening on http://{}", addr); 74 | 75 | ServiceBuilder::new() 76 | .resource(SelfServing) 77 | .run(&addr) 78 | .unwrap(); 79 | } 80 | -------------------------------------------------------------------------------- /tower-web-macros/src/resource/route.rs: -------------------------------------------------------------------------------- 1 | use crate::resource::{Arg, Attributes, Signature, TyTree}; 2 | 3 | use proc_macro2::TokenStream; 4 | use syn; 5 | use quote::quote; 6 | 7 | /// Represents a resource route 8 | #[derive(Debug)] 9 | pub(crate) struct Route { 10 | pub index: usize, 11 | 12 | sig: Signature, 13 | 14 | pub attributes: Attributes, 15 | } 16 | 17 | impl Route { 18 | pub fn new(index: usize, sig: Signature, attributes: Attributes) -> Self { 19 | Route { 20 | index, 21 | sig, 22 | attributes, 23 | } 24 | } 25 | 26 | pub fn ident(&self) -> &syn::Ident { 27 | self.sig.ident() 28 | } 29 | 30 | pub fn args(&self) -> &[Arg] { 31 | self.sig.args() 32 | } 33 | 34 | pub fn template(&self) -> Option<&str> { 35 | self.attributes.template() 36 | } 37 | 38 | /// Route builder fn call to add the route definition. 39 | pub fn build_route(&self, destination: TokenStream) -> TokenStream { 40 | let method = self.attributes.method_expr(); 41 | let path = self.attributes.path_expr(); 42 | 43 | quote! { 44 | .insert({ 45 | __tw::routing::Route::new(#destination) 46 | .method(#method) 47 | .path(#path) 48 | }) 49 | } 50 | } 51 | 52 | pub fn dispatch_fn(&self) -> TokenStream { 53 | TyTree::new(self.args()) 54 | .extract_args() 55 | } 56 | 57 | pub fn dispatch(&self) -> TokenStream { 58 | // Because the arguments *might* be closed over into a trait object that may or may not be 59 | // send, the values must be extracted eagerly. 60 | // 61 | // To do this, the data is extracted from the futures into a tuple. The tuple is closed 62 | // over, which no longer has the problem of being `Send`. 63 | 64 | let args_outer = self.sig.args().iter().map(|arg| { 65 | let index = syn::Index::from(arg.index); 66 | quote! { __tw::extract::ExtractFuture::extract(args.#index) } 67 | }); 68 | 69 | let args_inner = self.sig.args().iter().map(|arg| { 70 | let index = syn::Index::from(arg.index); 71 | quote! { args.#index } 72 | }); 73 | 74 | let body = self.sig.dispatch( 75 | quote!(self.inner), 76 | args_inner); 77 | 78 | quote! { 79 | let args = args.into_inner(); 80 | let args = (#(#args_outer,)*); 81 | #body 82 | } 83 | } 84 | 85 | pub fn handler_args_ty(&self) -> TokenStream { 86 | TyTree::new(self.args()) 87 | .extract_args_ty() 88 | } 89 | 90 | /// The response future type 91 | pub fn future_ty(&self) -> TokenStream { 92 | self.sig.future_ty() 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # `tower-web` usage examples. 2 | 3 | This directory contains a number of examples showcasing various capabilities of 4 | `tower-web`. 5 | 6 | All examples can be executed with: 7 | 8 | ``` 9 | cargo run --example $name 10 | ``` 11 | 12 | It is recommended to explore the examples in (approximately) the following 13 | order: 14 | 15 | * [`hello_world`](hello_world.rs) - getting started with `tower_web`. This 16 | demonstrates how to get a basic web service running. 17 | 18 | * [`args`](args.rs) - Handler arguments are populated using the HTTP request. 19 | 20 | * [`derive_extract`](derive_extract.rs) - Custom type handler arguments are 21 | populated using the HTTP request. 22 | 23 | * [`json`](json.rs) - Receiving and responding with JSON. This example also 24 | shows how to customize the HTTP response status and headers. 25 | 26 | * [`static_file`](static_file.rs) - Respond with static files from disk. This 27 | examplee also shows glob path parameteres. 28 | 29 | * [`middleware`](middleware.rs) - Decorate the application with middleware. 30 | Doing so adds additional functionality. This example adds request logging. 31 | 32 | * [`html_handlebars`](html_handlebars.rs) - Respond with HTML by rendering 33 | handlebars templates. 34 | 35 | Tower Web provides experimental support for Rust's `async` / `await` 36 | syntax. To use this syntax, the Rust nightly release channel is required 37 | and the crate must be set to the 2018 edition. 38 | 39 | The example in the [`async-await`] directory contains a [`Cargo.toml`] 40 | as well as code. 41 | 42 | 1) Add [`edition = 2018`][2018] to your `Cargo.toml`. 43 | 2) Add [`features = ["async-await-preview"]`][feature] to the 44 | `tower-web` dependency. 45 | 3) Use the necessary [nightly features] in the application. 46 | 4) Import Tokio's [`await!` macro][await]. * 47 | 5) Define [`async`][async-handler] handlers. 48 | 49 | \* You should use nightly version prior to `nightly-2019-05-09`. 50 | As of that version the syntax change for `.await` syntax and `tokio` 0.1 51 | is probably not going to track nightly changes. 52 | 53 | Support for serving data over TLS is provided with the rustls feature. 54 | The [`rustls`](rustls) directory contains an example along with a 55 | [Cargo.toml](rustls/Cargo.toml) file. 56 | 57 | 1) Add [`features = ["rustls"]`](rustls/Cargo.toml) to the `tower-web` dependency. 58 | 2) Import [tokio-rustls](https://crates.io/crates/tokio-rustls). 59 | 3) Configure [TLSAcceptor](rustls/src/main.rs#L47). 60 | 4) Wrap [incoming TcpStream](rustls/src/main.rs#L66) and handle errors 61 | 62 | [`async-await`]: async-await 63 | [`Cargo.toml`]: async-await/Cargo.toml 64 | [2018]: async-await/Cargo.toml 65 | [feature]: async-await/Cargo.toml 66 | [nightly features]: async-await/src/hyper.rs#L22 67 | [await]: async-await/src/hyper.rs#L30 68 | [async-handler]: async-await/src/hyper.rs#L54 69 | -------------------------------------------------------------------------------- /tests/catch.rs: -------------------------------------------------------------------------------- 1 | use http; 2 | use tower_web::impl_web; 3 | 4 | mod support; 5 | use crate::support::*; 6 | 7 | #[derive(Clone, Debug)] 8 | struct TestCatch; 9 | 10 | #[derive(Clone, Debug)] 11 | struct TestDefaultCatch; 12 | 13 | impl_web! { 14 | impl TestCatch { 15 | #[get("/buggy")] 16 | fn buggy(&self) -> Result { 17 | Err(()) 18 | } 19 | 20 | #[get("/not_buggy")] 21 | fn not_buggy(&self) -> Result { 22 | Ok("not buggy".to_string()) 23 | } 24 | 25 | #[catch] 26 | fn catch(&self) -> Result<&'static str, ()> { 27 | Ok("catch") 28 | } 29 | } 30 | 31 | impl TestDefaultCatch { 32 | #[get("/buggy")] 33 | fn buggy(&self) -> Result { 34 | Err(()) 35 | } 36 | } 37 | } 38 | 39 | #[test] 40 | fn catch_error() { 41 | let mut web = service(TestCatch); 42 | 43 | let response = web.call_unwrap(get!("/buggy")); 44 | assert_ok!(response); 45 | assert_body!(response, "catch"); 46 | } 47 | 48 | #[test] 49 | fn success() { 50 | let mut web = service(TestCatch); 51 | 52 | let response = web.call_unwrap(get!("/not_buggy")); 53 | assert_ok!(response); 54 | assert_body!(response, "not buggy"); 55 | } 56 | 57 | #[test] 58 | fn default_catch_internal() { 59 | let mut web = service(TestDefaultCatch); 60 | 61 | let response = web.call_unwrap(get!("/buggy")); 62 | assert_internal_error!(response); 63 | assert_body!(response, r#"{"type":"about:blank","title":"Internal Server Error"}"#); 64 | } 65 | 66 | #[test] 67 | fn default_catch_not_found() { 68 | let mut web = service(TestDefaultCatch); 69 | 70 | let response = web.call_unwrap(get!("/missing")); 71 | assert_not_found!(response); 72 | assert_body!(response, r#"{"type":"about:blank","title":"Not Found"}"#); 73 | } 74 | 75 | #[test] 76 | fn custom_global_catch() { 77 | use tower_service::NewService; 78 | 79 | let mut web = ::tower_web::ServiceBuilder::new() 80 | .resource(TestDefaultCatch) 81 | .catch(|_: &http::Request<()>, error: ::tower_web::Error| { 82 | assert!(error.status_code() == http::StatusCode::NOT_FOUND); 83 | 84 | let response = http::response::Builder::new() 85 | .status(http::StatusCode::NOT_FOUND) 86 | .header("content-type", "text/plain") 87 | .body("where you at?") 88 | .unwrap(); 89 | 90 | Ok(response) 91 | }) 92 | .build_new_service() 93 | .new_service() 94 | .wait().unwrap(); 95 | 96 | let response = web.call_unwrap(get!("/missing")); 97 | assert_not_found!(response); 98 | assert_body!(response, "where you at?"); 99 | } 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tower Web 2 | 3 | A web framework for Rust with a focus on removing boilerplate. 4 | 5 | [![Build Status](https://travis-ci.org/carllerche/tower-web.svg?branch=master)](https://travis-ci.org/carllerche/tower-web) 6 | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) 7 | [![Crates.io](https://img.shields.io/crates/v/tower-web.svg?maxAge=2592000)](https://crates.io/crates/tower-web) 8 | [![Gitter](https://badges.gitter.im/tower-rs/tower.svg)](https://gitter.im/tower-rs/tower) 9 | 10 | [API Documentation][dox] 11 | 12 | Tower Web is: 13 | 14 | * **Fast**: Fully asynchronous, built on [Tokio] and [Hyper]. 15 | * **Ergonomic**: Tower-web decouples HTTP from your application logic, removing 16 | all boilerplate. 17 | * **Works on Rust stable**: You can use it today. 18 | 19 | [Tokio]: https://github.com/tokio-rs/tokio 20 | [Hyper]: http://github.com/hyperium/hyper 21 | [dox]: https://docs.rs/tower-web/0.3.7/tower_web/ 22 | 23 | ## Hello World 24 | 25 | ```rust 26 | #[macro_use] 27 | extern crate tower_web; 28 | extern crate tokio; 29 | 30 | use tower_web::ServiceBuilder; 31 | use tokio::prelude::*; 32 | 33 | /// This type will be part of the web service as a resource. 34 | #[derive(Clone, Debug)] 35 | struct HelloWorld; 36 | 37 | /// This will be the JSON response 38 | #[derive(Response)] 39 | struct HelloResponse { 40 | message: &'static str, 41 | } 42 | 43 | impl_web! { 44 | impl HelloWorld { 45 | #[get("/")] 46 | #[content_type("json")] 47 | fn hello_world(&self) -> Result { 48 | Ok(HelloResponse { 49 | message: "hello world", 50 | }) 51 | } 52 | } 53 | } 54 | 55 | pub fn main() { 56 | let addr = "127.0.0.1:8080".parse().expect("Invalid address"); 57 | println!("Listening on http://{}", addr); 58 | 59 | ServiceBuilder::new() 60 | .resource(HelloWorld) 61 | .run(&addr) 62 | .unwrap(); 63 | } 64 | ``` 65 | 66 | ## Overview 67 | 68 | Tower Web aims to decouple all HTTP concepts from the application logic. You 69 | define a "plain old Rust method" (PORM?). This method takes only the data it 70 | needs to complete and returns a struct representing the response. Tower Web does 71 | the rest. 72 | 73 | The `impl_web` macro looks at the definition and generates the glue code, 74 | allowing the method to respond to HTTP requests. 75 | 76 | ## Getting Started 77 | 78 | The best way to get started is to read the [examples] and [API docs][dox]. 79 | 80 | [dox]: # 81 | [examples]: examples/ 82 | 83 | ## License 84 | 85 | This project is licensed under the [MIT license](LICENSE). 86 | 87 | ### Contribution 88 | 89 | Unless you explicitly state otherwise, any contribution intentionally submitted 90 | for inclusion in `tower-web` by you, shall be licensed as MIT, without any 91 | additional terms or conditions. 92 | -------------------------------------------------------------------------------- /tests/resource.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit="256"] 2 | 3 | use futures; 4 | use tower_web::{Serialize, Response, impl_web}; 5 | 6 | use std::fmt::Display; 7 | 8 | mod support; 9 | use crate::support::*; 10 | 11 | #[derive(Clone, Debug)] 12 | struct TestResource; 13 | 14 | #[derive(Clone, Debug)] 15 | struct HelloWorld(S); 16 | 17 | #[derive(Clone, Debug)] 18 | struct WhereHelloWorld(S); 19 | 20 | #[derive(Clone, Debug)] 21 | struct EmptyGeneric(S); 22 | 23 | #[derive(Clone, Debug)] 24 | struct EmptyWhere(S); 25 | 26 | #[derive(Response)] 27 | struct Inner(T); 28 | 29 | #[derive(Serialize)] 30 | struct GeneratedResource(T); 31 | 32 | #[derive(Serialize)] 33 | struct ResponseFuture { 34 | msg: &'static str, 35 | } 36 | 37 | impl_web! { 38 | impl TestResource { 39 | #[get("/impl_future")] 40 | #[content_type("plain")] 41 | fn impl_future(&self) -> impl Future { 42 | use futures::IntoFuture; 43 | Ok("impl_future").into_future() 44 | } 45 | 46 | #[get("/inner")] 47 | #[content_type("json")] 48 | fn inner(&self) -> Result>, ()> { 49 | Ok(Inner(GeneratedResource(ResponseFuture { 50 | msg: "hello", 51 | }))) 52 | } 53 | } 54 | 55 | impl HelloWorld { 56 | #[get("/")] 57 | fn hello(&self) -> Result { 58 | let r = self.0.to_string(); 59 | Ok(r) 60 | } 61 | } 62 | 63 | impl WhereHelloWorld 64 | where 65 | S: Display, 66 | { 67 | #[get("/")] 68 | fn hello(&self) -> Result { 69 | let r = self.0.to_string(); 70 | Ok(r) 71 | } 72 | } 73 | 74 | // Just make sure this compiles 75 | impl EmptyGeneric { 76 | } 77 | 78 | impl EmptyWhere where S: Display { 79 | } 80 | } 81 | 82 | #[test] 83 | fn impl_future() { 84 | let mut web = service(TestResource); 85 | 86 | let response = web.call_unwrap(get!("/impl_future")); 87 | assert_ok!(response); 88 | assert_body!(response, "impl_future"); 89 | } 90 | 91 | #[test] 92 | fn generic() { 93 | let mut web = service(HelloWorld("hello")); 94 | 95 | let response = web.call_unwrap(get!("/")); 96 | assert_ok!(response); 97 | assert_body!(response, "hello"); 98 | 99 | let mut web = service(WhereHelloWorld("hello")); 100 | 101 | let response = web.call_unwrap(get!("/")); 102 | assert_ok!(response); 103 | assert_body!(response, "hello"); 104 | } 105 | 106 | #[test] 107 | fn inner_type() { 108 | let mut web = service(TestResource); 109 | 110 | let response = web.call_unwrap(get!("/inner")); 111 | assert_ok!(response); 112 | assert_body!(response, r#"{"msg":"hello"}"#); 113 | } 114 | -------------------------------------------------------------------------------- /src/extract/str.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::CallSite; 2 | use crate::extract::{Context, Error, Extract, ExtractFuture}; 3 | use crate::extract::bytes::ExtractBytes; 4 | use percent_encoding; 5 | use std::borrow::Cow; 6 | use crate::util::BufStream; 7 | 8 | use futures::{Poll, Async, try_ready}; 9 | 10 | #[derive(Debug)] 11 | pub struct ExtractString { 12 | inner: Option, B>>, 13 | decode: bool, 14 | item: Option, 15 | } 16 | 17 | impl Extract for String { 18 | type Future = ExtractString; 19 | 20 | fn extract(ctx: &Context<'_>) -> Self::Future { 21 | use crate::codegen::Source::*; 22 | 23 | let inner = Vec::extract(ctx); 24 | 25 | match ctx.callsite().source() { 26 | Capture(_) | QueryString => { 27 | ExtractString { 28 | inner: Some(inner), 29 | decode: true, 30 | item: None, 31 | } 32 | } 33 | _ => { 34 | ExtractString { 35 | inner: Some(inner), 36 | decode: false, 37 | item: None, 38 | } 39 | } 40 | } 41 | } 42 | 43 | fn extract_body(ctx: &Context<'_>, body: B) -> Self::Future { 44 | ExtractString { 45 | inner: Some(Vec::extract_body(ctx, body)), 46 | decode: false, 47 | item: None, 48 | } 49 | } 50 | 51 | fn requires_body(callsite: &CallSite) -> bool { 52 | as Extract>::requires_body(callsite) 53 | } 54 | } 55 | 56 | impl ExtractFuture for ExtractString 57 | where 58 | B: BufStream, 59 | { 60 | type Item = String; 61 | 62 | fn poll(&mut self) -> Poll<(), Error> { 63 | try_ready!(self.inner.as_mut().unwrap().poll()); 64 | 65 | let bytes = self.inner.take().unwrap().extract(); 66 | 67 | let mut string = String::from_utf8(bytes) 68 | .map_err(|_| { 69 | Error::invalid_argument(&"invalid UTF-8 string") 70 | })?; 71 | 72 | if self.decode { 73 | string = decode(&string)?; 74 | } 75 | 76 | self.item = Some(string); 77 | 78 | Ok(Async::Ready(())) 79 | } 80 | 81 | fn extract(self) -> String { 82 | self.item.unwrap() 83 | } 84 | } 85 | 86 | fn decode(s: &str) -> Result { 87 | percent_encoding::percent_decode(s.as_bytes()) 88 | .decode_utf8() 89 | .map(Cow::into_owned) 90 | .map_err(|e| Error::invalid_argument(&e)) 91 | } 92 | 93 | #[cfg(test)] 94 | mod test { 95 | use super::*; 96 | 97 | #[test] 98 | fn extract() { 99 | assert_eq!("hello, world", decode("hello,%20world").unwrap()); 100 | assert!(decode("%ff").unwrap_err().is_invalid_argument()); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/util/buf_stream/size_hint.rs: -------------------------------------------------------------------------------- 1 | //! `SizeHint` type and builder 2 | 3 | /// A `BufStream` size hint 4 | /// 5 | /// The default implementation returns: 6 | /// 7 | /// * 0 for `available` 8 | /// * 0 for `lower` 9 | /// * `None` for `upper`. 10 | #[derive(Debug, Default, Clone)] 11 | pub struct SizeHint { 12 | available: usize, 13 | lower: usize, 14 | upper: Option, 15 | } 16 | 17 | /// Build a `SizeHint` 18 | #[derive(Debug)] 19 | pub struct Builder { 20 | hint: SizeHint, 21 | } 22 | 23 | impl Builder { 24 | /// Create a new `Builder` configured with default values. 25 | pub fn new() -> Builder { 26 | Builder { 27 | hint: SizeHint { 28 | available: 0, 29 | lower: 0, 30 | upper: None, 31 | }, 32 | } 33 | } 34 | 35 | /// Sets the `available` hint value. 36 | pub fn available(&mut self, val: usize) -> &mut Self { 37 | self.hint.available = val; 38 | 39 | if self.hint.lower < val { 40 | self.hint.lower = val; 41 | 42 | match self.hint.upper { 43 | Some(ref mut upper) if *upper < val => { 44 | *upper = val; 45 | } 46 | _ => {} 47 | } 48 | } 49 | 50 | self 51 | } 52 | 53 | /// Sets the `lower` hint value. 54 | /// 55 | /// # Panics 56 | /// 57 | /// This function panics if `val` is smaller than `available`. 58 | pub fn lower(&mut self, val: usize) -> &mut Self { 59 | assert!(val >= self.hint.available); 60 | 61 | self.hint.lower = val; 62 | self 63 | } 64 | 65 | /// Set the `upper` hint value. 66 | /// 67 | /// # Panics 68 | /// 69 | /// This function panics if `val` is smaller than `lower`. 70 | pub fn upper(&mut self, val: usize) -> &mut Self { 71 | // There is no need to check `available` as that is guaranteed to be 72 | // less than or equal to `lower`. 73 | assert!(val >= self.hint.lower, "`val` is smaller than `lower`"); 74 | 75 | self.hint.upper = Some(val); 76 | self 77 | } 78 | 79 | /// Build the `SizeHint` 80 | pub fn build(&self) -> SizeHint { 81 | self.hint.clone() 82 | } 83 | } 84 | 85 | impl SizeHint { 86 | /// Returns the **lower bound** of the amount of data that can be read from 87 | /// the `BufStream` without `NotReady` being returned. 88 | /// 89 | /// It is possible that more data is currently available. 90 | pub fn available(&self) -> usize { 91 | self.available 92 | } 93 | 94 | /// Returns the lower bound of data that the `BufStream` will yield before 95 | /// completing. 96 | pub fn lower(&self) -> usize { 97 | self.lower 98 | } 99 | 100 | /// Returns the upper bound of data the `BufStream` will yield before 101 | /// completing, or `None` if the value is unknown. 102 | pub fn upper(&self) -> Option { 103 | self.upper 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /tests/params.rs: -------------------------------------------------------------------------------- 1 | use tower_web::impl_web; 2 | 3 | mod support; 4 | use crate::support::*; 5 | 6 | #[derive(Clone, Debug)] 7 | struct TestParams; 8 | 9 | impl_web! { 10 | impl TestParams { 11 | #[get("/str/:foo")] 12 | #[content_type("plain")] 13 | fn one_str_param(&self, foo: String) -> Result<&'static str, ()> { 14 | assert_eq!(foo, "hello"); 15 | Ok("one_str_param") 16 | } 17 | 18 | #[get("/x-hello")] 19 | #[content_type("plain")] 20 | fn one_str_header(&self, x_hello: String) -> Result<&'static str, ()> { 21 | assert_eq!(x_hello, "world"); 22 | Ok("one_str_header") 23 | } 24 | 25 | #[get("/u32/:foo")] 26 | #[content_type("plain")] 27 | fn one_u32_param(&self, foo: u32) -> Result<&'static str, ()> { 28 | assert_eq!(foo, 123); 29 | Ok("one_u32_param") 30 | } 31 | 32 | #[post("/content_length")] 33 | #[content_type("plain")] 34 | fn one_u32_header(&self, content_length: u32) -> Result<&'static str, ()> { 35 | assert_eq!(content_length, 5); 36 | Ok("one_u32_header") 37 | } 38 | 39 | #[get("/option_hdr")] 40 | #[content_type("plain")] 41 | fn option_header(&self, user_agent: Option) -> Result<&'static str, ()> { 42 | if let Some(user_agent) = user_agent { 43 | assert_eq!(user_agent, "testin"); 44 | Ok("option_header - some") 45 | } else { 46 | Ok("option_header - none") 47 | } 48 | } 49 | } 50 | } 51 | 52 | // TODO: 53 | // - header missing 54 | 55 | #[test] 56 | fn one_str_param() { 57 | let mut web = service(TestParams); 58 | 59 | let response = web.call_unwrap(get!("/str/hello")); 60 | assert_ok!(response); 61 | assert_body!(response, "one_str_param"); 62 | } 63 | 64 | #[test] 65 | fn one_str_header() { 66 | let mut web = service(TestParams); 67 | 68 | 69 | let response = web.call_unwrap(get!("/x-hello", "x-hello": "world")); 70 | assert_ok!(response); 71 | assert_body!(response, "one_str_header"); 72 | } 73 | 74 | #[test] 75 | fn one_u32_param() { 76 | let mut web = service(TestParams); 77 | 78 | let response = web.call_unwrap(get!("/u32/123")); 79 | assert_ok!(response); 80 | assert_body!(response, "one_u32_param"); 81 | } 82 | 83 | #[test] 84 | fn one_u32_header() { 85 | let mut web = service(TestParams); 86 | 87 | let response = web.call_unwrap(post!("/content_length", "hello", "content-length": "5")); 88 | assert_ok!(response); 89 | assert_body!(response, "one_u32_header"); 90 | } 91 | 92 | #[test] 93 | fn option_header() { 94 | let mut web = service(TestParams); 95 | 96 | let response = web.call_unwrap(get!("/option_hdr")); 97 | assert_ok!(response); 98 | assert_body!(response, "option_header - none"); 99 | 100 | let response = web.call_unwrap(get!("/option_hdr", "user-agent": "testin")); 101 | 102 | assert_ok!(response); 103 | assert_body!(response, "option_header - some"); 104 | } 105 | -------------------------------------------------------------------------------- /examples/html_handlebars.rs: -------------------------------------------------------------------------------- 1 | /// Web service that receives and responds with HTML. 2 | /// 3 | /// ## Overview 4 | /// 5 | /// Tower web supports templates and responding with HTML using the handlebars 6 | /// templating engine. Plain old Rust structs are used to represent data and 7 | /// are used as the handler return value. Tower Web passes the response structs 8 | /// to the handlebars serializer. HTML is rendered using a handlebars template 9 | /// and populated using the data in the response struct. 10 | /// 11 | /// ## Usage 12 | /// 13 | /// Run the example: 14 | /// 15 | /// cargo run --example html_handlebars 16 | /// 17 | /// Then send a request: 18 | /// 19 | /// curl -v http://localhost:8080/ 20 | 21 | extern crate env_logger; 22 | #[macro_use] 23 | extern crate tower_web; 24 | 25 | use tower_web::ServiceBuilder; 26 | use tower_web::view::Handlebars; 27 | 28 | /// This type will be the web service implementation. 29 | #[derive(Clone, Debug)] 30 | struct HtmlResource; 31 | 32 | /// The type is annotated with `#[derive(Response)]`, this allows `MyResponse` 33 | /// to be used as a response to resource methods. 34 | /// 35 | /// We are using the handlebars serializer to render the HTML response. It 36 | /// requires that a template to render is specified. This is done with the 37 | /// `#[web(template = "