diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 98d40cd28bd36..06fc824fb1287 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -948,8 +948,8 @@ REPOSITORY_LOCATIONS_SPEC = dict( project_name = "WebAssembly for Proxies (C++ host implementation)", project_desc = "WebAssembly for Proxies (C++ host implementation)", project_url = "https://github.com/proxy-wasm/proxy-wasm-cpp-host", - version = "4b33df7638637fcc680291daa9d7a57c59e0411e", - sha256 = "e78507e04dc8b154ced5b19d6175bd1435fd850fdd127bce541d31799db06cd4", + version = "579189940ee48ebf1fb1d6539483506bee89f0b4", + sha256 = "eedcc5d0e73a715d9361eda39b21b178e077ab4d749d6bf2f030de81f668d6d3", strip_prefix = "proxy-wasm-cpp-host-{version}", urls = ["https://github.com/proxy-wasm/proxy-wasm-cpp-host/archive/{version}.tar.gz"], use_category = ["dataplane_ext"], @@ -964,7 +964,7 @@ REPOSITORY_LOCATIONS_SPEC = dict( "envoy.wasm.runtime.wavm", "envoy.wasm.runtime.wasmtime", ], - release_date = "2021-04-20", + release_date = "2021-04-28", cpe = "N/A", ), proxy_wasm_rust_sdk = dict( diff --git a/source/extensions/common/wasm/context.cc b/source/extensions/common/wasm/context.cc index fa75e27fa82e8..3293cb01155db 100644 --- a/source/extensions/common/wasm/context.cc +++ b/source/extensions/common/wasm/context.cc @@ -88,8 +88,6 @@ Http::RequestHeaderMapPtr buildRequestHeaderMapFromPairs(const Pairs& pairs) { template static uint32_t headerSize(const P& p) { return p ? p->size() : 0; } -constexpr absl::string_view FailStreamResponseDetails = "wasm_fail_stream"; - } // namespace // Test support. @@ -1584,12 +1582,14 @@ WasmResult Context::continueStream(WasmStreamType stream_type) { return WasmResult::Ok; } +constexpr absl::string_view CloseStreamResponseDetails = "wasm_close_stream"; + WasmResult Context::closeStream(WasmStreamType stream_type) { switch (stream_type) { case WasmStreamType::Request: if (decoder_callbacks_) { if (!decoder_callbacks_->streamInfo().responseCodeDetails().has_value()) { - decoder_callbacks_->streamInfo().setResponseCodeDetails(FailStreamResponseDetails); + decoder_callbacks_->streamInfo().setResponseCodeDetails(CloseStreamResponseDetails); } decoder_callbacks_->resetStream(); } @@ -1597,7 +1597,7 @@ WasmResult Context::closeStream(WasmStreamType stream_type) { case WasmStreamType::Response: if (encoder_callbacks_) { if (!encoder_callbacks_->streamInfo().responseCodeDetails().has_value()) { - encoder_callbacks_->streamInfo().setResponseCodeDetails(FailStreamResponseDetails); + encoder_callbacks_->streamInfo().setResponseCodeDetails(CloseStreamResponseDetails); } encoder_callbacks_->resetStream(); } @@ -1618,6 +1618,39 @@ WasmResult Context::closeStream(WasmStreamType stream_type) { return WasmResult::BadArgument; } +constexpr absl::string_view FailStreamResponseDetails = "wasm_fail_stream"; + +void Context::failStream(WasmStreamType stream_type) { + switch (stream_type) { + case WasmStreamType::Request: + if (decoder_callbacks_) { + decoder_callbacks_->sendLocalReply(Envoy::Http::Code::ServiceUnavailable, "", nullptr, + Grpc::Status::WellKnownGrpcStatus::Unavailable, + FailStreamResponseDetails); + } + break; + case WasmStreamType::Response: + if (encoder_callbacks_) { + encoder_callbacks_->sendLocalReply(Envoy::Http::Code::ServiceUnavailable, "", nullptr, + Grpc::Status::WellKnownGrpcStatus::Unavailable, + FailStreamResponseDetails); + } + break; + case WasmStreamType::Downstream: + if (network_read_filter_callbacks_) { + network_read_filter_callbacks_->connection().close( + Envoy::Network::ConnectionCloseType::FlushWrite); + } + break; + case WasmStreamType::Upstream: + if (network_write_filter_callbacks_) { + network_write_filter_callbacks_->connection().close( + Envoy::Network::ConnectionCloseType::FlushWrite); + } + break; + } +} + WasmResult Context::sendLocalResponse(uint32_t response_code, absl::string_view body_text, Pairs additional_headers, uint32_t grpc_status, absl::string_view details) { diff --git a/source/extensions/common/wasm/context.h b/source/extensions/common/wasm/context.h index 1db0f8826c25b..4876a4685305f 100644 --- a/source/extensions/common/wasm/context.h +++ b/source/extensions/common/wasm/context.h @@ -209,6 +209,7 @@ class Context : public proxy_wasm::ContextBase, // Continue WasmResult continueStream(WasmStreamType stream_type) override; WasmResult closeStream(WasmStreamType stream_type) override; + void failStream(WasmStreamType stream_type) override; WasmResult sendLocalResponse(uint32_t response_code, absl::string_view body_text, Pairs additional_headers, uint32_t grpc_status, absl::string_view details) override; diff --git a/test/extensions/filters/http/wasm/BUILD b/test/extensions/filters/http/wasm/BUILD index 3ad408691e67c..0a3b7363af8a2 100644 --- a/test/extensions/filters/http/wasm/BUILD +++ b/test/extensions/filters/http/wasm/BUILD @@ -27,6 +27,7 @@ envoy_extension_cc_test( "//test/extensions/filters/http/wasm/test_data:body_rust.wasm", "//test/extensions/filters/http/wasm/test_data:headers_rust.wasm", "//test/extensions/filters/http/wasm/test_data:metadata_rust.wasm", + "//test/extensions/filters/http/wasm/test_data:panic_rust.wasm", "//test/extensions/filters/http/wasm/test_data:resume_call_rust.wasm", "//test/extensions/filters/http/wasm/test_data:shared_data_rust.wasm", "//test/extensions/filters/http/wasm/test_data:shared_queue_rust.wasm", diff --git a/test/extensions/filters/http/wasm/config_test.cc b/test/extensions/filters/http/wasm/config_test.cc index e80d77ec98928..35be66bca4743 100644 --- a/test/extensions/filters/http/wasm/config_test.cc +++ b/test/extensions/filters/http/wasm/config_test.cc @@ -837,9 +837,12 @@ TEST_P(WasmFilterConfigTest, YamlLoadFromRemoteSuccessBadcode) { context->setDecoderFilterCallbacks(decoder_callbacks); EXPECT_CALL(decoder_callbacks, streamInfo()).WillRepeatedly(ReturnRef(stream_info)); - EXPECT_CALL(stream_info, setResponseCodeDetails("wasm_fail_stream")); - EXPECT_CALL(decoder_callbacks, resetStream()); - + auto headers = Http::TestResponseHeaderMapImpl{{":status", "503"}}; + EXPECT_CALL(decoder_callbacks, encodeHeaders_(HeaderMapEqualRef(&headers), true)); + EXPECT_CALL(decoder_callbacks, + sendLocalReply(Envoy::Http::Code::ServiceUnavailable, testing::Eq(""), _, + testing::Eq(Grpc::Status::WellKnownGrpcStatus::Unavailable), + testing::Eq("wasm_fail_stream"))); EXPECT_EQ(context->onRequestHeaders(10, false), proxy_wasm::FilterHeadersStatus::StopAllIterationAndWatermark); } diff --git a/test/extensions/filters/http/wasm/test_data/BUILD b/test/extensions/filters/http/wasm/test_data/BUILD index 3e807289f76cb..0501db42c10b3 100644 --- a/test/extensions/filters/http/wasm/test_data/BUILD +++ b/test/extensions/filters/http/wasm/test_data/BUILD @@ -48,6 +48,15 @@ wasm_rust_binary( ], ) +wasm_rust_binary( + name = "panic_rust.wasm", + srcs = ["panic_rust.rs"], + deps = [ + "@proxy_wasm_rust_sdk//:proxy_wasm", + "@proxy_wasm_rust_sdk//bazel/cargo:log", + ], +) + wasm_rust_binary( name = "resume_call_rust.wasm", srcs = ["resume_call_rust.rs"], @@ -80,6 +89,7 @@ envoy_cc_library( srcs = [ "test_async_call_cpp.cc", "test_body_cpp.cc", + "test_close_stream_cpp.cc", "test_cpp.cc", "test_cpp_null_plugin.cc", "test_grpc_call_cpp.cc", @@ -107,9 +117,11 @@ envoy_wasm_cc_binary( srcs = [ "test_async_call_cpp.cc", "test_body_cpp.cc", + "test_close_stream_cpp.cc", "test_cpp.cc", "test_grpc_call_cpp.cc", "test_grpc_stream_cpp.cc", + "test_panic_cpp.cc", "test_resume_call_cpp.cc", "test_shared_data_cpp.cc", "test_shared_queue_cpp.cc", diff --git a/test/extensions/filters/http/wasm/test_data/panic_rust.rs b/test/extensions/filters/http/wasm/test_data/panic_rust.rs new file mode 100644 index 0000000000000..c22fa50ba2fd7 --- /dev/null +++ b/test/extensions/filters/http/wasm/test_data/panic_rust.rs @@ -0,0 +1,39 @@ +use proxy_wasm::traits::{Context, HttpContext}; +use proxy_wasm::types::*; + +#[no_mangle] +pub fn _start() { + proxy_wasm::set_http_context(|_, _| -> Box { + Box::new(TestStream{}) + }); +} + +struct TestStream {} + +impl Context for TestStream {} + +impl HttpContext for TestStream { + fn on_http_request_headers(&mut self, _: usize) -> Action { + panic!(""); + } + + fn on_http_request_body(&mut self, _: usize, _: bool) -> Action { + panic!(""); + } + + fn on_http_request_trailers(&mut self, _: usize) -> Action { + panic!(""); + } + + fn on_http_response_headers(&mut self, _: usize) -> Action { + panic!(""); + } + + fn on_http_response_body(&mut self, _: usize, _: bool) -> Action { + panic!(""); + } + + fn on_http_response_trailers(&mut self, _: usize) -> Action { + panic!(""); + } +} diff --git a/test/extensions/filters/http/wasm/test_data/test_close_stream_cpp.cc b/test/extensions/filters/http/wasm/test_data/test_close_stream_cpp.cc new file mode 100644 index 0000000000000..7aa7c7d687a2e --- /dev/null +++ b/test/extensions/filters/http/wasm/test_data/test_close_stream_cpp.cc @@ -0,0 +1,40 @@ +// NOLINT(namespace-envoy) +#include +#include +#include + +#ifndef NULL_PLUGIN +#include "proxy_wasm_intrinsics_lite.h" +#else +#include "extensions/common/wasm/ext/envoy_null_plugin.h" +#endif + +START_WASM_PLUGIN(HttpWasmTestCpp) + +class CloseStreamRootContext : public RootContext { +public: + explicit CloseStreamRootContext(uint32_t id, std::string_view root_id) : RootContext(id, root_id) {} +}; + +class CloseStreamContext : public Context { +public: + explicit CloseStreamContext(uint32_t id, RootContext* root) : Context(id, root) {} + + FilterHeadersStatus onRequestHeaders(uint32_t, bool) override; + FilterHeadersStatus onResponseHeaders(uint32_t, bool) override; +}; + +static RegisterContextFactory register_CloseStreamContext(CONTEXT_FACTORY(CloseStreamContext), + ROOT_FACTORY(CloseStreamRootContext), "close_stream"); + +FilterHeadersStatus CloseStreamContext::onRequestHeaders(uint32_t, bool) { + closeRequest(); + return FilterHeadersStatus::Continue; +} + +FilterHeadersStatus CloseStreamContext::onResponseHeaders(uint32_t, bool) { + closeResponse(); + return FilterHeadersStatus::Continue; +} + +END_WASM_PLUGIN diff --git a/test/extensions/filters/http/wasm/test_data/test_panic_cpp.cc b/test/extensions/filters/http/wasm/test_data/test_panic_cpp.cc new file mode 100644 index 0000000000000..44fa5e19f9892 --- /dev/null +++ b/test/extensions/filters/http/wasm/test_data/test_panic_cpp.cc @@ -0,0 +1,66 @@ +// NOLINT(namespace-envoy) +#include +#include +#include + +#ifndef NULL_PLUGIN +#include "proxy_wasm_intrinsics_lite.h" +#else +#include "extensions/common/wasm/ext/envoy_null_plugin.h" +#endif + +START_WASM_PLUGIN(HttpWasmTestCpp) + +class PanicRootContext : public RootContext { +public: + explicit PanicRootContext(uint32_t id, std::string_view root_id) : RootContext(id, root_id) {} +}; + +class PanicContext : public Context { +public: + explicit PanicContext(uint32_t id, RootContext* root) : Context(id, root) {} + + FilterHeadersStatus onRequestHeaders(uint32_t, bool) override; + FilterDataStatus onRequestBody(size_t , bool ) override; + FilterTrailersStatus onRequestTrailers(uint32_t) override; + FilterHeadersStatus onResponseHeaders(uint32_t, bool) override; + FilterDataStatus onResponseBody(size_t, bool) override; + FilterTrailersStatus onResponseTrailers(uint32_t) override; +}; + +static RegisterContextFactory register_PanicContext(CONTEXT_FACTORY(PanicContext), + ROOT_FACTORY(PanicRootContext), "panic"); + +static int* badptr = nullptr; + +FilterHeadersStatus PanicContext::onRequestHeaders(uint32_t, bool) { + *badptr = 0; + return FilterHeadersStatus::Continue; +} + +FilterHeadersStatus PanicContext::onResponseHeaders(uint32_t, bool) { + *badptr = 0; + return FilterHeadersStatus::Continue; +} + +FilterTrailersStatus PanicContext::onRequestTrailers(uint32_t) { + *badptr = 0; + return FilterTrailersStatus::Continue; +} + +FilterDataStatus PanicContext::onRequestBody(size_t , bool ) { + *badptr = 0; + return FilterDataStatus::Continue; +} + +FilterDataStatus PanicContext::onResponseBody(size_t, bool) { + *badptr = 0; + return FilterDataStatus::Continue; +} + +FilterTrailersStatus PanicContext::onResponseTrailers(uint32_t) { + *badptr = 0; + return FilterTrailersStatus::Continue; +} + +END_WASM_PLUGIN diff --git a/test/extensions/filters/http/wasm/wasm_filter_test.cc b/test/extensions/filters/http/wasm/wasm_filter_test.cc index adfff9ce26f4d..74f6c0ce9dbe1 100644 --- a/test/extensions/filters/http/wasm/wasm_filter_test.cc +++ b/test/extensions/filters/http/wasm/wasm_filter_test.cc @@ -1820,6 +1820,163 @@ TEST_P(WasmHttpFilterTest, RootId2) { filter().decodeHeaders(request_headers, true)); } +TEST_P(WasmHttpFilterTest, PanicOnRequestHeaders) { + if (std::get<0>(GetParam()) == "null") { + return; + } + setupTest("panic"); + setupFilter(); + Http::MockStreamDecoderFilterCallbacks decoder_callbacks; + filter().setDecoderFilterCallbacks(decoder_callbacks); + + auto headers = Http::TestResponseHeaderMapImpl{{":status", "503"}}; + EXPECT_CALL(decoder_callbacks, encodeHeaders_(HeaderMapEqualRef(&headers), true)); + EXPECT_CALL(decoder_callbacks, + sendLocalReply(Envoy::Http::Code::ServiceUnavailable, testing::Eq(""), _, + testing::Eq(Grpc::Status::WellKnownGrpcStatus::Unavailable), + testing::Eq("wasm_fail_stream"))); + + // Create in-VM context. + filter().onCreate(); + EXPECT_EQ(proxy_wasm::FilterHeadersStatus::StopAllIterationAndWatermark, + filter().onRequestHeaders(0, false)); +} + +TEST_P(WasmHttpFilterTest, PanicOnRequestBody) { + if (std::get<0>(GetParam()) == "null") { + return; + } + setupTest("panic"); + setupFilter(); + Http::MockStreamDecoderFilterCallbacks decoder_callbacks; + filter().setDecoderFilterCallbacks(decoder_callbacks); + + auto headers = Http::TestResponseHeaderMapImpl{{":status", "503"}}; + EXPECT_CALL(decoder_callbacks, encodeHeaders_(HeaderMapEqualRef(&headers), true)); + EXPECT_CALL(decoder_callbacks, + sendLocalReply(Envoy::Http::Code::ServiceUnavailable, testing::Eq(""), _, + testing::Eq(Grpc::Status::WellKnownGrpcStatus::Unavailable), + testing::Eq("wasm_fail_stream"))); + + // Create in-VM context. + filter().onCreate(); + EXPECT_EQ(proxy_wasm::FilterDataStatus::StopIterationNoBuffer, filter().onRequestBody(0, false)); +} + +TEST_P(WasmHttpFilterTest, PanicOnRequestTrailers) { + if (std::get<0>(GetParam()) == "null") { + return; + } + setupTest("panic"); + setupFilter(); + Http::MockStreamDecoderFilterCallbacks decoder_callbacks; + filter().setDecoderFilterCallbacks(decoder_callbacks); + + auto headers = Http::TestResponseHeaderMapImpl{{":status", "503"}}; + EXPECT_CALL(decoder_callbacks, encodeHeaders_(HeaderMapEqualRef(&headers), true)); + EXPECT_CALL(decoder_callbacks, + sendLocalReply(Envoy::Http::Code::ServiceUnavailable, testing::Eq(""), _, + testing::Eq(Grpc::Status::WellKnownGrpcStatus::Unavailable), + testing::Eq("wasm_fail_stream"))); + + // Create in-VM context. + filter().onCreate(); + EXPECT_EQ(proxy_wasm::FilterTrailersStatus::StopIteration, filter().onRequestTrailers(0)); +} + +TEST_P(WasmHttpFilterTest, PanicOnResponseHeaders) { + if (std::get<0>(GetParam()) == "null") { + return; + } + setupTest("panic"); + setupFilter(); + Http::MockStreamEncoderFilterCallbacks encoder_callbacks; + filter().setEncoderFilterCallbacks(encoder_callbacks); + EXPECT_CALL(encoder_callbacks, + sendLocalReply(Envoy::Http::Code::ServiceUnavailable, testing::Eq(""), _, + testing::Eq(Grpc::Status::WellKnownGrpcStatus::Unavailable), + testing::Eq("wasm_fail_stream"))); + + // Create in-VM context. + filter().onCreate(); + EXPECT_EQ(proxy_wasm::FilterHeadersStatus::StopAllIterationAndWatermark, + filter().onResponseHeaders(0, false)); +} + +TEST_P(WasmHttpFilterTest, PanicOnResponseBody) { + if (std::get<0>(GetParam()) == "null") { + return; + } + setupTest("panic"); + setupFilter(); + Http::MockStreamEncoderFilterCallbacks encoder_callbacks; + filter().setEncoderFilterCallbacks(encoder_callbacks); + EXPECT_CALL(encoder_callbacks, + sendLocalReply(Envoy::Http::Code::ServiceUnavailable, testing::Eq(""), _, + testing::Eq(Grpc::Status::WellKnownGrpcStatus::Unavailable), + testing::Eq("wasm_fail_stream"))); + + // Create in-VM context. + filter().onCreate(); + EXPECT_EQ(proxy_wasm::FilterDataStatus::StopIterationNoBuffer, filter().onResponseBody(0, false)); +} + +TEST_P(WasmHttpFilterTest, PanicOnResponseTrailers) { + if (std::get<0>(GetParam()) == "null") { + return; + } + setupTest("panic"); + setupFilter(); + Http::MockStreamEncoderFilterCallbacks encoder_callbacks; + filter().setEncoderFilterCallbacks(encoder_callbacks); + EXPECT_CALL(encoder_callbacks, + sendLocalReply(Envoy::Http::Code::ServiceUnavailable, testing::Eq(""), _, + testing::Eq(Grpc::Status::WellKnownGrpcStatus::Unavailable), + testing::Eq("wasm_fail_stream"))); + + // Create in-VM context. + filter().onCreate(); + EXPECT_EQ(proxy_wasm::FilterTrailersStatus::StopIteration, filter().onResponseTrailers(0)); +} + +TEST_P(WasmHttpFilterTest, CloseRequest) { + if (std::get<1>(GetParam()) == "rust") { + // TODO(mathetake): not yet supported in the Rust SDK. + return; + } + setupTest("close_stream"); + setupFilter(); + NiceMock stream_info; + Http::MockStreamDecoderFilterCallbacks decoder_callbacks; + filter().setDecoderFilterCallbacks(decoder_callbacks); + EXPECT_CALL(decoder_callbacks, streamInfo()).WillRepeatedly(ReturnRef(stream_info)); + EXPECT_CALL(stream_info, setResponseCodeDetails("wasm_close_stream")); + EXPECT_CALL(decoder_callbacks, resetStream()); + + // Create in-VM context. + filter().onCreate(); + EXPECT_EQ(proxy_wasm::FilterHeadersStatus::Continue, filter().onRequestHeaders(0, false)); +} + +TEST_P(WasmHttpFilterTest, CloseResponse) { + if (std::get<1>(GetParam()) == "rust") { + // TODO(mathetake): not yet supported in the Rust SDK. + return; + } + setupTest("close_stream"); + setupFilter(); + NiceMock stream_info; + Http::MockStreamEncoderFilterCallbacks encoder_callbacks; + filter().setEncoderFilterCallbacks(encoder_callbacks); + EXPECT_CALL(encoder_callbacks, streamInfo()).WillRepeatedly(ReturnRef(stream_info)); + EXPECT_CALL(stream_info, setResponseCodeDetails("wasm_close_stream")); + EXPECT_CALL(encoder_callbacks, resetStream()); + + // Create in-VM context. + filter().onCreate(); + EXPECT_EQ(proxy_wasm::FilterHeadersStatus::Continue, filter().onResponseHeaders(0, false)); +} + } // namespace Wasm } // namespace HttpFilters } // namespace Extensions diff --git a/test/extensions/filters/network/wasm/BUILD b/test/extensions/filters/network/wasm/BUILD index d974226945d71..2d4b634840909 100644 --- a/test/extensions/filters/network/wasm/BUILD +++ b/test/extensions/filters/network/wasm/BUILD @@ -46,6 +46,7 @@ envoy_extension_cc_test( "//test/extensions/filters/network/wasm/test_data:test_cpp.wasm", ]) + envoy_select_wasm_rust_tests([ "//test/extensions/filters/network/wasm/test_data:logging_rust.wasm", + "//test/extensions/filters/network/wasm/test_data:panic_rust.wasm", ]), extension_name = "envoy.filters.network.wasm", deps = [ diff --git a/test/extensions/filters/network/wasm/test_data/BUILD b/test/extensions/filters/network/wasm/test_data/BUILD index 2ab2622650bc2..02fbb6028c224 100644 --- a/test/extensions/filters/network/wasm/test_data/BUILD +++ b/test/extensions/filters/network/wasm/test_data/BUILD @@ -18,9 +18,19 @@ wasm_rust_binary( ], ) +wasm_rust_binary( + name = "panic_rust.wasm", + srcs = ["panic_rust.rs"], + deps = [ + "@proxy_wasm_rust_sdk//:proxy_wasm", + "@proxy_wasm_rust_sdk//bazel/cargo:log", + ], +) + envoy_cc_library( name = "test_cpp_plugin", srcs = [ + "test_close_stream_cpp.cc", "test_cpp.cc", "test_cpp_null_plugin.cc", "test_resume_call_cpp.cc", @@ -39,7 +49,9 @@ envoy_cc_library( envoy_wasm_cc_binary( name = "test_cpp.wasm", srcs = [ + "test_close_stream_cpp.cc", "test_cpp.cc", + "test_panic_cpp.cc", "test_resume_call_cpp.cc", ], deps = [ diff --git a/test/extensions/filters/network/wasm/test_data/panic_rust.rs b/test/extensions/filters/network/wasm/test_data/panic_rust.rs new file mode 100644 index 0000000000000..d994650e4166b --- /dev/null +++ b/test/extensions/filters/network/wasm/test_data/panic_rust.rs @@ -0,0 +1,27 @@ +use proxy_wasm::traits::{Context, StreamContext}; +use proxy_wasm::types::*; + +#[no_mangle] +pub fn _start() { + proxy_wasm::set_stream_context(|_, _| -> Box { + Box::new(TestStream {}) + }); +} + +struct TestStream {} + +impl Context for TestStream {} + +impl StreamContext for TestStream { + fn on_new_connection(&mut self) -> Action { + panic!(""); + } + + fn on_downstream_data(&mut self, _: usize, _: bool) -> Action { + panic!(""); + } + + fn on_upstream_data(&mut self, _: usize, _: bool) -> Action { + panic!(""); + } +} diff --git a/test/extensions/filters/network/wasm/test_data/test_close_stream_cpp.cc b/test/extensions/filters/network/wasm/test_data/test_close_stream_cpp.cc new file mode 100644 index 0000000000000..a45dccca1bc0b --- /dev/null +++ b/test/extensions/filters/network/wasm/test_data/test_close_stream_cpp.cc @@ -0,0 +1,37 @@ +// NOLINT(namespace-envoy) +#ifndef NULL_PLUGIN +#include "proxy_wasm_intrinsics.h" +#else +#include "include/proxy-wasm/null_plugin.h" +#endif + +START_WASM_PLUGIN(NetworkTestCpp) + +class CloseStreamContext : public Context { +public: + explicit CloseStreamContext(uint32_t id, RootContext* root) : Context(id, root) {} + FilterStatus onDownstreamData(size_t data_length, bool end_stream) override; + FilterStatus onUpstreamData(size_t data_length, bool end_stream) override; +}; + +class CloseStreamRootContext : public RootContext { +public: + explicit CloseStreamRootContext(uint32_t id, std::string_view root_id) + : RootContext(id, root_id) {} +}; + +static RegisterContextFactory register_CloseStreamContext(CONTEXT_FACTORY(CloseStreamContext), + ROOT_FACTORY(CloseStreamRootContext), + "close_stream"); + +FilterStatus CloseStreamContext::onDownstreamData(size_t, bool) { + closeDownstream(); + return FilterStatus::Continue; +} + +FilterStatus CloseStreamContext::onUpstreamData(size_t, bool) { + closeUpstream(); + return FilterStatus::Continue; +} + +END_WASM_PLUGIN diff --git a/test/extensions/filters/network/wasm/test_data/test_panic_cpp.cc b/test/extensions/filters/network/wasm/test_data/test_panic_cpp.cc new file mode 100644 index 0000000000000..695f8d0eff0cc --- /dev/null +++ b/test/extensions/filters/network/wasm/test_data/test_panic_cpp.cc @@ -0,0 +1,43 @@ +// NOLINT(namespace-envoy) +#ifndef NULL_PLUGIN +#include "proxy_wasm_intrinsics.h" +#else +#include "include/proxy-wasm/null_plugin.h" +#endif + +START_WASM_PLUGIN(NetworkTestCpp) + +class PanicContext : public Context { +public: + explicit PanicContext(uint32_t id, RootContext* root) : Context(id, root) {} + FilterStatus onNewConnection() override; + FilterStatus onDownstreamData(size_t data_length, bool end_stream) override; + FilterStatus onUpstreamData(size_t data_length, bool end_stream) override; +}; + +class PanicRootContext : public RootContext { +public: + explicit PanicRootContext(uint32_t id, std::string_view root_id) + : RootContext(id, root_id) {} +}; + +static RegisterContextFactory register_PanicContext(CONTEXT_FACTORY(PanicContext), + ROOT_FACTORY(PanicRootContext), "panic"); + +static int* badptr = nullptr; +FilterStatus PanicContext::onNewConnection() { + *badptr = 0; + return FilterStatus::Continue; +} + +FilterStatus PanicContext::onDownstreamData(size_t, bool) { + *badptr = 0; + return FilterStatus::Continue; +} + +FilterStatus PanicContext::onUpstreamData(size_t, bool) { + *badptr = 0; + return FilterStatus::Continue; +} + +END_WASM_PLUGIN diff --git a/test/extensions/filters/network/wasm/wasm_filter_test.cc b/test/extensions/filters/network/wasm/wasm_filter_test.cc index 6977de806d294..f0ab349395019 100644 --- a/test/extensions/filters/network/wasm/wasm_filter_test.cc +++ b/test/extensions/filters/network/wasm/wasm_filter_test.cc @@ -1,3 +1,4 @@ +#include "envoy/network/transport_socket.h" #include "envoy/server/lifecycle_notifier.h" #include "extensions/common/wasm/wasm.h" @@ -263,8 +264,13 @@ TEST_P(WasmNetworkFilterTest, RestrictLog) { // Do not expect this call, because proxy_log is not allowed EXPECT_CALL(filter(), log_(spdlog::level::trace, Eq(absl::string_view("onNewConnection 2")))) .Times(0); - EXPECT_EQ(Network::FilterStatus::Continue, filter().onNewConnection()); - + if (std::get<1>(GetParam()) == "rust") { + // Rust code panics on WasmResult::InternalFailure returned by restricted calls, and + // that eventually results in calling failStream on post VM failure check after onNewConnection. + EXPECT_EQ(Network::FilterStatus::StopIteration, filter().onNewConnection()); + } else { + EXPECT_EQ(Network::FilterStatus::Continue, filter().onNewConnection()); + } // Do not expect this call, because proxy_log is not allowed EXPECT_CALL(filter(), log_(spdlog::level::trace, Eq(absl::string_view("onDownstreamConnectionClose 2 1")))) @@ -370,6 +376,97 @@ TEST_P(WasmNetworkFilterTest, StopAndResumeUpstreamViaAsyncCall) { EXPECT_NE(callbacks, nullptr); } +TEST_P(WasmNetworkFilterTest, PanicOnNewConnection) { + if (std::get<0>(GetParam()) == "null") { + return; + } + setupConfig("", "panic"); + setupFilter(); + EXPECT_CALL(filter(), log_(spdlog::level::critical, _)) + .Times(testing::AtMost(1)); // Rust logs on panic. + EXPECT_EQ(read_filter_callbacks_.connection().state(), Network::Connection::State::Open); + EXPECT_EQ(write_filter_callbacks_.connection().state(), Network::Connection::State::Open); + EXPECT_EQ(Network::FilterStatus::StopIteration, filter().onNewConnection()); + + // Should close both up and down streams. + EXPECT_EQ(read_filter_callbacks_.connection().state(), Network::Connection::State::Closed); + EXPECT_EQ(write_filter_callbacks_.connection().state(), Network::Connection::State::Closed); +} + +TEST_P(WasmNetworkFilterTest, PanicOnDownstreamData) { + if (std::get<0>(GetParam()) == "null") { + return; + } + setupConfig("", "panic"); + setupFilter(); + EXPECT_CALL(filter(), log_(spdlog::level::critical, _)) + .Times(testing::AtMost(1)); // Rust logs on panic. + EXPECT_EQ(read_filter_callbacks_.connection().state(), Network::Connection::State::Open); + EXPECT_EQ(write_filter_callbacks_.connection().state(), Network::Connection::State::Open); + Buffer::OwnedImpl fake_downstream_data("Fake"); + filter().onCreate(); // Create context without calling OnNewConnection. + EXPECT_EQ(Network::FilterStatus::StopIteration, filter().onData(fake_downstream_data, false)); + + // Should close both up and down streams. + EXPECT_EQ(read_filter_callbacks_.connection().state(), Network::Connection::State::Closed); + EXPECT_EQ(write_filter_callbacks_.connection().state(), Network::Connection::State::Closed); +} + +TEST_P(WasmNetworkFilterTest, PanicOnUpstreamData) { + if (std::get<0>(GetParam()) == "null") { + return; + } + setupConfig("", "panic"); + setupFilter(); + EXPECT_CALL(filter(), log_(spdlog::level::critical, _)) + .Times(testing::AtMost(1)); // Rust logs on panic. + EXPECT_EQ(read_filter_callbacks_.connection().state(), Network::Connection::State::Open); + EXPECT_EQ(write_filter_callbacks_.connection().state(), Network::Connection::State::Open); + Buffer::OwnedImpl fake_downstream_data("Fake"); + filter().onCreate(); // Create context without calling OnNewConnection. + EXPECT_EQ(Network::FilterStatus::StopIteration, filter().onWrite(fake_downstream_data, false)); + + // Should close both up and down streams. + EXPECT_EQ(read_filter_callbacks_.connection().state(), Network::Connection::State::Closed); + EXPECT_EQ(write_filter_callbacks_.connection().state(), Network::Connection::State::Closed); +} + +TEST_P(WasmNetworkFilterTest, CloseDownstream) { + if (std::get<1>(GetParam()) == "rust") { + // TODO(mathetake): not yet supported in the Rust SDK. + return; + } + setupConfig("", "close_stream"); + setupFilter(); + EXPECT_EQ(read_filter_callbacks_.connection().state(), Network::Connection::State::Open); + EXPECT_EQ(write_filter_callbacks_.connection().state(), Network::Connection::State::Open); + Buffer::OwnedImpl fake_downstream_data("Fake"); + filter().onCreate(); // Create context without calling OnNewConnection. + EXPECT_EQ(Network::FilterStatus::Continue, filter().onWrite(fake_downstream_data, false)); + + // Should close downstream. + EXPECT_EQ(read_filter_callbacks_.connection().state(), Network::Connection::State::Open); + EXPECT_EQ(write_filter_callbacks_.connection().state(), Network::Connection::State::Closed); +} + +TEST_P(WasmNetworkFilterTest, CloseUpstream) { + if (std::get<1>(GetParam()) == "rust") { + // TODO(mathetake): not yet supported in the Rust SDK. + return; + } + setupConfig("", "close_stream"); + setupFilter(); + EXPECT_EQ(read_filter_callbacks_.connection().state(), Network::Connection::State::Open); + EXPECT_EQ(write_filter_callbacks_.connection().state(), Network::Connection::State::Open); + Buffer::OwnedImpl fake_upstream_data("Fake"); + filter().onCreate(); // Create context without calling OnNewConnection. + EXPECT_EQ(Network::FilterStatus::Continue, filter().onData(fake_upstream_data, false)); + + // Should close downstream. + EXPECT_EQ(read_filter_callbacks_.connection().state(), Network::Connection::State::Closed); + EXPECT_EQ(write_filter_callbacks_.connection().state(), Network::Connection::State::Open); +} + } // namespace Wasm } // namespace NetworkFilters } // namespace Extensions