curl -O -sSL https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-21/wasi-sdk-21.0-macos.tar.gz
tar xf wasi-sdk-21.0-macos.tar.gz
This step is based on the documentation from Fermyon: https://www.fermyon.com/wasm-languages/c-lang
The fastly.h
file contains only the necessary bindings to parse some information from the request and return Hello, world!
in the Response body.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "fastly.h"
int main() {
char *version = getenv("FASTLY_SERVICE_VERSION");
if (version == NULL) {
version = "";
}
printf("FASTLY_SERVICE_VERSION: %s\n", version);
// Req
uint32_t req_h = 0;
uint32_t req_body_h = 0;
req_body_downstream_get(&req_h, &req_body_h);
// Parse URL
uint32_t buf_size = 1024;
char buf[buf_size];
size_t nwritten = 0;
req_uri_get(req_h, buf, buf_size, &nwritten);
buf[nwritten] = '\0';
printf("uri: %s\n", buf);
// Body
uint32_t resp_body_h = 0;
body_new(&resp_body_h);
char resp_buffer[] = "Hello, world!";
nwritten = 0;
body_write(resp_body_h, resp_buffer, strlen(resp_buffer), 0, &nwritten);
// Resp
uint32_t resp_h = 0;
resp_new(&resp_h);
resp_send_downstream(resp_h, resp_body_h, 0);
return 0;
}
#include <stdlib.h>
#define WASM_IMPORT(module, name) __attribute__((import_module(module), import_name(name)))
// fastly_http_req
WASM_IMPORT("fastly_http_req", "body_downstream_get")
int req_body_downstream_get(uint32_t *req_handle_out, uint32_t *body_handle_out);
WASM_IMPORT("fastly_http_req", "uri_get")
int req_uri_get(uint32_t req_handle, char *uri, size_t uri_max_len, size_t *nwritten);
WASM_IMPORT("fastly_http_req", "method_get")
int req_method_get(uint32_t req_handle, char *method, size_t method_max_len, size_t *nwritten);
// fastly_http_resp
WASM_IMPORT("fastly_http_resp", "new")
int resp_new(uint32_t *resp_handle_out);
WASM_IMPORT("fastly_http_resp", "header_insert")
int resp_header_insert(uint32_t resp_handle, const char *name, size_t name_len, const char *value, size_t value_len);
WASM_IMPORT("fastly_http_resp", "send_downstream")
int resp_send_downstream(uint32_t resp_handle, uint32_t body_handle, uint32_t streaming);
// fastly_http_body
WASM_IMPORT("fastly_http_body", "new")
int body_new(uint32_t *handle_out);
WASM_IMPORT("fastly_http_body", "write")
int body_write(uint32_t body_handle, const char *buf,
size_t buf_len, size_t end, size_t *nwritten);
The Canonical ABI can be found at the following link. It's not entirely clear how the types defined in witx are mapped to each language's types. https://github.com/fastly/Viceroy/tree/main/lib/compute-at-edge-abi
wasi-sdk-21.0/bin/clang --target=wasm32-wasi --sysroot=wasi-sdk-21.0/share/wasi-sysroot/ main.c -o main.wasm
viceroy -v main.wasm
authors = []
description = ""
language = "c"
manifest_version = 3
name = "hello-world"
service_id = ""
fastly compute pack --wasm-binary main.wasm
fastly compute deploy -p pkg/package.tar.gz
We can convert the .wasm
binary to a readable text format using the wasm-tools print command.
Example:
wasm-tools print main.wasm | grep -e "import\b"
(import "fastly_http_req" "body_downstream_get" (func $req_body_downstream_get (;0;) (type 2)))
(import "fastly_http_req" "uri_get" (func $req_uri_get (;1;) (type 3)))
(import "fastly_http_body" "new" (func $body_new (;2;) (type 4)))
(import "fastly_http_body" "write" (func $body_write (;3;) (type 5)))
(import "fastly_http_resp" "new" (func $resp_new (;4;) (type 4)))
(import "fastly_http_resp" "send_downstream" (func $resp_send_downstream (;5;) (type 0)))
(import "wasi_snapshot_preview1" "environ_get" (func $__imported_wasi_snapshot_preview1_environ_get (;6;) (type 2)))
(import "wasi_snapshot_preview1" "environ_sizes_get" (func $__imported_wasi_snapshot_preview1_environ_sizes_get (;7;) (type 2)))
(import "wasi_snapshot_preview1" "fd_close" (func $__imported_wasi_snapshot_preview1_fd_close (;8;) (type 4)))
(import "wasi_snapshot_preview1" "fd_fdstat_get" (func $__imported_wasi_snapshot_preview1_fd_fdstat_get (;9;) (type 2)))
(import "wasi_snapshot_preview1" "fd_seek" (func $__imported_wasi_snapshot_preview1_fd_seek (;10;) (type 6)))
(import "wasi_snapshot_preview1" "fd_write" (func $__imported_wasi_snapshot_preview1_fd_write (;11;) (type 3)))
(import "wasi_snapshot_preview1" "proc_exit" (func $__imported_wasi_snapshot_preview1_proc_exit (;12;) (type 7)))
The output includes Fastly-specific host calls and some functions from WASI Preview 1. For instance, environ_get
from the WASI API is used for accessing environment variables.
It's not clear how Fastly Compute platform prints messages to the stdout log. In the wasm file compiled from C code, we see function calls like this:
(func $printf (;37;) (type 2) (param i32 i32) (result i32)
In comparison, a wasm file compiled from Rust, using the println! macro, shows different function calls:
(func $_ZN3std2io5stdio6_print17h993c40612de5425cE (;92;) (type 1) (param i32)