/* 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 #include #include #include #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; }