├── .gitignore ├── .travis.yml ├── Cargo.toml ├── examples ├── hello-world.rs └── simple-routing.rs ├── LICENSE ├── benches ├── bytes.rs ├── push_headers.rs ├── convert.rs ├── push_bytes.rs └── parse_request.rs ├── src ├── request.rs ├── response.rs └── lib.rs └── Readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | - beta 5 | - nightly 6 | script: 7 | - cargo build 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zap" 3 | version = "0.0.4" 4 | authors = ["Daniel Oltmanns "] 5 | description = "Ligthing fast web server library for rust" 6 | keywords = ["web-framework", "http", "efficiency"] 7 | categories = ["network-programming", "web-programming"] 8 | 9 | exclude = [".gitignore", ".travis.yml", "examples/*", "benches/*"] 10 | readme = "Readme.md" 11 | license = "MIT" 12 | repository = "https://github.com/oltdaniel/zap" 13 | 14 | [dependencies] 15 | bytes = "0.4.6" 16 | futures = "0.1.18" 17 | tokio-io = "0.1.5" 18 | tokio-proto = "0.1.1" 19 | tokio-service = "0.1.0" 20 | -------------------------------------------------------------------------------- /examples/hello-world.rs: -------------------------------------------------------------------------------- 1 | extern crate zap; 2 | 3 | use std::io::Error as ZapError; 4 | use zap::prelude::*; 5 | 6 | // Our HelloWorld Handler 7 | struct HelloWorld; 8 | 9 | impl Handler for HelloWorld { 10 | type Request = Request; 11 | type Response = Response; 12 | type Error = ZapError; 13 | type Future = ZapResult; 14 | 15 | fn call(&self, _: Request) -> ZapResult { 16 | // Create new Response 17 | let mut resp = Response::new(); 18 | 19 | // Set body, status is 200 by default 20 | resp.body("Hello World!"); 21 | 22 | // Send response 23 | resp.ok() 24 | } 25 | } 26 | 27 | fn main() { 28 | // Set address 29 | let addr = "0.0.0.0:8080".parse().unwrap(); 30 | 31 | // Create server 32 | let mut server = Server::new(Http, addr); 33 | 34 | // Set number of threads 35 | server.threads(8); 36 | 37 | // Serve the Handler 38 | server.serve(|| Ok(HelloWorld)); 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Daniel Oltmanns 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/simple-routing.rs: -------------------------------------------------------------------------------- 1 | extern crate zap; 2 | 3 | use std::io::Error as ZapError; 4 | use zap::prelude::*; 5 | 6 | // Our handler 7 | struct HelloWorld; 8 | 9 | impl Handler for HelloWorld { 10 | type Request = Request; 11 | type Response = Response; 12 | type Error = ZapError; 13 | type Future = ZapResult; 14 | 15 | fn call(&self, req: Request) -> ZapResult { 16 | // Create new Response 17 | let mut resp = Response::new(); 18 | let head = req.first(); 19 | 20 | // Different content, depending on route 21 | if head.starts_with(b"GET / HTTP/1.1\r") { 22 | resp.body_raw(b"Hello World"); 23 | } else if head.starts_with(b"GET /bye HTTP/1.1\r") { 24 | resp.body_raw(b"Bye Bye"); 25 | } else { 26 | resp.body_raw(b"Not Found"); 27 | resp.status(404); 28 | } 29 | 30 | // Send response 31 | resp.ok() 32 | } 33 | } 34 | 35 | fn main() { 36 | // Set address 37 | let addr = "0.0.0.0:8080".parse().unwrap(); 38 | 39 | // Create server 40 | let mut server = Server::new(Http, addr); 41 | 42 | // Set number of threads 43 | server.threads(8); 44 | 45 | // Serve the Handler 46 | server.serve(|| Ok(HelloWorld)); 47 | } 48 | -------------------------------------------------------------------------------- /benches/bytes.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | extern crate test; 3 | extern crate bytes; 4 | 5 | mod tests { 6 | use test::Bencher; 7 | use bytes::{BytesMut, BufMut}; 8 | 9 | #[bench] 10 | fn bench_allocate_pieces(b : &mut Bencher) { 11 | let mut buffer = BytesMut::new(); 12 | 13 | let data = b"This is some data!"; 14 | 15 | b.iter(|| { 16 | for _i in 0..100 { 17 | push(&mut buffer, data); 18 | } 19 | }); 20 | } 21 | 22 | #[bench] 23 | fn bench_allocate_pile(b : &mut Bencher) { 24 | let mut buffer = BytesMut::new(); 25 | 26 | let data = b"This is some data!"; 27 | let mut length = 0 as usize; 28 | 29 | b.iter(|| { 30 | length = data.len() * 100; 31 | buffer.reserve(length); 32 | 33 | for _i in 0..100 { 34 | unsafe { 35 | buffer.bytes_mut()[..data.len()].copy_from_slice(data.as_ref()); 36 | } 37 | } 38 | 39 | unsafe { 40 | buffer.advance_mut(length); 41 | } 42 | }); 43 | } 44 | 45 | fn push(buf: &mut BytesMut, data: &[u8]) { 46 | buf.reserve(data.len()); 47 | unsafe { 48 | buf.bytes_mut()[..data.len()].copy_from_slice(data); 49 | buf.advance_mut(data.len()); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /benches/push_headers.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | extern crate test; 3 | extern crate bytes; 4 | 5 | mod tests { 6 | use test::Bencher; 7 | use bytes::{BytesMut, BufMut}; 8 | 9 | #[bench] 10 | fn bench_vec_to_bytes_out(b : &mut Bencher) { 11 | let mut buffer = BytesMut::new(); 12 | 13 | b.iter(|| { 14 | let mut headers : Vec<(String, String)> = Vec::new(); 15 | 16 | headers.push(("Content-Type".to_string(), "text/plain".to_string())); 17 | headers.push(("X-Access-Token".to_string(), "XQB6h4MkjhJT9Mqde5kkdLupYU8MrL6d".to_string())); 18 | 19 | for &(ref n, ref v) in &headers { 20 | push(&mut buffer, n.as_bytes()); 21 | push(&mut buffer, b": "); 22 | push(&mut buffer, v.as_bytes()); 23 | push(&mut buffer, b"\r\n"); 24 | } 25 | }); 26 | } 27 | 28 | #[bench] 29 | fn bench_bytes_to_bytes_out(b : &mut Bencher) { 30 | let mut buffer = BytesMut::new(); 31 | 32 | b.iter(|| { 33 | let mut name = "Content-Type"; 34 | let mut value = "text/plain"; 35 | 36 | push(&mut buffer, name.as_bytes()); 37 | push(&mut buffer, b": "); 38 | push(&mut buffer, value.as_bytes()); 39 | push(&mut buffer, b"\r\n"); 40 | 41 | name = "X-Access-Token"; 42 | value = "XQB6h4MkjhJT9Mqde5kkdLupYU8MrL6d"; 43 | 44 | push(&mut buffer, name.as_bytes()); 45 | push(&mut buffer, b": "); 46 | push(&mut buffer, value.as_bytes()); 47 | push(&mut buffer, b"\r\n"); 48 | }); 49 | } 50 | 51 | fn push(buf: &mut BytesMut, data: &[u8]) { 52 | buf.reserve(data.len()); 53 | unsafe { 54 | buf.bytes_mut()[..data.len()].copy_from_slice(data); 55 | buf.advance_mut(data.len()); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /benches/convert.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | extern crate test; 3 | extern crate bytes; 4 | 5 | mod tests { 6 | use test::Bencher; 7 | use bytes::{BytesMut, BufMut}; 8 | 9 | #[bench] 10 | fn bench_usize_to_ascii(b : &mut Bencher) { 11 | let mut buffer = BytesMut::new(); 12 | let s : usize = 12345; 13 | 14 | b.iter(|| { 15 | let mut data : BytesMut = BytesMut::new(); 16 | let mut length = s as u32; 17 | 18 | loop { 19 | let c = 48 + (&length % 10 as u32) as u8; 20 | data.put_u8(c); 21 | length = (&length / 10 as u32) as u32; 22 | 23 | if length == 0 { 24 | break; 25 | } 26 | } 27 | 28 | data.reverse(); 29 | 30 | push(&mut buffer, data.as_ref()); 31 | }); 32 | } 33 | 34 | #[bench] 35 | fn bench_usize_to_string(b : &mut Bencher) { 36 | let mut buffer = BytesMut::new(); 37 | let s : usize = 12345; 38 | 39 | b.iter(|| { 40 | push(&mut buffer, s.to_string().as_bytes()); 41 | }) 42 | } 43 | 44 | #[bench] 45 | fn bench_limited_usize_to_ascii(b : &mut Bencher) { 46 | let mut buffer = BytesMut::new(); 47 | 48 | b.iter(|| { 49 | let mut data : [u8; 6] = [0; 6]; 50 | let mut length : usize = 123456; 51 | 52 | for i in 0..5 { 53 | data[5 - i] = 48 + (&length % 10) as u8; 54 | length = (&length / 10) as usize; 55 | 56 | if length <= 9 { 57 | data[4 - i] = 48 + length as u8; 58 | push(&mut buffer, &data[(4 - i)..6]); 59 | break; 60 | } 61 | } 62 | }); 63 | } 64 | 65 | fn push(buf : &mut BytesMut, data : &[u8]) { 66 | buf.reserve(data.len()); 67 | 68 | unsafe { 69 | buf.bytes_mut()[..data.len()].copy_from_slice(data); 70 | buf.advance_mut(data.len()); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /benches/push_bytes.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | extern crate test; 3 | extern crate bytes; 4 | 5 | mod tests { 6 | use test::Bencher; 7 | use bytes::{BytesMut, BufMut}; 8 | use std::fmt::{self, Write}; 9 | 10 | #[bench] 11 | fn bench_push_to_buffer(b : &mut Bencher) { 12 | let mut buffer = BytesMut::new(); 13 | 14 | b.iter(|| push(&mut buffer, &[72, 84, 84, 80, 47, 49, 46, 49, 32])); 15 | } 16 | 17 | #[bench] 18 | fn bench_push_bstr_to_buffer(b : &mut Bencher) { 19 | let mut buffer = BytesMut::new(); 20 | 21 | b.iter(|| push(&mut buffer, b"HTTP/1.1 ")); 22 | } 23 | 24 | #[bench] 25 | fn bench_extend_from_slice(b : &mut Bencher) { 26 | let mut buffer = BytesMut::new(); 27 | 28 | b.iter(|| buffer.extend_from_slice(&[72, 84, 84, 80, 47, 49, 46, 49, 32])) 29 | } 30 | 31 | #[bench] 32 | fn bench_fastwrite(b : &mut Bencher) { 33 | let mut buffer = BytesMut::new(); 34 | 35 | b.iter(|| write!(FastWrite(&mut buffer), "HTTP/1.1 ").unwrap() ) 36 | } 37 | 38 | #[bench] 39 | fn bench_all_combined(b : &mut Bencher) { 40 | let mut buffer = BytesMut::new(); 41 | 42 | b.iter(|| { 43 | push(&mut buffer, &[72, 84, 84]); 44 | buffer.extend_from_slice(&[80, 47, 49]); 45 | write!(FastWrite(&mut buffer), ".1 ").unwrap(); 46 | }); 47 | } 48 | 49 | #[bench] 50 | fn bench_string_to_push(b : &mut Bencher) { 51 | let mut buffer = BytesMut::new(); 52 | let data = String::from("200 OK"); 53 | 54 | b.iter(|| { 55 | push(&mut buffer, data.as_bytes()); 56 | }); 57 | } 58 | 59 | fn push(buf: &mut BytesMut, data: &[u8]) { 60 | buf.reserve(data.len()); 61 | unsafe { 62 | buf.bytes_mut()[..data.len()].copy_from_slice(data); 63 | buf.advance_mut(data.len()); 64 | } 65 | } 66 | 67 | struct FastWrite<'a>(&'a mut BytesMut); 68 | 69 | impl<'a> fmt::Write for FastWrite<'a> { 70 | fn write_str(&mut self, s: &str) -> fmt::Result { 71 | push(&mut *self.0, s.as_bytes()); 72 | Ok(()) 73 | } 74 | 75 | fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result { 76 | fmt::write(self, args) 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/request.rs: -------------------------------------------------------------------------------- 1 | // Use the stuff we need to 2 | use std::io; 3 | 4 | use bytes::BytesMut; 5 | 6 | /// The result of an incoming request 7 | pub struct Request { 8 | first: BytesMut, 9 | head: BytesMut, 10 | body: BytesMut, 11 | } 12 | 13 | impl Request { 14 | /// Get raw body 15 | pub fn body(&self) -> &BytesMut { 16 | &self.body 17 | } 18 | 19 | /// Get first line 20 | pub fn first(&self) -> &BytesMut { 21 | &self.first 22 | } 23 | 24 | /// Get head 25 | pub fn head(&self) -> &BytesMut { 26 | &self.head 27 | } 28 | } 29 | 30 | /// Decode the given raw HTTP request 31 | pub fn decode(buf: &mut BytesMut) -> io::Result> { 32 | if buf.is_empty() { 33 | return Ok(None); 34 | } 35 | 36 | // Clone buffer for iter 37 | let iter = buf.clone(); 38 | 39 | // Loop over bytes, to find line endings 40 | let mut it = iter.iter(); 41 | let mut firstc : u16 = 0_u16; 42 | let mut headc : u16 = 0_u16; 43 | 44 | loop { 45 | // Check if end is reached 46 | match it.next() { 47 | Some(&b'\n') => break, 48 | None => break, 49 | _ => { 50 | firstc += 1; 51 | } 52 | }; 53 | } 54 | 55 | // Cache headers line length 56 | let mut linec : u16 = 0_u16; 57 | 58 | loop { 59 | // Check if end of headers reached 60 | match it.next() { 61 | // On line end 62 | Some(&b'\n') => { 63 | // Headers end reached 64 | if linec == 1 { 65 | break; 66 | } 67 | 68 | // Increment total length 69 | headc += 1; 70 | 71 | // Reset line length 72 | linec = 0; 73 | }, 74 | // Buffer end 75 | None => break, 76 | _ => { 77 | // Else increment length 78 | linec += 1; 79 | headc += 1; 80 | } 81 | }; 82 | } 83 | 84 | // Split buffers into parts 85 | let first = buf.split_to(firstc as usize); 86 | let head = buf.split_to(headc as usize); 87 | let body = buf.clone(); 88 | 89 | // Clear buffer for next request 90 | buf.clear(); 91 | 92 | // Build request object 93 | let request = Request { 94 | first: first, 95 | head: head, 96 | body: body, 97 | }; 98 | 99 | // Create a new Request object 100 | Ok(request.into()) 101 | } 102 | -------------------------------------------------------------------------------- /benches/parse_request.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | extern crate test; 3 | extern crate bytes; 4 | 5 | mod tests { 6 | use test::Bencher; 7 | use bytes::{BytesMut, BufMut}; 8 | 9 | pub struct Request { 10 | first: BytesMut, 11 | head: BytesMut, 12 | body: BytesMut, 13 | } 14 | 15 | #[bench] 16 | fn bench_parse_request(b : &mut Bencher) { 17 | let mut buffer = BytesMut::new(); 18 | push(&mut buffer, b"GET / HTTP/1.1\nContent-Type: text/html\nContent-Length: 120\n\nHello World\n"); 19 | 20 | b.iter(|| { 21 | // Clone buffer for iter 22 | let iter = buffer.clone(); 23 | 24 | // Loop over bytes, to find line endings 25 | let mut it = iter.iter(); 26 | let mut firstc : u16 = 0_u16; 27 | let mut headc : u16 = 0_u16; 28 | 29 | loop { 30 | // Check if end is reached 31 | match it.next() { 32 | Some(&b'\n') => break, 33 | None => break, 34 | _ => { 35 | firstc += 1; 36 | } 37 | }; 38 | } 39 | 40 | // Cache headers line length 41 | let mut linec : u16 = 0_u16; 42 | 43 | loop { 44 | // Check if end of headers reached 45 | match it.next() { 46 | // On line end 47 | Some(&b'\n') => { 48 | // Headers end reached 49 | if linec == 1 { 50 | break; 51 | } 52 | 53 | // Increment total length 54 | headc += 1; 55 | 56 | // Reset line length 57 | linec = 0; 58 | }, 59 | // Buffer end 60 | None => break, 61 | _ => { 62 | // Else increment length 63 | linec += 1; 64 | headc += 1; 65 | } 66 | }; 67 | } 68 | 69 | 70 | // Split buffers into parts 71 | let first = buffer.split_to(firstc as usize); 72 | let head = buffer.split_to(headc as usize); 73 | let body = buffer.clone(); 74 | 75 | // Clear buffer for next request 76 | buffer.clear(); 77 | 78 | // Build request object 79 | let request = Request { 80 | first: first, 81 | head: head, 82 | body: body, 83 | }; 84 | }); 85 | } 86 | 87 | fn push(buf : &mut BytesMut, data : &[u8]) { 88 | buf.reserve(data.len()); 89 | 90 | unsafe { 91 | buf.bytes_mut()[..data.len()].copy_from_slice(data); 92 | buf.advance_mut(data.len()); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/response.rs: -------------------------------------------------------------------------------- 1 | use bytes::{BytesMut, BufMut}; 2 | use futures::future; 3 | use std::fmt::Write; 4 | 5 | use ZapResult; 6 | 7 | /// The result of an outgoing request 8 | pub struct Response { 9 | headers: BytesMut, 10 | response: BytesMut, 11 | status: usize, 12 | } 13 | 14 | impl Response { 15 | // Create a new default response 16 | pub fn new() -> Response { 17 | Response { 18 | headers: BytesMut::new(), 19 | response: BytesMut::new(), 20 | status: 200, 21 | } 22 | } 23 | 24 | /// Set the status code 25 | pub fn status(&mut self, code: usize) -> &mut Response { 26 | self.status = code; 27 | self 28 | } 29 | 30 | /// Set a header with value 31 | pub fn header(&mut self, name: &str, val: &str) -> &mut Response { 32 | // Encode new header and write it to buffer 33 | push(&mut self.headers, name.as_bytes()); 34 | push(&mut self.headers, b": "); 35 | push(&mut self.headers, val.as_bytes()); 36 | push(&mut self.headers, b"\r\n"); 37 | 38 | self 39 | } 40 | 41 | /// Set the response body 42 | pub fn body(&mut self, s: &str) -> &mut Response { 43 | self.response.write_str(s).unwrap(); 44 | self 45 | } 46 | 47 | /// Write raw data to the body 48 | pub fn body_raw(&mut self, s: &[u8]) -> &mut Response { 49 | push(&mut self.response, s); 50 | self 51 | } 52 | 53 | // Finish the request 54 | pub fn ok(self) -> ZapResult { 55 | future::ok(self) 56 | } 57 | } 58 | 59 | /// Encode our response to a buffer 60 | pub fn encode(msg: &Response, buf: &mut BytesMut) { 61 | // Get the content length 62 | let length = msg.response.len(); 63 | 64 | // Encode the data 65 | push(buf, b"HTTP/1.1 "); 66 | usize_to_bytes(msg.status, buf); 67 | push(buf, b"\r\nContent-Length: "); 68 | usize_to_bytes(length, buf); 69 | push(buf, b"\r\n"); 70 | push(buf, msg.headers.as_ref()); 71 | 72 | push(buf, b"\r\n"); 73 | push(buf, msg.response.as_ref()); 74 | } 75 | 76 | // Push bytes to a buffer 77 | fn push(buf: &mut BytesMut, data: &[u8]) { 78 | // Allocate new space 79 | buf.reserve(data.len()); 80 | 81 | unsafe { 82 | // Put the data in the reserved space 83 | buf.bytes_mut()[..data.len()].copy_from_slice(data); 84 | buf.advance_mut(data.len()); 85 | } 86 | } 87 | 88 | // Convert a usize to raw data without strings 89 | fn usize_to_bytes(s : usize, buf : &mut BytesMut) { 90 | // Define varibales we need 91 | let mut length = s; 92 | let mut data : [u8; 6] = [0; 6]; 93 | 94 | for i in 0..5 { 95 | data[5 - i] = 48 + (&length % 10) as u8; 96 | length = (&length / 10) as usize; 97 | 98 | if length <= 9 { 99 | data[4 - i] = 48 + length as u8; 100 | push(buf, &data[(4 - i)..6]); 101 | break; 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! The `zap` web framework crate. 2 | //! 3 | //! ## Example 4 | //! 5 | //! Here a short HelloWorld example writte in Rust with zap: 6 | //! 7 | //! ```no_run 8 | //! extern crate zap; 9 | //! 10 | //! use std::io::Error as ZapError; 11 | //! use zap::prelude::*; 12 | //! 13 | //! struct HelloWorld; 14 | //! 15 | //! impl Handler for HelloWorld { 16 | //! type Request = Request; 17 | //! type Response = Response; 18 | //! type Error = ZapError; 19 | //! type Future = ZapResult; 20 | //! 21 | //! fn call(&self, _ : Request) -> ZapResult { 22 | //! let mut resp = Response::new(); 23 | //! 24 | //! resp.body("Hello World!"); 25 | //! 26 | //! resp.ok() 27 | //! } 28 | //! } 29 | //! ``` 30 | //! 31 | 32 | // Load all crates and modules 33 | extern crate bytes; 34 | extern crate futures; 35 | extern crate tokio_io; 36 | extern crate tokio_proto; 37 | extern crate tokio_service; 38 | 39 | mod request; 40 | mod response; 41 | 42 | // Use the stuff we need to 43 | use std::io; 44 | 45 | use bytes::BytesMut; 46 | use tokio_io::codec::{Encoder, Decoder, Framed}; 47 | use tokio_io::{AsyncRead, AsyncWrite}; 48 | use tokio_proto::pipeline::ServerProto; 49 | use futures::future; 50 | 51 | // Make Server, Handler, Request and Response public accessable 52 | pub use tokio_proto::TcpServer as Server; 53 | pub use tokio_service::Service as Handler; 54 | pub use request::Request; 55 | pub use response::Response; 56 | 57 | /// The expected response result 58 | pub type ZapResult = future::Ok; 59 | 60 | /// A module to import the required things. 61 | pub mod prelude { 62 | pub use ZapResult; 63 | pub use Server; 64 | pub use Handler; 65 | pub use request::Request; 66 | pub use response::Response; 67 | pub use Http; 68 | } 69 | 70 | /// Handling incoming requests with tokio-proto 71 | pub struct Http; 72 | 73 | impl ServerProto for Http { 74 | // Setup ServerProto types 75 | type Request = Request; 76 | type Response = Response; 77 | type Transport = Framed; 78 | type BindTransport = io::Result>; 79 | 80 | fn bind_transport(&self, io: T) -> io::Result> { 81 | // Frame the request with tokio-io and handle it with HttpCodec 82 | Ok(io.framed(HttpCodec)) 83 | } 84 | } 85 | 86 | /// Encode and decode our request 87 | pub struct HttpCodec; 88 | 89 | impl Decoder for HttpCodec { 90 | type Item = Request; 91 | type Error = io::Error; 92 | 93 | fn decode(&mut self, buf: &mut BytesMut) -> io::Result> { 94 | // Decode our buffer 95 | request::decode(buf) 96 | } 97 | } 98 | 99 | impl Encoder for HttpCodec { 100 | type Item = Response; 101 | type Error = io::Error; 102 | 103 | fn encode(&mut self, msg: Response, buf: &mut BytesMut) -> io::Result<()> { 104 | // Encode and write response 105 | response::encode(&msg, buf); 106 | Ok(()) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # `zap` :zap: 2 | 3 | [![GitHub issues](https://img.shields.io/github/issues/oltdaniel/zap.svg)](https://github.com/oltdaniel/zap/issues) 4 | [![Travis](https://img.shields.io/travis/oltdaniel/zap.svg)](https://travis-ci.org/oltdaniel/zap) 5 | [![GitHub stars](https://img.shields.io/github/stars/oltdaniel/zap.svg?style=social&label=Stars)](https://github.com/oltdaniel/zap) 6 | [![Crates.io](https://img.shields.io/crates/d/zap.svg)](https://crates.io/crates/zap) 7 | [![Crates.io](https://img.shields.io/crates/v/zap.svg)](https://crates.io/crates/zap) 8 | 9 | The mission of `zap` is, to deliver a basic, but fast rust web server library. 10 | 11 | [Documentation](https://docs.rs/zap/) 12 | 13 | ## About 14 | 15 | **This code is based on tokio's minihttp project, so a big thanks to them.** ([source](https://github.com/tokio-rs/tokio-minihttp)) 16 | 17 | 18 | The goal of this project is, to show how fast Rust can be. It isn't made for huge complex applications, just a test project for benchmark reasons. 19 | 20 | ## How to use 21 | 22 | Add the following to your `Cargo.toml`: 23 | ```toml 24 | [dependencies] 25 | zap = "0.0.4" 26 | ``` 27 | 28 | ## Speed 29 | 30 | So `zap` is not only fast, it is wapping **2.96** times faster than [iron](https://github.com/iron/iron), which is based on [hyper](https://github.com/hyperium/hyper). Benchmarks below: 31 | 32 | ### Benchmark Code 33 | 34 | **Iron** 35 | 36 | This code had been taken from the [ironframework.io](http://ironframework.io) webpage. 37 | 38 | ```rust 39 | extern crate iron; 40 | 41 | use iron::prelude::*; 42 | use iron::status; 43 | 44 | fn main() { 45 | fn hello_world(_: &mut Request) -> IronResult { 46 | Ok(Response::with((status::Ok, "Hello World!"))) 47 | } 48 | 49 | Iron::new(hello_world).http("localhost:3000").unwrap(); 50 | } 51 | ``` 52 | 53 | **Zap** 54 | 55 | This example can be run, by: 56 | 57 | ``` 58 | $ git clone https://github.com/oltdaniel/zap && cd zap 59 | $ cargo run --example hello-world --release 60 | ``` 61 | 62 | ```rust 63 | extern crate zap; 64 | 65 | use std::io::Error as ZapError; 66 | use zap::prelude::*; 67 | 68 | struct HelloWorld; 69 | 70 | impl Handler for HelloWorld { 71 | type Request = Request; 72 | type Response = Response; 73 | type Error = ZapError; 74 | type Future = ZapResult; 75 | 76 | fn call(&self, _: Request) -> ZapResult { 77 | let mut resp = Response::new(); 78 | 79 | resp.body("Hello World!"); 80 | 81 | resp.ok() 82 | } 83 | } 84 | 85 | fn main() { 86 | let addr = "0.0.0.0:8080".parse().unwrap(); 87 | let mut server = Server::new(Http, addr); 88 | server.threads(8); 89 | server.serve(|| Ok(HelloWorld)); 90 | } 91 | ``` 92 | 93 | ### Benchmark Results 94 | 95 | The benchmark results have been computed with this command: `wrk -t16 -c500 -d10s http://127.0.0.1:8080 --latency` 96 | 97 | Technical details about the server: 98 | 99 | - Intel Core I7-6700K, hyper-threaded 100 | - 16GB RAM, 2400MHZ 101 | 102 | Detailed results: [in the wiki](https://github.com/oltdaniel/zap/wiki/Benchmarks). 103 | 104 | **Iron** 105 | 106 | ``` 107 | [...] 108 | Requests/sec: 307581.17 109 | Transfer/sec: 33.44MB 110 | ``` 111 | 112 | **Zap** 113 | 114 | ``` 115 | [...] 116 | Requests/sec: 912832.31 117 | Transfer/sec: 40.90MB 118 | ``` 119 | 120 | ## Credits & License 121 | 122 | [Daniel Oltmanns](https://github.com/oltdaniel) & [others](https://github.com/oltdaniel/zap/graphs/contributors) 123 | 124 | _Basically do what you'd like to._ 125 | 126 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/oltdaniel/zap/blob/master/LICENSE) 127 | --------------------------------------------------------------------------------