Skip to content

Instantly share code, notes, and snippets.

@ac000
Last active December 1, 2023 22:17
Show Gist options
  • Save ac000/906707af37b42cdcc9d3cc4c2715da99 to your computer and use it in GitHub Desktop.
Save ac000/906707af37b42cdcc9d3cc4c2715da99 to your computer and use it in GitHub Desktop.
libunit-wasm vs wasi-http
This will compare the libunit-wasm luw-echo-request module with the same
implemented using wasi-http and built as a component.
You will notice we don't get all the same information with wasi-http.
I also hasten to add that I have somewhat sanitised the wasi-http API (which of
course is an autogenerated monstrosity) by removing `_0_2_0_rc_2023_11_10` and
`_0_2_0_RC_2023_11_10` from all the `wasi_` and `WASI_` function/type names.
I have also removed _all_ the error checking from the below pasted wasi-http
version.
The wasi-http component is running under the wasmtime CLI.
(I now have a version of this (using the wasi-http wit files from Alex's
example) running under the new language module)
I'll let you decide which API is nicer!
First up is the wasi-http version, followed by the libunit-wasm version.
## wasi-http
$ curl -X POST -d "Hello World" --cookie "mycookie=hmmm" localhost:8080/echo/?q=a
*** Welcome to WebAssembly with wasi-http / C ***
[Request Info]
REQUEST_PATH = /echo/?q=a
METHOD = POST
QUERY = q=a
[Request Headers]
host = localhost:8080
user-agent = curl/8.2.1
accept = */*
cookie = mycookie=hmmm
content-length = 11
content-type = application/x-www-form-urlencoded
[POST data]
Hello World
## libunit-wasm
$ curl -X POST -d "Hello World" --cookie "mycookie=hmmm" localhost:8080/echo/?q=a
*** Welcome to WebAssembly on Unit! [libunit-wasm (0.3.0/0x00030000)] ***
[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
/* SPDX-License-Identifier: Apache-2.0 */
/*
* component.c - Example of writing a wasi-http Wasm component
*
* Copyright (C) Andrew Clayton
* Copyright (C) F5, Inc.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "proxy.h"
static const struct {
const char *method;
} http_method_map[] = {
[WASI_HTTP_TYPES_METHOD_GET] = { "GET" },
[WASI_HTTP_TYPES_METHOD_HEAD] = { "HEAD" },
[WASI_HTTP_TYPES_METHOD_POST] = { "POST" },
[WASI_HTTP_TYPES_METHOD_PUT] = { "PUT" },
[WASI_HTTP_TYPES_METHOD_DELETE] = { "DELETE" },
[WASI_HTTP_TYPES_METHOD_CONNECT] = { "CONNECT" },
[WASI_HTTP_TYPES_METHOD_OPTIONS] = { "OPTIONS" },
[WASI_HTTP_TYPES_METHOD_TRACE] = { "TRACE" },
[WASI_HTTP_TYPES_METHOD_PATCH] = { "PATCH" },
[WASI_HTTP_TYPES_METHOD_OTHER] = { "OTHER" }
};
void exports_wasi_http_incoming_handler_handle(
exports_wasi_http_incoming_handler_own_incoming_request_t request,
exports_wasi_http_incoming_handler_own_response_outparam_t response_out)
{
FILE *out;
size_t size;
char *out_ptr;
char *ptr;
char clen[32];
wasi_http_types_borrow_incoming_request_t b_req;
wasi_http_types_own_headers_t hdrs;
wasi_http_types_borrow_fields_t b_hdrs;
wasi_http_types_own_incoming_body_t r_body;
wasi_http_types_borrow_incoming_body_t b_r_body;
wasi_http_types_own_outgoing_response_t resp;
wasi_http_types_borrow_outgoing_response_t b_resp;
wasi_http_types_own_fields_t fields;
wasi_http_types_borrow_fields_t b_fields;
wasi_http_types_result_own_outgoing_response_error_code_t rerr = { };
wasi_http_types_own_outgoing_body_t body;
wasi_http_types_borrow_outgoing_body_t b_body;
wasi_io_streams_own_output_stream_t out_stream;
wasi_io_streams_borrow_output_stream_t b_out_stream;
wasi_io_streams_list_u8_t stream_data;
wasi_io_streams_stream_error_t stream_err;
wasi_http_types_header_error_t hdr_err;
wasi_http_types_field_key_t key;
wasi_http_types_field_value_t value;
wasi_http_types_method_t method;
wasi_http_types_list_tuple2_field_key_field_value_t fvk;
wasi_http_types_own_input_stream_t in_stream;
wasi_io_streams_borrow_input_stream_t b_in_stream;
wasi_io_streams_list_u8_t data;
wasi_io_streams_stream_error_t in_stream_err;
proxy_string_t prstr;
b_req = wasi_http_types_borrow_incoming_request(request);
out = open_memstream(&out_ptr, &size);
#define BUF_ADD(fmt, ...) \
fprintf(out, fmt, ##__VA_ARGS__);
BUF_ADD("*** Welcome to WebAssembly with wasi-http / C ***\n\n");
BUF_ADD("[Request Info]\n");
wasi_http_types_method_incoming_request_path_with_query(b_req, &prstr);
BUF_ADD("REQUEST_PATH = %.*s\n", (int)prstr.len, prstr.ptr);
wasi_http_types_method_incoming_request_method(b_req, &method);
BUF_ADD("METHOD = %s\n", http_method_map[method.tag].method);
ptr = memchr(prstr.ptr, '?', prstr.len);
BUF_ADD("QUERY = %.*s\n",
ptr ? (int)(((char *)(prstr.ptr + prstr.len)) - ptr - 1) : 0,
ptr ? ptr + 1 : "");
BUF_ADD("\n[Request Headers]\n");
hdrs = wasi_http_types_method_incoming_request_headers(b_req);
b_hdrs = wasi_http_types_borrow_fields(hdrs);
wasi_http_types_method_fields_entries(b_hdrs, &fvk);
for (size_t i = 0; i < fvk.len; i++)
BUF_ADD("%.*s = %.*s\n",
(int)fvk.ptr[i].f0.len, fvk.ptr[i].f0.ptr,
(int)fvk.ptr[i].f1.len, fvk.ptr[i].f1.ptr);
wasi_http_types_list_tuple2_field_key_field_value_free(&fvk);
wasi_http_types_method_incoming_request_consume(b_req, &r_body);
b_r_body = wasi_http_types_borrow_incoming_body(r_body);
wasi_http_types_method_incoming_body_stream(b_r_body, &in_stream);
b_in_stream = wasi_io_streams_borrow_input_stream(in_stream);
ok = wasi_io_streams_method_input_stream_blocking_read(b_in_stream,
8 * 1024*1024,
&data,
&in_stream_err);
if (method.tag == WASI_HTTP_TYPES_METHOD_POST ||
method.tag == WASI_HTTP_TYPES_METHOD_PUT) {
BUF_ADD("\n[%s data]\n",
http_method_map[method.tag].method);
BUF_ADD("%.*s\n", (int)data.len, data.ptr);
}
fclose(out);
fields = wasi_http_types_constructor_fields();
b_fields = wasi_http_types_borrow_fields(fields);
proxy_string_set((proxy_string_t *)&key, "Content-Length");
sprintf(clen, "%zu", size);
proxy_string_set((proxy_string_t *)&value, clen);
wasi_http_types_method_fields_append(b_fields, &key, &value,
&hdr_err);
resp = wasi_http_types_constructor_outgoing_response(fields);
b_resp = wasi_http_types_borrow_outgoing_response(resp);
wasi_http_types_method_outgoing_response_body(b_resp, &body);
b_body = wasi_http_types_borrow_outgoing_body(body);
wasi_http_types_method_outgoing_body_write(b_body, &out_stream);
b_out_stream = wasi_io_streams_borrow_output_stream(out_stream);
stream_data.len = size;
stream_data.ptr = (uint8_t *)out_ptr;
ok = wasi_io_streams_method_output_stream_blocking_write_and_flush(
b_out_stream,
&stream_data,
&stream_err);
free(out_ptr);
wasi_http_types_static_response_outparam_set(response_out, &rerr);
}
/* SPDX-License-Identifier: Apache-2.0 */
/*
* examples/c/luw-echo-request.c - Example of writing a WASM module for use
* with Unit using libunit-wasm
*
* Copyright (C) Andrew Clayton
* Copyright (C) F5, Inc.
*/
#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "unit/unit-wasm.h"
static u8 *request_buf;
__luw_export_name("luw_module_end_handler")
void luw_module_end_handler(void)
{
free(request_buf);
}
__luw_export_name("luw_module_init_handler")
void luw_module_init_handler(void)
{
request_buf = malloc(luw_mem_get_init_size());
}
static bool hdr_iter_func(luw_ctx_t *ctx, const char *name, const char *value,
void *user_data __luw_unused)
{
luw_mem_writep(ctx, "%s = %s\n", name, value);
return true;
}
__luw_export_name("luw_request_handler")
int luw_request_handler(u8 *addr)
{
luw_ctx_t ctx;
char clen[32];
const char *method;
luw_init_ctx(&ctx, addr, 4096 /* Response offset */);
/* Take a copy of the request and use that */
luw_set_req_buf(&ctx, &request_buf, LUW_SRB_NONE);
#define BUF_ADD(fmt, member) \
luw_mem_writep(&ctx, fmt, luw_get_http_##member(&ctx));
luw_mem_writep(&ctx,
" *** Welcome to WebAssembly on Unit! "
"[libunit-wasm (%d.%d.%d/%#0.8x)] ***\n\n",
LUW_VERSION_MAJOR, LUW_VERSION_MINOR, LUW_VERSION_PATCH,
LUW_VERSION_NUMBER);
luw_mem_writep(&ctx, "[Request Info]\n");
BUF_ADD("REQUEST_PATH = %s\n", path);
BUF_ADD("METHOD = %s\n", method);
BUF_ADD("VERSION = %s\n", version);
BUF_ADD("QUERY = %s\n", query);
BUF_ADD("REMOTE = %s\n", remote);
BUF_ADD("LOCAL_ADDR = %s\n", local_addr);
BUF_ADD("LOCAL_PORT = %s\n", local_port);
BUF_ADD("SERVER_NAME = %s\n", server_name);
luw_mem_writep(&ctx, "\n[Request Headers]\n");
luw_http_hdr_iter(&ctx, hdr_iter_func, NULL);
method = luw_get_http_method(&ctx);
if (memcmp(method, "POST", strlen(method)) == 0 ||
memcmp(method, "PUT", strlen(method)) == 0) {
luw_mem_writep(&ctx, "\n[%s data]\n", method);
luw_mem_writep_data(&ctx, luw_get_http_content(&ctx),
luw_get_http_content_len(&ctx));
luw_mem_writep(&ctx, "\n");
}
luw_http_init_headers(&ctx, 2, 0);
snprintf(clen, sizeof(clen), "%lu", luw_get_response_data_size(&ctx));
luw_http_add_header(&ctx, "Content-Type", "text/plain");
luw_http_add_header(&ctx, "Content-Length", clen);
luw_http_send_headers(&ctx);
luw_http_send_response(&ctx);
/* Tell Unit no more data to send */
luw_http_response_end();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment