Compare commits
48 Commits
Author | SHA1 | Date | |
---|---|---|---|
84167d359e | |||
145a0f4aa6 | |||
|
b2b4d6a5a3 | ||
|
f17afa7950 | ||
bb763c71b5 | |||
622a79bd06 | |||
|
0eb3c0b452 | ||
676634850e | |||
0d442683ee | |||
|
c8587ec93a | ||
7e35316cf2 | |||
|
475452a781 | ||
|
dd0bb7c5d4 | ||
|
b74f3762dd | ||
|
e30590c707 | ||
|
132e95dec6 | ||
48620e6297 | |||
|
e31185b6dc | ||
|
151e45ca30 | ||
|
93c4f219e9 | ||
|
396654b066 | ||
|
a769031246 | ||
|
99915c1633 | ||
|
c7759d29b8 | ||
|
0f97e432a4 | ||
|
713b4f96be | ||
360b15a3b6 | |||
|
4959b0040b | ||
e93c6dc1de | |||
|
9ad01954dc | ||
|
8483b85c94 | ||
|
d1a8f76d77 | ||
|
9d7f4bfa66 | ||
|
656dfd4d05 | ||
|
70e14e6592 | ||
39e6d9f586 | |||
a46c9d395a | |||
|
d3c5a503c6 | ||
|
be224692a4 | ||
|
0da73b62ae | ||
|
a30c2972fd | ||
f28de46379 | |||
af91220a09 | |||
|
14bf4a9f30 | ||
e88f524042 | |||
45670661d0 | |||
|
f7ed037633 | ||
|
8f36e1753a |
1
.github/renovate.json
vendored
1
.github/renovate.json
vendored
@@ -2,6 +2,7 @@
|
|||||||
"extends": [
|
"extends": [
|
||||||
"config:base"
|
"config:base"
|
||||||
],
|
],
|
||||||
|
"postUpdateOptions": ["gomodTidy"],
|
||||||
"packageRules": [
|
"packageRules": [
|
||||||
{
|
{
|
||||||
"matchUpdateTypes": ["minor", "patch", "pin", "digest"],
|
"matchUpdateTypes": ["minor", "patch", "pin", "digest"],
|
||||||
|
6
go.mod
6
go.mod
@@ -1,10 +1,10 @@
|
|||||||
module github.com/unistack-org/micro-server-http/v3
|
module github.com/unistack-org/micro-server-http/v3
|
||||||
|
|
||||||
go 1.13
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/unistack-org/micro/v3 v3.2.14
|
github.com/unistack-org/micro/v3 v3.3.13
|
||||||
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7
|
golang.org/x/net v0.0.0-20210415231046-e915ea6b2b7d
|
||||||
)
|
)
|
||||||
|
|
||||||
//replace github.com/unistack-org/micro/v3 => ../../micro
|
//replace github.com/unistack-org/micro/v3 => ../../micro
|
||||||
|
23
go.sum
23
go.sum
@@ -1,28 +1,21 @@
|
|||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/ef-ds/deque v1.0.4/go.mod h1:gXDnTC3yqvBcHbq2lcExjtAcVrOnJCbMcZXmuj8Z4tg=
|
github.com/ef-ds/deque v1.0.4/go.mod h1:gXDnTC3yqvBcHbq2lcExjtAcVrOnJCbMcZXmuj8Z4tg=
|
||||||
github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
|
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
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/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/heimdalr/dag v1.0.1/go.mod h1:t+ZkR+sjKL4xhlE1B9rwpvwfo+x+2R0363efS+Oghns=
|
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
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/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/silas/dag v0.0.0-20210121180416-41cf55125c34/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I=
|
||||||
github.com/unistack-org/micro/v3 v3.2.14 h1:BD7JR2W0WlJvJgHN3uPWrE/vNAGyxhIQrIODeDCfoSk=
|
github.com/unistack-org/micro/v3 v3.3.13 h1:y4bDDkbwnjgOckrhFkC6D/o42tr75X33UbrB+Ko0M68=
|
||||||
github.com/unistack-org/micro/v3 v3.2.14/go.mod h1:3j13mSd/rILNjyP0tEVtDxyDkJBtnHUXShNCuPHkC5A=
|
github.com/unistack-org/micro/v3 v3.3.13/go.mod h1:98hNcMXp/WyWJwLwCuwrhN1Jm7aCWaRNsMfRjK8Fq+Y=
|
||||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7 h1:OgUuv8lsRpBibGNbSizVwKWlysjaNzmC9gYMhPVfqFM=
|
golang.org/x/net v0.0.0-20210415231046-e915ea6b2b7d h1:BgJvlyh+UqCUaPlscHJ+PN8GcpfrFdr7NHjd1JL0+Gs=
|
||||||
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210415231046-e915ea6b2b7d/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
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.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=
|
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/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
133
handler.go
133
handler.go
@@ -7,6 +7,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/unistack-org/micro/v3/codec"
|
"github.com/unistack-org/micro/v3/codec"
|
||||||
"github.com/unistack-org/micro/v3/errors"
|
"github.com/unistack-org/micro/v3/errors"
|
||||||
@@ -34,16 +35,23 @@ type patHandler struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type httpHandler struct {
|
type httpHandler struct {
|
||||||
name string
|
name string
|
||||||
opts server.HandlerOptions
|
opts server.HandlerOptions
|
||||||
sopts server.Options
|
sopts server.Options
|
||||||
eps []*register.Endpoint
|
eps []*register.Endpoint
|
||||||
hd interface{}
|
hd interface{}
|
||||||
handlers map[string][]patHandler
|
handlers map[string][]patHandler
|
||||||
errorHandler func(context.Context, server.Handler, http.ResponseWriter, *http.Request, error, int)
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpHandler) newCodec(ct string) (codec.Codec, error) {
|
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 {
|
if cf, ok := h.sopts.Codecs[ct]; ok {
|
||||||
return cf, nil
|
return cf, nil
|
||||||
}
|
}
|
||||||
@@ -66,14 +74,13 @@ func (h *httpHandler) Options() server.HandlerOptions {
|
|||||||
return h.opts
|
return h.opts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
|
||||||
|
|
||||||
defer r.Body.Close()
|
for exp, ph := range h.pathHandlers {
|
||||||
|
if exp.MatchString(r.URL.String()) {
|
||||||
path := r.URL.Path
|
ph(w, r)
|
||||||
if !strings.HasPrefix(path, "/") {
|
return
|
||||||
h.errorHandler(ctx, h, w, r, fmt.Errorf("path must contains /"), http.StatusBadRequest)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ct := DefaultContentType
|
ct := DefaultContentType
|
||||||
@@ -81,9 +88,27 @@ func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
ct = htype
|
ct = htype
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if idx := strings.Index(ct, ":"); idx > 0 {
|
||||||
|
if ph, ok := h.contentTypeHandlers[ct[:idx]]; ok {
|
||||||
|
ph(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := metadata.NewContext(r.Context(), nil)
|
||||||
|
|
||||||
|
defer r.Body.Close()
|
||||||
|
|
||||||
|
path := r.URL.Path
|
||||||
|
if !strings.HasPrefix(path, "/") {
|
||||||
|
h.errorHandler(ctx, nil, w, r, fmt.Errorf("path must contains /"), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
cf, err := h.newCodec(ct)
|
cf, err := h.newCodec(ct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.errorHandler(ctx, h, w, r, err, http.StatusBadRequest)
|
h.errorHandler(ctx, nil, w, r, err, http.StatusBadRequest)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
components := strings.Split(path[1:], "/")
|
components := strings.Split(path[1:], "/")
|
||||||
@@ -91,7 +116,7 @@ func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
var verb string
|
var verb string
|
||||||
idx := strings.LastIndex(components[l-1], ":")
|
idx := strings.LastIndex(components[l-1], ":")
|
||||||
if idx == 0 {
|
if idx == 0 {
|
||||||
h.errorHandler(ctx, h, w, r, fmt.Errorf("not found"), http.StatusNotFound)
|
h.errorHandler(ctx, nil, w, r, fmt.Errorf("not found"), http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if idx > 0 {
|
if idx > 0 {
|
||||||
@@ -100,21 +125,28 @@ func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
matches := make(map[string]interface{})
|
matches := make(map[string]interface{})
|
||||||
|
|
||||||
var match bool
|
var match bool
|
||||||
var hldr patHandler
|
var hldr patHandler
|
||||||
for _, hldr = range h.handlers[r.Method] {
|
var handler *httpHandler
|
||||||
mp, err := hldr.pat.Match(components, verb)
|
for _, hpat := range h.handlers {
|
||||||
if err == nil {
|
handlertmp := hpat.(*httpHandler)
|
||||||
match = true
|
for _, hldrtmp := range handlertmp.handlers[r.Method] {
|
||||||
for k, v := range mp {
|
mp, err := hldrtmp.pat.Match(components, verb)
|
||||||
matches[k] = v
|
if err == nil {
|
||||||
|
match = true
|
||||||
|
for k, v := range mp {
|
||||||
|
matches[k] = v
|
||||||
|
}
|
||||||
|
hldr = hldrtmp
|
||||||
|
handler = handlertmp
|
||||||
|
break
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !match {
|
if !match {
|
||||||
h.errorHandler(ctx, h, 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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +163,8 @@ func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
if len(r.URL.RawQuery) > 0 {
|
if len(r.URL.RawQuery) > 0 {
|
||||||
umd, err := rflutil.URLMap(r.URL.RawQuery)
|
umd, err := rflutil.URLMap(r.URL.RawQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.errorHandler(ctx, h, w, r, err, http.StatusBadRequest)
|
h.errorHandler(ctx, handler, w, r, err, http.StatusBadRequest)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
for k, v := range umd {
|
for k, v := range umd {
|
||||||
matches[k] = v
|
matches[k] = v
|
||||||
@@ -161,24 +194,25 @@ func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
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 {
|
||||||
h.errorHandler(ctx, h, w, r, err, http.StatusInternalServerError)
|
h.errorHandler(ctx, handler, w, r, err, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
matches = rflutil.FlattenMap(matches)
|
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, h, w, r, err, http.StatusBadRequest)
|
h.errorHandler(ctx, handler, w, r, err, http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := cf.Marshal(argv.Interface())
|
b, err := cf.Marshal(argv.Interface())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.errorHandler(ctx, h, w, r, err, http.StatusBadRequest)
|
h.errorHandler(ctx, handler, w, r, err, http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
hr := &rpcRequest{
|
hr := &rpcRequest{
|
||||||
codec: cf,
|
codec: cf,
|
||||||
service: h.sopts.Name,
|
service: handler.sopts.Name,
|
||||||
contentType: ct,
|
contentType: ct,
|
||||||
method: fmt.Sprintf("%s.%s", hldr.name, hldr.mtype.method.Name),
|
method: fmt.Sprintf("%s.%s", hldr.name, hldr.mtype.method.Name),
|
||||||
body: b,
|
body: b,
|
||||||
@@ -187,23 +221,35 @@ func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
var scode int
|
var scode int
|
||||||
// define the handler func
|
// define the handler func
|
||||||
fn := func(ctx context.Context, req server.Request, rsp interface{}) (err error) {
|
fn := func(fctx context.Context, req server.Request, rsp interface{}) (err error) {
|
||||||
ctx = context.WithValue(ctx, rspCodeKey{}, &rspCodeVal{})
|
fctx = context.WithValue(fctx, rspCodeKey{}, &rspCodeVal{})
|
||||||
ctx = metadata.NewIncomingContext(ctx, md)
|
fctx = metadata.NewIncomingContext(fctx, md)
|
||||||
returnValues = function.Call([]reflect.Value{hldr.rcvr, hldr.mtype.prepareContext(ctx), reflect.ValueOf(argv.Interface()), reflect.ValueOf(rsp)})
|
returnValues = function.Call([]reflect.Value{hldr.rcvr, hldr.mtype.prepareContext(fctx), reflect.ValueOf(argv.Interface()), reflect.ValueOf(rsp)})
|
||||||
|
|
||||||
scode = GetRspCode(ctx)
|
scode = GetRspCode(fctx)
|
||||||
// The return value for the method is an error.
|
// The return value for the method is an error.
|
||||||
if rerr := returnValues[0].Interface(); rerr != nil {
|
if rerr := returnValues[0].Interface(); rerr != nil {
|
||||||
err = rerr.(error)
|
err = rerr.(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if md, ok := metadata.FromOutgoingContext(fctx); ok {
|
||||||
|
metadata.SetOutgoingContext(ctx, md)
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// wrap the handler func
|
// wrap the handler func
|
||||||
for i := len(h.sopts.HdlrWrappers); i > 0; i-- {
|
for i := len(handler.sopts.HdlrWrappers); i > 0; i-- {
|
||||||
fn = h.sopts.HdlrWrappers[i-1](fn)
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if appErr := fn(ctx, hr, replyv.Interface()); appErr != nil {
|
if appErr := fn(ctx, hr, replyv.Interface()); appErr != nil {
|
||||||
@@ -219,16 +265,23 @@ func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
} else {
|
} else {
|
||||||
b, err = cf.Marshal(replyv.Interface())
|
b, err = cf.Marshal(replyv.Interface())
|
||||||
}
|
}
|
||||||
if err != nil && h.sopts.Logger.V(logger.ErrorLevel) {
|
if err != nil && handler.sopts.Logger.V(logger.ErrorLevel) {
|
||||||
h.sopts.Logger.Errorf(h.sopts.Context, "handler err: %v", err)
|
handler.sopts.Logger.Errorf(handler.sopts.Context, "handler err: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if md, ok := metadata.FromOutgoingContext(ctx); ok {
|
||||||
|
for k, v := range md {
|
||||||
|
w.Header().Set(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
w.Header().Set("content-Type", ct)
|
w.Header().Set("content-Type", ct)
|
||||||
if scode != 0 {
|
if scode != 0 {
|
||||||
w.WriteHeader(scode)
|
w.WriteHeader(scode)
|
||||||
} else {
|
} else {
|
||||||
h.sopts.Logger.Warn(h.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.Write(b)
|
w.Write(b)
|
||||||
}
|
}
|
||||||
|
132
http.go
132
http.go
@@ -2,12 +2,13 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -25,16 +26,28 @@ import (
|
|||||||
type httpServer struct {
|
type httpServer struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
opts server.Options
|
opts server.Options
|
||||||
|
handlers map[string]server.Handler
|
||||||
hd server.Handler
|
hd server.Handler
|
||||||
exit chan chan error
|
exit chan chan error
|
||||||
subscribers map[*httpSubscriber][]broker.Subscriber
|
subscribers map[*httpSubscriber][]broker.Subscriber
|
||||||
// used for first registration
|
// used for first registration
|
||||||
registered bool
|
registered bool
|
||||||
// register service instance
|
// register service instance
|
||||||
rsvc *register.Service
|
rsvc *register.Service
|
||||||
|
init bool
|
||||||
|
errorHandler func(context.Context, server.Handler, http.ResponseWriter, *http.Request, error, int)
|
||||||
|
pathHandlers map[*regexp.Regexp]http.HandlerFunc
|
||||||
|
contentTypeHandlers map[string]http.HandlerFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpServer) newCodec(ct string) (codec.Codec, error) {
|
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 {
|
if cf, ok := h.opts.Codecs[ct]; ok {
|
||||||
return cf, nil
|
return cf, nil
|
||||||
}
|
}
|
||||||
@@ -49,17 +62,84 @@ func (h *httpServer) Options() server.Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpServer) Init(opts ...server.Option) error {
|
func (h *httpServer) Init(opts ...server.Option) error {
|
||||||
|
if len(opts) == 0 && h.init {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
h.Lock()
|
h.Lock()
|
||||||
|
defer h.Unlock()
|
||||||
|
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&h.opts)
|
o(&h.opts)
|
||||||
}
|
}
|
||||||
h.Unlock()
|
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
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpServer) Handle(handler server.Handler) error {
|
func (h *httpServer) Handle(handler server.Handler) error {
|
||||||
h.Lock()
|
h.Lock()
|
||||||
h.hd = handler
|
if hdlr, ok := handler.(*httpHandler); ok {
|
||||||
|
if _, ok := hdlr.hd.(http.Handler); ok {
|
||||||
|
h.hd = handler
|
||||||
|
} else {
|
||||||
|
if h.handlers == nil {
|
||||||
|
h.handlers = make(map[string]server.Handler)
|
||||||
|
}
|
||||||
|
h.handlers[handler.Name()] = handler
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
h.hd = handler
|
||||||
|
}
|
||||||
h.Unlock()
|
h.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -179,8 +259,11 @@ func (h *httpServer) Subscribe(sb server.Subscriber) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpServer) Register() error {
|
func (h *httpServer) Register() error {
|
||||||
|
var eps []*register.Endpoint
|
||||||
h.RLock()
|
h.RLock()
|
||||||
eps := h.hd.Endpoints()
|
for _, hdlr := range h.handlers {
|
||||||
|
eps = append(eps, hdlr.Endpoints()...)
|
||||||
|
}
|
||||||
rsvc := h.rsvc
|
rsvc := h.rsvc
|
||||||
config := h.opts
|
config := h.opts
|
||||||
h.RUnlock()
|
h.RUnlock()
|
||||||
@@ -317,7 +400,6 @@ func (h *httpServer) Deregister() error {
|
|||||||
func (h *httpServer) Start() error {
|
func (h *httpServer) Start() error {
|
||||||
h.RLock()
|
h.RLock()
|
||||||
config := h.opts
|
config := h.opts
|
||||||
hd := h.hd
|
|
||||||
h.RUnlock()
|
h.RUnlock()
|
||||||
|
|
||||||
// micro: config.Transport.Listen(config.Address)
|
// micro: config.Transport.Listen(config.Address)
|
||||||
@@ -352,13 +434,32 @@ func (h *httpServer) Start() error {
|
|||||||
h.opts.Address = ts.Addr().String()
|
h.opts.Address = ts.Addr().String()
|
||||||
h.Unlock()
|
h.Unlock()
|
||||||
|
|
||||||
handler, ok := hd.Handler().(http.Handler)
|
var handler http.Handler
|
||||||
if !ok {
|
var srvFunc func(net.Listener) error
|
||||||
handler, ok = hd.(http.Handler)
|
|
||||||
|
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 {
|
||||||
|
if hdlr, ok := h.hd.Handler().(http.Handler); ok {
|
||||||
|
hs.Handler = hdlr
|
||||||
|
handler = hs.Handler
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handler = hs.Handler
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ok {
|
if handler == nil && h.hd == nil {
|
||||||
return errors.New("Server required http.Handler")
|
handler = h
|
||||||
|
} else if handler == nil && h.hd != nil {
|
||||||
|
if hdlr, ok := h.hd.Handler().(http.Handler); ok {
|
||||||
|
handler = hdlr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if handler == nil {
|
||||||
|
return fmt.Errorf("cant process with nil handler")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := config.Broker.Connect(h.opts.Context); err != nil {
|
if err := config.Broker.Connect(h.opts.Context); err != nil {
|
||||||
@@ -376,7 +477,6 @@ func (h *httpServer) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn := handler
|
fn := handler
|
||||||
var srvFunc func(net.Listener) error
|
|
||||||
|
|
||||||
if h.opts.Context != nil {
|
if h.opts.Context != nil {
|
||||||
if mwf, ok := h.opts.Context.Value(middlewareKey{}).([]func(http.Handler) http.Handler); ok && len(mwf) > 0 {
|
if mwf, ok := h.opts.Context.Value(middlewareKey{}).([]func(http.Handler) http.Handler); ok && len(mwf) > 0 {
|
||||||
@@ -481,8 +581,10 @@ func (h *httpServer) Name() string {
|
|||||||
func NewServer(opts ...server.Option) server.Server {
|
func NewServer(opts ...server.Option) server.Server {
|
||||||
options := server.NewOptions(opts...)
|
options := server.NewOptions(opts...)
|
||||||
return &httpServer{
|
return &httpServer{
|
||||||
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),
|
||||||
|
errorHandler: DefaultErrorHandler,
|
||||||
|
pathHandlers: make(map[*regexp.Regexp]http.HandlerFunc),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
52
options.go
52
options.go
@@ -13,14 +13,17 @@ type rspCodeVal struct {
|
|||||||
code int
|
code int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetError pass error to caller
|
||||||
func SetError(err interface{}) error {
|
func SetError(err interface{}) error {
|
||||||
return &Error{err: err}
|
return &Error{err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Error struct holds error
|
||||||
type Error struct {
|
type Error struct {
|
||||||
err interface{}
|
err interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Error func for error interface
|
||||||
func (err *Error) Error() string {
|
func (err *Error) Error() string {
|
||||||
return fmt.Sprintf("%v", err.err)
|
return fmt.Sprintf("%v", err.err)
|
||||||
}
|
}
|
||||||
@@ -43,12 +46,61 @@ func GetRspCode(ctx context.Context) int {
|
|||||||
|
|
||||||
type middlewareKey struct{}
|
type middlewareKey struct{}
|
||||||
|
|
||||||
|
// Middleware passes http middlewares
|
||||||
func Middleware(mw ...func(http.Handler) http.Handler) server.Option {
|
func Middleware(mw ...func(http.Handler) http.Handler) server.Option {
|
||||||
return server.SetOption(middlewareKey{}, mw)
|
return server.SetOption(middlewareKey{}, mw)
|
||||||
}
|
}
|
||||||
|
|
||||||
type serverKey struct{}
|
type serverKey struct{}
|
||||||
|
|
||||||
|
// Server provide ability to pass *http.Server
|
||||||
func Server(hs *http.Server) server.Option {
|
func Server(hs *http.Server) server.Option {
|
||||||
return server.SetOption(serverKey{}, hs)
|
return server.SetOption(serverKey{}, hs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type errorHandlerKey struct{}
|
||||||
|
|
||||||
|
// ErrorHandler specifies handler for errors
|
||||||
|
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{}
|
||||||
|
type 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{}
|
||||||
|
type 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user