crossbind 2.0.0-beta.63 → 2.0.0-beta.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -1
- package/package.json +2 -2
- package/src/actions/buildBinTools.js +0 -0
- package/src/actions/buildJs.js +2 -9
- package/src/actions/buildNode.js +4 -6
- package/src/actions/buildWasiCommand.js +10 -5
- package/src/actions/buildWasm.js +24 -3
- package/src/actions/createImportedBridges.js +7 -0
- package/src/actions/createInterface.js +3 -3
- package/src/actions/createLib.js +14 -2
- package/src/actions/getData.js +2 -1
- package/src/actions/getEnv.js +7 -0
- package/src/actions/runnerCommands.js +16 -4
- package/src/actions/writeRuntimeEntry.js +18 -7
- package/src/assets/cpp-runtime/node.cpp +18 -1
- package/src/assets/js-runtime/adapters/worker-comlink.js +51 -0
- package/src/assets/js-runtime/core.js +5 -4
- package/src/assets/toolchain-digests.json +19 -19
- package/src/assets/wasi-runtime/http/README.md +17 -0
- package/src/assets/wasi-runtime/http/bindings/crossbind_http.c +2537 -0
- package/src/assets/wasi-runtime/http/bindings/crossbind_http.h +826 -0
- package/src/assets/wasi-runtime/http/bindings/crossbind_http_component_type.o +0 -0
- package/src/assets/wasi-runtime/http/handler.cpp +298 -0
- package/src/assets/wasi-runtime/http/include/crossbind/http.h +31 -0
- package/src/assets/wasi-runtime/http/wit/deps/LICENSE.md +8 -0
- package/src/assets/wasi-runtime/http/wit/deps/cli.wit +28 -0
- package/src/assets/wasi-runtime/http/wit/deps/clocks.wit +43 -0
- package/src/assets/wasi-runtime/http/wit/deps/http.wit +509 -0
- package/src/assets/wasi-runtime/http/wit/deps/random.wit +18 -0
- package/src/assets/wasi-runtime/http/wit/world.wit +6 -0
- package/src/bin.js +29 -179
- package/src/cli.js +14 -0
- package/src/integration/getCrossbindScript.js +4 -3
- package/src/program.js +169 -0
- package/src/state/index.js +4 -3
- package/src/state/loadConfig.js +4 -11
- package/src/utils/appRustCrates.js +4 -0
- package/src/utils/bridgeExtras.js +74 -6
- package/src/utils/cargoLock.js +19 -0
- package/src/utils/cppDts.js +2 -0
- package/src/utils/embindObjectLifetime.js +35 -0
- package/src/utils/packageLicense.js +4 -2
- package/src/utils/resetCMakeCacheForSource.js +26 -0
- package/src/utils/rustBridgeGen.js +13 -4
- package/src/utils/rustMt.js +5 -0
- package/src/utils/rustSysroot.js +7 -5
- package/src/utils/swigInterface.js +5 -4
- package/src/utils/systemConfig.js +13 -0
- package/src/utils/targets.js +11 -0
- package/src/utils/wasiHttp.js +50 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// The wasi:http/handler export of an http build: each request goes to crossbind::http::handle, and its
|
|
2
|
+
// Response goes back to the host.
|
|
3
|
+
|
|
4
|
+
#include <crossbind/http.h>
|
|
5
|
+
|
|
6
|
+
#include <algorithm>
|
|
7
|
+
#include <cctype>
|
|
8
|
+
#include <cstdio>
|
|
9
|
+
#include <exception>
|
|
10
|
+
#include <string>
|
|
11
|
+
#include <utility>
|
|
12
|
+
|
|
13
|
+
#include "crossbind_http.h"
|
|
14
|
+
|
|
15
|
+
namespace {
|
|
16
|
+
|
|
17
|
+
using crossbind::http::Headers;
|
|
18
|
+
using crossbind::http::Request;
|
|
19
|
+
using crossbind::http::Response;
|
|
20
|
+
|
|
21
|
+
constexpr size_t READ_CHUNK = 64 * 1024;
|
|
22
|
+
// The canonical ABI traps on a copy of 2^28 bytes or more.
|
|
23
|
+
constexpr size_t WRITE_CHUNK = 1024 * 1024;
|
|
24
|
+
constexpr int LOWEST_STATUS = 100;
|
|
25
|
+
constexpr int HIGHEST_STATUS = 999;
|
|
26
|
+
constexpr int INTERNAL_SERVER_ERROR = 500;
|
|
27
|
+
|
|
28
|
+
// The host reads a blocked write after the frame that made it is gone, so the value cannot live on a stack.
|
|
29
|
+
const wasi_http_types_result_option_own_trailers_error_code_t NO_TRAILERS = {};
|
|
30
|
+
|
|
31
|
+
// A response whose body is still being written after task.return.
|
|
32
|
+
struct Task {
|
|
33
|
+
Task *next;
|
|
34
|
+
crossbind_http_waitable_set_t set;
|
|
35
|
+
uint32_t waiting;
|
|
36
|
+
wasi_http_types_stream_u8_writer_t body;
|
|
37
|
+
wasi_http_types_future_result_option_own_trailers_error_code_writer_t trailers;
|
|
38
|
+
std::string text;
|
|
39
|
+
size_t written;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// wasi-sdk keeps the stack pointer in context slot 0 and hosts reject slot 1, so a callback finds its task by
|
|
43
|
+
// the waitable it reports.
|
|
44
|
+
Task *tasks = nullptr;
|
|
45
|
+
|
|
46
|
+
std::string textOf(const uint8_t *data, size_t length) {
|
|
47
|
+
return std::string(reinterpret_cast<const char *>(data), length);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
std::string lowercase(std::string text) {
|
|
51
|
+
std::transform(text.begin(), text.end(), text.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
|
52
|
+
return text;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
std::string methodOf(wasi_http_types_borrow_request_t request) {
|
|
56
|
+
static const char *const NAMES[] = { "GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH" };
|
|
57
|
+
wasi_http_types_method_t method;
|
|
58
|
+
wasi_http_types_method_request_get_method(request, &method);
|
|
59
|
+
std::string name = method.tag == WASI_HTTP_TYPES_METHOD_OTHER ? textOf(method.val.other.ptr, method.val.other.len) : NAMES[method.tag];
|
|
60
|
+
wasi_http_types_method_free(&method);
|
|
61
|
+
return name;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Spin passes the authority of an HTTP/1.1 request, wasmtime only its host header.
|
|
65
|
+
std::string authorityOf(wasi_http_types_borrow_request_t request, const Headers &headers) {
|
|
66
|
+
std::string value;
|
|
67
|
+
crossbind_http_string_t authority;
|
|
68
|
+
if (wasi_http_types_method_request_get_authority(request, &authority)) {
|
|
69
|
+
value = textOf(authority.ptr, authority.len);
|
|
70
|
+
crossbind_http_string_free(&authority);
|
|
71
|
+
}
|
|
72
|
+
if (!value.empty()) return value;
|
|
73
|
+
const auto host = std::find_if(headers.begin(), headers.end(), [](const auto &header) { return header.first == "host"; });
|
|
74
|
+
return host == headers.end() ? std::string() : host->second;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
void readTarget(wasi_http_types_borrow_request_t request, Request &incoming) {
|
|
78
|
+
crossbind_http_string_t target;
|
|
79
|
+
if (!wasi_http_types_method_request_get_path_with_query(request, &target)) {
|
|
80
|
+
incoming.path = "/";
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const std::string value = textOf(target.ptr, target.len);
|
|
84
|
+
crossbind_http_string_free(&target);
|
|
85
|
+
const size_t mark = value.find('?');
|
|
86
|
+
incoming.path = value.substr(0, mark);
|
|
87
|
+
if (mark != std::string::npos) incoming.query = value.substr(mark + 1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
Headers headersOf(wasi_http_types_borrow_request_t request) {
|
|
91
|
+
wasi_http_types_own_headers_t fields = wasi_http_types_method_request_get_headers(request);
|
|
92
|
+
wasi_http_types_list_tuple2_field_name_field_value_t entries;
|
|
93
|
+
wasi_http_types_method_fields_copy_all(wasi_http_types_borrow_fields(fields), &entries);
|
|
94
|
+
Headers headers;
|
|
95
|
+
headers.reserve(entries.len);
|
|
96
|
+
for (size_t i = 0; i < entries.len; i++) {
|
|
97
|
+
headers.emplace_back(lowercase(textOf(entries.ptr[i].f0.ptr, entries.ptr[i].f0.len)), textOf(entries.ptr[i].f1.ptr, entries.ptr[i].f1.len));
|
|
98
|
+
}
|
|
99
|
+
wasi_http_types_list_tuple2_field_name_field_value_free(&entries);
|
|
100
|
+
wasi_http_types_fields_drop_own(fields);
|
|
101
|
+
return headers;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Blocks until the waitable makes progress. Only the steps before task.return block: a cancellation can only
|
|
105
|
+
// reach a task that has not returned, and its event names no waitable this file could find the task by.
|
|
106
|
+
crossbind_http_waitable_status_t await(uint32_t waitable) {
|
|
107
|
+
crossbind_http_waitable_set_t set = crossbind_http_waitable_set_new();
|
|
108
|
+
crossbind_http_waitable_join(waitable, set);
|
|
109
|
+
crossbind_http_event_t event;
|
|
110
|
+
crossbind_http_waitable_set_wait(set, &event);
|
|
111
|
+
crossbind_http_waitable_join(waitable, 0);
|
|
112
|
+
crossbind_http_waitable_set_drop(set);
|
|
113
|
+
return event.code;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// False when the body does not fit in memory; the rest of it is left unread.
|
|
117
|
+
bool readBody(wasi_http_types_stream_u8_t stream, std::string &body) {
|
|
118
|
+
bool fits = true;
|
|
119
|
+
for (;;) {
|
|
120
|
+
const size_t start = body.size();
|
|
121
|
+
try {
|
|
122
|
+
body.resize(start + READ_CHUNK);
|
|
123
|
+
} catch (const std::exception &) {
|
|
124
|
+
fits = false;
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
crossbind_http_waitable_status_t status = wasi_http_types_stream_u8_read(stream, reinterpret_cast<uint8_t *>(body.data()) + start, READ_CHUNK);
|
|
128
|
+
if (status == CROSSBIND_HTTP_WAITABLE_STATUS_BLOCKED) status = await(stream);
|
|
129
|
+
body.resize(start + CROSSBIND_HTTP_WAITABLE_COUNT(status));
|
|
130
|
+
if (CROSSBIND_HTTP_WAITABLE_STATE(status) != CROSSBIND_HTTP_WAITABLE_COMPLETED) break;
|
|
131
|
+
}
|
|
132
|
+
wasi_http_types_stream_u8_drop_readable(stream);
|
|
133
|
+
return fits;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// The future settles once the body has ended; anything but ok means the body did not arrive whole.
|
|
137
|
+
bool bodyArrived(wasi_http_types_future_result_option_own_trailers_error_code_t future, wasi_http_types_error_code_t &error) {
|
|
138
|
+
wasi_http_types_result_option_own_trailers_error_code_t result;
|
|
139
|
+
crossbind_http_waitable_status_t status = wasi_http_types_future_result_option_own_trailers_error_code_read(future, &result);
|
|
140
|
+
if (status == CROSSBIND_HTTP_WAITABLE_STATUS_BLOCKED) status = await(future);
|
|
141
|
+
wasi_http_types_future_result_option_own_trailers_error_code_drop_readable(future);
|
|
142
|
+
if (CROSSBIND_HTTP_WAITABLE_STATE(status) != CROSSBIND_HTTP_WAITABLE_COMPLETED) {
|
|
143
|
+
error = {};
|
|
144
|
+
error.tag = WASI_HTTP_TYPES_ERROR_CODE_INTERNAL_ERROR;
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
if (result.is_err) {
|
|
148
|
+
error = result.val.err;
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
if (result.val.ok.is_some) wasi_http_types_fields_drop_own(result.val.ok.val);
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
void settle(wasi_http_types_future_result_void_error_code_writer_t writer) {
|
|
156
|
+
const wasi_http_types_result_void_error_code_t ok = {};
|
|
157
|
+
if (wasi_http_types_future_result_void_error_code_write(writer, &ok) == CROSSBIND_HTTP_WAITABLE_STATUS_BLOCKED) await(writer);
|
|
158
|
+
wasi_http_types_future_result_void_error_code_drop_writable(writer);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
crossbind_http_callback_code_t fail(wasi_http_types_error_code_t error) {
|
|
162
|
+
exports_wasi_http_handler_result_own_response_error_code_t result = {};
|
|
163
|
+
result.is_err = true;
|
|
164
|
+
result.val.err = error;
|
|
165
|
+
exports_wasi_http_handler_handle_return(result);
|
|
166
|
+
wasi_http_types_error_code_free(&error);
|
|
167
|
+
return CROSSBIND_HTTP_CALLBACK_CODE_EXIT;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
Response respond(Request request) {
|
|
171
|
+
const std::string line = request.method + " " + request.path;
|
|
172
|
+
try {
|
|
173
|
+
return crossbind::http::handle(std::move(request));
|
|
174
|
+
} catch (const std::exception &error) {
|
|
175
|
+
std::fprintf(stderr, "crossbind: %s threw: %s\n", line.c_str(), error.what());
|
|
176
|
+
} catch (...) {
|
|
177
|
+
std::fprintf(stderr, "crossbind: %s threw\n", line.c_str());
|
|
178
|
+
}
|
|
179
|
+
return { INTERNAL_SERVER_ERROR, {}, {} };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
wasi_http_types_own_fields_t fieldsOf(const Headers &headers) {
|
|
183
|
+
wasi_http_types_own_fields_t fields = wasi_http_types_constructor_fields();
|
|
184
|
+
for (const auto &[name, value] : headers) {
|
|
185
|
+
wasi_http_types_field_name_t key = { reinterpret_cast<uint8_t *>(const_cast<char *>(name.data())), name.size() };
|
|
186
|
+
wasi_http_types_field_value_t bytes = { reinterpret_cast<uint8_t *>(const_cast<char *>(value.data())), value.size() };
|
|
187
|
+
wasi_http_types_header_error_t error;
|
|
188
|
+
if (!wasi_http_types_method_fields_append(wasi_http_types_borrow_fields(fields), &key, &bytes, &error)) {
|
|
189
|
+
std::fprintf(stderr, "crossbind: the host rejected the response header \"%s\"\n", name.c_str());
|
|
190
|
+
wasi_http_types_header_error_free(&error);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return fields;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
crossbind_http_callback_code_t wait(Task *task, uint32_t waitable) {
|
|
197
|
+
task->waiting = waitable;
|
|
198
|
+
crossbind_http_waitable_join(waitable, task->set);
|
|
199
|
+
return CROSSBIND_HTTP_CALLBACK_CODE_WAIT(task->set);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
crossbind_http_callback_code_t finish(Task *task) {
|
|
203
|
+
crossbind_http_waitable_set_drop(task->set);
|
|
204
|
+
Task **link = &tasks;
|
|
205
|
+
while (*link != task) link = &(*link)->next;
|
|
206
|
+
*link = task->next;
|
|
207
|
+
delete task;
|
|
208
|
+
return CROSSBIND_HTTP_CALLBACK_CODE_EXIT;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
crossbind_http_callback_code_t sendTrailers(Task *task) {
|
|
212
|
+
if (wasi_http_types_future_result_option_own_trailers_error_code_write(task->trailers, &NO_TRAILERS) == CROSSBIND_HTTP_WAITABLE_STATUS_BLOCKED) {
|
|
213
|
+
return wait(task, task->trailers);
|
|
214
|
+
}
|
|
215
|
+
wasi_http_types_future_result_option_own_trailers_error_code_drop_writable(task->trailers);
|
|
216
|
+
return finish(task);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
crossbind_http_callback_code_t sendBody(Task *task) {
|
|
220
|
+
while (task->written < task->text.size()) {
|
|
221
|
+
const size_t length = std::min(task->text.size() - task->written, WRITE_CHUNK);
|
|
222
|
+
const crossbind_http_waitable_status_t status = wasi_http_types_stream_u8_write(
|
|
223
|
+
task->body, reinterpret_cast<const uint8_t *>(task->text.data()) + task->written, length);
|
|
224
|
+
if (status == CROSSBIND_HTTP_WAITABLE_STATUS_BLOCKED) return wait(task, task->body);
|
|
225
|
+
task->written += CROSSBIND_HTTP_WAITABLE_COUNT(status);
|
|
226
|
+
if (CROSSBIND_HTTP_WAITABLE_STATE(status) != CROSSBIND_HTTP_WAITABLE_COMPLETED) break;
|
|
227
|
+
}
|
|
228
|
+
wasi_http_types_stream_u8_drop_writable(task->body);
|
|
229
|
+
return sendTrailers(task);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
crossbind_http_callback_code_t reply(Response response) {
|
|
233
|
+
Task *task = new Task{ tasks, crossbind_http_waitable_set_new(), 0, 0, 0, std::move(response.body), 0 };
|
|
234
|
+
tasks = task;
|
|
235
|
+
wasi_http_types_stream_u8_t contents = wasi_http_types_stream_u8_new(&task->body);
|
|
236
|
+
wasi_http_types_future_result_option_own_trailers_error_code_t trailers = wasi_http_types_future_result_option_own_trailers_error_code_new(&task->trailers);
|
|
237
|
+
wasi_http_types_tuple2_own_response_future_result_void_error_code_t created;
|
|
238
|
+
wasi_http_types_static_response_new(fieldsOf(response.headers), &contents, trailers, &created);
|
|
239
|
+
wasi_http_types_future_result_void_error_code_drop_readable(created.f1);
|
|
240
|
+
|
|
241
|
+
wasi_http_types_borrow_response_t borrowed = wasi_http_types_borrow_response(created.f0);
|
|
242
|
+
const bool isStatus = response.status >= LOWEST_STATUS && response.status <= HIGHEST_STATUS
|
|
243
|
+
&& wasi_http_types_method_response_set_status_code(borrowed, static_cast<wasi_http_types_status_code_t>(response.status));
|
|
244
|
+
if (!isStatus) {
|
|
245
|
+
std::fprintf(stderr, "crossbind: %d is not an HTTP status, answered %d\n", response.status, INTERNAL_SERVER_ERROR);
|
|
246
|
+
wasi_http_types_method_response_set_status_code(borrowed, INTERNAL_SERVER_ERROR);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
exports_wasi_http_handler_result_own_response_error_code_t result = {};
|
|
250
|
+
result.val.ok = created.f0;
|
|
251
|
+
exports_wasi_http_handler_handle_return(result);
|
|
252
|
+
return sendBody(task);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
extern "C" crossbind_http_callback_code_t exports_wasi_http_handler_handle(exports_wasi_http_handler_own_request_t request) {
|
|
258
|
+
Request incoming;
|
|
259
|
+
wasi_http_types_borrow_request_t borrowed = wasi_http_types_borrow_request(request);
|
|
260
|
+
incoming.method = methodOf(borrowed);
|
|
261
|
+
readTarget(borrowed, incoming);
|
|
262
|
+
incoming.headers = headersOf(borrowed);
|
|
263
|
+
incoming.authority = authorityOf(borrowed, incoming.headers);
|
|
264
|
+
|
|
265
|
+
wasi_http_types_future_result_void_error_code_writer_t settled;
|
|
266
|
+
wasi_http_types_future_result_void_error_code_t res = wasi_http_types_future_result_void_error_code_new(&settled);
|
|
267
|
+
wasi_http_types_tuple2_stream_u8_future_result_option_own_trailers_error_code_t body;
|
|
268
|
+
wasi_http_types_static_request_consume_body(request, res, &body);
|
|
269
|
+
if (!readBody(body.f0, incoming.body)) {
|
|
270
|
+
std::string().swap(incoming.body);
|
|
271
|
+
wasi_http_types_future_result_option_own_trailers_error_code_drop_readable(body.f1);
|
|
272
|
+
settle(settled);
|
|
273
|
+
wasi_http_types_error_code_t error = {};
|
|
274
|
+
error.tag = WASI_HTTP_TYPES_ERROR_CODE_HTTP_REQUEST_BODY_SIZE;
|
|
275
|
+
return fail(error);
|
|
276
|
+
}
|
|
277
|
+
wasi_http_types_error_code_t error;
|
|
278
|
+
const bool arrived = bodyArrived(body.f1, error);
|
|
279
|
+
settle(settled);
|
|
280
|
+
if (!arrived) return fail(error);
|
|
281
|
+
return reply(respond(std::move(incoming)));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
extern "C" crossbind_http_callback_code_t exports_wasi_http_handler_handle_callback(crossbind_http_event_t *event) {
|
|
285
|
+
Task *task = tasks;
|
|
286
|
+
while (task && task->waiting != event->waitable) task = task->next;
|
|
287
|
+
if (!task) return CROSSBIND_HTTP_CALLBACK_CODE_EXIT;
|
|
288
|
+
task->waiting = 0;
|
|
289
|
+
crossbind_http_waitable_join(event->waitable, 0);
|
|
290
|
+
if (event->event == CROSSBIND_HTTP_EVENT_STREAM_WRITE) {
|
|
291
|
+
task->written += CROSSBIND_HTTP_WAITABLE_COUNT(event->code);
|
|
292
|
+
if (CROSSBIND_HTTP_WAITABLE_STATE(event->code) == CROSSBIND_HTTP_WAITABLE_COMPLETED) return sendBody(task);
|
|
293
|
+
wasi_http_types_stream_u8_drop_writable(task->body);
|
|
294
|
+
return sendTrailers(task);
|
|
295
|
+
}
|
|
296
|
+
wasi_http_types_future_result_option_own_trailers_error_code_drop_writable(task->trailers);
|
|
297
|
+
return finish(task);
|
|
298
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <string>
|
|
4
|
+
#include <utility>
|
|
5
|
+
#include <vector>
|
|
6
|
+
|
|
7
|
+
namespace crossbind::http {
|
|
8
|
+
|
|
9
|
+
using Headers = std::vector<std::pair<std::string, std::string>>;
|
|
10
|
+
|
|
11
|
+
struct Request {
|
|
12
|
+
std::string method;
|
|
13
|
+
std::string authority;
|
|
14
|
+
// As the client sent them, percent-encoding included; the query without its '?'.
|
|
15
|
+
std::string path;
|
|
16
|
+
std::string query;
|
|
17
|
+
// Names in lowercase.
|
|
18
|
+
Headers headers;
|
|
19
|
+
std::string body;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
struct Response {
|
|
23
|
+
int status = 200;
|
|
24
|
+
Headers headers;
|
|
25
|
+
std::string body;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Defined by the application; called once per request, after its whole body has arrived.
|
|
29
|
+
Response handle(Request request);
|
|
30
|
+
|
|
31
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Copyright © 2019-2023 the Contributors to the WASI Specification, published
|
|
2
|
+
by the [WebAssembly Community Group][cg] under the
|
|
3
|
+
[W3C Community Contributor License Agreement (CLA)][cla]. A human-readable
|
|
4
|
+
[summary][summary] is available.
|
|
5
|
+
|
|
6
|
+
[cg]: https://www.w3.org/community/webassembly/
|
|
7
|
+
[cla]: https://www.w3.org/community/about/agreements/cla/
|
|
8
|
+
[summary]: https://www.w3.org/community/about/agreements/cla-deed/
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
package wasi:cli@0.3.0;
|
|
2
|
+
|
|
3
|
+
interface types {
|
|
4
|
+
enum error-code {
|
|
5
|
+
io,
|
|
6
|
+
illegal-byte-sequence,
|
|
7
|
+
pipe,
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface stdout {
|
|
12
|
+
use types.{error-code};
|
|
13
|
+
|
|
14
|
+
write-via-stream: func(data: stream<u8>) -> future<result<_, error-code>>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface stderr {
|
|
18
|
+
use types.{error-code};
|
|
19
|
+
|
|
20
|
+
write-via-stream: func(data: stream<u8>) -> future<result<_, error-code>>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface stdin {
|
|
24
|
+
use types.{error-code};
|
|
25
|
+
|
|
26
|
+
read-via-stream: func() -> tuple<stream<u8>, future<result<_, error-code>>>;
|
|
27
|
+
}
|
|
28
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
package wasi:clocks@0.3.0;
|
|
2
|
+
|
|
3
|
+
interface types {
|
|
4
|
+
type duration = u64;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface monotonic-clock {
|
|
8
|
+
use types.{duration};
|
|
9
|
+
|
|
10
|
+
type mark = u64;
|
|
11
|
+
|
|
12
|
+
now: func() -> mark;
|
|
13
|
+
|
|
14
|
+
get-resolution: func() -> duration;
|
|
15
|
+
|
|
16
|
+
wait-until: async func(when: mark);
|
|
17
|
+
|
|
18
|
+
wait-for: async func(how-long: duration);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface system-clock {
|
|
22
|
+
use types.{duration};
|
|
23
|
+
|
|
24
|
+
record instant {
|
|
25
|
+
seconds: s64,
|
|
26
|
+
nanoseconds: u32,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
now: func() -> instant;
|
|
30
|
+
|
|
31
|
+
get-resolution: func() -> duration;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface timezone {
|
|
35
|
+
use system-clock.{instant};
|
|
36
|
+
|
|
37
|
+
iana-id: func() -> option<string>;
|
|
38
|
+
|
|
39
|
+
utc-offset: func(when: instant) -> option<s64>;
|
|
40
|
+
|
|
41
|
+
to-debug-string: func() -> string;
|
|
42
|
+
}
|
|
43
|
+
|