diff --git a/docs/site/content/zh/latest/tasks/mock.md b/docs/site/content/zh/latest/tasks/mock.md index 007b00d7c..37f894fd9 100644 --- a/docs/site/content/zh/latest/tasks/mock.md +++ b/docs/site/content/zh/latest/tasks/mock.md @@ -196,4 +196,11 @@ proxies: 当前代理支持 HTTP 和 TCP 协议,上面的例子中代理了 MySQL 的 `33060` 端口。 +## Webhook + +有些场景下,需要定时向服务器发送请求,这时可以使用 Webhook。当前支持的协议包括: + +* HTTP +* Syslog + > 更多 URL 中通配符的用法,请参考 https://github.com/gorilla/mux diff --git a/docs/site/content/zh/latest/tasks/mock/webhook-syslog.yaml b/docs/site/content/zh/latest/tasks/mock/webhook-syslog.yaml new file mode 100644 index 000000000..0b2bb069e --- /dev/null +++ b/docs/site/content/zh/latest/tasks/mock/webhook-syslog.yaml @@ -0,0 +1,11 @@ +webhooks: + - timer: 3s + name: syslog + request: + protocol: syslog + path: 192.168.123.58:5140 + body: | + { + "level": "{{ randEnum "FATAL" "ERROR" "WARNING" "INFO" }}", + "message": "{{ randAlphaNum 10 }}" + } diff --git a/pkg/mock/in_memory.go b/pkg/mock/in_memory.go index 235b10e56..89b156b9d 100644 --- a/pkg/mock/in_memory.go +++ b/pkg/mock/in_memory.go @@ -24,6 +24,7 @@ import ( "io" "net" "net/http" + "sort" "strings" "sync" "time" @@ -507,7 +508,20 @@ func (s *inMemoryServer) startWebhook(webhook *Webhook) (err error) { } func runWebhook(ctx context.Context, objCtx interface{}, wh *Webhook) (err error) { - client := http.DefaultClient + rawParams := make(map[string]string, len(wh.Param)) + paramKeys := make([]string, 0, len(wh.Param)) + for k, v := range wh.Param { + paramKeys = append(paramKeys, k) + rawParams[k] = v + } + sort.Strings(paramKeys) + + for _, k := range paramKeys { + v, vErr := render.Render("mock webhook server param", wh.Param[k], wh) + if vErr == nil { + wh.Param[k] = v + } + } var payload io.Reader payload, err = render.RenderAsReader("mock webhook server payload", wh.Request.Body, wh) @@ -515,8 +529,8 @@ func runWebhook(ctx context.Context, objCtx interface{}, wh *Webhook) (err error err = fmt.Errorf("error when render payload: %w", err) return } + wh.Param = rawParams - method := util.EmptyThenDefault(wh.Request.Method, http.MethodPost) var api string api, err = render.Render("webhook request api", wh.Request.Path, objCtx) if err != nil { @@ -524,6 +538,27 @@ func runWebhook(ctx context.Context, objCtx interface{}, wh *Webhook) (err error return } + switch wh.Request.Protocol { + case "syslog": + err = sendSyslogWebhookRequest(ctx, wh, api, payload) + default: + err = sendHTTPWebhookRequest(ctx, wh, api, payload) + } + return +} + +func sendSyslogWebhookRequest(ctx context.Context, wh *Webhook, api string, payload io.Reader) (err error) { + var conn net.Conn + if conn, err = net.Dial("udp", api); err == nil { + _, err = io.Copy(conn, payload) + } + return +} + +func sendHTTPWebhookRequest(ctx context.Context, wh *Webhook, api string, payload io.Reader) (err error) { + method := util.EmptyThenDefault(wh.Request.Method, http.MethodPost) + client := http.DefaultClient + var bearerToken string bearerToken, err = getBearerToken(ctx, wh.Request) if err != nil { @@ -550,7 +585,7 @@ func runWebhook(ctx context.Context, objCtx interface{}, wh *Webhook) (err error memLogger.Info("send webhook request", "api", api) resp, err := client.Do(req) if err != nil { - err = fmt.Errorf("error when sending webhook") + err = fmt.Errorf("error when sending webhook: %v", err) } else { if resp.StatusCode != http.StatusOK { memLogger.Info("unexpected status", "code", resp.StatusCode) diff --git a/pkg/mock/types.go b/pkg/mock/types.go index b1bb0d8e9..5b9f456f8 100644 --- a/pkg/mock/types.go +++ b/pkg/mock/types.go @@ -29,10 +29,11 @@ type Item struct { } type Request struct { - Path string `yaml:"path" json:"path"` - Method string `yaml:"method" json:"method"` - Header map[string]string `yaml:"header" json:"header"` - Body string `yaml:"body" json:"body"` + Protocol string `yaml:"protocol" json:"protocol"` + Path string `yaml:"path" json:"path"` + Method string `yaml:"method" json:"method"` + Header map[string]string `yaml:"header" json:"header"` + Body string `yaml:"body" json:"body"` } type RequestWithAuth struct { @@ -51,9 +52,10 @@ type Response struct { } type Webhook struct { - Name string `yaml:"name" json:"name"` - Timer string `yaml:"timer" json:"timer"` - Request RequestWithAuth `yaml:"request" json:"request"` + Name string `yaml:"name" json:"name"` + Timer string `yaml:"timer" json:"timer"` + Param map[string]string `yaml:"param" json:"param"` + Request RequestWithAuth `yaml:"request" json:"request"` } type Proxy struct {