To begin, ensure you have Rust installed on your machine. If you haven't set it up yet, you can follow the instructions on the official Rust website.

Setting Up the Project

First, create a new Rust project using Cargo, Rust’s package manager and build system:

cargo new simple_http_server
cd simple_http_server

Next, add hyper and tokio to your Cargo.toml file. tokio is an asynchronous runtime for Rust, which is required to run our HTTP server.

[dependencies]
hyper = "0.14"
tokio = { version = "1", features = ["full"] }

Writing the Server Code

Now, let's write the server code. Open src/main.rs and replace its content with the following:

use hyper::{Body, Request, Response, Server, Error};
use hyper::service::{make_service_fn, service_fn};
use std::convert::Infallible;

async fn handle_request(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
    let response = Response::new(Body::from("Hello, World!"));
    Ok(response)
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let make_svc = make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(handle_request)) });

    let addr = ([127, 0, 0, 1], 3000).into();
    let server = Server::bind(&addr).serve(make_svc);

    println!("Listening on http://{}", addr);

    if let Err(e) = server.await {
        eprintln!("Server error: {}", e);
    }
    Ok(())
}

Code Explanation

  1. Imports: We import necessary modules from hyper and tokio. The hyper crate provides the HTTP server functionalities, while tokio allows us to run asynchronous code.
  1. Request Handler: The handle_request function takes an incoming request and returns a simple "Hello, World!" response. The return type is wrapped in Result to handle potential errors gracefully.
  1. Main Function: The main function is marked with #[tokio::main], which sets up the asynchronous runtime. We create a service using make_service_fn, which allows us to specify how to handle incoming connections.
  1. Server Initialization: The server listens on 127.0.0.1:3000, and we print a message to indicate where the server is running. The server will continue running until an error occurs.

Running the Server

To run your server, execute the following command in your terminal:

cargo run

You should see the output indicating the server is listening:

Listening on http://127.0.0.1:3000

Testing the Server

You can test your server using curl or a web browser. Open a terminal and run:

curl http://127.0.0.1:3000

You should receive a response:

Hello, World!

Error Handling Best Practices

In the provided code, we handle errors using Rust's Result type. It's essential to manage errors effectively, especially in a web server context. Below is a brief overview of common error handling techniques in Rust:

TechniqueDescription
Result<T, E>Used to return success (T) or error (E).
? OperatorPropagates errors upwards, simplifying error handling.
unwrap()Panics on error; use cautiously in production code.
expect()Similar to unwrap(), but allows you to specify a message.

Conclusion

In this tutorial, we built a simple HTTP server using Rust's hyper library, demonstrating asynchronous programming and error handling. This foundational knowledge will help you create more complex applications and services in Rust.

Learn more with useful resources