Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
Василий Толстов 2021-04-26 00:35:33 +03:00
parent a215521821
commit 4bf874bfe7
10 changed files with 118 additions and 81 deletions

13
.github/stale.sh vendored
View File

@ -1,13 +0,0 @@
#!/bin/bash -ex
export PATH=$PATH:$(pwd)/bin
export GO111MODULE=on
export GOBIN=$(pwd)/bin
#go get github.com/rvflash/goup@v0.4.1
#goup -v ./...
#go get github.com/psampaz/go-mod-outdated@v0.6.0
go list -u -m -mod=mod -json all | go-mod-outdated -update -direct -ci || true
#go list -u -m -json all | go-mod-outdated -update

View File

@ -34,10 +34,9 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: lint - name: lint
uses: golangci/golangci-lint-action@v2 uses: golangci/golangci-lint-action@v2
continue-on-error: true
with: with:
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
version: v1.30 version: v1.39
# Optional: working directory, useful for monorepos # Optional: working directory, useful for monorepos
# working-directory: somedir # working-directory: somedir
# Optional: golangci-lint command line arguments. # Optional: golangci-lint command line arguments.

View File

@ -34,10 +34,9 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: lint - name: lint
uses: golangci/golangci-lint-action@v2 uses: golangci/golangci-lint-action@v2
continue-on-error: true
with: with:
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
version: v1.30 version: v1.39
# Optional: working directory, useful for monorepos # Optional: working directory, useful for monorepos
# working-directory: somedir # working-directory: somedir
# Optional: golangci-lint command line arguments. # Optional: golangci-lint command line arguments.

44
.golangci.yml Normal file
View File

@ -0,0 +1,44 @@
run:
concurrency: 4
deadline: 5m
issues-exit-code: 1
tests: true
linters-settings:
govet:
check-shadowing: true
enable:
- fieldalignment
linters:
enable:
- govet
- deadcode
- errcheck
- govet
- ineffassign
- staticcheck
- structcheck
- typecheck
- unused
- varcheck
- bodyclose
- gci
- goconst
- gocritic
- gosimple
- gofmt
- gofumpt
- goimports
- golint
- gosec
- makezero
- misspell
- nakedret
- nestif
- nilerr
- noctx
- prealloc
- unconvert
- unparam
disable-all: false

View File

@ -9,7 +9,6 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/unistack-org/micro/v3/codec"
"github.com/unistack-org/micro/v3/errors" "github.com/unistack-org/micro/v3/errors"
"github.com/unistack-org/micro/v3/logger" "github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/metadata" "github.com/unistack-org/micro/v3/metadata"
@ -22,7 +21,9 @@ import (
var ( var (
DefaultErrorHandler = func(ctx context.Context, s server.Handler, w http.ResponseWriter, r *http.Request, err error, status int) { DefaultErrorHandler = func(ctx context.Context, s server.Handler, w http.ResponseWriter, r *http.Request, err error, status int) {
w.WriteHeader(status) w.WriteHeader(status)
w.Write([]byte(err.Error())) if _, cerr := w.Write([]byte(err.Error())); cerr != nil {
logger.DefaultLogger.Errorf(ctx, "write failed: %v", cerr)
}
} }
DefaultContentType = "application/json" DefaultContentType = "application/json"
) )
@ -44,20 +45,6 @@ type httpHandler struct {
sync.RWMutex sync.RWMutex
} }
func (h *httpHandler) newCodec(ct string) (codec.Codec, error) {
h.RLock()
defer h.RUnlock()
if idx := strings.IndexRune(ct, ';'); idx >= 0 {
ct = ct[:idx]
}
if cf, ok := h.sopts.Codecs[ct]; ok {
return cf, nil
}
return nil, codec.ErrUnknownContentType
}
func (h *httpHandler) Name() string { func (h *httpHandler) Name() string {
return h.name return h.name
} }
@ -75,7 +62,6 @@ func (h *httpHandler) Options() server.HandlerOptions {
} }
func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
for exp, ph := range h.pathHandlers { for exp, ph := range h.pathHandlers {
if exp.MatchString(r.URL.String()) { if exp.MatchString(r.URL.String()) {
ph(w, r) ph(w, r)
@ -95,7 +81,7 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
} }
ctx := metadata.NewContext(r.Context(), nil) ctx := metadata.NewIncomingContext(r.Context(), nil)
defer r.Body.Close() defer r.Body.Close()
@ -132,8 +118,8 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
for _, hpat := range h.handlers { for _, hpat := range h.handlers {
handlertmp := hpat.(*httpHandler) handlertmp := hpat.(*httpHandler)
for _, hldrtmp := range handlertmp.handlers[r.Method] { for _, hldrtmp := range handlertmp.handlers[r.Method] {
mp, err := hldrtmp.pat.Match(components, verb) mp, merr := hldrtmp.pat.Match(components, verb)
if err == nil { if merr == nil {
match = true match = true
for k, v := range mp { for k, v := range mp {
matches[k] = v matches[k] = v
@ -161,9 +147,9 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// get fields from url values // get fields from url values
if len(r.URL.RawQuery) > 0 { if len(r.URL.RawQuery) > 0 {
umd, err := rflutil.URLMap(r.URL.RawQuery) umd, cerr := rflutil.URLMap(r.URL.RawQuery)
if err != nil { if cerr != nil {
h.errorHandler(ctx, handler, w, r, err, http.StatusBadRequest) h.errorHandler(ctx, handler, w, r, cerr, http.StatusBadRequest)
return return
} }
for k, v := range umd { for k, v := range umd {
@ -190,7 +176,7 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
replyv = reflect.New(hldr.mtype.ReplyType.Elem()) replyv = reflect.New(hldr.mtype.ReplyType.Elem())
function := hldr.mtype.method.Func function := hldr.mtype.method.Func
//function := hldr.rcvr // function := hldr.rcvr
var returnValues []reflect.Value var returnValues []reflect.Value
if err = cf.ReadBody(r.Body, argv.Interface()); err != nil && err != io.EOF { if err = cf.ReadBody(r.Body, argv.Interface()); err != nil && err != io.EOF {
@ -281,8 +267,10 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if scode != 0 { if scode != 0 {
w.WriteHeader(scode) w.WriteHeader(scode)
} else { } else {
//handler.sopts.Logger.Warn(handler.sopts.Context, "response code not set in handler via SetRspCode(ctx, http.StatusXXX)") // handler.sopts.Logger.Warn(handler.sopts.Context, "response code not set in handler via SetRspCode(ctx, http.StatusXXX)")
w.WriteHeader(200) w.WriteHeader(200)
} }
w.Write(b) if _, cerr := w.Write(b); cerr != nil {
logger.DefaultLogger.Errorf(ctx, "write failed: %v", cerr)
}
} }

42
http.go
View File

@ -126,19 +126,23 @@ func (h *httpServer) Init(opts ...server.Option) error {
func (h *httpServer) Handle(handler server.Handler) error { func (h *httpServer) Handle(handler server.Handler) error {
h.Lock() h.Lock()
if hdlr, ok := handler.(*httpHandler); ok { defer h.Unlock()
if _, ok := hdlr.hd.(http.Handler); ok { hdlr, ok := handler.(*httpHandler)
h.hd = handler if !ok {
} else {
if h.handlers == nil {
h.handlers = make(map[string]server.Handler)
}
h.handlers[handler.Name()] = handler
}
} else {
h.hd = handler h.hd = handler
return nil
} }
h.Unlock()
if _, ok := hdlr.hd.(http.Handler); ok {
h.hd = handler
return nil
}
if h.handlers == nil {
h.handlers = make(map[string]server.Handler)
}
h.handlers[handler.Name()] = handler
return nil return nil
} }
@ -279,7 +283,7 @@ func (h *httpServer) Register() error {
service.Endpoints = eps service.Endpoints = eps
h.Lock() h.Lock()
var subscriberList []*httpSubscriber subscriberList := make([]*httpSubscriber, 0, len(h.subscribers))
for e := range h.subscribers { for e := range h.subscribers {
// Only advertise non internal subscribers // Only advertise non internal subscribers
subscriberList = append(subscriberList, e) subscriberList = append(subscriberList, e)
@ -430,6 +434,7 @@ func (h *httpServer) Start() error {
var handler http.Handler var handler http.Handler
var srvFunc func(net.Listener) error var srvFunc func(net.Listener) error
// nolint: nestif
if h.opts.Context != nil { if h.opts.Context != nil {
if hs, ok := h.opts.Context.Value(serverKey{}).(*http.Server); ok && hs != nil { if hs, ok := h.opts.Context.Value(serverKey{}).(*http.Server); ok && hs != nil {
if hs.Handler == nil && h.hd != nil { if hs.Handler == nil && h.hd != nil {
@ -485,9 +490,17 @@ func (h *httpServer) Start() error {
} }
if srvFunc != nil { if srvFunc != nil {
go srvFunc(ts) go func() {
if cerr := srvFunc(ts); cerr != nil {
h.opts.Logger.Error(h.opts.Context, cerr)
}
}()
} else { } else {
go http.Serve(ts, fn) go func() {
if cerr := http.Serve(ts, fn); cerr != nil {
h.opts.Logger.Error(h.opts.Context, cerr)
}
}()
} }
go func() { go func() {
@ -511,6 +524,7 @@ func (h *httpServer) Start() error {
registered := h.registered registered := h.registered
h.RUnlock() h.RUnlock()
rerr := config.RegisterCheck(h.opts.Context) rerr := config.RegisterCheck(h.opts.Context)
// nolint: nestif
if rerr != nil && registered { if rerr != nil && registered {
if config.Logger.V(logger.ErrorLevel) { if config.Logger.V(logger.ErrorLevel) {
config.Logger.Errorf(config.Context, "Server %s-%s register check error: %s, deregister it", config.Name, config.Id, rerr) config.Logger.Errorf(config.Context, "Server %s-%s register check error: %s, deregister it", config.Name, config.Id, rerr)

View File

@ -8,11 +8,6 @@ import (
"github.com/unistack-org/micro/v3/server" "github.com/unistack-org/micro/v3/server"
) )
type rspCodeKey struct{}
type rspCodeVal struct {
code int
}
// SetError pass error to caller // SetError pass error to caller
func SetError(err interface{}) error { func SetError(err interface{}) error {
return &Error{err: err} return &Error{err: err}
@ -28,6 +23,13 @@ func (err *Error) Error() string {
return fmt.Sprintf("%v", err.err) return fmt.Sprintf("%v", err.err)
} }
type (
rspCodeKey struct{}
rspCodeVal struct {
code int
}
)
// SetRspCode saves response code in context, must be used by handler to specify http code // SetRspCode saves response code in context, must be used by handler to specify http code
func SetRspCode(ctx context.Context, code int) { func SetRspCode(ctx context.Context, code int) {
if rsp, ok := ctx.Value(rspCodeKey{}).(*rspCodeVal); ok { if rsp, ok := ctx.Value(rspCodeKey{}).(*rspCodeVal); ok {
@ -65,10 +67,12 @@ func ErrorHandler(fn func(ctx context.Context, s server.Handler, w http.Response
return server.SetOption(errorHandlerKey{}, fn) return server.SetOption(errorHandlerKey{}, fn)
} }
type pathHandlerKey struct{} type (
type pathHandlerVal struct { pathHandlerKey struct{}
h map[string]http.HandlerFunc pathHandlerVal struct {
} h map[string]http.HandlerFunc
}
)
// PathHandler specifies http handler for path regexp // PathHandler specifies http handler for path regexp
func PathHandler(path string, h http.HandlerFunc) server.Option { func PathHandler(path string, h http.HandlerFunc) server.Option {
@ -85,10 +89,12 @@ func PathHandler(path string, h http.HandlerFunc) server.Option {
} }
} }
type contentTypeHandlerKey struct{} type (
type contentTypeHandlerVal struct { contentTypeHandlerKey struct{}
h map[string]http.HandlerFunc contentTypeHandlerVal struct {
} h map[string]http.HandlerFunc
}
)
// ContentTypeHandler specifies http handler for Content-Type // ContentTypeHandler specifies http handler for Content-Type
func ContentTypeHandler(ct string, h http.HandlerFunc) server.Option { func ContentTypeHandler(ct string, h http.HandlerFunc) server.Option {

View File

@ -5,6 +5,12 @@ import (
"github.com/unistack-org/micro/v3/codec" "github.com/unistack-org/micro/v3/codec"
"github.com/unistack-org/micro/v3/metadata" "github.com/unistack-org/micro/v3/metadata"
"github.com/unistack-org/micro/v3/server"
)
var (
_ server.Request = &rpcRequest{}
_ server.Message = &rpcMessage{}
) )
type rpcRequest struct { type rpcRequest struct {
@ -16,7 +22,6 @@ type rpcRequest struct {
endpoint string endpoint string
contentType string contentType string
service string service string
target string
body []byte body []byte
stream bool stream bool
} }
@ -43,7 +48,7 @@ func (r *rpcRequest) Method() string {
} }
func (r *rpcRequest) Endpoint() string { func (r *rpcRequest) Endpoint() string {
return r.method return r.endpoint
} }
func (r *rpcRequest) Codec() codec.Codec { func (r *rpcRequest) Codec() codec.Codec {

View File

@ -62,15 +62,14 @@ func prepareEndpoint(method reflect.Method) (*methodType, error) {
return nil, fmt.Errorf("method %v of %v has wrong number of ins: %v", mname, mtype, mtype.NumIn()) return nil, fmt.Errorf("method %v of %v has wrong number of ins: %v", mname, mtype, mtype.NumIn())
} }
if stream { switch stream {
case true:
// check stream type // check stream type
streamType := reflect.TypeOf((*server.Stream)(nil)).Elem() streamType := reflect.TypeOf((*server.Stream)(nil)).Elem()
if !argType.Implements(streamType) { if !argType.Implements(streamType) {
return nil, fmt.Errorf("%v argument does not implement Streamer interface: %v", mname, argType) return nil, fmt.Errorf("%v argument does not implement Streamer interface: %v", mname, argType)
} }
} else { default:
// if not stream check the replyType
// First arg need not be a pointer. // First arg need not be a pointer.
if !isExportedOrBuiltinType(argType) { if !isExportedOrBuiltinType(argType) {
return nil, fmt.Errorf("%v argument type not exported: %v", mname, argType) return nil, fmt.Errorf("%v argument type not exported: %v", mname, argType)

View File

@ -14,10 +14,6 @@ import (
"github.com/unistack-org/micro/v3/server" "github.com/unistack-org/micro/v3/server"
) )
const (
subSig = "func(context.Context, interface{}) error"
)
var typeOfError = reflect.TypeOf((*error)(nil)).Elem() var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
type handler struct { type handler struct {
@ -116,7 +112,7 @@ func (s *httpServer) createSubHandler(sb *httpSubscriber, opts server.Options) b
hdr := metadata.Copy(msg.Header) hdr := metadata.Copy(msg.Header)
delete(hdr, "Content-Type") delete(hdr, "Content-Type")
ctx := metadata.NewContext(context.Background(), hdr) ctx := metadata.NewIncomingContext(context.Background(), hdr)
results := make(chan error, len(sb.handlers)) results := make(chan error, len(sb.handlers))