Touché is a low level but fully featured HTTP 1.0/1.1 library.
It tries to mimic hyper, but with a synchronous API.
For now only the server API is implemented.
```rust no_run use touche::{Response, Server, StatusCode};
fn main() -> std::io::Result<()> { Server::bind("0.0.0.0:4444").serve(|_req| { Response::builder() .status(StatusCode::OK) .body("Hello World") }) } ```
Touché shares a lot of similarities with Hyper:
But also has some key differences:
Vec<u8>
to represent bytes instead of Bytes```rust no_run use std::{error::Error, thread};
use touche::{Body, Response, Server, StatusCode};
fn main() -> std::io::Result<()> { Server::bind("0.0.0.0:4444").serve(|_req| { let (channel, body) = Body::channel();
thread::spawn(move || {
channel.send("chunk1").unwrap();
channel.send("chunk2").unwrap();
channel.send("chunk3").unwrap();
});
Response::builder()
.status(StatusCode::OK)
.body(body)
})
} ```
```rust no_run use std::{fs, io};
use touche::{Body, Response, Server, StatusCode};
fn main() -> std::io::Result<()> { Server::bind("0.0.0.0:4444").serve(|req| { let file = fs::File::open("./examples/file.rs")?; Ok::<_, io::Error>( Response::builder() .status(StatusCode::OK) .body(Body::tryfrom(file)?) .unwrap(), ) }) } ```
```rust no_run use touche::{body::HttpBody, Body, Method, Request, Response, Server, StatusCode};
fn main() -> std::io::Result<()> { Server::builder() .bind("0.0.0.0:4444") .serve(|req: Request
| { match (req.method(), req.uri().path()) { (_, "/") => Response::builder() .status(StatusCode::OK) .body(Body::from("Usage: curl -d hello localhost:4444/echo\n")), // Responds with the same payload
(&Method::POST, "/echo") => Response::builder()
.status(StatusCode::OK)
.body(req.into_body()),
// Responds with the reversed payload
(&Method::POST, "/reverse") => {
let body = req.into_body().into_bytes().unwrap_or_default();
match std::str::from_utf8(&body) {
Ok(message) => Response::builder()
.status(StatusCode::OK)
.body(message.chars().rev().collect::<String>().into()),
Err(err) => Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(err.to_string().into()),
}
}
_ => Response::builder()
.status(StatusCode::NOT_FOUND)
.body(Body::empty()),
}
})
}
```
```rust no_run use std::io::{BufRead, BufReader, BufWriter, Write};
use touche::{header, upgrade::Upgrade, Body, Connection, Response, Server, StatusCode};
fn main() -> std::io::Result<()> { Server::bind("0.0.0.0:4444").serve(|req| { Response::builder() .status(StatusCode::SWITCHINGPROTOCOLS) .header(header::UPGRADE, "line-protocol") .upgrade(|stream: Connection| { let reader = BufReader::new(stream.clone()); let mut writer = BufWriter::new(stream);
// Just a simple protocol that will echo every line sent
for line in reader.lines() {
match line {
Ok(line) if line.as_str() == "quit" => break,
Ok(line) => {
writer.write_all(format!("{line}\n").as_bytes());
writer.flush();
}
Err(_err) => break,
};
}
})
.body(Body::empty())
})
} ```
You can find other examples in the examples directory.
While the primary focus is having a simple and readable implementation, the library shows some decent performance.
A simple benchmark of the hello_world.rs example gives the following result:
```sh $ cat /proc/cpuinfo | grep name | uniq model name : AMD Ryzen 5 5600G with Radeon Graphics
$ wrk --latency -t6 -c 200 -d 10s http://localhost:4444 Running 10s test @ http://localhost:4444 6 threads and 200 connections Thread Stats Avg Stdev Max +/- Stdev Latency 153.37us 391.20us 19.41ms 99.37% Req/Sec 76.11k 13.21k 89.14k 82.67% Latency Distribution 50% 126.00us 75% 160.00us 90% 209.00us 99% 360.00us 4544074 requests in 10.01s, 225.35MB read Requests/sec: 454157.11 Transfer/sec: 22.52MB ```
The result is on par with Hyper's hello world running on the same machine.
This library is by no means a critique to Hyper or to async Rust. I really love both of them.
The main motivation I had to write this library was to be able to introduce Rust to my co-workers (which are mainly web developers). A synchronous library is way more beginner friendly than an async one, and by having an API that ressembles the "canonical" HTTP Rust library, people can learn Rust concepts in a easier way before adventuring through Hyper and async.