Higress WASM Go Plugin Development
Develop Higress gateway WASM plugins using Go language with the wasm-go SDK.
Quick Start
Project Setup
bash
# Create project directory mkdir my-plugin && cd my-plugin # Initialize Go module go mod init my-plugin # Set proxy (China) go env -w GOPROXY=https://proxy.golang.com.cn,direct # Download dependencies go get github.com/higress-group/proxy-wasm-go-sdk@go-1.24 go get github.com/higress-group/wasm-go@main go get github.com/tidwall/gjson
Minimal Plugin Template
go
package main
import (
"github.com/higress-group/wasm-go/pkg/wrapper"
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm"
"github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types"
"github.com/tidwall/gjson"
)
func main() {}
func init() {
wrapper.SetCtx(
"my-plugin",
wrapper.ParseConfig(parseConfig),
wrapper.ProcessRequestHeaders(onHttpRequestHeaders),
)
}
type MyConfig struct {
Enabled bool
}
func parseConfig(json gjson.Result, config *MyConfig) error {
config.Enabled = json.Get("enabled").Bool()
return nil
}
func onHttpRequestHeaders(ctx wrapper.HttpContext, config MyConfig) types.Action {
if config.Enabled {
proxywasm.AddHttpRequestHeader("x-my-header", "hello")
}
return types.HeaderContinue
}
Compile
bash
go mod tidy GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o main.wasm ./
Core Concepts
Plugin Lifecycle
- •init() - Register plugin with
wrapper.SetCtx() - •parseConfig - Parse YAML config (auto-converted to JSON)
- •HTTP processing phases - Handle requests/responses
HTTP Processing Phases
| Phase | Trigger | Handler |
|---|---|---|
| Request Headers | Gateway receives client request headers | ProcessRequestHeaders |
| Request Body | Gateway receives client request body | ProcessRequestBody |
| Response Headers | Gateway receives backend response headers | ProcessResponseHeaders |
| Response Body | Gateway receives backend response body | ProcessResponseBody |
| Stream Done | HTTP stream completes | ProcessStreamDone |
Action Return Values
| Action | Behavior |
|---|---|
types.HeaderContinue | Continue to next filter |
types.HeaderStopIteration | Stop header processing, wait for body |
types.HeaderStopAllIterationAndWatermark | Stop all processing, buffer data, call proxywasm.ResumeHttpRequest/Response() to resume |
API Reference
HttpContext Methods
go
// Request info (cached, safe to call in any phase) ctx.Scheme() // :scheme ctx.Host() // :authority ctx.Path() // :path ctx.Method() // :method // Body handling ctx.HasRequestBody() // Check if request has body ctx.HasResponseBody() // Check if response has body ctx.DontReadRequestBody() // Skip reading request body ctx.DontReadResponseBody() // Skip reading response body ctx.BufferRequestBody() // Buffer instead of stream ctx.BufferResponseBody() // Buffer instead of stream // Content detection ctx.IsWebsocket() // Check WebSocket upgrade ctx.IsBinaryRequestBody() // Check binary content ctx.IsBinaryResponseBody() // Check binary content // Context storage ctx.SetContext(key, value) ctx.GetContext(key) ctx.GetStringContext(key, defaultValue) ctx.GetBoolContext(key, defaultValue) // Custom logging ctx.SetUserAttribute(key, value) ctx.WriteUserAttributeToLog()
Header/Body Operations (proxywasm)
go
// Request headers proxywasm.GetHttpRequestHeader(name) proxywasm.AddHttpRequestHeader(name, value) proxywasm.ReplaceHttpRequestHeader(name, value) proxywasm.RemoveHttpRequestHeader(name) proxywasm.GetHttpRequestHeaders() proxywasm.ReplaceHttpRequestHeaders(headers) // Response headers proxywasm.GetHttpResponseHeader(name) proxywasm.AddHttpResponseHeader(name, value) proxywasm.ReplaceHttpResponseHeader(name, value) proxywasm.RemoveHttpResponseHeader(name) proxywasm.GetHttpResponseHeaders() proxywasm.ReplaceHttpResponseHeaders(headers) // Request body (only in body phase) proxywasm.GetHttpRequestBody(start, size) proxywasm.ReplaceHttpRequestBody(body) proxywasm.AppendHttpRequestBody(data) proxywasm.PrependHttpRequestBody(data) // Response body (only in body phase) proxywasm.GetHttpResponseBody(start, size) proxywasm.ReplaceHttpResponseBody(body) proxywasm.AppendHttpResponseBody(data) proxywasm.PrependHttpResponseBody(data) // Direct response proxywasm.SendHttpResponse(statusCode, headers, body, grpcStatus) // Flow control proxywasm.ResumeHttpRequest() // Resume paused request proxywasm.ResumeHttpResponse() // Resume paused response
Common Patterns
External HTTP Call
See references/http-client.md for complete HTTP client patterns.
go
func parseConfig(json gjson.Result, config *MyConfig) error {
serviceName := json.Get("serviceName").String()
servicePort := json.Get("servicePort").Int()
config.client = wrapper.NewClusterClient(wrapper.FQDNCluster{
FQDN: serviceName,
Port: servicePort,
})
return nil
}
func onHttpRequestHeaders(ctx wrapper.HttpContext, config MyConfig) types.Action {
err := config.client.Get("/api/check", nil, func(statusCode int, headers http.Header, body []byte) {
if statusCode != 200 {
proxywasm.SendHttpResponse(403, nil, []byte("Forbidden"), -1)
return
}
proxywasm.ResumeHttpRequest()
}, 3000) // timeout ms
if err != nil {
return types.HeaderContinue // fallback on error
}
return types.HeaderStopAllIterationAndWatermark
}
Redis Integration
See references/redis-client.md for complete Redis patterns.
go
func parseConfig(json gjson.Result, config *MyConfig) error {
config.redis = wrapper.NewRedisClusterClient(wrapper.FQDNCluster{
FQDN: json.Get("redisService").String(),
Port: json.Get("redisPort").Int(),
})
return config.redis.Init(
json.Get("username").String(),
json.Get("password").String(),
json.Get("timeout").Int(),
)
}
Multi-level Config
插件配置支持在控制台不同级别设置:全局、域名级、路由级。控制面会自动处理配置的优先级和匹配逻辑,插件代码中通过 parseConfig 解析到的就是当前请求匹配到的配置。
Local Testing
See references/local-testing.md for Docker Compose setup.
Advanced Topics
See references/advanced-patterns.md for:
- •Streaming body processing
- •Route call pattern
- •Tick functions (periodic tasks)
- •Leader election
- •Memory management
- •Custom logging
Best Practices
- •Never call Resume after SendHttpResponse - Response auto-resumes
- •Check HasRequestBody() before returning HeaderStopIteration - Avoids blocking
- •Use cached ctx methods -
ctx.Path()works in any phase,GetHttpRequestHeader(":path")only in header phase - •Handle external call failures gracefully - Return
HeaderContinueon error to avoid blocking - •Set appropriate timeouts - Default HTTP call timeout is 500ms