summaryrefslogtreecommitdiffhomepage
path: root/src/wasm
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm')
-rw-r--r--src/wasm/nxt_rt_wasmtime.c407
-rw-r--r--src/wasm/nxt_wasm.c258
-rw-r--r--src/wasm/nxt_wasm.h136
3 files changed, 801 insertions, 0 deletions
diff --git a/src/wasm/nxt_rt_wasmtime.c b/src/wasm/nxt_rt_wasmtime.c
new file mode 100644
index 00000000..07a6165a
--- /dev/null
+++ b/src/wasm/nxt_rt_wasmtime.c
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) Andrew Clayton
+ * Copyright (C) F5, Inc.
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdarg.h>
+
+#include <wasm.h>
+#include <wasi.h>
+#include <wasmtime.h>
+
+#include "nxt_wasm.h"
+
+
+typedef struct nxt_wasmtime_ctx_s nxt_wasmtime_ctx_t;
+
+struct nxt_wasmtime_ctx_s {
+ wasm_engine_t *engine;
+ wasmtime_store_t *store;
+ wasmtime_memory_t memory;
+ wasmtime_module_t *module;
+ wasmtime_linker_t *linker;
+ wasmtime_context_t *ctx;
+};
+
+static nxt_wasmtime_ctx_t nxt_wasmtime_ctx;
+
+
+static void
+nxt_wasmtime_err_msg(wasmtime_error_t *error, wasm_trap_t *trap,
+ const char *fmt, ...)
+{
+ va_list args;
+ wasm_byte_vec_t error_message;
+
+ fprintf(stderr, "WASMTIME ERROR: ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+
+ if (error == NULL && trap == NULL) {
+ return;
+ }
+
+ if (error != NULL) {
+ wasmtime_error_message(error, &error_message);
+ wasmtime_error_delete(error);
+ } else {
+ wasm_trap_message(trap, &error_message);
+ wasm_trap_delete(trap);
+ }
+ fprintf(stderr, "%.*s\n", (int)error_message.size, error_message.data);
+
+ wasm_byte_vec_delete(&error_message);
+}
+
+
+static wasm_trap_t *
+nxt_wasm_get_init_mem_size(void *env, wasmtime_caller_t *caller,
+ const wasmtime_val_t *args, size_t nargs,
+ wasmtime_val_t *results, size_t nresults)
+{
+ results[0].of.i32 = NXT_WASM_MEM_SIZE;
+
+ return NULL;
+}
+
+
+static wasm_trap_t *
+nxt_wasm_response_end(void *env, wasmtime_caller_t *caller,
+ const wasmtime_val_t *args, size_t nargs,
+ wasmtime_val_t *results, size_t nresults)
+{
+ nxt_wasm_do_response_end(env);
+
+ return NULL;
+}
+
+
+static wasm_trap_t *
+nxt_wasm_send_response(void *env, wasmtime_caller_t *caller,
+ const wasmtime_val_t *args, size_t nargs,
+ wasmtime_val_t *results, size_t nresults)
+{
+ nxt_wasm_do_send_response(env, args[0].of.i32);
+
+ return NULL;
+}
+
+
+static wasm_trap_t *
+nxt_wasm_send_headers(void *env, wasmtime_caller_t *caller,
+ const wasmtime_val_t *args, size_t nargs,
+ wasmtime_val_t *results, size_t nresults)
+{
+ nxt_wasm_do_send_headers(env, args[0].of.i32);
+
+ return NULL;
+}
+
+
+static void
+nxt_wasmtime_execute_hook(const nxt_wasm_ctx_t *ctx, nxt_wasm_fh_t hook)
+{
+ const char *name = ctx->fh[hook].func_name;
+ wasm_trap_t *trap = NULL;
+ wasmtime_error_t *error;
+ nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
+ const nxt_wasm_func_t *func = &ctx->fh[hook].func;
+
+ if (name == NULL) {
+ return;
+ }
+
+ error = wasmtime_func_call(rt_ctx->ctx, func, NULL, 0, NULL, 0, &trap);
+ if (error != NULL || trap != NULL) {
+ nxt_wasmtime_err_msg(error, trap, "failed to call hook function [%s]",
+ name);
+ }
+}
+
+
+static void
+nxt_wasmtime_execute_request(const nxt_wasm_ctx_t *ctx)
+{
+ int i = 0;
+ wasm_trap_t *trap = NULL;
+ wasmtime_val_t args[1] = { };
+ wasmtime_val_t results[1] = { };
+ wasmtime_error_t *error;
+ nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
+ const nxt_wasm_func_t *func = &ctx->fh[NXT_WASM_FH_REQUEST].func;
+
+ args[i].kind = WASMTIME_I32;
+ args[i++].of.i32 = ctx->baddr_off;
+
+ error = wasmtime_func_call(rt_ctx->ctx, func, args, i, results, 1, &trap);
+ if (error != NULL || trap != NULL) {
+ nxt_wasmtime_err_msg(error, trap,
+ "failed to call function [->wasm_request_handler]"
+ );
+ }
+}
+
+
+static void
+nxt_wasmtime_set_function_imports(nxt_wasm_ctx_t *ctx)
+{
+ nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
+
+ static const struct {
+ const char *func_name;
+
+ wasmtime_func_callback_t func;
+ wasm_valkind_t params[1];
+ wasm_valkind_t results[1];
+
+ enum {
+ NXT_WASM_FT_0_0,
+ NXT_WASM_FT_1_0,
+ NXT_WASM_FT_0_1,
+ } ft;
+ } import_functions[] = {
+ {
+ .func_name = "nxt_wasm_get_init_mem_size",
+ .func = nxt_wasm_get_init_mem_size,
+ .results = { WASM_I32 },
+ .ft = NXT_WASM_FT_0_1
+ }, {
+ .func_name = "nxt_wasm_response_end",
+ .func = nxt_wasm_response_end,
+ .ft = NXT_WASM_FT_0_0
+ }, {
+ .func_name = "nxt_wasm_send_response",
+ .func = nxt_wasm_send_response,
+ .params = { WASM_I32 },
+ .ft = NXT_WASM_FT_1_0
+ }, {
+ .func_name = "nxt_wasm_send_headers",
+ .func = nxt_wasm_send_headers,
+ .params = { WASM_I32 },
+ .ft = NXT_WASM_FT_1_0
+ },
+
+ { }
+ }, *imf;
+
+ for (imf = import_functions; imf->func_name != NULL; imf++) {
+ wasm_functype_t *func_ty;
+
+ switch (imf->ft) {
+ case NXT_WASM_FT_0_0:
+ func_ty = wasm_functype_new_0_0();
+ break;
+ case NXT_WASM_FT_1_0:
+ func_ty = wasm_functype_new_1_0(wasm_valtype_new(imf->params[0]));
+ break;
+ case NXT_WASM_FT_0_1:
+ func_ty = wasm_functype_new_0_1(wasm_valtype_new(imf->results[0]));
+ break;
+ default:
+ /* Stop GCC complaining about func_ty being used uninitialised */
+ func_ty = NULL;
+ }
+
+ wasmtime_linker_define_func(rt_ctx->linker, "env", 3,
+ imf->func_name, strlen(imf->func_name),
+ func_ty, imf->func, ctx, NULL);
+ wasm_functype_delete(func_ty);
+ }
+}
+
+
+static int
+nxt_wasmtime_get_function_exports(nxt_wasm_ctx_t *ctx)
+{
+ int i;
+ nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
+
+ for (i = 0; i < NXT_WASM_FH_NR; i++) {
+ bool ok;
+ wasmtime_extern_t item;
+
+ if (ctx->fh[i].func_name == NULL) {
+ continue;
+ }
+
+ ok = wasmtime_linker_get(rt_ctx->linker, rt_ctx->ctx, "", 0,
+ ctx->fh[i].func_name,
+ strlen(ctx->fh[i].func_name), &item);
+ if (!ok) {
+ nxt_wasmtime_err_msg(NULL, NULL,
+ "couldn't get (%s) export from module",
+ ctx->fh[i].func_name);
+ return -1;
+ }
+ ctx->fh[i].func = item.of.func;
+ }
+
+ return 0;
+}
+
+
+static int
+nxt_wasmtime_wasi_init(const nxt_wasm_ctx_t *ctx)
+{
+ wasi_config_t *wasi_config;
+ wasmtime_error_t *error;
+ nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
+
+ wasi_config = wasi_config_new();
+
+ wasi_config_inherit_env(wasi_config);
+ wasi_config_inherit_stdin(wasi_config);
+ wasi_config_inherit_stdout(wasi_config);
+ wasi_config_inherit_stderr(wasi_config);
+
+ error = wasmtime_context_set_wasi(rt_ctx->ctx, wasi_config);
+ if (error != NULL) {
+ nxt_wasmtime_err_msg(error, NULL, "failed to instantiate WASI");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+nxt_wasmtime_init_memory(nxt_wasm_ctx_t *ctx)
+{
+ int i = 0;
+ bool ok;
+ wasm_trap_t *trap = NULL;
+ wasmtime_val_t args[1] = { };
+ wasmtime_val_t results[1] = { };
+ wasmtime_error_t *error;
+ wasmtime_extern_t item;
+ nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
+ const nxt_wasm_func_t *func = &ctx->fh[NXT_WASM_FH_MALLOC].func;
+
+ args[i].kind = WASMTIME_I32;
+ args[i++].of.i32 = NXT_WASM_MEM_SIZE + NXT_WASM_PAGE_SIZE;
+
+ error = wasmtime_func_call(rt_ctx->ctx, func, args, i, results, 1, &trap);
+ if (error != NULL || trap != NULL) {
+ nxt_wasmtime_err_msg(error, trap,
+ "failed to call function [->wasm_malloc_handler]"
+ );
+ return -1;
+ }
+
+ ok = wasmtime_linker_get(rt_ctx->linker, rt_ctx->ctx, "", 0, "memory",
+ strlen("memory"), &item);
+ if (!ok) {
+ nxt_wasmtime_err_msg(NULL, NULL, "couldn't get 'memory' from module\n");
+ return -1;
+ }
+ rt_ctx->memory = item.of.memory;
+
+ ctx->baddr_off = results[0].of.i32;
+ ctx->baddr = wasmtime_memory_data(rt_ctx->ctx, &rt_ctx->memory);
+
+ ctx->baddr += ctx->baddr_off;
+
+ return 0;
+}
+
+
+static int
+nxt_wasmtime_init(nxt_wasm_ctx_t *ctx)
+{
+ int err;
+ FILE *fp;
+ size_t file_size;
+ wasm_byte_vec_t wasm;
+ wasmtime_error_t *error;
+ nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
+
+ rt_ctx->engine = wasm_engine_new();
+ rt_ctx->store = wasmtime_store_new(rt_ctx->engine, NULL, NULL);
+ rt_ctx->ctx = wasmtime_store_context(rt_ctx->store);
+
+ rt_ctx->linker = wasmtime_linker_new(rt_ctx->engine);
+ error = wasmtime_linker_define_wasi(rt_ctx->linker);
+ if (error != NULL) {
+ nxt_wasmtime_err_msg(error, NULL, "failed to link wasi");
+ return -1;
+ }
+
+ fp = fopen(ctx->module_path, "r");
+ if (!fp) {
+ nxt_wasmtime_err_msg(NULL, NULL,
+ "error opening file (%s)", ctx->module_path);
+ return -1;
+ }
+ fseek(fp, 0L, SEEK_END);
+ file_size = ftell(fp);
+ wasm_byte_vec_new_uninitialized(&wasm, file_size);
+ fseek(fp, 0L, SEEK_SET);
+ if (fread(wasm.data, file_size, 1, fp) != 1) {
+ nxt_wasmtime_err_msg(NULL, NULL, "error loading module");
+ fclose(fp);
+ return -1;
+ }
+ fclose(fp);
+
+ error = wasmtime_module_new(rt_ctx->engine, (uint8_t *)wasm.data, wasm.size,
+ &rt_ctx->module);
+ if (!rt_ctx->module) {
+ nxt_wasmtime_err_msg(error, NULL, "failed to compile module");
+ return -1;
+ }
+ wasm_byte_vec_delete(&wasm);
+
+ nxt_wasmtime_set_function_imports(ctx);
+
+ nxt_wasmtime_wasi_init(ctx);
+
+ error = wasmtime_linker_module(rt_ctx->linker, rt_ctx->ctx, "", 0,
+ rt_ctx->module);
+ if (error != NULL) {
+ nxt_wasmtime_err_msg(error, NULL, "failed to instantiate");
+ return -1;
+ }
+
+ err = nxt_wasmtime_get_function_exports(ctx);
+ if (err) {
+ return -1;
+ }
+
+ err = nxt_wasmtime_init_memory(ctx);
+ if (err) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void
+nxt_wasmtime_destroy(const nxt_wasm_ctx_t *ctx)
+{
+ int i = 0;
+ wasmtime_val_t args[1] = { };
+ nxt_wasmtime_ctx_t *rt_ctx = &nxt_wasmtime_ctx;
+ const nxt_wasm_func_t *func = &ctx->fh[NXT_WASM_FH_FREE].func;
+
+ args[i].kind = WASMTIME_I32;
+ args[i++].of.i32 = ctx->baddr_off;
+
+ wasmtime_func_call(rt_ctx->ctx, func, args, i, NULL, 0, NULL);
+
+ wasmtime_module_delete(rt_ctx->module);
+ wasmtime_store_delete(rt_ctx->store);
+ wasm_engine_delete(rt_ctx->engine);
+}
+
+
+const nxt_wasm_operations_t nxt_wasm_ops = {
+ .init = nxt_wasmtime_init,
+ .destroy = nxt_wasmtime_destroy,
+ .exec_request = nxt_wasmtime_execute_request,
+ .exec_hook = nxt_wasmtime_execute_hook,
+};
diff --git a/src/wasm/nxt_wasm.c b/src/wasm/nxt_wasm.c
new file mode 100644
index 00000000..d96668ad
--- /dev/null
+++ b/src/wasm/nxt_wasm.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) Andrew Clayton
+ * Copyright (C) F5, Inc.
+ */
+
+#include <nxt_main.h>
+#include <nxt_application.h>
+#include <nxt_unit.h>
+#include <nxt_unit_request.h>
+
+#include "nxt_wasm.h"
+
+
+#define NXT_WASM_VERSION "0.1"
+
+#define NXT_WASM_DO_HOOK(hook) nxt_wops->exec_hook(&nxt_wasm_ctx, hook);
+
+
+static uint32_t compat[] = {
+ NXT_VERNUM, NXT_DEBUG,
+};
+
+static nxt_wasm_ctx_t nxt_wasm_ctx;
+
+static const nxt_wasm_operations_t *nxt_wops;
+
+
+void
+nxt_wasm_do_response_end(nxt_wasm_ctx_t *ctx)
+{
+ nxt_unit_request_done(ctx->req, NXT_UNIT_OK);
+
+ NXT_WASM_DO_HOOK(NXT_WASM_FH_RESPONSE_END);
+}
+
+
+void
+nxt_wasm_do_send_headers(nxt_wasm_ctx_t *ctx, uint32_t offset)
+{
+ size_t fields_len;
+ unsigned int i;
+ nxt_wasm_response_fields_t *rh;
+
+ rh = (nxt_wasm_response_fields_t *)(ctx->baddr + offset);
+
+ fields_len = 0;
+ for (i = 0; i < rh->nfields; i++) {
+ fields_len += rh->fields[i].name_len + rh->fields[i].value_len;
+ }
+
+ nxt_unit_response_init(ctx->req, 200, rh->nfields, fields_len);
+
+ for (i = 0; i < rh->nfields; i++) {
+ const char *name;
+ const char *val;
+
+ name = (const char *)rh + rh->fields[i].name_off;
+ val = (const char *)rh + rh->fields[i].value_off;
+
+ nxt_unit_response_add_field(ctx->req, name, rh->fields[i].name_len,
+ val, rh->fields[i].value_len);
+ }
+
+ nxt_unit_response_send(ctx->req);
+}
+
+
+void
+nxt_wasm_do_send_response(nxt_wasm_ctx_t *ctx, uint32_t offset)
+{
+ nxt_wasm_response_t *resp;
+ nxt_unit_request_info_t *req = ctx->req;
+
+ if (!nxt_unit_response_is_init(req)) {
+ nxt_unit_response_init(req, 200, 0, 0);
+ }
+
+ resp = (nxt_wasm_response_t *)(nxt_wasm_ctx.baddr + offset);
+
+ nxt_unit_response_write(req, (const char *)resp->data, resp->size);
+}
+
+
+static void
+nxt_wasm_request_handler(nxt_unit_request_info_t *req)
+{
+ size_t offset, read_bytes, content_sent, content_len;
+ ssize_t bytes_read;
+ nxt_unit_field_t *sf, *sf_end;
+ nxt_unit_request_t *r;
+ nxt_wasm_request_t *wr;
+ nxt_wasm_http_field_t *df;
+
+ NXT_WASM_DO_HOOK(NXT_WASM_FH_REQUEST_INIT);
+
+ wr = (nxt_wasm_request_t *)nxt_wasm_ctx.baddr;
+
+#define SET_REQ_MEMBER(dmember, smember) \
+ do { \
+ const char *str = nxt_unit_sptr_get(&r->smember); \
+ wr->dmember##_off = offset; \
+ wr->dmember##_len = strlen(str); \
+ memcpy((uint8_t *)wr + offset, str, wr->dmember##_len + 1); \
+ offset += wr->dmember##_len + 1; \
+ } while (0)
+
+ r = req->request;
+ offset = sizeof(nxt_wasm_request_t)
+ + (r->fields_count * sizeof(nxt_wasm_http_field_t));
+
+ SET_REQ_MEMBER(path, path);
+ SET_REQ_MEMBER(method, method);
+ SET_REQ_MEMBER(version, version);
+ SET_REQ_MEMBER(query, query);
+ SET_REQ_MEMBER(remote, remote);
+ SET_REQ_MEMBER(local_addr, local_addr);
+ SET_REQ_MEMBER(local_port, local_port);
+ SET_REQ_MEMBER(server_name, server_name);
+#undef SET_REQ_MEMBER
+
+ df = wr->fields;
+ sf_end = r->fields + r->fields_count;
+ for (sf = r->fields; sf < sf_end; sf++) {
+ const char *name = nxt_unit_sptr_get(&sf->name);
+ const char *value = nxt_unit_sptr_get(&sf->value);
+
+ df->name_off = offset;
+ df->name_len = strlen(name);
+ memcpy((uint8_t *)wr + offset, name, df->name_len + 1);
+ offset += df->name_len + 1;
+
+ df->value_off = offset;
+ df->value_len = strlen(value);
+ memcpy((uint8_t *)wr + offset, value, df->value_len + 1);
+ offset += df->value_len + 1;
+
+ df++;
+ }
+
+ wr->tls = r->tls;
+ wr->nfields = r->fields_count;
+ wr->content_off = offset;
+ wr->content_len = content_len = r->content_length;
+
+ read_bytes = nxt_min(wr->content_len, NXT_WASM_MEM_SIZE - offset);
+
+ bytes_read = nxt_unit_request_read(req, (uint8_t *)wr + offset, read_bytes);
+ wr->content_sent = wr->total_content_sent = content_sent = bytes_read;
+
+ wr->request_size = offset + bytes_read;
+
+ nxt_wasm_ctx.req = req;
+ nxt_wops->exec_request(&nxt_wasm_ctx);
+
+ if (content_len == content_sent) {
+ goto request_done;
+ }
+
+ wr->nfields = 0;
+ wr->content_off = offset = sizeof(nxt_wasm_request_t);
+ do {
+ read_bytes = nxt_min(content_len - content_sent,
+ NXT_WASM_MEM_SIZE - offset);
+ bytes_read = nxt_unit_request_read(req, (uint8_t *)wr + offset,
+ read_bytes);
+
+ content_sent += bytes_read;
+ wr->request_size = wr->content_sent = bytes_read;
+ wr->total_content_sent = content_sent;
+
+ nxt_wops->exec_request(&nxt_wasm_ctx);
+ } while (content_sent < content_len);
+
+request_done:
+ NXT_WASM_DO_HOOK(NXT_WASM_FH_REQUEST_END);
+}
+
+
+static nxt_int_t
+nxt_wasm_start(nxt_task_t *task, nxt_process_data_t *data)
+{
+ nxt_int_t ret;
+ nxt_unit_ctx_t *unit_ctx;
+ nxt_unit_init_t wasm_init;
+ nxt_common_app_conf_t *conf;
+
+ conf = data->app;
+
+ ret = nxt_unit_default_init(task, &wasm_init, conf);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ nxt_alert(task, "nxt_unit_default_init() failed");
+ return ret;
+ }
+
+ wasm_init.callbacks.request_handler = nxt_wasm_request_handler;
+
+ unit_ctx = nxt_unit_init(&wasm_init);
+ if (nxt_slow_path(unit_ctx == NULL)) {
+ return NXT_ERROR;
+ }
+
+ NXT_WASM_DO_HOOK(NXT_WASM_FH_MODULE_INIT);
+ nxt_unit_run(unit_ctx);
+ nxt_unit_done(unit_ctx);
+ NXT_WASM_DO_HOOK(NXT_WASM_FH_MODULE_END);
+
+ nxt_wops->destroy(&nxt_wasm_ctx);
+
+ exit(EXIT_SUCCESS);
+}
+
+
+static nxt_int_t
+nxt_wasm_setup(nxt_task_t *task, nxt_process_t *process,
+ nxt_common_app_conf_t *conf)
+{
+ int err;
+ nxt_wasm_app_conf_t *c;
+ nxt_wasm_func_handler_t *fh;
+
+ c = &conf->u.wasm;
+
+ nxt_wops = &nxt_wasm_ops;
+
+ nxt_wasm_ctx.module_path = c->module;
+
+ fh = nxt_wasm_ctx.fh;
+
+ fh[NXT_WASM_FH_REQUEST].func_name = c->request_handler;
+ fh[NXT_WASM_FH_MALLOC].func_name = c->malloc_handler;
+ fh[NXT_WASM_FH_FREE].func_name = c->free_handler;
+
+ /* Optional function handlers (hooks) */
+ fh[NXT_WASM_FH_MODULE_INIT].func_name = c->module_init_handler;
+ fh[NXT_WASM_FH_MODULE_END].func_name = c->module_end_handler;
+ fh[NXT_WASM_FH_REQUEST_INIT].func_name = c->request_init_handler;
+ fh[NXT_WASM_FH_REQUEST_END].func_name = c->request_end_handler;
+ fh[NXT_WASM_FH_RESPONSE_END].func_name = c->response_end_handler;
+
+ err = nxt_wops->init(&nxt_wasm_ctx);
+ if (err) {
+ exit(EXIT_FAILURE);
+ }
+
+ return NXT_OK;
+}
+
+
+NXT_EXPORT nxt_app_module_t nxt_app_module = {
+ .compat_length = sizeof(compat),
+ .compat = compat,
+ .type = nxt_string("wasm"),
+ .version = NXT_WASM_VERSION,
+ .mounts = NULL,
+ .nmounts = 0,
+ .setup = nxt_wasm_setup,
+ .start = nxt_wasm_start,
+};
diff --git a/src/wasm/nxt_wasm.h b/src/wasm/nxt_wasm.h
new file mode 100644
index 00000000..9e18f931
--- /dev/null
+++ b/src/wasm/nxt_wasm.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) Andrew Clayton
+ * Copyright (C) F5, Inc.
+ */
+
+#ifndef _NXT_WASM_H_INCLUDED_
+#define _NXT_WASM_H_INCLUDED_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <nxt_unit.h>
+
+#include <wasm.h>
+#if defined(NXT_HAVE_WASM_WASMTIME)
+#include <wasmtime.h>
+#endif
+
+
+#define NXT_WASM_PAGE_SIZE (64 * 1024)
+#define NXT_WASM_MEM_SIZE (32UL * 1024 * 1024)
+
+#if defined(NXT_HAVE_WASM_WASMTIME)
+typedef wasmtime_func_t nxt_wasm_func_t;
+#endif
+
+
+typedef struct nxt_wasm_http_field_s nxt_wasm_http_field_t;
+typedef struct nxt_wasm_request_s nxt_wasm_request_t;
+typedef struct nxt_wasm_response_s nxt_wasm_response_t;
+typedef struct nxt_wasm_response_fields_s nxt_wasm_response_fields_t;
+typedef enum nxt_wasm_fh_e nxt_wasm_fh_t;
+typedef struct nxt_wasm_func_handler_s nxt_wasm_func_handler_t;
+typedef struct nxt_wasm_ctx_s nxt_wasm_ctx_t;
+typedef struct nxt_wasm_operations_s nxt_wasm_operations_t;
+
+struct nxt_wasm_http_field_s {
+ uint32_t name_off;
+ uint32_t name_len;
+ uint32_t value_off;
+ uint32_t value_len;
+};
+
+struct nxt_wasm_request_s {
+ uint32_t method_off;
+ uint32_t method_len;
+ uint32_t version_off;
+ uint32_t version_len;
+ uint32_t path_off;
+ uint32_t path_len;
+ uint32_t query_off;
+ uint32_t query_len;
+ uint32_t remote_off;
+ uint32_t remote_len;
+ uint32_t local_addr_off;
+ uint32_t local_addr_len;
+ uint32_t local_port_off;
+ uint32_t local_port_len;
+ uint32_t server_name_off;
+ uint32_t server_name_len;
+
+ uint32_t content_off;
+ uint32_t content_len;
+ uint32_t content_sent;
+ uint32_t total_content_sent;
+
+ uint32_t request_size;
+
+ uint32_t nfields;
+
+ uint32_t tls;
+
+ nxt_wasm_http_field_t fields[];
+};
+
+struct nxt_wasm_response_s {
+ uint32_t size;
+
+ uint8_t data[];
+};
+
+struct nxt_wasm_response_fields_s {
+ uint32_t nfields;
+
+ nxt_wasm_http_field_t fields[];
+};
+
+enum nxt_wasm_fh_e {
+ NXT_WASM_FH_REQUEST = 0,
+ NXT_WASM_FH_MALLOC,
+ NXT_WASM_FH_FREE,
+
+ /* Optional handlers */
+ NXT_WASM_FH_MODULE_INIT,
+ NXT_WASM_FH_MODULE_END,
+ NXT_WASM_FH_REQUEST_INIT,
+ NXT_WASM_FH_REQUEST_END,
+ NXT_WASM_FH_RESPONSE_END,
+
+ NXT_WASM_FH_NR
+};
+
+struct nxt_wasm_func_handler_s {
+ const char *func_name;
+ nxt_wasm_func_t func;
+};
+
+struct nxt_wasm_ctx_s {
+ const char *module_path;
+
+ nxt_wasm_func_handler_t fh[NXT_WASM_FH_NR];
+
+ nxt_unit_request_info_t *req;
+
+ uint8_t *baddr;
+ size_t baddr_off;
+
+ size_t response_off;
+};
+
+struct nxt_wasm_operations_s {
+ int (*init)(nxt_wasm_ctx_t *ctx);
+ void (*destroy)(const nxt_wasm_ctx_t *ctx);
+ void (*exec_request)(const nxt_wasm_ctx_t *ctx);
+ void (*exec_hook)(const nxt_wasm_ctx_t *ctx, nxt_wasm_fh_t hook);
+};
+
+extern const nxt_wasm_operations_t nxt_wasm_ops;
+
+
+/* Exported to the WASM module */
+extern void nxt_wasm_do_response_end(nxt_wasm_ctx_t *ctx);
+extern void nxt_wasm_do_send_response(nxt_wasm_ctx_t *ctx, uint32_t offset);
+extern void nxt_wasm_do_send_headers(nxt_wasm_ctx_t *ctx, uint32_t offset);
+
+#endif /* _NXT_WASM_H_INCLUDED_ */