Permalink
| 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 + "'") | |
| } | |
| } |