Maker_web v0.1.1: adds `ConnectionFilter` for TCP-level early rejection

Version 0.1.1 of maker_web introduces TCP-level connection filtering, giving you more control over which clients can reach your HTTP server.

Main feature: ConnectionFilter trait

The new ConnectionFilter trait allows you to intercept connections before any HTTP parsing begins. This is useful for:

  • Early IP filtering/whitelisting
  • Implementing custom rate limiting at the TCP layer
  • Rejecting unwanted traffic with minimal overhead

Examples

Here's how you can implement a simple IP-based filter:

use std::net::{IpAddr, SocketAddr};
use maker_web::{Server, Handler, ConnectionFilter, Request, Response, Handled, StatusCode};
use tokio::net::TcpListener;

struct MyConnFilter {
    blacklist: Vec<IpAddr>
}

impl ConnectionFilter for MyConnFilter {
    fn filter(
        &self, client_addr: SocketAddr, _: SocketAddr, err_resp: &mut Response
    ) -> Result<(), Handled> {
        if self.blacklist.contains(&client_addr.ip()) {
            // Reject with custom response
            Err(err_resp
                .status(StatusCode::Forbidden)
                .body("Your IP is permanently banned"))
        } else {
            Ok(()) // Accept connection
        }
    }
}

struct HelloWorld;

impl Handler for HelloWorld {
    async fn handle(&self, _: &mut (), _: &Request, resp: &mut Response) -> Handled {
        resp.status(StatusCode::Ok)
            .header("Content-Type", "text/plain")
            .body("Hello, world!")
    }
}

#[tokio::main]
async fn main() {
    let filter = MyConnFilter {
	    blacklist: vec![
	        "192.0.2.1".parse().unwrap(),
	        "198.51.100.1".parse().unwrap(),
	        "203.0.113.1".parse().unwrap(),
	        "10.0.0.1".parse().unwrap(),
	    ]
	};

	Server::builder()
	    .listener(TcpListener::bind("127.0.0.1:8080").await.unwrap())
	    .handler(HelloWorld)
	    .conn_filter(filter)
        .build()
        .launch()
        .await;
} 

Links

Your opinion

  1. What's your opinion about this ConnectionFilter feature?
  2. How convenient is this feature's API to use?
  3. What's your general impression of maker_web's API design?

I'd like to hear your thoughts on this addition. Your opinion is very important to me.

Hey everyone! I've just released maker_web v0.1.2! :tada:

What's new

  • Parser v2: Completely rewritten parser with simplified architecture, stricter request validation, and UTF-8 checking (except body)
  • New _str API: String-based convenience methods (like .path_segments_str()) to reduce byte parsing boilerplate
  • Added precise requirements for incoming requests

What's new, but not in the library

  • Added benchmarks against: Actix Web, Axum and Rocket

Examples using the new API:

Routing:

match req.url().path_segments_str() {
    ["api", user, "name"] => resp.status(StatusCode::Ok).body(user),
    ["api", "echo", text] => resp.status(StatusCode::Ok).body(text),
    ["api", "debug", name] => {
    	println!("{name}"); // A convenient `&str` will be displayed

    	resp.status(StatusCode::Ok).body(r#"{"status": "ok"}"#)
    },
    _ => resp.status(StatusCode::NotFound),
}

Working with headings:

if let Some(host) = req.header_str("Host") {
	println!("{host}"); // 127.0.0.1
}

if let Some(user_agent) = req.header_str("User-Agent") {
	println!("{user_agent}"); // Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36
}

// The old API still works!
if let Some(binary_data) = req.header("X-Hello-World") {
	println!("{binary_data}"); // [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33]
}

Work with query:

let debug = req.url().query_str("debug") == Some("false");

match (req.url().query_str("find"), req.url().query_str("name")) {
    (Some("country"), Some(name)) => find_country(name, debug),
    (Some("city"), Some(name)) => find_city(name, debug),
    (Some("age"), Some(name)) => find_age(name, debug),
    _ => resp.status(StatusCode::NotFound).body(""),
}

Links:

I would love to hear your thoughts and feedback!:heart: