Optimize router. (#144)

diff --git a/pkg/chain/callback.go b/pkg/chain/callback.go
index 74ea53a..efefc95 100644
--- a/pkg/chain/callback.go
+++ b/pkg/chain/callback.go
@@ -14,6 +14,7 @@
 package chain
 
 import (
+	"fmt"
 	"github.com/ServiceComb/service-center/pkg/util"
 )
 
@@ -23,17 +24,28 @@
 	Args []interface{}
 }
 
-type CallbackFunc func(r Result)
+func (r Result) String() string {
+	if r.OK {
+		return "OK"
+	}
+	return fmt.Sprintf("FAIL(error: %s)", r.Err)
+}
 
 type Callback struct {
-	Func CallbackFunc
+	Func func(r Result)
 }
 
 func (cb *Callback) Invoke(r Result) {
-	go func() {
-		defer util.RecoverAndReport()
-		cb.Func(r)
-	}()
+	go cb.syncInvoke(r)
+}
+
+func (cb *Callback) syncInvoke(r Result) {
+	defer util.RecoverAndReport()
+	if cb.Func == nil {
+		util.Logger().Errorf(nil, "Callback function is nil. result: %s,", r)
+		return
+	}
+	cb.Func(r)
 }
 
 func (cb *Callback) Fail(err error, args ...interface{}) {
@@ -50,9 +62,3 @@
 		Args: args,
 	})
 }
-
-func NewCallback(f CallbackFunc) Callback {
-	return Callback{
-		Func: f,
-	}
-}
diff --git a/pkg/chain/chain.go b/pkg/chain/chain.go
index 1db2de9..e958abf 100644
--- a/pkg/chain/chain.go
+++ b/pkg/chain/chain.go
@@ -14,25 +14,21 @@
 package chain
 
 import (
-	"fmt"
+	errorsEx "github.com/ServiceComb/service-center/pkg/errors"
 	"github.com/ServiceComb/service-center/pkg/util"
-	"sync"
 )
 
-var handlersMap map[string][]Handler
-
 type Chain struct {
 	name         string
 	handlers     []Handler
 	currentIndex int
-	mux          sync.Mutex
 }
 
 func (c *Chain) Init(chainName string, hs []Handler) {
 	c.name = chainName
 	c.currentIndex = -1
 	if len(hs) > 0 {
-		c.handlers = make([]Handler, 0, len(hs))
+		c.handlers = make([]Handler, len(hs))
 		copy(c.handlers, hs)
 	}
 }
@@ -41,17 +37,31 @@
 	return c.name
 }
 
-func (c *Chain) doNext(i *Invocation) {
-	defer util.RecoverAndReport()
+func (c *Chain) syncNext(i *Invocation) {
+	defer func() {
+		itf := recover()
+		if itf == nil {
+			return
+		}
+		util.Logger().Errorf(nil, "recover! %v", itf)
 
-	if c.currentIndex >= len(c.handlers) {
-		i.Fail(fmt.Errorf("Over end of chain '%s'", c.name))
+		i.Fail(errorsEx.RaiseError(itf))
+	}()
+
+	if c.currentIndex >= len(c.handlers)-1 {
+		i.Success()
 		return
 	}
 	c.currentIndex += 1
 	c.handlers[c.currentIndex].Handle(i)
 }
 
-func (c *Chain) next(i *Invocation) {
-	go c.doNext(i)
+func (c *Chain) Next(i *Invocation) {
+	go c.syncNext(i)
+}
+
+func NewChain(name string, handlers ...Handler) *Chain {
+	var ch Chain
+	ch.Init(name, handlers)
+	return &ch
 }
diff --git a/pkg/chain/handler.go b/pkg/chain/handler.go
index f382848..3aba84d 100644
--- a/pkg/chain/handler.go
+++ b/pkg/chain/handler.go
@@ -13,6 +13,21 @@
 //limitations under the License.
 package chain
 
+var handlersMap map[string][]Handler = make(map[string][]Handler, CAP_SIZE)
+
 type Handler interface {
 	Handle(i *Invocation)
 }
+
+func RegisterHandler(catalog string, h Handler) {
+	handlers, ok := handlersMap[catalog]
+	if !ok {
+		handlers = make([]Handler, 0, CAP_SIZE)
+	}
+	handlers = append(handlers, h)
+	handlersMap[catalog] = handlers
+}
+
+func Handlers(catalog string) []Handler {
+	return handlersMap[catalog]
+}
diff --git a/pkg/chain/invocation.go b/pkg/chain/invocation.go
index b244b22..9365b40 100644
--- a/pkg/chain/invocation.go
+++ b/pkg/chain/invocation.go
@@ -17,7 +17,7 @@
 	"errors"
 )
 
-const DEFAULT_MAP_SIZE = 10
+const CAP_SIZE = 10
 
 type Invocation struct {
 	Callback
@@ -27,8 +27,8 @@
 }
 
 func (i *Invocation) Init(ch *Chain) {
-	i.handlerContext = make(map[string]interface{}, DEFAULT_MAP_SIZE)
-	i.context = make(map[string]interface{}, DEFAULT_MAP_SIZE)
+	i.handlerContext = make(map[string]interface{}, CAP_SIZE)
+	i.context = make(map[string]interface{}, CAP_SIZE)
 	i.chain = ch
 }
 
@@ -50,11 +50,21 @@
 	return i
 }
 
-func (i *Invocation) Next(f CallbackFunc) {
-	i.Func = f
+func (i *Invocation) Next() {
 	if i.chain == nil {
 		i.Fail(errors.New("Can not find any chain for this invocation"))
 		return
 	}
-	i.chain.next(i)
+	i.chain.Next(i)
+}
+
+func (i *Invocation) Invoke(f func(r Result)) {
+	i.Func = f
+	i.Next()
+}
+
+func NewInvocation(ch *Chain) *Invocation {
+	var inv Invocation
+	inv.Init(ch)
+	return &inv
 }
diff --git a/pkg/errors/error.go b/pkg/errors/error.go
index 4b93625..1e35f79 100644
--- a/pkg/errors/error.go
+++ b/pkg/errors/error.go
@@ -13,8 +13,22 @@
 //limitations under the License.
 package errors
 
+import (
+	"fmt"
+)
+
 type InternalError string
 
 func (e InternalError) Error() string {
 	return string(e)
 }
+
+func RaiseError(itf interface{}) InternalError {
+	if itf == nil {
+		return InternalError("panic: unknown")
+	}
+	if err, ok := itf.(error); ok {
+		return InternalError(err.Error())
+	}
+	return InternalError(fmt.Sprintf("%v", itf))
+}
diff --git a/pkg/rest/route.go b/pkg/rest/route.go
index 35a3eba..25959c7 100644
--- a/pkg/rest/route.go
+++ b/pkg/rest/route.go
@@ -18,7 +18,6 @@
 	"fmt"
 	"github.com/ServiceComb/service-center/pkg/util"
 	"net/http"
-	"net/url"
 	"strings"
 )
 
@@ -47,13 +46,13 @@
 //   2. redirect not supported
 type ROAServerHandler struct {
 	handlers map[string][]*urlPatternHandler
-	filters []Filter
+	filters  []Filter
 }
 
 func NewROAServerHander() *ROAServerHandler {
 	return &ROAServerHandler{
 		handlers: make(map[string][]*urlPatternHandler),
-		filters: make([]Filter, 0, 5),
+		filters:  make([]Filter, 0, 5),
 	}
 }
 
@@ -76,7 +75,7 @@
 	for _, ph := range this.handlers[r.Method] {
 		if params, ok := ph.try(r.URL.Path); ok {
 			if len(params) > 0 {
-				r.URL.RawQuery = url.Values(params).Encode() + "&" + r.URL.RawQuery
+				r.URL.RawQuery = util.UrlEncode(params) + "&" + r.URL.RawQuery
 			}
 
 			if err = this.doFilter(r); err != nil {
@@ -124,12 +123,13 @@
 	return nil
 }
 
-func (this *urlPatternHandler) try(path string) (p map[string][]string, _ bool) {
+func (this *urlPatternHandler) try(path string) (p map[string]string, _ bool) {
 	var i, j int
-	for i < len(path) {
+	l, sl := len(this.Path), len(path)
+	for i < sl {
 		switch {
-		case j >= len(this.Path):
-			if this.Path != "/" && len(this.Path) > 0 && this.Path[len(this.Path)-1] == '/' {
+		case j >= l:
+			if this.Path != "/" && l > 0 && this.Path[l-1] == '/' {
 				return p, true
 			}
 			return nil, false
@@ -141,9 +141,9 @@
 			val, _, i = match(path, matchParticial, nextc, i)
 
 			if p == nil {
-				p = make(map[string][]string)
+				p = make(map[string]string, 5)
 			}
-			p[this.Path[o:j]] = []string{val}
+			p[this.Path[o:j]] = val
 		case path[i] == this.Path[j]:
 			i++
 			j++
@@ -151,7 +151,7 @@
 			return nil, false
 		}
 	}
-	if j != len(this.Path) {
+	if j != l {
 		return nil, false
 	}
 	return p, true
@@ -188,4 +188,4 @@
 type Filter interface {
 	IsMatch(r *http.Request) bool
 	Do(r *http.Request) error
-}
\ No newline at end of file
+}
diff --git a/pkg/util/util.go b/pkg/util/util.go
index 015cc7c..6267c7e 100644
--- a/pkg/util/util.go
+++ b/pkg/util/util.go
@@ -209,10 +209,11 @@
 	}
 }
 
-func RecoverAndReport() {
-	if r := recover(); r != nil {
+func RecoverAndReport() (r interface{}) {
+	if r = recover(); r != nil {
 		Logger().Errorf(nil, "recover! %v", r)
 	}
+	return
 }
 
 func ParseEndpoint(ep string) (string, error) {
@@ -269,3 +270,15 @@
 	}
 	return
 }
+
+func UrlEncode(keys map[string]string) string {
+	l := len(keys)
+	if l == 0 {
+		return ""
+	}
+	arr := make([]string, 0, l)
+	for k, v := range keys {
+		arr = append(arr, url.QueryEscape(k)+"="+url.QueryEscape(v))
+	}
+	return StringJoin(arr, "&")
+}