blob: 48fce76d9e02912c1de85294912683f4ada44814 [file]
// Copyright 2016-2019 Envoy Project Authors
// Copyright 2020 Google LLC
//
// 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.
#pragma once
#include <time.h>
#include <atomic>
#include <chrono>
#include <functional>
#include <iostream>
#include <optional>
#include <map>
#include <memory>
#include <vector>
#include "include/proxy-wasm/sdk.h"
namespace proxy_wasm {
using Pairs = std::vector<std::pair<std::string_view, std::string_view>>;
using PairsWithStringValues = std::vector<std::pair<std::string_view, std::string>>;
using TimerToken = uint32_t;
using HttpCallToken = uint32_t;
using GrpcToken = uint32_t;
using GrpcStatusCode = uint32_t;
using SharedQueueDequeueToken = uint32_t;
using SharedQueueEnqueueToken = uint32_t;
// TODO: update SDK and use this.
enum class ProxyAction : uint32_t {
Illegal = 0,
Continue = 1,
Pause = 2,
};
struct PluginBase;
class WasmBase;
/**
* BufferInterface provides a interface between proxy-specific buffers and the proxy-independent ABI
* implementation. Embedders should subclass BufferInterface to enable the proxy-independent code to
* implement ABI calls which use buffers (e.g. the HTTP body).
*/
struct BufferInterface {
virtual ~BufferInterface() = default;
virtual size_t size() const = 0;
/**
* Copy bytes from the buffer into the Wasm VM corresponding to 'wasm'.
* @param start is the first buffer byte to copy.
* @param length is the length of sequence of buffer bytes to copy.
* @param ptr_ptr is the location in the VM address space to place the address of the newly
* allocated memory block which contains the copied bytes.
* @param size_ptr is the location in the VM address space to place the size of the newly
* allocated memory block which contains the copied bytes (i.e. length).
* @return a WasmResult with any error or WasmResult::Ok.
* NB: in order to support (future) 64-bit VMs and the NullVM we need 64-bits worth of resolution
* for addresses.
*/
virtual WasmResult copyTo(WasmBase *wasm, size_t start, size_t length, uint64_t ptr_ptr,
uint64_t size_ptr) const = 0;
/**
* Copy (alias) bytes from the VM 'data' into the buffer, replacing the provided range..
* @param start is the first buffer byte to replace.
* @param length is the length of sequence of buffer bytes to replace.
* @param data the data to copy over the replaced region.
* @return a WasmResult with any error or WasmResult::Ok.
*/
virtual WasmResult copyFrom(size_t start, size_t length, std::string_view data) = 0;
};
/**
* RootGrpcInterface is the interface for gRPC events arriving at RootContext(s).
*/
struct RootGrpcInterface {
virtual ~RootGrpcInterface() = default;
/**
* Called on Root Context to with the initial metadata of a grpcStream.
* @token is the token returned by grpcStream.
* The metadata can be retrieved via the HeaderInterface functions with
* WasmHeaderMapType::GrpcReceiveInitialMetadata as the type.
*/
virtual void onGrpcReceiveInitialMetadata(GrpcToken token, uint32_t elements) = 0;
/**
* Called on Root Context when gRPC data has arrived.
* @token is the token returned by grpcCall or grpcStream.
* For Call this implies OK close. For Stream may be called repeatedly.
* The data can be retrieved via the getBuffer function with
* WasmBufferType::GrpcReceiveBuffer as the type.
*/
virtual void onGrpcReceive(GrpcToken token, uint32_t response_size) = 0;
/**
* Called on Root Context to with the trailing metadata of a grpcStream.
* @token is the token returned by grpcStream.
* The trailers can be retrieved via the HeaderInterface functions with
* WasmHeaderMapType::GrpcReceiveTrailingMetadata as the type.
*/
virtual void onGrpcReceiveTrailingMetadata(GrpcToken token, uint32_t trailers) = 0;
/**
* Called on Root Context when a gRPC call has closed prematurely or a gRPC stream has closed.
* @token is the token returned by grpcStream.
* @status is status of the stream.
* For Call only used when not Ok. For stream indicates the peer has closed.
*/
virtual void onGrpcClose(GrpcToken token, GrpcStatusCode status) = 0;
};
/**
* RootInterface is the interface specific to RootContexts.
* A RootContext is associated with one more more plugins and is the parent of all stream Context(s)
* created for that plugin. It can be used to store data shared between stream Context(s) in the
* same VM.
*/
struct RootInterface : public RootGrpcInterface {
virtual ~RootInterface() = default;
/**
* Call on a host Context to create a corresponding Context in the VM. Note:
* onNetworkNewConnection and onRequestHeaders() call onCreate().
* stream Context this will be a Root Context id (or sub-Context thereof).
*/
virtual void onCreate() = 0;
/**
* Call on a Root Context when a VM first starts up.
* @param plugin is the plugin which caused the VM to be started.
* Called by the host code.
*/
virtual bool onStart(std::shared_ptr<PluginBase> plugin) = 0;
/**
* Call on a Root Context when a plugin is configured or reconfigured dynamically.
* @param plugin is the plugin which is being configured or reconfigured.
*/
virtual bool onConfigure(std::shared_ptr<PluginBase> plugin) = 0;
/**
* Call on a Root Context when a timer has expired. Timers are automatically rearmed. The cancel
* the rearm call setTimerPeriod with a period of zero.
* @param token is the token returned by setTimerPeriod.
*/
virtual void onTick(TimerToken token) = 0;
/**
* Called on a Root Context when a response arrives for an outstanding httpCall().
* @param token is the token returned by the corresponding httpCall().
*/
virtual void onHttpCallResponse(HttpCallToken token, uint32_t headers, uint32_t body_size,
uint32_t trailers) = 0;
/**
* Called on a Root Context when an Inter-VM shared queue message has arrived.
* @token is the token returned by registerSharedQueue().
*/
virtual void onQueueReady(SharedQueueDequeueToken token) = 0;
/**
* Call when a stream has completed (both sides have closed) or on a Root Context when the VM is
* shutting down.
* @return true for stream contexts or for Root Context(s) if the VM can shutdown, false for Root
* Context(s) if the VM should wait until the Root Context calls the proxy_done() ABI call. Note:
* the VM may (std::optionally) shutdown after some configured timeout even if the Root Context
* does not call proxy_done().
*/
virtual bool onDone() = 0;
// Call for logging not associated with a stream lifecycle (e.g. logging only plugin).
virtual void onLog() = 0;
/**
* Call when no further stream calls will occur. This will cause the corresponding Context in the
* VM to be deleted.
* Called by the host code.
*/
virtual void onDelete() = 0;
};
/**
* HttpInterface is the interface between the VM host and the VM for HTTP streams. Note: headers and
* trailers are obtained by the code in the VM via the XXXHeaderMapXXX set of functions. Body data
* is obtained by the code in the VM via calls implemented by the proxy-independent host code using
* the getBuffer() call.
*/
struct HttpInterface {
public:
virtual ~HttpInterface() = default;
/**
* Call on a stream context to indicate that the request headers have arrived. Calls
* onCreate() to create a Context in the VM first.
*/
virtual FilterHeadersStatus onRequestHeaders(uint32_t headers, bool end_of_stream) = 0;
// Call on a stream context to indicate that body data has arrived.
virtual FilterDataStatus onRequestBody(uint32_t body_length, bool end_of_stream) = 0;
// Call on a stream context to indicate that the request trailers have arrived.
virtual FilterTrailersStatus onRequestTrailers(uint32_t trailers) = 0;
// Call on a stream context to indicate that the request metadata has arrived.
virtual FilterMetadataStatus onRequestMetadata(uint32_t elements) = 0;
// Call on a stream context to indicate that the request trailers have arrived.
virtual FilterHeadersStatus onResponseHeaders(uint32_t trailers, bool end_of_stream) = 0;
// Call on a stream context to indicate that body data has arrived.
virtual FilterDataStatus onResponseBody(uint32_t body_length, bool end_of_stream) = 0;
// Call on a stream context to indicate that the request trailers have arrived.
virtual FilterTrailersStatus onResponseTrailers(uint32_t trailers) = 0;
// Call on a stream context to indicate that the request metadata has arrived.
virtual FilterMetadataStatus onResponseMetadata(uint32_t elements) = 0;
/**
* Respond directly to an HTTP request.
* @param response_code is the response code to send.
* @param body is the body of the response.
* @param additional_headers are additional headers to send in the response.
* @param grpc_status is an std::optional gRPC status if the connection is a gRPC connection.
* @param details are details of any (gRPC) error.
*/
virtual WasmResult sendLocalResponse(uint32_t response_code, std::string_view body,
Pairs additional_headers, uint32_t grpc_status,
std::string_view details) = 0;
// Clears the route cache for the current request.
virtual void clearRouteCache() = 0;
// Call when the stream closes. See RootInterface.
virtual bool onDone() = 0;
// Call when the stream status has finalized, e.g. for logging. See RootInterface.
virtual void onLog() = 0;
// Call just before the Context is deleted. See RootInterface.
virtual void onDelete() = 0;
};
/**
* NetworkInterface is the interface between the VM host and the VM for network streams.
*/
struct NetworkInterface {
public:
virtual ~NetworkInterface() = default;
/**
* Network
* Note: Body data is obtained by the code in the VM via calls implemented by the
* proxy-independent host code using the getBuffer() call.
*/
/**
* Call on a stream Context to indicate that a new network connection has been been created.
* Calls onStart().
*/
virtual FilterStatus onNetworkNewConnection() = 0;
/**
* Call on a stream Context to indicate that data has arrived from downstream (e.g. on the
* incoming port that was accepted and for which the proxy is acting as a server).
* @return indicates the subsequent behavior of this stream, e.g. continue proxying or pause
* proxying.
*/
virtual FilterStatus onDownstreamData(uint32_t data_length, bool end_of_stream) = 0;
/**
* Call on a stream Context to indicate that data has arrived from upstream (e.g. on the
* outgoing port that the proxy connected and for which the proxy is acting as a client).
* @param data_length the amount of data in the buffer.
* @param end_of_stream is true if no more data will be arriving.
* @return indicates the subsequent behavior of this stream, e.g. continue proxying or pause
* proxying.
*/
virtual FilterStatus onUpstreamData(uint32_t data_length, bool end_of_stream) = 0;
/**
* Call on a stream context to indicate that the downstream connection has closed.
* @close_type is the source of the close.
*/
virtual void onDownstreamConnectionClose(CloseType close_type) = 0;
/**
* Call on a stream context to indicate that the upstream connection has closed.
* @close_type is the source of the close.
*/
virtual void onUpstreamConnectionClose(CloseType close_type) = 0;
// Call when the stream closes. See RootInterface.
virtual bool onDone() = 0;
// Call when the stream status has finalized, e.g. for logging. See RootInterface.
virtual void onLog() = 0;
// Call just before the Context is deleted. See RootInterface.
virtual void onDelete() = 0;
};
/**
* General Stream interface (e.g. HTTP, Network).
**/
struct StreamInterface {
virtual ~StreamInterface() = default;
// Continue processing e.g. after returning ProxyAction::Pause.
virtual WasmResult continueStream(WasmStreamType type) = 0;
// Close a stream.
virtual WasmResult closeStream(WasmStreamType type) = 0;
// Called when a Wasm VM has failed and the plugin is set to fail closed.
virtual void failStream(WasmStreamType) = 0;
/**
* Provides a BufferInterface to be used to return buffered data to the VM.
* @param type is the type of buffer to provide.
*/
virtual const BufferInterface *getBuffer(WasmBufferType type) = 0;
/**
* Provides the end-of-stream status of the given flow (if any) or false. End of stream occurs
* when a connection is closed (or half-closed) either locally or remotely.
* @param stream_type is the flow
*/
virtual bool endOfStream(WasmStreamType type) = 0;
};
// Header/Trailer/Metadata Maps
struct HeaderInterface {
virtual ~HeaderInterface() = default;
/**
* Add a key-value pair to a header map.
* @param type of the header map.
* @param key is the key (header).
* @param value is the value (header value).
*/
virtual WasmResult addHeaderMapValue(WasmHeaderMapType type, std::string_view key,
std::string_view value) = 0;
/**
* Get a value from to a header map.
* @param type of the header map.
* @param key is the key (header).
* @param result is a pointer to the returned header value.
*/
virtual WasmResult getHeaderMapValue(WasmHeaderMapType type, std::string_view key,
std::string_view *result) = 0;
/**
* Get all the key-value pairs in a header map.
* @param type of the header map.
* @param result is a pointer to the pairs.
*/
virtual WasmResult getHeaderMapPairs(WasmHeaderMapType type, Pairs *result) = 0;
/**
* Set a header map so that it contains the given pairs (does not merge with existing data).
* @param type of the header map.
* @param the pairs to set the header map to.
*/
virtual WasmResult setHeaderMapPairs(WasmHeaderMapType type, const Pairs &pairs) = 0;
/**
* Remove a key-value pair from a header map.
* @param type of the header map.
* @param key of the header map.
*/
virtual WasmResult removeHeaderMapValue(WasmHeaderMapType type, std::string_view key) = 0;
/**
* Replace (or set) a value in a header map.
* @param type of the header map.
* @param key of the header map.
* @param value to set in the header map.
*/
virtual WasmResult replaceHeaderMapValue(WasmHeaderMapType type, std::string_view key,
std::string_view value) = 0;
/**
* Returns the number of entries in a header map.
* @param type of the header map.
* @param result is a pointer to the result.
*/
virtual WasmResult getHeaderMapSize(WasmHeaderMapType type, uint32_t *result) = 0;
};
struct HttpCallInterface {
virtual ~HttpCallInterface() = default;
/**
* Make an outgoing HTTP request.
* @param target specifies the proxy-specific target of the call (e.g. a cluster, peer, or
* host).
* @param request_headers are the request headers.
* @param request_body is the request body.
* @param request_trailers are the request trailers.
* @param timeout_milliseconds is a timeout after which the request will be considered to have
* failed.
* @param token_ptr contains a pointer to a location to store the token which will be used with
* the corresponding onHttpCallResponse.
* Note: the response arrives on the ancestor RootContext as this call may outlive any stream.
* Plugin writers should use the VM SDK setEffectiveContext() to switch to any waiting streams.
*/
virtual WasmResult httpCall(std::string_view target, const Pairs &request_headers,
std::string_view request_body, const Pairs &request_trailers,
int timeout_milliseconds, HttpCallToken *token_ptr) = 0;
};
struct GrpcCallInterface {
virtual ~GrpcCallInterface() = default;
/**
* Make a gRPC call.
* @param grpc_service is proxy-specific metadata describing the service (e.g. security certs).
* @param service_name the name of the gRPC service.
* @param method_name the gRPC method name.
* @param request the serialized request.
* @param initial_metadata the initial metadata.
* @param timeout a timeout in milliseconds.
* @param token_ptr contains a pointer to a location to store the token which will be used with
* the corresponding onGrpc and grpc calls.
*/
virtual WasmResult grpcCall(std::string_view /* grpc_service */,
std::string_view /* service_name */,
std::string_view /* method_name */,
const Pairs & /* initial_metadata */, std::string_view /* request */,
std::chrono::milliseconds /* timeout */,
GrpcToken * /* token_ptr */) = 0;
/**
* Close a gRPC stream. In flight data may still result in calls into the VM.
* @param token is a token returned from grpcStream.
* For call the same as grpcCancel. For Stream, close the stream: outstanding peer messages may
* still arrive.
*/
virtual WasmResult grpcClose(GrpcToken /* token */) = 0;
/**
* Cancel a gRPC stream or call. No more calls will occur.
* @param token is a token returned from grpcSream or grpcCall.
* For Call, cancel on call. For Stream, reset the stream: no further callbacks will arrive.
*/
virtual WasmResult grpcCancel(GrpcToken /* token */) = 0;
};
struct GrpcStreamInterface {
virtual ~GrpcStreamInterface() = default;
/**
* Open a gRPC stream.
* @param grpc_service is proxy-specific metadata describing the service (e.g. security certs).
* @param service_name the name of the gRPC service.
* @param method_name the gRPC method name.
* @param token_ptr contains a pointer to a location to store the token which will be used with
* the corresponding onGrpc and grpc calls.
*/
virtual WasmResult grpcStream(std::string_view grpc_service, std::string_view service_name,
std::string_view method_name, const Pairs & /* initial_metadata */,
GrpcToken *token_ptr) = 0;
/**
* Close a gRPC stream. In flight data may still result in calls into the VM.
* @param token is a token returned from grpcSream.
* @param message is a (serialized) message to be sent.
* @param end_stream indicates that the stream is now end_of_stream (e.g. WriteLast() or
* WritesDone).
*/
virtual WasmResult grpcSend(GrpcToken token, std::string_view message, bool end_stream) = 0;
// See GrpcCallInterface.
virtual WasmResult grpcClose(GrpcToken token) = 0;
// See GrpcCallInterface.
virtual WasmResult grpcCancel(GrpcToken token) = 0;
};
/**
* Metrics/Stats interface. See the proxy-wasm spec for details of the host ABI contract.
*/
struct MetricsInterface {
virtual ~MetricsInterface() = default;
/**
* Define a metric (Stat).
* @param type is the type of metric (e.g. Counter). This may be an element of MetricType from the
* SDK or some proxy-specific extension. It is the responsibility of the proxy-specific
* implementation to validate this parameter.
* @param name is a string uniquely identifying the metric.
* @param metric_id_ptr is a location to store a token used for subsequent operations on the
* metric.
*/
virtual WasmResult defineMetric(uint32_t /* type */, std::string_view /* name */,
uint32_t * /* metric_id_ptr */) = 0;
/**
* Increment a metric.
* @param metric_id is a token returned by defineMetric() identifying the metric to operate on.
* @param offset is the offset to apply to the Counter.
*/
virtual WasmResult incrementMetric(uint32_t /* metric_id */, int64_t /* offset */) = 0;
/**
* Record a metric.
* @param metric_id is a token returned by defineMetric() identifying the metric to operate on.
* @param value is the value to store for a Gauge or increment for a histogram or Counter.
*/
virtual WasmResult recordMetric(uint32_t /* metric_id */, uint64_t /* value */) = 0;
/**
* Get the current value of a metric.
* @param metric_id is a token returned by defineMetric() identifying the metric to operate on.
* @param value_ptr is a location to store the value of the metric.
*/
virtual WasmResult getMetric(uint32_t /* metric_id */, uint64_t * /* value_ptr */) = 0;
};
struct GeneralInterface {
virtual ~GeneralInterface() = default;
/**
* Will be called on severe Wasm errors. Callees may report and handle the error (e.g. via an
* Exception) to prevent the proxy from crashing.
*/
virtual void error(std::string_view message) = 0;
/**
* Called by all functions which are not overridden with a proxy-specific implementation.
* @return WasmResult::Unimplemented.
*/
virtual WasmResult unimplemented() = 0;
// Log a message.
virtual WasmResult log(uint32_t level, std::string_view message) = 0;
// Return the current log level in the host
virtual uint32_t getLogLevel() = 0;
/**
* Enables a periodic timer with the given period or sets the period of an existing timer. Note:
* the timer is associated with the Root Context of whatever Context this call was made on.
* @param period is the period of the periodic timer in milliseconds. If the period is 0 the
* timer is reset/deleted and will not call onTick.
* @param timer_token_ptr is a pointer to the timer_token. If the target of timer_token_ptr is
* zero, a new timer will be allocated its token will be set. If the target is non-zero, then
* that timer will have the new period (or be reset/deleted if period is zero).
*/
virtual WasmResult setTimerPeriod(std::chrono::milliseconds period,
uint32_t *timer_token_ptr) = 0;
// Provides the current time in nanoseconds since the Unix epoch.
virtual uint64_t getCurrentTimeNanoseconds() = 0;
// Provides the monotonic time in nanoseconds.
virtual uint64_t getMonotonicTimeNanoseconds() = 0;
// Returns plugin configuration.
virtual std::string_view getConfiguration() = 0;
/**
* Provides the status of the last call into the VM or out of the VM, similar to errno.
* @return the status code and a descriptive string.
*/
virtual std::pair<uint32_t, std::string_view> getStatus() = 0;
/**
* Get the value of a property. Some properties are proxy-independent (e.g. ["plugin_root_id"])
* while others can be proxy-specific.
* @param path is a sequence of strings describing a path to a property.
* @param result is a location to write the value of the property.
*/
virtual WasmResult getProperty(std::string_view path, std::string *result) = 0;
/**
* Set the value of a property.
* @param path is a sequence of strings describing a path to a property.
* @param value the value to set. For non-string, non-integral types, the value may be
* serialized..
*/
virtual WasmResult setProperty(std::string_view key, std::string_view value) = 0;
/**
* Custom extension call into the VM. Data is provided as WasmBufferType::CallData.
* @param foreign_function_id a unique identifier for the calling foreign function. These are
* defined and allocated by the foreign function implementor.
* @param data_size is the size of the WasmBufferType::CallData buffer containing data for this
* foreign function call.
*/
virtual void onForeignFunction(uint32_t foreign_function_id, uint32_t data_size) = 0;
};
/**
* SharedDataInterface is for sharing data between VMs. In general the VMs may be on different
* threads. Keys can have any format, but good practice would use reverse DNS and namespacing
* prefixes to avoid conflicts.
*/
struct SharedDataInterface {
virtual ~SharedDataInterface() = default;
/**
* Get proxy-wide key-value data shared between VMs.
* @param key is a proxy-wide key mapping to the shared data value.
* @param cas is a number which will be incremented when a data value has been changed.
* @param data is a location to store the returned stored 'value' and the corresponding 'cas'
* compare-and-swap value which can be used with setSharedData for safe concurrent updates.
*/
virtual WasmResult
getSharedData(std::string_view key,
std::pair<std::string /* value */, uint32_t /* cas */> *data) = 0;
/**
* Set a key-value data shared between VMs.
* @param key is a proxy-wide key mapping to the shared data value.
* @param cas is a compare-and-swap value. If it is zero it is ignored, otherwise it must match
* the cas associated with the value.
* @param data is a location to store the returned value.
*/
virtual WasmResult setSharedData(std::string_view key, std::string_view value, uint32_t cas) = 0;
/**
* Return all the keys from the data shraed between VMs
* @param data is a location to store the returned value.
*/
virtual WasmResult getSharedDataKeys(std::vector<std::string> *result) = 0;
/**
* Removes the given key from the data shared between VMs.
* @param key is a proxy-wide key mapping to the shared data value.
* @param cas is a compare-and-swap value. If it is zero it is ignored, otherwise it must match
* @param cas is a location to store value, and cas number, associated with the removed key
* the cas associated with the value.
*/
virtual WasmResult
removeSharedDataKey(std::string_view key, uint32_t cas,
std::pair<std::string /* value */, uint32_t /* cas */> *result) = 0;
}; // namespace proxy_wasm
struct SharedQueueInterface {
virtual ~SharedQueueInterface() = default;
/**
* Register a proxy-wide queue.
* @param queue_name is a name for the queue. The queue_name is combined with the vm_id (if any)
* to make a unique identifier for the queue.
* @param token_ptr a location to store a token corresponding to the queue.
*/
virtual WasmResult registerSharedQueue(std::string_view queue_name,
SharedQueueDequeueToken *token_ptr) = 0;
/**
* Get the token for a queue.
* @param vm_id is the vm_id of the Plugin of the Root Context which registered the queue.
* @param queue_name is a name for the queue. The queue_name is combined with the vm_id (if any)
* to make a unique identifier for the queue.
* @param token_ptr a location to store a token corresponding to the queue.
*/
virtual WasmResult lookupSharedQueue(std::string_view vm_id, std::string_view queue_name,
SharedQueueEnqueueToken *token_ptr) = 0;
/**
* Dequeue a message from a shared queue.
* @param token is a token returned by registerSharedQueue();
* @param data_ptr is a location to store the data dequeued.
*/
virtual WasmResult dequeueSharedQueue(SharedQueueDequeueToken token, std::string *data_ptr) = 0;
/**
* Enqueue a message on a shared queue.
* @param token is a token returned by resolveSharedQueue();
* @param data is the data to be queued.
*/
virtual WasmResult enqueueSharedQueue(SharedQueueEnqueueToken token, std::string_view data) = 0;
};
} // namespace proxy_wasm