Skip to content

Commit

Permalink
Merge pull request apache#2 from dubbogo/0.2.0
Browse files Browse the repository at this point in the history
0.2.0

Former-commit-id: 38b49aa [formerly ffbd137]
Former-commit-id: 38b49aa
Former-commit-id: 7dac763
  • Loading branch information
ztelur authored Jan 21, 2021
2 parents fa07ee6 + fb12619 commit 73b661c
Show file tree
Hide file tree
Showing 10 changed files with 443 additions and 6 deletions.
26 changes: 25 additions & 1 deletion configs/api_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ resources:
- path: '/api/v1/test-dubbo/user'
type: restful
description: user
plugins:
groupNames:
- group2
pluginNames:
- rate limit
- access
methods:
- httpVerb: GET
onAir: true
Expand Down Expand Up @@ -35,4 +41,22 @@ resources:
host: 127.0.0.1:8889
path: /UserProvider/CreateUser
group: "test"
version: 1.0.0
version: 1.0.0
pluginFilePath: ""
pluginsGroup:
- groupName: "group1"
plugins:
- name: "rate limit"
version: "0.0.1"
priority: 1000
externalLookupName: "ExternalPluginRateLimit"
- name: "access"
version: "0.0.1"
priority: 1000
externalLookupName: "ExternalPluginAccess"
- groupName: "group2"
plugins:
- name: "blacklist"
version: "0.0.1"
priority: 1000
externalLookupName: "ExternalPluginBlackList"
31 changes: 27 additions & 4 deletions pkg/config/api_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,32 @@ const (

// APIConfig defines the data structure of the api gateway configuration
type APIConfig struct {
Name string `json:"name" yaml:"name"`
Description string `json:"description" yaml:"description"`
Resources []Resource `json:"resources" yaml:"resources"`
Definitions []Definition `json:"definitions" yaml:"definitions"`
Name string `json:"name" yaml:"name"`
Description string `json:"description" yaml:"description"`
Resources []Resource `json:"resources" yaml:"resources"`
Definitions []Definition `json:"definitions" yaml:"definitions"`
PluginFilePath string `json:"pluginFilePath" yaml:"pluginFilePath"`
PluginsGroup []PluginsGroup `json:"pluginsGroup" yaml:"pluginsGroup"`
}

// Plugin defines plugin details
type Plugin struct {
Name string `json:"name" yaml:"name"`
Version string `json:"version" yaml:"version"`
Priority int `json:"priority" yaml:"priority"`
ExternalLookupName string `json:"externalLookupName" yaml:"externalLookupName"`
}

// PluginsGroup defines the plugins group info
type PluginsGroup struct {
GroupName string `json:"groupName" yaml:"groupName"`
Plugins []Plugin `json:"plugins" yaml:"plugins"`
}

// Resource defines resources.plugins
type PluginsInUse struct {
GroupNames []string `json:"groupNames" yaml:"groupNames"`
PluginNames []string `json:"pluginNames" yaml:"pluginNames"`
}

// Resource defines the API path
Expand All @@ -81,6 +103,7 @@ type Resource struct {
Timeout time.Duration `json:"timeout" yaml:"timeout"`
Description string `json:"description" yaml:"description"`
Filters []string `json:"filters" yaml:"filters"`
Plugins PluginsInUse `json:"plugins" yaml:"plugins"`
Methods []Method `json:"methods" yaml:"methods"`
Resources []Resource `json:"resources,omitempty" yaml:"resources,omitempty"`
Headers map[string]string `json:"headers,omitempty" yaml:"headers,omitempty"`
Expand Down
24 changes: 24 additions & 0 deletions pkg/config/mock/api_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ resources:
description: resource documentation
filters:
- filter0
plugins:
groupNames:
- group2
pluginNames:
- rate limit
- access
methods:
- httpVerb: GET
filters:
Expand Down Expand Up @@ -148,3 +154,21 @@ definitions:
}
}
}
pluginFilePath: ""
pluginsGroup:
- groupName: "group1"
plugins:
- name: "rate limit"
version: "0.0.1"
priority: 1000
externalLookupName: "ExternalPluginRateLimit"
- name: "access"
version: "0.0.1"
priority: 1000
externalLookupName: "ExternalPluginAccess"
- groupName: "group2"
plugins:
- name: "blacklist"
version: "0.0.1"
priority: 1000
externalLookupName: "ExternalPluginBlackList"
176 changes: 176 additions & 0 deletions pkg/filter/plugins/plugins.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 plugins

import (
"errors"
"plugin"
"strings"
)

import (
"github.com/dubbogo/dubbo-go-proxy/pkg/common/constant"
"github.com/dubbogo/dubbo-go-proxy/pkg/config"
"github.com/dubbogo/dubbo-go-proxy/pkg/context"
"github.com/dubbogo/dubbo-go-proxy/pkg/filter"
"github.com/dubbogo/dubbo-go-proxy/pkg/logger"
)

var (
apiURLWithPluginsMap = make(map[string]context.FilterChain)
groupWithPluginsMap = make(map[string]map[string]PluginsWithFunc)
errEmptyPluginConfig = errors.New("Empty plugin config")
)

// PluginsWithFunc is a single plugin details
type PluginsWithFunc struct {
Name string
Priority int
fn context.FilterFunc
}

// InitPluginsGroup prase api_config.yaml(pluginsGroup) to map[string][]PluginsWithFunc
func InitPluginsGroup(groups []config.PluginsGroup, filePath string) {

if "" == filePath || len(groups) == 0 {
return
}

// load file.so
pls, err := plugin.Open(filePath)
if nil != err {
panic(err)
}

for _, group := range groups {
pwdMap := make(map[string]PluginsWithFunc, len(group.Plugins))

// trans to context.FilterFunc
for _, pl := range group.Plugins {
pwf := PluginsWithFunc{pl.Name, pl.Priority, loadExternalPlugin(&pl, pls)}
pwdMap[pl.Name] = pwf
}

groupWithPluginsMap[group.GroupName] = pwdMap
}
}

// InitApiUrlWithFilterChain must behind InitPluginsGroup call
func InitAPIURLWithFilterChain(resources []config.Resource) {
pairURLWithFilterChain("", resources, context.FilterChain{})
}

func pairURLWithFilterChain(parentPath string, resources []config.Resource, parentFilterFuncs []context.FilterFunc) {

if len(resources) == 0 {
return
}

groupPath := parentPath
if parentPath == constant.PathSlash {
groupPath = ""
}

for _, resource := range resources {
fullPath := groupPath + resource.Path
if !strings.HasPrefix(resource.Path, constant.PathSlash) {
continue
}

currentFuncArr := getApiFilterFuncsWithPluginsGroup(&resource.Plugins)

if len(currentFuncArr) > 0 {
apiURLWithPluginsMap[fullPath] = currentFuncArr
parentFilterFuncs = currentFuncArr
} else {
if len(parentFilterFuncs) > 0 {
apiURLWithPluginsMap[fullPath] = parentFilterFuncs
}
}

if len(resource.Resources) > 0 {
pairURLWithFilterChain(resource.Path, resource.Resources, parentFilterFuncs)
}
}

}

// GetAPIFilterFuncsWithAPIURL is get filterchain with path
func GetAPIFilterFuncsWithAPIURL(url string) context.FilterChain {
// found from cache
if funcs, found := apiURLWithPluginsMap[url]; found {
logger.Debugf("GetExternalPlugins is:%v,len:%d", funcs, len(funcs))
return funcs
}

// or return empty FilterFunc
return context.FilterChain{}
}

func loadExternalPlugin(p *config.Plugin, pl *plugin.Plugin) context.FilterFunc {
if nil != p {
logger.Infof("loadExternalPlugin name is :%s,version:%s,Priority:%d", p.Name, p.Version, p.Priority)
sb, err := pl.Lookup(p.ExternalLookupName)
if nil != err {
panic(err)
}

sbf := sb.(func() filter.Filter)
logger.Infof("loadExternalPlugin %s success", p.Name)
return sbf().Do()
}

panic(errEmptyPluginConfig)
}

func getApiFilterFuncsWithPluginsGroup(plu *config.PluginsInUse) []context.FilterFunc {
// not set plugins
if nil == plu || nil != plu && len(plu.GroupNames) == 0 && len(plu.PluginNames) == 0 {
return []context.FilterFunc{}
}

tmpMap := make(map[string]context.FilterFunc)

// found with group name
for _, groupName := range plu.GroupNames {
pwfMap, found := groupWithPluginsMap[groupName]
if found {
for _, pwf := range pwfMap {
tmpMap[pwf.Name] = pwf.fn
}
}
}

// found with with name from all group
for _, group := range groupWithPluginsMap {
for _, name := range plu.PluginNames {
if pwf, found := group[name]; found {
tmpMap[pwf.Name] = pwf.fn
}
}
}

ret := make([]context.FilterFunc, 0)
for _, v := range tmpMap {
if nil != v {
ret = append(ret, v)
}
}

return ret
}
57 changes: 57 additions & 0 deletions pkg/filter/plugins/plugins_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 plugins

import (
"testing"
)

import (
"github.com/stretchr/testify/assert"
)

import (
"github.com/dubbogo/dubbo-go-proxy/pkg/config"
)

func TestInitPluginsGroup(t *testing.T) {

config, err := config.LoadAPIConfigFromFile("../../../configs/api_config.yaml")
assert.Empty(t, err)

InitPluginsGroup(config.PluginsGroup, config.PluginFilePath)
}

func TestInitApiUrlWithFilterChain(t *testing.T) {
config, err := config.LoadAPIConfigFromFile("../../../configs/api_config.yaml")
assert.Empty(t, err)

InitPluginsGroup(config.PluginsGroup, config.PluginFilePath)
InitAPIURLWithFilterChain(config.Resources)
}

func TestGetApiFilterFuncsWithApiUrl(t *testing.T) {
config, err := config.LoadAPIConfigFromFile("../../../configs/api_config.yaml")
assert.Empty(t, err)

InitPluginsGroup(config.PluginsGroup, config.PluginFilePath)
InitAPIURLWithFilterChain(config.Resources)

fltc := GetAPIFilterFuncsWithAPIURL("/")

assert.Equal(t, len(fltc), 0)
}
5 changes: 5 additions & 0 deletions pkg/proxy/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package proxy

import (
"github.com/dubbogo/dubbo-go-proxy/pkg/filter/header"
"github.com/dubbogo/dubbo-go-proxy/pkg/filter/plugins"
)

import (
Expand Down Expand Up @@ -166,6 +167,10 @@ func addFilter(ctx *h.HttpContext, api router.API) {

ctx.BuildFilters()

// load plugins
filterChain := plugins.GetAPIFilterFuncsWithAPIURL(ctx.Request.URL.Path)
ctx.AppendFilterFunc(filterChain...)

ctx.AppendFilterFunc(extension.GetMustFilterFunc(constant.ResponseFilter))
}

Expand Down
Loading

0 comments on commit 73b661c

Please sign in to comment.