├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── examples ├── README.md ├── args.rs ├── async-await │ ├── .cargo │ │ └── config │ ├── Cargo.toml │ ├── rust-toolchain │ └── src │ │ └── hyper.rs ├── catch.rs ├── derive_extract.rs ├── hello_world.rs ├── html_handlebars.rs ├── json.rs ├── middleware.rs ├── rustls │ ├── Cargo.toml │ └── src │ │ └── main.rs └── static_file.rs ├── src ├── codegen.rs ├── codegen │ ├── async_await.rs │ └── callsite.rs ├── config.rs ├── error.rs ├── error │ ├── catch.rs │ ├── error.rs │ ├── map.rs │ └── never.rs ├── extract.rs ├── extract │ ├── bytes.rs │ ├── context.rs │ ├── error.rs │ ├── http.rs │ ├── http_date_time.rs │ ├── immediate.rs │ ├── num.rs │ ├── option.rs │ ├── osstring.rs │ ├── pathbuf.rs │ ├── serde.rs │ └── str.rs ├── lib.rs ├── middleware.rs ├── middleware │ ├── chain.rs │ ├── cors.rs │ ├── cors │ │ ├── builder.rs │ │ ├── config.rs │ │ ├── middleware.rs │ │ └── service.rs │ ├── deflate.rs │ ├── deflate │ │ ├── middleware.rs │ │ └── service.rs │ ├── identity.rs │ ├── log.rs │ ├── log │ │ ├── middleware.rs │ │ └── service.rs │ └── middleware.rs ├── net.rs ├── response.rs ├── response │ ├── content_type.rs │ ├── context.rs │ ├── default_serializer.rs │ ├── either.rs │ ├── file.rs │ ├── json.rs │ ├── option.rs │ ├── response.rs │ ├── serde.rs │ ├── serializer.rs │ ├── serializer_context.rs │ ├── str.rs │ └── vec.rs ├── routing.rs ├── routing │ ├── builder.rs │ ├── captures.rs │ ├── path.rs │ ├── resource.rs │ ├── route.rs │ ├── route_match.rs │ ├── service.rs │ └── set.rs ├── run.rs ├── service.rs ├── service │ ├── builder.rs │ ├── new_service.rs │ └── web.rs ├── util.rs ├── util │ ├── buf_stream.rs │ ├── buf_stream │ │ ├── buf_stream.rs │ │ ├── bytes.rs │ │ ├── chain.rs │ │ ├── collect.rs │ │ ├── deflate.rs │ │ ├── either.rs │ │ ├── empty.rs │ │ ├── file.rs │ │ ├── from.rs │ │ ├── size_hint.rs │ │ ├── std.rs │ │ └── str.rs │ ├── chain.rs │ ├── http.rs │ ├── http │ │ ├── future.rs │ │ ├── middleware.rs │ │ ├── new_service.rs │ │ └── service.rs │ ├── mime_types.rs │ ├── never.rs │ ├── sealed.rs │ └── tuple.rs ├── view.rs └── view │ └── handlebars.rs ├── templates └── examples │ └── hello_world.hbs ├── test_suite ├── Cargo.toml └── tests │ └── derive_rsponse.rs ├── tests ├── catch.rs ├── content_type.rs ├── deflate_stream.rs ├── empty.rs ├── extract.rs ├── html_handlebars.rs ├── methods.rs ├── multi_resource.rs ├── params.rs ├── resource.rs ├── response.rs ├── serializer.rs ├── support.rs └── templates │ └── hbs │ ├── bar.hbs │ └── foo.hbs ├── tower-web-macros ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md └── src │ ├── derive.rs │ ├── derive │ ├── attr.rs │ ├── extract.rs │ ├── response.rs │ ├── test.rs │ └── test │ │ ├── extract.rs │ │ └── response.rs │ ├── header.rs │ ├── lib.rs │ ├── resource.rs │ └── resource │ ├── arg.rs │ ├── attr.rs │ ├── catch.rs │ ├── parse.rs │ ├── resource.rs │ ├── route.rs │ ├── signature.rs │ ├── test.rs │ ├── test │ └── parse.rs │ └── ty_tree.rs └── util └── gen-tuple.rs /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | Cargo.lock 5 | **/local.cert.pem 6 | **/local.key.pem 7 | **/target/ 8 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /examples/async-await/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target-dir = "../../target" 3 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /examples/async-await/rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly-2019-05-08 -------------------------------------------------------------------------------- /examples/async-await/src/hyper.rs: -------------------------------------------------------------------------------- 1 | //! Service with async/await handlers. 2 | //! 3 | //! A service that demonstrates how to use Tower Web's experimental support for the upcoming 4 | //! async/await syntax. 5 | //! 6 | //! # Overview 7 | //! 8 | //! async/await enables easier writing of asynchronous code. Handler functions are prefaced with 9 | //! the `async` keyword. The async fn implementation may then use `await!` and call other `async` 10 | //! functions. It requires using the Rust nightly channel as well as opting into unstable features. 11 | //! 12 | //! ## Usage 13 | //! 14 | //! From within the `examples/async-await` directory, run the example: 15 | //! 16 | //! cargo +nightly run --bin hyper 17 | //! 18 | //! Then send a request: 19 | //! 20 | //! curl -v http://localhost:8080/ 21 | 22 | #![feature(await_macro, async_await)] 23 | 24 | use tokio::prelude::*; 25 | use tokio::await; 26 | use tower_web::ServiceBuilder; 27 | use tower_web::impl_web; 28 | 29 | use std::str; 30 | 31 | // The HTTP client 32 | type HttpClient = hyper::Client; 33 | 34 | /// This type will be part of the web service as a resource. 35 | #[derive(Clone, Debug)] 36 | pub struct HelloWorld { 37 | client: HttpClient, 38 | } 39 | 40 | /// To derive `Resource`, the implementation of `HelloWorld` is contained in the 41 | /// `impl_web!` macro. This macro does not modify any of its contents. It will 42 | /// inspect the implementation and, with the help of some annotations, generate 43 | /// the necessary glue code to run the web service. 44 | /// 45 | /// impl_web! is a temporary solution to enable tower-web to work with stable 46 | /// rust. In the near future, this will be transitioned to use attribute macros. 47 | impl_web! { 48 | impl HelloWorld { 49 | #[get("/")] 50 | async fn hello_world(&self) -> String { 51 | // Get the URI of the server by issuing a query to `/ip`. 52 | let uri = "http://httpbin.org/ip".parse().unwrap(); 53 | 54 | // Issue the request and wait for the response 55 | let response = await!(self.client.get(uri)).unwrap(); 56 | 57 | // Get the body component of the HTTP response. This is a stream and as such, it must 58 | // be asynchronously collected. 59 | let mut body = response.into_body(); 60 | 61 | // The body chunks will be appended to this string. 62 | let mut ret = String::new(); 63 | 64 | while let Some(chunk) = await!(body.next()) { 65 | let chunk = chunk.unwrap(); 66 | 67 | // Convert to a string 68 | let chunk = str::from_utf8(&chunk[..]).unwrap(); 69 | 70 | // Append to buffer 71 | ret.push_str(chunk); 72 | } 73 | 74 | // Return the collected string 75 | ret 76 | } 77 | } 78 | } 79 | 80 | pub fn main() { 81 | // Next, we must run our web service. 82 | // 83 | // The HTTP service will listen on this address and port. 84 | let addr = "127.0.0.1:8080".parse().expect("Invalid address"); 85 | println!("Listening on http://{}", addr); 86 | 87 | // A service builder is used to configure our service. 88 | ServiceBuilder::new() 89 | .resource(HelloWorld { 90 | client: hyper::Client::new(), 91 | }) 92 | .run(&addr) 93 | .unwrap(); 94 | } 95 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 = "