Skip to content

Commit

Permalink
Merge pull request apache#57 from williamfeng323/feature/http-2-http-…
Browse files Browse the repository at this point in the history
…param-mapping

Feature/http 2 http param mapping

Former-commit-id: e270958 [formerly 1775274]
Former-commit-id: e270958
Former-commit-id: b60d0e2
  • Loading branch information
zhangshen023 committed Nov 19, 2020
2 parents b47a9db + 3bbf684 commit 0d02731
Show file tree
Hide file tree
Showing 17 changed files with 941 additions and 174 deletions.
5 changes: 3 additions & 2 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@

package client

// Client represents the interface of http/dubbo clients
type Client interface {
Init() error
Close() error
Call(req *Request) (res interface{}, err error)

// MappingParams mapping param, uri, query, body ...
MappingParams(req *Request) (types []string, reqData []interface{}, err error)
// MapParams mapping param, uri, query, body ...
MapParams(req *Request) (reqData interface{}, err error)
}
51 changes: 8 additions & 43 deletions pkg/client/dubbo/dubbo.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package dubbo

import (
"context"
"reflect"
"strings"
"sync"
"time"
Expand All @@ -29,7 +28,6 @@ import (
"github.com/apache/dubbo-go/common/constant"
dg "github.com/apache/dubbo-go/config"
"github.com/apache/dubbo-go/protocol/dubbo"
"github.com/pkg/errors"
)

import (
Expand All @@ -44,12 +42,6 @@ const (
JavaLangClassName = "java.lang.Long"
)

var mappers = map[string]client.ParamMapper{
"queryStrings": queryStringsMapper{},
"headers": headerMapper{},
"requestBody": bodyMapper{},
}

var (
dubboClient *Client
onceClient = sync.Once{}
Expand Down Expand Up @@ -140,11 +132,11 @@ func (dc *Client) Close() error {
// Call invoke service
func (dc *Client) Call(req *client.Request) (res interface{}, err error) {
dm := req.API.Method.IntegrationRequest
types, values, err := dc.MappingParams(req)
types := req.API.IntegrationRequest.ParamTypes
values, err := dc.MapParams(req)
if err != nil {
return nil, err
}

method := dm.Method

logger.Debugf("[dubbo-go-proxy] dubbo invoke, method:%s, types:%s, reqData:%v", method, types, values)
Expand All @@ -162,22 +154,22 @@ func (dc *Client) Call(req *client.Request) (res interface{}, err error) {
return rst, nil
}

// MappingParams param mapping to api.
func (dc *Client) MappingParams(req *client.Request) ([]string, []interface{}, error) {
// MapParams params mapping to api.
func (dc *Client) MapParams(req *client.Request) (interface{}, error) {
r := req.API.Method.IntegrationRequest
var values []interface{}
for _, mappingParam := range r.MappingParams {
source, _, err := client.ParseMapSource(mappingParam.Name)
if err != nil {
return nil, nil, err
return nil, err
}
if mapper, ok := mappers[source]; ok {
if err := mapper.Map(mappingParam, *req, &values); err != nil {
return nil, nil, err
if err := mapper.Map(mappingParam, req, &values); err != nil {
return nil, err
}
}
}
return req.API.IntegrationRequest.ParamTypes, values, nil
return values, nil
}

func (dc *Client) get(key string) *dg.GenericService {
Expand Down Expand Up @@ -243,30 +235,3 @@ func (dc *Client) create(key string, irequest config.IntegrationRequest) *dg.Gen
dc.GenericServicePool[key] = clientService
return clientService
}

func validateTarget(target interface{}) (reflect.Value, error) {
rv := reflect.ValueOf(target)
if rv.Kind() != reflect.Ptr || rv.IsNil() {
return rv, errors.New("Target params must be a non-nil pointer")
}
if _, ok := target.(*[]interface{}); !ok {
return rv, errors.New("Target params for dubbo backend must be *[]interface{}")
}
return rv, nil
}

func setTarget(rv reflect.Value, pos int, value interface{}) {
if rv.Kind() != reflect.Ptr && rv.Type().Name() != "" && rv.CanAddr() {
rv = rv.Addr()
} else {
rv = rv.Elem()
}

tempValue := rv.Interface().([]interface{})
if len(tempValue) <= pos {
list := make([]interface{}, pos+1-len(tempValue))
tempValue = append(tempValue, list...)
}
tempValue[pos] = value
rv.Set(reflect.ValueOf(tempValue))
}
61 changes: 13 additions & 48 deletions pkg/client/dubbo/dubbo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,10 @@ func TestMappingParams(t *testing.T) {
},
}
req := client.NewReq(context.TODO(), r, api)
_, params, err := dClient.MappingParams(req)
params, err := dClient.MapParams(req)
assert.Nil(t, err)
assert.Equal(t, params[0], "12345")
assert.Equal(t, params[1], "19")
assert.Equal(t, params.([]interface{})[0], "12345")
assert.Equal(t, params.([]interface{})[1], "19")

r, _ = http.NewRequest("GET", "/mock/test?id=12345&age=19", bytes.NewReader([]byte("")))
api = mock.GetMockAPI(config.MethodGet, "/mock/test")
Expand All @@ -127,11 +127,11 @@ func TestMappingParams(t *testing.T) {
}
r.Header.Set("Auth", "1234567")
req = client.NewReq(context.TODO(), r, api)
_, params, err = dClient.MappingParams(req)
params, err = dClient.MapParams(req)
assert.Nil(t, err)
assert.Equal(t, params[0], "12345")
assert.Equal(t, params[1], "19")
assert.Equal(t, params[2], "1234567")
assert.Equal(t, params.([]interface{})[0], "12345")
assert.Equal(t, params.([]interface{})[1], "19")
assert.Equal(t, params.([]interface{})[2], "1234567")

r, _ = http.NewRequest("POST", "/mock/test?id=12345&age=19", bytes.NewReader([]byte(`{"sex": "male", "name":{"firstName": "Joe", "lastName": "Biden"}}`)))
api = mock.GetMockAPI(config.MethodGet, "/mock/test")
Expand Down Expand Up @@ -159,46 +159,11 @@ func TestMappingParams(t *testing.T) {
}
r.Header.Set("Auth", "1234567")
req = client.NewReq(context.TODO(), r, api)
_, params, err = dClient.MappingParams(req)
params, err = dClient.MapParams(req)
assert.Nil(t, err)
assert.Equal(t, params[0], "12345")
assert.Equal(t, params[1], "19")
assert.Equal(t, params[2], "1234567")
assert.Equal(t, params[3], "male")
assert.Equal(t, params[4], "Joe")
}

func TestValidateTarget(t *testing.T) {
target := []interface{}{}
val, err := validateTarget(&target)
assert.Nil(t, err)
assert.NotNil(t, val)
_, err = validateTarget(target)
assert.EqualError(t, err, "Target params must be a non-nil pointer")
target2 := ""
_, err = validateTarget(&target2)
assert.EqualError(t, err, "Target params for dubbo backend must be *[]interface{}")
}

func TestParseMapSource(t *testing.T) {
from, key, err := client.ParseMapSource("queryStrings.id")
assert.Nil(t, err)
assert.Equal(t, from, "queryStrings")
assert.Equal(t, key[0], "id")

from, key, err = client.ParseMapSource("headers.id")
assert.Nil(t, err)
assert.Equal(t, from, "headers")
assert.Equal(t, key[0], "id")

from, key, err = client.ParseMapSource("requestBody.user.id")
assert.Nil(t, err)
assert.Equal(t, from, "requestBody")
assert.Equal(t, key[0], "user")
assert.Equal(t, key[1], "id")

from, key, err = client.ParseMapSource("what.user.id")
assert.EqualError(t, err, "Parameter mapping config incorrect. Please fix it")
from, key, err = client.ParseMapSource("requestBody.*userid")
assert.EqualError(t, err, "Parameter mapping config incorrect. Please fix it")
assert.Equal(t, params.([]interface{})[0], "12345")
assert.Equal(t, params.([]interface{})[1], "19")
assert.Equal(t, params.([]interface{})[2], "1234567")
assert.Equal(t, params.([]interface{})[3], "male")
assert.Equal(t, params.([]interface{})[4], "Joe")
}
104 changes: 75 additions & 29 deletions pkg/client/dubbo/mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"bytes"
"encoding/json"
"io/ioutil"
"net/url"
"reflect"
"strconv"
)
Expand All @@ -35,14 +36,25 @@ import (
"github.com/dubbogo/dubbo-go-proxy/pkg/config"
)

var mappers = map[string]client.ParamMapper{
constant.QueryStrings: queryStringsMapper{},
constant.Headers: headerMapper{},
constant.RequestBody: bodyMapper{},
constant.RequestURI: uriMapper{},
}

type queryStringsMapper struct{}

func (qm queryStringsMapper) Map(mp config.MappingParam, c client.Request, target interface{}) error {
// nolint
func (qm queryStringsMapper) Map(mp config.MappingParam, c *client.Request, target interface{}) error {
rv, err := validateTarget(target)
if err != nil {
return err
}
c.IngressRequest.ParseForm()
queryValues, err := url.ParseQuery(c.IngressRequest.URL.RawQuery)
if err != nil {
return errors.Wrap(err, "Error happened when parsing the query paramters")
}
_, key, err := client.ParseMapSource(mp.Name)
if err != nil {
return err
Expand All @@ -51,19 +63,20 @@ func (qm queryStringsMapper) Map(mp config.MappingParam, c client.Request, targe
if err != nil {
return errors.Errorf("Parameter mapping %v incorrect", mp)
}
formValue := c.IngressRequest.Form.Get(key[0])
if len(formValue) == 0 {
qValue := queryValues.Get(key[0])
if len(qValue) == 0 {
return errors.Errorf("Query parameter %s does not exist", key)
}

setTarget(rv, pos, formValue)
setTarget(rv, pos, qValue)

return nil
}

type headerMapper struct{}

func (hm headerMapper) Map(mp config.MappingParam, c client.Request, target interface{}) error {
// nolint
func (hm headerMapper) Map(mp config.MappingParam, c *client.Request, target interface{}) error {
rv, err := validateTarget(target)
if err != nil {
return err
Expand All @@ -83,7 +96,9 @@ func (hm headerMapper) Map(mp config.MappingParam, c client.Request, target inte

type bodyMapper struct{}

func (bm bodyMapper) Map(mp config.MappingParam, c client.Request, target interface{}) error {
// nolint
func (bm bodyMapper) Map(mp config.MappingParam, c *client.Request, target interface{}) error {
// TO-DO: add support for content-type other than application/json
rv, err := validateTarget(target)
if err != nil {
return err
Expand All @@ -98,36 +113,67 @@ func (bm bodyMapper) Map(mp config.MappingParam, c client.Request, target interf
}

rawBody, err := ioutil.ReadAll(c.IngressRequest.Body)
defer func() {
c.IngressRequest.Body = ioutil.NopCloser(bytes.NewReader(rawBody))
}()
if err != nil {
return err
}
mapBody := map[string]interface{}{}
err = json.Unmarshal(rawBody, &mapBody)
if err != nil {
return err
}

val, err := getMapValue(mapBody, keys)
json.Unmarshal(rawBody, &mapBody)
val, err := client.GetMapValue(mapBody, keys)

setTarget(rv, pos, val)
c.IngressRequest.Body = ioutil.NopCloser(bytes.NewBuffer(rawBody))
return nil
}

func getMapValue(sourceMap map[string]interface{}, keys []string) (interface{}, error) {
if len(keys) == 1 && keys[0] == constant.DefaultBodyAll {
return sourceMap, nil
}
for i, key := range keys {
_, ok := sourceMap[key]
if !ok {
return nil, errors.Errorf("%s does not exist in request body", key)
}
rvalue := reflect.ValueOf(sourceMap[key])
if rvalue.Type().Kind() != reflect.Map {
return rvalue.Interface(), nil
}
return getMapValue(sourceMap[key].(map[string]interface{}), keys[i+1:])
}
return nil, nil
type uriMapper struct{}

// nolint
func (um uriMapper) Map(mp config.MappingParam, c *client.Request, target interface{}) error {
rv, err := validateTarget(target)
if err != nil {
return err
}
_, keys, err := client.ParseMapSource(mp.Name)
if err != nil {
return err
}
pos, err := strconv.Atoi(mp.MapTo)
if err != nil {
return errors.Errorf("Parameter mapping %v incorrect", mp)
}
uriValues := c.API.GetURIParams(*c.IngressRequest.URL)
setTarget(rv, pos, uriValues.Get(keys[0]))
return nil
}

// validateTarget verify if the incoming target for the Map function
// can be processed as expected.
func validateTarget(target interface{}) (reflect.Value, error) {
rv := reflect.ValueOf(target)
if rv.Kind() != reflect.Ptr || rv.IsNil() {
return rv, errors.New("Target params must be a non-nil pointer")
}
if _, ok := target.(*[]interface{}); !ok {
return rv, errors.New("Target params for dubbo backend must be *[]interface{}")
}
return rv, nil
}

func setTarget(rv reflect.Value, pos int, value interface{}) {
if rv.Kind() != reflect.Ptr && rv.Type().Name() != "" && rv.CanAddr() {
rv = rv.Addr()
} else {
rv = rv.Elem()
}

tempValue := rv.Interface().([]interface{})
if len(tempValue) <= pos {
list := make([]interface{}, pos+1-len(tempValue))
tempValue = append(tempValue, list...)
}
tempValue[pos] = value
rv.Set(reflect.ValueOf(tempValue))
}
Loading

0 comments on commit 0d02731

Please sign in to comment.