diff --git a/.gitignore b/.gitignore index 0482ed5f9..02a57e664 100644 --- a/.gitignore +++ b/.gitignore @@ -27,5 +27,6 @@ coverage.txt .vscode/* bfe dist/* +conf/wasm_plugin .DS_Store diff --git a/Makefile b/Makefile index ddb73b5d2..4c7ec07b4 100644 --- a/Makefile +++ b/Makefile @@ -74,7 +74,7 @@ strip: prepare compile-strip package # make prepare, download dependencies prepare: prepare-dep prepare-gen prepare-dep: - $(call INSTALL_PKG, goyacc, golang.org/x/tools/cmd/goyacc) + $(call INSTALL_PKG, goyacc, golang.org/x/tools/cmd/goyacc@latest) prepare-gen: cd "bfe_basic/condition/parser" && $(GOGEN) @@ -117,7 +117,7 @@ package: # make deps deps: $(call PIP_INSTALL_PKG, pre-commit) - $(call INSTALL_PKG, goyacc, golang.org/x/tools/cmd/goyacc) + $(call INSTALL_PKG, goyacc, golang.org/x/tools/cmd/goyacc@latest) $(call INSTALL_PKG, staticcheck, honnef.co/go/tools/cmd/staticcheck) $(call INSTALL_PKG, license-eye, github.com/apache/skywalking-eyes/cmd/license-eye@latest) diff --git a/bfe_modules/bfe_modules.go b/bfe_modules/bfe_modules.go index 326a9830a..56017ca5f 100644 --- a/bfe_modules/bfe_modules.go +++ b/bfe_modules/bfe_modules.go @@ -44,6 +44,7 @@ import ( "github.com/bfenetworks/bfe/bfe_modules/mod_trust_clientip" "github.com/bfenetworks/bfe/bfe_modules/mod_userid" "github.com/bfenetworks/bfe/bfe_modules/mod_waf" + "github.com/bfenetworks/bfe/bfe_modules/mod_wasmplugin" ) // list of all modules, the order is very important @@ -131,6 +132,9 @@ var moduleList = []bfe_module.BfeModule{ // mod_access mod_access.NewModuleAccess(), + + // mod_wasm + mod_wasmplugin.NewModuleWasm(), } // init modules list diff --git a/bfe_modules/mod_wasmplugin/conf_mod_wasmplugin.go b/bfe_modules/mod_wasmplugin/conf_mod_wasmplugin.go new file mode 100644 index 000000000..8aa498529 --- /dev/null +++ b/bfe_modules/mod_wasmplugin/conf_mod_wasmplugin.go @@ -0,0 +1,68 @@ +// Copyright (c) 2024 The BFE Authors. +// +// 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 mod_wasmplugin + +import ( + "github.com/baidu/go-lib/log" + "github.com/bfenetworks/bfe/bfe_util" + gcfg "gopkg.in/gcfg.v1" +) + +type ConfModWasm struct { + Basic struct { + WasmPluginPath string // path of Wasm plugins + DataPath string // path of config data + } + + Log struct { + OpenDebug bool + } +} + +// ConfLoad loads config from config file +func ConfLoad(filePath string, confRoot string) (*ConfModWasm, error) { + var cfg ConfModWasm + var err error + + // read config from file + err = gcfg.ReadFileInto(&cfg, filePath) + if err != nil { + return &cfg, err + } + + // check conf of mod_redirect + err = cfg.Check(confRoot) + if err != nil { + return &cfg, err + } + + return &cfg, nil +} + +func (cfg *ConfModWasm) Check(confRoot string) error { + if cfg.Basic.WasmPluginPath == "" { + log.Logger.Warn("ModWasm.WasmPluginPath not set, use default value") + cfg.Basic.WasmPluginPath = "mod_wasm" + } + cfg.Basic.WasmPluginPath = bfe_util.ConfPathProc(cfg.Basic.WasmPluginPath, confRoot) + + if cfg.Basic.DataPath == "" { + log.Logger.Warn("ModWasm.DataPath not set, use default value") + cfg.Basic.WasmPluginPath = "mod_wasm/wasm.data" + } + cfg.Basic.DataPath = bfe_util.ConfPathProc(cfg.Basic.DataPath, confRoot) + + return nil +} diff --git a/bfe_modules/mod_wasmplugin/mod_wasmplugin.go b/bfe_modules/mod_wasmplugin/mod_wasmplugin.go new file mode 100644 index 000000000..ecd46dd09 --- /dev/null +++ b/bfe_modules/mod_wasmplugin/mod_wasmplugin.go @@ -0,0 +1,224 @@ +// Copyright (c) 2024 The BFE Authors. +// +// 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 mod_wasmplugin + +import ( + "fmt" + "net/url" + + _ "github.com/baidu/go-lib/log" + "github.com/baidu/go-lib/web-monitor/web_monitor" + "github.com/bfenetworks/bfe/bfe_basic" + "github.com/bfenetworks/bfe/bfe_http" + "github.com/bfenetworks/bfe/bfe_module" + "github.com/bfenetworks/bfe/bfe_wasmplugin" +) + +const ( + ModWasm = "mod_wasm" + ModWasmBeforeLocationKey = "mod_wasm_before_location_key" +) + +var ( + openDebug = false +) + +type ModuleWasm struct { + name string + wasmPluginPath string // path of Wasm plugins + configPath string // path of config file + pluginTable *PluginTable +} + +func NewModuleWasm() *ModuleWasm { + m := new(ModuleWasm) + m.name = ModWasm + m.pluginTable = NewPluginTable() + return m +} + +func (m *ModuleWasm) Name() string { + return m.name +} + +func (m *ModuleWasm) loadConfData(query url.Values) error { + path := query.Get("path") + if path == "" { + path = m.configPath + } + + // load from config file + conf, err := pluginConfLoad(path) + + if err != nil { + return fmt.Errorf("err in pluginConfLoad(%s):%s", path, err.Error()) + } + + // update to plugin table + return updatePluginConf(m.pluginTable, conf, m.wasmPluginPath) +} + +func (m *ModuleWasm) Init(cbs *bfe_module.BfeCallbacks, whs *web_monitor.WebHandlers, + cr string) error { + var err error + var conf *ConfModWasm + + confPath := bfe_module.ModConfPath(cr, m.name) + if conf, err = ConfLoad(confPath, cr); err != nil { + return fmt.Errorf("%s: cond load err %s", m.name, err.Error()) + } + + // init wasm engine + + return m.init(conf, cbs, whs) +} + +func (m *ModuleWasm) init(cfg *ConfModWasm, cbs *bfe_module.BfeCallbacks, + whs *web_monitor.WebHandlers) error { + openDebug = cfg.Log.OpenDebug + + m.wasmPluginPath = cfg.Basic.WasmPluginPath + m.configPath = cfg.Basic.DataPath + + // load plugins from WasmPluginPath + // construct plugin list + if err := m.loadConfData(nil); err != nil { + return fmt.Errorf("err in loadConfData(): %s", err.Error()) + } + + // register handler + err := cbs.AddFilter(bfe_module.HandleBeforeLocation, m.wasmBeforeLocationHandler) + if err != nil { + return fmt.Errorf("%s.Init(): AddFilter(m.wasmBeforeLocationHandler): %s", m.name, err.Error()) + } + + // register handler + err = cbs.AddFilter(bfe_module.HandleFoundProduct, m.wasmRequestHandler) + if err != nil { + return fmt.Errorf("%s.Init(): AddFilter(m.HandleFoundProduct): %s", m.name, err.Error()) + } + + // register handler + err = cbs.AddFilter(bfe_module.HandleReadResponse, m.wasmResponseHandler) + if err != nil { + return fmt.Errorf("%s.Init(): AddFilter(m.HandleReadResponse): %s", m.name, err.Error()) + } + + // register web handler for reload + err = whs.RegisterHandler(web_monitor.WebHandleReload, m.name, m.loadConfData) + if err != nil { + return fmt.Errorf("%s.Init(): RegisterHandler(m.loadConfData): %s", m.name, err.Error()) + } + + return nil +} + +// +func (m *ModuleWasm) wasmBeforeLocationHandler(request *bfe_basic.Request) (int, *bfe_http.Response) { + var pl []bfe_wasmplugin.WasmPlugin + rl := m.pluginTable.GetBeforeLocationRules() + for _, rule := range rl { + if rule.Cond.Match(request) { + // find pluginlist + pl = rule.PluginList + break + } + } + + var resp *bfe_http.Response + if pl != nil { + // do the pluginlist + retCode := bfe_module.BfeHandlerGoOn + var fl []*bfe_wasmplugin.Filter + for _, plug := range pl { + filter := bfe_wasmplugin.NewFilter(plug, request) + var ret int + ret, resp = filter.RequestHandler(request) + fl = append(fl, filter) + if ret != bfe_module.BfeHandlerGoOn { + retCode = ret + break + } + } + + request.Context[ModWasmBeforeLocationKey] = fl + return retCode, resp + } + + return bfe_module.BfeHandlerGoOn, resp +} + +// +func (m *ModuleWasm) wasmRequestHandler(request *bfe_basic.Request) (int, *bfe_http.Response) { + var pl []bfe_wasmplugin.WasmPlugin + rl, _ := m.pluginTable.Search(request.Route.Product) + for _, rule := range rl { + if rule.Cond.Match(request) { + // find pluginlist + pl = rule.PluginList + break + } + } + + var resp *bfe_http.Response + if pl != nil { + // do the pluginlist + retCode := bfe_module.BfeHandlerGoOn + var fl []*bfe_wasmplugin.Filter + for _, plug := range pl { + filter := bfe_wasmplugin.NewFilter(plug, request) + var ret int + ret, resp = filter.RequestHandler(request) + fl = append(fl, filter) + if ret != bfe_module.BfeHandlerGoOn { + retCode = ret + break + } + } + + var fl0 []*bfe_wasmplugin.Filter + val, ok := request.Context[ModWasmBeforeLocationKey] + if ok { + fl0 = val.([]*bfe_wasmplugin.Filter) + } + + fl0 = append(fl0, fl...) + request.Context[ModWasmBeforeLocationKey] = fl0 + return retCode, resp + } + + return bfe_module.BfeHandlerGoOn, resp +} + +// +func (m *ModuleWasm) wasmResponseHandler(request *bfe_basic.Request, res *bfe_http.Response) int { + val, ok := request.Context[ModWasmBeforeLocationKey] + + if ok { + fl, matched := val.([]*bfe_wasmplugin.Filter) + if !matched { + // error + return bfe_module.BfeHandlerGoOn + } + + n := len(fl) + for i := n-1; i >= 0; i-- { + fl[i].ResponseHandler(request) + fl[i].OnDestroy() + } + } + + return bfe_module.BfeHandlerGoOn +} diff --git a/bfe_modules/mod_wasmplugin/plugin_rule_load.go b/bfe_modules/mod_wasmplugin/plugin_rule_load.go new file mode 100644 index 000000000..30c36eb76 --- /dev/null +++ b/bfe_modules/mod_wasmplugin/plugin_rule_load.go @@ -0,0 +1,211 @@ +// Copyright (c) 2024 The BFE Authors. +// +// 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 mod_wasmplugin + +import ( + "fmt" + "os" + "path" + + "github.com/bfenetworks/bfe/bfe_basic/condition" + "github.com/bfenetworks/bfe/bfe_util/json" + "github.com/bfenetworks/bfe/bfe_wasmplugin" +) + +type PluginConfFile struct { + Version *string // version of the config + BeforeLocationRules *[]FilterRuleFile // rule list for BeforeLocation + FoundProductRules *map[string][]FilterRuleFile // product --> rule list for FoundProduct + PluginMap *map[string]PluginMeta +} + +type FilterRuleFile struct { + Cond *string // condition for plugin + PluginList *[]string +} + +type PluginMeta struct { + Name string + WasmVersion string + ConfVersion string + InstanceNum int + Product string +} + +type FilterRule struct { + Cond condition.Condition // condition for plugin + PluginList []bfe_wasmplugin.WasmPlugin +} + +type RuleList []FilterRule +type ProductRules map[string]RuleList // product => list of filter rules + +type PluginMap map[string]bfe_wasmplugin.WasmPlugin + +func buildRuleList(rules []FilterRuleFile, pluginMap PluginMap) (RuleList, error) { + var rulelist RuleList + + for _, r := range rules { + rule := FilterRule{} + cond, err := condition.Build(*r.Cond) + if err != nil { + return nil, err + } + + rule.Cond =cond + + for _, pn := range *r.PluginList { + plug := pluginMap[pn] + if plug == nil { + return nil, fmt.Errorf("unknown plugin: %s", pn) + } + rule.PluginList = append(rule.PluginList, plug) + } + + rulelist = append(rulelist, rule) + } + + return rulelist, nil +} + +func buildNewPluginMap(conf *map[string]PluginMeta, pmOld PluginMap, + pluginPath string) (pmNew PluginMap, unchanged map[string]bool, err error) { + + pmNew = PluginMap{} + unchanged = map[string]bool{} + + if conf != nil { + for pn, p := range *conf { + plugOld := pmOld[pn] + // check whether plugin version changed. + if plugOld != nil { + configOld := plugOld.GetConfig() + if configOld.WasmVersion == p.WasmVersion && configOld.ConfigVersion == p.ConfVersion { + // not change, just copy to new map + pmNew[pn] = plugOld + + // grow instance num if needed + if p.InstanceNum > plugOld.InstanceNum() { + actual := plugOld.EnsureInstanceNum(p.InstanceNum) + if actual != p.InstanceNum { + err = fmt.Errorf("can not EnsureInstanceNum, plugin:%s, num:%d", pn, p.InstanceNum) + return + } + } + + unchanged[pn] = true + continue + } + } + // if changed, construct a new plugin. + wasmconf := bfe_wasmplugin.WasmPluginConfig { + PluginName: pn, + WasmVersion: p.WasmVersion, + ConfigVersion: p.ConfVersion, + InstanceNum: p.InstanceNum, + Path: path.Join(pluginPath, pn), + } + plug, err1 := bfe_wasmplugin.NewWasmPlugin(wasmconf) + if err1 != nil { + // build plugin error + err = err1 + return + } + + pmNew[pn] = plug + } + } + + return +} + +func cleanPlugins(pm PluginMap, unchanged map[string]bool, conf *map[string]PluginMeta) { + for pn, plug := range pm { + if unchanged[pn] { + // shink instance num if needed + confnum := (*conf)[pn].InstanceNum + if plug.InstanceNum() > confnum { + plug.EnsureInstanceNum(confnum) + } + } else { + // stop plug + plug.OnPluginDestroy() + plug.Clear() + } + } +} + +func updatePluginConf(t *PluginTable, conf PluginConfFile, pluginPath string) error { + if conf.Version != nil && *conf.Version != t.GetVersion() { + + // 1. check plugin map + pm := t.GetPluginMap() + pluginMapNew, unchanged, err := buildNewPluginMap(conf.PluginMap, pm, pluginPath) + if err != nil { + return err + } + + // 2. construct product rules + var beforeLocationRulesNew RuleList + if conf.BeforeLocationRules != nil { + if rulelist, err := buildRuleList(*conf.BeforeLocationRules, pluginMapNew); err == nil { + beforeLocationRulesNew = rulelist + } else { + return err + } + } + + productRulesNew := make(ProductRules) + if conf.FoundProductRules != nil { + for product, rules := range *conf.FoundProductRules { + if rulelist, err := buildRuleList(rules, pluginMapNew); err == nil { + productRulesNew[product] = rulelist + } else { + return err + } + } + } + + // 3. update PluginTable + t.Update(*conf.Version, beforeLocationRulesNew, productRulesNew, pluginMapNew) + + // 4. stop & clean old plugins + cleanPlugins(pm, unchanged, conf.PluginMap) + } + return nil +} + +func pluginConfLoad(filename string) (PluginConfFile, error) { + var conf PluginConfFile + + /* open the file */ + file, err := os.Open(filename) + + if err != nil { + return conf, err + } + + /* decode the file */ + decoder := json.NewDecoder(file) + + err = decoder.Decode(&conf) + file.Close() + + if err != nil { + return conf, err + } + + return conf, nil +} diff --git a/bfe_modules/mod_wasmplugin/plugin_table.go b/bfe_modules/mod_wasmplugin/plugin_table.go new file mode 100644 index 000000000..53273ee9a --- /dev/null +++ b/bfe_modules/mod_wasmplugin/plugin_table.go @@ -0,0 +1,72 @@ +// Copyright (c) 2024 The BFE Authors. +// +// 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 mod_wasmplugin + +import ( + "sync" +) + +type PluginTable struct { + lock sync.RWMutex + version string + beforeLocationRules RuleList + productRules ProductRules + pluginMap PluginMap +} + +func NewPluginTable() *PluginTable { + t := new(PluginTable) + t.productRules = make(ProductRules) + t.pluginMap = make(PluginMap) + return t +} + +func (t *PluginTable) Update(version string, beforeLocationRules RuleList, productRules ProductRules, pluginMap PluginMap) { + t.lock.Lock() + + t.version = version + t.beforeLocationRules = beforeLocationRules + t.productRules = productRules + t.pluginMap = pluginMap + + t.lock.Unlock() +} + +func (t *PluginTable) GetVersion() string { + defer t.lock.RUnlock() + t.lock.RLock() + return t.version +} + +func (t *PluginTable) GetPluginMap() PluginMap { + defer t.lock.RUnlock() + t.lock.RLock() + return t.pluginMap +} + +func (t *PluginTable) GetBeforeLocationRules() RuleList { + defer t.lock.RUnlock() + t.lock.RLock() + return t.beforeLocationRules +} + +func (t *PluginTable) Search(product string) (RuleList, bool) { + t.lock.RLock() + productRules := t.productRules + t.lock.RUnlock() + + rules, ok := productRules[product] + return rules, ok +} diff --git a/bfe_wasmplugin/abi/proxywasm010/factory.go b/bfe_wasmplugin/abi/proxywasm010/factory.go new file mode 100644 index 000000000..3cbf866ed --- /dev/null +++ b/bfe_wasmplugin/abi/proxywasm010/factory.go @@ -0,0 +1,27 @@ +// Copyright (c) 2024 The BFE Authors. +// +// 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 proxywasm010 + +import ( + "github.com/bfenetworks/proxy-wasm-go-host/proxywasm/common" + proxywasm "github.com/bfenetworks/proxy-wasm-go-host/proxywasm/v1" +) + +func ABIContextFactory(instance common.WasmInstance) proxywasm.ContextHandler { + return &proxywasm.ABIContext{ + Imports: &DefaultImportsHandler{Instance: instance}, + Instance: instance, + } +} diff --git a/bfe_wasmplugin/abi/proxywasm010/imports.go b/bfe_wasmplugin/abi/proxywasm010/imports.go new file mode 100644 index 000000000..52d0b9a1d --- /dev/null +++ b/bfe_wasmplugin/abi/proxywasm010/imports.go @@ -0,0 +1,198 @@ +// Copyright (c) 2024 The BFE Authors. +// +// 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 proxywasm010 + +import ( + "bytes" + "io/ioutil" + + "net/http" + "net/url" + "sync/atomic" + "time" + + "github.com/baidu/go-lib/log" + "github.com/bfenetworks/bfe/bfe_http" + "github.com/bfenetworks/proxy-wasm-go-host/proxywasm/common" + proxywasm "github.com/bfenetworks/proxy-wasm-go-host/proxywasm/v1" +) + +type DefaultImportsHandler struct { + proxywasm.DefaultImportsHandler + Instance common.WasmInstance + hc *httpCallout +} + +// override +func (d *DefaultImportsHandler) Log(level proxywasm.LogLevel, msg string) proxywasm.WasmResult { + logFunc := log.Logger.Info + + switch level { + case proxywasm.LogLevelTrace: + logFunc = log.Logger.Trace + case proxywasm.LogLevelDebug: + logFunc = log.Logger.Debug + case proxywasm.LogLevelInfo: + logFunc = log.Logger.Info + case proxywasm.LogLevelWarn: + logFunc = func(arg0 interface{}, args ...interface{}) { + log.Logger.Warn(arg0, args...) + } + case proxywasm.LogLevelError: + logFunc = func(arg0 interface{}, args ...interface{}) { + log.Logger.Error(arg0, args...) + } + case proxywasm.LogLevelCritical: + logFunc = func(arg0 interface{}, args ...interface{}) { + log.Logger.Error(arg0, args...) + } + } + + logFunc(msg) + + return proxywasm.WasmResultOk +} + +var httpCalloutID int32 + +type httpCallout struct { + id int32 + d *DefaultImportsHandler + instance common.WasmInstance + abiContext *proxywasm.ABIContext + + urlString string + client *http.Client + req *http.Request + resp *http.Response + respHeader common.HeaderMap + respBody common.IoBuffer + reqOnFly bool +} + +// override +func (d *DefaultImportsHandler) HttpCall(reqURL string, header common.HeaderMap, body common.IoBuffer, + trailer common.HeaderMap, timeoutMilliseconds int32) (int32, proxywasm.WasmResult) { + u, err := url.Parse(reqURL) + if err != nil { + log.Logger.Error("[proxywasm010][imports] HttpCall fail to parse url, err: %v, reqURL: %v", err, reqURL) + return 0, proxywasm.WasmResultBadArgument + } + + calloutID := atomic.AddInt32(&httpCalloutID, 1) + + d.hc = &httpCallout{ + id: calloutID, + d: d, + instance: d.Instance, + abiContext: d.Instance.GetData().(*proxywasm.ABIContext), + urlString: reqURL, + } + + d.hc.client = &http.Client{Timeout: time.Millisecond * time.Duration(timeoutMilliseconds)} + + d.hc.req, err = http.NewRequest(http.MethodGet, u.String(), bytes.NewReader(body.Bytes())) + if err != nil { + log.Logger.Error("[proxywasm010][imports] HttpCall fail to create http req, err: %v, reqURL: %v", err, reqURL) + return 0, proxywasm.WasmResultInternalFailure + } + + header.Range(func(key, value string) bool { + d.hc.req.Header.Add(key, value) + return true + }) + + d.hc.reqOnFly = true + + return calloutID, proxywasm.WasmResultOk +} + +// override +func (d *DefaultImportsHandler) Wait() proxywasm.Action { + if d.hc == nil || !d.hc.reqOnFly { + return proxywasm.ActionContinue + } + + // release the instance lock and do sync http req + d.Instance.Unlock() + resp, err := d.hc.client.Do(d.hc.req) + d.Instance.Lock(d.hc.abiContext) + + d.hc.reqOnFly = false + + if err != nil { + log.Logger.Error("[proxywasm010][imports] HttpCall id: %v fail to do http req, err: %v, reqURL: %v", + d.hc.id, err, d.hc.urlString) + return proxywasm.ActionPause + } + d.hc.resp = resp + + // process http resp header + // var respHeader common.HeaderMap = protocol.CommonHeader{} + // for key, _ := range resp.Header { + // respHeader.Set(key, resp.Header.Get(key)) + // } + d.hc.respHeader = HeaderMapWrapper{Header: bfe_http.Header(resp.Header)} + + // process http resp body + var respBody common.IoBuffer + respBodyLen := 0 + + respBodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Logger.Error("[proxywasm010][imports] HttpCall id: %v fail to read bytes from resp body, err: %v, reqURL: %v", + d.hc.id, err, d.hc.urlString) + } + + err = resp.Body.Close() + if err != nil { + log.Logger.Error("[proxywasm010][imports] HttpCall id: %v fail to close resp body, err: %v, reqURL: %v", + d.hc.id, err, d.hc.urlString) + } + + if respBodyBytes != nil { + respBody = common.NewIoBufferBytes(respBodyBytes) + respBodyLen = respBody.Len() + } + d.hc.respBody = respBody + + // call proxy_on_http_call_response + rootContextID := d.hc.abiContext.Imports.GetRootContextID() + exports := d.hc.abiContext.GetExports() + + err = exports.ProxyOnHttpCallResponse(rootContextID, d.hc.id, int32(len(resp.Header)), int32(respBodyLen), 0) + if err != nil { + log.Logger.Error("[proxywasm010][imports] httpCall id: %v fail to call ProxyOnHttpCallResponse, err: %v", d.hc.id, err) + } + return proxywasm.ActionContinue +} + +// override +func (d *DefaultImportsHandler) GetHttpCallResponseHeaders() common.HeaderMap { + if d.hc != nil && d.hc.respHeader != nil { + return d.hc.respHeader + } + + return nil +} + +// override +func (d *DefaultImportsHandler) GetHttpCallResponseBody() common.IoBuffer { + if d.hc != nil && d.hc.respBody != nil { + return d.hc.respBody + } + + return nil +} diff --git a/bfe_wasmplugin/abi/proxywasm010/shim.go b/bfe_wasmplugin/abi/proxywasm010/shim.go new file mode 100644 index 000000000..ff895a92e --- /dev/null +++ b/bfe_wasmplugin/abi/proxywasm010/shim.go @@ -0,0 +1,55 @@ +// Copyright (c) 2024 The BFE Authors. +// +// 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 proxywasm010 + +import ( + "github.com/bfenetworks/bfe/bfe_http" + "github.com/bfenetworks/proxy-wasm-go-host/proxywasm/common" +) + +// HeaderMapWrapper wraps api.HeaderMap into proxy-wasm-go-host/common.HeaderMap +// implement common.HeaderMap +type HeaderMapWrapper struct { + bfe_http.Header +} + +// Override +func (h HeaderMapWrapper) Get(key string) (string, bool) { + s := h.Header.Get(key) + if s == "" { + return "", false + } else { + return s, true + } +} + +func (h HeaderMapWrapper) Range(f func(key, value string) bool) { + stopped := false + for k, v := range h.Header { + if stopped { + return + } + stopped = !f(k, v[0]) + } +} + +func (h HeaderMapWrapper) ByteSize() uint64 { + // TODO: to implement + return 0 +} + +func (h HeaderMapWrapper) Clone() common.HeaderMap { + return &HeaderMapWrapper{h.Header} +} diff --git a/bfe_wasmplugin/abi/registry.go b/bfe_wasmplugin/abi/registry.go new file mode 100644 index 000000000..05e9b3f2a --- /dev/null +++ b/bfe_wasmplugin/abi/registry.go @@ -0,0 +1,38 @@ +// Copyright (c) 2024 The BFE Authors. +// +// 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 abi + +import ( + "github.com/baidu/go-lib/log" + "github.com/bfenetworks/bfe/bfe_wasmplugin/abi/proxywasm010" + "github.com/bfenetworks/proxy-wasm-go-host/proxywasm/common" + proxywasm "github.com/bfenetworks/proxy-wasm-go-host/proxywasm/v1" +) + +func GetABIList(instance common.WasmInstance) []proxywasm.ContextHandler { + if instance == nil { + log.Logger.Error("[abi][registry] GetABIList nil instance: %v", instance) + return nil + } + + res := make([]proxywasm.ContextHandler, 0) + + abiNameList := instance.GetModule().GetABINameList() + if len(abiNameList) > 0 { + res = append(res, proxywasm010.ABIContextFactory(instance)) + } + + return res +} diff --git a/bfe_wasmplugin/adapter.go b/bfe_wasmplugin/adapter.go new file mode 100644 index 000000000..50f6c2df4 --- /dev/null +++ b/bfe_wasmplugin/adapter.go @@ -0,0 +1,109 @@ +// Copyright (c) 2024 The BFE Authors. +// +// 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 bfe_wasmplugin + +import ( + "bytes" + "io/ioutil" + + "github.com/bfenetworks/bfe/bfe_http" + "github.com/bfenetworks/bfe/bfe_wasmplugin/abi/proxywasm010" + "github.com/bfenetworks/proxy-wasm-go-host/proxywasm/common" + v1Host "github.com/bfenetworks/proxy-wasm-go-host/proxywasm/v1" +) + +// v1 Imports +type v1Imports struct { + proxywasm010.DefaultImportsHandler + plugin WasmPlugin + filter *Filter +} + +func (v1 *v1Imports) GetRootContextID() int32 { + return v1.plugin.GetRootContextID() +} + +func (v1 *v1Imports) GetVmConfig() common.IoBuffer { + return common.NewIoBufferBytes([]byte{}) +} + +func (v1 *v1Imports) GetPluginConfig() common.IoBuffer { + return common.NewIoBufferBytes(v1.plugin.GetPluginConfig()) +} + +func (v1 *v1Imports) GetHttpRequestHeader() common.HeaderMap { + if v1.filter.request == nil { + return nil + } + + return &proxywasm010.HeaderMapWrapper{Header: v1.filter.request.HttpRequest.Header} +} + +func (v1 *v1Imports) GetHttpRequestBody() common.IoBuffer { + if v1.filter.request == nil { + return nil + } + + return nil +} + +func (v1 *v1Imports) GetHttpRequestTrailer() common.HeaderMap { + if v1.filter.request == nil { + return nil + } + + return &proxywasm010.HeaderMapWrapper{Header: v1.filter.request.HttpRequest.Trailer} +} + +func (v1 *v1Imports) GetHttpResponseHeader() common.HeaderMap { + if v1.filter.request == nil || v1.filter.request.HttpResponse == nil { + return nil + } + + return &proxywasm010.HeaderMapWrapper{Header: v1.filter.request.HttpResponse.Header} +} + +func (v1 *v1Imports) GetHttpResponseBody() common.IoBuffer { + if v1.filter.request == nil || v1.filter.request.HttpResponse == nil { + return nil + } + + return nil +} + +func (v1 *v1Imports) GetHttpResponseTrailer() common.HeaderMap { + if v1.filter.request == nil || v1.filter.request.HttpResponse == nil { + return nil + } + + return &proxywasm010.HeaderMapWrapper{Header: v1.filter.request.HttpResponse.Trailer} +} + +func (v1 *v1Imports) SendHttpResp(respCode int32, respCodeDetail common.IoBuffer, respBody common.IoBuffer, additionalHeaderMap common.HeaderMap, grpcCode int32) v1Host.WasmResult { + resp := &bfe_http.Response{ + StatusCode: int(respCode), + Status: string(respCodeDetail.Bytes()), + Body: ioutil.NopCloser(bytes.NewReader(respBody.Bytes())), + Header: make(bfe_http.Header), + } + + additionalHeaderMap.Range(func(key, value string) bool { + resp.Header.Add(key, value) + return true + }) + + v1.filter.request.HttpResponse = resp + return v1Host.WasmResultOk +} diff --git a/bfe_wasmplugin/engine.go b/bfe_wasmplugin/engine.go new file mode 100644 index 000000000..ade6f1241 --- /dev/null +++ b/bfe_wasmplugin/engine.go @@ -0,0 +1,26 @@ +// Copyright (c) 2024 The BFE Authors. +// +// 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 bfe_wasmplugin + +import ( + "github.com/bfenetworks/proxy-wasm-go-host/proxywasm/common" + "github.com/bfenetworks/proxy-wasm-go-host/wazero" +) + +var defaultEngine = wazero.NewVM() + +func GetWasmEngine() common.WasmVM { + return defaultEngine +} diff --git a/bfe_wasmplugin/filter.go b/bfe_wasmplugin/filter.go new file mode 100644 index 000000000..183187cea --- /dev/null +++ b/bfe_wasmplugin/filter.go @@ -0,0 +1,145 @@ +// Copyright (c) 2024 The BFE Authors. +// +// 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 bfe_wasmplugin + +import ( + "sync" + "sync/atomic" + + "github.com/bfenetworks/bfe/bfe_basic" + "github.com/bfenetworks/bfe/bfe_http" + "github.com/bfenetworks/bfe/bfe_module" + + "github.com/baidu/go-lib/log" + wasmABI "github.com/bfenetworks/bfe/bfe_wasmplugin/abi" + "github.com/bfenetworks/proxy-wasm-go-host/proxywasm/common" + v1Host "github.com/bfenetworks/proxy-wasm-go-host/proxywasm/v1" +) + +type Filter struct { + plugin WasmPlugin + instance common.WasmInstance + abi v1Host.ContextHandler + exports v1Host.Exports + + rootContextID int32 + contextID int32 + + request *bfe_basic.Request + + destroyOnce sync.Once +} + +var contextIDGenerator int32 + +func newContextID(rootContextID int32) int32 { + for { + id := atomic.AddInt32(&contextIDGenerator, 1) + if id != rootContextID { + return id + } + } +} + +func NewFilter(plugin WasmPlugin, request *bfe_basic.Request) *Filter { + instance := plugin.GetInstance() + rootContextID := plugin.GetRootContextID() + + filter := &Filter{ + plugin: plugin, + instance: instance, + rootContextID: rootContextID, + contextID: newContextID(rootContextID), + request: request, + } + + filter.abi = wasmABI.GetABIList(instance)[0] + log.Logger.Info("[proxywasm][filter] abi version: %v", filter.abi.Name()) + if filter.abi != nil { + // v1 + imports := &v1Imports{plugin: plugin, filter: filter} + imports.DefaultImportsHandler.Instance = instance + filter.abi.SetImports(imports) + filter.exports = filter.abi.GetExports() + } else { + log.Logger.Error("[proxywasm][filter] unknown abi list: %v", filter.abi) + return nil + } + + filter.instance.Lock(filter.abi) + defer filter.instance.Unlock() + + err := filter.exports.ProxyOnContextCreate(filter.contextID, filter.rootContextID) + if err != nil { + log.Logger.Error("[proxywasm][filter] NewFilter fail to create context id: %v, rootContextID: %v, err: %v", + filter.contextID, filter.rootContextID, err) + return nil + } + + return filter +} + +func (f *Filter) OnDestroy() { + f.destroyOnce.Do(func() { + f.instance.Lock(f.abi) + + _, err := f.exports.ProxyOnDone(f.contextID) + if err != nil { + log.Logger.Error("[proxywasm][filter] OnDestroy fail to call ProxyOnDone, err: %v", err) + } + + err = f.exports.ProxyOnDelete(f.contextID) + if err != nil { + // warn instead of error as some proxy_abi_version_0_1_0 wasm don't + // export proxy_on_delete + log.Logger.Warn("[proxywasm][filter] OnDestroy fail to call ProxyOnDelete, err: %v", err) + } + + f.instance.Unlock() + f.plugin.ReleaseInstance(f.instance) + }) +} + +func (f *Filter) RequestHandler(request *bfe_basic.Request) (int, *bfe_http.Response) { + f.instance.Lock(f.abi) + defer f.instance.Unlock() + + action, err := f.exports.ProxyOnRequestHeaders(f.contextID, int32(len(request.HttpRequest.Header)), 0) + if err != nil { + log.Logger.Error("[proxywasm][filter][v1] ProxyOnRequestHeaders action: %v, err: %v", action, err) + } + + status := bfe_module.BfeHandlerGoOn + if f.request.HttpResponse != nil { + status = bfe_module.BfeHandlerResponse + } + return status, f.request.HttpResponse +} + +func (f *Filter) ResponseHandler(request *bfe_basic.Request) (int, *bfe_http.Response) { + f.instance.Lock(f.abi) + defer f.instance.Unlock() + + action, err := f.exports.ProxyOnResponseHeaders(f.contextID, int32(len(request.HttpResponse.Header)), 0) + if err != nil { + log.Logger.Error("[proxywasm][filter][v1] ProxyOnResponseHeaders action: %v, err: %v", action, err) + } + + status := bfe_module.BfeHandlerGoOn + if f.request.HttpResponse != nil { + status = bfe_module.BfeHandlerResponse + } + return status, f.request.HttpResponse +} diff --git a/bfe_wasmplugin/plugin.go b/bfe_wasmplugin/plugin.go new file mode 100644 index 000000000..83ad2e6ba --- /dev/null +++ b/bfe_wasmplugin/plugin.go @@ -0,0 +1,394 @@ +// Copyright (c) 2024 The BFE Authors. +// +// 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 bfe_wasmplugin + +import ( + "crypto/md5" + "encoding/hex" + "errors" + "os" + "path" + "runtime" + "strings" + "sync" + "sync/atomic" + + "github.com/baidu/go-lib/log" + wasmABI "github.com/bfenetworks/bfe/bfe_wasmplugin/abi" + "github.com/bfenetworks/proxy-wasm-go-host/proxywasm/common" + v1Host "github.com/bfenetworks/proxy-wasm-go-host/proxywasm/v1" +) + +var ( + ErrEngineNotFound = errors.New("fail to get wasm engine") + ErrWasmBytesLoad = errors.New("fail to load wasm bytes") + ErrWasmBytesIncorrect = errors.New("incorrect hash of wasm bytes") + ErrConfigFileLoad = errors.New("fail to load config file") + ErrMd5FileLoad = errors.New("fail to load md5 file") + ErrInstanceCreate = errors.New("fail to create wasm instance") + ErrModuleCreate = errors.New("fail to create wasm module") +) + +type WasmPluginConfig struct { + PluginName string `json:"plugin_name,omitempty"` + Path string `json:"path,omitempty"` + Md5 string `json:"md5,omitempty"` + WasmVersion string + ConfigVersion string + InstanceNum int `json:"instance_num,omitempty"` +} + +// WasmPlugin manages the collection of wasm plugin instances +type WasmPlugin interface { + // PluginName returns the name of wasm plugin + PluginName() string + + // GetPluginConfig returns the config of wasm plugin + GetPluginConfig() []byte + + // GetPluginConfig returns the config of wasm plugin + GetConfig() WasmPluginConfig + + // EnsureInstanceNum tries to expand/shrink the num of instance to 'num' + // and returns the actual instance num + EnsureInstanceNum(num int) int + + // InstanceNum returns the current number of wasm instance + InstanceNum() int + + // GetInstance returns one plugin instance of the plugin + GetInstance() common.WasmInstance + + // ReleaseInstance releases the instance to the plugin + ReleaseInstance(instance common.WasmInstance) + + // Exec execute the f for each instance + Exec(f func(instance common.WasmInstance) bool) + + // Clear got called when the plugin is destroyed + Clear() + + // OnPluginStart got called when starting the wasm plugin + OnPluginStart() + + // OnPluginDestroy got called when destroying the wasm plugin + OnPluginDestroy() + + GetRootContextID() int32 +} + +type wasmPluginImpl struct { + config WasmPluginConfig + + lock sync.RWMutex + + instanceNum int32 + instances []common.WasmInstance + instancesIndex int32 + + occupy int32 + + vm common.WasmVM + wasmBytes []byte + module common.WasmModule + + pluginConfig []byte + rootContextID int32 +} + +// load wasm bytes +func loadWasmBytes(dir string, name string) (wasmBytes []byte, configBytes []byte, err error) { + wasmBytes, err = os.ReadFile(path.Join(dir, name + ".wasm")) + if err != nil || len(wasmBytes) == 0 { + // wasm file error + err = ErrWasmBytesLoad + return + } + + configBytes, err = os.ReadFile(path.Join(dir, name + ".conf")) + if err != nil { + // plugin config file error + err = ErrConfigFileLoad + return + } + + var md5File []byte + md5File, err = os.ReadFile(path.Join(dir, name + ".md5")) + if err != nil { + // md5 file error + err = ErrMd5FileLoad + return + } + md5str := "" + fields := strings.Fields(string(md5File)) + if len(fields) > 0 { + md5str = fields[0] + } + + md5Bytes := md5.Sum(wasmBytes) + newMd5 := hex.EncodeToString(md5Bytes[:]) + if newMd5 != md5str { + err = ErrWasmBytesIncorrect + return + } + + return +} + +func NewWasmPlugin(wasmConfig WasmPluginConfig) (WasmPlugin, error) { + // check instance num + instanceNum := wasmConfig.InstanceNum + if instanceNum <= 0 { + instanceNum = runtime.NumCPU() + } + + wasmConfig.InstanceNum = instanceNum + + // get wasm engine + vm := GetWasmEngine() + if vm == nil { + return nil, ErrEngineNotFound + } + + // load wasm bytes + wasmBytes, configBytes, err := loadWasmBytes(wasmConfig.Path, wasmConfig.PluginName) + if err != nil { + // wasm file error + return nil, err + } + + // create wasm module + module := vm.NewModule(wasmBytes) + if module == nil { + return nil, ErrModuleCreate + } + + plugin := &wasmPluginImpl{ + config: wasmConfig, + vm: vm, + wasmBytes: wasmBytes, + module: module, + pluginConfig: configBytes, + rootContextID: newContextID(0), + } + + // ensure instance num + actual := plugin.EnsureInstanceNum(wasmConfig.InstanceNum) + if actual == 0 { + return nil, ErrInstanceCreate + } + + return plugin, nil +} + +// reduce to n instances and return the cut-offs +func (w *wasmPluginImpl) cutInstance(n int) []common.WasmInstance { + w.lock.Lock() + defer w.lock.Unlock() + + oldcopy := make([]common.WasmInstance, w.InstanceNum() - n) + copy(oldcopy, w.instances[n:]) + w.instances = w.instances[:n] + atomic.StoreInt32(&w.instanceNum, int32(n)) + + return oldcopy +} + +func (w *wasmPluginImpl) appendInstance(newInstance []common.WasmInstance) { + w.lock.Lock() + defer w.lock.Unlock() + + w.instances = append(w.instances, newInstance...) + atomic.AddInt32(&w.instanceNum, int32(len(newInstance))) +} + +// EnsureInstanceNum try to expand/shrink the num of instance to 'num' +// and return the actual instance num. +func (w *wasmPluginImpl) EnsureInstanceNum(num int) int { + if num == w.InstanceNum() { + return w.InstanceNum() + } + + if num < w.InstanceNum() { + todel := w.cutInstance(num) + + // stop the cut-off instances + for _, instance := range todel { + instance.Stop() + } + } else { + newInstance := make([]common.WasmInstance, 0) + numToCreate := num - w.InstanceNum() + + for i := 0; i < numToCreate; i++ { + instance := w.module.NewInstance() + if instance == nil { + log.Logger.Error("[wasm][plugin] EnsureInstanceNum fail to create instance, i: %v", i) + continue + } + + // Instantiate any ABI needed by the guest. + abilist := wasmABI.GetABIList(instance) + if len(abilist) == 0 { + log.Logger.Error("[wasm][plugin] EnsureInstanceNum fail to get abilist, i: %v", i) + break + } + for _, abi := range abilist { + //abi.OnInstanceCreate(instance) + if err := instance.RegisterImports(abi.Name()); err != nil { + panic(err) + } + } + + err := instance.Start() + if err != nil { + log.Logger.Error("[wasm][plugin] EnsureInstanceNum fail to start instance, i: %v, err: %v", i, err) + continue + } + + if !w.OnInstanceStart(instance) { + log.Logger.Error("[wasm][plugin] EnsureInstanceNum fail on instance start, i: %v", i) + break + } + newInstance = append(newInstance, instance) + } + + w.appendInstance(newInstance) + } + + return w.InstanceNum() +} + +func (w *wasmPluginImpl) InstanceNum() int { + return int(atomic.LoadInt32(&w.instanceNum)) +} + +func (w *wasmPluginImpl) PluginName() string { + return w.config.PluginName +} + +func (w *wasmPluginImpl) Clear() { + // do nothing + log.Logger.Info("[wasm][plugin] Clear wasm plugin, config: %v, instanceNum: %v", w.config, w.instanceNum) + w.EnsureInstanceNum(0) + log.Logger.Info("[wasm][plugin] Clear wasm plugin done, config: %v, instanceNum: %v", w.config, w.instanceNum) +} + +// Exec execute the f for each instance. +func (w *wasmPluginImpl) Exec(f func(instance common.WasmInstance) bool) { + w.lock.RLock() + defer w.lock.RUnlock() + + for _, iw := range w.instances { + if !f(iw) { + break + } + } +} + +func (w *wasmPluginImpl) GetConfig() WasmPluginConfig { + return w.config +} + +func (w *wasmPluginImpl) GetPluginConfig() []byte { + return w.pluginConfig +} + +func (w *wasmPluginImpl) GetInstance() common.WasmInstance { + w.lock.RLock() + defer w.lock.RUnlock() + + for i := 0; i < len(w.instances); i++ { + idx := int(atomic.LoadInt32(&w.instancesIndex)) % len(w.instances) + atomic.AddInt32(&w.instancesIndex, 1) + + instance := w.instances[idx] + if !instance.Acquire() { + continue + } + + atomic.AddInt32(&w.occupy, 1) + return instance + } + + return nil +} + +func (w *wasmPluginImpl) ReleaseInstance(instance common.WasmInstance) { + instance.Release() + atomic.AddInt32(&w.occupy, -1) +} + +func (w *wasmPluginImpl) OnInstanceStart(instance common.WasmInstance) bool { + abilist := wasmABI.GetABIList(instance) + if len(abilist) == 0 { + log.Logger.Error("[proxywasm][factory] instance has no correct abi list") + return false + } + + abi := abilist[0] + var exports v1Host.Exports + if abi != nil { + // v1 + imports := &v1Imports{plugin: w} + imports.DefaultImportsHandler.Instance = instance + abi.SetImports(imports) + exports = abi.GetExports() + } else { + log.Logger.Error("[proxywasm][factory] unknown abi list: %v", abi) + return false + } + + instance.Lock(abi) + defer instance.Unlock() + + err := exports.ProxyOnContextCreate(w.rootContextID, 0) + if err != nil { + log.Logger.Error("[proxywasm][factory] OnPluginStart fail to create root context id, err: %v", err) + return true + } + + vmConfigSize := 0 + // no vm config + + _, err = exports.ProxyOnVmStart(w.rootContextID, int32(vmConfigSize)) + if err != nil { + log.Logger.Error("[proxywasm][factory] OnPluginStart fail to create root context id, err: %v", err) + return true + } + + pluginConfigSize := 0 + if pluginConfigBytes := w.GetPluginConfig(); pluginConfigBytes != nil { + pluginConfigSize = len(pluginConfigBytes) + } + + _, err = exports.ProxyOnConfigure(w.rootContextID, int32(pluginConfigSize)) + if err != nil { + log.Logger.Error("[proxywasm][factory] OnPluginStart fail to create root context id, err: %v", err) + return true + } + + return true +} + +func (w *wasmPluginImpl) OnPluginStart() { + // w.Exec(w.OnInstanceStart) +} + +func (d *wasmPluginImpl) OnPluginDestroy() {} + +func (w *wasmPluginImpl) GetRootContextID() int32 { + return w.rootContextID +} diff --git a/conf/bfe.conf b/conf/bfe.conf index 94a5fbd5b..9a4d043b2 100644 --- a/conf/bfe.conf +++ b/conf/bfe.conf @@ -61,6 +61,7 @@ Modules = mod_access Modules = mod_prison #Modules = mod_auth_request # Modules = mod_cors +Modules = mod_wasm # interval for get diff of proxy-state MonitorInterval = 20 diff --git a/conf/mod_wasm/mod_wasm.conf b/conf/mod_wasm/mod_wasm.conf new file mode 100644 index 000000000..7a00935bb --- /dev/null +++ b/conf/mod_wasm/mod_wasm.conf @@ -0,0 +1,7 @@ +[basic] +DataPath = mod_wasm/mod_wasm.data +WasmPluginPath=wasm_plugin/ + +[log] +OpenDebug=true + diff --git a/conf/mod_wasm/mod_wasm.data b/conf/mod_wasm/mod_wasm.data new file mode 100644 index 000000000..d491ce813 --- /dev/null +++ b/conf/mod_wasm/mod_wasm.data @@ -0,0 +1,21 @@ +{ + "Version": "5", + "BeforeLocationRules": [{ + "Cond": "req_path_prefix_in(\"/headers\", false)", + "PluginList": [ "headers" ] + }], + "ProductRules": { + "local_product": [{ + "Cond": "default_t()", + "PluginList": [] + }] + }, + "PluginMap": { + "headers": { + "Name": "headers", + "WasmVersion": "3", + "ConfVersion": "6", + "InstanceNum": 20 + } + } +} diff --git a/conf/server_data_conf/host_rule.data b/conf/server_data_conf/host_rule.data index 49ee4b352..734d675f7 100644 --- a/conf/server_data_conf/host_rule.data +++ b/conf/server_data_conf/host_rule.data @@ -6,11 +6,17 @@ "example.org", "fcgi.example.org", "h2c.example.org" + ], + "localTag":[ + "localhost" ] }, "HostTags": { "example_product":[ "exampleTag" + ], + "local_product":[ + "localTag" ] } } diff --git a/conf/wasm_plugin/headers/headers.conf b/conf/wasm_plugin/headers/headers.conf new file mode 100644 index 000000000..77d8544e5 --- /dev/null +++ b/conf/wasm_plugin/headers/headers.conf @@ -0,0 +1,4 @@ +{ + "header": "Wasm-Header-1", + "value": "Hello WasmPlugin!" +} diff --git a/conf/wasm_plugin/headers/headers.md5 b/conf/wasm_plugin/headers/headers.md5 new file mode 100644 index 000000000..fc2ba2dd2 --- /dev/null +++ b/conf/wasm_plugin/headers/headers.md5 @@ -0,0 +1 @@ +82f14b56c9114d420f4624443bd76ac0 diff --git a/conf/wasm_plugin/headers/headers.wasm b/conf/wasm_plugin/headers/headers.wasm new file mode 100755 index 000000000..767910bb8 Binary files /dev/null and b/conf/wasm_plugin/headers/headers.wasm differ diff --git a/go.mod b/go.mod index 4308053b1..d1b2abf87 100644 --- a/go.mod +++ b/go.mod @@ -6,11 +6,10 @@ toolchain go1.22.2 require ( github.com/abbot/go-http-auth v0.4.1-0.20181019201920-860ed7f246ff - github.com/andybalholm/brotli v1.0.0 + github.com/andybalholm/brotli v1.0.2 github.com/armon/go-radix v1.0.0 github.com/asergeyev/nradix v0.0.0-20170505151046-3872ab85bb56 // indirect github.com/baidu/go-lib v0.0.0-20200819072111-21df249f5e6a - github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect github.com/golang-jwt/jwt v3.2.2+incompatible github.com/gomodule/redigo v2.0.0+incompatible github.com/json-iterator/go v1.1.12 @@ -26,42 +25,48 @@ require ( github.com/spaolacci/murmur3 v1.1.0 github.com/stretchr/testify v1.9.0 github.com/tjfoc/gmsm v1.3.2 - github.com/uber/jaeger-client-go v2.22.1+incompatible - github.com/uber/jaeger-lib v2.2.0+incompatible + github.com/uber/jaeger-client-go v2.25.0+incompatible + github.com/uber/jaeger-lib v2.4.0+incompatible github.com/zmap/go-iptree v0.0.0-20170831022036-1948b1097e25 go.elastic.co/apm v1.11.0 go.elastic.co/apm/module/apmot v1.7.2 - go.uber.org/atomic v1.6.0 // indirect + go.uber.org/atomic v1.7.0 // indirect go.uber.org/automaxprocs v1.4.0 golang.org/x/crypto v0.25.0 golang.org/x/net v0.25.0 golang.org/x/sys v0.22.0 - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d gopkg.in/gcfg.v1 v1.2.3 gopkg.in/warnings.v0 v0.1.2 // indirect ) require ( + github.com/bfenetworks/proxy-wasm-go-host v0.0.0-20241202144118-62704e5df808 + github.com/go-jose/go-jose/v4 v4.0.4 +) + +require ( + github.com/HdrHistogram/hdrhistogram-go v1.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/elastic/go-sysinfo v1.1.1 // indirect github.com/elastic/go-windows v1.0.0 // indirect - github.com/go-jose/go-jose/v4 v4.0.4 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 // indirect - github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 // indirect github.com/oschwald/maxminddb-golang v1.6.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/procfs v0.0.3 // indirect + github.com/prometheus/procfs v0.2.0 // indirect github.com/santhosh-tekuri/jsonschema v1.2.4 // indirect + github.com/tetratelabs/wazero v1.2.1 // indirect go.elastic.co/apm/module/apmhttp v1.7.2 // indirect go.elastic.co/fastjson v1.1.0 // indirect - golang.org/x/sync v0.7.0 // indirect golang.org/x/text v0.16.0 // indirect google.golang.org/grpc v1.56.3 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect howett.net/plist v0.0.0-20181124034731-591f970eefbb // indirect ) + +// replace github.com/bfenetworks/proxy-wasm-go-host => ../proxy-wasm-go-host diff --git a/go.sum b/go.sum index d1f9c7151..6532d8530 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,13 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/HdrHistogram/hdrhistogram-go v1.0.1 h1:GX8GAYDuhlFQnI2fRDHQhTlkHMz8bEn0jTI6LJU0mpw= +github.com/HdrHistogram/hdrhistogram-go v1.0.1/go.mod h1:BWJ+nMSHY3L41Zj7CA3uXnloDp7xxV0YvstAE7nKTaM= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/abbot/go-http-auth v0.4.1-0.20181019201920-860ed7f246ff h1:9ZqcMQ0fB+ywKACVjGfZM4C7Uq9D5rq0iSmwIjX187k= github.com/abbot/go-http-auth v0.4.1-0.20181019201920-860ed7f246ff/go.mod h1:Cz6ARTIzApMJDzh5bRMSUou6UMSp0IEXg9km/ci7TJM= -github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4= -github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/andybalholm/brotli v1.0.2 h1:JKnhI/XQ75uFBTiuzXpzFrUriDPiZjlOSzh6wXogP0E= +github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asergeyev/nradix v0.0.0-20170505151046-3872ab85bb56 h1:Wi5Tgn8K+jDcBYL+dIMS1+qXYH2r7tpRAyBgqrWfQtw= @@ -14,9 +16,10 @@ github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuP github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/baidu/go-lib v0.0.0-20200819072111-21df249f5e6a h1:m/u39GNhkoUSC9WxTuM5hWShEqEfVioeXDiqiQd6tKg= github.com/baidu/go-lib v0.0.0-20200819072111-21df249f5e6a/go.mod h1:FneHDqz3wLeDGdWfRyW4CzBbCwaqesLGIFb09N80/ww= +github.com/bfenetworks/proxy-wasm-go-host v0.0.0-20241202144118-62704e5df808 h1:v0ckUMaZJFe8XvoM9x3kn+lDtMfI9EvpFadiOiV/s8A= +github.com/bfenetworks/proxy-wasm-go-host v0.0.0-20241202144118-62704e5df808/go.mod h1:VG3ZZ8Zg7dYkla2hHy9UsX0GLl/dgJYP4IxuPvoq+/U= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cucumber/godog v0.8.1 h1:lVb+X41I4YDreE+ibZ50bdXmySxgRviYFgKY6Aw4XE8= github.com/cucumber/godog v0.8.1/go.mod h1:vSh3r/lM+psC1BPXvdkSEuNjmXfpVqrMGYAElF6hxnA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -46,8 +49,9 @@ github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= @@ -63,20 +67,24 @@ github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8 github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/microcosm-cc/bluemonday v1.0.16 h1:kHmAq2t7WPWLjiGvzKa5o3HzSfahUKiOq7fAPUiMNIc= github.com/microcosm-cc/bluemonday v1.0.16/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM= github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg= github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -102,9 +110,12 @@ github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6J 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/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/santhosh-tekuri/jsonschema v1.2.4 h1:hNhW8e7t+H1vgY+1QeEQpveR6D4+OwKPXCfD2aieJis= @@ -115,18 +126,21 @@ github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0b github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tetratelabs/wazero v1.2.1 h1:J4X2hrGzJvt+wqltuvcSjHQ7ujQxA9gb6PeMs4qlUWs= +github.com/tetratelabs/wazero v1.2.1/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ= github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM= github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= -github.com/uber/jaeger-client-go v2.22.1+incompatible h1:NHcubEkVbahf9t3p75TOCR83gdUHXjRJvjoBh1yACsM= -github.com/uber/jaeger-client-go v2.22.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw= -github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U= +github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.4.0+incompatible h1:fY7QsGQWiCt8pajv4r7JEvmATdCVaWxXbjwyYwsNaLQ= +github.com/uber/jaeger-lib v2.4.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/zmap/go-iptree v0.0.0-20170831022036-1948b1097e25 h1:LRoXAcKX48QV4LV23W5ZtsG/MbJOgNUNvWiXwM0iLWw= github.com/zmap/go-iptree v0.0.0-20170831022036-1948b1097e25/go.mod h1:qOasALtPByO1Jk6LhgpNv6htPMK2QJfiGorUk57nO/U= @@ -140,20 +154,16 @@ go.elastic.co/apm/module/apmot v1.7.2/go.mod h1:VD2nUkebUPrP1hqIarimIEsoM9xyuK0l go.elastic.co/fastjson v1.0.0/go.mod h1:PmeUOMMtLHQr9ZS9J9owrAVg0FkaZDRZJEFTTGHtchs= go.elastic.co/fastjson v1.1.0 h1:3MrGBWWVIxe/xvsbpghtkFoPciPhOCmjsR/HfwEeQR4= go.elastic.co/fastjson v1.1.0/go.mod h1:boNGISWMjQsUPy/t6yqt2/1Wx4YNPSe+mZjlyw9vKKI= -go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/automaxprocs v1.4.0 h1:CpDZl6aOlLhReez+8S3eEotD7Jx0Os++lemPlMULQP0= go.uber.org/automaxprocs v1.4.0/go.mod h1:/mTEdr7LvHhs0v7mjdxDreTz1OG5zdZGqgOnhWiR/+Q= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -164,8 +174,6 @@ golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -174,8 +182,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -185,34 +191,27 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191025021431-6c3a3bfe00ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -220,15 +219,13 @@ google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3 h1:m8OOJ4ccYHnx2f4gQwpno8nAX5OGOh7RLaaz0pj3Ogs= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y= -gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= -gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=