higress-wasm-go-plugin
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseHigress WASM Go Plugin Development
Higress WASM Go插件开发
Develop Higress gateway WASM plugins using Go language with the SDK.
wasm-go使用 SDK,通过Go语言开发Higress网关WASM插件。
wasm-goQuick Start
快速开始
Project Setup
项目搭建
bash
undefinedbash
undefinedCreate project directory
Create project directory
mkdir my-plugin && cd my-plugin
mkdir my-plugin && cd my-plugin
Initialize Go module
Initialize Go module
go mod init my-plugin
go mod init my-plugin
Set proxy (China)
Set proxy (China)
go env -w GOPROXY=https://proxy.golang.com.cn,direct
go env -w GOPROXY=https://proxy.golang.com.cn,direct
Download dependencies
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
undefinedgo 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
undefinedMinimal 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
}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 ./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
- init() - 通过注册插件
wrapper.SetCtx() - parseConfig - 解析YAML配置(自动转换为JSON)
- HTTP处理阶段 - 处理请求/响应
HTTP Processing Phases
HTTP处理阶段
| Phase | Trigger | Handler |
|---|---|---|
| Request Headers | Gateway receives client request headers | |
| Request Body | Gateway receives client request body | |
| Response Headers | Gateway receives backend response headers | |
| Response Body | Gateway receives backend response body | |
| Stream Done | HTTP stream completes | |
| 阶段 | 触发时机 | 处理器 |
|---|---|---|
| 请求头 | 网关接收到客户端请求头时 | |
| 请求体 | 网关接收到客户端请求体时 | |
| 响应头 | 网关接收到后端响应头时 | |
| 响应体 | 网关接收到后端响应体时 | |
| 流完成 | HTTP流结束时 | |
Action Return Values
动作返回值
| Action | Behavior |
|---|---|
| Continue to next filter |
| Stop header processing, wait for body |
| Stop all processing, buffer data, call |
| 动作 | 行为 |
|---|---|
| 继续执行下一个过滤器 |
| 停止头处理,等待体数据 |
| 停止所有处理,缓存数据,调用 |
API Reference
API参考
HttpContext Methods
HttpContext方法
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()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)
头/体操作(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 responsego
// 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 responseCommon Patterns
常见模式
External HTTP Call
外部HTTP调用
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
}完整的HTTP客户端模式请参考references/http-client.md。
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
Redis集成
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(),
)
}完整的Redis模式请参考references/redis-client.md。
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插件配置支持在控制台不同级别设置:全局、域名级、路由级。控制面会自动处理配置的优先级和匹配逻辑,插件代码中通过 解析到的就是当前请求匹配到的配置。
parseConfigLocal Testing
本地测试
See references/local-testing.md for Docker Compose setup.
Docker Compose搭建方式请参考references/local-testing.md。
Advanced Topics
进阶主题
See references/advanced-patterns.md for:
- Streaming body processing
- Route call pattern
- Tick functions (periodic tasks)
- Leader election
- Memory management
- Custom logging
请参考references/advanced-patterns.md了解:
- 流式体处理
- 路由调用模式
- Tick函数(周期性任务)
- 主节点选举
- 内存管理
- 自定义日志
Best Practices
最佳实践
- Never call Resume after SendHttpResponse - Response auto-resumes
- Check HasRequestBody() before returning HeaderStopIteration - Avoids blocking
- Use cached ctx methods - works in any phase,
ctx.Path()only in header phaseGetHttpRequestHeader(":path") - Handle external call failures gracefully - Return on error to avoid blocking
HeaderContinue - Set appropriate timeouts - Default HTTP call timeout is 500ms
- 在SendHttpResponse后切勿调用Resume - 响应会自动恢复
- 返回HeaderStopIteration前检查HasRequestBody() - 避免阻塞
- 使用缓存的ctx方法 - 可在任意阶段使用,
ctx.Path()仅能在头阶段使用GetHttpRequestHeader(":path") - 优雅处理外部调用失败 - 出错时返回避免阻塞
HeaderContinue - 设置合适的超时时间 - 默认HTTP调用超时为500ms