diff options
author | Andrew Clayton <a.clayton@nginx.com> | 2023-08-02 17:03:48 +0100 |
---|---|---|
committer | Andrew Clayton <a.clayton@nginx.com> | 2023-08-21 23:24:12 +0100 |
commit | d6ed6a219b31a58526721f96195c80061d41ce54 (patch) | |
tree | 17a1fd6ecf72a327916ff0f8bc7aaf85b981ceff /examples/c/echo-request-raw.c | |
download | unit-wasm-d6ed6a219b31a58526721f96195c80061d41ce54.tar.gz unit-wasm-d6ed6a219b31a58526721f96195c80061d41ce54.tar.bz2 |
Initial commitv0.1.0
libunit-wasm and example C and Rust WebAssembly modules for NGINX Unit.
Co-developed-by: Timo Stark <t.stark@nginx.com>
Co-developed-by: Liam Crilly <liam@nginx.com>
Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
Diffstat (limited to 'examples/c/echo-request-raw.c')
-rw-r--r-- | examples/c/echo-request-raw.c | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/examples/c/echo-request-raw.c b/examples/c/echo-request-raw.c new file mode 100644 index 0000000..2071597 --- /dev/null +++ b/examples/c/echo-request-raw.c @@ -0,0 +1,187 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +/* + * Copyright (C) Andrew Clayton + * Copyright (C) F5, Inc. + */ + +/* + * echo-request-raw.c - Raw example of writing a WASM module for use with Unit + * + * Download the wasi-sysroot tarball from https://github.com/WebAssembly/wasi-sdk/releases + */ + +#define _GNU_SOURCE + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> + +#include "unit-wasm-raw.h" + +static u8 *request_buf; + +__attribute__((import_module("env"), import_name("nxt_wasm_get_init_mem_size"))) +u32 nxt_wasm_get_init_mem_size(void); +__attribute__((import_module("env"), import_name("nxt_wasm_response_end"))) +void nxt_wasm_response_end(void); +__attribute__((import_module("env"), import_name("nxt_wasm_send_response"))) +void nxt_wasm_send_response(u32 offset); + +__attribute__((export_name("wasm_module_end_handler"))) +void wasm_module_end_handler(void) +{ + free(request_buf); +} + +__attribute__((export_name("wasm_module_init_handler"))) +void wasm_module_init_handler(void) +{ + request_buf = malloc(nxt_wasm_get_init_mem_size()); +} + +__attribute__((export_name("wasm_free_handler"))) +void wasm_free_handler(u32 addr) +{ + free((void *)addr); +} + +__attribute__((export_name("wasm_malloc_handler"))) +u32 wasm_malloc_handler(size_t size) +{ + return (u32)malloc(size); +} + +static int echo_request(u8 *addr) +{ + u8 *p; + const char *method; + struct req *req; + struct resp *resp; + struct hdr_field *hf; + struct hdr_field *hf_end; + static const int resp_offs = 4096; + + printf("==[WASM RESP]== %s:\n", __func__); + + /* + * For convenience, we will return our headers at the start + * of the shared memory so leave a little space (resp_offs) + * before storing the main response. + * + * send_headers() will return the start of the shared memory, + * echo_request() will return the start of the shared memory + * plus resp_offs. + */ + resp = (struct resp *)(addr + resp_offs); + + req = (struct req *)request_buf; + +#define BUF_ADD(name, member) \ + do { \ + p = mempcpy(p, name, strlen(name)); \ + p = mempcpy(p, (u8 *)req + req->member##_offs, req->member##_len); \ + p = mempcpy(p, "\n", 1); \ + } while (0) + +#define BUF_ADD_HF() \ + do { \ + p = mempcpy(p, (u8 *)req + hf->name_offs, hf->name_len); \ + p = mempcpy(p, " = ", 3); \ + p = mempcpy(p, (u8 *)req + hf->value_offs, hf->value_len); \ + p = mempcpy(p, "\n", 1); \ + } while (0) + + p = resp->data; + + p = mempcpy(p, "Welcome to WebAssembly on Unit!\n\n", 33); + + p = mempcpy(p, "[Request Info]\n", 15); + BUF_ADD("REQUEST_PATH = ", path); + BUF_ADD("METHOD = ", method); + BUF_ADD("VERSION = ", version); + BUF_ADD("QUERY = ", query); + BUF_ADD("REMOTE = ", remote); + BUF_ADD("LOCAL_ADDR = ", local_addr); + BUF_ADD("LOCAL_PORT = ", local_port); + BUF_ADD("SERVER_NAME = ", server_name); + + p = mempcpy(p, "\n[Request Headers]\n", 19); + hf_end = req->fields + req->nr_fields; + for (hf = req->fields; hf < hf_end; hf++) + BUF_ADD_HF(); + + method = (char *)req + req->method_offs; + if (memcmp(method, "POST", req->method_len) == 0 || + memcmp(method, "PUT", req->method_len) == 0) { + p = mempcpy(p, "\n[", 2); + p = mempcpy(p, method, req->method_len); + p = mempcpy(p, " data]\n", 7); + p = mempcpy(p, (u8 *)req + req->content_offs, req->content_len); + p = mempcpy(p, "\n", 1); + } + + p = memcpy(p, "\0", 1); + + resp->size = p - resp->data; + + send_headers(addr, "text/plain", resp->size); + + nxt_wasm_send_response(resp_offs); + /* Tell Unit no more data to send */ + nxt_wasm_response_end(); + + return 0; +} + +__attribute__((export_name("wasm_request_handler"))) +int wasm_request_handler(u8 *addr) +{ + struct req *req = (struct req *)addr; + struct req *rb = (struct req *)request_buf; + + printf("==[WASM REQ]== %s:\n", __func__); + + /* + * This function _may_ be called multiple times during a single + * request if there is a large amount of data to transfer. + * + * In this simple demo, we are only expecting it to be called + * once per request. + * + * Some useful request meta data: + * + * req->content_len contains the overall size of the POST/PUT + * data. + * req->content_sent shows how much of the body content has been + * in _this_ request. + * req->total_content_sent shows how much of it has been sent in + * total. + * req->content_offs is the offset in the passed in memory where + * the body content starts. + * + * For new requests req->request_size shows the total size of + * _this_ request, incl the req structure itself. + * For continuation requests, req->request_size is just the amount + * of new content, i.e req->content_sent + * + * When req->content_len == req->total_content_sent, that's the end + * of that request. + */ + + printf("==[WASM REQ]== req->request_size : %u\n", req->request_size); + memcpy(request_buf, addr, req->request_size); + + rb = (struct req *)request_buf; + printf("==[WASM REQ]== rb@%p\n", rb); + printf("==[WASM REQ]== request_buf@%p\n", request_buf); + printf("==[WASM REQ]== rb->content_offs : %u\n", rb->content_offs); + printf("==[WASM REQ]== rb->content_len : %u\n", rb->content_len); + printf("==[WASM REQ]== rb->content_sent : %u\n", rb->content_sent); + printf("==[WASM REQ]== rb->request_size : %u\n", rb->request_size); + + echo_request(addr); + + return 0; +} |