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  }