[SCB-1512] Allow custom handler chain. (#592)
diff --git a/pkg/rest/client.go b/pkg/rest/client.go
index 96fc92d..0bb9948 100644
--- a/pkg/rest/client.go
+++ b/pkg/rest/client.go
@@ -21,10 +21,6 @@
"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"
@@ -33,6 +29,12 @@
"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{
diff --git a/pkg/rest/common.go b/pkg/rest/common.go
index 1d99ffc..a1936e3 100644
--- a/pkg/rest/common.go
+++ b/pkg/rest/common.go
@@ -31,7 +31,7 @@
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"
diff --git a/pkg/rest/roa.go b/pkg/rest/roa.go
index 27ce39e..5bff1bd 100644
--- a/pkg/rest/roa.go
+++ b/pkg/rest/roa.go
@@ -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
diff --git a/pkg/rest/route.go b/pkg/rest/route.go
index e3bac55..d8f220e 100644
--- a/pkg/rest/route.go
+++ b/pkg/rest/route.go
@@ -19,15 +19,18 @@
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
@@ -39,6 +42,7 @@
http.Handler
}
+// Route is a http route
type Route struct {
// Method is one of the following: GET,PUT,POST,DELETE
Method string
@@ -48,21 +52,75 @@
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)
@@ -70,27 +128,28 @@
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
}
@@ -111,14 +170,14 @@
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).
@@ -148,25 +207,25 @@
})
}
-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:
diff --git a/pkg/rest/server.go b/pkg/rest/server.go
index 37daacc..16c7e2c 100644
--- a/pkg/rest/server.go
+++ b/pkg/rest/server.go
@@ -19,15 +19,17 @@
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 (
diff --git a/server/handler/auth/auth.go b/server/handler/auth/auth.go
index f99e54b..bdc165b 100644
--- a/server/handler/auth/auth.go
+++ b/server/handler/auth/auth.go
@@ -46,5 +46,5 @@
}
func RegisterHandlers() {
- chain.RegisterHandler(rest.SERVER_CHAIN_NAME, &AuthRequest{})
+ chain.RegisterHandler(rest.ServerChainName, &AuthRequest{})
}
diff --git a/server/handler/cache/cache.go b/server/handler/cache/cache.go
index adc3e66..c37b414 100644
--- a/server/handler/cache/cache.go
+++ b/server/handler/cache/cache.go
@@ -58,5 +58,5 @@
}
func RegisterHandlers() {
- chain.RegisterHandler(rest.SERVER_CHAIN_NAME, &CacheResponse{})
+ chain.RegisterHandler(rest.ServerChainName, &CacheResponse{})
}
diff --git a/server/handler/context/context.go b/server/handler/context/context.go
index 7cde901..abc705d 100644
--- a/server/handler/context/context.go
+++ b/server/handler/context/context.go
@@ -65,5 +65,5 @@
}
func RegisterHandlers() {
- chain.RegisterHandler(roa.SERVER_CHAIN_NAME, &ContextHandler{})
+ chain.RegisterHandler(roa.ServerChainName, &ContextHandler{})
}
diff --git a/server/handler/maxbody/maxbody.go b/server/handler/maxbody/maxbody.go
index ba7315b..26adbe5 100644
--- a/server/handler/maxbody/maxbody.go
+++ b/server/handler/maxbody/maxbody.go
@@ -66,5 +66,5 @@
}
func RegisterHandlers() {
- chain.RegisterHandler(rest.SERVER_CHAIN_NAME, &MaxBodyHandler{})
+ chain.RegisterHandler(rest.ServerChainName, &MaxBodyHandler{})
}
diff --git a/server/handler/metric/metric.go b/server/handler/metric/metric.go
index 814f2c9..ec68eb4 100644
--- a/server/handler/metric/metric.go
+++ b/server/handler/metric/metric.go
@@ -42,5 +42,5 @@
}
func RegisterHandlers() {
- chain.RegisterHandler(rest.SERVER_CHAIN_NAME, &MetricsHandler{})
+ chain.RegisterHandler(rest.ServerChainName, &MetricsHandler{})
}
diff --git a/server/handler/tracing/tracing.go b/server/handler/tracing/tracing.go
index f7c0bcd..e081c14 100644
--- a/server/handler/tracing/tracing.go
+++ b/server/handler/tracing/tracing.go
@@ -45,5 +45,5 @@
}
func RegisterHandlers() {
- chain.RegisterHandler(rest.SERVER_CHAIN_NAME, &TracingHandler{})
+ chain.RegisterHandler(rest.ServerChainName, &TracingHandler{})
}
diff --git a/server/rest/handler.go b/server/rest/handler.go
index 9a8c77f..bf5288d 100644
--- a/server/rest/handler.go
+++ b/server/rest/handler.go
@@ -18,23 +18,34 @@
package rest
import (
+ "net/http"
+ "time"
+
roa "github.com/apache/servicecomb-service-center/pkg/rest"
"github.com/apache/servicecomb-service-center/pkg/util"
"github.com/apache/servicecomb-service-center/server/interceptor"
- "net/http"
- "time"
)
const CTX_START_TIMESTAMP = "x-start-timestamp"
func init() {
// api
- RegisterServerHandler("/", &ServerHandler{})
+ RegisterServerHandler("/", NewServerHandler(roa.GetRouter()))
}
+// NewServerHandler news a ServerHandler
+func NewServerHandler(h http.Handler) *ServerHandler {
+ return &ServerHandler{
+ Handler: h,
+ }
+}
+
+// ServerHandler is a http handler for service-center api
type ServerHandler struct {
+ Handler http.Handler
}
+// ServeHTTP implements http.Handler
func (s *ServerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
util.SetRequestContext(r, CTX_START_TIMESTAMP, time.Now())
@@ -43,7 +54,7 @@
return
}
- roa.GetRouter().ServeHTTP(w, r)
+ s.Handler.ServeHTTP(w, r)
// CAUTION: There will be cause a concurrent problem,
// if here get/set the HTTP request headers.