mirror of
https://github.com/alibaba/higress.git
synced 2026-06-09 04:37:31 +08:00
Add plugins (#27)
This commit is contained in:
196
plugins/wasm-go/pkg/matcher/rule_matcher.go
Normal file
196
plugins/wasm-go/pkg/matcher/rule_matcher.go
Normal file
@@ -0,0 +1,196 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package matcher
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
type Category int
|
||||
|
||||
const (
|
||||
Route Category = iota
|
||||
Host
|
||||
)
|
||||
|
||||
type MatchType int
|
||||
|
||||
const (
|
||||
Prefix MatchType = iota
|
||||
Exact
|
||||
Suffix
|
||||
)
|
||||
|
||||
const (
|
||||
RULES_KEY = "_rules_"
|
||||
MATCH_ROUTE_KEY = "_match_route_"
|
||||
MATCH_DOMAIN_KEY = "_match_domain_"
|
||||
)
|
||||
|
||||
type HostMatcher struct {
|
||||
matchType MatchType
|
||||
host string
|
||||
}
|
||||
|
||||
type RuleConfig[PluginConfig any] struct {
|
||||
category Category
|
||||
routes map[string]struct{}
|
||||
hosts []HostMatcher
|
||||
config PluginConfig
|
||||
}
|
||||
|
||||
type RuleMatcher[PluginConfig any] struct {
|
||||
ruleConfig []RuleConfig[PluginConfig]
|
||||
globalConfig PluginConfig
|
||||
hasGlobalConfig bool
|
||||
}
|
||||
|
||||
func (m RuleMatcher[PluginConfig]) GetMatchConfig() (*PluginConfig, error) {
|
||||
host, err := proxywasm.GetHttpRequestHeader(":authority")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
routeName, err := proxywasm.GetProperty([]string{"route_name"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, rule := range m.ruleConfig {
|
||||
if rule.category == Host {
|
||||
if m.hostMatch(rule, host) {
|
||||
return &rule.config, nil
|
||||
}
|
||||
}
|
||||
// category == Route
|
||||
if _, ok := rule.routes[string(routeName)]; ok {
|
||||
return &rule.config, nil
|
||||
}
|
||||
}
|
||||
if m.hasGlobalConfig {
|
||||
return &m.globalConfig, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *RuleMatcher[PluginConfig]) ParseRuleConfig(config gjson.Result,
|
||||
parsePluginConfig func(gjson.Result, *PluginConfig) error) error {
|
||||
var rules []gjson.Result
|
||||
obj := config.Map()
|
||||
keyCount := len(obj)
|
||||
if keyCount == 0 {
|
||||
// enable globally for empty config
|
||||
m.hasGlobalConfig = true
|
||||
return nil
|
||||
}
|
||||
if rulesJson, ok := obj[RULES_KEY]; ok {
|
||||
rules = rulesJson.Array()
|
||||
keyCount--
|
||||
}
|
||||
var pluginConfig PluginConfig
|
||||
if keyCount > 0 {
|
||||
err := parsePluginConfig(config, &pluginConfig)
|
||||
if err != nil {
|
||||
proxywasm.LogInfof("parse global config failed, err:%v", err)
|
||||
} else {
|
||||
m.globalConfig = pluginConfig
|
||||
m.hasGlobalConfig = true
|
||||
}
|
||||
}
|
||||
if len(rules) == 0 {
|
||||
if m.hasGlobalConfig {
|
||||
return nil
|
||||
}
|
||||
return errors.New("parse config failed, no valid rules")
|
||||
}
|
||||
for _, ruleJson := range rules {
|
||||
var rule RuleConfig[PluginConfig]
|
||||
err := parsePluginConfig(ruleJson, &rule.config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rule.routes = m.parseRouteMatchConfig(ruleJson)
|
||||
rule.hosts = m.parseHostMatchConfig(ruleJson)
|
||||
noRoute := len(rule.routes) == 0
|
||||
noHosts := len(rule.hosts) == 0
|
||||
if (noRoute && noHosts) || (!noRoute && !noHosts) {
|
||||
return errors.New("there is only one of '_match_route_' and '_match_domain_' can present in configuration.")
|
||||
}
|
||||
if !noRoute {
|
||||
rule.category = Route
|
||||
} else {
|
||||
rule.category = Host
|
||||
}
|
||||
m.ruleConfig = append(m.ruleConfig, rule)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m RuleMatcher[PluginConfig]) parseRouteMatchConfig(config gjson.Result) map[string]struct{} {
|
||||
keys := config.Get(MATCH_ROUTE_KEY).Array()
|
||||
routes := make(map[string]struct{})
|
||||
for _, item := range keys {
|
||||
routeName := item.String()
|
||||
if routeName != "" {
|
||||
routes[routeName] = struct{}{}
|
||||
}
|
||||
}
|
||||
return routes
|
||||
}
|
||||
|
||||
func (m RuleMatcher[PluginConfig]) parseHostMatchConfig(config gjson.Result) []HostMatcher {
|
||||
keys := config.Get(MATCH_DOMAIN_KEY).Array()
|
||||
var hostMatchers []HostMatcher
|
||||
for _, item := range keys {
|
||||
host := item.String()
|
||||
var hostMatcher HostMatcher
|
||||
if strings.HasPrefix(host, "*") {
|
||||
hostMatcher.matchType = Suffix
|
||||
hostMatcher.host = host[1:]
|
||||
} else if strings.HasSuffix(host, "*") {
|
||||
hostMatcher.matchType = Prefix
|
||||
hostMatcher.host = host[:len(host)-1]
|
||||
} else {
|
||||
hostMatcher.matchType = Exact
|
||||
hostMatcher.host = host
|
||||
}
|
||||
hostMatchers = append(hostMatchers, hostMatcher)
|
||||
}
|
||||
return hostMatchers
|
||||
}
|
||||
|
||||
func (m RuleMatcher[PluginConfig]) hostMatch(rule RuleConfig[PluginConfig], reqHost string) bool {
|
||||
for _, hostMatch := range rule.hosts {
|
||||
switch hostMatch.matchType {
|
||||
case Suffix:
|
||||
if strings.HasSuffix(reqHost, hostMatch.host) {
|
||||
return true
|
||||
}
|
||||
case Prefix:
|
||||
if strings.HasPrefix(reqHost, hostMatch.host) {
|
||||
return true
|
||||
}
|
||||
case Exact:
|
||||
if reqHost == hostMatch.host {
|
||||
return true
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
238
plugins/wasm-go/pkg/matcher/rule_matcher_test.go
Normal file
238
plugins/wasm-go/pkg/matcher/rule_matcher_test.go
Normal file
@@ -0,0 +1,238 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package matcher
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
type customConfig struct {
|
||||
name string
|
||||
age int64
|
||||
}
|
||||
|
||||
func parseConfig(json gjson.Result, config *customConfig) error {
|
||||
config.name = json.Get("name").String()
|
||||
config.age = json.Get("age").Int()
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestHostMatch(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
config RuleConfig[customConfig]
|
||||
host string
|
||||
result bool
|
||||
}{
|
||||
{
|
||||
name: "prefix",
|
||||
config: RuleConfig[customConfig]{
|
||||
hosts: []HostMatcher{
|
||||
{
|
||||
matchType: Prefix,
|
||||
host: "www.",
|
||||
},
|
||||
},
|
||||
},
|
||||
host: "www.test.com",
|
||||
result: true,
|
||||
},
|
||||
{
|
||||
name: "prefix failed",
|
||||
config: RuleConfig[customConfig]{
|
||||
hosts: []HostMatcher{
|
||||
{
|
||||
matchType: Prefix,
|
||||
host: "www.",
|
||||
},
|
||||
},
|
||||
},
|
||||
host: "test.com",
|
||||
result: false,
|
||||
},
|
||||
{
|
||||
name: "suffix",
|
||||
config: RuleConfig[customConfig]{
|
||||
hosts: []HostMatcher{
|
||||
{
|
||||
matchType: Suffix,
|
||||
host: ".example.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
host: "www.example.com",
|
||||
result: true,
|
||||
},
|
||||
{
|
||||
name: "suffix failed",
|
||||
config: RuleConfig[customConfig]{
|
||||
hosts: []HostMatcher{
|
||||
{
|
||||
matchType: Suffix,
|
||||
host: ".example.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
host: "example.com",
|
||||
result: false,
|
||||
},
|
||||
{
|
||||
name: "exact",
|
||||
config: RuleConfig[customConfig]{
|
||||
hosts: []HostMatcher{
|
||||
{
|
||||
matchType: Exact,
|
||||
host: "www.example.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
host: "www.example.com",
|
||||
result: true,
|
||||
},
|
||||
{
|
||||
name: "exact failed",
|
||||
config: RuleConfig[customConfig]{
|
||||
hosts: []HostMatcher{
|
||||
{
|
||||
matchType: Exact,
|
||||
host: "www.example.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
host: "example.com",
|
||||
result: false,
|
||||
},
|
||||
{
|
||||
name: "any",
|
||||
config: RuleConfig[customConfig]{
|
||||
hosts: []HostMatcher{
|
||||
{
|
||||
matchType: Suffix,
|
||||
host: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
host: "www.example.com",
|
||||
result: true,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var m RuleMatcher[customConfig]
|
||||
assert.Equal(t, c.result, m.hostMatch(c.config, c.host))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRuleConfig(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
config string
|
||||
errMsg string
|
||||
expected RuleMatcher[customConfig]
|
||||
}{
|
||||
{
|
||||
name: "global config",
|
||||
config: `{"name":"john", "age":18}`,
|
||||
expected: RuleMatcher[customConfig]{
|
||||
globalConfig: customConfig{
|
||||
name: "john",
|
||||
age: 18,
|
||||
},
|
||||
hasGlobalConfig: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rules config",
|
||||
config: `{"_rules_":[{"_match_domain_":["*.example.com","www.*","*","www.abc.com"],"name":"john", "age":18},{"_match_route_":["test1","test2"],"name":"ann", "age":16}]}`,
|
||||
expected: RuleMatcher[customConfig]{
|
||||
ruleConfig: []RuleConfig[customConfig]{
|
||||
{
|
||||
category: Host,
|
||||
hosts: []HostMatcher{
|
||||
{
|
||||
matchType: Suffix,
|
||||
host: ".example.com",
|
||||
},
|
||||
{
|
||||
matchType: Prefix,
|
||||
host: "www.",
|
||||
},
|
||||
{
|
||||
matchType: Suffix,
|
||||
host: "",
|
||||
},
|
||||
{
|
||||
matchType: Exact,
|
||||
host: "www.abc.com",
|
||||
},
|
||||
},
|
||||
routes: map[string]struct{}{},
|
||||
config: customConfig{
|
||||
name: "john",
|
||||
age: 18,
|
||||
},
|
||||
},
|
||||
{
|
||||
category: Route,
|
||||
routes: map[string]struct{}{
|
||||
"test1": {},
|
||||
"test2": {},
|
||||
},
|
||||
config: customConfig{
|
||||
name: "ann",
|
||||
age: 16,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no rule",
|
||||
config: `{"_rules_":[]}`,
|
||||
errMsg: "parse config failed, no valid rules",
|
||||
},
|
||||
{
|
||||
name: "invalid rule",
|
||||
config: `{"_rules_":[{"_match_domain_":["*"],"_match_route_":["test"]}]}`,
|
||||
errMsg: "there is only one of '_match_route_' and '_match_domain_' can present in configuration.",
|
||||
},
|
||||
{
|
||||
name: "invalid rule",
|
||||
config: `{"_rules_":[{"age":16}]}`,
|
||||
errMsg: "there is only one of '_match_route_' and '_match_domain_' can present in configuration.",
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var actual RuleMatcher[customConfig]
|
||||
err := actual.ParseRuleConfig(gjson.Parse(c.config), parseConfig)
|
||||
if err != nil {
|
||||
if c.errMsg == "" {
|
||||
t.Errorf("parse failed: %v", err)
|
||||
}
|
||||
if err.Error() != c.errMsg {
|
||||
t.Errorf("expect err: %s, actual err: %s", c.errMsg,
|
||||
err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
assert.Equal(t, c.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
112
plugins/wasm-go/pkg/wrapper/cluster_wrapper.go
Normal file
112
plugins/wasm-go/pkg/wrapper/cluster_wrapper.go
Normal file
@@ -0,0 +1,112 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package wrapper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Cluster interface {
|
||||
ClusterName() string
|
||||
HostName() string
|
||||
}
|
||||
|
||||
type K8sCluster struct {
|
||||
ServiceName string
|
||||
Namespace string
|
||||
Port int64
|
||||
Version string
|
||||
Host string
|
||||
}
|
||||
|
||||
func (c K8sCluster) ClusterName() string {
|
||||
namespace := "default"
|
||||
if c.Namespace != "" {
|
||||
namespace = c.Namespace
|
||||
}
|
||||
return fmt.Sprintf("outbound|%d|%s|%s.%s.svc.cluster.local",
|
||||
c.Port, c.Version, c.ServiceName, namespace)
|
||||
}
|
||||
|
||||
func (c K8sCluster) HostName() string {
|
||||
if c.Host != "" {
|
||||
return c.Host
|
||||
}
|
||||
return fmt.Sprintf("%s.%s.svc.cluster.local", c.ServiceName, c.Namespace)
|
||||
}
|
||||
|
||||
type NacosCluster struct {
|
||||
ServiceName string
|
||||
// use DEFAULT-GROUP by default
|
||||
Group string
|
||||
NamespaceID string
|
||||
Port int64
|
||||
// set true if use edas/sae registry
|
||||
IsExtRegistry bool
|
||||
Version string
|
||||
Host string
|
||||
}
|
||||
|
||||
func (c NacosCluster) ClusterName() string {
|
||||
group := "DEFAULT-GROUP"
|
||||
if c.Group != "" {
|
||||
group = strings.ReplaceAll(c.Group, "_", "-")
|
||||
}
|
||||
tail := "nacos"
|
||||
if c.IsExtRegistry {
|
||||
tail += "-ext"
|
||||
}
|
||||
return fmt.Sprintf("outbound|%d|%s|%s.%s.%s.%s",
|
||||
c.Port, c.Version, c.ServiceName, group, c.NamespaceID, tail)
|
||||
}
|
||||
|
||||
func (c NacosCluster) HostName() string {
|
||||
if c.Host != "" {
|
||||
return c.Host
|
||||
}
|
||||
return c.ServiceName
|
||||
}
|
||||
|
||||
type StaticIpCluster struct {
|
||||
ServiceName string
|
||||
Port int64
|
||||
Host string
|
||||
}
|
||||
|
||||
func (c StaticIpCluster) ClusterName() string {
|
||||
return fmt.Sprintf("outbound|%d||%s.static", c.Port, c.ServiceName)
|
||||
}
|
||||
|
||||
func (c StaticIpCluster) HostName() string {
|
||||
if c.Host != "" {
|
||||
return c.Host
|
||||
}
|
||||
return c.ServiceName
|
||||
}
|
||||
|
||||
type DnsCluster struct {
|
||||
ServiceName string
|
||||
Domain string
|
||||
Port int64
|
||||
}
|
||||
|
||||
func (c DnsCluster) ClusterName() string {
|
||||
return fmt.Sprintf("outbound|%d||%s.dns", c.Port, c.ServiceName)
|
||||
}
|
||||
|
||||
func (c DnsCluster) HostName() string {
|
||||
return c.Domain
|
||||
}
|
||||
102
plugins/wasm-go/pkg/wrapper/cluster_wrapper_test.go
Normal file
102
plugins/wasm-go/pkg/wrapper/cluster_wrapper_test.go
Normal file
@@ -0,0 +1,102 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package wrapper
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestClusterAndHost(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
cluster Cluster
|
||||
expectCluster string
|
||||
expectHost string
|
||||
}{
|
||||
{
|
||||
name: "k8s",
|
||||
cluster: K8sCluster{
|
||||
ServiceName: "foo",
|
||||
Namespace: "bar",
|
||||
Port: 8080,
|
||||
Version: "1.0",
|
||||
},
|
||||
expectCluster: "outbound|8080|1.0|foo.bar.svc.cluster.local",
|
||||
expectHost: "foo.bar.svc.cluster.local",
|
||||
},
|
||||
{
|
||||
name: "k8s default",
|
||||
cluster: K8sCluster{
|
||||
ServiceName: "foo",
|
||||
Port: 8080,
|
||||
Host: "www.example.com",
|
||||
},
|
||||
expectCluster: "outbound|8080||foo.default.svc.cluster.local",
|
||||
expectHost: "www.example.com",
|
||||
},
|
||||
{
|
||||
name: "nacos",
|
||||
cluster: NacosCluster{
|
||||
ServiceName: "foo",
|
||||
Group: "DEFAULT_GROUP",
|
||||
NamespaceID: "xxxx",
|
||||
Port: 8080,
|
||||
Version: "1.0",
|
||||
},
|
||||
expectCluster: "outbound|8080|1.0|foo.DEFAULT-GROUP.xxxx.nacos",
|
||||
expectHost: "foo",
|
||||
},
|
||||
{
|
||||
name: "nacos ext",
|
||||
cluster: NacosCluster{
|
||||
ServiceName: "foo",
|
||||
NamespaceID: "xxxx",
|
||||
Port: 8080,
|
||||
IsExtRegistry: true,
|
||||
Host: "www.test.com",
|
||||
},
|
||||
expectCluster: "outbound|8080||foo.DEFAULT-GROUP.xxxx.nacos-ext",
|
||||
expectHost: "www.test.com",
|
||||
},
|
||||
{
|
||||
name: "static",
|
||||
cluster: StaticIpCluster{
|
||||
ServiceName: "foo",
|
||||
Port: 8080,
|
||||
Host: "www.test.com",
|
||||
},
|
||||
expectCluster: "outbound|8080||foo.static",
|
||||
expectHost: "www.test.com",
|
||||
},
|
||||
{
|
||||
name: "dns",
|
||||
cluster: DnsCluster{
|
||||
ServiceName: "foo",
|
||||
Port: 8080,
|
||||
Domain: "www.test.com",
|
||||
},
|
||||
expectCluster: "outbound|8080||foo.dns",
|
||||
expectHost: "www.test.com",
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
assert.Equal(t, c.expectCluster, c.cluster.ClusterName())
|
||||
assert.Equal(t, c.expectHost, c.cluster.HostName())
|
||||
})
|
||||
}
|
||||
}
|
||||
121
plugins/wasm-go/pkg/wrapper/http_wrapper.go
Normal file
121
plugins/wasm-go/pkg/wrapper/http_wrapper.go
Normal file
@@ -0,0 +1,121 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package wrapper
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
|
||||
)
|
||||
|
||||
type ResponseCallback func(statusCode int, responseHeaders http.Header, responseBody []byte)
|
||||
|
||||
type HttpClient interface {
|
||||
Get(path string, headers [][2]string, cb ResponseCallback, timeoutMillisecond ...uint32) error
|
||||
Head(path string, headers [][2]string, cb ResponseCallback, timeoutMillisecond ...uint32) error
|
||||
Options(path string, headers [][2]string, cb ResponseCallback, timeoutMillisecond ...uint32) error
|
||||
Post(path string, headers [][2]string, body []byte, cb ResponseCallback, timeoutMillisecond ...uint32) error
|
||||
Put(path string, headers [][2]string, body []byte, cb ResponseCallback, timeoutMillisecond ...uint32) error
|
||||
Patch(path string, headers [][2]string, body []byte, cb ResponseCallback, timeoutMillisecond ...uint32) error
|
||||
Delete(path string, headers [][2]string, body []byte, cb ResponseCallback, timeoutMillisecond ...uint32) error
|
||||
Connect(path string, headers [][2]string, body []byte, cb ResponseCallback, timeoutMillisecond ...uint32) error
|
||||
Trace(path string, headers [][2]string, body []byte, cb ResponseCallback, timeoutMillisecond ...uint32) error
|
||||
}
|
||||
|
||||
type ClusterClient[C Cluster] struct {
|
||||
cluster C
|
||||
}
|
||||
|
||||
func NewClusterClient[C Cluster](cluster C) *ClusterClient[C] {
|
||||
return &ClusterClient[C]{cluster: cluster}
|
||||
}
|
||||
|
||||
func (c ClusterClient[C]) Get(path string, headers [][2]string, cb ResponseCallback, timeoutMillisecond ...uint32) error {
|
||||
return HttpCall(c.cluster, http.MethodGet, path, headers, nil, cb, timeoutMillisecond...)
|
||||
}
|
||||
func (c ClusterClient[C]) Head(path string, headers [][2]string, cb ResponseCallback, timeoutMillisecond ...uint32) error {
|
||||
return HttpCall(c.cluster, http.MethodHead, path, headers, nil, cb, timeoutMillisecond...)
|
||||
}
|
||||
func (c ClusterClient[C]) Options(path string, headers [][2]string, cb ResponseCallback, timeoutMillisecond ...uint32) error {
|
||||
return HttpCall(c.cluster, http.MethodOptions, path, headers, nil, cb, timeoutMillisecond...)
|
||||
}
|
||||
func (c ClusterClient[C]) Post(path string, headers [][2]string, body []byte, cb ResponseCallback, timeoutMillisecond ...uint32) error {
|
||||
return HttpCall(c.cluster, http.MethodPost, path, headers, body, cb, timeoutMillisecond...)
|
||||
}
|
||||
func (c ClusterClient[C]) Put(path string, headers [][2]string, body []byte, cb ResponseCallback, timeoutMillisecond ...uint32) error {
|
||||
return HttpCall(c.cluster, http.MethodPut, path, headers, body, cb, timeoutMillisecond...)
|
||||
}
|
||||
func (c ClusterClient[C]) Patch(path string, headers [][2]string, body []byte, cb ResponseCallback, timeoutMillisecond ...uint32) error {
|
||||
return HttpCall(c.cluster, http.MethodPatch, path, headers, body, cb, timeoutMillisecond...)
|
||||
}
|
||||
func (c ClusterClient[C]) Delete(path string, headers [][2]string, body []byte, cb ResponseCallback, timeoutMillisecond ...uint32) error {
|
||||
return HttpCall(c.cluster, http.MethodDelete, path, headers, body, cb, timeoutMillisecond...)
|
||||
}
|
||||
func (c ClusterClient[C]) Connect(path string, headers [][2]string, body []byte, cb ResponseCallback, timeoutMillisecond ...uint32) error {
|
||||
return HttpCall(c.cluster, http.MethodConnect, path, headers, body, cb, timeoutMillisecond...)
|
||||
}
|
||||
func (c ClusterClient[C]) Trace(path string, headers [][2]string, body []byte, cb ResponseCallback, timeoutMillisecond ...uint32) error {
|
||||
return HttpCall(c.cluster, http.MethodTrace, path, headers, body, cb, timeoutMillisecond...)
|
||||
}
|
||||
|
||||
func HttpCall(cluster Cluster, method, path string, headers [][2]string, body []byte,
|
||||
callback ResponseCallback, timeoutMillisecond ...uint32) error {
|
||||
for i := len(headers) - 1; i >= 0; i-- {
|
||||
key := headers[i][0]
|
||||
if key == ":method" || key == ":path" || key == ":authority" {
|
||||
headers = append(headers[:i], headers[i+1:]...)
|
||||
}
|
||||
}
|
||||
// default timeout is 500ms
|
||||
var timeout uint32 = 500
|
||||
if len(timeoutMillisecond) > 0 {
|
||||
timeout = timeoutMillisecond[0]
|
||||
}
|
||||
headers = append(headers, [2]string{":method", method}, [2]string{":path", path}, [2]string{":authority", cluster.HostName()})
|
||||
requestID := uuid.New().String()
|
||||
_, err := proxywasm.DispatchHttpCall(cluster.ClusterName(), headers, body, nil, timeout, func(numHeaders, bodySize, numTrailers int) {
|
||||
respBody, err := proxywasm.GetHttpCallResponseBody(0, bodySize)
|
||||
if err != nil {
|
||||
proxywasm.LogCriticalf("failed to get response body: %v", err)
|
||||
}
|
||||
respHeaders, err := proxywasm.GetHttpCallResponseHeaders()
|
||||
if err != nil {
|
||||
proxywasm.LogCriticalf("failed to get response headers: %v", err)
|
||||
}
|
||||
code := http.StatusBadGateway
|
||||
var normalResponse bool
|
||||
headers := make(http.Header)
|
||||
for _, h := range respHeaders {
|
||||
if h[0] == ":status" {
|
||||
code, err = strconv.Atoi(h[1])
|
||||
if err != nil {
|
||||
proxywasm.LogErrorf("failed to parse status: %v", err)
|
||||
code = http.StatusInternalServerError
|
||||
} else {
|
||||
normalResponse = true
|
||||
}
|
||||
}
|
||||
headers.Add(h[0], h[1])
|
||||
}
|
||||
proxywasm.LogDebugf("http call end, id: %s, code: %d, normal: %t, body: %s",
|
||||
requestID, code, normalResponse, respBody)
|
||||
callback(code, headers, respBody)
|
||||
})
|
||||
proxywasm.LogDebugf("http call start, id: %s, cluster: %+v, method: %s, path: %s, body: %s, timeout: %d",
|
||||
requestID, cluster, method, path, body, timeout)
|
||||
return err
|
||||
}
|
||||
120
plugins/wasm-go/pkg/wrapper/log_wrapper.go
Normal file
120
plugins/wasm-go/pkg/wrapper/log_wrapper.go
Normal file
@@ -0,0 +1,120 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package wrapper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
|
||||
)
|
||||
|
||||
type LogLevel uint32
|
||||
|
||||
const (
|
||||
LogLevelTrace LogLevel = iota
|
||||
LogLevelDebug
|
||||
LogLevelInfo
|
||||
LogLevelWarn
|
||||
LogLevelError
|
||||
LogLevelCritical
|
||||
)
|
||||
|
||||
type LogWrapper struct {
|
||||
pluginName string
|
||||
}
|
||||
|
||||
func (l LogWrapper) log(level LogLevel, msg string) {
|
||||
msg = fmt.Sprintf("[%s] %s", l.pluginName, msg)
|
||||
switch level {
|
||||
case LogLevelTrace:
|
||||
proxywasm.LogTrace(msg)
|
||||
case LogLevelDebug:
|
||||
proxywasm.LogDebug(msg)
|
||||
case LogLevelInfo:
|
||||
proxywasm.LogInfo(msg)
|
||||
case LogLevelWarn:
|
||||
proxywasm.LogWarn(msg)
|
||||
case LogLevelError:
|
||||
proxywasm.LogError(msg)
|
||||
case LogLevelCritical:
|
||||
proxywasm.LogCritical(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (l LogWrapper) logFormat(level LogLevel, format string, args ...interface{}) {
|
||||
format = fmt.Sprintf("[%s] %s", l.pluginName, format)
|
||||
switch level {
|
||||
case LogLevelTrace:
|
||||
proxywasm.LogTracef(format, args...)
|
||||
case LogLevelDebug:
|
||||
proxywasm.LogDebugf(format, args...)
|
||||
case LogLevelInfo:
|
||||
proxywasm.LogInfof(format, args...)
|
||||
case LogLevelWarn:
|
||||
proxywasm.LogWarnf(format, args...)
|
||||
case LogLevelError:
|
||||
proxywasm.LogErrorf(format, args...)
|
||||
case LogLevelCritical:
|
||||
proxywasm.LogCriticalf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l LogWrapper) Trace(msg string) {
|
||||
l.log(LogLevelTrace, msg)
|
||||
}
|
||||
|
||||
func (l LogWrapper) Tracef(format string, args ...interface{}) {
|
||||
l.logFormat(LogLevelTrace, format, args...)
|
||||
}
|
||||
|
||||
func (l LogWrapper) Debug(msg string) {
|
||||
l.log(LogLevelDebug, msg)
|
||||
}
|
||||
|
||||
func (l LogWrapper) Debugf(format string, args ...interface{}) {
|
||||
l.logFormat(LogLevelDebug, format, args...)
|
||||
}
|
||||
|
||||
func (l LogWrapper) Info(msg string) {
|
||||
l.log(LogLevelInfo, msg)
|
||||
}
|
||||
|
||||
func (l LogWrapper) Infof(format string, args ...interface{}) {
|
||||
l.logFormat(LogLevelInfo, format, args...)
|
||||
}
|
||||
|
||||
func (l LogWrapper) Warn(msg string) {
|
||||
l.log(LogLevelWarn, msg)
|
||||
}
|
||||
|
||||
func (l LogWrapper) Warnf(format string, args ...interface{}) {
|
||||
l.logFormat(LogLevelWarn, format, args...)
|
||||
}
|
||||
|
||||
func (l LogWrapper) Error(msg string) {
|
||||
l.log(LogLevelError, msg)
|
||||
}
|
||||
|
||||
func (l LogWrapper) Errorf(format string, args ...interface{}) {
|
||||
l.logFormat(LogLevelError, format, args...)
|
||||
}
|
||||
|
||||
func (l LogWrapper) Critical(msg string) {
|
||||
l.log(LogLevelCritical, msg)
|
||||
}
|
||||
|
||||
func (l LogWrapper) Criticalf(format string, args ...interface{}) {
|
||||
l.logFormat(LogLevelCritical, format, args...)
|
||||
}
|
||||
235
plugins/wasm-go/pkg/wrapper/plugin_wrapper.go
Normal file
235
plugins/wasm-go/pkg/wrapper/plugin_wrapper.go
Normal file
@@ -0,0 +1,235 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package wrapper
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
|
||||
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/mse-group/wasm-extensions-go/pkg/matcher"
|
||||
)
|
||||
|
||||
type ParseConfigFunc[PluginConfig any] func(json gjson.Result, config *PluginConfig, log LogWrapper) error
|
||||
type onHttpHeadersFunc[PluginConfig any] func(contextID uint32, config PluginConfig, needBody *bool, log LogWrapper) types.Action
|
||||
type onHttpBodyFunc[PluginConfig any] func(contextID uint32, config PluginConfig, body []byte, log LogWrapper) types.Action
|
||||
|
||||
type CommonVmCtx[PluginConfig any] struct {
|
||||
types.DefaultVMContext
|
||||
pluginName string
|
||||
log LogWrapper
|
||||
parseConfig ParseConfigFunc[PluginConfig]
|
||||
onHttpRequestHeaders onHttpHeadersFunc[PluginConfig]
|
||||
onHttpRequestBody onHttpBodyFunc[PluginConfig]
|
||||
onHttpResponseHeaders onHttpHeadersFunc[PluginConfig]
|
||||
onHttpResponseBody onHttpBodyFunc[PluginConfig]
|
||||
}
|
||||
|
||||
func SetCtx[PluginConfig any](pluginName string, setFuncs ...SetPluginFunc[PluginConfig]) {
|
||||
proxywasm.SetVMContext(NewCommonVmCtx(pluginName, setFuncs...))
|
||||
}
|
||||
|
||||
type SetPluginFunc[PluginConfig any] func(*CommonVmCtx[PluginConfig])
|
||||
|
||||
func ParseConfigBy[PluginConfig any](f ParseConfigFunc[PluginConfig]) SetPluginFunc[PluginConfig] {
|
||||
return func(ctx *CommonVmCtx[PluginConfig]) {
|
||||
ctx.parseConfig = f
|
||||
}
|
||||
}
|
||||
|
||||
func ProcessRequestHeadersBy[PluginConfig any](f onHttpHeadersFunc[PluginConfig]) SetPluginFunc[PluginConfig] {
|
||||
return func(ctx *CommonVmCtx[PluginConfig]) {
|
||||
ctx.onHttpRequestHeaders = f
|
||||
}
|
||||
}
|
||||
|
||||
func ProcessRequestBodyBy[PluginConfig any](f onHttpBodyFunc[PluginConfig]) SetPluginFunc[PluginConfig] {
|
||||
return func(ctx *CommonVmCtx[PluginConfig]) {
|
||||
ctx.onHttpRequestBody = f
|
||||
}
|
||||
}
|
||||
|
||||
func ProcessResponseHeadersBy[PluginConfig any](f onHttpHeadersFunc[PluginConfig]) SetPluginFunc[PluginConfig] {
|
||||
return func(ctx *CommonVmCtx[PluginConfig]) {
|
||||
ctx.onHttpResponseHeaders = f
|
||||
}
|
||||
}
|
||||
|
||||
func ProcessResponseBodyBy[PluginConfig any](f onHttpBodyFunc[PluginConfig]) SetPluginFunc[PluginConfig] {
|
||||
return func(ctx *CommonVmCtx[PluginConfig]) {
|
||||
ctx.onHttpResponseBody = f
|
||||
}
|
||||
}
|
||||
|
||||
func parseEmptyPluginConfig[PluginConfig any](gjson.Result, *PluginConfig, LogWrapper) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewCommonVmCtx[PluginConfig any](pluginName string, setFuncs ...SetPluginFunc[PluginConfig]) *CommonVmCtx[PluginConfig] {
|
||||
ctx := &CommonVmCtx[PluginConfig]{
|
||||
pluginName: pluginName,
|
||||
log: LogWrapper{pluginName},
|
||||
}
|
||||
for _, set := range setFuncs {
|
||||
set(ctx)
|
||||
}
|
||||
if ctx.parseConfig == nil {
|
||||
var config PluginConfig
|
||||
if unsafe.Sizeof(config) != 0 {
|
||||
msg := "the `parseConfig` is missing in NewCommonVmCtx's arguments"
|
||||
ctx.log.Critical(msg)
|
||||
panic(msg)
|
||||
}
|
||||
ctx.parseConfig = parseEmptyPluginConfig[PluginConfig]
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (ctx *CommonVmCtx[PluginConfig]) NewPluginContext(uint32) types.PluginContext {
|
||||
return &CommonPluginCtx[PluginConfig]{
|
||||
vm: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
type CommonPluginCtx[PluginConfig any] struct {
|
||||
types.DefaultPluginContext
|
||||
matcher.RuleMatcher[PluginConfig]
|
||||
vm *CommonVmCtx[PluginConfig]
|
||||
}
|
||||
|
||||
func (ctx *CommonPluginCtx[PluginConfig]) OnPluginStart(int) types.OnPluginStartStatus {
|
||||
data, err := proxywasm.GetPluginConfiguration()
|
||||
if err != nil && err != types.ErrorStatusNotFound {
|
||||
ctx.vm.log.Criticalf("error reading plugin configuration: %v", err)
|
||||
return types.OnPluginStartStatusFailed
|
||||
}
|
||||
if len(data) == 0 {
|
||||
ctx.vm.log.Warn("need config")
|
||||
return types.OnPluginStartStatusFailed
|
||||
}
|
||||
if !gjson.ValidBytes(data) {
|
||||
ctx.vm.log.Warnf("the plugin configuration is not a valid json: %s", string(data))
|
||||
return types.OnPluginStartStatusFailed
|
||||
|
||||
}
|
||||
jsonData := gjson.ParseBytes(data)
|
||||
err = ctx.ParseRuleConfig(jsonData, func(js gjson.Result, cfg *PluginConfig) error {
|
||||
return ctx.vm.parseConfig(js, cfg, ctx.vm.log)
|
||||
})
|
||||
if err != nil {
|
||||
ctx.vm.log.Warnf("parse rule config failed: %v", err)
|
||||
return types.OnPluginStartStatusFailed
|
||||
}
|
||||
return types.OnPluginStartStatusOK
|
||||
}
|
||||
|
||||
func (ctx *CommonPluginCtx[PluginConfig]) NewHttpContext(contextID uint32) types.HttpContext {
|
||||
httpCtx := &CommonHttpCtx[PluginConfig]{
|
||||
plugin: ctx,
|
||||
contextID: contextID,
|
||||
}
|
||||
if ctx.vm.onHttpRequestBody != nil {
|
||||
httpCtx.needRequestBody = true
|
||||
}
|
||||
if ctx.vm.onHttpResponseBody != nil {
|
||||
httpCtx.needResponseBody = true
|
||||
}
|
||||
return httpCtx
|
||||
}
|
||||
|
||||
type CommonHttpCtx[PluginConfig any] struct {
|
||||
types.DefaultHttpContext
|
||||
plugin *CommonPluginCtx[PluginConfig]
|
||||
config *PluginConfig
|
||||
needRequestBody bool
|
||||
needResponseBody bool
|
||||
requestBodySize int
|
||||
responseBodySize int
|
||||
contextID uint32
|
||||
}
|
||||
|
||||
func (ctx *CommonHttpCtx[PluginConfig]) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
|
||||
config, err := ctx.plugin.GetMatchConfig()
|
||||
if err != nil {
|
||||
ctx.plugin.vm.log.Errorf("get match config failed, err:%v", err)
|
||||
return types.ActionContinue
|
||||
}
|
||||
if config == nil {
|
||||
return types.ActionContinue
|
||||
}
|
||||
ctx.config = config
|
||||
if ctx.plugin.vm.onHttpRequestHeaders == nil {
|
||||
return types.ActionContinue
|
||||
}
|
||||
return ctx.plugin.vm.onHttpRequestHeaders(ctx.contextID, *config,
|
||||
&ctx.needRequestBody, ctx.plugin.vm.log)
|
||||
}
|
||||
|
||||
func (ctx *CommonHttpCtx[PluginConfig]) OnHttpRequestBody(bodySize int, endOfStream bool) types.Action {
|
||||
if ctx.config == nil {
|
||||
return types.ActionContinue
|
||||
}
|
||||
if ctx.plugin.vm.onHttpRequestBody == nil {
|
||||
return types.ActionContinue
|
||||
}
|
||||
if !ctx.needRequestBody {
|
||||
return types.ActionContinue
|
||||
}
|
||||
ctx.requestBodySize += bodySize
|
||||
if !endOfStream {
|
||||
return types.ActionPause
|
||||
}
|
||||
body, err := proxywasm.GetHttpRequestBody(0, ctx.requestBodySize)
|
||||
if err != nil {
|
||||
ctx.plugin.vm.log.Warnf("get request body failed: %v", err)
|
||||
return types.ActionContinue
|
||||
}
|
||||
return ctx.plugin.vm.onHttpRequestBody(ctx.contextID, *ctx.config, body, ctx.plugin.vm.log)
|
||||
}
|
||||
|
||||
func (ctx *CommonHttpCtx[PluginConfig]) OnHttpResponseHeaders(numHeaders int, endOfStream bool) types.Action {
|
||||
if ctx.config == nil {
|
||||
return types.ActionContinue
|
||||
}
|
||||
if ctx.plugin.vm.onHttpResponseHeaders == nil {
|
||||
return types.ActionContinue
|
||||
}
|
||||
return ctx.plugin.vm.onHttpResponseHeaders(ctx.contextID, *ctx.config,
|
||||
&ctx.needResponseBody, ctx.plugin.vm.log)
|
||||
}
|
||||
|
||||
func (ctx *CommonHttpCtx[PluginConfig]) OnHttpResponseBody(bodySize int, endOfStream bool) types.Action {
|
||||
if ctx.config == nil {
|
||||
return types.ActionContinue
|
||||
}
|
||||
if ctx.plugin.vm.onHttpResponseBody == nil {
|
||||
return types.ActionContinue
|
||||
}
|
||||
if !ctx.needResponseBody {
|
||||
return types.ActionContinue
|
||||
}
|
||||
ctx.responseBodySize += bodySize
|
||||
if !endOfStream {
|
||||
return types.ActionPause
|
||||
}
|
||||
body, err := proxywasm.GetHttpResponseBody(0, ctx.responseBodySize)
|
||||
if err != nil {
|
||||
ctx.plugin.vm.log.Warnf("get response body failed: %v", err)
|
||||
return types.ActionContinue
|
||||
}
|
||||
return ctx.plugin.vm.onHttpResponseBody(ctx.contextID, *ctx.config, body, ctx.plugin.vm.log)
|
||||
}
|
||||
53
plugins/wasm-go/pkg/wrapper/request_wrapper.go
Normal file
53
plugins/wasm-go/pkg/wrapper/request_wrapper.go
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package wrapper
|
||||
|
||||
import "github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
|
||||
|
||||
func GetRequestScheme() string {
|
||||
scheme, err := proxywasm.GetHttpRequestHeader(":scheme")
|
||||
if err != nil {
|
||||
proxywasm.LogError("parse request scheme failed")
|
||||
return ""
|
||||
}
|
||||
return scheme
|
||||
}
|
||||
|
||||
func GetRequestHost() string {
|
||||
host, err := proxywasm.GetHttpRequestHeader(":authority")
|
||||
if err != nil {
|
||||
proxywasm.LogError("parse request host failed")
|
||||
return ""
|
||||
}
|
||||
return host
|
||||
}
|
||||
|
||||
func GetRequestPath() string {
|
||||
path, err := proxywasm.GetHttpRequestHeader(":path")
|
||||
if err != nil {
|
||||
proxywasm.LogError("parse request path failed")
|
||||
return ""
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func GetRequestMethod() string {
|
||||
method, err := proxywasm.GetHttpRequestHeader(":method")
|
||||
if err != nil {
|
||||
proxywasm.LogError("parse request path failed")
|
||||
return ""
|
||||
}
|
||||
return method
|
||||
}
|
||||
Reference in New Issue
Block a user