全部產品
Search
文件中心

API Gateway:使用Go語言開發網關外掛程式

更新時間:Oct 17, 2025

開發網關外掛程式可以擴充API Gateway的核心功能,使其能夠滿足更加複雜和特定的業務需求。本文介紹如何使用Go語言開發網關外掛程式,並提供了本地開發和調試的指引。

重要

Higress 已完成從 TinyGo 0.29 + Go 1.20 編譯方案遷移至 Go 1.24 原生支援的 Wasm 編譯。Go 1.24 版本現已原生支援 Wasm 檔案編譯。

對於此前使用 TinyGo 編譯外掛程式的使用者,若需遷移到 Go 1.24 編譯模式,除調整 go.mod 中的依賴外,還需將外掛程式初始化邏輯由 main 函數移至 init 函數。具體樣本請參考後續文檔內容。

針對原有 TinyGo 實現的外掛程式,還需注意以下適配事項:

1. 若在 Header 處理階段調用外部服務並返回 type.ActionPause,需改為返回 types.HeaderStopAllIterationAndWatermark。相關實現可參考後文提供的外掛程式調用外部服務樣本。

2. 若因 TinyGo 對標準庫 regexp 支援不完整而使用了 go-re2 庫,現應替換為 Go 官方 regexp 包。

工具準備

需要先安裝 Golang。

Golang

官方指引連結(需為1.24版本以上)。

說明

使用 Go 1.24 編譯的外掛程式需要雲原生API Gateway版本不低於2.1.5,低版本網關參考使用 GO 語言開發 WASM 外掛程式

Windows

  • 下載安裝檔案

  • 開啟下載好的安裝檔案直接安裝,預設會安裝到 Program Files 或 Program Files (x86) 目錄。

  • 安裝成功後,使用鍵盤上的快速鍵Win+R開啟運行視窗,在運行視窗中輸入cmd單擊確定即可開啟命令視窗,輸入命令go version,成功輸出當前安裝的版本,表明安裝成功。

MacOS

  • 下載安裝檔案

  • 開啟下載好的安裝檔案雙擊進行安裝,預設會安裝到/usr/local/go目錄。

  • 開啟終端命令列工具,輸入命令go version,成功輸出當前安裝的版本,表明安裝成功。

Linux

  • 下載安裝檔案

  • 執行下列命令進行安裝。

    • 安裝Golang。

      rm -rf /usr/local/go && tar -C /usr/local -xzf go1.24.4.linux-amd64.tar.gz
    • 配置環境變數。

      export PATH=$PATH:/usr/local/go/bin
    • 執行go version,成功輸出當前安裝的版本,表明安裝成功。

編寫外掛程式

初始化工程目錄

  1. 建立一個工程目錄檔案,例如wasm-demo-go

  2. 在所建目錄下執行以下命令,進行 Go 工程初始化。

    go mod init wasm-demo-go
  3. 國內環境可能需要設定下載依賴包的代理。

    go env -w GOPROXY=https://proxy.golang.com.cn,direct
  4. 下載構建外掛程式的依賴。

    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

編寫 main.go 檔案

下面是一個簡單樣本,實現了在外掛程式配置mockEnable: true時直接返回hello world應答;未做外掛程式配置,或者設定mockEnable: false時給原始請求添加 hello: world要求標頭。更多例子請參考本文第四節。

說明
注意:在網關控制台中的外掛程式配置為 yaml 格式,下發給外掛程式時將自動轉換為 json 格式,所以例子中的 parseConfig 可以直接從 json 中解析配置。
package main

import (
  "github.com/higress-group/wasm-go/pkg/wrapper"
  logs "github.com/higress-group/wasm-go/pkg/log"
  "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.ParseConfigBy(parseConfig),
    // 為處理要求標頭,設定自訂函數
    wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders),
  )
}

// 自訂外掛程式配置
type MyConfig struct {
  mockEnable bool
}

// 在控制台外掛程式配置中填寫的yaml配置會自動轉換為json,此處直接從json這個參數裡解析配置即可
func parseConfig(json gjson.Result, config *MyConfig, log logs.Log) error {
  // 解析出配置,更新到config中
  config.mockEnable = json.Get("mockEnable").Bool()
  return nil
}

func onHttpRequestHeaders(ctx wrapper.HttpContext, config MyConfig, log logs.Log) types.Action {
  proxywasm.AddHttpRequestHeader("hello", "world")
  if config.mockEnable {
    proxywasm.SendHttpResponse(200, nil, []byte("hello world"), -1)
  }
  return types.HeaderContinue
}

HTTP 處理掛載點

上面範例程式碼中通過 wrapper.ProcessRequestHeadersBy將自訂函數 onHttpRequestHeaders用於HTTP 要求頭處理階段處理請求。除此之外,還可以通過下面方式,設定其他階段的自訂處理函數。

HTTP 處理階段

觸發時機

掛載方法

HTTP 要求頭處理階段

網關接收到用戶端發送來的要求標頭資料時

wrapper.ProcessRequestHeadersBy

HTTP 要求 Body 處理階段

網關接收到用戶端發送來的請求 Body 資料時

wrapper.ProcessRequestBodyBy

HTTP 應答頭處理階段

網關接收到後端服務響應的應答頭資料時

wrapper.ProcessResponseHeadersBy

HTTP 應答 Body 處理階段

網關接收到後端服務響應的應答 Body 資料時

wrapper.ProcessResponseBodyBy

工具方法

上面範例程式碼中的 proxywasm.AddHttpRequestHeader 和 proxywasm.SendHttpResponse是外掛程式 SDK 提供的兩個工具方法,主要的工具方法見下表。

分類

方法名稱

用途

可以生效的

HTTP 處理階段

要求標頭處理

GetHttpRequestHeaders

擷取用戶端請求的全部要求標頭

HTTP 要求頭處理階段

ReplaceHttpRequestHeaders

替換用戶端請求的全部要求標頭

HTTP 要求頭處理階段

GetHttpRequestHeader

擷取用戶端請求的指定要求標頭

HTTP 要求頭處理階段

RemoveHttpRequestHeader

移除用戶端請求的指定要求標頭

HTTP 要求頭處理階段

ReplaceHttpRequestHeader

替換用戶端請求的指定要求標頭

HTTP 要求頭處理階段

AddHttpRequestHeader

新增一個用戶端要求標頭

HTTP 要求頭處理階段

請求 Body 處理

GetHttpRequestBody

擷取用戶端請求 Body

HTTP 要求 Body 處理階段

AppendHttpRequestBody

將指定的位元組串附加到用戶端請求 Body 末尾

HTTP 要求 Body 處理階段

PrependHttpRequestBody

將指定的位元組串附加到用戶端請求 Body 的開頭

HTTP 要求 Body 處理階段

ReplaceHttpRequestBody

替換用戶端請求 Body

HTTP 要求 Body 處理階段

應答頭處理

GetHttpResponseHeaders

擷取後端響應的全部應答頭

HTTP 應答頭處理階段

ReplaceHttpResponseHeaders

替換後端響應的全部應答頭

HTTP 應答頭處理階段

GetHttpResponseHeader

擷取後端響應的指定應答頭

HTTP 應答頭處理階段

RemoveHttpResponseHeader

移除後端響應的指定應答頭

HTTP 應答頭處理階段

ReplaceHttpResponseHeader

替換後端響應的指定應答頭

HTTP 應答頭處理階段

AddHttpResponseHeader

新增一個後端回應標頭

HTTP 應答頭處理階段

應答 Body 處理

GetHttpResponseBody

擷取用戶端請求 Body

HTTP 應答 Body 處理階段

AppendHttpResponseBody

將指定的位元組串附加到後端響應 Body 末尾

HTTP 應答 Body 處理階段

PrependHttpResponseBody

將指定的位元組串附加到後端響應 Body 的開頭

HTTP 應答 Body 處理階段

ReplaceHttpResponseBody

替換後端響應 Body

HTTP 應答 Body 處理階段

HTTP 調用

DispatchHttpCall

發送一個 HTTP 要求

-

GetHttpCallResponseHeaders

擷取 DispatchHttpCall 請求響應的應答頭

-

GetHttpCallResponseBody

擷取 DispatchHttpCall 請求響應的應答 Body

-

GetHttpCallResponseTrailers

擷取 DispatchHttpCall 請求響應的應答 Trailer

-

直接響應

SendHttpResponse

直接返回一個特定的 HTTP 應答

-

流程恢複

ResumeHttpRequest

恢複先前被暫停請求處理流程

-

ResumeHttpResponse

恢複先前被暫停應答處理流程

-

重要

請不要在請求/響應未處於Pause狀態時,調用ResumeHttpRequest或調用ResumeHttpResponse。尤其注意在SendHttpResponse之後,Pause狀態的請求/響應將自動回復,若再調用ResumeHttpRequest或ResumeHttpResponse將導致未定義的行為。

編譯產生 WASM 檔案

本地編譯 wasm 檔案

如果使用自訂初始化的目錄,執行以下命令來編譯 wasm 檔案。

go mod tidy
GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o main.wasm ./

成功編譯後,會產生一個名為 main.wasm 的新檔案。該檔案將在後續的本地調試樣本中使用。當需要在雲原生網關市場中使用自訂外掛程式功能時,上傳此檔案即可。

Header 的狀態管理說明

Header

說明

HeaderContinue

表示當前 filter 已經處理完畢,可以繼續交給下一個 filter 處理。 types.ActionContinue 對應該狀態。

HeaderStopIteration

表示 header 還不能繼續交給下一個 filter 進行處理。 但並不停止從串連讀資料,繼續觸發 body data 的處理。 這樣可以在 body data 處理階段更新 Http 要求標頭內容。 如果 body data 需要交給下一個 filter 處理, 此時 header 也會被一起交給下一個 filter 處理。

說明

返回該狀態時,要求必須有 body,如果沒有 body,請求/響應將被一直阻塞。

判斷是否存在請求 body 可以使用 HasRequestBody()

HeaderContinueAndEndStream

表示 header 可以繼續交給下一個 filter 處理,但下一個 filter 收到的 end_stream = false標記請求還未結束,以便當前 filter 再增加 body。

HeaderStopAllIterationAndBuffer

停止所有迭代,表示 header 不能繼續交給下一個 filter,並且當前 filter 也不能收到 body data。 並對當前過濾器及後續過濾器的頭部、資料和尾部進行緩衝。如果緩衝大小超過了 buffer limit,在要求階段就直接返回 413,響應階段就直接返回 500。 同時需要調用 proxywasm.ResumeHttpRequest()proxywasm.ResumeHttpResponse() 或者 proxywasm.SendHttpResponseWithDetail() 函數來恢複後續處理。

HeaderStopAllIterationAndWatermark

HeaderStopAllIterationAndBuffer相同,區別是當緩衝超過了 buffer limit 會觸發流控,即暫停從串連上讀資料。 0.2.1 ABI 中的 types.ActionPause 實際上對應就是這個狀態。

說明

關於 types.HeaderStopIteration 和 HeaderStopAllIterationAndWatermark 的使用情境可以參考 Higress 官方提供 ai-transformer 外掛程式和 ai-quota 外掛程式

要在 Higress 中配合 Wasmplugin CRD 或者 Console 的 UI 互動配置該外掛程式,需要將該 wasm 檔案打包成 oci 或者 docker 鏡像,可以參考《自訂外掛程式》

本地調試

工具準備

安裝Docker

使用docker compose啟動驗證

  1. 進入在編寫外掛程式時建立的目錄,例如wasm-demo目錄,確認該目錄下已經編譯產生main.wasm檔案。

  2. 在目錄下建立檔案docker-compose.yaml,內容如下:

    version: '3.7'
    services:
      envoy:
        image: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/gateway:v2.1.5
        entrypoint: /usr/local/bin/envoy
        # 注意這裡對wasm開啟了debug層級日誌,正式部署時則預設info層級
        command: -c /etc/envoy/envoy.yaml --component-log-level wasm:debug
        depends_on:
        - httpbin
        networks:
        - wasmtest
        ports:
        - "10000:10000"
        volumes:
        - ./envoy.yaml:/etc/envoy/envoy.yaml
        - ./main.wasm:/etc/envoy/main.wasm
    
      httpbin:
        image: kennethreitz/httpbin:latest
        networks:
        - wasmtest
        ports:
        - "12345:80"
    
    networks:
      wasmtest: {}
  3. 繼續在該目錄下建立檔案envoy.yaml,內容如下:

    admin:
      address:
        socket_address:
          protocol: TCP
          address: 0.0.0.0
          port_value: 9901
    static_resources:
      listeners:
      - name: listener_0
        address:
          socket_address:
            protocol: TCP
            address: 0.0.0.0
            port_value: 10000
        filter_chains:
        - filters:
          - name: envoy.filters.network.http_connection_manager
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
              scheme_header_transformation:
                scheme_to_overwrite: https
              stat_prefix: ingress_http
              route_config:
                name: local_route
                virtual_hosts:
                - name: local_service
                  domains: ["*"]
                  routes:
                  - match:
                      prefix: "/"
                    route:
                      cluster: httpbin
              http_filters:
              - name: wasmdemo
                typed_config:
                  "@type": type.googleapis.com/udpa.type.v1.TypedStruct
                  type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
                  value:
                    config:
                      name: wasmdemo
                      vm_config:
                        runtime: envoy.wasm.runtime.v8
                        code:
                          local:
                            filename: /etc/envoy/main.wasm
                      configuration:
                        "@type": "type.googleapis.com/google.protobuf.StringValue"
                        value: |
                          {
                            "mockEnable": false
                          }
              - name: envoy.filters.http.router
                typed_config:
                  "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
      clusters:
      - name: httpbin
        connect_timeout: 30s
        type: LOGICAL_DNS
        # Comment out the following line to test on v6 networks
        dns_lookup_family: V4_ONLY
        lb_policy: ROUND_ROBIN
        load_assignment:
          cluster_name: httpbin
          endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    address: httpbin
                    port_value: 80
  4. 執行以下命令啟動docker compose。

    docker compose up

功能驗證

WASM功能驗證

  1. 使用curl直接存取httpbin,可以看到不經過網關時的要求標頭內容,如下。

    curl http://127.0.0.1:12345/get
    
    {
      "args": {},
      "headers": {
        "Accept": "*/*",
        "Host": "127.0.0.1:12345",
        "User-Agent": "curl/7.79.1"
      },
      "origin": "172.18.0.1",
      "url": "http://127.0.0.1:12345/get"
    }
  2. 使用curl通過網關訪問httpbin,可以看到經過網關處理後的要求標頭的內容,如下。

    curl http://127.0.0.1:10000/get
    
    {
      "args": {},
      "headers": {
        "Accept": "*/*",
        "Hello": "world",
        "Host": "127.0.0.1:10000",
        "Original-Host": "127.0.0.1:10000",
        "Req-Start-Time": "1681269273896",
        "User-Agent": "curl/7.79.1",
        "X-Envoy-Expected-Rq-Timeout-Ms": "15000"
      },
      "origin": "172.18.0.3",
      "url": "https://127.0.0.1:10000/get"
    }

此時上文編寫外掛程式的功能已生效,加入了hello: world要求標頭。

外掛程式配置修改驗證

  1. 修改envoy.yaml,將mockEnable配置修改為true

      configuration:
        "@type": "type.googleapis.com/google.protobuf.StringValue"
        value: |
          {
            "mockEnable": true
          }
  2. 使用curl通過網關訪問httpbin,可以看到經過網關處理後的要求標頭的內容,如下。

    curl http://127.0.0.1:10000/get
    
    hello world

說明外掛程式配置修改生效,開啟了mock應答直接返回了hello world。

更多樣本

無配置外掛程式

外掛程式無需配置時,直接定義空結構體即可。

package main

import (
  "github.com/higress-group/wasm-go/pkg/wrapper"
  logs "github.com/higress-group/wasm-go/pkg/log"
  "github.com/higress-group/proxy-wasm-go-sdk/proxywasm"
  "github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types"
)

func main() {}

func init() {
  wrapper.SetCtx(
    "hello-world",
    wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders),
  )
}

type MyConfig struct {}

func onHttpRequestHeaders(ctx wrapper.HttpContext, config MyConfig, log logs.Log) types.Action {
  proxywasm.SendHttpResponse(200, nil, []byte("hello world"), -1)
  return types.HeaderContinue
}

在外掛程式中請求外部服務

目前僅支援 http 調用,支援訪問在網關控制台中設定了服務來源的 Nacos、K8s 服務,以及固定地址或 DNS 來源的服務。請注意,無法直接使用net/http庫中的 HTTP client,必須使用如下例中封裝的 HTTP client。

下面例子中,在配置解析階段解析服務類型,產生對應的 HTTP client ;在要求標頭處理階段根據配置的請求路徑訪問對應服務,解析應答頭,然後再設定在原始的要求標頭中。

package main

import (
  "errors"
  "net/http"
  "strings"
  "github.com/higress-group/wasm-go/pkg/wrapper"
  logs "github.com/higress-group/wasm-go/pkg/log"
  "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(
    "http-call",
    wrapper.ParseConfigBy(parseConfig),
    wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders),
  )
}

type MyConfig struct {
  // 用於發起HTTP調用client
  client      wrapper.HttpClient
  // 請求url
  requestPath string
  // 根據這個key取出調用服務的應答頭對應欄位,再設定到原始請求的要求標頭,key為此配置項
  tokenHeader string
}

func parseConfig(json gjson.Result, config *MyConfig, log logs.Log) error {
  config.tokenHeader = json.Get("tokenHeader").String()
  if config.tokenHeader == "" {
    return errors.New("missing tokenHeader in config")
  }
  config.requestPath = json.Get("requestPath").String()
  if config.requestPath == "" {
    return errors.New("missing requestPath in config")
  }
  // 帶服務類型的完整 FQDN 名稱,例如 my-svc.dns, my-svc.static, service-provider.DEFAULT-GROUP.public.nacos, httpbin.my-ns.svc.cluster.local
  serviceName := json.Get("serviceName").String()
  servicePort := json.Get("servicePort").Int()
  if servicePort == 0 {
    if strings.HasSuffix(serviceName, ".static") {
      // 靜態IP類型服務的邏輯連接埠是80
      servicePort = 80
    }
  }
  config.client = wrapper.NewClusterClient(wrapper.FQDNCluster{
    FQDN: serviceName,
    Port: servicePort,
        })
}

func onHttpRequestHeaders(ctx wrapper.HttpContext, config MyConfig, log logs.Log) types.Action {
  // 使用client的Get方法發起HTTP Get調用,此處省略了timeout參數,預設逾時時間500毫秒
  err := config.client.Get(config.requestPath, nil,
           // 回呼函數,將在響應非同步返回時被執行
           func(statusCode int, responseHeaders http.Header, responseBody []byte) {
             // 請求沒有返回200狀態代碼,進行處理
             if statusCode != http.StatusOK {
               log.Errorf("http call failed, status: %d", statusCode)
               proxywasm.SendHttpResponse(http.StatusInternalServerError, nil,
                 []byte("http call failed"), -1)
               return
             }
             // 列印響應的HTTP狀態代碼和應答body
             log.Infof("get status: %d, response body: %s", statusCode, responseBody)
             // 從應答頭中解析token欄位設定到原始要求標頭中
             token := responseHeaders.Get(config.tokenHeader)
             if token != "" {
               proxywasm.AddHttpRequestHeader(config.tokenHeader, token)
             }
             // 恢複原始請求流程,繼續往下處理,才能正常轉寄給後端服務
             proxywasm.ResumeHttpRequest()
    })

  if err != nil {
    // 由於調用外部服務失敗,允許存取請求,記錄日誌
    log.Errorf("Error occured while calling http, it seems cannot find the service cluster.")
    return types.ActionContinue
  } else {
    // 需要等待非同步回調完成,返回HeaderStopAllIterationAndWatermark狀態,可以被ResumeHttpRequest恢複
    return types.HeaderStopAllIterationAndWatermark
  }
}

在外掛程式中調用Redis

使用如下範例程式碼實現Redis限流外掛程式。

package main

import (
  "strconv"
  "time"

  "github.com/higress-group/proxy-wasm-go-sdk/proxywasm"
  "github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types"
  "github.com/tidwall/gjson"
  "github.com/tidwall/resp"

  "github.com/higress-group/wasm-go/pkg/wrapper"
  logs "github.com/higress-group/wasm-go/pkg/log"
)

func main() {}

func init() {
  wrapper.SetCtx(
    "redis-demo",
    wrapper.ParseConfigBy(parseConfig),
    wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders),
    wrapper.ProcessResponseHeadersBy(onHttpResponseHeaders),
  )
}

type RedisCallConfig struct {
  client wrapper.RedisClient
  qpm    int
}

func parseConfig(json gjson.Result, config *RedisCallConfig, log logs.Log) error {
  // 帶服務類型的完整 FQDN 名稱,例如 my-redis.dns、redis.my-ns.svc.cluster.local
  serviceName := json.Get("serviceName").String()
  servicePort := json.Get("servicePort").Int()
  if servicePort == 0 {
    if strings.HasSuffix(serviceName, ".static") {
      // 靜態IP類型服務的邏輯連接埠是80
      servicePort = 80
    } else {
      servicePort = 6379
    }
  }
  username := json.Get("username").String()
  password := json.Get("password").String()
  // 單位是毫秒
  timeout := json.Get("timeout").Int()
  if timeout == 0 {
    timeout = 1000
  }
  qpm := json.Get("qpm").Int()
  config.qpm = int(qpm)
  config.client = wrapper.NewRedisClusterClient(wrapper.FQDNCluster{
    FQDN: serviceName,
    Port: servicePort,
  })
  return config.client.Init(username, password, timeout)
}

func onHttpRequestHeaders(ctx wrapper.HttpContext, config RedisCallConfig, log logs.Log) types.Action {
  now := time.Now()
  minuteAligned := now.Truncate(time.Minute)
  timeStamp := strconv.FormatInt(minuteAligned.Unix(), 10)
  // 如果 redis api 返回的 err != nil,一般是由於網關找不到 redis 後端服務,請檢查是否誤刪除了 redis 後端服務
  err := config.client.Incr(timeStamp, func(response resp.Value) {
    if response.Error() != nil {
      log.Errorf("call redis error: %v", response.Error())
      proxywasm.ResumeHttpRequest()
    } else {
      ctx.SetContext("timeStamp", timeStamp)
      ctx.SetContext("callTimeLeft", strconv.Itoa(config.qpm-response.Integer()))
      if response.Integer() == 1 {
        err := config.client.Expire(timeStamp, 60, func(response resp.Value) {
          if response.Error() != nil {
            log.Errorf("call redis error: %v", response.Error())
          }
          proxywasm.ResumeHttpRequest()
        })
        if err != nil {
          log.Errorf("Error occured while calling redis, it seems cannot find the redis cluster.")
          proxywasm.ResumeHttpRequest()
        }
      } else {
        if response.Integer() > config.qpm {
          proxywasm.SendHttpResponse(429, [][2]string{{"timeStamp", timeStamp}, {"callTimeLeft", "0"}}, []byte("Too many requests\n"), -1)
        } else {
          proxywasm.ResumeHttpRequest()
        }
      }
    }
  })
  if err != nil {
    // 由於調用redis失敗,允許存取請求,記錄日誌
    log.Errorf("Error occured while calling redis, it seems cannot find the redis cluster.")
    return types.HeaderContinue
  } else {
    // 請求hold住,等待redis調用完成
    return types.HeaderStopAllIterationAndWatermark
  }
}

func onHttpResponseHeaders(ctx wrapper.HttpContext, config RedisCallConfig, log logs.Log) types.Action {
  if ctx.GetContext("timeStamp") != nil {
    proxywasm.AddHttpResponseHeader("timeStamp", ctx.GetContext("timeStamp").(string))
  }
  if ctx.GetContext("callTimeLeft") != nil {
    proxywasm.AddHttpResponseHeader("callTimeLeft", ctx.GetContext("callTimeLeft").(string))
  }
  return types.HeaderContinue
}