Higress WASM Go Plugin Development
Develop Higress gateway WASM plugins using Go language with the wasm-go SDK.
Quick Start
Project Setup
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
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
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
// 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)
// 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.
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.
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 HeaderContinue on error to avoid blocking
-
Set appropriate timeouts - Default HTTP call timeout is 500ms