Skip to content
Find file
5a7db2d
409 lines (334 sloc) 11.3 KB
package lion
import (
"fmt"
"net/http"
"os"
"sync"
"golang.org/x/net/context"
)
// Router is responsible for registering handlers and middlewares
type Router struct {
tree *tree
rm RegisterMatcher
router *Router
middlewares Middlewares
handler Handler // TODO: create a handler
pattern string
notFoundHandler Handler
registeredHandlers []registeredHandler // Used for Mount()
pool sync.Pool
namedMiddlewares map[string]Middlewares
}
// New creates a new router instance
func New(mws ...Middleware) *Router {
r := &Router{
middlewares: Middlewares{},
rm: newRadixMatcher(),
namedMiddlewares: make(map[string]Middlewares),
}
r.pool.New = func() interface{} {
return NewContext()
}
r.router = r
r.Use(mws...)
return r
}
// Group creates a subrouter with parent pattern provided.
func (r *Router) Group(pattern string, mws ...Middleware) *Router {
p := r.pattern + pattern
if pattern == "/" && r.pattern != "/" {
p = r.pattern
}
validatePattern(p)
nr := &Router{
router: r,
rm: r.rm,
pattern: p,
middlewares: Middlewares{},
namedMiddlewares: make(map[string]Middlewares),
}
nr.Use(mws...)
return nr
}
// Get registers an http GET method receiver with the provided Handler
func (r *Router) Get(pattern string, handler Handler) {
r.Handle("GET", pattern, handler)
}
// Head registers an http HEAD method receiver with the provided Handler
func (r *Router) Head(pattern string, handler Handler) {
r.Handle("HEAD", pattern, handler)
}
// Post registers an http POST method receiver with the provided Handler
func (r *Router) Post(pattern string, handler Handler) {
r.Handle("POST", pattern, handler)
}
// Put registers an http PUT method receiver with the provided Handler
func (r *Router) Put(pattern string, handler Handler) {
r.Handle("PUT", pattern, handler)
}
// Delete registers an http DELETE method receiver with the provided Handler
func (r *Router) Delete(pattern string, handler Handler) {
r.Handle("DELETE", pattern, handler)
}
// Trace registers an http TRACE method receiver with the provided Handler
func (r *Router) Trace(pattern string, handler Handler) {
r.Handle("TRACE", pattern, handler)
}
// Options registers an http OPTIONS method receiver with the provided Handler
func (r *Router) Options(pattern string, handler Handler) {
r.Handle("OPTIONS", pattern, handler)
}
// Connect registers an http CONNECT method receiver with the provided Handler
func (r *Router) Connect(pattern string, handler Handler) {
r.Handle("CONNECT", pattern, handler)
}
// Patch registers an http PATCH method receiver with the provided Handler
func (r *Router) Patch(pattern string, handler Handler) {
r.Handle("PATCH", pattern, handler)
}
// GetFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) GetFunc(pattern string, fn HandlerFunc) {
r.Get(pattern, HandlerFunc(fn))
}
// HeadFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) HeadFunc(pattern string, fn HandlerFunc) {
r.Head(pattern, HandlerFunc(fn))
}
// PostFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) PostFunc(pattern string, fn HandlerFunc) {
r.Post(pattern, HandlerFunc(fn))
}
// PutFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) PutFunc(pattern string, fn HandlerFunc) {
r.Put(pattern, HandlerFunc(fn))
}
// DeleteFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) DeleteFunc(pattern string, fn HandlerFunc) {
r.Delete(pattern, HandlerFunc(fn))
}
// TraceFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) TraceFunc(pattern string, fn HandlerFunc) {
r.Trace(pattern, HandlerFunc(fn))
}
// OptionsFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) OptionsFunc(pattern string, fn HandlerFunc) {
r.Options(pattern, HandlerFunc(fn))
}
// ConnectFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) ConnectFunc(pattern string, fn HandlerFunc) {
r.Connect(pattern, HandlerFunc(fn))
}
// PatchFunc wraps a HandlerFunc as a Handler and registers it to the router
func (r *Router) PatchFunc(pattern string, fn HandlerFunc) {
r.Patch(pattern, HandlerFunc(fn))
}
// Use registers middlewares to be used
func (r *Router) Use(middlewares ...Middleware) {
r.middlewares = append(r.middlewares, middlewares...)
}
// UseFunc wraps a MiddlewareFunc as a Middleware and registers it middlewares to be used
func (r *Router) UseFunc(middlewareFuncs ...MiddlewareFunc) {
for _, fn := range middlewareFuncs {
r.Use(MiddlewareFunc(fn))
}
}
type negroniHandler interface {
ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}
type negroniHandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
func (h negroniHandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
h(rw, r, next)
}
func (r *Router) UseNegroni(n negroniHandler) {
r.Use(MiddlewareFunc(func(next Handler) Handler {
return HandlerFunc(func(c context.Context, w http.ResponseWriter, r *http.Request) {
n.ServeHTTP(w, r, HandlerFunc(func(_ context.Context, w http.ResponseWriter, r *http.Request) {
next.ServeHTTPC(c, w, r)
}).ServeHTTP)
})
}))
}
func (r *Router) UseNegroniFunc(n func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)) {
r.UseNegroni(negroniHandlerFunc(n))
}
// UseHandler uses
func (r *Router) UseHandler(handler Handler) {
r.UseFunc(func(next Handler) Handler {
return HandlerFunc(func(c context.Context, w http.ResponseWriter, r *http.Request) {
handler.ServeHTTPC(c, w, r)
next.ServeHTTPC(c, w, r)
})
})
}
// UseHandlerFunc uses
func (r *Router) UseHandlerFunc(fn HandlerFunc) {
r.UseHandler(HandlerFunc(fn))
}
// Handle is the underling method responsible for registering a handler for a specific method and pattern.
func (r *Router) Handle(method, pattern string, handler Handler) {
validatePattern(pattern)
var p string
if !r.isRoot() && pattern == "/" {
p = r.pattern
} else {
p = r.pattern + pattern
}
built := r.buildMiddlewares(handler)
r.registeredHandlers = append(r.registeredHandlers, registeredHandler{method, pattern, built})
r.router.rm.Register(method, p, built)
}
type registeredHandler struct {
method, pattern string
handler Handler
}
// Mount mounts a subrouter at the provided pattern
func (r *Router) Mount(pattern string, router *Router, mws ...Middleware) {
sub := r.Group(pattern, mws...)
for _, rh := range router.registeredHandlers {
sub.Handle(rh.method, rh.pattern, rh.handler)
}
}
func (r *Router) buildMiddlewares(handler Handler) Handler {
handler = r.middlewares.BuildHandler(handler)
if !r.isRoot() {
handler = r.router.buildMiddlewares(handler)
}
return handler
}
func (r *Router) isRoot() bool {
return r.router == r
}
// HandleFunc wraps a HandlerFunc and pass it to Handle method
func (r *Router) HandleFunc(method, pattern string, fn HandlerFunc) {
r.Handle(method, pattern, HandlerFunc(fn))
}
// ServeHTTP calls ServeHTTPC with a context.Background()
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
r.ServeHTTPC(context.TODO(), w, req)
}
// ServeHTTPC finds the handler associated with the request's path.
// If it is not found it calls the NotFound handler
func (r *Router) ServeHTTPC(c context.Context, w http.ResponseWriter, req *http.Request) {
ctx := r.pool.Get().(*Context)
ctx.parent = c
if ctx, h := r.router.rm.Match(ctx, req); h != nil {
h.ServeHTTPC(ctx, w, req)
} else {
r.notFound(ctx, w, req) // r.middlewares.BuildHandler(HandlerFunc(r.NotFound)).ServeHTTPC
}
ctx.reset()
r.pool.Put(ctx)
}
// NotFound calls NotFoundHandler() if it is set. Otherwise, it calls net/http.NotFound
func (r *Router) notFound(c context.Context, w http.ResponseWriter, req *http.Request) {
if r.router.notFoundHandler != nil {
r.router.notFoundHandler.ServeHTTPC(c, w, req)
} else {
http.NotFound(w, req)
}
}
func (r *Router) NotFoundHandler(handler Handler) {
r.notFoundHandler = handler
}
// ServeFiles serves files located in root http.FileSystem
//
// This can be used as shown below:
// r := New()
// r.ServeFiles("/static", http.Dir("static")) // This will serve files in the directory static with /static prefix
func (r *Router) ServeFiles(path string, root http.FileSystem) {
fileServer := http.FileServer(root)
r.Get(path, HandlerFunc(func(c context.Context, w http.ResponseWriter, r *http.Request) {
fmt.Println("rurl", r.URL.Path)
fileServer.ServeHTTP(w, r)
}))
}
// GetH wraps a http.Handler
func (r *Router) GetH(pattern string, handler http.Handler) {
r.Get(pattern, Wrap(handler))
}
// HeadH wraps a http.Handler
func (r *Router) HeadH(pattern string, handler http.Handler) {
r.Head(pattern, Wrap(handler))
}
// PostH wraps a http.Handler
func (r *Router) PostH(pattern string, handler http.Handler) {
r.Post(pattern, Wrap(handler))
}
// PutH wraps a http.Handler
func (r *Router) PutH(pattern string, handler http.Handler) {
r.Put(pattern, Wrap(handler))
}
// DeleteH wraps a http.Handler
func (r *Router) DeleteH(pattern string, handler http.Handler) {
r.Delete(pattern, Wrap(handler))
}
// TraceH wraps a http.Handler
func (r *Router) TraceH(pattern string, handler http.Handler) {
r.Trace(pattern, Wrap(handler))
}
// OptionsH wraps a http.Handler
func (r *Router) OptionsH(pattern string, handler http.Handler) {
r.Options(pattern, Wrap(handler))
}
// ConnectH wraps a http.Handler
func (r *Router) ConnectH(pattern string, handler http.Handler) {
r.Connect(pattern, Wrap(handler))
}
// PatchH wraps a http.Handler
func (r *Router) PatchH(pattern string, handler http.Handler) {
r.Patch(pattern, Wrap(handler))
}
// Run calls http.ListenAndServe for the current router.
// If no addresses are specified as arguments, it will use the PORT environnement variable if it is defined. Otherwise, it will listen on port 3000 of the localmachine
//
// r := New()
// r.Run() // will call
// r.Run(":8080")
func (r *Router) Run(addr ...string) {
var a string
if len(addr) == 0 {
if p := os.Getenv("PORT"); p != "" {
a = p
} else {
a = ":3000"
}
} else {
a = addr[0]
}
lionLogger.Printf("listening on %s", a)
lionLogger.Fatal(http.ListenAndServe(a, r))
}
// RunTLS calls http.ListenAndServeTLS for the current router
//
// r := New()
// r.RunTLS(":3443", "cert.pem", "key.pem")
func (r *Router) RunTLS(addr, certFile, keyFile string) {
lionLogger.Printf("listening on %s", addr)
lionLogger.Fatal(http.ListenAndServeTLS(addr, certFile, keyFile, r))
}
func (r *Router) Define(name string, mws ...Middleware) {
r.namedMiddlewares[name] = append(r.namedMiddlewares[name], mws...)
}
func (r *Router) DefineFunc(name string, mws ...MiddlewareFunc) {
for _, mw := range mws {
r.Define(name, mw)
}
}
func (r *Router) UseNamed(name string) {
if r.hasNamed(name) { // Find if it this is registered in the current router
r.Use(r.namedMiddlewares[name]...)
} else if !r.isRoot() { // Otherwise, look for it in parent router.
r.router.UseNamed(name)
} else { // not found
panic("Unknow named middlewares: " + name)
}
}
func (r *Router) hasNamed(name string) bool {
_, exist := r.namedMiddlewares[name]
return exist
}
func validatePattern(pattern string) {
if len(pattern) > 0 && pattern[0] != '/' {
panic("path must start with '/' in path '" + pattern + "'")
}
}
Something went wrong with that request. Please try again.