mirror of
https://gitee.com/wa-lang/wa.git
synced 2025-12-06 09:18:53 +08:00
915 lines
38 KiB
Go
915 lines
38 KiB
Go
// Copyright 2020-2021 Tetrate
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package proxywasm
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
|
|
"wa-lang.org/wa/internal/3rdparty/wazero/imports/proxywasm/internal"
|
|
"wa-lang.org/wa/internal/3rdparty/wazero/imports/proxywasm/types"
|
|
)
|
|
|
|
// GetVMConfiguration is used for retrieving configurations given in the "vm_config.configuration" field.
|
|
// This hostcall is only available during types.PluginContext.OnVMStart call.
|
|
func GetVMConfiguration() ([]byte, error) {
|
|
return getBuffer(internal.BufferTypeVMConfiguration, 0, math.MaxInt32)
|
|
}
|
|
|
|
// GetPluginConfiguration is used for retrieving configurations given in the "config.configuration" field.
|
|
// This hostcall is only available during types.PluginContext.OnPluginStart call.
|
|
func GetPluginConfiguration() ([]byte, error) {
|
|
return getBuffer(internal.BufferTypePluginConfiguration, 0, math.MaxInt32)
|
|
}
|
|
|
|
// SetTickPeriodMilliSeconds sets the tick interval of types.PluginContext.OnTick calls.
|
|
// Only available for types.PluginContext.
|
|
func SetTickPeriodMilliSeconds(millSec uint32) error {
|
|
return internal.StatusToError(internal.ProxySetTickPeriodMilliseconds(millSec))
|
|
}
|
|
|
|
// SetEffectiveContext sets the effective context to "context_id".
|
|
// This hostcall is usually used to change the context after receiving
|
|
// types.PluginContext.OnQueueReady or types.PluginContext.OnTick
|
|
func SetEffectiveContext(contextID uint32) error {
|
|
return internal.StatusToError(internal.ProxySetEffectiveContext(contextID))
|
|
}
|
|
|
|
// RegisterSharedQueue registers the shared queue on this plugin context.
|
|
// "Register" means that OnQueueReady is called for this plugin context whenever a new item is enqueued on that queueID.
|
|
// Only available for types.PluginContext. The returned queueID can be used for Enqueue/DequeueSharedQueue.
|
|
// Note that "name" must be unique across all Wasm VMs which share the same "vm_id".
|
|
// That means you can use "vm_id" to separate shared queue namespace.
|
|
func RegisterSharedQueue(name string) (queueID uint32, err error) {
|
|
ptr := internal.StringBytePtr(name)
|
|
st := internal.ProxyRegisterSharedQueue(ptr, len(name), &queueID)
|
|
return queueID, internal.StatusToError(st)
|
|
}
|
|
|
|
// ResolveSharedQueue acquires the queueID for the given vmID and queueName.
|
|
// The returned queueID can be used for Enqueue/DequeueSharedQueue.
|
|
func ResolveSharedQueue(vmID, queueName string) (queueID uint32, err error) {
|
|
var ret uint32
|
|
st := internal.ProxyResolveSharedQueue(internal.StringBytePtr(vmID),
|
|
len(vmID), internal.StringBytePtr(queueName), len(queueName), &ret)
|
|
return ret, internal.StatusToError(st)
|
|
}
|
|
|
|
// EnqueueSharedQueue enqueues data to the shared queue of the given queueID.
|
|
// In order to get queue id for a target queue, use "ResolveSharedQueue" first.
|
|
func EnqueueSharedQueue(queueID uint32, data []byte) error {
|
|
return internal.StatusToError(internal.ProxyEnqueueSharedQueue(queueID, &data[0], len(data)))
|
|
}
|
|
|
|
// DequeueSharedQueue dequeues data from the shared queue of the given queueID.
|
|
// In order to get queue id for a target queue, use "ResolveSharedQueue" first.
|
|
func DequeueSharedQueue(queueID uint32) ([]byte, error) {
|
|
var raw *byte
|
|
var size int
|
|
st := internal.ProxyDequeueSharedQueue(queueID, &raw, &size)
|
|
if st != internal.StatusOK {
|
|
return nil, internal.StatusToError(st)
|
|
}
|
|
return internal.RawBytePtrToByteSlice(raw, size), nil
|
|
}
|
|
|
|
// PluginDone must be called when OnPluginDone returns false indicating that the plugin is in pending state
|
|
// right before deletion by the hosts. Only available for types.PluginContext.
|
|
func PluginDone() {
|
|
internal.ProxyDone()
|
|
}
|
|
|
|
// DispatchHttpCall is for dispatching HTTP calls to a remote cluster. This can be used by all contexts
|
|
// including Tcp and Root contexts. "cluster" arg specifies the remote cluster the host will send
|
|
// the request against with "headers", "body", and "trailers" arguments. "callBack" function is called if the host successfully
|
|
// made the request and received the response from the remote cluster.
|
|
// When the callBack function is called, the "GetHttpCallResponseHeaders", "GetHttpCallResponseBody", "GetHttpCallResponseTrailers"
|
|
// calls are available for accessing the response information.
|
|
func DispatchHttpCall(
|
|
cluster string,
|
|
headers [][2]string,
|
|
body []byte,
|
|
trailers [][2]string,
|
|
timeoutMillisecond uint32,
|
|
callBack func(numHeaders, bodySize, numTrailers int),
|
|
) (calloutID uint32, err error) {
|
|
shs := internal.SerializeMap(headers)
|
|
hp := &shs[0]
|
|
hl := len(shs)
|
|
|
|
sts := internal.SerializeMap(trailers)
|
|
tp := &sts[0]
|
|
tl := len(sts)
|
|
|
|
var bodyPtr *byte
|
|
if len(body) > 0 {
|
|
bodyPtr = &body[0]
|
|
}
|
|
|
|
u := internal.StringBytePtr(cluster)
|
|
switch st := internal.ProxyHttpCall(u, len(cluster),
|
|
hp, hl, bodyPtr, len(body), tp, tl, timeoutMillisecond, &calloutID); st {
|
|
case internal.StatusOK:
|
|
internal.RegisterHttpCallout(calloutID, callBack)
|
|
return calloutID, nil
|
|
default:
|
|
return 0, internal.StatusToError(st)
|
|
}
|
|
}
|
|
|
|
// GetHttpCallResponseHeaders is used for retrieving HTTP response headers
|
|
// returned by a remote cluster in response to the DispatchHttpCall.
|
|
// Only available during "callback" function passed to the DispatchHttpCall.
|
|
func GetHttpCallResponseHeaders() ([][2]string, error) {
|
|
return getMap(internal.MapTypeHttpCallResponseHeaders)
|
|
}
|
|
|
|
// GetHttpCallResponseBody is used for retrieving HTTP response body
|
|
// returned by a remote cluster in response to the DispatchHttpCall.
|
|
// Only available during "callback" function passed to the DispatchHttpCall.
|
|
func GetHttpCallResponseBody(start, maxSize int) ([]byte, error) {
|
|
return getBuffer(internal.BufferTypeHttpCallResponseBody, start, maxSize)
|
|
}
|
|
|
|
// GetHttpCallResponseTrailers is used for retrieving HTTP response trailers
|
|
// returned by a remote cluster in response to the DispatchHttpCall.
|
|
// Only available during "callback" function passed to DispatchHttpCall.
|
|
func GetHttpCallResponseTrailers() ([][2]string, error) {
|
|
return getMap(internal.MapTypeHttpCallResponseTrailers)
|
|
}
|
|
|
|
// GetDownstreamData can be used for retrieving TCP downstream data buffered in the host.
|
|
// Returned bytes beginning from "start" to "start" + "maxSize" in the buffer.
|
|
// Only available during types.TcpContext.OnDownstreamData.
|
|
func GetDownstreamData(start, maxSize int) ([]byte, error) {
|
|
return getBuffer(internal.BufferTypeDownstreamData, start, maxSize)
|
|
}
|
|
|
|
// AppendDownstreamData appends the given bytes to the downstream TCP data buffered in the host.
|
|
// Only available during types.TcpContext.OnDownstreamData.
|
|
func AppendDownstreamData(data []byte) error {
|
|
return appendToBuffer(internal.BufferTypeDownstreamData, data)
|
|
}
|
|
|
|
// PrependDownstreamData prepends the given bytes to the downstream TCP data buffered in the host.
|
|
// Only available during types.TcpContext.OnDownstreamData.
|
|
func PrependDownstreamData(data []byte) error {
|
|
return prependToBuffer(internal.BufferTypeDownstreamData, data)
|
|
}
|
|
|
|
// ReplaceDownstreamData replaces the downstream TCP data buffered in the host
|
|
// with the given bytes. Only available during types.TcpContext.OnDownstreamData.
|
|
func ReplaceDownstreamData(data []byte) error {
|
|
return replaceBuffer(internal.BufferTypeDownstreamData, data)
|
|
}
|
|
|
|
// GetUpstreamData can be used for retrieving upstream TCP data buffered in the host.
|
|
// Returned bytes beginning from "start" to "start" + "maxSize" in the buffer.
|
|
// Only available during types.TcpContext.OnUpstreamData.
|
|
func GetUpstreamData(start, maxSize int) ([]byte, error) {
|
|
return getBuffer(internal.BufferTypeUpstreamData, start, maxSize)
|
|
}
|
|
|
|
// AppendUpstreamData appends the given bytes to the upstream TCP data buffered in the host.
|
|
// Only available during types.TcpContext.OnUpstreamData.
|
|
func AppendUpstreamData(data []byte) error {
|
|
return appendToBuffer(internal.BufferTypeUpstreamData, data)
|
|
}
|
|
|
|
// PrependUpstreamData prepends the given bytes to the upstream TCP data buffered in the host.
|
|
// Only available during types.TcpContext.OnUpstreamData.
|
|
func PrependUpstreamData(data []byte) error {
|
|
return prependToBuffer(internal.BufferTypeUpstreamData, data)
|
|
}
|
|
|
|
// ReplaceUpstreamData replaces the upstream TCP data buffered in the host
|
|
// with the given bytes. Only available during types.TcpContext.OnUpstreamData.
|
|
func ReplaceUpstreamData(data []byte) error {
|
|
return replaceBuffer(internal.BufferTypeUpstreamData, data)
|
|
}
|
|
|
|
// ContinueTcpStream continues interating on the TCP connection
|
|
// after types.Action.Pause was returned by types.TcpContext.
|
|
// Only available for types.TcpContext.
|
|
func ContinueTcpStream() error {
|
|
// Note that internal.ProxyContinueStream is not implemented in Envoy,
|
|
// so we intentionally choose to pass StreamTypeDownstream here while
|
|
// the name itself is not indicating "continue downstream".
|
|
return internal.StatusToError(internal.ProxyContinueStream(internal.StreamTypeDownstream))
|
|
}
|
|
|
|
// CloseDownstream closes the downstream TCP connection for this Tcp context.
|
|
// Only available for types.TcpContext.
|
|
func CloseDownstream() error {
|
|
return internal.StatusToError(internal.ProxyCloseStream(internal.StreamTypeDownstream))
|
|
}
|
|
|
|
// CloseUpstream closes the upstream TCP connection for this Tcp context.
|
|
// Only available for types.TcpContext.
|
|
func CloseUpstream() error {
|
|
return internal.StatusToError(internal.ProxyCloseStream(internal.StreamTypeUpstream))
|
|
}
|
|
|
|
// GetHttpRequestHeaders is used for retrieving HTTP request headers.
|
|
// Only available during types.HttpContext.OnHttpRequestHeaders and
|
|
// types.HttpContext.OnHttpStreamDone.
|
|
func GetHttpRequestHeaders() ([][2]string, error) {
|
|
return getMap(internal.MapTypeHttpRequestHeaders)
|
|
}
|
|
|
|
// ReplaceHttpRequestHeaders is used for replacing HTTP request headers
|
|
// with given headers. Only available during
|
|
// types.HttpContext.OnHttpRequestHeaders.
|
|
func ReplaceHttpRequestHeaders(headers [][2]string) error {
|
|
return setMap(internal.MapTypeHttpRequestHeaders, headers)
|
|
}
|
|
|
|
// GetHttpRequestHeader is used for retrieving an HTTP request header value
|
|
// for given "key". Only available during types.HttpContext.OnHttpRequestHeaders and
|
|
// types.HttpContext.OnHttpStreamDone.
|
|
// If multiple values are present for the key, the "first" value found in the host is returned.
|
|
// See https://github.com/envoyproxy/envoy/blob/72bf41fb0ecc039f196be02f534bfc2c9c69f348/source/extensions/common/wasm/context.cc#L762-L763
|
|
// for detail.
|
|
func GetHttpRequestHeader(key string) (string, error) {
|
|
return getMapValue(internal.MapTypeHttpRequestHeaders, key)
|
|
}
|
|
|
|
// RemoveHttpRequestHeader is used for removing an HTTP request header with
|
|
// a given "key". Only available during types.HttpContext.OnHttpRequestHeaders.
|
|
func RemoveHttpRequestHeader(key string) error {
|
|
return removeMapValue(internal.MapTypeHttpRequestHeaders, key)
|
|
}
|
|
|
|
// ReplaceHttpRequestHeader replaces a value for given "key" from request headers.
|
|
// Only available during types.HttpContext.OnHttpRequestHeaders.
|
|
// If multiple values are present for the key, only the "first" value in the host is replaced.
|
|
// See https://github.com/envoyproxy/envoy/blob/72bf41fb0ecc039f196be02f534bfc2c9c69f348/envoy/http/header_map.h#L547-L549
|
|
// for detail.
|
|
func ReplaceHttpRequestHeader(key, value string) error {
|
|
return replaceMapValue(internal.MapTypeHttpRequestHeaders, key, value)
|
|
}
|
|
|
|
// AddHttpRequestHeader adds a value for given "key" to the request headers.
|
|
// Only available during types.HttpContext.OnHttpRequestHeaders.
|
|
func AddHttpRequestHeader(key, value string) error {
|
|
return addMapValue(internal.MapTypeHttpRequestHeaders, key, value)
|
|
}
|
|
|
|
// GetHttpRequestBody is used for retrieving the entire HTTP request body.
|
|
// Only available during types.HttpContext.OnHttpRequestBody.
|
|
func GetHttpRequestBody(start, maxSize int) ([]byte, error) {
|
|
return getBuffer(internal.BufferTypeHttpRequestBody, start, maxSize)
|
|
}
|
|
|
|
// AppendHttpRequestBody appends the given bytes to the HTTP request body buffer.
|
|
// Only available during types.HttpContext.OnHttpRequestBody.
|
|
// Please note that you must remove the "content-length" header during OnHttpRequestHeaders.
|
|
// Otherwise, the wrong content-length is sent to the upstream and that might result in a client crash.
|
|
func AppendHttpRequestBody(data []byte) error {
|
|
return appendToBuffer(internal.BufferTypeHttpRequestBody, data)
|
|
}
|
|
|
|
// PrependHttpRequestBody prepends the given bytes to the HTTP request body buffer.
|
|
// Only available during types.HttpContext.OnHttpRequestBody.
|
|
// Please note that you must remove the "content-length" header during OnHttpRequestHeaders.
|
|
// Otherwise, the wrong content-length is sent to the upstream and that might result in client crash.
|
|
func PrependHttpRequestBody(data []byte) error {
|
|
return prependToBuffer(internal.BufferTypeHttpRequestBody, data)
|
|
}
|
|
|
|
// ReplaceHttpRequestBody replaces the HTTP request body buffer with the given bytes.
|
|
// Only available during types.HttpContext.OnHttpRequestBody.
|
|
// Please note that you must remove the "content-length" header during OnHttpRequestHeaders.
|
|
// Otherwise, the wrong content-length is sent to the upstream and that might result in client crash,
|
|
// if the size of the data differs from the original size.
|
|
func ReplaceHttpRequestBody(data []byte) error {
|
|
return replaceBuffer(internal.BufferTypeHttpRequestBody, data)
|
|
}
|
|
|
|
// GetHttpRequestTrailers is used for retrieving HTTP request trailers.
|
|
// Only available during types.HttpContext.OnHttpRequestTrailers and
|
|
// types.HttpContext.OnHttpStreamDone.
|
|
func GetHttpRequestTrailers() ([][2]string, error) {
|
|
return getMap(internal.MapTypeHttpRequestTrailers)
|
|
}
|
|
|
|
// ReplaceHttpRequestTrailers is used for replacing HTTP request trailers
|
|
// with given trailers. Only available during
|
|
// types.HttpContext.OnHttpRequestTrailers.
|
|
func ReplaceHttpRequestTrailers(trailers [][2]string) error {
|
|
return setMap(internal.MapTypeHttpRequestTrailers, trailers)
|
|
}
|
|
|
|
// GetHttpRequestTrailer is used for retrieving HTTP request trailer value
|
|
// for given "key". Only available during types.HttpContext.OnHttpRequestTrailers and
|
|
// types.HttpContext.OnHttpStreamDone.
|
|
// If multiple values are present for the key, the "first" value found in the host is returned.
|
|
// See https://github.com/envoyproxy/envoy/blob/72bf41fb0ecc039f196be02f534bfc2c9c69f348/source/extensions/common/wasm/context.cc#L762-L763
|
|
// for detail.
|
|
func GetHttpRequestTrailer(key string) (string, error) {
|
|
return getMapValue(internal.MapTypeHttpRequestTrailers, key)
|
|
}
|
|
|
|
// RemoveHttpRequestTrailer removes all values for given "key" from the request trailers.
|
|
// Only available during types.HttpContext.OnHttpRequestTrailers.
|
|
func RemoveHttpRequestTrailer(key string) error {
|
|
return removeMapValue(internal.MapTypeHttpRequestTrailers, key)
|
|
}
|
|
|
|
// ReplaceHttpRequestTrailer replaces a value for given "key" from the request trailers.
|
|
// Only available during types.HttpContext.OnHttpRequestTrailers.
|
|
// If multiple values are present for the key, only the "first" value in the host is replaced.
|
|
// See https://github.com/envoyproxy/envoy/blob/72bf41fb0ecc039f196be02f534bfc2c9c69f348/envoy/http/header_map.h#L547-L549
|
|
// for detail.
|
|
func ReplaceHttpRequestTrailer(key, value string) error {
|
|
return replaceMapValue(internal.MapTypeHttpRequestTrailers, key, value)
|
|
}
|
|
|
|
// AddHttpRequestTrailer adds a value for given "key" to the request trailers.
|
|
// Only available during types.HttpContext.OnHttpRequestTrailers.
|
|
func AddHttpRequestTrailer(key, value string) error {
|
|
return addMapValue(internal.MapTypeHttpRequestTrailers, key, value)
|
|
}
|
|
|
|
// ResumeHttpRequest can be used for resuming HTTP request processing that was stopped
|
|
// after returning the types.Action.Pause. Only available during types.HttpContext.
|
|
func ResumeHttpRequest() error {
|
|
return internal.StatusToError(internal.ProxyContinueStream(internal.StreamTypeRequest))
|
|
}
|
|
|
|
// GetHttpResponseHeaders is used for retrieving HTTP response headers.
|
|
// Only available during types.HttpContext.OnHttpResponseHeaders and
|
|
// types.HttpContext.OnHttpStreamDone.
|
|
func GetHttpResponseHeaders() ([][2]string, error) {
|
|
return getMap(internal.MapTypeHttpResponseHeaders)
|
|
}
|
|
|
|
// ReplaceHttpResponseHeaders is used for replacing HTTP response headers
|
|
// with given headers. Only available during
|
|
// types.HttpContext.OnHttpResponseHeaders.
|
|
func ReplaceHttpResponseHeaders(headers [][2]string) error {
|
|
return setMap(internal.MapTypeHttpResponseHeaders, headers)
|
|
}
|
|
|
|
// GetHttpResponseHeader is used for retrieving an HTTP response header value
|
|
// for a given "key". Only available during types.HttpContext.OnHttpResponseHeaders and
|
|
// types.HttpContext.OnHttpStreamDone.
|
|
// If multiple values are present for the key, the "first" value found in the host is returned.
|
|
// See https://github.com/envoyproxy/envoy/blob/72bf41fb0ecc039f196be02f534bfc2c9c69f348/source/extensions/common/wasm/context.cc#L762-L763
|
|
// for detail.
|
|
func GetHttpResponseHeader(key string) (string, error) {
|
|
return getMapValue(internal.MapTypeHttpResponseHeaders, key)
|
|
}
|
|
|
|
// RemoveHttpResponseHeader removes all values for given "key" from the response headers.
|
|
// Only available during types.HttpContext.OnHttpResponseHeaders.
|
|
func RemoveHttpResponseHeader(key string) error {
|
|
return removeMapValue(internal.MapTypeHttpResponseHeaders, key)
|
|
}
|
|
|
|
// ReplaceHttpResponseHeader replaces the value for given "key" from the response headers.
|
|
// Only available during types.HttpContext.OnHttpResponseHeaders.
|
|
// If multiple values are present for the key, only the "first" value in the host is replaced.
|
|
// See https://github.com/envoyproxy/envoy/blob/72bf41fb0ecc039f196be02f534bfc2c9c69f348/envoy/http/header_map.h#L547-L549
|
|
// for detail.
|
|
func ReplaceHttpResponseHeader(key, value string) error {
|
|
return replaceMapValue(internal.MapTypeHttpResponseHeaders, key, value)
|
|
}
|
|
|
|
// AddHttpResponseHeader adds a value for a given "key" to the response headers.
|
|
// Only available during types.HttpContext.OnHttpResponseHeaders.
|
|
func AddHttpResponseHeader(key, value string) error {
|
|
return addMapValue(internal.MapTypeHttpResponseHeaders, key, value)
|
|
}
|
|
|
|
// GetHttpResponseBody is used for retrieving the entire HTTP response body.
|
|
// Only available during types.HttpContext.OnHttpResponseBody.
|
|
func GetHttpResponseBody(start, maxSize int) ([]byte, error) {
|
|
return getBuffer(internal.BufferTypeHttpResponseBody, start, maxSize)
|
|
}
|
|
|
|
// AppendHttpResponseBody appends the given bytes to the HTTP response body buffer.
|
|
// Only available during types.HttpContext.OnHttpResponseBody.
|
|
// Please note that you must remove the "content-length" header during OnHttpResponseHeaders.
|
|
// Otherwise, the wrong content-length is sent to the upstream and that might result in client crash.
|
|
func AppendHttpResponseBody(data []byte) error {
|
|
return appendToBuffer(internal.BufferTypeHttpResponseBody, data)
|
|
}
|
|
|
|
// PrependHttpResponseBody prepends the given bytes to the HTTP response body buffer.
|
|
// Only available during types.HttpContext.OnHttpResponseBody.
|
|
// Please note that you must remove the "content-length" header during OnHttpResponseHeaders.
|
|
// Otherwise, the wrong content-length is sent to the upstream and that might result in client crash.
|
|
func PrependHttpResponseBody(data []byte) error {
|
|
return prependToBuffer(internal.BufferTypeHttpResponseBody, data)
|
|
}
|
|
|
|
// ReplaceHttpResponseBody replaces the http response body buffer with the given bytes.
|
|
// Only available during types.HttpContext.OnHttpResponseBody.
|
|
// Please note that you must remove "content-length" header during OnHttpResponseHeaders.
|
|
// Otherwise, the wrong content-length is sent to the upstream and that might result in client crash
|
|
// if the size of the data differs from the original one.
|
|
func ReplaceHttpResponseBody(data []byte) error {
|
|
return replaceBuffer(internal.BufferTypeHttpResponseBody, data)
|
|
}
|
|
|
|
// GetHttpResponseTrailers is used for retrieving HTTP response trailers.
|
|
// Only available during types.HttpContext.OnHttpResponseTrailers and
|
|
// types.HttpContext.OnHttpStreamDone.
|
|
func GetHttpResponseTrailers() ([][2]string, error) {
|
|
return getMap(internal.MapTypeHttpResponseTrailers)
|
|
}
|
|
|
|
// ReplaceHttpResponseTrailers is used for replacing HTTP response trailers
|
|
// with given trailers. Only available during
|
|
// types.HttpContext.OnHttpResponseTrailers.
|
|
func ReplaceHttpResponseTrailers(trailers [][2]string) error {
|
|
return setMap(internal.MapTypeHttpResponseTrailers, trailers)
|
|
}
|
|
|
|
// GetHttpResponseTrailer is used for retrieving an HTTP response trailer value
|
|
// for a given "key". Only available during types.HttpContext.OnHttpResponseTrailers and
|
|
// types.HttpContext.OnHttpStreamDone.
|
|
// If multiple values are present for the key, the "first" value found in the host is returned.
|
|
// See https://github.com/envoyproxy/envoy/blob/72bf41fb0ecc039f196be02f534bfc2c9c69f348/source/extensions/common/wasm/context.cc#L762-L763
|
|
// for detail.
|
|
func GetHttpResponseTrailer(key string) (string, error) {
|
|
return getMapValue(internal.MapTypeHttpResponseTrailers, key)
|
|
}
|
|
|
|
// RemoveHttpResponseTrailer removes all values for given "key" from the response trailers.
|
|
// Only available during types.HttpContext.OnHttpResponseTrailers.
|
|
func RemoveHttpResponseTrailer(key string) error {
|
|
return removeMapValue(internal.MapTypeHttpResponseTrailers, key)
|
|
}
|
|
|
|
// ReplaceHttpResponseTrailer replaces a value for given "key" from the response trailers.
|
|
// Only available during types.HttpContext.OnHttpResponseHeaders.
|
|
// If multiple values are present for the key, only the "first" value in the host is replaced.
|
|
// See https://github.com/envoyproxy/envoy/blob/72bf41fb0ecc039f196be02f534bfc2c9c69f348/envoy/http/header_map.h#L547-L549
|
|
// for detail.
|
|
func ReplaceHttpResponseTrailer(key, value string) error {
|
|
return replaceMapValue(internal.MapTypeHttpResponseTrailers, key, value)
|
|
}
|
|
|
|
// AddHttpResponseTrailer adds a value for given "key" to the response trailers.
|
|
// Only available during types.HttpContext.OnHttpResponseHeaders.
|
|
func AddHttpResponseTrailer(key, value string) error {
|
|
return addMapValue(internal.MapTypeHttpResponseTrailers, key, value)
|
|
}
|
|
|
|
// ResumeHttpResponse can be used to resume the HTTP response processing that was stopped
|
|
// after returning types.Action.Pause. Only available during types.HttpContext.
|
|
func ResumeHttpResponse() error {
|
|
return internal.StatusToError(internal.ProxyContinueStream(internal.StreamTypeResponse))
|
|
}
|
|
|
|
// SendHttpResponse sends an HTTP response to the downstream with given information (headers, statusCode, body).
|
|
// The function returns an error if used outside the types.HttpContext.
|
|
// You cannot use this call after types.HttpContext.OnHttpResponseHeaders returns
|
|
// Continue because the response headers may have already arrived downstream, and
|
|
// there is no way to override the headers that were already sent.
|
|
// After you've invoked this function, you *must* return types.Action.Pause to
|
|
// stop further processing of the initial HTTP request/response.
|
|
// Note that the gRPCStatus can be set to -1 if this is a not gRPC stream.
|
|
func SendHttpResponse(statusCode uint32, headers [][2]string, body []byte, gRPCStatus int32) error {
|
|
shs := internal.SerializeMap(headers)
|
|
var bp *byte
|
|
if len(body) > 0 {
|
|
bp = &body[0]
|
|
}
|
|
hp := &shs[0]
|
|
hl := len(shs)
|
|
return internal.StatusToError(
|
|
internal.ProxySendLocalResponse(
|
|
statusCode, nil, 0,
|
|
bp, len(body), hp, hl, gRPCStatus,
|
|
),
|
|
)
|
|
}
|
|
|
|
// GetSharedData is used for retrieving the value for given "key".
|
|
// For thread-safe updates you must use the returned "cas" value
|
|
// when calling SetSharedData for the same key.
|
|
func GetSharedData(key string) (value []byte, cas uint32, err error) {
|
|
var raw *byte
|
|
var size int
|
|
|
|
st := internal.ProxyGetSharedData(internal.StringBytePtr(key), len(key), &raw, &size, &cas)
|
|
if st != internal.StatusOK {
|
|
return nil, 0, internal.StatusToError(st)
|
|
}
|
|
return internal.RawBytePtrToByteSlice(raw, size), cas, nil
|
|
}
|
|
|
|
// SetSharedData is used for setting key-value pairs in the shared data storage
|
|
// which is defined per "vm_config.vm_id" in the hosts.
|
|
// The function returns ErrorStatusCasMismatch when the given CAS value
|
|
// doesn't match the current value. The error indicates other Wasm VMs
|
|
// have already set a value on the same key, and the current CAS for the key gets incremented.
|
|
// Implementing a retry logic to handle this error is recommended.
|
|
// Setting the cas value to 0 will never return ErrorStatusCasMismatch,
|
|
// and the call will always succeed. However, it is not thread-safe.
|
|
// Another VM may have already incremented the value, and the value you
|
|
// see is already different from the one stored when you call this function.
|
|
func SetSharedData(key string, data []byte, cas uint32) error {
|
|
var dataPtr *byte
|
|
if len(data) > 0 { // Empty data is allowed to set, so we need this check.
|
|
dataPtr = &data[0]
|
|
}
|
|
st := internal.ProxySetSharedData(internal.StringBytePtr(key), len(key), dataPtr, len(data), cas)
|
|
return internal.StatusToError(st)
|
|
}
|
|
|
|
// GetProperty is used for retrieving property/metadata in the host
|
|
// for a given path.
|
|
// Available path and properties depend on the host implementation.
|
|
// For Envoy, please refer to https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/advanced/attributes
|
|
//
|
|
// Note: if the target property is map-type, use GetPropertyMap instead. This GetProperty returns
|
|
// raw un-serialized bytes for such properties. For example, if you have the metadata as
|
|
//
|
|
// clusters:
|
|
// - name: web_service
|
|
// metadata:
|
|
// filter_metadata:
|
|
// foo:
|
|
// my_value: '1234'
|
|
// my_map:
|
|
// k1: v1
|
|
// k2: v2
|
|
//
|
|
// Then,
|
|
// - use GetPropertyMap for {"cluster_metadata", "filter_metadata", "foo", "my_map"}.
|
|
// - use GetProperty for {"cluster_metadata", "filter_metadata", "foo", "my_value"}.
|
|
//
|
|
// Note: you cannot get the raw bytes of protobuf. For example, accessing {"cluster_metadata", "filter_data", "foo"}) doesn't
|
|
// return the protobuf bytes, but instead this returns the serialized map of "foo". Therefore, we recommend to access individual
|
|
// "leaf" fields (not the middle or top field of metadata) to avoid the need to figure out the (host-dependent) encoding of properties.
|
|
func GetProperty(path []string) ([]byte, error) {
|
|
if len(path) == 0 {
|
|
return nil, errors.New("path must not be empty")
|
|
}
|
|
var ret *byte
|
|
var retSize int
|
|
raw := internal.SerializePropertyPath(path)
|
|
|
|
err := internal.StatusToError(internal.ProxyGetProperty(&raw[0], len(raw), &ret, &retSize))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return internal.RawBytePtrToByteSlice(ret, retSize), nil
|
|
}
|
|
|
|
// GetPropertyMap is the same as GetProperty but can be used to decode map-typed properties.
|
|
// See GetProperty for detail.
|
|
//
|
|
// Note: this operates under the assumption that the path is encoded as map, therefore this might
|
|
// cause panic if it is used on the non-map types.
|
|
func GetPropertyMap(path []string) ([][2]string, error) {
|
|
b, err := GetProperty(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return internal.DeserializeMap(b), nil
|
|
}
|
|
|
|
// SetProperty is used for setting property/metadata in the host
|
|
// for a given path.
|
|
// Available path and properties depend on the host implementation.
|
|
// For Envoy, please refer to https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/advanced/attributes
|
|
func SetProperty(path []string, data []byte) error {
|
|
if len(path) == 0 {
|
|
return errors.New("path must not be empty")
|
|
} else if len(data) == 0 {
|
|
return errors.New("data must not be empty")
|
|
}
|
|
raw := internal.SerializePropertyPath(path)
|
|
return internal.StatusToError(internal.ProxySetProperty(
|
|
&raw[0], len(raw), &data[0], len(data),
|
|
))
|
|
}
|
|
|
|
// CallForeignFunction calls a foreign function of given funcName defined by host implementations.
|
|
// Foreign functions are host-specific functions, so please refer to the doc of your host implementation for detail.
|
|
func CallForeignFunction(funcName string, param []byte) (ret []byte, err error) {
|
|
f := internal.StringBytePtr(funcName)
|
|
|
|
var returnData *byte
|
|
var returnSize int
|
|
|
|
var paramPtr *byte
|
|
if len(param) != 0 {
|
|
paramPtr = ¶m[0]
|
|
}
|
|
|
|
switch st := internal.ProxyCallForeignFunction(f, len(funcName), paramPtr, len(param), &returnData, &returnSize); st {
|
|
case internal.StatusOK:
|
|
return internal.RawBytePtrToByteSlice(returnData, returnSize), nil
|
|
default:
|
|
return nil, internal.StatusToError(st)
|
|
}
|
|
}
|
|
|
|
// LogTrace emits a message as a log with Trace log level.
|
|
func LogTrace(msg string) {
|
|
internal.ProxyLog(internal.LogLevelTrace, internal.StringBytePtr(msg), len(msg))
|
|
}
|
|
|
|
// LogTracef formats according to a format specifier and emits as a log with Trace log level.
|
|
//
|
|
// Note that not all combinations of format and args are supported by tinygo.
|
|
// For example, %v with a map will cause a panic. See
|
|
// https://tinygo.org/docs/reference/lang-support/stdlib/#fmt for more
|
|
// information.
|
|
func LogTracef(format string, args ...interface{}) {
|
|
msg := fmt.Sprintf(format, args...)
|
|
internal.ProxyLog(internal.LogLevelTrace, internal.StringBytePtr(msg), len(msg))
|
|
}
|
|
|
|
// LogDebug emits a message as a log with Debug log level.
|
|
func LogDebug(msg string) {
|
|
internal.ProxyLog(internal.LogLevelDebug, internal.StringBytePtr(msg), len(msg))
|
|
}
|
|
|
|
// LogDebugf formats according to a format specifier and emits as a log with Debug log level.
|
|
//
|
|
// Note that not all combinations of format and args are supported by tinygo.
|
|
// For example, %v with a map will cause a panic. See
|
|
// https://tinygo.org/docs/reference/lang-support/stdlib/#fmt for more
|
|
// information.
|
|
func LogDebugf(format string, args ...interface{}) {
|
|
msg := fmt.Sprintf(format, args...)
|
|
internal.ProxyLog(internal.LogLevelDebug, internal.StringBytePtr(msg), len(msg))
|
|
}
|
|
|
|
// LogInfo emits a message as a log with Info log level.
|
|
func LogInfo(msg string) {
|
|
internal.ProxyLog(internal.LogLevelInfo, internal.StringBytePtr(msg), len(msg))
|
|
}
|
|
|
|
// LogInfof formats according to a format specifier and emits as a log with Info log level.
|
|
//
|
|
// Note that not all combinations of format and args are supported by tinygo.
|
|
// For example, %v with a map will cause a panic. See
|
|
// https://tinygo.org/docs/reference/lang-support/stdlib/#fmt for more
|
|
// information.
|
|
func LogInfof(format string, args ...interface{}) {
|
|
msg := fmt.Sprintf(format, args...)
|
|
internal.ProxyLog(internal.LogLevelInfo, internal.StringBytePtr(msg), len(msg))
|
|
}
|
|
|
|
// LogWarn emits a message as a log with Warn log level.
|
|
func LogWarn(msg string) {
|
|
internal.ProxyLog(internal.LogLevelWarn, internal.StringBytePtr(msg), len(msg))
|
|
}
|
|
|
|
// LogWarnf formats according to a format specifier and emits as a log with Warn log level.
|
|
//
|
|
// Note that not all combinations of format and args are supported by tinygo.
|
|
// For example, %v with a map will cause a panic. See
|
|
// https://tinygo.org/docs/reference/lang-support/stdlib/#fmt for more
|
|
// information.
|
|
func LogWarnf(format string, args ...interface{}) {
|
|
msg := fmt.Sprintf(format, args...)
|
|
internal.ProxyLog(internal.LogLevelWarn, internal.StringBytePtr(msg), len(msg))
|
|
}
|
|
|
|
// LogError emits a message as a log with Error log level.
|
|
func LogError(msg string) {
|
|
internal.ProxyLog(internal.LogLevelError, internal.StringBytePtr(msg), len(msg))
|
|
}
|
|
|
|
// LogErrorf formats according to a format specifier and emits as a log with Error log level.
|
|
//
|
|
// Note that not all combinations of format and args are supported by tinygo.
|
|
// For example, %v with a map will cause a panic. See
|
|
// https://tinygo.org/docs/reference/lang-support/stdlib/#fmt for more
|
|
// information.
|
|
func LogErrorf(format string, args ...interface{}) {
|
|
msg := fmt.Sprintf(format, args...)
|
|
internal.ProxyLog(internal.LogLevelError, internal.StringBytePtr(msg), len(msg))
|
|
}
|
|
|
|
// LogCritical emits a message as a log with Critical log level.
|
|
func LogCritical(msg string) {
|
|
internal.ProxyLog(internal.LogLevelCritical, internal.StringBytePtr(msg), len(msg))
|
|
}
|
|
|
|
// LogCriticalf formats according to a format specifier and emits as a log with Critical log level.
|
|
//
|
|
// Note that not all combinations of format and args are supported by tinygo.
|
|
// For example, %v with a map will cause a panic. See
|
|
// https://tinygo.org/docs/reference/lang-support/stdlib/#fmt for more
|
|
// information.
|
|
func LogCriticalf(format string, args ...interface{}) {
|
|
msg := fmt.Sprintf(format, args...)
|
|
internal.ProxyLog(internal.LogLevelCritical, internal.StringBytePtr(msg), len(msg))
|
|
}
|
|
|
|
type (
|
|
// MetricCounter represents a counter metric.
|
|
// Use DefineCounterMetric for initialization.
|
|
MetricCounter uint32
|
|
// MetricGauge represents a gauge metric.
|
|
// Use DefineGaugeMetric for initialization.
|
|
MetricGauge uint32
|
|
// MetricHistogram represents a histogram metric.
|
|
// Use DefineHistogramMetric for initialization.
|
|
MetricHistogram uint32
|
|
)
|
|
|
|
// DefineCounterMetric returns MetricCounter for a name.
|
|
func DefineCounterMetric(name string) MetricCounter {
|
|
var id uint32
|
|
ptr := internal.StringBytePtr(name)
|
|
st := internal.ProxyDefineMetric(internal.MetricTypeCounter, ptr, len(name), &id)
|
|
if err := internal.StatusToError(st); err != nil {
|
|
panic(fmt.Sprintf("define metric of name %s: %v", name, internal.StatusToError(st)))
|
|
}
|
|
return MetricCounter(id)
|
|
}
|
|
|
|
// Value returns the current value for this counter.
|
|
func (m MetricCounter) Value() uint64 {
|
|
var val uint64
|
|
st := internal.ProxyGetMetric(uint32(m), &val)
|
|
if err := internal.StatusToError(st); err != nil {
|
|
panic(fmt.Sprintf("get metric of %d: %v", uint32(m), internal.StatusToError(st)))
|
|
}
|
|
return val
|
|
}
|
|
|
|
// Increment increments the current value by an offset for this counter.
|
|
func (m MetricCounter) Increment(offset uint64) {
|
|
if err := internal.StatusToError(internal.ProxyIncrementMetric(uint32(m), int64(offset))); err != nil {
|
|
panic(fmt.Sprintf("increment %d by %d: %v", uint32(m), offset, err))
|
|
}
|
|
}
|
|
|
|
// DefineCounterMetric returns MetricGauge for a name.
|
|
func DefineGaugeMetric(name string) MetricGauge {
|
|
var id uint32
|
|
ptr := internal.StringBytePtr(name)
|
|
st := internal.ProxyDefineMetric(internal.MetricTypeGauge, ptr, len(name), &id)
|
|
if err := internal.StatusToError(st); err != nil {
|
|
panic(fmt.Sprintf("error define metric of name %s: %v", name, internal.StatusToError(st)))
|
|
}
|
|
return MetricGauge(id)
|
|
}
|
|
|
|
// Value returns the current value for this gauge.
|
|
func (m MetricGauge) Value() int64 {
|
|
var val uint64
|
|
if err := internal.StatusToError(internal.ProxyGetMetric(uint32(m), &val)); err != nil {
|
|
panic(fmt.Sprintf("get metric of %d: %v", uint32(m), err))
|
|
}
|
|
return int64(val)
|
|
}
|
|
|
|
// Add adds an offset to the current value for this gauge.
|
|
func (m MetricGauge) Add(offset int64) {
|
|
if err := internal.StatusToError(internal.ProxyIncrementMetric(uint32(m), offset)); err != nil {
|
|
panic(fmt.Sprintf("error adding %d by %d: %v", uint32(m), offset, err))
|
|
}
|
|
}
|
|
|
|
// DefineHistogramMetric returns MetricHistogram for a name.
|
|
func DefineHistogramMetric(name string) MetricHistogram {
|
|
var id uint32
|
|
ptr := internal.StringBytePtr(name)
|
|
st := internal.ProxyDefineMetric(internal.MetricTypeHistogram, ptr, len(name), &id)
|
|
if err := internal.StatusToError(st); err != nil {
|
|
panic(fmt.Sprintf("error define metric of name %s: %v", name, internal.StatusToError(st)))
|
|
}
|
|
return MetricHistogram(id)
|
|
}
|
|
|
|
// Value returns the current value for this histogram.
|
|
func (m MetricHistogram) Value() uint64 {
|
|
var val uint64
|
|
st := internal.ProxyGetMetric(uint32(m), &val)
|
|
if err := internal.StatusToError(st); err != nil {
|
|
panic(fmt.Sprintf("get metric of %d: %v", uint32(m), internal.StatusToError(st)))
|
|
}
|
|
return val
|
|
}
|
|
|
|
// Record records a value for this histogram.
|
|
func (m MetricHistogram) Record(value uint64) {
|
|
if err := internal.StatusToError(internal.ProxyRecordMetric(uint32(m), value)); err != nil {
|
|
panic(fmt.Sprintf("error adding %d: %v", uint32(m), err))
|
|
}
|
|
}
|
|
|
|
func setMap(mapType internal.MapType, headers [][2]string) error {
|
|
shs := internal.SerializeMap(headers)
|
|
hp := &shs[0]
|
|
hl := len(shs)
|
|
return internal.StatusToError(internal.ProxySetHeaderMapPairs(mapType, hp, hl))
|
|
}
|
|
|
|
func getMapValue(mapType internal.MapType, key string) (string, error) {
|
|
var rvs int
|
|
var raw *byte
|
|
if st := internal.ProxyGetHeaderMapValue(
|
|
mapType, internal.StringBytePtr(key), len(key), &raw, &rvs,
|
|
); st != internal.StatusOK {
|
|
return "", internal.StatusToError(st)
|
|
}
|
|
|
|
ret := internal.RawBytePtrToString(raw, rvs)
|
|
return ret, nil
|
|
}
|
|
|
|
func removeMapValue(mapType internal.MapType, key string) error {
|
|
return internal.StatusToError(
|
|
internal.ProxyRemoveHeaderMapValue(mapType, internal.StringBytePtr(key), len(key)),
|
|
)
|
|
}
|
|
|
|
func replaceMapValue(mapType internal.MapType, key, value string) error {
|
|
return internal.StatusToError(
|
|
internal.ProxyReplaceHeaderMapValue(
|
|
mapType, internal.StringBytePtr(key), len(key), internal.StringBytePtr(value), len(value),
|
|
),
|
|
)
|
|
}
|
|
|
|
func addMapValue(mapType internal.MapType, key, value string) error {
|
|
return internal.StatusToError(
|
|
internal.ProxyAddHeaderMapValue(
|
|
mapType, internal.StringBytePtr(key), len(key), internal.StringBytePtr(value), len(value),
|
|
),
|
|
)
|
|
}
|
|
|
|
func getMap(mapType internal.MapType) ([][2]string, error) {
|
|
var rvs int
|
|
var raw *byte
|
|
|
|
st := internal.ProxyGetHeaderMapPairs(mapType, &raw, &rvs)
|
|
if st != internal.StatusOK {
|
|
return nil, internal.StatusToError(st)
|
|
} else if raw == nil {
|
|
return nil, types.ErrorStatusNotFound
|
|
}
|
|
|
|
bs := internal.RawBytePtrToByteSlice(raw, rvs)
|
|
return internal.DeserializeMap(bs), nil
|
|
}
|
|
|
|
func getBuffer(bufType internal.BufferType, start, maxSize int) ([]byte, error) {
|
|
var retData *byte
|
|
var retSize int
|
|
switch st := internal.ProxyGetBufferBytes(bufType, start, maxSize, &retData, &retSize); st {
|
|
case internal.StatusOK:
|
|
if retData == nil {
|
|
return nil, types.ErrorStatusNotFound
|
|
}
|
|
return internal.RawBytePtrToByteSlice(retData, retSize), nil
|
|
default:
|
|
return nil, internal.StatusToError(st)
|
|
}
|
|
}
|
|
|
|
func appendToBuffer(bufType internal.BufferType, buffer []byte) error {
|
|
var bufferData *byte
|
|
if len(buffer) != 0 {
|
|
bufferData = &buffer[0]
|
|
}
|
|
return internal.StatusToError(internal.ProxySetBufferBytes(bufType, math.MaxInt32, 0, bufferData, len(buffer)))
|
|
}
|
|
|
|
func prependToBuffer(bufType internal.BufferType, buffer []byte) error {
|
|
var bufferData *byte
|
|
if len(buffer) != 0 {
|
|
bufferData = &buffer[0]
|
|
}
|
|
return internal.StatusToError(internal.ProxySetBufferBytes(bufType, 0, 0, bufferData, len(buffer)))
|
|
}
|
|
|
|
func replaceBuffer(bufType internal.BufferType, buffer []byte) error {
|
|
var bufferData *byte
|
|
if len(buffer) != 0 {
|
|
bufferData = &buffer[0]
|
|
}
|
|
return internal.StatusToError(
|
|
internal.ProxySetBufferBytes(bufType, 0, math.MaxInt32, bufferData, len(buffer)),
|
|
)
|
|
}
|