Merge remote-tracking branch 'gitlab/release/v0.10.1' into release/v0.10.1

This commit is contained in:
Liujian
2023-02-23 15:31:50 +08:00
36 changed files with 1349 additions and 58 deletions

View File

@@ -19,9 +19,12 @@ import (
circuit_breaker "github.com/eolinker/apinto/drivers/plugins/circuit-breaker"
"github.com/eolinker/apinto/drivers/plugins/cors"
dubbo2_proxy_rewrite "github.com/eolinker/apinto/drivers/plugins/dubbo2-proxy-rewrite"
dubbo2_to_http "github.com/eolinker/apinto/drivers/plugins/dubbo2-to-http"
extra_params "github.com/eolinker/apinto/drivers/plugins/extra-params"
grpc_proxy_rewrite "github.com/eolinker/apinto/drivers/plugins/grpc-proxy-rewrite"
"github.com/eolinker/apinto/drivers/plugins/gzip"
http_to_dubbo2 "github.com/eolinker/apinto/drivers/plugins/http-to-dubbo2"
http_to_grpc "github.com/eolinker/apinto/drivers/plugins/http-to-gRPC"
ip_restriction "github.com/eolinker/apinto/drivers/plugins/ip-restriction"
"github.com/eolinker/apinto/drivers/plugins/monitor"
params_transformer "github.com/eolinker/apinto/drivers/plugins/params-transformer"
@@ -131,5 +134,9 @@ func Register(extenderRegister eosc.IExtenderDriverRegister) {
grpc_proxy_rewrite.Register(extenderRegister)
dubbo2_proxy_rewrite.Register(extenderRegister)
http_to_dubbo2.Register(extenderRegister)
dubbo2_to_http.Register(extenderRegister)
http_to_grpc.Register(extenderRegister)
}

View File

@@ -2,6 +2,6 @@ package certs
type Config struct {
Name string `json:"name" label:"证书名"`
Key string `json:"key" label:"密钥内容" format:"text" description:"密钥文件的后缀名一般为.key"`
Pem string `json:"pem" label:"证书内容" format:"text" description:"证书文件的后缀名一般为.crt 或 .pem"`
Key string `json:"key" label:"密钥内容" format:"file" description:"密钥文件的后缀名一般为.key"`
Pem string `json:"pem" label:"证书内容" format:"file" description:"证书文件的后缀名一般为.crt 或 .pem"`
}

View File

@@ -1,8 +1,9 @@
package certs
import (
"github.com/eolinker/eosc"
"reflect"
"github.com/eolinker/eosc"
)
var (

View File

@@ -0,0 +1,216 @@
package dubbo2_to_http
import (
"dubbo.apache.org/dubbo-go/v3/protocol"
"dubbo.apache.org/dubbo-go/v3/protocol/dubbo/impl"
"encoding/json"
"errors"
"fmt"
hessian "github.com/apache/dubbo-go-hessian2"
"github.com/eolinker/apinto/utils"
"github.com/eolinker/eosc/eocontext"
dubbo2_context "github.com/eolinker/eosc/eocontext/dubbo2-context"
"github.com/eolinker/eosc/log"
"strings"
"time"
)
var (
errorTimeoutComplete = errors.New("complete timeout")
errParamLen = errors.New("args.length != types.length")
errNodeIsNull = errors.New("node is null")
)
type Complete struct {
retry int
timeOut time.Duration
contentType string
path string
method string
params []param
}
func NewComplete(retry int, timeOut time.Duration, contentType string, path string, method string, params []param) *Complete {
return &Complete{retry: retry, timeOut: timeOut, contentType: contentType, path: path, method: method, params: params}
}
func (c *Complete) Complete(org eocontext.EoContext) error {
ctx, err := dubbo2_context.Assert(org)
if err != nil {
return err
}
paramBody := ctx.Proxy().GetParam()
if len(paramBody.TypesList) != len(paramBody.ValuesList) || len(c.params) != len(paramBody.TypesList) {
ctx.Response().SetBody(Dubbo2ErrorResult(errParamLen))
return err
}
paramMap := make(map[string]interface{})
for i := range paramBody.ValuesList {
paramMap[paramBody.TypesList[i]] = paramBody.ValuesList[i]
}
log.DebugF("dubbo2-to-http complete paramMap = %v", paramMap)
//设置响应开始时间
proxyTime := time.Now()
defer func() {
ctx.Response().SetResponseTime(time.Now().Sub(proxyTime))
}()
var reqBody []byte
if len(paramMap) == 1 && c.params[0].fieldName != "" {
object, ok := paramMap[c.params[0].className]
if !ok {
err = errors.New(fmt.Sprintf("参数解析错误,未找到的名称为 %s className", c.params[0].className))
ctx.Response().SetBody(Dubbo2ErrorResult(err))
return err
}
object = formatData(object)
maps := make(map[string]hessian.Object)
maps[c.params[0].fieldName] = object
bytes, err := json.Marshal(maps)
if err != nil {
ctx.Response().SetBody(Dubbo2ErrorResult(err))
return err
}
reqBody = bytes
} else if len(paramMap) == 1 && c.params[0].fieldName == "" {
object, ok := paramMap[c.params[0].className]
if !ok {
err = errors.New(fmt.Sprintf("参数解析错误,未找到的名称为 %s className", c.params[0].className))
ctx.Response().SetBody(Dubbo2ErrorResult(err))
return err
}
log.DebugF("dubbo2-to-http complete paramMap = %v params[0] = %v object=%v", paramMap, c.params[0], object)
object = formatData(object)
bytes, err := json.Marshal(object)
if err != nil {
log.Errorf("dubbo2-to-http complete err=%v", err)
ctx.Response().SetBody(Dubbo2ErrorResult(err))
return err
}
reqBody = bytes
} else {
maps := make(map[string]hessian.Object)
for _, p := range c.params {
object, ok := paramMap[p.className]
if !ok {
err = errors.New(fmt.Sprintf("参数解析错误,未找到的名称为 %s className", p.className))
ctx.Response().SetBody(Dubbo2ErrorResult(err))
return err
}
object = formatData(object)
maps[p.fieldName] = object
}
bytes, err := json.Marshal(maps)
if err != nil {
ctx.Response().SetBody(Dubbo2ErrorResult(err))
return err
}
reqBody = bytes
}
balance := ctx.GetBalance()
app := ctx.GetApp()
var lastErr error
scheme := app.Scheme()
switch strings.ToLower(scheme) {
case "", "tcp":
scheme = "http"
case "tsl", "ssl", "https":
scheme = "https"
}
httpClient := NewClient(c.method, reqBody, c.path)
timeOut := app.TimeOut()
for index := 0; index <= c.retry; index++ {
if c.timeOut > 0 && time.Now().Sub(proxyTime) > c.timeOut {
ctx.Response().SetBody(Dubbo2ErrorResult(errorTimeoutComplete))
return errorTimeoutComplete
}
node, err := balance.Select(ctx)
if err != nil {
log.Error("select error: ", err)
ctx.Response().SetBody(Dubbo2ErrorResult(errNodeIsNull))
return err
}
addr := fmt.Sprintf("%s://%s", scheme, node.Addr())
log.Debug("node: ", node.Addr())
var resBody []byte
resBody, lastErr = httpClient.dial(addr, timeOut)
if lastErr == nil {
var val interface{}
if err = json.Unmarshal(resBody, &val); err != nil {
ctx.Response().SetBody(Dubbo2ErrorResult(err))
return err
}
ctx.Response().SetBody(getResponse(val, ctx.Proxy().Attachments()))
return nil
}
log.Error("dubbo upstream send error: ", lastErr)
}
ctx.Response().SetBody(Dubbo2ErrorResult(lastErr))
return lastErr
}
func Dubbo2ErrorResult(err error) protocol.RPCResult {
payload := impl.NewResponsePayload(nil, err, nil)
return protocol.RPCResult{
Attrs: payload.Attachments,
Err: payload.Exception,
Rest: payload.RspObj,
}
}
func getResponse(obj interface{}, attachments map[string]interface{}) protocol.RPCResult {
payload := impl.NewResponsePayload(obj, nil, attachments)
return protocol.RPCResult{
Attrs: payload.Attachments,
Err: payload.Exception,
Rest: payload.RspObj,
}
}
func formatData(value interface{}) interface{} {
switch valueTemp := value.(type) {
case map[interface{}]interface{}:
maps := make(map[string]interface{})
for k, v := range valueTemp {
maps[utils.InterfaceToString(k)] = formatData(v)
}
return maps
case []interface{}:
values := make([]interface{}, 0)
for _, v := range valueTemp {
values = append(values, formatData(v))
}
return values
default:
return value
}
}

View File

@@ -0,0 +1,12 @@
package dubbo2_to_http
type Config struct {
Method string `json:"method" label:"方法" enum:"POST,GET,HEAD,PUT,PATCH,DELETE,CONNECT,OPTIONS,TRACE" required:"true"` //get/post
Path string `json:"path" label:"转发路径" required:"true"`
ContentType string `json:"content_type" label:"ContentType" enum:"application/json" required:"true"` // application/json 只支持json格式传输
Params []Param `json:"params" label:"参数解析" required:"true"`
}
type Param struct {
ClassName string `json:"class_name" label:"class_name" required:"true"` //对应Java中类的class_name
FieldName string `json:"field_name" label:"字段名"` //用于http传输中body中的key名
}

View File

@@ -0,0 +1,54 @@
package dubbo2_to_http
import (
"errors"
"github.com/eolinker/apinto/drivers"
"github.com/eolinker/eosc"
)
func check(v interface{}) (*Config, error) {
conf, err := drivers.Assert[Config](v)
if err != nil {
return nil, err
}
if conf.Method == "" {
return nil, errors.New("method is null")
}
if len(conf.Params) == 0 {
return nil, errors.New("params is null")
}
return conf, nil
}
func Create(id, name string, conf *Config, workers map[eosc.RequireId]eosc.IWorker) (eosc.IWorker, error) {
if conf.Method == "" {
return nil, errors.New("method is null")
}
if len(conf.Params) == 0 {
return nil, errors.New("params is null")
}
params := make([]param, 0, len(conf.Params))
for _, p := range conf.Params {
params = append(params, param{
className: p.ClassName,
fieldName: p.FieldName,
})
}
pw := &ToHttp{
WorkerBase: drivers.Worker(id, name),
method: conf.Method,
path: conf.Path,
contentType: conf.ContentType,
params: params,
}
return pw, nil
}

View File

@@ -0,0 +1,18 @@
package dubbo2_to_http
import (
"github.com/eolinker/apinto/drivers"
"github.com/eolinker/eosc"
)
const (
Name = "dubbo2-to-http"
)
func Register(register eosc.IExtenderDriverRegister) {
register.RegisterExtenderDriver(Name, NewFactory())
}
func NewFactory() eosc.IExtenderDriverFactory {
return drivers.NewFactory[Config](Create)
}

View File

@@ -0,0 +1,49 @@
package dubbo2_to_http
import (
"bytes"
"io"
"net/http"
"time"
)
var client = http.DefaultClient
type Client struct {
method string
body []byte
path string
}
func NewClient(method string, body []byte, path string) *Client {
return &Client{method: method, body: body, path: path}
}
func (h *Client) dial(address string, timeout time.Duration) ([]byte, error) {
client.Timeout = timeout
request, err := http.NewRequest(h.method, address, bytes.NewReader(h.body))
path := h.path
if path != "" {
if path[:1] != "/" {
path = "/" + path
}
request.URL.Path = path
}
if err != nil {
return nil, err
}
resp, err := client.Do(request)
if err != nil {
return nil, err
}
defer resp.Body.Close()
resBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return resBody, nil
}

View File

@@ -0,0 +1,79 @@
package dubbo2_to_http
import (
"github.com/eolinker/apinto/drivers"
"github.com/eolinker/eosc"
"github.com/eolinker/eosc/eocontext"
dubbo2_context "github.com/eolinker/eosc/eocontext/dubbo2-context"
"time"
)
var _ eocontext.IFilter = (*ToHttp)(nil)
var _ dubbo2_context.DubboFilter = (*ToHttp)(nil)
type ToHttp struct {
drivers.WorkerBase
method string
path string
contentType string
params []param
}
func (t *ToHttp) DoDubboFilter(ctx dubbo2_context.IDubbo2Context, next eocontext.IChain) (err error) {
complete := NewComplete(0, time.Second*30, t.contentType, t.path, t.method, t.params)
ctx.SetCompleteHandler(complete)
if next != nil {
return next.DoChain(ctx)
}
return nil
}
func (t *ToHttp) DoFilter(ctx eocontext.EoContext, next eocontext.IChain) (err error) {
return dubbo2_context.DoDubboFilter(t, ctx, next)
}
func (t *ToHttp) Destroy() {
return
}
func (t *ToHttp) Start() error {
return nil
}
func (t *ToHttp) Reset(v interface{}, workers map[eosc.RequireId]eosc.IWorker) error {
conf, err := check(v)
if err != nil {
return err
}
t.path = conf.Path
t.method = conf.Method
t.contentType = conf.ContentType
params := make([]param, 0, len(conf.Params))
for _, val := range conf.Params {
params = append(params, param{
className: val.ClassName,
fieldName: val.FieldName,
})
}
t.params = params
return nil
}
func (t *ToHttp) Stop() error {
return nil
}
func (t *ToHttp) CheckSkill(skill string) bool {
return dubbo2_context.FilterSkillName == skill
}
type param struct {
className string
fieldName string
}

View File

@@ -0,0 +1,133 @@
package http_to_dubbo2
import (
"encoding/json"
"errors"
"fmt"
hessian "github.com/apache/dubbo-go-hessian2"
"github.com/eolinker/eosc/eocontext"
http_service "github.com/eolinker/eosc/eocontext/http-context"
"github.com/eolinker/eosc/log"
"time"
)
var (
ErrorTimeoutComplete = errors.New("complete timeout")
)
type Complete struct {
retry int
timeOut time.Duration
service string
method string
params []param
}
func NewComplete(retry int, timeOut time.Duration, service string, method string, params []param) *Complete {
return &Complete{retry: retry, timeOut: timeOut, service: service, method: method, params: params}
}
func (c *Complete) Complete(org eocontext.EoContext) error {
ctx, err := http_service.Assert(org)
if err != nil {
return err
}
//设置响应开始时间
proxyTime := time.Now()
balance := ctx.GetBalance()
var lastErr error
defer func() {
if lastErr != nil {
ctx.Response().SetStatus(400, "400")
ctx.Response().SetBody([]byte(lastErr.Error()))
}
ctx.Response().SetResponseTime(time.Now().Sub(proxyTime))
ctx.SetLabel("handler", "proxy")
}()
body, _ := ctx.Request().Body().RawBody()
var types []string
var valuesList []hessian.Object
for _, v := range c.params {
types = append(types, v.className)
}
//从body中提取内容
if len(c.params) == 1 && c.params[0].fieldName == "" {
var val interface{}
if lastErr = json.Unmarshal(body, &val); lastErr != nil {
log.Errorf("doHttpFilter jsonUnmarshal err:%v body:%v", lastErr, body)
return lastErr
}
valuesList = append(valuesList, val)
} else if len(c.params) == 1 && c.params[0].fieldName != "" {
var maps map[string]interface{}
if lastErr = json.Unmarshal(body, &maps); lastErr != nil {
log.Errorf("doHttpFilter jsonUnmarshal err:%v body:%v", lastErr, body)
return lastErr
}
if val, ok := maps[c.params[0].fieldName]; ok {
valuesList = append(valuesList, val)
} else {
lastErr = errors.New(fmt.Sprintf("参数解析错误body中未包含%s的参数名", c.params[0].fieldName))
return lastErr
}
} else {
var maps map[string]interface{}
if lastErr = json.Unmarshal(body, &maps); lastErr != nil {
log.Errorf("doHttpFilter jsonUnmarshal err:%v body:%v", lastErr, body)
return lastErr
}
for _, v := range c.params {
if val, ok := maps[v.fieldName]; ok {
valuesList = append(valuesList, val)
} else {
lastErr = errors.New(fmt.Sprintf("参数解析错误body中未包含%s的参数名", c.params[0].fieldName))
return lastErr
}
}
}
client := newDubbo2Client(c.service, c.method, types, valuesList)
for index := 0; index <= c.retry; index++ {
if c.timeOut > 0 && time.Now().Sub(proxyTime) > c.timeOut {
return ErrorTimeoutComplete
}
node, err := balance.Select(ctx)
if err != nil {
log.Error("select error: ", err)
ctx.Response().SetStatus(501, "501")
ctx.Response().SetBody([]byte(err.Error()))
return err
}
log.Debug("node: ", node.Addr())
var result interface{}
result, lastErr = client.dial(ctx.Context(), node.Addr(), c.timeOut)
if lastErr == nil {
bytes, err := json.Marshal(result)
if err != nil {
lastErr = err
return err
}
ctx.Response().SetBody(bytes)
return nil
}
log.Error("http to dubbo2 dial error: ", lastErr)
}
return lastErr
}

View File

@@ -0,0 +1,12 @@
package http_to_dubbo2
type Config struct {
Service string `json:"service" label:"服务名称" required:"true"`
Method string `json:"method" label:"方法名称" required:"true"`
Params []*Param `json:"params" label:"参数" required:"true"`
}
type Param struct {
ClassName string `json:"class_name" label:"class_name" required:"true"` //对应Java中类的class_name
FieldName string `json:"field_name" label:"根字段名"` //读取body中json的根字段名
}

View File

@@ -0,0 +1,91 @@
package http_to_dubbo2
import (
"context"
"dubbo.apache.org/dubbo-go/v3/common"
"dubbo.apache.org/dubbo-go/v3/common/constant"
"dubbo.apache.org/dubbo-go/v3/protocol/dubbo"
"dubbo.apache.org/dubbo-go/v3/protocol/invocation"
hessian "github.com/apache/dubbo-go-hessian2"
"github.com/eolinker/apinto/utils"
"reflect"
"time"
)
type dubbo2Client struct {
serviceName string
methodName string
typesList []string
valuesList []hessian.Object
}
func newDubbo2Client(serviceName string, methodName string, typesList []string, valuesList []hessian.Object) *dubbo2Client {
return &dubbo2Client{serviceName: serviceName, methodName: methodName, typesList: typesList, valuesList: valuesList}
}
func (d *dubbo2Client) dial(ctx context.Context, addr string, timeout time.Duration) (interface{}, error) {
arguments := make([]interface{}, 3)
parameterValues := make([]reflect.Value, 3)
arguments[0] = d.methodName
arguments[1] = d.typesList
arguments[2] = d.valuesList
parameterValues[0] = reflect.ValueOf(arguments[0])
parameterValues[1] = reflect.ValueOf(arguments[1])
parameterValues[2] = reflect.ValueOf(arguments[2])
invoc := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("$invoke"),
invocation.WithArguments(arguments),
invocation.WithParameterValues(parameterValues))
serviceName := d.serviceName
url, err := common.NewURL(addr,
common.WithProtocol(dubbo.DUBBO), common.WithParamsValue(constant.SerializationKey, constant.Hessian2Serialization),
common.WithParamsValue(constant.GenericFilterKey, "true"),
common.WithParamsValue(constant.TimeoutKey, timeout.String()),
common.WithParamsValue(constant.InterfaceKey, serviceName),
common.WithParamsValue(constant.ReferenceFilterKey, "generic,filter"),
common.WithPath(serviceName),
)
if err != nil {
return nil, err
}
dubboProtocol := dubbo.NewDubboProtocol()
invoker := dubboProtocol.Refer(url)
var resp interface{}
invoc.SetReply(&resp)
result := invoker.Invoke(ctx, invoc)
if result.Error() != nil {
return nil, result.Error()
}
val := result.Result().(*interface{})
data := formatData(*val)
return data, nil
}
func formatData(value interface{}) interface{} {
switch valueTemp := value.(type) {
case map[interface{}]interface{}:
maps := make(map[string]interface{})
for k, v := range valueTemp {
maps[utils.InterfaceToString(k)] = formatData(v)
}
return maps
case []interface{}:
values := make([]interface{}, 0)
for _, v := range valueTemp {
values = append(values, formatData(v))
}
return values
default:
return value
}
}

View File

@@ -0,0 +1,59 @@
package http_to_dubbo2
import (
"errors"
"github.com/eolinker/apinto/drivers"
"github.com/eolinker/eosc"
)
func check(v interface{}) (*Config, error) {
conf, err := drivers.Assert[Config](v)
if err != nil {
return nil, err
}
if conf.Service == "" {
return nil, errors.New("service is null")
}
if conf.Method == "" {
return nil, errors.New("method is null")
}
if len(conf.Params) == 0 {
return nil, errors.New("params is null")
}
return conf, nil
}
func Create(id, name string, conf *Config, workers map[eosc.RequireId]eosc.IWorker) (eosc.IWorker, error) {
if conf.Service == "" {
return nil, errors.New("service is null")
}
if conf.Method == "" {
return nil, errors.New("method is null")
}
if len(conf.Params) == 0 {
return nil, errors.New("params is null")
}
params := make([]param, 0, len(conf.Params))
for _, p := range conf.Params {
params = append(params, param{
className: p.ClassName,
fieldName: p.FieldName,
})
}
pw := &ToDubbo2{
WorkerBase: drivers.Worker(id, name),
service: conf.Service,
method: conf.Method,
params: params,
}
return pw, nil
}

View File

@@ -0,0 +1,18 @@
package http_to_dubbo2
import (
"github.com/eolinker/apinto/drivers"
"github.com/eolinker/eosc"
)
const (
Name = "http-to-dubbo2"
)
func Register(register eosc.IExtenderDriverRegister) {
register.RegisterExtenderDriver(Name, NewFactory())
}
func NewFactory() eosc.IExtenderDriverFactory {
return drivers.NewFactory[Config](Create)
}

View File

@@ -0,0 +1,75 @@
package http_to_dubbo2
import (
"github.com/eolinker/apinto/drivers"
"github.com/eolinker/eosc"
"github.com/eolinker/eosc/eocontext"
http_context "github.com/eolinker/eosc/eocontext/http-context"
"time"
)
var _ eocontext.IFilter = (*ToDubbo2)(nil)
var _ http_context.HttpFilter = (*ToDubbo2)(nil)
type ToDubbo2 struct {
drivers.WorkerBase
service string
method string
params []param
}
func (p *ToDubbo2) DoHttpFilter(ctx http_context.IHttpContext, next eocontext.IChain) error {
complete := NewComplete(0, time.Second*30, p.service, p.method, p.params)
ctx.SetCompleteHandler(complete)
if next != nil {
return next.DoChain(ctx)
}
return nil
}
func (p *ToDubbo2) DoFilter(ctx eocontext.EoContext, next eocontext.IChain) (err error) {
return http_context.DoHttpFilter(p, ctx, next)
}
type param struct {
className string
fieldName string
}
func (p *ToDubbo2) Start() error {
return nil
}
func (p *ToDubbo2) Reset(v interface{}, workers map[eosc.RequireId]eosc.IWorker) error {
conf, err := check(v)
if err != nil {
return err
}
p.service = conf.Service
p.method = conf.Method
params := make([]param, 0, len(conf.Params))
for _, val := range conf.Params {
params = append(params, param{
className: val.ClassName,
fieldName: val.FieldName,
})
}
p.params = params
return nil
}
func (p *ToDubbo2) Stop() error {
return nil
}
func (p *ToDubbo2) Destroy() {
}
func (p *ToDubbo2) CheckSkill(skill string) bool {
return http_context.FilterSkillName == skill
}

View File

@@ -0,0 +1,195 @@
package http_to_grpc
import (
"context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
"time"
"google.golang.org/grpc/codes"
"github.com/jhump/protoreflect/grpcreflect"
reflectpb "google.golang.org/grpc/reflection/grpc_reflection_v1alpha"
"github.com/eolinker/eosc/log"
"google.golang.org/grpc/status"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc"
"github.com/fullstorydev/grpcurl"
"google.golang.org/grpc/metadata"
http_context "github.com/eolinker/eosc/eocontext/http-context"
"github.com/eolinker/eosc/eocontext"
)
var (
ErrorTimeoutComplete = errors.New("complete timeout")
options = grpcurl.FormatOptions{
AllowUnknownFields: true,
}
defaultTimeout = 10 * time.Second
)
type complete struct {
format grpcurl.Format
descSource grpcurl.DescriptorSource
timeout time.Duration
authority string
service string
method string
headers map[string]string
retry int
reflect bool
}
func newComplete(descSource grpcurl.DescriptorSource, conf *Config) *complete {
timeout := defaultTimeout
return &complete{
format: grpcurl.Format(conf.Format),
descSource: descSource,
timeout: timeout,
authority: conf.Authority,
service: conf.Service,
method: conf.Method,
reflect: conf.Reflect,
headers: conf.Headers,
}
}
func (h *complete) Complete(org eocontext.EoContext) error {
ctx, err := http_context.Assert(org)
if err != nil {
return err
}
body, err := ctx.Proxy().Body().RawBody()
if err != nil {
return err
}
in := strings.NewReader(string(body))
balance := ctx.GetBalance()
app := ctx.GetApp()
md := httpHeaderToMD(ctx.Proxy().Header().Headers(), h.headers)
opts := genDialOpts(app.Scheme() == "https", h.authority)
newCtx := metadata.NewOutgoingContext(ctx.Context(), md)
symbol := fmt.Sprintf("%s/%s", h.service, h.method)
var lastErr error
var conn *grpc.ClientConn
for i := h.retry + 1; i > 0; i-- {
node, err := balance.Select(ctx)
if err != nil {
log.Error("select node error: ", err)
return err
}
conn, lastErr = dial(node.Addr(), h.timeout, opts...)
if lastErr != nil {
log.Error("dial error: ", lastErr)
continue
}
descSource := h.descSource
if h.reflect {
refClient := grpcreflect.NewClientV1Alpha(newCtx, reflectpb.NewServerReflectionClient(conn))
refSource := grpcurl.DescriptorSourceFromServer(newCtx, refClient)
if h.descSource == nil {
descSource = refSource
} else {
descSource = &compositeSource{reflection: refSource, file: h.descSource}
}
}
rf, formatter, err := grpcurl.RequestParserAndFormatter(h.format, descSource, in, options)
if err != nil {
return fmt.Errorf("failed to construct request parser and formatter for %s", h.format)
}
response := NewResponse()
handler := &grpcurl.DefaultEventHandler{
Out: response,
Formatter: formatter,
}
err = grpcurl.InvokeRPC(newCtx, descSource, conn, symbol, []string{}, handler, rf.Next)
if err != nil {
if errStatus, ok := status.FromError(err); ok {
data, _ := json.Marshal(StatusErr{
Code: fmt.Sprintf("%s", errStatus.Code()),
Msg: errStatus.Message(),
})
ctx.Response().SetBody(data)
return err
}
err = fmt.Errorf("error invoking method %s", symbol)
data, _ := json.Marshal(StatusErr{
Code: fmt.Sprintf("%s", codes.Unavailable),
Msg: err.Error(),
})
ctx.Response().SetBody(data)
return err
}
ctx.Response().SetBody(response.Body())
return nil
}
return lastErr
}
type StatusErr struct {
Code string `json:"code"`
Msg string `json:"msg"`
}
func httpHeaderToMD(headers http.Header, additionalHeader map[string]string) metadata.MD {
md := metadata.New(map[string]string{})
for key, value := range headers {
if strings.ToLower(key) == "user-agent" {
md.Set("grpc-go", value...)
continue
}
md.Set(key, value...)
}
for key, value := range additionalHeader {
md.Set(key, value)
}
md.Set("content-type", "application/grpc")
md.Delete("connection")
return md
}
func genDialOpts(isTLS bool, authority string) []grpc.DialOption {
var opts []grpc.DialOption
if isTLS {
opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})))
} else {
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
}
if authority != "" {
opts = append(opts, grpc.WithAuthority(authority))
}
return opts
}
func dial(target string, timeout time.Duration, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
cc, err := grpc.DialContext(ctx, target, opts...)
if err != nil {
return nil, err
}
return cc, nil
}

View File

@@ -0,0 +1,13 @@
package http_to_grpc
import "github.com/eolinker/eosc"
type Config struct {
Service string `json:"service" label:"服务名称"`
Method string `json:"method" label:"方法名称"`
Authority string `json:"authority" label:"虚拟主机域名(Authority)"`
Format string `json:"format" label:"数据格式" enum:"json"`
Reflect bool `json:"reflect" label:"反射"`
ProtobufID eosc.RequireId `json:"protobuf_id" required:"false" label:"Protobuf ID" skill:"github.com/eolinker/apinto/grpc-transcode.transcode.IDescriptor" switch:"reflect === false"`
Headers map[string]string `json:"headers" label:"额外头部"`
}

View File

@@ -0,0 +1,49 @@
package http_to_grpc
import (
"github.com/fullstorydev/grpcurl"
"github.com/jhump/protoreflect/desc"
)
// Uses a file source as a fallback for resolving symbols and extensions, but
// only uses the reflection source for listing services
type compositeSource struct {
reflection grpcurl.DescriptorSource
file grpcurl.DescriptorSource
}
func (cs compositeSource) ListServices() ([]string, error) {
return cs.reflection.ListServices()
}
func (cs compositeSource) FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) {
d, err := cs.reflection.FindSymbol(fullyQualifiedName)
if err == nil {
return d, nil
}
return cs.file.FindSymbol(fullyQualifiedName)
}
func (cs compositeSource) AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) {
exts, err := cs.reflection.AllExtensionsForType(typeName)
if err != nil {
// On error fall back to file source
return cs.file.AllExtensionsForType(typeName)
}
// Track the tag numbers from the reflection source
tags := make(map[int32]bool)
for _, ext := range exts {
tags[ext.GetNumber()] = true
}
fileExts, err := cs.file.AllExtensionsForType(typeName)
if err != nil {
return exts, nil
}
for _, ext := range fileExts {
// Prioritize extensions found via reflection
if !tags[ext.GetNumber()] {
exts = append(exts, ext)
}
}
return exts, nil
}

View File

@@ -0,0 +1,45 @@
package http_to_grpc
import (
"fmt"
"github.com/eolinker/apinto/drivers"
grpc_descriptor "github.com/eolinker/apinto/grpc-descriptor"
"github.com/eolinker/eosc"
"github.com/fullstorydev/grpcurl"
)
func check(v interface{}) (*Config, error) {
conf, err := drivers.Assert[Config](v)
if err != nil {
return nil, err
}
return conf, nil
}
func Create(id, name string, conf *Config, workers map[eosc.RequireId]eosc.IWorker) (eosc.IWorker, error) {
descSource, err := getDescSource(conf.ProtobufID, workers)
if err != nil {
return nil, err
}
return &toGRPC{
WorkerBase: drivers.Worker(id, name),
handler: newComplete(descSource, conf),
}, nil
}
func getDescSource(protobufID eosc.RequireId, workers map[eosc.RequireId]eosc.IWorker) (grpcurl.DescriptorSource, error) {
var descSource grpcurl.DescriptorSource
if protobufID != "" {
worker, ok := workers[protobufID]
if ok {
v, ok := worker.(grpc_descriptor.IDescriptor)
if !ok {
return nil, fmt.Errorf("invalid protobuf id: %s", protobufID)
}
descSource = v.Descriptor()
}
}
return descSource, nil
}

View File

@@ -0,0 +1,18 @@
package http_to_grpc
import (
"github.com/eolinker/apinto/drivers"
"github.com/eolinker/eosc"
)
const (
Name = "http_to_grpc"
)
func Register(register eosc.IExtenderDriverRegister) {
register.RegisterExtenderDriver(Name, NewFactory())
}
func NewFactory() eosc.IExtenderDriverFactory {
return drivers.NewFactory[Config](Create)
}

View File

@@ -0,0 +1,18 @@
package http_to_grpc
func NewResponse() *Response {
return &Response{}
}
type Response struct {
buf []byte
}
func (r *Response) Write(p []byte) (n int, err error) {
r.buf = p
return len(r.buf), nil
}
func (r *Response) Body() []byte {
return r.buf
}

View File

@@ -0,0 +1,56 @@
package http_to_grpc
import (
"github.com/eolinker/apinto/drivers"
"github.com/eolinker/eosc"
"github.com/eolinker/eosc/eocontext"
http_context "github.com/eolinker/eosc/eocontext/http-context"
)
type toGRPC struct {
drivers.WorkerBase
handler eocontext.CompleteHandler
}
func (t *toGRPC) Start() error {
return nil
}
func (t *toGRPC) DoFilter(ctx eocontext.EoContext, next eocontext.IChain) (err error) {
return http_context.DoHttpFilter(t, ctx, next)
}
func (t *toGRPC) Destroy() {
t.handler = nil
return
}
func (t *toGRPC) DoHttpFilter(ctx http_context.IHttpContext, next eocontext.IChain) (err error) {
if t.handler != nil {
ctx.SetCompleteHandler(t.handler)
}
if next != nil {
return next.DoChain(ctx)
}
return nil
}
func (t *toGRPC) Reset(conf interface{}, workers map[eosc.RequireId]eosc.IWorker) error {
cfg, err := check(conf)
if err != nil {
return err
}
descSource, err := getDescSource(cfg.ProtobufID, workers)
if err != nil {
return err
}
t.handler = newComplete(descSource, cfg)
return nil
}
func (t *toGRPC) Stop() error {
return nil
}
func (t *toGRPC) CheckSkill(skill string) bool {
return http_context.FilterSkillName == skill
}

View File

@@ -16,6 +16,11 @@ var _ IManger = (*dubboManger)(nil)
var completeCaller = NewCompleteCaller()
var (
errNoResult = errors.New("no result")
errNotFound = errors.New("not found")
)
type IManger interface {
Set(id string, port int, serviceName, methodName string, rule []AppendRule, handler router.IRouterHandler) error
Delete(id string)
@@ -70,12 +75,12 @@ func (d *dubboManger) Delete(id string) {
}
func (d *dubboManger) Handler(port int, req *invocation.RPCInvocation) protocol.RPCResult {
log.DebugF("dubbo2 Handler port=%d req=%v", port, req)
ctx := dubbo2_context.NewContext(req, port)
match, has := d.matcher.Match(port, ctx.HeaderReader())
if !has {
errHandler := NewErrHandler(errors.New("not found"))
errHandler := NewErrHandler(errNotFound)
ctx.SetFinish(errHandler)
ctx.SetCompleteHandler(errHandler)
@@ -99,7 +104,7 @@ func (d *dubboManger) Handler(port int, req *invocation.RPCInvocation) protocol.
rpcResult, ok := ctx.Response().GetBody().(protocol.RPCResult)
if !ok {
rpcResult = Dubbo2ErrorResult(errors.New("no result"))
rpcResult = Dubbo2ErrorResult(errNoResult)
}
return rpcResult

View File

@@ -48,10 +48,10 @@ func Register(tp RouterType, handler RouterServerHandler) error {
type RouterServerHandler func(port int, listener net.Listener)
func init() {
matchWriters[AnyTCP] = matchersToMatchWriters([]cmux.Matcher{cmux.Any()})
matchWriters[TslTCP] = matchersToMatchWriters([]cmux.Matcher{cmux.TLS()})
matchWriters[Http] = matchersToMatchWriters([]cmux.Matcher{cmux.HTTP1Fast()})
matchWriters[Dubbo2] = matchersToMatchWriters([]cmux.Matcher{cmux.PrefixMatcher(string([]byte{0xda, 0xbb}))})
matchWriters[AnyTCP] = matchersToMatchWriters(cmux.Any())
matchWriters[TslTCP] = matchersToMatchWriters(cmux.TLS())
matchWriters[Http] = matchersToMatchWriters(cmux.HTTP1Fast())
matchWriters[Dubbo2] = matchersToMatchWriters(cmux.PrefixMatcher(string([]byte{0xda, 0xbb})))
matchWriters[GRPC] = []cmux.MatchWriter{cmux.HTTP2MatchHeaderFieldPrefixSendSettings("content-type", "application/grpc")}
var tf traffic.ITraffic
var listenCfg *config.ListenUrl
@@ -113,7 +113,7 @@ func readPort(addr net.Addr) int {
return pv
}
func matchersToMatchWriters(matchers []cmux.Matcher) []cmux.MatchWriter {
func matchersToMatchWriters(matchers ...cmux.Matcher) []cmux.MatchWriter {
mws := make([]cmux.MatchWriter, 0, len(matchers))
for _, m := range matchers {
cm := m

View File

@@ -0,0 +1,14 @@
package protocbuf
import (
"encoding/json"
)
// Config service_http驱动配置
type Config struct {
}
func (c *Config) String() string {
data, _ := json.Marshal(c)
return string(data)
}

View File

@@ -0,0 +1,10 @@
package protocbuf
import (
"github.com/eolinker/eosc"
)
// Create 创建service_http驱动的实例
func Create(id, name string, v *Config, workers map[eosc.RequireId]eosc.IWorker) (eosc.IWorker, error) {
return nil, nil
}

View File

@@ -0,0 +1,27 @@
package protocbuf
import (
"github.com/eolinker/apinto/drivers"
"github.com/eolinker/apinto/drivers/discovery/static"
round_robin "github.com/eolinker/apinto/upstream/round-robin"
"github.com/eolinker/eosc"
)
var DriverName = "protobuf_transcode"
var (
defaultHttpDiscovery = static.CreateAnonymous(&static.Config{
Health: nil,
HealthOn: false,
})
)
// Register 注册service_http驱动工厂
func Register(register eosc.IExtenderDriverRegister) {
register.RegisterExtenderDriver(DriverName, NewFactory())
}
// NewFactory 创建service_http驱动工厂
func NewFactory() eosc.IExtenderDriverFactory {
round_robin.Register()
return drivers.NewFactory[Config](Create)
}

View File

@@ -7,6 +7,6 @@ var (
)
func init() {
flag.StringVar(&address, "addr", "127.0.0.1:8099", "The address to connect dubbo2 server.")
flag.StringVar(&address, "addr", "172.28.187.118:8099", "The address to connect dubbo2 server.")
flag.Parse()
}

View File

@@ -62,10 +62,10 @@ func client(addr string, serviceName, methodName string, timeout time.Duration,
func main() {
ComplexServer()
List()
GetById(101)
UpdateList()
Update()
//List()
//GetById(101)
//UpdateList()
//Update()
}
func ComplexServer() {

View File

@@ -32,7 +32,7 @@ func (s *Server) Hello(ctx context.Context, request *service.HelloRequest) (*ser
grpc.SetTrailer(ctx, trailingMD)
}
return &service.HelloResponse{
Msg: "hello",
Msg: fmt.Sprintf("hello,%s", request.Name),
}, nil
}

32
go.mod
View File

@@ -5,9 +5,10 @@ go 1.19
require (
github.com/Shopify/sarama v1.32.0
github.com/coocood/freecache v1.2.2
github.com/dubbogo/gost v1.11.25
github.com/dubbogo/gost v1.13.1
github.com/eolinker/eosc v0.9.1
github.com/fasthttp/websocket v1.5.0
github.com/fullstorydev/grpcurl v1.8.7
github.com/go-redis/redis/v8 v8.11.5
github.com/google/uuid v1.3.0
github.com/gorilla/websocket v1.4.2
@@ -32,11 +33,10 @@ require (
cloud.google.com/go v0.65.0 // indirect
contrib.go.opencensus.io/exporter/prometheus v0.4.1 // indirect
github.com/RoaringBitmap/roaring v0.7.1 // indirect
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
github.com/Workiva/go-datastructures v1.0.52 // indirect
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 // indirect
github.com/alibaba/sentinel-golang v1.0.4 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.61.18 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1704 // indirect
github.com/apache/dubbo-getty v1.4.8 // indirect
github.com/bits-and-blooms/bitset v1.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
@@ -52,18 +52,17 @@ require (
github.com/envoyproxy/protoc-gen-validate v0.1.0 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/go-co-op/gocron v1.9.0 // indirect
github.com/go-errors/errors v1.0.1 // indirect
github.com/go-kit/log v0.1.0 // indirect
github.com/go-logfmt/logfmt v0.5.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.4 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.11.0 // indirect
github.com/go-resty/resty/v2 v2.7.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/mock v1.5.0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
@@ -74,20 +73,22 @@ require (
github.com/k0kubun/pp v3.0.1+incompatible // indirect
github.com/knadh/koanf v1.4.1 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/mschoch/smat v0.2.0 // indirect
github.com/nacos-group/nacos-sdk-go v1.1.1 // indirect
github.com/nacos-group/nacos-sdk-go/v2 v2.1.2 // indirect
github.com/natefinch/lumberjack v2.0.0+incompatible // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pelletier/go-toml v1.7.0 // indirect
github.com/polarismesh/polaris-go v1.1.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/prometheus/statsd_exporter v0.21.0 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b // indirect
github.com/shirou/gopsutil v3.20.11+incompatible // indirect
github.com/shirou/gopsutil/v3 v3.21.6 // indirect
github.com/shirou/gopsutil/v3 v3.22.2 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cast v1.3.0 // indirect
@@ -95,24 +96,25 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.7.1 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/tklauser/go-sysconf v0.3.6 // indirect
github.com/tklauser/numcpus v0.2.2 // indirect
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/tklauser/numcpus v0.4.0 // indirect
github.com/uber/jaeger-client-go v2.29.1+incompatible // indirect
github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
github.com/ugorji/go/codec v1.2.6 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
github.com/zouyx/agollo/v3 v3.4.5 // indirect
go.opencensus.io v0.23.0 // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
google.golang.org/appengine v1.6.6 // indirect
gopkg.in/ini.v1 v1.51.0 // indirect
gopkg.in/ini.v1 v1.66.2 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
)
require (
dubbo.apache.org/dubbo-go/v3 v3.0.2-0.20220519062747-f6405fa79d5c
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/apache/dubbo-go-hessian2 v1.11.3
github.com/apache/dubbo-go-hessian2 v1.11.6
github.com/armon/go-metrics v0.3.9 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
@@ -162,7 +164,7 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.12.1 // indirect
github.com/prometheus/client_golang v1.12.2 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
@@ -189,7 +191,7 @@ require (
go.uber.org/zap v1.23.0 // indirect
golang.org/x/sys v0.2.0 // indirect
golang.org/x/text v0.4.0 // indirect
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
google.golang.org/genproto v0.0.0-20211104193956-4c6863e31247 // indirect
gopkg.in/sourcemap.v1 v1.0.5 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect

View File

@@ -0,0 +1,20 @@
package grpc_descriptor
import (
"github.com/eolinker/eosc"
"github.com/fullstorydev/grpcurl"
)
const (
ServiceSkill = "github.com/eolinker/apinto/grpc-transcode.transcode.IDescriptor"
)
type IDescriptor interface {
eosc.IWorker
Descriptor() grpcurl.DescriptorSource
}
// CheckSkill 检查目标技能是否符合
func CheckSkill(skill string) bool {
return skill == ServiceSkill
}

View File

@@ -14,6 +14,7 @@ import (
"github.com/eolinker/eosc/eocontext"
eoscContext "github.com/eolinker/eosc/eocontext"
dubbo2_context "github.com/eolinker/eosc/eocontext/dubbo2-context"
"github.com/eolinker/eosc/log"
"github.com/eolinker/eosc/utils/config"
"github.com/google/uuid"
"net"
@@ -23,13 +24,8 @@ import (
"time"
)
type DubboParamBody struct {
typesList []string
valuesList []hessian.Object
}
func NewDubboParamBody(typesList []string, valuesList []hessian.Object) *DubboParamBody {
return &DubboParamBody{typesList: typesList, valuesList: valuesList}
func NewDubboParamBody(typesList []string, valuesList []hessian.Object) *dubbo2_context.Dubbo2ParamBody {
return &dubbo2_context.Dubbo2ParamBody{TypesList: typesList, ValuesList: valuesList}
}
var _ dubbo2_context.IDubbo2Context = (*DubboContext)(nil)
@@ -62,6 +58,9 @@ func NewContext(req *invocation.RPCInvocation, port int) dubbo2_context.IDubbo2C
t := time.Now()
method, typesList, valuesList := argumentsUnmarshal(req.Arguments())
if method == "" || len(typesList) == 0 || len(valuesList) == 0 {
log.Errorf("dubbo2 NewContext method=%s typesList=%v valuesList=%v req=%v", method, typesList, valuesList, req)
}
path := req.GetAttachmentWithDefaultValue(constant.PathKey, "")
serviceName := req.GetAttachmentWithDefaultValue(constant.InterfaceKey, "")
@@ -121,16 +120,9 @@ func (d *DubboContext) dial(addr string, timeout time.Duration) error {
arguments := make([]interface{}, 3)
parameterValues := make([]reflect.Value, 3)
typesList := make([]string, 0)
valuesList := make([]hessian.Object, 0)
if param, ok := d.proxy.GetParam().(*DubboParamBody); ok {
typesList = param.typesList
valuesList = param.valuesList
}
arguments[0] = d.proxy.Service().Method()
arguments[1] = typesList
arguments[2] = valuesList
arguments[1] = d.proxy.GetParam().TypesList
arguments[2] = d.proxy.GetParam().ValuesList
parameterValues[0] = reflect.ValueOf(arguments[0])
parameterValues[1] = reflect.ValueOf(arguments[1])

View File

@@ -6,19 +6,19 @@ var _ dubbo2_context.IProxy = (*Proxy)(nil)
type Proxy struct {
serviceWriter dubbo2_context.IServiceWriter
param interface{}
param *dubbo2_context.Dubbo2ParamBody
attachments map[string]interface{}
}
func NewProxy(serviceWriter dubbo2_context.IServiceWriter, param interface{}, attachments map[string]interface{}) *Proxy {
func NewProxy(serviceWriter dubbo2_context.IServiceWriter, param *dubbo2_context.Dubbo2ParamBody, attachments map[string]interface{}) *Proxy {
return &Proxy{serviceWriter: serviceWriter, param: param, attachments: attachments}
}
func (p *Proxy) GetParam() interface{} {
func (p *Proxy) GetParam() *dubbo2_context.Dubbo2ParamBody {
return p.param
}
func (p *Proxy) SetParam(param interface{}) {
func (p *Proxy) SetParam(param *dubbo2_context.Dubbo2ParamBody) {
p.param = param
}

View File

@@ -242,13 +242,16 @@ func (b *BodyRequestHandler) resetFile() error {
}
for key, values := range multipartForm.Value {
temp := make(url.Values)
temp[key] = values
value := temp.Encode()
err := writer.WriteField(key, value)
if err != nil {
return err
//temp := make(url.Values)
//temp[key] = values
//value := temp.Encode()
for _, value := range values {
err := writer.WriteField(key, value)
if err != nil {
return err
}
}
}
err := writer.Close()
if err != nil {