summaryrefslogblamecommitdiffhomepage
path: root/examples/c/upload-reflector-raw.c
blob: 3da4f8d46a13920d857e8d431169be83f1f0aa9a (plain) (tree)






























































































































































































































                                                                                          
/* SPDX-License-Identifier: Apache-2.0 */

/*
 * Copyright (C) Andrew Clayton
 * Copyright (C) F5, Inc.
 */

/*
 * upload-reflector-raw.c - Raw example of writing a WASM module for use with
 *			    Unit
 *
 * Download the wasi-sysroot tarball from https://github.com/WebAssembly/wasi-sdk/releases
 */

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "unit-wasm-raw.h"

static size_t total_response_sent;

static u8 *request_buf;

__attribute__((import_module("env"), import_name("nxt_wasm_get_init_mem_size")))
u32 nxt_wasm_get_init_mem_size(void);
__attribute__((import_module("env"), import_name("nxt_wasm_response_end")))
void nxt_wasm_response_end(void);
__attribute__((import_module("env"), import_name("nxt_wasm_send_response")))
void nxt_wasm_send_response(u32 offset);

__attribute__((export_name("wasm_response_end_handler")))
void wasm_response_end_handler(void)
{
	total_response_sent = 0;
}

__attribute__((export_name("wasm_request_end_handler")))
void wasm_request_end_handler(void)
{
	if (!request_buf)
		return;

	free(request_buf);
	request_buf = NULL;
}

__attribute__((export_name("wasm_free_handler")))
void wasm_free_handler(u32 addr)
{
	free((void *)addr);
}

__attribute__((export_name("wasm_malloc_handler")))
u32 wasm_malloc_handler(size_t size)
{
	return (u32)malloc(size);
}

static int upload_reflector(u8 *addr)
{
	size_t mem_size = nxt_wasm_get_init_mem_size();
	size_t rsize = sizeof(struct resp);
	size_t write_bytes;
	struct req *req;
	struct resp *resp;

	printf("==[WASM RESP]== %s:\n", __func__);

	resp = (struct resp *)addr;
	req = (struct req *)request_buf;

	printf("==[WASM RESP]== resp@%p\n", resp);
	printf("==[WASM RESP]== req@%p\n", req);
	printf("==[WASM RESP]== req->content_len    : %u\n", req->content_len);

	resp = (struct resp *)addr;

	/* Send headers */
	if (total_response_sent == 0) {
		const char *field;
		struct hdr_field *f;
		struct hdr_field *f_end;
		char ct[256];

		/* Try to set the Content-Type */
		f_end = req->fields + req->nr_fields;
		for (f = req->fields; f < f_end; f++) {
			field = (const char *)(u8 *)req + f->name_offs;

			if (strncasecmp(field, "Content-Type", 12) == 0) {
				snprintf(ct, sizeof(ct), "%.*s", f->value_len,
					 (u8 *)req + f->value_offs);
				break;
			}

			field = NULL;
		}
		if (!field)
			sprintf(ct, "application/octet-stream");

		send_headers(addr, ct, req->content_len);
	}

	write_bytes = req->content_sent;
	if (write_bytes > mem_size - rsize)
		write_bytes = mem_size - rsize;

	printf("==[WASM RESP]== write_bytes         : %lu\n", write_bytes);
	printf("==[WASM RESP]== req->content_len    : %u\n", req->content_len);
	printf("==[WASM RESP]== total_response_sent : %lu\n",
	       total_response_sent);

	printf("==[WASM RESP]== Copying (%lu) bytes of data from [%p+%lx] to "
	       "[%p]\n", write_bytes, req,
	       req->content_offs + total_response_sent, resp->data);
	memcpy(resp->data,
	       (u8 *)req + req->content_offs + total_response_sent,
	       write_bytes);

	total_response_sent += write_bytes;
	resp->size = write_bytes;
	printf("==[WASM RESP]== resp->size          : %u\n", resp->size);

	nxt_wasm_send_response(0);

	if (total_response_sent == req->content_len) {
		printf("==[WASM RESP]== All data sent. Cleaning up...\n");
		total_response_sent = 0;

		free(request_buf);
		request_buf = NULL;

		/* Tell Unit no more data to send */
		nxt_wasm_response_end();
	}

	return 0;
}

__attribute__((export_name("wasm_request_handler")))
int wasm_request_handler(u8 *addr)
{
	struct req *req = (struct req *)addr;
	struct req *rb = (struct req *)request_buf;

	printf("==[WASM REQ]== %s:\n", __func__);

	/*
	 * This function _may_ be called multiple times during a single
	 * request if there is a large amount of data to transfer.
	 *
	 * Some useful request meta data:
	 *
	 * req->content_len contains the overall size of the POST/PUT
	 * data.
	 * req->content_sent shows how much of the body content has been
	 * in _this_ request.
	 * req->total_content_sent shows how much of it has been sent in
	 * total.
	 * req->content_offs is the offset in the passed in memory where
	 * the body content starts.
	 *
	 * For new requests req->request_size shows the total size of
	 * _this_ request, incl the req structure itself.
	 * For continuation requests, req->request_size is just the amount
	 * of new content, i.e req->content_sent
	 *
	 * When req->content_len == req->total_content_sent, that's the end
	 * of that request.
	 */

	if (!request_buf) {
		/*
		 * 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.
		 */
		printf("==[WASM REQ]== malloc(%u)\n",
		       req->content_offs + req->content_len);
		request_buf = malloc(req->content_offs + req->content_len);

		/*
		 * Regardless of how much memory we allocated above, here
		 * we only want to copy the amount of data we actually
		 * received in this request.
		 */
		printf("==[WASM REQ]== req->request_size : %u\n",
		       req->request_size);
		memcpy(request_buf, addr, req->request_size);

		rb = (struct req *)request_buf;
		printf("==[WASM REQ]== rb@%p\n", rb);
		printf("==[WASM REQ]== request_buf@%p\n", request_buf);
		printf("==[WASM REQ]== rb->content_offs : %u\n",
		       rb->content_offs);
		printf("==[WASM REQ]== rb->content_len  : %u\n",
		       rb->content_len);
		printf("==[WASM REQ]== rb->content_sent : %u\n",
		       rb->content_sent);
		printf("==[WASM REQ]== rb->request_size : %u\n",
		       rb->request_size);
	} else {
		memcpy(request_buf + rb->request_size, addr + req->content_offs,
		       req->request_size);

		printf("==[WASM REQ +]== req->content_offs : %u\n",
		       req->content_offs);
		printf("==[WASM REQ +]== req->content_sent : %u\n",
		       req->content_sent);
		printf("==[WASM REQ +]== req->request_size : %u\n",
		       req->request_size);

		rb->content_sent = req->content_sent;
		rb->total_content_sent = req->total_content_sent;
	}

	upload_reflector(addr);

	return 0;
}