Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1180 lines (846 sloc) 31.3 KB

WebAssembly

Overview

TODO: add text about high level design principal and feature set.

This video talk is a great introduction about architecture of WebAssembly integration.

Configuration

  • v2 API reference (TODO: add link)
  • This filter should be configured with name envoy.wasm.

Example

An example C++ WASM filter could be found here.

To implement a WASM filter:

Context object

WASM module is running in a stack-based virtual machine and its memory is isolated from the host environment. All interactions between host and WASM module are through functions and callbacks wrapped by context object.

At bootstrap time, a root context is created. The root context has the same lifetime as the VM/runtime instance and acts as a target for any interactions which happen at initial setup. It is also used for interactions that outlive a request.

At request time, a context with incremental is created for each stream. Stream context has the same lifetime as the stream itself and acts as a target for interactions that are local to that stream.

image

Context object API

onConfigure

void onConfigure(std::unique_ptr<WasmData> configuration)

Called when host loads the WASM module. configuration is passed in using WasmData. If the VM that the module running in has not been configured, onConfigure is called first with VM config (TODO: add link), then a second call will be invoked to pass in module config (TODO: add link). onConfigure will only be called in root context.

If VM is shared by multiple filters and has already been configured via other WASM filter in the chain, onConfigure will only be called once with module config.

onStart

void onStart()

Called after finishing loading WASM module and before serving any stream events. onStart will only be called in root context.

The following methods are called in order during the lifetime of a stream.

onCreate

void onCreate()

Called at the beginning of filter chain iteration. Indicates creation of the new stream context.

onRequestHeaders

void onRequestHeaders()

Called when headers are decoded. Request Headers could be fetched and manipulated by request header API

Returns FilterHeadersStatus to determine how filter chain iteration proceeds.

onRequestBody

FilterDataStatus onRequestBody(size_t body_buffer_length, bool end_of_stream)

Called when request body is decoded. body_buffer_length is used to indicate size of decoded request body. end_of_stream indicates if this is the last data frame. Request body could be fetched by body API.

Returns FilterDataStatus to determine how filter chain iteration proceeds.

onRequestTrailers

FilterTrailersStatus onRequestTrailers()

Called when request trailers are decoded. Request trailers could be fetched and manipulated by request trailer API.

Returns FilterTrailerStatus to determine how filter chain iteration proceeds.

onResponseHeaders

void onResponseHeaders()

Called when headers are decoded. Response headers could be fetched and manipulated by response header API.

Returns FilterHeadersStatus to determine how filter chain iteration proceeds.

onResponseBody

FilterDataStatus onResponseBody(size_t body_buffer_length, bool end_of_stream)

Called when response body is decoded. body_buffer_length is used to indicate size of decoded response body. end_of_stream indicates if this is the last data frame. Response body could be fetched by body API.

Returns FilterDataStatus to determine how filter chain iteration proceeds.

onResponseTrailers

FilterTrailersStatus onResponseTrailers()

Called when response trailers are decoded. Response trailers could be fetched and manipulated response trailer API.

Returns FilterTrailerStatus FilterTrailerStatus to determine how filter chain iteration proceeds.

onDone

void onDone()

Called after stream is ended or reset. All stream info will not be changed any more and is safe for access logging.

note

This is called before onLog.

onLog

void onLog()

Called to log any stream info. Several types of stream info are available from API: Request headers could be fetched by request header API Response headers could be fetched by response header API. Response trailers could be fetched by response trailer API. Streaminfo could be fetched by streaminfo API.

note

This is called after [onDone])(#ondone).

onDelete

void onDelete()

Called after logging is done. This call indicates no more handler will be called on the stream context and it is up for deconstruction, The stream context needs to make sure all async events are cleaned up, such as network calls, timers.

Root context object is used to handle timer event.

onTick

void onTick()

Called when a timer is set and fired. Timer could be set by setTickPeriodMilliseconds.

The following methods on context object are supported.

httpCall

void httpCall(StringView cluster,
              const HeaderStringPairs& request_headers,
              StringView request_body,
              const HeaderStringPairs& request_trailers,
              uint32_t timeout_milliseconds,
              HttpCallCallback callback)

Makes an HTTP call to an upstream host.

cluster is a string which maps to a configured cluster manager cluster. request_headers is a vector of key/value pairs to send. Note that the :method, :path, and :authority headers must be set. request_body is an optional string of body data to send. timeout is an integer that specifies the call timeout in milliseconds. timeout_milliseconds is an unsigned integer as timeout period for the http call in milliseconds. callback is the callback function to be called when the HTTP request finishes.

note

If the call outlives the stream context, httpCall should be called within root context.

grpcSimpleCall

template<typename Response>
void grpcSimpleCall(StringView service,
                    StringView service_name,
                    StringView method_name,
                    const google::protobuf::MessageLite &request,
                    uint32_t timeout_milliseconds,
                    std::function<void(Response&& response)> success_callback,
                    std::function<void(GrpcStatus status, StringView error_message)> failure_callback)

Makes a unary gRPC call to an upstream host.

service is a serialized proto string of gRPC service for gRPC client initialization. service_name and method_name indicates the target gRPC service and method name. request is a lite proto message that gRPC service accepts as request. timeout_milliseconds is an unsigned integer as timeout period for the gRPC call in milliseconds. success_callback is the callback function that will be called when gRPC call succeeds. response is the returned message from gRPC service. failure_callback is the callback function that will be invoked when gRPC call fails. status is the returned gRPC status code. error_message is detailed error message extracted from gRPC response.

note

if the call outlives the stream context, grpcSimpleCall should be called within root context.

grpcCallHandler

void grpcCallHandler(
    StringView service,
    StringView service_name,
    StringView method_name,
    const google::protobuf::MessageLite &request,
    uint32_t timeout_milliseconds,
    std::unique_ptr<GrpcCallHandlerBase> handler)

Makes a unary gRPC call to an upstream host.

Similar to grpcSimpleCall for gRPC client initialization, but uses GrpcCallHandler as target for callback and fine grained control on the call.

grpcStreamHandler

void grpcStreamHandler(StringView service,
                       StringView service_name,
                       StringView method_name,
                       std::unique_ptr<GrpcStreamHandlerBase> handler)

Makes an gRPC stream to an upstream host.

service is a serialized proto string of gRPC service for gRPC client initialization. service_name and method_name indicates the target gRPC service and method name. handler GrpcStreamHandler is used to control the stream and as target for gRPC stream callbacks.

note

if the stream call outlives the per request context, grpcStreamHandler should be called within root context.

Application log API

log*

void LogTrace(const std::string& logMessage)
void LogDebug(const std::string& logMessage)
void LogInfo(const std::string& logMessage)
void LogWarn(const std::string& logMessage)
void LogError(const std::string& logMessage)
void LogCritical(const std::string& logMessage)

Logs a message using Envoy's application logging. logMessage is a string to log.

Header API

addRequestHeader

void addRequestHeader(StringView key, StringView value)

Adds a new request header with the key and value if header does not exist, or append the value if header exists. This method is effective only when called in onRequestHeader.

replaceRequestHeader

void replaceRequestHeader(StringView key, StringView value)

Replaces the value of an existing request header with the given key, or create a new request header with the key and value if not existing. This method is effective only when called in onRequestHeader.

removeRequestHeader

void removeRequestHeader(StringView key)

Removes request header with the given key. No-op if the request header does not exist. This method is effective only when called in onRequestHeader.

setRequestHeaderPairs

void setRequestHeaderPairs(const HeaderStringPairs &pairs)

Sets request headers with the given header pairs. For each header key value pair, it acts the same way as replaceRequestHeader. This method is effective only when called in onRequestHeader.

getRequestHeader

WasmDataPtr getRequestHeader(StringView key)

Gets value of header with the given key. Returns empty string if header does not exist. This method is effective only when called in onRequestHeader and onLog

Returns WasmData pointer which contains the header value data.

getRequestHeaderPairs

WasmDataPtr getRequestHeaderPairs()

Gets all header pairs. This method is effective only when called in onRequestHeader and onLog

Returns WasmData pointer which contains header pairs data.

addResponseHeader

void addResponseHeader(StringView key, StringView value)

Adds a new response header with the key and value if header does not exist, or append the value if header exists. This method is effective only when called in onResponseHeaders.

replaceResponseHeader

void replaceResponseHeader(StringView key, StringView value)

Replaces the value of an existing response header with the given key, or create a new response header with the key and value if not existing. This method is effective only when called in onResponseHeaders.

removeResponseHeader

void removeResponseHeader(StringView key)

Removes response header with the given key. No-op if the response header does not exist. This method is effective only when called in onResponseHeaders.

setResponseHeaderPairs

void setResponseHeaderPairs(const HeaderStringPairs &pairs)

Sets response headers with the given header pairs. For each header key value pair, it acts the same way as replaceResponseHeader. This method is effective only when called in onResponseHeaders.

getResponseHeader

WasmDataPtr getResponseHeader(StringView key)

Gets value of header with the given key. Returns empty string if header does not exist. This method is effective only when called in onResponseHeaders and onLog

Returns WasmData pointer which holds the header value.

getResponseHeaderPairs

WasmDataPtr getResponseHeaderPairs()

Gets all header pairs. This method is effective only when called in onResponseHeaders and onLog

Returns WasmData pointer which holds the header pairs.

addRequestTrailer

void addRequestTrailer(StringView key, StringView value)

Adds a new request trailer with the key and value if trailer does not exist, or append the value if trailer exists. This method is effective only when called in onRequestTrailers.

replaceRequestTrailer

void replaceRequestTrailer(StringView key, StringView value)

Replaces the value of an existing request trailer with the given key, or create a new request trailer with the key and value if not existing. This method is effective only when called in onRequestTrailers.

removeRequestTrailer

void removeRequestTrailer(StringView key)

Removes request trailer with the given key. No-op if the request trailer does not exist. This method is effective only when called in onRequestTrailers.

setRequestTrailerPairs

void setRequestTrailerPairs(const HeaderStringPairs &pairs)

Sets request trailers with the given trailer pairs. For each trailer key value pair,it acts the same way as replaceRequestHeader. This method is effective only when called in onRequestTrailers.

getRequestTrailer

WasmDataPtr getRequestTrailer(StringView key)

Gets value of trailer with the given key. Returns empty string if trailer does not exist. This method is effective only when called in onRequestTrailers.

Returns WasmData pointer which holds the trailer value.

getRequestTrailerPairs

WasmDataPtr getRequestTrailerPairs()

Gets all trailer pairs. This method is effective only when called in onRequestTrailers.

Returns WasmData pointer which holds the trailer pairs.

addResponseTrailer

void addResponseTrailer(StringView key, StringView value)

Adds a new response trailer with the key and value if trailer does not exist, or append the value if trailer exists. This method is effective only when called in onResponseTrailers.

replaceResponseTrailer

void replaceResponseTrailer(StringView key, StringView value)

Replaces the value of an existing response trailer with the given key, or create a new response trailer with the key and value if not existing. This method is effective only when called in onResponseTrailers.

removeResponseTrailer

void removeResponseTrailer(StringView key)

Removes response trailer with the given key. No-op if the response trailer does not exist. This method is effective only when called in onResponseTrailers.

setResponseTrailerPairs

void setResponseTrailerPairs(const TrailerStringPairs &pairs)

Sets response trailers with the given trailer pairs. For each trailer key value pair, it acts the same way as replaceResponseTrailer. This method is effective only when called in onResponseTrailers.

getResponseTrailer

WasmDataPtr getResponseTrailer(StringView key)

Gets value of trailer with the given key. Returns empty string if trailer does not exist. This method is effective only when called in onResponseTrailers and onLog

Returns WasmData pointer which holds the trailer value.

getResponseTrailerPairs

WasmDataPtr getResponseTrailerPairs()

Gets all trailer pairs. This method is effective only when called in onResponseTrailers and onLog

Returns WasmData pointer which holds the trailer pairs.

Body API

getRequestBodyBufferBytes

WasmDataPtr getRequestBodyBufferBytes(size_t start, size_t length)

Returns buffered request body. This copies segment of request body. start is an integer and supplies the body buffer start index to copy. length is an integer and supplies the buffer length to copy. This method is effective when calling from onRequestBody.

Returns WasmData pointer which holds the request body data.

getResponseBodyBufferBytes

WasmDataPtr getResponseBodyBufferBytes(size_t start, size_t length)

Returns buffered response body. This copies segment of response body. start is an integer and supplies the body buffer start index to copy. length is an integer and supplies the buffer length to copy. This method is effective when calling from onResponseBody.

Returns WasmData pointer which holds the response body data.

Metadata API

TODO: Add metadata related API

StreamInfo API

getProtocol

WasmDataPtr getProtocol(StreamType type)

Returns the string representation of HTTP protocol used by the current request. The possible values are: HTTP/1.0, HTTP/1.1, and HTTP/2. type is the stream type with two possible values: StreamType::Request and StreamType::Response. The string protocol is returned as WasmData.

Timer API

Timer API is used to set a timer and get current timestamp.

setTickPeriodMilliseconds

void setTickPeriodMilliseconds(uint32_t millisecond)

Set a timer. millisecond is tick interval in millisecond. onTick will be invoked when timer fires.

note

Only one timer could be set per root_id which is vectored to the appropriate RootContext. Any context can call setTickPeriodMilliseconds and the onTick will come on the corresponding RootContext.

getCurrentTimeNanoseconds

uint64 getCurrentTimeNanoseconds()

Returns timestamp of now in nanosecond precision.

Stats API

The following objects are supported to export stats from WASM module to host stats sink.

Counter

New

static Counter<Tags...>* New(StringView name, MetricTagDescriptor<Tags>... fieldnames)

Create a new counter with the given metric name and tag names. Example code to create a counter metric:

auto c = Counter<std::string, int, bool>::New(
             "test_counter", "string_tag", "int_tag", "bool_tag");

Returns a pointer to counter object.

increment

void increment(int64_t offset, Tags... tags)

Increments a counter. offset is the value the counter incremented by. tags is a list of tag values to identify a specific counter. Example code to increment the aforementioned counter:

c->increment(1, "test_tag", 7, true)

get

uint64_t get(Tags... tags)

Returns value of a counter. tags is a list of tag values to identify a specific counter. Example code to get value of a counter:

c->get("test_tag", 7, true);

resolve

SimpleCounter resolve(Tags... f)

Resolves counter object to a specific counter for a list of tag values.

Returns a SimpleCounter resolved from the counter object, so that tag values do not need to be specified in every increment call. Example code:

auto simple_counter = c->resolve("test_tag", 7, true);

SimpleCounter

SimpleCounter is resolved from a Counter object with predetermined tag values.

increment

void increment(int64_t offset)

Increment a counter. offset is the value counter incremented by.

get

uint64_t get()

Returns current value of a counter.

Gauge

New

static Gauge<Tags...>* New(StringView name, MetricTagDescriptor<Tags>... fieldnames)

Create a new gauge with the given metric name and tag names. Example code to create a gauge metric:

auto c = Gauge<std::string, int, bool>::New(
             "test_gauge", "string_tag", "int_tag", "bool_tag");

Returns a pointer to Gauge object.

record

void record(int64_t offset, Tags... tags)

Records current value of a gauge. offset is the value to set for current gauge. tags is a list of tag values to identify a specific gauge. Example code to record value of a gauge metric:

c->record(1, "test_tag", 7, true)

get

uint64_t get(Tags... tags)

Returns value of a gauge. tags is a list of tag values to identify a specific gauge. Example code to get value of a gauge:

c->get("test_tag", 7, true);

resolve

SimpleGauge resolve(Tags... f)

Resolves gauge object to a specific gauge for a list of tag values.

Returns a SimpleGauge resolved from the gauge object, so that tag values do not need to be specified in every record call. Example code:

auto simple_gauge = c->resolve("test_tag", 7, true);

SimpleGauge

SimpleGauge is resolved from a Gauge object with predetermined tag values.

record

void record(int64_t offset)

Records current value of a gauge. offset is the value to set for current gauge.

get

uint64_t get()

Returns current value of a gauge.

Histogram

New

static Histogram<Tags...>* New(StringView name, MetricTagDescriptor<Tags>... fieldnames)

Create a new histogram object with the given metric name and tag names. Example code to create a histogram metric:

auto h = Histogram<std::string, int, bool>::New(
             "test_histogram", "string_tag", "int_tag", "bool_tag");

Returns a pointer to Histogram object.

record

void record(int64_t offset, Tags... tags)

Records a value in histogram stats. offset is the value to be recorded. tags is a list of tag values to identify a specific histogram. Example code to add a new value into histogram:

h->record(1, "test_tag", 7, true)

resolve

SimpleHistogram resolve(Tags... f)

Resolves histogram object to a specific histogram for a list of tag values.

Returns a SimpleHistogram resolved from the histogram object, so that tag values do not need to be specified in every record call. Example code:

auto simple_histogram = c->resolve("test_tag", 7, true);

SimpleHistogram

SimpleHistogram is resolved from a Histogram object with predetermined tag values.

record

void record(int64_t offset)

Records a value in histogram. offset is the value to be recorded.

Data Structure

GrpcCallHandler class

Base class for gRPC unary call handler. Subclass should specify response message type and override necessary callbacks. Example code to create a call handler using google::protobuf::Empty as response message.

class CallHandler : public GrpcCallHandler<google::protobuf::Empty> {
  public:
    void onSuccess(google::protobuf::Empty&& response) {
        /* override onSuccess code */
    }
    /*
        more callbacks such as onFailure
    */
};

To initialize a handler, pass in a pointer to context object that this call should attach to. For example, passing in root context:

auto handler = std::make_unique<CallHandler>(&root_context);

Note the context object needs to outlive the call. handler is also used for WASM module to interact with the stream, such as canceling the call.

onSuccess

void onSuccess(Message&& response)

Called when the async gRPC request succeeds. No further callbacks will be invoked.

onFailure

void onFailure(GrpcStatus status, std::unique_ptr<WasmData> error_message)

Called when the async gRPC request fails. No further callbacks will be invoked. status is returned grpc status. error_message is the gRPC status message or empty string if not present.

cancel

void cancel()

Signals that the request should be cancelled. No further callbacks will be invoked.

GrpcStreamHandler class

Base class for gRPC stream handler. Subclass should specify stream message type and override callbacks. Example code to create a stream handler using google::protobuf::Struct as request message and google::protobuf::Any response message:

class StreamHandler : public GrpcStreamHandler<google::protobuf::Struct, google::protobuf::Any> {
  public:
    void onReceive(google::protobuf::Any&& message) {
        /* override onReceive code */
    }
    /*
        more callbacks such as onReceiveTrailingMetadata, onReceive, onRemoteClose
    */
};

To initialize a handler, pass in a pointer to context object that this stream should attach to. For example, passing in root context:

auto handler = std::make_unique<StreamHandler>(&root_context);

Note the context object needs to outlive the stream. handler is also used for WASM module to interact with the stream, such as sending message, closing and resetting stream.

send

void send(const Request& message, bool end_of_stream)

Sends a request message to the stream. end_of_stream indicates if this is the last message to send. With end_of_stream as true, callbacks can still occur.

close

void close()

Close the stream locally and send an empty DATA frame to the remote. No further methods may be invoked on the stream object, but callbacks may still be received until the stream is closed remotely.

reset

void reset()

Close the stream locally and remotely (as needed). No further methods may be invoked on the handler object and no further callbacks will be invoked.

onReceiveInitialMetadata

void onReceiveInitialMetadata()

Called when initial metadata is received. This will be called with empty metadata on a trailers-only response, followed by onReceiveTrailingMetadata() with the trailing metadata. . TODO: how to get initial metadata?

onReceiveTrailingMetadata

void onReceiveTrailingMetadata()

Called when trailing metadata is received. This will also be called on non-Ok grpc-status stream termination.

onReceive

void onReceive(Response&& message)

Called when an async gRPC message is received.

onRemoteClose

void onRemoteClose(GrpcStatus status, std::unique_ptr<WasmData> error_message)

Called when the remote closes or an error occurs on the gRPC stream. The stream is considered remotely closed after this invocation and no further callbacks will be invoked. In addition, no further stream operations are permitted. status is the grpc status, error_message is the gRPC status error message or empty string if not present.

WasmData

WasmData is used to represent data passed into WASM module from host. It is like string view, which holds a pointer to start of the data and a size. It also supports several methods to access the data.

data

const char* data()

Returns the start pointer of the data.

view

StringView view()

Returns data as a string view constructed with the start pointer and the size.

toString

std::string toString()

Returns data as a string by converting the string view to string.

pairs

std::vector<std::pair<StringView, StringView>> pairs()

Returns a vector of string view pair parsed from the data.

proto

template<typename T> T proto()

Returns a proto message parsed from the data based on the specified proto type.

Out of tree WASM module

TODO: add an example about out of tree WASM module example

VM Sharing

TODO: add instruction about vm sharing

You can’t perform that action at this time.