Compare commits
6 Commits
v4.0.1
...
4a9cc0f03f
| Author | SHA1 | Date | |
|---|---|---|---|
| 4a9cc0f03f | |||
| 99af727138 | |||
| 89fe1dd6bc | |||
| 8b591f7fd4 | |||
| bec1705d09 | |||
| 316f644090 |
299
handler.go
299
handler.go
@@ -60,7 +60,280 @@ func (h *httpHandler) Options() server.HandlerOptions {
|
|||||||
return h.opts
|
return h.opts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (h *Server) HTTPHandlerFunc(handler interface{}) (http.HandlerFunc, error) {
|
||||||
|
if handler == nil {
|
||||||
|
return nil, fmt.Errorf("invalid handler specified: %v", handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
rtype := reflect.TypeOf(handler)
|
||||||
|
if rtype.NumIn() != 3 {
|
||||||
|
return nil, fmt.Errorf("invalid handler, NumIn != 3: %v", rtype.NumIn())
|
||||||
|
}
|
||||||
|
|
||||||
|
argType := rtype.In(1)
|
||||||
|
replyType := rtype.In(2)
|
||||||
|
|
||||||
|
// First arg need not be a pointer.
|
||||||
|
if !isExportedOrBuiltinType(argType) {
|
||||||
|
return nil, fmt.Errorf("invalid handler, argument type not exported: %v", argType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if replyType.Kind() != reflect.Ptr {
|
||||||
|
return nil, fmt.Errorf("invalid handler, reply type not a pointer: %v", replyType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply type must be exported.
|
||||||
|
if !isExportedOrBuiltinType(replyType) {
|
||||||
|
return nil, fmt.Errorf("invalid handler, reply type not exported: %v", replyType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rtype.NumOut() != 1 {
|
||||||
|
return nil, fmt.Errorf("invalid handler, has wrong number of outs: %v", rtype.NumOut())
|
||||||
|
}
|
||||||
|
|
||||||
|
// The return type of the method must be error.
|
||||||
|
if returnType := rtype.Out(0); returnType != typeOfError {
|
||||||
|
return nil, fmt.Errorf("invalid handler, returns %v not error", returnType.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ct := DefaultContentType
|
||||||
|
if htype := r.Header.Get(metadata.HeaderContentType); htype != "" {
|
||||||
|
ct = htype
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.WithValue(r.Context(), rspCodeKey{}, &rspCodeVal{})
|
||||||
|
ctx = context.WithValue(ctx, rspHeaderKey{}, &rspHeaderVal{})
|
||||||
|
md, ok := metadata.FromIncomingContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
md = metadata.New(len(r.Header) + 8)
|
||||||
|
}
|
||||||
|
for k, v := range r.Header {
|
||||||
|
md[k] = strings.Join(v, ", ")
|
||||||
|
}
|
||||||
|
md["RemoteAddr"] = r.RemoteAddr
|
||||||
|
md["Method"] = r.Method
|
||||||
|
md["URL"] = r.URL.String()
|
||||||
|
md["Proto"] = r.Proto
|
||||||
|
md["ContentLength"] = fmt.Sprintf("%d", r.ContentLength)
|
||||||
|
md["TransferEncoding"] = strings.Join(r.TransferEncoding, ",")
|
||||||
|
md["Host"] = r.Host
|
||||||
|
md["RequestURI"] = r.RequestURI
|
||||||
|
ctx = metadata.NewIncomingContext(ctx, md)
|
||||||
|
|
||||||
|
path := r.URL.Path
|
||||||
|
|
||||||
|
if r.Body != nil {
|
||||||
|
defer r.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
matches := make(map[string]interface{})
|
||||||
|
var match bool
|
||||||
|
var hldr *patHandler
|
||||||
|
var handler *httpHandler
|
||||||
|
|
||||||
|
for _, shdlr := range h.handlers {
|
||||||
|
hdlr := shdlr.(*httpHandler)
|
||||||
|
fh, mp, err := hdlr.handlers.Search(r.Method, path)
|
||||||
|
if err == nil {
|
||||||
|
match = true
|
||||||
|
for k, v := range mp {
|
||||||
|
matches[k] = v
|
||||||
|
}
|
||||||
|
hldr = fh.(*patHandler)
|
||||||
|
handler = hdlr
|
||||||
|
break
|
||||||
|
} else if err == rhttp.ErrMethodNotAllowed && !h.registerRPC {
|
||||||
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
|
_, _ = w.Write([]byte("not matching route found"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !match && h.registerRPC {
|
||||||
|
microMethod, mok := md.Get(metadata.HeaderEndpoint)
|
||||||
|
if mok {
|
||||||
|
serviceMethod := strings.Split(microMethod, ".")
|
||||||
|
if len(serviceMethod) == 2 {
|
||||||
|
if shdlr, ok := h.handlers[serviceMethod[0]]; ok {
|
||||||
|
hdlr := shdlr.(*httpHandler)
|
||||||
|
fh, mp, err := hdlr.handlers.Search(http.MethodPost, "/"+microMethod)
|
||||||
|
if err == nil {
|
||||||
|
match = true
|
||||||
|
for k, v := range mp {
|
||||||
|
matches[k] = v
|
||||||
|
}
|
||||||
|
hldr = fh.(*patHandler)
|
||||||
|
handler = hdlr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get fields from url values
|
||||||
|
if len(r.URL.RawQuery) > 0 {
|
||||||
|
umd, cerr := rflutil.URLMap(r.URL.RawQuery)
|
||||||
|
if cerr != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
_, _ = w.Write([]byte(cerr.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for k, v := range umd {
|
||||||
|
matches[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cf, err := h.newCodec(ct)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
_, _ = w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var argv, replyv reflect.Value
|
||||||
|
|
||||||
|
// Decode the argument value.
|
||||||
|
argIsValue := false // if true, need to indirect before calling.
|
||||||
|
if hldr.mtype.ArgType.Kind() == reflect.Ptr {
|
||||||
|
argv = reflect.New(hldr.mtype.ArgType.Elem())
|
||||||
|
} else {
|
||||||
|
argv = reflect.New(hldr.mtype.ArgType)
|
||||||
|
argIsValue = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if argIsValue {
|
||||||
|
argv = argv.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
// reply value
|
||||||
|
replyv = reflect.New(hldr.mtype.ReplyType.Elem())
|
||||||
|
|
||||||
|
function := hldr.mtype.method.Func
|
||||||
|
var returnValues []reflect.Value
|
||||||
|
|
||||||
|
if r.Body != nil {
|
||||||
|
var buf []byte
|
||||||
|
buf, err = io.ReadAll(r.Body)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
h.errorHandler(ctx, handler, w, r, err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = cf.Unmarshal(buf, argv.Interface()); err != nil {
|
||||||
|
h.errorHandler(ctx, handler, w, r, err, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matches = rflutil.FlattenMap(matches)
|
||||||
|
if err = rflutil.Merge(argv.Interface(), matches, rflutil.SliceAppend(true), rflutil.Tags([]string{"protobuf", "json"})); err != nil {
|
||||||
|
h.errorHandler(ctx, handler, w, r, err, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hr := &rpcRequest{
|
||||||
|
codec: cf,
|
||||||
|
service: handler.sopts.Name,
|
||||||
|
contentType: ct,
|
||||||
|
method: fmt.Sprintf("%s.%s", hldr.name, hldr.mtype.method.Name),
|
||||||
|
endpoint: fmt.Sprintf("%s.%s", hldr.name, hldr.mtype.method.Name),
|
||||||
|
payload: argv.Interface(),
|
||||||
|
header: md,
|
||||||
|
}
|
||||||
|
|
||||||
|
// define the handler func
|
||||||
|
fn := func(fctx context.Context, req server.Request, rsp interface{}) (err error) {
|
||||||
|
returnValues = function.Call([]reflect.Value{hldr.rcvr, hldr.mtype.prepareContext(fctx), argv, reflect.ValueOf(rsp)})
|
||||||
|
|
||||||
|
// The return value for the method is an error.
|
||||||
|
if rerr := returnValues[0].Interface(); rerr != nil {
|
||||||
|
err = rerr.(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
md, ok := metadata.FromOutgoingContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
md = metadata.New(0)
|
||||||
|
}
|
||||||
|
if nmd, ok := metadata.FromOutgoingContext(fctx); ok {
|
||||||
|
for k, v := range nmd {
|
||||||
|
md.Set(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
metadata.SetOutgoingContext(ctx, md)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrap the handler func
|
||||||
|
for i := len(handler.sopts.HdlrWrappers); i > 0; i-- {
|
||||||
|
fn = handler.sopts.HdlrWrappers[i-1](fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ct == "application/x-www-form-urlencoded" {
|
||||||
|
cf, err = h.newCodec(DefaultContentType)
|
||||||
|
if err != nil {
|
||||||
|
h.errorHandler(ctx, handler, w, r, err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ct = DefaultContentType
|
||||||
|
}
|
||||||
|
|
||||||
|
scode := int(200)
|
||||||
|
appErr := fn(ctx, hr, replyv.Interface())
|
||||||
|
|
||||||
|
w.Header().Set(metadata.HeaderContentType, ct)
|
||||||
|
if md, ok := metadata.FromOutgoingContext(ctx); ok {
|
||||||
|
for k, v := range md {
|
||||||
|
w.Header().Set(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if md := getRspHeader(ctx); md != nil {
|
||||||
|
for k, v := range md {
|
||||||
|
for _, vv := range v {
|
||||||
|
w.Header().Add(k, vv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nct := w.Header().Get(metadata.HeaderContentType); nct != ct {
|
||||||
|
if cf, err = h.newCodec(nct); err != nil {
|
||||||
|
h.errorHandler(ctx, nil, w, r, err, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf []byte
|
||||||
|
if appErr != nil {
|
||||||
|
switch verr := appErr.(type) {
|
||||||
|
case *errors.Error:
|
||||||
|
scode = int(verr.Code)
|
||||||
|
buf, err = cf.Marshal(verr)
|
||||||
|
case *Error:
|
||||||
|
buf, err = cf.Marshal(verr.err)
|
||||||
|
default:
|
||||||
|
buf, err = cf.Marshal(appErr)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buf, err = cf.Marshal(replyv.Interface())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil && handler.sopts.Logger.V(logger.ErrorLevel) {
|
||||||
|
handler.sopts.Logger.Errorf(handler.sopts.Context, "handler err: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if nscode := GetRspCode(ctx); nscode != 0 {
|
||||||
|
scode = nscode
|
||||||
|
}
|
||||||
|
w.WriteHeader(scode)
|
||||||
|
|
||||||
|
if _, cerr := w.Write(buf); cerr != nil {
|
||||||
|
handler.sopts.Logger.Errorf(ctx, "write failed: %v", cerr)
|
||||||
|
}
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
// check for http.HandlerFunc handlers
|
// check for http.HandlerFunc handlers
|
||||||
if ph, _, err := h.pathHandlers.Search(r.Method, r.URL.Path); err == nil {
|
if ph, _, err := h.pathHandlers.Search(r.Method, r.URL.Path); err == nil {
|
||||||
ph.(http.HandlerFunc)(w, r)
|
ph.(http.HandlerFunc)(w, r)
|
||||||
@@ -91,7 +364,9 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
md["RequestURI"] = r.RequestURI
|
md["RequestURI"] = r.RequestURI
|
||||||
ctx = metadata.NewIncomingContext(ctx, md)
|
ctx = metadata.NewIncomingContext(ctx, md)
|
||||||
|
|
||||||
defer r.Body.Close()
|
if r.Body != nil {
|
||||||
|
defer r.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
path := r.URL.Path
|
path := r.URL.Path
|
||||||
if !strings.HasPrefix(path, "/") {
|
if !strings.HasPrefix(path, "/") {
|
||||||
@@ -192,15 +467,18 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
function := hldr.mtype.method.Func
|
function := hldr.mtype.method.Func
|
||||||
var returnValues []reflect.Value
|
var returnValues []reflect.Value
|
||||||
|
|
||||||
buf, err := io.ReadAll(r.Body)
|
if r.Body != nil {
|
||||||
if err != nil && err != io.EOF {
|
var buf []byte
|
||||||
h.errorHandler(ctx, handler, w, r, err, http.StatusInternalServerError)
|
buf, err = io.ReadAll(r.Body)
|
||||||
return
|
if err != nil && err != io.EOF {
|
||||||
}
|
h.errorHandler(ctx, handler, w, r, err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err = cf.Unmarshal(buf, argv.Interface()); err != nil {
|
if err = cf.Unmarshal(buf, argv.Interface()); err != nil {
|
||||||
h.errorHandler(ctx, handler, w, r, err, http.StatusBadRequest)
|
h.errorHandler(ctx, handler, w, r, err, http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
matches = rflutil.FlattenMap(matches)
|
matches = rflutil.FlattenMap(matches)
|
||||||
@@ -279,6 +557,7 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var buf []byte
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
switch verr := appErr.(type) {
|
switch verr := appErr.(type) {
|
||||||
case *errors.Error:
|
case *errors.Error:
|
||||||
|
|||||||
34
http.go
34
http.go
@@ -23,9 +23,9 @@ import (
|
|||||||
"golang.org/x/net/netutil"
|
"golang.org/x/net/netutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ server.Server = &httpServer{}
|
var _ server.Server = (*Server)(nil)
|
||||||
|
|
||||||
type httpServer struct {
|
type Server struct {
|
||||||
hd server.Handler
|
hd server.Handler
|
||||||
rsvc *register.Service
|
rsvc *register.Service
|
||||||
handlers map[string]server.Handler
|
handlers map[string]server.Handler
|
||||||
@@ -40,7 +40,7 @@ type httpServer struct {
|
|||||||
init bool
|
init bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpServer) newCodec(ct string) (codec.Codec, error) {
|
func (h *Server) newCodec(ct string) (codec.Codec, error) {
|
||||||
if idx := strings.IndexRune(ct, ';'); idx >= 0 {
|
if idx := strings.IndexRune(ct, ';'); idx >= 0 {
|
||||||
ct = ct[:idx]
|
ct = ct[:idx]
|
||||||
}
|
}
|
||||||
@@ -53,14 +53,14 @@ func (h *httpServer) newCodec(ct string) (codec.Codec, error) {
|
|||||||
return nil, codec.ErrUnknownContentType
|
return nil, codec.ErrUnknownContentType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpServer) Options() server.Options {
|
func (h *Server) Options() server.Options {
|
||||||
h.Lock()
|
h.Lock()
|
||||||
opts := h.opts
|
opts := h.opts
|
||||||
h.Unlock()
|
h.Unlock()
|
||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpServer) Init(opts ...server.Option) error {
|
func (h *Server) Init(opts ...server.Option) error {
|
||||||
if len(opts) == 0 && h.init {
|
if len(opts) == 0 && h.init {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -130,7 +130,7 @@ func (h *httpServer) Init(opts ...server.Option) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpServer) Handle(handler server.Handler) error {
|
func (h *Server) Handle(handler server.Handler) error {
|
||||||
// passed unknown handler
|
// passed unknown handler
|
||||||
hdlr, ok := handler.(*httpHandler)
|
hdlr, ok := handler.(*httpHandler)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -159,7 +159,7 @@ func (h *httpServer) Handle(handler server.Handler) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpServer) NewHandler(handler interface{}, opts ...server.HandlerOption) server.Handler {
|
func (h *Server) NewHandler(handler interface{}, opts ...server.HandlerOption) server.Handler {
|
||||||
options := server.NewHandlerOptions(opts...)
|
options := server.NewHandlerOptions(opts...)
|
||||||
|
|
||||||
eps := make([]*register.Endpoint, 0, len(options.Metadata))
|
eps := make([]*register.Endpoint, 0, len(options.Metadata))
|
||||||
@@ -288,11 +288,11 @@ func (h *httpServer) NewHandler(handler interface{}, opts ...server.HandlerOptio
|
|||||||
return hdlr
|
return hdlr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpServer) NewSubscriber(topic string, handler interface{}, opts ...server.SubscriberOption) server.Subscriber {
|
func (h *Server) NewSubscriber(topic string, handler interface{}, opts ...server.SubscriberOption) server.Subscriber {
|
||||||
return newSubscriber(topic, handler, opts...)
|
return newSubscriber(topic, handler, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpServer) Subscribe(sb server.Subscriber) error {
|
func (h *Server) Subscribe(sb server.Subscriber) error {
|
||||||
sub, ok := sb.(*httpSubscriber)
|
sub, ok := sb.(*httpSubscriber)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("invalid subscriber: expected *httpSubscriber")
|
return fmt.Errorf("invalid subscriber: expected *httpSubscriber")
|
||||||
@@ -317,7 +317,7 @@ func (h *httpServer) Subscribe(sb server.Subscriber) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpServer) Register() error {
|
func (h *Server) Register() error {
|
||||||
var eps []*register.Endpoint
|
var eps []*register.Endpoint
|
||||||
h.RLock()
|
h.RLock()
|
||||||
for _, hdlr := range h.handlers {
|
for _, hdlr := range h.handlers {
|
||||||
@@ -408,7 +408,7 @@ func (h *httpServer) Register() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpServer) Deregister() error {
|
func (h *Server) Deregister() error {
|
||||||
h.RLock()
|
h.RLock()
|
||||||
config := h.opts
|
config := h.opts
|
||||||
h.RUnlock()
|
h.RUnlock()
|
||||||
@@ -456,7 +456,7 @@ func (h *httpServer) Deregister() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpServer) Start() error {
|
func (h *Server) Start() error {
|
||||||
h.RLock()
|
h.RLock()
|
||||||
config := h.opts
|
config := h.opts
|
||||||
h.RUnlock()
|
h.RUnlock()
|
||||||
@@ -636,27 +636,27 @@ func (h *httpServer) Start() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpServer) Stop() error {
|
func (h *Server) Stop() error {
|
||||||
ch := make(chan error)
|
ch := make(chan error)
|
||||||
h.exit <- ch
|
h.exit <- ch
|
||||||
return <-ch
|
return <-ch
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpServer) String() string {
|
func (h *Server) String() string {
|
||||||
return "http"
|
return "http"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpServer) Name() string {
|
func (h *Server) Name() string {
|
||||||
return h.opts.Name
|
return h.opts.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(opts ...server.Option) *httpServer {
|
func NewServer(opts ...server.Option) *Server {
|
||||||
options := server.NewOptions(opts...)
|
options := server.NewOptions(opts...)
|
||||||
eh := DefaultErrorHandler
|
eh := DefaultErrorHandler
|
||||||
if v, ok := options.Context.Value(errorHandlerKey{}).(errorHandler); ok && v != nil {
|
if v, ok := options.Context.Value(errorHandlerKey{}).(errorHandler); ok && v != nil {
|
||||||
eh = v
|
eh = v
|
||||||
}
|
}
|
||||||
return &httpServer{
|
return &Server{
|
||||||
opts: options,
|
opts: options,
|
||||||
exit: make(chan chan error),
|
exit: make(chan chan error),
|
||||||
subscribers: make(map[*httpSubscriber][]broker.Subscriber),
|
subscribers: make(map[*httpSubscriber][]broker.Subscriber),
|
||||||
|
|||||||
33
request.go
33
request.go
@@ -8,10 +8,7 @@ import (
|
|||||||
"go.unistack.org/micro/v4/server"
|
"go.unistack.org/micro/v4/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var _ server.Request = &rpcRequest{}
|
||||||
_ server.Request = &rpcRequest{}
|
|
||||||
_ server.Message = &rpcMessage{}
|
|
||||||
)
|
|
||||||
|
|
||||||
type rpcRequest struct {
|
type rpcRequest struct {
|
||||||
rw io.ReadWriter
|
rw io.ReadWriter
|
||||||
@@ -25,14 +22,6 @@ type rpcRequest struct {
|
|||||||
stream bool
|
stream bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type rpcMessage struct {
|
|
||||||
payload interface{}
|
|
||||||
codec codec.Codec
|
|
||||||
header metadata.Metadata
|
|
||||||
topic string
|
|
||||||
contentType string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rpcRequest) ContentType() string {
|
func (r *rpcRequest) ContentType() string {
|
||||||
return r.contentType
|
return r.contentType
|
||||||
}
|
}
|
||||||
@@ -72,23 +61,3 @@ func (r *rpcRequest) Stream() bool {
|
|||||||
func (r *rpcRequest) Body() interface{} {
|
func (r *rpcRequest) Body() interface{} {
|
||||||
return r.payload
|
return r.payload
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpcMessage) ContentType() string {
|
|
||||||
return r.contentType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rpcMessage) Topic() string {
|
|
||||||
return r.topic
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rpcMessage) Body() interface{} {
|
|
||||||
return r.payload
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rpcMessage) Header() metadata.Metadata {
|
|
||||||
return r.header
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rpcMessage) Codec() codec.Codec {
|
|
||||||
return r.codec
|
|
||||||
}
|
|
||||||
|
|||||||
208
subscriber.go
208
subscriber.go
@@ -1,208 +0,0 @@
|
|||||||
package http
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"go.unistack.org/micro/v4/broker"
|
|
||||||
"go.unistack.org/micro/v4/codec"
|
|
||||||
"go.unistack.org/micro/v4/metadata"
|
|
||||||
"go.unistack.org/micro/v4/register"
|
|
||||||
"go.unistack.org/micro/v4/server"
|
|
||||||
)
|
|
||||||
|
|
||||||
var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
|
|
||||||
|
|
||||||
type handler struct {
|
|
||||||
reqType reflect.Type
|
|
||||||
ctxType reflect.Type
|
|
||||||
method reflect.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
type httpSubscriber struct {
|
|
||||||
topic string
|
|
||||||
rcvr reflect.Value
|
|
||||||
typ reflect.Type
|
|
||||||
subscriber interface{}
|
|
||||||
handlers []*handler
|
|
||||||
endpoints []*register.Endpoint
|
|
||||||
opts server.SubscriberOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSubscriber(topic string, sub interface{}, opts ...server.SubscriberOption) server.Subscriber {
|
|
||||||
options := server.NewSubscriberOptions(opts...)
|
|
||||||
|
|
||||||
var endpoints []*register.Endpoint
|
|
||||||
var handlers []*handler
|
|
||||||
|
|
||||||
if typ := reflect.TypeOf(sub); typ.Kind() == reflect.Func {
|
|
||||||
h := &handler{
|
|
||||||
method: reflect.ValueOf(sub),
|
|
||||||
}
|
|
||||||
|
|
||||||
switch typ.NumIn() {
|
|
||||||
case 1:
|
|
||||||
h.reqType = typ.In(0)
|
|
||||||
case 2:
|
|
||||||
h.ctxType = typ.In(0)
|
|
||||||
h.reqType = typ.In(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
handlers = append(handlers, h)
|
|
||||||
ep := ®ister.Endpoint{
|
|
||||||
Name: "Func",
|
|
||||||
Request: register.ExtractSubValue(typ),
|
|
||||||
Metadata: metadata.New(2),
|
|
||||||
}
|
|
||||||
ep.Metadata.Set("topic", topic)
|
|
||||||
ep.Metadata.Set("subscriber", "true")
|
|
||||||
endpoints = append(endpoints, ep)
|
|
||||||
} else {
|
|
||||||
hdlr := reflect.ValueOf(sub)
|
|
||||||
name := reflect.Indirect(hdlr).Type().Name()
|
|
||||||
|
|
||||||
for m := 0; m < typ.NumMethod(); m++ {
|
|
||||||
method := typ.Method(m)
|
|
||||||
h := &handler{
|
|
||||||
method: method.Func,
|
|
||||||
}
|
|
||||||
|
|
||||||
switch method.Type.NumIn() {
|
|
||||||
case 2:
|
|
||||||
h.reqType = method.Type.In(1)
|
|
||||||
case 3:
|
|
||||||
h.ctxType = method.Type.In(1)
|
|
||||||
h.reqType = method.Type.In(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
handlers = append(handlers, h)
|
|
||||||
ep := ®ister.Endpoint{
|
|
||||||
Name: name + "." + method.Name,
|
|
||||||
Request: register.ExtractSubValue(method.Type),
|
|
||||||
Metadata: metadata.New(2),
|
|
||||||
}
|
|
||||||
ep.Metadata.Set("topic", topic)
|
|
||||||
ep.Metadata.Set("subscriber", "true")
|
|
||||||
endpoints = append(endpoints, ep)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &httpSubscriber{
|
|
||||||
rcvr: reflect.ValueOf(sub),
|
|
||||||
typ: reflect.TypeOf(sub),
|
|
||||||
topic: topic,
|
|
||||||
subscriber: sub,
|
|
||||||
handlers: handlers,
|
|
||||||
endpoints: endpoints,
|
|
||||||
opts: options,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *httpServer) createSubHandler(sb *httpSubscriber, opts server.Options) broker.Handler {
|
|
||||||
return func(p broker.Event) error {
|
|
||||||
msg := p.Message()
|
|
||||||
ct := msg.Header["Content-Type"]
|
|
||||||
cf, err := s.newCodec(ct)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
hdr := metadata.Copy(msg.Header)
|
|
||||||
delete(hdr, "Content-Type")
|
|
||||||
ctx := metadata.NewIncomingContext(context.Background(), hdr)
|
|
||||||
|
|
||||||
results := make(chan error, len(sb.handlers))
|
|
||||||
|
|
||||||
for i := 0; i < len(sb.handlers); i++ {
|
|
||||||
handler := sb.handlers[i]
|
|
||||||
|
|
||||||
var isVal bool
|
|
||||||
var req reflect.Value
|
|
||||||
|
|
||||||
if handler.reqType.Kind() == reflect.Ptr {
|
|
||||||
req = reflect.New(handler.reqType.Elem())
|
|
||||||
} else {
|
|
||||||
req = reflect.New(handler.reqType)
|
|
||||||
isVal = true
|
|
||||||
}
|
|
||||||
if isVal {
|
|
||||||
req = req.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := bytes.NewBuffer(msg.Body)
|
|
||||||
|
|
||||||
if err := cf.ReadHeader(buf, &codec.Message{}, codec.Event); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := cf.ReadBody(buf, req.Interface()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fn := func(ctx context.Context, msg server.Message) error {
|
|
||||||
var vals []reflect.Value
|
|
||||||
if sb.typ.Kind() != reflect.Func {
|
|
||||||
vals = append(vals, sb.rcvr)
|
|
||||||
}
|
|
||||||
if handler.ctxType != nil {
|
|
||||||
vals = append(vals, reflect.ValueOf(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
vals = append(vals, reflect.ValueOf(msg.Body()))
|
|
||||||
|
|
||||||
returnValues := handler.method.Call(vals)
|
|
||||||
if err := returnValues[0].Interface(); err != nil {
|
|
||||||
return err.(error)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := len(opts.SubWrappers); i > 0; i-- {
|
|
||||||
fn = opts.SubWrappers[i-1](fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
results <- fn(ctx, &httpMessage{
|
|
||||||
topic: sb.topic,
|
|
||||||
contentType: ct,
|
|
||||||
payload: req.Interface(),
|
|
||||||
header: msg.Header,
|
|
||||||
codec: cf,
|
|
||||||
})
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
var errors []string
|
|
||||||
|
|
||||||
for i := 0; i < len(sb.handlers); i++ {
|
|
||||||
if err := <-results; err != nil {
|
|
||||||
errors = append(errors, err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(errors) > 0 {
|
|
||||||
return fmt.Errorf("subscriber error: %s", strings.Join(errors, "\n"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *httpSubscriber) Topic() string {
|
|
||||||
return s.topic
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *httpSubscriber) Subscriber() interface{} {
|
|
||||||
return s.subscriber
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *httpSubscriber) Endpoints() []*register.Endpoint {
|
|
||||||
return s.endpoints
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *httpSubscriber) Options() server.SubscriberOptions {
|
|
||||||
return s.opts
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user