Skip to content

Commit

Permalink
[SCB-1512] Allow custom handler chain. (#592)
Browse files Browse the repository at this point in the history
  • Loading branch information
humingcheng authored and tianxiaoliang committed Oct 9, 2019
1 parent 5c3a16d commit a87136e
Show file tree
Hide file tree
Showing 12 changed files with 117 additions and 90 deletions.
10 changes: 6 additions & 4 deletions pkg/rest/client.go
Expand Up @@ -21,10 +21,6 @@ import (
"crypto/tls"
"errors"
"fmt"
"github.com/apache/servicecomb-service-center/pkg/buffer"
"github.com/apache/servicecomb-service-center/pkg/tlsutil"
"github.com/apache/servicecomb-service-center/pkg/util"
"golang.org/x/net/context"
"io"
"io/ioutil"
"net/http"
Expand All @@ -33,6 +29,12 @@ import (
"os"
"strings"
"time"

"github.com/apache/servicecomb-service-center/pkg/buffer"
"github.com/apache/servicecomb-service-center/pkg/tlsutil"
"github.com/apache/servicecomb-service-center/pkg/util"

"golang.org/x/net/context"
)

var defaultURLClientOption = URLClientOption{
Expand Down
2 changes: 1 addition & 1 deletion pkg/rest/common.go
Expand Up @@ -31,7 +31,7 @@ const (
CTX_MATCH_PATTERN = "_server_match_pattern"
CTX_MATCH_FUNC = "_server_match_func"

SERVER_CHAIN_NAME = "_server_chain"
ServerChainName = "_server_chain"

HEADER_RESPONSE_STATUS = "X-Response-Status"

Expand Down
55 changes: 4 additions & 51 deletions pkg/rest/roa.go
Expand Up @@ -17,69 +17,22 @@
package rest

import (
"github.com/apache/servicecomb-service-center/pkg/log"
"github.com/apache/servicecomb-service-center/pkg/util"
"net/http"
"reflect"
)

var (
serverHandler *ROAServerHandler
serverHandler *ROAServerHandler // serverHandler is the default handler
)

func init() {
initROAServerHandler()
}

type ROAServantService interface {
URLPatterns() []Route
}

func initROAServerHandler() *ROAServerHandler {
serverHandler = NewROAServerHander()
return serverHandler
serverHandler.setChainName(ServerChainName)
}

// RegisterServant registers a ROAServantService into serverHandler
// servant must be an pointer to service object
func RegisterServant(servant interface{}) {
val := reflect.ValueOf(servant)
ind := reflect.Indirect(val)
typ := ind.Type()
name := util.FileLastName(typ.PkgPath() + "." + typ.Name())
if val.Kind() != reflect.Ptr {
log.Errorf(nil, "<rest.RegisterServant> cannot use non-ptr servant struct `%s`", name)
return
}

urlPatternFunc := val.MethodByName("URLPatterns")
if !urlPatternFunc.IsValid() {
log.Errorf(nil, "<rest.RegisterServant> no 'URLPatterns' function in servant struct `%s`", name)
return
}

vals := urlPatternFunc.Call([]reflect.Value{})
if len(vals) <= 0 {
log.Errorf(nil, "<rest.RegisterServant> call 'URLPatterns' function failed in servant struct `%s`", name)
return
}

val0 := vals[0]
if !val.CanInterface() {
log.Errorf(nil, "<rest.RegisterServant> result of 'URLPatterns' function not interface type in servant struct `%s`", name)
return
}

if routes, ok := val0.Interface().([]Route); ok {
log.Infof("register servant %s", name)
for _, route := range routes {
err := serverHandler.addRoute(&route)
if err != nil {
log.Errorf(err, "register route failed.")
}
}
} else {
log.Errorf(nil, "<rest.RegisterServant> result of 'URLPatterns' function not []*Route type in servant struct `%s`", name)
}
serverHandler.RegisterServant(servant)
}

//GetRouter return the router fo REST service
Expand Down
101 changes: 80 additions & 21 deletions pkg/rest/route.go
Expand Up @@ -19,15 +19,18 @@ package rest
import (
"errors"
"fmt"
"net/http"
"net/url"
"reflect"
"strings"

"github.com/apache/servicecomb-service-center/pkg/chain"
errorsEx "github.com/apache/servicecomb-service-center/pkg/errors"
"github.com/apache/servicecomb-service-center/pkg/log"
"github.com/apache/servicecomb-service-center/pkg/util"
"net/http"
"net/url"
"strings"
)

// URLPattern defines an uri pattern
type URLPattern struct {
Method string
Path string
Expand All @@ -39,6 +42,7 @@ type urlPatternHandler struct {
http.Handler
}

// Route is a http route
type Route struct {
// Method is one of the following: GET,PUT,POST,DELETE
Method string
Expand All @@ -48,49 +52,104 @@ type Route struct {
Func func(w http.ResponseWriter, r *http.Request)
}

// HTTP request multiplexer
// ROAServantService defines a group of Routes
type ROAServantService interface {
URLPatterns() []Route
}

// ROAServerHandler is a HTTP request multiplexer
// Attention:
// 1. not thread-safe, must be initialized completely before serve http request
// 2. redirect not supported
type ROAServerHandler struct {
handlers map[string][]*urlPatternHandler
handlers map[string][]*urlPatternHandler
chainName string
}

// NewROAServerHander news an ROAServerHandler
func NewROAServerHander() *ROAServerHandler {
return &ROAServerHandler{
handlers: make(map[string][]*urlPatternHandler),
}
}

func (this *ROAServerHandler) addRoute(route *Route) (err error) {
// RegisterServant registers a ROAServantService
// servant must be an pointer to service object
func (roa *ROAServerHandler) RegisterServant(servant interface{}) {
val := reflect.ValueOf(servant)
ind := reflect.Indirect(val)
typ := ind.Type()
name := util.FileLastName(typ.PkgPath() + "." + typ.Name())
if val.Kind() != reflect.Ptr {
log.Errorf(nil, "<rest.RegisterServant> cannot use non-ptr servant struct `%s`", name)
return
}

urlPatternFunc := val.MethodByName("URLPatterns")
if !urlPatternFunc.IsValid() {
log.Errorf(nil, "<rest.RegisterServant> no 'URLPatterns' function in servant struct `%s`", name)
return
}

vals := urlPatternFunc.Call([]reflect.Value{})
if len(vals) <= 0 {
log.Errorf(nil, "<rest.RegisterServant> call 'URLPatterns' function failed in servant struct `%s`", name)
return
}

val0 := vals[0]
if !val.CanInterface() {
log.Errorf(nil, "<rest.RegisterServant> result of 'URLPatterns' function not interface type in servant struct `%s`", name)
return
}

if routes, ok := val0.Interface().([]Route); ok {
log.Infof("register servant %s", name)
for _, route := range routes {
err := roa.addRoute(&route)
if err != nil {
log.Errorf(err, "register route failed.")
}
}
} else {
log.Errorf(nil, "<rest.RegisterServant> result of 'URLPatterns' function not []*Route type in servant struct `%s`", name)
}
}

func (roa *ROAServerHandler) setChainName(name string) {
roa.chainName = name
}

func (roa *ROAServerHandler) addRoute(route *Route) (err error) {
method := strings.ToUpper(route.Method)
if !isValidMethod(method) || !strings.HasPrefix(route.Path, "/") || route.Func == nil {
message := fmt.Sprintf("Invalid route parameters(method: %s, path: %s)", method, route.Path)
log.Errorf(nil, message)
return errors.New(message)
}

this.handlers[method] = append(this.handlers[method], &urlPatternHandler{
roa.handlers[method] = append(roa.handlers[method], &urlPatternHandler{
util.FormatFuncName(util.FuncName(route.Func)), route.Path, http.HandlerFunc(route.Func)})
log.Infof("register route %s(%s)", route.Path, method)

return nil
}

func (this *ROAServerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
for _, ph := range this.handlers[r.Method] {
// ServeHTTP implements http.Handler
func (roa *ROAServerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
for _, ph := range roa.handlers[r.Method] {
if params, ok := ph.try(r.URL.Path); ok {
if len(params) > 0 {
r.URL.RawQuery = params + r.URL.RawQuery
}

this.serve(ph, w, r)
roa.serve(ph, w, r)
return
}
}

allowed := make([]string, 0, len(this.handlers))
for method, handlers := range this.handlers {
allowed := make([]string, 0, len(roa.handlers))
for method, handlers := range roa.handlers {
if method == r.Method {
continue
}
Expand All @@ -111,14 +170,14 @@ func (this *ROAServerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
}

func (this *ROAServerHandler) serve(ph *urlPatternHandler, w http.ResponseWriter, r *http.Request) {
func (roa *ROAServerHandler) serve(ph *urlPatternHandler, w http.ResponseWriter, r *http.Request) {
ctx := util.NewStringContext(r.Context())
if ctx != r.Context() {
nr := r.WithContext(ctx)
*r = *nr
}

inv := chain.NewInvocation(ctx, chain.NewChain(SERVER_CHAIN_NAME, chain.Handlers(SERVER_CHAIN_NAME)))
inv := chain.NewInvocation(ctx, chain.NewChain(roa.chainName, chain.Handlers(roa.chainName)))
inv.WithContext(CTX_RESPONSE, w).
WithContext(CTX_REQUEST, r).
WithContext(CTX_MATCH_PATTERN, ph.Path).
Expand Down Expand Up @@ -148,25 +207,25 @@ func (this *ROAServerHandler) serve(ph *urlPatternHandler, w http.ResponseWriter
})
}

func (this *urlPatternHandler) try(path string) (p string, _ bool) {
func (roa *urlPatternHandler) try(path string) (p string, _ bool) {
var i, j int
l, sl := len(this.Path), len(path)
l, sl := len(roa.Path), len(path)
for i < sl {
switch {
case j >= l:
if this.Path != "/" && l > 0 && this.Path[l-1] == '/' {
if roa.Path != "/" && l > 0 && roa.Path[l-1] == '/' {
return p, true
}
return "", false
case this.Path[j] == ':':
case roa.Path[j] == ':':
var val string
var nextc byte
o := j
_, nextc, j = match(this.Path, isAlnum, 0, j+1)
_, nextc, j = match(roa.Path, isAlnum, 0, j+1)
val, _, i = match(path, matchParticial, nextc, i)

p += url.QueryEscape(this.Path[o:j]) + "=" + url.QueryEscape(val) + "&"
case path[i] == this.Path[j]:
p += url.QueryEscape(roa.Path[o:j]) + "=" + url.QueryEscape(val) + "&"
case path[i] == roa.Path[j]:
i++
j++
default:
Expand Down
8 changes: 5 additions & 3 deletions pkg/rest/server.go
Expand Up @@ -19,15 +19,17 @@ package rest
import (
"compress/gzip"
"crypto/tls"
"github.com/NYTimes/gziphandler"
"github.com/apache/servicecomb-service-center/pkg/grace"
"github.com/apache/servicecomb-service-center/pkg/log"
"net"
"net/http"
"os"
"sync"
"sync/atomic"
"time"

"github.com/apache/servicecomb-service-center/pkg/grace"
"github.com/apache/servicecomb-service-center/pkg/log"

"github.com/NYTimes/gziphandler"
)

const (
Expand Down
2 changes: 1 addition & 1 deletion server/handler/auth/auth.go
Expand Up @@ -46,5 +46,5 @@ func (h *AuthRequest) Handle(i *chain.Invocation) {
}

func RegisterHandlers() {
chain.RegisterHandler(rest.SERVER_CHAIN_NAME, &AuthRequest{})
chain.RegisterHandler(rest.ServerChainName, &AuthRequest{})
}
2 changes: 1 addition & 1 deletion server/handler/cache/cache.go
Expand Up @@ -58,5 +58,5 @@ func (l *CacheResponse) Handle(i *chain.Invocation) {
}

func RegisterHandlers() {
chain.RegisterHandler(rest.SERVER_CHAIN_NAME, &CacheResponse{})
chain.RegisterHandler(rest.ServerChainName, &CacheResponse{})
}
2 changes: 1 addition & 1 deletion server/handler/context/context.go
Expand Up @@ -65,5 +65,5 @@ func IsSkip(url string) bool {
}

func RegisterHandlers() {
chain.RegisterHandler(roa.SERVER_CHAIN_NAME, &ContextHandler{})
chain.RegisterHandler(roa.ServerChainName, &ContextHandler{})
}
2 changes: 1 addition & 1 deletion server/handler/maxbody/maxbody.go
Expand Up @@ -66,5 +66,5 @@ func (c *MaxBodyHandler) Handle(i *chain.Invocation) {
}

func RegisterHandlers() {
chain.RegisterHandler(rest.SERVER_CHAIN_NAME, &MaxBodyHandler{})
chain.RegisterHandler(rest.ServerChainName, &MaxBodyHandler{})
}
2 changes: 1 addition & 1 deletion server/handler/metric/metric.go
Expand Up @@ -42,5 +42,5 @@ func (h *MetricsHandler) Handle(i *chain.Invocation) {
}

func RegisterHandlers() {
chain.RegisterHandler(rest.SERVER_CHAIN_NAME, &MetricsHandler{})
chain.RegisterHandler(rest.ServerChainName, &MetricsHandler{})
}
2 changes: 1 addition & 1 deletion server/handler/tracing/tracing.go
Expand Up @@ -45,5 +45,5 @@ func (h *TracingHandler) Handle(i *chain.Invocation) {
}

func RegisterHandlers() {
chain.RegisterHandler(rest.SERVER_CHAIN_NAME, &TracingHandler{})
chain.RegisterHandler(rest.ServerChainName, &TracingHandler{})
}

0 comments on commit a87136e

Please sign in to comment.