diff --git a/include/proxy-wasm/context.h b/include/proxy-wasm/context.h index 9c1189cbe..53717f5b2 100644 --- a/include/proxy-wasm/context.h +++ b/include/proxy-wasm/context.h @@ -238,6 +238,10 @@ class ContextBase : public RootInterface, t += tpe.tv_nsec; return t; } + std::string_view getConfiguration() override { + unimplemented(); + return ""; + } std::pair getStatus() override { unimplemented(); return std::make_pair(1, "unimplmemented"); @@ -316,6 +320,7 @@ class ContextBase : public RootInterface, std::string_view /* details */) override { return unimplemented(); } + void clearRouteCache() override { unimplemented(); } void failStream(WasmStreamType stream_type) override { closeStream(stream_type); } // Shared Data diff --git a/include/proxy-wasm/context_interface.h b/include/proxy-wasm/context_interface.h index b7c252fbe..2fdc9087a 100644 --- a/include/proxy-wasm/context_interface.h +++ b/include/proxy-wasm/context_interface.h @@ -235,6 +235,9 @@ struct HttpInterface { 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; @@ -551,6 +554,9 @@ struct GeneralInterface { // Provides the current time in nanoseconds since the Unix epoch. virtual uint64_t getCurrentTimeNanoseconds() = 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. diff --git a/include/proxy-wasm/exports.h b/include/proxy-wasm/exports.h index a5bacb7f1..5d4df20dd 100644 --- a/include/proxy-wasm/exports.h +++ b/include/proxy-wasm/exports.h @@ -29,18 +29,22 @@ namespace exports { // ABI functions exported from envoy to wasm. +Word get_configuration(void *raw_context, Word address, Word size); Word get_status(void *raw_context, Word status_code, Word address, Word size); Word log(void *raw_context, Word level, Word address, Word size); Word get_log_level(void *raw_context, Word result_level_uint32_ptr); Word get_property(void *raw_context, Word path_ptr, Word path_size, Word value_ptr_ptr, Word value_size_ptr); Word set_property(void *raw_context, Word key_ptr, Word key_size, Word value_ptr, Word value_size); +Word continue_request(void *raw_context); +Word continue_response(void *raw_context); Word continue_stream(void *raw_context, Word stream_type); Word close_stream(void *raw_context, Word stream_type); Word send_local_response(void *raw_context, Word response_code, Word response_code_details_ptr, Word response_code_details_size, Word body_ptr, Word body_size, Word additional_response_header_pairs_ptr, Word additional_response_header_pairs_size, Word grpc_status); +Word clear_route_cache(void *raw_context); Word get_shared_data(void *raw_context, Word key_ptr, Word key_size, Word value_ptr_ptr, Word value_size_ptr, Word cas_ptr); Word set_shared_data(void *raw_context, Word key_ptr, Word key_size, Word value_ptr, diff --git a/include/proxy-wasm/null_plugin.h b/include/proxy-wasm/null_plugin.h index 9c8120892..9d8d01068 100644 --- a/include/proxy-wasm/null_plugin.h +++ b/include/proxy-wasm/null_plugin.h @@ -37,6 +37,7 @@ namespace proxy_wasm { */ struct NullPluginRegistry { void (*proxy_abi_version_0_1_0_)() = nullptr; + void (*proxy_abi_version_0_2_0_)() = nullptr; void (*proxy_on_log_)(uint32_t context_id) = nullptr; uint32_t (*proxy_validate_configuration_)(uint32_t root_context_id, uint32_t plugin_configuration_size) = nullptr; diff --git a/include/proxy-wasm/null_vm.h b/include/proxy-wasm/null_vm.h index f86ba1038..bfa62d896 100644 --- a/include/proxy-wasm/null_vm.h +++ b/include/proxy-wasm/null_vm.h @@ -36,6 +36,7 @@ struct NullVm : public WasmVm { Cloneable cloneable() override { return Cloneable::InstantiatedModule; }; std::unique_ptr clone() override; bool load(const std::string &code, bool allow_precompiled) override; + AbiVersion getAbiVersion() override; bool link(std::string_view debug_name) override; uint64_t getMemorySize() override; std::optional getMemory(uint64_t pointer, uint64_t size) override; diff --git a/include/proxy-wasm/wasm.h b/include/proxy-wasm/wasm.h index 4b7bb7d76..90347f6f0 100644 --- a/include/proxy-wasm/wasm.h +++ b/include/proxy-wasm/wasm.h @@ -122,6 +122,8 @@ class WasmBase : public std::enable_shared_from_this { virtual void error(std::string_view message) { std::cerr << message << "\n"; } virtual void unimplemented() { error("unimplemented proxy-wasm API"); } + AbiVersion abiVersion() { return abi_version_; } + bool getEmscriptenVersion(uint32_t *emscripten_metadata_major_version, uint32_t *emscripten_metadata_minor_version, uint32_t *emscripten_abi_major_version, @@ -184,8 +186,6 @@ class WasmBase : public std::enable_shared_from_this { std::unique_ptr shutdown_handle_; std::unordered_set pending_done_; // Root contexts not done during shutdown. - WasmCallVoid<0> abi_version_0_1_0_; - WasmCallVoid<0> _start_; /* Emscripten v1.39.0+ */ WasmCallVoid<0> __wasm_call_ctors_; @@ -205,12 +205,14 @@ class WasmBase : public std::enable_shared_from_this { WasmCallVoid<2> on_downstream_connection_close_; WasmCallVoid<2> on_upstream_connection_close_; - WasmCallWord<3> on_request_headers_; + WasmCallWord<2> on_request_headers_abi_01_; + WasmCallWord<3> on_request_headers_abi_02_; WasmCallWord<3> on_request_body_; WasmCallWord<2> on_request_trailers_; WasmCallWord<2> on_request_metadata_; - WasmCallWord<3> on_response_headers_; + WasmCallWord<2> on_response_headers_abi_01_; + WasmCallWord<3> on_response_headers_abi_02_; WasmCallWord<3> on_response_body_; WasmCallWord<2> on_response_trailers_; WasmCallWord<2> on_response_metadata_; @@ -238,6 +240,9 @@ class WasmBase : public std::enable_shared_from_this { bool allow_precompiled_ = false; FailState failed_ = FailState::Ok; // Wasm VM fatal error. + // ABI version. + AbiVersion abi_version_ = AbiVersion::Unknown; + bool is_emscripten_ = false; uint32_t emscripten_metadata_major_version_ = 0; uint32_t emscripten_metadata_minor_version_ = 0; diff --git a/include/proxy-wasm/wasm_api_impl.h b/include/proxy-wasm/wasm_api_impl.h index d145fad67..78518a7d6 100644 --- a/include/proxy-wasm/wasm_api_impl.h +++ b/include/proxy-wasm/wasm_api_impl.h @@ -23,6 +23,13 @@ namespace null_plugin { inline WasmResult wordToWasmResult(Word w) { return static_cast(w.u64_); } +// Configuration and Status +inline WasmResult proxy_get_configuration(const char **configuration_ptr, + size_t *configuration_size) { + return wordToWasmResult( + exports::get_configuration(current_context_, WR(configuration_ptr), WR(configuration_size))); +} + inline WasmResult proxy_get_status(uint32_t *code_ptr, const char **ptr, size_t *size) { return wordToWasmResult(exports::get_status(current_context_, WR(code_ptr), WR(ptr), WR(size))); } @@ -58,6 +65,12 @@ inline WasmResult proxy_set_property(const char *key_ptr, size_t key_size, const } // Continue +inline WasmResult proxy_continue_request() { + return wordToWasmResult(exports::continue_request(current_context_)); +} +inline WasmResult proxy_continue_response() { + return wordToWasmResult(exports::continue_response(current_context_)); +} inline WasmResult proxy_continue_stream(WasmStreamType stream_type) { return wordToWasmResult(exports::continue_stream(current_context_, WS(stream_type))); } @@ -76,6 +89,10 @@ proxy_send_local_response(uint32_t response_code, const char *response_code_deta WS(grpc_status))); } +inline WasmResult proxy_clear_route_cache() { + return wordToWasmResult(exports::clear_route_cache(current_context_)); +} + // SharedData inline WasmResult proxy_get_shared_data(const char *key_ptr, size_t key_size, const char **value_ptr, size_t *value_size, uint32_t *cas) { diff --git a/include/proxy-wasm/wasm_vm.h b/include/proxy-wasm/wasm_vm.h index 5aaca8ced..354b4ba01 100644 --- a/include/proxy-wasm/wasm_vm.h +++ b/include/proxy-wasm/wasm_vm.h @@ -101,6 +101,8 @@ enum class Cloneable { InstantiatedModule // VMs can be cloned from an instantiated module. }; +enum class AbiVersion { ProxyWasm_0_1_0, ProxyWasm_0_2_0, Unknown }; + class NullPlugin; // Integrator specific WasmVm operations. @@ -185,6 +187,12 @@ class WasmVm { */ virtual bool link(std::string_view debug_name) = 0; + /** + * Get ABI version of the module. + * @return the ABI version. + */ + virtual AbiVersion getAbiVersion() = 0; + /** * Get size of the currently allocated memory in the VM. * @return the size of memory in bytes. diff --git a/src/context.cc b/src/context.cc index b7b8d4d1f..4b31f6075 100644 --- a/src/context.cc +++ b/src/context.cc @@ -37,8 +37,24 @@ } \ } +#define CHECK_FAIL2(_call1, _call2, _stream_type, _return_open, _return_closed) \ + if (isFailed()) { \ + if (plugin_->fail_open_) { \ + return _return_open; \ + } else { \ + failStream(_stream_type); \ + return _return_closed; \ + } \ + } else { \ + if (!wasm_->_call1 && !wasm_->_call2) { \ + return _return_open; \ + } \ + } + #define CHECK_HTTP(_call, _return_open, _return_closed) \ CHECK_FAIL(_call, WasmStreamType::Request, _return_open, _return_closed) +#define CHECK_HTTP2(_call1, _call2, _return_open, _return_closed) \ + CHECK_FAIL2(_call1, _call2, WasmStreamType::Request, _return_open, _return_closed) #define CHECK_NET(_call, _return_open, _return_closed) \ CHECK_FAIL(_call, WasmStreamType::Downstream, _return_open, _return_closed) @@ -443,11 +459,15 @@ void ContextBase::onUpstreamConnectionClose(CloseType close_type) { template static uint32_t headerSize(const P &p) { return p ? p->size() : 0; } FilterHeadersStatus ContextBase::onRequestHeaders(uint32_t headers, bool end_of_stream) { - CHECK_HTTP(on_request_headers_, FilterHeadersStatus::Continue, - FilterHeadersStatus::StopIteration); + CHECK_HTTP2(on_request_headers_abi_01_, on_request_headers_abi_02_, FilterHeadersStatus::Continue, + FilterHeadersStatus::StopIteration); DeferAfterCallActions actions(this); - auto result = - wasm_->on_request_headers_(this, id_, headers, static_cast(end_of_stream)).u64_; + auto result = wasm_->on_request_headers_abi_01_ + ? wasm_->on_request_headers_abi_01_(this, id_, headers).u64_ + : wasm_ + ->on_request_headers_abi_02_(this, id_, headers, + static_cast(end_of_stream)) + .u64_; if (result > static_cast(FilterHeadersStatus::StopAllIterationAndWatermark)) return FilterHeadersStatus::StopAllIterationAndWatermark; return static_cast(result); @@ -485,11 +505,15 @@ FilterMetadataStatus ContextBase::onRequestMetadata(uint32_t elements) { } FilterHeadersStatus ContextBase::onResponseHeaders(uint32_t headers, bool end_of_stream) { - CHECK_HTTP(on_response_headers_, FilterHeadersStatus::Continue, - FilterHeadersStatus::StopIteration); + CHECK_HTTP2(on_response_headers_abi_01_, on_response_headers_abi_02_, + FilterHeadersStatus::Continue, FilterHeadersStatus::StopIteration); DeferAfterCallActions actions(this); - auto result = - wasm_->on_response_headers_(this, id_, headers, static_cast(end_of_stream)).u64_; + auto result = wasm_->on_response_headers_abi_01_ + ? wasm_->on_response_headers_abi_01_(this, id_, headers).u64_ + : wasm_ + ->on_response_headers_abi_02_(this, id_, headers, + static_cast(end_of_stream)) + .u64_; if (result > static_cast(FilterHeadersStatus::StopAllIterationAndWatermark)) return FilterHeadersStatus::StopAllIterationAndWatermark; return static_cast(result); diff --git a/src/exports.cc b/src/exports.cc index 7ad07b93e..07ce7956a 100644 --- a/src/exports.cc +++ b/src/exports.cc @@ -148,6 +148,15 @@ Word get_property(void *raw_context, Word path_ptr, Word path_size, Word value_p return WasmResult::Ok; } +Word get_configuration(void *raw_context, Word value_ptr_ptr, Word value_size_ptr) { + auto context = WASM_CONTEXT(raw_context); + auto value = context->getConfiguration(); + if (!context->wasm()->copyToPointerSize(value, value_ptr_ptr, value_size_ptr)) { + return WasmResult::InvalidMemoryAccess; + } + return WasmResult::Ok; +} + Word get_status(void *raw_context, Word code_ptr, Word value_ptr_ptr, Word value_size_ptr) { auto context = WASM_CONTEXT(raw_context); auto status = context->getStatus(); @@ -163,6 +172,16 @@ Word get_status(void *raw_context, Word code_ptr, Word value_ptr_ptr, Word value // HTTP // Continue/Reply/Route +Word continue_request(void *raw_context) { + auto context = WASM_CONTEXT(raw_context); + return context->continueStream(WasmStreamType::Request); +} + +Word continue_response(void *raw_context) { + auto context = WASM_CONTEXT(raw_context); + return context->continueStream(WasmStreamType::Response); +} + Word continue_stream(void *raw_context, Word type) { auto context = WASM_CONTEXT(raw_context); if (type > static_cast(WasmStreamType::MAX)) { @@ -198,6 +217,12 @@ Word send_local_response(void *raw_context, Word response_code, Word response_co return WasmResult::Ok; } +Word clear_route_cache(void *raw_context) { + auto context = WASM_CONTEXT(raw_context); + context->clearRouteCache(); + return WasmResult::Ok; +} + Word set_effective_context(void *raw_context, Word context_id) { auto context = WASM_CONTEXT(raw_context); uint32_t cid = static_cast(context_id); diff --git a/src/null/null_plugin.cc b/src/null/null_plugin.cc index dbac0e9cf..0b2974015 100644 --- a/src/null/null_plugin.cc +++ b/src/null/null_plugin.cc @@ -33,9 +33,7 @@ namespace proxy_wasm { void NullPlugin::getFunction(std::string_view function_name, WasmCallVoid<0> *f) { - if (function_name == "proxy_abi_version_0_1_0") { - *f = [](ContextBase *) { /* dummy function */ }; - } else if (function_name == "_start") { + if (function_name == "_start") { *f = nullptr; } else if (function_name == "__wasm_call_ctors") { *f = nullptr; diff --git a/src/null/null_vm.cc b/src/null/null_vm.cc index c5d24ed27..aa7549357 100644 --- a/src/null/null_vm.cc +++ b/src/null/null_vm.cc @@ -57,6 +57,8 @@ bool NullVm::load(const std::string &name, bool /* allow_precompiled */) { return true; } +AbiVersion NullVm::getAbiVersion() { return AbiVersion::ProxyWasm_0_2_0; } + bool NullVm::link(std::string_view /* name */) { return true; } uint64_t NullVm::getMemorySize() { return std::numeric_limits::max(); } diff --git a/src/v8/v8.cc b/src/v8/v8.cc index faa2a6e6e..789e29cc3 100644 --- a/src/v8/v8.cc +++ b/src/v8/v8.cc @@ -54,6 +54,7 @@ class V8 : public WasmVm { std::string_view runtime() override { return "v8"; } bool load(const std::string &code, bool allow_precompiled) override; + AbiVersion getAbiVersion() override; std::string_view getCustomSection(std::string_view name) override; std::string_view getPrecompiledSectionName() override; bool link(std::string_view debug_name) override; @@ -366,6 +367,24 @@ std::string_view V8::getPrecompiledSectionName() { return name; } +AbiVersion V8::getAbiVersion() { + assert(module_ != nullptr); + + const auto export_types = module_.get()->exports(); + for (size_t i = 0; i < export_types.size(); i++) { + if (export_types[i]->type()->kind() == wasm::EXTERN_FUNC) { + std::string_view name(export_types[i]->name().get(), export_types[i]->name().size()); + if (name == "proxy_abi_version_0_1_0") { + return AbiVersion::ProxyWasm_0_1_0; + } else if (name == "proxy_abi_version_0_2_0") { + return AbiVersion::ProxyWasm_0_2_0; + } + } + } + + return AbiVersion::Unknown; +} + bool V8::link(std::string_view debug_name) { assert(module_ != nullptr); diff --git a/src/wasm.cc b/src/wasm.cc index a874d1ace..9be5938bc 100644 --- a/src/wasm.cc +++ b/src/wasm.cc @@ -148,8 +148,6 @@ void WasmBase::registerCallbacks() { _REGISTER_PROXY(set_property); _REGISTER_PROXY(get_property); - _REGISTER_PROXY(continue_stream); - _REGISTER_PROXY(close_stream); _REGISTER_PROXY(send_local_response); _REGISTER_PROXY(get_shared_data); @@ -191,6 +189,16 @@ void WasmBase::registerCallbacks() { _REGISTER_PROXY(set_effective_context); _REGISTER_PROXY(done); _REGISTER_PROXY(call_foreign_function); + + if (abiVersion() == AbiVersion::ProxyWasm_0_1_0) { + _REGISTER_PROXY(get_configuration); + _REGISTER_PROXY(continue_request); + _REGISTER_PROXY(continue_response); + _REGISTER_PROXY(clear_route_cache); + } else if (abiVersion() == AbiVersion::ProxyWasm_0_2_0) { + _REGISTER_PROXY(continue_stream); + _REGISTER_PROXY(close_stream); + } #undef _REGISTER_PROXY } @@ -200,14 +208,17 @@ void WasmBase::getFunctions() { _GET(__wasm_call_ctors); _GET(malloc); + if (!malloc_) { + fail(FailState::MissingFunction, "Wasm module is missing malloc function."); + } #undef _GET #define _GET_PROXY(_fn) wasm_vm_->getFunction("proxy_" #_fn, &_fn##_); +#define _GET_PROXY_ABI(_fn, _abi) wasm_vm_->getFunction("proxy_" #_fn, &_fn##_abi##_); _GET_PROXY(validate_configuration); _GET_PROXY(on_vm_start); _GET_PROXY(on_configure); _GET_PROXY(on_tick); - _GET_PROXY(on_foreign_function); _GET_PROXY(on_context_create); @@ -217,11 +228,9 @@ void WasmBase::getFunctions() { _GET_PROXY(on_downstream_connection_close); _GET_PROXY(on_upstream_connection_close); - _GET_PROXY(on_request_headers); _GET_PROXY(on_request_body); _GET_PROXY(on_request_trailers); _GET_PROXY(on_request_metadata); - _GET_PROXY(on_response_headers); _GET_PROXY(on_response_body); _GET_PROXY(on_response_trailers); _GET_PROXY(on_response_metadata); @@ -234,11 +243,17 @@ void WasmBase::getFunctions() { _GET_PROXY(on_done); _GET_PROXY(on_log); _GET_PROXY(on_delete); -#undef _GET_PROXY - if (!malloc_) { - fail(FailState::MissingFunction, "Wasm missing malloc"); + if (abiVersion() == AbiVersion::ProxyWasm_0_1_0) { + _GET_PROXY_ABI(on_request_headers, _abi_01); + _GET_PROXY_ABI(on_response_headers, _abi_01); + } else if (abiVersion() == AbiVersion::ProxyWasm_0_2_0) { + _GET_PROXY_ABI(on_request_headers, _abi_02); + _GET_PROXY_ABI(on_response_headers, _abi_02); + _GET_PROXY(on_foreign_function); } +#undef _GET_PROXY_ABI +#undef _GET_PROXY } WasmBase::WasmBase(const std::shared_ptr &base_wasm_handle, WasmVmFactory factory) @@ -315,6 +330,11 @@ bool WasmBase::initialize(const std::string &code, bool allow_precompiled) { allow_precompiled_ = allow_precompiled; } + abi_version_ = wasm_vm_->getAbiVersion(); + if (abi_version_ == AbiVersion::Unknown) { + return false; + } + if (started_from_ != Cloneable::InstantiatedModule) { registerCallbacks(); wasm_vm_->link(vm_id_); diff --git a/src/wavm/wavm.cc b/src/wavm/wavm.cc index 03c9f299b..f883656f6 100644 --- a/src/wavm/wavm.cc +++ b/src/wavm/wavm.cc @@ -272,6 +272,8 @@ bool Wavm::load(const std::string &code, bool allow_precompiled) { return true; } +AbiVersion Wavm::getAbiVersion() { return AbiVersion::Unknown; } + void Wavm::link(std::string_view debug_name) { RootResolver rootResolver(compartment_); for (auto &p : intrinsic_modules_) {