Guide - HTTP Server

Back to the guides

This guide walks you through all steps of setting up an HTTP server Onyx, writing some routes on it, and then deploying it to Wasmer Edge using WCGI.

Setting up the Project

First, you need create a new folder for this project, and initialize that folder as an Onyx project. See the package manager explainer for more details.

mkdir my-http-server
cd my-http-server
onyx package init

Then you need to add the HTTP server package to this project, and synchronize changes to install the package.

# Add the package
onyx package add http-server
# Synchronize changes
onyx package sync

Once the package is successfully fetched, you can start writing some code!

Writing the code

You can now write the code for your server. Begin by creating a main.onyx file that will house your code. In this file, you first need to load your packages and use the http package.

#load "./lib/packages"

// Include everything from the core library.
use core {*}

// Include the http package, and specifically use some common symbols.
use http
use http.server { Request, Response, route }

Now you write a simple main function that will create a TCP HTTP server, with one route for getting the "/" endpoint.

main :: () {
    // Create a router that will route the traffic.
    router := http.server.router();

    // Register a handler for 'GET /'.
    router->get("/", (req: &Request, res: &Response) {
        // Respond with HTML
        res->html("

HTTP Server in Onyx!

"); // Set status to 200 res->status(200); // Mark the response as completed res->end(); }); // Create a TCP server out of your router. app := http.server.tcp(&router); // Serve on port 8000. app->serve(8000); }

That is everything you need to create a simple HTTP server that will respond to a GET request on / with "<h1>HTTP Server in Onyx!<h1>".

You could define more routes in this way, using router->get or router->post, but there is a better way described shortly.

Running your server

To run your server, you can either use the builtin Onyx runtime, or Wasmer with WASIX support. These are the only two options for now, as there no other runtimes with fully networking support.

In a later section, you can make this web server use WCGI, so it can be deployed anywhere.

To run using the Onyx runtime (if it is installed), simply run the following command.

$ onyx run main.onyx
[Info][Http-Server] Serving on port 8000

To run using Wasmer and WASIX, you need to compile your server to a WASM binary, then run it with wasmer.

$ # Adding '-r wasi' to target WASI, and '-DWASIX' to add WASIX extensions.
$ onyx build main.onyx -r wasi -DWASIX
$ wasmer run --net out.wasm
[Info][Http-Server] Serving on port 8000

To test your server, you can visit localhost:8000 in your browser. Or, make a request using cURL.

$ curl http://localhost:8000
<h1>HTTP Server in Onyx!<h1>

Rewriting your code

The server you have is perfectly functional, and you could keep adding more routes to it in the same way as above. However, with all of this code in main, it doesn't scale terribly well.

An alternative way to register routes is to use Onyx's procedure tags feature to label certain procedures as request handlers. Then, the router can automatically find them and register them!

Here is how this works. First, you move the request handler from before into a new global procedure, and add a #tag to it.

// Tag the procedure with a `route` structure, so it can be found by the router.
#tag route.{ .GET, "/" }
index :: (req: &Request, res: &Response) {
    // Respond with HTML
    res->html("

HTTP Server in Onyx!

"); // Set status to 200 res->status(200); // Mark the response as completed res->end(); }

Now in main, you can remove the router->get() call and replace it with router->collect_routes(). This procedure will find all route tags in all packages in the code base.

main :: () {
    // Create a router that will route the traffic.
    router := http.server.router();

    // Collect routes from all packages.
    router->collect_routes();

    // Create a TCP server out of your router.
    app := http.server.tcp(&router);

    // Serve on port 8000.
    app->serve(8000);
}

You can test the server in the same way as before.

$ onyx run main.onyx
[Info][Http-Server] Serving on port 8000
$ curl http://localhost:8000
<h1>HTTP Server in Onyx!<h1>

Deploying to Wasmer Edge

To deploy to Wasmer Edge, you need to first convert the code to use the Common Gateway Interface (CGI) protocol instead of being a TCP server.

Simply, replace the http.server.tcp call with http.server.cgi.

main :: () {
    // Create a router that will route the traffic.
    router := http.server.router();

    // Collect routes from all packages.
    router->collect_routes();

    // Respond using the CGI protocol
    http.server.cgi(&router);
}

Then compile the code to a WebAssembly module targeting the WASI runtime.

$ onyx build main.onyx -r wasi -o my-http-server.wasm 

To deploy to Wasmer Edge, create a wasmer.toml file that contains the following. Replace <your-namespace> and <your-package-name> with your details.

[package]
name = "<your-namespace>/<your-package-name>"
version = "0.1.0"
description = "My first HTTP server"
license = "MIT"
 
[[module]]
name = "server"
source = "my-http-server.wasm"
abi = "wasi"
 
[[command]]
name = "server"
module = "server"
runner = "https://webc.org/runner/wcgi"
 
[command.annotations.wasi]
env = ["SCRIPT_NAME=rust_wcgi"]
 
[command.annotations.wcgi]
dialect = "rfc-3875"

Also, create an app.yaml file with the following contents.

---
kind: wasmer.io/App.v0
name: <your-app-name>
package: <your-namespace>/<your-package-name>

Then use Wasmer CLI to deploy it.

$ wasmer deploy

Follow the instructions from the Wasmer to test your deployment.

Next Steps

If you want to keep building up your HTTP server, you can look at more examples in the HTTP Server package.

© 2020-2024 Brendan Hansen