Skip to content

Building HTTP service with Rust ​

Cargo ​

Cargo is the package manager and build system for Rust programming language. It is used to manage dependencies, build, and run Rust projects. Here are a few frequently used cargo commands:

txt
cargo new: Creates a new Rust project.
cargo build: Builds the project and its dependencies.
cargo run: Builds and runs the project.
cargo test: Runs the tests in the project.
cargo check: Checks the syntax and type correctness without producing an executable.
cargo update: Updates dependencies in the project.
cargo clean: Cleans the project files and directories.

Create a project with cargo ​

shell
mkdir rust-study && cd rust-study
cargo init

Then we will have a project with this layout

shell
lujunjie@mac:/Users/lujunjie/code/github/jaylu/rust-study > tree
.
├── Cargo.toml
└── src
    └── main.rs

More folder structure info can be seen here

When can then try to run the project via cargo run

shell
lujunjie@mac:/Users/lujunjie/code/github/jaylu/rust-study > cargo run 
    Blocking waiting for file lock on package cache
   Compiling rust-study v0.1.0 (/Users/lujunjie/code/github/jaylu/rust-study)
    Finished dev [unoptimized + debuginfo] target(s) in 10.62s
     Running `target/debug/rust-study`
Hello, world!

Here we are, the project skeleton is set up!!

Building a simple web server with Actix ​

I try do some quick study of the Rust HTTP lib:

LibraryAsync SupportPerformanceStabilityEase of UseFeatures
Actix WebYesHighStableModerateMiddlewares, WebSocket
RocketYesModerateStableEasyMacros, Form Handling
WarpYesHighExperimentalEasyFilters, WebSockets
HyperYesHighStableModerateLow-level, Flexibility
TideYesHighExperimentalEasyMiddleware Chain, Async
IronNoModerateUnmaintainedModerateMiddleware
GothamYesHighExperimentalEasyRouter, Middlewares

And Actix seems to be supporting Middleware and the API is quite fit for my intuition. Below is what I had done to start up a simple http server:

  1. Adding dependencies in cargo.toml

    txt
    [dev-dependencies]
    actix-web = "4"
    actix-files = "0.6"
    env_logger = "0.9.0"
    log = "0.4"
    serde = { version = "1.0", features = ["derive"] }
    serde_json = "1"
    futures = "0.3"
  2. Add code in examples/actix_web.rs

    rust
    use actix_files::Files;
    use actix_web::{App, get, HttpServer, middleware::Logger, Responder, Result, web};
    use serde::Deserialize;
    
    #[get("/hello/{name}")]
    async fn greet(name: web::Path<String>) -> impl Responder {
        format!("Hello {name}!")
    }
    
    #[derive(Deserialize)]
    struct Info {
        username: String,
    }
    
    /// extract `Info` using serde
    async fn do_post(info: web::Json<Info>) -> Result<String> {
        println!("received request");
        Ok(format!("Welcome {}!", info.username))
    }
    
    
    #[actix_web::main] // or #[tokio::main]
    async fn main() -> std::io::Result<()> {
        env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
        log::info!("starting HTTP server at http://localhost:8080");
    
        HttpServer::new(|| {
            App::new()
                .route("/hello", web::get().to(|| async { "Hello World!" }))
                .route("/post", web::post().to(do_post))
                .service(greet)
                .service(Files::new("/web", "./examples/static")
                    .show_files_listing()
                    .use_last_modified(true)
                )
                .wrap(Logger::default())
        })
            .bind(("127.0.0.1", 8080))?
            .run()
            .await
    }
  3. Run command cargo run --example actix_web

source code here

Summary ​

It's simple enough to kick-start, but I might want a more low level HTTP server to build a web proxy in the near future, maybe let's look at using Hyper next time to read the request body in a streaming way.

Reference ​