Compare commits
14 Commits
3fdff4312c
...
v4.0.8
Author | SHA1 | Date | |
---|---|---|---|
bcd27b833a | |||
090b5e3c07 | |||
95109c9dc2 | |||
69dcf71d3f | |||
0a0a986a70 | |||
76fe748e4a | |||
9926d52f78 | |||
2ed04e3e24 | |||
4a9cc0f03f | |||
99af727138 | |||
89fe1dd6bc | |||
8b591f7fd4 | |||
bec1705d09 | |||
316f644090 |
329
handler.go
329
handler.go
@@ -60,13 +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) {
|
||||||
// check for http.HandlerFunc handlers
|
if handler == nil {
|
||||||
if ph, _, err := h.pathHandlers.Search(r.Method, r.URL.Path); err == nil {
|
return nil, fmt.Errorf("invalid handler specified: %v", handler)
|
||||||
ph.(http.HandlerFunc)(w, r)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
ct := DefaultContentType
|
ct := DefaultContentType
|
||||||
if htype := r.Header.Get(metadata.HeaderContentType); htype != "" {
|
if htype := r.Header.Get(metadata.HeaderContentType); htype != "" {
|
||||||
ct = htype
|
ct = htype
|
||||||
@@ -82,17 +349,22 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
md[k] = strings.Join(v, ", ")
|
md[k] = strings.Join(v, ", ")
|
||||||
}
|
}
|
||||||
md["RemoteAddr"] = r.RemoteAddr
|
md["RemoteAddr"] = r.RemoteAddr
|
||||||
|
if r.TLS != nil {
|
||||||
|
md["Scheme"] = "https"
|
||||||
|
} else {
|
||||||
|
md["Scheme"] = "http"
|
||||||
|
}
|
||||||
md["Method"] = r.Method
|
md["Method"] = r.Method
|
||||||
md["URL"] = r.URL.String()
|
md["URL"] = r.URL.String()
|
||||||
md["Proto"] = r.Proto
|
md["Proto"] = r.Proto
|
||||||
md["ContentLength"] = fmt.Sprintf("%d", r.ContentLength)
|
md["ContentLength"] = fmt.Sprintf("%d", r.ContentLength)
|
||||||
md["TransferEncoding"] = strings.Join(r.TransferEncoding, ",")
|
if len(r.TransferEncoding) > 0 {
|
||||||
|
md["TransferEncoding"] = strings.Join(r.TransferEncoding, ",")
|
||||||
|
}
|
||||||
md["Host"] = r.Host
|
md["Host"] = r.Host
|
||||||
md["RequestURI"] = r.RequestURI
|
md["RequestURI"] = r.RequestURI
|
||||||
ctx = metadata.NewIncomingContext(ctx, md)
|
ctx = metadata.NewIncomingContext(ctx, md)
|
||||||
|
|
||||||
defer r.Body.Close()
|
|
||||||
|
|
||||||
path := r.URL.Path
|
path := r.URL.Path
|
||||||
if !strings.HasPrefix(path, "/") {
|
if !strings.HasPrefix(path, "/") {
|
||||||
h.errorHandler(ctx, nil, w, r, fmt.Errorf("path must starts with /"), http.StatusBadRequest)
|
h.errorHandler(ctx, nil, w, r, fmt.Errorf("path must starts with /"), http.StatusBadRequest)
|
||||||
@@ -149,6 +421,11 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else if !match {
|
} else if !match {
|
||||||
|
// check for http.HandlerFunc handlers
|
||||||
|
if ph, _, err := h.pathHandlers.Search(r.Method, r.URL.Path); err == nil {
|
||||||
|
ph.(http.HandlerFunc)(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
h.errorHandler(ctx, nil, w, r, fmt.Errorf("not matching route found"), http.StatusNotFound)
|
h.errorHandler(ctx, nil, w, r, fmt.Errorf("not matching route found"), http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -165,6 +442,10 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if r.Body != nil {
|
||||||
|
defer r.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
cf, err := h.newCodec(ct)
|
cf, err := h.newCodec(ct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.errorHandler(ctx, nil, w, r, err, http.StatusBadRequest)
|
h.errorHandler(ctx, nil, w, r, err, http.StatusBadRequest)
|
||||||
@@ -192,21 +473,26 @@ 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 {
|
||||||
|
h.errorHandler(ctx, handler, w, r, err, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = cf.Unmarshal(buf, argv.Interface()); err != nil {
|
if len(matches) > 0 {
|
||||||
h.errorHandler(ctx, handler, w, r, err, http.StatusBadRequest)
|
matches = rflutil.FlattenMap(matches)
|
||||||
return
|
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
|
||||||
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{
|
hr := &rpcRequest{
|
||||||
@@ -279,6 +565,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:
|
||||||
|
130
http.go
130
http.go
@@ -9,12 +9,10 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.unistack.org/micro/v4/broker"
|
|
||||||
"go.unistack.org/micro/v4/codec"
|
"go.unistack.org/micro/v4/codec"
|
||||||
"go.unistack.org/micro/v4/logger"
|
"go.unistack.org/micro/v4/logger"
|
||||||
"go.unistack.org/micro/v4/register"
|
"go.unistack.org/micro/v4/register"
|
||||||
@@ -23,14 +21,13 @@ 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
|
||||||
exit chan chan error
|
exit chan chan error
|
||||||
subscribers map[*httpSubscriber][]broker.Subscriber
|
|
||||||
errorHandler func(context.Context, server.Handler, http.ResponseWriter, *http.Request, error, int)
|
errorHandler func(context.Context, server.Handler, http.ResponseWriter, *http.Request, error, int)
|
||||||
pathHandlers *rhttp.Trie
|
pathHandlers *rhttp.Trie
|
||||||
opts server.Options
|
opts server.Options
|
||||||
@@ -40,7 +37,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 +50,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
|
||||||
}
|
}
|
||||||
@@ -101,10 +98,6 @@ func (h *httpServer) Init(opts ...server.Option) error {
|
|||||||
h.RUnlock()
|
h.RUnlock()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := h.opts.Broker.Init(); err != nil {
|
|
||||||
h.RUnlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := h.opts.Tracer.Init(); err != nil {
|
if err := h.opts.Tracer.Init(); err != nil {
|
||||||
h.RUnlock()
|
h.RUnlock()
|
||||||
return err
|
return err
|
||||||
@@ -130,7 +123,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 +152,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,36 +281,7 @@ 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) Register() error {
|
||||||
return newSubscriber(topic, handler, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *httpServer) Subscribe(sb server.Subscriber) error {
|
|
||||||
sub, ok := sb.(*httpSubscriber)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("invalid subscriber: expected *httpSubscriber")
|
|
||||||
}
|
|
||||||
if len(sub.handlers) == 0 {
|
|
||||||
return fmt.Errorf("invalid subscriber: no handler functions")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := server.ValidateSubscriber(sb); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
h.RLock()
|
|
||||||
_, ok = h.subscribers[sub]
|
|
||||||
h.RUnlock()
|
|
||||||
if ok {
|
|
||||||
return fmt.Errorf("subscriber %v already exists", h)
|
|
||||||
}
|
|
||||||
h.Lock()
|
|
||||||
h.subscribers[sub] = nil
|
|
||||||
h.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *httpServer) Register() error {
|
|
||||||
var eps []*register.Endpoint
|
var eps []*register.Endpoint
|
||||||
h.RLock()
|
h.RLock()
|
||||||
for _, hdlr := range h.handlers {
|
for _, hdlr := range h.handlers {
|
||||||
@@ -342,21 +306,6 @@ func (h *httpServer) Register() error {
|
|||||||
service.Nodes[0].Metadata["protocol"] = "http"
|
service.Nodes[0].Metadata["protocol"] = "http"
|
||||||
service.Endpoints = eps
|
service.Endpoints = eps
|
||||||
|
|
||||||
h.Lock()
|
|
||||||
subscriberList := make([]*httpSubscriber, 0, len(h.subscribers))
|
|
||||||
for e := range h.subscribers {
|
|
||||||
// Only advertise non internal subscribers
|
|
||||||
subscriberList = append(subscriberList, e)
|
|
||||||
}
|
|
||||||
sort.Slice(subscriberList, func(i, j int) bool {
|
|
||||||
return subscriberList[i].topic > subscriberList[j].topic
|
|
||||||
})
|
|
||||||
|
|
||||||
for _, e := range subscriberList {
|
|
||||||
service.Endpoints = append(service.Endpoints, e.Endpoints()...)
|
|
||||||
}
|
|
||||||
h.Unlock()
|
|
||||||
|
|
||||||
h.RLock()
|
h.RLock()
|
||||||
registered := h.registered
|
registered := h.registered
|
||||||
h.RUnlock()
|
h.RUnlock()
|
||||||
@@ -378,29 +327,6 @@ func (h *httpServer) Register() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
h.Lock()
|
h.Lock()
|
||||||
for sb := range h.subscribers {
|
|
||||||
handler := h.createSubHandler(sb, config)
|
|
||||||
var opts []broker.SubscribeOption
|
|
||||||
if queue := sb.Options().Queue; len(queue) > 0 {
|
|
||||||
opts = append(opts, broker.SubscribeGroup(queue))
|
|
||||||
}
|
|
||||||
|
|
||||||
subCtx := config.Context
|
|
||||||
if cx := sb.Options().Context; cx != nil {
|
|
||||||
subCtx = cx
|
|
||||||
}
|
|
||||||
opts = append(opts, broker.SubscribeContext(subCtx))
|
|
||||||
opts = append(opts, broker.SubscribeAutoAck(sb.Options().AutoAck))
|
|
||||||
opts = append(opts, broker.SubscribeBodyOnly(sb.Options().BodyOnly))
|
|
||||||
|
|
||||||
sub, err := config.Broker.Subscribe(subCtx, sb.Topic(), handler, opts...)
|
|
||||||
if err != nil {
|
|
||||||
h.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
h.subscribers[sb] = []broker.Subscriber{sub}
|
|
||||||
}
|
|
||||||
|
|
||||||
h.registered = true
|
h.registered = true
|
||||||
h.rsvc = service
|
h.rsvc = service
|
||||||
h.Unlock()
|
h.Unlock()
|
||||||
@@ -408,7 +334,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()
|
||||||
@@ -435,28 +361,11 @@ func (h *httpServer) Deregister() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
h.registered = false
|
h.registered = false
|
||||||
|
|
||||||
subCtx := h.opts.Context
|
|
||||||
for sb, subs := range h.subscribers {
|
|
||||||
if cx := sb.Options().Context; cx != nil {
|
|
||||||
subCtx = cx
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, sub := range subs {
|
|
||||||
config.Logger.Infof(config.Context, "Unsubscribing from topic: %s", sub.Topic())
|
|
||||||
if err := sub.Unsubscribe(subCtx); err != nil {
|
|
||||||
h.Unlock()
|
|
||||||
config.Logger.Errorf(config.Context, "failed to unsubscribe topic: %s, error: %v", sb.Topic(), err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h.subscribers[sb] = nil
|
|
||||||
}
|
|
||||||
h.Unlock()
|
h.Unlock()
|
||||||
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()
|
||||||
@@ -525,10 +434,6 @@ func (h *httpServer) Start() error {
|
|||||||
return fmt.Errorf("cant process with nil handler")
|
return fmt.Errorf("cant process with nil handler")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := config.Broker.Connect(h.opts.Context); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := config.RegisterCheck(h.opts.Context); err != nil {
|
if err := config.RegisterCheck(h.opts.Context); err != nil {
|
||||||
if config.Logger.V(logger.ErrorLevel) {
|
if config.Logger.V(logger.ErrorLevel) {
|
||||||
config.Logger.Errorf(config.Context, "Server %s-%s register check error: %s", config.Name, config.ID, err)
|
config.Logger.Errorf(config.Context, "Server %s-%s register check error: %s", config.Name, config.ID, err)
|
||||||
@@ -626,40 +531,35 @@ func (h *httpServer) Start() error {
|
|||||||
config.Logger.Errorf(config.Context, "Server deregister error: %s", err)
|
config.Logger.Errorf(config.Context, "Server deregister error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := config.Broker.Disconnect(config.Context); err != nil {
|
|
||||||
config.Logger.Errorf(config.Context, "Broker disconnect error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ch <- ts.Close()
|
ch <- ts.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
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),
|
|
||||||
errorHandler: eh,
|
errorHandler: eh,
|
||||||
pathHandlers: rhttp.NewTrie(),
|
pathHandlers: rhttp.NewTrie(),
|
||||||
}
|
}
|
||||||
|
@@ -85,8 +85,8 @@ func Middleware(mw ...func(http.Handler) http.Handler) server.Option {
|
|||||||
|
|
||||||
type serverKey struct{}
|
type serverKey struct{}
|
||||||
|
|
||||||
// Server provide ability to pass *http.Server
|
// HTTPServer provide ability to pass *http.Server
|
||||||
func Server(hs *http.Server) server.Option {
|
func HTTPServer(hs *http.Server) server.Option {
|
||||||
return server.SetOption(serverKey{}, hs)
|
return server.SetOption(serverKey{}, hs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
|
||||||
}
|
|
||||||
|
@@ -10,6 +10,8 @@ import (
|
|||||||
"go.unistack.org/micro/v4/server"
|
"go.unistack.org/micro/v4/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
|
||||||
|
|
||||||
type methodType struct {
|
type methodType struct {
|
||||||
ArgType reflect.Type
|
ArgType reflect.Type
|
||||||
ReplyType reflect.Type
|
ReplyType reflect.Type
|
||||||
|
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