Compare commits

..

18 Commits

Author SHA1 Message Date
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
14 changed files with 270 additions and 211 deletions

View File

@@ -2,6 +2,7 @@
"extends": [ "extends": [
"config:base" "config:base"
], ],
"postUpdateOptions": ["gomodTidy"],
"packageRules": [ "packageRules": [
{ {
"matchUpdateTypes": ["minor", "patch", "pin", "digest"], "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 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

6
go.mod
View File

@@ -3,8 +3,6 @@ module github.com/unistack-org/micro-server-http/v3
go 1.16 go 1.16
require ( require (
github.com/unistack-org/micro/v3 v3.3.13 github.com/unistack-org/micro/v3 v3.3.17
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6
) )
//replace github.com/unistack-org/micro/v3 => ../../micro

55
go.sum
View File

@@ -6,57 +6,14 @@ github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH
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.3.0 h1:pEj/8QVFzMlNMEL//q/Te8qgG+XI6LTYIQrb6hMymgk= github.com/unistack-org/micro/v3 v3.3.17 h1:WcyS7InP0DlS/JpRQGLh5sG6VstkdHJbgpMp+gmHmwg=
github.com/unistack-org/micro/v3 v3.3.0/go.mod h1:iJwCWq2PECMxigfqe6TPC5GLWvj6P94Kk+PTVZGL3w8= github.com/unistack-org/micro/v3 v3.3.17/go.mod h1:022EOEZZ789hZY3yB5ZSMXU6jLiadBgcNB/cpediV3c=
github.com/unistack-org/micro/v3 v3.3.1 h1:2KUgb5THy5GUjYgMcOf3TXYIJ0h2RbJHb2j6eeWdRx0= golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6 h1:0PC75Fz/kyMGhL0e1QnypqK2kQMqKt9csD1GnMJR+Zk=
github.com/unistack-org/micro/v3 v3.3.1/go.mod h1:iJwCWq2PECMxigfqe6TPC5GLWvj6P94Kk+PTVZGL3w8= golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
github.com/unistack-org/micro/v3 v3.3.2 h1:1r7fmaobJVZBKDhIewSBy8R4H/6YazQFetsDgULV6Mw=
github.com/unistack-org/micro/v3 v3.3.2/go.mod h1:tX95c0Qx4w6oqU7qKThs9lya9P507BdZ29MsTVDmU6w=
github.com/unistack-org/micro/v3 v3.3.4 h1:wSYjL1sfrqtMmgFfYKylrKpAepMHrPcOpe10hvQAKuo=
github.com/unistack-org/micro/v3 v3.3.4/go.mod h1:tX95c0Qx4w6oqU7qKThs9lya9P507BdZ29MsTVDmU6w=
github.com/unistack-org/micro/v3 v3.3.7 h1:AKQEevrlT2aqHYRFGd6BEFNnurHLvJyqvWvLCFP7iX4=
github.com/unistack-org/micro/v3 v3.3.7/go.mod h1:M3RGgKZX1OlTn0QB+3R1tlSoGwmEg6rb/Isr0lLYmjQ=
github.com/unistack-org/micro/v3 v3.3.8 h1:4e4gWuZI/cRUGZ6ZZpdrIDOF2MLO6cjHxeFfZzy610Y=
github.com/unistack-org/micro/v3 v3.3.8/go.mod h1:M3RGgKZX1OlTn0QB+3R1tlSoGwmEg6rb/Isr0lLYmjQ=
github.com/unistack-org/micro/v3 v3.3.9 h1:NFF4wymDDxgYeZU++04BxvETrrAnd0iNzVyfm/ssQI4=
github.com/unistack-org/micro/v3 v3.3.9/go.mod h1:M3RGgKZX1OlTn0QB+3R1tlSoGwmEg6rb/Isr0lLYmjQ=
github.com/unistack-org/micro/v3 v3.3.10 h1:yMSiyplupFQ7xy/kNXL/zqSnqGSjrlNa2GTCwea5BVg=
github.com/unistack-org/micro/v3 v3.3.10/go.mod h1:5ragE2E8ER5d4FZQJG9pB6qdfOoLXLfKW89l77Dy3jQ=
github.com/unistack-org/micro/v3 v3.3.11 h1:Jr0gAw5lLqgddiHKQeWUOUeP6ZqgRhz52EA9zJ5MJ3U=
github.com/unistack-org/micro/v3 v3.3.11/go.mod h1:PPCt675o3HPcODFbJ4iRWPmQFAk1WQ+asQSOb/syq6U=
github.com/unistack-org/micro/v3 v3.3.13 h1:y4bDDkbwnjgOckrhFkC6D/o42tr75X33UbrB+Ko0M68=
github.com/unistack-org/micro/v3 v3.3.13/go.mod h1:98hNcMXp/WyWJwLwCuwrhN1Jm7aCWaRNsMfRjK8Fq+Y=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210323141857-08027d57d8cf h1:sewfyKLWuY3ko6EI4hbFziQ8bHkfammpzCDfLT92I1c=
golang.org/x/net v0.0.0-20210323141857-08027d57d8cf/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210324051636-2c4c8ecb7826 h1:lNRDRnwZWawoPHDS50ebYHTOHjctRMLSrUSQFcAHiW4=
golang.org/x/net v0.0.0-20210324051636-2c4c8ecb7826/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210324205630-d1beb07c2056 h1:sANdAef76Ioam9aQUUdcAqricwY/WUaMc4+7LY4eGg8=
golang.org/x/net v0.0.0-20210324205630-d1beb07c2056/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20210326060303-6b1517762897 h1:KrsHThm5nFk34YtATK1LsThyGhGbGe1olrte/HInHvs=
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20210326220855-61e056675ecf h1:WUcCxqQqDT0aXO4VnQbfMvp4zh7m1Gb2clVuHUAGGRE=
golang.org/x/net v0.0.0-20210326220855-61e056675ecf/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20210329181859-df645c7b52b1 h1:GDp1VG8WvY8lq4ic4L7GzKC3fNRlOa3UqUEpib8aBQE=
golang.org/x/net v0.0.0-20210329181859-df645c7b52b1/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20210330075724-22f4162a9025 h1:WkEv4x2mvGbnkuK9cWl/Ru2pzfVxAgpSDLT/holPSug=
golang.org/x/net v0.0.0-20210330075724-22f4162a9025/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20210330142815-c8897c278d10 h1:j6SlnKgJhONcqBZAH/cLNz6hg79L7Q18RY4BiN0r0LI=
golang.org/x/net v0.0.0-20210330142815-c8897c278d10/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20210330230544-e57232859fb2 h1:nGCZOty+lVDsc4H2qPFksI5Se296+V+GhMiL/TzmYNk=
golang.org/x/net v0.0.0-20210330230544-e57232859fb2/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210331060903-cb1fcc7394e5 h1:zuP3axpB9rV3xH0EA7n3/gCrNPZm2SRl0l4mVH2BRj4=
golang.org/x/net v0.0.0-20210331060903-cb1fcc7394e5/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c h1:KHUzaHIpjWVlVVNh65G3hhuj3KB1HnjY6Cq5cTvRQT8=
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
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-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/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.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/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@@ -7,8 +7,8 @@ import (
"net/http" "net/http"
"reflect" "reflect"
"strings" "strings"
"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"
@@ -21,32 +21,28 @@ 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"
) )
type patHandler struct { type patHandler struct {
pat rutil.Pattern
mtype *methodType mtype *methodType
name string
rcvr reflect.Value rcvr reflect.Value
name string
pat rutil.Pattern
} }
type httpHandler struct { type httpHandler struct {
name string
opts server.HandlerOptions opts server.HandlerOptions
sopts server.Options
eps []*register.Endpoint
hd interface{} hd interface{}
handlers map[string][]patHandler handlers map[string][]patHandler
} name string
eps []*register.Endpoint
func (h *httpHandler) newCodec(ct string) (codec.Codec, error) { sopts server.Options
if cf, ok := h.sopts.Codecs[ct]; ok { sync.RWMutex
return cf, nil
}
return nil, codec.ErrUnknownContentType
} }
func (h *httpHandler) Name() string { func (h *httpHandler) Name() string {
@@ -66,7 +62,26 @@ 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) {
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 := metadata.NewIncomingContext(r.Context(), nil)
defer r.Body.Close() defer r.Body.Close()
@@ -76,20 +91,7 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} }
ct := strings.Split(DefaultContentType, ";")[0] cf, err := h.newCodec(ct)
if htype := r.Header.Get("Content-Type"); htype != "" {
ct = strings.Split(htype, ";")[0]
}
var cf codec.Codec
var err error
switch ct {
case "application/x-www-form-urlencoded":
cf, err = h.newCodec(strings.Split(DefaultContentType, ";")[0])
default:
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)
return return
@@ -116,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
@@ -145,25 +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
}
for k, v := range umd {
matches[k] = v
}
}
if ct == "application/x-www-form-urlencoded" {
buf, err := io.ReadAll(r.Body)
if err != nil {
h.errorHandler(ctx, nil, w, r, err, http.StatusBadRequest)
return
}
umd, err := rflutil.URLMap(string(buf))
if err != nil {
h.errorHandler(ctx, handler, w, r, err, http.StatusBadRequest)
return return
} }
for k, v := range umd { for k, v := range umd {
@@ -190,14 +176,12 @@ 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 ct != "application/x-www-form-urlencoded" { 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, handler, w, r, err, http.StatusInternalServerError)
h.errorHandler(ctx, handler, w, r, err, http.StatusInternalServerError) return
return
}
} }
matches = rflutil.FlattenMap(matches) matches = rflutil.FlattenMap(matches)
@@ -206,13 +190,10 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} }
var b []byte b, err := cf.Marshal(argv.Interface())
if ct != "application/x-www-form-urlencoded" { if err != nil {
b, err = cf.Marshal(argv.Interface()) h.errorHandler(ctx, handler, w, r, err, http.StatusBadRequest)
if err != nil { return
h.errorHandler(ctx, handler, w, r, err, http.StatusBadRequest)
return
}
} }
hr := &rpcRequest{ hr := &rpcRequest{
@@ -249,6 +230,15 @@ func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fn = handler.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
}
ct = DefaultContentType
}
if appErr := fn(ctx, hr, replyv.Interface()); appErr != nil { if appErr := fn(ctx, hr, replyv.Interface()); appErr != nil {
switch verr := appErr.(type) { switch verr := appErr.(type) {
case *errors.Error: case *errors.Error:
@@ -277,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)
}
} }

127
http.go
View File

@@ -8,6 +8,7 @@ import (
"net" "net"
"net/http" "net/http"
"reflect" "reflect"
"regexp"
"sort" "sort"
"strings" "strings"
"sync" "sync"
@@ -23,21 +24,28 @@ import (
) )
type httpServer struct { 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 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 registered bool
// register service instance init bool
rsvc *register.Service
init bool
errorHandler func(context.Context, server.Handler, http.ResponseWriter, *http.Request, error, int)
} }
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
} }
@@ -57,6 +65,8 @@ func (h *httpServer) Init(opts ...server.Option) error {
} }
h.Lock() h.Lock()
defer h.Unlock()
for _, o := range opts { for _, o := range opts {
o(&h.opts) o(&h.opts)
} }
@@ -66,7 +76,26 @@ func (h *httpServer) Init(opts ...server.Option) error {
if h.handlers == nil { if h.handlers == nil {
h.handlers = make(map[string]server.Handler) h.handlers = make(map[string]server.Handler)
} }
h.Unlock() 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 { if err := h.opts.Register.Init(); err != nil {
return err return err
@@ -97,34 +126,35 @@ 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
} }
func (h *httpServer) NewHandler(handler interface{}, opts ...server.HandlerOption) server.Handler { func (h *httpServer) NewHandler(handler interface{}, opts ...server.HandlerOption) server.Handler {
options := server.NewHandlerOptions(opts...) options := server.NewHandlerOptions(opts...)
var eps []*register.Endpoint eps := make([]*register.Endpoint, 0, len(options.Metadata))
for name, metadata := range options.Metadata {
if !options.Internal { eps = append(eps, &register.Endpoint{
for name, metadata := range options.Metadata { Name: name,
eps = append(eps, &register.Endpoint{ Metadata: metadata,
Name: name, })
Metadata: metadata,
})
}
} }
hdlr := &httpHandler{ hdlr := &httpHandler{
@@ -253,12 +283,10 @@ 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
if !e.Options().Internal { subscriberList = append(subscriberList, e)
subscriberList = append(subscriberList, e)
}
} }
sort.Slice(subscriberList, func(i, j int) bool { sort.Slice(subscriberList, func(i, j int) bool {
return subscriberList[i].topic > subscriberList[j].topic return subscriberList[i].topic > subscriberList[j].topic
@@ -275,7 +303,7 @@ func (h *httpServer) Register() error {
if !registered { if !registered {
if config.Logger.V(logger.InfoLevel) { 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)
} }
} }
@@ -330,7 +358,7 @@ func (h *httpServer) Deregister() error {
} }
if config.Logger.V(logger.InfoLevel) { 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 { if err := server.DefaultDeregisterFunc(service, config); err != nil {
@@ -406,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 {
@@ -437,7 +466,7 @@ func (h *httpServer) Start() error {
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)
} }
} else { } else {
if err = h.Register(); err != nil { if err = h.Register(); err != nil {
@@ -461,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() {
@@ -487,25 +524,26 @@ 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)
} }
// deregister self in case of error // deregister self in case of error
if err := h.Deregister(); err != nil { if err := h.Deregister(); err != nil {
if config.Logger.V(logger.ErrorLevel) { 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 { } else 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", config.Name, config.Id, rerr) config.Logger.Errorf(config.Context, "Server %s-%s register check error: %s", config.Name, config.ID, rerr)
} }
continue continue
} }
if err := h.Register(); err != nil { if err := h.Register(); err != nil {
if config.Logger.V(logger.ErrorLevel) { 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)
} }
} }
@@ -554,5 +592,6 @@ func NewServer(opts ...server.Option) server.Server {
exit: make(chan chan error), exit: make(chan chan error),
subscribers: make(map[*httpSubscriber][]broker.Subscriber), subscribers: make(map[*httpSubscriber][]broker.Subscriber),
errorHandler: DefaultErrorHandler, errorHandler: DefaultErrorHandler,
pathHandlers: make(map[*regexp.Regexp]http.HandlerFunc),
} }
} }

View File

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

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,5 +67,46 @@ 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 (
// PathHandler specifies http handler for path 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/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 {
rw io.ReadWriter rw io.ReadWriter
service string payload interface{}
method string
endpoint string
target string
contentType string
codec codec.Codec codec codec.Codec
header metadata.Metadata header metadata.Metadata
method string
endpoint string
contentType string
service string
body []byte body []byte
stream bool stream bool
payload interface{}
} }
type rpcMessage struct { type rpcMessage struct {
payload interface{}
codec codec.Codec
header metadata.Metadata
topic string topic string
contentType string contentType string
payload interface{}
header metadata.Metadata
body []byte body []byte
codec codec.Codec
} }
func (r *rpcRequest) ContentType() string { func (r *rpcRequest) ContentType() string {
@@ -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

@@ -11,10 +11,10 @@ import (
) )
type methodType struct { type methodType struct {
method reflect.Method
ArgType reflect.Type ArgType reflect.Type
ReplyType reflect.Type ReplyType reflect.Type
ContextType reflect.Type ContextType reflect.Type
method reflect.Method
stream bool 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()) 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,16 +14,12 @@ 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 {
method reflect.Value
reqType reflect.Type reqType reflect.Type
ctxType reflect.Type ctxType reflect.Type
method reflect.Value
} }
type httpSubscriber struct { type httpSubscriber 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))