-
Notifications
You must be signed in to change notification settings - Fork 561
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add try-paths plugins #902
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
ARG BUILDER=higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/wasm-go-builder:go1.19-tinygo0.28.1-oras1.0.0 | ||
FROM $BUILDER as builder | ||
|
||
ENV GO111MODULE=on \ | ||
GOPROXY=https://goproxy.cn,direct | ||
|
||
WORKDIR /workspace | ||
|
||
COPY . . | ||
|
||
RUN go mod tidy | ||
RUN tinygo build -o /main.wasm -scheduler=none -gc=custom -tags='custommalloc nottinygc_finalizer' -target=wasi ./ | ||
|
||
FROM scratch as output | ||
|
||
COPY --from=builder /main.wasm plugin.wasm | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# 功能说明 | ||
`try-paths`插件支持请求基于不同的路径进行重试,直到请求到正确返回的请求,功能类似nginx的try files指令。 | ||
|
||
# 配置字段 | ||
|
||
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 | | ||
| -------- | -------- | -------- | -------- | -------- | | ||
| serviceSource | string | 必填 | - | k8s,nacos,ip,dns | | ||
| domain | string | 非必填 | - | 服务主机(serviceSource为`dns`必填) | | ||
| host | string | 非必填 | - | 访问的域名地址(serviceSource为`k8s,nacos,ip`填写有效) | | ||
| serviceName | string | 非必填 | - | 服务名称(serviceSource为`k8s,nacos,ip,dns`必填) | | ||
| servicePort | string | 非必填 | - | 服务端口(serviceSource为`k8s,nacos,ip,dns`必填) | | ||
| namespace | string | 非必填 | - | 服务端口(serviceSource为`k8s,nacos`必填) | | ||
| tryPaths | array of string | 必填 | - | 尝试路径,比如index.html,$uri/, index.html等 | | ||
| code | array of int | 非必填 | [403, 404] | 重试状态码,可自定义,默认是403, 404 | | ||
|
||
|
||
# 配置示例 | ||
|
||
## 配置了try-paths插件的场景 | ||
|
||
```yaml | ||
namespace: "default" | ||
serviceName: "oss" | ||
servicePort: 80 | ||
serviceSource: "k8s" | ||
host: "<bucket name>.oss-cn-hangzhou.aliyuncs.com" | ||
tryPaths: | ||
- "$uri/" | ||
- "$uri.html" | ||
- "/index.html" | ||
|
||
``` | ||
|
||
基于该配置开启插件,触发插件的请求curl "http://a.com/a", 会依次请求 | ||
http://oss.default.svc.cluster.local:80/a/、http://oss.default.svc.cluster.local:80/a.html、http://oss.default.svc.cluster.local:80/index.html, 如果返回码不是默认的404或者403,就会直接返回该请求体,直到重试所有的请求。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
module try-paths | ||
|
||
go 1.19 | ||
|
||
require ( | ||
github.com/alibaba/higress/plugins/wasm-go v1.3.5 | ||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226065437-8f7a0b3c9071 | ||
github.com/stretchr/testify v1.8.4 | ||
github.com/tidwall/gjson v1.17.1 | ||
) | ||
|
||
require ( | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/google/uuid v1.3.0 // indirect | ||
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 // indirect | ||
github.com/magefile/mage v1.14.0 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
github.com/tidwall/match v1.1.1 // indirect | ||
github.com/tidwall/pretty v1.2.0 // indirect | ||
github.com/tidwall/resp v0.1.1 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
github.com/alibaba/higress/plugins/wasm-go v1.3.5 h1:VOLL3m442IHCSu8mR5AZ4sc6LVT9X0w1hdqDI7oB9jY= | ||
github.com/alibaba/higress/plugins/wasm-go v1.3.5/go.mod h1:kr3V9Ntbspj1eSrX8rgjBsdMXkGupYEf+LM72caGPQc= | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= | ||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 h1:IHDghbGQ2DTIXHBHxWfqCYQW1fKjyJ/I7W1pMyUDeEA= | ||
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew= | ||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226065437-8f7a0b3c9071 h1:STb5rOHRZOzoiAa+gTz2LFqO1nYj7U/1eIVUJJadU4A= | ||
github.com/higress-group/proxy-wasm-go-sdk v0.0.0-20240226065437-8f7a0b3c9071/go.mod h1:hNFjhrLUIq+kJ9bOcs8QtiplSQ61GZXtd2xHKx4BYRo= | ||
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo= | ||
github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= | ||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= | ||
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= | ||
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= | ||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= | ||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= | ||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= | ||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= | ||
github.com/tidwall/resp v0.1.1 h1:Ly20wkhqKTmDUPlyM1S7pWo5kk0tDu8OoC/vFArXmwE= | ||
github.com/tidwall/resp v0.1.1/go.mod h1:3/FrruOBAxPTPtundW0VXgmsQ4ZBA0Aw714lVYgwFa0= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package main | ||
|
||
import ( | ||
"net/http" | ||
"strings" | ||
|
||
"github.com/alibaba/higress/plugins/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" | ||
) | ||
|
||
// 默认超时时间为60s | ||
var defaultTimeout uint32 = 60000 | ||
|
||
const VariableReplacedStr = "$uri" | ||
|
||
func main() { | ||
wrapper.SetCtx( | ||
"try-paths", | ||
wrapper.ParseConfigBy(parseConfig), | ||
wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders), | ||
) | ||
} | ||
|
||
// 自定义插件配置 | ||
type TryPathsConfig struct { | ||
tryPaths []string | ||
code []int // 支持多个返回码 | ||
client wrapper.HttpClient | ||
} | ||
|
||
func parseConfig(json gjson.Result, config *TryPathsConfig, log wrapper.Log) error { | ||
// 解析出配置,更新到config中 | ||
for _, result := range json.Get("tryPaths").Array() { | ||
config.tryPaths = append(config.tryPaths, result.String()) | ||
} | ||
|
||
// code默认值为["404", "403"] | ||
if json.Get("code").String() == "" { | ||
config.code = []int{http.StatusNotFound, http.StatusForbidden} | ||
} else { | ||
for _, result := range json.Get("code").Array() { | ||
config.code = append(config.code, int(result.Int())) | ||
} | ||
} | ||
client, err := Client(json) | ||
if err != nil { | ||
return err | ||
} | ||
config.client = client | ||
return nil | ||
} | ||
|
||
func tryHttpCall(ctx wrapper.HttpContext, config TryPathsConfig, index int, path string, log wrapper.Log) { | ||
if len(config.tryPaths) == index { | ||
proxywasm.ResumeHttpRequest() | ||
return | ||
} | ||
requestPath := strings.Replace(config.tryPaths[index], VariableReplacedStr, path, -1) | ||
config.client.Get(requestPath, nil, | ||
func(statusCode int, responseHeaders http.Header, responseBody []byte) { | ||
if !contains(config.code, statusCode) { | ||
proxywasm.SendHttpResponse(uint32(statusCode), convertHttpHeadersToStruct(responseHeaders), responseBody, -1) | ||
return | ||
} | ||
tryHttpCall(ctx, config, index+1, path, log) | ||
}, defaultTimeout) | ||
} | ||
|
||
func onHttpRequestHeaders(ctx wrapper.HttpContext, config TryPathsConfig, log wrapper.Log) types.Action { | ||
path := ctx.Path() | ||
if len(config.tryPaths) == 0 { | ||
return types.ActionContinue | ||
} | ||
|
||
tryHttpCall(ctx, config, 0, path, log) | ||
// 需要等待异步回调完成,返回Pause状态,可以被ResumeHttpRequest恢复 | ||
return types.ActionPause | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"net/http" | ||
|
||
"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper" | ||
"github.com/tidwall/gjson" | ||
) | ||
|
||
func convertHttpHeadersToStruct(responseHeaders http.Header) [][2]string { | ||
headerStruct := make([][2]string, len(responseHeaders)) | ||
i := 0 | ||
for key, values := range responseHeaders { | ||
headerStruct[i][0] = key | ||
headerStruct[i][1] = values[0] | ||
i++ | ||
} | ||
return headerStruct | ||
} | ||
|
||
func contains(array []int, value int) bool { | ||
for _, v := range array { | ||
if v == value { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func Client(json gjson.Result) (wrapper.HttpClient, error) { | ||
serviceSource := json.Get("serviceSource").String() | ||
serviceName := json.Get("serviceName").String() | ||
host := json.Get("host").String() | ||
servicePort := json.Get("servicePort").Int() | ||
if serviceName == "" || servicePort == 0 { | ||
return nil, errors.New("invalid service config") | ||
} | ||
switch serviceSource { | ||
case "k8s": | ||
namespace := json.Get("namespace").String() | ||
return wrapper.NewClusterClient(wrapper.K8sCluster{ | ||
ServiceName: serviceName, | ||
Namespace: namespace, | ||
Port: servicePort, | ||
Host: host, | ||
}), nil | ||
case "nacos": | ||
namespace := json.Get("namespace").String() | ||
return wrapper.NewClusterClient(wrapper.NacosCluster{ | ||
ServiceName: serviceName, | ||
NamespaceID: namespace, | ||
Port: servicePort, | ||
Host: host, | ||
}), nil | ||
case "ip": | ||
return wrapper.NewClusterClient(wrapper.StaticIpCluster{ | ||
ServiceName: serviceName, | ||
Port: servicePort, | ||
Host: host, | ||
}), nil | ||
case "dns": | ||
domain := json.Get("domain").String() | ||
return wrapper.NewClusterClient(wrapper.DnsCluster{ | ||
ServiceName: serviceName, | ||
Port: servicePort, | ||
Domain: domain, | ||
}), nil | ||
default: | ||
return nil, errors.New("unknown service source: " + serviceSource) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package main | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestConvertHttpHeadersToStruct(t *testing.T) { | ||
cases := []struct { | ||
name string | ||
httpHeaders map[string][]string | ||
expected [][2]string | ||
}{ | ||
{ | ||
name: "empty header", | ||
httpHeaders: map[string][]string{}, | ||
expected: [][2]string{}, | ||
}, | ||
{ | ||
name: "headers with content type", | ||
httpHeaders: map[string][]string{ | ||
"Content-Type": []string{"application/json", "application/xml"}, | ||
}, | ||
expected: [][2]string{ | ||
{"Content-Type", "application/json"}, | ||
{"Content-Type", "application/xml"}, | ||
}, | ||
}, | ||
} | ||
|
||
for _, c := range cases { | ||
t.Run(c.name, func(t *testing.T) { | ||
result := convertHttpHeadersToStruct(c.httpHeaders) | ||
require.Equal(t, c.expected, result) | ||
}) | ||
} | ||
} | ||
|
||
func TestContains(t *testing.T) { | ||
cases := []struct { | ||
name string | ||
array []int | ||
value int | ||
expected bool | ||
}{ | ||
{ | ||
name: "multi value, contains", | ||
array: []int{404, 304}, | ||
value: 404, | ||
expected: true, | ||
}, | ||
{ | ||
name: "multi value, no contains", | ||
array: []int{404, 304}, | ||
value: 200, | ||
expected: false, | ||
}, | ||
{ | ||
name: "one value contains", | ||
array: []int{200}, | ||
value: 200, | ||
expected: true, | ||
}, | ||
} | ||
|
||
for _, c := range cases { | ||
t.Run(c.name, func(t *testing.T) { | ||
result := contains(c.array, c.value) | ||
require.Equal(t, c.expected, result) | ||
}) | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
默认60秒超时有点久,建议改成1秒,另外可以支持一下配置