Compare commits

...

49 Commits

Author SHA1 Message Date
b18465083b fill metadata early
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-30 00:40:15 +03:00
Renovate Bot
11020aa6e5 Update golang.org/x/net commit hash to 89ef3d9 2021-04-28 16:43:11 +00:00
Renovate Bot
43c0eb4fda Update golang.org/x/net commit hash to 85d9c07 2021-04-28 01:42:52 +00:00
5ab606c2b5 update for latest micro
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-27 19:10:35 +03:00
6c661c9d72 lint (#57)
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-26 00:43:06 +03:00
a215521821 fieldalignment
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-25 12:09:09 +03:00
Renovate Bot
ec5af022b3 Update golang.org/x/net commit hash to 5f58ad6 2021-04-23 22:20:32 +00:00
Renovate Bot
bd88f6e485 Update golang.org/x/net commit hash to 4e50805 2021-04-22 02:47:31 +00:00
Renovate Bot
34a39be017 Update golang.org/x/net commit hash to 798c215 2021-04-20 23:49:53 +00:00
Renovate Bot
360703d8bb Update golang.org/x/net commit hash to d25e304 2021-04-20 11:33:44 +00:00
2660a0d94d update for latest micro
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-20 12:53:48 +03:00
Renovate Bot
6e9a693b51 Update module github.com/unistack-org/micro/v3 to v3.3.15 2021-04-19 17:06:46 +00:00
renovate[bot]
06d197e356 Update module github.com/unistack-org/micro/v3 to v3.3.14 (#49)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-04-19 02:12:57 +03:00
30609135a4 use DefaultContentType on application/x-www-form-urlencoded
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-18 15:50:41 +03:00
84167d359e extract application/x-www-form-urlencoded handling to codec
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-17 13:54:32 +03:00
145a0f4aa6 minor content-type fixes
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-16 17:14:27 +03:00
Renovate Bot
b2b4d6a5a3 Update golang.org/x/net commit hash to e915ea6 2021-04-16 01:28:59 +00:00
Renovate Bot
f17afa7950 Update golang.org/x/net commit hash to 0645797 2021-04-14 21:16:29 +00:00
bb763c71b5 add content type handlers
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-13 23:42:33 +03:00
622a79bd06 add path handler option
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-13 11:33:03 +03:00
Renovate Bot
0eb3c0b452 Update golang.org/x/net commit hash to afb366f 2021-04-10 12:19:24 +00:00
676634850e allow to set default content type with charset
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-10 12:41:55 +03:00
0d442683ee properly detect content-type
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-10 12:38:17 +03:00
Renovate Bot
c8587ec93a Update module github.com/unistack-org/micro/v3 to v3.3.13 2021-04-10 00:02:08 +00:00
7e35316cf2 rewrite x-www-form-urlencoded
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-04-10 01:24:34 +03:00
Renovate Bot
475452a781 Update golang.org/x/net commit hash to a5a99cb 2021-04-05 21:22:50 +00:00
Renovate Bot
dd0bb7c5d4 Update module github.com/unistack-org/micro/v3 to v3.3.11 2021-04-03 11:43:48 +00:00
Renovate Bot
b74f3762dd Update all deps 2021-04-01 00:46:07 +00:00
Renovate Bot
e30590c707 Update golang.org/x/net commit hash to cb1fcc7 2021-03-31 08:14:56 +00:00
Renovate Bot
132e95dec6 Update golang.org/x/net commit hash to e572328 2021-03-31 00:41:40 +00:00
48620e6297 handle x-form-urlencoded content-type
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-31 00:50:02 +03:00
Renovate Bot
e31185b6dc Update golang.org/x/net commit hash to c8897c2 2021-03-30 16:23:29 +00:00
Renovate Bot
151e45ca30 Update golang.org/x/net commit hash to 22f4162 2021-03-30 12:06:34 +00:00
Renovate Bot
93c4f219e9 Update all deps 2021-03-30 06:47:00 +00:00
Renovate Bot
396654b066 Update module github.com/unistack-org/micro/v3 to v3.3.8 2021-03-29 03:27:06 +00:00
Renovate Bot
a769031246 Update module github.com/unistack-org/micro/v3 to v3.3.7 2021-03-28 20:50:08 +00:00
Renovate Bot
99915c1633 Update golang.org/x/net commit hash to 61e0566 2021-03-27 01:57:17 +00:00
Renovate Bot
c7759d29b8 Update all deps 2021-03-26 17:42:44 +00:00
Renovate Bot
0f97e432a4 Update module github.com/unistack-org/micro/v3 to v3.3.2 2021-03-25 13:14:58 +00:00
Renovate Bot
713b4f96be Update all deps 2021-03-25 00:08:52 +00:00
360b15a3b6 remove Warn on missing rsp code
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-24 23:58:35 +03:00
Renovate Bot
4959b0040b Update golang.org/x/net commit hash to 2c4c8ec 2021-03-24 15:09:22 +00:00
e93c6dc1de cleanup
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-03-24 15:22:20 +03:00
Renovate Bot
9ad01954dc Update all deps 2021-03-23 17:40:45 +00:00
Renovate Bot
8483b85c94 Update module github.com/unistack-org/micro/v3 to v3.2.24 2021-03-21 17:16:53 +00:00
Renovate Bot
d1a8f76d77 Update module github.com/unistack-org/micro/v3 to v3.2.23 2021-03-16 19:22:28 +00:00
Renovate Bot
9d7f4bfa66 Update golang.org/x/net commit hash to d523dce 2021-03-16 13:48:28 +00:00
Renovate Bot
656dfd4d05 Update golang.org/x/net commit hash to 34ac3e1 2021-03-15 22:07:09 +00:00
Renovate Bot
70e14e6592 Update module github.com/unistack-org/micro/v3 to v3.2.22 2021-03-15 02:19:34 +00:00
14 changed files with 313 additions and 152 deletions

View File

@@ -2,6 +2,7 @@
"extends": [
"config:base"
],
"postUpdateOptions": ["gomodTidy"],
"packageRules": [
{
"matchUpdateTypes": ["minor", "patch", "pin", "digest"],

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
- name: lint
uses: golangci/golangci-lint-action@v2
continue-on-error: true
with:
# 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
# working-directory: somedir
# Optional: golangci-lint command line arguments.

View File

@@ -34,10 +34,9 @@ jobs:
uses: actions/checkout@v2
- name: lint
uses: golangci/golangci-lint-action@v2
continue-on-error: true
with:
# 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
# working-directory: somedir
# 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

8
go.mod
View File

@@ -1,10 +1,8 @@
module github.com/unistack-org/micro-server-http/v3
go 1.13
go 1.16
require (
github.com/unistack-org/micro/v3 v3.2.20
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
github.com/unistack-org/micro/v3 v3.3.17
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781
)
//replace github.com/unistack-org/micro/v3 => ../../micro

19
go.sum
View File

@@ -2,22 +2,19 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
github.com/ef-ds/deque v1.0.4/go.mod h1:gXDnTC3yqvBcHbq2lcExjtAcVrOnJCbMcZXmuj8Z4tg=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/silas/dag v0.0.0-20210121180416-41cf55125c34/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I=
github.com/unistack-org/micro/v3 v3.2.15 h1:9Wg6FAFVPUijAm5ZFF+y4oYEd8zd/Pp48QwmMrSs074=
github.com/unistack-org/micro/v3 v3.2.15/go.mod h1:y+fV+BPNK2IqGoLquRU396jTYifG0HCw3zxFfI4E0dc=
github.com/unistack-org/micro/v3 v3.2.16/go.mod h1:y+fV+BPNK2IqGoLquRU396jTYifG0HCw3zxFfI4E0dc=
github.com/unistack-org/micro/v3 v3.2.17/go.mod h1:y+fV+BPNK2IqGoLquRU396jTYifG0HCw3zxFfI4E0dc=
github.com/unistack-org/micro/v3 v3.2.18 h1:+EnZ6xJJzHINQr7c8nkwygkfy+qLssiKd1hktgEzHL4=
github.com/unistack-org/micro/v3 v3.2.18/go.mod h1:y+fV+BPNK2IqGoLquRU396jTYifG0HCw3zxFfI4E0dc=
github.com/unistack-org/micro/v3 v3.2.20/go.mod h1:y+fV+BPNK2IqGoLquRU396jTYifG0HCw3zxFfI4E0dc=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
github.com/unistack-org/micro/v3 v3.3.17 h1:WcyS7InP0DlS/JpRQGLh5sG6VstkdHJbgpMp+gmHmwg=
github.com/unistack-org/micro/v3 v3.3.17/go.mod h1:022EOEZZ789hZY3yB5ZSMXU6jLiadBgcNB/cpediV3c=
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@@ -7,8 +7,8 @@ import (
"net/http"
"reflect"
"strings"
"sync"
"github.com/unistack-org/micro/v3/codec"
"github.com/unistack-org/micro/v3/errors"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/metadata"
@@ -21,32 +21,28 @@ import (
var (
DefaultErrorHandler = func(ctx context.Context, s server.Handler, w http.ResponseWriter, r *http.Request, err error, status int) {
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"
)
type patHandler struct {
pat rutil.Pattern
mtype *methodType
name string
rcvr reflect.Value
name string
pat rutil.Pattern
}
type httpHandler struct {
name string
opts server.HandlerOptions
sopts server.Options
eps []*register.Endpoint
hd interface{}
handlers map[string][]patHandler
}
func (h *httpHandler) newCodec(ct string) (codec.Codec, error) {
if cf, ok := h.sopts.Codecs[ct]; ok {
return cf, nil
}
return nil, codec.ErrUnknownContentType
name string
eps []*register.Endpoint
sopts server.Options
sync.RWMutex
}
func (h *httpHandler) Name() string {
@@ -66,7 +62,34 @@ func (h *httpHandler) Options() server.HandlerOptions {
}
func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := metadata.NewContext(r.Context(), nil)
for exp, ph := range h.pathHandlers {
if exp.MatchString(r.URL.String()) {
ph(w, r)
return
}
}
ct := DefaultContentType
if htype := r.Header.Get("Content-Type"); htype != "" {
ct = htype
}
if idx := strings.Index(ct, ":"); idx > 0 {
if ph, ok := h.contentTypeHandlers[ct[:idx]]; ok {
ph(w, r)
return
}
}
ctx := context.WithValue(r.Context(), rspCodeKey{}, &rspCodeVal{})
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
md = metadata.New(len(r.Header))
}
for k, v := range r.Header {
md.Set(k, strings.Join(v, ", "))
}
ctx = metadata.NewIncomingContext(ctx, md)
defer r.Body.Close()
@@ -76,11 +99,6 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
ct := DefaultContentType
if htype := r.Header.Get("Content-Type"); htype != "" {
ct = htype
}
cf, err := h.newCodec(ct)
if err != nil {
h.errorHandler(ctx, nil, w, r, err, http.StatusBadRequest)
@@ -101,14 +119,15 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
matches := make(map[string]interface{})
var match bool
var hldr patHandler
var handler *httpHandler
for _, hpat := range h.handlers {
handlertmp := hpat.(*httpHandler)
for _, hldrtmp := range handlertmp.handlers[r.Method] {
mp, err := hldrtmp.pat.Match(components, verb)
if err == nil {
mp, merr := hldrtmp.pat.Match(components, verb)
if merr == nil {
match = true
for k, v := range mp {
matches[k] = v
@@ -125,20 +144,11 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
md = metadata.New(0)
}
for k, v := range r.Header {
md.Set(k, strings.Join(v, ", "))
}
// get fields from url values
if len(r.URL.RawQuery) > 0 {
umd, err := rflutil.URLMap(r.URL.RawQuery)
if err != nil {
h.errorHandler(ctx, handler, w, r, err, http.StatusBadRequest)
umd, cerr := rflutil.URLMap(r.URL.RawQuery)
if cerr != nil {
h.errorHandler(ctx, handler, w, r, cerr, http.StatusBadRequest)
return
}
for k, v := range umd {
@@ -165,7 +175,7 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
replyv = reflect.New(hldr.mtype.ReplyType.Elem())
function := hldr.mtype.method.Func
//function := hldr.rcvr
// function := hldr.rcvr
var returnValues []reflect.Value
if err = cf.ReadBody(r.Body, argv.Interface()); err != nil && err != io.EOF {
@@ -174,7 +184,7 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
matches = rflutil.FlattenMap(matches)
if err = rflutil.MergeMap(argv.Interface(), matches); err != nil {
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
}
@@ -192,13 +202,12 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
method: fmt.Sprintf("%s.%s", hldr.name, hldr.mtype.method.Name),
body: b,
payload: argv.Interface(),
header: md,
}
var scode int
// define the handler func
fn := func(fctx context.Context, req server.Request, rsp interface{}) (err error) {
fctx = context.WithValue(fctx, rspCodeKey{}, &rspCodeVal{})
fctx = metadata.NewIncomingContext(fctx, md)
returnValues = function.Call([]reflect.Value{hldr.rcvr, hldr.mtype.prepareContext(fctx), reflect.ValueOf(argv.Interface()), reflect.ValueOf(rsp)})
scode = GetRspCode(fctx)
@@ -219,6 +228,15 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
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
}
if appErr := fn(ctx, hr, replyv.Interface()); appErr != nil {
switch verr := appErr.(type) {
case *errors.Error:
@@ -247,7 +265,10 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if scode != 0 {
w.WriteHeader(scode)
} 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)
}
if _, cerr := w.Write(b); cerr != nil {
logger.DefaultLogger.Errorf(ctx, "write failed: %v", cerr)
}
w.Write(b)
}

167
http.go
View File

@@ -8,6 +8,7 @@ import (
"net"
"net/http"
"reflect"
"regexp"
"sort"
"strings"
"sync"
@@ -23,21 +24,28 @@ import (
)
type httpServer struct {
hd server.Handler
rsvc *register.Service
handlers map[string]server.Handler
exit chan chan error
subscribers map[*httpSubscriber][]broker.Subscriber
errorHandler func(context.Context, server.Handler, http.ResponseWriter, *http.Request, error, int)
pathHandlers map[*regexp.Regexp]http.HandlerFunc
contentTypeHandlers map[string]http.HandlerFunc
opts server.Options
sync.RWMutex
opts server.Options
handlers map[string]server.Handler
hd server.Handler
exit chan chan error
subscribers map[*httpSubscriber][]broker.Subscriber
// used for first registration
registered bool
// register service instance
rsvc *register.Service
errorHandler func(context.Context, server.Handler, http.ResponseWriter, *http.Request, error, int)
init bool
}
func (h *httpServer) 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.opts.Codecs[ct]; ok {
return cf, nil
}
@@ -52,50 +60,101 @@ func (h *httpServer) Options() server.Options {
}
func (h *httpServer) Init(opts ...server.Option) error {
if len(opts) == 0 && h.init {
return nil
}
h.Lock()
defer h.Unlock()
for _, o := range opts {
o(&h.opts)
}
h.errorHandler = DefaultErrorHandler
if fn, ok := h.opts.Context.Value(errorHandlerKey{}).(func(ctx context.Context, s server.Handler, w http.ResponseWriter, r *http.Request, err error, status int)); ok && fn != nil {
h.errorHandler = fn
}
if h.handlers == nil {
h.handlers = make(map[string]server.Handler)
}
if h.pathHandlers == nil {
h.pathHandlers = make(map[*regexp.Regexp]http.HandlerFunc)
}
if h.contentTypeHandlers == nil {
h.contentTypeHandlers = make(map[string]http.HandlerFunc)
}
if phs, ok := h.opts.Context.Value(pathHandlerKey{}).(*pathHandlerVal); ok && phs.h != nil {
for pp, ph := range phs.h {
exp, err := regexp.Compile(pp)
if err != nil {
return err
}
h.pathHandlers[exp] = ph
}
}
if phs, ok := h.opts.Context.Value(contentTypeHandlerKey{}).(*contentTypeHandlerVal); ok && phs.h != nil {
for pp, ph := range phs.h {
h.contentTypeHandlers[pp] = ph
}
}
if err := h.opts.Register.Init(); err != nil {
return err
}
if err := h.opts.Broker.Init(); err != nil {
return err
}
if err := h.opts.Tracer.Init(); err != nil {
return err
}
if err := h.opts.Auth.Init(); err != nil {
return err
}
if err := h.opts.Logger.Init(); err != nil {
return err
}
if err := h.opts.Meter.Init(); err != nil {
return err
}
if err := h.opts.Transport.Init(); err != nil {
return err
}
h.init = true
h.handlers = make(map[string]server.Handler)
h.Unlock()
return nil
}
func (h *httpServer) Handle(handler server.Handler) error {
h.Lock()
if hdlr, ok := handler.(*httpHandler); ok {
if h.handlers == nil {
h.handlers = make(map[string]server.Handler)
}
if _, ok := hdlr.hd.(http.Handler); ok {
h.hd = handler
} else {
h.handlers[handler.Name()] = handler
}
} else {
defer h.Unlock()
hdlr, ok := handler.(*httpHandler)
if !ok {
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
}
func (h *httpServer) NewHandler(handler interface{}, opts ...server.HandlerOption) server.Handler {
options := server.NewHandlerOptions(opts...)
var eps []*register.Endpoint
if !options.Internal {
for name, metadata := range options.Metadata {
eps = append(eps, &register.Endpoint{
Name: name,
Metadata: metadata,
})
}
eps := make([]*register.Endpoint, 0, len(options.Metadata))
for name, metadata := range options.Metadata {
eps = append(eps, &register.Endpoint{
Name: name,
Metadata: metadata,
})
}
hdlr := &httpHandler{
@@ -224,12 +283,10 @@ func (h *httpServer) Register() error {
service.Endpoints = eps
h.Lock()
var subscriberList []*httpSubscriber
subscriberList := make([]*httpSubscriber, 0, len(h.subscribers))
for e := range h.subscribers {
// Only advertise non internal subscribers
if !e.Options().Internal {
subscriberList = append(subscriberList, e)
}
subscriberList = append(subscriberList, e)
}
sort.Slice(subscriberList, func(i, j int) bool {
return subscriberList[i].topic > subscriberList[j].topic
@@ -246,7 +303,7 @@ func (h *httpServer) Register() error {
if !registered {
if config.Logger.V(logger.InfoLevel) {
config.Logger.Infof(config.Context, "Register [%s] Registering node: %s", config.Register.String(), service.Nodes[0].Id)
config.Logger.Infof(config.Context, "Register [%s] Registering node: %s", config.Register.String(), service.Nodes[0].ID)
}
}
@@ -301,7 +358,7 @@ func (h *httpServer) Deregister() error {
}
if config.Logger.V(logger.InfoLevel) {
config.Logger.Infof(config.Context, "Deregistering node: %s", service.Nodes[0].Id)
config.Logger.Infof(config.Context, "Deregistering node: %s", service.Nodes[0].ID)
}
if err := server.DefaultDeregisterFunc(service, config); err != nil {
@@ -377,6 +434,7 @@ func (h *httpServer) Start() error {
var handler http.Handler
var srvFunc func(net.Listener) error
// nolint: nestif
if h.opts.Context != nil {
if hs, ok := h.opts.Context.Value(serverKey{}).(*http.Server); ok && hs != nil {
if hs.Handler == nil && h.hd != nil {
@@ -408,7 +466,7 @@ func (h *httpServer) Start() error {
if err := config.RegisterCheck(h.opts.Context); err != nil {
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)
}
} else {
if err = h.Register(); err != nil {
@@ -432,9 +490,17 @@ func (h *httpServer) Start() error {
}
if srvFunc != nil {
go srvFunc(ts)
go func() {
if cerr := srvFunc(ts); cerr != nil {
h.opts.Logger.Error(h.opts.Context, cerr)
}
}()
} 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() {
@@ -458,25 +524,26 @@ func (h *httpServer) Start() error {
registered := h.registered
h.RUnlock()
rerr := config.RegisterCheck(h.opts.Context)
// nolint: nestif
if rerr != nil && registered {
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)
}
// deregister self in case of error
if err := h.Deregister(); err != nil {
if config.Logger.V(logger.ErrorLevel) {
config.Logger.Errorf(config.Context, "Server %s-%s deregister error: %s", config.Name, config.Id, err)
config.Logger.Errorf(config.Context, "Server %s-%s deregister error: %s", config.Name, config.ID, err)
}
}
} else if rerr != nil && !registered {
if config.Logger.V(logger.ErrorLevel) {
config.Logger.Errorf(config.Context, "Server %s-%s register check error: %s", config.Name, config.Id, rerr)
config.Logger.Errorf(config.Context, "Server %s-%s register check error: %s", config.Name, config.ID, rerr)
}
continue
}
if err := h.Register(); err != nil {
if config.Logger.V(logger.ErrorLevel) {
config.Logger.Errorf(config.Context, "Server %s-%s register error: %s", config.Name, config.Id, err)
config.Logger.Errorf(config.Context, "Server %s-%s register error: %s", config.Name, config.ID, err)
}
}
@@ -521,8 +588,10 @@ func (h *httpServer) Name() string {
func NewServer(opts ...server.Option) server.Server {
options := server.NewOptions(opts...)
return &httpServer{
opts: options,
exit: make(chan chan error),
subscribers: make(map[*httpSubscriber][]broker.Subscriber),
opts: options,
exit: make(chan chan error),
subscribers: make(map[*httpSubscriber][]broker.Subscriber),
errorHandler: DefaultErrorHandler,
pathHandlers: make(map[*regexp.Regexp]http.HandlerFunc),
}
}

View File

@@ -6,12 +6,12 @@ import (
)
type httpMessage struct {
topic string
payload interface{}
contentType string
header metadata.Metadata
body []byte
codec codec.Codec
header metadata.Metadata
topic string
contentType string
body []byte
}
func (r *httpMessage) Topic() string {

View File

@@ -8,11 +8,6 @@ import (
"github.com/unistack-org/micro/v3/server"
)
type rspCodeKey struct{}
type rspCodeVal struct {
code int
}
// SetError pass error to caller
func SetError(err interface{}) error {
return &Error{err: err}
@@ -28,6 +23,13 @@ func (err *Error) Error() string {
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
func SetRspCode(ctx context.Context, code int) {
if rsp, ok := ctx.Value(rspCodeKey{}).(*rspCodeVal); ok {
@@ -64,3 +66,47 @@ type errorHandlerKey struct{}
func ErrorHandler(fn func(ctx context.Context, s server.Handler, w http.ResponseWriter, r *http.Request, err error, status int)) server.Option {
return server.SetOption(errorHandlerKey{}, fn)
}
type (
pathHandlerKey struct{}
pathHandlerVal struct {
h map[string]http.HandlerFunc
}
)
// PathHandler specifies http handler for path regexp
func PathHandler(path string, h http.HandlerFunc) server.Option {
return func(o *server.Options) {
if o.Context == nil {
o.Context = context.Background()
}
v, ok := o.Context.Value(pathHandlerKey{}).(*pathHandlerVal)
if !ok {
v = &pathHandlerVal{h: make(map[string]http.HandlerFunc)}
}
v.h[path] = h
o.Context = context.WithValue(o.Context, pathHandlerKey{}, v)
}
}
type (
contentTypeHandlerKey struct{}
contentTypeHandlerVal struct {
h map[string]http.HandlerFunc
}
)
// ContentTypeHandler specifies http handler for Content-Type
func ContentTypeHandler(ct string, h http.HandlerFunc) server.Option {
return func(o *server.Options) {
if o.Context == nil {
o.Context = context.Background()
}
v, ok := o.Context.Value(contentTypeHandlerKey{}).(*contentTypeHandlerVal)
if !ok {
v = &contentTypeHandlerVal{h: make(map[string]http.HandlerFunc)}
}
v.h[ct] = h
o.Context = context.WithValue(o.Context, contentTypeHandlerKey{}, v)
}
}

View File

@@ -5,29 +5,34 @@ import (
"github.com/unistack-org/micro/v3/codec"
"github.com/unistack-org/micro/v3/metadata"
"github.com/unistack-org/micro/v3/server"
)
var (
_ server.Request = &rpcRequest{}
_ server.Message = &rpcMessage{}
)
type rpcRequest struct {
rw io.ReadWriter
service string
method string
endpoint string
target string
contentType string
payload interface{}
codec codec.Codec
header metadata.Metadata
method string
endpoint string
contentType string
service string
body []byte
stream bool
payload interface{}
}
type rpcMessage struct {
payload interface{}
codec codec.Codec
header metadata.Metadata
topic string
contentType string
payload interface{}
header metadata.Metadata
body []byte
codec codec.Codec
}
func (r *rpcRequest) ContentType() string {
@@ -43,7 +48,7 @@ func (r *rpcRequest) Method() string {
}
func (r *rpcRequest) Endpoint() string {
return r.method
return r.endpoint
}
func (r *rpcRequest) Codec() codec.Codec {

View File

@@ -11,10 +11,10 @@ import (
)
type methodType struct {
method reflect.Method
ArgType reflect.Type
ReplyType reflect.Type
ContextType reflect.Type
method reflect.Method
stream bool
}
@@ -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())
}
if stream {
switch stream {
case true:
// check stream type
streamType := reflect.TypeOf((*server.Stream)(nil)).Elem()
if !argType.Implements(streamType) {
return nil, fmt.Errorf("%v argument does not implement Streamer interface: %v", mname, argType)
}
} else {
// if not stream check the replyType
default:
// First arg need not be a pointer.
if !isExportedOrBuiltinType(argType) {
return nil, fmt.Errorf("%v argument type not exported: %v", mname, argType)

View File

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