mirror of
https://github.com/alibaba/higress.git
synced 2026-03-02 23:51:11 +08:00
feat: add wasm-rust sdk with say-hello simple extension (#350)
This commit is contained in:
39
plugins/wasm-rust/src/error.rs
Normal file
39
plugins/wasm-rust/src/error.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2023 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// 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.
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct WasmRustError {
|
||||
message: String,
|
||||
}
|
||||
|
||||
impl WasmRustError {
|
||||
pub const fn new(message: String) -> Self {
|
||||
WasmRustError { message }
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for WasmRustError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for WasmRustError {
|
||||
fn description(&self) -> &str {
|
||||
&self.message
|
||||
}
|
||||
}
|
||||
383
plugins/wasm-rust/src/internal.rs
Normal file
383
plugins/wasm-rust/src/internal.rs
Normal file
@@ -0,0 +1,383 @@
|
||||
// Copyright (c) 2023 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use proxy_wasm::hostcalls;
|
||||
use proxy_wasm::types::{BufferType, Bytes, MapType, Status};
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
pub(crate) fn get_current_time() -> SystemTime {
|
||||
hostcalls::get_current_time().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_property(path: Vec<&str>) -> Option<Bytes> {
|
||||
hostcalls::get_property(path).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn set_property(path: Vec<&str>, value: Option<&[u8]>) {
|
||||
hostcalls::set_property(path, value).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_shared_data(key: &str) -> (Option<Bytes>, Option<u32>) {
|
||||
hostcalls::get_shared_data(key).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn set_shared_data(
|
||||
key: &str,
|
||||
value: Option<&[u8]>,
|
||||
cas: Option<u32>,
|
||||
) -> Result<(), Status> {
|
||||
hostcalls::set_shared_data(key, value, cas)
|
||||
}
|
||||
|
||||
pub(crate) fn register_shared_queue(name: &str) -> u32 {
|
||||
hostcalls::register_shared_queue(name).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_shared_queue(vm_id: &str, name: &str) -> Option<u32> {
|
||||
hostcalls::resolve_shared_queue(vm_id, name).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn dequeue_shared_queue(queue_id: u32) -> Result<Option<Bytes>, Status> {
|
||||
hostcalls::dequeue_shared_queue(queue_id)
|
||||
}
|
||||
|
||||
pub(crate) fn enqueue_shared_queue(queue_id: u32, value: Option<&[u8]>) -> Result<(), Status> {
|
||||
hostcalls::enqueue_shared_queue(queue_id, value)
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_http_call(
|
||||
upstream: &str,
|
||||
headers: Vec<(&str, &str)>,
|
||||
body: Option<&[u8]>,
|
||||
trailers: Vec<(&str, &str)>,
|
||||
timeout: Duration,
|
||||
) -> Result<u32, Status> {
|
||||
hostcalls::dispatch_http_call(upstream, headers, body, trailers, timeout)
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_call_response_headers() -> Vec<(String, String)> {
|
||||
hostcalls::get_map(MapType::HttpCallResponseHeaders).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_call_response_headers_bytes() -> Vec<(String, Bytes)> {
|
||||
hostcalls::get_map_bytes(MapType::HttpCallResponseHeaders).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_call_response_header(name: &str) -> Option<String> {
|
||||
hostcalls::get_map_value(MapType::HttpCallResponseHeaders, name).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_call_response_header_bytes(name: &str) -> Option<Bytes> {
|
||||
hostcalls::get_map_value_bytes(MapType::HttpCallResponseHeaders, name).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_call_response_body(start: usize, max_size: usize) -> Option<Bytes> {
|
||||
hostcalls::get_buffer(BufferType::HttpCallResponseBody, start, max_size).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_call_response_trailers() -> Vec<(String, String)> {
|
||||
hostcalls::get_map(MapType::HttpCallResponseTrailers).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_call_response_trailers_bytes() -> Vec<(String, Bytes)> {
|
||||
hostcalls::get_map_bytes(MapType::HttpCallResponseTrailers).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_call_response_trailer(name: &str) -> Option<String> {
|
||||
hostcalls::get_map_value(MapType::HttpCallResponseTrailers, name).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_call_response_trailer_bytes(name: &str) -> Option<Bytes> {
|
||||
hostcalls::get_map_value_bytes(MapType::HttpCallResponseTrailers, name).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_grpc_call(
|
||||
upstream_name: &str,
|
||||
service_name: &str,
|
||||
method_name: &str,
|
||||
initial_metadata: Vec<(&str, &[u8])>,
|
||||
message: Option<&[u8]>,
|
||||
timeout: Duration,
|
||||
) -> Result<u32, Status> {
|
||||
hostcalls::dispatch_grpc_call(
|
||||
upstream_name,
|
||||
service_name,
|
||||
method_name,
|
||||
initial_metadata,
|
||||
message,
|
||||
timeout,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn get_grpc_call_response_body(start: usize, max_size: usize) -> Option<Bytes> {
|
||||
hostcalls::get_buffer(BufferType::GrpcReceiveBuffer, start, max_size).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn cancel_grpc_call(token_id: u32) {
|
||||
hostcalls::cancel_grpc_call(token_id).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn open_grpc_stream(
|
||||
cluster_name: &str,
|
||||
service_name: &str,
|
||||
method_name: &str,
|
||||
initial_metadata: Vec<(&str, &[u8])>,
|
||||
) -> Result<u32, Status> {
|
||||
hostcalls::open_grpc_stream(cluster_name, service_name, method_name, initial_metadata)
|
||||
}
|
||||
|
||||
pub(crate) fn get_grpc_stream_initial_metadata() -> Vec<(String, Bytes)> {
|
||||
hostcalls::get_map_bytes(MapType::GrpcReceiveInitialMetadata).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_grpc_stream_initial_metadata_value(name: &str) -> Option<Bytes> {
|
||||
hostcalls::get_map_value_bytes(MapType::GrpcReceiveInitialMetadata, name).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn send_grpc_stream_message(token_id: u32, message: Option<&[u8]>, end_stream: bool) {
|
||||
hostcalls::send_grpc_stream_message(token_id, message, end_stream).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_grpc_stream_trailing_metadata() -> Vec<(String, Bytes)> {
|
||||
hostcalls::get_map_bytes(MapType::GrpcReceiveTrailingMetadata).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_grpc_stream_trailing_metadata_value(name: &str) -> Option<Bytes> {
|
||||
hostcalls::get_map_value_bytes(MapType::GrpcReceiveTrailingMetadata, name).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn cancel_grpc_stream(token_id: u32) {
|
||||
hostcalls::cancel_grpc_stream(token_id).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn close_grpc_stream(token_id: u32) {
|
||||
hostcalls::close_grpc_stream(token_id).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_grpc_status() -> (u32, Option<String>) {
|
||||
hostcalls::get_grpc_status().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn call_foreign_function(
|
||||
function_name: &str,
|
||||
arguments: Option<&[u8]>,
|
||||
) -> Result<Option<Bytes>, Status> {
|
||||
hostcalls::call_foreign_function(function_name, arguments)
|
||||
}
|
||||
|
||||
pub(crate) fn done() {
|
||||
hostcalls::done().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_request_headers() -> Vec<(String, String)> {
|
||||
hostcalls::get_map(MapType::HttpRequestHeaders).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_request_headers_bytes() -> Vec<(String, Bytes)> {
|
||||
hostcalls::get_map_bytes(MapType::HttpRequestHeaders).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn set_http_request_headers(headers: Vec<(&str, &str)>) {
|
||||
hostcalls::set_map(MapType::HttpRequestHeaders, headers).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn set_http_request_headers_bytes(headers: Vec<(&str, &[u8])>) {
|
||||
hostcalls::set_map_bytes(MapType::HttpRequestHeaders, headers).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_request_header(name: &str) -> Option<String> {
|
||||
hostcalls::get_map_value(MapType::HttpRequestHeaders, name).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_request_header_bytes(name: &str) -> Option<Bytes> {
|
||||
hostcalls::get_map_value_bytes(MapType::HttpRequestHeaders, name).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn set_http_request_header(name: &str, value: Option<&str>) {
|
||||
hostcalls::set_map_value(MapType::HttpRequestHeaders, name, value).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn set_http_request_header_bytes(name: &str, value: Option<&[u8]>) {
|
||||
hostcalls::set_map_value_bytes(MapType::HttpRequestHeaders, name, value).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn add_http_request_header(name: &str, value: &str) {
|
||||
hostcalls::add_map_value(MapType::HttpRequestHeaders, name, value).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn add_http_request_header_bytes(name: &str, value: &[u8]) {
|
||||
hostcalls::add_map_value_bytes(MapType::HttpRequestHeaders, name, value).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_request_body(start: usize, max_size: usize) -> Option<Bytes> {
|
||||
hostcalls::get_buffer(BufferType::HttpRequestBody, start, max_size).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn set_http_request_body(start: usize, size: usize, value: &[u8]) {
|
||||
hostcalls::set_buffer(BufferType::HttpRequestBody, start, size, value).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_request_trailers() -> Vec<(String, String)> {
|
||||
hostcalls::get_map(MapType::HttpRequestTrailers).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_request_trailers_bytes() -> Vec<(String, Bytes)> {
|
||||
hostcalls::get_map_bytes(MapType::HttpRequestTrailers).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn set_http_request_trailers(trailers: Vec<(&str, &str)>) {
|
||||
hostcalls::set_map(MapType::HttpRequestTrailers, trailers).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn set_http_request_trailers_bytes(trailers: Vec<(&str, &[u8])>) {
|
||||
hostcalls::set_map_bytes(MapType::HttpRequestTrailers, trailers).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_request_trailer(name: &str) -> Option<String> {
|
||||
hostcalls::get_map_value(MapType::HttpRequestTrailers, name).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_request_trailer_bytes(name: &str) -> Option<Bytes> {
|
||||
hostcalls::get_map_value_bytes(MapType::HttpRequestTrailers, name).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn set_http_request_trailer(name: &str, value: Option<&str>) {
|
||||
hostcalls::set_map_value(MapType::HttpRequestTrailers, name, value).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn set_http_request_trailer_bytes(name: &str, value: Option<&[u8]>) {
|
||||
hostcalls::set_map_value_bytes(MapType::HttpRequestTrailers, name, value).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn add_http_request_trailer(name: &str, value: &str) {
|
||||
hostcalls::add_map_value(MapType::HttpRequestTrailers, name, value).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn add_http_request_trailer_bytes(name: &str, value: &[u8]) {
|
||||
hostcalls::add_map_value_bytes(MapType::HttpRequestTrailers, name, value).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn resume_http_request() {
|
||||
hostcalls::resume_http_request().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn reset_http_request() {
|
||||
hostcalls::reset_http_request().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_response_headers() -> Vec<(String, String)> {
|
||||
hostcalls::get_map(MapType::HttpResponseHeaders).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_response_headers_bytes() -> Vec<(String, Bytes)> {
|
||||
hostcalls::get_map_bytes(MapType::HttpResponseHeaders).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn set_http_response_headers(headers: Vec<(&str, &str)>) {
|
||||
hostcalls::set_map(MapType::HttpResponseHeaders, headers).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn set_http_response_headers_bytes(headers: Vec<(&str, &[u8])>) {
|
||||
hostcalls::set_map_bytes(MapType::HttpResponseHeaders, headers).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_response_header(name: &str) -> Option<String> {
|
||||
hostcalls::get_map_value(MapType::HttpResponseHeaders, name).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_response_header_bytes(name: &str) -> Option<Bytes> {
|
||||
hostcalls::get_map_value_bytes(MapType::HttpResponseHeaders, name).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn set_http_response_header(name: &str, value: Option<&str>) {
|
||||
hostcalls::set_map_value(MapType::HttpResponseHeaders, name, value).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn set_http_response_header_bytes(name: &str, value: Option<&[u8]>) {
|
||||
hostcalls::set_map_value_bytes(MapType::HttpResponseHeaders, name, value).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn add_http_response_header(name: &str, value: &str) {
|
||||
hostcalls::add_map_value(MapType::HttpResponseHeaders, name, value).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn add_http_response_header_bytes(name: &str, value: &[u8]) {
|
||||
hostcalls::add_map_value_bytes(MapType::HttpResponseHeaders, name, value).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_response_body(start: usize, max_size: usize) -> Option<Bytes> {
|
||||
hostcalls::get_buffer(BufferType::HttpResponseBody, start, max_size).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn set_http_response_body(start: usize, size: usize, value: &[u8]) {
|
||||
hostcalls::set_buffer(BufferType::HttpResponseBody, start, size, value).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_response_trailers() -> Vec<(String, String)> {
|
||||
hostcalls::get_map(MapType::HttpResponseTrailers).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_response_trailers_bytes() -> Vec<(String, Bytes)> {
|
||||
hostcalls::get_map_bytes(MapType::HttpResponseTrailers).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn set_http_response_trailers(trailers: Vec<(&str, &str)>) {
|
||||
hostcalls::set_map(MapType::HttpResponseTrailers, trailers).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn set_http_response_trailers_bytes(trailers: Vec<(&str, &[u8])>) {
|
||||
hostcalls::set_map_bytes(MapType::HttpResponseTrailers, trailers).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_response_trailer(name: &str) -> Option<String> {
|
||||
hostcalls::get_map_value(MapType::HttpResponseTrailers, name).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn get_http_response_trailer_bytes(name: &str) -> Option<Bytes> {
|
||||
hostcalls::get_map_value_bytes(MapType::HttpResponseTrailers, name).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn set_http_response_trailer(name: &str, value: Option<&str>) {
|
||||
hostcalls::set_map_value(MapType::HttpResponseTrailers, name, value).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn set_http_response_trailer_bytes(name: &str, value: Option<&[u8]>) {
|
||||
hostcalls::set_map_value_bytes(MapType::HttpResponseTrailers, name, value).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn add_http_response_trailer(name: &str, value: &str) {
|
||||
hostcalls::add_map_value(MapType::HttpResponseTrailers, name, value).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn add_http_response_trailer_bytes(name: &str, value: &[u8]) {
|
||||
hostcalls::add_map_value_bytes(MapType::HttpResponseTrailers, name, value).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn resume_http_response() {
|
||||
hostcalls::resume_http_response().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn reset_http_response() {
|
||||
hostcalls::reset_http_response().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn send_http_response(
|
||||
status_code: u32,
|
||||
headers: Vec<(&str, &str)>,
|
||||
body: Option<&[u8]>,
|
||||
) {
|
||||
hostcalls::send_http_response(status_code, headers, body).unwrap()
|
||||
}
|
||||
18
plugins/wasm-rust/src/lib.rs
Normal file
18
plugins/wasm-rust/src/lib.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2023 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// 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.
|
||||
|
||||
pub mod error;
|
||||
mod internal;
|
||||
pub mod log;
|
||||
pub mod rule_matcher;
|
||||
71
plugins/wasm-rust/src/log.rs
Normal file
71
plugins/wasm-rust/src/log.rs
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) 2023 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// 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.
|
||||
|
||||
use proxy_wasm::hostcalls;
|
||||
|
||||
pub enum LogLevel {
|
||||
Trace,
|
||||
Debug,
|
||||
Info,
|
||||
Warn,
|
||||
Error,
|
||||
Critical,
|
||||
}
|
||||
|
||||
pub struct Log {
|
||||
plugin_name: String,
|
||||
}
|
||||
|
||||
impl Log {
|
||||
pub fn new(plugin_name: String) -> Log {
|
||||
Log { plugin_name }
|
||||
}
|
||||
|
||||
fn log(&self, level: LogLevel, msg: &str) {
|
||||
let msg = format!("[{}] {}", self.plugin_name, msg);
|
||||
let level = match level {
|
||||
LogLevel::Trace => proxy_wasm::types::LogLevel::Trace,
|
||||
LogLevel::Debug => proxy_wasm::types::LogLevel::Debug,
|
||||
LogLevel::Info => proxy_wasm::types::LogLevel::Info,
|
||||
LogLevel::Warn => proxy_wasm::types::LogLevel::Warn,
|
||||
LogLevel::Error => proxy_wasm::types::LogLevel::Error,
|
||||
LogLevel::Critical => proxy_wasm::types::LogLevel::Critical,
|
||||
};
|
||||
hostcalls::log(level, msg.as_str()).unwrap();
|
||||
}
|
||||
|
||||
pub fn trace(&self, msg: &str) {
|
||||
self.log(LogLevel::Trace, msg)
|
||||
}
|
||||
|
||||
pub fn debug(&self, msg: &str) {
|
||||
self.log(LogLevel::Debug, msg)
|
||||
}
|
||||
|
||||
pub fn info(&self, msg: &str) {
|
||||
self.log(LogLevel::Info, msg)
|
||||
}
|
||||
|
||||
pub fn warn(&self, msg: &str) {
|
||||
self.log(LogLevel::Warn, msg)
|
||||
}
|
||||
|
||||
pub fn error(&self, msg: &str) {
|
||||
self.log(LogLevel::Error, msg)
|
||||
}
|
||||
|
||||
pub fn critical(&self, msg: &str) {
|
||||
self.log(LogLevel::Critical, msg)
|
||||
}
|
||||
}
|
||||
218
plugins/wasm-rust/src/rule_matcher.rs
Normal file
218
plugins/wasm-rust/src/rule_matcher.rs
Normal file
@@ -0,0 +1,218 @@
|
||||
// Copyright (c) 2023 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// 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.
|
||||
|
||||
use crate::error::WasmRustError;
|
||||
use crate::internal::{get_http_request_header, get_property};
|
||||
use proxy_wasm::hostcalls::log;
|
||||
use proxy_wasm::types::LogLevel;
|
||||
use serde_json::{Map, Value};
|
||||
use std::collections::HashSet;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
enum Category {
|
||||
Route,
|
||||
Host,
|
||||
}
|
||||
|
||||
enum MatchType {
|
||||
Prefix,
|
||||
Exact,
|
||||
Suffix,
|
||||
}
|
||||
|
||||
const RULES_KEY: &str = "_rules_";
|
||||
const MATCH_ROUTE_KEY: &str = "_match_route_";
|
||||
const MATCH_DOMAIN_KEY: &str = "_match_domain_";
|
||||
|
||||
struct HostMatcher {
|
||||
match_type: MatchType,
|
||||
host: String,
|
||||
}
|
||||
|
||||
struct RuleConfig<PluginConfig> {
|
||||
category: Category,
|
||||
routes: HashSet<String>,
|
||||
hosts: Vec<HostMatcher>,
|
||||
config: PluginConfig,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RuleMatcher<PluginConfig> {
|
||||
rule_config: Vec<RuleConfig<PluginConfig>>,
|
||||
global_config: Option<PluginConfig>,
|
||||
}
|
||||
|
||||
impl<PluginConfig> RuleMatcher<PluginConfig>
|
||||
where
|
||||
PluginConfig: Default+DeserializeOwned,
|
||||
{
|
||||
pub fn parse_rule_config(
|
||||
&mut self,
|
||||
config: &Value,
|
||||
) -> Result<(), WasmRustError> {
|
||||
let empty_object = Map::new();
|
||||
let empty_vec = Vec::new();
|
||||
|
||||
let object = config.as_object().unwrap_or(&empty_object);
|
||||
let mut key_count = object.len();
|
||||
|
||||
if object.is_empty() {
|
||||
self.global_config = Some(PluginConfig::default());
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let rules = if object.contains_key(RULES_KEY) {
|
||||
key_count -= 1;
|
||||
object[RULES_KEY].as_array().unwrap_or(&empty_vec)
|
||||
} else {
|
||||
&empty_vec
|
||||
};
|
||||
|
||||
let mut global_config_error: WasmRustError = WasmRustError::default();
|
||||
if key_count > 0 {
|
||||
match serde_json::from_value::<PluginConfig>(config.clone()) {
|
||||
Ok(plugin_config) => {
|
||||
self.global_config = Some(plugin_config);
|
||||
}
|
||||
Err(err) => {
|
||||
log(
|
||||
LogLevel::Warn,
|
||||
format!("parse global config failed, err:{:?}", err).as_str(),
|
||||
)
|
||||
.unwrap();
|
||||
global_config_error = WasmRustError::new(err.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if rules.is_empty() {
|
||||
return match self.global_config {
|
||||
Some(_) => Ok(()),
|
||||
None => Err(WasmRustError::new(format!(
|
||||
"parse config failed, no valid rules; global config parse error:{}",
|
||||
global_config_error
|
||||
))),
|
||||
};
|
||||
}
|
||||
|
||||
for rule_json in rules {
|
||||
let config = match serde_json::from_value::<PluginConfig>(rule_json.clone()) {
|
||||
Ok(config) => config,
|
||||
Err(error) => return Err(WasmRustError::new(error.to_string())),
|
||||
};
|
||||
let routes = RuleMatcher::<PluginConfig>::parse_route_match_config(rule_json);
|
||||
let hosts = RuleMatcher::<PluginConfig>::parse_host_match_config(rule_json);
|
||||
|
||||
let no_routes = routes.is_empty();
|
||||
let no_hosts = hosts.is_empty();
|
||||
|
||||
if (no_routes && no_hosts) || (!no_routes && !no_hosts) {
|
||||
return Err(WasmRustError::new("there is only one of '_match_route_' and '_match_domain_' can present in configuration.".to_string()));
|
||||
}
|
||||
|
||||
let category = if no_routes {
|
||||
Category::Host
|
||||
} else {
|
||||
Category::Route
|
||||
};
|
||||
|
||||
self.rule_config.push(RuleConfig {
|
||||
category,
|
||||
routes,
|
||||
hosts,
|
||||
config,
|
||||
})
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_match_config(&self) -> Option<&PluginConfig> {
|
||||
let host = get_http_request_header(":authority").unwrap_or_default();
|
||||
let route_name = get_property(vec!["route_name"]).unwrap_or_default();
|
||||
|
||||
for rule in &self.rule_config {
|
||||
match rule.category {
|
||||
Category::Host => {
|
||||
if self.host_match(rule, host.as_str()) {
|
||||
return Some(&rule.config);
|
||||
}
|
||||
}
|
||||
Category::Route => {
|
||||
if rule.routes.contains(
|
||||
String::from_utf8(route_name.to_vec())
|
||||
.unwrap_or_else(|_| "".to_string())
|
||||
.as_str(),
|
||||
) {
|
||||
return Some(&rule.config);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self.global_config.as_ref();
|
||||
}
|
||||
|
||||
fn parse_route_match_config(config: &Value) -> HashSet<String> {
|
||||
let empty_vec = Vec::new();
|
||||
let keys = config[MATCH_ROUTE_KEY].as_array().unwrap_or(&empty_vec);
|
||||
let mut routes = HashSet::new();
|
||||
for key in keys {
|
||||
let route_name = key.as_str().unwrap_or("").to_string();
|
||||
if !route_name.is_empty() {
|
||||
routes.insert(route_name);
|
||||
}
|
||||
}
|
||||
routes
|
||||
}
|
||||
|
||||
fn parse_host_match_config(config: &Value) -> Vec<HostMatcher> {
|
||||
let empty_vec = Vec::new();
|
||||
let keys = config[MATCH_DOMAIN_KEY].as_array().unwrap_or(&empty_vec);
|
||||
let mut host_matchers: Vec<HostMatcher> = Vec::new();
|
||||
for key in keys {
|
||||
let host = key.as_str().unwrap_or("").to_string();
|
||||
let mut host_matcher = HostMatcher {
|
||||
match_type: MatchType::Prefix,
|
||||
host: String::new(),
|
||||
};
|
||||
if let Some(suffix) = host.strip_prefix('*') {
|
||||
host_matcher.match_type = MatchType::Suffix;
|
||||
host_matcher.host = suffix.to_string()
|
||||
} else if let Some(prefix) = host.strip_suffix('*') {
|
||||
host_matcher.match_type = MatchType::Prefix;
|
||||
host_matcher.host = prefix.to_string();
|
||||
} else {
|
||||
host_matcher.match_type = MatchType::Exact;
|
||||
host_matcher.host = host
|
||||
}
|
||||
host_matchers.push(host_matcher)
|
||||
}
|
||||
host_matchers
|
||||
}
|
||||
|
||||
fn host_match(&self, rule: &RuleConfig<PluginConfig>, request_host: &str) -> bool {
|
||||
for host in &rule.hosts {
|
||||
let matched = match host.match_type {
|
||||
MatchType::Prefix => request_host.starts_with(host.host.as_str()),
|
||||
MatchType::Suffix => request_host.ends_with(host.host.as_str()),
|
||||
MatchType::Exact => request_host == host.host.as_str(),
|
||||
};
|
||||
if matched {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user