Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wasm extension #1204

Merged
merged 10 commits into from
Dec 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ coverage.txt
.vscode/*
bfe
dist/*
conf/wasm_plugin

.DS_Store
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)

Expand Down
4 changes: 4 additions & 0 deletions bfe_modules/bfe_modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -131,6 +132,9 @@ var moduleList = []bfe_module.BfeModule{

// mod_access
mod_access.NewModuleAccess(),

// mod_wasm
mod_wasmplugin.NewModuleWasm(),
}

// init modules list
Expand Down
68 changes: 68 additions & 0 deletions bfe_modules/mod_wasmplugin/conf_mod_wasmplugin.go
Original file line number Diff line number Diff line change
@@ -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
}
224 changes: 224 additions & 0 deletions bfe_modules/mod_wasmplugin/mod_wasmplugin.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading
Loading