| name | higress-wasm-go-plugin |
| description | Develop Higress WASM plugins using Go 1.24+. Use when creating, modifying, or debugging Higress gateway plugins for HTTP request/response processing, external service calls, Redis integration, or custom gateway logic. |
Higress WASM Go Plugin Development
Develop Higress gateway WASM plugins using Go language with the wasm-go SDK.
Quick Start
Project Setup
mkdir my-plugin && cd my-plugin
go mod init my-plugin
go env -w GOPROXY=https://proxy.golang.com.cn,direct
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
ctx.Scheme()
ctx.Host()
ctx.Path()
ctx.Method()
ctx.HasRequestBody()
ctx.HasResponseBody()
ctx.DontReadRequestBody()
ctx.DontReadResponseBody()
ctx.BufferRequestBody()
ctx.BufferResponseBody()
ctx.IsWebsocket()
ctx.IsBinaryRequestBody()
ctx.IsBinaryResponseBody()
ctx.SetContext(key, value)
ctx.GetContext(key)
ctx.GetStringContext(key, defaultValue)
ctx.GetBoolContext(key, defaultValue)
ctx.SetUserAttribute(key, value)
ctx.WriteUserAttributeToLog()
Header/Body Operations (proxywasm)
proxywasm.GetHttpRequestHeader(name)
proxywasm.AddHttpRequestHeader(name, value)
proxywasm.ReplaceHttpRequestHeader(name, value)
proxywasm.RemoveHttpRequestHeader(name)
proxywasm.GetHttpRequestHeaders()
proxywasm.ReplaceHttpRequestHeaders(headers)
proxywasm.GetHttpResponseHeader(name)
proxywasm.AddHttpResponseHeader(name, value)
proxywasm.ReplaceHttpResponseHeader(name, value)
proxywasm.RemoveHttpResponseHeader(name)
proxywasm.GetHttpResponseHeaders()
proxywasm.ReplaceHttpResponseHeaders(headers)
proxywasm.GetHttpRequestBody(start, size)
proxywasm.ReplaceHttpRequestBody(body)
proxywasm.AppendHttpRequestBody(data)
proxywasm.PrependHttpRequestBody(data)
proxywasm.GetHttpResponseBody(start, size)
proxywasm.ReplaceHttpResponseBody(body)
proxywasm.AppendHttpResponseBody(data)
proxywasm.PrependHttpResponseBody(data)
proxywasm.SendHttpResponse(statusCode, headers, body, grpcStatus)
proxywasm.ResumeHttpRequest()
proxywasm.ResumeHttpResponse()
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)
if err != nil {
return types.HeaderContinue
}
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