feat: add wasm-rust sdk with say-hello simple extension (#350)

This commit is contained in:
纪卓志
2023-06-19 10:40:53 +08:00
committed by GitHub
parent ea7b581e26
commit 32c2acefda
17 changed files with 1332 additions and 0 deletions

View 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
}
}

View 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()
}

View 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;

View 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)
}
}

View 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
}
}