Agent Skills: Higress WASM Go Plugin Development

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.

UncategorizedID: alibaba/higress/higress-wasm-go-plugin

Repository

higress-groupLicense: Apache-2.0
8,7731,173

Install this agent skill to your local

pnpm dlx add-skill https://github.com/higress-group/higress/tree/HEAD/.claude/skills/higress-wasm-go-plugin

Skill Files

Browse the full folder contents for higress-wasm-go-plugin.

Download Skill

Loading file tree…

.claude/skills/higress-wasm-go-plugin/SKILL.md

Skill Metadata

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

# 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

  1. init() - Register plugin with wrapper.SetCtx()
  2. parseConfig - Parse YAML config (auto-converted to JSON)
  3. 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

  1. Never call Resume after SendHttpResponse - Response auto-resumes
  2. Check HasRequestBody() before returning HeaderStopIteration - Avoids blocking
  3. Use cached ctx methods - ctx.Path() works in any phase, GetHttpRequestHeader(":path") only in header phase
  4. Handle external call failures gracefully - Return HeaderContinue on error to avoid blocking
  5. Set appropriate timeouts - Default HTTP call timeout is 500ms