diff options
-rw-r--r-- | src/wasm/nxt_rt_wasmtime.c | 407 | ||||
-rw-r--r-- | src/wasm/nxt_wasm.c | 258 | ||||
-rw-r--r-- | src/wasm/nxt_wasm.h | 136 |
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_ */ |