mirror of
https://github.com/eolinker/apinto
synced 2025-09-26 21:01:19 +08:00
Merge remote-tracking branch 'gitlab/release/v0.10.1' into release/v0.10.1
This commit is contained in:
@@ -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)
|
||||
|
||||
}
|
||||
|
@@ -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"`
|
||||
}
|
||||
|
@@ -1,8 +1,9 @@
|
||||
package certs
|
||||
|
||||
import (
|
||||
"github.com/eolinker/eosc"
|
||||
"reflect"
|
||||
|
||||
"github.com/eolinker/eosc"
|
||||
)
|
||||
|
||||
var (
|
||||
|
216
drivers/plugins/dubbo2-to-http/complete.go
Normal file
216
drivers/plugins/dubbo2-to-http/complete.go
Normal 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
|
||||
}
|
||||
}
|
12
drivers/plugins/dubbo2-to-http/config.go
Normal file
12
drivers/plugins/dubbo2-to-http/config.go
Normal 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名
|
||||
}
|
54
drivers/plugins/dubbo2-to-http/driver.go
Normal file
54
drivers/plugins/dubbo2-to-http/driver.go
Normal 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
|
||||
}
|
18
drivers/plugins/dubbo2-to-http/factory.go
Normal file
18
drivers/plugins/dubbo2-to-http/factory.go
Normal 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)
|
||||
}
|
49
drivers/plugins/dubbo2-to-http/http.go
Normal file
49
drivers/plugins/dubbo2-to-http/http.go
Normal 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
|
||||
}
|
79
drivers/plugins/dubbo2-to-http/to-http.go
Normal file
79
drivers/plugins/dubbo2-to-http/to-http.go
Normal 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
|
||||
}
|
133
drivers/plugins/http-to-dubbo2/complete.go
Normal file
133
drivers/plugins/http-to-dubbo2/complete.go
Normal 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
|
||||
}
|
12
drivers/plugins/http-to-dubbo2/config.go
Normal file
12
drivers/plugins/http-to-dubbo2/config.go
Normal 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的根字段名
|
||||
}
|
91
drivers/plugins/http-to-dubbo2/dial.go
Normal file
91
drivers/plugins/http-to-dubbo2/dial.go
Normal 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
|
||||
}
|
||||
}
|
59
drivers/plugins/http-to-dubbo2/driver.go
Normal file
59
drivers/plugins/http-to-dubbo2/driver.go
Normal 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
|
||||
}
|
18
drivers/plugins/http-to-dubbo2/factory.go
Normal file
18
drivers/plugins/http-to-dubbo2/factory.go
Normal 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)
|
||||
}
|
75
drivers/plugins/http-to-dubbo2/to-dubbo2.go
Normal file
75
drivers/plugins/http-to-dubbo2/to-dubbo2.go
Normal 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
|
||||
}
|
195
drivers/plugins/http-to-gRPC/complete.go
Normal file
195
drivers/plugins/http-to-gRPC/complete.go
Normal 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
|
||||
}
|
13
drivers/plugins/http-to-gRPC/config.go
Normal file
13
drivers/plugins/http-to-gRPC/config.go
Normal 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:"额外头部"`
|
||||
}
|
49
drivers/plugins/http-to-gRPC/descriptor.go
Normal file
49
drivers/plugins/http-to-gRPC/descriptor.go
Normal 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
|
||||
}
|
45
drivers/plugins/http-to-gRPC/driver.go
Normal file
45
drivers/plugins/http-to-gRPC/driver.go
Normal 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
|
||||
}
|
18
drivers/plugins/http-to-gRPC/factory.go
Normal file
18
drivers/plugins/http-to-gRPC/factory.go
Normal 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)
|
||||
}
|
18
drivers/plugins/http-to-gRPC/response.go
Normal file
18
drivers/plugins/http-to-gRPC/response.go
Normal 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
|
||||
}
|
56
drivers/plugins/http-to-gRPC/toGRPC.go
Normal file
56
drivers/plugins/http-to-gRPC/toGRPC.go
Normal 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
|
||||
}
|
@@ -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
|
||||
|
@@ -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
|
||||
|
14
drivers/transcode/protobuf/config.go
Normal file
14
drivers/transcode/protobuf/config.go
Normal 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)
|
||||
}
|
10
drivers/transcode/protobuf/driver.go
Normal file
10
drivers/transcode/protobuf/driver.go
Normal 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
|
||||
}
|
27
drivers/transcode/protobuf/factory.go
Normal file
27
drivers/transcode/protobuf/factory.go
Normal 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)
|
||||
}
|
@@ -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()
|
||||
}
|
||||
|
@@ -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() {
|
||||
|
@@ -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
32
go.mod
@@ -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
|
||||
|
20
grpc-descriptor/transcode.go
Normal file
20
grpc-descriptor/transcode.go
Normal 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
|
||||
}
|
@@ -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])
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -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 {
|
||||
|
Reference in New Issue
Block a user