github.com/tetratelabs/proxy-wasm-go-sdk@v0.23.1-0.20240517021853-021aa9cf78e8/proxywasm/proxytest/wasmwrapper.go (about) 1 // Copyright 2020-2022 Tetrate 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package proxytest 16 17 import ( 18 "context" 19 "io" 20 "os" 21 "reflect" 22 "unsafe" 23 24 "github.com/tetratelabs/wazero" 25 "github.com/tetratelabs/wazero/api" 26 "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 27 28 "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/internal" 29 "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types" 30 ) 31 32 type guestABI struct { 33 proxyOnVMStart api.Function 34 proxyOnContextCreate api.Function 35 proxyOnConfigure api.Function 36 proxyOnDone api.Function 37 proxyOnQueueReady api.Function 38 proxyOnTick api.Function 39 proxyOnRequestHeaders api.Function 40 proxyOnRequestBody api.Function 41 proxyOnRequestTrailers api.Function 42 proxyOnResponseHeaders api.Function 43 proxyOnResponseBody api.Function 44 proxyOnResponseTrailers api.Function 45 proxyOnLog api.Function 46 } 47 48 // WasmVMContext is a VMContext that delegates execution to a compiled wasm binary. 49 type WasmVMContext interface { 50 types.VMContext 51 io.Closer 52 } 53 54 // vmContext implements WasmVMContext. 55 type vmContext struct { 56 runtime wazero.Runtime 57 abi guestABI 58 ctx context.Context 59 } 60 61 // NewWasmVMContext returns a types.VMContext that delegates plugin invocations to the provided compiled wasm binary. 62 // proxytest can be run with a compiled wasm binary by passing this to proxytest.WithVMContext. 63 // 64 // Running proxytest with the compiled wasm binary helps to ensure that the plugin will run when actually compiled with 65 // TinyGo, however stack traces and other debug features will be much worse. It is recommended to run unit tests both 66 // with Go and with wasm. Tests will run much faster under Go for quicker development cycles, and the wasm runner can 67 // confirm the behavior matches when actually compiled. 68 // 69 // For example, this snippet allows determining the types.VMContext based on a test case flag. 70 // 71 // var vm types.VMContext 72 // switch runner { 73 // case "go": 74 // vm = &vmContext{} 75 // case "wasm": 76 // wasm, err := os.ReadFile("plugin.wasm") 77 // if err != nil { 78 // t.Skip("wasm not found") 79 // } 80 // v, err := proxytest.NewWasmVMContext(wasm) 81 // require.NoError(t, err) 82 // vm = v 83 // } 84 // 85 // Note: Currently only HTTP plugins are supported. 86 func NewWasmVMContext(wasm []byte) (WasmVMContext, error) { 87 ctx := context.Background() 88 r := wazero.NewRuntime(ctx) 89 90 _, err := wasi_snapshot_preview1.Instantiate(ctx, r) 91 if err != nil { 92 return nil, err 93 } 94 95 err = exportHostABI(ctx, r) 96 if err != nil { 97 return nil, err 98 } 99 100 compiled, err := r.CompileModule(ctx, wasm) 101 if err != nil { 102 return nil, err 103 } 104 105 wazeroconfig := wazero.NewModuleConfig().WithStdout(os.Stderr).WithStderr(os.Stderr) 106 mod, err := r.InstantiateModule(ctx, compiled, wazeroconfig) 107 if err != nil { 108 return nil, err 109 } 110 111 abi := guestABI{ 112 proxyOnVMStart: mod.ExportedFunction("proxy_on_vm_start"), 113 proxyOnContextCreate: mod.ExportedFunction("proxy_on_context_create"), 114 proxyOnConfigure: mod.ExportedFunction("proxy_on_configure"), 115 proxyOnDone: mod.ExportedFunction("proxy_on_done"), 116 proxyOnQueueReady: mod.ExportedFunction("proxy_on_queue_ready"), 117 proxyOnTick: mod.ExportedFunction("proxy_on_tick"), 118 proxyOnRequestHeaders: mod.ExportedFunction("proxy_on_request_headers"), 119 proxyOnRequestBody: mod.ExportedFunction("proxy_on_request_body"), 120 proxyOnRequestTrailers: mod.ExportedFunction("proxy_on_request_trailers"), 121 proxyOnResponseHeaders: mod.ExportedFunction("proxy_on_response_headers"), 122 proxyOnResponseBody: mod.ExportedFunction("proxy_on_response_body"), 123 proxyOnResponseTrailers: mod.ExportedFunction("proxy_on_response_trailers"), 124 proxyOnLog: mod.ExportedFunction("proxy_on_log"), 125 } 126 127 return &vmContext{ 128 runtime: r, 129 abi: abi, 130 ctx: ctx, 131 }, nil 132 } 133 134 // OnVMStart implements the same method on types.VMContext. 135 func (v *vmContext) OnVMStart(vmConfigurationSize int) types.OnVMStartStatus { 136 rootContextID := uint64(0) // unused 137 res, err := v.abi.proxyOnVMStart.Call(v.ctx, rootContextID, uint64(vmConfigurationSize)) 138 handleErr(err) 139 return res[0] == 1 140 } 141 142 // NewPluginContext implements the same method on types.VMContext. 143 func (v *vmContext) NewPluginContext(contextID uint32) types.PluginContext { 144 _, err := v.abi.proxyOnContextCreate.Call(v.ctx, uint64(contextID), 0) 145 handleErr(err) 146 return &pluginContext{ 147 id: uint64(contextID), 148 abi: v.abi, 149 ctx: withPluginContextID(v.ctx, contextID), 150 } 151 } 152 153 // Close implements the same method on io.Closer. 154 func (v *vmContext) Close() error { 155 return v.runtime.Close(v.ctx) 156 } 157 158 // pluginContext implements types.PluginContext. 159 type pluginContext struct { 160 id uint64 161 abi guestABI 162 ctx context.Context 163 } 164 165 // OnPluginStart implements the same method on types.PluginContext. 166 func (p *pluginContext) OnPluginStart(pluginConfigurationSize int) types.OnPluginStartStatus { 167 res, err := p.abi.proxyOnConfigure.Call(p.ctx, p.id, uint64(pluginConfigurationSize)) 168 handleErr(err) 169 return res[0] == 1 170 } 171 172 // OnPluginDone implements the same method on types.PluginContext. 173 func (p *pluginContext) OnPluginDone() bool { 174 res, err := p.abi.proxyOnDone.Call(p.ctx, p.id) 175 handleErr(err) 176 return res[0] == 1 177 } 178 179 // OnQueueReady implements the same method on types.PluginContext. 180 func (p *pluginContext) OnQueueReady(queueID uint32) { 181 _, err := p.abi.proxyOnQueueReady.Call(p.ctx, p.id, uint64(queueID)) 182 handleErr(err) 183 } 184 185 // OnTick implements the same method on types.PluginContext. 186 func (p *pluginContext) OnTick() { 187 _, err := p.abi.proxyOnTick.Call(p.ctx, p.id) 188 handleErr(err) 189 } 190 191 // NewTcpContext implements the same method on types.PluginContext. 192 func (p *pluginContext) NewTcpContext(uint32) types.TcpContext { 193 return nil 194 } 195 196 // NewHttpContext implements the same method on types.PluginContext. 197 func (p *pluginContext) NewHttpContext(contextID uint32) types.HttpContext { 198 _, err := p.abi.proxyOnContextCreate.Call(p.ctx, uint64(contextID), p.id) 199 handleErr(err) 200 return &httpContext{ 201 id: uint64(contextID), 202 abi: p.abi, 203 ctx: p.ctx, 204 } 205 } 206 207 // httpContext implements types.HttpContext. 208 type httpContext struct { 209 id uint64 210 abi guestABI 211 ctx context.Context 212 } 213 214 // OnHttpRequestHeaders implements the same method on types.HttpContext. 215 func (h *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action { 216 res, err := h.abi.proxyOnRequestHeaders.Call(h.ctx, h.id, uint64(numHeaders), wasmBool(endOfStream)) 217 handleErr(err) 218 return types.Action(res[0]) 219 } 220 221 // OnHttpRequestBody implements the same method on types.HttpContext. 222 func (h *httpContext) OnHttpRequestBody(bodySize int, endOfStream bool) types.Action { 223 res, err := h.abi.proxyOnRequestBody.Call(h.ctx, h.id, uint64(bodySize), wasmBool(endOfStream)) 224 handleErr(err) 225 return types.Action(res[0]) 226 } 227 228 // OnHttpRequestTrailers implements the same method on types.HttpContext. 229 func (h *httpContext) OnHttpRequestTrailers(numTrailers int) types.Action { 230 res, err := h.abi.proxyOnRequestTrailers.Call(h.ctx, h.id, uint64(numTrailers)) 231 handleErr(err) 232 return types.Action(res[0]) 233 } 234 235 // OnHttpResponseHeaders implements the same method on types.HttpContext. 236 func (h *httpContext) OnHttpResponseHeaders(numHeaders int, endOfStream bool) types.Action { 237 res, err := h.abi.proxyOnResponseHeaders.Call(h.ctx, h.id, uint64(numHeaders), wasmBool(endOfStream)) 238 handleErr(err) 239 return types.Action(res[0]) 240 } 241 242 // OnHttpResponseBody implements the same method on types.HttpContext. 243 func (h *httpContext) OnHttpResponseBody(bodySize int, endOfStream bool) types.Action { 244 res, err := h.abi.proxyOnResponseBody.Call(h.ctx, h.id, uint64(bodySize), wasmBool(endOfStream)) 245 handleErr(err) 246 return types.Action(res[0]) 247 } 248 249 // OnHttpResponseTrailers implements the same method on types.HttpContext. 250 func (h *httpContext) OnHttpResponseTrailers(numTrailers int) types.Action { 251 res, err := h.abi.proxyOnResponseTrailers.Call(h.ctx, h.id, uint64(numTrailers)) 252 handleErr(err) 253 return types.Action(res[0]) 254 } 255 256 // OnHttpStreamDone implements the same method on types.HttpContext. 257 func (h *httpContext) OnHttpStreamDone() { 258 _, err := h.abi.proxyOnLog.Call(h.ctx, h.id) 259 handleErr(err) 260 } 261 262 func handleErr(err error) { 263 if err != nil { 264 panic(err) 265 } 266 } 267 268 func handleMemoryStatus(ok bool) { 269 if !ok { 270 panic("could not access memory") 271 } 272 } 273 274 func wasmBytePtr(mod api.Module, off uint32, size uint32) *byte { 275 if size == 0 { 276 return nil 277 278 } 279 buf, ok := mod.Memory().Read(off, size) 280 handleMemoryStatus(ok) 281 return &buf[0] 282 } 283 284 func copyBytesToWasm(ctx context.Context, mod api.Module, hostPtr *byte, size int, wasmPtrPtr uint32, wasmSizePtr uint32) { 285 if size == 0 { 286 return 287 } 288 var hostSlice []byte 289 hdr := (*reflect.SliceHeader)(unsafe.Pointer(&hostSlice)) 290 hdr.Data = uintptr(unsafe.Pointer(hostPtr)) 291 hdr.Cap = size 292 hdr.Len = size 293 294 alloc := mod.ExportedFunction("proxy_on_memory_allocate") 295 res, err := alloc.Call(ctx, uint64(size)) 296 handleErr(err) 297 buf, ok := mod.Memory().Read(uint32(res[0]), uint32(size)) 298 handleMemoryStatus(ok) 299 300 copy(buf, hostSlice) 301 ok = mod.Memory().WriteUint32Le(wasmPtrPtr, uint32(res[0])) 302 handleMemoryStatus(ok) 303 304 ok = mod.Memory().WriteUint32Le(wasmSizePtr, uint32(size)) 305 handleMemoryStatus(ok) 306 } 307 308 func wasmBool(b bool) uint64 { 309 if b { 310 return 1 311 } 312 return 0 313 } 314 315 func exportHostABI(ctx context.Context, r wazero.Runtime) error { 316 _, err := r.NewHostModuleBuilder("env"). 317 // proxy_log logs a message at the given log_level. 318 // See https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_log 319 NewFunctionBuilder(). 320 WithParameterNames("log_level", "message_data", "message_size"). 321 WithResultNames("call_result"). 322 WithFunc(func(ctx context.Context, mod api.Module, logLevel, messageData, messageSize uint32) uint32 { 323 messageDataPtr := wasmBytePtr(mod, messageData, messageSize) 324 return uint32(internal.ProxyLog(internal.LogLevel(logLevel), messageDataPtr, int(messageSize))) 325 }). 326 Export("proxy_log"). 327 // proxy_set_property sets a property value. 328 // See https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_set_property 329 NewFunctionBuilder(). 330 WithParameterNames("property_path_data", "property_path_size", "property_value_data", 331 "property_value_size"). 332 WithResultNames("call_result"). 333 WithFunc(func(ctx context.Context, mod api.Module, pathData, pathSize, valueData, valueSize uint32) uint32 { 334 pathDataPtr := wasmBytePtr(mod, pathData, pathSize) 335 valueDataPtr := wasmBytePtr(mod, valueData, valueSize) 336 return uint32(internal.ProxySetProperty(pathDataPtr, int(pathSize), valueDataPtr, int(valueSize))) 337 }). 338 Export("proxy_set_property"). 339 // proxy_get_property gets a property value. 340 // See https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_get_property 341 NewFunctionBuilder(). 342 WithParameterNames("property_path_data", "property_path_size", "return_property_value_data", 343 "return_property_value_size"). 344 WithResultNames("call_result"). 345 WithFunc(func(ctx context.Context, mod api.Module, pathData, pathSize, returnValueData, 346 returnValueSize uint32) uint32 { 347 pathDataPtr := wasmBytePtr(mod, pathData, pathSize) 348 var returnValueHostPtr *byte 349 var returnValueSizePtr int 350 ret := uint32(internal.ProxyGetProperty(pathDataPtr, int(pathSize), &returnValueHostPtr, &returnValueSizePtr)) 351 copyBytesToWasm(ctx, mod, returnValueHostPtr, returnValueSizePtr, returnValueData, returnValueSize) 352 return ret 353 }). 354 Export("proxy_get_property"). 355 // proxy_send_local_response sends an HTTP response without forwarding request to the upstream. 356 // 357 // Note: proxy-wasm spec calls this proxy_send_http_response. See 358 // https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_send_http_response 359 NewFunctionBuilder(). 360 WithParameterNames("response_code", "response_code_details_data", "response_code_details_size", 361 "response_body_data", "response_body_size", "additional_headers_map_data", "additional_headers_size", 362 "grpc_status"). 363 WithResultNames("call_result"). 364 WithFunc(func(ctx context.Context, mod api.Module, statusCode, statusCodeDetailData, statusCodeDetailsSize, 365 bodyData, bodySize, headersData, headersSize, grpcStatus uint32) uint32 { 366 statusCodeDetailDataPtr := wasmBytePtr(mod, statusCodeDetailData, statusCodeDetailsSize) 367 bodyDataPtr := wasmBytePtr(mod, bodyData, bodySize) 368 headersDataPtr := wasmBytePtr(mod, headersData, headersSize) 369 return uint32(internal.ProxySendLocalResponse(statusCode, statusCodeDetailDataPtr, 370 int(statusCodeDetailsSize), bodyDataPtr, int(bodySize), headersDataPtr, int(headersSize), int32(grpcStatus))) 371 }). 372 Export("proxy_send_local_response"). 373 // proxy_get_shared_data gets shared data identified by a key. The compare-and-switch value is returned and can 374 // be used when updating the value with proxy_set_shared_data. 375 // See https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_get_shared_data 376 NewFunctionBuilder(). 377 WithParameterNames("key_data", "key_size", "return_value_data", "return_value_size", "return_cas"). 378 WithResultNames("call_result"). 379 WithFunc(func(ctx context.Context, mod api.Module, keyData, keySize, returnValueData, returnValueSize, 380 returnCas uint32) uint32 { 381 keyDataPtr := wasmBytePtr(mod, keyData, keySize) 382 var returnValueHostPtr *byte 383 var returnValueSizePtr int 384 var returnCasPtr uint32 385 ret := uint32(internal.ProxyGetSharedData(keyDataPtr, int(keySize), &returnValueHostPtr, 386 &returnValueSizePtr, &returnCasPtr)) 387 copyBytesToWasm(ctx, mod, returnValueHostPtr, returnValueSizePtr, returnValueData, returnValueSize) 388 handleMemoryStatus(mod.Memory().WriteUint32Le(returnCas, returnCasPtr)) 389 return ret 390 }). 391 Export("proxy_get_shared_data"). 392 // proxy_set_shared_data sets the value of shared data using its key. If compare-and-switch value is set, it 393 // must match the current value. 394 // See https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_set_shared_data 395 NewFunctionBuilder(). 396 WithParameterNames("key_data", "key_size", "value_data", "value_size", "cas"). 397 WithResultNames("call_result"). 398 WithFunc(func(ctx context.Context, mod api.Module, keyData, keySize, valueData, valueSize, cas uint32) uint32 { 399 keyDataPtr := wasmBytePtr(mod, keyData, keySize) 400 valueDataPtr := wasmBytePtr(mod, valueData, valueSize) 401 return uint32(internal.ProxySetSharedData(keyDataPtr, int(keySize), valueDataPtr, int(valueSize), cas)) 402 }). 403 Export("proxy_set_shared_data"). 404 // proxy_register_shared_queue registers a shared queue using a given name. It can be referred to in 405 // proxy_enqueue_shared_queue and proxy_dequeue_shared_queue using the returned ID. 406 // See https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_register_shared_queue 407 NewFunctionBuilder(). 408 WithParameterNames("queue_name_data", "queue_name_size", "return_queue_id"). 409 WithResultNames("call_result"). 410 WithFunc(func(ctx context.Context, mod api.Module, nameData, nameSize, returnID uint32) uint32 { 411 namePtr := wasmBytePtr(mod, nameData, nameSize) 412 var returnIDPtr uint32 413 ret := uint32(internal.ProxyRegisterSharedQueue(namePtr, int(nameSize), &returnIDPtr)) 414 handleMemoryStatus(mod.Memory().WriteUint32Le(returnID, returnIDPtr)) 415 return ret 416 }). 417 Export("proxy_register_shared_queue"). 418 // proxy_resolve_shared_queue resolves existing shared queue using a given name. It can be referred to in 419 // proxy_enqueue_shared_queue and proxy_dequeue_shared_queue using the returned ID. 420 // See https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_resolve_shared_queue 421 // 422 // Note: The "vm_id_data" and "vm_id_size" parameters are not documented in proxy-wasm spec. 423 NewFunctionBuilder(). 424 WithParameterNames("vm_id_data", "vm_id_size", "queue_name_data", "queue_name_size", "return_queue_id"). 425 WithResultNames("call_result"). 426 WithFunc(func(ctx context.Context, mod api.Module, vmIDData, vmIDSize, nameData, nameSize, returnID uint32) uint32 { 427 vmID := wasmBytePtr(mod, vmIDData, vmIDSize) 428 namePtr := wasmBytePtr(mod, nameData, nameSize) 429 var returnIDPtr uint32 430 ret := uint32(internal.ProxyResolveSharedQueue(vmID, int(vmIDSize), namePtr, int(nameSize), &returnIDPtr)) 431 handleMemoryStatus(mod.Memory().WriteUint32Le(returnID, returnIDPtr)) 432 return ret 433 }). 434 Export("proxy_resolve_shared_queue"). 435 // proxy_dequeue_shared_queue gets data from the end of the queue. 436 // See https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_dequeue_shared_queue 437 NewFunctionBuilder(). 438 WithParameterNames("queue_id", "payload_data", "payload_size"). 439 WithResultNames("call_result"). 440 WithFunc(func(ctx context.Context, mod api.Module, queueID, returnValueData, returnValueSize uint32) uint32 { 441 var returnValueHostPtr *byte 442 var returnValueSizePtr int 443 ret := uint32(internal.ProxyDequeueSharedQueue(queueID, &returnValueHostPtr, &returnValueSizePtr)) 444 copyBytesToWasm(ctx, mod, returnValueHostPtr, returnValueSizePtr, returnValueData, returnValueSize) 445 return ret 446 }). 447 Export("proxy_dequeue_shared_queue"). 448 // proxy_enqueue_shared_queue adds data to the front of the queue. 449 // See https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_enqueue_shared_queue 450 NewFunctionBuilder(). 451 WithParameterNames("queue_id", "payload_data", "payload_size"). 452 WithResultNames("call_result"). 453 WithFunc(func(ctx context.Context, mod api.Module, queueID, valueData, valueSize uint32) uint32 { 454 valuePtr := wasmBytePtr(mod, valueData, valueSize) 455 return uint32(internal.ProxyEnqueueSharedQueue(queueID, valuePtr, int(valueSize))) 456 }). 457 Export("proxy_enqueue_shared_queue"). 458 // proxy_get_header_map_value gets the content of key from a given map. 459 // 460 // Note: proxy-wasm-spec calls this proxy_get_map_value. See 461 // https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_get_map_value 462 NewFunctionBuilder(). 463 WithParameterNames("map_type", "key_data", "key_size", "return_value_data", "return_value_size"). 464 WithResultNames("call_result"). 465 WithFunc(func(ctx context.Context, mod api.Module, mapType, keyData, keySize, returnValueData, 466 returnValueSize uint32) uint32 { 467 keyPtr := wasmBytePtr(mod, keyData, keySize) 468 var retValDataHostPtr *byte 469 var retValSizePtr int 470 ret := uint32(internal.ProxyGetHeaderMapValue(internal.MapType(mapType), keyPtr, int(keySize), &retValDataHostPtr, &retValSizePtr)) 471 copyBytesToWasm(ctx, mod, retValDataHostPtr, retValSizePtr, returnValueData, returnValueSize) 472 return ret 473 }). 474 Export("proxy_get_header_map_value"). 475 // proxy_add_header_map_value adds a value to the key of a given map. 476 // 477 // Note: proxy-wasm-spec calls this proxy_add_map_value. See 478 // https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_add_map_value 479 NewFunctionBuilder(). 480 WithParameterNames("map_type", "key_data", "key_size", "value_data", "value_size"). 481 WithResultNames("call_result"). 482 WithFunc(func(ctx context.Context, mod api.Module, mapType, keyData, keySize, valueData, valueSize uint32) uint32 { 483 keyPtr := wasmBytePtr(mod, keyData, keySize) 484 valuePtr := wasmBytePtr(mod, valueData, valueSize) 485 return uint32(internal.ProxyAddHeaderMapValue(internal.MapType(mapType), keyPtr, int(keySize), valuePtr, int(valueSize))) 486 }). 487 Export("proxy_add_header_map_value"). 488 // proxy_replace_header_map_value replaces any value of the key in a given map. 489 // 490 // Note: proxy-wasm-spec calls this proxy_set_map_value. See 491 // https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_set_map_value 492 NewFunctionBuilder(). 493 WithParameterNames("map_type", "key_data", "key_size", "value_data", "value_size"). 494 WithResultNames("call_result"). 495 WithFunc(func(ctx context.Context, mod api.Module, mapType, keyData, keySize, valueData, valueSize uint32) uint32 { 496 keyPtr := wasmBytePtr(mod, keyData, keySize) 497 valuePtr := wasmBytePtr(mod, valueData, valueSize) 498 return uint32(internal.ProxyReplaceHeaderMapValue(internal.MapType(mapType), keyPtr, int(keySize), valuePtr, int(valueSize))) 499 }). 500 Export("proxy_replace_header_map_value"). 501 // proxy_continue_stream resume processing of paused stream. 502 // 503 // Note: This is similar to proxy_resume_downstream, proxy_resume_upstream, proxy_resume_http_request and 504 // proxy_resume_http_response in proxy-wasm spec. See 505 // https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_continue_stream 506 NewFunctionBuilder(). 507 WithParameterNames("stream_type"). 508 WithResultNames("call_result"). 509 WithFunc(func(streamType uint32) uint32 { 510 return uint32(internal.ProxyContinueStream(internal.StreamType(streamType))) 511 }). 512 Export("proxy_continue_stream"). 513 // proxy_close_stream closes a stream. 514 // 515 // Note: This is undocumented in proxy-wasm spec. 516 NewFunctionBuilder(). 517 WithParameterNames("stream_type"). 518 WithResultNames("call_result"). 519 WithFunc(func(streamType uint32) uint32 { 520 return uint32(internal.ProxyCloseStream(internal.StreamType(streamType))) 521 }). 522 Export("proxy_close_stream"). 523 // proxy_remove_header_map_value removes all values of the key in a given map. 524 // 525 // Note: proxy-wasm-spec calls this proxy_remove_map_value. See 526 // https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_remove_map_value 527 NewFunctionBuilder(). 528 WithParameterNames("map_type", "key_data", "key_size"). 529 WithResultNames("call_result"). 530 WithFunc(func(ctx context.Context, mod api.Module, mapType, keyData, keySize uint32) uint32 { 531 keyPtr := wasmBytePtr(mod, keyData, keySize) 532 return uint32(internal.ProxyRemoveHeaderMapValue(internal.MapType(mapType), keyPtr, int(keySize))) 533 }). 534 Export("proxy_remove_header_map_value"). 535 // proxy_get_header_map_pairs gets all key-value pairs from a given map. 536 // 537 // Note: proxy-wasm-spec calls this proxy_get_map. See 538 // https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_get_map 539 NewFunctionBuilder(). 540 WithParameterNames("map_type", "return_map_data", "return_map_size"). 541 WithResultNames("call_result"). 542 WithFunc(func(ctx context.Context, mod api.Module, mapType, returnValueData, returnValueSize uint32) uint32 { 543 var returnValueHostPtr *byte 544 var returnValueSizePtr int 545 ret := uint32(internal.ProxyGetHeaderMapPairs(internal.MapType(mapType), &returnValueHostPtr, &returnValueSizePtr)) 546 copyBytesToWasm(ctx, mod, returnValueHostPtr, returnValueSizePtr, returnValueData, returnValueSize) 547 return ret 548 }). 549 Export("proxy_get_header_map_pairs"). 550 // proxy_set_header_map_pairs gets all key-value pairs from a given map. 551 // 552 // Note: proxy-wasm-spec calls this proxy_set_map. See 553 // https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_set_map 554 NewFunctionBuilder(). 555 WithParameterNames("map_type", "map_data", "map_size"). 556 WithResultNames("call_result"). 557 WithFunc(func(ctx context.Context, mod api.Module, mapType, mapData, mapSize uint32) uint32 { 558 mapPtr := wasmBytePtr(mod, mapData, mapSize) 559 return uint32(internal.ProxySetHeaderMapPairs(internal.MapType(mapType), mapPtr, int(mapSize))) 560 }). 561 Export("proxy_set_header_map_pairs"). 562 // proxy_get_buffer_bytes gets up to max_size bytes from the buffer, starting from offset. 563 // 564 // Note: proxy-wasm-spec calls this proxy_get_buffer, but the signature is incompatible. See 565 // https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_get_buffer 566 NewFunctionBuilder(). 567 WithParameterNames("buffer_type", "offset", "max_size", "return_buffer_data", "return_buffer_size"). 568 WithResultNames("call_result"). 569 WithFunc(func(ctx context.Context, mod api.Module, bufferType, start, maxSize, returnBufferData, 570 returnBufferSize uint32) uint32 { 571 var returnBufferDataHostPtr *byte 572 var returnBufferSizePtr int 573 ret := uint32(internal.ProxyGetBufferBytes(internal.BufferType(bufferType), int(start), int(maxSize), &returnBufferDataHostPtr, &returnBufferSizePtr)) 574 copyBytesToWasm(ctx, mod, returnBufferDataHostPtr, returnBufferSizePtr, returnBufferData, returnBufferSize) 575 return ret 576 }). 577 Export("proxy_get_buffer_bytes"). 578 // proxy_set_buffer_bytes replaces a byte range of the given buffer type. 579 // 580 // Note: proxy-wasm-spec calls this proxy_set_buffer, but the signature is incompatible. See 581 // https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_set_buffer 582 NewFunctionBuilder(). 583 WithParameterNames("buffer_type", "offset", "size", "buffer_data", "buffer_size"). 584 WithResultNames("call_result"). 585 WithFunc(func(ctx context.Context, mod api.Module, bufferType, start, maxSize, bufferData, 586 bufferSize uint32) uint32 { 587 bufferPtr := wasmBytePtr(mod, bufferData, bufferSize) 588 return uint32(internal.ProxySetBufferBytes(internal.BufferType(bufferType), int(start), int(maxSize), bufferPtr, int(bufferSize))) 589 }). 590 Export("proxy_set_buffer_bytes"). 591 // proxy_http_call dispatches an HTTP call to upstream. Once the response is returned to the host, 592 // proxy_on_http_call_response will be called with a unique call identifier (return_callout_id). 593 // 594 // Note: proxy-wasm-spec calls this proxy_dispatch_http_call. See 595 // https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_dispatch_http_call 596 NewFunctionBuilder(). 597 WithParameterNames("upstream_name_data", "upstream_name_size", "headers_map_data", "headers_map_size", 598 "body_data", "body_size", "trailers_map_data", "trailers_map_size", "timeout_milliseconds", 599 "return_callout_id"). 600 WithResultNames("call_result"). 601 WithFunc(func(ctx context.Context, mod api.Module, upstreamData, upstreamSize, headerData, headerSize, bodyData, 602 bodySize, trailersData, trailersSize, timeout, calloutIDPtr uint32) uint32 { 603 upstreamPtr := wasmBytePtr(mod, upstreamData, upstreamSize) 604 headerPtr := wasmBytePtr(mod, headerData, headerSize) 605 bodyPtr := wasmBytePtr(mod, bodyData, bodySize) 606 trailersPtr := wasmBytePtr(mod, trailersData, trailersSize) 607 var calloutID uint32 608 ret := uint32(internal.ProxyHttpCall(upstreamPtr, int(upstreamSize), headerPtr, int(headerSize), bodyPtr, int(bodySize), trailersPtr, int(trailersSize), timeout, &calloutID)) 609 handleMemoryStatus(mod.Memory().WriteUint32Le(calloutIDPtr, calloutID)) 610 611 // Finishing proxy_http_call executes a callback, not a plugin lifecycle method, unlike every other host function which would then end up in wasm. 612 // We can work around this by registering a callback here to go back to the wasm. 613 internal.RegisterHttpCallout(calloutID, func(numHeaders, bodySize, numTrailers int) { 614 proxyOnHttpCallResponse := mod.ExportedFunction("proxy_on_http_call_response") 615 _, err := proxyOnHttpCallResponse.Call(ctx, uint64(getPluginContextID(ctx)), uint64(calloutID), uint64(numHeaders), uint64(bodySize), uint64(numTrailers)) 616 if err != nil { 617 panic(err) 618 } 619 }) 620 621 return ret 622 }). 623 Export("proxy_http_call"). 624 // proxy_call_foreign_function calls a registered foreign function. 625 // 626 // See https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_call_foreign_function 627 NewFunctionBuilder(). 628 WithParameterNames("function_name_data", "function_name_size", "parameters_data", "parameters_size", 629 "return_results_data", "return_results_size"). 630 WithResultNames("call_result"). 631 WithFunc(func(ctx context.Context, mod api.Module, funcNamePtr, funcNameSize, paramPtr, paramSize, returnData, 632 returnSize uint32) uint32 { 633 funcName := wasmBytePtr(mod, funcNamePtr, funcNameSize) 634 paramHostPtr := wasmBytePtr(mod, paramPtr, paramSize) 635 var returnDataHostPtr *byte 636 var returnDataSizePtr int 637 ret := uint32(internal.ProxyCallForeignFunction(funcName, int(funcNameSize), paramHostPtr, int(paramSize), &returnDataHostPtr, &returnDataSizePtr)) 638 copyBytesToWasm(ctx, mod, returnDataHostPtr, returnDataSizePtr, returnData, returnSize) 639 return ret 640 }). 641 Export("proxy_call_foreign_function"). 642 // proxy_set_tick_period_milliseconds sets the timer period. Once set, the host environment will call 643 // proxy_on_tick every tick_period milliseconds. 644 // 645 // Note: proxy-wasm spec calls this proxy_set_tick_period. See 646 // https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_set_tick_period 647 NewFunctionBuilder(). 648 WithParameterNames("tick_period"). 649 WithResultNames("call_result"). 650 WithFunc(func(period uint32) uint32 { 651 return uint32(internal.ProxySetTickPeriodMilliseconds(period)) 652 }). 653 Export("proxy_set_tick_period_milliseconds"). 654 // proxy_set_effective_context changes the effective context. This function is usually used to change the 655 // context after receiving proxy_on_http_call_response, proxy_on_grpc_call_response or proxy_on_queue_ready. 656 // 657 // See https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_set_effective_context 658 NewFunctionBuilder(). 659 WithParameterNames("context_id"). 660 WithResultNames("call_result"). 661 WithFunc(func(contextID uint32) uint32 { 662 return uint32(internal.ProxySetEffectiveContext(contextID)) 663 }). 664 Export("proxy_set_effective_context"). 665 // proxy_done indicates to the host environment that Wasm VM side is done processing current context. This can 666 // be used after returning false in proxy_on_done. 667 // 668 // See https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_done 669 NewFunctionBuilder(). 670 WithResultNames("call_result"). 671 WithFunc(func() uint32 { 672 return uint32(internal.ProxyDone()) 673 }). 674 Export("proxy_done"). 675 // proxy_define_metric defines a metric using a given name. It can be referred to in proxy_get_metric, 676 // proxy_increment_metric and proxy_record_metric using returned ID. 677 // 678 // See https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_define_metric 679 NewFunctionBuilder(). 680 WithParameterNames("metric_type", "metric_name_data", "metric_name_size", "return_metric_id"). 681 WithResultNames("call_result"). 682 WithFunc(func(ctx context.Context, mod api.Module, metricType, metricNameData, metricNameSize, 683 returnMetricIDPtr uint32) uint32 { 684 metricName := wasmBytePtr(mod, metricNameData, metricNameSize) 685 var returnMetricID uint32 686 ret := uint32(internal.ProxyDefineMetric(internal.MetricType(metricType), metricName, int(metricNameSize), &returnMetricID)) 687 handleMemoryStatus(mod.Memory().WriteUint32Le(returnMetricIDPtr, returnMetricID)) 688 return ret 689 }). 690 Export("proxy_define_metric"). 691 // proxy_increment_metric increments or decrements a metric value using an offset. 692 // 693 // See https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_increment_metric 694 NewFunctionBuilder(). 695 WithParameterNames("metric_id", "offset"). 696 WithResultNames("call_result"). 697 WithFunc(func(metricID uint32, offset int64) uint32 { 698 return uint32(internal.ProxyIncrementMetric(metricID, offset)) 699 }). 700 Export("proxy_increment_metric"). 701 // proxy_record_metric sets the value of a metric. 702 // 703 // See https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_record_metric 704 NewFunctionBuilder(). 705 WithParameterNames("metric_id", "value"). 706 WithResultNames("call_result"). 707 WithFunc(func(metricID uint32, value uint64) uint32 { 708 return uint32(internal.ProxyRecordMetric(metricID, value)) 709 }). 710 Export("proxy_record_metric"). 711 // proxy_get_metric gets the value of a metric. 712 // 713 // See https://github.com/proxy-wasm/spec/tree/master/abi-versions/vNEXT#proxy_get_metric 714 NewFunctionBuilder(). 715 WithParameterNames("metric_id", "return_value"). 716 WithResultNames("call_result"). 717 WithFunc(func(ctx context.Context, mod api.Module, metricID, returnMetricValue uint32) uint32 { 718 var returnMetricValuePtr uint64 719 ret := uint32(internal.ProxyGetMetric(metricID, &returnMetricValuePtr)) 720 handleMemoryStatus(mod.Memory().WriteUint64Le(returnMetricValue, returnMetricValuePtr)) 721 return ret 722 }). 723 Export("proxy_get_metric"). 724 Instantiate(ctx) 725 return err 726 } 727 728 type pluginContextIDKeyType struct{} 729 730 var pluginContextIDKey = pluginContextIDKeyType{} 731 732 func withPluginContextID(ctx context.Context, id uint32) context.Context { 733 return context.WithValue(ctx, pluginContextIDKey, id) 734 } 735 736 func getPluginContextID(ctx context.Context) uint32 { 737 id, _ := ctx.Value(pluginContextIDKey).(uint32) 738 return id 739 }