Skip to content

Commit

Permalink
Flexible handler and middleware
Browse files Browse the repository at this point in the history
Signed-off-by: Vishal Rana <[email protected]>
  • Loading branch information
vishr committed Mar 25, 2015
1 parent 99124cd commit e49e74a
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 86 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ Bolt is a fast HTTP router (zero memory allocation) + micro web framework in Go.

### Features
- Zippy router.
- Extensible middleware.
- Bring you own handler / middleware.
- Serve static files, including index.
- Extensible middleware which also allows you to use third party handler / middleware.

### Example
https://github.com/labstack/bolt/tree/master/example
Expand Down
135 changes: 84 additions & 51 deletions bolt.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ import (
type (
Bolt struct {
Router *router
handlers []HandlerFunc
middleware []MiddlewareFunc
maxParam byte
notFoundHandler HandlerFunc
methodNotAllowedHandler HandlerFunc
internalServerErrorHandler HandlerFunc
pool sync.Pool
}
HandlerFunc func(*Context)
Handler interface{}
HandlerFunc func(*Context)
Middleware interface{}
MiddlewareFunc func(HandlerFunc) HandlerFunc
)

const (
Expand All @@ -28,34 +31,21 @@ const (
HeaderContentType = "Content-Type"
)

// Methods is a map for looking up HTTP method index.
var Methods = map[string]uint8{
"CONNECT": 0,
"DELETE": 1,
"GET": 2,
"HEAD": 3,
"OPTIONS": 4,
"PATCH": 5,
"POST": 6,
"PUT": 7,
"TRACE": 8,
}

// New creates a bolt instance.
func New() (b *Bolt) {
b = &Bolt{
maxParam: 5,
notFoundHandler: func(c *Context) {
http.Error(c.Response, http.StatusText(http.StatusNotFound), http.StatusNotFound)
c.Halt()
// c.Halt()
},
methodNotAllowedHandler: func(c *Context) {
http.Error(c.Response, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
c.Halt()
// c.Halt()
},
internalServerErrorHandler: func(c *Context) {
http.Error(c.Response, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
c.Halt()
// c.Halt()
},
}
b.Router = NewRouter(b)
Expand All @@ -64,13 +54,24 @@ func New() (b *Bolt) {
Response: &response{},
params: make(Params, b.maxParam),
store: make(store),
i: -1,
bolt: b,
// i: -1,
bolt: b,
}
}
return
}

// NOP
func (h HandlerFunc) ServeHTTP(r http.ResponseWriter, w *http.Request) {
}

func (b *Bolt) Sub(prefix string, m ...MiddlewareFunc) *Bolt {
return &Bolt{
// prefix: b.prefix + prefix,
// middleware: append(b.handlers, handlers...),
}
}

// MaxParam sets the max path params allowed. Default is 5, good enough for
// many users.
func (b *Bolt) MaxParam(n uint8) {
Expand All @@ -92,74 +93,68 @@ func (b *Bolt) InternalServerErrorHandler(h HandlerFunc) {
b.internalServerErrorHandler = h
}

// Chain adds middleware to the chain.
func (b *Bolt) Chain(h ...HandlerFunc) {
b.handlers = append(b.handlers, h...)
}

// Wrap wraps any http.Handler into bolt.HandlerFunc. It facilitates to use
// third party handler / middleware with bolt.
func (b *Bolt) Wrap(h http.Handler) HandlerFunc {
return func(c *Context) {
h.ServeHTTP(c.Response, c.Request)
c.Next()
// Use adds handler to the middleware chain.
func (b *Bolt) Use(m ...Middleware) {
for _, h := range m {
b.middleware = append(b.middleware, wrapM(h))
}
}

// Connect adds a CONNECT route.
func (b *Bolt) Connect(path string, h ...HandlerFunc) {
func (b *Bolt) Connect(path string, h Handler) {
b.Handle("CONNECT", path, h)
}

// Delete adds a DELETE route.
func (b *Bolt) Delete(path string, h ...HandlerFunc) {
func (b *Bolt) Delete(path string, h Handler) {
b.Handle("DELETE", path, h)
}

// Get adds a GET route.
func (b *Bolt) Get(path string, h ...HandlerFunc) {
func (b *Bolt) Get(path string, h Handler) {
b.Handle("GET", path, h)
}

// Head adds a HEAD route.
func (b *Bolt) Head(path string, h ...HandlerFunc) {
func (b *Bolt) Head(path string, h Handler) {
b.Handle("HEAD", path, h)
}

// Options adds an OPTIONS route.
func (b *Bolt) Options(path string, h ...HandlerFunc) {
func (b *Bolt) Options(path string, h Handler) {
b.Handle("OPTIONS", path, h)
}

// Patch adds a PATCH route.
func (b *Bolt) Patch(path string, h ...HandlerFunc) {
func (b *Bolt) Patch(path string, h Handler) {
b.Handle("PATCH", path, h)
}

// Post adds a POST route.
func (b *Bolt) Post(path string, h ...HandlerFunc) {
func (b *Bolt) Post(path string, h Handler) {
b.Handle("POST", path, h)
}

// Put adds a PUT route.
func (b *Bolt) Put(path string, h ...HandlerFunc) {
func (b *Bolt) Put(path string, h Handler) {
b.Handle("PUT", path, h)
}

// Trace adds a TRACE route.
func (b *Bolt) Trace(path string, h ...HandlerFunc) {
func (b *Bolt) Trace(path string, h Handler) {
b.Handle("TRACE", path, h)
}

// Handle adds method, path handler to the router.
func (b *Bolt) Handle(method, path string, h []HandlerFunc) {
h = append(b.handlers, h...)
l := len(h)
b.Router.Add(method, path, func(c *Context) {
c.handlers = h
c.l = l
c.Next()
})
func (b *Bolt) Handle(method, path string, h Handler) {
b.Router.Add(method, path, b.wrapH(h))
// hs := append(b.middleware, wrap(h, false))
// l := len(hs)
// b.Router.Add(method, path, func(c *Context) {
// c.handlers = hs
// c.l = l
// c.Next()
// })
}

// Static serves static files.
Expand All @@ -183,10 +178,14 @@ func (b *Bolt) Index(file string) {
}

func (b *Bolt) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
// Find and execute handler
h, c, s := b.Router.Find(r.Method, r.URL.Path)
c.reset(rw, r)
if h != nil {
// Middleware
for i := len(b.middleware) - 1; i >= 0; i-- {
h = b.middleware[i](h)
}
// Handler
h(c)
} else {
if s == NotFound {
Expand All @@ -202,6 +201,40 @@ func (b *Bolt) Run(addr string) {
log.Fatal(http.ListenAndServe(addr, b))
}

func (b *Bolt) Stop(addr string) {
panic("implement it")
// wraps Handler
func (b *Bolt) wrapH(h Handler) HandlerFunc {
switch h := h.(type) {
case func(*Context):
return HandlerFunc(h)
case http.HandlerFunc:
return func(c *Context) {
h.ServeHTTP(c.Response, c.Request)
}
default:
panic("bolt: unknown handler")
}
}

// wraps Middleware
func wrapM(m Middleware) MiddlewareFunc {
switch m := m.(type) {
case func(HandlerFunc) HandlerFunc:
return MiddlewareFunc(m)
case func(http.ResponseWriter, *http.Request):
return func(h HandlerFunc) HandlerFunc {
return func(c *Context) {
m(c.Response, c.Request)
h(c)
}
}
case func(http.Handler) http.Handler:
return func(h HandlerFunc) HandlerFunc {
return func(c *Context) {
m(h).ServeHTTP(c.Response, c.Request)
h(c)
}
}
default:
panic("bolt: unknown middleware")
}
}
26 changes: 13 additions & 13 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ type (
params Params
handlers []HandlerFunc
store map[string]interface{}
l int // Handlers' length
i int // Current handler index
bolt *Bolt
// l int // Handlers' length
// i int // Current handler index
bolt *Bolt
}
store map[string]interface{}
)
Expand Down Expand Up @@ -64,12 +64,12 @@ func (c *Context) JSON(n int, i interface{}) {
// }

// Next executes the next handler in the chain.
func (c *Context) Next() {
c.i++
if c.i < c.l {
c.handlers[c.i](c)
}
}
// func (c *Context) Next() {
// c.i++
// if c.i < c.l {
// c.handlers[c.i](c)
// }
// }

// Get retrieves data from the context.
func (c *Context) Get(key string) interface{} {
Expand All @@ -89,10 +89,10 @@ func (c *Context) Redirect(n int, url string) {
func (c *Context) reset(rw http.ResponseWriter, r *http.Request) {
c.Response.reset(rw)
c.Request = r
c.i = -1
// c.i = -1
}

// Halt halts the current request.
func (c *Context) Halt() {
c.i = c.l
}
// func (c *Context) Halt() {
// c.i = c.l
// }
4 changes: 3 additions & 1 deletion example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (

"github.com/labstack/bolt"
mw "github.com/labstack/bolt/middleware"
"github.com/rs/cors"
"github.com/thoas/stats"
)

type user struct {
Expand Down Expand Up @@ -41,7 +43,7 @@ func getUser(c *bolt.Context) {

func main() {
b := bolt.New()
b.Chain(mw.Logger())
b.Use(mw.Logger)
b.Index("public/index.html")
b.Static("/js", "public/js")
b.Post("/users", createUser)
Expand Down
8 changes: 4 additions & 4 deletions middleware/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import (
)

type (
BasicAuthFunc func(usr, pwd string) bool
BasicAuthFunc func(string, string) bool
AuthorizedHandler bolt.HandlerFunc
UnauthorizedHandler func(c *bolt.Context, err error)
JwtKeyFunc func(kid string) ([]byte, error)
UnauthorizedHandler func(*bolt.Context, error)
JwtKeyFunc func(string) ([]byte, error)
Claims map[string]interface{}
)

Expand Down Expand Up @@ -57,7 +57,7 @@ func JwtAuth(ah AuthorizedHandler, uah UnauthorizedHandler, fn JwtKeyFunc) bolt.
if t.Valid {
c.Set("claims", Claims(t.Claims))
ah(c)
c.Next()
// c.Next()
} else {
// TODO: capture errors
uah(c, err)
Expand Down
8 changes: 4 additions & 4 deletions middleware/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import (
"github.com/labstack/gommon/color"
)

func Logger() bolt.HandlerFunc {
return func(c *bolt.Context) {
func Logger(h bolt.HandlerFunc) bolt.HandlerFunc {
return bolt.HandlerFunc(func(c *bolt.Context) {
start := time.Now()
c.Next()
h(c)
end := time.Now()
col := color.Green
m := c.Request.Method
Expand All @@ -28,5 +28,5 @@ func Logger() bolt.HandlerFunc {
}

log.Printf("%s %s %s %s", m, p, col(s), end.Sub(start))
}
})
}
8 changes: 5 additions & 3 deletions response.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@ type (
}
)

func (r *response) WriteHeader(c int) {

func (r *response) WriteHeader(n int) {
// TODO: fix when halted.
if r.committed {
// TODO: Warning
log.Println("bolt: response already committed")
return
}
r.status = c
r.ResponseWriter.WriteHeader(c)
r.status = n
r.ResponseWriter.WriteHeader(n)
r.committed = true
}

Expand Down
Loading

0 comments on commit e49e74a

Please sign in to comment.