The Microservices Engine (MSE) cloud-native gateway supports custom plug-in development through WebAssembly (Wasm). Write your plug-in logic in Rust or C++, compile it to a .wasm binary, and deploy it to the gateway. Both SDKs are part of the Higress open-source gateway project and follow the proxy-wasm standard.
| Language | SDK |
|---|---|
| Rust | SDK for Rust |
| C++ | SDK for C++ |
Both SDKs include ready-to-use example plug-ins in the extensions/ directory.
How plug-ins work
Gateway plug-ins use a callback-based model. The gateway invokes your plug-in at specific points in the HTTP request lifecycle -- for example, when request headers arrive or when the response body is ready. Your plug-in can make hostcalls back to the gateway to read headers, modify responses, or access shared data.
Gateway (Envoy Proxy)
┌──────────────────────────────────────────────────┐
│ │
│ RootContext (1 per plug-in instance) │
│ ├── on_configure: parse plug-in config │
│ ├── create_http_context: spawn per-request ctx │
│ │ │
│ ├── HttpContext (1 per HTTP request) │
│ │ ├── on_http_request_headers │
│ │ ├── on_http_request_body │
│ │ ├── on_http_response_headers │
│ │ └── on_http_response_body │
│ │ │
│ └── HttpContext (another request) │
│ └── ... │
│ │
│ RuleMatcher ── routes config per domain/route │
└──────────────────────────────────────────────────┘Key concepts:
RootContext -- Created once per plug-in instance. The
on_configurecallback parses plug-in configuration. Thecreate_http_contextmethod spawns a new context for each incoming request.HttpContext -- Created per HTTP request. Implements callbacks such as
on_http_request_headersandon_http_response_bodyto inspect and modify traffic.RuleMatcher -- A Higress SDK utility that routes different configurations to different domains or routes. One plug-in binary can apply distinct rules per ingress or domain.
Develop a Rust plug-in
Prerequisites
Before you begin, make sure you have:
Rust 1.80 or later, installed through rustup
The WASI compile target:
rustup target add wasm32-wasip1Docker
Make
Step 1: Create the plug-in project
Clone the Higress repository and create a new plug-in directory:
git clone https://github.com/alibaba/higress.git
cd higress/plugins/wasm-rust/
# Create the plug-in directory
mkdir -p extensions/my-pluginCreate extensions/my-plugin/Cargo.toml:
[package]
name = "my-plugin"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
crate-type = ["cdylib"]
[dependencies]
higress-wasm-rust = { path = "../../", version = "0.1.0" }
proxy-wasm = { git = "https://github.com/higress-group/proxy-wasm-rust-sdk", branch = "main", version = "0.2.2" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"Step 2: Write the plug-in code
Create extensions/my-plugin/src/lib.rs. The following example adds a custom header to every incoming request:
use higress_wasm_rust::*;
use proxy_wasm::traits::*;
use proxy_wasm::types::*;
use serde::{Deserialize, Serialize};
use std::cell::RefCell;
use std::rc::Rc;
// Define the plug-in configuration struct.
#[derive(Default, Clone, Serialize, Deserialize)]
struct MyPluginConfig {
header_name: String,
header_value: String,
}
// RootContext: handles configuration and spawns per-request contexts.
struct MyPluginRoot {
log: Log,
rule_matcher: SharedRuleMatcher<MyPluginConfig>,
}
impl MyPluginRoot {
fn new() -> Self {
Self {
log: Log::new("my-plugin".to_string()),
rule_matcher: Rc::new(RefCell::new(RuleMatcher::new())),
}
}
}
impl Context for MyPluginRoot {}
impl RootContext for MyPluginRoot {
fn on_configure(&mut self, plugin_configuration_size: usize) -> bool {
on_configure(
self,
plugin_configuration_size,
&mut self.rule_matcher.borrow_mut(),
&self.log,
)
}
fn create_http_context(&self, context_id: u32) -> Option<Box<dyn HttpContext>> {
Some(Box::new(MyPlugin {
log: self.log.clone(),
rule_matcher: self.rule_matcher.clone(),
context_id,
}))
}
fn get_type(&self) -> Option<ContextType> {
Some(ContextType::HttpContext)
}
}
// HttpContext: processes each HTTP request.
struct MyPlugin {
log: Log,
rule_matcher: SharedRuleMatcher<MyPluginConfig>,
context_id: u32,
}
impl Context for MyPlugin {}
impl HttpContext for MyPlugin {
fn on_http_request_headers(&mut self, _num_headers: usize, _end_of_stream: bool) -> Action {
// Match the current request against configured rules.
let binding = self.rule_matcher.borrow();
if let Some(config) = binding.get_match_config() {
self.log.info(&format!(
"Adding header {}={} to request",
config.header_name, config.header_value
));
self.add_http_request_header(&config.header_name, &config.header_value);
}
Action::Continue
}
}
// Register the plug-in entry point.
proxy_wasm::main! {{
proxy_wasm::set_root_context(|_| -> Box<dyn RootContext> {
Box::new(MyPluginRoot::new())
});
}}Step 3: Build the Wasm binary
Run the build from the plugins/wasm-rust/ directory:
# Build the plug-in Wasm file
make build PLUGIN_NAME=my-pluginAfter a successful build, the compiled Wasm file is at extensions/my-plugin/plugin.wasm.
To build a container image for deployment:
# Build a Docker image containing the Wasm binary
make build-image PLUGIN_NAME=my-plugin PLUGIN_VERSION=1.0.0The image follows the OCI artifact specification. Push it to any OCI-compatible registry.
Step 4: Test the plug-in
# Run unit tests
make test PLUGIN_NAME=my-plugin
# Run lint checks
make lint PLUGIN_NAME=my-pluginFor more Rust plug-in examples, see the extensions/ directory in the SDK for Rust repository. Available examples include say-hello, request-block, and ai-data-masking.
Develop a C++ plug-in
Prerequisites
Before you begin, make sure you have:
Docker
Make
The C++ SDK uses a Docker-based build environment, so no local C++ toolchain is required.
Step 1: Set up the project
Clone the Higress repository:
git clone https://github.com/alibaba/higress.git
cd higress/plugins/wasm-cpp/Plug-in source code goes in the extensions/ directory. Use the built-in examples such as hello-world and request-block as a starting point.
Step 2: Build the plug-in
Build a specific plug-in with the Makefile:
PLUGIN_NAME=request_block make buildThis command:
Builds the Wasm binary inside a Docker container.
Outputs the compiled
.wasmfile toextensions/<plug-in-name>/.Creates a Docker image tagged with the plug-in name, build timestamp, and git commit ID.
Build parameters:
| Parameter | Required | Default | Description |
|---|---|---|---|
PLUGIN_NAME | No | hello-world | Name of the plug-in to build |
IMG | No | Auto-generated from registry, plug-in name, timestamp, and commit ID | Custom image tag |
Step 3: Deploy the plug-in
After building, push the image to a container registry. Then reference it in a WasmPlugin custom resource:
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
name: request-block
namespace: higress-system
spec:
defaultConfig:
block_urls:
- "swagger.html"
url: oci://<your-registry>/request_block:1.0.0To apply different configurations per route or domain, use matchRules:
spec:
defaultConfig:
block_urls:
- "swagger.html"
matchRules:
- ingress:
- default/foo
config:
block_bodies:
- "foo"
- domain:
- "*.example.com"
config:
block_bodies:
- "bar"Rules are evaluated in order. The first matching rule determines which configuration the plug-in uses for that request.
For more C++ plug-in examples and API details, see the SDK for C++ repository.
Project structure
Both SDKs follow a similar layout:
plugins/wasm-{rust,cpp}/
├── src/ or common/ # SDK core library
├── extensions/ # Plug-in implementations
│ ├── hello-world/ # Basic example
│ ├── request-block/ # Request blocking example
│ └── ...
├── Makefile # Build commands
└── Dockerfile # Container build definitionRelated topics
Develop gateway plug-ins in Go -- The Go SDK offers garbage collection and a simpler memory model.