ratpack: a simpleton's HTTP framework (for rust-lang)

Overview

ratpack: a simpleton's HTTP framework (for rust-lang)

ratpack is idealized in the simplicity of the sinatra (ruby) framework in its goal, and attempts to be an alternative to other async HTTP frameworks such as tower, warp, axum, and tide.

ratpack tries to deliver a promise that any handler can also be middleware, by implementing a "chain of responsibility" pattern that crosses handler boundaries. In summary, what you return from the first handler is fed to the second, which returns to the third, until all handlers are processed, or an error is received. Errors can return valid status codes or plain text errors in the form of a HTTP 500 (Internal Service Error).

What ratpack is not

  • Complicated: ratpack is not especially designed for services with a large web of routes or complicated interactions with the HTTP protocol, such as SSE or Websockets (at this time, at least). ratpack is very focused on somewhat typical request/response cycles.
  • Verbose: ratpack tries very hard to make both its internals and your interaction with it the simplest thing that could possibly work. This means that your request handlers are functions you pass to a macro called compose_handler! which you pass to routing calls, and that likely, you won't be spending your time implementing complicated, extremely verbose traits or even need complicated understandings of how futures and async work.
  • Focused on one platform: while at this time we only directly support tokio, nothing is keeping us from moving into smol and async-std's territory. The majority of ratpack's use of async are futures that tokio ends up leveraging from a very high level.

Example

Here is an example which carries global application state as an authentication token validator middleware handler, which then passes forward to a greeting handler. The greeting handler can also be re-used without authentication at a different endpoint, which is also demonstrated.

Note: this is available at examples/auth-with-state.rs. It can also be run with cargo: cargo run --example auth-with-state.

, _resp: Option >, params: Params, _app: App , authstate: AuthedState, ) -> HTTPResult { let name = params.get("name").unwrap(); let bytes = Body::from(format!("hello, {}!\n", name)); if let Some(authed) = authstate.authed { if authed { return Ok(( req, Some(Response::builder().status(200).body(bytes).unwrap()), authstate, )); } } else if authstate.authed.is_none() { return Ok(( req, Some(Response::builder().status(200).body(bytes).unwrap()), authstate, )); } Err(Error::StatusCode(StatusCode::UNAUTHORIZED)) } // Our global application state; must be `Clone`. #[derive(Clone)] struct State { authtoken: &'static str, } // ServerError is a catch-all for errors returned by serving content through // ratpack. #[tokio::main] async fn main() -> Result<(), ServerError> { let mut app = App::with_state(State { authtoken: "867-5309", }); app.get("/auth/:name", compose_handler!(validate_authtoken, hello)); app.get("/:name", compose_handler!(hello)); app.serve("127.0.0.1:3000").await?; Ok(()) }">
use ratpack::prelude::*;

// We'll use authstate to (optionally) capture information about the token
// being correct. if it is Some(true), the user was authed, if None, there was no
// authentication performed.
#[derive(Clone)]
struct AuthedState {
    authed: Option<bool>,
}

// All transient state structs must have an initial state, which will be
// initialized internally in the router.
impl TransientState for AuthedState {
    fn initial() -> Self {
        Self { authed: None }
    }
}

// our authtoken validator, this queries the app state and the header
// `X-AuthToken` and compares the two. If there are any discrepancies, it
// returns `401 Unauthorized`.
//
// every handler & middleware takes and returns the same params and has the
// same prototype.
//
async fn validate_authtoken(
    req: Request,
    resp: Option
      >,
    _params: Params,
    app: App
      
       ,
    
       mut authstate: AuthedState,
) -> HTTPResult
       
         {
    
        if 
        let (
        Some(token), 
        Some(state)) 
        = (req.
        headers().
        get(
        "X-AuthToken"), app.
        state().
        await) {
        authstate.authed 
        = 
        Some(state.
        clone().
        lock().
        await.authtoken 
        == token);
        
        Ok((req, resp, authstate))
    } 
        else {
        
        Err(Error
        ::
        StatusCode(StatusCode
        ::UNAUTHORIZED))
    }
}


        // our `hello` responder; it simply echoes the `name` parameter provided in the

        // route.

        async 
        fn 
        hello(
    req: Request,
    _resp: 
        Option
        
         >,
    params: Params,
    _app: App
         
          ,
    authstate: AuthedState,
) -> HTTPResult
          
            { 
           let name 
           = params.
           get(
           "name").
           unwrap(); 
           let bytes 
           = Body
           ::
           from(
           format!(
           "hello, {}!\n", name)); 
           if 
           let 
           Some(authed) 
           = authstate.authed { 
           if authed { 
           return 
           Ok(( req, 
           Some(Response
           ::
           builder().
           status(
           200).
           body(bytes).
           unwrap()), authstate, )); } } 
           else 
           if authstate.authed.
           is_none() { 
           return 
           Ok(( req, 
           Some(Response
           ::
           builder().
           status(
           200).
           body(bytes).
           unwrap()), authstate, )); } 
           Err(Error
           ::
           StatusCode(StatusCode
           ::UNAUTHORIZED)) } 
           // Our global application state; must be `Clone`. #[derive(Clone)] 
           struct 
           State { authtoken: &
           'static 
           str, } 
           // ServerError is a catch-all for errors returned by serving content through 
           // ratpack. #[tokio::main] 
           async 
           fn 
           main() -> 
           Result<(), ServerError> { 
           let 
           mut app 
           = App
           ::
           with_state(State { authtoken: 
           "867-5309", }); app.
           get(
           "/auth/:name", 
           compose_handler!(validate_authtoken, hello)); app.
           get(
           "/:name", 
           compose_handler!(hello)); app.
           serve(
           "127.0.0.1:3000").
           await?; 
           Ok(()) }
          
         
        
       
      
     

Hitting this service with curl gives the result you'd expect:

% curl localhost:3000/erik
hello, erik!

% curl -D- localhost:3000/auth/erik
HTTP/1.1 401 Unauthorized
content-length: 0
date: Fri, 21 Jan 2022 18:29:03 GMT

% curl -D- -H "X-AuthToken: 867-5309" localhost:3000/auth/erik
HTTP/1.1 200 OK
content-length: 13
date: Fri, 21 Jan 2022 18:29:19 GMT

hello, erik!

More information & documentation

For more information, see the docs.

Author

Erik Hollensbe [email protected]

License

BSD 3-Clause

You might also like...
 HTTPie: human-friendly CLI HTTP client for the API era
HTTPie: human-friendly CLI HTTP client for the API era

HTTPie: human-friendly CLI HTTP client for the API era HTTPie (pronounced aitch-tee-tee-pie) is a command-line HTTP client. Its goal is to make CLI in

xh is a friendly and fast tool for sending HTTP requests. It reimplements as much as possible of HTTPie's excellent design, with a focus on improved performance.
xh is a friendly and fast tool for sending HTTP requests. It reimplements as much as possible of HTTPie's excellent design, with a focus on improved performance.

xh is a friendly and fast tool for sending HTTP requests. It reimplements as much as possible of HTTPie's excellent design, with a focus on improved performance

rh: user-friendly command-line HTTP client

Rust HTTP Cli The command name in your terminal is rh. rh: user-friendly command-line HTTP client rh is a user-friendly, lightweight and performant co

Rust bindings to libcurl

curl-rust libcurl bindings for Rust Quick Start use std::io::{stdout, Write}; use curl::easy::Easy; // Print a web page onto stdout fn main() {

A GraphQL server library implemented in Rust

A GraphQL server library implemented in Rust Async-graphql is a high-performance server-side library that supports all GraphQL specifications. Feature

Typed, correct GraphQL requests and responses in Rust

graphql_client A typed GraphQL client library for Rust. Features Precise types for query variables and responses. Supports GraphQL fragments, objects,

A rule34 scraper made in rust this time

rust-34-scraper A rule34 scraper made in rust this time Building Clone the repository Execute cargo build --release --release is an optimized build pr

Easy c̵̰͠r̵̛̠ö̴̪s̶̩̒s̵̭̀-t̶̲͝h̶̯̚r̵̺͐e̷̖̽ḁ̴̍d̶̖̔ ȓ̵͙ė̶͎ḟ̴͙e̸̖͛r̶̖͗ë̶̱́ṉ̵̒ĉ̷̥e̷͚̍ s̷̹͌h̷̲̉a̵̭͋r̷̫̊ḭ̵̊n̷̬͂g̵̦̃ f̶̻̊ơ̵̜ṟ̸̈́ R̵̞̋ù̵̺s̷̖̅ţ̸͗!̸̼͋

Rust S̵̓i̸̓n̵̉ I̴n̴f̶e̸r̵n̷a̴l mutability! Howdy, friendly Rust developer! Ever had a value get m̵̯̅ð̶͊v̴̮̾ê̴̼͘d away right under your nose just when

An experimental HTTP server in Rust that supports HTTP/1.1, HTTP/2, and HTTP/3 over QUIC.

🚀 H123 An experimental HTTP server in Rust that supports HTTP/1.1, HTTP/2, and HTTP/3 over QUIC. Warning This is an experimental project and not inte

A more modern http framework benchmarker supporting HTTP/1 and HTTP/2 benchmarks.

rewrk A more modern http framework benchmark utility.

A MITM Proxy Written in Rust 🦀! Toolkit for HTTP/1, HTTP/2, and WebSockets with SSL/TLS Capabilities. Learning Project.
A MITM Proxy Written in Rust 🦀! Toolkit for HTTP/1, HTTP/2, and WebSockets with SSL/TLS Capabilities. Learning Project.

Man In The Middle Proxy Description Rust-based Man in the Middle proxy, an early-stage project aimed at providing visibility into network traffic. Cur

Automated builded images for rust-lang with rustup, "the ultimate way to install RUST"

rustup Automated builded images on store and hub for rust-lang with musl added, using rustup "the ultimate way to install RUST". tag changed: all3 -

In this repository you can find modules with code and comments that explain rust syntax and all about Rust lang.
In this repository you can find modules with code and comments that explain rust syntax and all about Rust lang.

Learn Rust What is this? In this repository you can find modules with code and comments that explain rust syntax and all about Rust lang. This is usef

GitHub Actions for mdBook (rust-lang/mdBook) ⚡️ Setup mdBook quickly and build your site fast. Linux (Ubuntu), macOS, and Windows are supported.
GitHub Actions for mdBook (rust-lang/mdBook) ⚡️ Setup mdBook quickly and build your site fast. Linux (Ubuntu), macOS, and Windows are supported.

GitHub Actions for mdBook rust-lang/mdBook Setup Action. We can run mdBook on a virtual machine of GitHub Actions by this mdBook action. Linux, macOS,

Open-source Autonomy Software in Rust-lang with gRPC for the Roomba series robot vacuum cleaners
Open-source Autonomy Software in Rust-lang with gRPC for the Roomba series robot vacuum cleaners

CleanIt Open-source Autonomy Software in Rust-lang with gRPC for the Roomba series robot vacuum cleaners Motivation Motivation is to build a complete

Gostd is the golang standard library implementation in rust-lang.

Gostd Gostd is the golang standard library implementation in rust-lang.

The library provides basic functions to work with Graphviz dot lang from rust code.

Description The library provides the basic access to the graphs in graphviz format with ability to import into or export from it. Base examples: Parse

A simple port scanner built using rust-lang

A simple port scanner built using rust-lang

Fish Fight is a tactical 2D shooter. Made with Rust-lang and Macroquad 🦀🌶
Fish Fight is a tactical 2D shooter. Made with Rust-lang and Macroquad 🦀🌶

Fish Fight Introduction Fish Fight is a tactical 2D shooter, played by up to 4 players online or on a shared screen. Aim either left or right; the res

Comments
  • Some fixes

    Some fixes

    • StatusCode error types now take a string for the body.
    • Route failures return METHOD_NOT_ALLOWED, which I should double check but is more accurate.

    Signed-off-by: Erik Hollensbe [email protected]

    opened by erikh 0
Owner
ZeroTier, Inc.
Directly Connecting the World's Devices with Universal Software Defined Networking
ZeroTier, Inc.
Fast and friendly HTTP server framework for async Rust

Tide Serve the web API Docs | Contributing | Chat Tide is a minimal and pragmatic Rust web application framework built for rapid development. It comes

http-rs 4.1k Jan 2, 2023
An HTTP library for Rust

hyper A fast and correct HTTP implementation for Rust. HTTP/1 and HTTP/2 Asynchronous design Leading in performance Tested and correct Extensive produ

null 11k Jan 8, 2023
An easy and powerful Rust HTTP Client

reqwest An ergonomic, batteries-included HTTP Client for Rust. Plain bodies, JSON, urlencoded, multipart Customizable redirect policy HTTP Proxies HTT

Sean McArthur 6.8k Dec 31, 2022
FeignHttp is a declarative HTTP client. Based on rust macros.

FeignHttp is a declarative HTTP client. Based on rust macros. Features Easy to use Asynchronous request Configurable timeout settings Suppor

null 46 Nov 30, 2022
Pretend is a macros-based declarative Rust HTTP client

pretend is a modular, Feign-inspired HTTP, client based on macros. It's goal is to decouple the definition of a REST API from it's implementation.

null 24 Aug 3, 2022
Pyre - A fast python HTTP server inspired by japronto written in rust.

Pyre - A fast python HTTP server inspired by japronto written in rust.

null 135 Nov 26, 2022
Minimal Rust HTTP client for both native and WASM

ehttp: a minimal Rust HTTP client for both native and WASM If you want to do HTTP requests and are targetting both native and web (WASM), then this is

Emil Ernerfeldt 105 Dec 25, 2022
A backend providing a HTTP REST like interface for uploading files written in rust.

UploadServer A backend providing a HTTP REST like interface for uploading files written in rust. API Documentation License This project is licensed un

null 4 Nov 20, 2022
🐱‍👤 Drop-in HTTP replacement module for Garry's Mod

??‍?? gmsv_reqwest This module is a drop-in replacement for Garry's Mod's HTTP function, inspired by gmsv_chttp created by timschumi. The module uses

William 38 Dec 12, 2022
Multi-stream HTTP downloader using range requests

chooch - An Amazing Project Downloads files faster than wget/curl (in theory) using multiple connections. Chooch recycles the slowest connection and r

null 13 Sep 14, 2022