Skip to content

Documentation

Emir edited this page Feb 28, 2021 · 4 revisions

Welcome to the spot wiki!

Table of Contents

Request

Response

Route

Middleware

Public folder/Static files

Example code

Request

The Request object is a parsed http request. It has:

  • url: String which is the url of the requested resource
  • params: HashMap<String, String> which contains the params specified in the url (for example /user?name=cory&age=21 would yield name and age as keys with cory and 21 as values respectively)
  • body: String which is the body of the request if the request has specified a content-length header, otherwise the string is an empty string ("")
  • http_version: String which is the http version. Note: Spot only supporst 1.1 at the moment
  • method: String which is the request method (GET, POST, PUT etc). Method should always be fully capitalized.
  • headers: HashMap<String, String> which contains all the request headers content-length: 120 would for example yield content-length as a key with value "120"

Response

The Response object represents a http response and is eventually writen to the client that sent the request. It has:

  • status: u16 which is the http status code (https://en.wikipedia.org/wiki/List_of_HTTP_status_codes)
  • body: Vec which is the body in form of a vector of bytes that can be written to the client. When this field is set with the body() method it automatically adds a content-length header with value equal to the length of the vector, which is necessary for the client to read the body.
  • headers: HashMap<String, String> which contains all the headers of the response

Route

Routes in Spot are defined using the app.route() or app.route_file() functions which take in a path and a function. The function takes inn a request and a premade response. The function then modifies the response and returns it. When the response is returned it is written to the client. Routes are agnostic towards trailing forwardslashes ('/' at the end of the path string), you can write both /user/ and /user in both the declartion of the route and the http request and it will route to the same endpoint.

Middleware

Middleware is run if its route contains the resource requested (for example /user middleware would run if the request was /user/personal/data). Middleware is defined in Spot by using the app.middle() method which takes inn a request and a response, then returns the request, the response and a boolean value which is true if it wants to send the request further down the route or false if it wants to return the response directly to the client. Shorter middleware routes are always run before longer ones.

Public folder/Static files

Public folders can be used to serve static files to the client such as html files and images. These can be added to your server easily by creating a public folder with the app.public() method. The public method takes inn the name or path to your desired public folder (for example "public" or "public/new_folder"). All files in this folder are added as routes to your app, they are also subject to middleware if their path matches up with one of your middleware paths (for example middleware for /images would run for the /images/rust.png resource). You may only have one public folder at this moment, if you want multiple feel free to post an issue.

Example Code

extern crate spot;
use spot::request::Request;
use spot::response::Response;

fn main() {
    // Create a spot app with 2 worker threads
    let mut app = spot::Spot::new(2);
    // Use a directory called public in the project root to serve static files
    app.public("public");

    // Middleware for all requests starting with /post/ 
    app.middle(
        "/post/",
        |req: Request, mut res: Response| -> (Request, Response, bool) {
            if req.method == "POST" {
                if req.body.len() > 0 {
                    return (req, res, true);
                }
                res.status(400);
            }
            return (req, res, false);
        },
    );

    // Redirect
    app.route("/", |req: Request, mut res: Response| -> Response {
        if req.method == "GET" {
            res.status(301);
            res.header("Location", "/index.html");
        }
        return res;
    });

    // GET with params
    app.route("/user/", |req: Request, mut res: Response| -> Response {
        let param_keys = ["name", "age"];
        if req.method == "GET" {
            for key in param_keys.iter() {
                if !req.params.contains_key(&key[..]) {
                    res.status(400);
                    res.body(format!("Missing parameter: {}", key));
                    return res;
                }
            }
            res.status(200);
            res.body(format!(
                "<h1>Hello {}, age {}!</h1>",
                req.params.get("name").unwrap(),
                req.params.get("age").unwrap(),
            ));
            return res;
        } else {
            // Default response is 404
            return res;
        };
    });

    // Add a POST endpoint to /post
    app.route("/post/", |req: Request, mut res: Response| -> Response {
        // Spot does not have JSON serilization built inn,
        // if you want to parse JSON consider combining spot with serde_json (https://crates.io/crates/serde_json)
        println!("{}", req.body);
        if req.method == "POST" {
            res.status(200);
            res.body("{\"message\": \"Hello World!\"}");
            // HTTP headers can be added like this
            res.header("content-type", "application/json");
            return res;
        } else {
            return res;
        };
    });

    // Bind the spot app to port 3000 on the local IP adress
    let err = app.bind("127.0.0.1:3000");
    println!("{}", err);
}
Clone this wiki locally