/* SPDX-License-Identifier: Apache-2.0 */ /* * Copyright (C) Andrew Clayton * Copyright (C) F5, Inc. */ /* * upload-reflector-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 "unit-wasm-raw.h" static size_t total_response_sent; 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_response_end_handler"))) void wasm_response_end_handler(void) { total_response_sent = 0; } __attribute__((export_name("wasm_request_end_handler"))) void wasm_request_end_handler(void) { if (!request_buf) return; free(request_buf); request_buf = NULL; } __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 upload_reflector(u8 *addr) { size_t mem_size = nxt_wasm_get_init_mem_size(); size_t rsize = sizeof(struct resp); size_t write_bytes; struct req *req; struct resp *resp; printf("==[WASM RESP]== %s:\n", __func__); resp = (struct resp *)addr; req = (struct req *)request_buf; printf("==[WASM RESP]== resp@%p\n", resp); printf("==[WASM RESP]== req@%p\n", req); printf("==[WASM RESP]== req->content_len : %u\n", req->content_len); resp = (struct resp *)addr; /* Send headers */ if (total_response_sent == 0) { const char *field; struct hdr_field *f; struct hdr_field *f_end; char ct[256]; /* Try to set the Content-Type */ f_end = req->fields + req->nr_fields; for (f = req->fields; f < f_end; f++) { field = (const char *)(u8 *)req + f->name_offs; if (strncasecmp(field, "Content-Type", 12) == 0) { snprintf(ct, sizeof(ct), "%.*s", f->value_len, (u8 *)req + f->value_offs); break; } field = NULL; } if (!field) sprintf(ct, "application/octet-stream"); send_headers(addr, ct, req->content_len); } write_bytes = req->content_sent; if (write_bytes > mem_size - rsize) write_bytes = mem_size - rsize; printf("==[WASM RESP]== write_bytes : %lu\n", write_bytes); printf("==[WASM RESP]== req->content_len : %u\n", req->content_len); printf("==[WASM RESP]== total_response_sent : %lu\n", total_response_sent); printf("==[WASM RESP]== Copying (%lu) bytes of data from [%p+%lx] to " "[%p]\n", write_bytes, req, req->content_offs + total_response_sent, resp->data); memcpy(resp->data, (u8 *)req + req->content_offs + total_response_sent, write_bytes); total_response_sent += write_bytes; resp->size = write_bytes; printf("==[WASM RESP]== resp->size : %u\n", resp->size); nxt_wasm_send_response(0); if (total_response_sent == req->content_len) { printf("==[WASM RESP]== All data sent. Cleaning up...\n"); total_response_sent = 0; free(request_buf); request_buf = NULL; /* 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. * * 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. */ if (!request_buf) { /* * Just allocate memory for the total amount of data we * expect to get, this includes the request structure * itself as well as any body content. */ printf("==[WASM REQ]== malloc(%u)\n", req->content_offs + req->content_len); request_buf = malloc(req->content_offs + req->content_len); /* * Regardless of how much memory we allocated above, here * we only want to copy the amount of data we actually * received in this 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); } else { memcpy(request_buf + rb->request_size, addr + req->content_offs, req->request_size); printf("==[WASM REQ +]== req->content_offs : %u\n", req->content_offs); printf("==[WASM REQ +]== req->content_sent : %u\n", req->content_sent); printf("==[WASM REQ +]== req->request_size : %u\n", req->request_size); rb->content_sent = req->content_sent; rb->total_content_sent = req->total_content_sent; } upload_reflector(addr); return 0; }