From 89b20ca4160ab671926477a81b8b2ad83163ccc8 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Tue, 20 May 2025 19:07:53 +0100 Subject: [PATCH] Convert docs to reStructuredText ... it's just better than markdown. Signed-off-by: Andrew Clayton --- API-C.md | 1174 --------------------------------------- API-C.rst | 1274 +++++++++++++++++++++++++++++++++++++++++++ API-Rust.md | 1138 -------------------------------------- API-Rust.rst | 1238 +++++++++++++++++++++++++++++++++++++++++ CODE_OF_CONDUCT.md | 74 --- CODE_OF_CONDUCT.rst | 88 +++ CONTRIBUTING.md | 83 --- CONTRIBUTING.rst | 88 +++ HOWTO.md | 210 ------- HOWTO.rst | 223 ++++++++ README.md | 490 ----------------- README.rst | 513 +++++++++++++++++ 12 files changed, 3424 insertions(+), 3169 deletions(-) delete mode 100644 API-C.md create mode 100644 API-C.rst delete mode 100644 API-Rust.md create mode 100644 API-Rust.rst delete mode 100644 CODE_OF_CONDUCT.md create mode 100644 CODE_OF_CONDUCT.rst delete mode 100644 CONTRIBUTING.md create mode 100644 CONTRIBUTING.rst delete mode 100644 HOWTO.md create mode 100644 HOWTO.rst delete mode 100644 README.md create mode 100644 README.rst diff --git a/API-C.md b/API-C.md deleted file mode 100644 index 298867d..0000000 --- a/API-C.md +++ /dev/null @@ -1,1174 +0,0 @@ -# libunit-wasm C API - -C Library for creating WebAssembly modules for use with NGINX Unit. - -```C -#include -``` - -1. [libunit-wasm C API](#libunit-wasm-c-api) -2. [Macros](#macros) - * [Version](#version) - * [Misc](#misc) -3. [Types](#types) -4. [Enums](#enums) - * [luw_srb_flags_t](#luw_srb_flags_t) - * [luw_http_status_t](#luw_http_status_t) -5. [Structs](#structs) -6. [Function Handlers](#function-handlers) - * [Optional](#optional) - - [luw_module_init_handler](#luw_module_init_handler) - - [luw_module_end_handler](#luw_module_end_handler) - - [luw_request_init_handler](#luw_request_init_handler) - - [luw_request_end_handler](#luw_request_end_handler) - - [luw_response_end_handler](#luw_response_end_handler) - * [Required](#required) - - [luw_request_handler](#luw_request_handler) - - [luw_free_handler](#luw_free_handler) - - [luw_malloc_handler](#luw_malloc_handler) -7. [Functions](#functions) - * [luw_init_ctx](#luw_init_ctx) - * [luw_set_req_buf](#luw_set_req_buf) - * [luw_get_http_path](#luw_get_http_path) - * [luw_get_http_method](#luw_get_http_method) - * [luw_get_http_version](#luw_get_http_version) - * [luw_get_http_query](#luw_get_http_query) - * [luw_get_http_remote](#luw_get_http_remote) - * [luw_get_http_local_addr](#luw_get_http_local_addr) - * [luw_get_http_local_port](#luw_get_http_local_port) - * [luw_get_http_server_name](#luw_get_http_server_name) - * [luw_get_http_content](#luw_get_http_content) - * [luw_get_http_content_len](#luw_get_http_content_len) - * [luw_get_http_content_sent](#luw_get_http_content_sent) - * [luw_get_http_total_content_sent](#luw_get_http_total_content_sent) - * [luw_http_is_tls](#luw_http_is_tls) - * [luw_http_hdr_iter](#luw_http_hdr_iter) - * [luw_http_hdr_get_value](#luw_http_hdr_get_value) - * [luw_get_response_data_size](#luw_get_response_data_size) - * [luw_mem_writep](#luw_mem_writep) - * [luw_mem_writep_data](#luw_mem_writep_data) - * [luw_req_buf_append](#luw_req_buf_append) - * [luw_req_buf_copy](#luw_req_buf_copy) - * [luw_mem_splice_file](#luw_mem_splice_file) - * [luw_mem_fill_buf_from_req](#luw_mem_fill_buf_from_req) - * [luw_mem_reset](#luw_mem_reset) - * [luw_http_set_response_status](#luw_http_set_response_status) - * [luw_http_send_response](#luw_http_send_response) - * [luw_http_init_headers](#luw_http_init_headers) - * [luw_http_add_header](#luw_http_add_header) - * [luw_http_send_headers](#luw_http_send_headers) - * [luw_http_response_end](#luw_http_response_end) - * [luw_mem_get_init_size](#luw_mem_get_init_size) - * [luw_foreach_http_hdr](#luw_foreach_http_hdr) -8. [Misc. Functions](#misc-functions) - * [luw_malloc](#luw_malloc) - * [luw_free](#luw_free) - -## Macros - -### Version - -```C -#define LUW_VERSION_MAJOR M -#define LUW_VERSION_MINOR m -#define LUW_VERSION_PATCH p -``` - -```C -/* Version number in hex 0xMMmmpp00 */ -#define LUW_VERSION_NUMBER \ - ( (LUW_VERSION_MAJOR << 24) | \ - (LUW_VERSION_MINOR << 16) | \ - (LUW_VERSION_PATCH << 8) ) -``` - -### Misc - -```C -#define __luw_export_name(name) __attribute__((export_name(name))) -``` - -```C -#define __luw_unused __attribute__((unused)) -#define __luw_maybe_unused __luw_unused -``` - -```C -#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) -``` - -## Types - -```C -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; -``` - -## Enums - -### luw_srb_flags_t - -```C -typedef enum { - LUW_SRB_NONE = 0x00, - LUW_SRB_APPEND = 0x01, - LUW_SRB_ALLOC = 0x02, - LUW_SRB_FULL_SIZE = 0x04, - - LUW_SRB_FLAGS_ALL = (LUW_SRB_NONE|LUW_SRB_APPEND|LUW_SRB_ALLOC| - LUW_SRB_FULL_SIZE) -} luw_srb_flags_t; -``` - -### luw_http_status_t - -```C -typedef enum { - LUW_HTTP_CONTINUE = 100, - LUW_HTTP_SWITCHING_PROTOCOLS = 101, - - LUW_HTTP_OK = 200, - LUW_HTTP_CREATED = 201, - LUW_HTTP_ACCEPTED = 202, - LUW_HTTP_NO_CONTENT = 204, - - LUW_HTTP_MULTIPLE_CHOICES = 300, - LUW_HTTP_MOVED_PERMANENTLY = 301, - LUW_HTTP_FOUND = 302, - LUW_HTTP_SEE_OTHER = 303, - LUW_HTTP_NOT_MODIFIED = 304, - LUW_HTTP_TEMPORARY_REDIRECT = 307, - LUW_HTTP_PERMANENT_REDIRECT = 308, - - LUW_HTTP_BAD_REQUEST = 400, - LUW_HTTP_UNAUTHORIZED = 401, - LUW_HTTP_FORBIDDEN = 403, - LUW_HTTP_NOT_FOUND = 404, - LUW_HTTP_METHOD_NOT_ALLOWED = 405, - LUW_HTTP_NOT_ACCEPTABLE = 406, - LUW_HTTP_REQUEST_TIMEOUT = 408, - LUW_HTTP_CONFLICT = 409, - LUW_HTTP_GONE = 410, - LUW_HTTP_LENGTH_REQUIRED = 411, - LUW_HTTP_PAYLOAD_TOO_LARGE = 413, - LUW_HTTP_URI_TOO_LONG = 414, - LUW_HTTP_UNSUPPORTED_MEDIA_TYPE = 415, - LUW_HTTP_UPGRADE_REQUIRED = 426, - LUW_HTTP_TOO_MANY_REQUESTS = 429, - LUW_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431, - - /* Proposed by RFC 7725 */ - LUW_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451, - - LUW_HTTP_INTERNAL_SERVER_ERROR = 500, - LUW_HTTP_NOT_IMPLEMENTED = 501, - LUW_HTTP_BAD_GATEWAY = 502, - LUW_HTTP_SERVICE_UNAVAILABLE = 503, - LUW_HTTP_GATEWAY_TIMEOUT = 504, -} luw_http_status_t; -``` - -## Structs - -```C -struct luw_hdr_field { - u32 name_off; - u32 name_len; - u32 value_off; - u32 value_len; -}; -``` - -```C -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; - - u64 content_len; - u64 total_content_sent; - u32 content_sent; - u32 content_off; - - u32 request_size; - - u32 nr_fields; - - u32 tls; - - char __pad[4]; - - struct luw_hdr_field fields[]; -}; -``` - -```C -struct luw_resp { - u32 size; - - u8 data[]; -}; -``` - -```C -struct luw_resp_hdr { - u32 nr_fields; - - struct luw_hdr_field fields[]; -}; -``` - -```C -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 representation 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; - - /* tracks the response header index number */ - s32 resp_hdr_idx; -} luw_ctx_t; -``` - -```C -typedef struct luw_hdr_field luw_http_hdr_iter_t; -``` - -## Function Handlers - -These functions are exported from the WebAssembly module and are called from -the WebAssembly runtime (the Unit WebAssembly language module in this case). - -There are two types of handlers; required & optional. - -luw_request_handler(), luw_malloc_handler() & luw_free_handler() are required -with the rest being optional. - -libunit-wasm includes exports for these handlers and some default -implementations. - -These functions are defined as _weak_ symbols and so if a developer writes -their own function of the same name, that will take precedence. - -However, developers are under no obligation to use these and can create their -own with any (valid) names they like. - -Whatever names developers choose, they are specified in the Unit config. - -## Required - -#### luw_request_handler - -```C -__attribute__((export_name("luw_request_handler"), __weak__)) -int luw_request_handler(u8 *addr); -``` - -This is called by Unit during a request. It may be called multiple times for -a single HTTP request if there is more request data than the available memory -for host <--> module communications. - -You will need to provide your own implementation of this function. - -It receives the base address of the shared memory. Essentially what is -returned by luw_malloc_handler(). - -This memory will contain a *struct luw_req*. - -It returns an int. This should nearly always be _0_. - -If you wish to indicate a '500 Internal Server Error', for example if some -internal API has failed or an OS level error occurred, then you can simply -return _-1_, _if_ you have haven't already _sent_ any response or headers. - -You can still return 0 _and_ set the HTTP response status to 500 using -[luw_http_set_response_status](#luw_http_set_response_status). - -#### luw_malloc_handler - -```C -__attribute__((export_name("luw_malloc_handler"), __weak__)) -u32 luw_malloc_handler(size_t size); -``` - -This is called by Unit when it loads the WebAssembly language module. This -provides the shared memory used for host <--> module communications. - -It receives the desired size of the memory, which is currently -NXT_WASM_MEM_SIZE + NXT_WASM_PAGE_SIZE. - -However calls to luw_mem_get_init_size() will return just NXT_WASM_MEM_SIZE -(which is currently 32MiB). The extra NXT_WASM_PAGE_SIZE is to cater for -structure sizes in the response so developers can generally assume they have -the full NXT_WASM_MEM_SIZE for their data. - -A default implementation of this function is provided ready for use that -calls malloc(3). - -#### luw_free_handler - -```C -__attribute__((export_name("luw_free_handler"), __weak__)) -void luw_free_handler(u32 addr); -``` - -This is called by Unit when it shuts down the WebAssembly language module and -free's the memory previously allocated by luw_malloc_handler(). - -It receives the address of the memory to free. - -An implementation of this function is provided ready for use that calls -free(3), in which case it receives the address that was previously returned -by luw_malloc_handler(). - -### Optional - -#### luw_module_init_handler - -```C -__attribute__((export_name("luw_module_init_handler"), __weak__)) -void luw_module_init_handler(void); -``` - -This is called by Unit when it loads the WebAssembly language module. - -A default dummy function is provided. If this handler is not required, there -is no need to specify it in the Unit config. - -#### luw_module_end_handler - -```C -__attribute__((export_name("luw_module_end_handler"), __weak__)) -void luw_module_end_handler(void); -``` - -This is called by Unit when it shuts down the WebAssembly language module. - -A default dummy function is provided. If this handler is not required, there -is no need to specify it in the Unit config. - -#### luw_request_init_handler - -```C -__attribute__((export_name("luw_request_init_handler"), __weak__)) -void luw_request_init_handler(void); -``` - -This is called by Unit at the start of nxt_wasm_request_handler(), i.e at the -start of a new request. - -A default dummy function is provided. If this handler is not required, there -is no need to specify it in the Unit config. - -#### luw_request_end_handler - -```C -__attribute__((export_name("luw_request_end_handler"), __weak__)) -void luw_request_end_handler(void); -``` - -This is called by Unit at the end of nxt_wasm_request_handler(), i.e at the -end of a request. - -A default dummy function is provided. If this handler is not required, there -is no need to specify it in the Unit config. - -#### luw_response_end_handler - -```C -__attribute__((export_name("luw_response_end_handler"), __weak__)) -void luw_response_end_handler(void); -``` - -This is called by Unit after luw_http_response_end() has been called. - -A default dummy function is provided. If this handler is not required, there -is no need to specify it in the Unit config. - -## Functions - -### luw_init_ctx - -```C -void luw_init_ctx(luw_ctx_t *ctx, u8 *addr, size_t offset); -``` - -This function sets up a *luw_ctx_t* context structure, this contains stuff -required all throughout the API. It's a typedef for opaqueness and you should -not in general be concerned with its contents. - -It take a pointer to a stack allocated luw_ctx_t, this will be zeroed and -have various members initialised. - -**addr** is a pointer to the shared memory as passed into luw_request_handler(). - -**offset** is where in the shared memory it should start writing the response. - -#### A quick word about memory - -The way the Unit WebAssembly language module (the host/runtime) and the -WebAssembly module you want to write (the guest) communicate is via a chunk -of shared memory. - -This shared memory is simply the modules (guest) address space from which we -can allocate a chunk. How this memory is laid out varies on how the module -is built. - -With clang/linker flags of -Wl,--stack-first -Wl,-z,stack-size=$((8*1024*1024)) -we get a memory layout something like - -``` - |----------------------------------------------------------------------| - | | | | - | <-- Stack | Global Data | Heap --> | - | | | | - |----------------------------------------------------------------------| - 0 0x800000 0x100000000 - - WebAssembly Module Linear Memory / Process Memory Layout -``` - -(The above is assuming _--target=wasm32-wasi_, i.e 32bit) - -A chunk of memory from the heap is allocated at Unit WebAssembly language -module startup. - -We currently use this same chunk of memory for both requests and responses. -This means that depending on what you're doing, you'll want to take a copy -of the request (and remember luw_request_handler() may be called multiple -times for a single http request). - -That will be covered in more detail by the next function, luw_set_req_buf(). - -Now back to _offset_, it may be convenient to put the response headers at the -beginning of this memory and then put the response after it, rather than -doing the headers and then doing the response as separate steps, if the -headers depends on some aspect of the response, its size for example and -Content-Length. - -Example - -```C -luw_ctx_t ctx; -/* ... */ -luw_init_ctx(&ctx, addr, 4096 /* Response offset */); -``` - -### luw_set_req_buf - -```C -int luw_set_req_buf(luw_ctx_t *ctx, u8 **buf, unsigned int flags); -``` - -This function is used to take a copy of the request buffer (as discussed -above). - -This takes a previously initialised (with luw_init_ctx()) luw_ctx_t. - -**buf** is a buffer where the request data will written. - -**flags** can be some combination (OR'd) of the following - -**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. - -Example - -```C -static u8 *request_buf; -*/ ... */ -int luw_request_handler(u8 *addr) -{ - if (!request_buf) { - luw_init_ctx(&ctx, addr, 0); - /* - * 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); - } - - /* operate on the request (ctx) */ - - return 0; -} -``` - -That example is taken from the [luw-upload-reflector.c](https://github.com/nginx/unit-wasm/blob/main/examples/c/luw-upload-reflector.c) demo module. For a -simpler example see [luw-echo-request.c](https://github.com/nginx/unit-wasm/blob/main/examples/c/luw-echo-request.c) - -### luw_get_http_path - -```C -const char *luw_get_http_path(const luw_ctx_t *ctx); -``` - -This function returns a pointer to the HTTP request path. - -E.g - -Given a request of -``` -http://localhost:8080/echo/?q=a -``` -this function will return -``` -/echo/?q=a -``` - -### luw_get_http_method - -```C -const char *luw_get_http_method(const luw_ctx_t *ctx); -``` - -This function returns a pointer to the HTTP method. - -E.g - -``` -GET -``` - -### luw_get_http_version - -```C -const char *luw_get_http_version(const luw_ctx_t *ctx); -``` - -This function returns a pointer to the HTTP version. - -E.g - -``` -1.1 -``` - -### luw_get_http_query - -```C -const char *luw_get_http_query(const luw_ctx_t *ctx); -``` - -This function returns a pointer to the query string (empty string for no query -string). - -E.g - -Given a request of -``` -http://localhost:8080/echo/?q=a -``` -this function will return -``` -q=a -``` - -### luw_get_http_remote - -```C -const char *luw_get_http_remote(const luw_ctx_t *ctx); -``` - -This function returns a pointer to the remote/client/peer address. - -E.g - -``` -2001:db8::f00 -``` - -### luw_get_http_local_addr - -```C -const char *luw_get_http_local_addr(const luw_ctx_t *ctx); -``` - -This function returns a pointer to the local/server address. - -E.g - -``` -2001:db8::1 -``` - -### luw_get_http_local_port - -```C -const char *luw_get_http_local_port(const luw_ctx_t *ctx); -``` - -This function returns a pointer to the local/server port. - -E.g - -``` -443 -``` - -### luw_get_http_server_name - -```C -const char *luw_get_http_server_name(const luw_ctx_t *ctx); -``` - -This function returns a pointer to the local/server name. - -E.g - -``` -www.example.com -``` - -### luw_get_http_content - -```C -const u8 *luw_get_http_content(const luw_ctx_t *ctx); -``` - -This function returns a pointer to the start of the request body. - -### luw_get_http_content_len - -```C -u64 luw_get_http_content_len(const luw_ctx_t *ctx); -``` - -This function returns the size of the overall content. I.e Content-Length. - -Prior to version 0.3.0 it returned a size_t - -### luw_get_http_content_sent - -```C -size_t luw_get_http_content_sent(const luw_ctx_t *ctx); -``` - -This function returns the length of the content that was sent to the -WebAssembly module in _this_ request. Remember, a single HTTP request may be -split over several calls to luw_request_handler(). - -### luw_get_http_total_content_sent - -```C -u64 luw_get_http_total_content_sent(const luw_ctx_t *ctx); -``` - -This function returns the total length of the content that was sent to the -WebAssembly module so far. Remember, a single HTTP request may be split over -several calls to luw_request_handler(). - -_Version: 0.2.0_ Prior to 0.3.0 it returned a size_t - -### luw_http_is_tls - -```C -bool luw_http_is_tls(const luw_ctx_t *ctx); -``` - -This function returns _true_ if the connection to Unit was made over TLS. - -### luw_http_hdr_iter - -```C -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) -``` - -This function allows to iterate over the HTTP headers. For each header it -will call the given luw_http_hdr_iter_func() function whose prototype is - -```C -bool luw_http_hdr_iter_func(luw_ctx_t *ctx, - const char *name, const char *value, void *data); -``` - -You may call this function whatever you like. For each header it will be -passed the *luw_ctx_t*, the header name, its value and a user specified -pointer if any, can be NULL. - -Returning _true_ from this function will cause the iteration process to -continue, returning _false_ will terminate it. - -Example - -```C -static bool hdr_iter_func(luw_ctx_t *ctx, const char *name, const char *value, - void *user_data __luw_unused) -{ - /* Do something with name & value */ - - /* Continue iteration or return false to stop */ - return true; -} - -/* ... * - -luw_http_hdr_iter(&ctx, hdr_iter_func, NULL); -``` - -### luw_http_hdr_get_value - -```C -const char *luw_http_hdr_get_value(const luw_ctx_t *ctx, const char *hdr); -``` - -Given a HTTP header _hdr_ this function will look it up in the request and -return its value if found, otherwise _NULL_. - -The lookup is done case insensitively. - -### luw_get_response_data_size - -```C -size_t luw_get_response_data_size(const luw_ctx_t *ctx); -``` - -This function returns the size of the response data written to memory. - -### luw_mem_writep - -```C -__attribute__((__format__(printf, 2, 3))) -int luw_mem_writep(luw_ctx_t *ctx, const char *fmt, ...); -``` - -This function is a cross between vasprintf(3) and mempcpy(3). - -It takes a format argument and zero or more arguments that will be -substituted into the format string. - -It then appends this formatted string to the memory. Note this string will -_not_ be nul terminated. Unit does not expect this response data to be nul -terminated and we track the size of the response and return that to Unit. - -This function returns -1 on error or the length of the string written. - -### luw_mem_writep_data - -```C -size_t luw_mem_writep_data(luw_ctx_t *ctx, const u8 *src, size_t size); -``` - -This function just appends _size_ bytes from _src_ to the response. - -It returns the new size of the response. - -### luw_req_buf_append - -```C -void luw_req_buf_append(luw_ctx_t *ctx, const u8 *src); -``` - -This function appends the request data contained in _src_ to the previously -setup *request_buffer* with luw_set_req_buf(). - -This function would be used after an initial request to append the data from -subsequent requests to the request_buffer. - -Example - -```C -int luw_request_handler(u8 *addr) -{ - if (!request_buf) { - luw_init_ctx(&ctx, addr, 0); - /* - * 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); - } - - /* Do something with the request (ctx) */ - - return 0; -} -``` - -### luw_req_buf_copy - -```C -void luw_req_buf_copy(luw_ctx_t *ctx, const u8 *src); -``` - -This function is analogous to [luw_req_buf_append](#luw_req_buf_append) but -rather than appending the request data contained in _src_ to the previously -setup *request_buffer* with luw_set_req_buf(), it simply overwrites what's -currently there. - -This function could be used to handle large requests/uploads that you want to -save out to disk or some such and can't buffer it all in memory. - -Example - -```C -int luw_request_handler(u8 *addr) -{ - const u8 *buf; - ssize_t bytes_wrote; - - if (total_bytes_wrote == 0) { - luw_init_ctx(&ctx, addr, 0); - luw_set_req_buf(&ctx, &request_buf, LUW_SRB_NONE); - - fd = open("/var/tmp/large-file.dat", O_CREAT|O_TRUNC|O_WRONLY, - 0666); - } else { - luw_req_buf_copy(&ctx, addr); - } - - buf = luw_get_http_content(&ctx); - bytes_wrote = write(fd, buf, luw_get_http_content_sent(&ctx)); - if (bytes_wrote == -1) - return -1; - - total_bytes_wrote += bytes_wrote; - if (total_bytes_wrote == luw_get_http_content_len(&ctx)) - luw_http_response_end(); - - return 0; -} -``` - -_Version: 0.3.0_ - -### luw_mem_splice_file - -```C -ssize_t luw_mem_splice_file(const u8 *src, int fd); -``` - -This function write(2)'s the request data directly from the shared memory -(_src_) to the file represented by the given file-descriptor (_fd_). - -This can be used as an alternative to [luw_req_buf_copy](#luw_req_buf_copy) -and avoids an extra copying of the request data. - -Example - -```C -int luw_request_handler(u8 *addr) { - ssize_t bytes_wrote; - - if (total_bytes_wrote == 0) { - luw_init_ctx(&ctx, addr, 0); - luw_set_req_buf(&ctx, &request_buf, LUW_SRB_NONE); - - fd = open("/var/tmp/large-file.dat", O_CREAT|O_TRUNC|O_WRONLY, - 0666); - } - - bytes_wrote = luw_mem_splice_file(addr, fd); - if (bytes_wrote == -1) - return -1; - - total_bytes_wrote += bytes_wrote; - if (total_bytes_wrote == luw_get_http_content_len(&ctx)) - luw_http_response_end(); - - return 0; -} -``` - -_Version: 0.3.0_ - -### luw_mem_fill_buf_from_req - -```C -size_t luw_mem_fill_buf_from_req(luw_ctx_t *ctx, size_t from); -``` - -This is a convenience function to fill the response buffer with data from -the request buffer. - -_from_ is basically the offset in the request_buffer where to start copying -data from. - -Example - -```C -/* ... */ -write_bytes = luw_mem_fill_buf_from_req(ctx, total_response_sent); -total_response_sent += write_bytes; -/* ... */ -``` - -This is taken from the [luw-upload-reflector.c](https://github.com/nginx/unit-wasm/blob/main/examples/c/luw-upload-reflector.c) demo module. - -In this case we build up a request_buffer on each call of -luw_request_handler(), so total_response_sent grows each time by how much data -was sent in _that_ request. - -Here are are sending data back to the client after each time we receive it to -demonstrate the interleaving of requests and responses from the WebAssembly -module during a single http request. - -This function returns the number of bytes written to the response buffer. - -### luw_mem_reset - -```C -void luw_mem_reset(luw_ctx_t *ctx); -``` - -This function resets the response buffer size and the number of response -headers back to 0. - -### luw_http_set_response_status - -```C -void luw_http_set_response_status(luw_http_status_t status); -``` - -This function is used to set the HTTP response status. It takes one of the -[luw_http_status_t](#luw_http_status_t) enum values. - -It should be called before any calls to *luw_http_send_response()* or -*luw_http_send_headers()*. - -If you don't call this function the response status defaults to '200 OK'. - -If you wish to error out with a '500 Internal Server Error', you don't need to -call this function. Simply returning _-1_ from the request_handler function -will indicate this error. - -E.g - -Send a '403 Forbidden' - -```C -/* ... */ -luw_http_set_response_status(LUW_HTTP_FORBIDDEN); -luw_http_send_response(ctx); /* Doesn't require any body */ -luw_http_response_end(); -/* ... */ -return 0; -``` - -Send a '307 Temporary Re-direct' - -```C -/* ... */ -luw_http_set_response_status(LUW_HTTP_TEMPORARY_REDIRECT); - -luw_http_init_headers(ctx, 1, 0); -luw_http_add_header(ctx, "Location", "https://example.com/"); -luw_http_send_headers(ctx); -luw_http_response_end(); -/* ... */ -return 0; -``` - -_Version: 0.3.0_ - -### luw_http_send_response - -```C -void luw_http_send_response(const luw_ctx_t *ctx); -``` - -This function calls into Unit to send the response buffer back. - -### luw_http_init_headers - -```C -void luw_http_init_headers(luw_ctx_t *ctx, size_t nr, size_t offset); -``` - -This function is used in the preparation of sending back response headers. - -_nr_ is the number of headers we are sending. - -_offset_ is the offset into the response buffer where we are placing these -headers. This will usually be 0. - -Example - -```C -luw_http_init_headers(ctx, 2, 0); -``` - -### luw_http_add_header - -```C -void luw_http_add_header(luw_ctx_t *ctx, const char *name, const char *value); -``` - -This function is used to add a header to the response. - -_name_ is the name of the header. - -_value_ is the value of the header. - -Example - -```C -char clen[32]; -/* ... */ -snprintf(clen, sizeof(clen), "%lu", luw_get_response_data_size(&ctx)); -luw_http_add_header(&ctx, "Content-Type", "text/plain"); -luw_http_add_header(&ctx, "Content-Length", clen); -``` - -### luw_http_send_headers - -```C -void luw_http_send_headers(const luw_ctx_t *ctx); -``` - -This function calls into Unit and triggers the sending of the response -headers. - -### luw_http_response_end - -```C -void luw_http_response_end(void); -``` - -This function calls into Unit and tells it this is the end of the response -which will trigger Unit to send it to the client. - -### luw_mem_get_init_size - -```C -u32 luw_mem_get_init_size(void); -``` - -This function calls into Unit to get the size of the shared memory. This is -the amount of memory you should assume you have for creating responses. -Remember you can create multiple responses before calling -luw_http_response_end(). - -### luw_foreach_http_hdr - -```C -void luw_foreach_http_hdr(luw_ctx_t ctx, luw_http_hdr_iter_t *iter, - const char *name, const char *value) -``` - -Defined as a macro, this is used to iterate over the HTTP header fields. - -It takes a _luw_ctx_t *_ and a _luw_http_hdr_iter_t *_ and returns pointers -to the field name and value. - -Example - -```C -luw_ctx_t ctx; -luw_http_hdr_iter_t *iter; -const char *name; -const char *value; -/* ... */ -luw_foreach_http_hdr(ctx, iter, name, value) { - printf("Field name : %s, field value : %s\n", name, value); - /* do something else with name & value */ -} -``` - -## Misc. Functions - -The following functions are convenience wrappers for the Rust bindings and -should **not** be used directly. - -### luw_malloc - -```C -void *luw_malloc(size_t size); -``` - -Straight wrapper for malloc(3). - -### luw_free - -```C -void luw_free(void *ptr); -``` - -Straight wrapper for free(3). diff --git a/API-C.rst b/API-C.rst new file mode 100644 index 0000000..719fd6f --- /dev/null +++ b/API-C.rst @@ -0,0 +1,1274 @@ +libunit-wasm C API +================== + +C Library for creating WebAssembly modules for use with NGINX Unit. + +.. code:: c + + #include + +1. `libunit-wasm C API <#libunit-wasm-c-api>`__ +2. `Macros <#macros>`__ + +- `Version <#version>`__ +- `Misc <#misc>`__ + +3. `Types <#types>`__ +4. `Enums <#enums>`__ + +- `luw_srb_flags_t <#luw_srb_flags_t>`__ +- `luw_http_status_t <#luw_http_status_t>`__ + +5. `Structs <#structs>`__ +6. `Function Handlers <#function-handlers>`__ + +- `Optional <#optional>`__ + + - `luw_module_init_handler <#luw_module_init_handler>`__ + - `luw_module_end_handler <#luw_module_end_handler>`__ + - `luw_request_init_handler <#luw_request_init_handler>`__ + - `luw_request_end_handler <#luw_request_end_handler>`__ + - `luw_response_end_handler <#luw_response_end_handler>`__ + +- `Required <#required>`__ + + - `luw_request_handler <#luw_request_handler>`__ + - `luw_free_handler <#luw_free_handler>`__ + - `luw_malloc_handler <#luw_malloc_handler>`__ + +7. `Functions <#functions>`__ + +- `luw_init_ctx <#luw_init_ctx>`__ +- `luw_set_req_buf <#luw_set_req_buf>`__ +- `luw_get_http_path <#luw_get_http_path>`__ +- `luw_get_http_method <#luw_get_http_method>`__ +- `luw_get_http_version <#luw_get_http_version>`__ +- `luw_get_http_query <#luw_get_http_query>`__ +- `luw_get_http_remote <#luw_get_http_remote>`__ +- `luw_get_http_local_addr <#luw_get_http_local_addr>`__ +- `luw_get_http_local_port <#luw_get_http_local_port>`__ +- `luw_get_http_server_name <#luw_get_http_server_name>`__ +- `luw_get_http_content <#luw_get_http_content>`__ +- `luw_get_http_content_len <#luw_get_http_content_len>`__ +- `luw_get_http_content_sent <#luw_get_http_content_sent>`__ +- `luw_get_http_total_content_sent <#luw_get_http_total_content_sent>`__ +- `luw_http_is_tls <#luw_http_is_tls>`__ +- `luw_http_hdr_iter <#luw_http_hdr_iter>`__ +- `luw_http_hdr_get_value <#luw_http_hdr_get_value>`__ +- `luw_get_response_data_size <#luw_get_response_data_size>`__ +- `luw_mem_writep <#luw_mem_writep>`__ +- `luw_mem_writep_data <#luw_mem_writep_data>`__ +- `luw_req_buf_append <#luw_req_buf_append>`__ +- `luw_req_buf_copy <#luw_req_buf_copy>`__ +- `luw_mem_splice_file <#luw_mem_splice_file>`__ +- `luw_mem_fill_buf_from_req <#luw_mem_fill_buf_from_req>`__ +- `luw_mem_reset <#luw_mem_reset>`__ +- `luw_http_set_response_status <#luw_http_set_response_status>`__ +- `luw_http_send_response <#luw_http_send_response>`__ +- `luw_http_init_headers <#luw_http_init_headers>`__ +- `luw_http_add_header <#luw_http_add_header>`__ +- `luw_http_send_headers <#luw_http_send_headers>`__ +- `luw_http_response_end <#luw_http_response_end>`__ +- `luw_mem_get_init_size <#luw_mem_get_init_size>`__ +- `luw_foreach_http_hdr <#luw_foreach_http_hdr>`__ + +8. `Misc. Functions <#misc-functions>`__ + +- `luw_malloc <#luw_malloc>`__ +- `luw_free <#luw_free>`__ + +Macros +------ + +Version +~~~~~~~ + +.. code:: c + + #define LUW_VERSION_MAJOR M + #define LUW_VERSION_MINOR m + #define LUW_VERSION_PATCH p + +.. code:: c + + /* Version number in hex 0xMMmmpp00 */ + #define LUW_VERSION_NUMBER \ + ( (LUW_VERSION_MAJOR << 24) | \ + (LUW_VERSION_MINOR << 16) | \ + (LUW_VERSION_PATCH << 8) ) + +Misc +~~~~ + +.. code:: c + + #define __luw_export_name(name) __attribute__((export_name(name))) + +.. code:: c + + #define __luw_unused __attribute__((unused)) + #define __luw_maybe_unused __luw_unused + +.. code:: c + + #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) + +Types +----- + +.. code:: c + + 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; + +Enums +----- + +luw_srb_flags_t +~~~~~~~~~~~~~~~ + +.. code:: c + + typedef enum { + LUW_SRB_NONE = 0x00, + LUW_SRB_APPEND = 0x01, + LUW_SRB_ALLOC = 0x02, + LUW_SRB_FULL_SIZE = 0x04, + + LUW_SRB_FLAGS_ALL = (LUW_SRB_NONE|LUW_SRB_APPEND|LUW_SRB_ALLOC| + LUW_SRB_FULL_SIZE) + } luw_srb_flags_t; + +luw_http_status_t +~~~~~~~~~~~~~~~~~ + +.. code:: c + + typedef enum { + LUW_HTTP_CONTINUE = 100, + LUW_HTTP_SWITCHING_PROTOCOLS = 101, + + LUW_HTTP_OK = 200, + LUW_HTTP_CREATED = 201, + LUW_HTTP_ACCEPTED = 202, + LUW_HTTP_NO_CONTENT = 204, + + LUW_HTTP_MULTIPLE_CHOICES = 300, + LUW_HTTP_MOVED_PERMANENTLY = 301, + LUW_HTTP_FOUND = 302, + LUW_HTTP_SEE_OTHER = 303, + LUW_HTTP_NOT_MODIFIED = 304, + LUW_HTTP_TEMPORARY_REDIRECT = 307, + LUW_HTTP_PERMANENT_REDIRECT = 308, + + LUW_HTTP_BAD_REQUEST = 400, + LUW_HTTP_UNAUTHORIZED = 401, + LUW_HTTP_FORBIDDEN = 403, + LUW_HTTP_NOT_FOUND = 404, + LUW_HTTP_METHOD_NOT_ALLOWED = 405, + LUW_HTTP_NOT_ACCEPTABLE = 406, + LUW_HTTP_REQUEST_TIMEOUT = 408, + LUW_HTTP_CONFLICT = 409, + LUW_HTTP_GONE = 410, + LUW_HTTP_LENGTH_REQUIRED = 411, + LUW_HTTP_PAYLOAD_TOO_LARGE = 413, + LUW_HTTP_URI_TOO_LONG = 414, + LUW_HTTP_UNSUPPORTED_MEDIA_TYPE = 415, + LUW_HTTP_UPGRADE_REQUIRED = 426, + LUW_HTTP_TOO_MANY_REQUESTS = 429, + LUW_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431, + + /* Proposed by RFC 7725 */ + LUW_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451, + + LUW_HTTP_INTERNAL_SERVER_ERROR = 500, + LUW_HTTP_NOT_IMPLEMENTED = 501, + LUW_HTTP_BAD_GATEWAY = 502, + LUW_HTTP_SERVICE_UNAVAILABLE = 503, + LUW_HTTP_GATEWAY_TIMEOUT = 504, + } luw_http_status_t; + +Structs +------- + +.. code:: c + + struct luw_hdr_field { + u32 name_off; + u32 name_len; + u32 value_off; + u32 value_len; + }; + +.. code:: c + + 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; + + u64 content_len; + u64 total_content_sent; + u32 content_sent; + u32 content_off; + + u32 request_size; + + u32 nr_fields; + + u32 tls; + + char __pad[4]; + + struct luw_hdr_field fields[]; + }; + +.. code:: c + + struct luw_resp { + u32 size; + + u8 data[]; + }; + +.. code:: c + + struct luw_resp_hdr { + u32 nr_fields; + + struct luw_hdr_field fields[]; + }; + +.. code:: c + + 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 representation 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; + + /* tracks the response header index number */ + s32 resp_hdr_idx; + } luw_ctx_t; + +.. code:: c + + typedef struct luw_hdr_field luw_http_hdr_iter_t; + +Function Handlers +----------------- + +These functions are exported from the WebAssembly module and are called +from the WebAssembly runtime (the Unit WebAssembly language module in +this case). + +There are two types of handlers; required & optional. + +luw_request_handler(), luw_malloc_handler() & luw_free_handler() are +required with the rest being optional. + +libunit-wasm includes exports for these handlers and some default +implementations. + +These functions are defined as *weak* symbols and so if a developer +writes their own function of the same name, that will take precedence. + +However, developers are under no obligation to use these and can create +their own with any (valid) names they like. + +Whatever names developers choose, they are specified in the Unit config. + +Required +~~~~~~~~ + +luw_request_handler +^^^^^^^^^^^^^^^^^^^ + +.. code:: c + + __attribute__((export_name("luw_request_handler"), __weak__)) + int luw_request_handler(u8 *addr); + +This is called by Unit during a request. It may be called multiple times +for a single HTTP request if there is more request data than the +available memory for host <–> module communications. + +You will need to provide your own implementation of this function. + +It receives the base address of the shared memory. Essentially what is +returned by luw_malloc_handler(). + +This memory will contain a *struct luw_req*. + +It returns an int. This should nearly always be *0*. + +If you wish to indicate a ‘500 Internal Server Error’, for example if +some internal API has failed or an OS level error occurred, then you can +simply return *-1*, *if* you have haven’t already *sent* any response or +headers. + +You can still return 0 *and* set the HTTP response status to 500 using +`luw_http_set_response_status <#luw_http_set_response_status>`__. + +luw_malloc_handler +^^^^^^^^^^^^^^^^^^ + +.. code:: c + + __attribute__((export_name("luw_malloc_handler"), __weak__)) + u32 luw_malloc_handler(size_t size); + +This is called by Unit when it loads the WebAssembly language module. +This provides the shared memory used for host <–> module communications. + +It receives the desired size of the memory, which is currently +NXT_WASM_MEM_SIZE + NXT_WASM_PAGE_SIZE. + +However calls to luw_mem_get_init_size() will return just +NXT_WASM_MEM_SIZE (which is currently 32MiB). The extra +NXT_WASM_PAGE_SIZE is to cater for structure sizes in the response so +developers can generally assume they have the full NXT_WASM_MEM_SIZE for +their data. + +A default implementation of this function is provided ready for use that +calls malloc(3). + +luw_free_handler +^^^^^^^^^^^^^^^^ + +.. code:: c + + __attribute__((export_name("luw_free_handler"), __weak__)) + void luw_free_handler(u32 addr); + +This is called by Unit when it shuts down the WebAssembly language +module and free’s the memory previously allocated by +luw_malloc_handler(). + +It receives the address of the memory to free. + +An implementation of this function is provided ready for use that calls +free(3), in which case it receives the address that was previously +returned by luw_malloc_handler(). + +Optional +~~~~~~~~ + +luw_module_init_handler +^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: c + + __attribute__((export_name("luw_module_init_handler"), __weak__)) + void luw_module_init_handler(void); + +This is called by Unit when it loads the WebAssembly language module. + +A default dummy function is provided. If this handler is not required, +there is no need to specify it in the Unit config. + +luw_module_end_handler +^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: c + + __attribute__((export_name("luw_module_end_handler"), __weak__)) + void luw_module_end_handler(void); + +This is called by Unit when it shuts down the WebAssembly language +module. + +A default dummy function is provided. If this handler is not required, +there is no need to specify it in the Unit config. + +luw_request_init_handler +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: c + + __attribute__((export_name("luw_request_init_handler"), __weak__)) + void luw_request_init_handler(void); + +This is called by Unit at the start of nxt_wasm_request_handler(), i.e +at the start of a new request. + +A default dummy function is provided. If this handler is not required, +there is no need to specify it in the Unit config. + +luw_request_end_handler +^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: c + + __attribute__((export_name("luw_request_end_handler"), __weak__)) + void luw_request_end_handler(void); + +This is called by Unit at the end of nxt_wasm_request_handler(), i.e at +the end of a request. + +A default dummy function is provided. If this handler is not required, +there is no need to specify it in the Unit config. + +luw_response_end_handler +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: c + + __attribute__((export_name("luw_response_end_handler"), __weak__)) + void luw_response_end_handler(void); + +This is called by Unit after luw_http_response_end() has been called. + +A default dummy function is provided. If this handler is not required, +there is no need to specify it in the Unit config. + +Functions +--------- + +luw_init_ctx +~~~~~~~~~~~~ + +.. code:: c + + void luw_init_ctx(luw_ctx_t *ctx, u8 *addr, size_t offset); + +This function sets up a *luw_ctx_t* context structure, this contains +stuff required all throughout the API. It’s a typedef for opaqueness and +you should not in general be concerned with its contents. + +It take a pointer to a stack allocated luw_ctx_t, this will be zeroed +and have various members initialised. + +**addr** is a pointer to the shared memory as passed into +luw_request_handler(). + +**offset** is where in the shared memory it should start writing the +response. + +A quick word about memory +^^^^^^^^^^^^^^^^^^^^^^^^^ + +The way the Unit WebAssembly language module (the host/runtime) and the +WebAssembly module you want to write (the guest) communicate is via a +chunk of shared memory. + +This shared memory is simply the modules (guest) address space from +which we can allocate a chunk. How this memory is laid out varies on how +the module is built. + +With clang/linker flags of -Wl,–stack-first +-Wl,-z,stack-size=$((8\ *1024*\ 1024)) we get a memory layout something +like + +:: + + |----------------------------------------------------------------------| + | | | | + | <-- Stack | Global Data | Heap --> | + | | | | + |----------------------------------------------------------------------| + 0 0x800000 0x100000000 + + WebAssembly Module Linear Memory / Process Memory Layout + +(The above is assuming *–target=wasm32-wasi*, i.e 32bit) + +A chunk of memory from the heap is allocated at Unit WebAssembly +language module startup. + +We currently use this same chunk of memory for both requests and +responses. This means that depending on what you’re doing, you’ll want +to take a copy of the request (and remember luw_request_handler() may be +called multiple times for a single http request). + +That will be covered in more detail by the next function, +luw_set_req_buf(). + +Now back to *offset*, it may be convenient to put the response headers +at the beginning of this memory and then put the response after it, +rather than doing the headers and then doing the response as separate +steps, if the headers depends on some aspect of the response, its size +for example and Content-Length. + +Example + +.. code:: c + + luw_ctx_t ctx; + /* ... */ + luw_init_ctx(&ctx, addr, 4096 /* Response offset */); + +luw_set_req_buf +~~~~~~~~~~~~~~~ + +.. code:: c + + int luw_set_req_buf(luw_ctx_t *ctx, u8 **buf, unsigned int flags); + +This function is used to take a copy of the request buffer (as discussed +above). + +This takes a previously initialised (with luw_init_ctx()) luw_ctx_t. + +**buf** is a buffer where the request data will written. + +**flags** can be some combination (OR’d) of the following + +**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. + +Example + +.. code:: c + + static u8 *request_buf; + */ ... */ + int luw_request_handler(u8 *addr) + { + if (!request_buf) { + luw_init_ctx(&ctx, addr, 0); + /* + * 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); + } + + /* operate on the request (ctx) */ + + return 0; + } + +That example is taken from the +`luw-upload-reflector.c `__ +demo module. For a simpler example see +`luw-echo-request.c `__ + +luw_get_http_path +~~~~~~~~~~~~~~~~~ + +.. code:: c + + const char *luw_get_http_path(const luw_ctx_t *ctx); + +This function returns a pointer to the HTTP request path. + +E.g + +Given a request of + +:: + + http://localhost:8080/echo/?q=a + +this function will return + +:: + + /echo/?q=a + +luw_get_http_method +~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + const char *luw_get_http_method(const luw_ctx_t *ctx); + +This function returns a pointer to the HTTP method. + +E.g + +:: + + GET + +luw_get_http_version +~~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + const char *luw_get_http_version(const luw_ctx_t *ctx); + +This function returns a pointer to the HTTP version. + +E.g + +:: + + 1.1 + +luw_get_http_query +~~~~~~~~~~~~~~~~~~ + +.. code:: c + + const char *luw_get_http_query(const luw_ctx_t *ctx); + +This function returns a pointer to the query string (empty string for no +query string). + +E.g + +Given a request of + +:: + + http://localhost:8080/echo/?q=a + +this function will return + +:: + + q=a + +luw_get_http_remote +~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + const char *luw_get_http_remote(const luw_ctx_t *ctx); + +This function returns a pointer to the remote/client/peer address. + +E.g + +:: + + 2001:db8::f00 + +luw_get_http_local_addr +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + const char *luw_get_http_local_addr(const luw_ctx_t *ctx); + +This function returns a pointer to the local/server address. + +E.g + +:: + + 2001:db8::1 + +luw_get_http_local_port +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + const char *luw_get_http_local_port(const luw_ctx_t *ctx); + +This function returns a pointer to the local/server port. + +E.g + +:: + + 443 + +luw_get_http_server_name +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + const char *luw_get_http_server_name(const luw_ctx_t *ctx); + +This function returns a pointer to the local/server name. + +E.g + +:: + + www.example.com + +luw_get_http_content +~~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + const u8 *luw_get_http_content(const luw_ctx_t *ctx); + +This function returns a pointer to the start of the request body. + +luw_get_http_content_len +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + u64 luw_get_http_content_len(const luw_ctx_t *ctx); + +This function returns the size of the overall content. I.e +Content-Length. + +Prior to version 0.3.0 it returned a size_t + +luw_get_http_content_sent +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + size_t luw_get_http_content_sent(const luw_ctx_t *ctx); + +This function returns the length of the content that was sent to the +WebAssembly module in *this* request. Remember, a single HTTP request +may be split over several calls to luw_request_handler(). + +luw_get_http_total_content_sent +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + u64 luw_get_http_total_content_sent(const luw_ctx_t *ctx); + +This function returns the total length of the content that was sent to +the WebAssembly module so far. Remember, a single HTTP request may be +split over several calls to luw_request_handler(). + +*Version: 0.2.0* Prior to 0.3.0 it returned a size_t + +luw_http_is_tls +~~~~~~~~~~~~~~~ + +.. code:: c + + bool luw_http_is_tls(const luw_ctx_t *ctx); + +This function returns *true* if the connection to Unit was made over +TLS. + +luw_http_hdr_iter +~~~~~~~~~~~~~~~~~ + +.. code:: c + + 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) + +This function allows to iterate over the HTTP headers. For each header +it will call the given luw_http_hdr_iter_func() function whose prototype +is + +.. code:: c + + bool luw_http_hdr_iter_func(luw_ctx_t *ctx, + const char *name, const char *value, void *data); + +You may call this function whatever you like. For each header it will be +passed the *luw_ctx_t*, the header name, its value and a user specified +pointer if any, can be NULL. + +Returning *true* from this function will cause the iteration process to +continue, returning *false* will terminate it. + +Example + +.. code:: c + + static bool hdr_iter_func(luw_ctx_t *ctx, const char *name, const char *value, + void *user_data __luw_unused) + { + /* Do something with name & value */ + + /* Continue iteration or return false to stop */ + return true; + } + + /* ... * + + luw_http_hdr_iter(&ctx, hdr_iter_func, NULL); + +luw_http_hdr_get_value +~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + const char *luw_http_hdr_get_value(const luw_ctx_t *ctx, const char *hdr); + +Given a HTTP header *hdr* this function will look it up in the request +and return its value if found, otherwise *NULL*. + +The lookup is done case insensitively. + +luw_get_response_data_size +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + size_t luw_get_response_data_size(const luw_ctx_t *ctx); + +This function returns the size of the response data written to memory. + +luw_mem_writep +~~~~~~~~~~~~~~ + +.. code:: c + + __attribute__((__format__(printf, 2, 3))) + int luw_mem_writep(luw_ctx_t *ctx, const char *fmt, ...); + +This function is a cross between vasprintf(3) and mempcpy(3). + +It takes a format argument and zero or more arguments that will be +substituted into the format string. + +It then appends this formatted string to the memory. Note this string +will *not* be nul terminated. Unit does not expect this response data to +be nul terminated and we track the size of the response and return that +to Unit. + +This function returns -1 on error or the length of the string written. + +luw_mem_writep_data +~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + size_t luw_mem_writep_data(luw_ctx_t *ctx, const u8 *src, size_t size); + +This function just appends *size* bytes from *src* to the response. + +It returns the new size of the response. + +luw_req_buf_append +~~~~~~~~~~~~~~~~~~ + +.. code:: c + + void luw_req_buf_append(luw_ctx_t *ctx, const u8 *src); + +This function appends the request data contained in *src* to the +previously setup *request_buffer* with luw_set_req_buf(). + +This function would be used after an initial request to append the data +from subsequent requests to the request_buffer. + +Example + +.. code:: c + + int luw_request_handler(u8 *addr) + { + if (!request_buf) { + luw_init_ctx(&ctx, addr, 0); + /* + * 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); + } + + /* Do something with the request (ctx) */ + + return 0; + } + +luw_req_buf_copy +~~~~~~~~~~~~~~~~ + +.. code:: c + + void luw_req_buf_copy(luw_ctx_t *ctx, const u8 *src); + +This function is analogous to +`luw_req_buf_append <#luw_req_buf_append>`__ but rather than appending +the request data contained in *src* to the previously setup +*request_buffer* with luw_set_req_buf(), it simply overwrites what’s +currently there. + +This function could be used to handle large requests/uploads that you +want to save out to disk or some such and can’t buffer it all in memory. + +Example + +.. code:: c + + int luw_request_handler(u8 *addr) + { + const u8 *buf; + ssize_t bytes_wrote; + + if (total_bytes_wrote == 0) { + luw_init_ctx(&ctx, addr, 0); + luw_set_req_buf(&ctx, &request_buf, LUW_SRB_NONE); + + fd = open("/var/tmp/large-file.dat", O_CREAT|O_TRUNC|O_WRONLY, + 0666); + } else { + luw_req_buf_copy(&ctx, addr); + } + + buf = luw_get_http_content(&ctx); + bytes_wrote = write(fd, buf, luw_get_http_content_sent(&ctx)); + if (bytes_wrote == -1) + return -1; + + total_bytes_wrote += bytes_wrote; + if (total_bytes_wrote == luw_get_http_content_len(&ctx)) + luw_http_response_end(); + + return 0; + } + +*Version: 0.3.0* + +luw_mem_splice_file +~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + ssize_t luw_mem_splice_file(const u8 *src, int fd); + +This function write(2)’s the request data directly from the shared +memory (*src*) to the file represented by the given file-descriptor +(*fd*). + +This can be used as an alternative to +`luw_req_buf_copy <#luw_req_buf_copy>`__ and avoids an extra copying of +the request data. + +Example + +.. code:: c + + int luw_request_handler(u8 *addr) { + ssize_t bytes_wrote; + + if (total_bytes_wrote == 0) { + luw_init_ctx(&ctx, addr, 0); + luw_set_req_buf(&ctx, &request_buf, LUW_SRB_NONE); + + fd = open("/var/tmp/large-file.dat", O_CREAT|O_TRUNC|O_WRONLY, + 0666); + } + + bytes_wrote = luw_mem_splice_file(addr, fd); + if (bytes_wrote == -1) + return -1; + + total_bytes_wrote += bytes_wrote; + if (total_bytes_wrote == luw_get_http_content_len(&ctx)) + luw_http_response_end(); + + return 0; + } + +*Version: 0.3.0* + +luw_mem_fill_buf_from_req +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + size_t luw_mem_fill_buf_from_req(luw_ctx_t *ctx, size_t from); + +This is a convenience function to fill the response buffer with data +from the request buffer. + +*from* is basically the offset in the request_buffer where to start +copying data from. + +Example + +.. code:: c + + /* ... */ + write_bytes = luw_mem_fill_buf_from_req(ctx, total_response_sent); + total_response_sent += write_bytes; + /* ... */ + +This is taken from the +`luw-upload-reflector.c `__ +demo module. + +In this case we build up a request_buffer on each call of +luw_request_handler(), so total_response_sent grows each time by how +much data was sent in *that* request. + +Here are are sending data back to the client after each time we receive +it to demonstrate the interleaving of requests and responses from the +WebAssembly module during a single http request. + +This function returns the number of bytes written to the response +buffer. + +luw_mem_reset +~~~~~~~~~~~~~ + +.. code:: c + + void luw_mem_reset(luw_ctx_t *ctx); + +This function resets the response buffer size and the number of response +headers back to 0. + +luw_http_set_response_status +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + void luw_http_set_response_status(luw_http_status_t status); + +This function is used to set the HTTP response status. It takes one of +the `luw_http_status_t <#luw_http_status_t>`__ enum values. + +It should be called before any calls to *luw_http_send_response()* or +*luw_http_send_headers()*. + +If you don’t call this function the response status defaults to ‘200 +OK’. + +If you wish to error out with a ‘500 Internal Server Error’, you don’t +need to call this function. Simply returning *-1* from the +request_handler function will indicate this error. + +E.g + +Send a ‘403 Forbidden’ + +.. code:: c + + /* ... */ + luw_http_set_response_status(LUW_HTTP_FORBIDDEN); + luw_http_send_response(ctx); /* Doesn't require any body */ + luw_http_response_end(); + /* ... */ + return 0; + +Send a ‘307 Temporary Re-direct’ + +.. code:: c + + /* ... */ + luw_http_set_response_status(LUW_HTTP_TEMPORARY_REDIRECT); + + luw_http_init_headers(ctx, 1, 0); + luw_http_add_header(ctx, "Location", "https://example.com/"); + luw_http_send_headers(ctx); + luw_http_response_end(); + /* ... */ + return 0; + +*Version: 0.3.0* + +luw_http_send_response +~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + void luw_http_send_response(const luw_ctx_t *ctx); + +This function calls into Unit to send the response buffer back. + +luw_http_init_headers +~~~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + void luw_http_init_headers(luw_ctx_t *ctx, size_t nr, size_t offset); + +This function is used in the preparation of sending back response +headers. + +*nr* is the number of headers we are sending. + +*offset* is the offset into the response buffer where we are placing +these headers. This will usually be 0. + +Example + +.. code:: c + + luw_http_init_headers(ctx, 2, 0); + +luw_http_add_header +~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + void luw_http_add_header(luw_ctx_t *ctx, const char *name, const char *value); + +This function is used to add a header to the response. + +*name* is the name of the header. + +*value* is the value of the header. + +Example + +.. code:: c + + char clen[32]; + /* ... */ + snprintf(clen, sizeof(clen), "%lu", luw_get_response_data_size(&ctx)); + luw_http_add_header(&ctx, "Content-Type", "text/plain"); + luw_http_add_header(&ctx, "Content-Length", clen); + +luw_http_send_headers +~~~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + void luw_http_send_headers(const luw_ctx_t *ctx); + +This function calls into Unit and triggers the sending of the response +headers. + +luw_http_response_end +~~~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + void luw_http_response_end(void); + +This function calls into Unit and tells it this is the end of the +response which will trigger Unit to send it to the client. + +luw_mem_get_init_size +~~~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + u32 luw_mem_get_init_size(void); + +This function calls into Unit to get the size of the shared memory. This +is the amount of memory you should assume you have for creating +responses. Remember you can create multiple responses before calling +luw_http_response_end(). + +luw_foreach_http_hdr +~~~~~~~~~~~~~~~~~~~~ + +.. code:: c + + void luw_foreach_http_hdr(luw_ctx_t ctx, luw_http_hdr_iter_t *iter, + const char *name, const char *value) + +Defined as a macro, this is used to iterate over the HTTP header fields. + +It takes a \_luw_ctx_t \*\_ and a \_luw_http_hdr_iter_t \*\_ and returns +pointers to the field name and value. + +Example + +.. code:: c + + luw_ctx_t ctx; + luw_http_hdr_iter_t *iter; + const char *name; + const char *value; + /* ... */ + luw_foreach_http_hdr(ctx, iter, name, value) { + printf("Field name : %s, field value : %s\n", name, value); + /* do something else with name & value */ + } + +Misc. Functions +--------------- + +The following functions are convenience wrappers for the Rust bindings +and should **not** be used directly. + +luw_malloc +~~~~~~~~~~ + +.. code:: c + + void *luw_malloc(size_t size); + +Straight wrapper for malloc(3). + +luw_free +~~~~~~~~ + +.. code:: c + + void luw_free(void *ptr); + +Straight wrapper for free(3). diff --git a/API-Rust.md b/API-Rust.md deleted file mode 100644 index 9b4a9d9..0000000 --- a/API-Rust.md +++ /dev/null @@ -1,1138 +0,0 @@ -# 'Rusty' Rust API - -Rusty is a more native Rust wrapper around the auto-generated bindings to -[libunit-wasm](https://github.com/nginx/unit-wasm/blob/main/API-C.md). - -```Rust -use unit_wasm::rusty::*; -``` - -If using - -```Rust -uwr_http_hdr_iter(); -``` - -```Rust -use std::ffi::CStr; -use std::os::raw::c_char; -use std::os::raw::c_void; -``` - -## Naming - -You will see references to functions etc starting with *luw_* or *LUW_* and -*uwr_". - -**luw/LUW** (libunit-wasm) come from the underlying C library and in the Rust -case are the auto-generated bindings with a few manual additions. - -**uwr** (Unit Wasm Rust aka '_rusty_') is a more Rust native wrapper ontop of -the bindings. - -In _rusty_ the luw/LUW API is generally the low level stuff like the library -version macros and the various function handlers where they can be used as is -and there isn't a real need to create wrappers specifically for them. - -1. ['Rusty' Rust API](#rusty-rust-api) - * [Naming](#naming) -2. [Macros](#macros) - * [Version](#version) - * [String Conversion](#string-conversion) - * [uwr_write_str!](#uwr_write_str) -3. [Enums](#enums) - [luw_http_status_t](#luw_http_status_t) -3. [Function Handlers](#function-handlers) - * [Optional](#optional) - - [luw_module_init_handler](#luw_module_init_handler) - - [luw_module_end_handler](#luw_module_end_handler) - - [luw_request_init_handler](#luw_request_init_handler) - - [luw_request_end_handler](#luw_request_end_handler) - - [luw_response_end_handler](#luw_response_end_handler) - * [Required](#required) - - [luw_request_handler](#luw_request_handler) - - [luw_free_handler](#luw_free_handler) - - [luw_malloc_handler](#luw_malloc_handler) -4. [Functions](#functions) - * [UWR_CTX_INITIALIZER](#uwr_ctx_initializer) - * [uwr_init_ctx](#uwr_init_ctx) - * [uwr_set_req_buf](#uwr_set_req_buf) - * [uwr_get_http_path](#uwr_get_http_path) - * [uwr_get_http_method](#uwr_get_http_method) - * [uwr_get_http_version](#uwr_get_http_version) - * [uwr_get_http_query](#uwr_get_http_query) - * [uwr_get_http_remote](#uwr_get_http_remote) - * [uwr_get_http_local_addr](#uwr_get_http_local_addr) - * [uwr_get_http_local_port](#uwr_get_http_local_port) - * [uwr_get_http_server_name](#uwr_get_http_server_name) - * [uwr_get_http_content](#uwr_get_http_content) - * [uwr_get_http_content_str](#uwr_get_http_content_str) - * [uwr_get_http_content_len](#uwr_get_http_content_len) - * [uwr_get_http_content_sent](#uwr_get_http_content_sent) - * [uwr_get_http_total_content_sent](#uwr_get_http_total_content_sent) - * [uwr_http_is_tls](#uwr_http_is_tls) - * [uwr_http_hdr_iter](#uwr_http_hdr_iter) - * [uwr_http_hdr_get_value](#uwr_http_hdr_get_value) - * [uwr_get_response_data_size](#uwr_get_response_data_size) - * [uwr_mem_write_buf](#uwr_mem_write_buf) - * [uwr_req_buf_append](#uwr_req_buf_append) - * [uwr_req_buf_copy](#uwr_req_buf_copy) - * [uwr_mem_splice_file](#uwr_mem_splice_file) - * [uwr_mem_fill_buf_from_req](#uwr_mem_fill_buf_from_req) - * [uwr_mem_reset](#uwr_mem_reset) - * [uwr_http_set_response_status](#uwr_http_set_response_status) - * [uwr_http_send_response](#uwr_http_send_response) - * [uwr_http_init_headers](#uwr_http_init_headers) - * [uwr_http_add_header](#uwr_http_add_header) - * [uwr_http_add_header_content_type](#uwr_http_add_header_content_type) - * [uwr_http_add_header_content_len](#uwr_http_add_header_content_len) - * [uwr_http_send_headers](#uwr_http_send_headers) - * [uwr_http_response_end](#uwr_http_response_end) - * [uwr_mem_get_init_size](#uwr_mem_get_init_size) -5. [Misc. Functions](#misc-functions) - * [uwr_malloc](#uwr_malloc) - * [uwr_free](#uwr_free) - -## Macros - -### Version - -For the underlying libunit-wasm version. - -```Rust -pub const LUW_VERSION_MAJOR: i32; -pub const LUW_VERSION_MINOR: i32; -pub const LUW_VERSION_PATCH: i32; -``` - -```Rust -/* Version number in hex 0xMMmmpp00 */ -pub const LUW_VERSION_NUMBER: i32 = - (LUW_VERSION_MAJOR << 24) | \ - (LUW_VERSION_MINOR << 16) | \ - (LUW_VERSION_PATCH << 8); -``` - -### String Conversion - -```Rust -C2S!(string); -``` - -Converts a C string into a Rust String - -Main use is internally and in the *uwr_http_hdr_iter()* callback function, -e.g - -```Rust -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 { - uwr_write_str!(ctx, "{} = {}\n", C2S!(name), C2S!(value)); - - return true; -} -``` - -Example taken from the -[echo-request](https://github.com/nginx/unit-wasm/blob/main/examples/rust/echo-request/src/lib.rs) -Wasm demo module - -```Rust -S2C!(formatted string); -``` - -Converts a Rust String, with optional formatting, to a C string. - -Used internally. - -### uwr_write_str! - -```Rust -uwr_write_str!*ctx, fmt, ...); -``` - -This is essentially a wrapper around -[luw_mem_writep_data()](https://github.com/nginx/unit-wasm/blob/main/API-C.md#luw_mem_writep_data) - -It is the main way to write responses back to the client. - -It takes the luw_ctx_t context pointer, a string that will be run through the -[format!()](https://doc.rust-lang.org/std/macro.format.html) macro and any -optional arguments. - -## Enums - -### luw_http_status_t -```Rust -pub enum luw_http_status_t { - LUW_HTTP_CONTINUE = 100, - LUW_HTTP_SWITCHING_PROTOCOLS = 101, - - LUW_HTTP_OK = 200, - LUW_HTTP_CREATED = 201, - LUW_HTTP_ACCEPTED = 202, - LUW_HTTP_NO_CONTENT = 204, - - LUW_HTTP_MULTIPLE_CHOICES = 300, - LUW_HTTP_MOVED_PERMANENTLY = 301, - LUW_HTTP_FOUND = 302, - LUW_HTTP_SEE_OTHER = 303, - LUW_HTTP_NOT_MODIFIED = 304, - LUW_HTTP_TEMPORARY_REDIRECT = 307, - LUW_HTTP_PERMANENT_REDIRECT = 308, - - LUW_HTTP_BAD_REQUEST = 400, - LUW_HTTP_UNAUTHORIZED = 401, - LUW_HTTP_FORBIDDEN = 403, - LUW_HTTP_NOT_FOUND = 404, - LUW_HTTP_METHOD_NOT_ALLOWED = 405, - LUW_HTTP_NOT_ACCEPTABLE = 406, - LUW_HTTP_REQUEST_TIMEOUT = 408, - LUW_HTTP_CONFLICT = 409, - LUW_HTTP_GONE = 410, - LUW_HTTP_LENGTH_REQUIRED = 411, - LUW_HTTP_PAYLOAD_TOO_LARGE = 413, - LUW_HTTP_URI_TOO_LONG = 414, - LUW_HTTP_UNSUPPORTED_MEDIA_TYPE = 415, - LUW_HTTP_UPGRADE_REQUIRED = 426, - LUW_HTTP_TOO_MANY_REQUESTS = 429, - LUW_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431, - - /* Proposed by RFC 7725 */ - LUW_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451, - - LUW_HTTP_INTERNAL_SERVER_ERROR = 500, - LUW_HTTP_NOT_IMPLEMENTED = 501, - LUW_HTTP_BAD_GATEWAY = 502, - LUW_HTTP_SERVICE_UNAVAILABLE = 503, - LUW_HTTP_GATEWAY_TIMEOUT = 504, -} -``` - -## Function Handlers - -These functions are exported from the WebAssembly module and are called from -the WebAssembly runtime (the Unit WebAssembly language module in this case). - -There are two types of handlers; required & optional. - -luw_request_handler(), luw_malloc_handler() & luw_free_handler() are required -with the rest being optional. - -libunit-wasm includes exports for these handlers and some default -implementations. - -These functions are defined as _weak_ symbols and so if a developer writes -their own function of the same name, that will take precedence. - -However, developers are under no obligation to use these and can create their -own with any (valid) names they like. - -Whatever names developers choose, they are specified in the Unit config. - -## Required - -#### luw_request_handler - -```Rust -#[no_mangle] -pub extern "C" fn luw_request_handler(addr: *mut u8) -> i32; -``` - -This is called by Unit during a request. It may be called multiple times for -a single HTTP request if there is more request data than the available memory -for host <--> module communications. - -You will need to provide your own implementation of this function. - -It receives the base address of the shared memory. Essentially what is -returned by luw_malloc_handler(). - -It returns an int. This should nearly always be _0_. - -If you wish to indicate a '500 Internal Server Error', for example if some -internal API has failed or an OS level error occurred, then you can simply -return _-1_, _if_ you have haven't already _sent_ any response or headers. - -You can still return 0 _and_ set the HTTP response status to 500 using -[uwr_http_set_response_status](#uwr_http_set_response_status). - -#### luw_malloc_handler - -```Rust -#[no_mangle] -pub extern "C" fn luw_malloc_handler(size: usize) -> u32; -``` - -This is called by Unit when it loads the WebAssembly language module. This -provides the shared memory used for host <--> module communications. - -It receives the desired size of the memory, which is currently -NXT_WASM_MEM_SIZE + NXT_WASM_PAGE_SIZE. - -However calls to luw_mem_get_init_size() will return just NXT_WASM_MEM_SIZE -(which is currently 32MiB). The extra NXT_WASM_PAGE_SIZE is to cater for -structure sizes in the response so developers can generally assume they have -the full NXT_WASM_MEM_SIZE for their data. - -A default implementation of this function is provided ready for use that -calls malloc(3). - -#### luw_free_handler - -```Rust -#[no_mangle] -pub extern "C" fn luw_free_handler(addr: u32); -``` - -This is called by Unit when it shuts down the WebAssembly language module and -free's the memory previously allocated by luw_malloc_handler(). - -It receives the address of the memory to free. - -An implementation of this function is provided ready for use that calls -free(3), in which case it receives the address that was previously returned -by luw_malloc_handler(). - -### Optional - -#### luw_module_init_handler - -```Rust -#[no_mangle] -pub extern "C" fn luw_module_init_handler(); -``` - -This is called by Unit when it loads the WebAssembly language module. - -A default dummy function is provided. If this handler is not required, there -is no need to specify it in the Unit config. - -#### luw_module_end_handler - -```Rust -#[no_mangle] -pub extern "C" fn luw_module_end_handler(); -``` - -This is called by Unit when it shuts down the WebAssembly language module. - -A default dummy function is provided. If this handler is not required, there -is no need to specify it in the Unit config. - -#### luw_request_init_handler - -```Rust -#[no_mangle] -pub extern "C" fn luw_request_init_handler(); -``` - -This is called by Unit at the start of nxt_wasm_request_handler(), i.e at the -start of a new request. - -A default dummy function is provided. If this handler is not required, there -is no need to specify it in the Unit config. - -#### luw_request_end_handler - -```Rust -#[no_mangle] -pub extern "C" fn luw_request_end_handler(); -``` - -This is called by Unit at the end of nxt_wasm_request_handler(), i.e at the -end of a request. - -A default dummy function is provided. If this handler is not required, there -is no need to specify it in the Unit config. - -#### luw_response_end_handler - -```Rust -#[no_mangle] -pub extern "C" fn luw_response_end_handler(); -``` - -This is called by Unit after luw_http_response_end() has been called. - -A default dummy function is provided. If this handler is not required, there -is no need to specify it in the Unit config. - -## Functions - -### UWR_CTX_INITIALIZER - -```Rust -pub const fn UWR_CTX_INITIALIZER() -> luw_ctx_t; -``` - -Used to initialise a luw_ctx_t context structure. E.g - -```Rust -let ctx = &mut UWR_CTX_INITIALIZER(); -``` - -### uwr_init_ctx - -```Rust -pub fn uwr_init_ctx(ctx: *mut luw_ctx_t, addr: *mut u8, offset: usize); -``` - -This function sets up a *luw_ctx_t* context structure, this contains stuff -required all throughout the API. - -**addr** is a pointer to the shared memory as passed into luw_request_handler(). - -**offset** is where in the shared memory it should start writing the response. - -#### A quick word about memory - -The way the Unit WebAssembly language module (the host/runtime) and the -WebAssembly module you want to write (the guest) communicate is via a chunk -of shared memory. - -This shared memory is simply the modules (guest) address space from which we -can allocate a chunk. How this memory is laid out varies on how the module -is built. - -With clang/linker flags of -Wl,--stack-first -Wl,-z,stack-size=$((8*1024*1024)) -we get a memory layout something like - -``` - |----------------------------------------------------------------------| - | | | | - | <-- Stack | Global Data | Heap --> | - | | | | - |----------------------------------------------------------------------| - 0 0x800000 0x100000000 - - WebAssembly Module Linear Memory / Process Memory Layout -``` - -(The above is assuming _--target=wasm32-wasi_, i.e 32bit) - -A chunk of memory from the heap is allocated at Unit WebAssembly language -module startup. - -We currently use this same chunk of memory for both requests and responses. -This means that depending on what you're doing, you'll want to take a copy -of the request (and remember luw_request_handler() may be called multiple -times for a single http request). - -That will be covered in more detail by the next function, uwr_set_req_buf(). - -Now back to _offset_, it may be convenient to put the response headers at the -beginning of this memory and then put the response after it, rather than -doing the headers and then doing the response as separate steps, if the -headers depends on some aspect of the response, its size for example and -Content-Length. - -Example - -```Rust -#[no_mangle] -pub extern "C" fn uwr_request_handler(addr: *mut u8) -> i32 { - let ctx = &mut UWR_CTX_INITIALIZER(); - /* ... */ - uwr_init_ctx(ctx, addr, 4096 /* Response offset */); -``` - -### uwr_set_req_buf - -```Rust -pub fn uwr_set_req_buf( - ctx: *mut luw_ctx_t, - buf: *mut *mut u8, - flags: u32, -) -> i32; -``` - -This function is used to take a copy of the request buffer (as discussed -above). - -This takes a previously initialised (with uwr_init_ctx()) luw_ctx_t. - -**buf** is a buffer where the request data will written. - -**flags** can be some combination (OR'd) of the following - -**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. - -Example - -```Rust -static mut CTX: luw_ctx_t = UWR_CTX_INITIALIZER(); - -static mut REQUEST_BUF: *mut u8 = null_mut(); -*/ ... */ -#[no_mangle] -pub extern "C" fn uwr_request_handler(addr: *mut u8) -> i32 { - let ctx: *mut luw_ctx_t = addr_of_mut!(CTX); - - if unsafe { REQUEST_BUF.is_null() } { - uwr_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. - */ - uwr_set_req_buf( - ctx, - addr_of_mut!(REQUEST_BUF), - LUW_SRB_APPEND | LUW_SRB_ALLOC | LUW_SRB_FULL_SIZE, - ); - } else { - uwr_req_buf_append(ctx, addr); - } - - upload_reflector(ctx); - - return 0; -} -``` - -That example is taken from the -[upload-reflector demo](https://github.com/nginx/unit-wasm/blob/main/examples/rust/upload-reflector/src/lib.rs) -demo module. For a simpler example see the -[echo-request demo](https://github.com/nginx/unit-wasm/blob/main/examples/rust/echo-request/src/lib.rs) - -### uwr_get_http_path - -```Rust -pub fn uwr_get_http_path(ctx: *const luw_ctx_t) -> &'static str; -``` - -This function returns a pointer to the HTTP request path. - -E.g - -Given a request of -``` -http://localhost:8080/echo/?q=a -``` -this function will return -``` -/echo/?q=a -``` - -### uwr_get_http_method - -```Rust -pub fn uwr_get_http_method(ctx: *const luw_ctx_t) -> &'static str; -``` - -This function returns a pointer to the HTTP method. - -E.g - -``` -GET -``` - -### uwr_get_http_version - -```Rust -pub fn uwr_get_http_version(ctx: *const luw_ctx_t) -> &'static str; -``` - -This function returns a pointer to the HTTP version. - -E.g - -``` -1.1 -``` - -### uwr_get_http_query - -```Rust -pub fn uwr_get_http_query(ctx: *const luw_ctx_t) -> &'static str; -``` - -This function returns a pointer to the query string (empty string for no query -string). - -E.g - -Given a request of -``` -http://localhost:8080/echo/?q=a -``` -this function will return -``` -q=a -``` - -### uwr_get_http_remote - -```Rust -pub fn uwr_get_http_remote(ctx: *const luw_ctx_t) -> &'static str; -``` - -This function returns a pointer to the remote/client/peer address. - -E.g - -``` -2001:db8::f00 -``` - -### uwr_get_http_local_addr - -```Rust -pub fn uwr_get_http_local_addr(ctx: *const luw_ctx_t) -> &'static str; -``` - -This function returns a pointer to the local/server address. - -E.g - -``` -2001:db8::1 -``` - -### uwr_get_http_local_port - -```Rust -pub fn uwr_get_http_local_port(ctx: *const luw_ctx_t) -> &'static str; -``` - -This function returns a pointer to the local/server port. - -E.g - -``` -443 -``` - -### uwr_get_http_server_name - -```Rust -pub fn uwr_get_http_server_name(ctx: *const luw_ctx_t) -> &'static str; -``` - -This function returns a pointer to the local/server name. - -E.g - -``` -www.example.com -``` - -### uwr_get_http_content - -```Rust -pub fn uwr_get_http_content(ctx: *const luw_ctx_t) -> *const u8; -``` - -This function returns a pointer to the start of the request body. - -### uwr_get_http_content_str - -```Rsut -pub fn uwr_get_http_content_str(ctx: *const luw_ctx_t) -> &'static str; -``` - -Same as above but returns a Rust str. - -_Version: 0.2.0_ - -### uwr_get_http_content_len - -```Rust -pub fn uwr_get_http_content_len(ctx: *const luw_ctx_t) -> u64; -``` - -This function returns the size of the overall content. I.e Content-Length. - -Prior to version 0.3.0 it returned a usize - -### uwr_get_http_content_sent - -```Rust -pub fn uwr_get_http_content_sent(ctx: *const luw_ctx_t) -> usize; -``` - -This function returns the length of the content that was sent to the -WebAssembly module in _this_ request. Remember, a single HTTP request may be -split over several calls to luw_request_handler(). - -### uwr_get_http_total_content_sent - -```Rust -pub fn uwr_get_http_total_content_sent(ctx: *const luw_ctx_t) -> u64; -``` - -This function returns the total length of the content that was sent to the -WebAssembly module so far. Remember, a single HTTP request may be split over -several calls to luw_request_handler(). - -_Version: 0.2.0_ Prior to 0.3.0 it returned a usize - -### uwr_http_is_tls - -```Rust -pub fn uwr_http_is_tls(ctx: *const luw_ctx_t) -> bool; -``` - -This function returns _true_ if the connection to Unit was made over TLS. - -### uwr_http_hdr_iter - -```Rust -pub fn uwr_http_hdr_iter( - ctx: *mut luw_ctx_t, - luw_http_hdr_iter_func: ::std::option::Option< - unsafe extern "C" fn( - ctx: *mut luw_ctx_t, - name: *const c_char, - value: *const c_char, - data: *mut c_void, - ) -> bool, - >, - user_data: *mut c_void, -); -``` - -This function allows to iterate over the HTTP headers. For each header it -will call the given luw_http_hdr_iter_func() function whose prototype is - -```Rust -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; -``` - -You may call this function whatever you like. For each header it will be -passed the *luw_ctx_t*, the header name, its value and a user specified -pointer if any, can be NULL. - -Returning _true_ from this function will cause the iteration process to -continue, returning _false_ will terminate it. - -Example - -```Rust -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 { - /* Do something with name & value, ignoring data */ - - return true; -} - -/* ... * - -uwr_http_hdr_iter(ctx, Some(hdr_iter_func), null_mut()); -``` - -### uwr_http_hdr_get_value - -```Rust -pub fn uwr_http_hdr_get_value(ctx: *const luw_ctx_t, hdr: &str) -> &'static str; -``` - -Given a HTTP header _hdr_ this function will look it up in the request and -return its value if found, otherwise _NULL_. - -The lookup is done case insensitively. - -### uwr_get_response_data_size - -```Rust -pub fn uwr_get_response_data_size(ctx: *const luw_ctx_t) -> usize; -``` - -This function returns the size of the response data written to memory. - -### uwr_mem_write_buf - -```Rust -pub fn uwr_mem_write_buf( - ctx: *mut luw_ctx_t, - src: *const u8, - size: usize, -) -> usize; -``` - -This function just appends _size_ bytes from _src_ to the response. - -It returns the new size of the response. - -### uwr_req_buf_append - -```Rust -pub fn uwr_req_buf_append(ctx: *mut luw_ctx_t, src: *const u8); -``` - -This function appends the request data contained in _src_ to the previously -setup *request_buffer* with uwr_set_req_buf(). - -This function would be used after an initial request to append the data from -subsequent requests to the request_buffer. - -Example - -```Rust -#[no_mangle] -pub extern "C" fn uwr_request_handler(addr: *mut u8) -> i32 { - let ctx: *mut luw_ctx_t = addr_of_mut!(CTX); - - if unsafe { REQUEST_BUF.is_null() } { - uwr_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. - */ - uwr_set_req_buf( - ctx, - addr_of_mut!(REQUEST_BUF), - LUW_SRB_APPEND | LUW_SRB_ALLOC | LUW_SRB_FULL_SIZE, - ); - } else { - uwr_req_buf_append(ctx, addr); - } - - upload_reflector(ctx); - - return 0; -} -``` - -### uwr_req_buf_copy - -```Rust -pub fn uwr_req_buf_copy(ctx: *mut luw_ctx_t, src: *const u8); -``` - -This function is analogous to [uwr_req_buf_append](#uwr_req_buf_append) but -rather than appending the request data contained in _src_ to the previously -setup *request_buffer* with uwr_set_req_buf(), it simply overwrites what's -currently there. - -This function could be used to handle large requests/uploads that you want to -save out to disk or some such and can't buffer it all in memory. - -### uwr_mem_splice_file - -```Rust -pub fn uwr_mem_splice_file(src: *const u8, f: &mut File) -> isize; -``` -This function write(2)'s the request data directly from the shared memory -(_src_) to the file represented by the given _File_ object (_f_). - -This can be used as an alternative to [uwr_req_buf_copy](#uwr_req_buf_copy) -and avoids an extra copying of the request data. - -Example - -```Rust -pub extern "C" fn uwr_request_handler(addr: *mut u8) -> i32 { - let ctx: *mut luw_ctx_t = addr_of_mut!(CTX); - let mut f; - let bytes_wrote: isize; - let mut total = unsafe { TOTAL_BYTES_WROTE }; - - if total == 0 { - uwr_init_ctx(ctx, addr, 0); - uwr_set_req_buf( - ctx, - addr_of_mut!(REQUEST_BUF), - LUW_SRB_NONE - ); - - f = File::create("/var/tmp/large-file.dat").unwrap(); - } else { - f = File::options() - .append(true) - .open("/var/tmp/large-file.dat") - .unwrap(); - } - - bytes_wrote = uwr_mem_splice_file(addr, &mut f); - if bytes_wrote == -1 { - return -1; - } - - total += bytes_wrote as u64; - if total == uwr_get_http_content_len(ctx) { - total = 0; - - uwr_http_response_end(); - } - - unsafe { TOTAL_BYTES_WROTE = total }; - - return 0; -} -``` - -### uwr_mem_fill_buf_from_req - -```Rust -pub fn uwr_req_buf_append(ctx: *mut luw_ctx_t, src: *const u8); -``` - -This is a convenience function to fill the response buffer with data from -the request buffer. - -_from_ is basically the offset in the request_buffer where to start copying -data from. - -Example - -```Rust -/* ... */ -write_bytes = uwr_mem_fill_buf_from_req(ctx, TOTAL_RESPONSE_SENT); -TOTAL_RESPONSE_SENT += write_bytes; -/* ... */ -``` - -This is taken from the -[upload-reflector demo](https://github.com/nginx/unit-wasm/blob/main/examples/c/upload-reflector/src/lib.rs) -demo module. - -In this case we build up a request_buffer on each call of -luw_request_handler(), so TOTAL_RESPONSE_SENT grows each time by how much -data was sent in _that_ request. - -Here are are sending data back to the client after each time we receive it to -demonstrate the interleaving of requests and responses from the WebAssembly -module during a single http request. - -This function returns the number of bytes written to the response buffer. - -### uwr_mem_reset - -```Rust -pub fn uwr_luw_mem_reset(ctx: *mut luw_ctx_t); -``` - -This function resets the response buffer size and the number of response -headers back to 0. - -### uwr_http_set_response_status - -```Rust -pub fn uwr_http_set_response_status(status: luw_http_status_t); -``` - -This function is used to set the HTTP response status. It takes one of the -[luw_http_status_t](#luw_http_status_t) enum values. - -It should be called before any calls to *uwr_http_send_response()* or -*uwr_http_send_headers()*. - -If you don't call this function the response status defaults to '200 OK'. - -If you wish to error out with a '500 Internal Server Error', you don't need to -call this function. Simply returning _-1_ from the request_handler function -will indicate this error. - -E.g - -Send a '403 Forbidden' - -```Rust -/* ... */ -uwr_http_set_response_status(LUW_HTTP_FORBIDDEN); -uwr_http_send_response(ctx); /* Doesn't require any body */ -uwr_http_response_end(); -/* ... */ -return 0; -``` - -Send a '307 Temporary Re-direct' - -```Rust -/* ... */ -uwr_http_set_response_status(LUW_HTTP_TEMPORARY_REDIRECT); - -uwr_http_init_headers(ctx, 1, 0); -uwr_http_add_header(ctx, "Location", "https://example.com/"); -uwr_http_send_headers(ctx); -uwr_http_response_end(); -/* ... */ -return 0; -``` - -_Version: 0.3.0_ - -### uwr_http_send_response - -```Rust -pub fn uwr_http_send_response(ctx: *const luw_ctx_t); -``` - -This function calls into Unit to send the response buffer back. - -### uwr_http_init_headers - -```Rust -pub fn uwr_http_init_headers(ctx: *mut luw_ctx_t, nr: usize, offset: usize); -``` - -This function is used in the preparation of sending back response headers. - -_nr_ is the number of headers we are sending. - -_offset_ is the offset into the response buffer where we are placing these -headers. This will usually be 0. - -Example - -```Rust -uwr_http_init_headers(ctx, 2, 0); -``` - -### uwr_http_add_header - -```Rust -pub fn uwr_http_add_header( - ctx: *mut luw_ctx_t, - name: &str, - value: &str, -); -``` - -This function is used to add a header to the response. - -_name_ is the name of the header. - -_value_ is the value of the header. - -Example - -```Rust -uwr_http_add_header(&ctx, "Content-Type", "text/plain"); -uwr_http_add_header( - ctx, - "Content-Length", - &format!("{}", uwr_get_response_data_size(ctx)), -); -``` - -### uwr_http_add_header_content_type - -```Rust -pub fn uwr_http_add_header_content_type(ctx: *mut luw_ctx_t, ctype: &str); -``` - -A convenience function for setting the 'Content-Type' response header. -E.g the above example that adds the _Content-Type_ header could be -written as - -```Rust -uwr_http_add_header_content_type(ctx, "text/plain"); -``` - -_Version: 0.2.0_ - -### uwr_http_add_header_content_len - -```Rust -pub fn uwr_http_add_header_content_len(ctx: *mut luw_ctx_t); -``` - -A convenience function for setting the 'Content-Length' response header. -E.g the above example that adds the _Content-Length_ header could be -written as - -```Rust -uwr_http_add_header_content_len(ctx); -``` - -This function uses [uwr_get_response_data_size](#uwr_get_response_data_size) -internally to get the size of the response data. - -_Version: 0.2.0_ - -### uwr_http_send_headers - -```Rust -pub fn uwr_http_send_headers(ctx: *const luw_ctx_t); -``` - -This function calls into Unit and triggers the sending of the response -headers. - -### uwr_http_response_end - -```Rust -pub fn uwr_http_response_end(); -``` - -This function calls into Unit and tells it this is the end of the response -which will trigger Unit to send it to the client. - -### uwr_mem_get_init_size - -```Rust -pub fn uwr_mem_get_init_size() -> u32; -``` - -This function calls into Unit to get the size of the shared memory. This is -the amount of memory you should assume you have for creating responses. -Remember you can create multiple responses before calling -luw_http_response_end(). - -## Misc. Functions - -The following functions are convenience wrappers for the Rust bindings and -should **not** be used directly. - -### uwr_malloc - -```Rust -pub fn uwr_malloc(size: u32) -> *mut u8; -``` - -Essentially a straight wrapper for malloc(3). - -### uwr_free - -```Rust -pub fn uwr_free(ptr: *mut u8); -``` - -Essentially a straight wrapper for free(3). diff --git a/API-Rust.rst b/API-Rust.rst new file mode 100644 index 0000000..6bac9a3 --- /dev/null +++ b/API-Rust.rst @@ -0,0 +1,1238 @@ +‘Rusty’ Rust API +================ + +Rusty is a more native Rust wrapper around the auto-generated bindings +to +`libunit-wasm `__. + +.. code:: rust + + use unit_wasm::rusty::*; + +If using + +.. code:: rust + + uwr_http_hdr_iter(); + +.. code:: rust + + use std::ffi::CStr; + use std::os::raw::c_char; + use std::os::raw::c_void; + +Naming +------ + +You will see references to functions etc starting with *luw\_* or +*LUW\_* and \*uwr\_“. + +**luw/LUW** (libunit-wasm) come from the underlying C library and in the +Rust case are the auto-generated bindings with a few manual additions. + +**uwr** (Unit Wasm Rust aka ‘*rusty*’) is a more Rust native wrapper +ontop of the bindings. + +In *rusty* the luw/LUW API is generally the low level stuff like the +library version macros and the various function handlers where they can +be used as is and there isn’t a real need to create wrappers +specifically for them. + +1. `‘Rusty’ Rust API <#rusty-rust-api>`__ + +- `Naming <#naming>`__ + +2. `Macros <#macros>`__ + +- `Version <#version>`__ +- `String Conversion <#string-conversion>`__ +- `uwr_write_str! <#uwr_write_str>`__ + +3. `Enums <#enums>`__ `luw_http_status_t <#luw_http_status_t>`__ +4. `Function Handlers <#function-handlers>`__ + +- `Optional <#optional>`__ + + - `luw_module_init_handler <#luw_module_init_handler>`__ + - `luw_module_end_handler <#luw_module_end_handler>`__ + - `luw_request_init_handler <#luw_request_init_handler>`__ + - `luw_request_end_handler <#luw_request_end_handler>`__ + - `luw_response_end_handler <#luw_response_end_handler>`__ + +- `Required <#required>`__ + + - `luw_request_handler <#luw_request_handler>`__ + - `luw_free_handler <#luw_free_handler>`__ + - `luw_malloc_handler <#luw_malloc_handler>`__ + +4. `Functions <#functions>`__ + +- `UWR_CTX_INITIALIZER <#uwr_ctx_initializer>`__ +- `uwr_init_ctx <#uwr_init_ctx>`__ +- `uwr_set_req_buf <#uwr_set_req_buf>`__ +- `uwr_get_http_path <#uwr_get_http_path>`__ +- `uwr_get_http_method <#uwr_get_http_method>`__ +- `uwr_get_http_version <#uwr_get_http_version>`__ +- `uwr_get_http_query <#uwr_get_http_query>`__ +- `uwr_get_http_remote <#uwr_get_http_remote>`__ +- `uwr_get_http_local_addr <#uwr_get_http_local_addr>`__ +- `uwr_get_http_local_port <#uwr_get_http_local_port>`__ +- `uwr_get_http_server_name <#uwr_get_http_server_name>`__ +- `uwr_get_http_content <#uwr_get_http_content>`__ +- `uwr_get_http_content_str <#uwr_get_http_content_str>`__ +- `uwr_get_http_content_len <#uwr_get_http_content_len>`__ +- `uwr_get_http_content_sent <#uwr_get_http_content_sent>`__ +- `uwr_get_http_total_content_sent <#uwr_get_http_total_content_sent>`__ +- `uwr_http_is_tls <#uwr_http_is_tls>`__ +- `uwr_http_hdr_iter <#uwr_http_hdr_iter>`__ +- `uwr_http_hdr_get_value <#uwr_http_hdr_get_value>`__ +- `uwr_get_response_data_size <#uwr_get_response_data_size>`__ +- `uwr_mem_write_buf <#uwr_mem_write_buf>`__ +- `uwr_req_buf_append <#uwr_req_buf_append>`__ +- `uwr_req_buf_copy <#uwr_req_buf_copy>`__ +- `uwr_mem_splice_file <#uwr_mem_splice_file>`__ +- `uwr_mem_fill_buf_from_req <#uwr_mem_fill_buf_from_req>`__ +- `uwr_mem_reset <#uwr_mem_reset>`__ +- `uwr_http_set_response_status <#uwr_http_set_response_status>`__ +- `uwr_http_send_response <#uwr_http_send_response>`__ +- `uwr_http_init_headers <#uwr_http_init_headers>`__ +- `uwr_http_add_header <#uwr_http_add_header>`__ +- `uwr_http_add_header_content_type <#uwr_http_add_header_content_type>`__ +- `uwr_http_add_header_content_len <#uwr_http_add_header_content_len>`__ +- `uwr_http_send_headers <#uwr_http_send_headers>`__ +- `uwr_http_response_end <#uwr_http_response_end>`__ +- `uwr_mem_get_init_size <#uwr_mem_get_init_size>`__ + +5. `Misc. Functions <#misc-functions>`__ + +- `uwr_malloc <#uwr_malloc>`__ +- `uwr_free <#uwr_free>`__ + +Macros +------ + +Version +~~~~~~~ + +For the underlying libunit-wasm version. + +.. code:: rust + + pub const LUW_VERSION_MAJOR: i32; + pub const LUW_VERSION_MINOR: i32; + pub const LUW_VERSION_PATCH: i32; + +.. code:: rust + + /* Version number in hex 0xMMmmpp00 */ + pub const LUW_VERSION_NUMBER: i32 = + (LUW_VERSION_MAJOR << 24) | \ + (LUW_VERSION_MINOR << 16) | \ + (LUW_VERSION_PATCH << 8); + +String Conversion +~~~~~~~~~~~~~~~~~ + +.. code:: rust + + C2S!(string); + +Converts a C string into a Rust String + +Main use is internally and in the *uwr_http_hdr_iter()* callback +function, e.g + +.. code:: rust + + 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 { + uwr_write_str!(ctx, "{} = {}\n", C2S!(name), C2S!(value)); + + return true; + } + +Example taken from the +`echo-request `__ +Wasm demo module + +.. code:: rust + + S2C!(formatted string); + +Converts a Rust String, with optional formatting, to a C string. + +Used internally. + +uwr_write_str! +~~~~~~~~~~~~~~ + +.. code:: rust + + uwr_write_str!*ctx, fmt, ...); + +This is essentially a wrapper around +`luw_mem_writep_data() `__ + +It is the main way to write responses back to the client. + +It takes the luw_ctx_t context pointer, a string that will be run +through the +`format!() `__ macro +and any optional arguments. + +Enums +----- + +luw_http_status_t +~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub enum luw_http_status_t { + LUW_HTTP_CONTINUE = 100, + LUW_HTTP_SWITCHING_PROTOCOLS = 101, + + LUW_HTTP_OK = 200, + LUW_HTTP_CREATED = 201, + LUW_HTTP_ACCEPTED = 202, + LUW_HTTP_NO_CONTENT = 204, + + LUW_HTTP_MULTIPLE_CHOICES = 300, + LUW_HTTP_MOVED_PERMANENTLY = 301, + LUW_HTTP_FOUND = 302, + LUW_HTTP_SEE_OTHER = 303, + LUW_HTTP_NOT_MODIFIED = 304, + LUW_HTTP_TEMPORARY_REDIRECT = 307, + LUW_HTTP_PERMANENT_REDIRECT = 308, + + LUW_HTTP_BAD_REQUEST = 400, + LUW_HTTP_UNAUTHORIZED = 401, + LUW_HTTP_FORBIDDEN = 403, + LUW_HTTP_NOT_FOUND = 404, + LUW_HTTP_METHOD_NOT_ALLOWED = 405, + LUW_HTTP_NOT_ACCEPTABLE = 406, + LUW_HTTP_REQUEST_TIMEOUT = 408, + LUW_HTTP_CONFLICT = 409, + LUW_HTTP_GONE = 410, + LUW_HTTP_LENGTH_REQUIRED = 411, + LUW_HTTP_PAYLOAD_TOO_LARGE = 413, + LUW_HTTP_URI_TOO_LONG = 414, + LUW_HTTP_UNSUPPORTED_MEDIA_TYPE = 415, + LUW_HTTP_UPGRADE_REQUIRED = 426, + LUW_HTTP_TOO_MANY_REQUESTS = 429, + LUW_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431, + + /* Proposed by RFC 7725 */ + LUW_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451, + + LUW_HTTP_INTERNAL_SERVER_ERROR = 500, + LUW_HTTP_NOT_IMPLEMENTED = 501, + LUW_HTTP_BAD_GATEWAY = 502, + LUW_HTTP_SERVICE_UNAVAILABLE = 503, + LUW_HTTP_GATEWAY_TIMEOUT = 504, + } + +Function Handlers +----------------- + +These functions are exported from the WebAssembly module and are called +from the WebAssembly runtime (the Unit WebAssembly language module in +this case). + +There are two types of handlers; required & optional. + +luw_request_handler(), luw_malloc_handler() & luw_free_handler() are +required with the rest being optional. + +libunit-wasm includes exports for these handlers and some default +implementations. + +These functions are defined as *weak* symbols and so if a developer +writes their own function of the same name, that will take precedence. + +However, developers are under no obligation to use these and can create +their own with any (valid) names they like. + +Whatever names developers choose, they are specified in the Unit config. + +Required +~~~~~~~~ + +luw_request_handler +^^^^^^^^^^^^^^^^^^^ + +.. code:: rust + + #[no_mangle] + pub extern "C" fn luw_request_handler(addr: *mut u8) -> i32; + +This is called by Unit during a request. It may be called multiple times +for a single HTTP request if there is more request data than the +available memory for host <–> module communications. + +You will need to provide your own implementation of this function. + +It receives the base address of the shared memory. Essentially what is +returned by luw_malloc_handler(). + +It returns an int. This should nearly always be *0*. + +If you wish to indicate a ‘500 Internal Server Error’, for example if +some internal API has failed or an OS level error occurred, then you can +simply return *-1*, *if* you have haven’t already *sent* any response or +headers. + +You can still return 0 *and* set the HTTP response status to 500 using +`uwr_http_set_response_status <#uwr_http_set_response_status>`__. + +luw_malloc_handler +^^^^^^^^^^^^^^^^^^ + +.. code:: rust + + #[no_mangle] + pub extern "C" fn luw_malloc_handler(size: usize) -> u32; + +This is called by Unit when it loads the WebAssembly language module. +This provides the shared memory used for host <–> module communications. + +It receives the desired size of the memory, which is currently +NXT_WASM_MEM_SIZE + NXT_WASM_PAGE_SIZE. + +However calls to luw_mem_get_init_size() will return just +NXT_WASM_MEM_SIZE (which is currently 32MiB). The extra +NXT_WASM_PAGE_SIZE is to cater for structure sizes in the response so +developers can generally assume they have the full NXT_WASM_MEM_SIZE for +their data. + +A default implementation of this function is provided ready for use that +calls malloc(3). + +luw_free_handler +^^^^^^^^^^^^^^^^ + +.. code:: rust + + #[no_mangle] + pub extern "C" fn luw_free_handler(addr: u32); + +This is called by Unit when it shuts down the WebAssembly language +module and free’s the memory previously allocated by +luw_malloc_handler(). + +It receives the address of the memory to free. + +An implementation of this function is provided ready for use that calls +free(3), in which case it receives the address that was previously +returned by luw_malloc_handler(). + +Optional +~~~~~~~~ + +luw_module_init_handler +^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: rust + + #[no_mangle] + pub extern "C" fn luw_module_init_handler(); + +This is called by Unit when it loads the WebAssembly language module. + +A default dummy function is provided. If this handler is not required, +there is no need to specify it in the Unit config. + +luw_module_end_handler +^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: rust + + #[no_mangle] + pub extern "C" fn luw_module_end_handler(); + +This is called by Unit when it shuts down the WebAssembly language +module. + +A default dummy function is provided. If this handler is not required, +there is no need to specify it in the Unit config. + +luw_request_init_handler +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: rust + + #[no_mangle] + pub extern "C" fn luw_request_init_handler(); + +This is called by Unit at the start of nxt_wasm_request_handler(), i.e +at the start of a new request. + +A default dummy function is provided. If this handler is not required, +there is no need to specify it in the Unit config. + +luw_request_end_handler +^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: rust + + #[no_mangle] + pub extern "C" fn luw_request_end_handler(); + +This is called by Unit at the end of nxt_wasm_request_handler(), i.e at +the end of a request. + +A default dummy function is provided. If this handler is not required, +there is no need to specify it in the Unit config. + +luw_response_end_handler +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: rust + + #[no_mangle] + pub extern "C" fn luw_response_end_handler(); + +This is called by Unit after luw_http_response_end() has been called. + +A default dummy function is provided. If this handler is not required, +there is no need to specify it in the Unit config. + +Functions +--------- + +UWR_CTX_INITIALIZER +~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub const fn UWR_CTX_INITIALIZER() -> luw_ctx_t; + +Used to initialise a luw_ctx_t context structure. E.g + +.. code:: rust + + let ctx = &mut UWR_CTX_INITIALIZER(); + +uwr_init_ctx +~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_init_ctx(ctx: *mut luw_ctx_t, addr: *mut u8, offset: usize); + +This function sets up a *luw_ctx_t* context structure, this contains +stuff required all throughout the API. + +**addr** is a pointer to the shared memory as passed into +luw_request_handler(). + +**offset** is where in the shared memory it should start writing the +response. + +A quick word about memory +^^^^^^^^^^^^^^^^^^^^^^^^^ + +The way the Unit WebAssembly language module (the host/runtime) and the +WebAssembly module you want to write (the guest) communicate is via a +chunk of shared memory. + +This shared memory is simply the modules (guest) address space from +which we can allocate a chunk. How this memory is laid out varies on how +the module is built. + +With clang/linker flags of -Wl,–stack-first +-Wl,-z,stack-size=$((8\ *1024*\ 1024)) we get a memory layout something +like + +:: + + |----------------------------------------------------------------------| + | | | | + | <-- Stack | Global Data | Heap --> | + | | | | + |----------------------------------------------------------------------| + 0 0x800000 0x100000000 + + WebAssembly Module Linear Memory / Process Memory Layout + +(The above is assuming *–target=wasm32-wasi*, i.e 32bit) + +A chunk of memory from the heap is allocated at Unit WebAssembly +language module startup. + +We currently use this same chunk of memory for both requests and +responses. This means that depending on what you’re doing, you’ll want +to take a copy of the request (and remember luw_request_handler() may be +called multiple times for a single http request). + +That will be covered in more detail by the next function, +uwr_set_req_buf(). + +Now back to *offset*, it may be convenient to put the response headers +at the beginning of this memory and then put the response after it, +rather than doing the headers and then doing the response as separate +steps, if the headers depends on some aspect of the response, its size +for example and Content-Length. + +Example + +.. code:: rust + + #[no_mangle] + pub extern "C" fn uwr_request_handler(addr: *mut u8) -> i32 { + let ctx = &mut UWR_CTX_INITIALIZER(); + /* ... */ + uwr_init_ctx(ctx, addr, 4096 /* Response offset */); + +uwr_set_req_buf +~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_set_req_buf( + ctx: *mut luw_ctx_t, + buf: *mut *mut u8, + flags: u32, + ) -> i32; + +This function is used to take a copy of the request buffer (as discussed +above). + +This takes a previously initialised (with uwr_init_ctx()) luw_ctx_t. + +**buf** is a buffer where the request data will written. + +**flags** can be some combination (OR’d) of the following + +**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. + +Example + +.. code:: rust + + static mut CTX: luw_ctx_t = UWR_CTX_INITIALIZER(); + + static mut REQUEST_BUF: *mut u8 = null_mut(); + */ ... */ + #[no_mangle] + pub extern "C" fn uwr_request_handler(addr: *mut u8) -> i32 { + let ctx: *mut luw_ctx_t = addr_of_mut!(CTX); + + if unsafe { REQUEST_BUF.is_null() } { + uwr_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. + */ + uwr_set_req_buf( + ctx, + addr_of_mut!(REQUEST_BUF), + LUW_SRB_APPEND | LUW_SRB_ALLOC | LUW_SRB_FULL_SIZE, + ); + } else { + uwr_req_buf_append(ctx, addr); + } + + upload_reflector(ctx); + + return 0; + } + +That example is taken from the `upload-reflector +demo `__ +demo module. For a simpler example see the `echo-request +demo `__ + +uwr_get_http_path +~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_get_http_path(ctx: *const luw_ctx_t) -> &'static str; + +This function returns a pointer to the HTTP request path. + +E.g + +Given a request of + +:: + + http://localhost:8080/echo/?q=a + +this function will return + +:: + + /echo/?q=a + +uwr_get_http_method +~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_get_http_method(ctx: *const luw_ctx_t) -> &'static str; + +This function returns a pointer to the HTTP method. + +E.g + +:: + + GET + +uwr_get_http_version +~~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_get_http_version(ctx: *const luw_ctx_t) -> &'static str; + +This function returns a pointer to the HTTP version. + +E.g + +:: + + 1.1 + +uwr_get_http_query +~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_get_http_query(ctx: *const luw_ctx_t) -> &'static str; + +This function returns a pointer to the query string (empty string for no +query string). + +E.g + +Given a request of + +:: + + http://localhost:8080/echo/?q=a + +this function will return + +:: + + q=a + +uwr_get_http_remote +~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_get_http_remote(ctx: *const luw_ctx_t) -> &'static str; + +This function returns a pointer to the remote/client/peer address. + +E.g + +:: + + 2001:db8::f00 + +uwr_get_http_local_addr +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_get_http_local_addr(ctx: *const luw_ctx_t) -> &'static str; + +This function returns a pointer to the local/server address. + +E.g + +:: + + 2001:db8::1 + +uwr_get_http_local_port +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_get_http_local_port(ctx: *const luw_ctx_t) -> &'static str; + +This function returns a pointer to the local/server port. + +E.g + +:: + + 443 + +uwr_get_http_server_name +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_get_http_server_name(ctx: *const luw_ctx_t) -> &'static str; + +This function returns a pointer to the local/server name. + +E.g + +:: + + www.example.com + +uwr_get_http_content +~~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_get_http_content(ctx: *const luw_ctx_t) -> *const u8; + +This function returns a pointer to the start of the request body. + +uwr_get_http_content_str +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: rsut + + pub fn uwr_get_http_content_str(ctx: *const luw_ctx_t) -> &'static str; + +Same as above but returns a Rust str. + +*Version: 0.2.0* + +uwr_get_http_content_len +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_get_http_content_len(ctx: *const luw_ctx_t) -> u64; + +This function returns the size of the overall content. I.e +Content-Length. + +Prior to version 0.3.0 it returned a usize + +uwr_get_http_content_sent +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_get_http_content_sent(ctx: *const luw_ctx_t) -> usize; + +This function returns the length of the content that was sent to the +WebAssembly module in *this* request. Remember, a single HTTP request +may be split over several calls to luw_request_handler(). + +uwr_get_http_total_content_sent +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_get_http_total_content_sent(ctx: *const luw_ctx_t) -> u64; + +This function returns the total length of the content that was sent to +the WebAssembly module so far. Remember, a single HTTP request may be +split over several calls to luw_request_handler(). + +*Version: 0.2.0* Prior to 0.3.0 it returned a usize + +uwr_http_is_tls +~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_http_is_tls(ctx: *const luw_ctx_t) -> bool; + +This function returns *true* if the connection to Unit was made over +TLS. + +uwr_http_hdr_iter +~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_http_hdr_iter( + ctx: *mut luw_ctx_t, + luw_http_hdr_iter_func: ::std::option::Option< + unsafe extern "C" fn( + ctx: *mut luw_ctx_t, + name: *const c_char, + value: *const c_char, + data: *mut c_void, + ) -> bool, + >, + user_data: *mut c_void, + ); + +This function allows to iterate over the HTTP headers. For each header +it will call the given luw_http_hdr_iter_func() function whose prototype +is + +.. code:: rust + + 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; + +You may call this function whatever you like. For each header it will be +passed the *luw_ctx_t*, the header name, its value and a user specified +pointer if any, can be NULL. + +Returning *true* from this function will cause the iteration process to +continue, returning *false* will terminate it. + +Example + +.. code:: rust + + 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 { + /* Do something with name & value, ignoring data */ + + return true; + } + + /* ... * + + uwr_http_hdr_iter(ctx, Some(hdr_iter_func), null_mut()); + +uwr_http_hdr_get_value +~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_http_hdr_get_value(ctx: *const luw_ctx_t, hdr: &str) -> &'static str; + +Given a HTTP header *hdr* this function will look it up in the request +and return its value if found, otherwise *NULL*. + +The lookup is done case insensitively. + +uwr_get_response_data_size +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_get_response_data_size(ctx: *const luw_ctx_t) -> usize; + +This function returns the size of the response data written to memory. + +uwr_mem_write_buf +~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_mem_write_buf( + ctx: *mut luw_ctx_t, + src: *const u8, + size: usize, + ) -> usize; + +This function just appends *size* bytes from *src* to the response. + +It returns the new size of the response. + +uwr_req_buf_append +~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_req_buf_append(ctx: *mut luw_ctx_t, src: *const u8); + +This function appends the request data contained in *src* to the +previously setup *request_buffer* with uwr_set_req_buf(). + +This function would be used after an initial request to append the data +from subsequent requests to the request_buffer. + +Example + +.. code:: rust + + #[no_mangle] + pub extern "C" fn uwr_request_handler(addr: *mut u8) -> i32 { + let ctx: *mut luw_ctx_t = addr_of_mut!(CTX); + + if unsafe { REQUEST_BUF.is_null() } { + uwr_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. + */ + uwr_set_req_buf( + ctx, + addr_of_mut!(REQUEST_BUF), + LUW_SRB_APPEND | LUW_SRB_ALLOC | LUW_SRB_FULL_SIZE, + ); + } else { + uwr_req_buf_append(ctx, addr); + } + + upload_reflector(ctx); + + return 0; + } + +uwr_req_buf_copy +~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_req_buf_copy(ctx: *mut luw_ctx_t, src: *const u8); + +This function is analogous to +`uwr_req_buf_append <#uwr_req_buf_append>`__ but rather than appending +the request data contained in *src* to the previously setup +*request_buffer* with uwr_set_req_buf(), it simply overwrites what’s +currently there. + +This function could be used to handle large requests/uploads that you +want to save out to disk or some such and can’t buffer it all in memory. + +uwr_mem_splice_file +~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_mem_splice_file(src: *const u8, f: &mut File) -> isize; + +This function write(2)’s the request data directly from the shared +memory (*src*) to the file represented by the given *File* object (*f*). + +This can be used as an alternative to +`uwr_req_buf_copy <#uwr_req_buf_copy>`__ and avoids an extra copying of +the request data. + +Example + +.. code:: rust + + pub extern "C" fn uwr_request_handler(addr: *mut u8) -> i32 { + let ctx: *mut luw_ctx_t = addr_of_mut!(CTX); + let mut f; + let bytes_wrote: isize; + let mut total = unsafe { TOTAL_BYTES_WROTE }; + + if total == 0 { + uwr_init_ctx(ctx, addr, 0); + uwr_set_req_buf( + ctx, + addr_of_mut!(REQUEST_BUF), + LUW_SRB_NONE + ); + + f = File::create("/var/tmp/large-file.dat").unwrap(); + } else { + f = File::options() + .append(true) + .open("/var/tmp/large-file.dat") + .unwrap(); + } + + bytes_wrote = uwr_mem_splice_file(addr, &mut f); + if bytes_wrote == -1 { + return -1; + } + + total += bytes_wrote as u64; + if total == uwr_get_http_content_len(ctx) { + total = 0; + + uwr_http_response_end(); + } + + unsafe { TOTAL_BYTES_WROTE = total }; + + return 0; + } + +uwr_mem_fill_buf_from_req +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_req_buf_append(ctx: *mut luw_ctx_t, src: *const u8); + +This is a convenience function to fill the response buffer with data +from the request buffer. + +*from* is basically the offset in the request_buffer where to start +copying data from. + +Example + +.. code:: rust + + /* ... */ + write_bytes = uwr_mem_fill_buf_from_req(ctx, TOTAL_RESPONSE_SENT); + TOTAL_RESPONSE_SENT += write_bytes; + /* ... */ + +This is taken from the `upload-reflector +demo `__ +demo module. + +In this case we build up a request_buffer on each call of +luw_request_handler(), so TOTAL_RESPONSE_SENT grows each time by how +much data was sent in *that* request. + +Here are are sending data back to the client after each time we receive +it to demonstrate the interleaving of requests and responses from the +WebAssembly module during a single http request. + +This function returns the number of bytes written to the response +buffer. + +uwr_mem_reset +~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_luw_mem_reset(ctx: *mut luw_ctx_t); + +This function resets the response buffer size and the number of response +headers back to 0. + +uwr_http_set_response_status +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_http_set_response_status(status: luw_http_status_t); + +This function is used to set the HTTP response status. It takes one of +the `luw_http_status_t <#luw_http_status_t>`__ enum values. + +It should be called before any calls to *uwr_http_send_response()* or +*uwr_http_send_headers()*. + +If you don’t call this function the response status defaults to ‘200 +OK’. + +If you wish to error out with a ‘500 Internal Server Error’, you don’t +need to call this function. Simply returning *-1* from the +request_handler function will indicate this error. + +E.g + +Send a ‘403 Forbidden’ + +.. code:: rust + + /* ... */ + uwr_http_set_response_status(LUW_HTTP_FORBIDDEN); + uwr_http_send_response(ctx); /* Doesn't require any body */ + uwr_http_response_end(); + /* ... */ + return 0; + +Send a ‘307 Temporary Re-direct’ + +.. code:: rust + + /* ... */ + uwr_http_set_response_status(LUW_HTTP_TEMPORARY_REDIRECT); + + uwr_http_init_headers(ctx, 1, 0); + uwr_http_add_header(ctx, "Location", "https://example.com/"); + uwr_http_send_headers(ctx); + uwr_http_response_end(); + /* ... */ + return 0; + +*Version: 0.3.0* + +uwr_http_send_response +~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_http_send_response(ctx: *const luw_ctx_t); + +This function calls into Unit to send the response buffer back. + +uwr_http_init_headers +~~~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_http_init_headers(ctx: *mut luw_ctx_t, nr: usize, offset: usize); + +This function is used in the preparation of sending back response +headers. + +*nr* is the number of headers we are sending. + +*offset* is the offset into the response buffer where we are placing +these headers. This will usually be 0. + +Example + +.. code:: rust + + uwr_http_init_headers(ctx, 2, 0); + +uwr_http_add_header +~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_http_add_header( + ctx: *mut luw_ctx_t, + name: &str, + value: &str, + ); + +This function is used to add a header to the response. + +*name* is the name of the header. + +*value* is the value of the header. + +Example + +.. code:: rust + + uwr_http_add_header(&ctx, "Content-Type", "text/plain"); + uwr_http_add_header( + ctx, + "Content-Length", + &format!("{}", uwr_get_response_data_size(ctx)), + ); + +uwr_http_add_header_content_type +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_http_add_header_content_type(ctx: *mut luw_ctx_t, ctype: &str); + +A convenience function for setting the ‘Content-Type’ response header. +E.g the above example that adds the *Content-Type* header could be +written as + +.. code:: rust + + uwr_http_add_header_content_type(ctx, "text/plain"); + +*Version: 0.2.0* + +uwr_http_add_header_content_len +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_http_add_header_content_len(ctx: *mut luw_ctx_t); + +A convenience function for setting the ‘Content-Length’ response header. +E.g the above example that adds the *Content-Length* header could be +written as + +.. code:: rust + + uwr_http_add_header_content_len(ctx); + +This function uses +`uwr_get_response_data_size <#uwr_get_response_data_size>`__ internally +to get the size of the response data. + +*Version: 0.2.0* + +uwr_http_send_headers +~~~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_http_send_headers(ctx: *const luw_ctx_t); + +This function calls into Unit and triggers the sending of the response +headers. + +uwr_http_response_end +~~~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_http_response_end(); + +This function calls into Unit and tells it this is the end of the +response which will trigger Unit to send it to the client. + +uwr_mem_get_init_size +~~~~~~~~~~~~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_mem_get_init_size() -> u32; + +This function calls into Unit to get the size of the shared memory. This +is the amount of memory you should assume you have for creating +responses. Remember you can create multiple responses before calling +luw_http_response_end(). + +Misc. Functions +--------------- + +The following functions are convenience wrappers for the Rust bindings +and should **not** be used directly. + +uwr_malloc +~~~~~~~~~~ + +.. code:: rust + + pub fn uwr_malloc(size: u32) -> *mut u8; + +Essentially a straight wrapper for malloc(3). + +uwr_free +~~~~~~~~ + +.. code:: rust + + pub fn uwr_free(ptr: *mut u8); + +Essentially a straight wrapper for free(3). diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index aea287f..0000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,74 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, sex characteristics, gender identity and expression, -level of experience, education, socio-economic status, nationality, personal -appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -- Using welcoming and inclusive language -- Being respectful of differing viewpoints and experiences -- Gracefully accepting constructive criticism -- Focusing on what is best for the community -- Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -- The use of sexualized language or imagery and unwelcome sexual attention or - advances -- Trolling, insulting/derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or electronic - address, without explicit permission -- Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the moderation team at nginx-oss-community@f5.com. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 1.4, -available at - -For answers to common questions about this code of conduct, see - diff --git a/CODE_OF_CONDUCT.rst b/CODE_OF_CONDUCT.rst new file mode 100644 index 0000000..60233d9 --- /dev/null +++ b/CODE_OF_CONDUCT.rst @@ -0,0 +1,88 @@ +Contributor Covenant Code of Conduct +==================================== + +Our Pledge +---------- + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our +project and our community a harassment-free experience for everyone, +regardless of age, body size, disability, ethnicity, sex +characteristics, gender identity and expression, level of experience, +education, socio-economic status, nationality, personal appearance, +race, religion, or sexual identity and orientation. + +Our Standards +------------- + +Examples of behavior that contributes to creating a positive environment +include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual + attention or advances +- Trolling, insulting/derogatory comments, and personal or political + attacks +- Public or private harassment +- Publishing others’ private information, such as a physical or + electronic address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +Our Responsibilities +-------------------- + +Project maintainers are responsible for clarifying the standards of +acceptable behavior and are expected to take appropriate and fair +corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, +or reject comments, commits, code, wiki edits, issues, and other +contributions that are not aligned to this Code of Conduct, or to ban +temporarily or permanently any contributor for other behaviors that they +deem inappropriate, threatening, offensive, or harmful. + +Scope +----- + +This Code of Conduct applies both within project spaces and in public +spaces when an individual is representing the project or its community. +Examples of representing a project or community include using an +official project e-mail address, posting via an official social media +account, or acting as an appointed representative at an online or +offline event. Representation of a project may be further defined and +clarified by project maintainers. + +Enforcement +----------- + +Instances of abusive, harassing, or otherwise unacceptable behavior may +be reported by contacting the moderation team at +nginx-oss-community@f5.com. All complaints will be reviewed and +investigated and will result in a response that is deemed necessary and +appropriate to the circumstances. The project team is obligated to +maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted +separately. + +Project maintainers who do not follow or enforce the Code of Conduct in +good faith may face temporary or permanent repercussions as determined +by other members of the project’s leadership. + +Attribution +----------- + +This Code of Conduct is adapted from the `Contributor +Covenant `__, version 1.4, +available at +https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 90917bf..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,83 +0,0 @@ -# Contributing Guidelines - -The following is a set of guidelines for contributing to unit-wasm. We do -appreciate that you are considering contributing! - -## Table Of Contents - -- [Getting Started](#getting-started) -- [Ask a Question](#ask-a-question) -- [Contributing](#contributing) -- [Git Style Guide](#git-style-guide) - - -## Getting Started - -Check out the [README](README.md). - - -## Ask a Question - -Please open an [issue](https://github.com/nginx/unit-wasm/issues/new) on -GitHub with the label `question`. You can also ask a question on -[Slack](https://nginxcommunity.slack.com) or the NGINX Unit mailing list, -unit@nginx.org (subscribe -[here](https://mailman.nginx.org/mailman3/lists/unit.nginx.org/)). - - -## Contributing - -### Report a Bug - -Ensure the bug was not already reported by searching on GitHub under -[Issues](https://github.com/nginx/unit-wasm/issues). - -If the bug is a potential security vulnerability, please report using our -[security policy](https://unit.nginx.org/troubleshooting/#getting-support). - -To report a non-security bug, open an -[issue](https://github.com/nginx/unit-wasm/issues/new) on GitHub with the -label `bug`. Be sure to include a title and clear description, as much -relevant information as possible, and a code sample or an executable test -case showing the expected behavior that doesn't occur. - - -### Suggest an Enhancement - -To suggest an enhancement, open an -[issue](https://github.com/nginx/unit-wasm/issues/new) on GitHub with the -label `enhancement`. Please do this before implementing a new feature to -discuss the feature first. - - -### Open a Pull Request - -Clone the repo, create a branch, and submit a PR when your changes are tested -and ready for review. Again, if you'd like to implement a new feature, please -consider creating a feature request issue first to start a discussion about -the feature. - - -## Git Style Guide - -- Split your work into multiple commits is necessary. Each commit should make - one logical change. I.e don't mix code re-formatting with a fix in the same - commit. - -- Subject lines should be short (around 50 characters, not a hard rule) and - concisely describe the change. - -- The commit message body should be limited to 72 character lines. - -- You can use subject line prefixes for commits that affect a specific - portion of the code; examples include "libunit-wasm:" and "rust-bindings:". - -- Reference issues and PRs at the end of the commit messages, e.g if the - commit remedies a GitHub issue add a tag like - - Closes: - - If the commit fixes an issue introduced in a previous commit use the "Fixes" - tag to reference it, e.g - - Fixes: abbrev commit id ("Commit subject line") diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 0000000..0cacb11 --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,88 @@ +Contributing Guidelines +======================= + +The following is a set of guidelines for contributing to unit-wasm. We +do appreciate that you are considering contributing! + +Table Of Contents +----------------- + +- `Getting Started <#getting-started>`__ +- `Ask a Question <#ask-a-question>`__ +- `Contributing <#contributing>`__ +- `Git Style Guide <#git-style-guide>`__ + +Getting Started +--------------- + +Check out the `README `__. + +Ask a Question +-------------- + +Please open an `issue `__ +on GitHub with the label ``question``. You can also ask a question on +`Slack `__ or the NGINX Unit mailing +list, unit@nginx.org (subscribe +`here `__). + +Contributing +------------ + +Report a Bug +~~~~~~~~~~~~ + +Ensure the bug was not already reported by searching on GitHub under +`Issues `__. + +If the bug is a potential security vulnerability, please report using +our `security +policy `__. + +To report a non-security bug, open an +`issue `__ on GitHub with +the label ``bug``. Be sure to include a title and clear description, as +much relevant information as possible, and a code sample or an +executable test case showing the expected behavior that doesn’t occur. + +Suggest an Enhancement +~~~~~~~~~~~~~~~~~~~~~~ + +To suggest an enhancement, open an +`issue `__ on GitHub with +the label ``enhancement``. Please do this before implementing a new +feature to discuss the feature first. + +Open a Pull Request +~~~~~~~~~~~~~~~~~~~ + +Clone the repo, create a branch, and submit a PR when your changes are +tested and ready for review. Again, if you’d like to implement a new +feature, please consider creating a feature request issue first to start +a discussion about the feature. + +Git Style Guide +--------------- + +- Split your work into multiple commits is necessary. Each commit + should make one logical change. I.e don’t mix code re-formatting with + a fix in the same commit. + +- Subject lines should be short (around 50 characters, not a hard rule) + and concisely describe the change. + +- The commit message body should be limited to 72 character lines. + +- You can use subject line prefixes for commits that affect a specific + portion of the code; examples include “libunit-wasm:” and + “rust-bindings:”. + +- Reference issues and PRs at the end of the commit messages, e.g if + the commit remedies a GitHub issue add a tag like + + Closes: https://github.com/nginx/unit-wasm/issues/NNN + + If the commit fixes an issue introduced in a previous commit use the + “Fixes” tag to reference it, e.g + + Fixes: abbrev commit id (“Commit subject line”) diff --git a/HOWTO.md b/HOWTO.md deleted file mode 100644 index 7f1cd92..0000000 --- a/HOWTO.md +++ /dev/null @@ -1,210 +0,0 @@ -Trying it out -============= - -For a quick and simple 'hello world' experience, you can use docker with - -```shell -$ make docker -``` - -which will create two images: - - 1. `unit:wasm` (based on the Docker Official image, with Wasm Module) - 2. `unit:demo-wasm` (based on the Wasm image, with demo application) - -Manual build instructions below. - -## Prerequisites and Assumptions - -You will need: - * Modern Linux platform (might work on others, not yet tested). - * Ability to build Unit from source. - If you haven't done this before, please first run through the -[Building From Source how-to guide](https://unit.nginx.org/howto/source/). - * Additional build tools (required for the demo Wasm Module) - - clang - - llvm - - lld - -## Building the Wasm Language Module - -0. Do a test build of Unit from source ([see docs](https://unit.nginx.org/howto/source/)) with this PR/patch applied. The following steps assume you're -starting in the `unit` directory and used `./configure --prefix=$PWD/build`. - -2. Download and extract the Wasmtime C API (newer versions may or may not -work). Notice that we use `$(arch)` to substitute-in the appropriate CPU -architecture. This works for **x86_64** and **aarch64** (ARM) platforms. -``` -wget -O- https://github.com/bytecodealliance/wasmtime/releases/download/v11.0.0/wasmtime-v11.0.0-$(arch)-linux-c-api.tar.xz | tar Jxfv - -``` - -3. Configure the Wasm Language Module for Unit -``` -./configure wasm --include-path=$PWD/wasmtime-v11.0.0-$(arch)-linux-c-api/include \ - --lib-path=$PWD/wasmtime-v11.0.0-$(arch)-linux-c-api/lib --rpath -``` - -4. Build the Wasm Language Module -``` -make -``` - -5. Test that **unitd** Can Load the Language Module - -Run `unitd` in the foreground (attached to the console) to check that Unit -can discover and load the `wasm` Language Module at startup. You should see -console output similar to this: -``` -$ $PWD/build/sbin/unitd --no-daemon --log /dev/stderr -2023/06/15 11:29:31 [info] 1#1 unit 1.31.0 started -2023/06/15 11:29:31 [info] 43#43 discovery started -2023/06/15 11:29:31 [notice] 43#43 module: wasm 0.1 "/path/to/modules/wasm.unit.so" -``` - -## Building the demo application - -From a suitable directory... - -Clone the [unit-wasm](https://github.com/nginx/unit-wasm) repository - -```shell -$ git clone https://github.com/nginx/unit-wasm.git -``` - -Download and extract the wasi-sysroot from the [WASI SDK](https://github.com/WebAssembly/wasi-sdk) - -```shell -wget -O- https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/wasi-sysroot-20.0.tar.gz | tar zxfv - -``` - -Next Compile the C demo Wasm Modules to `.wasm` files. This requires at least -the following; make and clang, llvm, compiler-rt, and lld from LLVM 9.0+ - -```shell -$ cd unit-wasm -$ make WASI_SYSROOT=../wasi-sysroot examples -``` - -If the above fails like - -``` -wasm-ld: error: cannot open /usr/lib/llvm-11/lib/clang/11.0.1/lib/wasi/libclang_rt.builtins-wasm32.a: No such file or directory -clang: error: linker command failed with exit code 1 (use -v to see invocation) -``` -Then you need to download the wasm32 clang runtime and copy it into the -location mentioned in the error message. - -E.g - -In the above case we would untar -*libclang_rt.builtins-wasm32-wasi-20.0.tar.gz* into -*/usr/lib/llvm-11/lib/clang/11.0.1/* - -On Fedora this would be more like */usr/lib64/clang/16/* - -Adjust the tar '-C ...' option accordingly below... - -```shell -wget -O- https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/libclang_rt.builtins-wasm32-wasi-20.0.tar.gz | sudo tar -xvzf - -C /usr/lib/llvm-11/lib/clang/11.0.1 -``` - -With recent enough versions of Clang (that support the -print-runtime-dir -option) you can use the following command (as root) - -```shell -wget -O- https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/libclang_rt.builtins-wasm32-wasi-20.0.tar.gz | tar --strip-components=1 -xvzf - -C $(dirname $(clang -print-runtime-dir)) -``` - -Then try again... - -If everything built OK then you should have the following two WASM modules - -``` -examples/c/luw-echo-request.wasm -examples/c/luw-upload-reflector.wasm -``` - -## Configure Unit to run the demo application - -```json - { - "listeners": { - "[::1]:8080": { - "pass": "routes" - } - }, - - "settings": { - "http": { - "max_body_size": 1073741824 - } - }, - - "routes": [ - { - "match": { - "uri": "/echo*" - }, - "action": { - "pass": "applications/luw-echo-request" - } - }, - { - "match": { - "uri": "/upload*" - }, - "action": { - "pass": "applications/luw-upload-reflector" - } - } - ], - - "applications": { - "luw-echo-request": { - "type": "wasm", - "module": "/path/to/unit-wasm/examples/c/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", - "/foo/bar" - ] - } - }, - "luw-upload-reflector": { - "type": "wasm", - "module": "/path/to/unit-wasm/examples/c/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" - } - } -} - -``` - -Apply the above configuration to the **/config** URI of Unit's Control API. -With the JSON in a file, you can use the CLI to apply it. -``` -cat conf.json | tools/unitc /config -``` - -The following messages should then appear in the Unit log file (or console if -running with `--no-daemon`). -``` -2023/07/26 13:28:14 [info] 182585#182585 "luw-echo-request" prototype started -2023/07/26 13:28:14 [info] 182590#182590 "luw-echo-request" application started -2023/07/26 13:28:14 [info] 182591#182591 "luw-upload-reflector" prototype started -2023/07/26 13:28:14 [info] 182596#182596 "luw-upload-reflector" application started -``` - -Now make a request to the demo application. -``` -curl http://localhost:8080/echo -``` diff --git a/HOWTO.rst b/HOWTO.rst new file mode 100644 index 0000000..5883a5d --- /dev/null +++ b/HOWTO.rst @@ -0,0 +1,223 @@ +Trying it out +============= + +For a quick and simple ‘hello world’ experience, you can use docker with + +.. code:: shell + + $ make docker + +which will create two images: + +1. ``unit:wasm`` (based on the Docker Official image, with Wasm Module) +2. ``unit:demo-wasm`` (based on the Wasm image, with demo application) + +Manual build instructions below. + +Prerequisites and Assumptions +----------------------------- + +You will need: \* Modern Linux platform (might work on others, not yet +tested). \* Ability to build Unit from source. If you haven’t done this +before, please first run through the `Building From Source how-to +guide `__. \* Additional build +tools (required for the demo Wasm Module) - clang - llvm - lld + +Building the Wasm Language Module +--------------------------------- + +0. Do a test build of Unit from source (`see + docs `__) with this PR/patch + applied. The following steps assume you’re starting in the ``unit`` + directory and used ``./configure --prefix=$PWD/build``. + +1. Download and extract the Wasmtime C API (newer versions may or may + not work). Notice that we use ``$(arch)`` to substitute-in the + appropriate CPU architecture. This works for **x86_64** and + **aarch64** (ARM) platforms. + +:: + + wget -O- https://github.com/bytecodealliance/wasmtime/releases/download/v11.0.0/wasmtime-v11.0.0-$(arch)-linux-c-api.tar.xz | tar Jxfv - + +3. Configure the Wasm Language Module for Unit + +:: + + ./configure wasm --include-path=$PWD/wasmtime-v11.0.0-$(arch)-linux-c-api/include \ + --lib-path=$PWD/wasmtime-v11.0.0-$(arch)-linux-c-api/lib --rpath + +4. Build the Wasm Language Module + +:: + + make + +5. Test that **unitd** Can Load the Language Module + +Run ``unitd`` in the foreground (attached to the console) to check that +Unit can discover and load the ``wasm`` Language Module at startup. You +should see console output similar to this: + +:: + + $ $PWD/build/sbin/unitd --no-daemon --log /dev/stderr + 2023/06/15 11:29:31 [info] 1#1 unit 1.31.0 started + 2023/06/15 11:29:31 [info] 43#43 discovery started + 2023/06/15 11:29:31 [notice] 43#43 module: wasm 0.1 "/path/to/modules/wasm.unit.so" + +Building the demo application +----------------------------- + +From a suitable directory… + +Clone the `unit-wasm `__ repository + +.. code:: shell + + $ git clone https://github.com/nginx/unit-wasm.git + +Download and extract the wasi-sysroot from the `WASI +SDK `__ + +.. code:: shell + + wget -O- https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/wasi-sysroot-20.0.tar.gz | tar zxfv - + +Next Compile the C demo Wasm Modules to ``.wasm`` files. This requires +at least the following; make and clang, llvm, compiler-rt, and lld from +LLVM 9.0+ + +.. code:: shell + + $ cd unit-wasm + $ make WASI_SYSROOT=../wasi-sysroot examples + +If the above fails like + +:: + + wasm-ld: error: cannot open /usr/lib/llvm-11/lib/clang/11.0.1/lib/wasi/libclang_rt.builtins-wasm32.a: No such file or directory + clang: error: linker command failed with exit code 1 (use -v to see invocation) + +Then you need to download the wasm32 clang runtime and copy it into the +location mentioned in the error message. + +E.g + +In the above case we would untar +*libclang_rt.builtins-wasm32-wasi-20.0.tar.gz* into +*/usr/lib/llvm-11/lib/clang/11.0.1/* + +On Fedora this would be more like */usr/lib64/clang/16/* + +Adjust the tar ‘-C …’ option accordingly below… + +.. code:: shell + + wget -O- https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/libclang_rt.builtins-wasm32-wasi-20.0.tar.gz | sudo tar -xvzf - -C /usr/lib/llvm-11/lib/clang/11.0.1 + +With recent enough versions of Clang (that support the +-print-runtime-dir option) you can use the following command (as root) + +.. code:: shell + + wget -O- https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/libclang_rt.builtins-wasm32-wasi-20.0.tar.gz | tar --strip-components=1 -xvzf - -C $(dirname $(clang -print-runtime-dir)) + +Then try again… + +If everything built OK then you should have the following two WASM +modules + +:: + + examples/c/luw-echo-request.wasm + examples/c/luw-upload-reflector.wasm + +Configure Unit to run the demo application +------------------------------------------ + +.. code:: json + + { + "listeners": { + "[::1]:8080": { + "pass": "routes" + } + }, + + "settings": { + "http": { + "max_body_size": 1073741824 + } + }, + + "routes": [ + { + "match": { + "uri": "/echo*" + }, + "action": { + "pass": "applications/luw-echo-request" + } + }, + { + "match": { + "uri": "/upload*" + }, + "action": { + "pass": "applications/luw-upload-reflector" + } + } + ], + + "applications": { + "luw-echo-request": { + "type": "wasm", + "module": "/path/to/unit-wasm/examples/c/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", + "/foo/bar" + ] + } + }, + "luw-upload-reflector": { + "type": "wasm", + "module": "/path/to/unit-wasm/examples/c/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" + } + } + } + +Apply the above configuration to the **/config** URI of Unit’s Control +API. With the JSON in a file, you can use the CLI to apply it. + +:: + + cat conf.json | tools/unitc /config + +The following messages should then appear in the Unit log file (or +console if running with ``--no-daemon``). + +:: + + 2023/07/26 13:28:14 [info] 182585#182585 "luw-echo-request" prototype started + 2023/07/26 13:28:14 [info] 182590#182590 "luw-echo-request" application started + 2023/07/26 13:28:14 [info] 182591#182591 "luw-upload-reflector" prototype started + 2023/07/26 13:28:14 [info] 182596#182596 "luw-upload-reflector" application started + +Now make a request to the demo application. + +:: + + curl http://localhost:8080/echo diff --git a/README.md b/README.md deleted file mode 100644 index 76dec73..0000000 --- a/README.md +++ /dev/null @@ -1,490 +0,0 @@ -# C & Rust Library & Examples for Building WebAssembly Modules for NGINX Unit - -This provides a C library (lbunit-wasm) and Rust crates based on that library -to aid in the creation of WebAssembly modules in C and Rust. - -It also has some demo WebAssembly modules written in C and Rust. - -1. [C & Rust Library & Examples for Building WebAssembly Modules for NGINX Unit](#c---rust-library---examples-for-building-webassembly-modules-for-nginx-unit) -2. [Repository Layout](#repository-layout) -3. [Setup a Suitable Environment](#setup-a-suitable-environment) - 1. [Fedora](#fedora) - 2. [Debian / Ubuntu](#debian--ubuntu) -4. [Quickstart in Developing Rust WebAssembly Modules for Unit](#quickstart-in-developing-rust-webassembly-modules-for-unit) -5. [Working With the Repository](#working-with-the-repository) -6. [Using With Unit](#using-with-unit) -7. [Consuming the C Library](#consuming-the-c-library) -8. [License](#license) - -## Repository Layout - -**src/c** contains the main libunit-wasm library. - -**src/rust** contains the rust version of the above. - -**examples/c** contains some demo WebAssembly modules that show both the raw -interface to Unit (\*-raw.c) and also the use of libunit-wasm (luw-\*.c). - -**examples/rust** contains rust versions of the above C demo modules and more. - -**examples/docker** contains docker files for building Unit with WebAssembly -support and the C examples. - -## Setup a Suitable Environment - -To make full use of this repository you will require numerous tools/packages. - -Exactly what you need and how to get it will depend on your Operating System. - -This has been primarily developed and tested on Fedora & Ubuntu Linux. - -### Fedora - -On Fedora make sure you have the following installed - -``` -# dnf install make clang llvm compiler-rt lld wasi-libc-devel \ - wasi-libc-static cargo rust rustfmt rust-std-static \ - rust-std-static-wasm32-unknown-unknown \ - rust-std-static-wasm32-wasi -``` - -One last item you will need is the libclang wasm32-wasi runtime library, this -can be done with - -``` -# wget -O- https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/libclang_rt.builtins-wasm32-wasi-20.0.tar.gz | tar --strip-components=1 -xvzf - -C $(dirname $(clang -print-runtime-dir)) -``` - -This will install the following, path may vary slightly - -``` -/usr/lib64/clang/16/lib/wasi/libclang_rt.builtins-wasm32.a -``` - -The above should also work (perhaps with slight alterations) on recent -CentOS/RHEL etc... - -**NOTE:** If you get a major Clang version update, you may need to repeat -that last task. - -### Debian / Ubuntu - -Install the following as normal - -``` -# apt install wasi-libc make clang llvm lld -``` - -For the rest you will likely need to use [rustup](https://rustup.rs). Caveat -Emptor. - -After you've completed the _rustup_ installation by following the on screen -instructions as yourself (defaults are fine), do the following - -```shell -$ rustup target add wasm32-wasi -``` - -You will also need to grab the wasi-sysroot, this is essentially a C library -targeting WebAssembly (it is based partly on cloudlibc and musl libc) and -is required for building server side WebAssembly modules. - -It's up to you where you put this. - -```shell -$ wget -O- https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/wasi-sysroot-20.0.tar.gz | tar -xzf - -``` - -And finally similarly as Fedora (this requires a recentish clang) - -``` -# wget -O- https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/libclang_rt.builtins-wasm32-wasi-20.0.tar.gz | tar --strip-components=1 -xvzf - -C $(dirname $(clang -print-runtime-dir)) -``` - -## Quickstart in Developing Rust WebAssembly Modules for Unit - -1) Setup a suitable build environment as above. - -2) Create a new rust project - -```shell -$ cargo init --lib my-wasm-example -``` - -3) Add the [unit-wasm crate](https://crates.io/crates/unit-wasm) as dependency - -```shell -$ cd my-wasm-example -$ cargo add unit-wasm -``` - -4) Set the crate type - -```shell -$ printf '\n[lib]\ncrate-type = ["cdylib"]\n' >>Cargo.toml -``` - -5) Create an example application - -To do this you can simply take a copy of our echo-request demo in this -repository - -```shell -$ wget -O src/lib.rs https://raw.githubusercontent.com/nginx/unit-wasm/main/examples/rust/echo-request/src/lib.rs -``` - -6) Build it! - -```shell -$ cargo build --target wasm32-wasi -``` - -You should now have a *target/wasm32-wasi/debug/my_wasm_example.wasm* file -(yes, hyphens will be turned to underscores) - -You can now use this in Unit with the following config - -```JSON -{ - "listeners": { - "[::1]:8888": { - "pass": "applications/my-wasm-example" - } - }, - - "applications": { - "my-wasm-example": { - "type": "wasm", - "module": "/path/to/my-wasm-example/target/wasm32-wasi/debug/my_wasm_example.wasm", - "request_handler": "uwr_request_handler", - "malloc_handler": "luw_malloc_handler", - "free_handler": "luw_free_handler", - "module_init_handler": "uwr_module_init_handler", - "module_end_handler": "uwr_module_end_handler" - } - } -} -``` - -and curl command - -```shell -$ curl http://localhost:8888/ -``` - -7) Start writing your own Wasm modules in Rust! - -To help you get started, you can check out the Rust examples under -[examples/rust](https://github.com/nginx/unit-wasm/tree/main/examples/rust) -and also the -[API-Rust.md](https://github.com/nginx/unit-wasm/blob/main/API-Rust.md) -for an overview of the API. - -## Working With the Repository - -The project uses good old _make(1)_ as the build system. - -Typing _make help_ will show the list of available targets and the various -make variables you can set. E.g - -``` -$ make help -Available Targets: - default / - libunit-wasm - Builds libunit-wasm C library - examples - Builds the above as well as C examples - examples-raw - Builds raw (non libunit-wasm) C examples - rust - Builds the libunit-wasm rust crate - examples-rust _ Builds the above and rust examples - all - Builds all the above - docker - Builds demo docker images - clean - Removes auto generated artifacts - tags - Generate ctags - -Variables: - make CC= - Specify compiler to use - Defaults to clang - make WASI_SYSROOT= - Specify the path to the WASI sysroot - Defaults to autodetected - make V=1 - Enables verbose output - make D=1 - Enables debug builds (-O0) - make E=1 - Enables Werror -``` - -If you have previously followed the steps outlined above in -[Setup a Suitable Environment](#setup-a-suitable-environment) - -You can build the libunit-wasm C library, C example Wasm modules, the Rust -crates that provides Rust bindings for the libunit-wasm and the Rust example -Wasm modules. - -E.g - -```shell -$ make # Build just the C library -$ make examples # Build the C library and C examples -$ make rust # Build the Rust crates -$ make examples-rust # Build the Rust examples -$ make all # Build all the above -``` - -The C and Rust example Wasm modules will be located at - -``` -examples/c/luw-echo-request.wasm -examples/c/luw-upload-reflector.wasm -examples/rust/echo-request/target/wasm32-wasi/debug/rust_echo_request.wasm -examples/rust/hello-world/target/wasm32-wasi/debug/rust_hello_world.wasm -examples/rust/upload-reflector/target/wasm32-wasi/debug/rust_upload_reflector.wasm -``` - -**NOTE:** To build the C library and examples you will need to specify the -wasi-sysroot. - -E.g - -```shell -$ make WASI_SYSROOT=/path/to/wasi-sysroot all -``` - -**However** if you are on Fedora and installed the _wasi-*_ packages listed -above in the [Setup a Suitable Environment](#fedora) then the wasi-sysroot -path will be autodetected and set by the Makefile. You can override the -autodetection by explicitly specifying it as above. - -## Using With Unit - -If you have all the above built, you are now ready to test it out with Unit. - -We won't go into the details of building Unit from source and enabling the Unit -WebAssembly language module here (see the -[HOWTO.md](https://github.com/nginx/unit-wasm/blob/main/HOWTO.md) in the -repository root for more details) but will instead assume you already have a -Unit with the WebAssembly language module already running, perhaps installed -via a package. - -Create the following Unit config (editing the module paths as appropriate) - -```JSON -{ - "listeners": { - "[::1]:8888": { - "pass": "routes" - } - }, - - "settings": { - "http": { - "max_body_size": 8589934592 - } - }, - - "routes": [ - { - "match": { - "uri": "/echo*" - }, - "action": { - "pass": "applications/luw-echo-request" - } - }, - { - "match": { - "uri": "/upload*" - }, - "action": { - "pass": "applications/luw-upload-reflector" - } - }, - { - "match": { - "uri": "/large-upload*" - }, - "action": { - "pass": "applications/large-upload" - } - }, - { - "match": { - "uri": "/rust-echo*" - }, - "action": { - "pass": "applications/rust-echo-request" - } - }, - { - "match": { - "uri": "/rust-upload*" - }, - "action": { - "pass": "applications/rust-upload-reflector" - } - }, - { - "match": { - "uri": "/rust-large-upload*" - }, - "action": { - "pass": "applications/rust-large-upload" - } - }, - { - "match": { - "uri": "/rust-hello-world*" - }, - "action": { - "pass": "applications/rust-hello-world" - } - } - ], - - "applications": { - "luw-echo-request": { - "type": "wasm", - "module": "/path/to/unit-wasm/examples/c/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" - }, - "luw-upload-reflector": { - "type": "wasm", - "module": "/path/to/unit-wasm/examples/c/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" - }, - "large-upload": { - "type": "wasm", - "module": "/path/to/unit-wasm/examples/c/large-upload.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", - "response_end_handler": "luw_response_end_handler", - "access": { - "filesystem": [ - "/var/tmp" - ] - } - }, - "rust-echo-request": { - "type": "wasm", - "module": "/path/to/unit-wasm/examples/rust/echo-request/target/wasm32-wasi/debug/rust_echo_request.wasm", - "request_handler": "uwr_request_handler", - "malloc_handler": "luw_malloc_handler", - "free_handler": "luw_free_handler", - "module_init_handler": "uwr_module_init_handler", - "module_end_handler": "uwr_module_end_handler" - }, - "rust-upload-reflector": { - "type": "wasm", - "module": "/path/to/unit-wasm/examples/rust/upload-reflector/rust_upload_reflector.wasm", - "request_handler": "uwr_request_handler", - "malloc_handler": "luw_malloc_handler", - "free_handler": "luw_free_handler", - "request_end_handler": "uwr_request_end_handler", - "response_end_handler": "uwr_response_end_handler" - }, - "rust-large-upload": { - "type": "wasm", - "module": "/path/to/src/unit-wasm/examples/rust/large-upload/target/wasm32-wasi/debug/rust_large_upload.wasm", - "request_handler": "uwr_request_handler", - "malloc_handler": "luw_malloc_handler", - "free_handler": "luw_free_handler", - "module_init_handler": "uwr_module_init_handler", - "module_end_handler": "uwr_module_end_handler", - "response_end_handler": "uwr_response_end_handler", - "access": { - "filesystem": [ - "/var/tmp" - ] - } - }, - "rust-hello-world": { - "type": "wasm", - "module": "/path/to/unit-wasm/examples/rust/hello-world/target/wasm32-wasi/debug/rust_hello_world.wasm", - "request_handler": "uwr_request_handler", - "malloc_handler": "luw_malloc_handler", - "free_handler": "luw_free_handler" - } - } -} -``` - -Load this config then you should be ready to try it. - -``` -$ curl -X POST -d "Hello World" --cookie "mycookie=hmmm" http://localhost:8888/echo/?q=a - *** Welcome to WebAssembly on Unit! [libunit-wasm (0.1.0/0x00010000)] *** - -[Request Info] -REQUEST_PATH = /echo/?q=a -METHOD = POST -VERSION = HTTP/1.1 -QUERY = q=a -REMOTE = ::1 -LOCAL_ADDR = ::1 -LOCAL_PORT = 8080 -SERVER_NAME = localhost - -[Request Headers] -Host = localhost:8080 -User-Agent = curl/8.0.1 -Accept = */* -Cookie = mycookie=hmmm -Content-Length = 11 -Content-Type = application/x-www-form-urlencoded - -[POST data] -Hello World -``` - -``` -$ curl -v -X POST --data-binary @audio.flac -H "Content-Type: audio/flac" http://localhost:8888/upload-reflector/ -o wasm-test.dat -... -> Content-Type: audio/flac -> Content-Length: 60406273 -... - % Total % Received % Xferd Average Speed Time Time Time Current - Dload Upload Total Spent Left Speed -100 115M 100 57.6M 100 57.6M 47.6M 47.6M 0:00:01 0:00:01 --:--:-- 95.2M -... -< Content-Type: audio/flac -< Content-Length: 60406273 -... -$ sha1sum audio.flac wasm-test.dat -ef5c9c228544b237022584a8ac4612005cd6263e audio.flac -ef5c9c228544b237022584a8ac4612005cd6263e wasm-test.dat -``` - -## Consuming the C Library - -If **unit/unit-wasm.h** and **libunit.a** are installed into standard -include/library directories then - -Include the libunit-wasm header file - -```C -.... -#include -... -``` - -Link against libunit-wasm - -```shell -$ clang ... -o myapp.wasm myapp.c -lunit-wasm -``` - -See [API-C.md](https://github.com/nginx/unit-wasm/blob/main/API-C.md) for an -overview of the API. - -## License - -This project is licensed under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..a13f157 --- /dev/null +++ b/README.rst @@ -0,0 +1,513 @@ +C & Rust Library & Examples for Building WebAssembly Modules for NGINX Unit +=========================================================================== + +This provides a C library (lbunit-wasm) and Rust crates based on that +library to aid in the creation of WebAssembly modules in C and Rust. + +It also has some demo WebAssembly modules written in C and Rust. + +1. `C & Rust Library & Examples for Building WebAssembly Modules for + NGINX + Unit <#c---rust-library---examples-for-building-webassembly-modules-for-nginx-unit>`__ +2. `Repository Layout <#repository-layout>`__ +3. `Setup a Suitable Environment <#setup-a-suitable-environment>`__ + + 1. `Fedora <#fedora>`__ + 2. `Debian / Ubuntu <#debian--ubuntu>`__ + +4. `Quickstart in Developing Rust WebAssembly Modules for + Unit <#quickstart-in-developing-rust-webassembly-modules-for-unit>`__ +5. `Working With the Repository <#working-with-the-repository>`__ +6. `Using With Unit <#using-with-unit>`__ +7. `Consuming the C Library <#consuming-the-c-library>`__ +8. `License <#license>`__ + +Repository Layout +----------------- + +**src/c** contains the main libunit-wasm library. + +**src/rust** contains the rust version of the above. + +**examples/c** contains some demo WebAssembly modules that show both the +raw interface to Unit (\*-raw.c) and also the use of libunit-wasm +(luw-\*.c). + +**examples/rust** contains rust versions of the above C demo modules and +more. + +**examples/docker** contains docker files for building Unit with +WebAssembly support and the C examples. + +Setup a Suitable Environment +---------------------------- + +To make full use of this repository you will require numerous +tools/packages. + +Exactly what you need and how to get it will depend on your Operating +System. + +This has been primarily developed and tested on Fedora & Ubuntu Linux. + +Fedora +~~~~~~ + +On Fedora make sure you have the following installed + +:: + + # dnf install make clang llvm compiler-rt lld wasi-libc-devel \ + wasi-libc-static cargo rust rustfmt rust-std-static \ + rust-std-static-wasm32-unknown-unknown \ + rust-std-static-wasm32-wasi + +One last item you will need is the libclang wasm32-wasi runtime library, +this can be done with + +:: + + # wget -O- https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/libclang_rt.builtins-wasm32-wasi-20.0.tar.gz | tar --strip-components=1 -xvzf - -C $(dirname $(clang -print-runtime-dir)) + +This will install the following, path may vary slightly + +:: + + /usr/lib64/clang/16/lib/wasi/libclang_rt.builtins-wasm32.a + +The above should also work (perhaps with slight alterations) on recent +CentOS/RHEL etc… + +**NOTE:** If you get a major Clang version update, you may need to +repeat that last task. + +Debian / Ubuntu +~~~~~~~~~~~~~~~ + +Install the following as normal + +:: + + # apt install wasi-libc make clang llvm lld + +For the rest you will likely need to use `rustup `__. +Caveat Emptor. + +After you’ve completed the *rustup* installation by following the on +screen instructions as yourself (defaults are fine), do the following + +.. code:: shell + + $ rustup target add wasm32-wasi + +You will also need to grab the wasi-sysroot, this is essentially a C +library targeting WebAssembly (it is based partly on cloudlibc and musl +libc) and is required for building server side WebAssembly modules. + +It’s up to you where you put this. + +.. code:: shell + + $ wget -O- https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/wasi-sysroot-20.0.tar.gz | tar -xzf - + +And finally similarly as Fedora (this requires a recentish clang) + +:: + + # wget -O- https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/libclang_rt.builtins-wasm32-wasi-20.0.tar.gz | tar --strip-components=1 -xvzf - -C $(dirname $(clang -print-runtime-dir)) + +Quickstart in Developing Rust WebAssembly Modules for Unit +---------------------------------------------------------- + +1) Setup a suitable build environment as above. + +2) Create a new rust project + +.. code:: shell + + $ cargo init --lib my-wasm-example + +3) Add the `unit-wasm crate `__ as + dependency + +.. code:: shell + + $ cd my-wasm-example + $ cargo add unit-wasm + +4) Set the crate type + +.. code:: shell + + $ printf '\n[lib]\ncrate-type = ["cdylib"]\n' >>Cargo.toml + +5) Create an example application + +To do this you can simply take a copy of our echo-request demo in this +repository + +.. code:: shell + + $ wget -O src/lib.rs https://raw.githubusercontent.com/nginx/unit-wasm/main/examples/rust/echo-request/src/lib.rs + +6) Build it! + +.. code:: shell + + $ cargo build --target wasm32-wasi + +You should now have a *target/wasm32-wasi/debug/my_wasm_example.wasm* +file (yes, hyphens will be turned to underscores) + +You can now use this in Unit with the following config + +.. code:: json + + { + "listeners": { + "[::1]:8888": { + "pass": "applications/my-wasm-example" + } + }, + + "applications": { + "my-wasm-example": { + "type": "wasm", + "module": "/path/to/my-wasm-example/target/wasm32-wasi/debug/my_wasm_example.wasm", + "request_handler": "uwr_request_handler", + "malloc_handler": "luw_malloc_handler", + "free_handler": "luw_free_handler", + "module_init_handler": "uwr_module_init_handler", + "module_end_handler": "uwr_module_end_handler" + } + } + } + +and curl command + +.. code:: shell + + $ curl http://localhost:8888/ + +7) Start writing your own Wasm modules in Rust! + +To help you get started, you can check out the Rust examples under +`examples/rust `__ +and also the +`API-Rust.md `__ +for an overview of the API. + +Working With the Repository +--------------------------- + +The project uses good old *make(1)* as the build system. + +Typing *make help* will show the list of available targets and the +various make variables you can set. E.g + +:: + + $ make help + Available Targets: + default / + libunit-wasm - Builds libunit-wasm C library + examples - Builds the above as well as C examples + examples-raw - Builds raw (non libunit-wasm) C examples + rust - Builds the libunit-wasm rust crate + examples-rust _ Builds the above and rust examples + all - Builds all the above + docker - Builds demo docker images + clean - Removes auto generated artifacts + tags - Generate ctags + + Variables: + make CC= - Specify compiler to use + Defaults to clang + make WASI_SYSROOT= - Specify the path to the WASI sysroot + Defaults to autodetected + make V=1 - Enables verbose output + make D=1 - Enables debug builds (-O0) + make E=1 - Enables Werror + +If you have previously followed the steps outlined above in `Setup a +Suitable Environment <#setup-a-suitable-environment>`__ + +You can build the libunit-wasm C library, C example Wasm modules, the +Rust crates that provides Rust bindings for the libunit-wasm and the +Rust example Wasm modules. + +E.g + +.. code:: shell + + $ make # Build just the C library + $ make examples # Build the C library and C examples + $ make rust # Build the Rust crates + $ make examples-rust # Build the Rust examples + $ make all # Build all the above + +The C and Rust example Wasm modules will be located at + +:: + + examples/c/luw-echo-request.wasm + examples/c/luw-upload-reflector.wasm + examples/rust/echo-request/target/wasm32-wasi/debug/rust_echo_request.wasm + examples/rust/hello-world/target/wasm32-wasi/debug/rust_hello_world.wasm + examples/rust/upload-reflector/target/wasm32-wasi/debug/rust_upload_reflector.wasm + +**NOTE:** To build the C library and examples you will need to specify +the wasi-sysroot. + +E.g + +.. code:: shell + + $ make WASI_SYSROOT=/path/to/wasi-sysroot all + +**However** if you are on Fedora and installed the \_wasi-\*\_ packages +listed above in the `Setup a Suitable Environment <#fedora>`__ then the +wasi-sysroot path will be autodetected and set by the Makefile. You can +override the autodetection by explicitly specifying it as above. + +Using With Unit +--------------- + +If you have all the above built, you are now ready to test it out with +Unit. + +We won’t go into the details of building Unit from source and enabling +the Unit WebAssembly language module here (see the +`HOWTO.md `__ in +the repository root for more details) but will instead assume you +already have a Unit with the WebAssembly language module already +running, perhaps installed via a package. + +Create the following Unit config (editing the module paths as +appropriate) + +.. code:: json + + { + "listeners": { + "[::1]:8888": { + "pass": "routes" + } + }, + + "settings": { + "http": { + "max_body_size": 8589934592 + } + }, + + "routes": [ + { + "match": { + "uri": "/echo*" + }, + "action": { + "pass": "applications/luw-echo-request" + } + }, + { + "match": { + "uri": "/upload*" + }, + "action": { + "pass": "applications/luw-upload-reflector" + } + }, + { + "match": { + "uri": "/large-upload*" + }, + "action": { + "pass": "applications/large-upload" + } + }, + { + "match": { + "uri": "/rust-echo*" + }, + "action": { + "pass": "applications/rust-echo-request" + } + }, + { + "match": { + "uri": "/rust-upload*" + }, + "action": { + "pass": "applications/rust-upload-reflector" + } + }, + { + "match": { + "uri": "/rust-large-upload*" + }, + "action": { + "pass": "applications/rust-large-upload" + } + }, + { + "match": { + "uri": "/rust-hello-world*" + }, + "action": { + "pass": "applications/rust-hello-world" + } + } + ], + + "applications": { + "luw-echo-request": { + "type": "wasm", + "module": "/path/to/unit-wasm/examples/c/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" + }, + "luw-upload-reflector": { + "type": "wasm", + "module": "/path/to/unit-wasm/examples/c/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" + }, + "large-upload": { + "type": "wasm", + "module": "/path/to/unit-wasm/examples/c/large-upload.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", + "response_end_handler": "luw_response_end_handler", + "access": { + "filesystem": [ + "/var/tmp" + ] + } + }, + "rust-echo-request": { + "type": "wasm", + "module": "/path/to/unit-wasm/examples/rust/echo-request/target/wasm32-wasi/debug/rust_echo_request.wasm", + "request_handler": "uwr_request_handler", + "malloc_handler": "luw_malloc_handler", + "free_handler": "luw_free_handler", + "module_init_handler": "uwr_module_init_handler", + "module_end_handler": "uwr_module_end_handler" + }, + "rust-upload-reflector": { + "type": "wasm", + "module": "/path/to/unit-wasm/examples/rust/upload-reflector/rust_upload_reflector.wasm", + "request_handler": "uwr_request_handler", + "malloc_handler": "luw_malloc_handler", + "free_handler": "luw_free_handler", + "request_end_handler": "uwr_request_end_handler", + "response_end_handler": "uwr_response_end_handler" + }, + "rust-large-upload": { + "type": "wasm", + "module": "/path/to/src/unit-wasm/examples/rust/large-upload/target/wasm32-wasi/debug/rust_large_upload.wasm", + "request_handler": "uwr_request_handler", + "malloc_handler": "luw_malloc_handler", + "free_handler": "luw_free_handler", + "module_init_handler": "uwr_module_init_handler", + "module_end_handler": "uwr_module_end_handler", + "response_end_handler": "uwr_response_end_handler", + "access": { + "filesystem": [ + "/var/tmp" + ] + } + }, + "rust-hello-world": { + "type": "wasm", + "module": "/path/to/unit-wasm/examples/rust/hello-world/target/wasm32-wasi/debug/rust_hello_world.wasm", + "request_handler": "uwr_request_handler", + "malloc_handler": "luw_malloc_handler", + "free_handler": "luw_free_handler" + } + } + } + +Load this config then you should be ready to try it. + +:: + + $ curl -X POST -d "Hello World" --cookie "mycookie=hmmm" http://localhost:8888/echo/?q=a + *** Welcome to WebAssembly on Unit! [libunit-wasm (0.1.0/0x00010000)] *** + + [Request Info] + REQUEST_PATH = /echo/?q=a + METHOD = POST + VERSION = HTTP/1.1 + QUERY = q=a + REMOTE = ::1 + LOCAL_ADDR = ::1 + LOCAL_PORT = 8080 + SERVER_NAME = localhost + + [Request Headers] + Host = localhost:8080 + User-Agent = curl/8.0.1 + Accept = */* + Cookie = mycookie=hmmm + Content-Length = 11 + Content-Type = application/x-www-form-urlencoded + + [POST data] + Hello World + +:: + + $ curl -v -X POST --data-binary @audio.flac -H "Content-Type: audio/flac" http://localhost:8888/upload-reflector/ -o wasm-test.dat + ... + > Content-Type: audio/flac + > Content-Length: 60406273 + ... + % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed + 100 115M 100 57.6M 100 57.6M 47.6M 47.6M 0:00:01 0:00:01 --:--:-- 95.2M + ... + < Content-Type: audio/flac + < Content-Length: 60406273 + ... + $ sha1sum audio.flac wasm-test.dat + ef5c9c228544b237022584a8ac4612005cd6263e audio.flac + ef5c9c228544b237022584a8ac4612005cd6263e wasm-test.dat + +Consuming the C Library +----------------------- + +If **unit/unit-wasm.h** and **libunit.a** are installed into standard +include/library directories then + +Include the libunit-wasm header file + +.. code:: c + + .... + #include + ... + +Link against libunit-wasm + +.. code:: shell + + $ clang ... -o myapp.wasm myapp.c -lunit-wasm + +See `API-C.md `__ +for an overview of the API. + +License +------- + +This project is licensed under the `Apache License +2.0 `__.