@gcoredev/proxy-wasm-sdk-as 1.0.0-alpha.2
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/LICENSE.txt +202 -0
- package/README.md +102 -0
- package/assembly/exports.ts +138 -0
- package/assembly/imports.ts +175 -0
- package/assembly/index.ts +15 -0
- package/assembly/malloc.ts +16 -0
- package/assembly/proxy.ts +2 -0
- package/assembly/runtime.ts +1114 -0
- package/assembly/tsconfig.json +6 -0
- package/index.js +16 -0
- package/package.json +54 -0
|
@@ -0,0 +1,1114 @@
|
|
|
1
|
+
import * as imports from "./imports";
|
|
2
|
+
import { free } from "./malloc";
|
|
3
|
+
|
|
4
|
+
// abort function.
|
|
5
|
+
// use with:
|
|
6
|
+
// --use abort=index/abort_proc_exit
|
|
7
|
+
// compiler flag
|
|
8
|
+
// @ts-ignore: decorator
|
|
9
|
+
@global
|
|
10
|
+
export function abort_proc_exit(message: string | null, fileName: string | null, lineNumber: u32, columnNumber: u32): void {
|
|
11
|
+
let logMessage = "abort: ";
|
|
12
|
+
if (message !== null) {
|
|
13
|
+
logMessage += message.toString();
|
|
14
|
+
}
|
|
15
|
+
if (fileName !== null) {
|
|
16
|
+
logMessage += " at: " + fileName.toString() + "(" + lineNumber.toString() + ":" + columnNumber.toString() + ")";
|
|
17
|
+
}
|
|
18
|
+
log(LogLevelValues.critical, logMessage);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function CHECK_RESULT(c: imports.WasmResult): void {
|
|
22
|
+
if (c != WasmResultValues.Ok) {
|
|
23
|
+
if (c == WasmResultValues.NotFound) {
|
|
24
|
+
log(LogLevelValues.debug, "Not found");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
log(LogLevelValues.critical, c.toString());
|
|
28
|
+
throw new Error(":(");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/////////////// Access helpers
|
|
33
|
+
|
|
34
|
+
export class Reference<T> {
|
|
35
|
+
data: T;
|
|
36
|
+
|
|
37
|
+
ptr(): usize {
|
|
38
|
+
return changetype<usize>(this) + offsetof<Reference<T>>("data");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
class ArrayBufferReference {
|
|
43
|
+
private buffer: usize;
|
|
44
|
+
private size: usize;
|
|
45
|
+
|
|
46
|
+
constructor() {
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
sizePtr(): usize {
|
|
50
|
+
return changetype<usize>(this) + offsetof<ArrayBufferReference>("size");
|
|
51
|
+
}
|
|
52
|
+
bufferPtr(): usize {
|
|
53
|
+
return changetype<usize>(this) + offsetof<ArrayBufferReference>("buffer");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Before calling toArrayBuffer below, you must call out to the host to fill in the values.
|
|
57
|
+
// toArrayBuffer below **must** be called once and only once.
|
|
58
|
+
toArrayBuffer(): ArrayBuffer {
|
|
59
|
+
if (this.size == 0) {
|
|
60
|
+
return new ArrayBuffer(0);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let array = changetype<ArrayBuffer>(this.buffer);
|
|
64
|
+
// host code used malloc to allocate this buffer.
|
|
65
|
+
// release the allocated ptr. array variable will retain it, so it won't be actually free (as it is ref counted).
|
|
66
|
+
free(this.buffer);
|
|
67
|
+
// should we return a this sliced up to size?
|
|
68
|
+
return array;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
var globalArrayBufferReference = new ArrayBufferReference();
|
|
73
|
+
let globalU32Ref = new Reference<u32>();
|
|
74
|
+
let globalLogLevelRef = new Reference<imports.LogLevel>();
|
|
75
|
+
let globalU64Ref = new Reference<u64>();
|
|
76
|
+
let globalUsizeRef = new Reference<usize>();
|
|
77
|
+
|
|
78
|
+
export class HeaderPair {
|
|
79
|
+
key: ArrayBuffer;
|
|
80
|
+
value: ArrayBuffer;
|
|
81
|
+
|
|
82
|
+
toString(): string {
|
|
83
|
+
return this.key.toString() + ":" + this.value.toString();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
constructor(header_key_data: ArrayBuffer, header_value_data: ArrayBuffer) {
|
|
87
|
+
this.key = header_key_data;
|
|
88
|
+
this.value = header_value_data;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function makeHeaderPair(key: string, value: string): HeaderPair {
|
|
93
|
+
let key_arr = String.UTF8.encode(key);
|
|
94
|
+
let value_arr = String.UTF8.encode(value);
|
|
95
|
+
return new HeaderPair(key_arr, value_arr);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export type Headers = Array<HeaderPair>;
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
export enum LogLevelValues { trace, debug, info, warn, error, critical }
|
|
102
|
+
export enum FilterStatusValues { Continue = 0, StopIteration = 1 }
|
|
103
|
+
export enum FilterHeadersStatusValues {
|
|
104
|
+
Continue = 0,
|
|
105
|
+
StopIteration = 1,
|
|
106
|
+
ContinueAndEndStream = 2,
|
|
107
|
+
StopAllIterationAndBuffer = 3,
|
|
108
|
+
StopAllIterationAndWatermark = 4,
|
|
109
|
+
}
|
|
110
|
+
export enum FilterMetadataStatusValues { Continue = 0 }
|
|
111
|
+
export enum FilterTrailersStatusValues { Continue = 0, StopIteration = 1 }
|
|
112
|
+
export enum FilterDataStatusValues {
|
|
113
|
+
Continue = 0,
|
|
114
|
+
StopIterationAndBuffer = 1,
|
|
115
|
+
StopIterationAndWatermark = 2,
|
|
116
|
+
StopIterationNoBuffer = 3
|
|
117
|
+
}
|
|
118
|
+
export enum GrpcStatusValues {
|
|
119
|
+
Ok = 0,
|
|
120
|
+
Canceled = 1,
|
|
121
|
+
Unknown = 2,
|
|
122
|
+
InvalidArgument = 3,
|
|
123
|
+
DeadlineExceeded = 4,
|
|
124
|
+
NotFound = 5,
|
|
125
|
+
AlreadyExists = 6,
|
|
126
|
+
PermissionDenied = 7,
|
|
127
|
+
ResourceExhausted = 8,
|
|
128
|
+
FailedPrecondition = 9,
|
|
129
|
+
Aborted = 10,
|
|
130
|
+
OutOfRange = 11,
|
|
131
|
+
Unimplemented = 12,
|
|
132
|
+
Internal = 13,
|
|
133
|
+
Unavailable = 14,
|
|
134
|
+
DataLoss = 15,
|
|
135
|
+
Unauthenticated = 16,
|
|
136
|
+
MaximumValid = Unauthenticated,
|
|
137
|
+
InvalidCode = -1
|
|
138
|
+
}
|
|
139
|
+
export enum MetricTypeValues {
|
|
140
|
+
Counter = 0,
|
|
141
|
+
Gauge = 1,
|
|
142
|
+
Histogram = 2,
|
|
143
|
+
}
|
|
144
|
+
export enum PeerTypeValues {
|
|
145
|
+
Unknown = 0,
|
|
146
|
+
Local = 1,
|
|
147
|
+
Remote = 2,
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export enum WasmResultValues {
|
|
151
|
+
Ok = 0,
|
|
152
|
+
// The result could not be found, e.g. a provided key did not appear in a
|
|
153
|
+
// table.
|
|
154
|
+
NotFound = 1,
|
|
155
|
+
// An argument was bad, e.g. did not not conform to the required range.
|
|
156
|
+
BadArgument = 2,
|
|
157
|
+
// A protobuf could not be serialized.
|
|
158
|
+
SerializationFailure = 3,
|
|
159
|
+
// A protobuf could not be parsed.
|
|
160
|
+
ParseFailure = 4,
|
|
161
|
+
// A provided expression (e.g. "foo.bar") was illegal or unrecognized.
|
|
162
|
+
BadExpression = 5,
|
|
163
|
+
// A provided memory range was not legal.
|
|
164
|
+
InvalidMemoryAccess = 6,
|
|
165
|
+
// Data was requested from an empty container.
|
|
166
|
+
Empty = 7,
|
|
167
|
+
// The provided CAS did not match that of the stored data.
|
|
168
|
+
CasMismatch = 8,
|
|
169
|
+
// Returned result was unexpected, e.g. of the incorrect size.
|
|
170
|
+
ResultMismatch = 9,
|
|
171
|
+
// Internal failure: trying check logs of the surrounding system.
|
|
172
|
+
InternalFailure = 10,
|
|
173
|
+
// The connection/stream/pipe was broken/closed unexpectedly.
|
|
174
|
+
BrokenConnection = 11,
|
|
175
|
+
// Feature not implemented.
|
|
176
|
+
Unimplemented = 12,
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export enum HeaderMapTypeValues {
|
|
180
|
+
RequestHeaders = 0, // During the onLog callback these are immutable
|
|
181
|
+
RequestTrailers = 1, // During the onLog callback these are immutable
|
|
182
|
+
ResponseHeaders = 2, // During the onLog callback these are immutable
|
|
183
|
+
ResponseTrailers = 3, // During the onLog callback these are immutable
|
|
184
|
+
GrpcReceiveInitialMetadata = 4, // Immutable
|
|
185
|
+
GrpcReceiveTrailingMetadata = 5, // Immutable
|
|
186
|
+
HttpCallResponseHeaders = 6, // Immutable
|
|
187
|
+
HttpCallResponseTrailers = 7, // Immutable
|
|
188
|
+
MAX = 7,
|
|
189
|
+
}
|
|
190
|
+
export enum BufferTypeValues {
|
|
191
|
+
HttpRequestBody = 0, // During the onLog callback these are immutable
|
|
192
|
+
HttpResponseBody = 1, // During the onLog callback these are immutable
|
|
193
|
+
NetworkDownstreamData = 2, // During the onLog callback these are immutable
|
|
194
|
+
NetworkUpstreamData = 3, // During the onLog callback these are immutable
|
|
195
|
+
HttpCallResponseBody = 4, // Immutable
|
|
196
|
+
GrpcReceiveBuffer = 5, // Immutable
|
|
197
|
+
VmConfiguration = 6, // Immutable
|
|
198
|
+
PluginConfiguration = 7, // Immutable
|
|
199
|
+
CallData = 8, // Immutable
|
|
200
|
+
MAX = 8,
|
|
201
|
+
}
|
|
202
|
+
export enum BufferFlagsValues {
|
|
203
|
+
// These must be powers of 2.
|
|
204
|
+
EndOfStream = 1,
|
|
205
|
+
}
|
|
206
|
+
export enum StreamTypeValues {
|
|
207
|
+
Request = 0,
|
|
208
|
+
Response = 1,
|
|
209
|
+
Downstream = 2,
|
|
210
|
+
Upstream = 3,
|
|
211
|
+
MAX = 3,
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/////////////////// wrappers below
|
|
215
|
+
/////////////////// these are the same as the imports above, but with more native typescript interface.
|
|
216
|
+
|
|
217
|
+
export function log(level: LogLevelValues, logMessage: string): void {
|
|
218
|
+
// from the docs:
|
|
219
|
+
// Like JavaScript, AssemblyScript stores strings in UTF-16 encoding represented by the API as UCS-2,
|
|
220
|
+
let buffer = String.UTF8.encode(logMessage);
|
|
221
|
+
imports.proxy_log(level as imports.LogLevel, changetype<usize>(buffer), buffer.byteLength);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function logLevel(): LogLevelValues {
|
|
225
|
+
let level = globalLogLevelRef;
|
|
226
|
+
CHECK_RESULT(imports.proxy_get_log_level(level.ptr()));
|
|
227
|
+
return level.data;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
class StatusWithData {
|
|
231
|
+
status: u32;
|
|
232
|
+
data: ArrayBuffer;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function get_status(): StatusWithData {
|
|
236
|
+
let status = globalU32Ref;
|
|
237
|
+
CHECK_RESULT(imports.proxy_get_status(status.ptr(), globalArrayBufferReference.bufferPtr(), globalArrayBufferReference.sizePtr()));
|
|
238
|
+
return { status: status.data, data: globalArrayBufferReference.toArrayBuffer() };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export function set_tick_period_milliseconds(millisecond: u32): void {
|
|
242
|
+
CHECK_RESULT(imports.proxy_set_tick_period_milliseconds(millisecond));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function get_current_time_nanoseconds(): u64 {
|
|
246
|
+
// TODO: use global var?
|
|
247
|
+
let nanos = globalU64Ref;
|
|
248
|
+
CHECK_RESULT(imports.proxy_get_current_time_nanoseconds(nanos.ptr()));
|
|
249
|
+
return nanos.data;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function get_property(path: string): ArrayBuffer {
|
|
253
|
+
let buffer = String.UTF8.encode(path);
|
|
254
|
+
CHECK_RESULT(imports.proxy_get_property(changetype<usize>(buffer), buffer.byteLength, globalArrayBufferReference.bufferPtr(), globalArrayBufferReference.sizePtr()));
|
|
255
|
+
return globalArrayBufferReference.toArrayBuffer();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export function set_property(path: string, data: ArrayBuffer): WasmResultValues {
|
|
259
|
+
let buffer = String.UTF8.encode(path);
|
|
260
|
+
return imports.proxy_set_property(changetype<usize>(buffer), buffer.byteLength, changetype<usize>(data), data.byteLength);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function pairsSize(headers: Headers): i32 {
|
|
264
|
+
let size = 4; // number of headers
|
|
265
|
+
// for in loop doesn't seem to be supported..
|
|
266
|
+
for (let i = 0; i < headers.length; i++) {
|
|
267
|
+
let header = headers[i];
|
|
268
|
+
size += 8; // size of key, size of value
|
|
269
|
+
size += header.key.byteLength + 1; // null terminated key
|
|
270
|
+
size += header.value.byteLength + 1; // null terminated value
|
|
271
|
+
}
|
|
272
|
+
return size;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function serializeHeaders(headers: Headers): ArrayBuffer {
|
|
276
|
+
let result = new ArrayBuffer(pairsSize(headers));
|
|
277
|
+
let sizes = Uint32Array.wrap(result, 0, 1 + 2 * headers.length);
|
|
278
|
+
sizes[0] = headers.length;
|
|
279
|
+
|
|
280
|
+
// header sizes:
|
|
281
|
+
let index = 1;
|
|
282
|
+
|
|
283
|
+
// for in loop doesn't seem to be supported..
|
|
284
|
+
for (let i = 0; i < headers.length; i++) {
|
|
285
|
+
let header = headers[i];
|
|
286
|
+
sizes[index] = header.key.byteLength;
|
|
287
|
+
index++;
|
|
288
|
+
sizes[index] = header.value.byteLength;
|
|
289
|
+
index++;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
let data = Uint8Array.wrap(result, sizes.byteLength);
|
|
293
|
+
|
|
294
|
+
let currentOffset = 0;
|
|
295
|
+
// for in loop doesn't seem to be supported..
|
|
296
|
+
for (let i = 0; i < headers.length; i++) {
|
|
297
|
+
let header = headers[i];
|
|
298
|
+
// i'm sure there's a better way to copy, i just don't know what it is :/
|
|
299
|
+
let wrappedKey = Uint8Array.wrap(header.key);
|
|
300
|
+
let keyData = data.subarray(currentOffset, currentOffset + wrappedKey.byteLength);
|
|
301
|
+
for (let i = 0; i < wrappedKey.byteLength; i++) {
|
|
302
|
+
keyData[i] = wrappedKey[i];
|
|
303
|
+
}
|
|
304
|
+
currentOffset += wrappedKey.byteLength + 1; // + 1 for terminating nil
|
|
305
|
+
|
|
306
|
+
let wrappedValue = Uint8Array.wrap(header.value);
|
|
307
|
+
let valueData = data.subarray(currentOffset, currentOffset + wrappedValue.byteLength);
|
|
308
|
+
for (let i = 0; i < wrappedValue.byteLength; i++) {
|
|
309
|
+
valueData[i] = wrappedValue[i];
|
|
310
|
+
}
|
|
311
|
+
currentOffset += wrappedValue.byteLength + 1; // + 1 for terminating nil
|
|
312
|
+
}
|
|
313
|
+
return result;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function deserializeHeaders(headers: ArrayBuffer): Headers {
|
|
317
|
+
if (headers.byteLength == 0) {
|
|
318
|
+
return [];
|
|
319
|
+
}
|
|
320
|
+
let numheaders = Uint32Array.wrap(headers, 0, 1)[0];
|
|
321
|
+
let sizes = Uint32Array.wrap(headers, sizeof<u32>(), 2 * numheaders);
|
|
322
|
+
let data = headers.slice(sizeof<u32>() * (1 + 2 * numheaders));
|
|
323
|
+
let result: Headers = [];
|
|
324
|
+
let sizeIndex = 0;
|
|
325
|
+
let dataIndex = 0;
|
|
326
|
+
// for in loop doesn't seem to be supported..
|
|
327
|
+
for (let i: u32 = 0; i < numheaders; i++) {
|
|
328
|
+
let keySize = sizes[sizeIndex];
|
|
329
|
+
sizeIndex++;
|
|
330
|
+
let header_key_data = data.slice(dataIndex, dataIndex + keySize);
|
|
331
|
+
dataIndex += keySize + 1; // +1 for nil termination.
|
|
332
|
+
|
|
333
|
+
let valueSize = sizes[sizeIndex];
|
|
334
|
+
sizeIndex++;
|
|
335
|
+
let header_value_data = data.slice(dataIndex, dataIndex + valueSize);
|
|
336
|
+
dataIndex += valueSize + 1; // +1 for nil termination.
|
|
337
|
+
|
|
338
|
+
let pair = new HeaderPair(header_key_data, header_value_data);
|
|
339
|
+
result.push(pair);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return result;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export function continue_request(): WasmResultValues { return imports.proxy_continue_stream(StreamTypeValues.Request); }
|
|
346
|
+
|
|
347
|
+
export function continue_response(): WasmResultValues { return imports.proxy_continue_stream(StreamTypeValues.Response); }
|
|
348
|
+
|
|
349
|
+
export function send_local_response(response_code: u32, response_code_details: string, body: ArrayBuffer,
|
|
350
|
+
additional_headers: Headers, grpc_status: GrpcStatusValues): WasmResultValues {
|
|
351
|
+
let response_code_details_buffer = String.UTF8.encode(response_code_details);
|
|
352
|
+
let headers = serializeHeaders(additional_headers);
|
|
353
|
+
return imports.proxy_send_local_response(response_code, changetype<usize>(response_code_details_buffer), response_code_details_buffer.byteLength,
|
|
354
|
+
changetype<usize>(body), body.byteLength, changetype<usize>(headers), headers.byteLength, grpc_status);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export function send_http_response(
|
|
358
|
+
response_code: u32,
|
|
359
|
+
response_code_details: string,
|
|
360
|
+
body: ArrayBuffer,
|
|
361
|
+
additional_headers: Headers): WasmResultValues {
|
|
362
|
+
let response_code_details_buffer = String.UTF8.encode(response_code_details);
|
|
363
|
+
let headers = serializeHeaders(additional_headers);
|
|
364
|
+
|
|
365
|
+
return imports.proxy_send_local_response(
|
|
366
|
+
response_code,
|
|
367
|
+
changetype<usize>(response_code_details_buffer),
|
|
368
|
+
response_code_details_buffer.byteLength,
|
|
369
|
+
changetype<usize>(body),
|
|
370
|
+
body.byteLength,
|
|
371
|
+
changetype<usize>(headers),
|
|
372
|
+
headers.byteLength,
|
|
373
|
+
-1);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
export function clear_route_cache(): WasmResultValues { return imports.proxy_clear_route_cache(); }
|
|
378
|
+
export class GetSharedData {
|
|
379
|
+
value: ArrayBuffer | null;
|
|
380
|
+
result: WasmResultValues;
|
|
381
|
+
cas: u32;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
export function get_shared_data(key: string): GetSharedData {
|
|
385
|
+
const key_buffer = String.UTF8.encode(key);
|
|
386
|
+
let cas = globalU32Ref;
|
|
387
|
+
let data = globalArrayBufferReference;
|
|
388
|
+
let result = new GetSharedData();
|
|
389
|
+
result.result = imports.proxy_get_shared_data(changetype<usize>(key_buffer), key_buffer.byteLength, data.bufferPtr(), data.sizePtr(), cas.ptr());
|
|
390
|
+
if (result.result == WasmResultValues.Ok) {
|
|
391
|
+
result.value = data.toArrayBuffer();
|
|
392
|
+
result.cas = cas.data;
|
|
393
|
+
}
|
|
394
|
+
return result;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export function set_shared_data(key: string, value: ArrayBuffer, cas: u32 = 0): WasmResultValues {
|
|
398
|
+
const key_buffer = String.UTF8.encode(key);
|
|
399
|
+
return imports.proxy_set_shared_data(changetype<usize>(key_buffer), key_buffer.byteLength, changetype<usize>(value), value.byteLength, cas);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
class TokenSharedQueueResult {
|
|
403
|
+
result: WasmResultValues;
|
|
404
|
+
token: u32;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export function register_shared_queue(queue_name: string): TokenSharedQueueResult {
|
|
408
|
+
let qid = globalU32Ref;
|
|
409
|
+
let queue_name_buffer = String.UTF8.encode(queue_name);
|
|
410
|
+
let result = new TokenSharedQueueResult();
|
|
411
|
+
result.result = imports.proxy_register_shared_queue(changetype<usize>(queue_name_buffer), queue_name_buffer.byteLength, qid.ptr());
|
|
412
|
+
if (result.result == WasmResultValues.Ok) {
|
|
413
|
+
result.token = qid.data;
|
|
414
|
+
}
|
|
415
|
+
return result;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export function resolve_shared_queue(vm_id: string, queue_name: string): TokenSharedQueueResult {
|
|
419
|
+
let qid = globalU32Ref;
|
|
420
|
+
let vm_id_buffer = String.UTF8.encode(vm_id);
|
|
421
|
+
let queue_name_buffer = String.UTF8.encode(queue_name);
|
|
422
|
+
let result = new TokenSharedQueueResult();
|
|
423
|
+
result.result = imports.proxy_resolve_shared_queue(changetype<usize>(vm_id_buffer), vm_id_buffer.byteLength,
|
|
424
|
+
changetype<usize>(queue_name_buffer), queue_name_buffer.byteLength, qid.ptr());
|
|
425
|
+
if (result.result == WasmResultValues.Ok) {
|
|
426
|
+
result.token = qid.data;
|
|
427
|
+
}
|
|
428
|
+
return result;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
class DequeueSharedQueueResult {
|
|
432
|
+
result: WasmResultValues;
|
|
433
|
+
data: ArrayBuffer | null;
|
|
434
|
+
}
|
|
435
|
+
export function dequeue_shared_queue(token: u32): DequeueSharedQueueResult {
|
|
436
|
+
let result = new DequeueSharedQueueResult();
|
|
437
|
+
|
|
438
|
+
let data = globalArrayBufferReference;
|
|
439
|
+
|
|
440
|
+
let res = imports.proxy_dequeue_shared_queue(token, data.bufferPtr(), data.sizePtr());
|
|
441
|
+
result.result = res;
|
|
442
|
+
if (res == WasmResultValues.Ok) {
|
|
443
|
+
result.data = data.toArrayBuffer();
|
|
444
|
+
}
|
|
445
|
+
return result;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export function enqueue_shared_queue(token: u32, data: ArrayBuffer): WasmResultValues {
|
|
449
|
+
return imports.proxy_enqueue_shared_queue(token, changetype<usize>(data), data.byteLength);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
export function add_header_map_value(typ: HeaderMapTypeValues, key: ArrayBuffer, value: ArrayBuffer): WasmResultValues {
|
|
453
|
+
return imports.proxy_add_header_map_value(typ, changetype<usize>(key), key.byteLength, changetype<usize>(value), value.byteLength);
|
|
454
|
+
}
|
|
455
|
+
export function add_header_map_value_string(typ: HeaderMapTypeValues, key: string, value: string): WasmResultValues {
|
|
456
|
+
let key_arr = String.UTF8.encode(key);
|
|
457
|
+
let value_arr = String.UTF8.encode(value);
|
|
458
|
+
return imports.proxy_add_header_map_value(typ, changetype<usize>(key_arr), key_arr.byteLength, changetype<usize>(value_arr), value_arr.byteLength);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
class HeaderStreamManipulator {
|
|
462
|
+
typ: HeaderMapTypeValues;
|
|
463
|
+
constructor(typ: HeaderMapTypeValues) {
|
|
464
|
+
this.typ = typ;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Add a header.
|
|
470
|
+
* @param key the header name.
|
|
471
|
+
* @param value the header value.
|
|
472
|
+
*/
|
|
473
|
+
add(key: string, value: string): void {
|
|
474
|
+
add_header_map_value_string(this.typ, key, value);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Replace a header.
|
|
479
|
+
* @param key the header name.
|
|
480
|
+
* @param value the header value.
|
|
481
|
+
*/
|
|
482
|
+
replace(key: string, value: string): void {
|
|
483
|
+
replace_header_map_value_string(this.typ, key, value);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Get a header.
|
|
488
|
+
* @param key the header name.
|
|
489
|
+
* @return the header value.
|
|
490
|
+
*/
|
|
491
|
+
get(key: string): string {
|
|
492
|
+
return get_header_map_value_string(this.typ, key);
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Remove a header.
|
|
496
|
+
* @param key the header name.
|
|
497
|
+
*/
|
|
498
|
+
remove(key: string): void {
|
|
499
|
+
remove_header_map_value_string(this.typ, key);
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* get all headers.
|
|
503
|
+
*/
|
|
504
|
+
get_headers(): Headers {
|
|
505
|
+
return get_header_map_pairs(this.typ);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* set all headers.
|
|
510
|
+
*/
|
|
511
|
+
set_headers(headers: Headers): void {
|
|
512
|
+
return set_header_map_pairs(this.typ, headers);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Manipulate request and response headers.
|
|
519
|
+
* Note that request header manipulation will only have effect before the request goes upstream.
|
|
520
|
+
* Response header manipulation can happen only after the response was started and before it
|
|
521
|
+
* was sent downstream.
|
|
522
|
+
*/
|
|
523
|
+
class HeaderMapManipulator {
|
|
524
|
+
request: HeaderStreamManipulator;
|
|
525
|
+
response: HeaderStreamManipulator;
|
|
526
|
+
http_callback: HeaderStreamManipulator;
|
|
527
|
+
constructor(request: HeaderStreamManipulator, response: HeaderStreamManipulator, http_callback: HeaderStreamManipulator) {
|
|
528
|
+
this.request = request;
|
|
529
|
+
this.response = response;
|
|
530
|
+
this.http_callback = http_callback;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Methods to manipulate the current stream. No need to instantiate this class. use the global
|
|
536
|
+
* stream_context variable.
|
|
537
|
+
*/
|
|
538
|
+
class StreamContext {
|
|
539
|
+
headers: HeaderMapManipulator;
|
|
540
|
+
trailers: HeaderMapManipulator;
|
|
541
|
+
constructor(headers: HeaderMapManipulator, trailers: HeaderMapManipulator) {
|
|
542
|
+
this.headers = headers;
|
|
543
|
+
this.trailers = trailers;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Use this variable to manipulate the current stream.
|
|
549
|
+
*/
|
|
550
|
+
export var stream_context = new StreamContext(
|
|
551
|
+
new HeaderMapManipulator(new HeaderStreamManipulator(HeaderMapTypeValues.RequestHeaders), new HeaderStreamManipulator(HeaderMapTypeValues.ResponseHeaders), new HeaderStreamManipulator(HeaderMapTypeValues.HttpCallResponseHeaders)),
|
|
552
|
+
new HeaderMapManipulator(new HeaderStreamManipulator(HeaderMapTypeValues.RequestTrailers), new HeaderStreamManipulator(HeaderMapTypeValues.ResponseTrailers), new HeaderStreamManipulator(HeaderMapTypeValues.HttpCallResponseTrailers)));
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
function get_header_map_value_string(typ: HeaderMapTypeValues, key: string): string {
|
|
556
|
+
const key_buffer = String.UTF8.encode(key);
|
|
557
|
+
let result = get_header_map_value(typ, key_buffer);
|
|
558
|
+
return String.UTF8.decode(result);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
export function get_header_map_value(typ: HeaderMapTypeValues, key: ArrayBuffer): ArrayBuffer {
|
|
562
|
+
let result = imports.proxy_get_header_map_value(typ, changetype<usize>(key), key.byteLength, globalArrayBufferReference.bufferPtr(), globalArrayBufferReference.sizePtr());
|
|
563
|
+
if (result == WasmResultValues.Ok) {
|
|
564
|
+
return globalArrayBufferReference.toArrayBuffer()
|
|
565
|
+
}
|
|
566
|
+
return new ArrayBuffer(0);
|
|
567
|
+
}
|
|
568
|
+
function get_header_map_flat_pairs(typ: HeaderMapTypeValues): ArrayBuffer {
|
|
569
|
+
let result = imports.proxy_get_header_map_pairs(typ, globalArrayBufferReference.bufferPtr(), globalArrayBufferReference.sizePtr());
|
|
570
|
+
if (result == WasmResultValues.Ok) {
|
|
571
|
+
return globalArrayBufferReference.toArrayBuffer()
|
|
572
|
+
}
|
|
573
|
+
return new ArrayBuffer(0);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function get_header_map_pairs(typ: HeaderMapTypeValues): Headers {
|
|
577
|
+
let pairs = get_header_map_flat_pairs(typ);
|
|
578
|
+
return deserializeHeaders(pairs);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function set_header_map_flat_pairs(typ: HeaderMapTypeValues, flat_headers: ArrayBuffer): void {
|
|
582
|
+
CHECK_RESULT(imports.proxy_set_header_map_pairs(typ, changetype<usize>(flat_headers), flat_headers.byteLength));
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
function set_header_map_pairs(typ: HeaderMapTypeValues, headers: Headers): void {
|
|
586
|
+
let flat_headers = serializeHeaders(headers);
|
|
587
|
+
set_header_map_flat_pairs(typ, flat_headers);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
export function replace_header_map_value_string(typ: HeaderMapTypeValues, key: string, value: string): void {
|
|
591
|
+
let key_arr = String.UTF8.encode(key);
|
|
592
|
+
let value_arr = String.UTF8.encode(value);
|
|
593
|
+
replace_header_map_value(typ, key_arr, value_arr);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
export function replace_header_map_value(typ: HeaderMapTypeValues, key: ArrayBuffer, value: ArrayBuffer): void {
|
|
597
|
+
CHECK_RESULT(imports.proxy_replace_header_map_value(typ, changetype<usize>(key), key.byteLength, changetype<usize>(value), value.byteLength));
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
export function remove_header_map_value_string(typ: HeaderMapTypeValues, key: string): void {
|
|
601
|
+
let key_arr = String.UTF8.encode(key);
|
|
602
|
+
remove_header_map_value(typ, key_arr);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
export function remove_header_map_value(typ: HeaderMapTypeValues, key: ArrayBuffer): void {
|
|
606
|
+
CHECK_RESULT(imports.proxy_remove_header_map_value(typ, changetype<usize>(key), key.byteLength));
|
|
607
|
+
}
|
|
608
|
+
export function get_header_map_size(typ: HeaderMapTypeValues): usize {
|
|
609
|
+
let size = globalUsizeRef;
|
|
610
|
+
CHECK_RESULT(imports.proxy_get_header_map_size(typ, size.ptr()));
|
|
611
|
+
return size.data;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// unclear if start and length are 64 or 32
|
|
615
|
+
export function get_buffer_bytes(typ: BufferTypeValues, start: u32, length: u32): ArrayBuffer {
|
|
616
|
+
let result = imports.proxy_get_buffer_bytes(typ, start, length, globalArrayBufferReference.bufferPtr(), globalArrayBufferReference.sizePtr());
|
|
617
|
+
// TODO: return the result as well. not sure what the best way to do this as it doesn't seem that
|
|
618
|
+
// assembly scripts supports tuples.
|
|
619
|
+
if (result == WasmResultValues.Ok) {
|
|
620
|
+
return globalArrayBufferReference.toArrayBuffer()
|
|
621
|
+
}
|
|
622
|
+
return new ArrayBuffer(0);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// returning tuples is not supported.
|
|
626
|
+
class BufferStatusResult {
|
|
627
|
+
result: WasmResultValues;
|
|
628
|
+
length: usize;
|
|
629
|
+
flags: u32;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
export function get_buffer_status(typ: BufferTypeValues): BufferStatusResult {
|
|
633
|
+
let length_ptr = globalUsizeRef;
|
|
634
|
+
let flags_ptr = globalU32Ref;
|
|
635
|
+
let result = imports.proxy_get_buffer_status(typ, length_ptr.ptr(), flags_ptr.ptr());
|
|
636
|
+
let resultTuple = new BufferStatusResult();
|
|
637
|
+
resultTuple.result = result;
|
|
638
|
+
if (result == WasmResultValues.Ok) {
|
|
639
|
+
resultTuple.length = length_ptr.data;
|
|
640
|
+
resultTuple.flags = flags_ptr.data;
|
|
641
|
+
return resultTuple;
|
|
642
|
+
}
|
|
643
|
+
return resultTuple;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
class Metric {
|
|
647
|
+
metric_id: u32;
|
|
648
|
+
|
|
649
|
+
constructor(typ: MetricTypeValues, name: string) {
|
|
650
|
+
let metric_res = define_metric(typ, name);
|
|
651
|
+
if (metric_res.result != WasmResultValues.Ok) {
|
|
652
|
+
throw new Error("can't define metric")
|
|
653
|
+
}
|
|
654
|
+
this.metric_id = metric_res.metric_id;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
export class Gauge extends Metric {
|
|
659
|
+
|
|
660
|
+
constructor(name: string) {
|
|
661
|
+
super(MetricTypeValues.Gauge, name);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
increment(offset: i64): WasmResultValues {
|
|
665
|
+
return imports.proxy_increment_metric(this.metric_id, offset);
|
|
666
|
+
}
|
|
667
|
+
record(metric_id: u32, value: u64): WasmResultValues {
|
|
668
|
+
return imports.proxy_record_metric(metric_id, value);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
export class Histogram extends Metric {
|
|
673
|
+
constructor(name: string) {
|
|
674
|
+
super(MetricTypeValues.Histogram, name);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
increment(offset: i64): WasmResultValues {
|
|
678
|
+
return imports.proxy_increment_metric(this.metric_id, offset);
|
|
679
|
+
}
|
|
680
|
+
record(metric_id: u32, value: u64): WasmResultValues {
|
|
681
|
+
return imports.proxy_record_metric(metric_id, value);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
export class Counter extends Metric {
|
|
686
|
+
constructor(name: string) {
|
|
687
|
+
super(MetricTypeValues.Counter, name);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
increment(offset: u32): WasmResultValues {
|
|
691
|
+
return imports.proxy_increment_metric(this.metric_id, offset);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
class MetricResult {
|
|
696
|
+
result: WasmResultValues;
|
|
697
|
+
metric_id: u32;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
export function define_metric(typ: MetricTypeValues, name: string): MetricResult {
|
|
701
|
+
let metric_id = globalU32Ref;
|
|
702
|
+
let nameutf8 = String.UTF8.encode(name);
|
|
703
|
+
let res = imports.proxy_define_metric(typ, changetype<usize>(nameutf8), nameutf8.byteLength, metric_id.ptr());
|
|
704
|
+
let result = new MetricResult();
|
|
705
|
+
result.result = res;
|
|
706
|
+
if (res == WasmResultValues.Ok) {
|
|
707
|
+
result.metric_id = metric_id.data;
|
|
708
|
+
}
|
|
709
|
+
return result;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
export function increment_metric(metric_id: u32, offset: i64): WasmResultValues {
|
|
713
|
+
return imports.proxy_increment_metric(metric_id, offset);
|
|
714
|
+
}
|
|
715
|
+
export function record_metric(metric_id: u32, value: u64): WasmResultValues {
|
|
716
|
+
return imports.proxy_record_metric(metric_id, value);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
class MetricData {
|
|
720
|
+
result: WasmResultValues;
|
|
721
|
+
data: u64;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
export function get_metric(metric_id: u32): MetricData {
|
|
725
|
+
let metric_data = globalU64Ref;
|
|
726
|
+
let res = imports.proxy_record_metric(metric_id, metric_data.ptr());
|
|
727
|
+
let result = new MetricData();
|
|
728
|
+
result.result = res;
|
|
729
|
+
if (res == WasmResultValues.Ok) {
|
|
730
|
+
result.data = metric_data.data;
|
|
731
|
+
}
|
|
732
|
+
return result;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
export function done(): WasmResultValues { return imports.proxy_done(); }
|
|
736
|
+
|
|
737
|
+
//Only exporting this function while we are still working on the http call support.
|
|
738
|
+
//Once we decided how to read http response headers and pass those to the callback function,
|
|
739
|
+
// we should remove this export and make sure this proxy call is only made thru BaseContext.setEffectiveContext().
|
|
740
|
+
export function proxy_set_effective_context(_id: u32): WasmResultValues {
|
|
741
|
+
const result = imports.proxy_set_effective_context(_id);
|
|
742
|
+
if (result != WasmResultValues.Ok) {
|
|
743
|
+
log(LogLevelValues.critical, "Unable to set effective context: " + _id.toString() + " with result: " + result.toString());
|
|
744
|
+
}
|
|
745
|
+
return result;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Call foreign function with the specified name and argument
|
|
750
|
+
* @param function_name the function name
|
|
751
|
+
* @param argument the parameters
|
|
752
|
+
*/
|
|
753
|
+
export function call_foreign_function(function_name: string, argument: string): ArrayBuffer {
|
|
754
|
+
let function_name_buffer = String.UTF8.encode(function_name);
|
|
755
|
+
let argument_name_buffer = String.UTF8.encode(argument);
|
|
756
|
+
|
|
757
|
+
CHECK_RESULT(imports.proxy_call_foreign_function(
|
|
758
|
+
changetype<usize>(function_name_buffer),
|
|
759
|
+
function_name_buffer.byteLength,
|
|
760
|
+
changetype<usize>(argument_name_buffer),
|
|
761
|
+
argument_name_buffer.byteLength,
|
|
762
|
+
globalArrayBufferReference.bufferPtr(),
|
|
763
|
+
globalArrayBufferReference.sizePtr()));
|
|
764
|
+
return globalArrayBufferReference.toArrayBuffer();
|
|
765
|
+
}
|
|
766
|
+
/////// runtime support
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Sets the effective context id to this context. this is useful for example if you receive an
|
|
770
|
+
* http call in a RootContext, and want to modify headers based on the response in a regular
|
|
771
|
+
* Context. You then will call `setEffectiveContext(_id)` so that the header manipulation will
|
|
772
|
+
* occur in the request context and not in the root context.
|
|
773
|
+
* @param _id
|
|
774
|
+
*/
|
|
775
|
+
function setEffectiveContext(_id: u32): WasmResultValues {
|
|
776
|
+
return proxy_set_effective_context(_id);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* BaseContexts contains things that are common to RootContext and Context.
|
|
781
|
+
*/
|
|
782
|
+
export abstract class BaseContext {
|
|
783
|
+
context_id: u32;
|
|
784
|
+
|
|
785
|
+
constructor(context_id_: u32) {
|
|
786
|
+
this.context_id = context_id_;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
continueRequest(): void {
|
|
790
|
+
const result = imports.proxy_continue_stream(StreamTypeValues.Request);
|
|
791
|
+
if (result != WasmResultValues.Ok) {
|
|
792
|
+
log(LogLevelValues.critical, "Unable to continue request: " + result.toString());
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// Called when the VM is being torn down.
|
|
797
|
+
onDone(): bool {
|
|
798
|
+
log(LogLevelValues.debug, "context id: " + this.context_id.toString() + ": onDone()");
|
|
799
|
+
return true;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// Called when the VM is being torn down.
|
|
803
|
+
onDelete(): void {
|
|
804
|
+
log(LogLevelValues.debug, "context id: " + this.context_id.toString() + ": onDelete()");
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* Wrapper around http callbacks. When AS script supports closures, we can refactor \ remove this.
|
|
810
|
+
*/
|
|
811
|
+
export class HttpCallback {
|
|
812
|
+
origin_context: BaseContext;
|
|
813
|
+
cb: (origin_context: BaseContext, headers: u32, body_size: usize, trailers: u32) => void;
|
|
814
|
+
constructor(origin_context: BaseContext, cb: (origin_context: BaseContext, headers: u32, body_size: usize, trailers: u32) => void) {
|
|
815
|
+
this.origin_context = origin_context;
|
|
816
|
+
this.cb = cb;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
class GrpcCallback {
|
|
820
|
+
ctx: Object;
|
|
821
|
+
cb: (c: Object) => void;
|
|
822
|
+
constructor(ctx: Object, cb: (c: Context) => void) {
|
|
823
|
+
this.ctx = ctx;
|
|
824
|
+
this.cb = cb;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
/**
|
|
829
|
+
* A root context represents a class of instance specific contexts. This is usually used to hold
|
|
830
|
+
* configuration that is used in the individual contexts.
|
|
831
|
+
*/
|
|
832
|
+
export class RootContext extends BaseContext {
|
|
833
|
+
private configuration_: string = "";
|
|
834
|
+
private http_calls_: Map<u32, HttpCallback> = new Map();
|
|
835
|
+
private grpc_calls_: Map<u32, GrpcCallback> = new Map();
|
|
836
|
+
|
|
837
|
+
constructor(context_id: u32) {
|
|
838
|
+
super(context_id);
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
getConfiguration(): string {
|
|
842
|
+
return this.configuration_;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// Need to be overloaded on Filter Root Context implementation
|
|
846
|
+
createContext(context_id: u32): Context {
|
|
847
|
+
log(LogLevelValues.error, "Base createContext called - this should never happen!");
|
|
848
|
+
return new Context(context_id, this);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// Cancels all pending http requests. Called automatically on onDone.
|
|
852
|
+
cancelPendingRequests(): void {
|
|
853
|
+
log(LogLevelValues.debug, "context id: " + this.context_id.toString() + ": cancelPendingRequests()");
|
|
854
|
+
const callbacks = this.http_calls_.values();
|
|
855
|
+
for (let i = 0; i < callbacks.length; ++i) {
|
|
856
|
+
// Calling callbacks with no response
|
|
857
|
+
// TODO: return some parameter telling the filter that these requests were canceled.
|
|
858
|
+
callbacks[i].cb(callbacks[i].origin_context, 0, 0, 0);
|
|
859
|
+
}
|
|
860
|
+
let keys = this.http_calls_.keys();
|
|
861
|
+
for (let i = 0; i < keys.length; ++i) {
|
|
862
|
+
let key = keys[i];
|
|
863
|
+
// TODO cancel pending http requests and call callbacks with failure.?
|
|
864
|
+
// when it becomes possible in the proxy.
|
|
865
|
+
}
|
|
866
|
+
this.http_calls_.clear()
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// Can be used to validate the configuration (e.g. in the control plane). Returns false if the
|
|
870
|
+
// configuration is invalid.
|
|
871
|
+
validateConfiguration(configuration_size: usize): bool {
|
|
872
|
+
log(LogLevelValues.debug, "context id: " + this.context_id.toString() + ": validateConfiguration(configuration_size:" + configuration_size.toString() + ")");
|
|
873
|
+
return true;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
// Called once when the VM loads and once when each hook loads and whenever configuration changes.
|
|
877
|
+
// Returns false if the configuration is invalid.
|
|
878
|
+
onConfigure(configuration_size: u32): bool {
|
|
879
|
+
log(LogLevelValues.debug, "context id: " + this.context_id.toString() + ": onConfigure(configuration_size: " + configuration_size.toString() + ")");
|
|
880
|
+
if (configuration_size == 0) {
|
|
881
|
+
// No config
|
|
882
|
+
return true
|
|
883
|
+
}
|
|
884
|
+
CHECK_RESULT(imports.proxy_get_buffer_bytes(BufferTypeValues.PluginConfiguration, 0, configuration_size, globalArrayBufferReference.bufferPtr(), globalArrayBufferReference.sizePtr()));
|
|
885
|
+
this.configuration_ = String.UTF8.decode(globalArrayBufferReference.toArrayBuffer());
|
|
886
|
+
log(LogLevelValues.debug, "context id: " + this.context_id.toString() + ": Updating this.configuration=" + this.configuration_);
|
|
887
|
+
return true;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
// Called when each hook loads. Returns false if the configuration is invalid.
|
|
891
|
+
onStart(vm_configuration_size: usize): bool {
|
|
892
|
+
log(LogLevelValues.debug, "context id: " + this.context_id.toString() + ": onStart(vm_configuration_size:" + vm_configuration_size.toString() + ")");
|
|
893
|
+
return true;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// Called when the timer goes off.
|
|
897
|
+
onTick(): void {
|
|
898
|
+
log(LogLevelValues.debug, "context id: " + this.context_id.toString() + ": onTick()");
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
// Calleed when
|
|
902
|
+
onQueueReady(token: u32): void {
|
|
903
|
+
log(LogLevelValues.debug, "context id: " + this.context_id.toString() + ": onQueueReady(token:" + token.toString() + ")");
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// Called when the VM is being torn down.
|
|
907
|
+
onDone(): bool {
|
|
908
|
+
log(LogLevelValues.debug, "context id: " + this.context_id.toString() + ": onDone()");
|
|
909
|
+
this.cancelPendingRequests();
|
|
910
|
+
return true;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// Report that we are now done following returning false from onDone.
|
|
914
|
+
done(): void {
|
|
915
|
+
log(LogLevelValues.debug, "context id: " + this.context_id.toString() + ": done()");
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
/**
|
|
919
|
+
* Make an http call.
|
|
920
|
+
* @param cluster The cluster name to make http request to.
|
|
921
|
+
* @param headers The headers of the request.
|
|
922
|
+
* @param body The body of the request.
|
|
923
|
+
* @param trailers The trailers of the request.
|
|
924
|
+
* @param timeout_milliseconds Timeout for the request, in milliseconds.
|
|
925
|
+
* @param cb Callback to be invoked when the request is complete.
|
|
926
|
+
*/
|
|
927
|
+
httpCall(cluster: string, headers: Headers, body: ArrayBuffer, trailers: Headers, timeout_milliseconds: u32, origin_context: BaseContext, cb: (origin_context: BaseContext, headers: u32, body_size: usize, trailers: u32) => void): WasmResultValues {
|
|
928
|
+
log(LogLevelValues.debug, "context id: " + this.context_id.toString() + ": httpCall(cluster: " + cluster + ", headers:" + headers.toString() + ", body:" + body.toString() + ", trailers:" + trailers.toString() + ")");
|
|
929
|
+
let buffer = String.UTF8.encode(cluster);
|
|
930
|
+
let header_pairs = serializeHeaders(headers);
|
|
931
|
+
let trailer_pairs = serializeHeaders(trailers);
|
|
932
|
+
let token = new Reference<u32>();
|
|
933
|
+
let result = imports.proxy_http_call(changetype<usize>(buffer), buffer.byteLength, changetype<usize>(header_pairs), header_pairs.byteLength, changetype<usize>(body), body.byteLength, changetype<usize>(trailer_pairs), trailer_pairs.byteLength, timeout_milliseconds, token.ptr());
|
|
934
|
+
log(LogLevelValues.debug, "Http call executed with result: " + result.toString());
|
|
935
|
+
if (result == WasmResultValues.Ok) {
|
|
936
|
+
log(LogLevelValues.debug, "set token: " + token.data.toString() + " on " + this.context_id.toString());
|
|
937
|
+
this.http_calls_.set(token.data, new HttpCallback(origin_context, cb));
|
|
938
|
+
}
|
|
939
|
+
return result;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
onHttpCallResponse(token: u32, headers: u32, body_size: u32, trailers: u32): void {
|
|
943
|
+
log(LogLevelValues.debug, "context id: " + this.context_id.toString() + ": onHttpCallResponse(token: " + token.toString() + ", headers:" + headers.toString() + ", body_size:" + body_size.toString() + ", trailers:" + trailers.toString() + ")");
|
|
944
|
+
log(LogLevelValues.debug, "http_calls_: " + this.http_calls_.size.toString());
|
|
945
|
+
log(LogLevelValues.debug, "get token: " + token.toString() + " from " + this.context_id.toString());
|
|
946
|
+
log(LogLevelValues.debug, "context_map: " + context_map.keys().join(", "));
|
|
947
|
+
if (this.http_calls_.has(token)) {
|
|
948
|
+
let callback = this.http_calls_.get(token);
|
|
949
|
+
log(LogLevelValues.debug, "onHttpCallResponse: calling callback for context id: " + callback.origin_context.context_id.toString());
|
|
950
|
+
this.http_calls_.delete(token);
|
|
951
|
+
setEffectiveContext(callback.origin_context.context_id);
|
|
952
|
+
callback.cb(callback.origin_context, headers, body_size, trailers);
|
|
953
|
+
} else {
|
|
954
|
+
log(LogLevelValues.error, "onHttpCallResponse: Token " + token.toString() + " not found.");
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
on_grpc_receive_initial_metadata(token: u32, headers: u32): void { }
|
|
959
|
+
on_grpc_trailing_metadata(token: u32, trailers: u32): void { }
|
|
960
|
+
on_grpc_receive(token: u32, response_size: u32): void { }
|
|
961
|
+
on_grpc_close(token: u32, status_code: u32): void { }
|
|
962
|
+
|
|
963
|
+
/*
|
|
964
|
+
grpc_call(service_proto:ArrayBuffer, service_name:string, method_name:string, request :ArrayBuffer, timeout_milliseconds : u32): WasmResultValues {
|
|
965
|
+
let service_name_buffer = String.UTF8.encode(service_name);
|
|
966
|
+
let method_name_buffer = String.UTF8.encode(method_name);
|
|
967
|
+
let token = globalU32Ref;
|
|
968
|
+
let result = imports.proxy_grpc_call(changetype<usize>(service_proto), service_proto.byteLength,
|
|
969
|
+
changetype<usize>(service_name_buffer), service_name_buffer.byteLength,
|
|
970
|
+
changetype<usize>(method_name_buffer), method_name_buffer.byteLength,
|
|
971
|
+
changetype<usize>(request), request.byteLength, timeout_milliseconds, token.ptr());
|
|
972
|
+
if (result == WasmResultValues.Ok) {
|
|
973
|
+
this.grpc_calls_.set(token.data, new GrpcCallback(ctx, cb));
|
|
974
|
+
}
|
|
975
|
+
return result;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
grpc_stream(service_ptr, service_size, service_name_ptr, service_name_size, method_name_ptr, method_name_size, token_ptr) { return 0; },
|
|
979
|
+
// {proxy_grpc_cancel as grpc_cancel,proxy_grpc_close as grpc_close,proxy_grpc_send as grpc_send} from "./imports";
|
|
980
|
+
*/
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* Context class the base for class for entities that are per-request or per-connection.
|
|
985
|
+
*/
|
|
986
|
+
export class Context extends BaseContext {
|
|
987
|
+
|
|
988
|
+
root_context: RootContext;
|
|
989
|
+
|
|
990
|
+
constructor(context_id_: u32, root_context: RootContext) {
|
|
991
|
+
super(context_id_);
|
|
992
|
+
this.root_context = root_context;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
createContext(): Context {
|
|
996
|
+
log(LogLevelValues.critical, "ctx: can't create context");
|
|
997
|
+
throw new Error("not implemented");
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
onNewConnection(): FilterStatusValues {
|
|
1001
|
+
log(LogLevelValues.debug, "context id: " + this.context_id.toString() + ": onNewConnection()");
|
|
1002
|
+
return FilterStatusValues.Continue;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
onDownstreamData(size: usize, end: bool): FilterStatusValues {
|
|
1006
|
+
log(LogLevelValues.debug, "context id: " + this.context_id.toString() + ": onDownstreamData(size: " + size.toString() + ", end: " + end.toString() + ")");
|
|
1007
|
+
return FilterStatusValues.Continue;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
onUpstreamData(size: usize, end: bool): FilterStatusValues {
|
|
1011
|
+
log(LogLevelValues.debug, "context id: " + this.context_id.toString() + ": onUpstreamData(size: " + size.toString() + ", end: " + end.toString() + ")");
|
|
1012
|
+
return FilterStatusValues.Continue;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
onDownstreamConnectionClose(t: PeerTypeValues): void {
|
|
1016
|
+
log(LogLevelValues.debug, "context id: " + this.context_id.toString() + ": onDownstreamConnectionClose(t: " + t.toString() + ")");
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
onUpstreamConnectionClose(t: PeerTypeValues): void {
|
|
1020
|
+
log(LogLevelValues.debug, "context id: " + this.context_id.toString() + ": onUpstreamConnectionClose(t: " + t.toString() + ")");
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { return FilterHeadersStatusValues.Continue }
|
|
1024
|
+
onRequestMetadata(a: u32): FilterMetadataStatusValues { return FilterMetadataStatusValues.Continue }
|
|
1025
|
+
onRequestBody(body_buffer_length: usize, end_of_stream: bool): FilterDataStatusValues { return FilterDataStatusValues.Continue }
|
|
1026
|
+
onRequestTrailers(a: u32): FilterTrailersStatusValues { return FilterTrailersStatusValues.Continue }
|
|
1027
|
+
onResponseHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { return FilterHeadersStatusValues.Continue }
|
|
1028
|
+
onResponseMetadata(a: u32): FilterMetadataStatusValues { return FilterMetadataStatusValues.Continue }
|
|
1029
|
+
onResponseBody(body_buffer_length: usize, end_of_stream: bool): FilterDataStatusValues { return FilterDataStatusValues.Continue }
|
|
1030
|
+
onResponseTrailers(s: u32): FilterTrailersStatusValues { return FilterTrailersStatusValues.Continue }
|
|
1031
|
+
|
|
1032
|
+
// Called after onDone when logging is requested.
|
|
1033
|
+
onLog(): void {
|
|
1034
|
+
log(LogLevelValues.debug, "context id: " + this.context_id.toString() + ": onLog()");
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
setEffectiveContext(): WasmResultValues {
|
|
1038
|
+
return imports.proxy_set_effective_context(this.context_id);
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
function get_plugin_root_id(): string {
|
|
1043
|
+
let root_id = get_property("plugin_root_id");
|
|
1044
|
+
if (root_id.byteLength == 0) {
|
|
1045
|
+
return "";
|
|
1046
|
+
}
|
|
1047
|
+
return String.UTF8.decode(root_id);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
const undefined_context_factory: (context_id: u32) => RootContext = (context_id: u32) => { return new RootContext(0); };
|
|
1051
|
+
let root_context_factory: (context_id: u32) => RootContext = undefined_context_factory;
|
|
1052
|
+
|
|
1053
|
+
let root_context: RootContext | null;
|
|
1054
|
+
let context_map = new Map<u32, Context>();
|
|
1055
|
+
|
|
1056
|
+
//create root context if doesn't exist
|
|
1057
|
+
export function ensureRootContext(root_context_id: u32): RootContext {
|
|
1058
|
+
log(LogLevelValues.debug, "ensureRootContext(root_context_id: " + root_context_id.toString() + ")");
|
|
1059
|
+
if (root_context != null) {
|
|
1060
|
+
log(LogLevelValues.debug, "Returning root context");
|
|
1061
|
+
return getRootContext(root_context_id);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
log(LogLevelValues.debug, "Registering new root context with id: " + root_context_id.toString());
|
|
1065
|
+
if (root_context_factory == undefined_context_factory) {
|
|
1066
|
+
throw new Error("Missing root context factory");
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
let context = root_context_factory(root_context_id);
|
|
1070
|
+
context.context_id = root_context_id;
|
|
1071
|
+
root_context = context;
|
|
1072
|
+
|
|
1073
|
+
return root_context as RootContext;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
// create a context if doesnt exist.
|
|
1077
|
+
export function ensureContext(context_id: u32, root_context_id: u32): void {
|
|
1078
|
+
log(LogLevelValues.debug, "ensureContext(context_id: " + context_id.toString() + ", root_context_id: " + root_context_id.toString() + ")");
|
|
1079
|
+
log(LogLevelValues.debug, "Current context_map: " + context_map.keys().join(", "));
|
|
1080
|
+
const root_context = ensureRootContext(root_context_id);
|
|
1081
|
+
if (context_map.has(context_id)) {
|
|
1082
|
+
log(LogLevelValues.debug, "Existing context id: " + context_id.toString());
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
log(LogLevelValues.debug, "Registering new context with context_id: " + context_id.toString() + " under root_context: " + root_context_id.toString());
|
|
1086
|
+
let context = root_context.createContext(context_id);
|
|
1087
|
+
context_map.set(context_id, context);
|
|
1088
|
+
log(LogLevelValues.debug, "Updated context_map: " + context_map.keys().join(", "));
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
export function getBaseContext(context_id: u32): BaseContext {
|
|
1092
|
+
return context_map.get(context_id) as BaseContext;
|
|
1093
|
+
}
|
|
1094
|
+
export function getContext(context_id: u32): Context {
|
|
1095
|
+
return context_map.get(context_id) as Context;
|
|
1096
|
+
}
|
|
1097
|
+
export function getRootContext(context_id: u32): RootContext {
|
|
1098
|
+
return root_context as RootContext;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
export function deleteContext(context_id: u32): void {
|
|
1102
|
+
context_map.delete(context_id);
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
/**
|
|
1106
|
+
* Register a root context factory and make it available to the runtime.
|
|
1107
|
+
* @param root_context_factory A function that creates a new root context.
|
|
1108
|
+
* @param name Paremeter kept for backwards compatibility. It will be ignored.
|
|
1109
|
+
*/
|
|
1110
|
+
export function registerRootContext(
|
|
1111
|
+
context_factory: (context_id: u32) => RootContext,
|
|
1112
|
+
name: string): void {
|
|
1113
|
+
root_context_factory = context_factory;
|
|
1114
|
+
}
|