[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.