summaryrefslogtreecommitdiffhomepage
path: root/src/c
diff options
context:
space:
mode:
Diffstat (limited to 'src/c')
-rw-r--r--src/c/.gitignore1
-rw-r--r--src/c/Makefile21
-rw-r--r--src/c/include/unit/unit-wasm.h211
-rw-r--r--src/c/libunit-wasm.c381
4 files changed, 614 insertions, 0 deletions
diff --git a/src/c/.gitignore b/src/c/.gitignore
new file mode 100644
index 0000000..a676009
--- /dev/null
+++ b/src/c/.gitignore
@@ -0,0 +1 @@
+libunit-wasm.a
diff --git a/src/c/Makefile b/src/c/Makefile
new file mode 100644
index 0000000..83262f6
--- /dev/null
+++ b/src/c/Makefile
@@ -0,0 +1,21 @@
+include ../../shared.mk
+
+CFLAGS += -Iinclude
+
+SDIR = src/c
+
+TARGETS = libunit-wasm.o libunit-wasm.a
+
+.PHONY: libunit-wasm
+libunit-wasm: $(TARGETS)
+
+libunit-wasm.o: libunit-wasm.c include/unit/unit-wasm.h
+ $(PP_CC) $(SDIR)/$@
+ $(v)$(CC) $(CFLAGS) -fvisibility=hidden -c $<
+
+libunit-wasm.a: libunit-wasm.o
+ $(PP_AR) $(SDIR)/$@
+ $(v)llvm-ar rcs $@ $<
+
+clean:
+ rm -f *.o *.a *.gch
diff --git a/src/c/include/unit/unit-wasm.h b/src/c/include/unit/unit-wasm.h
new file mode 100644
index 0000000..df3852a
--- /dev/null
+++ b/src/c/include/unit/unit-wasm.h
@@ -0,0 +1,211 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+/*
+ * Copyright (C) Andrew Clayton
+ * Copyright (C) F5, Inc.
+ */
+
+#ifndef _UNIT_WASM_H_
+#define _UNIT_WASM_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <strings.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LUW_VERSION_MAJOR 0
+#define LUW_VERSION_MINOR 1
+#define LUW_VERSION_PATCH 0
+
+/* Version number in hex 0xMMmmpp00 */
+#define LUW_VERSION_NUMBER \
+ ( (LUW_VERSION_MAJOR << 24) | \
+ (LUW_VERSION_MINOR << 16) | \
+ (LUW_VERSION_PATCH << 8) )
+
+#define __luw_export_name(name) __attribute__((export_name(name)))
+
+#define __luw_unused __attribute__((unused))
+#define __luw_maybe_unused __luw_unused
+
+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;
+
+struct luw_hdr_field {
+ u32 name_off;
+ u32 name_len;
+ u32 value_off;
+ u32 value_len;
+};
+
+struct luw_req {
+ u32 method_off;
+ u32 method_len;
+ u32 version_off;
+ u32 version_len;
+ u32 path_off;
+ u32 path_len;
+ u32 query_off;
+ u32 query_len;
+ u32 remote_off;
+ u32 remote_len;
+ u32 local_addr_off;
+ u32 local_addr_len;
+ u32 local_port_off;
+ u32 local_port_len;
+ u32 server_name_off;
+ u32 server_name_len;
+
+ u32 content_off;
+ u32 content_len;
+ u32 content_sent;
+ u32 total_content_sent;
+
+ u32 request_size;
+
+ u32 nr_fields;
+
+ u32 tls;
+
+ struct luw_hdr_field fields[];
+};
+
+struct luw_resp {
+ u32 size;
+
+ u8 data[];
+};
+
+struct luw_resp_hdr {
+ u32 nr_fields;
+
+ struct luw_hdr_field fields[];
+};
+
+typedef struct {
+ /* pointer to the shared memory */
+ u8 *addr;
+
+ /* points to the end of ctx->resp->data */
+ u8 *mem;
+
+ /* struct luw_req representation of the shared memory */
+ struct luw_req *req;
+
+ /* struct luw_resp representation of the shared memory */
+ struct luw_resp *resp;
+
+ /* struct luw_resp_hdr represnetation of the shared memory */
+ struct luw_resp_hdr *resp_hdr;
+
+ /* offset to where the struct resp starts in the shared memory */
+ size_t resp_offset;
+
+ /* points to the external buffer used for a copy of the request */
+ u8 *req_buf;
+
+ /* points to the end of the fields array in struct luw_resp_hdr */
+ u8 *hdrp;
+
+ /* points to the end of ctx->req_buf */
+ u8 *reqp;
+} luw_ctx_t;
+
+typedef enum {
+ LUW_SRB_NONE = 0x00,
+ LUW_SRB_APPEND = 0x01,
+ LUW_SRB_ALLOC = 0x02,
+ LUW_SRB_FULL_SIZE = 0x04,
+} luw_srb_flags_t;
+#define LUW_SRB_FLAGS_ALL \
+ (LUW_SRB_NONE|LUW_SRB_APPEND|LUW_SRB_ALLOC|LUW_SRB_FULL_SIZE)
+
+typedef struct luw_hdr_field luw_http_hdr_iter_t;
+
+#define luw_foreach_http_hdr(ctx, iter, name, value) \
+ for (iter = ctx.req->fields, \
+ name = (const char *)ctx.req + iter->name_off; \
+ (iter < (ctx.req->fields + ctx.req->nr_fields)) && \
+ (value = (const char *)ctx.req + iter->value_off); \
+ iter++, name = (const char *)ctx.req + iter->name_off)
+
+/* Imported functions from the host/runtime */
+__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_headers")))
+void nxt_wasm_send_headers(u32 offset);
+__attribute__((import_module("env"), import_name("nxt_wasm_send_response")))
+void nxt_wasm_send_response(u32 offset);
+
+extern void luw_module_init_handler(void);
+extern void luw_module_end_handler(void);
+extern void luw_request_init_handler(void);
+extern void luw_request_end_handler(void);
+extern void luw_response_end_handler(void);
+extern int luw_request_handler(u8 *addr);
+extern void luw_free_handler(u32 addr);
+extern u32 luw_malloc_handler(size_t size);
+
+#pragma GCC visibility push(default)
+
+extern void luw_init_ctx(luw_ctx_t *ctx, u8 *addr, size_t offset);
+extern void luw_destroy_ctx(const luw_ctx_t *ctx);
+extern int luw_set_req_buf(luw_ctx_t *ctx, u8 **buf, unsigned long flags);
+extern const char *luw_get_http_path(const luw_ctx_t *ctx);
+extern const char *luw_get_http_method(const luw_ctx_t *ctx);
+extern const char *luw_get_http_version(const luw_ctx_t *ctx);
+extern const char *luw_get_http_query(const luw_ctx_t *ctx);
+extern const char *luw_get_http_remote(const luw_ctx_t *ctx);
+extern const char *luw_get_http_local_addr(const luw_ctx_t *ctx);
+extern const char *luw_get_http_local_port(const luw_ctx_t *ctx);
+extern const char *luw_get_http_server_name(const luw_ctx_t *ctx);
+extern const u8 *luw_get_http_content(const luw_ctx_t *ctx);
+extern size_t luw_get_http_content_len(const luw_ctx_t *ctx);
+extern size_t luw_get_http_content_sent(const luw_ctx_t *ctx);
+extern bool luw_http_is_tls(const luw_ctx_t *ctx);
+extern void luw_http_hdr_iter(luw_ctx_t *ctx,
+ bool (*luw_http_hdr_iter_func)(luw_ctx_t *ctx,
+ const char *name,
+ const char *value,
+ void *data),
+ void *user_data);
+extern const char *luw_http_hdr_get_value(luw_ctx_t *ctx, const char *hdr);
+extern size_t luw_get_response_data_size(const luw_ctx_t *ctx);
+extern int luw_mem_writep(luw_ctx_t *ctx, const char *fmt, ...);
+extern size_t luw_mem_writep_data(luw_ctx_t *ctx, const u8 *src, size_t size);
+extern void luw_req_buf_append(luw_ctx_t *ctx, const u8 *src);
+extern size_t luw_mem_fill_buf_from_req(luw_ctx_t *ctx, size_t from);
+extern void luw_mem_reset(luw_ctx_t *ctx);
+extern void luw_http_send_response(const luw_ctx_t *ctx);
+extern void luw_http_init_headers(luw_ctx_t *ctx, size_t nr, size_t offset);
+extern void luw_http_add_header(luw_ctx_t *ctx, u16 idx, const char *name,
+ const char *value);
+extern void luw_http_send_headers(const luw_ctx_t *ctx);
+extern void luw_http_response_end(void);
+extern u32 luw_mem_get_init_size(void);
+
+/*
+ * Convenience wrappers for the Rust bindings, not for general consumption.
+ */
+extern void *luw_malloc(size_t size);
+extern void luw_free(void *ptr);
+
+#pragma GCC visibility pop
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* _UNIT_WASM_H_ */
diff --git a/src/c/libunit-wasm.c b/src/c/libunit-wasm.c
new file mode 100644
index 0000000..1860fe3
--- /dev/null
+++ b/src/c/libunit-wasm.c
@@ -0,0 +1,381 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+/*
+ * Copyright (C) Andrew Clayton
+ * Copyright (C) F5, Inc.
+ */
+
+#define _GNU_SOURCE
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+
+#include "unit/unit-wasm.h"
+
+/*
+ * Some handlers are required some are optional.
+ *
+ * They are defined as _weak_ symbols so they can be overridden
+ * in the module.
+ */
+
+/* Optional Handlers */
+__attribute__((export_name("luw_module_init_handler"), __weak__))
+void luw_module_init_handler(void)
+{
+}
+
+__attribute__((export_name("luw_module_end_handler"), __weak__))
+void luw_module_end_handler(void)
+{
+}
+
+__attribute__((export_name("luw_request_init_handler"), __weak__))
+void luw_request_init_handler(void)
+{
+}
+
+__attribute__((export_name("luw_request_end_handler"), __weak__))
+void luw_request_end_handler(void)
+{
+}
+
+__attribute__((export_name("luw_response_end_handler"), __weak__))
+void luw_response_end_handler(void)
+{
+}
+
+/* Required Handlers */
+__attribute__((export_name("luw_request_handler"), __weak__))
+int luw_request_handler(u8 *addr)
+{
+ (void)addr;
+
+ return 0;
+}
+
+__attribute__((export_name("luw_free_handler"), __weak__))
+void luw_free_handler(u32 addr)
+{
+ free((void *)addr);
+}
+
+__attribute__((export_name("luw_malloc_handler"), __weak__))
+u32 luw_malloc_handler(size_t size)
+{
+ return (u32)malloc(size);
+}
+
+void luw_init_ctx(luw_ctx_t *ctx, u8 *addr, size_t offset)
+{
+ *ctx = (luw_ctx_t){ };
+
+ ctx->addr = addr;
+ ctx->req = (struct luw_req *)addr;
+ ctx->resp = (struct luw_resp *)(addr + offset);
+ ctx->mem = ctx->resp->data;
+ ctx->resp_offset = offset;
+ ctx->resp->size = 0;
+ ctx->resp_hdr->nr_fields = 0;
+}
+
+/*
+ * Allows to set an external buffer to be used as a copy for
+ * the request.
+ *
+ * The flags dictate a few behaviours
+ *
+ * LUW_SRB_NONE - No specific action to be performed. It will
+ * simply copy the request data into the specified
+ * buffer.
+ *
+ * LUW_SRB_APPEND - Sets up append mode whereby multiple successive
+ * requests will be appended to the specified buffer.
+ *
+ * The first request will have all its metadata
+ * copied. Subsequent requests will _only_ have the
+ * actual body data appended.
+ *
+ * LUW_SRB_ALLOC - Allocate memory for the specified buffer.
+ *
+ * LUW_SRB_FULL_SIZE - Used in conjunction with LUW_SRB_ALLOC. By
+ * default only ctx->req->request_size is
+ * allocated. If this flag is present it says to
+ * allocate memory for the _entire_ request that
+ * will eventually be sent.
+ */
+int luw_set_req_buf(luw_ctx_t *ctx, u8 **buf, unsigned long flags)
+{
+ size_t alloc_size;
+ size_t copy_bytes;
+
+ /* Check for unknown flags */
+ if (flags & ~LUW_SRB_FLAGS_ALL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Check for invalid combinations of flags */
+ if (flags & LUW_SRB_FULL_SIZE && !(flags & LUW_SRB_ALLOC)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ alloc_size = copy_bytes = ctx->req->request_size;
+
+ if (flags & LUW_SRB_FULL_SIZE)
+ alloc_size = ctx->req->content_off + ctx->req->content_len;
+
+ if (flags & LUW_SRB_ALLOC) {
+ *buf = malloc(alloc_size);
+ if (!*buf)
+ return -1;
+ }
+
+ memcpy(*buf, ctx->addr, copy_bytes);
+ ctx->req_buf = *buf;
+ ctx->req = (struct luw_req *)ctx->req_buf;
+ ctx->reqp = ctx->req_buf;
+
+ if (flags & LUW_SRB_APPEND)
+ ctx->reqp = ctx->req_buf + copy_bytes;
+
+ return 0;
+}
+
+const char *luw_get_http_path(const luw_ctx_t *ctx)
+{
+ return (const char *)ctx->req + ctx->req->path_off;
+}
+
+const char *luw_get_http_method(const luw_ctx_t *ctx)
+{
+ return (const char *)ctx->req + ctx->req->method_off;
+}
+
+const char *luw_get_http_version(const luw_ctx_t *ctx)
+{
+ return (const char *)ctx->req + ctx->req->version_off;
+}
+
+const char *luw_get_http_query(const luw_ctx_t *ctx)
+{
+ return (const char *)ctx->req + ctx->req->query_off;
+}
+
+const char *luw_get_http_remote(const luw_ctx_t *ctx)
+{
+ return (const char *)ctx->req + ctx->req->remote_off;
+}
+
+const char *luw_get_http_local_addr(const luw_ctx_t *ctx)
+{
+ return (const char *)ctx->req + ctx->req->local_addr_off;
+}
+
+const char *luw_get_http_local_port(const luw_ctx_t *ctx)
+{
+ return (const char *)ctx->req + ctx->req->local_port_off;
+}
+
+const char *luw_get_http_server_name(const luw_ctx_t *ctx)
+{
+ return (const char *)ctx->req + ctx->req->server_name_off;
+}
+
+const u8 *luw_get_http_content(const luw_ctx_t *ctx)
+{
+ return (u8 *)ctx->req + ctx->req->content_off;
+}
+
+/* Returns the size of the overall content length */
+size_t luw_get_http_content_len(const luw_ctx_t *ctx)
+{
+ return ctx->req->content_len;
+}
+
+/* Returns the size of the content sent in _this_ request */
+size_t luw_get_http_content_sent(const luw_ctx_t *ctx)
+{
+ return ctx->req->content_sent;
+}
+
+bool luw_http_is_tls(const luw_ctx_t *ctx)
+{
+ return ctx->req->tls;
+}
+
+void luw_http_hdr_iter(luw_ctx_t *ctx,
+ bool (*luw_http_hdr_iter_func)(luw_ctx_t *ctx,
+ const char *name,
+ const char *value,
+ void *data),
+ void *user_data)
+{
+ struct luw_hdr_field *hf;
+ struct luw_hdr_field *hf_end;
+
+ hf_end = ctx->req->fields + ctx->req->nr_fields;
+ for (hf = ctx->req->fields; hf < hf_end; hf++) {
+ const char *name = (const char *)ctx->req + hf->name_off;
+ const char *value = (const char *)ctx->req + hf->value_off;
+ bool again;
+
+ again = luw_http_hdr_iter_func(ctx, name, value, user_data);
+ if (!again)
+ break;
+ }
+}
+
+const char *luw_http_hdr_get_value(luw_ctx_t *ctx, const char *hdr)
+{
+ luw_http_hdr_iter_t *iter;
+ const char *name;
+ const char *value;
+
+ luw_foreach_http_hdr((*ctx), iter, name, value) {
+ if (strcasecmp(name, hdr) == 0)
+ return value;
+ }
+
+ return NULL;
+}
+
+/* Returns the size of ctx->resp->data[] */
+size_t luw_get_response_data_size(const luw_ctx_t *ctx)
+{
+ return ctx->mem - ctx->resp->data;
+}
+
+/* Appends (non-nul terminmates) formatted data to the response buffer */
+__attribute__((__format__(printf, 2, 3)))
+int luw_mem_writep(luw_ctx_t *ctx, const char *fmt, ...)
+{
+ int len;
+ char *logbuf;
+ va_list ap;
+
+ va_start(ap, fmt);
+ len = vasprintf(&logbuf, fmt, ap);
+ if (len == -1) {
+ va_end(ap);
+ return -1;
+ }
+ va_end(ap);
+
+ ctx->mem = mempcpy(ctx->mem, logbuf, len);
+ ctx->resp->size += len;
+
+ free(logbuf);
+
+ return len;
+}
+
+/* Appends data of length size to the response buffer */
+size_t luw_mem_writep_data(luw_ctx_t *ctx, const u8 *src, size_t size)
+{
+ ctx->mem = mempcpy(ctx->mem, src, size);
+ ctx->resp->size += size;
+
+ return ctx->resp->size;
+}
+
+/* Append data from the request to the previously setup request_buffer. */
+void luw_req_buf_append(luw_ctx_t *ctx, const u8 *src)
+{
+ struct luw_req *req = (struct luw_req *)src;
+
+ ctx->reqp = mempcpy(ctx->reqp, src + req->content_off,
+ req->request_size);
+ ctx->req->content_sent = req->content_sent;
+ ctx->req->total_content_sent = req->total_content_sent;
+}
+
+/*
+ * Convenience function to fill the response buffer with data from
+ * the request buffer.
+ *
+ * The runtime allocates NXT_WASM_MEM_SIZE + NXT_WASM_PAGE_SIZE
+ * bytes so we don't need to worry about the size of the actual
+ * response structures.
+ */
+size_t luw_mem_fill_buf_from_req(luw_ctx_t *ctx, size_t from)
+{
+ size_t write_bytes;
+ size_t mem_size = nxt_wasm_get_init_mem_size();
+
+ write_bytes = ctx->req->content_sent;
+ if (write_bytes > mem_size)
+ write_bytes = mem_size;
+
+ memcpy(ctx->resp->data, ctx->req_buf + ctx->req->content_off + from,
+ write_bytes);
+ ctx->resp->size = write_bytes;
+
+ return write_bytes;
+}
+
+void luw_mem_reset(luw_ctx_t *ctx)
+{
+ ctx->mem = ctx->resp->data;
+ ctx->resp->size = 0;
+ ctx->resp_hdr->nr_fields = 0;
+}
+
+void luw_http_send_response(const luw_ctx_t *ctx)
+{
+ nxt_wasm_send_response(ctx->resp_offset);
+}
+
+void luw_http_init_headers(luw_ctx_t *ctx, size_t nr, size_t offset)
+{
+ ctx->resp_hdr = (struct luw_resp_hdr *)(ctx->addr + offset);
+ ctx->hdrp = (u8 *)ctx->resp_hdr + sizeof(struct luw_resp_hdr) +
+ (nr * sizeof(struct luw_hdr_field));
+
+ ctx->resp_hdr->nr_fields = nr;
+}
+
+void luw_http_add_header(luw_ctx_t *ctx, u16 idx, const char *name,
+ const char *value)
+{
+ ctx->resp_hdr->fields[idx].name_off = ctx->hdrp - ctx->addr;
+ ctx->resp_hdr->fields[idx].name_len = strlen(name);
+ ctx->hdrp = mempcpy(ctx->hdrp, name, strlen(name));
+
+ ctx->resp_hdr->fields[idx].value_off = ctx->hdrp - ctx->addr;
+ ctx->resp_hdr->fields[idx].value_len = strlen(value);
+ ctx->hdrp = mempcpy(ctx->hdrp, value, strlen(value));
+}
+
+void luw_http_send_headers(const luw_ctx_t *ctx)
+{
+ nxt_wasm_send_headers((u8 *)ctx->resp_hdr - ctx->addr);
+}
+
+void luw_http_response_end(void)
+{
+ nxt_wasm_response_end();
+}
+
+u32 luw_mem_get_init_size(void)
+{
+ return nxt_wasm_get_init_mem_size();
+}
+
+/* These are really just convenience wrappers for the rust bindings... */
+
+void *luw_malloc(size_t size)
+{
+ return malloc(size);
+}
+
+void luw_free(void *ptr)
+{
+ free(ptr);
+}