summaryrefslogtreecommitdiffhomepage
path: root/examples
diff options
context:
space:
mode:
authorAndrew Clayton <a.clayton@nginx.com>2023-08-02 17:03:48 +0100
committerAndrew Clayton <a.clayton@nginx.com>2023-08-21 23:24:12 +0100
commitd6ed6a219b31a58526721f96195c80061d41ce54 (patch)
tree17a1fd6ecf72a327916ff0f8bc7aaf85b981ceff /examples
downloadunit-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')
-rw-r--r--examples/.gitignore1
-rw-r--r--examples/c/Makefile40
-rw-r--r--examples/c/echo-request-raw.c187
-rw-r--r--examples/c/luw-echo-request.c98
-rw-r--r--examples/c/luw-upload-reflector.c101
-rw-r--r--examples/c/unit-wasm-raw.c46
-rw-r--r--examples/c/unit-wasm-raw.h87
-rw-r--r--examples/c/upload-reflector-raw.c223
-rw-r--r--examples/docker/README.md31
-rw-r--r--examples/docker/demo-wasm.Dockerfile20
-rw-r--r--examples/docker/unit-wasm.Dockerfile40
-rw-r--r--examples/docker/wasm-conf.json76
-rw-r--r--examples/rust/.gitignore3
-rw-r--r--examples/rust/Makefile17
-rw-r--r--examples/rust/echo-request/Cargo.toml12
-rw-r--r--examples/rust/echo-request/src/lib.rs200
-rw-r--r--examples/rust/upload-reflector/Cargo.toml12
-rw-r--r--examples/rust/upload-reflector/src/lib.rs131
18 files changed, 1325 insertions, 0 deletions
diff --git a/examples/.gitignore b/examples/.gitignore
new file mode 100644
index 0000000..19e1bce
--- /dev/null
+++ b/examples/.gitignore
@@ -0,0 +1 @@
+*.wasm
diff --git a/examples/c/Makefile b/examples/c/Makefile
new file mode 100644
index 0000000..1b10269
--- /dev/null
+++ b/examples/c/Makefile
@@ -0,0 +1,40 @@
+include ../../shared.mk
+
+CFLAGS += -I../../src/c/include
+LIBS = -L../../src/c -lunit-wasm
+
+SDIR = examples/c
+
+LUW_SRCDIR = ../../src/c
+
+luw_deps = $(LUW_SRCDIR)/libunit-wasm.a \
+ $(LUW_SRCDIR)/include/unit/unit-wasm.h
+
+examples: examples-luw
+
+examples-luw: luw-echo-request.wasm luw-upload-reflector.wasm
+
+examples-raw: echo-request-raw.wasm upload-reflector-raw.wasm
+
+luw-echo-request.wasm: luw-echo-request.c $(luw_deps)
+ $(PP_CCLNK) $(SDIR)/$@
+ $(v)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS)
+
+luw-upload-reflector.wasm: luw-upload-reflector.c $(luw_deps)
+ $(PP_CCLNK) $(SDIR)/$@
+ $(v)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS)
+
+unit-wasm-raw.o: unit-wasm-raw.c unit-wasm-raw.h
+ $(PP_CC) $(SDIR)/$@
+ $(v)$(CC) $(CFLAGS) -c $<
+
+echo-request-raw.wasm: echo-request-raw.c unit-wasm-raw.o
+ $(PP_CCLNK) $(SDIR)/$@
+ $(v)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< unit-wasm-raw.o
+
+upload-reflector-raw.wasm: upload-reflector-raw.c unit-wasm-raw.o
+ $(PP_CCLNK) $(SDIR)/$@
+ $(v)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< unit-wasm-raw.o
+
+clean:
+ rm -f *.wasm *.o *.gch
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;
+}
diff --git a/examples/c/luw-echo-request.c b/examples/c/luw-echo-request.c
new file mode 100644
index 0000000..5655c65
--- /dev/null
+++ b/examples/c/luw-echo-request.c
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+/*
+ * examples/c/luw-echo-request.c - Example of writing a WASM module for use
+ * with Unit using libunit-wasm
+ *
+ * Copyright (C) Andrew Clayton
+ * Copyright (C) F5, Inc.
+ */
+
+#define _XOPEN_SOURCE 500
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "unit/unit-wasm.h"
+
+static u8 *request_buf;
+
+__luw_export_name("luw_module_end_handler")
+void luw_module_end_handler(void)
+{
+ free(request_buf);
+}
+
+__luw_export_name("luw_module_init_handler")
+void luw_module_init_handler(void)
+{
+ request_buf = malloc(luw_mem_get_init_size());
+}
+
+static bool hdr_iter_func(luw_ctx_t *ctx, const char *name, const char *value,
+ void *user_data __luw_unused)
+{
+ luw_mem_writep(ctx, "%s = %s\n", name, value);
+
+ return true;
+}
+
+__luw_export_name("luw_request_handler")
+int luw_request_handler(u8 *addr)
+{
+ luw_ctx_t ctx;
+ char clen[32];
+ const char *method;
+
+ luw_init_ctx(&ctx, addr, 4096 /* Response offset */);
+ /* Take a copy of the request and use that */
+ luw_set_req_buf(&ctx, &request_buf, LUW_SRB_NONE);
+
+#define BUF_ADD(fmt, member) \
+ luw_mem_writep(&ctx, fmt, luw_get_http_##member(&ctx));
+
+ luw_mem_writep(&ctx,
+ " *** Welcome to WebAssembly on Unit! "
+ "[libunit-wasm (%d.%d.%d/%#0.8x)] ***\n\n",
+ LUW_VERSION_MAJOR, LUW_VERSION_MINOR, LUW_VERSION_PATCH,
+ LUW_VERSION_NUMBER);
+
+ luw_mem_writep(&ctx, "[Request Info]\n");
+ BUF_ADD("REQUEST_PATH = %s\n", path);
+ BUF_ADD("METHOD = %s\n", method);
+ BUF_ADD("VERSION = %s\n", version);
+ BUF_ADD("QUERY = %s\n", query);
+ BUF_ADD("REMOTE = %s\n", remote);
+ BUF_ADD("LOCAL_ADDR = %s\n", local_addr);
+ BUF_ADD("LOCAL_PORT = %s\n", local_port);
+ BUF_ADD("SERVER_NAME = %s\n", server_name);
+
+ luw_mem_writep(&ctx, "\n[Request Headers]\n");
+
+ luw_http_hdr_iter(&ctx, hdr_iter_func, NULL);
+
+ method = luw_get_http_method(&ctx);
+ if (memcmp(method, "POST", strlen(method)) == 0 ||
+ memcmp(method, "PUT", strlen(method)) == 0) {
+ luw_mem_writep(&ctx, "\n[%s data]\n", method);
+ luw_mem_writep_data(&ctx, luw_get_http_content(&ctx),
+ luw_get_http_content_len(&ctx));
+ luw_mem_writep(&ctx, "\n");
+ }
+
+ luw_http_init_headers(&ctx, 2, 0);
+
+ snprintf(clen, sizeof(clen), "%lu", luw_get_response_data_size(&ctx));
+ luw_http_add_header(&ctx, 0, "Content-Type", "text/plain");
+ luw_http_add_header(&ctx, 1, "Content-Length", clen);
+
+ luw_http_send_headers(&ctx);
+
+ luw_http_send_response(&ctx);
+ /* Tell Unit no more data to send */
+ luw_http_response_end();
+
+ return 0;
+}
diff --git a/examples/c/luw-upload-reflector.c b/examples/c/luw-upload-reflector.c
new file mode 100644
index 0000000..95bc514
--- /dev/null
+++ b/examples/c/luw-upload-reflector.c
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+/*
+ * examples/c/luw-upload-reflector.c - Example of writing a WASM module for
+ * use with Unit using libunit-wasm
+ *
+ * Copyright (C) Andrew Clayton
+ * Copyright (C) F5, Inc.
+ */
+
+#define _XOPEN_SOURCE 500
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "unit/unit-wasm.h"
+
+static luw_ctx_t ctx;
+
+static size_t total_response_sent;
+
+static u8 *request_buf;
+
+/*
+ * While these first two _handlers_ aren't technically required, they
+ * could be combined or the code could just go in upload_reflector(),
+ * they demonstrate their use in ensuring the module is in the right
+ * state for a new request.
+ */
+__luw_export_name("luw_response_end_handler")
+void luw_response_end_handler(void)
+{
+ total_response_sent = 0;
+}
+
+__luw_export_name("luw_request_end_handler")
+void luw_request_end_handler(void)
+{
+ if (!request_buf)
+ return;
+
+ free(request_buf);
+ request_buf = NULL;
+}
+
+static int upload_reflector(luw_ctx_t *ctx)
+{
+ size_t write_bytes;
+
+ /* Send headers */
+ if (total_response_sent == 0) {
+ static const char *defct = "application/octet-stream";
+ const char *ct = luw_http_hdr_get_value(ctx, "Content-Type");
+ char clen[32];
+
+ snprintf(clen, sizeof(clen), "%lu",
+ luw_get_http_content_len(ctx));
+
+ luw_http_init_headers(ctx, 2, 0);
+ luw_http_add_header(ctx, 0, "Content-Type", ct ? ct : defct);
+ luw_http_add_header(ctx, 1, "Content-Length", clen);
+ luw_http_send_headers(ctx);
+ }
+
+ write_bytes = luw_mem_fill_buf_from_req(ctx, total_response_sent);
+ total_response_sent += write_bytes;
+
+ luw_http_send_response(ctx);
+
+ if (total_response_sent == luw_get_http_content_len(ctx)) {
+ /* Tell Unit no more data to send */
+ luw_http_response_end();
+ }
+
+ return 0;
+}
+
+__luw_export_name("luw_request_handler")
+int luw_request_handler(u8 *addr)
+{
+ if (!request_buf) {
+ luw_init_ctx(&ctx, addr, 0 /* Response offset */);
+ /*
+ * Take a copy of the request and use that, we do this
+ * in APPEND mode so we can build up request_buf from
+ * multiple requests.
+ *
+ * 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.
+ */
+ luw_set_req_buf(&ctx, &request_buf,
+ LUW_SRB_APPEND|LUW_SRB_ALLOC|LUW_SRB_FULL_SIZE);
+ } else {
+ luw_req_buf_append(&ctx, addr);
+ }
+
+ upload_reflector(&ctx);
+
+ return 0;
+}
diff --git a/examples/c/unit-wasm-raw.c b/examples/c/unit-wasm-raw.c
new file mode 100644
index 0000000..42ebcbf
--- /dev/null
+++ b/examples/c/unit-wasm-raw.c
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+/*
+ * Copyright (C) Andrew Clayton
+ * Copyright (C) F5, Inc.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <string.h>
+
+#include "unit-wasm-raw.h"
+
+__attribute__((import_module("env"), import_name("nxt_wasm_send_headers")))
+void nxt_wasm_send_headers(u32 offset);
+
+void send_headers(u8 *addr, const char *ct, size_t len)
+{
+ struct resp_hdr *rh;
+ char clen[32];
+ u8 *p;
+ static const u32 hdr_offs = 0;
+
+ rh = (struct resp_hdr *)addr;
+
+#define SET_HDR_FIELD(idx, name, val) \
+ do { \
+ rh->fields[idx].name_offs = p - addr; \
+ rh->fields[idx].name_len = strlen(name); \
+ p = mempcpy(p, name, rh->fields[idx].name_len); \
+ rh->fields[idx].value_offs = p - addr; \
+ rh->fields[idx].value_len = strlen(val); \
+ p = mempcpy(p, val, rh->fields[idx].value_len); \
+ } while (0)
+
+ rh->nr_fields = 2;
+ p = addr + sizeof(struct resp_hdr) +
+ (rh->nr_fields * sizeof(struct hdr_field));
+
+ SET_HDR_FIELD(0, "Content-Type", ct);
+ snprintf(clen, sizeof(clen), "%lu", len);
+ SET_HDR_FIELD(1, "Content-Length", clen);
+
+ nxt_wasm_send_headers(hdr_offs);
+}
diff --git a/examples/c/unit-wasm-raw.h b/examples/c/unit-wasm-raw.h
new file mode 100644
index 0000000..6fd9d35
--- /dev/null
+++ b/examples/c/unit-wasm-raw.h
@@ -0,0 +1,87 @@
+#ifndef _UNIT_WASM_H_
+#define _UNIT_WASM_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+typedef uint64_t u64;
+typedef int64_t s64;
+typedef uint32_t u32;
+typedef int32_t s32;
+typedef uint16_t u16;
+typedef int16_t s16;
+typedef uint8_t u8;
+typedef int8_t s8;
+
+#ifndef __unused
+#define __unused __attribute__((unused))
+#endif
+#ifndef __maybe_unused
+#define __maybe_unused __unused
+#endif
+#ifndef __always_unused
+#define __always_unused __unused
+#endif
+
+struct hdr_field {
+ u32 name_offs;
+ u32 name_len;
+ u32 value_offs;
+ u32 value_len;
+};
+
+struct req {
+ u32 method_offs;
+ u32 method_len;
+ u32 version_offs;
+ u32 version_len;
+ u32 path_offs;
+ u32 path_len;
+ u32 query_offs;
+ u32 query_len;
+ u32 remote_offs;
+ u32 remote_len;
+ u32 local_addr_offs;
+ u32 local_addr_len;
+ u32 local_port_offs;
+ u32 local_port_len;
+ u32 server_name_offs;
+ u32 server_name_len;
+
+ u32 content_offs;
+ u32 content_len;
+ u32 content_sent;
+ u32 total_content_sent;
+
+ u32 request_size;
+
+ u32 nr_fields;
+
+ u32 tls;
+
+ struct hdr_field fields[];
+};
+
+struct resp {
+ u32 size;
+
+ u8 data[];
+};
+
+struct resp_hdr {
+ u32 nr_fields;
+
+ struct hdr_field fields[];
+};
+
+extern void wasm_module_end_handler(void);
+extern void wasm_module_init_handler(void);
+extern void wasm_response_end_handler(void);
+extern void wasm_request_end_handler(void);
+extern void wasm_free_handler(u32 addr);
+extern u32 wasm_malloc_handler(size_t size);
+extern int wasm_request_handler(u8 *addr);
+
+extern void send_headers(u8 *addr, const char *ct, size_t len);
+
+#endif /* _UNIT_WASM_H_ */
diff --git a/examples/c/upload-reflector-raw.c b/examples/c/upload-reflector-raw.c
new file mode 100644
index 0000000..3da4f8d
--- /dev/null
+++ b/examples/c/upload-reflector-raw.c
@@ -0,0 +1,223 @@
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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;
+}
diff --git a/examples/docker/README.md b/examples/docker/README.md
new file mode 100644
index 0000000..61d2740
--- /dev/null
+++ b/examples/docker/README.md
@@ -0,0 +1,31 @@
+Unit-Wasm demo
+==============
+
+## Build the docker images
+
+From the repository root, run
+
+```shell
+$ make docker
+```
+
+This builds two docker images.
+
+### 1. unit:wasm
+
+This image is based on the Docker Official Images for Unit 1.30 with a fresh
+build of unitd and the experimental Wasm module. Wasmtime is included as a
+shared object.
+
+### 2. unit:demo-wasm
+
+This image is based on the new `unit:wasm` image created above. It includes
+a demo application written in C and compiled to wasm.
+
+## Run the demo
+
+```shell
+$ docker run -d -p 9000:80 unit:demo-wasm
+$ curl localhost:9000
+$ curl localhost:9000/echo
+```
diff --git a/examples/docker/demo-wasm.Dockerfile b/examples/docker/demo-wasm.Dockerfile
new file mode 100644
index 0000000..2385211
--- /dev/null
+++ b/examples/docker/demo-wasm.Dockerfile
@@ -0,0 +1,20 @@
+FROM unit:wasm AS build
+WORKDIR /demo
+
+# Get all the build tools we need
+#
+RUN apt update && apt install -y wget build-essential clang llvm lld
+RUN cd /usr/lib/llvm-11/lib/clang/11.0.1 && wget -O- https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/libclang_rt.builtins-wasm32-wasi-20.0.tar.gz | tar zxvf -
+RUN wget -O- https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/wasi-sysroot-20.0.tar.gz | tar zxfv -
+
+# Copy-in the demo application source code and build into a .wasm module
+#
+ADD ${PWD} /demo/
+RUN make WASI_SYSROOT=/demo/wasi-sysroot examples
+
+# Copy the .wasm modules and Unit configuration to the final Docker image
+# that will run the demo application.
+#
+FROM unit:wasm
+COPY --from=build /demo/examples/c/*.wasm /demo/
+ADD examples/docker/wasm-conf.json /docker-entrypoint.d
diff --git a/examples/docker/unit-wasm.Dockerfile b/examples/docker/unit-wasm.Dockerfile
new file mode 100644
index 0000000..b7b47a5
--- /dev/null
+++ b/examples/docker/unit-wasm.Dockerfile
@@ -0,0 +1,40 @@
+# Start with the minimal Docker Official Image so we can use the same defaults
+#
+FROM unit:minimal AS build
+WORKDIR /src
+
+# Get all the build tools we need, including Wasmtime
+#
+#RUN apt update && apt install -y wget git build-essential clang lld libpcre2-dev libssl-dev
+RUN apt update && apt install -y wget git build-essential libpcre2-dev libssl-dev
+RUN wget -O- https://github.com/bytecodealliance/wasmtime/releases/download/v11.0.0/wasmtime-v11.0.0-$(arch)-linux-c-api.tar.xz \
+ | tar Jxfv - && \
+ mkdir /usr/lib/wasmtime && \
+ cp /src/wasmtime-v11.0.0-$(arch)-linux-c-api/lib/* /usr/lib/wasmtime
+
+# Build NGINX JavaScript (njs) so that we have a feature-complete Unit
+#
+RUN git clone https://github.com/nginx/njs.git && \
+ cd njs && \
+ ./configure --no-libxml2 --no-zlib && \
+ make
+
+# Build Unit with the Wasm module, copying the configure arguments from the
+# official image.
+#
+RUN git clone https://github.com/nginx/unit.git && \
+ cd unit && \
+ wget -O- https://github.com/nginx/unit/pull/902.patch | patch -p1 && \
+ ./configure $(unitd --version 2>&1 | tr ' ' '\n' | grep ^-- | grep -v opt=) \
+ --cc-opt="-I/src/njs/src -I/src/njs/build" --ld-opt=-L/src/njs/build && \
+ ./configure wasm --include-path=/src/wasmtime-v11.0.0-$(arch)-linux-c-api/include \
+ --lib-path=/usr/lib/wasmtime --rpath && \
+ make
+
+# Create a clean final image by copying over only Wasmtime, the new unitd
+# binary, and the Wasm module.
+#
+FROM unit:minimal
+COPY --from=build /src/unit/build/sbin/unitd /usr/sbin
+COPY --from=build /src/unit/build/lib/unit/modules/wasm.unit.so /usr/lib/unit/modules
+COPY --from=build /usr/lib/wasmtime/*.so /usr/lib/wasmtime/
diff --git a/examples/docker/wasm-conf.json b/examples/docker/wasm-conf.json
new file mode 100644
index 0000000..5ed173e
--- /dev/null
+++ b/examples/docker/wasm-conf.json
@@ -0,0 +1,76 @@
+{
+ "access_log": "/dev/stdout",
+ "settings": {
+ "http": {
+ "log_route": true,
+ "max_body_size": 1073741824
+ }
+ },
+
+ "listeners": {
+ "*:80": {
+ "pass": "routes"
+ }
+ },
+
+ "routes": [
+ {
+ "match": {
+ "uri": "/echo*"
+ },
+ "action": {
+ "pass": "applications/luw-echo-request"
+ }
+ },
+ {
+ "match": {
+ "uri": "/upload*"
+ },
+ "action": {
+ "pass": "applications/luw-upload-reflector"
+ }
+ },
+ {
+ "match": {
+ "headers": {
+ "accept": "*text/html*"
+ }
+ },
+ "action": {
+ "share": "/usr/share/unit/welcome/welcome.html"
+ }
+ },
+ {
+ "action": {
+ "share": "/usr/share/unit/welcome/welcome.md"
+ }
+ }
+ ],
+
+ "applications": {
+ "luw-echo-request": {
+ "type": "wasm",
+ "module": "/demo/luw-echo-request.wasm",
+ "request_handler": "luw_request_handler",
+ "malloc_handler": "luw_malloc_handler",
+ "free_handler": "luw_free_handler",
+ "module_init_handler": "luw_module_init_handler",
+ "module_end_handler": "luw_module_end_handler",
+ "access": {
+ "filesystem": [
+ "/tmp",
+ "/var/tmp"
+ ]
+ }
+ },
+ "luw-upload-reflector": {
+ "type": "wasm",
+ "module": "/demo/luw-upload-reflector.wasm",
+ "request_handler": "luw_request_handler",
+ "malloc_handler": "luw_malloc_handler",
+ "free_handler": "luw_free_handler",
+ "request_end_handler": "luw_request_end_handler",
+ "response_end_handler": "luw_response_end_handler"
+ }
+ }
+}
diff --git a/examples/rust/.gitignore b/examples/rust/.gitignore
new file mode 100644
index 0000000..559d663
--- /dev/null
+++ b/examples/rust/.gitignore
@@ -0,0 +1,3 @@
+Cargo.lock
+
+target/
diff --git a/examples/rust/Makefile b/examples/rust/Makefile
new file mode 100644
index 0000000..b41bc33
--- /dev/null
+++ b/examples/rust/Makefile
@@ -0,0 +1,17 @@
+include ../../shared.mk
+
+SDIR = examples/rust
+
+examples: rust-echo-request rust-upload-reflector
+
+rust-echo-request: echo-request/Cargo.toml echo-request/src/lib.rs
+ $(PP_GEN) $(SDIR)/echo-request/target/wasm32-wasi/
+ $(v)cd echo-request; cargo build --target=wasm32-wasi
+
+rust-upload-reflector: upload-reflector/Cargo.toml upload-reflector/src/lib.rs
+ $(PP_GEN) $(SDIR)/upload-reflector/target/wasm32-wasi/
+ $(v)cd upload-reflector; cargo build --target=wasm32-wasi
+
+clean:
+ rm -f echo-request/Cargo.lock upload-reflector/Cargo.lock
+ rm -rf echo-request/target upload-reflector/target
diff --git a/examples/rust/echo-request/Cargo.toml b/examples/rust/echo-request/Cargo.toml
new file mode 100644
index 0000000..256242e
--- /dev/null
+++ b/examples/rust/echo-request/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "rust-echo-request"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+unit-wasm = { path = "../../../src/rust", version = "0.1.0-beta" }
+
+[lib]
+crate-type = ["cdylib"]
diff --git a/examples/rust/echo-request/src/lib.rs b/examples/rust/echo-request/src/lib.rs
new file mode 100644
index 0000000..4802cff
--- /dev/null
+++ b/examples/rust/echo-request/src/lib.rs
@@ -0,0 +1,200 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+/*
+ * Copyright (C) Andrew Clayton
+ * Copyright (C) Timo Stark
+ * Copyright (C) F5, Inc.
+ */
+
+// Include RAW FFI Bindings.
+// @todo: Replace this with the new native Rust API
+use unit_wasm::ffi::*;
+
+use std::ffi::CStr;
+use std::os::raw::c_char;
+use std::os::raw::c_void;
+use std::ptr;
+
+// Buffer of some size to store the copy of the request
+static mut REQUEST_BUF: *mut u8 = ptr::null_mut();
+
+#[no_mangle]
+pub extern "C" fn luw_module_end_handler() {
+ unsafe {
+ luw_free(REQUEST_BUF as *mut c_void);
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn luw_module_init_handler() {
+ unsafe {
+ REQUEST_BUF = luw_malloc(luw_mem_get_init_size().try_into().unwrap())
+ as *mut u8;
+ }
+}
+
+pub extern "C" fn hdr_iter_func(
+ ctx: *mut luw_ctx_t,
+ name: *const c_char,
+ value: *const c_char,
+ _data: *mut c_void,
+) -> bool {
+ unsafe {
+ luw_mem_writep(
+ ctx,
+ "%s = %s\n\0".as_ptr() as *const c_char,
+ name,
+ value,
+ );
+ }
+
+ return true;
+}
+
+#[no_mangle]
+pub extern "C" fn luw_request_handler(addr: *mut u8) -> i32 {
+ // Need a initalization
+ //
+ // It sucks that rust needs this, this is supposed to be
+ // an opaque structure and the structure is 0-initialised
+ // in luw_init_ctx();
+ let mut ctx_: luw_ctx_t = luw_ctx_t {
+ addr: ptr::null_mut(),
+ mem: ptr::null_mut(),
+ req: ptr::null_mut(),
+ resp: ptr::null_mut(),
+ resp_hdr: ptr::null_mut(),
+ resp_offset: 0,
+ req_buf: ptr::null_mut(),
+ hdrp: ptr::null_mut(),
+ reqp: ptr::null_mut(),
+ };
+ let ctx: *mut luw_ctx_t = &mut ctx_;
+
+ unsafe {
+ // Initialise the context structure.
+ //
+ // addr is the address of the previously allocated memory shared
+ // between the module and unit.
+ //
+ // The response data will be stored @ addr + offset (of 4096 bytes).
+ // This will leave some space for the response headers.
+ luw_init_ctx(ctx, addr, 4096);
+
+ // Allocate memory to store the request and copy the request data.
+ luw_set_req_buf(ctx, &mut REQUEST_BUF, luw_srb_flags_t_LUW_SRB_NONE);
+
+ // Define the Response Body Text.
+
+ luw_mem_writep(
+ ctx,
+ " * Welcome to WebAssembly in Rust on Unit! \
+ [libunit-wasm (%d.%d.%d/%#0.8x)] \
+ *\n\n\0"
+ .as_ptr() as *const c_char,
+ LUW_VERSION_MAJOR,
+ LUW_VERSION_MINOR,
+ LUW_VERSION_PATCH,
+ LUW_VERSION_NUMBER,
+ );
+
+ luw_mem_writep(ctx, "[Request Info]\n\0".as_ptr() as *const c_char);
+
+ luw_mem_writep(
+ ctx,
+ "REQUEST_PATH = %s\n\0".as_ptr() as *const c_char,
+ luw_get_http_path(ctx) as *const c_char,
+ );
+ luw_mem_writep(
+ ctx,
+ "METHOD = %s\n\0".as_ptr() as *const c_char,
+ luw_get_http_method(ctx) as *const c_char,
+ );
+ luw_mem_writep(
+ ctx,
+ "VERSION = %s\n\0".as_ptr() as *const c_char,
+ luw_get_http_version(ctx) as *const c_char,
+ );
+ luw_mem_writep(
+ ctx,
+ "QUERY = %s\n\0".as_ptr() as *const c_char,
+ luw_get_http_query(ctx) as *const c_char,
+ );
+ luw_mem_writep(
+ ctx,
+ "REMOTE = %s\n\0".as_ptr() as *const c_char,
+ luw_get_http_remote(ctx) as *const c_char,
+ );
+ luw_mem_writep(
+ ctx,
+ "LOCAL_ADDR = %s\n\0".as_ptr() as *const c_char,
+ luw_get_http_local_addr(ctx) as *const c_char,
+ );
+ luw_mem_writep(
+ ctx,
+ "LOCAL_PORT = %s\n\0".as_ptr() as *const c_char,
+ luw_get_http_local_port(ctx) as *const c_char,
+ );
+ luw_mem_writep(
+ ctx,
+ "SERVER_NAME = %s\n\0".as_ptr() as *const c_char,
+ luw_get_http_server_name(ctx) as *const c_char,
+ );
+
+ luw_mem_writep(
+ ctx,
+ "\n[Request Headers]\n\0".as_ptr() as *const c_char,
+ );
+
+ luw_http_hdr_iter(ctx, Some(hdr_iter_func), ptr::null_mut());
+
+ let method = CStr::from_ptr(luw_get_http_method(ctx)).to_str().unwrap();
+ if method == "POST" || method == "PUT" {
+ luw_mem_writep(
+ ctx,
+ "\n[%s data]\n\0".as_ptr() as *const c_char,
+ luw_get_http_method(ctx) as *const c_char,
+ );
+ luw_mem_writep_data(
+ ctx,
+ luw_get_http_content(ctx),
+ luw_get_http_content_len(ctx),
+ );
+ luw_mem_writep(ctx, "\n\0".as_ptr() as *const c_char);
+ }
+
+ let content_len = format!("{}\0", luw_get_response_data_size(ctx));
+
+ // Init Response Headers
+ //
+ // Needs the context, number of headers about to add as well as
+ // the offset where to store the headers. In this case we are
+ // storing the response headers at the beginning of our shared
+ // memory at offset 0.
+
+ luw_http_init_headers(ctx, 2, 0);
+ luw_http_add_header(
+ ctx,
+ 0,
+ "Content-Type\0".as_ptr() as *const c_char,
+ "text/plain\0".as_ptr() as *const c_char,
+ );
+ luw_http_add_header(
+ ctx,
+ 1,
+ "Content-Length\0".as_ptr() as *const c_char,
+ content_len.as_ptr() as *const c_char,
+ );
+
+ // This calls nxt_wasm_send_headers() in Unit
+ luw_http_send_headers(ctx);
+
+ // This calls nxt_wasm_send_response() in Unit
+ luw_http_send_response(ctx);
+
+ // This calls nxt_wasm_response_end() in Unit
+ luw_http_response_end();
+ }
+
+ return 0;
+}
diff --git a/examples/rust/upload-reflector/Cargo.toml b/examples/rust/upload-reflector/Cargo.toml
new file mode 100644
index 0000000..dc9d23f
--- /dev/null
+++ b/examples/rust/upload-reflector/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "rust-upload-reflector"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+unit-wasm = { path = "../../../src/rust", version = "0.1.0-beta" }
+
+[lib]
+crate-type = ["cdylib"]
diff --git a/examples/rust/upload-reflector/src/lib.rs b/examples/rust/upload-reflector/src/lib.rs
new file mode 100644
index 0000000..9893d5a
--- /dev/null
+++ b/examples/rust/upload-reflector/src/lib.rs
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+/*
+ * Copyright (C) Andrew Clayton
+ * Copyright (C) Timo Stark
+ * Copyright (C) F5, Inc.
+ */
+
+// Include RAW FFI Bindings.
+// @todo: Replace this with the new native Rust API
+use unit_wasm::ffi::*;
+
+use std::os::raw::c_char;
+use std::os::raw::c_void;
+use std::ptr;
+
+static mut CTX: luw_ctx_t = luw_ctx_t {
+ addr: ptr::null_mut(),
+ mem: ptr::null_mut(),
+ req: ptr::null_mut(),
+ resp: ptr::null_mut(),
+ resp_hdr: ptr::null_mut(),
+ resp_offset: 0,
+ req_buf: ptr::null_mut(),
+ hdrp: ptr::null_mut(),
+ reqp: ptr::null_mut(),
+};
+
+static mut TOTAL_RESPONSE_SENT: usize = 0;
+
+// Buffer of some size to store the copy of the request
+static mut REQUEST_BUF: *mut u8 = ptr::null_mut();
+
+#[no_mangle]
+pub extern "C" fn luw_response_end_handler() {
+ unsafe {
+ TOTAL_RESPONSE_SENT = 0;
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn luw_request_end_handler() {
+ unsafe {
+ if REQUEST_BUF.is_null() {
+ return;
+ }
+
+ luw_free(REQUEST_BUF as *mut c_void);
+ REQUEST_BUF = ptr::null_mut();
+ }
+}
+
+pub fn upload_reflector(ctx: *mut luw_ctx_t) -> i32 {
+ let write_bytes: usize;
+
+ unsafe {
+ // Send headers
+ if TOTAL_RESPONSE_SENT == 0 {
+ let content_len = format!("{}\0", luw_get_http_content_len(ctx));
+ let defct = "application/octet-stream\0".as_ptr() as *const c_char;
+ let mut ct = luw_http_hdr_get_value(
+ ctx,
+ "Content-Type\0".as_ptr() as *const c_char,
+ );
+
+ if ct == ptr::null_mut() {
+ ct = defct;
+ }
+
+ luw_http_init_headers(ctx, 2, 0);
+ luw_http_add_header(
+ ctx,
+ 0,
+ "Content-Type\0".as_ptr() as *const c_char,
+ ct,
+ );
+ luw_http_add_header(
+ ctx,
+ 1,
+ "Content-Length\0".as_ptr() as *const c_char,
+ content_len.as_ptr() as *const c_char,
+ );
+ luw_http_send_headers(ctx);
+ }
+
+ write_bytes = luw_mem_fill_buf_from_req(ctx, TOTAL_RESPONSE_SENT);
+ TOTAL_RESPONSE_SENT += write_bytes;
+
+ luw_http_send_response(ctx);
+
+ if TOTAL_RESPONSE_SENT == luw_get_http_content_len(ctx) {
+ // Tell Unit no more data to send
+ luw_http_response_end();
+ }
+ }
+
+ return 0;
+}
+
+#[no_mangle]
+pub extern "C" fn luw_request_handler(addr: *mut u8) -> i32 {
+ unsafe {
+ let ctx: *mut luw_ctx_t = &mut CTX;
+
+ if REQUEST_BUF.is_null() {
+ luw_init_ctx(ctx, addr, 0 /* Response offset */);
+ /*
+ * Take a copy of the request and use that, we do this
+ * in APPEND mode so we can build up request_buf from
+ * multiple requests.
+ *
+ * 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.
+ */
+ luw_set_req_buf(
+ ctx,
+ &mut REQUEST_BUF,
+ luw_srb_flags_t_LUW_SRB_APPEND
+ | luw_srb_flags_t_LUW_SRB_ALLOC
+ | luw_srb_flags_t_LUW_SRB_FULL_SIZE,
+ );
+ } else {
+ luw_req_buf_append(ctx, addr);
+ }
+
+ upload_reflector(ctx);
+ }
+
+ return 0;
+}