1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
|
/* SPDX-License-Identifier: Apache-2.0 */
/*
* Copyright (C) Andrew Clayton
* Copyright (C) F5, Inc.
*/
#define _GNU_SOURCE
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include "unit/unit-wasm.h"
/*
* Some handlers are required some are optional.
*
* They are defined as _weak_ symbols so they can be overridden
* in the module.
*/
/* Optional Handlers */
__attribute__((export_name("luw_module_init_handler"), __weak__))
void luw_module_init_handler(void)
{
}
__attribute__((export_name("luw_module_end_handler"), __weak__))
void luw_module_end_handler(void)
{
}
__attribute__((export_name("luw_request_init_handler"), __weak__))
void luw_request_init_handler(void)
{
}
__attribute__((export_name("luw_request_end_handler"), __weak__))
void luw_request_end_handler(void)
{
}
__attribute__((export_name("luw_response_end_handler"), __weak__))
void luw_response_end_handler(void)
{
}
/* Required Handlers */
__attribute__((export_name("luw_request_handler"), __weak__))
int luw_request_handler(u8 *addr)
{
(void)addr;
return 0;
}
__attribute__((export_name("luw_free_handler"), __weak__))
void luw_free_handler(u32 addr)
{
free((void *)addr);
}
__attribute__((export_name("luw_malloc_handler"), __weak__))
u32 luw_malloc_handler(size_t size)
{
return (u32)malloc(size);
}
void luw_init_ctx(luw_ctx_t *ctx, u8 *addr, size_t offset)
{
*ctx = (luw_ctx_t){ };
ctx->addr = addr;
ctx->req = (struct luw_req *)addr;
ctx->resp = (struct luw_resp *)(addr + offset);
ctx->mem = ctx->resp->data;
ctx->resp_offset = offset;
ctx->resp->size = 0;
ctx->resp_hdr->nr_fields = 0;
}
/*
* Allows to set an external buffer to be used as a copy for
* the request.
*
* The flags dictate a few behaviours
*
* 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.
*/
int luw_set_req_buf(luw_ctx_t *ctx, u8 **buf, unsigned long flags)
{
size_t alloc_size;
size_t copy_bytes;
/* Check for unknown flags */
if (flags & ~LUW_SRB_FLAGS_ALL) {
errno = EINVAL;
return -1;
}
/* Check for invalid combinations of flags */
if (flags & LUW_SRB_FULL_SIZE && !(flags & LUW_SRB_ALLOC)) {
errno = EINVAL;
return -1;
}
alloc_size = copy_bytes = ctx->req->request_size;
if (flags & LUW_SRB_FULL_SIZE)
alloc_size = ctx->req->content_off + ctx->req->content_len;
if (flags & LUW_SRB_ALLOC) {
*buf = malloc(alloc_size);
if (!*buf)
return -1;
}
memcpy(*buf, ctx->addr, copy_bytes);
ctx->req_buf = *buf;
ctx->req = (struct luw_req *)ctx->req_buf;
ctx->reqp = ctx->req_buf;
if (flags & LUW_SRB_APPEND)
ctx->reqp = ctx->req_buf + copy_bytes;
return 0;
}
const char *luw_get_http_path(const luw_ctx_t *ctx)
{
return (const char *)ctx->req + ctx->req->path_off;
}
const char *luw_get_http_method(const luw_ctx_t *ctx)
{
return (const char *)ctx->req + ctx->req->method_off;
}
const char *luw_get_http_version(const luw_ctx_t *ctx)
{
return (const char *)ctx->req + ctx->req->version_off;
}
const char *luw_get_http_query(const luw_ctx_t *ctx)
{
return (const char *)ctx->req + ctx->req->query_off;
}
const char *luw_get_http_remote(const luw_ctx_t *ctx)
{
return (const char *)ctx->req + ctx->req->remote_off;
}
const char *luw_get_http_local_addr(const luw_ctx_t *ctx)
{
return (const char *)ctx->req + ctx->req->local_addr_off;
}
const char *luw_get_http_local_port(const luw_ctx_t *ctx)
{
return (const char *)ctx->req + ctx->req->local_port_off;
}
const char *luw_get_http_server_name(const luw_ctx_t *ctx)
{
return (const char *)ctx->req + ctx->req->server_name_off;
}
const u8 *luw_get_http_content(const luw_ctx_t *ctx)
{
return (u8 *)ctx->req + ctx->req->content_off;
}
/* Returns the size of the overall content length */
size_t luw_get_http_content_len(const luw_ctx_t *ctx)
{
return ctx->req->content_len;
}
/* Returns the size of the content sent in _this_ request */
size_t luw_get_http_content_sent(const luw_ctx_t *ctx)
{
return ctx->req->content_sent;
}
bool luw_http_is_tls(const luw_ctx_t *ctx)
{
return ctx->req->tls;
}
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)
{
struct luw_hdr_field *hf;
struct luw_hdr_field *hf_end;
hf_end = ctx->req->fields + ctx->req->nr_fields;
for (hf = ctx->req->fields; hf < hf_end; hf++) {
const char *name = (const char *)ctx->req + hf->name_off;
const char *value = (const char *)ctx->req + hf->value_off;
bool again;
again = luw_http_hdr_iter_func(ctx, name, value, user_data);
if (!again)
break;
}
}
const char *luw_http_hdr_get_value(luw_ctx_t *ctx, const char *hdr)
{
luw_http_hdr_iter_t *iter;
const char *name;
const char *value;
luw_foreach_http_hdr((*ctx), iter, name, value) {
if (strcasecmp(name, hdr) == 0)
return value;
}
return NULL;
}
/* Returns the size of ctx->resp->data[] */
size_t luw_get_response_data_size(const luw_ctx_t *ctx)
{
return ctx->mem - ctx->resp->data;
}
/* Appends (non-nul terminmates) formatted data to the response buffer */
__attribute__((__format__(printf, 2, 3)))
int luw_mem_writep(luw_ctx_t *ctx, const char *fmt, ...)
{
int len;
char *logbuf;
va_list ap;
va_start(ap, fmt);
len = vasprintf(&logbuf, fmt, ap);
if (len == -1) {
va_end(ap);
return -1;
}
va_end(ap);
ctx->mem = mempcpy(ctx->mem, logbuf, len);
ctx->resp->size += len;
free(logbuf);
return len;
}
/* Appends data of length size to the response buffer */
size_t luw_mem_writep_data(luw_ctx_t *ctx, const u8 *src, size_t size)
{
ctx->mem = mempcpy(ctx->mem, src, size);
ctx->resp->size += size;
return ctx->resp->size;
}
/* Append data from the request to the previously setup request_buffer. */
void luw_req_buf_append(luw_ctx_t *ctx, const u8 *src)
{
struct luw_req *req = (struct luw_req *)src;
ctx->reqp = mempcpy(ctx->reqp, src + req->content_off,
req->request_size);
ctx->req->content_sent = req->content_sent;
ctx->req->total_content_sent = req->total_content_sent;
}
/*
* Convenience function to fill the response buffer with data from
* the request buffer.
*
* The runtime allocates NXT_WASM_MEM_SIZE + NXT_WASM_PAGE_SIZE
* bytes so we don't need to worry about the size of the actual
* response structures.
*/
size_t luw_mem_fill_buf_from_req(luw_ctx_t *ctx, size_t from)
{
size_t write_bytes;
size_t mem_size = nxt_wasm_get_init_mem_size();
write_bytes = ctx->req->content_sent;
if (write_bytes > mem_size)
write_bytes = mem_size;
memcpy(ctx->resp->data, ctx->req_buf + ctx->req->content_off + from,
write_bytes);
ctx->resp->size = write_bytes;
return write_bytes;
}
void luw_mem_reset(luw_ctx_t *ctx)
{
ctx->mem = ctx->resp->data;
ctx->resp->size = 0;
ctx->resp_hdr->nr_fields = 0;
}
void luw_http_send_response(const luw_ctx_t *ctx)
{
nxt_wasm_send_response(ctx->resp_offset);
}
void luw_http_init_headers(luw_ctx_t *ctx, size_t nr, size_t offset)
{
ctx->resp_hdr = (struct luw_resp_hdr *)(ctx->addr + offset);
ctx->hdrp = (u8 *)ctx->resp_hdr + sizeof(struct luw_resp_hdr) +
(nr * sizeof(struct luw_hdr_field));
ctx->resp_hdr->nr_fields = nr;
}
void luw_http_add_header(luw_ctx_t *ctx, u16 idx, const char *name,
const char *value)
{
ctx->resp_hdr->fields[idx].name_off = ctx->hdrp - ctx->addr;
ctx->resp_hdr->fields[idx].name_len = strlen(name);
ctx->hdrp = mempcpy(ctx->hdrp, name, strlen(name));
ctx->resp_hdr->fields[idx].value_off = ctx->hdrp - ctx->addr;
ctx->resp_hdr->fields[idx].value_len = strlen(value);
ctx->hdrp = mempcpy(ctx->hdrp, value, strlen(value));
}
void luw_http_send_headers(const luw_ctx_t *ctx)
{
nxt_wasm_send_headers((u8 *)ctx->resp_hdr - ctx->addr);
}
void luw_http_response_end(void)
{
nxt_wasm_response_end();
}
u32 luw_mem_get_init_size(void)
{
return nxt_wasm_get_init_mem_size();
}
/* These are really just convenience wrappers for the rust bindings... */
void *luw_malloc(size_t size)
{
return malloc(size);
}
void luw_free(void *ptr)
{
free(ptr);
}
|