aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com')
-rw-r--r--vendor/github.com/alexedwards/stack/LICENSE20
-rw-r--r--vendor/github.com/alexedwards/stack/context.go55
-rw-r--r--vendor/github.com/alexedwards/stack/stack.go94
-rw-r--r--vendor/github.com/codegangsta/inject/LICENSE20
-rw-r--r--vendor/github.com/codegangsta/inject/inject.go187
-rw-r--r--vendor/github.com/codegangsta/negroni/LICENSE21
-rw-r--r--vendor/github.com/codegangsta/negroni/doc.go25
-rw-r--r--vendor/github.com/codegangsta/negroni/logger.go35
-rw-r--r--vendor/github.com/codegangsta/negroni/negroni.go133
-rw-r--r--vendor/github.com/codegangsta/negroni/recovery.go65
-rw-r--r--vendor/github.com/codegangsta/negroni/response_writer.go113
-rw-r--r--vendor/github.com/codegangsta/negroni/static.go88
-rw-r--r--vendor/github.com/go-martini/martini/LICENSE20
-rw-r--r--vendor/github.com/go-martini/martini/env.go31
-rw-r--r--vendor/github.com/go-martini/martini/go_version.go7
-rw-r--r--vendor/github.com/go-martini/martini/logger.go29
-rw-r--r--vendor/github.com/go-martini/martini/martini.go189
-rw-r--r--vendor/github.com/go-martini/martini/recovery.go144
-rw-r--r--vendor/github.com/go-martini/martini/response_writer.go107
-rw-r--r--vendor/github.com/go-martini/martini/return_handler.go43
-rw-r--r--vendor/github.com/go-martini/martini/router.go425
-rw-r--r--vendor/github.com/go-martini/martini/static.go135
-rw-r--r--vendor/github.com/julienschmidt/httprouter/LICENSE24
-rw-r--r--vendor/github.com/julienschmidt/httprouter/path.go123
-rw-r--r--vendor/github.com/julienschmidt/httprouter/router.go411
-rw-r--r--vendor/github.com/julienschmidt/httprouter/tree.go651
-rw-r--r--vendor/github.com/justinas/alice/LICENSE20
-rw-r--r--vendor/github.com/justinas/alice/chain.go112
-rw-r--r--vendor/github.com/martini-contrib/render/LICENSE20
-rw-r--r--vendor/github.com/martini-contrib/render/render.go386
-rw-r--r--vendor/github.com/oxtoacart/bpool/LICENSE202
-rw-r--r--vendor/github.com/oxtoacart/bpool/bpool.go6
-rw-r--r--vendor/github.com/oxtoacart/bpool/bufferpool.go40
-rw-r--r--vendor/github.com/oxtoacart/bpool/bytepool.go45
-rw-r--r--vendor/github.com/oxtoacart/bpool/sizedbufferpool.go60
-rw-r--r--vendor/github.com/rs/cors/LICENSE19
-rw-r--r--vendor/github.com/rs/cors/cors.go412
-rw-r--r--vendor/github.com/rs/cors/examples/alice/server.go24
-rw-r--r--vendor/github.com/rs/cors/examples/default/server.go19
-rw-r--r--vendor/github.com/rs/cors/examples/goji/server.go22
-rw-r--r--vendor/github.com/rs/cors/examples/martini/server.go23
-rw-r--r--vendor/github.com/rs/cors/examples/negroni/server.go26
-rw-r--r--vendor/github.com/rs/cors/examples/nethttp/server.go20
-rw-r--r--vendor/github.com/rs/cors/examples/openbar/server.go22
-rw-r--r--vendor/github.com/rs/cors/examples/xhandler/server.go24
-rw-r--r--vendor/github.com/rs/cors/utils.go70
-rw-r--r--vendor/github.com/rs/xhandler/LICENSE19
-rw-r--r--vendor/github.com/rs/xhandler/chain.go121
-rw-r--r--vendor/github.com/rs/xhandler/middleware.go59
-rw-r--r--vendor/github.com/rs/xhandler/xhandler.go42
-rw-r--r--vendor/github.com/stretchr/graceful/LICENSE21
-rw-r--r--vendor/github.com/stretchr/graceful/graceful.go484
-rw-r--r--vendor/github.com/stretchr/graceful/keepalive_listener.go32
-rw-r--r--vendor/github.com/stretchr/graceful/limit_listen.go77
-rw-r--r--vendor/github.com/stretchr/graceful/signal.go17
-rw-r--r--vendor/github.com/stretchr/graceful/signal_appengine.go13
-rw-r--r--vendor/github.com/stretchr/graceful/tests/main.go40
-rw-r--r--vendor/github.com/urfave/negroni/LICENSE21
-rw-r--r--vendor/github.com/urfave/negroni/doc.go25
-rw-r--r--vendor/github.com/urfave/negroni/logger.go35
-rw-r--r--vendor/github.com/urfave/negroni/negroni.go133
-rw-r--r--vendor/github.com/urfave/negroni/recovery.go65
-rw-r--r--vendor/github.com/urfave/negroni/response_writer.go113
-rw-r--r--vendor/github.com/urfave/negroni/static.go88
-rw-r--r--vendor/github.com/zenazn/goji/LICENSE20
-rw-r--r--vendor/github.com/zenazn/goji/bind/bind.go155
-rw-r--r--vendor/github.com/zenazn/goji/bind/einhorn.go91
-rw-r--r--vendor/github.com/zenazn/goji/bind/einhorn_stub.go12
-rw-r--r--vendor/github.com/zenazn/goji/bind/systemd.go36
-rw-r--r--vendor/github.com/zenazn/goji/bind/systemd_stub.go6
-rw-r--r--vendor/github.com/zenazn/goji/default.go102
-rw-r--r--vendor/github.com/zenazn/goji/example/main.go177
-rw-r--r--vendor/github.com/zenazn/goji/example/middleware.go47
-rw-r--r--vendor/github.com/zenazn/goji/example/models.go49
-rw-r--r--vendor/github.com/zenazn/goji/goji.go36
-rw-r--r--vendor/github.com/zenazn/goji/graceful/clone.go11
-rw-r--r--vendor/github.com/zenazn/goji/graceful/clone16.go34
-rw-r--r--vendor/github.com/zenazn/goji/graceful/einhorn.go21
-rw-r--r--vendor/github.com/zenazn/goji/graceful/graceful.go62
-rw-r--r--vendor/github.com/zenazn/goji/graceful/listener/conn.go151
-rw-r--r--vendor/github.com/zenazn/goji/graceful/listener/listener.go178
-rw-r--r--vendor/github.com/zenazn/goji/graceful/listener/shard.go98
-rw-r--r--vendor/github.com/zenazn/goji/graceful/middleware.go103
-rw-r--r--vendor/github.com/zenazn/goji/graceful/serve.go33
-rw-r--r--vendor/github.com/zenazn/goji/graceful/serve13.go76
-rw-r--r--vendor/github.com/zenazn/goji/graceful/server.go108
-rw-r--r--vendor/github.com/zenazn/goji/graceful/signal.go197
-rw-r--r--vendor/github.com/zenazn/goji/serve.go64
-rw-r--r--vendor/github.com/zenazn/goji/serve_appengine.go23
-rw-r--r--vendor/github.com/zenazn/goji/web/atomic.go18
-rw-r--r--vendor/github.com/zenazn/goji/web/atomic_appengine.go14
-rw-r--r--vendor/github.com/zenazn/goji/web/bytecode_compiler.go265
-rw-r--r--vendor/github.com/zenazn/goji/web/bytecode_runner.go83
-rw-r--r--vendor/github.com/zenazn/goji/web/chanpool.go31
-rw-r--r--vendor/github.com/zenazn/goji/web/cpool.go23
-rw-r--r--vendor/github.com/zenazn/goji/web/func_equal.go32
-rw-r--r--vendor/github.com/zenazn/goji/web/handler.go42
-rw-r--r--vendor/github.com/zenazn/goji/web/match.go66
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware.go154
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/envinit.go27
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/logger.go92
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/middleware.go4
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/nocache.go55
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/options.go97
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/realip.go51
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/recoverer.go44
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/request_id.go88
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/subrouter.go65
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/terminal.go60
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/urlquery.go24
-rw-r--r--vendor/github.com/zenazn/goji/web/mutil/mutil.go3
-rw-r--r--vendor/github.com/zenazn/goji/web/mutil/writer_proxy.go139
-rw-r--r--vendor/github.com/zenazn/goji/web/mux.go213
-rw-r--r--vendor/github.com/zenazn/goji/web/pattern.go58
-rw-r--r--vendor/github.com/zenazn/goji/web/regexp_pattern.go149
-rw-r--r--vendor/github.com/zenazn/goji/web/router.go154
-rw-r--r--vendor/github.com/zenazn/goji/web/string_pattern.go137
-rw-r--r--vendor/github.com/zenazn/goji/web/web.go112
118 files changed, 10362 insertions, 0 deletions
diff --git a/vendor/github.com/alexedwards/stack/LICENSE b/vendor/github.com/alexedwards/stack/LICENSE
new file mode 100644
index 0000000..f25a33b
--- /dev/null
+++ b/vendor/github.com/alexedwards/stack/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Alex Edwards
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/alexedwards/stack/context.go b/vendor/github.com/alexedwards/stack/context.go
new file mode 100644
index 0000000..07afe21
--- /dev/null
+++ b/vendor/github.com/alexedwards/stack/context.go
@@ -0,0 +1,55 @@
+package stack
+
+import (
+ "sync"
+)
+
+type Context struct {
+ mu sync.RWMutex
+ m map[string]interface{}
+}
+
+func NewContext() *Context {
+ m := make(map[string]interface{})
+ return &Context{m: m}
+}
+
+func (c *Context) Get(key string) interface{} {
+ if !c.Exists(key) {
+ return nil
+ }
+ c.mu.RLock()
+ defer c.mu.RUnlock()
+ return c.m[key]
+}
+
+func (c *Context) Put(key string, val interface{}) *Context {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ c.m[key] = val
+ return c
+}
+
+func (c *Context) Delete(key string) *Context {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ delete(c.m, key)
+ return c
+}
+
+func (c *Context) Exists(key string) bool {
+ c.mu.RLock()
+ defer c.mu.RUnlock()
+ _, ok := c.m[key]
+ return ok
+}
+
+func (c *Context) copy() *Context {
+ nc := NewContext()
+ c.mu.RLock()
+ defer c.mu.RUnlock()
+ for k, v := range c.m {
+ nc.m[k] = v
+ }
+ return nc
+}
diff --git a/vendor/github.com/alexedwards/stack/stack.go b/vendor/github.com/alexedwards/stack/stack.go
new file mode 100644
index 0000000..47c42b2
--- /dev/null
+++ b/vendor/github.com/alexedwards/stack/stack.go
@@ -0,0 +1,94 @@
+package stack
+
+import "net/http"
+
+type chainHandler func(*Context) http.Handler
+type chainMiddleware func(*Context, http.Handler) http.Handler
+
+type Chain struct {
+ mws []chainMiddleware
+ h chainHandler
+}
+
+func New(mws ...chainMiddleware) Chain {
+ return Chain{mws: mws}
+}
+
+func (c Chain) Append(mws ...chainMiddleware) Chain {
+ newMws := make([]chainMiddleware, len(c.mws)+len(mws))
+ copy(newMws[:len(c.mws)], c.mws)
+ copy(newMws[len(c.mws):], mws)
+ c.mws = newMws
+ return c
+}
+
+func (c Chain) Then(chf func(ctx *Context, w http.ResponseWriter, r *http.Request)) HandlerChain {
+ c.h = adaptContextHandlerFunc(chf)
+ return newHandlerChain(c)
+}
+
+func (c Chain) ThenHandler(h http.Handler) HandlerChain {
+ c.h = adaptHandler(h)
+ return newHandlerChain(c)
+}
+
+func (c Chain) ThenHandlerFunc(fn func(http.ResponseWriter, *http.Request)) HandlerChain {
+ c.h = adaptHandlerFunc(fn)
+ return newHandlerChain(c)
+}
+
+type HandlerChain struct {
+ context *Context
+ Chain
+}
+
+func newHandlerChain(c Chain) HandlerChain {
+ return HandlerChain{context: NewContext(), Chain: c}
+}
+
+func (hc HandlerChain) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ // Always take a copy of context (i.e. pointing to a brand new memory location)
+ ctx := hc.context.copy()
+
+ final := hc.h(ctx)
+ for i := len(hc.mws) - 1; i >= 0; i-- {
+ final = hc.mws[i](ctx, final)
+ }
+ final.ServeHTTP(w, r)
+}
+
+func Inject(hc HandlerChain, key string, val interface{}) HandlerChain {
+ hc.context = hc.context.copy().Put(key, val)
+ return hc
+}
+
+// Adapt third party middleware with the signature
+// func(http.Handler) http.Handler into chainMiddleware
+func Adapt(fn func(http.Handler) http.Handler) chainMiddleware {
+ return func(ctx *Context, h http.Handler) http.Handler {
+ return fn(h)
+ }
+}
+
+// Adapt http.Handler into a chainHandler
+func adaptHandler(h http.Handler) chainHandler {
+ return func(ctx *Context) http.Handler {
+ return h
+ }
+}
+
+// Adapt a function with the signature
+// func(http.ResponseWriter, *http.Request) into a chainHandler
+func adaptHandlerFunc(fn func(w http.ResponseWriter, r *http.Request)) chainHandler {
+ return adaptHandler(http.HandlerFunc(fn))
+}
+
+// Adapt a function with the signature
+// func(Context, http.ResponseWriter, *http.Request) into a chainHandler
+func adaptContextHandlerFunc(fn func(ctx *Context, w http.ResponseWriter, r *http.Request)) chainHandler {
+ return func(ctx *Context) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ fn(ctx, w, r)
+ })
+ }
+}
diff --git a/vendor/github.com/codegangsta/inject/LICENSE b/vendor/github.com/codegangsta/inject/LICENSE
new file mode 100644
index 0000000..eb68a0e
--- /dev/null
+++ b/vendor/github.com/codegangsta/inject/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 Jeremy Saenz
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/codegangsta/inject/inject.go b/vendor/github.com/codegangsta/inject/inject.go
new file mode 100644
index 0000000..3ff713c
--- /dev/null
+++ b/vendor/github.com/codegangsta/inject/inject.go
@@ -0,0 +1,187 @@
+// Package inject provides utilities for mapping and injecting dependencies in various ways.
+package inject
+
+import (
+ "fmt"
+ "reflect"
+)
+
+// Injector represents an interface for mapping and injecting dependencies into structs
+// and function arguments.
+type Injector interface {
+ Applicator
+ Invoker
+ TypeMapper
+ // SetParent sets the parent of the injector. If the injector cannot find a
+ // dependency in its Type map it will check its parent before returning an
+ // error.
+ SetParent(Injector)
+}
+
+// Applicator represents an interface for mapping dependencies to a struct.
+type Applicator interface {
+ // Maps dependencies in the Type map to each field in the struct
+ // that is tagged with 'inject'. Returns an error if the injection
+ // fails.
+ Apply(interface{}) error
+}
+
+// Invoker represents an interface for calling functions via reflection.
+type Invoker interface {
+ // Invoke attempts to call the interface{} provided as a function,
+ // providing dependencies for function arguments based on Type. Returns
+ // a slice of reflect.Value representing the returned values of the function.
+ // Returns an error if the injection fails.
+ Invoke(interface{}) ([]reflect.Value, error)
+}
+
+// TypeMapper represents an interface for mapping interface{} values based on type.
+type TypeMapper interface {
+ // Maps the interface{} value based on its immediate type from reflect.TypeOf.
+ Map(interface{}) TypeMapper
+ // Maps the interface{} value based on the pointer of an Interface provided.
+ // This is really only useful for mapping a value as an interface, as interfaces
+ // cannot at this time be referenced directly without a pointer.
+ MapTo(interface{}, interface{}) TypeMapper
+ // Provides a possibility to directly insert a mapping based on type and value.
+ // This makes it possible to directly map type arguments not possible to instantiate
+ // with reflect like unidirectional channels.
+ Set(reflect.Type, reflect.Value) TypeMapper
+ // Returns the Value that is mapped to the current type. Returns a zeroed Value if
+ // the Type has not been mapped.
+ Get(reflect.Type) reflect.Value
+}
+
+type injector struct {
+ values map[reflect.Type]reflect.Value
+ parent Injector
+}
+
+// InterfaceOf dereferences a pointer to an Interface type.
+// It panics if value is not an pointer to an interface.
+func InterfaceOf(value interface{}) reflect.Type {
+ t := reflect.TypeOf(value)
+
+ for t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+
+ if t.Kind() != reflect.Interface {
+ panic("Called inject.InterfaceOf with a value that is not a pointer to an interface. (*MyInterface)(nil)")
+ }
+
+ return t
+}
+
+// New returns a new Injector.
+func New() Injector {
+ return &injector{
+ values: make(map[reflect.Type]reflect.Value),
+ }
+}
+
+// Invoke attempts to call the interface{} provided as a function,
+// providing dependencies for function arguments based on Type.
+// Returns a slice of reflect.Value representing the returned values of the function.
+// Returns an error if the injection fails.
+// It panics if f is not a function
+func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) {
+ t := reflect.TypeOf(f)
+
+ var in = make([]reflect.Value, t.NumIn()) //Panic if t is not kind of Func
+ for i := 0; i < t.NumIn(); i++ {
+ argType := t.In(i)
+ val := inj.Get(argType)
+ if !val.IsValid() {
+ return nil, fmt.Errorf("Value not found for type %v", argType)
+ }
+
+ in[i] = val
+ }
+
+ return reflect.ValueOf(f).Call(in), nil
+}
+
+// Maps dependencies in the Type map to each field in the struct
+// that is tagged with 'inject'.
+// Returns an error if the injection fails.
+func (inj *injector) Apply(val interface{}) error {
+ v := reflect.ValueOf(val)
+
+ for v.Kind() == reflect.Ptr {
+ v = v.Elem()
+ }
+
+ if v.Kind() != reflect.Struct {
+ return nil // Should not panic here ?
+ }
+
+ t := v.Type()
+
+ for i := 0; i < v.NumField(); i++ {
+ f := v.Field(i)
+ structField := t.Field(i)
+ if f.CanSet() && (structField.Tag == "inject" || structField.Tag.Get("inject") != "") {
+ ft := f.Type()
+ v := inj.Get(ft)
+ if !v.IsValid() {
+ return fmt.Errorf("Value not found for type %v", ft)
+ }
+
+ f.Set(v)
+ }
+
+ }
+
+ return nil
+}
+
+// Maps the concrete value of val to its dynamic type using reflect.TypeOf,
+// It returns the TypeMapper registered in.
+func (i *injector) Map(val interface{}) TypeMapper {
+ i.values[reflect.TypeOf(val)] = reflect.ValueOf(val)
+ return i
+}
+
+func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper {
+ i.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val)
+ return i
+}
+
+// Maps the given reflect.Type to the given reflect.Value and returns
+// the Typemapper the mapping has been registered in.
+func (i *injector) Set(typ reflect.Type, val reflect.Value) TypeMapper {
+ i.values[typ] = val
+ return i
+}
+
+func (i *injector) Get(t reflect.Type) reflect.Value {
+ val := i.values[t]
+
+ if val.IsValid() {
+ return val
+ }
+
+ // no concrete types found, try to find implementors
+ // if t is an interface
+ if t.Kind() == reflect.Interface {
+ for k, v := range i.values {
+ if k.Implements(t) {
+ val = v
+ break
+ }
+ }
+ }
+
+ // Still no type found, try to look it up on the parent
+ if !val.IsValid() && i.parent != nil {
+ val = i.parent.Get(t)
+ }
+
+ return val
+
+}
+
+func (i *injector) SetParent(parent Injector) {
+ i.parent = parent
+}
diff --git a/vendor/github.com/codegangsta/negroni/LICENSE b/vendor/github.com/codegangsta/negroni/LICENSE
new file mode 100644
index 0000000..08b5e20
--- /dev/null
+++ b/vendor/github.com/codegangsta/negroni/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Jeremy Saenz
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/codegangsta/negroni/doc.go b/vendor/github.com/codegangsta/negroni/doc.go
new file mode 100644
index 0000000..add1ed9
--- /dev/null
+++ b/vendor/github.com/codegangsta/negroni/doc.go
@@ -0,0 +1,25 @@
+// Package negroni is an idiomatic approach to web middleware in Go. It is tiny, non-intrusive, and encourages use of net/http Handlers.
+//
+// If you like the idea of Martini, but you think it contains too much magic, then Negroni is a great fit.
+//
+// For a full guide visit http://github.com/urfave/negroni
+//
+// package main
+//
+// import (
+// "github.com/urfave/negroni"
+// "net/http"
+// "fmt"
+// )
+//
+// func main() {
+// mux := http.NewServeMux()
+// mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
+// fmt.Fprintf(w, "Welcome to the home page!")
+// })
+//
+// n := negroni.Classic()
+// n.UseHandler(mux)
+// n.Run(":3000")
+// }
+package negroni
diff --git a/vendor/github.com/codegangsta/negroni/logger.go b/vendor/github.com/codegangsta/negroni/logger.go
new file mode 100644
index 0000000..04cd53b
--- /dev/null
+++ b/vendor/github.com/codegangsta/negroni/logger.go
@@ -0,0 +1,35 @@
+package negroni
+
+import (
+ "log"
+ "net/http"
+ "os"
+ "time"
+)
+
+// ALogger interface
+type ALogger interface {
+ Println(v ...interface{})
+ Printf(format string, v ...interface{})
+}
+
+// Logger is a middleware handler that logs the request as it goes in and the response as it goes out.
+type Logger struct {
+ // ALogger implements just enough log.Logger interface to be compatible with other implementations
+ ALogger
+}
+
+// NewLogger returns a new Logger instance
+func NewLogger() *Logger {
+ return &Logger{log.New(os.Stdout, "[negroni] ", 0)}
+}
+
+func (l *Logger) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
+ start := time.Now()
+ l.Printf("Started %s %s", r.Method, r.URL.Path)
+
+ next(rw, r)
+
+ res := rw.(ResponseWriter)
+ l.Printf("Completed %v %s in %v", res.Status(), http.StatusText(res.Status()), time.Since(start))
+}
diff --git a/vendor/github.com/codegangsta/negroni/negroni.go b/vendor/github.com/codegangsta/negroni/negroni.go
new file mode 100644
index 0000000..9c7c187
--- /dev/null
+++ b/vendor/github.com/codegangsta/negroni/negroni.go
@@ -0,0 +1,133 @@
+package negroni
+
+import (
+ "log"
+ "net/http"
+ "os"
+)
+
+// Handler handler is an interface that objects can implement to be registered to serve as middleware
+// in the Negroni middleware stack.
+// ServeHTTP should yield to the next middleware in the chain by invoking the next http.HandlerFunc
+// passed in.
+//
+// If the Handler writes to the ResponseWriter, the next http.HandlerFunc should not be invoked.
+type Handler interface {
+ ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
+}
+
+// HandlerFunc is an adapter to allow the use of ordinary functions as Negroni handlers.
+// If f is a function with the appropriate signature, HandlerFunc(f) is a Handler object that calls f.
+type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
+
+func (h HandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
+ h(rw, r, next)
+}
+
+type middleware struct {
+ handler Handler
+ next *middleware
+}
+
+func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+ m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
+}
+
+// Wrap converts a http.Handler into a negroni.Handler so it can be used as a Negroni
+// middleware. The next http.HandlerFunc is automatically called after the Handler
+// is executed.
+func Wrap(handler http.Handler) Handler {
+ return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
+ handler.ServeHTTP(rw, r)
+ next(rw, r)
+ })
+}
+
+// Negroni is a stack of Middleware Handlers that can be invoked as an http.Handler.
+// Negroni middleware is evaluated in the order that they are added to the stack using
+// the Use and UseHandler methods.
+type Negroni struct {
+ middleware middleware
+ handlers []Handler
+}
+
+// New returns a new Negroni instance with no middleware preconfigured.
+func New(handlers ...Handler) *Negroni {
+ return &Negroni{
+ handlers: handlers,
+ middleware: build(handlers),
+ }
+}
+
+// Classic returns a new Negroni instance with the default middleware already
+// in the stack.
+//
+// Recovery - Panic Recovery Middleware
+// Logger - Request/Response Logging
+// Static - Static File Serving
+func Classic() *Negroni {
+ return New(NewRecovery(), NewLogger(), NewStatic(http.Dir("public")))
+}
+
+func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+ n.middleware.ServeHTTP(NewResponseWriter(rw), r)
+}
+
+// Use adds a Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni.
+func (n *Negroni) Use(handler Handler) {
+ if handler == nil {
+ panic("handler cannot be nil")
+ }
+
+ n.handlers = append(n.handlers, handler)
+ n.middleware = build(n.handlers)
+}
+
+// UseFunc adds a Negroni-style handler function onto the middleware stack.
+func (n *Negroni) UseFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)) {
+ n.Use(HandlerFunc(handlerFunc))
+}
+
+// UseHandler adds a http.Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni.
+func (n *Negroni) UseHandler(handler http.Handler) {
+ n.Use(Wrap(handler))
+}
+
+// UseHandler adds a http.HandlerFunc-style handler function onto the middleware stack.
+func (n *Negroni) UseHandlerFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request)) {
+ n.UseHandler(http.HandlerFunc(handlerFunc))
+}
+
+// Run is a convenience function that runs the negroni stack as an HTTP
+// server. The addr string takes the same format as http.ListenAndServe.
+func (n *Negroni) Run(addr string) {
+ l := log.New(os.Stdout, "[negroni] ", 0)
+ l.Printf("listening on %s", addr)
+ l.Fatal(http.ListenAndServe(addr, n))
+}
+
+// Returns a list of all the handlers in the current Negroni middleware chain.
+func (n *Negroni) Handlers() []Handler {
+ return n.handlers
+}
+
+func build(handlers []Handler) middleware {
+ var next middleware
+
+ if len(handlers) == 0 {
+ return voidMiddleware()
+ } else if len(handlers) > 1 {
+ next = build(handlers[1:])
+ } else {
+ next = voidMiddleware()
+ }
+
+ return middleware{handlers[0], &next}
+}
+
+func voidMiddleware() middleware {
+ return middleware{
+ HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {}),
+ &middleware{},
+ }
+}
diff --git a/vendor/github.com/codegangsta/negroni/recovery.go b/vendor/github.com/codegangsta/negroni/recovery.go
new file mode 100644
index 0000000..8396cb1
--- /dev/null
+++ b/vendor/github.com/codegangsta/negroni/recovery.go
@@ -0,0 +1,65 @@
+package negroni
+
+import (
+ "fmt"
+ "log"
+ "net/http"
+ "os"
+ "runtime"
+ "runtime/debug"
+)
+
+// Recovery is a Negroni middleware that recovers from any panics and writes a 500 if there was one.
+type Recovery struct {
+ Logger ALogger
+ PrintStack bool
+ ErrorHandlerFunc func(interface{})
+ StackAll bool
+ StackSize int
+}
+
+// NewRecovery returns a new instance of Recovery
+func NewRecovery() *Recovery {
+ return &Recovery{
+ Logger: log.New(os.Stdout, "[negroni] ", 0),
+ PrintStack: true,
+ StackAll: false,
+ StackSize: 1024 * 8,
+ }
+}
+
+func (rec *Recovery) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
+ defer func() {
+ if err := recover(); err != nil {
+ if rw.Header().Get("Content-Type") == "" {
+ rw.Header().Set("Content-Type", "text/plain; charset=utf-8")
+ }
+
+ rw.WriteHeader(http.StatusInternalServerError)
+
+ stack := make([]byte, rec.StackSize)
+ stack = stack[:runtime.Stack(stack, rec.StackAll)]
+
+ f := "PANIC: %s\n%s"
+ rec.Logger.Printf(f, err, stack)
+
+ if rec.PrintStack {
+ fmt.Fprintf(rw, f, err, stack)
+ }
+
+ if rec.ErrorHandlerFunc != nil {
+ func() {
+ defer func() {
+ if err := recover(); err != nil {
+ rec.Logger.Printf("provided ErrorHandlerFunc panic'd: %s, trace:\n%s", err, debug.Stack())
+ rec.Logger.Printf("%s\n", debug.Stack())
+ }
+ }()
+ rec.ErrorHandlerFunc(err)
+ }()
+ }
+ }
+ }()
+
+ next(rw, r)
+}
diff --git a/vendor/github.com/codegangsta/negroni/response_writer.go b/vendor/github.com/codegangsta/negroni/response_writer.go
new file mode 100644
index 0000000..bfb83a6
--- /dev/null
+++ b/vendor/github.com/codegangsta/negroni/response_writer.go
@@ -0,0 +1,113 @@
+package negroni
+
+import (
+ "bufio"
+ "fmt"
+ "net"
+ "net/http"
+)
+
+// ResponseWriter is a wrapper around http.ResponseWriter that provides extra information about
+// the response. It is recommended that middleware handlers use this construct to wrap a responsewriter
+// if the functionality calls for it.
+type ResponseWriter interface {
+ http.ResponseWriter
+ http.Flusher
+ // Status returns the status code of the response or 200 if the response has
+ // not been written (as this is the default response code in net/http)
+ Status() int
+ // Written returns whether or not the ResponseWriter has been written.
+ Written() bool
+ // Size returns the size of the response body.
+ Size() int
+ // Before allows for a function to be called before the ResponseWriter has been written to. This is
+ // useful for setting headers or any other operations that must happen before a response has been written.
+ Before(func(ResponseWriter))
+}
+
+type beforeFunc func(ResponseWriter)
+
+// NewResponseWriter creates a ResponseWriter that wraps an http.ResponseWriter
+func NewResponseWriter(rw http.ResponseWriter) ResponseWriter {
+ nrw := &responseWriter{
+ ResponseWriter: rw,
+ }
+
+ if _, ok := rw.(http.CloseNotifier); ok {
+ return &responseWriterCloseNotifer{nrw}
+ }
+
+ return nrw
+}
+
+type responseWriter struct {
+ http.ResponseWriter
+ status int
+ size int
+ beforeFuncs []beforeFunc
+}
+
+func (rw *responseWriter) WriteHeader(s int) {
+ rw.status = s
+ rw.callBefore()
+ rw.ResponseWriter.WriteHeader(s)
+}
+
+func (rw *responseWriter) Write(b []byte) (int, error) {
+ if !rw.Written() {
+ // The status will be StatusOK if WriteHeader has not been called yet
+ rw.WriteHeader(http.StatusOK)
+ }
+ size, err := rw.ResponseWriter.Write(b)
+ rw.size += size
+ return size, err
+}
+
+func (rw *responseWriter) Status() int {
+ return rw.status
+}
+
+func (rw *responseWriter) Size() int {
+ return rw.size
+}
+
+func (rw *responseWriter) Written() bool {
+ return rw.status != 0
+}
+
+func (rw *responseWriter) Before(before func(ResponseWriter)) {
+ rw.beforeFuncs = append(rw.beforeFuncs, before)
+}
+
+func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
+ hijacker, ok := rw.ResponseWriter.(http.Hijacker)
+ if !ok {
+ return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface")
+ }
+ return hijacker.Hijack()
+}
+
+func (rw *responseWriter) callBefore() {
+ for i := len(rw.beforeFuncs) - 1; i >= 0; i-- {
+ rw.beforeFuncs[i](rw)
+ }
+}
+
+func (rw *responseWriter) Flush() {
+ flusher, ok := rw.ResponseWriter.(http.Flusher)
+ if ok {
+ if !rw.Written() {
+ // The status will be StatusOK if WriteHeader has not been called yet
+ rw.WriteHeader(http.StatusOK)
+ }
+ flusher.Flush()
+ }
+}
+
+type responseWriterCloseNotifer struct {
+ *responseWriter
+}
+
+func (rw *responseWriterCloseNotifer) CloseNotify() <-chan bool {
+ return rw.ResponseWriter.(http.CloseNotifier).CloseNotify()
+}
diff --git a/vendor/github.com/codegangsta/negroni/static.go b/vendor/github.com/codegangsta/negroni/static.go
new file mode 100644
index 0000000..34be967
--- /dev/null
+++ b/vendor/github.com/codegangsta/negroni/static.go
@@ -0,0 +1,88 @@
+package negroni
+
+import (
+ "net/http"
+ "path"
+ "strings"
+)
+
+// Static is a middleware handler that serves static files in the given
+// directory/filesystem. If the file does not exist on the filesystem, it
+// passes along to the next middleware in the chain. If you desire "fileserver"
+// type behavior where it returns a 404 for unfound files, you should consider
+// using http.FileServer from the Go stdlib.
+type Static struct {
+ // Dir is the directory to serve static files from
+ Dir http.FileSystem
+ // Prefix is the optional prefix used to serve the static directory content
+ Prefix string
+ // IndexFile defines which file to serve as index if it exists.
+ IndexFile string
+}
+
+// NewStatic returns a new instance of Static
+func NewStatic(directory http.FileSystem) *Static {
+ return &Static{
+ Dir: directory,
+ Prefix: "",
+ IndexFile: "index.html",
+ }
+}
+
+func (s *Static) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
+ if r.Method != "GET" && r.Method != "HEAD" {
+ next(rw, r)
+ return
+ }
+ file := r.URL.Path
+ // if we have a prefix, filter requests by stripping the prefix
+ if s.Prefix != "" {
+ if !strings.HasPrefix(file, s.Prefix) {
+ next(rw, r)
+ return
+ }
+ file = file[len(s.Prefix):]
+ if file != "" && file[0] != '/' {
+ next(rw, r)
+ return
+ }
+ }
+ f, err := s.Dir.Open(file)
+ if err != nil {
+ // discard the error?
+ next(rw, r)
+ return
+ }
+ defer f.Close()
+
+ fi, err := f.Stat()
+ if err != nil {
+ next(rw, r)
+ return
+ }
+
+ // try to serve index file
+ if fi.IsDir() {
+ // redirect if missing trailing slash
+ if !strings.HasSuffix(r.URL.Path, "/") {
+ http.Redirect(rw, r, r.URL.Path+"/", http.StatusFound)
+ return
+ }
+
+ file = path.Join(file, s.IndexFile)
+ f, err = s.Dir.Open(file)
+ if err != nil {
+ next(rw, r)
+ return
+ }
+ defer f.Close()
+
+ fi, err = f.Stat()
+ if err != nil || fi.IsDir() {
+ next(rw, r)
+ return
+ }
+ }
+
+ http.ServeContent(rw, r, file, fi.ModTime(), f)
+}
diff --git a/vendor/github.com/go-martini/martini/LICENSE b/vendor/github.com/go-martini/martini/LICENSE
new file mode 100644
index 0000000..d3fefb8
--- /dev/null
+++ b/vendor/github.com/go-martini/martini/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Jeremy Saenz
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/go-martini/martini/env.go b/vendor/github.com/go-martini/martini/env.go
new file mode 100644
index 0000000..54d5857
--- /dev/null
+++ b/vendor/github.com/go-martini/martini/env.go
@@ -0,0 +1,31 @@
+package martini
+
+import (
+ "os"
+)
+
+// Envs
+const (
+ Dev string = "development"
+ Prod string = "production"
+ Test string = "test"
+)
+
+// Env is the environment that Martini is executing in. The MARTINI_ENV is read on initialization to set this variable.
+var Env = Dev
+var Root string
+
+func setENV(e string) {
+ if len(e) > 0 {
+ Env = e
+ }
+}
+
+func init() {
+ setENV(os.Getenv("MARTINI_ENV"))
+ var err error
+ Root, err = os.Getwd()
+ if err != nil {
+ panic(err)
+ }
+}
diff --git a/vendor/github.com/go-martini/martini/go_version.go b/vendor/github.com/go-martini/martini/go_version.go
new file mode 100644
index 0000000..bd271a8
--- /dev/null
+++ b/vendor/github.com/go-martini/martini/go_version.go
@@ -0,0 +1,7 @@
+// +build !go1.1
+
+package martini
+
+func MartiniDoesNotSupportGo1Point0() {
+ "Martini requires Go 1.1 or greater."
+}
diff --git a/vendor/github.com/go-martini/martini/logger.go b/vendor/github.com/go-martini/martini/logger.go
new file mode 100644
index 0000000..d01107c
--- /dev/null
+++ b/vendor/github.com/go-martini/martini/logger.go
@@ -0,0 +1,29 @@
+package martini
+
+import (
+ "log"
+ "net/http"
+ "time"
+)
+
+// Logger returns a middleware handler that logs the request as it goes in and the response as it goes out.
+func Logger() Handler {
+ return func(res http.ResponseWriter, req *http.Request, c Context, log *log.Logger) {
+ start := time.Now()
+
+ addr := req.Header.Get("X-Real-IP")
+ if addr == "" {
+ addr = req.Header.Get("X-Forwarded-For")
+ if addr == "" {
+ addr = req.RemoteAddr
+ }
+ }
+
+ log.Printf("Started %s %s for %s", req.Method, req.URL.Path, addr)
+
+ rw := res.(ResponseWriter)
+ c.Next()
+
+ log.Printf("Completed %v %s in %v\n", rw.Status(), http.StatusText(rw.Status()), time.Since(start))
+ }
+}
diff --git a/vendor/github.com/go-martini/martini/martini.go b/vendor/github.com/go-martini/martini/martini.go
new file mode 100644
index 0000000..0ce4f3d
--- /dev/null
+++ b/vendor/github.com/go-martini/martini/martini.go
@@ -0,0 +1,189 @@
+// Package martini is a powerful package for quickly writing modular web applications/services in Golang.
+//
+// For a full guide visit http://github.com/go-martini/martini
+//
+// package main
+//
+// import "github.com/go-martini/martini"
+//
+// func main() {
+// m := martini.Classic()
+//
+// m.Get("/", func() string {
+// return "Hello world!"
+// })
+//
+// m.Run()
+// }
+package martini
+
+import (
+ "log"
+ "net/http"
+ "os"
+ "reflect"
+
+ "github.com/codegangsta/inject"
+)
+
+// Martini represents the top level web application. inject.Injector methods can be invoked to map services on a global level.
+type Martini struct {
+ inject.Injector
+ handlers []Handler
+ action Handler
+ logger *log.Logger
+}
+
+// New creates a bare bones Martini instance. Use this method if you want to have full control over the middleware that is used.
+func New() *Martini {
+ m := &Martini{Injector: inject.New(), action: func() {}, logger: log.New(os.Stdout, "[martini] ", 0)}
+ m.Map(m.logger)
+ m.Map(defaultReturnHandler())
+ return m
+}
+
+// Handlers sets the entire middleware stack with the given Handlers. This will clear any current middleware handlers.
+// Will panic if any of the handlers is not a callable function
+func (m *Martini) Handlers(handlers ...Handler) {
+ m.handlers = make([]Handler, 0)
+ for _, handler := range handlers {
+ m.Use(handler)
+ }
+}
+
+// Action sets the handler that will be called after all the middleware has been invoked. This is set to martini.Router in a martini.Classic().
+func (m *Martini) Action(handler Handler) {
+ validateHandler(handler)
+ m.action = handler
+}
+
+// Logger sets the logger
+func (m *Martini) Logger(logger *log.Logger) {
+ m.logger = logger
+ m.Map(m.logger)
+}
+
+// Use adds a middleware Handler to the stack. Will panic if the handler is not a callable func. Middleware Handlers are invoked in the order that they are added.
+func (m *Martini) Use(handler Handler) {
+ validateHandler(handler)
+
+ m.handlers = append(m.handlers, handler)
+}
+
+// ServeHTTP is the HTTP Entry point for a Martini instance. Useful if you want to control your own HTTP server.
+func (m *Martini) ServeHTTP(res http.ResponseWriter, req *http.Request) {
+ m.createContext(res, req).run()
+}
+
+// Run the http server on a given host and port.
+func (m *Martini) RunOnAddr(addr string) {
+ // TODO: Should probably be implemented using a new instance of http.Server in place of
+ // calling http.ListenAndServer directly, so that it could be stored in the martini struct for later use.
+ // This would also allow to improve testing when a custom host and port are passed.
+
+ logger := m.Injector.Get(reflect.TypeOf(m.logger)).Interface().(*log.Logger)
+ logger.Printf("listening on %s (%s)\n", addr, Env)
+ logger.Fatalln(http.ListenAndServe(addr, m))
+}
+
+// Run the http server. Listening on os.GetEnv("PORT") or 3000 by default.
+func (m *Martini) Run() {
+ port := os.Getenv("PORT")
+ if len(port) == 0 {
+ port = "3000"
+ }
+
+ host := os.Getenv("HOST")
+
+ m.RunOnAddr(host + ":" + port)
+}
+
+func (m *Martini) createContext(res http.ResponseWriter, req *http.Request) *context {
+ c := &context{inject.New(), m.handlers, m.action, NewResponseWriter(res), 0}
+ c.SetParent(m)
+ c.MapTo(c, (*Context)(nil))
+ c.MapTo(c.rw, (*http.ResponseWriter)(nil))
+ c.Map(req)
+ return c
+}
+
+// ClassicMartini represents a Martini with some reasonable defaults. Embeds the router functions for convenience.
+type ClassicMartini struct {
+ *Martini
+ Router
+}
+
+// Classic creates a classic Martini with some basic default middleware - martini.Logger, martini.Recovery and martini.Static.
+// Classic also maps martini.Routes as a service.
+func Classic() *ClassicMartini {
+ r := NewRouter()
+ m := New()
+ m.Use(Logger())
+ m.Use(Recovery())
+ m.Use(Static("public"))
+ m.MapTo(r, (*Routes)(nil))
+ m.Action(r.Handle)
+ return &ClassicMartini{m, r}
+}
+
+// Handler can be any callable function. Martini attempts to inject services into the handler's argument list.
+// Martini will panic if an argument could not be fullfilled via dependency injection.
+type Handler interface{}
+
+func validateHandler(handler Handler) {
+ if reflect.TypeOf(handler).Kind() != reflect.Func {
+ panic("martini handler must be a callable func")
+ }
+}
+
+// Context represents a request context. Services can be mapped on the request level from this interface.
+type Context interface {
+ inject.Injector
+ // Next is an optional function that Middleware Handlers can call to yield the until after
+ // the other Handlers have been executed. This works really well for any operations that must
+ // happen after an http request
+ Next()
+ // Written returns whether or not the response for this context has been written.
+ Written() bool
+}
+
+type context struct {
+ inject.Injector
+ handlers []Handler
+ action Handler
+ rw ResponseWriter
+ index int
+}
+
+func (c *context) handler() Handler {
+ if c.index < len(c.handlers) {
+ return c.handlers[c.index]
+ }
+ if c.index == len(c.handlers) {
+ return c.action
+ }
+ panic("invalid index for context handler")
+}
+
+func (c *context) Next() {
+ c.index += 1
+ c.run()
+}
+
+func (c *context) Written() bool {
+ return c.rw.Written()
+}
+
+func (c *context) run() {
+ for c.index <= len(c.handlers) {
+ _, err := c.Invoke(c.handler())
+ if err != nil {
+ panic(err)
+ }
+ c.index += 1
+
+ if c.Written() {
+ return
+ }
+ }
+}
diff --git a/vendor/github.com/go-martini/martini/recovery.go b/vendor/github.com/go-martini/martini/recovery.go
new file mode 100644
index 0000000..fe0d918
--- /dev/null
+++ b/vendor/github.com/go-martini/martini/recovery.go
@@ -0,0 +1,144 @@
+package martini
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "runtime"
+
+ "github.com/codegangsta/inject"
+)
+
+const (
+ panicHtml = `<html>
+<head><title>PANIC: %s</title>
+<style type="text/css">
+html, body {
+ font-family: "Roboto", sans-serif;
+ color: #333333;
+ background-color: #ea5343;
+ margin: 0px;
+}
+h1 {
+ color: #d04526;
+ background-color: #ffffff;
+ padding: 20px;
+ border-bottom: 1px dashed #2b3848;
+}
+pre {
+ margin: 20px;
+ padding: 20px;
+ border: 2px solid #2b3848;
+ background-color: #ffffff;
+}
+</style>
+</head><body>
+<h1>PANIC</h1>
+<pre style="font-weight: bold;">%s</pre>
+<pre>%s</pre>
+</body>
+</html>`
+)
+
+var (
+ dunno = []byte("???")
+ centerDot = []byte("·")
+ dot = []byte(".")
+ slash = []byte("/")
+)
+
+// stack returns a nicely formated stack frame, skipping skip frames
+func stack(skip int) []byte {
+ buf := new(bytes.Buffer) // the returned data
+ // As we loop, we open files and read them. These variables record the currently
+ // loaded file.
+ var lines [][]byte
+ var lastFile string
+ for i := skip; ; i++ { // Skip the expected number of frames
+ pc, file, line, ok := runtime.Caller(i)
+ if !ok {
+ break
+ }
+ // Print this much at least. If we can't find the source, it won't show.
+ fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
+ if file != lastFile {
+ data, err := ioutil.ReadFile(file)
+ if err != nil {
+ continue
+ }
+ lines = bytes.Split(data, []byte{'\n'})
+ lastFile = file
+ }
+ fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
+ }
+ return buf.Bytes()
+}
+
+// source returns a space-trimmed slice of the n'th line.
+func source(lines [][]byte, n int) []byte {
+ n-- // in stack trace, lines are 1-indexed but our array is 0-indexed
+ if n < 0 || n >= len(lines) {
+ return dunno
+ }
+ return bytes.TrimSpace(lines[n])
+}
+
+// function returns, if possible, the name of the function containing the PC.
+func function(pc uintptr) []byte {
+ fn := runtime.FuncForPC(pc)
+ if fn == nil {
+ return dunno
+ }
+ name := []byte(fn.Name())
+ // The name includes the path name to the package, which is unnecessary
+ // since the file name is already included. Plus, it has center dots.
+ // That is, we see
+ // runtime/debug.*T·ptrmethod
+ // and want
+ // *T.ptrmethod
+ // Also the package path might contains dot (e.g. code.google.com/...),
+ // so first eliminate the path prefix
+ if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 {
+ name = name[lastslash+1:]
+ }
+ if period := bytes.Index(name, dot); period >= 0 {
+ name = name[period+1:]
+ }
+ name = bytes.Replace(name, centerDot, dot, -1)
+ return name
+}
+
+// Recovery returns a middleware that recovers from any panics and writes a 500 if there was one.
+// While Martini is in development mode, Recovery will also output the panic as HTML.
+func Recovery() Handler {
+ return func(c Context, log *log.Logger) {
+ defer func() {
+ if err := recover(); err != nil {
+ stack := stack(3)
+ log.Printf("PANIC: %s\n%s", err, stack)
+
+ // Lookup the current responsewriter
+ val := c.Get(inject.InterfaceOf((*http.ResponseWriter)(nil)))
+ res := val.Interface().(http.ResponseWriter)
+
+ // respond with panic message while in development mode
+ var body []byte
+ if Env == Dev {
+ res.Header().Set("Content-Type", "text/html")
+ body = []byte(fmt.Sprintf(panicHtml, err, err, stack))
+ } else {
+ body = []byte("500 Internal Server Error")
+ }
+
+ res.WriteHeader(http.StatusInternalServerError)
+ if nil != body {
+ res.Write(body)
+ }
+ }
+ }()
+
+ c.Next()
+ }
+}
diff --git a/vendor/github.com/go-martini/martini/response_writer.go b/vendor/github.com/go-martini/martini/response_writer.go
new file mode 100644
index 0000000..12574b1
--- /dev/null
+++ b/vendor/github.com/go-martini/martini/response_writer.go
@@ -0,0 +1,107 @@
+package martini
+
+import (
+ "bufio"
+ "fmt"
+ "net"
+ "net/http"
+)
+
+// ResponseWriter is a wrapper around http.ResponseWriter that provides extra information about
+// the response. It is recommended that middleware handlers use this construct to wrap a responsewriter
+// if the functionality calls for it.
+type ResponseWriter interface {
+ http.ResponseWriter
+ http.Flusher
+ http.Hijacker
+ // Status returns the status code of the response or 0 if the response has not been written.
+ Status() int
+ // Written returns whether or not the ResponseWriter has been written.
+ Written() bool
+ // Size returns the size of the response body.
+ Size() int
+ // Before allows for a function to be called before the ResponseWriter has been written to. This is
+ // useful for setting headers or any other operations that must happen before a response has been written.
+ Before(BeforeFunc)
+}
+
+// BeforeFunc is a function that is called before the ResponseWriter has been written to.
+type BeforeFunc func(ResponseWriter)
+
+// NewResponseWriter creates a ResponseWriter that wraps an http.ResponseWriter
+func NewResponseWriter(rw http.ResponseWriter) ResponseWriter {
+ newRw := responseWriter{rw, 0, 0, nil}
+ if cn, ok := rw.(http.CloseNotifier); ok {
+ return &closeNotifyResponseWriter{newRw, cn}
+ }
+ return &newRw
+}
+
+type responseWriter struct {
+ http.ResponseWriter
+ status int
+ size int
+ beforeFuncs []BeforeFunc
+}
+
+func (rw *responseWriter) WriteHeader(s int) {
+ rw.callBefore()
+ rw.ResponseWriter.WriteHeader(s)
+ rw.status = s
+}
+
+func (rw *responseWriter) Write(b []byte) (int, error) {
+ if !rw.Written() {
+ // The status will be StatusOK if WriteHeader has not been called yet
+ rw.WriteHeader(http.StatusOK)
+ }
+ size, err := rw.ResponseWriter.Write(b)
+ rw.size += size
+ return size, err
+}
+
+func (rw *responseWriter) Status() int {
+ return rw.status
+}
+
+func (rw *responseWriter) Size() int {
+ return rw.size
+}
+
+func (rw *responseWriter) Written() bool {
+ return rw.status != 0
+}
+
+func (rw *responseWriter) Before(before BeforeFunc) {
+ rw.beforeFuncs = append(rw.beforeFuncs, before)
+}
+
+func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
+ hijacker, ok := rw.ResponseWriter.(http.Hijacker)
+ if !ok {
+ return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface")
+ }
+ return hijacker.Hijack()
+}
+
+func (rw *responseWriter) callBefore() {
+ for i := len(rw.beforeFuncs) - 1; i >= 0; i-- {
+ rw.beforeFuncs[i](rw)
+ }
+}
+
+func (rw *responseWriter) Flush() {
+ flusher, ok := rw.ResponseWriter.(http.Flusher)
+ if ok {
+ flusher.Flush()
+ }
+}
+
+type closeNotifyResponseWriter struct {
+ responseWriter
+ closeNotifier http.CloseNotifier
+}
+
+func (rw *closeNotifyResponseWriter) CloseNotify() <-chan bool {
+ return rw.closeNotifier.CloseNotify()
+}
diff --git a/vendor/github.com/go-martini/martini/return_handler.go b/vendor/github.com/go-martini/martini/return_handler.go
new file mode 100644
index 0000000..4ea8f34
--- /dev/null
+++ b/vendor/github.com/go-martini/martini/return_handler.go
@@ -0,0 +1,43 @@
+package martini
+
+import (
+ "github.com/codegangsta/inject"
+ "net/http"
+ "reflect"
+)
+
+// ReturnHandler is a service that Martini provides that is called
+// when a route handler returns something. The ReturnHandler is
+// responsible for writing to the ResponseWriter based on the values
+// that are passed into this function.
+type ReturnHandler func(Context, []reflect.Value)
+
+func defaultReturnHandler() ReturnHandler {
+ return func(ctx Context, vals []reflect.Value) {
+ rv := ctx.Get(inject.InterfaceOf((*http.ResponseWriter)(nil)))
+ res := rv.Interface().(http.ResponseWriter)
+ var responseVal reflect.Value
+ if len(vals) > 1 && vals[0].Kind() == reflect.Int {
+ res.WriteHeader(int(vals[0].Int()))
+ responseVal = vals[1]
+ } else if len(vals) > 0 {
+ responseVal = vals[0]
+ }
+ if canDeref(responseVal) {
+ responseVal = responseVal.Elem()
+ }
+ if isByteSlice(responseVal) {
+ res.Write(responseVal.Bytes())
+ } else {
+ res.Write([]byte(responseVal.String()))
+ }
+ }
+}
+
+func isByteSlice(val reflect.Value) bool {
+ return val.Kind() == reflect.Slice && val.Type().Elem().Kind() == reflect.Uint8
+}
+
+func canDeref(val reflect.Value) bool {
+ return val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr
+}
diff --git a/vendor/github.com/go-martini/martini/router.go b/vendor/github.com/go-martini/martini/router.go
new file mode 100644
index 0000000..3abbabb
--- /dev/null
+++ b/vendor/github.com/go-martini/martini/router.go
@@ -0,0 +1,425 @@
+package martini
+
+import (
+ "fmt"
+ "net/http"
+ "reflect"
+ "regexp"
+ "strconv"
+ "sync"
+)
+
+// Params is a map of name/value pairs for named routes. An instance of martini.Params is available to be injected into any route handler.
+type Params map[string]string
+
+// Router is Martini's de-facto routing interface. Supports HTTP verbs, stacked handlers, and dependency injection.
+type Router interface {
+ Routes
+
+ // Group adds a group where related routes can be added.
+ Group(string, func(Router), ...Handler)
+ // Get adds a route for a HTTP GET request to the specified matching pattern.
+ Get(string, ...Handler) Route
+ // Patch adds a route for a HTTP PATCH request to the specified matching pattern.
+ Patch(string, ...Handler) Route
+ // Post adds a route for a HTTP POST request to the specified matching pattern.
+ Post(string, ...Handler) Route
+ // Put adds a route for a HTTP PUT request to the specified matching pattern.
+ Put(string, ...Handler) Route
+ // Delete adds a route for a HTTP DELETE request to the specified matching pattern.
+ Delete(string, ...Handler) Route
+ // Options adds a route for a HTTP OPTIONS request to the specified matching pattern.
+ Options(string, ...Handler) Route
+ // Head adds a route for a HTTP HEAD request to the specified matching pattern.
+ Head(string, ...Handler) Route
+ // Any adds a route for any HTTP method request to the specified matching pattern.
+ Any(string, ...Handler) Route
+ // AddRoute adds a route for a given HTTP method request to the specified matching pattern.
+ AddRoute(string, string, ...Handler) Route
+
+ // NotFound sets the handlers that are called when a no route matches a request. Throws a basic 404 by default.
+ NotFound(...Handler)
+
+ // Handle is the entry point for routing. This is used as a martini.Handler
+ Handle(http.ResponseWriter, *http.Request, Context)
+}
+
+type router struct {
+ routes []*route
+ notFounds []Handler
+ groups []group
+ routesLock sync.RWMutex
+}
+
+type group struct {
+ pattern string
+ handlers []Handler
+}
+
+// NewRouter creates a new Router instance.
+// If you aren't using ClassicMartini, then you can add Routes as a
+// service with:
+//
+// m := martini.New()
+// r := martini.NewRouter()
+// m.MapTo(r, (*martini.Routes)(nil))
+//
+// If you are using ClassicMartini, then this is done for you.
+func NewRouter() Router {
+ return &router{notFounds: []Handler{http.NotFound}, groups: make([]group, 0)}
+}
+
+func (r *router) Group(pattern string, fn func(Router), h ...Handler) {
+ r.groups = append(r.groups, group{pattern, h})
+ fn(r)
+ r.groups = r.groups[:len(r.groups)-1]
+}
+
+func (r *router) Get(pattern string, h ...Handler) Route {
+ return r.addRoute("GET", pattern, h)
+}
+
+func (r *router) Patch(pattern string, h ...Handler) Route {
+ return r.addRoute("PATCH", pattern, h)
+}
+
+func (r *router) Post(pattern string, h ...Handler) Route {
+ return r.addRoute("POST", pattern, h)
+}
+
+func (r *router) Put(pattern string, h ...Handler) Route {
+ return r.addRoute("PUT", pattern, h)
+}
+
+func (r *router) Delete(pattern string, h ...Handler) Route {
+ return r.addRoute("DELETE", pattern, h)
+}
+
+func (r *router) Options(pattern string, h ...Handler) Route {
+ return r.addRoute("OPTIONS", pattern, h)
+}
+
+func (r *router) Head(pattern string, h ...Handler) Route {
+ return r.addRoute("HEAD", pattern, h)
+}
+
+func (r *router) Any(pattern string, h ...Handler) Route {
+ return r.addRoute("*", pattern, h)
+}
+
+func (r *router) AddRoute(method, pattern string, h ...Handler) Route {
+ return r.addRoute(method, pattern, h)
+}
+
+func (r *router) Handle(res http.ResponseWriter, req *http.Request, context Context) {
+ bestMatch := NoMatch
+ var bestVals map[string]string
+ var bestRoute *route
+ for _, route := range r.getRoutes() {
+ match, vals := route.Match(req.Method, req.URL.Path)
+ if match.BetterThan(bestMatch) {
+ bestMatch = match
+ bestVals = vals
+ bestRoute = route
+ if match == ExactMatch {
+ break
+ }
+ }
+ }
+ if bestMatch != NoMatch {
+ params := Params(bestVals)
+ context.Map(params)
+ bestRoute.Handle(context, res)
+ return
+ }
+
+ // no routes exist, 404
+ c := &routeContext{context, 0, r.notFounds}
+ context.MapTo(c, (*Context)(nil))
+ c.run()
+}
+
+func (r *router) NotFound(handler ...Handler) {
+ r.notFounds = handler
+}
+
+func (r *router) addRoute(method string, pattern string, handlers []Handler) *route {
+ if len(r.groups) > 0 {
+ groupPattern := ""
+ h := make([]Handler, 0)
+ for _, g := range r.groups {
+ groupPattern += g.pattern
+ h = append(h, g.handlers...)
+ }
+
+ pattern = groupPattern + pattern
+ h = append(h, handlers...)
+ handlers = h
+ }
+
+ route := newRoute(method, pattern, handlers)
+ route.Validate()
+ r.appendRoute(route)
+ return route
+}
+
+func (r *router) appendRoute(rt *route) {
+ r.routesLock.Lock()
+ defer r.routesLock.Unlock()
+ r.routes = append(r.routes, rt)
+}
+
+func (r *router) getRoutes() []*route {
+ r.routesLock.RLock()
+ defer r.routesLock.RUnlock()
+ return r.routes[:]
+}
+
+func (r *router) findRoute(name string) *route {
+ for _, route := range r.getRoutes() {
+ if route.name == name {
+ return route
+ }
+ }
+
+ return nil
+}
+
+// Route is an interface representing a Route in Martini's routing layer.
+type Route interface {
+ // URLWith returns a rendering of the Route's url with the given string params.
+ URLWith([]string) string
+ // Name sets a name for the route.
+ Name(string)
+ // GetName returns the name of the route.
+ GetName() string
+ // Pattern returns the pattern of the route.
+ Pattern() string
+ // Method returns the method of the route.
+ Method() string
+}
+
+type route struct {
+ method string
+ regex *regexp.Regexp
+ handlers []Handler
+ pattern string
+ name string
+}
+
+var routeReg1 = regexp.MustCompile(`:[^/#?()\.\\]+`)
+var routeReg2 = regexp.MustCompile(`\*\*`)
+
+func newRoute(method string, pattern string, handlers []Handler) *route {
+ route := route{method, nil, handlers, pattern, ""}
+ pattern = routeReg1.ReplaceAllStringFunc(pattern, func(m string) string {
+ return fmt.Sprintf(`(?P<%s>[^/#?]+)`, m[1:])
+ })
+ var index int
+ pattern = routeReg2.ReplaceAllStringFunc(pattern, func(m string) string {
+ index++
+ return fmt.Sprintf(`(?P<_%d>[^#?]*)`, index)
+ })
+ pattern += `\/?`
+ route.regex = regexp.MustCompile(pattern)
+ return &route
+}
+
+type RouteMatch int
+
+const (
+ NoMatch RouteMatch = iota
+ StarMatch
+ OverloadMatch
+ ExactMatch
+)
+
+//Higher number = better match
+func (r RouteMatch) BetterThan(o RouteMatch) bool {
+ return r > o
+}
+
+func (r route) MatchMethod(method string) RouteMatch {
+ switch {
+ case method == r.method:
+ return ExactMatch
+ case method == "HEAD" && r.method == "GET":
+ return OverloadMatch
+ case r.method == "*":
+ return StarMatch
+ default:
+ return NoMatch
+ }
+}
+
+func (r route) Match(method string, path string) (RouteMatch, map[string]string) {
+ // add Any method matching support
+ match := r.MatchMethod(method)
+ if match == NoMatch {
+ return match, nil
+ }
+
+ matches := r.regex.FindStringSubmatch(path)
+ if len(matches) > 0 && matches[0] == path {
+ params := make(map[string]string)
+ for i, name := range r.regex.SubexpNames() {
+ if len(name) > 0 {
+ params[name] = matches[i]
+ }
+ }
+ return match, params
+ }
+ return NoMatch, nil
+}
+
+func (r *route) Validate() {
+ for _, handler := range r.handlers {
+ validateHandler(handler)
+ }
+}
+
+func (r *route) Handle(c Context, res http.ResponseWriter) {
+ context := &routeContext{c, 0, r.handlers}
+ c.MapTo(context, (*Context)(nil))
+ c.MapTo(r, (*Route)(nil))
+ context.run()
+}
+
+var urlReg = regexp.MustCompile(`:[^/#?()\.\\]+|\(\?P<[a-zA-Z0-9]+>.*\)`)
+
+// URLWith returns the url pattern replacing the parameters for its values
+func (r *route) URLWith(args []string) string {
+ if len(args) > 0 {
+ argCount := len(args)
+ i := 0
+ url := urlReg.ReplaceAllStringFunc(r.pattern, func(m string) string {
+ var val interface{}
+ if i < argCount {
+ val = args[i]
+ } else {
+ val = m
+ }
+ i += 1
+ return fmt.Sprintf(`%v`, val)
+ })
+
+ return url
+ }
+ return r.pattern
+}
+
+func (r *route) Name(name string) {
+ r.name = name
+}
+
+func (r *route) GetName() string {
+ return r.name
+}
+
+func (r *route) Pattern() string {
+ return r.pattern
+}
+
+func (r *route) Method() string {
+ return r.method
+}
+
+// Routes is a helper service for Martini's routing layer.
+type Routes interface {
+ // URLFor returns a rendered URL for the given route. Optional params can be passed to fulfill named parameters in the route.
+ URLFor(name string, params ...interface{}) string
+ // MethodsFor returns an array of methods available for the path
+ MethodsFor(path string) []string
+ // All returns an array with all the routes in the router.
+ All() []Route
+}
+
+// URLFor returns the url for the given route name.
+func (r *router) URLFor(name string, params ...interface{}) string {
+ route := r.findRoute(name)
+
+ if route == nil {
+ panic("route not found")
+ }
+
+ var args []string
+ for _, param := range params {
+ switch v := param.(type) {
+ case int:
+ args = append(args, strconv.FormatInt(int64(v), 10))
+ case string:
+ args = append(args, v)
+ default:
+ if v != nil {
+ panic("Arguments passed to URLFor must be integers or strings")
+ }
+ }
+ }
+
+ return route.URLWith(args)
+}
+
+func (r *router) All() []Route {
+ routes := r.getRoutes()
+ var ri = make([]Route, len(routes))
+
+ for i, route := range routes {
+ ri[i] = Route(route)
+ }
+
+ return ri
+}
+
+func hasMethod(methods []string, method string) bool {
+ for _, v := range methods {
+ if v == method {
+ return true
+ }
+ }
+ return false
+}
+
+// MethodsFor returns all methods available for path
+func (r *router) MethodsFor(path string) []string {
+ methods := []string{}
+ for _, route := range r.getRoutes() {
+ matches := route.regex.FindStringSubmatch(path)
+ if len(matches) > 0 && matches[0] == path && !hasMethod(methods, route.method) {
+ methods = append(methods, route.method)
+ }
+ }
+ return methods
+}
+
+type routeContext struct {
+ Context
+ index int
+ handlers []Handler
+}
+
+func (r *routeContext) Next() {
+ r.index += 1
+ r.run()
+}
+
+func (r *routeContext) run() {
+ for r.index < len(r.handlers) {
+ handler := r.handlers[r.index]
+ vals, err := r.Invoke(handler)
+ if err != nil {
+ panic(err)
+ }
+ r.index += 1
+
+ // if the handler returned something, write it to the http response
+ if len(vals) > 0 {
+ ev := r.Get(reflect.TypeOf(ReturnHandler(nil)))
+ handleReturn := ev.Interface().(ReturnHandler)
+ handleReturn(r, vals)
+ }
+
+ if r.Written() {
+ return
+ }
+ }
+}
diff --git a/vendor/github.com/go-martini/martini/static.go b/vendor/github.com/go-martini/martini/static.go
new file mode 100644
index 0000000..51af6cf
--- /dev/null
+++ b/vendor/github.com/go-martini/martini/static.go
@@ -0,0 +1,135 @@
+package martini
+
+import (
+ "log"
+ "net/http"
+ "net/url"
+ "path"
+ "path/filepath"
+ "strings"
+)
+
+// StaticOptions is a struct for specifying configuration options for the martini.Static middleware.
+type StaticOptions struct {
+ // Prefix is the optional prefix used to serve the static directory content
+ Prefix string
+ // SkipLogging will disable [Static] log messages when a static file is served.
+ SkipLogging bool
+ // IndexFile defines which file to serve as index if it exists.
+ IndexFile string
+ // Expires defines which user-defined function to use for producing a HTTP Expires Header
+ // https://developers.google.com/speed/docs/insights/LeverageBrowserCaching
+ Expires func() string
+ // Fallback defines a default URL to serve when the requested resource was
+ // not found.
+ Fallback string
+ // Exclude defines a pattern for URLs this handler should never process.
+ Exclude string
+}
+
+func prepareStaticOptions(options []StaticOptions) StaticOptions {
+ var opt StaticOptions
+ if len(options) > 0 {
+ opt = options[0]
+ }
+
+ // Defaults
+ if len(opt.IndexFile) == 0 {
+ opt.IndexFile = "index.html"
+ }
+ // Normalize the prefix if provided
+ if opt.Prefix != "" {
+ // Ensure we have a leading '/'
+ if opt.Prefix[0] != '/' {
+ opt.Prefix = "/" + opt.Prefix
+ }
+ // Remove any trailing '/'
+ opt.Prefix = strings.TrimRight(opt.Prefix, "/")
+ }
+ return opt
+}
+
+// Static returns a middleware handler that serves static files in the given directory.
+func Static(directory string, staticOpt ...StaticOptions) Handler {
+ if !filepath.IsAbs(directory) {
+ directory = filepath.Join(Root, directory)
+ }
+ dir := http.Dir(directory)
+ opt := prepareStaticOptions(staticOpt)
+
+ return func(res http.ResponseWriter, req *http.Request, log *log.Logger) {
+ if req.Method != "GET" && req.Method != "HEAD" {
+ return
+ }
+ if opt.Exclude != "" && strings.HasPrefix(req.URL.Path, opt.Exclude) {
+ return
+ }
+ file := req.URL.Path
+ // if we have a prefix, filter requests by stripping the prefix
+ if opt.Prefix != "" {
+ if !strings.HasPrefix(file, opt.Prefix) {
+ return
+ }
+ file = file[len(opt.Prefix):]
+ if file != "" && file[0] != '/' {
+ return
+ }
+ }
+ f, err := dir.Open(file)
+ if err != nil {
+ // try any fallback before giving up
+ if opt.Fallback != "" {
+ file = opt.Fallback // so that logging stays true
+ f, err = dir.Open(opt.Fallback)
+ }
+
+ if err != nil {
+ // discard the error?
+ return
+ }
+ }
+ defer f.Close()
+
+ fi, err := f.Stat()
+ if err != nil {
+ return
+ }
+
+ // try to serve index file
+ if fi.IsDir() {
+ // redirect if missing trailing slash
+ if !strings.HasSuffix(req.URL.Path, "/") {
+ dest := url.URL{
+ Path: req.URL.Path + "/",
+ RawQuery: req.URL.RawQuery,
+ Fragment: req.URL.Fragment,
+ }
+ http.Redirect(res, req, dest.String(), http.StatusFound)
+ return
+ }
+
+ file = path.Join(file, opt.IndexFile)
+ f, err = dir.Open(file)
+ if err != nil {
+ return
+ }
+ defer f.Close()
+
+ fi, err = f.Stat()
+ if err != nil || fi.IsDir() {
+ return
+ }
+ }
+
+ if !opt.SkipLogging {
+ log.Println("[Static] Serving " + file)
+ }
+
+ // Add an Expires header to the static content
+ if opt.Expires != nil {
+ res.Header().Set("Expires", opt.Expires())
+ }
+
+ http.ServeContent(res, req, file, fi.ModTime(), f)
+ }
+}
diff --git a/vendor/github.com/julienschmidt/httprouter/LICENSE b/vendor/github.com/julienschmidt/httprouter/LICENSE
new file mode 100644
index 0000000..b829abc
--- /dev/null
+++ b/vendor/github.com/julienschmidt/httprouter/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2013 Julien Schmidt. All rights reserved.
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * The names of the contributors may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL JULIEN SCHMIDT BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file
diff --git a/vendor/github.com/julienschmidt/httprouter/path.go b/vendor/github.com/julienschmidt/httprouter/path.go
new file mode 100644
index 0000000..486134d
--- /dev/null
+++ b/vendor/github.com/julienschmidt/httprouter/path.go
@@ -0,0 +1,123 @@
+// Copyright 2013 Julien Schmidt. All rights reserved.
+// Based on the path package, Copyright 2009 The Go Authors.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+package httprouter
+
+// CleanPath is the URL version of path.Clean, it returns a canonical URL path
+// for p, eliminating . and .. elements.
+//
+// The following rules are applied iteratively until no further processing can
+// be done:
+// 1. Replace multiple slashes with a single slash.
+// 2. Eliminate each . path name element (the current directory).
+// 3. Eliminate each inner .. path name element (the parent directory)
+// along with the non-.. element that precedes it.
+// 4. Eliminate .. elements that begin a rooted path:
+// that is, replace "/.." by "/" at the beginning of a path.
+//
+// If the result of this process is an empty string, "/" is returned
+func CleanPath(p string) string {
+ // Turn empty string into "/"
+ if p == "" {
+ return "/"
+ }
+
+ n := len(p)
+ var buf []byte
+
+ // Invariants:
+ // reading from path; r is index of next byte to process.
+ // writing to buf; w is index of next byte to write.
+
+ // path must start with '/'
+ r := 1
+ w := 1
+
+ if p[0] != '/' {
+ r = 0
+ buf = make([]byte, n+1)
+ buf[0] = '/'
+ }
+
+ trailing := n > 2 && p[n-1] == '/'
+
+ // A bit more clunky without a 'lazybuf' like the path package, but the loop
+ // gets completely inlined (bufApp). So in contrast to the path package this
+ // loop has no expensive function calls (except 1x make)
+
+ for r < n {
+ switch {
+ case p[r] == '/':
+ // empty path element, trailing slash is added after the end
+ r++
+
+ case p[r] == '.' && r+1 == n:
+ trailing = true
+ r++
+
+ case p[r] == '.' && p[r+1] == '/':
+ // . element
+ r++
+
+ case p[r] == '.' && p[r+1] == '.' && (r+2 == n || p[r+2] == '/'):
+ // .. element: remove to last /
+ r += 2
+
+ if w > 1 {
+ // can backtrack
+ w--
+
+ if buf == nil {
+ for w > 1 && p[w] != '/' {
+ w--
+ }
+ } else {
+ for w > 1 && buf[w] != '/' {
+ w--
+ }
+ }
+ }
+
+ default:
+ // real path element.
+ // add slash if needed
+ if w > 1 {
+ bufApp(&buf, p, w, '/')
+ w++
+ }
+
+ // copy element
+ for r < n && p[r] != '/' {
+ bufApp(&buf, p, w, p[r])
+ w++
+ r++
+ }
+ }
+ }
+
+ // re-append trailing slash
+ if trailing && w > 1 {
+ bufApp(&buf, p, w, '/')
+ w++
+ }
+
+ if buf == nil {
+ return p[:w]
+ }
+ return string(buf[:w])
+}
+
+// internal helper to lazily create a buffer if necessary
+func bufApp(buf *[]byte, s string, w int, c byte) {
+ if *buf == nil {
+ if s[w] == c {
+ return
+ }
+
+ *buf = make([]byte, len(s))
+ copy(*buf, s[:w])
+ }
+ (*buf)[w] = c
+}
diff --git a/vendor/github.com/julienschmidt/httprouter/router.go b/vendor/github.com/julienschmidt/httprouter/router.go
new file mode 100644
index 0000000..bb17330
--- /dev/null
+++ b/vendor/github.com/julienschmidt/httprouter/router.go
@@ -0,0 +1,411 @@
+// Copyright 2013 Julien Schmidt. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+// Package httprouter is a trie based high performance HTTP request router.
+//
+// A trivial example is:
+//
+// package main
+//
+// import (
+// "fmt"
+// "github.com/julienschmidt/httprouter"
+// "net/http"
+// "log"
+// )
+//
+// func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+// fmt.Fprint(w, "Welcome!\n")
+// }
+//
+// func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+// fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
+// }
+//
+// func main() {
+// router := httprouter.New()
+// router.GET("/", Index)
+// router.GET("/hello/:name", Hello)
+//
+// log.Fatal(http.ListenAndServe(":8080", router))
+// }
+//
+// The router matches incoming requests by the request method and the path.
+// If a handle is registered for this path and method, the router delegates the
+// request to that function.
+// For the methods GET, POST, PUT, PATCH and DELETE shortcut functions exist to
+// register handles, for all other methods router.Handle can be used.
+//
+// The registered path, against which the router matches incoming requests, can
+// contain two types of parameters:
+// Syntax Type
+// :name named parameter
+// *name catch-all parameter
+//
+// Named parameters are dynamic path segments. They match anything until the
+// next '/' or the path end:
+// Path: /blog/:category/:post
+//
+// Requests:
+// /blog/go/request-routers match: category="go", post="request-routers"
+// /blog/go/request-routers/ no match, but the router would redirect
+// /blog/go/ no match
+// /blog/go/request-routers/comments no match
+//
+// Catch-all parameters match anything until the path end, including the
+// directory index (the '/' before the catch-all). Since they match anything
+// until the end, catch-all parameters must always be the final path element.
+// Path: /files/*filepath
+//
+// Requests:
+// /files/ match: filepath="/"
+// /files/LICENSE match: filepath="/LICENSE"
+// /files/templates/article.html match: filepath="/templates/article.html"
+// /files no match, but the router would redirect
+//
+// The value of parameters is saved as a slice of the Param struct, consisting
+// each of a key and a value. The slice is passed to the Handle func as a third
+// parameter.
+// There are two ways to retrieve the value of a parameter:
+// // by the name of the parameter
+// user := ps.ByName("user") // defined by :user or *user
+//
+// // by the index of the parameter. This way you can also get the name (key)
+// thirdKey := ps[2].Key // the name of the 3rd parameter
+// thirdValue := ps[2].Value // the value of the 3rd parameter
+package httprouter
+
+import (
+ "net/http"
+)
+
+// Handle is a function that can be registered to a route to handle HTTP
+// requests. Like http.HandlerFunc, but has a third parameter for the values of
+// wildcards (variables).
+type Handle func(http.ResponseWriter, *http.Request, Params)
+
+// Param is a single URL parameter, consisting of a key and a value.
+type Param struct {
+ Key string
+ Value string
+}
+
+// Params is a Param-slice, as returned by the router.
+// The slice is ordered, the first URL parameter is also the first slice value.
+// It is therefore safe to read values by the index.
+type Params []Param
+
+// ByName returns the value of the first Param which key matches the given name.
+// If no matching Param is found, an empty string is returned.
+func (ps Params) ByName(name string) string {
+ for i := range ps {
+ if ps[i].Key == name {
+ return ps[i].Value
+ }
+ }
+ return ""
+}
+
+// Router is a http.Handler which can be used to dispatch requests to different
+// handler functions via configurable routes
+type Router struct {
+ trees map[string]*node
+
+ // Enables automatic redirection if the current route can't be matched but a
+ // handler for the path with (without) the trailing slash exists.
+ // For example if /foo/ is requested but a route only exists for /foo, the
+ // client is redirected to /foo with http status code 301 for GET requests
+ // and 307 for all other request methods.
+ RedirectTrailingSlash bool
+
+ // If enabled, the router tries to fix the current request path, if no
+ // handle is registered for it.
+ // First superfluous path elements like ../ or // are removed.
+ // Afterwards the router does a case-insensitive lookup of the cleaned path.
+ // If a handle can be found for this route, the router makes a redirection
+ // to the corrected path with status code 301 for GET requests and 307 for
+ // all other request methods.
+ // For example /FOO and /..//Foo could be redirected to /foo.
+ // RedirectTrailingSlash is independent of this option.
+ RedirectFixedPath bool
+
+ // If enabled, the router checks if another method is allowed for the
+ // current route, if the current request can not be routed.
+ // If this is the case, the request is answered with 'Method Not Allowed'
+ // and HTTP status code 405.
+ // If no other Method is allowed, the request is delegated to the NotFound
+ // handler.
+ HandleMethodNotAllowed bool
+
+ // If enabled, the router automatically replies to OPTIONS requests.
+ // Custom OPTIONS handlers take priority over automatic replies.
+ HandleOPTIONS bool
+
+ // Configurable http.Handler which is called when no matching route is
+ // found. If it is not set, http.NotFound is used.
+ NotFound http.Handler
+
+ // Configurable http.Handler which is called when a request
+ // cannot be routed and HandleMethodNotAllowed is true.
+ // If it is not set, http.Error with http.StatusMethodNotAllowed is used.
+ // The "Allow" header with allowed request methods is set before the handler
+ // is called.
+ MethodNotAllowed http.Handler
+
+ // Function to handle panics recovered from http handlers.
+ // It should be used to generate a error page and return the http error code
+ // 500 (Internal Server Error).
+ // The handler can be used to keep your server from crashing because of
+ // unrecovered panics.
+ PanicHandler func(http.ResponseWriter, *http.Request, interface{})
+}
+
+// Make sure the Router conforms with the http.Handler interface
+var _ http.Handler = New()
+
+// New returns a new initialized Router.
+// Path auto-correction, including trailing slashes, is enabled by default.
+func New() *Router {
+ return &Router{
+ RedirectTrailingSlash: true,
+ RedirectFixedPath: true,
+ HandleMethodNotAllowed: true,
+ HandleOPTIONS: true,
+ }
+}
+
+// GET is a shortcut for router.Handle("GET", path, handle)
+func (r *Router) GET(path string, handle Handle) {
+ r.Handle("GET", path, handle)
+}
+
+// HEAD is a shortcut for router.Handle("HEAD", path, handle)
+func (r *Router) HEAD(path string, handle Handle) {
+ r.Handle("HEAD", path, handle)
+}
+
+// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle)
+func (r *Router) OPTIONS(path string, handle Handle) {
+ r.Handle("OPTIONS", path, handle)
+}
+
+// POST is a shortcut for router.Handle("POST", path, handle)
+func (r *Router) POST(path string, handle Handle) {
+ r.Handle("POST", path, handle)
+}
+
+// PUT is a shortcut for router.Handle("PUT", path, handle)
+func (r *Router) PUT(path string, handle Handle) {
+ r.Handle("PUT", path, handle)
+}
+
+// PATCH is a shortcut for router.Handle("PATCH", path, handle)
+func (r *Router) PATCH(path string, handle Handle) {
+ r.Handle("PATCH", path, handle)
+}
+
+// DELETE is a shortcut for router.Handle("DELETE", path, handle)
+func (r *Router) DELETE(path string, handle Handle) {
+ r.Handle("DELETE", path, handle)
+}
+
+// Handle registers a new request handle with the given path and method.
+//
+// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
+// functions can be used.
+//
+// This function is intended for bulk loading and to allow the usage of less
+// frequently used, non-standardized or custom methods (e.g. for internal
+// communication with a proxy).
+func (r *Router) Handle(method, path string, handle Handle) {
+ if path[0] != '/' {
+ panic("path must begin with '/' in path '" + path + "'")
+ }
+
+ if r.trees == nil {
+ r.trees = make(map[string]*node)
+ }
+
+ root := r.trees[method]
+ if root == nil {
+ root = new(node)
+ r.trees[method] = root
+ }
+
+ root.addRoute(path, handle)
+}
+
+// Handler is an adapter which allows the usage of an http.Handler as a
+// request handle.
+func (r *Router) Handler(method, path string, handler http.Handler) {
+ r.Handle(method, path,
+ func(w http.ResponseWriter, req *http.Request, _ Params) {
+ handler.ServeHTTP(w, req)
+ },
+ )
+}
+
+// HandlerFunc is an adapter which allows the usage of an http.HandlerFunc as a
+// request handle.
+func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc) {
+ r.Handler(method, path, handler)
+}
+
+// ServeFiles serves files from the given file system root.
+// The path must end with "/*filepath", files are then served from the local
+// path /defined/root/dir/*filepath.
+// For example if root is "/etc" and *filepath is "passwd", the local file
+// "/etc/passwd" would be served.
+// Internally a http.FileServer is used, therefore http.NotFound is used instead
+// of the Router's NotFound handler.
+// To use the operating system's file system implementation,
+// use http.Dir:
+// router.ServeFiles("/src/*filepath", http.Dir("/var/www"))
+func (r *Router) ServeFiles(path string, root http.FileSystem) {
+ if len(path) < 10 || path[len(path)-10:] != "/*filepath" {
+ panic("path must end with /*filepath in path '" + path + "'")
+ }
+
+ fileServer := http.FileServer(root)
+
+ r.GET(path, func(w http.ResponseWriter, req *http.Request, ps Params) {
+ req.URL.Path = ps.ByName("filepath")
+ fileServer.ServeHTTP(w, req)
+ })
+}
+
+func (r *Router) recv(w http.ResponseWriter, req *http.Request) {
+ if rcv := recover(); rcv != nil {
+ r.PanicHandler(w, req, rcv)
+ }
+}
+
+// Lookup allows the manual lookup of a method + path combo.
+// This is e.g. useful to build a framework around this router.
+// If the path was found, it returns the handle function and the path parameter
+// values. Otherwise the third return value indicates whether a redirection to
+// the same path with an extra / without the trailing slash should be performed.
+func (r *Router) Lookup(method, path string) (Handle, Params, bool) {
+ if root := r.trees[method]; root != nil {
+ return root.getValue(path)
+ }
+ return nil, nil, false
+}
+
+func (r *Router) allowed(path, reqMethod string) (allow string) {
+ if path == "*" { // server-wide
+ for method := range r.trees {
+ if method == "OPTIONS" {
+ continue
+ }
+
+ // add request method to list of allowed methods
+ if len(allow) == 0 {
+ allow = method
+ } else {
+ allow += ", " + method
+ }
+ }
+ } else { // specific path
+ for method := range r.trees {
+ // Skip the requested method - we already tried this one
+ if method == reqMethod || method == "OPTIONS" {
+ continue
+ }
+
+ handle, _, _ := r.trees[method].getValue(path)
+ if handle != nil {
+ // add request method to list of allowed methods
+ if len(allow) == 0 {
+ allow = method
+ } else {
+ allow += ", " + method
+ }
+ }
+ }
+ }
+ if len(allow) > 0 {
+ allow += ", OPTIONS"
+ }
+ return
+}
+
+// ServeHTTP makes the router implement the http.Handler interface.
+func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+ if r.PanicHandler != nil {
+ defer r.recv(w, req)
+ }
+
+ path := req.URL.Path
+
+ if root := r.trees[req.Method]; root != nil {
+ if handle, ps, tsr := root.getValue(path); handle != nil {
+ handle(w, req, ps)
+ return
+ } else if req.Method != "CONNECT" && path != "/" {
+ code := 301 // Permanent redirect, request with GET method
+ if req.Method != "GET" {
+ // Temporary redirect, request with same method
+ // As of Go 1.3, Go does not support status code 308.
+ code = 307
+ }
+
+ if tsr && r.RedirectTrailingSlash {
+ if len(path) > 1 && path[len(path)-1] == '/' {
+ req.URL.Path = path[:len(path)-1]
+ } else {
+ req.URL.Path = path + "/"
+ }
+ http.Redirect(w, req, req.URL.String(), code)
+ return
+ }
+
+ // Try to fix the request path
+ if r.RedirectFixedPath {
+ fixedPath, found := root.findCaseInsensitivePath(
+ CleanPath(path),
+ r.RedirectTrailingSlash,
+ )
+ if found {
+ req.URL.Path = string(fixedPath)
+ http.Redirect(w, req, req.URL.String(), code)
+ return
+ }
+ }
+ }
+ }
+
+ if req.Method == "OPTIONS" {
+ // Handle OPTIONS requests
+ if r.HandleOPTIONS {
+ if allow := r.allowed(path, req.Method); len(allow) > 0 {
+ w.Header().Set("Allow", allow)
+ return
+ }
+ }
+ } else {
+ // Handle 405
+ if r.HandleMethodNotAllowed {
+ if allow := r.allowed(path, req.Method); len(allow) > 0 {
+ w.Header().Set("Allow", allow)
+ if r.MethodNotAllowed != nil {
+ r.MethodNotAllowed.ServeHTTP(w, req)
+ } else {
+ http.Error(w,
+ http.StatusText(http.StatusMethodNotAllowed),
+ http.StatusMethodNotAllowed,
+ )
+ }
+ return
+ }
+ }
+ }
+
+ // Handle 404
+ if r.NotFound != nil {
+ r.NotFound.ServeHTTP(w, req)
+ } else {
+ http.NotFound(w, req)
+ }
+}
diff --git a/vendor/github.com/julienschmidt/httprouter/tree.go b/vendor/github.com/julienschmidt/httprouter/tree.go
new file mode 100644
index 0000000..474da00
--- /dev/null
+++ b/vendor/github.com/julienschmidt/httprouter/tree.go
@@ -0,0 +1,651 @@
+// Copyright 2013 Julien Schmidt. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be found
+// in the LICENSE file.
+
+package httprouter
+
+import (
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+func min(a, b int) int {
+ if a <= b {
+ return a
+ }
+ return b
+}
+
+func countParams(path string) uint8 {
+ var n uint
+ for i := 0; i < len(path); i++ {
+ if path[i] != ':' && path[i] != '*' {
+ continue
+ }
+ n++
+ }
+ if n >= 255 {
+ return 255
+ }
+ return uint8(n)
+}
+
+type nodeType uint8
+
+const (
+ static nodeType = iota // default
+ root
+ param
+ catchAll
+)
+
+type node struct {
+ path string
+ wildChild bool
+ nType nodeType
+ maxParams uint8
+ indices string
+ children []*node
+ handle Handle
+ priority uint32
+}
+
+// increments priority of the given child and reorders if necessary
+func (n *node) incrementChildPrio(pos int) int {
+ n.children[pos].priority++
+ prio := n.children[pos].priority
+
+ // adjust position (move to front)
+ newPos := pos
+ for newPos > 0 && n.children[newPos-1].priority < prio {
+ // swap node positions
+ n.children[newPos-1], n.children[newPos] = n.children[newPos], n.children[newPos-1]
+
+ newPos--
+ }
+
+ // build new index char string
+ if newPos != pos {
+ n.indices = n.indices[:newPos] + // unchanged prefix, might be empty
+ n.indices[pos:pos+1] + // the index char we move
+ n.indices[newPos:pos] + n.indices[pos+1:] // rest without char at 'pos'
+ }
+
+ return newPos
+}
+
+// addRoute adds a node with the given handle to the path.
+// Not concurrency-safe!
+func (n *node) addRoute(path string, handle Handle) {
+ fullPath := path
+ n.priority++
+ numParams := countParams(path)
+
+ // non-empty tree
+ if len(n.path) > 0 || len(n.children) > 0 {
+ walk:
+ for {
+ // Update maxParams of the current node
+ if numParams > n.maxParams {
+ n.maxParams = numParams
+ }
+
+ // Find the longest common prefix.
+ // This also implies that the common prefix contains no ':' or '*'
+ // since the existing key can't contain those chars.
+ i := 0
+ max := min(len(path), len(n.path))
+ for i < max && path[i] == n.path[i] {
+ i++
+ }
+
+ // Split edge
+ if i < len(n.path) {
+ child := node{
+ path: n.path[i:],
+ wildChild: n.wildChild,
+ nType: static,
+ indices: n.indices,
+ children: n.children,
+ handle: n.handle,
+ priority: n.priority - 1,
+ }
+
+ // Update maxParams (max of all children)
+ for i := range child.children {
+ if child.children[i].maxParams > child.maxParams {
+ child.maxParams = child.children[i].maxParams
+ }
+ }
+
+ n.children = []*node{&child}
+ // []byte for proper unicode char conversion, see #65
+ n.indices = string([]byte{n.path[i]})
+ n.path = path[:i]
+ n.handle = nil
+ n.wildChild = false
+ }
+
+ // Make new node a child of this node
+ if i < len(path) {
+ path = path[i:]
+
+ if n.wildChild {
+ n = n.children[0]
+ n.priority++
+
+ // Update maxParams of the child node
+ if numParams > n.maxParams {
+ n.maxParams = numParams
+ }
+ numParams--
+
+ // Check if the wildcard matches
+ if len(path) >= len(n.path) && n.path == path[:len(n.path)] &&
+ // Check for longer wildcard, e.g. :name and :names
+ (len(n.path) >= len(path) || path[len(n.path)] == '/') {
+ continue walk
+ } else {
+ // Wildcard conflict
+ pathSeg := strings.SplitN(path, "/", 2)[0]
+ prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path
+ panic("'" + pathSeg +
+ "' in new path '" + fullPath +
+ "' conflicts with existing wildcard '" + n.path +
+ "' in existing prefix '" + prefix +
+ "'")
+ }
+ }
+
+ c := path[0]
+
+ // slash after param
+ if n.nType == param && c == '/' && len(n.children) == 1 {
+ n = n.children[0]
+ n.priority++
+ continue walk
+ }
+
+ // Check if a child with the next path byte exists
+ for i := 0; i < len(n.indices); i++ {
+ if c == n.indices[i] {
+ i = n.incrementChildPrio(i)
+ n = n.children[i]
+ continue walk
+ }
+ }
+
+ // Otherwise insert it
+ if c != ':' && c != '*' {
+ // []byte for proper unicode char conversion, see #65
+ n.indices += string([]byte{c})
+ child := &node{
+ maxParams: numParams,
+ }
+ n.children = append(n.children, child)
+ n.incrementChildPrio(len(n.indices) - 1)
+ n = child
+ }
+ n.insertChild(numParams, path, fullPath, handle)
+ return
+
+ } else if i == len(path) { // Make node a (in-path) leaf
+ if n.handle != nil {
+ panic("a handle is already registered for path '" + fullPath + "'")
+ }
+ n.handle = handle
+ }
+ return
+ }
+ } else { // Empty tree
+ n.insertChild(numParams, path, fullPath, handle)
+ n.nType = root
+ }
+}
+
+func (n *node) insertChild(numParams uint8, path, fullPath string, handle Handle) {
+ var offset int // already handled bytes of the path
+
+ // find prefix until first wildcard (beginning with ':'' or '*'')
+ for i, max := 0, len(path); numParams > 0; i++ {
+ c := path[i]
+ if c != ':' && c != '*' {
+ continue
+ }
+
+ // find wildcard end (either '/' or path end)
+ end := i + 1
+ for end < max && path[end] != '/' {
+ switch path[end] {
+ // the wildcard name must not contain ':' and '*'
+ case ':', '*':
+ panic("only one wildcard per path segment is allowed, has: '" +
+ path[i:] + "' in path '" + fullPath + "'")
+ default:
+ end++
+ }
+ }
+
+ // check if this Node existing children which would be
+ // unreachable if we insert the wildcard here
+ if len(n.children) > 0 {
+ panic("wildcard route '" + path[i:end] +
+ "' conflicts with existing children in path '" + fullPath + "'")
+ }
+
+ // check if the wildcard has a name
+ if end-i < 2 {
+ panic("wildcards must be named with a non-empty name in path '" + fullPath + "'")
+ }
+
+ if c == ':' { // param
+ // split path at the beginning of the wildcard
+ if i > 0 {
+ n.path = path[offset:i]
+ offset = i
+ }
+
+ child := &node{
+ nType: param,
+ maxParams: numParams,
+ }
+ n.children = []*node{child}
+ n.wildChild = true
+ n = child
+ n.priority++
+ numParams--
+
+ // if the path doesn't end with the wildcard, then there
+ // will be another non-wildcard subpath starting with '/'
+ if end < max {
+ n.path = path[offset:end]
+ offset = end
+
+ child := &node{
+ maxParams: numParams,
+ priority: 1,
+ }
+ n.children = []*node{child}
+ n = child
+ }
+
+ } else { // catchAll
+ if end != max || numParams > 1 {
+ panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'")
+ }
+
+ if len(n.path) > 0 && n.path[len(n.path)-1] == '/' {
+ panic("catch-all conflicts with existing handle for the path segment root in path '" + fullPath + "'")
+ }
+
+ // currently fixed width 1 for '/'
+ i--
+ if path[i] != '/' {
+ panic("no / before catch-all in path '" + fullPath + "'")
+ }
+
+ n.path = path[offset:i]
+
+ // first node: catchAll node with empty path
+ child := &node{
+ wildChild: true,
+ nType: catchAll,
+ maxParams: 1,
+ }
+ n.children = []*node{child}
+ n.indices = string(path[i])
+ n = child
+ n.priority++
+
+ // second node: node holding the variable
+ child = &node{
+ path: path[i:],
+ nType: catchAll,
+ maxParams: 1,
+ handle: handle,
+ priority: 1,
+ }
+ n.children = []*node{child}
+
+ return
+ }
+ }
+
+ // insert remaining path part and handle to the leaf
+ n.path = path[offset:]
+ n.handle = handle
+}
+
+// Returns the handle registered with the given path (key). The values of
+// wildcards are saved to a map.
+// If no handle can be found, a TSR (trailing slash redirect) recommendation is
+// made if a handle exists with an extra (without the) trailing slash for the
+// given path.
+func (n *node) getValue(path string) (handle Handle, p Params, tsr bool) {
+walk: // outer loop for walking the tree
+ for {
+ if len(path) > len(n.path) {
+ if path[:len(n.path)] == n.path {
+ path = path[len(n.path):]
+ // If this node does not have a wildcard (param or catchAll)
+ // child, we can just look up the next child node and continue
+ // to walk down the tree
+ if !n.wildChild {
+ c := path[0]
+ for i := 0; i < len(n.indices); i++ {
+ if c == n.indices[i] {
+ n = n.children[i]
+ continue walk
+ }
+ }
+
+ // Nothing found.
+ // We can recommend to redirect to the same URL without a
+ // trailing slash if a leaf exists for that path.
+ tsr = (path == "/" && n.handle != nil)
+ return
+
+ }
+
+ // handle wildcard child
+ n = n.children[0]
+ switch n.nType {
+ case param:
+ // find param end (either '/' or path end)
+ end := 0
+ for end < len(path) && path[end] != '/' {
+ end++
+ }
+
+ // save param value
+ if p == nil {
+ // lazy allocation
+ p = make(Params, 0, n.maxParams)
+ }
+ i := len(p)
+ p = p[:i+1] // expand slice within preallocated capacity
+ p[i].Key = n.path[1:]
+ p[i].Value = path[:end]
+
+ // we need to go deeper!
+ if end < len(path) {
+ if len(n.children) > 0 {
+ path = path[end:]
+ n = n.children[0]
+ continue walk
+ }
+
+ // ... but we can't
+ tsr = (len(path) == end+1)
+ return
+ }
+
+ if handle = n.handle; handle != nil {
+ return
+ } else if len(n.children) == 1 {
+ // No handle found. Check if a handle for this path + a
+ // trailing slash exists for TSR recommendation
+ n = n.children[0]
+ tsr = (n.path == "/" && n.handle != nil)
+ }
+
+ return
+
+ case catchAll:
+ // save param value
+ if p == nil {
+ // lazy allocation
+ p = make(Params, 0, n.maxParams)
+ }
+ i := len(p)
+ p = p[:i+1] // expand slice within preallocated capacity
+ p[i].Key = n.path[2:]
+ p[i].Value = path
+
+ handle = n.handle
+ return
+
+ default:
+ panic("invalid node type")
+ }
+ }
+ } else if path == n.path {
+ // We should have reached the node containing the handle.
+ // Check if this node has a handle registered.
+ if handle = n.handle; handle != nil {
+ return
+ }
+
+ if path == "/" && n.wildChild && n.nType != root {
+ tsr = true
+ return
+ }
+
+ // No handle found. Check if a handle for this path + a
+ // trailing slash exists for trailing slash recommendation
+ for i := 0; i < len(n.indices); i++ {
+ if n.indices[i] == '/' {
+ n = n.children[i]
+ tsr = (len(n.path) == 1 && n.handle != nil) ||
+ (n.nType == catchAll && n.children[0].handle != nil)
+ return
+ }
+ }
+
+ return
+ }
+
+ // Nothing found. We can recommend to redirect to the same URL with an
+ // extra trailing slash if a leaf exists for that path
+ tsr = (path == "/") ||
+ (len(n.path) == len(path)+1 && n.path[len(path)] == '/' &&
+ path == n.path[:len(n.path)-1] && n.handle != nil)
+ return
+ }
+}
+
+// Makes a case-insensitive lookup of the given path and tries to find a handler.
+// It can optionally also fix trailing slashes.
+// It returns the case-corrected path and a bool indicating whether the lookup
+// was successful.
+func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) (ciPath []byte, found bool) {
+ return n.findCaseInsensitivePathRec(
+ path,
+ strings.ToLower(path),
+ make([]byte, 0, len(path)+1), // preallocate enough memory for new path
+ [4]byte{}, // empty rune buffer
+ fixTrailingSlash,
+ )
+}
+
+// shift bytes in array by n bytes left
+func shiftNRuneBytes(rb [4]byte, n int) [4]byte {
+ switch n {
+ case 0:
+ return rb
+ case 1:
+ return [4]byte{rb[1], rb[2], rb[3], 0}
+ case 2:
+ return [4]byte{rb[2], rb[3]}
+ case 3:
+ return [4]byte{rb[3]}
+ default:
+ return [4]byte{}
+ }
+}
+
+// recursive case-insensitive lookup function used by n.findCaseInsensitivePath
+func (n *node) findCaseInsensitivePathRec(path, loPath string, ciPath []byte, rb [4]byte, fixTrailingSlash bool) ([]byte, bool) {
+ loNPath := strings.ToLower(n.path)
+
+walk: // outer loop for walking the tree
+ for len(loPath) >= len(loNPath) && (len(loNPath) == 0 || loPath[1:len(loNPath)] == loNPath[1:]) {
+ // add common path to result
+ ciPath = append(ciPath, n.path...)
+
+ if path = path[len(n.path):]; len(path) > 0 {
+ loOld := loPath
+ loPath = loPath[len(loNPath):]
+
+ // If this node does not have a wildcard (param or catchAll) child,
+ // we can just look up the next child node and continue to walk down
+ // the tree
+ if !n.wildChild {
+ // skip rune bytes already processed
+ rb = shiftNRuneBytes(rb, len(loNPath))
+
+ if rb[0] != 0 {
+ // old rune not finished
+ for i := 0; i < len(n.indices); i++ {
+ if n.indices[i] == rb[0] {
+ // continue with child node
+ n = n.children[i]
+ loNPath = strings.ToLower(n.path)
+ continue walk
+ }
+ }
+ } else {
+ // process a new rune
+ var rv rune
+
+ // find rune start
+ // runes are up to 4 byte long,
+ // -4 would definitely be another rune
+ var off int
+ for max := min(len(loNPath), 3); off < max; off++ {
+ if i := len(loNPath) - off; utf8.RuneStart(loOld[i]) {
+ // read rune from cached lowercase path
+ rv, _ = utf8.DecodeRuneInString(loOld[i:])
+ break
+ }
+ }
+
+ // calculate lowercase bytes of current rune
+ utf8.EncodeRune(rb[:], rv)
+ // skipp already processed bytes
+ rb = shiftNRuneBytes(rb, off)
+
+ for i := 0; i < len(n.indices); i++ {
+ // lowercase matches
+ if n.indices[i] == rb[0] {
+ // must use a recursive approach since both the
+ // uppercase byte and the lowercase byte might exist
+ // as an index
+ if out, found := n.children[i].findCaseInsensitivePathRec(
+ path, loPath, ciPath, rb, fixTrailingSlash,
+ ); found {
+ return out, true
+ }
+ break
+ }
+ }
+
+ // same for uppercase rune, if it differs
+ if up := unicode.ToUpper(rv); up != rv {
+ utf8.EncodeRune(rb[:], up)
+ rb = shiftNRuneBytes(rb, off)
+
+ for i := 0; i < len(n.indices); i++ {
+ // uppercase matches
+ if n.indices[i] == rb[0] {
+ // continue with child node
+ n = n.children[i]
+ loNPath = strings.ToLower(n.path)
+ continue walk
+ }
+ }
+ }
+ }
+
+ // Nothing found. We can recommend to redirect to the same URL
+ // without a trailing slash if a leaf exists for that path
+ return ciPath, (fixTrailingSlash && path == "/" && n.handle != nil)
+ }
+
+ n = n.children[0]
+ switch n.nType {
+ case param:
+ // find param end (either '/' or path end)
+ k := 0
+ for k < len(path) && path[k] != '/' {
+ k++
+ }
+
+ // add param value to case insensitive path
+ ciPath = append(ciPath, path[:k]...)
+
+ // we need to go deeper!
+ if k < len(path) {
+ if len(n.children) > 0 {
+ // continue with child node
+ n = n.children[0]
+ loNPath = strings.ToLower(n.path)
+ loPath = loPath[k:]
+ path = path[k:]
+ continue
+ }
+
+ // ... but we can't
+ if fixTrailingSlash && len(path) == k+1 {
+ return ciPath, true
+ }
+ return ciPath, false
+ }
+
+ if n.handle != nil {
+ return ciPath, true
+ } else if fixTrailingSlash && len(n.children) == 1 {
+ // No handle found. Check if a handle for this path + a
+ // trailing slash exists
+ n = n.children[0]
+ if n.path == "/" && n.handle != nil {
+ return append(ciPath, '/'), true
+ }
+ }
+ return ciPath, false
+
+ case catchAll:
+ return append(ciPath, path...), true
+
+ default:
+ panic("invalid node type")
+ }
+ } else {
+ // We should have reached the node containing the handle.
+ // Check if this node has a handle registered.
+ if n.handle != nil {
+ return ciPath, true
+ }
+
+ // No handle found.
+ // Try to fix the path by adding a trailing slash
+ if fixTrailingSlash {
+ for i := 0; i < len(n.indices); i++ {
+ if n.indices[i] == '/' {
+ n = n.children[i]
+ if (len(n.path) == 1 && n.handle != nil) ||
+ (n.nType == catchAll && n.children[0].handle != nil) {
+ return append(ciPath, '/'), true
+ }
+ return ciPath, false
+ }
+ }
+ }
+ return ciPath, false
+ }
+ }
+
+ // Nothing found.
+ // Try to fix the path by adding / removing a trailing slash
+ if fixTrailingSlash {
+ if path == "/" {
+ return ciPath, true
+ }
+ if len(loPath)+1 == len(loNPath) && loNPath[len(loPath)] == '/' &&
+ loPath[1:] == loNPath[1:len(loPath)] && n.handle != nil {
+ return append(ciPath, n.path...), true
+ }
+ }
+ return ciPath, false
+}
diff --git a/vendor/github.com/justinas/alice/LICENSE b/vendor/github.com/justinas/alice/LICENSE
new file mode 100644
index 0000000..0d0d352
--- /dev/null
+++ b/vendor/github.com/justinas/alice/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Justinas Stankevicius
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/justinas/alice/chain.go b/vendor/github.com/justinas/alice/chain.go
new file mode 100644
index 0000000..da0e2b5
--- /dev/null
+++ b/vendor/github.com/justinas/alice/chain.go
@@ -0,0 +1,112 @@
+// Package alice provides a convenient way to chain http handlers.
+package alice
+
+import "net/http"
+
+// A constructor for a piece of middleware.
+// Some middleware use this constructor out of the box,
+// so in most cases you can just pass somepackage.New
+type Constructor func(http.Handler) http.Handler
+
+// Chain acts as a list of http.Handler constructors.
+// Chain is effectively immutable:
+// once created, it will always hold
+// the same set of constructors in the same order.
+type Chain struct {
+ constructors []Constructor
+}
+
+// New creates a new chain,
+// memorizing the given list of middleware constructors.
+// New serves no other function,
+// constructors are only called upon a call to Then().
+func New(constructors ...Constructor) Chain {
+ return Chain{append(([]Constructor)(nil), constructors...)}
+}
+
+// Then chains the middleware and returns the final http.Handler.
+// New(m1, m2, m3).Then(h)
+// is equivalent to:
+// m1(m2(m3(h)))
+// When the request comes in, it will be passed to m1, then m2, then m3
+// and finally, the given handler
+// (assuming every middleware calls the following one).
+//
+// A chain can be safely reused by calling Then() several times.
+// stdStack := alice.New(ratelimitHandler, csrfHandler)
+// indexPipe = stdStack.Then(indexHandler)
+// authPipe = stdStack.Then(authHandler)
+// Note that constructors are called on every call to Then()
+// and thus several instances of the same middleware will be created
+// when a chain is reused in this way.
+// For proper middleware, this should cause no problems.
+//
+// Then() treats nil as http.DefaultServeMux.
+func (c Chain) Then(h http.Handler) http.Handler {
+ if h == nil {
+ h = http.DefaultServeMux
+ }
+
+ for i := range c.constructors {
+ h = c.constructors[len(c.constructors)-1-i](h)
+ }
+
+ return h
+}
+
+// ThenFunc works identically to Then, but takes
+// a HandlerFunc instead of a Handler.
+//
+// The following two statements are equivalent:
+// c.Then(http.HandlerFunc(fn))
+// c.ThenFunc(fn)
+//
+// ThenFunc provides all the guarantees of Then.
+func (c Chain) ThenFunc(fn http.HandlerFunc) http.Handler {
+ if fn == nil {
+ return c.Then(nil)
+ }
+ return c.Then(fn)
+}
+
+// Append extends a chain, adding the specified constructors
+// as the last ones in the request flow.
+//
+// Append returns a new chain, leaving the original one untouched.
+//
+// stdChain := alice.New(m1, m2)
+// extChain := stdChain.Append(m3, m4)
+// // requests in stdChain go m1 -> m2
+// // requests in extChain go m1 -> m2 -> m3 -> m4
+func (c Chain) Append(constructors ...Constructor) Chain {
+ newCons := make([]Constructor, 0, len(c.constructors)+len(constructors))
+ newCons = append(newCons, c.constructors...)
+ newCons = append(newCons, constructors...)
+
+ return Chain{newCons}
+}
+
+// Extend extends a chain by adding the specified chain
+// as the last one in the request flow.
+//
+// Extend returns a new chain, leaving the original one untouched.
+//
+// stdChain := alice.New(m1, m2)
+// ext1Chain := alice.New(m3, m4)
+// ext2Chain := stdChain.Extend(ext1Chain)
+// // requests in stdChain go m1 -> m2
+// // requests in ext1Chain go m3 -> m4
+// // requests in ext2Chain go m1 -> m2 -> m3 -> m4
+//
+// Another example:
+// aHtmlAfterNosurf := alice.New(m2)
+// aHtml := alice.New(m1, func(h http.Handler) http.Handler {
+// csrf := nosurf.New(h)
+// csrf.SetFailureHandler(aHtmlAfterNosurf.ThenFunc(csrfFail))
+// return csrf
+// }).Extend(aHtmlAfterNosurf)
+// // requests to aHtml hitting nosurfs success handler go m1 -> nosurf -> m2 -> target-handler
+// // requests to aHtml hitting nosurfs failure handler go m1 -> nosurf -> m2 -> csrfFail
+func (c Chain) Extend(chain Chain) Chain {
+ return c.Append(chain.constructors...)
+}
diff --git a/vendor/github.com/martini-contrib/render/LICENSE b/vendor/github.com/martini-contrib/render/LICENSE
new file mode 100644
index 0000000..eb68a0e
--- /dev/null
+++ b/vendor/github.com/martini-contrib/render/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 Jeremy Saenz
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/martini-contrib/render/render.go b/vendor/github.com/martini-contrib/render/render.go
new file mode 100644
index 0000000..0cacb97
--- /dev/null
+++ b/vendor/github.com/martini-contrib/render/render.go
@@ -0,0 +1,386 @@
+// Package render is a middleware for Martini that provides easy JSON serialization and HTML template rendering.
+//
+// package main
+//
+// import (
+// "encoding/xml"
+//
+// "github.com/go-martini/martini"
+// "github.com/martini-contrib/render"
+// )
+//
+// type Greeting struct {
+// XMLName xml.Name `xml:"greeting"`
+// One string `xml:"one,attr"`
+// Two string `xml:"two,attr"`
+// }
+//
+// func main() {
+// m := martini.Classic()
+// m.Use(render.Renderer()) // reads "templates" directory by default
+//
+// m.Get("/html", func(r render.Render) {
+// r.HTML(200, "mytemplate", nil)
+// })
+//
+// m.Get("/json", func(r render.Render) {
+// r.JSON(200, "hello world")
+// })
+//
+// m.Get("/xml", func(r render.Render) {
+// r.XML(200, Greeting{One: "hello", Two: "world"})
+// })
+//
+// m.Run()
+// }
+package render
+
+import (
+ "bytes"
+ "encoding/json"
+ "encoding/xml"
+ "fmt"
+ "html/template"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/oxtoacart/bpool"
+
+ "github.com/go-martini/martini"
+)
+
+const (
+ ContentType = "Content-Type"
+ ContentLength = "Content-Length"
+ ContentBinary = "application/octet-stream"
+ ContentText = "text/plain"
+ ContentJSON = "application/json"
+ ContentHTML = "text/html"
+ ContentXHTML = "application/xhtml+xml"
+ ContentXML = "text/xml"
+ defaultCharset = "UTF-8"
+)
+
+// Provides a temporary buffer to execute templates into and catch errors.
+var bufpool *bpool.BufferPool
+
+// Included helper functions for use when rendering html
+var helperFuncs = template.FuncMap{
+ "yield": func() (string, error) {
+ return "", fmt.Errorf("yield called with no layout defined")
+ },
+ "current": func() (string, error) {
+ return "", nil
+ },
+}
+
+// Render is a service that can be injected into a Martini handler. Render provides functions for easily writing JSON and
+// HTML templates out to a http Response.
+type Render interface {
+ // JSON writes the given status and JSON serialized version of the given value to the http.ResponseWriter.
+ JSON(status int, v interface{})
+ // HTML renders a html template specified by the name and writes the result and given status to the http.ResponseWriter.
+ HTML(status int, name string, v interface{}, htmlOpt ...HTMLOptions)
+ // XML writes the given status and XML serialized version of the given value to the http.ResponseWriter.
+ XML(status int, v interface{})
+ // Data writes the raw byte array to the http.ResponseWriter.
+ Data(status int, v []byte)
+ // Text writes the given status and plain text to the http.ResponseWriter.
+ Text(status int, v string)
+ // Error is a convenience function that writes an http status to the http.ResponseWriter.
+ Error(status int)
+ // Status is an alias for Error (writes an http status to the http.ResponseWriter)
+ Status(status int)
+ // Redirect is a convienience function that sends an HTTP redirect. If status is omitted, uses 302 (Found)
+ Redirect(location string, status ...int)
+ // Template returns the internal *template.Template used to render the HTML
+ Template() *template.Template
+ // Header exposes the header struct from http.ResponseWriter.
+ Header() http.Header
+}
+
+// Delims represents a set of Left and Right delimiters for HTML template rendering
+type Delims struct {
+ // Left delimiter, defaults to {{
+ Left string
+ // Right delimiter, defaults to }}
+ Right string
+}
+
+// Options is a struct for specifying configuration options for the render.Renderer middleware
+type Options struct {
+ // Directory to load templates. Default is "templates"
+ Directory string
+ // Layout template name. Will not render a layout if "". Defaults to "".
+ Layout string
+ // Extensions to parse template files from. Defaults to [".tmpl"]
+ Extensions []string
+ // Funcs is a slice of FuncMaps to apply to the template upon compilation. This is useful for helper functions. Defaults to [].
+ Funcs []template.FuncMap
+ // Delims sets the action delimiters to the specified strings in the Delims struct.
+ Delims Delims
+ // Appends the given charset to the Content-Type header. Default is "UTF-8".
+ Charset string
+ // Outputs human readable JSON
+ IndentJSON bool
+ // Outputs human readable XML
+ IndentXML bool
+ // Prefixes the JSON output with the given bytes.
+ PrefixJSON []byte
+ // Prefixes the XML output with the given bytes.
+ PrefixXML []byte
+ // Allows changing of output to XHTML instead of HTML. Default is "text/html"
+ HTMLContentType string
+}
+
+// HTMLOptions is a struct for overriding some rendering Options for specific HTML call
+type HTMLOptions struct {
+ // Layout template name. Overrides Options.Layout.
+ Layout string
+}
+
+// Renderer is a Middleware that maps a render.Render service into the Martini handler chain. An single variadic render.Options
+// struct can be optionally provided to configure HTML rendering. The default directory for templates is "templates" and the default
+// file extension is ".tmpl".
+//
+// If MARTINI_ENV is set to "" or "development" then templates will be recompiled on every request. For more performance, set the
+// MARTINI_ENV environment variable to "production"
+func Renderer(options ...Options) martini.Handler {
+ opt := prepareOptions(options)
+ cs := prepareCharset(opt.Charset)
+ t := compile(opt)
+ bufpool = bpool.NewBufferPool(64)
+ return func(res http.ResponseWriter, req *http.Request, c martini.Context) {
+ var tc *template.Template
+ if martini.Env == martini.Dev {
+ // recompile for easy development
+ tc = compile(opt)
+ } else {
+ // use a clone of the initial template
+ tc, _ = t.Clone()
+ }
+ c.MapTo(&renderer{res, req, tc, opt, cs}, (*Render)(nil))
+ }
+}
+
+func prepareCharset(charset string) string {
+ if len(charset) != 0 {
+ return "; charset=" + charset
+ }
+
+ return "; charset=" + defaultCharset
+}
+
+func prepareOptions(options []Options) Options {
+ var opt Options
+ if len(options) > 0 {
+ opt = options[0]
+ }
+
+ // Defaults
+ if len(opt.Directory) == 0 {
+ opt.Directory = "templates"
+ }
+ if len(opt.Extensions) == 0 {
+ opt.Extensions = []string{".tmpl"}
+ }
+ if len(opt.HTMLContentType) == 0 {
+ opt.HTMLContentType = ContentHTML
+ }
+
+ return opt
+}
+
+func compile(options Options) *template.Template {
+ dir := options.Directory
+ t := template.New(dir)
+ t.Delims(options.Delims.Left, options.Delims.Right)
+ // parse an initial template in case we don't have any
+ template.Must(t.Parse("Martini"))
+
+ filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
+ r, err := filepath.Rel(dir, path)
+ if err != nil {
+ return err
+ }
+
+ ext := getExt(r)
+
+ for _, extension := range options.Extensions {
+ if ext == extension {
+
+ buf, err := ioutil.ReadFile(path)
+ if err != nil {
+ panic(err)
+ }
+
+ name := (r[0 : len(r)-len(ext)])
+ tmpl := t.New(filepath.ToSlash(name))
+
+ // add our funcmaps
+ for _, funcs := range options.Funcs {
+ tmpl.Funcs(funcs)
+ }
+
+ // Bomb out if parse fails. We don't want any silent server starts.
+ template.Must(tmpl.Funcs(helperFuncs).Parse(string(buf)))
+ break
+ }
+ }
+
+ return nil
+ })
+
+ return t
+}
+
+func getExt(s string) string {
+ if strings.Index(s, ".") == -1 {
+ return ""
+ }
+ return "." + strings.Join(strings.Split(s, ".")[1:], ".")
+}
+
+type renderer struct {
+ http.ResponseWriter
+ req *http.Request
+ t *template.Template
+ opt Options
+ compiledCharset string
+}
+
+func (r *renderer) JSON(status int, v interface{}) {
+ var result []byte
+ var err error
+ if r.opt.IndentJSON {
+ result, err = json.MarshalIndent(v, "", " ")
+ } else {
+ result, err = json.Marshal(v)
+ }
+ if err != nil {
+ http.Error(r, err.Error(), 500)
+ return
+ }
+
+ // json rendered fine, write out the result
+ r.Header().Set(ContentType, ContentJSON+r.compiledCharset)
+ r.WriteHeader(status)
+ if len(r.opt.PrefixJSON) > 0 {
+ r.Write(r.opt.PrefixJSON)
+ }
+ r.Write(result)
+}
+
+func (r *renderer) HTML(status int, name string, binding interface{}, htmlOpt ...HTMLOptions) {
+ opt := r.prepareHTMLOptions(htmlOpt)
+ // assign a layout if there is one
+ if len(opt.Layout) > 0 {
+ r.addYield(name, binding)
+ name = opt.Layout
+ }
+
+ buf, err := r.execute(name, binding)
+ if err != nil {
+ http.Error(r, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ // template rendered fine, write out the result
+ r.Header().Set(ContentType, r.opt.HTMLContentType+r.compiledCharset)
+ r.WriteHeader(status)
+ io.Copy(r, buf)
+ bufpool.Put(buf)
+}
+
+func (r *renderer) XML(status int, v interface{}) {
+ var result []byte
+ var err error
+ if r.opt.IndentXML {
+ result, err = xml.MarshalIndent(v, "", " ")
+ } else {
+ result, err = xml.Marshal(v)
+ }
+ if err != nil {
+ http.Error(r, err.Error(), 500)
+ return
+ }
+
+ // XML rendered fine, write out the result
+ r.Header().Set(ContentType, ContentXML+r.compiledCharset)
+ r.WriteHeader(status)
+ if len(r.opt.PrefixXML) > 0 {
+ r.Write(r.opt.PrefixXML)
+ }
+ r.Write(result)
+}
+
+func (r *renderer) Data(status int, v []byte) {
+ if r.Header().Get(ContentType) == "" {
+ r.Header().Set(ContentType, ContentBinary)
+ }
+ r.WriteHeader(status)
+ r.Write(v)
+}
+
+func (r *renderer) Text(status int, v string) {
+ if r.Header().Get(ContentType) == "" {
+ r.Header().Set(ContentType, ContentText+r.compiledCharset)
+ }
+ r.WriteHeader(status)
+ r.Write([]byte(v))
+}
+
+// Error writes the given HTTP status to the current ResponseWriter
+func (r *renderer) Error(status int) {
+ r.WriteHeader(status)
+}
+
+func (r *renderer) Status(status int) {
+ r.WriteHeader(status)
+}
+
+func (r *renderer) Redirect(location string, status ...int) {
+ code := http.StatusFound
+ if len(status) == 1 {
+ code = status[0]
+ }
+
+ http.Redirect(r, r.req, location, code)
+}
+
+func (r *renderer) Template() *template.Template {
+ return r.t
+}
+
+func (r *renderer) execute(name string, binding interface{}) (*bytes.Buffer, error) {
+ buf := bufpool.Get()
+ return buf, r.t.ExecuteTemplate(buf, name, binding)
+}
+
+func (r *renderer) addYield(name string, binding interface{}) {
+ funcs := template.FuncMap{
+ "yield": func() (template.HTML, error) {
+ buf, err := r.execute(name, binding)
+ // return safe html here since we are rendering our own template
+ return template.HTML(buf.String()), err
+ },
+ "current": func() (string, error) {
+ return name, nil
+ },
+ }
+ r.t.Funcs(funcs)
+}
+
+func (r *renderer) prepareHTMLOptions(htmlOpt []HTMLOptions) HTMLOptions {
+ if len(htmlOpt) > 0 {
+ return htmlOpt[0]
+ }
+
+ return HTMLOptions{
+ Layout: r.opt.Layout,
+ }
+}
diff --git a/vendor/github.com/oxtoacart/bpool/LICENSE b/vendor/github.com/oxtoacart/bpool/LICENSE
new file mode 100644
index 0000000..f94e97c
--- /dev/null
+++ b/vendor/github.com/oxtoacart/bpool/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2014 Percy Wegmann
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/github.com/oxtoacart/bpool/bpool.go b/vendor/github.com/oxtoacart/bpool/bpool.go
new file mode 100644
index 0000000..6232a38
--- /dev/null
+++ b/vendor/github.com/oxtoacart/bpool/bpool.go
@@ -0,0 +1,6 @@
+/*
+Package bpool implements leaky pools of byte arrays and Buffers as bounded
+channels. It is based on the leaky buffer example from the Effective Go
+documentation: http://golang.org/doc/effective_go.html#leaky_buffer
+*/
+package bpool
diff --git a/vendor/github.com/oxtoacart/bpool/bufferpool.go b/vendor/github.com/oxtoacart/bpool/bufferpool.go
new file mode 100644
index 0000000..8c8ac64
--- /dev/null
+++ b/vendor/github.com/oxtoacart/bpool/bufferpool.go
@@ -0,0 +1,40 @@
+package bpool
+
+import (
+ "bytes"
+)
+
+// BufferPool implements a pool of bytes.Buffers in the form of a bounded
+// channel.
+type BufferPool struct {
+ c chan *bytes.Buffer
+}
+
+// NewBufferPool creates a new BufferPool bounded to the given size.
+func NewBufferPool(size int) (bp *BufferPool) {
+ return &BufferPool{
+ c: make(chan *bytes.Buffer, size),
+ }
+}
+
+// Get gets a Buffer from the BufferPool, or creates a new one if none are
+// available in the pool.
+func (bp *BufferPool) Get() (b *bytes.Buffer) {
+ select {
+ case b = <-bp.c:
+ // reuse existing buffer
+ default:
+ // create new buffer
+ b = bytes.NewBuffer([]byte{})
+ }
+ return
+}
+
+// Put returns the given Buffer to the BufferPool.
+func (bp *BufferPool) Put(b *bytes.Buffer) {
+ b.Reset()
+ select {
+ case bp.c <- b:
+ default: // Discard the buffer if the pool is full.
+ }
+}
diff --git a/vendor/github.com/oxtoacart/bpool/bytepool.go b/vendor/github.com/oxtoacart/bpool/bytepool.go
new file mode 100644
index 0000000..ef3898a
--- /dev/null
+++ b/vendor/github.com/oxtoacart/bpool/bytepool.go
@@ -0,0 +1,45 @@
+package bpool
+
+// BytePool implements a leaky pool of []byte in the form of a bounded
+// channel.
+type BytePool struct {
+ c chan []byte
+ w int
+}
+
+// NewBytePool creates a new BytePool bounded to the given maxSize, with new
+// byte arrays sized based on width.
+func NewBytePool(maxSize int, width int) (bp *BytePool) {
+ return &BytePool{
+ c: make(chan []byte, maxSize),
+ w: width,
+ }
+}
+
+// Get gets a []byte from the BytePool, or creates a new one if none are
+// available in the pool.
+func (bp *BytePool) Get() (b []byte) {
+ select {
+ case b = <-bp.c:
+ // reuse existing buffer
+ default:
+ // create new buffer
+ b = make([]byte, bp.w)
+ }
+ return
+}
+
+// Put returns the given Buffer to the BytePool.
+func (bp *BytePool) Put(b []byte) {
+ select {
+ case bp.c <- b:
+ // buffer went back into pool
+ default:
+ // buffer didn't go back into pool, just discard
+ }
+}
+
+// Width returns the width of the byte arrays in this pool.
+func (bp *BytePool) Width() (n int) {
+ return bp.w
+}
diff --git a/vendor/github.com/oxtoacart/bpool/sizedbufferpool.go b/vendor/github.com/oxtoacart/bpool/sizedbufferpool.go
new file mode 100644
index 0000000..8519aca
--- /dev/null
+++ b/vendor/github.com/oxtoacart/bpool/sizedbufferpool.go
@@ -0,0 +1,60 @@
+package bpool
+
+import (
+ "bytes"
+)
+
+// SizedBufferPool implements a pool of bytes.Buffers in the form of a bounded
+// channel. Buffers are pre-allocated to the requested size.
+type SizedBufferPool struct {
+ c chan *bytes.Buffer
+ a int
+}
+
+// SizedBufferPool creates a new BufferPool bounded to the given size.
+// size defines the number of buffers to be retained in the pool and alloc sets
+// the initial capacity of new buffers to minimize calls to make().
+//
+// The value of alloc should seek to provide a buffer that is representative of
+// most data written to the the buffer (i.e. 95th percentile) without being
+// overly large (which will increase static memory consumption). You may wish to
+// track the capacity of your last N buffers (i.e. using an []int) prior to
+// returning them to the pool as input into calculating a suitable alloc value.
+func NewSizedBufferPool(size int, alloc int) (bp *SizedBufferPool) {
+ return &SizedBufferPool{
+ c: make(chan *bytes.Buffer, size),
+ a: alloc,
+ }
+}
+
+// Get gets a Buffer from the SizedBufferPool, or creates a new one if none are
+// available in the pool. Buffers have a pre-allocated capacity.
+func (bp *SizedBufferPool) Get() (b *bytes.Buffer) {
+ select {
+ case b = <-bp.c:
+ // reuse existing buffer
+ default:
+ // create new buffer
+ b = bytes.NewBuffer(make([]byte, 0, bp.a))
+ }
+ return
+}
+
+// Put returns the given Buffer to the SizedBufferPool.
+func (bp *SizedBufferPool) Put(b *bytes.Buffer) {
+ b.Reset()
+
+ // Release buffers over our maximum capacity and re-create a pre-sized
+ // buffer to replace it.
+ // Note that the cap(b.Bytes()) provides the capacity from the read off-set
+ // only, but as we've called b.Reset() the full capacity of the underlying
+ // byte slice is returned.
+ if cap(b.Bytes()) > bp.a {
+ b = bytes.NewBuffer(make([]byte, 0, bp.a))
+ }
+
+ select {
+ case bp.c <- b:
+ default: // Discard the buffer if the pool is full.
+ }
+}
diff --git a/vendor/github.com/rs/cors/LICENSE b/vendor/github.com/rs/cors/LICENSE
new file mode 100644
index 0000000..d8e2df5
--- /dev/null
+++ b/vendor/github.com/rs/cors/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014 Olivier Poitrey <rs@dailymotion.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/github.com/rs/cors/cors.go b/vendor/github.com/rs/cors/cors.go
new file mode 100644
index 0000000..4bb22d8
--- /dev/null
+++ b/vendor/github.com/rs/cors/cors.go
@@ -0,0 +1,412 @@
+/*
+Package cors is net/http handler to handle CORS related requests
+as defined by http://www.w3.org/TR/cors/
+
+You can configure it by passing an option struct to cors.New:
+
+ c := cors.New(cors.Options{
+ AllowedOrigins: []string{"foo.com"},
+ AllowedMethods: []string{"GET", "POST", "DELETE"},
+ AllowCredentials: true,
+ })
+
+Then insert the handler in the chain:
+
+ handler = c.Handler(handler)
+
+See Options documentation for more options.
+
+The resulting handler is a standard net/http handler.
+*/
+package cors
+
+import (
+ "log"
+ "net/http"
+ "os"
+ "strconv"
+ "strings"
+
+ "github.com/rs/xhandler"
+ "golang.org/x/net/context"
+)
+
+// Options is a configuration container to setup the CORS middleware.
+type Options struct {
+ // AllowedOrigins is a list of origins a cross-domain request can be executed from.
+ // If the special "*" value is present in the list, all origins will be allowed.
+ // An origin may contain a wildcard (*) to replace 0 or more characters
+ // (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penality.
+ // Only one wildcard can be used per origin.
+ // Default value is ["*"]
+ AllowedOrigins []string
+ // AllowOriginFunc is a custom function to validate the origin. It take the origin
+ // as argument and returns true if allowed or false otherwise. If this option is
+ // set, the content of AllowedOrigins is ignored.
+ AllowOriginFunc func(origin string) bool
+ // AllowedMethods is a list of methods the client is allowed to use with
+ // cross-domain requests. Default value is simple methods (GET and POST)
+ AllowedMethods []string
+ // AllowedHeaders is list of non simple headers the client is allowed to use with
+ // cross-domain requests.
+ // If the special "*" value is present in the list, all headers will be allowed.
+ // Default value is [] but "Origin" is always appended to the list.
+ AllowedHeaders []string
+ // ExposedHeaders indicates which headers are safe to expose to the API of a CORS
+ // API specification
+ ExposedHeaders []string
+ // AllowCredentials indicates whether the request can include user credentials like
+ // cookies, HTTP authentication or client side SSL certificates.
+ AllowCredentials bool
+ // MaxAge indicates how long (in seconds) the results of a preflight request
+ // can be cached
+ MaxAge int
+ // OptionsPassthrough instructs preflight to let other potential next handlers to
+ // process the OPTIONS method. Turn this on if your application handles OPTIONS.
+ OptionsPassthrough bool
+ // Debugging flag adds additional output to debug server side CORS issues
+ Debug bool
+}
+
+// Cors http handler
+type Cors struct {
+ // Debug logger
+ Log *log.Logger
+ // Set to true when allowed origins contains a "*"
+ allowedOriginsAll bool
+ // Normalized list of plain allowed origins
+ allowedOrigins []string
+ // List of allowed origins containing wildcards
+ allowedWOrigins []wildcard
+ // Optional origin validator function
+ allowOriginFunc func(origin string) bool
+ // Set to true when allowed headers contains a "*"
+ allowedHeadersAll bool
+ // Normalized list of allowed headers
+ allowedHeaders []string
+ // Normalized list of allowed methods
+ allowedMethods []string
+ // Normalized list of exposed headers
+ exposedHeaders []string
+ allowCredentials bool
+ maxAge int
+ optionPassthrough bool
+}
+
+// New creates a new Cors handler with the provided options.
+func New(options Options) *Cors {
+ c := &Cors{
+ exposedHeaders: convert(options.ExposedHeaders, http.CanonicalHeaderKey),
+ allowOriginFunc: options.AllowOriginFunc,
+ allowCredentials: options.AllowCredentials,
+ maxAge: options.MaxAge,
+ optionPassthrough: options.OptionsPassthrough,
+ }
+ if options.Debug {
+ c.Log = log.New(os.Stdout, "[cors] ", log.LstdFlags)
+ }
+
+ // Normalize options
+ // Note: for origins and methods matching, the spec requires a case-sensitive matching.
+ // As it may error prone, we chose to ignore the spec here.
+
+ // Allowed Origins
+ if len(options.AllowedOrigins) == 0 {
+ // Default is all origins
+ c.allowedOriginsAll = true
+ } else {
+ c.allowedOrigins = []string{}
+ c.allowedWOrigins = []wildcard{}
+ for _, origin := range options.AllowedOrigins {
+ // Normalize
+ origin = strings.ToLower(origin)
+ if origin == "*" {
+ // If "*" is present in the list, turn the whole list into a match all
+ c.allowedOriginsAll = true
+ c.allowedOrigins = nil
+ c.allowedWOrigins = nil
+ break
+ } else if i := strings.IndexByte(origin, '*'); i >= 0 {
+ // Split the origin in two: start and end string without the *
+ w := wildcard{origin[0:i], origin[i+1 : len(origin)]}
+ c.allowedWOrigins = append(c.allowedWOrigins, w)
+ } else {
+ c.allowedOrigins = append(c.allowedOrigins, origin)
+ }
+ }
+ }
+
+ // Allowed Headers
+ if len(options.AllowedHeaders) == 0 {
+ // Use sensible defaults
+ c.allowedHeaders = []string{"Origin", "Accept", "Content-Type"}
+ } else {
+ // Origin is always appended as some browsers will always request for this header at preflight
+ c.allowedHeaders = convert(append(options.AllowedHeaders, "Origin"), http.CanonicalHeaderKey)
+ for _, h := range options.AllowedHeaders {
+ if h == "*" {
+ c.allowedHeadersAll = true
+ c.allowedHeaders = nil
+ break
+ }
+ }
+ }
+
+ // Allowed Methods
+ if len(options.AllowedMethods) == 0 {
+ // Default is spec's "simple" methods
+ c.allowedMethods = []string{"GET", "POST"}
+ } else {
+ c.allowedMethods = convert(options.AllowedMethods, strings.ToUpper)
+ }
+
+ return c
+}
+
+// Default creates a new Cors handler with default options
+func Default() *Cors {
+ return New(Options{})
+}
+
+// Handler apply the CORS specification on the request, and add relevant CORS headers
+// as necessary.
+func (c *Cors) Handler(h http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.Method == "OPTIONS" {
+ c.logf("Handler: Preflight request")
+ c.handlePreflight(w, r)
+ // Preflight requests are standalone and should stop the chain as some other
+ // middleware may not handle OPTIONS requests correctly. One typical example
+ // is authentication middleware ; OPTIONS requests won't carry authentication
+ // headers (see #1)
+ if c.optionPassthrough {
+ h.ServeHTTP(w, r)
+ } else {
+ w.WriteHeader(http.StatusOK)
+ }
+ } else {
+ c.logf("Handler: Actual request")
+ c.handleActualRequest(w, r)
+ h.ServeHTTP(w, r)
+ }
+ })
+}
+
+// HandlerC is net/context aware handler
+func (c *Cors) HandlerC(h xhandler.HandlerC) xhandler.HandlerC {
+ return xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
+ if r.Method == "OPTIONS" {
+ c.logf("Handler: Preflight request")
+ c.handlePreflight(w, r)
+ // Preflight requests are standalone and should stop the chain as some other
+ // middleware may not handle OPTIONS requests correctly. One typical example
+ // is authentication middleware ; OPTIONS requests won't carry authentication
+ // headers (see #1)
+ if c.optionPassthrough {
+ h.ServeHTTPC(ctx, w, r)
+ } else {
+ w.WriteHeader(http.StatusOK)
+ }
+ } else {
+ c.logf("Handler: Actual request")
+ c.handleActualRequest(w, r)
+ h.ServeHTTPC(ctx, w, r)
+ }
+ })
+}
+
+// HandlerFunc provides Martini compatible handler
+func (c *Cors) HandlerFunc(w http.ResponseWriter, r *http.Request) {
+ if r.Method == "OPTIONS" {
+ c.logf("HandlerFunc: Preflight request")
+ c.handlePreflight(w, r)
+ } else {
+ c.logf("HandlerFunc: Actual request")
+ c.handleActualRequest(w, r)
+ }
+}
+
+// Negroni compatible interface
+func (c *Cors) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
+ if r.Method == "OPTIONS" {
+ c.logf("ServeHTTP: Preflight request")
+ c.handlePreflight(w, r)
+ // Preflight requests are standalone and should stop the chain as some other
+ // middleware may not handle OPTIONS requests correctly. One typical example
+ // is authentication middleware ; OPTIONS requests won't carry authentication
+ // headers (see #1)
+ if c.optionPassthrough {
+ next(w, r)
+ } else {
+ w.WriteHeader(http.StatusOK)
+ }
+ } else {
+ c.logf("ServeHTTP: Actual request")
+ c.handleActualRequest(w, r)
+ next(w, r)
+ }
+}
+
+// handlePreflight handles pre-flight CORS requests
+func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) {
+ headers := w.Header()
+ origin := r.Header.Get("Origin")
+
+ if r.Method != "OPTIONS" {
+ c.logf(" Preflight aborted: %s!=OPTIONS", r.Method)
+ return
+ }
+ // Always set Vary headers
+ // see https://github.com/rs/cors/issues/10,
+ // https://github.com/rs/cors/commit/dbdca4d95feaa7511a46e6f1efb3b3aa505bc43f#commitcomment-12352001
+ headers.Add("Vary", "Origin")
+ headers.Add("Vary", "Access-Control-Request-Method")
+ headers.Add("Vary", "Access-Control-Request-Headers")
+
+ if origin == "" {
+ c.logf(" Preflight aborted: empty origin")
+ return
+ }
+ if !c.isOriginAllowed(origin) {
+ c.logf(" Preflight aborted: origin '%s' not allowed", origin)
+ return
+ }
+
+ reqMethod := r.Header.Get("Access-Control-Request-Method")
+ if !c.isMethodAllowed(reqMethod) {
+ c.logf(" Preflight aborted: method '%s' not allowed", reqMethod)
+ return
+ }
+ reqHeaders := parseHeaderList(r.Header.Get("Access-Control-Request-Headers"))
+ if !c.areHeadersAllowed(reqHeaders) {
+ c.logf(" Preflight aborted: headers '%v' not allowed", reqHeaders)
+ return
+ }
+ headers.Set("Access-Control-Allow-Origin", origin)
+ // Spec says: Since the list of methods can be unbounded, simply returning the method indicated
+ // by Access-Control-Request-Method (if supported) can be enough
+ headers.Set("Access-Control-Allow-Methods", strings.ToUpper(reqMethod))
+ if len(reqHeaders) > 0 {
+
+ // Spec says: Since the list of headers can be unbounded, simply returning supported headers
+ // from Access-Control-Request-Headers can be enough
+ headers.Set("Access-Control-Allow-Headers", strings.Join(reqHeaders, ", "))
+ }
+ if c.allowCredentials {
+ headers.Set("Access-Control-Allow-Credentials", "true")
+ }
+ if c.maxAge > 0 {
+ headers.Set("Access-Control-Max-Age", strconv.Itoa(c.maxAge))
+ }
+ c.logf(" Preflight response headers: %v", headers)
+}
+
+// handleActualRequest handles simple cross-origin requests, actual request or redirects
+func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) {
+ headers := w.Header()
+ origin := r.Header.Get("Origin")
+
+ if r.Method == "OPTIONS" {
+ c.logf(" Actual request no headers added: method == %s", r.Method)
+ return
+ }
+ // Always set Vary, see https://github.com/rs/cors/issues/10
+ headers.Add("Vary", "Origin")
+ if origin == "" {
+ c.logf(" Actual request no headers added: missing origin")
+ return
+ }
+ if !c.isOriginAllowed(origin) {
+ c.logf(" Actual request no headers added: origin '%s' not allowed", origin)
+ return
+ }
+
+ // Note that spec does define a way to specifically disallow a simple method like GET or
+ // POST. Access-Control-Allow-Methods is only used for pre-flight requests and the
+ // spec doesn't instruct to check the allowed methods for simple cross-origin requests.
+ // We think it's a nice feature to be able to have control on those methods though.
+ if !c.isMethodAllowed(r.Method) {
+ c.logf(" Actual request no headers added: method '%s' not allowed", r.Method)
+
+ return
+ }
+ headers.Set("Access-Control-Allow-Origin", origin)
+ if len(c.exposedHeaders) > 0 {
+ headers.Set("Access-Control-Expose-Headers", strings.Join(c.exposedHeaders, ", "))
+ }
+ if c.allowCredentials {
+ headers.Set("Access-Control-Allow-Credentials", "true")
+ }
+ c.logf(" Actual response added headers: %v", headers)
+}
+
+// convenience method. checks if debugging is turned on before printing
+func (c *Cors) logf(format string, a ...interface{}) {
+ if c.Log != nil {
+ c.Log.Printf(format, a...)
+ }
+}
+
+// isOriginAllowed checks if a given origin is allowed to perform cross-domain requests
+// on the endpoint
+func (c *Cors) isOriginAllowed(origin string) bool {
+ if c.allowOriginFunc != nil {
+ return c.allowOriginFunc(origin)
+ }
+ if c.allowedOriginsAll {
+ return true
+ }
+ origin = strings.ToLower(origin)
+ for _, o := range c.allowedOrigins {
+ if o == origin {
+ return true
+ }
+ }
+ for _, w := range c.allowedWOrigins {
+ if w.match(origin) {
+ return true
+ }
+ }
+ return false
+}
+
+// isMethodAllowed checks if a given method can be used as part of a cross-domain request
+// on the endpoing
+func (c *Cors) isMethodAllowed(method string) bool {
+ if len(c.allowedMethods) == 0 {
+ // If no method allowed, always return false, even for preflight request
+ return false
+ }
+ method = strings.ToUpper(method)
+ if method == "OPTIONS" {
+ // Always allow preflight requests
+ return true
+ }
+ for _, m := range c.allowedMethods {
+ if m == method {
+ return true
+ }
+ }
+ return false
+}
+
+// areHeadersAllowed checks if a given list of headers are allowed to used within
+// a cross-domain request.
+func (c *Cors) areHeadersAllowed(requestedHeaders []string) bool {
+ if c.allowedHeadersAll || len(requestedHeaders) == 0 {
+ return true
+ }
+ for _, header := range requestedHeaders {
+ header = http.CanonicalHeaderKey(header)
+ found := false
+ for _, h := range c.allowedHeaders {
+ if h == header {
+ found = true
+ }
+ }
+ if !found {
+ return false
+ }
+ }
+ return true
+}
diff --git a/vendor/github.com/rs/cors/examples/alice/server.go b/vendor/github.com/rs/cors/examples/alice/server.go
new file mode 100644
index 0000000..0a3e15c
--- /dev/null
+++ b/vendor/github.com/rs/cors/examples/alice/server.go
@@ -0,0 +1,24 @@
+package main
+
+import (
+ "net/http"
+
+ "github.com/justinas/alice"
+ "github.com/rs/cors"
+)
+
+func main() {
+ c := cors.New(cors.Options{
+ AllowedOrigins: []string{"http://foo.com"},
+ })
+
+ mux := http.NewServeMux()
+
+ mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.Write([]byte("{\"hello\": \"world\"}"))
+ })
+
+ chain := alice.New(c.Handler).Then(mux)
+ http.ListenAndServe(":8080", chain)
+}
diff --git a/vendor/github.com/rs/cors/examples/default/server.go b/vendor/github.com/rs/cors/examples/default/server.go
new file mode 100644
index 0000000..ccb5e1b
--- /dev/null
+++ b/vendor/github.com/rs/cors/examples/default/server.go
@@ -0,0 +1,19 @@
+package main
+
+import (
+ "net/http"
+
+ "github.com/rs/cors"
+)
+
+func main() {
+ mux := http.NewServeMux()
+ mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.Write([]byte("{\"hello\": \"world\"}"))
+ })
+
+ // Use default options
+ handler := cors.Default().Handler(mux)
+ http.ListenAndServe(":8080", handler)
+}
diff --git a/vendor/github.com/rs/cors/examples/goji/server.go b/vendor/github.com/rs/cors/examples/goji/server.go
new file mode 100644
index 0000000..1fb4073
--- /dev/null
+++ b/vendor/github.com/rs/cors/examples/goji/server.go
@@ -0,0 +1,22 @@
+package main
+
+import (
+ "net/http"
+
+ "github.com/rs/cors"
+ "github.com/zenazn/goji"
+)
+
+func main() {
+ c := cors.New(cors.Options{
+ AllowedOrigins: []string{"http://foo.com"},
+ })
+ goji.Use(c.Handler)
+
+ goji.Get("/", func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.Write([]byte("{\"hello\": \"world\"}"))
+ })
+
+ goji.Serve()
+}
diff --git a/vendor/github.com/rs/cors/examples/martini/server.go b/vendor/github.com/rs/cors/examples/martini/server.go
new file mode 100644
index 0000000..081af32
--- /dev/null
+++ b/vendor/github.com/rs/cors/examples/martini/server.go
@@ -0,0 +1,23 @@
+package main
+
+import (
+ "github.com/go-martini/martini"
+ "github.com/martini-contrib/render"
+ "github.com/rs/cors"
+)
+
+func main() {
+ c := cors.New(cors.Options{
+ AllowedOrigins: []string{"http://foo.com"},
+ })
+
+ m := martini.Classic()
+ m.Use(render.Renderer())
+ m.Use(c.HandlerFunc)
+
+ m.Get("/", func(r render.Render) {
+ r.JSON(200, map[string]interface{}{"hello": "world"})
+ })
+
+ m.Run()
+}
diff --git a/vendor/github.com/rs/cors/examples/negroni/server.go b/vendor/github.com/rs/cors/examples/negroni/server.go
new file mode 100644
index 0000000..3cb33bf
--- /dev/null
+++ b/vendor/github.com/rs/cors/examples/negroni/server.go
@@ -0,0 +1,26 @@
+package main
+
+import (
+ "net/http"
+
+ "github.com/codegangsta/negroni"
+ "github.com/rs/cors"
+)
+
+func main() {
+ c := cors.New(cors.Options{
+ AllowedOrigins: []string{"http://foo.com"},
+ })
+
+ mux := http.NewServeMux()
+
+ mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.Write([]byte("{\"hello\": \"world\"}"))
+ })
+
+ n := negroni.Classic()
+ n.Use(c)
+ n.UseHandler(mux)
+ n.Run(":3000")
+}
diff --git a/vendor/github.com/rs/cors/examples/nethttp/server.go b/vendor/github.com/rs/cors/examples/nethttp/server.go
new file mode 100644
index 0000000..eaa775e
--- /dev/null
+++ b/vendor/github.com/rs/cors/examples/nethttp/server.go
@@ -0,0 +1,20 @@
+package main
+
+import (
+ "net/http"
+
+ "github.com/rs/cors"
+)
+
+func main() {
+ c := cors.New(cors.Options{
+ AllowedOrigins: []string{"http://foo.com"},
+ })
+
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.Write([]byte("{\"hello\": \"world\"}"))
+ })
+
+ http.ListenAndServe(":8080", c.Handler(handler))
+}
diff --git a/vendor/github.com/rs/cors/examples/openbar/server.go b/vendor/github.com/rs/cors/examples/openbar/server.go
new file mode 100644
index 0000000..0940423
--- /dev/null
+++ b/vendor/github.com/rs/cors/examples/openbar/server.go
@@ -0,0 +1,22 @@
+package main
+
+import (
+ "net/http"
+
+ "github.com/rs/cors"
+)
+
+func main() {
+ c := cors.New(cors.Options{
+ AllowedOrigins: []string{"*"},
+ AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"},
+ AllowCredentials: true,
+ })
+
+ h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.Write([]byte("{\"hello\": \"world\"}"))
+ })
+
+ http.ListenAndServe(":8080", c.Handler(h))
+}
diff --git a/vendor/github.com/rs/cors/examples/xhandler/server.go b/vendor/github.com/rs/cors/examples/xhandler/server.go
new file mode 100644
index 0000000..649a1c7
--- /dev/null
+++ b/vendor/github.com/rs/cors/examples/xhandler/server.go
@@ -0,0 +1,24 @@
+package main
+
+import (
+ "net/http"
+
+ "github.com/rs/cors"
+ "github.com/rs/xhandler"
+ "golang.org/x/net/context"
+)
+
+func main() {
+ c := xhandler.Chain{}
+
+ // Use default options
+ c.UseC(cors.Default().HandlerC)
+
+ mux := http.NewServeMux()
+ mux.Handle("/", c.Handler(xhandler.HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.Write([]byte("{\"hello\": \"world\"}"))
+ })))
+
+ http.ListenAndServe(":8080", mux)
+}
diff --git a/vendor/github.com/rs/cors/utils.go b/vendor/github.com/rs/cors/utils.go
new file mode 100644
index 0000000..c7a0aa0
--- /dev/null
+++ b/vendor/github.com/rs/cors/utils.go
@@ -0,0 +1,70 @@
+package cors
+
+import "strings"
+
+const toLower = 'a' - 'A'
+
+type converter func(string) string
+
+type wildcard struct {
+ prefix string
+ suffix string
+}
+
+func (w wildcard) match(s string) bool {
+ return len(s) >= len(w.prefix+w.suffix) && strings.HasPrefix(s, w.prefix) && strings.HasSuffix(s, w.suffix)
+}
+
+// convert converts a list of string using the passed converter function
+func convert(s []string, c converter) []string {
+ out := []string{}
+ for _, i := range s {
+ out = append(out, c(i))
+ }
+ return out
+}
+
+// parseHeaderList tokenize + normalize a string containing a list of headers
+func parseHeaderList(headerList string) []string {
+ l := len(headerList)
+ h := make([]byte, 0, l)
+ upper := true
+ // Estimate the number headers in order to allocate the right splice size
+ t := 0
+ for i := 0; i < l; i++ {
+ if headerList[i] == ',' {
+ t++
+ }
+ }
+ headers := make([]string, 0, t)
+ for i := 0; i < l; i++ {
+ b := headerList[i]
+ if b >= 'a' && b <= 'z' {
+ if upper {
+ h = append(h, b-toLower)
+ } else {
+ h = append(h, b)
+ }
+ } else if b >= 'A' && b <= 'Z' {
+ if !upper {
+ h = append(h, b+toLower)
+ } else {
+ h = append(h, b)
+ }
+ } else if b == '-' || b == '_' || (b >= '0' && b <= '9') {
+ h = append(h, b)
+ }
+
+ if b == ' ' || b == ',' || i == l-1 {
+ if len(h) > 0 {
+ // Flush the found header
+ headers = append(headers, string(h))
+ h = h[:0]
+ upper = true
+ }
+ } else {
+ upper = b == '-' || b == '_'
+ }
+ }
+ return headers
+}
diff --git a/vendor/github.com/rs/xhandler/LICENSE b/vendor/github.com/rs/xhandler/LICENSE
new file mode 100644
index 0000000..47c5e9d
--- /dev/null
+++ b/vendor/github.com/rs/xhandler/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015 Olivier Poitrey <rs@dailymotion.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/github.com/rs/xhandler/chain.go b/vendor/github.com/rs/xhandler/chain.go
new file mode 100644
index 0000000..3e4bd35
--- /dev/null
+++ b/vendor/github.com/rs/xhandler/chain.go
@@ -0,0 +1,121 @@
+package xhandler
+
+import (
+ "net/http"
+
+ "golang.org/x/net/context"
+)
+
+// Chain is a helper for chaining middleware handlers together for easier
+// management.
+type Chain []func(next HandlerC) HandlerC
+
+// Add appends a variable number of additional middleware handlers
+// to the middleware chain. Middleware handlers can either be
+// context-aware or non-context aware handlers with the appropriate
+// function signatures.
+func (c *Chain) Add(f ...interface{}) {
+ for _, h := range f {
+ switch v := h.(type) {
+ case func(http.Handler) http.Handler:
+ c.Use(v)
+ case func(HandlerC) HandlerC:
+ c.UseC(v)
+ default:
+ panic("Adding invalid handler to the middleware chain")
+ }
+ }
+}
+
+// With creates a new middleware chain from an existing chain,
+// extending it with additional middleware. Middleware handlers
+// can either be context-aware or non-context aware handlers
+// with the appropriate function signatures.
+func (c *Chain) With(f ...interface{}) *Chain {
+ n := make(Chain, len(*c))
+ copy(n, *c)
+ n.Add(f...)
+ return &n
+}
+
+// UseC appends a context-aware handler to the middleware chain.
+func (c *Chain) UseC(f func(next HandlerC) HandlerC) {
+ *c = append(*c, f)
+}
+
+// Use appends a standard http.Handler to the middleware chain without
+// losing track of the context when inserted between two context aware handlers.
+//
+// Caveat: the f function will be called on each request so you are better off putting
+// any initialization sequence outside of this function.
+func (c *Chain) Use(f func(next http.Handler) http.Handler) {
+ xf := func(next HandlerC) HandlerC {
+ return HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
+ n := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ next.ServeHTTPC(ctx, w, r)
+ })
+ f(n).ServeHTTP(w, r)
+ })
+ }
+ *c = append(*c, xf)
+}
+
+// Handler wraps the provided final handler with all the middleware appended to
+// the chain and returns a new standard http.Handler instance.
+// The context.Background() context is injected automatically.
+func (c Chain) Handler(xh HandlerC) http.Handler {
+ ctx := context.Background()
+ return c.HandlerCtx(ctx, xh)
+}
+
+// HandlerFC is a helper to provide a function (HandlerFuncC) to Handler().
+//
+// HandlerFC is equivalent to:
+// c.Handler(xhandler.HandlerFuncC(xhc))
+func (c Chain) HandlerFC(xhf HandlerFuncC) http.Handler {
+ ctx := context.Background()
+ return c.HandlerCtx(ctx, HandlerFuncC(xhf))
+}
+
+// HandlerH is a helper to provide a standard http handler (http.HandlerFunc)
+// to Handler(). Your final handler won't have access to the context though.
+func (c Chain) HandlerH(h http.Handler) http.Handler {
+ ctx := context.Background()
+ return c.HandlerCtx(ctx, HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
+ h.ServeHTTP(w, r)
+ }))
+}
+
+// HandlerF is a helper to provide a standard http handler function
+// (http.HandlerFunc) to Handler(). Your final handler won't have access
+// to the context though.
+func (c Chain) HandlerF(hf http.HandlerFunc) http.Handler {
+ ctx := context.Background()
+ return c.HandlerCtx(ctx, HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
+ hf(w, r)
+ }))
+}
+
+// HandlerCtx wraps the provided final handler with all the middleware appended to
+// the chain and returns a new standard http.Handler instance.
+func (c Chain) HandlerCtx(ctx context.Context, xh HandlerC) http.Handler {
+ return New(ctx, c.HandlerC(xh))
+}
+
+// HandlerC wraps the provided final handler with all the middleware appended to
+// the chain and returns a HandlerC instance.
+func (c Chain) HandlerC(xh HandlerC) HandlerC {
+ for i := len(c) - 1; i >= 0; i-- {
+ xh = c[i](xh)
+ }
+ return xh
+}
+
+// HandlerCF wraps the provided final handler func with all the middleware appended to
+// the chain and returns a HandlerC instance.
+//
+// HandlerCF is equivalent to:
+// c.HandlerC(xhandler.HandlerFuncC(xhc))
+func (c Chain) HandlerCF(xhc HandlerFuncC) HandlerC {
+ return c.HandlerC(HandlerFuncC(xhc))
+}
diff --git a/vendor/github.com/rs/xhandler/middleware.go b/vendor/github.com/rs/xhandler/middleware.go
new file mode 100644
index 0000000..7ad8fba
--- /dev/null
+++ b/vendor/github.com/rs/xhandler/middleware.go
@@ -0,0 +1,59 @@
+package xhandler
+
+import (
+ "net/http"
+ "time"
+
+ "golang.org/x/net/context"
+)
+
+// CloseHandler returns a Handler, cancelling the context when the client
+// connection closes unexpectedly.
+func CloseHandler(next HandlerC) HandlerC {
+ return HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
+ // Cancel the context if the client closes the connection
+ if wcn, ok := w.(http.CloseNotifier); ok {
+ var cancel context.CancelFunc
+ ctx, cancel = context.WithCancel(ctx)
+ defer cancel()
+
+ notify := wcn.CloseNotify()
+ go func() {
+ select {
+ case <-notify:
+ cancel()
+ case <-ctx.Done():
+ }
+ }()
+ }
+
+ next.ServeHTTPC(ctx, w, r)
+ })
+}
+
+// TimeoutHandler returns a Handler which adds a timeout to the context.
+//
+// Child handlers have the responsability of obeying the context deadline and to return
+// an appropriate error (or not) response in case of timeout.
+func TimeoutHandler(timeout time.Duration) func(next HandlerC) HandlerC {
+ return func(next HandlerC) HandlerC {
+ return HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
+ ctx, _ = context.WithTimeout(ctx, timeout)
+ next.ServeHTTPC(ctx, w, r)
+ })
+ }
+}
+
+// If is a special handler that will skip insert the condNext handler only if a condition
+// applies at runtime.
+func If(cond func(ctx context.Context, w http.ResponseWriter, r *http.Request) bool, condNext func(next HandlerC) HandlerC) func(next HandlerC) HandlerC {
+ return func(next HandlerC) HandlerC {
+ return HandlerFuncC(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
+ if cond(ctx, w, r) {
+ condNext(next).ServeHTTPC(ctx, w, r)
+ } else {
+ next.ServeHTTPC(ctx, w, r)
+ }
+ })
+ }
+}
diff --git a/vendor/github.com/rs/xhandler/xhandler.go b/vendor/github.com/rs/xhandler/xhandler.go
new file mode 100644
index 0000000..bc832cb
--- /dev/null
+++ b/vendor/github.com/rs/xhandler/xhandler.go
@@ -0,0 +1,42 @@
+// Package xhandler provides a bridge between http.Handler and net/context.
+//
+// xhandler enforces net/context in your handlers without sacrificing
+// compatibility with existing http.Handlers nor imposing a specific router.
+//
+// Thanks to net/context deadline management, xhandler is able to enforce
+// a per request deadline and will cancel the context in when the client close
+// the connection unexpectedly.
+//
+// You may create net/context aware middlewares pretty much the same way as
+// you would with http.Handler.
+package xhandler // import "github.com/rs/xhandler"
+
+import (
+ "net/http"
+
+ "golang.org/x/net/context"
+)
+
+// HandlerC is a net/context aware http.Handler
+type HandlerC interface {
+ ServeHTTPC(context.Context, http.ResponseWriter, *http.Request)
+}
+
+// HandlerFuncC type is an adapter to allow the use of ordinary functions
+// as an xhandler.Handler. If f is a function with the appropriate signature,
+// xhandler.HandlerFuncC(f) is a xhandler.Handler object that calls f.
+type HandlerFuncC func(context.Context, http.ResponseWriter, *http.Request)
+
+// ServeHTTPC calls f(ctx, w, r).
+func (f HandlerFuncC) ServeHTTPC(ctx context.Context, w http.ResponseWriter, r *http.Request) {
+ f(ctx, w, r)
+}
+
+// New creates a conventional http.Handler injecting the provided root
+// context to sub handlers. This handler is used as a bridge between conventional
+// http.Handler and context aware handlers.
+func New(ctx context.Context, h HandlerC) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ h.ServeHTTPC(ctx, w, r)
+ })
+}
diff --git a/vendor/github.com/stretchr/graceful/LICENSE b/vendor/github.com/stretchr/graceful/LICENSE
new file mode 100644
index 0000000..a4f2f28
--- /dev/null
+++ b/vendor/github.com/stretchr/graceful/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Tyler Bunnell
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/stretchr/graceful/graceful.go b/vendor/github.com/stretchr/graceful/graceful.go
new file mode 100644
index 0000000..07c9904
--- /dev/null
+++ b/vendor/github.com/stretchr/graceful/graceful.go
@@ -0,0 +1,484 @@
+package graceful
+
+import (
+ "crypto/tls"
+ "log"
+ "net"
+ "net/http"
+ "os"
+ "sync"
+ "time"
+)
+
+// Server wraps an http.Server with graceful connection handling.
+// It may be used directly in the same way as http.Server, or may
+// be constructed with the global functions in this package.
+//
+// Example:
+// srv := &graceful.Server{
+// Timeout: 5 * time.Second,
+// Server: &http.Server{Addr: ":1234", Handler: handler},
+// }
+// srv.ListenAndServe()
+type Server struct {
+ *http.Server
+
+ // Timeout is the duration to allow outstanding requests to survive
+ // before forcefully terminating them.
+ Timeout time.Duration
+
+ // Limit the number of outstanding requests
+ ListenLimit int
+
+ // TCPKeepAlive sets the TCP keep-alive timeouts on accepted
+ // connections. It prunes dead TCP connections ( e.g. closing
+ // laptop mid-download)
+ TCPKeepAlive time.Duration
+
+ // ConnState specifies an optional callback function that is
+ // called when a client connection changes state. This is a proxy
+ // to the underlying http.Server's ConnState, and the original
+ // must not be set directly.
+ ConnState func(net.Conn, http.ConnState)
+
+ // BeforeShutdown is an optional callback function that is called
+ // before the listener is closed. Returns true if shutdown is allowed
+ BeforeShutdown func() bool
+
+ // ShutdownInitiated is an optional callback function that is called
+ // when shutdown is initiated. It can be used to notify the client
+ // side of long lived connections (e.g. websockets) to reconnect.
+ ShutdownInitiated func()
+
+ // NoSignalHandling prevents graceful from automatically shutting down
+ // on SIGINT and SIGTERM. If set to true, you must shut down the server
+ // manually with Stop().
+ NoSignalHandling bool
+
+ // Logger used to notify of errors on startup and on stop.
+ Logger *log.Logger
+
+ // LogFunc can be assigned with a logging function of your choice, allowing
+ // you to use whatever logging approach you would like
+ LogFunc func(format string, args ...interface{})
+
+ // Interrupted is true if the server is handling a SIGINT or SIGTERM
+ // signal and is thus shutting down.
+ Interrupted bool
+
+ // interrupt signals the listener to stop serving connections,
+ // and the server to shut down.
+ interrupt chan os.Signal
+
+ // stopLock is used to protect against concurrent calls to Stop
+ stopLock sync.Mutex
+
+ // stopChan is the channel on which callers may block while waiting for
+ // the server to stop.
+ stopChan chan struct{}
+
+ // chanLock is used to protect access to the various channel constructors.
+ chanLock sync.RWMutex
+
+ // connections holds all connections managed by graceful
+ connections map[net.Conn]struct{}
+
+ // idleConnections holds all idle connections managed by graceful
+ idleConnections map[net.Conn]struct{}
+}
+
+// Run serves the http.Handler with graceful shutdown enabled.
+//
+// timeout is the duration to wait until killing active requests and stopping the server.
+// If timeout is 0, the server never times out. It waits for all active requests to finish.
+func Run(addr string, timeout time.Duration, n http.Handler) {
+ srv := &Server{
+ Timeout: timeout,
+ TCPKeepAlive: 3 * time.Minute,
+ Server: &http.Server{Addr: addr, Handler: n},
+ // Logger: DefaultLogger(),
+ }
+
+ if err := srv.ListenAndServe(); err != nil {
+ if opErr, ok := err.(*net.OpError); !ok || (ok && opErr.Op != "accept") {
+ srv.logf("%s", err)
+ os.Exit(1)
+ }
+ }
+
+}
+
+// RunWithErr is an alternative version of Run function which can return error.
+//
+// Unlike Run this version will not exit the program if an error is encountered but will
+// return it instead.
+func RunWithErr(addr string, timeout time.Duration, n http.Handler) error {
+ srv := &Server{
+ Timeout: timeout,
+ TCPKeepAlive: 3 * time.Minute,
+ Server: &http.Server{Addr: addr, Handler: n},
+ Logger: DefaultLogger(),
+ }
+
+ return srv.ListenAndServe()
+}
+
+// ListenAndServe is equivalent to http.Server.ListenAndServe with graceful shutdown enabled.
+//
+// timeout is the duration to wait until killing active requests and stopping the server.
+// If timeout is 0, the server never times out. It waits for all active requests to finish.
+func ListenAndServe(server *http.Server, timeout time.Duration) error {
+ srv := &Server{Timeout: timeout, Server: server, Logger: DefaultLogger()}
+ return srv.ListenAndServe()
+}
+
+// ListenAndServe is equivalent to http.Server.ListenAndServe with graceful shutdown enabled.
+func (srv *Server) ListenAndServe() error {
+ // Create the listener so we can control their lifetime
+ addr := srv.Addr
+ if addr == "" {
+ addr = ":http"
+ }
+ conn, err := srv.newTCPListener(addr)
+ if err != nil {
+ return err
+ }
+
+ return srv.Serve(conn)
+}
+
+// ListenAndServeTLS is equivalent to http.Server.ListenAndServeTLS with graceful shutdown enabled.
+//
+// timeout is the duration to wait until killing active requests and stopping the server.
+// If timeout is 0, the server never times out. It waits for all active requests to finish.
+func ListenAndServeTLS(server *http.Server, certFile, keyFile string, timeout time.Duration) error {
+ srv := &Server{Timeout: timeout, Server: server, Logger: DefaultLogger()}
+ return srv.ListenAndServeTLS(certFile, keyFile)
+}
+
+// ListenTLS is a convenience method that creates an https listener using the
+// provided cert and key files. Use this method if you need access to the
+// listener object directly. When ready, pass it to the Serve method.
+func (srv *Server) ListenTLS(certFile, keyFile string) (net.Listener, error) {
+ // Create the listener ourselves so we can control its lifetime
+ addr := srv.Addr
+ if addr == "" {
+ addr = ":https"
+ }
+
+ config := &tls.Config{}
+ if srv.TLSConfig != nil {
+ *config = *srv.TLSConfig
+ }
+
+ var err error
+ config.Certificates = make([]tls.Certificate, 1)
+ config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
+ if err != nil {
+ return nil, err
+ }
+
+ // Enable http2
+ enableHTTP2ForTLSConfig(config)
+
+ conn, err := srv.newTCPListener(addr)
+ if err != nil {
+ return nil, err
+ }
+
+ srv.TLSConfig = config
+
+ tlsListener := tls.NewListener(conn, config)
+ return tlsListener, nil
+}
+
+// Enable HTTP2ForTLSConfig explicitly enables http/2 for a TLS Config. This is due to changes in Go 1.7 where
+// http servers are no longer automatically configured to enable http/2 if the server's TLSConfig is set.
+// See https://github.com/golang/go/issues/15908
+func enableHTTP2ForTLSConfig(t *tls.Config) {
+
+ if TLSConfigHasHTTP2Enabled(t) {
+ return
+ }
+
+ t.NextProtos = append(t.NextProtos, "h2")
+}
+
+// TLSConfigHasHTTP2Enabled checks to see if a given TLS Config has http2 enabled.
+func TLSConfigHasHTTP2Enabled(t *tls.Config) bool {
+ for _, value := range t.NextProtos {
+ if value == "h2" {
+ return true
+ }
+ }
+ return false
+}
+
+// ListenAndServeTLS is equivalent to http.Server.ListenAndServeTLS with graceful shutdown enabled.
+func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
+ l, err := srv.ListenTLS(certFile, keyFile)
+ if err != nil {
+ return err
+ }
+
+ return srv.Serve(l)
+}
+
+// ListenAndServeTLSConfig can be used with an existing TLS config and is equivalent to
+// http.Server.ListenAndServeTLS with graceful shutdown enabled,
+func (srv *Server) ListenAndServeTLSConfig(config *tls.Config) error {
+ addr := srv.Addr
+ if addr == "" {
+ addr = ":https"
+ }
+
+ conn, err := srv.newTCPListener(addr)
+ if err != nil {
+ return err
+ }
+
+ srv.TLSConfig = config
+
+ tlsListener := tls.NewListener(conn, config)
+ return srv.Serve(tlsListener)
+}
+
+// Serve is equivalent to http.Server.Serve with graceful shutdown enabled.
+//
+// timeout is the duration to wait until killing active requests and stopping the server.
+// If timeout is 0, the server never times out. It waits for all active requests to finish.
+func Serve(server *http.Server, l net.Listener, timeout time.Duration) error {
+ srv := &Server{Timeout: timeout, Server: server, Logger: DefaultLogger()}
+
+ return srv.Serve(l)
+}
+
+// Serve is equivalent to http.Server.Serve with graceful shutdown enabled.
+func (srv *Server) Serve(listener net.Listener) error {
+
+ if srv.ListenLimit != 0 {
+ listener = LimitListener(listener, srv.ListenLimit)
+ }
+
+ // Make our stopchan
+ srv.StopChan()
+
+ // Track connection state
+ add := make(chan net.Conn)
+ idle := make(chan net.Conn)
+ active := make(chan net.Conn)
+ remove := make(chan net.Conn)
+
+ srv.Server.ConnState = func(conn net.Conn, state http.ConnState) {
+ switch state {
+ case http.StateNew:
+ add <- conn
+ case http.StateActive:
+ active <- conn
+ case http.StateIdle:
+ idle <- conn
+ case http.StateClosed, http.StateHijacked:
+ remove <- conn
+ }
+
+ srv.stopLock.Lock()
+ defer srv.stopLock.Unlock()
+
+ if srv.ConnState != nil {
+ srv.ConnState(conn, state)
+ }
+ }
+
+ // Manage open connections
+ shutdown := make(chan chan struct{})
+ kill := make(chan struct{})
+ go srv.manageConnections(add, idle, active, remove, shutdown, kill)
+
+ interrupt := srv.interruptChan()
+ // Set up the interrupt handler
+ if !srv.NoSignalHandling {
+ signalNotify(interrupt)
+ }
+ quitting := make(chan struct{})
+ go srv.handleInterrupt(interrupt, quitting, listener)
+
+ // Serve with graceful listener.
+ // Execution blocks here until listener.Close() is called, above.
+ err := srv.Server.Serve(listener)
+ if err != nil {
+ // If the underlying listening is closed, Serve returns an error
+ // complaining about listening on a closed socket. This is expected, so
+ // let's ignore the error if we are the ones who explicitly closed the
+ // socket.
+ select {
+ case <-quitting:
+ err = nil
+ default:
+ }
+ }
+
+ srv.shutdown(shutdown, kill)
+
+ return err
+}
+
+// Stop instructs the type to halt operations and close
+// the stop channel when it is finished.
+//
+// timeout is grace period for which to wait before shutting
+// down the server. The timeout value passed here will override the
+// timeout given when constructing the server, as this is an explicit
+// command to stop the server.
+func (srv *Server) Stop(timeout time.Duration) {
+ srv.stopLock.Lock()
+ defer srv.stopLock.Unlock()
+
+ srv.Timeout = timeout
+ sendSignalInt(srv.interruptChan())
+}
+
+// StopChan gets the stop channel which will block until
+// stopping has completed, at which point it is closed.
+// Callers should never close the stop channel.
+func (srv *Server) StopChan() <-chan struct{} {
+ srv.chanLock.Lock()
+ defer srv.chanLock.Unlock()
+
+ if srv.stopChan == nil {
+ srv.stopChan = make(chan struct{})
+ }
+ return srv.stopChan
+}
+
+// DefaultLogger returns the logger used by Run, RunWithErr, ListenAndServe, ListenAndServeTLS and Serve.
+// The logger outputs to STDERR by default.
+func DefaultLogger() *log.Logger {
+ return log.New(os.Stderr, "[graceful] ", 0)
+}
+
+func (srv *Server) manageConnections(add, idle, active, remove chan net.Conn, shutdown chan chan struct{}, kill chan struct{}) {
+ var done chan struct{}
+ srv.connections = map[net.Conn]struct{}{}
+ srv.idleConnections = map[net.Conn]struct{}{}
+ for {
+ select {
+ case conn := <-add:
+ srv.connections[conn] = struct{}{}
+ case conn := <-idle:
+ srv.idleConnections[conn] = struct{}{}
+ case conn := <-active:
+ delete(srv.idleConnections, conn)
+ case conn := <-remove:
+ delete(srv.connections, conn)
+ delete(srv.idleConnections, conn)
+ if done != nil && len(srv.connections) == 0 {
+ done <- struct{}{}
+ return
+ }
+ case done = <-shutdown:
+ if len(srv.connections) == 0 && len(srv.idleConnections) == 0 {
+ done <- struct{}{}
+ return
+ }
+ // a shutdown request has been received. if we have open idle
+ // connections, we must close all of them now. this prevents idle
+ // connections from holding the server open while waiting for them to
+ // hit their idle timeout.
+ for k := range srv.idleConnections {
+ if err := k.Close(); err != nil {
+ srv.logf("[ERROR] %s", err)
+ }
+ }
+ case <-kill:
+ srv.stopLock.Lock()
+ defer srv.stopLock.Unlock()
+
+ srv.Server.ConnState = nil
+ for k := range srv.connections {
+ if err := k.Close(); err != nil {
+ srv.logf("[ERROR] %s", err)
+ }
+ }
+ return
+ }
+ }
+}
+
+func (srv *Server) interruptChan() chan os.Signal {
+ srv.chanLock.Lock()
+ defer srv.chanLock.Unlock()
+
+ if srv.interrupt == nil {
+ srv.interrupt = make(chan os.Signal, 1)
+ }
+
+ return srv.interrupt
+}
+
+func (srv *Server) handleInterrupt(interrupt chan os.Signal, quitting chan struct{}, listener net.Listener) {
+ for _ = range interrupt {
+ if srv.Interrupted {
+ srv.logf("already shutting down")
+ continue
+ }
+ srv.logf("shutdown initiated")
+ srv.Interrupted = true
+ if srv.BeforeShutdown != nil {
+ if !srv.BeforeShutdown() {
+ srv.Interrupted = false
+ continue
+ }
+ }
+
+ close(quitting)
+ srv.SetKeepAlivesEnabled(false)
+ if err := listener.Close(); err != nil {
+ srv.logf("[ERROR] %s", err)
+ }
+
+ if srv.ShutdownInitiated != nil {
+ srv.ShutdownInitiated()
+ }
+ }
+}
+
+func (srv *Server) logf(format string, args ...interface{}) {
+ if srv.LogFunc != nil {
+ srv.LogFunc(format, args...)
+ } else if srv.Logger != nil {
+ srv.Logger.Printf(format, args...)
+ }
+}
+
+func (srv *Server) shutdown(shutdown chan chan struct{}, kill chan struct{}) {
+ // Request done notification
+ done := make(chan struct{})
+ shutdown <- done
+
+ if srv.Timeout > 0 {
+ select {
+ case <-done:
+ case <-time.After(srv.Timeout):
+ close(kill)
+ }
+ } else {
+ <-done
+ }
+ // Close the stopChan to wake up any blocked goroutines.
+ srv.chanLock.Lock()
+ if srv.stopChan != nil {
+ close(srv.stopChan)
+ }
+ srv.chanLock.Unlock()
+}
+
+func (srv *Server) newTCPListener(addr string) (net.Listener, error) {
+ conn, err := net.Listen("tcp", addr)
+ if err != nil {
+ return conn, err
+ }
+ if srv.TCPKeepAlive != 0 {
+ conn = keepAliveListener{conn, srv.TCPKeepAlive}
+ }
+ return conn, nil
+}
diff --git a/vendor/github.com/stretchr/graceful/keepalive_listener.go b/vendor/github.com/stretchr/graceful/keepalive_listener.go
new file mode 100644
index 0000000..1484bc2
--- /dev/null
+++ b/vendor/github.com/stretchr/graceful/keepalive_listener.go
@@ -0,0 +1,32 @@
+package graceful
+
+import (
+ "net"
+ "time"
+)
+
+type keepAliveConn interface {
+ SetKeepAlive(bool) error
+ SetKeepAlivePeriod(d time.Duration) error
+}
+
+// keepAliveListener sets TCP keep-alive timeouts on accepted
+// connections. It's used by ListenAndServe and ListenAndServeTLS so
+// dead TCP connections (e.g. closing laptop mid-download) eventually
+// go away.
+type keepAliveListener struct {
+ net.Listener
+ keepAlivePeriod time.Duration
+}
+
+func (ln keepAliveListener) Accept() (net.Conn, error) {
+ c, err := ln.Listener.Accept()
+ if err != nil {
+ return nil, err
+ }
+
+ kac := c.(keepAliveConn)
+ kac.SetKeepAlive(true)
+ kac.SetKeepAlivePeriod(ln.keepAlivePeriod)
+ return c, nil
+}
diff --git a/vendor/github.com/stretchr/graceful/limit_listen.go b/vendor/github.com/stretchr/graceful/limit_listen.go
new file mode 100644
index 0000000..ce32ce9
--- /dev/null
+++ b/vendor/github.com/stretchr/graceful/limit_listen.go
@@ -0,0 +1,77 @@
+// Copyright 2013 The etcd Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package graceful
+
+import (
+ "errors"
+ "net"
+ "sync"
+ "time"
+)
+
+// ErrNotTCP indicates that network connection is not a TCP connection.
+var ErrNotTCP = errors.New("only tcp connections have keepalive")
+
+// LimitListener returns a Listener that accepts at most n simultaneous
+// connections from the provided Listener.
+func LimitListener(l net.Listener, n int) net.Listener {
+ return &limitListener{l, make(chan struct{}, n)}
+}
+
+type limitListener struct {
+ net.Listener
+ sem chan struct{}
+}
+
+func (l *limitListener) acquire() { l.sem <- struct{}{} }
+func (l *limitListener) release() { <-l.sem }
+
+func (l *limitListener) Accept() (net.Conn, error) {
+ l.acquire()
+ c, err := l.Listener.Accept()
+ if err != nil {
+ l.release()
+ return nil, err
+ }
+ return &limitListenerConn{Conn: c, release: l.release}, nil
+}
+
+type limitListenerConn struct {
+ net.Conn
+ releaseOnce sync.Once
+ release func()
+}
+
+func (l *limitListenerConn) Close() error {
+ err := l.Conn.Close()
+ l.releaseOnce.Do(l.release)
+ return err
+}
+
+func (l *limitListenerConn) SetKeepAlive(doKeepAlive bool) error {
+ tcpc, ok := l.Conn.(*net.TCPConn)
+ if !ok {
+ return ErrNotTCP
+ }
+ return tcpc.SetKeepAlive(doKeepAlive)
+}
+
+func (l *limitListenerConn) SetKeepAlivePeriod(d time.Duration) error {
+ tcpc, ok := l.Conn.(*net.TCPConn)
+ if !ok {
+ return ErrNotTCP
+ }
+ return tcpc.SetKeepAlivePeriod(d)
+}
diff --git a/vendor/github.com/stretchr/graceful/signal.go b/vendor/github.com/stretchr/graceful/signal.go
new file mode 100644
index 0000000..9550978
--- /dev/null
+++ b/vendor/github.com/stretchr/graceful/signal.go
@@ -0,0 +1,17 @@
+//+build !appengine
+
+package graceful
+
+import (
+ "os"
+ "os/signal"
+ "syscall"
+)
+
+func signalNotify(interrupt chan<- os.Signal) {
+ signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
+}
+
+func sendSignalInt(interrupt chan<- os.Signal) {
+ interrupt <- syscall.SIGINT
+}
diff --git a/vendor/github.com/stretchr/graceful/signal_appengine.go b/vendor/github.com/stretchr/graceful/signal_appengine.go
new file mode 100644
index 0000000..6b776f0
--- /dev/null
+++ b/vendor/github.com/stretchr/graceful/signal_appengine.go
@@ -0,0 +1,13 @@
+//+build appengine
+
+package graceful
+
+import "os"
+
+func signalNotify(interrupt chan<- os.Signal) {
+ // Does not notify in the case of AppEngine.
+}
+
+func sendSignalInt(interrupt chan<- os.Signal) {
+ // Does not send in the case of AppEngine.
+}
diff --git a/vendor/github.com/stretchr/graceful/tests/main.go b/vendor/github.com/stretchr/graceful/tests/main.go
new file mode 100644
index 0000000..9380ae6
--- /dev/null
+++ b/vendor/github.com/stretchr/graceful/tests/main.go
@@ -0,0 +1,40 @@
+package main
+
+import (
+ "fmt"
+ "sync"
+
+ "github.com/urfave/negroni"
+ "gopkg.in/tylerb/graceful.v1"
+)
+
+func main() {
+
+ var wg sync.WaitGroup
+
+ wg.Add(3)
+ go func() {
+ n := negroni.New()
+ fmt.Println("Launching server on :3000")
+ graceful.Run(":3000", 0, n)
+ fmt.Println("Terminated server on :3000")
+ wg.Done()
+ }()
+ go func() {
+ n := negroni.New()
+ fmt.Println("Launching server on :3001")
+ graceful.Run(":3001", 0, n)
+ fmt.Println("Terminated server on :3001")
+ wg.Done()
+ }()
+ go func() {
+ n := negroni.New()
+ fmt.Println("Launching server on :3002")
+ graceful.Run(":3002", 0, n)
+ fmt.Println("Terminated server on :3002")
+ wg.Done()
+ }()
+ fmt.Println("Press ctrl+c. All servers should terminate.")
+ wg.Wait()
+
+}
diff --git a/vendor/github.com/urfave/negroni/LICENSE b/vendor/github.com/urfave/negroni/LICENSE
new file mode 100644
index 0000000..08b5e20
--- /dev/null
+++ b/vendor/github.com/urfave/negroni/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Jeremy Saenz
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/urfave/negroni/doc.go b/vendor/github.com/urfave/negroni/doc.go
new file mode 100644
index 0000000..add1ed9
--- /dev/null
+++ b/vendor/github.com/urfave/negroni/doc.go
@@ -0,0 +1,25 @@
+// Package negroni is an idiomatic approach to web middleware in Go. It is tiny, non-intrusive, and encourages use of net/http Handlers.
+//
+// If you like the idea of Martini, but you think it contains too much magic, then Negroni is a great fit.
+//
+// For a full guide visit http://github.com/urfave/negroni
+//
+// package main
+//
+// import (
+// "github.com/urfave/negroni"
+// "net/http"
+// "fmt"
+// )
+//
+// func main() {
+// mux := http.NewServeMux()
+// mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
+// fmt.Fprintf(w, "Welcome to the home page!")
+// })
+//
+// n := negroni.Classic()
+// n.UseHandler(mux)
+// n.Run(":3000")
+// }
+package negroni
diff --git a/vendor/github.com/urfave/negroni/logger.go b/vendor/github.com/urfave/negroni/logger.go
new file mode 100644
index 0000000..04cd53b
--- /dev/null
+++ b/vendor/github.com/urfave/negroni/logger.go
@@ -0,0 +1,35 @@
+package negroni
+
+import (
+ "log"
+ "net/http"
+ "os"
+ "time"
+)
+
+// ALogger interface
+type ALogger interface {
+ Println(v ...interface{})
+ Printf(format string, v ...interface{})
+}
+
+// Logger is a middleware handler that logs the request as it goes in and the response as it goes out.
+type Logger struct {
+ // ALogger implements just enough log.Logger interface to be compatible with other implementations
+ ALogger
+}
+
+// NewLogger returns a new Logger instance
+func NewLogger() *Logger {
+ return &Logger{log.New(os.Stdout, "[negroni] ", 0)}
+}
+
+func (l *Logger) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
+ start := time.Now()
+ l.Printf("Started %s %s", r.Method, r.URL.Path)
+
+ next(rw, r)
+
+ res := rw.(ResponseWriter)
+ l.Printf("Completed %v %s in %v", res.Status(), http.StatusText(res.Status()), time.Since(start))
+}
diff --git a/vendor/github.com/urfave/negroni/negroni.go b/vendor/github.com/urfave/negroni/negroni.go
new file mode 100644
index 0000000..9c7c187
--- /dev/null
+++ b/vendor/github.com/urfave/negroni/negroni.go
@@ -0,0 +1,133 @@
+package negroni
+
+import (
+ "log"
+ "net/http"
+ "os"
+)
+
+// Handler handler is an interface that objects can implement to be registered to serve as middleware
+// in the Negroni middleware stack.
+// ServeHTTP should yield to the next middleware in the chain by invoking the next http.HandlerFunc
+// passed in.
+//
+// If the Handler writes to the ResponseWriter, the next http.HandlerFunc should not be invoked.
+type Handler interface {
+ ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
+}
+
+// HandlerFunc is an adapter to allow the use of ordinary functions as Negroni handlers.
+// If f is a function with the appropriate signature, HandlerFunc(f) is a Handler object that calls f.
+type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
+
+func (h HandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
+ h(rw, r, next)
+}
+
+type middleware struct {
+ handler Handler
+ next *middleware
+}
+
+func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+ m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
+}
+
+// Wrap converts a http.Handler into a negroni.Handler so it can be used as a Negroni
+// middleware. The next http.HandlerFunc is automatically called after the Handler
+// is executed.
+func Wrap(handler http.Handler) Handler {
+ return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
+ handler.ServeHTTP(rw, r)
+ next(rw, r)
+ })
+}
+
+// Negroni is a stack of Middleware Handlers that can be invoked as an http.Handler.
+// Negroni middleware is evaluated in the order that they are added to the stack using
+// the Use and UseHandler methods.
+type Negroni struct {
+ middleware middleware
+ handlers []Handler
+}
+
+// New returns a new Negroni instance with no middleware preconfigured.
+func New(handlers ...Handler) *Negroni {
+ return &Negroni{
+ handlers: handlers,
+ middleware: build(handlers),
+ }
+}
+
+// Classic returns a new Negroni instance with the default middleware already
+// in the stack.
+//
+// Recovery - Panic Recovery Middleware
+// Logger - Request/Response Logging
+// Static - Static File Serving
+func Classic() *Negroni {
+ return New(NewRecovery(), NewLogger(), NewStatic(http.Dir("public")))
+}
+
+func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
+ n.middleware.ServeHTTP(NewResponseWriter(rw), r)
+}
+
+// Use adds a Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni.
+func (n *Negroni) Use(handler Handler) {
+ if handler == nil {
+ panic("handler cannot be nil")
+ }
+
+ n.handlers = append(n.handlers, handler)
+ n.middleware = build(n.handlers)
+}
+
+// UseFunc adds a Negroni-style handler function onto the middleware stack.
+func (n *Negroni) UseFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)) {
+ n.Use(HandlerFunc(handlerFunc))
+}
+
+// UseHandler adds a http.Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni.
+func (n *Negroni) UseHandler(handler http.Handler) {
+ n.Use(Wrap(handler))
+}
+
+// UseHandler adds a http.HandlerFunc-style handler function onto the middleware stack.
+func (n *Negroni) UseHandlerFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request)) {
+ n.UseHandler(http.HandlerFunc(handlerFunc))
+}
+
+// Run is a convenience function that runs the negroni stack as an HTTP
+// server. The addr string takes the same format as http.ListenAndServe.
+func (n *Negroni) Run(addr string) {
+ l := log.New(os.Stdout, "[negroni] ", 0)
+ l.Printf("listening on %s", addr)
+ l.Fatal(http.ListenAndServe(addr, n))
+}
+
+// Returns a list of all the handlers in the current Negroni middleware chain.
+func (n *Negroni) Handlers() []Handler {
+ return n.handlers
+}
+
+func build(handlers []Handler) middleware {
+ var next middleware
+
+ if len(handlers) == 0 {
+ return voidMiddleware()
+ } else if len(handlers) > 1 {
+ next = build(handlers[1:])
+ } else {
+ next = voidMiddleware()
+ }
+
+ return middleware{handlers[0], &next}
+}
+
+func voidMiddleware() middleware {
+ return middleware{
+ HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {}),
+ &middleware{},
+ }
+}
diff --git a/vendor/github.com/urfave/negroni/recovery.go b/vendor/github.com/urfave/negroni/recovery.go
new file mode 100644
index 0000000..8396cb1
--- /dev/null
+++ b/vendor/github.com/urfave/negroni/recovery.go
@@ -0,0 +1,65 @@
+package negroni
+
+import (
+ "fmt"
+ "log"
+ "net/http"
+ "os"
+ "runtime"
+ "runtime/debug"
+)
+
+// Recovery is a Negroni middleware that recovers from any panics and writes a 500 if there was one.
+type Recovery struct {
+ Logger ALogger
+ PrintStack bool
+ ErrorHandlerFunc func(interface{})
+ StackAll bool
+ StackSize int
+}
+
+// NewRecovery returns a new instance of Recovery
+func NewRecovery() *Recovery {
+ return &Recovery{
+ Logger: log.New(os.Stdout, "[negroni] ", 0),
+ PrintStack: true,
+ StackAll: false,
+ StackSize: 1024 * 8,
+ }
+}
+
+func (rec *Recovery) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
+ defer func() {
+ if err := recover(); err != nil {
+ if rw.Header().Get("Content-Type") == "" {
+ rw.Header().Set("Content-Type", "text/plain; charset=utf-8")
+ }
+
+ rw.WriteHeader(http.StatusInternalServerError)
+
+ stack := make([]byte, rec.StackSize)
+ stack = stack[:runtime.Stack(stack, rec.StackAll)]
+
+ f := "PANIC: %s\n%s"
+ rec.Logger.Printf(f, err, stack)
+
+ if rec.PrintStack {
+ fmt.Fprintf(rw, f, err, stack)
+ }
+
+ if rec.ErrorHandlerFunc != nil {
+ func() {
+ defer func() {
+ if err := recover(); err != nil {
+ rec.Logger.Printf("provided ErrorHandlerFunc panic'd: %s, trace:\n%s", err, debug.Stack())
+ rec.Logger.Printf("%s\n", debug.Stack())
+ }
+ }()
+ rec.ErrorHandlerFunc(err)
+ }()
+ }
+ }
+ }()
+
+ next(rw, r)
+}
diff --git a/vendor/github.com/urfave/negroni/response_writer.go b/vendor/github.com/urfave/negroni/response_writer.go
new file mode 100644
index 0000000..bfb83a6
--- /dev/null
+++ b/vendor/github.com/urfave/negroni/response_writer.go
@@ -0,0 +1,113 @@
+package negroni
+
+import (
+ "bufio"
+ "fmt"
+ "net"
+ "net/http"
+)
+
+// ResponseWriter is a wrapper around http.ResponseWriter that provides extra information about
+// the response. It is recommended that middleware handlers use this construct to wrap a responsewriter
+// if the functionality calls for it.
+type ResponseWriter interface {
+ http.ResponseWriter
+ http.Flusher
+ // Status returns the status code of the response or 200 if the response has
+ // not been written (as this is the default response code in net/http)
+ Status() int
+ // Written returns whether or not the ResponseWriter has been written.
+ Written() bool
+ // Size returns the size of the response body.
+ Size() int
+ // Before allows for a function to be called before the ResponseWriter has been written to. This is
+ // useful for setting headers or any other operations that must happen before a response has been written.
+ Before(func(ResponseWriter))
+}
+
+type beforeFunc func(ResponseWriter)
+
+// NewResponseWriter creates a ResponseWriter that wraps an http.ResponseWriter
+func NewResponseWriter(rw http.ResponseWriter) ResponseWriter {
+ nrw := &responseWriter{
+ ResponseWriter: rw,
+ }
+
+ if _, ok := rw.(http.CloseNotifier); ok {
+ return &responseWriterCloseNotifer{nrw}
+ }
+
+ return nrw
+}
+
+type responseWriter struct {
+ http.ResponseWriter
+ status int
+ size int
+ beforeFuncs []beforeFunc
+}
+
+func (rw *responseWriter) WriteHeader(s int) {
+ rw.status = s
+ rw.callBefore()
+ rw.ResponseWriter.WriteHeader(s)
+}
+
+func (rw *responseWriter) Write(b []byte) (int, error) {
+ if !rw.Written() {
+ // The status will be StatusOK if WriteHeader has not been called yet
+ rw.WriteHeader(http.StatusOK)
+ }
+ size, err := rw.ResponseWriter.Write(b)
+ rw.size += size
+ return size, err
+}
+
+func (rw *responseWriter) Status() int {
+ return rw.status
+}
+
+func (rw *responseWriter) Size() int {
+ return rw.size
+}
+
+func (rw *responseWriter) Written() bool {
+ return rw.status != 0
+}
+
+func (rw *responseWriter) Before(before func(ResponseWriter)) {
+ rw.beforeFuncs = append(rw.beforeFuncs, before)
+}
+
+func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
+ hijacker, ok := rw.ResponseWriter.(http.Hijacker)
+ if !ok {
+ return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface")
+ }
+ return hijacker.Hijack()
+}
+
+func (rw *responseWriter) callBefore() {
+ for i := len(rw.beforeFuncs) - 1; i >= 0; i-- {
+ rw.beforeFuncs[i](rw)
+ }
+}
+
+func (rw *responseWriter) Flush() {
+ flusher, ok := rw.ResponseWriter.(http.Flusher)
+ if ok {
+ if !rw.Written() {
+ // The status will be StatusOK if WriteHeader has not been called yet
+ rw.WriteHeader(http.StatusOK)
+ }
+ flusher.Flush()
+ }
+}
+
+type responseWriterCloseNotifer struct {
+ *responseWriter
+}
+
+func (rw *responseWriterCloseNotifer) CloseNotify() <-chan bool {
+ return rw.ResponseWriter.(http.CloseNotifier).CloseNotify()
+}
diff --git a/vendor/github.com/urfave/negroni/static.go b/vendor/github.com/urfave/negroni/static.go
new file mode 100644
index 0000000..34be967
--- /dev/null
+++ b/vendor/github.com/urfave/negroni/static.go
@@ -0,0 +1,88 @@
+package negroni
+
+import (
+ "net/http"
+ "path"
+ "strings"
+)
+
+// Static is a middleware handler that serves static files in the given
+// directory/filesystem. If the file does not exist on the filesystem, it
+// passes along to the next middleware in the chain. If you desire "fileserver"
+// type behavior where it returns a 404 for unfound files, you should consider
+// using http.FileServer from the Go stdlib.
+type Static struct {
+ // Dir is the directory to serve static files from
+ Dir http.FileSystem
+ // Prefix is the optional prefix used to serve the static directory content
+ Prefix string
+ // IndexFile defines which file to serve as index if it exists.
+ IndexFile string
+}
+
+// NewStatic returns a new instance of Static
+func NewStatic(directory http.FileSystem) *Static {
+ return &Static{
+ Dir: directory,
+ Prefix: "",
+ IndexFile: "index.html",
+ }
+}
+
+func (s *Static) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
+ if r.Method != "GET" && r.Method != "HEAD" {
+ next(rw, r)
+ return
+ }
+ file := r.URL.Path
+ // if we have a prefix, filter requests by stripping the prefix
+ if s.Prefix != "" {
+ if !strings.HasPrefix(file, s.Prefix) {
+ next(rw, r)
+ return
+ }
+ file = file[len(s.Prefix):]
+ if file != "" && file[0] != '/' {
+ next(rw, r)
+ return
+ }
+ }
+ f, err := s.Dir.Open(file)
+ if err != nil {
+ // discard the error?
+ next(rw, r)
+ return
+ }
+ defer f.Close()
+
+ fi, err := f.Stat()
+ if err != nil {
+ next(rw, r)
+ return
+ }
+
+ // try to serve index file
+ if fi.IsDir() {
+ // redirect if missing trailing slash
+ if !strings.HasSuffix(r.URL.Path, "/") {
+ http.Redirect(rw, r, r.URL.Path+"/", http.StatusFound)
+ return
+ }
+
+ file = path.Join(file, s.IndexFile)
+ f, err = s.Dir.Open(file)
+ if err != nil {
+ next(rw, r)
+ return
+ }
+ defer f.Close()
+
+ fi, err = f.Stat()
+ if err != nil || fi.IsDir() {
+ next(rw, r)
+ return
+ }
+ }
+
+ http.ServeContent(rw, r, file, fi.ModTime(), f)
+}
diff --git a/vendor/github.com/zenazn/goji/LICENSE b/vendor/github.com/zenazn/goji/LICENSE
new file mode 100644
index 0000000..446aba0
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2014, 2015, 2016 Carl Jackson (carl@avtok.com)
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/zenazn/goji/bind/bind.go b/vendor/github.com/zenazn/goji/bind/bind.go
new file mode 100644
index 0000000..43d11dd
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/bind/bind.go
@@ -0,0 +1,155 @@
+/*
+Package bind provides a convenient way to bind to sockets. It exposes a flag in
+the default flag set named "bind" which provides syntax to bind TCP and UNIX
+sockets. It also supports binding to arbitrary file descriptors passed by a
+parent (for instance, systemd), and for binding to Einhorn sockets (including
+Einhorn ACK support).
+
+If the value passed to bind contains a colon, as in ":8000" or "127.0.0.1:9001",
+it will be treated as a TCP address. If it begins with a "/" or a ".", it will
+be treated as a path to a UNIX socket. If it begins with the string "fd@", as in
+"fd@3", it will be treated as a file descriptor (useful for use with systemd,
+for instance). If it begins with the string "einhorn@", as in "einhorn@0", the
+corresponding einhorn socket will be used.
+
+If an option is not explicitly passed, the implementation will automatically
+select between using "einhorn@0", "fd@3", and ":8000", depending on whether
+Einhorn or systemd (or neither) is detected.
+
+This package is a teensy bit magical, and goes out of its way to Do The Right
+Thing in many situations, including in both development and production. If
+you're looking for something less magical, you'd probably be better off just
+calling net.Listen() the old-fashioned way.
+*/
+package bind
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "net"
+ "os"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+var bind string
+
+func init() {
+ einhornInit()
+ systemdInit()
+}
+
+// DefaultBind specifies the fallback used for WithFlag() if it is
+// unable to discover an environment hint (after checking $GOJI_BIND,
+// Einhorn, systemd, and $PORT).
+//
+// If DefaultBind is overridden, it must be set before calling
+// WithFlag().
+var DefaultBind = ":8000"
+
+// WithFlag adds a standard flag to the global flag instance that
+// allows configuration of the default socket. Users who call
+// Default() must call this function before flags are parsed, for
+// example in an init() block.
+//
+// When selecting the default bind string, this function will examine
+// its environment for hints about what port to bind to, selecting the
+// GOJI_BIND environment variable, Einhorn, systemd, the PORT
+// environment variable, and the value of DefaultBind, in order. In
+// most cases, this means that the default behavior of the default
+// socket will be reasonable for use in your circumstance.
+func WithFlag() {
+ s := Sniff()
+ if s == "" {
+ s = DefaultBind
+ }
+ flag.StringVar(&bind, "bind", s,
+ `Address to bind on. If this value has a colon, as in ":8000" or
+ "127.0.0.1:9001", it will be treated as a TCP address. If it
+ begins with a "/" or a ".", it will be treated as a path to a
+ UNIX socket. If it begins with the string "fd@", as in "fd@3",
+ it will be treated as a file descriptor (useful for use with
+ systemd, for instance). If it begins with the string "einhorn@",
+ as in "einhorn@0", the corresponding einhorn socket will be
+ used. If an option is not explicitly passed, the implementation
+ will automatically select among "einhorn@0" (Einhorn), "fd@3"
+ (systemd), and ":8000" (fallback) based on its environment.`)
+}
+
+// Sniff attempts to select a sensible default bind string by examining its
+// environment. It examines the GOJI_BIND environment variable, Einhorn,
+// systemd, and the PORT environment variable, in that order, selecting the
+// first plausible option. It returns the empty string if no sensible default
+// could be extracted from the environment.
+func Sniff() string {
+ if bind := os.Getenv("GOJI_BIND"); bind != "" {
+ return bind
+ } else if usingEinhorn() {
+ return "einhorn@0"
+ } else if usingSystemd() {
+ return "fd@3"
+ } else if port := os.Getenv("PORT"); port != "" {
+ return ":" + port
+ }
+ return ""
+}
+
+func listenTo(bind string) (net.Listener, error) {
+ if strings.Contains(bind, ":") {
+ return net.Listen("tcp", bind)
+ } else if strings.HasPrefix(bind, ".") || strings.HasPrefix(bind, "/") {
+ return net.Listen("unix", bind)
+ } else if strings.HasPrefix(bind, "fd@") {
+ fd, err := strconv.Atoi(bind[3:])
+ if err != nil {
+ return nil, fmt.Errorf("error while parsing fd %v: %v",
+ bind, err)
+ }
+ f := os.NewFile(uintptr(fd), bind)
+ defer f.Close()
+ return net.FileListener(f)
+ } else if strings.HasPrefix(bind, "einhorn@") {
+ fd, err := strconv.Atoi(bind[8:])
+ if err != nil {
+ return nil, fmt.Errorf(
+ "error while parsing einhorn %v: %v", bind, err)
+ }
+ return einhornBind(fd)
+ }
+
+ return nil, fmt.Errorf("error while parsing bind arg %v", bind)
+}
+
+// Socket parses and binds to the specified address. If Socket encounters an
+// error while parsing or binding to the given socket it will exit by calling
+// log.Fatal.
+func Socket(bind string) net.Listener {
+ l, err := listenTo(bind)
+ if err != nil {
+ log.Fatal(err)
+ }
+ return l
+}
+
+// Default parses and binds to the default socket as given to us by the flag
+// module. If there was an error parsing or binding to that socket, Default will
+// exit by calling `log.Fatal`.
+func Default() net.Listener {
+ return Socket(bind)
+}
+
+// I'm not sure why you'd ever want to call Ready() more than once, but we may
+// as well be safe against it...
+var ready sync.Once
+
+// Ready notifies the environment (for now, just Einhorn) that the process is
+// ready to receive traffic. Should be called at the last possible moment to
+// maximize the chances that a faulty process exits before signaling that it's
+// ready.
+func Ready() {
+ ready.Do(func() {
+ einhornAck()
+ })
+}
diff --git a/vendor/github.com/zenazn/goji/bind/einhorn.go b/vendor/github.com/zenazn/goji/bind/einhorn.go
new file mode 100644
index 0000000..e695c0e
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/bind/einhorn.go
@@ -0,0 +1,91 @@
+// +build !windows
+
+package bind
+
+import (
+ "fmt"
+ "log"
+ "net"
+ "os"
+ "strconv"
+ "syscall"
+)
+
+const tooBigErr = "bind: einhorn@%d not found (einhorn only passed %d fds)"
+const bindErr = "bind: could not bind einhorn@%d: not running under einhorn"
+const einhornErr = "bind: einhorn environment initialization error"
+const ackErr = "bind: error ACKing to einhorn: %v"
+
+var einhornNumFds int
+
+func envInt(val string) (int, error) {
+ return strconv.Atoi(os.Getenv(val))
+}
+
+// Unfortunately this can't be a normal init function, because their execution
+// order is undefined, and we need to run before the init() in bind.go.
+func einhornInit() {
+ mpid, err := envInt("EINHORN_MASTER_PID")
+ if err != nil || mpid != os.Getppid() {
+ return
+ }
+
+ einhornNumFds, err = envInt("EINHORN_FD_COUNT")
+ if err != nil {
+ einhornNumFds = 0
+ return
+ }
+
+ // Prevent einhorn's fds from leaking to our children
+ for i := 0; i < einhornNumFds; i++ {
+ syscall.CloseOnExec(einhornFdMap(i))
+ }
+}
+
+func usingEinhorn() bool {
+ return einhornNumFds > 0
+}
+
+func einhornFdMap(n int) int {
+ name := fmt.Sprintf("EINHORN_FD_%d", n)
+ fno, err := envInt(name)
+ if err != nil {
+ log.Fatal(einhornErr)
+ }
+ return fno
+}
+
+func einhornBind(n int) (net.Listener, error) {
+ if !usingEinhorn() {
+ return nil, fmt.Errorf(bindErr, n)
+ }
+ if n >= einhornNumFds || n < 0 {
+ return nil, fmt.Errorf(tooBigErr, n, einhornNumFds)
+ }
+
+ fno := einhornFdMap(n)
+ f := os.NewFile(uintptr(fno), fmt.Sprintf("einhorn@%d", n))
+ defer f.Close()
+ return net.FileListener(f)
+}
+
+// Fun story: this is actually YAML, not JSON.
+const ackMsg = `{"command": "worker:ack", "pid": %d}` + "\n"
+
+func einhornAck() {
+ if !usingEinhorn() {
+ return
+ }
+ log.Print("bind: ACKing to einhorn")
+
+ ctl, err := net.Dial("unix", os.Getenv("EINHORN_SOCK_PATH"))
+ if err != nil {
+ log.Fatalf(ackErr, err)
+ }
+ defer ctl.Close()
+
+ _, err = fmt.Fprintf(ctl, ackMsg, os.Getpid())
+ if err != nil {
+ log.Fatalf(ackErr, err)
+ }
+}
diff --git a/vendor/github.com/zenazn/goji/bind/einhorn_stub.go b/vendor/github.com/zenazn/goji/bind/einhorn_stub.go
new file mode 100644
index 0000000..093707f
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/bind/einhorn_stub.go
@@ -0,0 +1,12 @@
+// +build windows
+
+package bind
+
+import (
+ "net"
+)
+
+func einhornInit() {}
+func einhornAck() {}
+func einhornBind(fd int) (net.Listener, error) { return nil, nil }
+func usingEinhorn() bool { return false }
diff --git a/vendor/github.com/zenazn/goji/bind/systemd.go b/vendor/github.com/zenazn/goji/bind/systemd.go
new file mode 100644
index 0000000..e7cd8e4
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/bind/systemd.go
@@ -0,0 +1,36 @@
+// +build !windows
+
+package bind
+
+import (
+ "os"
+ "syscall"
+)
+
+const systemdMinFd = 3
+
+var systemdNumFds int
+
+// Unfortunately this can't be a normal init function, because their execution
+// order is undefined, and we need to run before the init() in bind.go.
+func systemdInit() {
+ pid, err := envInt("LISTEN_PID")
+ if err != nil || pid != os.Getpid() {
+ return
+ }
+
+ systemdNumFds, err = envInt("LISTEN_FDS")
+ if err != nil {
+ systemdNumFds = 0
+ return
+ }
+
+ // Prevent fds from leaking to our children
+ for i := 0; i < systemdNumFds; i++ {
+ syscall.CloseOnExec(systemdMinFd + i)
+ }
+}
+
+func usingSystemd() bool {
+ return systemdNumFds > 0
+}
diff --git a/vendor/github.com/zenazn/goji/bind/systemd_stub.go b/vendor/github.com/zenazn/goji/bind/systemd_stub.go
new file mode 100644
index 0000000..4ad4d20
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/bind/systemd_stub.go
@@ -0,0 +1,6 @@
+// +build windows
+
+package bind
+
+func systemdInit() {}
+func usingSystemd() bool { return false }
diff --git a/vendor/github.com/zenazn/goji/default.go b/vendor/github.com/zenazn/goji/default.go
new file mode 100644
index 0000000..540e792
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/default.go
@@ -0,0 +1,102 @@
+package goji
+
+import (
+ "github.com/zenazn/goji/web"
+ "github.com/zenazn/goji/web/middleware"
+)
+
+// The default web.Mux.
+var DefaultMux *web.Mux
+
+func init() {
+ DefaultMux = web.New()
+
+ DefaultMux.Use(middleware.RequestID)
+ DefaultMux.Use(middleware.Logger)
+ DefaultMux.Use(middleware.Recoverer)
+ DefaultMux.Use(middleware.AutomaticOptions)
+}
+
+// Use appends the given middleware to the default Mux's middleware stack. See
+// the documentation for web.Mux.Use for more information.
+func Use(middleware web.MiddlewareType) {
+ DefaultMux.Use(middleware)
+}
+
+// Insert the given middleware into the default Mux's middleware stack. See the
+// documentation for web.Mux.Insert for more information.
+func Insert(middleware, before web.MiddlewareType) error {
+ return DefaultMux.Insert(middleware, before)
+}
+
+// Abandon removes the given middleware from the default Mux's middleware stack.
+// See the documentation for web.Mux.Abandon for more information.
+func Abandon(middleware web.MiddlewareType) error {
+ return DefaultMux.Abandon(middleware)
+}
+
+// Handle adds a route to the default Mux. See the documentation for web.Mux for
+// more information about what types this function accepts.
+func Handle(pattern web.PatternType, handler web.HandlerType) {
+ DefaultMux.Handle(pattern, handler)
+}
+
+// Connect adds a CONNECT route to the default Mux. See the documentation for
+// web.Mux for more information about what types this function accepts.
+func Connect(pattern web.PatternType, handler web.HandlerType) {
+ DefaultMux.Connect(pattern, handler)
+}
+
+// Delete adds a DELETE route to the default Mux. See the documentation for
+// web.Mux for more information about what types this function accepts.
+func Delete(pattern web.PatternType, handler web.HandlerType) {
+ DefaultMux.Delete(pattern, handler)
+}
+
+// Get adds a GET route to the default Mux. See the documentation for web.Mux for
+// more information about what types this function accepts.
+func Get(pattern web.PatternType, handler web.HandlerType) {
+ DefaultMux.Get(pattern, handler)
+}
+
+// Head adds a HEAD route to the default Mux. See the documentation for web.Mux
+// for more information about what types this function accepts.
+func Head(pattern web.PatternType, handler web.HandlerType) {
+ DefaultMux.Head(pattern, handler)
+}
+
+// Options adds a OPTIONS route to the default Mux. See the documentation for
+// web.Mux for more information about what types this function accepts.
+func Options(pattern web.PatternType, handler web.HandlerType) {
+ DefaultMux.Options(pattern, handler)
+}
+
+// Patch adds a PATCH route to the default Mux. See the documentation for web.Mux
+// for more information about what types this function accepts.
+func Patch(pattern web.PatternType, handler web.HandlerType) {
+ DefaultMux.Patch(pattern, handler)
+}
+
+// Post adds a POST route to the default Mux. See the documentation for web.Mux
+// for more information about what types this function accepts.
+func Post(pattern web.PatternType, handler web.HandlerType) {
+ DefaultMux.Post(pattern, handler)
+}
+
+// Put adds a PUT route to the default Mux. See the documentation for web.Mux for
+// more information about what types this function accepts.
+func Put(pattern web.PatternType, handler web.HandlerType) {
+ DefaultMux.Put(pattern, handler)
+}
+
+// Trace adds a TRACE route to the default Mux. See the documentation for
+// web.Mux for more information about what types this function accepts.
+func Trace(pattern web.PatternType, handler web.HandlerType) {
+ DefaultMux.Trace(pattern, handler)
+}
+
+// NotFound sets the NotFound handler for the default Mux. See the documentation
+// for web.Mux.NotFound for more information.
+func NotFound(handler web.HandlerType) {
+ DefaultMux.NotFound(handler)
+}
diff --git a/vendor/github.com/zenazn/goji/example/main.go b/vendor/github.com/zenazn/goji/example/main.go
new file mode 100644
index 0000000..b2bcf6f
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/example/main.go
@@ -0,0 +1,177 @@
+// Command example is a sample application built with Goji. Its goal is to give
+// you a taste for what Goji looks like in the real world by artificially using
+// all of its features.
+//
+// In particular, this is a complete working site for gritter.com, a site where
+// users can post 140-character "greets". Any resemblance to real websites,
+// alive or dead, is purely coincidental.
+package main
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "regexp"
+ "strconv"
+ "time"
+
+ "github.com/goji/param"
+ "github.com/zenazn/goji"
+ "github.com/zenazn/goji/web"
+ "github.com/zenazn/goji/web/middleware"
+)
+
+// Note: the code below cuts a lot of corners to make the example app simple.
+
+func main() {
+ // Add routes to the global handler
+ goji.Get("/", Root)
+ // Fully backwards compatible with net/http's Handlers
+ goji.Get("/greets", http.RedirectHandler("/", 301))
+ // Use your favorite HTTP verbs
+ goji.Post("/greets", NewGreet)
+ // Use Sinatra-style patterns in your URLs
+ goji.Get("/users/:name", GetUser)
+ // Goji also supports regular expressions with named capture groups.
+ goji.Get(regexp.MustCompile(`^/greets/(?P<id>\d+)$`), GetGreet)
+
+ // Middleware can be used to inject behavior into your app. The
+ // middleware for this application are defined in middleware.go, but you
+ // can put them wherever you like.
+ goji.Use(PlainText)
+
+ // If the patterns ends with "/*", the path is treated as a prefix, and
+ // can be used to implement sub-routes.
+ admin := web.New()
+ goji.Handle("/admin/*", admin)
+
+ // The standard SubRouter middleware helps make writing sub-routers
+ // easy. Ordinarily, Goji does not manipulate the request's URL.Path,
+ // meaning you'd have to repeat "/admin/" in each of the following
+ // routes. This middleware allows you to cut down on the repetition by
+ // eliminating the shared, already-matched prefix.
+ admin.Use(middleware.SubRouter)
+ // You can also easily attach extra middleware to sub-routers that are
+ // not present on the parent router. This one, for instance, presents a
+ // password prompt to users of the admin endpoints.
+ admin.Use(SuperSecure)
+
+ admin.Get("/", AdminRoot)
+ admin.Get("/finances", AdminFinances)
+
+ // Goji's routing, like Sinatra's, is exact: no effort is made to
+ // normalize trailing slashes.
+ goji.Get("/admin", http.RedirectHandler("/admin/", 301))
+
+ // Use a custom 404 handler
+ goji.NotFound(NotFound)
+
+ // Sometimes requests take a long time.
+ goji.Get("/waitforit", WaitForIt)
+
+ // Call Serve() at the bottom of your main() function, and it'll take
+ // care of everything else for you, including binding to a socket (with
+ // automatic support for systemd and Einhorn) and supporting graceful
+ // shutdown on SIGINT. Serve() is appropriate for both development and
+ // production.
+ goji.Serve()
+}
+
+// Root route (GET "/"). Print a list of greets.
+func Root(w http.ResponseWriter, r *http.Request) {
+ // In the real world you'd probably use a template or something.
+ io.WriteString(w, "Gritter\n======\n\n")
+ for i := len(Greets) - 1; i >= 0; i-- {
+ Greets[i].Write(w)
+ }
+}
+
+// NewGreet creates a new greet (POST "/greets"). Creates a greet and redirects
+// you to the created greet.
+//
+// To post a new greet, try this at a shell:
+// $ now=$(date +'%Y-%m-%dT%H:%M:%SZ')
+// $ curl -i -d "user=carl&message=Hello+World&time=$now" localhost:8000/greets
+func NewGreet(w http.ResponseWriter, r *http.Request) {
+ var greet Greet
+
+ // Parse the POST body into the Greet struct. The format is the same as
+ // is emitted by (e.g.) jQuery.param.
+ r.ParseForm()
+ err := param.Parse(r.Form, &greet)
+
+ if err != nil || len(greet.Message) > 140 {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ // We make no effort to prevent races against other insertions.
+ Greets = append(Greets, greet)
+ url := fmt.Sprintf("/greets/%d", len(Greets)-1)
+ http.Redirect(w, r, url, http.StatusCreated)
+}
+
+// GetUser finds a given user and her greets (GET "/user/:name")
+func GetUser(c web.C, w http.ResponseWriter, r *http.Request) {
+ io.WriteString(w, "Gritter\n======\n\n")
+ handle := c.URLParams["name"]
+ user, ok := Users[handle]
+ if !ok {
+ http.Error(w, http.StatusText(404), 404)
+ return
+ }
+
+ user.Write(w, handle)
+
+ io.WriteString(w, "\nGreets:\n")
+ for i := len(Greets) - 1; i >= 0; i-- {
+ if Greets[i].User == handle {
+ Greets[i].Write(w)
+ }
+ }
+}
+
+// GetGreet finds a particular greet by ID (GET "/greets/\d+"). Does no bounds
+// checking, so will probably panic.
+func GetGreet(c web.C, w http.ResponseWriter, r *http.Request) {
+ id, err := strconv.Atoi(c.URLParams["id"])
+ if err != nil {
+ http.Error(w, http.StatusText(404), 404)
+ return
+ }
+ // This will panic if id is too big. Try it out!
+ greet := Greets[id]
+
+ io.WriteString(w, "Gritter\n======\n\n")
+ greet.Write(w)
+}
+
+// WaitForIt is a particularly slow handler (GET "/waitforit"). Try loading this
+// endpoint and initiating a graceful shutdown (Ctrl-C) or Einhorn reload. The
+// old server will stop accepting new connections and will attempt to kill
+// outstanding idle (keep-alive) connections, but will patiently stick around
+// for this endpoint to finish. How kind of it!
+func WaitForIt(w http.ResponseWriter, r *http.Request) {
+ io.WriteString(w, "This is going to be legend... (wait for it)\n")
+ if fl, ok := w.(http.Flusher); ok {
+ fl.Flush()
+ }
+ time.Sleep(15 * time.Second)
+ io.WriteString(w, "...dary! Legendary!\n")
+}
+
+// AdminRoot is root (GET "/admin/root"). Much secret. Very administrate. Wow.
+func AdminRoot(w http.ResponseWriter, r *http.Request) {
+ io.WriteString(w, "Gritter\n======\n\nSuper secret admin page!\n")
+}
+
+// AdminFinances would answer the question 'How are we doing?'
+// (GET "/admin/finances")
+func AdminFinances(w http.ResponseWriter, r *http.Request) {
+ io.WriteString(w, "Gritter\n======\n\nWe're broke! :(\n")
+}
+
+// NotFound is a 404 handler.
+func NotFound(w http.ResponseWriter, r *http.Request) {
+ http.Error(w, "Umm... have you tried turning it off and on again?", 404)
+}
diff --git a/vendor/github.com/zenazn/goji/example/middleware.go b/vendor/github.com/zenazn/goji/example/middleware.go
new file mode 100644
index 0000000..9652ebb
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/example/middleware.go
@@ -0,0 +1,47 @@
+package main
+
+import (
+ "encoding/base64"
+ "net/http"
+ "strings"
+
+ "github.com/zenazn/goji/web"
+)
+
+// PlainText sets the content-type of responses to text/plain.
+func PlainText(h http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/plain")
+ h.ServeHTTP(w, r)
+ }
+ return http.HandlerFunc(fn)
+}
+
+// Nobody will ever guess this!
+const Password = "admin:admin"
+
+// SuperSecure is HTTP Basic Auth middleware for super-secret admin page. Shhhh!
+func SuperSecure(c *web.C, h http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ auth := r.Header.Get("Authorization")
+ if !strings.HasPrefix(auth, "Basic ") {
+ pleaseAuth(w)
+ return
+ }
+
+ password, err := base64.StdEncoding.DecodeString(auth[6:])
+ if err != nil || string(password) != Password {
+ pleaseAuth(w)
+ return
+ }
+
+ h.ServeHTTP(w, r)
+ }
+ return http.HandlerFunc(fn)
+}
+
+func pleaseAuth(w http.ResponseWriter) {
+ w.Header().Set("WWW-Authenticate", `Basic realm="Gritter"`)
+ w.WriteHeader(http.StatusUnauthorized)
+ w.Write([]byte("Go away!\n"))
+}
diff --git a/vendor/github.com/zenazn/goji/example/models.go b/vendor/github.com/zenazn/goji/example/models.go
new file mode 100644
index 0000000..4c34c08
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/example/models.go
@@ -0,0 +1,49 @@
+package main
+
+import (
+ "fmt"
+ "io"
+ "time"
+)
+
+// A Greet is a 140-character micro-blogpost that has no resemblance whatsoever
+// to the noise a bird makes.
+type Greet struct {
+ User string `param:"user"`
+ Message string `param:"message"`
+ Time time.Time `param:"time"`
+}
+
+// Store all our greets in a big list in memory, because, let's be honest, who's
+// actually going to use a service that only allows you to post 140-character
+// messages?
+var Greets = []Greet{
+ {"carl", "Welcome to Gritter!", time.Now()},
+ {"alice", "Wanna know a secret?", time.Now()},
+ {"bob", "Okay!", time.Now()},
+ {"eve", "I'm listening...", time.Now()},
+}
+
+// Write out a representation of the greet
+func (g Greet) Write(w io.Writer) {
+ fmt.Fprintf(w, "%s\n@%s at %s\n---\n", g.Message, g.User,
+ g.Time.Format(time.UnixDate))
+}
+
+// A User is a person. It may even be someone you know. Or a rabbit. Hard to say
+// from here.
+type User struct {
+ Name, Bio string
+}
+
+// All the users we know about! There aren't very many...
+var Users = map[string]User{
+ "alice": {"Alice in Wonderland", "Eating mushrooms"},
+ "bob": {"Bob the Builder", "Making children dumber"},
+ "carl": {"Carl Jackson", "Duct tape aficionado"},
+}
+
+// Write out the user
+func (u User) Write(w io.Writer, handle string) {
+ fmt.Fprintf(w, "%s (@%s)\n%s\n", u.Name, handle, u.Bio)
+}
diff --git a/vendor/github.com/zenazn/goji/goji.go b/vendor/github.com/zenazn/goji/goji.go
new file mode 100644
index 0000000..ab278cd
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/goji.go
@@ -0,0 +1,36 @@
+/*
+Package goji provides an out-of-box web server with reasonable defaults.
+
+Example:
+ package main
+
+ import (
+ "fmt"
+ "net/http"
+
+ "github.com/zenazn/goji"
+ "github.com/zenazn/goji/web"
+ )
+
+ func hello(c web.C, w http.ResponseWriter, r *http.Request) {
+ fmt.Fprintf(w, "Hello, %s!", c.URLParams["name"])
+ }
+
+ func main() {
+ goji.Get("/hello/:name", hello)
+ goji.Serve()
+ }
+
+This package exists purely as a convenience to programmers who want to get
+started as quickly as possible. It draws almost all of its code from goji's
+subpackages, the most interesting of which is goji/web, and where most of the
+documentation for the web framework lives.
+
+A side effect of this package's ease-of-use is the fact that it is opinionated.
+If you don't like (or have outgrown) its opinions, it should be straightforward
+to use the APIs of goji's subpackages to reimplement things to your liking. Both
+methods of using this library are equally well supported.
+
+Goji requires Go 1.2 or newer.
+*/
+package goji
diff --git a/vendor/github.com/zenazn/goji/graceful/clone.go b/vendor/github.com/zenazn/goji/graceful/clone.go
new file mode 100644
index 0000000..a9027e5
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/graceful/clone.go
@@ -0,0 +1,11 @@
+// +build !go1.6
+
+package graceful
+
+import "crypto/tls"
+
+// see clone16.go
+func cloneTLSConfig(cfg *tls.Config) *tls.Config {
+ c := *cfg
+ return &c
+}
diff --git a/vendor/github.com/zenazn/goji/graceful/clone16.go b/vendor/github.com/zenazn/goji/graceful/clone16.go
new file mode 100644
index 0000000..810b3a2
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/graceful/clone16.go
@@ -0,0 +1,34 @@
+// +build go1.6
+
+package graceful
+
+import "crypto/tls"
+
+// cloneTLSConfig was taken from the Go standard library's net/http package. We
+// need it because tls.Config objects now contain a sync.Once.
+func cloneTLSConfig(cfg *tls.Config) *tls.Config {
+ if cfg == nil {
+ return &tls.Config{}
+ }
+ return &tls.Config{
+ Rand: cfg.Rand,
+ Time: cfg.Time,
+ Certificates: cfg.Certificates,
+ NameToCertificate: cfg.NameToCertificate,
+ GetCertificate: cfg.GetCertificate,
+ RootCAs: cfg.RootCAs,
+ NextProtos: cfg.NextProtos,
+ ServerName: cfg.ServerName,
+ ClientAuth: cfg.ClientAuth,
+ ClientCAs: cfg.ClientCAs,
+ InsecureSkipVerify: cfg.InsecureSkipVerify,
+ CipherSuites: cfg.CipherSuites,
+ PreferServerCipherSuites: cfg.PreferServerCipherSuites,
+ SessionTicketsDisabled: cfg.SessionTicketsDisabled,
+ SessionTicketKey: cfg.SessionTicketKey,
+ ClientSessionCache: cfg.ClientSessionCache,
+ MinVersion: cfg.MinVersion,
+ MaxVersion: cfg.MaxVersion,
+ CurvePreferences: cfg.CurvePreferences,
+ }
+}
diff --git a/vendor/github.com/zenazn/goji/graceful/einhorn.go b/vendor/github.com/zenazn/goji/graceful/einhorn.go
new file mode 100644
index 0000000..082d1c4
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/graceful/einhorn.go
@@ -0,0 +1,21 @@
+// +build !windows
+
+package graceful
+
+import (
+ "os"
+ "strconv"
+ "syscall"
+)
+
+func init() {
+ // This is a little unfortunate: goji/bind already knows whether we're
+ // running under einhorn, but we don't want to introduce a dependency
+ // between the two packages. Since the check is short enough, inlining
+ // it here seems "fine."
+ mpid, err := strconv.Atoi(os.Getenv("EINHORN_MASTER_PID"))
+ if err != nil || mpid != os.Getppid() {
+ return
+ }
+ stdSignals = append(stdSignals, syscall.SIGUSR2)
+}
diff --git a/vendor/github.com/zenazn/goji/graceful/graceful.go b/vendor/github.com/zenazn/goji/graceful/graceful.go
new file mode 100644
index 0000000..ff9b186
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/graceful/graceful.go
@@ -0,0 +1,62 @@
+/*
+Package graceful implements graceful shutdown for HTTP servers by closing idle
+connections after receiving a signal. By default, this package listens for
+interrupts (i.e., SIGINT), but when it detects that it is running under Einhorn
+it will additionally listen for SIGUSR2 as well, giving your application
+automatic support for graceful restarts/code upgrades.
+*/
+package graceful
+
+import (
+ "net"
+ "runtime"
+ "sync/atomic"
+
+ "github.com/zenazn/goji/graceful/listener"
+)
+
+// WrapListener wraps an arbitrary net.Listener for use with graceful shutdowns.
+// In the background, it uses the listener sub-package to Wrap the listener in
+// Deadline mode. If another mode of operation is desired, you should call
+// listener.Wrap yourself: this function is smart enough to not double-wrap
+// listeners.
+func WrapListener(l net.Listener) net.Listener {
+ if lt, ok := l.(*listener.T); ok {
+ appendListener(lt)
+ return lt
+ }
+
+ lt := listener.Wrap(l, listener.Deadline)
+ appendListener(lt)
+ return lt
+}
+
+func appendListener(l *listener.T) {
+ mu.Lock()
+ defer mu.Unlock()
+
+ listeners = append(listeners, l)
+}
+
+const errClosing = "use of closed network connection"
+
+// During graceful shutdown, calls to Accept will start returning errors. This
+// is inconvenient, since we know these sorts of errors are peaceful, so we
+// silently swallow them.
+func peacefulError(err error) error {
+ if atomic.LoadInt32(&closing) == 0 {
+ return err
+ }
+ // Unfortunately Go doesn't really give us a better way to select errors
+ // than this, so *shrug*.
+ if oe, ok := err.(*net.OpError); ok {
+ errOp := "accept"
+ if runtime.GOOS == "windows" {
+ errOp = "AcceptEx"
+ }
+ if oe.Op == errOp && oe.Err.Error() == errClosing {
+ return nil
+ }
+ }
+ return err
+}
diff --git a/vendor/github.com/zenazn/goji/graceful/listener/conn.go b/vendor/github.com/zenazn/goji/graceful/listener/conn.go
new file mode 100644
index 0000000..7b34b47
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/graceful/listener/conn.go
@@ -0,0 +1,151 @@
+package listener
+
+import (
+ "errors"
+ "io"
+ "net"
+ "sync"
+ "time"
+)
+
+type conn struct {
+ net.Conn
+
+ shard *shard
+ mode mode
+
+ mu sync.Mutex // Protects the state machine below
+ busy bool // connection is in use (i.e., not idle)
+ closed bool // connection is closed
+ disowned bool // if true, this connection is no longer under our management
+}
+
+// This intentionally looks a lot like the one in package net.
+var errClosing = errors.New("use of closed network connection")
+
+func (c *conn) init() error {
+ c.shard.wg.Add(1)
+ if shouldExit := c.shard.track(c); shouldExit {
+ c.Close()
+ return errClosing
+ }
+ return nil
+}
+
+func (c *conn) Read(b []byte) (n int, err error) {
+ defer func() {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ if c.disowned {
+ return
+ }
+
+ // This protects against a Close/Read race. We're not really
+ // concerned about the general case (it's fundamentally racy),
+ // but are mostly trying to prevent a race between a new request
+ // getting read off the wire in one thread while the connection
+ // is being gracefully shut down in another.
+ if c.closed && err == nil {
+ n = 0
+ err = errClosing
+ return
+ }
+
+ if c.mode != Manual && !c.busy && !c.closed {
+ c.busy = true
+ c.shard.markInUse(c)
+ }
+ }()
+
+ return c.Conn.Read(b)
+}
+
+func (c *conn) Close() error {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ if c.disowned {
+ return c.Conn.Close()
+ } else if c.closed {
+ return errClosing
+ }
+
+ c.closed = true
+ c.shard.disown(c)
+ defer c.shard.wg.Done()
+
+ return c.Conn.Close()
+}
+
+func (c *conn) SetReadDeadline(t time.Time) error {
+ c.mu.Lock()
+ if !c.disowned && c.mode == Deadline {
+ defer c.markIdle()
+ }
+ c.mu.Unlock()
+ return c.Conn.SetReadDeadline(t)
+}
+
+func (c *conn) ReadFrom(r io.Reader) (int64, error) {
+ return io.Copy(c.Conn, r)
+}
+
+func (c *conn) markIdle() {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ if !c.busy {
+ return
+ }
+ c.busy = false
+
+ if exit := c.shard.markIdle(c); exit && !c.closed && !c.disowned {
+ c.closed = true
+ c.shard.disown(c)
+ defer c.shard.wg.Done()
+ c.Conn.Close()
+ return
+ }
+}
+
+func (c *conn) markInUse() {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ if !c.busy && !c.closed && !c.disowned {
+ c.busy = true
+ c.shard.markInUse(c)
+ }
+}
+
+func (c *conn) closeIfIdle() error {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ if !c.busy && !c.closed && !c.disowned {
+ c.closed = true
+ c.shard.disown(c)
+ defer c.shard.wg.Done()
+ return c.Conn.Close()
+ }
+
+ return nil
+}
+
+var errAlreadyDisowned = errors.New("listener: conn already disowned")
+
+func (c *conn) disown() error {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ if c.disowned {
+ return errAlreadyDisowned
+ }
+
+ c.shard.disown(c)
+ c.disowned = true
+ c.shard.wg.Done()
+
+ return nil
+}
diff --git a/vendor/github.com/zenazn/goji/graceful/listener/listener.go b/vendor/github.com/zenazn/goji/graceful/listener/listener.go
new file mode 100644
index 0000000..6c9c477
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/graceful/listener/listener.go
@@ -0,0 +1,178 @@
+/*
+Package listener provides a way to incorporate graceful shutdown to any
+net.Listener.
+
+This package provides low-level primitives, not a high-level API. If you're
+looking for a package that provides graceful shutdown for HTTP servers, I
+recommend this package's parent package, github.com/zenazn/goji/graceful.
+*/
+package listener
+
+import (
+ "errors"
+ "net"
+ "runtime"
+ "sync"
+ "sync/atomic"
+)
+
+type mode int8
+
+const (
+ // Manual mode is completely manual: users must use use MarkIdle and
+ // MarkInUse to indicate when connections are busy servicing requests or
+ // are eligible for termination.
+ Manual mode = iota
+ // Automatic mode is what most users probably want: calling Read on a
+ // connection will mark it as in use, but users must manually call
+ // MarkIdle to indicate when connections may be safely closed.
+ Automatic
+ // Deadline mode is like automatic mode, except that calling
+ // SetReadDeadline on a connection will also mark it as being idle. This
+ // is useful for many servers like net/http, where SetReadDeadline is
+ // used to implement read timeouts on new requests.
+ Deadline
+)
+
+// Wrap a net.Listener, returning a net.Listener which supports idle connection
+// tracking and shutdown. Listeners can be placed in to one of three modes,
+// exported as variables from this package: most users will probably want the
+// "Automatic" mode.
+func Wrap(l net.Listener, m mode) *T {
+ t := &T{
+ l: l,
+ mode: m,
+ // To keep the expected contention rate constant we'd have to
+ // grow this as numcpu**2. In practice, CPU counts don't
+ // generally grow without bound, and contention is probably
+ // going to be small enough that nobody cares anyways.
+ shards: make([]shard, 2*runtime.NumCPU()),
+ }
+ for i := range t.shards {
+ t.shards[i].init(t)
+ }
+ return t
+}
+
+// T is the type of this package's graceful listeners.
+type T struct {
+ mu sync.Mutex
+ l net.Listener
+
+ // TODO(carl): a count of currently outstanding connections.
+ connCount uint64
+ shards []shard
+
+ mode mode
+}
+
+var _ net.Listener = &T{}
+
+// Accept waits for and returns the next connection to the listener. The
+// returned net.Conn's idleness is tracked, and idle connections can be closed
+// from the associated T.
+func (t *T) Accept() (net.Conn, error) {
+ c, err := t.l.Accept()
+ if err != nil {
+ return nil, err
+ }
+
+ connID := atomic.AddUint64(&t.connCount, 1)
+ shard := &t.shards[int(connID)%len(t.shards)]
+ wc := &conn{
+ Conn: c,
+ shard: shard,
+ mode: t.mode,
+ }
+
+ if err = wc.init(); err != nil {
+ return nil, err
+ }
+ return wc, nil
+}
+
+// Addr returns the wrapped listener's network address.
+func (t *T) Addr() net.Addr {
+ return t.l.Addr()
+}
+
+// Close closes the wrapped listener.
+func (t *T) Close() error {
+ return t.l.Close()
+}
+
+// CloseIdle closes all connections that are currently marked as being idle. It,
+// however, makes no attempt to wait for in-use connections to die, or to close
+// connections which become idle in the future. Call this function if you're
+// interested in shedding useless connections, but otherwise wish to continue
+// serving requests.
+func (t *T) CloseIdle() error {
+ for i := range t.shards {
+ t.shards[i].closeConns(false, false)
+ }
+ // Not sure if returning errors is actually useful here :/
+ return nil
+}
+
+// Drain immediately closes all idle connections, prevents new connections from
+// being accepted, and waits for all outstanding connections to finish.
+//
+// Once a listener has been drained, there is no way to re-enable it. You
+// probably want to Close the listener before draining it, otherwise new
+// connections will be accepted and immediately closed.
+func (t *T) Drain() error {
+ for i := range t.shards {
+ t.shards[i].closeConns(false, true)
+ }
+ for i := range t.shards {
+ t.shards[i].wait()
+ }
+ return nil
+}
+
+// DrainAll closes all connections currently tracked by this listener (both idle
+// and in-use connections), and prevents new connections from being accepted.
+// Disowned connections are not closed.
+func (t *T) DrainAll() error {
+ for i := range t.shards {
+ t.shards[i].closeConns(true, true)
+ }
+ for i := range t.shards {
+ t.shards[i].wait()
+ }
+ return nil
+}
+
+var errNotManaged = errors.New("listener: passed net.Conn is not managed by this package")
+
+// Disown causes a connection to no longer be tracked by the listener. The
+// passed connection must have been returned by a call to Accept from this
+// listener.
+func Disown(c net.Conn) error {
+ if cn, ok := c.(*conn); ok {
+ return cn.disown()
+ }
+ return errNotManaged
+}
+
+// MarkIdle marks the given connection as being idle, and therefore eligible for
+// closing at any time. The passed connection must have been returned by a call
+// to Accept from this listener.
+func MarkIdle(c net.Conn) error {
+ if cn, ok := c.(*conn); ok {
+ cn.markIdle()
+ return nil
+ }
+ return errNotManaged
+}
+
+// MarkInUse marks this connection as being in use, removing it from the set of
+// connections which are eligible for closing. The passed connection must have
+// been returned by a call to Accept from this listener.
+func MarkInUse(c net.Conn) error {
+ if cn, ok := c.(*conn); ok {
+ cn.markInUse()
+ return nil
+ }
+ return errNotManaged
+}
diff --git a/vendor/github.com/zenazn/goji/graceful/listener/shard.go b/vendor/github.com/zenazn/goji/graceful/listener/shard.go
new file mode 100644
index 0000000..a9addad
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/graceful/listener/shard.go
@@ -0,0 +1,98 @@
+package listener
+
+import "sync"
+
+type shard struct {
+ l *T
+
+ mu sync.Mutex
+ idle map[*conn]struct{}
+ all map[*conn]struct{}
+ wg sync.WaitGroup
+ drain bool
+}
+
+// We pretty aggressively preallocate set entries in the hopes that we never
+// have to allocate memory with the lock held. This is definitely a premature
+// optimization and is probably misguided, but luckily it costs us essentially
+// nothing.
+const prealloc = 2048
+
+func (s *shard) init(l *T) {
+ s.l = l
+ s.idle = make(map[*conn]struct{}, prealloc)
+ s.all = make(map[*conn]struct{}, prealloc)
+}
+
+func (s *shard) track(c *conn) (shouldClose bool) {
+ s.mu.Lock()
+ if s.drain {
+ s.mu.Unlock()
+ return true
+ }
+ s.all[c] = struct{}{}
+ s.idle[c] = struct{}{}
+ s.mu.Unlock()
+ return false
+}
+
+func (s *shard) disown(c *conn) {
+ s.mu.Lock()
+ delete(s.all, c)
+ delete(s.idle, c)
+ s.mu.Unlock()
+}
+
+func (s *shard) markIdle(c *conn) (shouldClose bool) {
+ s.mu.Lock()
+ if s.drain {
+ s.mu.Unlock()
+ return true
+ }
+ s.idle[c] = struct{}{}
+ s.mu.Unlock()
+ return false
+}
+
+func (s *shard) markInUse(c *conn) {
+ s.mu.Lock()
+ delete(s.idle, c)
+ s.mu.Unlock()
+}
+
+func (s *shard) closeConns(all, drain bool) {
+ s.mu.Lock()
+ if drain {
+ s.drain = true
+ }
+ set := make(map[*conn]struct{}, len(s.all))
+ if all {
+ for c := range s.all {
+ set[c] = struct{}{}
+ }
+ } else {
+ for c := range s.idle {
+ set[c] = struct{}{}
+ }
+ }
+ // We have to drop the shard lock here to avoid deadlock: we cannot
+ // acquire the shard lock after the connection lock, and the closeIfIdle
+ // call below will grab a connection lock.
+ s.mu.Unlock()
+
+ for c := range set {
+ // This might return an error (from Close), but I don't think we
+ // can do anything about it, so let's just pretend it didn't
+ // happen. (I also expect that most errors returned in this way
+ // are going to be pretty boring)
+ if all {
+ c.Close()
+ } else {
+ c.closeIfIdle()
+ }
+ }
+}
+
+func (s *shard) wait() {
+ s.wg.Wait()
+}
diff --git a/vendor/github.com/zenazn/goji/graceful/middleware.go b/vendor/github.com/zenazn/goji/graceful/middleware.go
new file mode 100644
index 0000000..94edfe3
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/graceful/middleware.go
@@ -0,0 +1,103 @@
+// +build !go1.3
+
+package graceful
+
+import (
+ "bufio"
+ "io"
+ "net"
+ "net/http"
+ "sync/atomic"
+
+ "github.com/zenazn/goji/graceful/listener"
+)
+
+// Middleware provides functionality similar to net/http.Server's
+// SetKeepAlivesEnabled in Go 1.3, but in Go 1.2.
+func middleware(h http.Handler) http.Handler {
+ if h == nil {
+ return nil
+ }
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ _, cn := w.(http.CloseNotifier)
+ _, fl := w.(http.Flusher)
+ _, hj := w.(http.Hijacker)
+ _, rf := w.(io.ReaderFrom)
+
+ bw := basicWriter{ResponseWriter: w}
+
+ if cn && fl && hj && rf {
+ h.ServeHTTP(&fancyWriter{bw}, r)
+ } else {
+ h.ServeHTTP(&bw, r)
+ }
+ if !bw.headerWritten {
+ bw.maybeClose()
+ }
+ })
+}
+
+type basicWriter struct {
+ http.ResponseWriter
+ headerWritten bool
+}
+
+func (b *basicWriter) maybeClose() {
+ b.headerWritten = true
+ if atomic.LoadInt32(&closing) != 0 {
+ b.ResponseWriter.Header().Set("Connection", "close")
+ }
+}
+
+func (b *basicWriter) WriteHeader(code int) {
+ b.maybeClose()
+ b.ResponseWriter.WriteHeader(code)
+}
+
+func (b *basicWriter) Write(buf []byte) (int, error) {
+ if !b.headerWritten {
+ b.maybeClose()
+ }
+ return b.ResponseWriter.Write(buf)
+}
+
+func (b *basicWriter) Unwrap() http.ResponseWriter {
+ return b.ResponseWriter
+}
+
+// Optimize for the common case of a ResponseWriter that supports all three of
+// CloseNotifier, Flusher, and Hijacker.
+type fancyWriter struct {
+ basicWriter
+}
+
+func (f *fancyWriter) CloseNotify() <-chan bool {
+ cn := f.basicWriter.ResponseWriter.(http.CloseNotifier)
+ return cn.CloseNotify()
+}
+func (f *fancyWriter) Flush() {
+ fl := f.basicWriter.ResponseWriter.(http.Flusher)
+ fl.Flush()
+}
+func (f *fancyWriter) Hijack() (c net.Conn, b *bufio.ReadWriter, e error) {
+ hj := f.basicWriter.ResponseWriter.(http.Hijacker)
+ c, b, e = hj.Hijack()
+
+ if e == nil {
+ e = listener.Disown(c)
+ }
+
+ return
+}
+func (f *fancyWriter) ReadFrom(r io.Reader) (int64, error) {
+ rf := f.basicWriter.ResponseWriter.(io.ReaderFrom)
+ if !f.basicWriter.headerWritten {
+ f.basicWriter.maybeClose()
+ }
+ return rf.ReadFrom(r)
+}
+
+var _ http.CloseNotifier = &fancyWriter{}
+var _ http.Flusher = &fancyWriter{}
+var _ http.Hijacker = &fancyWriter{}
+var _ io.ReaderFrom = &fancyWriter{}
diff --git a/vendor/github.com/zenazn/goji/graceful/serve.go b/vendor/github.com/zenazn/goji/graceful/serve.go
new file mode 100644
index 0000000..edb2a53
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/graceful/serve.go
@@ -0,0 +1,33 @@
+// +build !go1.3
+
+package graceful
+
+import (
+ "net"
+ "net/http"
+ "time"
+
+ "github.com/zenazn/goji/graceful/listener"
+)
+
+// About 200 years, also known as "forever"
+const forever time.Duration = 200 * 365 * 24 * time.Hour
+
+// Serve behaves like the method on net/http.Server with the same name.
+func (srv *Server) Serve(l net.Listener) error {
+ // Spawn a shadow http.Server to do the actual servering. We do this
+ // because we need to sketch on some of the parameters you passed in,
+ // and it's nice to keep our sketching to ourselves.
+ shadow := *(*http.Server)(srv)
+
+ if shadow.ReadTimeout == 0 {
+ shadow.ReadTimeout = forever
+ }
+ shadow.Handler = middleware(shadow.Handler)
+
+ wrap := listener.Wrap(l, listener.Deadline)
+ appendListener(wrap)
+
+ err := shadow.Serve(wrap)
+ return peacefulError(err)
+}
diff --git a/vendor/github.com/zenazn/goji/graceful/serve13.go b/vendor/github.com/zenazn/goji/graceful/serve13.go
new file mode 100644
index 0000000..68cac04
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/graceful/serve13.go
@@ -0,0 +1,76 @@
+// +build go1.3
+
+package graceful
+
+import (
+ "log"
+ "net"
+ "net/http"
+
+ "github.com/zenazn/goji/graceful/listener"
+)
+
+// This is a slightly hacky shim to disable keepalives when shutting a server
+// down. We could have added extra functionality in listener or signal.go to
+// deal with this case, but this seems simpler.
+type gracefulServer struct {
+ net.Listener
+ s *http.Server
+}
+
+func (g gracefulServer) Close() error {
+ g.s.SetKeepAlivesEnabled(false)
+ return g.Listener.Close()
+}
+
+// A chaining http.ConnState wrapper
+type connState func(net.Conn, http.ConnState)
+
+func (c connState) Wrap(nc net.Conn, s http.ConnState) {
+ // There are a few other states defined, most notably StateActive.
+ // Unfortunately it doesn't look like it's possible to make use of
+ // StateActive to implement graceful shutdown, since StateActive is set
+ // after a complete request has been read off the wire with an intent to
+ // process it. If we were to race a graceful shutdown against a
+ // connection that was just read off the wire (but not yet in
+ // StateActive), we would accidentally close the connection out from
+ // underneath an active request.
+ //
+ // We already needed to work around this for Go 1.2 by shimming out a
+ // full net.Conn object, so we can just fall back to the old behavior
+ // there.
+ //
+ // I started a golang-nuts thread about this here:
+ // https://groups.google.com/forum/#!topic/golang-nuts/Xi8yjBGWfCQ
+ // I'd be very eager to find a better way to do this, so reach out to me
+ // if you have any ideas.
+ switch s {
+ case http.StateIdle:
+ if err := listener.MarkIdle(nc); err != nil {
+ log.Printf("error marking conn as idle: %v", err)
+ }
+ case http.StateHijacked:
+ if err := listener.Disown(nc); err != nil {
+ log.Printf("error disowning hijacked conn: %v", err)
+ }
+ }
+ if c != nil {
+ c(nc, s)
+ }
+}
+
+// Serve behaves like the method on net/http.Server with the same name.
+func (srv *Server) Serve(l net.Listener) error {
+ // Spawn a shadow http.Server to do the actual servering. We do this
+ // because we need to sketch on some of the parameters you passed in,
+ // and it's nice to keep our sketching to ourselves.
+ shadow := *(*http.Server)(srv)
+ shadow.ConnState = connState(shadow.ConnState).Wrap
+
+ l = gracefulServer{l, &shadow}
+ wrap := listener.Wrap(l, listener.Automatic)
+ appendListener(wrap)
+
+ err := shadow.Serve(wrap)
+ return peacefulError(err)
+}
diff --git a/vendor/github.com/zenazn/goji/graceful/server.go b/vendor/github.com/zenazn/goji/graceful/server.go
new file mode 100644
index 0000000..ae9a5fb
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/graceful/server.go
@@ -0,0 +1,108 @@
+package graceful
+
+import (
+ "crypto/tls"
+ "net"
+ "net/http"
+ "time"
+)
+
+// Most of the code here is lifted straight from net/http
+
+// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
+// connections. It's used by ListenAndServe and ListenAndServeTLS so
+// dead TCP connections (e.g. closing laptop mid-download) eventually
+// go away.
+type tcpKeepAliveListener struct {
+ *net.TCPListener
+}
+
+func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
+ tc, err := ln.AcceptTCP()
+ if err != nil {
+ return
+ }
+ tc.SetKeepAlive(true)
+ tc.SetKeepAlivePeriod(3 * time.Minute)
+ return tc, nil
+}
+
+// A Server is exactly the same as an http.Server, but provides more graceful
+// implementations of its methods.
+type Server http.Server
+
+// ListenAndServe behaves like the method on net/http.Server with the same name.
+func (srv *Server) ListenAndServe() error {
+ addr := srv.Addr
+ if addr == "" {
+ addr = ":http"
+ }
+ ln, err := net.Listen("tcp", addr)
+ if err != nil {
+ return err
+ }
+ return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
+}
+
+// ListenAndServeTLS behaves like the method on net/http.Server with the same
+// name. Unlike the method of the same name on http.Server, this function
+// defaults to enforcing TLS 1.0 or higher in order to address the POODLE
+// vulnerability. Users who wish to enable SSLv3 must do so by supplying a
+// TLSConfig explicitly.
+func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
+ addr := srv.Addr
+ if addr == "" {
+ addr = ":https"
+ }
+ config := &tls.Config{
+ MinVersion: tls.VersionTLS10,
+ }
+ if srv.TLSConfig != nil {
+ config = cloneTLSConfig(srv.TLSConfig)
+ }
+ if config.NextProtos == nil {
+ config.NextProtos = []string{"http/1.1"}
+ }
+
+ var err error
+ config.Certificates = make([]tls.Certificate, 1)
+ config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
+ if err != nil {
+ return err
+ }
+
+ ln, err := net.Listen("tcp", addr)
+ if err != nil {
+ return err
+ }
+
+ tlsListener := tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, config)
+ return srv.Serve(tlsListener)
+}
+
+// ListenAndServe behaves exactly like the net/http function of the same name.
+func ListenAndServe(addr string, handler http.Handler) error {
+ server := &Server{Addr: addr, Handler: handler}
+ return server.ListenAndServe()
+}
+
+// ListenAndServeTLS behaves almost exactly like the net/http function of the
+// same name. Unlike net/http, however, this function defaults to enforcing TLS
+// 1.0 or higher in order to address the POODLE vulnerability. Users who wish to
+// enable SSLv3 must do so by explicitly instantiating a server with an
+// appropriately configured TLSConfig property.
+func ListenAndServeTLS(addr, certfile, keyfile string, handler http.Handler) error {
+ server := &Server{Addr: addr, Handler: handler}
+ return server.ListenAndServeTLS(certfile, keyfile)
+}
+
+// Serve mostly behaves like the net/http function of the same name, except that
+// if the passed listener is a net.TCPListener, TCP keep-alives are enabled on
+// accepted connections.
+func Serve(l net.Listener, handler http.Handler) error {
+ if tl, ok := l.(*net.TCPListener); ok {
+ l = tcpKeepAliveListener{tl}
+ }
+ server := &Server{Handler: handler}
+ return server.Serve(l)
+}
diff --git a/vendor/github.com/zenazn/goji/graceful/signal.go b/vendor/github.com/zenazn/goji/graceful/signal.go
new file mode 100644
index 0000000..60612b8
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/graceful/signal.go
@@ -0,0 +1,197 @@
+package graceful
+
+import (
+ "os"
+ "os/signal"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/zenazn/goji/graceful/listener"
+)
+
+var mu sync.Mutex // protects everything that follows
+var listeners = make([]*listener.T, 0)
+var prehooks = make([]func(), 0)
+var posthooks = make([]func(), 0)
+var closing int32
+var doubleKick, timeout time.Duration
+
+var wait = make(chan struct{})
+var stdSignals = []os.Signal{os.Interrupt}
+var sigchan = make(chan os.Signal, 1)
+
+// HandleSignals installs signal handlers for a set of standard signals. By
+// default, this set only includes keyboard interrupts, however when the package
+// detects that it is running under Einhorn, a SIGUSR2 handler is installed as
+// well.
+func HandleSignals() {
+ AddSignal(stdSignals...)
+}
+
+// AddSignal adds the given signal to the set of signals that trigger a graceful
+// shutdown.
+func AddSignal(sig ...os.Signal) {
+ signal.Notify(sigchan, sig...)
+}
+
+// ResetSignals resets the list of signals that trigger a graceful shutdown.
+func ResetSignals() {
+ signal.Stop(sigchan)
+}
+
+// PreHook registers a function to be called before any of this package's normal
+// shutdown actions. All listeners will be called in the order they were added,
+// from a single goroutine.
+func PreHook(f func()) {
+ mu.Lock()
+ defer mu.Unlock()
+
+ prehooks = append(prehooks, f)
+}
+
+// PostHook registers a function to be called after all of this package's normal
+// shutdown actions. All listeners will be called in the order they were added,
+// from a single goroutine, and are guaranteed to be called after all listening
+// connections have been closed, but before Wait() returns.
+//
+// If you've Hijacked any connections that must be gracefully shut down in some
+// other way (since this library disowns all hijacked connections), it's
+// reasonable to use a PostHook to signal and wait for them.
+func PostHook(f func()) {
+ mu.Lock()
+ defer mu.Unlock()
+
+ posthooks = append(posthooks, f)
+}
+
+// Shutdown manually triggers a shutdown from your application. Like Wait,
+// blocks until all connections have gracefully shut down.
+func Shutdown() {
+ shutdown(false)
+}
+
+// ShutdownNow triggers an immediate shutdown from your application. All
+// connections (not just those that are idle) are immediately closed, even if
+// they are in the middle of serving a request.
+func ShutdownNow() {
+ shutdown(true)
+}
+
+// DoubleKickWindow sets the length of the window during which two back-to-back
+// signals are treated as an especially urgent or forceful request to exit
+// (i.e., ShutdownNow instead of Shutdown). Signals delivered more than this
+// duration apart are treated as separate requests to exit gracefully as usual.
+//
+// Setting DoubleKickWindow to 0 disables the feature.
+func DoubleKickWindow(d time.Duration) {
+ if d < 0 {
+ return
+ }
+ mu.Lock()
+ defer mu.Unlock()
+
+ doubleKick = d
+}
+
+// Timeout sets the maximum amount of time package graceful will wait for
+// connections to gracefully shut down after receiving a signal. After this
+// timeout, connections will be forcefully shut down (similar to calling
+// ShutdownNow).
+//
+// Setting Timeout to 0 disables the feature.
+func Timeout(d time.Duration) {
+ if d < 0 {
+ return
+ }
+ mu.Lock()
+ defer mu.Unlock()
+
+ timeout = d
+}
+
+// Wait for all connections to gracefully shut down. This is commonly called at
+// the bottom of the main() function to prevent the program from exiting
+// prematurely.
+func Wait() {
+ <-wait
+}
+
+func init() {
+ go sigLoop()
+}
+func sigLoop() {
+ var last time.Time
+ for {
+ <-sigchan
+ now := time.Now()
+ mu.Lock()
+ force := doubleKick != 0 && now.Sub(last) < doubleKick
+ if t := timeout; t != 0 && !force {
+ go func() {
+ time.Sleep(t)
+ shutdown(true)
+ }()
+ }
+ mu.Unlock()
+ go shutdown(force)
+ last = now
+ }
+}
+
+var preOnce, closeOnce, forceOnce, postOnce, notifyOnce sync.Once
+
+func shutdown(force bool) {
+ preOnce.Do(func() {
+ mu.Lock()
+ defer mu.Unlock()
+ for _, f := range prehooks {
+ f()
+ }
+ })
+
+ if force {
+ forceOnce.Do(func() {
+ closeListeners(force)
+ })
+ } else {
+ closeOnce.Do(func() {
+ closeListeners(force)
+ })
+ }
+
+ postOnce.Do(func() {
+ mu.Lock()
+ defer mu.Unlock()
+ for _, f := range posthooks {
+ f()
+ }
+ })
+
+ notifyOnce.Do(func() {
+ close(wait)
+ })
+}
+
+func closeListeners(force bool) {
+ atomic.StoreInt32(&closing, 1)
+
+ var wg sync.WaitGroup
+ defer wg.Wait()
+
+ mu.Lock()
+ defer mu.Unlock()
+ wg.Add(len(listeners))
+
+ for _, l := range listeners {
+ go func(l *listener.T) {
+ defer wg.Done()
+ l.Close()
+ if force {
+ l.DrainAll()
+ } else {
+ l.Drain()
+ }
+ }(l)
+ }
+}
diff --git a/vendor/github.com/zenazn/goji/serve.go b/vendor/github.com/zenazn/goji/serve.go
new file mode 100644
index 0000000..da73a9b
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/serve.go
@@ -0,0 +1,64 @@
+// +build !appengine
+
+package goji
+
+import (
+ "crypto/tls"
+ "flag"
+ "log"
+ "net"
+ "net/http"
+ "time"
+
+ "github.com/zenazn/goji/bind"
+ "github.com/zenazn/goji/graceful"
+)
+
+func init() {
+ bind.WithFlag()
+ if fl := log.Flags(); fl&log.Ltime != 0 {
+ log.SetFlags(fl | log.Lmicroseconds)
+ }
+ graceful.DoubleKickWindow(2 * time.Second)
+}
+
+// Serve starts Goji using reasonable defaults.
+func Serve() {
+ if !flag.Parsed() {
+ flag.Parse()
+ }
+
+ ServeListener(bind.Default())
+}
+
+// Like Serve, but enables TLS using the given config.
+func ServeTLS(config *tls.Config) {
+ if !flag.Parsed() {
+ flag.Parse()
+ }
+
+ ServeListener(tls.NewListener(bind.Default(), config))
+}
+
+// Like Serve, but runs Goji on top of an arbitrary net.Listener.
+func ServeListener(listener net.Listener) {
+ DefaultMux.Compile()
+ // Install our handler at the root of the standard net/http default mux.
+ // This allows packages like expvar to continue working as expected.
+ http.Handle("/", DefaultMux)
+
+ log.Println("Starting Goji on", listener.Addr())
+
+ graceful.HandleSignals()
+ bind.Ready()
+ graceful.PreHook(func() { log.Printf("Goji received signal, gracefully stopping") })
+ graceful.PostHook(func() { log.Printf("Goji stopped") })
+
+ err := graceful.Serve(listener, http.DefaultServeMux)
+
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ graceful.Wait()
+}
diff --git a/vendor/github.com/zenazn/goji/serve_appengine.go b/vendor/github.com/zenazn/goji/serve_appengine.go
new file mode 100644
index 0000000..88dc7a8
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/serve_appengine.go
@@ -0,0 +1,23 @@
+// +build appengine
+
+package goji
+
+import (
+ "log"
+ "net/http"
+)
+
+func init() {
+ if fl := log.Flags(); fl&log.Ltime != 0 {
+ log.SetFlags(fl | log.Lmicroseconds)
+ }
+}
+
+// Serve starts Goji using reasonable defaults.
+func Serve() {
+ DefaultMux.Compile()
+ // Install our handler at the root of the standard net/http default mux.
+ // This is required for App Engine, and also allows packages like expvar
+ // to continue working as expected.
+ http.Handle("/", DefaultMux)
+}
diff --git a/vendor/github.com/zenazn/goji/web/atomic.go b/vendor/github.com/zenazn/goji/web/atomic.go
new file mode 100644
index 0000000..795d8e5
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/atomic.go
@@ -0,0 +1,18 @@
+// +build !appengine
+
+package web
+
+import (
+ "sync/atomic"
+ "unsafe"
+)
+
+func (rt *router) getMachine() *routeMachine {
+ ptr := (*unsafe.Pointer)(unsafe.Pointer(&rt.machine))
+ sm := (*routeMachine)(atomic.LoadPointer(ptr))
+ return sm
+}
+func (rt *router) setMachine(m *routeMachine) {
+ ptr := (*unsafe.Pointer)(unsafe.Pointer(&rt.machine))
+ atomic.StorePointer(ptr, unsafe.Pointer(m))
+}
diff --git a/vendor/github.com/zenazn/goji/web/atomic_appengine.go b/vendor/github.com/zenazn/goji/web/atomic_appengine.go
new file mode 100644
index 0000000..027127a
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/atomic_appengine.go
@@ -0,0 +1,14 @@
+// +build appengine
+
+package web
+
+func (rt *router) getMachine() *routeMachine {
+ rt.lock.Lock()
+ defer rt.lock.Unlock()
+ return rt.machine
+}
+
+// We always hold the lock when calling setMachine.
+func (rt *router) setMachine(m *routeMachine) {
+ rt.machine = m
+}
diff --git a/vendor/github.com/zenazn/goji/web/bytecode_compiler.go b/vendor/github.com/zenazn/goji/web/bytecode_compiler.go
new file mode 100644
index 0000000..b6f52b1
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/bytecode_compiler.go
@@ -0,0 +1,265 @@
+package web
+
+/*
+This file implements a fast router by encoding a list of routes first into a
+pseudo-trie, then encoding that pseudo-trie into a state machine realized as
+a routing bytecode.
+
+The most interesting part of this router is not its speed (it is quite fast),
+but the guarantees it provides. In a naive router, routes are examined one after
+another until a match is found, and this is the programming model we want to
+support. For any given request ("GET /hello/carl"), there is a list of
+"plausible" routes: routes which match the method ("GET"), and which have a
+prefix that is a prefix of the requested path ("/" and "/hello/", for instance,
+but not "/foobar"). Patterns also have some amount of arbitrary code associated
+with them, which tells us whether or not the route matched. Just like the naive
+router, our goal is to call each plausible pattern, in the order they were
+added, until we find one that matches. The "fast" part here is being smart about
+which non-plausible routes we can skip.
+
+First, we sort routes using a pairwise comparison function: sorting occurs as
+normal on the prefixes, with the caveat that a route may not be moved past a
+route that might also match the same string. Among other things, this means
+we're forced to use particularly dumb sorting algorithms, but it only has to
+happen once, and there probably aren't even that many routes to begin with. This
+logic appears inline in the router's handle() function.
+
+We then build a pseudo-trie from the sorted list of routes. It's not quite a
+normal trie because there are certain routes we cannot reorder around other
+routes (since we're providing identical semantics to the naive router), but it's
+close enough and the basic idea is the same.
+
+Finally, we lower this psuedo-trie from its tree representation to a state
+machine bytecode. The bytecode is pretty simple: it contains up to three bytes,
+a choice of a bunch of flags, and an index. The state machine is pretty simple:
+if the bytes match the next few bytes after the cursor, the instruction matches,
+and the state machine advances to the next instruction. If it does not match, it
+jumps to the instruction at the index. Various flags modify this basic behavior,
+the documentation for which can be found below.
+
+The thing we're optimizing for here over pretty much everything else is memory
+locality. We make an effort to lay out both the trie child selection logic and
+the matching of long strings consecutively in memory, making both operations
+very cheap. In fact, our matching logic isn't particularly asymptotically good,
+but in practice the benefits of memory locality outweigh just about everything
+else.
+
+Unfortunately, the code implementing all of this is pretty bad (both inefficient
+and hard to read). Maybe someday I'll come and take a second pass at it.
+*/
+type state struct {
+ mode smMode
+ bs [3]byte
+ i int32
+}
+type stateMachine []state
+
+type smMode uint8
+
+// Many combinations of smModes don't make sense, but since this is interal to
+// the library I don't feel like documenting them.
+const (
+ // The two low bits of the mode are used as a length of how many bytes
+ // of bs are used. If the length is 0, the node is treated as a
+ // wildcard.
+ smLengthMask smMode = 3
+)
+
+const (
+ // Jump to the given index on a match. Ordinarily, the state machine
+ // will jump to the state given by the index if the characters do not
+ // match.
+ smJumpOnMatch smMode = 4 << iota
+ // The index is the index of a route to try. If running the route fails,
+ // the state machine advances by one.
+ smRoute
+ // Reset the state machine's cursor into the input string to the state's
+ // index value.
+ smSetCursor
+ // If this bit is set, the machine transitions into a non-accepting
+ // state if it matches.
+ smFail
+)
+
+type trie struct {
+ prefix string
+ children []trieSegment
+}
+
+// A trie segment is a route matching this point (or -1), combined with a list
+// of trie children that follow that route.
+type trieSegment struct {
+ route int
+ children []trie
+}
+
+func buildTrie(routes []route, dp, dr int) trie {
+ var t trie
+ ts := trieSegment{-1, nil}
+ for i, r := range routes {
+ if len(r.prefix) != dp {
+ continue
+ }
+
+ if i == 0 {
+ ts.route = 0
+ } else {
+ subroutes := routes[ts.route+1 : i]
+ ts.children = buildTrieSegment(subroutes, dp, dr+ts.route+1)
+ t.children = append(t.children, ts)
+ ts = trieSegment{i, nil}
+ }
+ }
+
+ // This could be a little DRYer...
+ subroutes := routes[ts.route+1:]
+ ts.children = buildTrieSegment(subroutes, dp, dr+ts.route+1)
+ t.children = append(t.children, ts)
+
+ for i := range t.children {
+ if t.children[i].route != -1 {
+ t.children[i].route += dr
+ }
+ }
+
+ return t
+}
+
+func commonPrefix(s1, s2 string) string {
+ if len(s1) > len(s2) {
+ return commonPrefix(s2, s1)
+ }
+ for i := 0; i < len(s1); i++ {
+ if s1[i] != s2[i] {
+ return s1[:i]
+ }
+ }
+ return s1
+}
+
+func buildTrieSegment(routes []route, dp, dr int) []trie {
+ if len(routes) == 0 {
+ return nil
+ }
+ var tries []trie
+
+ start := 0
+ p := routes[0].prefix[dp:]
+ for i := 1; i < len(routes); i++ {
+ ip := routes[i].prefix[dp:]
+ cp := commonPrefix(p, ip)
+ if len(cp) == 0 {
+ t := buildTrie(routes[start:i], dp+len(p), dr+start)
+ t.prefix = p
+ tries = append(tries, t)
+ start = i
+ p = ip
+ } else {
+ p = cp
+ }
+ }
+
+ t := buildTrie(routes[start:], dp+len(p), dr+start)
+ t.prefix = p
+ return append(tries, t)
+}
+
+// This is a bit confusing, since the encode method on a trie deals exclusively
+// with trieSegments (i.e., its children), and vice versa.
+//
+// These methods are also hideously inefficient, both in terms of memory usage
+// and algorithmic complexity. If it ever becomes a problem, maybe we can do
+// something smarter than stupid O(N^2) appends, but to be honest, I bet N is
+// small (it almost always is :P) and we only do it once at boot anyways.
+
+func (t trie) encode(dp, off int) stateMachine {
+ ms := make([]stateMachine, len(t.children))
+ subs := make([]stateMachine, len(t.children))
+ var l, msl, subl int
+
+ for i, ts := range t.children {
+ ms[i], subs[i] = ts.encode(dp, 0)
+ msl += len(ms[i])
+ l += len(ms[i]) + len(subs[i])
+ }
+
+ l++
+
+ m := make(stateMachine, 0, l)
+ for i, mm := range ms {
+ for j := range mm {
+ if mm[j].mode&(smRoute|smSetCursor) != 0 {
+ continue
+ }
+
+ mm[j].i += int32(off + msl + subl + 1)
+ }
+ m = append(m, mm...)
+ subl += len(subs[i])
+ }
+
+ m = append(m, state{mode: smJumpOnMatch, i: -1})
+
+ msl = 0
+ for i, sub := range subs {
+ msl += len(ms[i])
+ for j := range sub {
+ if sub[j].mode&(smRoute|smSetCursor) != 0 {
+ continue
+ }
+ if sub[j].i == -1 {
+ sub[j].i = int32(off + msl)
+ } else {
+ sub[j].i += int32(off + len(m))
+ }
+ }
+ m = append(m, sub...)
+ }
+
+ return m
+}
+
+func (ts trieSegment) encode(dp, off int) (me stateMachine, sub stateMachine) {
+ o := 1
+ if ts.route != -1 {
+ o++
+ }
+ me = make(stateMachine, len(ts.children)+o)
+
+ me[0] = state{mode: smSetCursor, i: int32(dp)}
+ if ts.route != -1 {
+ me[1] = state{mode: smRoute, i: int32(ts.route)}
+ }
+
+ for i, t := range ts.children {
+ p := t.prefix
+
+ bc := copy(me[i+o].bs[:], p)
+ me[i+o].mode = smMode(bc) | smJumpOnMatch
+ me[i+o].i = int32(off + len(sub))
+
+ for len(p) > bc {
+ var bs [3]byte
+ p = p[bc:]
+ bc = copy(bs[:], p)
+ sub = append(sub, state{bs: bs, mode: smMode(bc), i: -1})
+ }
+
+ sub = append(sub, t.encode(dp+len(t.prefix), off+len(sub))...)
+ }
+ return
+}
+
+func compile(routes []route) stateMachine {
+ if len(routes) == 0 {
+ return nil
+ }
+ t := buildTrie(routes, 0, 0)
+ m := t.encode(0, 0)
+ for i := range m {
+ if m[i].i == -1 {
+ m[i].mode = m[i].mode | smFail
+ }
+ }
+ return m
+}
diff --git a/vendor/github.com/zenazn/goji/web/bytecode_runner.go b/vendor/github.com/zenazn/goji/web/bytecode_runner.go
new file mode 100644
index 0000000..c32b16a
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/bytecode_runner.go
@@ -0,0 +1,83 @@
+package web
+
+import "net/http"
+
+type routeMachine struct {
+ sm stateMachine
+ routes []route
+}
+
+func matchRoute(route route, m method, ms *method, r *http.Request, c *C) bool {
+ if !route.pattern.Match(r, c) {
+ return false
+ }
+ *ms |= route.method
+
+ if route.method&m != 0 {
+ route.pattern.Run(r, c)
+ return true
+ }
+ return false
+}
+
+func (rm routeMachine) route(c *C, w http.ResponseWriter, r *http.Request) (method, *route) {
+ m := httpMethod(r.Method)
+ var methods method
+ p := r.URL.Path
+
+ if len(rm.sm) == 0 {
+ return methods, nil
+ }
+
+ var i int
+ for {
+ sm := rm.sm[i].mode
+ if sm&smSetCursor != 0 {
+ si := rm.sm[i].i
+ p = r.URL.Path[si:]
+ i++
+ continue
+ }
+
+ length := int(sm & smLengthMask)
+ match := false
+ if length <= len(p) {
+ bs := rm.sm[i].bs
+ switch length {
+ case 3:
+ if p[2] != bs[2] {
+ break
+ }
+ fallthrough
+ case 2:
+ if p[1] != bs[1] {
+ break
+ }
+ fallthrough
+ case 1:
+ if p[0] != bs[0] {
+ break
+ }
+ fallthrough
+ case 0:
+ p = p[length:]
+ match = true
+ }
+ }
+
+ if match && sm&smRoute != 0 {
+ si := rm.sm[i].i
+ if matchRoute(rm.routes[si], m, &methods, r, c) {
+ return 0, &rm.routes[si]
+ }
+ i++
+ } else if match != (sm&smJumpOnMatch == 0) {
+ if sm&smFail != 0 {
+ return methods, nil
+ }
+ i = int(rm.sm[i].i)
+ } else {
+ i++
+ }
+ }
+}
diff --git a/vendor/github.com/zenazn/goji/web/chanpool.go b/vendor/github.com/zenazn/goji/web/chanpool.go
new file mode 100644
index 0000000..6c53c74
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/chanpool.go
@@ -0,0 +1,31 @@
+// +build !go1.3
+
+package web
+
+// This is an alternate implementation of Go 1.3's sync.Pool.
+
+// Maximum size of the pool of spare middleware stacks
+const cPoolSize = 32
+
+type cPool chan *cStack
+
+func makeCPool() *cPool {
+ p := make(cPool, cPoolSize)
+ return &p
+}
+
+func (c cPool) alloc() *cStack {
+ select {
+ case cs := <-c:
+ return cs
+ default:
+ return nil
+ }
+}
+
+func (c cPool) release(cs *cStack) {
+ select {
+ case c <- cs:
+ default:
+ }
+}
diff --git a/vendor/github.com/zenazn/goji/web/cpool.go b/vendor/github.com/zenazn/goji/web/cpool.go
new file mode 100644
index 0000000..59f8764
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/cpool.go
@@ -0,0 +1,23 @@
+// +build go1.3
+
+package web
+
+import "sync"
+
+type cPool sync.Pool
+
+func makeCPool() *cPool {
+ return &cPool{}
+}
+
+func (c *cPool) alloc() *cStack {
+ cs := (*sync.Pool)(c).Get()
+ if cs == nil {
+ return nil
+ }
+ return cs.(*cStack)
+}
+
+func (c *cPool) release(cs *cStack) {
+ (*sync.Pool)(c).Put(cs)
+}
diff --git a/vendor/github.com/zenazn/goji/web/func_equal.go b/vendor/github.com/zenazn/goji/web/func_equal.go
new file mode 100644
index 0000000..9c8f7cb
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/func_equal.go
@@ -0,0 +1,32 @@
+package web
+
+import (
+ "reflect"
+)
+
+/*
+This is more than a little sketchtacular. Go's rules for function pointer
+equality are pretty restrictive: nil function pointers always compare equal, and
+all other pointer types never do. However, this is pretty limiting: it means
+that we can't let people reference the middleware they've given us since we have
+no idea which function they're referring to.
+
+To get better data out of Go, we sketch on the representation of interfaces. We
+happen to know that interfaces are pairs of pointers: one to the real data, one
+to data about the type. Therefore, two interfaces, including two function
+interface{}'s, point to exactly the same objects iff their interface
+representations are identical. And it turns out this is sufficient for our
+purposes.
+
+If you're curious, you can read more about the representation of functions here:
+http://golang.org/s/go11func
+We're in effect comparing the pointers of the indirect layer.
+
+This function also works on non-function values.
+*/
+func funcEqual(a, b interface{}) bool {
+ av := reflect.ValueOf(&a).Elem()
+ bv := reflect.ValueOf(&b).Elem()
+
+ return av.InterfaceData() == bv.InterfaceData()
+}
diff --git a/vendor/github.com/zenazn/goji/web/handler.go b/vendor/github.com/zenazn/goji/web/handler.go
new file mode 100644
index 0000000..746c9f0
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/handler.go
@@ -0,0 +1,42 @@
+package web
+
+import (
+ "log"
+ "net/http"
+)
+
+const unknownHandler = `Unknown handler type %T. See http://godoc.org/github.com/zenazn/goji/web#HandlerType for a list of acceptable types.`
+
+type netHTTPHandlerWrap struct{ http.Handler }
+type netHTTPHandlerFuncWrap struct {
+ fn func(http.ResponseWriter, *http.Request)
+}
+type handlerFuncWrap struct {
+ fn func(C, http.ResponseWriter, *http.Request)
+}
+
+func (h netHTTPHandlerWrap) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
+ h.Handler.ServeHTTP(w, r)
+}
+func (h netHTTPHandlerFuncWrap) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
+ h.fn(w, r)
+}
+func (h handlerFuncWrap) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
+ h.fn(c, w, r)
+}
+
+func parseHandler(h HandlerType) Handler {
+ switch f := h.(type) {
+ case func(c C, w http.ResponseWriter, r *http.Request):
+ return handlerFuncWrap{f}
+ case func(w http.ResponseWriter, r *http.Request):
+ return netHTTPHandlerFuncWrap{f}
+ case Handler:
+ return f
+ case http.Handler:
+ return netHTTPHandlerWrap{f}
+ default:
+ log.Fatalf(unknownHandler, h)
+ panic("log.Fatalf does not return")
+ }
+}
diff --git a/vendor/github.com/zenazn/goji/web/match.go b/vendor/github.com/zenazn/goji/web/match.go
new file mode 100644
index 0000000..1a44144
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/match.go
@@ -0,0 +1,66 @@
+package web
+
+// The key used to store route Matches in the Goji environment. If this key is
+// present in the environment and contains a value of type Match, routing will
+// not be performed, and the Match's Handler will be used instead.
+const MatchKey = "goji.web.Match"
+
+// Match is the type of routing matches. It is inserted into C.Env under
+// MatchKey when the Mux.Router middleware is invoked. If MatchKey is present at
+// route dispatch time, the Handler of the corresponding Match will be called
+// instead of performing routing as usual.
+//
+// By computing a Match and inserting it into the Goji environment as part of a
+// middleware stack (see Mux.Router, for instance), it is possible to customize
+// Goji's routing behavior or replace it entirely.
+type Match struct {
+ // Pattern is the Pattern that matched during routing. Will be nil if no
+ // route matched (Handler will be set to the Mux's NotFound handler)
+ Pattern Pattern
+ // The Handler corresponding to the matched pattern.
+ Handler Handler
+}
+
+// GetMatch returns the Match stored in the Goji environment, or an empty Match
+// if none exists (valid Matches always have a Handler property).
+func GetMatch(c C) Match {
+ if c.Env == nil {
+ return Match{}
+ }
+ mi, ok := c.Env[MatchKey]
+ if !ok {
+ return Match{}
+ }
+ if m, ok := mi.(Match); ok {
+ return m
+ }
+ return Match{}
+}
+
+// RawPattern returns the PatternType that was originally passed to ParsePattern
+// or any of the HTTP method functions (Get, Post, etc.).
+func (m Match) RawPattern() PatternType {
+ switch v := m.Pattern.(type) {
+ case regexpPattern:
+ return v.re
+ case stringPattern:
+ return v.raw
+ default:
+ return v
+ }
+}
+
+// RawHandler returns the HandlerType that was originally passed to the HTTP
+// method functions (Get, Post, etc.).
+func (m Match) RawHandler() HandlerType {
+ switch v := m.Handler.(type) {
+ case netHTTPHandlerWrap:
+ return v.Handler
+ case handlerFuncWrap:
+ return v.fn
+ case netHTTPHandlerFuncWrap:
+ return v.fn
+ default:
+ return v
+ }
+}
diff --git a/vendor/github.com/zenazn/goji/web/middleware.go b/vendor/github.com/zenazn/goji/web/middleware.go
new file mode 100644
index 0000000..7ec545d
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/middleware.go
@@ -0,0 +1,154 @@
+package web
+
+import (
+ "fmt"
+ "log"
+ "net/http"
+ "sync"
+)
+
+// mLayer is a single middleware stack layer. It contains a canonicalized
+// middleware representation, as well as the original function as passed to us.
+type mLayer struct {
+ fn func(*C, http.Handler) http.Handler
+ orig interface{}
+}
+
+// mStack is an entire middleware stack. It contains a slice of middleware
+// layers (outermost first) protected by a mutex, a cache of pre-built stack
+// instances, and a final routing function.
+type mStack struct {
+ lock sync.Mutex
+ stack []mLayer
+ pool *cPool
+ router internalRouter
+}
+
+type internalRouter interface {
+ route(*C, http.ResponseWriter, *http.Request)
+}
+
+/*
+cStack is a cached middleware stack instance. Constructing a middleware stack
+involves a lot of allocations: at the very least each layer will have to close
+over the layer after (inside) it and a stack N levels deep will incur at least N
+separate allocations. Instead of doing this on every request, we keep a pool of
+pre-built stacks around for reuse.
+*/
+type cStack struct {
+ C
+ m http.Handler
+ pool *cPool
+}
+
+func (s *cStack) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ s.C = C{}
+ s.m.ServeHTTP(w, r)
+}
+func (s *cStack) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
+ s.C = c
+ s.m.ServeHTTP(w, r)
+}
+
+const unknownMiddleware = `Unknown middleware type %T. See http://godoc.org/github.com/zenazn/goji/web#MiddlewareType for a list of acceptable types.`
+
+func (m *mStack) appendLayer(fn interface{}) {
+ ml := mLayer{orig: fn}
+ switch f := fn.(type) {
+ case func(http.Handler) http.Handler:
+ ml.fn = func(c *C, h http.Handler) http.Handler {
+ return f(h)
+ }
+ case func(*C, http.Handler) http.Handler:
+ ml.fn = f
+ default:
+ log.Fatalf(unknownMiddleware, fn)
+ }
+ m.stack = append(m.stack, ml)
+}
+
+func (m *mStack) findLayer(l interface{}) int {
+ for i, middleware := range m.stack {
+ if funcEqual(l, middleware.orig) {
+ return i
+ }
+ }
+ return -1
+}
+
+func (m *mStack) invalidate() {
+ m.pool = makeCPool()
+}
+
+func (m *mStack) newStack() *cStack {
+ cs := cStack{}
+ router := m.router
+
+ cs.m = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ router.route(&cs.C, w, r)
+ })
+ for i := len(m.stack) - 1; i >= 0; i-- {
+ cs.m = m.stack[i].fn(&cs.C, cs.m)
+ }
+
+ return &cs
+}
+
+func (m *mStack) alloc() *cStack {
+ p := m.pool
+ cs := p.alloc()
+ if cs == nil {
+ cs = m.newStack()
+ }
+
+ cs.pool = p
+ return cs
+}
+
+func (m *mStack) release(cs *cStack) {
+ cs.C = C{}
+ if cs.pool != m.pool {
+ return
+ }
+ cs.pool.release(cs)
+ cs.pool = nil
+}
+
+func (m *mStack) Use(middleware interface{}) {
+ m.lock.Lock()
+ defer m.lock.Unlock()
+ m.appendLayer(middleware)
+ m.invalidate()
+}
+
+func (m *mStack) Insert(middleware, before interface{}) error {
+ m.lock.Lock()
+ defer m.lock.Unlock()
+ i := m.findLayer(before)
+ if i < 0 {
+ return fmt.Errorf("web: unknown middleware %v", before)
+ }
+
+ m.appendLayer(middleware)
+ inserted := m.stack[len(m.stack)-1]
+ copy(m.stack[i+1:], m.stack[i:])
+ m.stack[i] = inserted
+
+ m.invalidate()
+ return nil
+}
+
+func (m *mStack) Abandon(middleware interface{}) error {
+ m.lock.Lock()
+ defer m.lock.Unlock()
+ i := m.findLayer(middleware)
+ if i < 0 {
+ return fmt.Errorf("web: unknown middleware %v", middleware)
+ }
+
+ copy(m.stack[i:], m.stack[i+1:])
+ m.stack = m.stack[:len(m.stack)-1 : len(m.stack)]
+
+ m.invalidate()
+ return nil
+}
diff --git a/vendor/github.com/zenazn/goji/web/middleware/envinit.go b/vendor/github.com/zenazn/goji/web/middleware/envinit.go
new file mode 100644
index 0000000..ae3b683
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/middleware/envinit.go
@@ -0,0 +1,27 @@
+package middleware
+
+import (
+ "net/http"
+
+ "github.com/zenazn/goji/web"
+)
+
+type envInit struct {
+ c *web.C
+ h http.Handler
+}
+
+func (e envInit) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ if e.c.Env == nil {
+ e.c.Env = make(map[interface{}]interface{})
+ }
+ e.h.ServeHTTP(w, r)
+}
+
+// EnvInit is a middleware that allocates an environment map if it is nil. While
+// it's impossible in general to ensure that Env is never nil in a middleware
+// stack, in most common cases placing this middleware at the top of the stack
+// will eliminate the need for repetative nil checks.
+func EnvInit(c *web.C, h http.Handler) http.Handler {
+ return envInit{c, h}
+}
diff --git a/vendor/github.com/zenazn/goji/web/middleware/logger.go b/vendor/github.com/zenazn/goji/web/middleware/logger.go
new file mode 100644
index 0000000..8bbcac8
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/middleware/logger.go
@@ -0,0 +1,92 @@
+package middleware
+
+import (
+ "bytes"
+ "log"
+ "net/http"
+ "time"
+
+ "github.com/zenazn/goji/web"
+ "github.com/zenazn/goji/web/mutil"
+)
+
+// Logger is a middleware that logs the start and end of each request, along
+// with some useful data about what was requested, what the response status was,
+// and how long it took to return. When standard output is a TTY, Logger will
+// print in color, otherwise it will print in black and white.
+//
+// Logger prints a request ID if one is provided.
+//
+// Logger has been designed explicitly to be Good Enough for use in small
+// applications and for people just getting started with Goji. It is expected
+// that applications will eventually outgrow this middleware and replace it with
+// a custom request logger, such as one that produces machine-parseable output,
+// outputs logs to a different service (e.g., syslog), or formats lines like
+// those printed elsewhere in the application.
+func Logger(c *web.C, h http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ reqID := GetReqID(*c)
+
+ printStart(reqID, r)
+
+ lw := mutil.WrapWriter(w)
+
+ t1 := time.Now()
+ h.ServeHTTP(lw, r)
+
+ if lw.Status() == 0 {
+ lw.WriteHeader(http.StatusOK)
+ }
+ t2 := time.Now()
+
+ printEnd(reqID, lw, t2.Sub(t1))
+ }
+
+ return http.HandlerFunc(fn)
+}
+
+func printStart(reqID string, r *http.Request) {
+ var buf bytes.Buffer
+
+ if reqID != "" {
+ cW(&buf, bBlack, "[%s] ", reqID)
+ }
+ buf.WriteString("Started ")
+ cW(&buf, bMagenta, "%s ", r.Method)
+ cW(&buf, nBlue, "%q ", r.URL.String())
+ buf.WriteString("from ")
+ buf.WriteString(r.RemoteAddr)
+
+ log.Print(buf.String())
+}
+
+func printEnd(reqID string, w mutil.WriterProxy, dt time.Duration) {
+ var buf bytes.Buffer
+
+ if reqID != "" {
+ cW(&buf, bBlack, "[%s] ", reqID)
+ }
+ buf.WriteString("Returning ")
+ status := w.Status()
+ if status < 200 {
+ cW(&buf, bBlue, "%03d", status)
+ } else if status < 300 {
+ cW(&buf, bGreen, "%03d", status)
+ } else if status < 400 {
+ cW(&buf, bCyan, "%03d", status)
+ } else if status < 500 {
+ cW(&buf, bYellow, "%03d", status)
+ } else {
+ cW(&buf, bRed, "%03d", status)
+ }
+ buf.WriteString(" in ")
+ if dt < 500*time.Millisecond {
+ cW(&buf, nGreen, "%s", dt)
+ } else if dt < 5*time.Second {
+ cW(&buf, nYellow, "%s", dt)
+ } else {
+ cW(&buf, nRed, "%s", dt)
+ }
+
+ log.Print(buf.String())
+}
diff --git a/vendor/github.com/zenazn/goji/web/middleware/middleware.go b/vendor/github.com/zenazn/goji/web/middleware/middleware.go
new file mode 100644
index 0000000..23cfde2
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/middleware/middleware.go
@@ -0,0 +1,4 @@
+/*
+Package middleware provides several standard middleware implementations.
+*/
+package middleware
diff --git a/vendor/github.com/zenazn/goji/web/middleware/nocache.go b/vendor/github.com/zenazn/goji/web/middleware/nocache.go
new file mode 100644
index 0000000..ae3d260
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/middleware/nocache.go
@@ -0,0 +1,55 @@
+package middleware
+
+import (
+ "net/http"
+ "time"
+)
+
+// Unix epoch time
+var epoch = time.Unix(0, 0).Format(time.RFC1123)
+
+// Taken from https://github.com/mytrile/nocache
+var noCacheHeaders = map[string]string{
+ "Expires": epoch,
+ "Cache-Control": "no-cache, private, max-age=0",
+ "Pragma": "no-cache",
+ "X-Accel-Expires": "0",
+}
+
+var etagHeaders = []string{
+ "ETag",
+ "If-Modified-Since",
+ "If-Match",
+ "If-None-Match",
+ "If-Range",
+ "If-Unmodified-Since",
+}
+
+// NoCache is a simple piece of middleware that sets a number of HTTP headers to prevent
+// a router (or subrouter) from being cached by an upstream proxy and/or client.
+//
+// As per http://wiki.nginx.org/HttpProxyModule - NoCache sets:
+// Expires: Thu, 01 Jan 1970 00:00:00 UTC
+// Cache-Control: no-cache, private, max-age=0
+// X-Accel-Expires: 0
+// Pragma: no-cache (for HTTP/1.0 proxies/clients)
+func NoCache(h http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+
+ // Delete any ETag headers that may have been set
+ for _, v := range etagHeaders {
+ if r.Header.Get(v) != "" {
+ r.Header.Del(v)
+ }
+ }
+
+ // Set our NoCache headers
+ for k, v := range noCacheHeaders {
+ w.Header().Set(k, v)
+ }
+
+ h.ServeHTTP(w, r)
+ }
+
+ return http.HandlerFunc(fn)
+}
diff --git a/vendor/github.com/zenazn/goji/web/middleware/options.go b/vendor/github.com/zenazn/goji/web/middleware/options.go
new file mode 100644
index 0000000..4bdce5f
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/middleware/options.go
@@ -0,0 +1,97 @@
+package middleware
+
+import (
+ "net/http"
+ "strings"
+
+ "github.com/zenazn/goji/web"
+)
+
+type autoOptionsState int
+
+const (
+ aosInit autoOptionsState = iota
+ aosHeaderWritten
+ aosProxying
+)
+
+// I originally used an httptest.ResponseRecorder here, but package httptest
+// adds a flag which I'm not particularly eager to expose. This is essentially a
+// ResponseRecorder that has been specialized for the purpose at hand to avoid
+// the httptest dependency.
+type autoOptionsProxy struct {
+ w http.ResponseWriter
+ c *web.C
+ state autoOptionsState
+}
+
+func (p *autoOptionsProxy) Header() http.Header {
+ return p.w.Header()
+}
+
+func (p *autoOptionsProxy) Write(buf []byte) (int, error) {
+ switch p.state {
+ case aosInit:
+ p.state = aosHeaderWritten
+ case aosProxying:
+ return len(buf), nil
+ }
+ return p.w.Write(buf)
+}
+
+func (p *autoOptionsProxy) WriteHeader(code int) {
+ methods := getValidMethods(*p.c)
+ switch p.state {
+ case aosInit:
+ if methods != nil && code == http.StatusNotFound {
+ p.state = aosProxying
+ break
+ }
+ p.state = aosHeaderWritten
+ fallthrough
+ default:
+ p.w.WriteHeader(code)
+ return
+ }
+
+ methods = addMethod(methods, "OPTIONS")
+ p.w.Header().Set("Allow", strings.Join(methods, ", "))
+ p.w.WriteHeader(http.StatusOK)
+}
+
+// AutomaticOptions automatically return an appropriate "Allow" header when the
+// request method is OPTIONS and the request would have otherwise been 404'd.
+func AutomaticOptions(c *web.C, h http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ if r.Method == "OPTIONS" {
+ w = &autoOptionsProxy{c: c, w: w}
+ }
+
+ h.ServeHTTP(w, r)
+ }
+
+ return http.HandlerFunc(fn)
+}
+
+func getValidMethods(c web.C) []string {
+ if c.Env == nil {
+ return nil
+ }
+ v, ok := c.Env[web.ValidMethodsKey]
+ if !ok {
+ return nil
+ }
+ if methods, ok := v.([]string); ok {
+ return methods
+ }
+ return nil
+}
+
+func addMethod(methods []string, method string) []string {
+ for _, m := range methods {
+ if m == method {
+ return methods
+ }
+ }
+ return append(methods, method)
+}
diff --git a/vendor/github.com/zenazn/goji/web/middleware/realip.go b/vendor/github.com/zenazn/goji/web/middleware/realip.go
new file mode 100644
index 0000000..ae5599f
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/middleware/realip.go
@@ -0,0 +1,51 @@
+package middleware
+
+import (
+ "net/http"
+ "strings"
+)
+
+var xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For")
+var xRealIP = http.CanonicalHeaderKey("X-Real-IP")
+
+// RealIP is a middleware that sets a http.Request's RemoteAddr to the results
+// of parsing either the X-Forwarded-For header or the X-Real-IP header (in that
+// order).
+//
+// This middleware should be inserted fairly early in the middleware stack to
+// ensure that subsequent layers (e.g., request loggers) which examine the
+// RemoteAddr will see the intended value.
+//
+// You should only use this middleware if you can trust the headers passed to
+// you (in particular, the two headers this middleware uses), for example
+// because you have placed a reverse proxy like HAProxy or nginx in front of
+// Goji. If your reverse proxies are configured to pass along arbitrary header
+// values from the client, or if you use this middleware without a reverse
+// proxy, malicious clients will be able to make you very sad (or, depending on
+// how you're using RemoteAddr, vulnerable to an attack of some sort).
+func RealIP(h http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ if rip := realIP(r); rip != "" {
+ r.RemoteAddr = rip
+ }
+ h.ServeHTTP(w, r)
+ }
+
+ return http.HandlerFunc(fn)
+}
+
+func realIP(r *http.Request) string {
+ var ip string
+
+ if xff := r.Header.Get(xForwardedFor); xff != "" {
+ i := strings.Index(xff, ", ")
+ if i == -1 {
+ i = len(xff)
+ }
+ ip = xff[:i]
+ } else if xrip := r.Header.Get(xRealIP); xrip != "" {
+ ip = xrip
+ }
+
+ return ip
+}
diff --git a/vendor/github.com/zenazn/goji/web/middleware/recoverer.go b/vendor/github.com/zenazn/goji/web/middleware/recoverer.go
new file mode 100644
index 0000000..43ad648
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/middleware/recoverer.go
@@ -0,0 +1,44 @@
+package middleware
+
+import (
+ "bytes"
+ "log"
+ "net/http"
+ "runtime/debug"
+
+ "github.com/zenazn/goji/web"
+)
+
+// Recoverer is a middleware that recovers from panics, logs the panic (and a
+// backtrace), and returns a HTTP 500 (Internal Server Error) status if
+// possible.
+//
+// Recoverer prints a request ID if one is provided.
+func Recoverer(c *web.C, h http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ reqID := GetReqID(*c)
+
+ defer func() {
+ if err := recover(); err != nil {
+ printPanic(reqID, err)
+ debug.PrintStack()
+ http.Error(w, http.StatusText(500), 500)
+ }
+ }()
+
+ h.ServeHTTP(w, r)
+ }
+
+ return http.HandlerFunc(fn)
+}
+
+func printPanic(reqID string, err interface{}) {
+ var buf bytes.Buffer
+
+ if reqID != "" {
+ cW(&buf, bBlack, "[%s] ", reqID)
+ }
+ cW(&buf, bRed, "panic: %+v", err)
+
+ log.Print(buf.String())
+}
diff --git a/vendor/github.com/zenazn/goji/web/middleware/request_id.go b/vendor/github.com/zenazn/goji/web/middleware/request_id.go
new file mode 100644
index 0000000..834d8e3
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/middleware/request_id.go
@@ -0,0 +1,88 @@
+package middleware
+
+import (
+ "crypto/rand"
+ "encoding/base64"
+ "fmt"
+ "net/http"
+ "os"
+ "strings"
+ "sync/atomic"
+
+ "github.com/zenazn/goji/web"
+)
+
+// Key to use when setting the request ID.
+const RequestIDKey = "reqID"
+
+var prefix string
+var reqid uint64
+
+/*
+A quick note on the statistics here: we're trying to calculate the chance that
+two randomly generated base62 prefixes will collide. We use the formula from
+http://en.wikipedia.org/wiki/Birthday_problem
+
+P[m, n] \approx 1 - e^{-m^2/2n}
+
+We ballpark an upper bound for $m$ by imagining (for whatever reason) a server
+that restarts every second over 10 years, for $m = 86400 * 365 * 10 = 315360000$
+
+For a $k$ character base-62 identifier, we have $n(k) = 62^k$
+
+Plugging this in, we find $P[m, n(10)] \approx 5.75%$, which is good enough for
+our purposes, and is surely more than anyone would ever need in practice -- a
+process that is rebooted a handful of times a day for a hundred years has less
+than a millionth of a percent chance of generating two colliding IDs.
+*/
+
+func init() {
+ hostname, err := os.Hostname()
+ if hostname == "" || err != nil {
+ hostname = "localhost"
+ }
+ var buf [12]byte
+ var b64 string
+ for len(b64) < 10 {
+ rand.Read(buf[:])
+ b64 = base64.StdEncoding.EncodeToString(buf[:])
+ b64 = strings.NewReplacer("+", "", "/", "").Replace(b64)
+ }
+
+ prefix = fmt.Sprintf("%s/%s", hostname, b64[0:10])
+}
+
+// RequestID is a middleware that injects a request ID into the context of each
+// request. A request ID is a string of the form "host.example.com/random-0001",
+// where "random" is a base62 random string that uniquely identifies this go
+// process, and where the last number is an atomically incremented request
+// counter.
+func RequestID(c *web.C, h http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ if c.Env == nil {
+ c.Env = make(map[interface{}]interface{})
+ }
+ myid := atomic.AddUint64(&reqid, 1)
+ c.Env[RequestIDKey] = fmt.Sprintf("%s-%06d", prefix, myid)
+
+ h.ServeHTTP(w, r)
+ }
+
+ return http.HandlerFunc(fn)
+}
+
+// GetReqID returns a request ID from the given context if one is present.
+// Returns the empty string if a request ID cannot be found.
+func GetReqID(c web.C) string {
+ if c.Env == nil {
+ return ""
+ }
+ v, ok := c.Env[RequestIDKey]
+ if !ok {
+ return ""
+ }
+ if reqID, ok := v.(string); ok {
+ return reqID
+ }
+ return ""
+}
diff --git a/vendor/github.com/zenazn/goji/web/middleware/subrouter.go b/vendor/github.com/zenazn/goji/web/middleware/subrouter.go
new file mode 100644
index 0000000..e5b0921
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/middleware/subrouter.go
@@ -0,0 +1,65 @@
+package middleware
+
+import (
+ "net/http"
+
+ "github.com/zenazn/goji/web"
+)
+
+type subrouter struct {
+ c *web.C
+ h http.Handler
+}
+
+func (s subrouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ if s.c.URLParams != nil {
+ path, ok := s.c.URLParams["*"]
+ if !ok {
+ path, ok = s.c.URLParams["_"]
+ }
+ if ok {
+ oldpath := r.URL.Path
+ oldmatch := web.GetMatch(*s.c)
+ r.URL.Path = path
+ if oldmatch.Handler != nil {
+ delete(s.c.Env, web.MatchKey)
+ }
+
+ defer func() {
+ r.URL.Path = oldpath
+
+ if s.c.Env == nil {
+ return
+ }
+ if oldmatch.Handler != nil {
+ s.c.Env[web.MatchKey] = oldmatch
+ } else {
+ delete(s.c.Env, web.MatchKey)
+ }
+ }()
+ }
+ }
+ s.h.ServeHTTP(w, r)
+}
+
+/*
+SubRouter is a helper middleware that makes writing sub-routers easier.
+
+If you register a sub-router under a key like "/admin/*", Goji's router will
+automatically set c.URLParams["*"] to the unmatched path suffix. This middleware
+will help you set the request URL's Path to this unmatched suffix, allowing you
+to write sub-routers with no knowledge of what routes the parent router matches.
+
+Since Go's regular expressions do not allow you to create a capturing group
+named "*", SubRouter also accepts the string "_". For instance, to duplicate the
+semantics of the string pattern "/foo/*", you might use the regular expression
+"^/foo(?P<_>/.*)$".
+
+This middleware is Match-aware: it will un-set any explicit routing information
+contained in the Goji context in order to prevent routing loops when using
+explicit routing with sub-routers. See the documentation for Mux.Router for
+more.
+*/
+func SubRouter(c *web.C, h http.Handler) http.Handler {
+ return subrouter{c, h}
+}
diff --git a/vendor/github.com/zenazn/goji/web/middleware/terminal.go b/vendor/github.com/zenazn/goji/web/middleware/terminal.go
new file mode 100644
index 0000000..db02917
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/middleware/terminal.go
@@ -0,0 +1,60 @@
+package middleware
+
+import (
+ "bytes"
+ "fmt"
+ "os"
+)
+
+var (
+ // Normal colors
+ nBlack = []byte{'\033', '[', '3', '0', 'm'}
+ nRed = []byte{'\033', '[', '3', '1', 'm'}
+ nGreen = []byte{'\033', '[', '3', '2', 'm'}
+ nYellow = []byte{'\033', '[', '3', '3', 'm'}
+ nBlue = []byte{'\033', '[', '3', '4', 'm'}
+ nMagenta = []byte{'\033', '[', '3', '5', 'm'}
+ nCyan = []byte{'\033', '[', '3', '6', 'm'}
+ nWhite = []byte{'\033', '[', '3', '7', 'm'}
+ // Bright colors
+ bBlack = []byte{'\033', '[', '3', '0', ';', '1', 'm'}
+ bRed = []byte{'\033', '[', '3', '1', ';', '1', 'm'}
+ bGreen = []byte{'\033', '[', '3', '2', ';', '1', 'm'}
+ bYellow = []byte{'\033', '[', '3', '3', ';', '1', 'm'}
+ bBlue = []byte{'\033', '[', '3', '4', ';', '1', 'm'}
+ bMagenta = []byte{'\033', '[', '3', '5', ';', '1', 'm'}
+ bCyan = []byte{'\033', '[', '3', '6', ';', '1', 'm'}
+ bWhite = []byte{'\033', '[', '3', '7', ';', '1', 'm'}
+
+ reset = []byte{'\033', '[', '0', 'm'}
+)
+
+var isTTY bool
+
+func init() {
+ // This is sort of cheating: if stdout is a character device, we assume
+ // that means it's a TTY. Unfortunately, there are many non-TTY
+ // character devices, but fortunately stdout is rarely set to any of
+ // them.
+ //
+ // We could solve this properly by pulling in a dependency on
+ // code.google.com/p/go.crypto/ssh/terminal, for instance, but as a
+ // heuristic for whether to print in color or in black-and-white, I'd
+ // really rather not.
+ fi, err := os.Stdout.Stat()
+ if err == nil {
+ m := os.ModeDevice | os.ModeCharDevice
+ isTTY = fi.Mode()&m == m
+ }
+}
+
+// colorWrite
+func cW(buf *bytes.Buffer, color []byte, s string, args ...interface{}) {
+ if isTTY {
+ buf.Write(color)
+ }
+ fmt.Fprintf(buf, s, args...)
+ if isTTY {
+ buf.Write(reset)
+ }
+}
diff --git a/vendor/github.com/zenazn/goji/web/middleware/urlquery.go b/vendor/github.com/zenazn/goji/web/middleware/urlquery.go
new file mode 100644
index 0000000..36c8820
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/middleware/urlquery.go
@@ -0,0 +1,24 @@
+package middleware
+
+import (
+ "github.com/zenazn/goji/web"
+ "net/http"
+)
+
+// URLQueryKey is the context key for the URL Query
+const URLQueryKey string = "urlquery"
+
+// URLQuery is a middleware to parse the URL Query parameters just once,
+// and store the resulting url.Values in the context.
+func URLQuery(c *web.C, h http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ if c.Env == nil {
+ c.Env = make(map[interface{}]interface{})
+ }
+ c.Env[URLQueryKey] = r.URL.Query()
+
+ h.ServeHTTP(w, r)
+ }
+
+ return http.HandlerFunc(fn)
+}
diff --git a/vendor/github.com/zenazn/goji/web/mutil/mutil.go b/vendor/github.com/zenazn/goji/web/mutil/mutil.go
new file mode 100644
index 0000000..e8d5b28
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/mutil/mutil.go
@@ -0,0 +1,3 @@
+// Package mutil contains various functions that are helpful when writing http
+// middleware.
+package mutil
diff --git a/vendor/github.com/zenazn/goji/web/mutil/writer_proxy.go b/vendor/github.com/zenazn/goji/web/mutil/writer_proxy.go
new file mode 100644
index 0000000..9f6d776
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/mutil/writer_proxy.go
@@ -0,0 +1,139 @@
+package mutil
+
+import (
+ "bufio"
+ "io"
+ "net"
+ "net/http"
+)
+
+// WriterProxy is a proxy around an http.ResponseWriter that allows you to hook
+// into various parts of the response process.
+type WriterProxy interface {
+ http.ResponseWriter
+ // Status returns the HTTP status of the request, or 0 if one has not
+ // yet been sent.
+ Status() int
+ // BytesWritten returns the total number of bytes sent to the client.
+ BytesWritten() int
+ // Tee causes the response body to be written to the given io.Writer in
+ // addition to proxying the writes through. Only one io.Writer can be
+ // tee'd to at once: setting a second one will overwrite the first.
+ // Writes will be sent to the proxy before being written to this
+ // io.Writer. It is illegal for the tee'd writer to be modified
+ // concurrently with writes.
+ Tee(io.Writer)
+ // Unwrap returns the original proxied target.
+ Unwrap() http.ResponseWriter
+}
+
+// WrapWriter wraps an http.ResponseWriter, returning a proxy that allows you to
+// hook into various parts of the response process.
+func WrapWriter(w http.ResponseWriter) WriterProxy {
+ _, cn := w.(http.CloseNotifier)
+ _, fl := w.(http.Flusher)
+ _, hj := w.(http.Hijacker)
+ _, rf := w.(io.ReaderFrom)
+
+ bw := basicWriter{ResponseWriter: w}
+ if cn && fl && hj && rf {
+ return &fancyWriter{bw}
+ }
+ if fl {
+ return &flushWriter{bw}
+ }
+ return &bw
+}
+
+// basicWriter wraps a http.ResponseWriter that implements the minimal
+// http.ResponseWriter interface.
+type basicWriter struct {
+ http.ResponseWriter
+ wroteHeader bool
+ code int
+ bytes int
+ tee io.Writer
+}
+
+func (b *basicWriter) WriteHeader(code int) {
+ if !b.wroteHeader {
+ b.code = code
+ b.wroteHeader = true
+ b.ResponseWriter.WriteHeader(code)
+ }
+}
+func (b *basicWriter) Write(buf []byte) (int, error) {
+ b.WriteHeader(http.StatusOK)
+ n, err := b.ResponseWriter.Write(buf)
+ if b.tee != nil {
+ _, err2 := b.tee.Write(buf[:n])
+ // Prefer errors generated by the proxied writer.
+ if err == nil {
+ err = err2
+ }
+ }
+ b.bytes += n
+ return n, err
+}
+func (b *basicWriter) maybeWriteHeader() {
+ if !b.wroteHeader {
+ b.WriteHeader(http.StatusOK)
+ }
+}
+func (b *basicWriter) Status() int {
+ return b.code
+}
+func (b *basicWriter) BytesWritten() int {
+ return b.bytes
+}
+func (b *basicWriter) Tee(w io.Writer) {
+ b.tee = w
+}
+func (b *basicWriter) Unwrap() http.ResponseWriter {
+ return b.ResponseWriter
+}
+
+// fancyWriter is a writer that additionally satisfies http.CloseNotifier,
+// http.Flusher, http.Hijacker, and io.ReaderFrom. It exists for the common case
+// of wrapping the http.ResponseWriter that package http gives you, in order to
+// make the proxied object support the full method set of the proxied object.
+type fancyWriter struct {
+ basicWriter
+}
+
+func (f *fancyWriter) CloseNotify() <-chan bool {
+ cn := f.basicWriter.ResponseWriter.(http.CloseNotifier)
+ return cn.CloseNotify()
+}
+func (f *fancyWriter) Flush() {
+ fl := f.basicWriter.ResponseWriter.(http.Flusher)
+ fl.Flush()
+}
+func (f *fancyWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
+ hj := f.basicWriter.ResponseWriter.(http.Hijacker)
+ return hj.Hijack()
+}
+func (f *fancyWriter) ReadFrom(r io.Reader) (int64, error) {
+ if f.basicWriter.tee != nil {
+ return io.Copy(&f.basicWriter, r)
+ }
+ rf := f.basicWriter.ResponseWriter.(io.ReaderFrom)
+ f.basicWriter.maybeWriteHeader()
+ return rf.ReadFrom(r)
+}
+
+var _ http.CloseNotifier = &fancyWriter{}
+var _ http.Flusher = &fancyWriter{}
+var _ http.Hijacker = &fancyWriter{}
+var _ io.ReaderFrom = &fancyWriter{}
+
+type flushWriter struct {
+ basicWriter
+}
+
+func (f *flushWriter) Flush() {
+ fl := f.basicWriter.ResponseWriter.(http.Flusher)
+ fl.Flush()
+}
+
+var _ http.Flusher = &flushWriter{}
diff --git a/vendor/github.com/zenazn/goji/web/mux.go b/vendor/github.com/zenazn/goji/web/mux.go
new file mode 100644
index 0000000..18b9991
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/mux.go
@@ -0,0 +1,213 @@
+package web
+
+import (
+ "net/http"
+)
+
+/*
+Mux is an HTTP multiplexer, much like net/http's ServeMux. It functions as both
+a middleware stack and as an HTTP router.
+
+Middleware provide a great abstraction for actions that must be performed on
+every request, such as request logging and authentication. To append, insert,
+and remove middleware, you can call the Use, Insert, and Abandon functions
+respectively.
+
+Routes may be added using any of the HTTP verb functions (Get, Post, etc.), or
+through the generic Handle function. Goji's routing algorithm is very simple:
+routes are processed in the order they are added, and the first matching route
+will be executed. Routes match if their HTTP method and Pattern both match.
+*/
+type Mux struct {
+ ms mStack
+ rt router
+}
+
+// New creates a new Mux without any routes or middleware.
+func New() *Mux {
+ mux := Mux{
+ ms: mStack{
+ stack: make([]mLayer, 0),
+ pool: makeCPool(),
+ },
+ rt: router{
+ routes: make([]route, 0),
+ notFound: parseHandler(http.NotFound),
+ },
+ }
+ mux.ms.router = &mux.rt
+ return &mux
+}
+
+// ServeHTTP processes HTTP requests. Satisfies net/http.Handler.
+func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ stack := m.ms.alloc()
+ stack.ServeHTTP(w, r)
+ m.ms.release(stack)
+}
+
+// ServeHTTPC creates a context dependent request with the given Mux. Satisfies
+// the Handler interface.
+func (m *Mux) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
+ stack := m.ms.alloc()
+ stack.ServeHTTPC(c, w, r)
+ m.ms.release(stack)
+}
+
+// Middleware Stack functions
+
+// Use appends the given middleware to the middleware stack.
+//
+// No attempt is made to enforce the uniqueness of middlewares. It is illegal to
+// call this function concurrently with active requests.
+func (m *Mux) Use(middleware MiddlewareType) {
+ m.ms.Use(middleware)
+}
+
+// Insert inserts the given middleware immediately before a given existing
+// middleware in the stack. Returns an error if "before" cannot be found in the
+// current stack.
+//
+// No attempt is made to enforce the uniqueness of middlewares. If the insertion
+// point is ambiguous, the first (outermost) one is chosen. It is illegal to
+// call this function concurrently with active requests.
+func (m *Mux) Insert(middleware, before MiddlewareType) error {
+ return m.ms.Insert(middleware, before)
+}
+
+// Abandon removes the given middleware from the middleware stack. Returns an
+// error if no such middleware can be found.
+//
+// If the name of the middleware to delete is ambiguous, the first (outermost)
+// one is chosen. It is illegal to call this function concurrently with active
+// requests.
+func (m *Mux) Abandon(middleware MiddlewareType) error {
+ return m.ms.Abandon(middleware)
+}
+
+// Router functions
+
+type routerMiddleware struct {
+ m *Mux
+ c *C
+ h http.Handler
+}
+
+func (rm routerMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ if rm.c.Env == nil {
+ rm.c.Env = make(map[interface{}]interface{}, 1)
+ }
+ rm.c.Env[MatchKey] = rm.m.rt.getMatch(rm.c, w, r)
+ rm.h.ServeHTTP(w, r)
+}
+
+/*
+Router is a middleware that performs routing and stores the resulting Match in
+Goji's environment. If a routing Match is present at the end of the middleware
+stack, that Match is used instead of re-routing.
+
+This middleware is especially useful to create post-routing middleware, e.g. a
+request logger which prints which pattern or handler was selected, or an
+authentication middleware which only applies to certain routes.
+
+If you use nested Muxes with explicit routing, you should be aware that the
+explicit routing information set by an outer Mux can be picked up by an inner
+Mux, inadvertently causing an infinite routing loop. If you use both explicit
+routing and nested Muxes, you should be sure to unset MatchKey before the inner
+Mux performs routing (or attach a Router to the inner Mux as well).
+*/
+func (m *Mux) Router(c *C, h http.Handler) http.Handler {
+ return routerMiddleware{m, c, h}
+}
+
+/*
+Handle dispatches to the given handler when the pattern matches, regardless of
+HTTP method.
+
+This method is commonly used to implement sub-routing: an admin application, for
+instance, can expose a single handler that is attached to the main Mux by
+calling Handle("/admin/*", adminHandler) or similar. Note that this function
+doesn't strip this prefix from the path before forwarding it on (e.g., the
+handler will see the full path, including the "/admin/" part), but this
+functionality can easily be performed by an extra middleware layer.
+*/
+func (m *Mux) Handle(pattern PatternType, handler HandlerType) {
+ m.rt.handleUntyped(pattern, mALL, handler)
+}
+
+// Connect dispatches to the given handler when the pattern matches and the HTTP
+// method is CONNECT.
+func (m *Mux) Connect(pattern PatternType, handler HandlerType) {
+ m.rt.handleUntyped(pattern, mCONNECT, handler)
+}
+
+// Delete dispatches to the given handler when the pattern matches and the HTTP
+// method is DELETE.
+func (m *Mux) Delete(pattern PatternType, handler HandlerType) {
+ m.rt.handleUntyped(pattern, mDELETE, handler)
+}
+
+// Get dispatches to the given handler when the pattern matches and the HTTP
+// method is GET.
+//
+// All GET handlers also transparently serve HEAD requests, since net/http will
+// take care of all the fiddly bits for you. If you wish to provide an alternate
+// implementation of HEAD, you should add a handler explicitly and place it
+// above your GET handler.
+func (m *Mux) Get(pattern PatternType, handler HandlerType) {
+ m.rt.handleUntyped(pattern, mGET|mHEAD, handler)
+}
+
+// Head dispatches to the given handler when the pattern matches and the HTTP
+// method is HEAD.
+func (m *Mux) Head(pattern PatternType, handler HandlerType) {
+ m.rt.handleUntyped(pattern, mHEAD, handler)
+}
+
+// Options dispatches to the given handler when the pattern matches and the HTTP
+// method is OPTIONS.
+func (m *Mux) Options(pattern PatternType, handler HandlerType) {
+ m.rt.handleUntyped(pattern, mOPTIONS, handler)
+}
+
+// Patch dispatches to the given handler when the pattern matches and the HTTP
+// method is PATCH.
+func (m *Mux) Patch(pattern PatternType, handler HandlerType) {
+ m.rt.handleUntyped(pattern, mPATCH, handler)
+}
+
+// Post dispatches to the given handler when the pattern matches and the HTTP
+// method is POST.
+func (m *Mux) Post(pattern PatternType, handler HandlerType) {
+ m.rt.handleUntyped(pattern, mPOST, handler)
+}
+
+// Put dispatches to the given handler when the pattern matches and the HTTP
+// method is PUT.
+func (m *Mux) Put(pattern PatternType, handler HandlerType) {
+ m.rt.handleUntyped(pattern, mPUT, handler)
+}
+
+// Trace dispatches to the given handler when the pattern matches and the HTTP
+// method is TRACE.
+func (m *Mux) Trace(pattern PatternType, handler HandlerType) {
+ m.rt.handleUntyped(pattern, mTRACE, handler)
+}
+
+// NotFound sets the fallback (i.e., 404) handler for this mux.
+//
+// As a convenience, the context environment variable "goji.web.validMethods"
+// (also available as the constant ValidMethodsKey) will be set to the list of
+// HTTP methods that could have been routed had they been provided on an
+// otherwise identical request.
+func (m *Mux) NotFound(handler HandlerType) {
+ m.rt.notFound = parseHandler(handler)
+}
+
+// Compile compiles the list of routes into bytecode. This only needs to be done
+// once after all the routes have been added, and will be called automatically
+// for you (at some performance cost on the first request) if you do not call it
+// explicitly.
+func (m *Mux) Compile() {
+ m.rt.compile()
+}
diff --git a/vendor/github.com/zenazn/goji/web/pattern.go b/vendor/github.com/zenazn/goji/web/pattern.go
new file mode 100644
index 0000000..9f9fc85
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/pattern.go
@@ -0,0 +1,58 @@
+package web
+
+import (
+ "log"
+ "net/http"
+ "regexp"
+)
+
+// A Pattern determines whether or not a given request matches some criteria.
+// They are often used in routes, which are essentially (pattern, methodSet,
+// handler) tuples. If the method and pattern match, the given handler is used.
+//
+// Built-in implementations of this interface are used to implement regular
+// expression and string matching.
+type Pattern interface {
+ // In practice, most real-world routes have a string prefix that can be
+ // used to quickly determine if a pattern is an eligible match. The
+ // router uses the result of this function to optimize away calls to the
+ // full Match function, which is likely much more expensive to compute.
+ // If your Pattern does not support prefixes, this function should
+ // return the empty string.
+ Prefix() string
+ // Returns true if the request satisfies the pattern. This function is
+ // free to examine both the request and the context to make this
+ // decision. Match should not modify either argument, and since it will
+ // potentially be called several times over the course of matching a
+ // request, it should be reasonably efficient.
+ Match(r *http.Request, c *C) bool
+ // Run the pattern on the request and context, modifying the context as
+ // necessary to bind URL parameters or other parsed state.
+ Run(r *http.Request, c *C)
+}
+
+const unknownPattern = `Unknown pattern type %T. See http://godoc.org/github.com/zenazn/goji/web#PatternType for a list of acceptable types.`
+
+/*
+ParsePattern is used internally by Goji to parse route patterns. It is exposed
+publicly to make it easier to write thin wrappers around the built-in Pattern
+implementations.
+
+ParsePattern fatally exits (using log.Fatalf) if it is passed a value of an
+unexpected type (see the documentation for PatternType for a list of which types
+are accepted). It is the caller's responsibility to ensure that ParsePattern is
+called in a type-safe manner.
+*/
+func ParsePattern(raw PatternType) Pattern {
+ switch v := raw.(type) {
+ case Pattern:
+ return v
+ case *regexp.Regexp:
+ return parseRegexpPattern(v)
+ case string:
+ return parseStringPattern(v)
+ default:
+ log.Fatalf(unknownPattern, v)
+ panic("log.Fatalf does not return")
+ }
+}
diff --git a/vendor/github.com/zenazn/goji/web/regexp_pattern.go b/vendor/github.com/zenazn/goji/web/regexp_pattern.go
new file mode 100644
index 0000000..95e7e09
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/regexp_pattern.go
@@ -0,0 +1,149 @@
+package web
+
+import (
+ "bytes"
+ "fmt"
+ "log"
+ "net/http"
+ "regexp"
+ "regexp/syntax"
+)
+
+type regexpPattern struct {
+ re *regexp.Regexp
+ prefix string
+ names []string
+}
+
+func (p regexpPattern) Prefix() string {
+ return p.prefix
+}
+func (p regexpPattern) Match(r *http.Request, c *C) bool {
+ return p.match(r, c, false)
+}
+func (p regexpPattern) Run(r *http.Request, c *C) {
+ p.match(r, c, false)
+}
+
+func (p regexpPattern) match(r *http.Request, c *C, dryrun bool) bool {
+ matches := p.re.FindStringSubmatch(r.URL.Path)
+ if matches == nil || len(matches) == 0 {
+ return false
+ }
+
+ if c == nil || dryrun || len(matches) == 1 {
+ return true
+ }
+
+ if c.URLParams == nil {
+ c.URLParams = make(map[string]string, len(matches)-1)
+ }
+ for i := 1; i < len(matches); i++ {
+ c.URLParams[p.names[i]] = matches[i]
+ }
+ return true
+}
+
+func (p regexpPattern) String() string {
+ return fmt.Sprintf("regexpPattern(%v)", p.re)
+}
+
+func (p regexpPattern) Raw() *regexp.Regexp {
+ return p.re
+}
+
+/*
+I'm sorry, dear reader. I really am.
+
+The problem here is to take an arbitrary regular expression and:
+1. return a regular expression that is just like it, but left-anchored,
+ preferring to return the original if possible.
+2. determine a string literal prefix that all matches of this regular expression
+ have, much like regexp.Regexp.Prefix(). Unfortunately, Prefix() does not work
+ in the presence of anchors, so we need to write it ourselves.
+
+What this actually means is that we need to sketch on the internals of the
+standard regexp library to forcefully extract the information we want.
+
+Unfortunately, regexp.Regexp hides a lot of its state, so our abstraction is
+going to be pretty leaky. The biggest leak is that we blindly assume that all
+regular expressions are perl-style, not POSIX. This is probably Mostly True, and
+I think most users of the library probably won't be able to notice.
+*/
+func sketchOnRegex(re *regexp.Regexp) (*regexp.Regexp, string) {
+ rawRe := re.String()
+ sRe, err := syntax.Parse(rawRe, syntax.Perl)
+ if err != nil {
+ log.Printf("WARN(web): unable to parse regexp %v as perl. "+
+ "This route might behave unexpectedly.", re)
+ return re, ""
+ }
+ sRe = sRe.Simplify()
+ p, err := syntax.Compile(sRe)
+ if err != nil {
+ log.Printf("WARN(web): unable to compile regexp %v. This "+
+ "route might behave unexpectedly.", re)
+ return re, ""
+ }
+ if p.StartCond()&syntax.EmptyBeginText == 0 {
+ // I hope doing this is always legal...
+ newRe, err := regexp.Compile(`\A` + rawRe)
+ if err != nil {
+ log.Printf("WARN(web): unable to create a left-"+
+ "anchored regexp from %v. This route might "+
+ "behave unexpectedly", re)
+ return re, ""
+ }
+ re = newRe
+ }
+
+ // Run the regular expression more or less by hand :(
+ pc := uint32(p.Start)
+ atStart := true
+ i := &p.Inst[pc]
+ var buf bytes.Buffer
+Sadness:
+ for {
+ switch i.Op {
+ case syntax.InstEmptyWidth:
+ if !atStart {
+ break Sadness
+ }
+ case syntax.InstCapture, syntax.InstNop:
+ // nop!
+ case syntax.InstRune, syntax.InstRune1, syntax.InstRuneAny,
+ syntax.InstRuneAnyNotNL:
+
+ atStart = false
+ if len(i.Rune) != 1 ||
+ syntax.Flags(i.Arg)&syntax.FoldCase != 0 {
+ break Sadness
+ }
+ buf.WriteRune(i.Rune[0])
+ default:
+ break Sadness
+ }
+ pc = i.Out
+ i = &p.Inst[pc]
+ }
+ return re, buf.String()
+}
+
+func parseRegexpPattern(re *regexp.Regexp) regexpPattern {
+ re, prefix := sketchOnRegex(re)
+ rnames := re.SubexpNames()
+ // We have to make our own copy since package regexp forbids us
+ // from scribbling over the slice returned by SubexpNames().
+ names := make([]string, len(rnames))
+ for i, rname := range rnames {
+ if rname == "" {
+ rname = fmt.Sprintf("$%d", i)
+ }
+ names[i] = rname
+ }
+ return regexpPattern{
+ re: re,
+ prefix: prefix,
+ names: names,
+ }
+}
diff --git a/vendor/github.com/zenazn/goji/web/router.go b/vendor/github.com/zenazn/goji/web/router.go
new file mode 100644
index 0000000..1fbc41f
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/router.go
@@ -0,0 +1,154 @@
+package web
+
+import (
+ "net/http"
+ "sort"
+ "strings"
+ "sync"
+)
+
+type method int
+
+const (
+ mCONNECT method = 1 << iota
+ mDELETE
+ mGET
+ mHEAD
+ mOPTIONS
+ mPATCH
+ mPOST
+ mPUT
+ mTRACE
+ // We only natively support the methods above, but we pass through other
+ // methods. This constant pretty much only exists for the sake of mALL.
+ mIDK
+
+ mALL method = mCONNECT | mDELETE | mGET | mHEAD | mOPTIONS | mPATCH |
+ mPOST | mPUT | mTRACE | mIDK
+)
+
+// The key used to communicate to the NotFound handler what methods would have
+// been allowed if they'd been provided.
+const ValidMethodsKey = "goji.web.ValidMethods"
+
+var validMethodsMap = map[string]method{
+ "CONNECT": mCONNECT,
+ "DELETE": mDELETE,
+ "GET": mGET,
+ "HEAD": mHEAD,
+ "OPTIONS": mOPTIONS,
+ "PATCH": mPATCH,
+ "POST": mPOST,
+ "PUT": mPUT,
+ "TRACE": mTRACE,
+}
+
+type route struct {
+ prefix string
+ method method
+ pattern Pattern
+ handler Handler
+}
+
+type router struct {
+ lock sync.Mutex
+ routes []route
+ notFound Handler
+ machine *routeMachine
+}
+
+func httpMethod(mname string) method {
+ if method, ok := validMethodsMap[mname]; ok {
+ return method
+ }
+ return mIDK
+}
+
+func (rt *router) compile() *routeMachine {
+ rt.lock.Lock()
+ defer rt.lock.Unlock()
+ sm := routeMachine{
+ sm: compile(rt.routes),
+ routes: rt.routes,
+ }
+ rt.setMachine(&sm)
+ return &sm
+}
+
+func (rt *router) getMatch(c *C, w http.ResponseWriter, r *http.Request) Match {
+ rm := rt.getMachine()
+ if rm == nil {
+ rm = rt.compile()
+ }
+
+ methods, route := rm.route(c, w, r)
+ if route != nil {
+ return Match{
+ Pattern: route.pattern,
+ Handler: route.handler,
+ }
+ }
+
+ if methods == 0 {
+ return Match{Handler: rt.notFound}
+ }
+
+ var methodsList = make([]string, 0)
+ for mname, meth := range validMethodsMap {
+ if methods&meth != 0 {
+ methodsList = append(methodsList, mname)
+ }
+ }
+ sort.Strings(methodsList)
+
+ if c.Env == nil {
+ c.Env = map[interface{}]interface{}{
+ ValidMethodsKey: methodsList,
+ }
+ } else {
+ c.Env[ValidMethodsKey] = methodsList
+ }
+ return Match{Handler: rt.notFound}
+}
+
+func (rt *router) route(c *C, w http.ResponseWriter, r *http.Request) {
+ match := GetMatch(*c)
+ if match.Handler == nil {
+ match = rt.getMatch(c, w, r)
+ }
+ match.Handler.ServeHTTPC(*c, w, r)
+}
+
+func (rt *router) handleUntyped(p PatternType, m method, h HandlerType) {
+ rt.handle(ParsePattern(p), m, parseHandler(h))
+}
+
+func (rt *router) handle(p Pattern, m method, h Handler) {
+ rt.lock.Lock()
+ defer rt.lock.Unlock()
+
+ // Calculate the sorted insertion point, because there's no reason to do
+ // swapping hijinks if we're already making a copy. We need to use
+ // bubble sort because we can only compare adjacent elements.
+ pp := p.Prefix()
+ var i int
+ for i = len(rt.routes); i > 0; i-- {
+ rip := rt.routes[i-1].prefix
+ if rip <= pp || strings.HasPrefix(rip, pp) {
+ break
+ }
+ }
+
+ newRoutes := make([]route, len(rt.routes)+1)
+ copy(newRoutes, rt.routes[:i])
+ newRoutes[i] = route{
+ prefix: pp,
+ method: m,
+ pattern: p,
+ handler: h,
+ }
+ copy(newRoutes[i+1:], rt.routes[i:])
+
+ rt.setMachine(nil)
+ rt.routes = newRoutes
+}
diff --git a/vendor/github.com/zenazn/goji/web/string_pattern.go b/vendor/github.com/zenazn/goji/web/string_pattern.go
new file mode 100644
index 0000000..aa9b33a
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/string_pattern.go
@@ -0,0 +1,137 @@
+package web
+
+import (
+ "fmt"
+ "net/http"
+ "regexp"
+ "strings"
+)
+
+// stringPattern is a struct describing
+type stringPattern struct {
+ raw string
+ pats []string
+ breaks []byte
+ literals []string
+ wildcard bool
+}
+
+func (s stringPattern) Prefix() string {
+ return s.literals[0]
+}
+func (s stringPattern) Match(r *http.Request, c *C) bool {
+ return s.match(r, c, true)
+}
+func (s stringPattern) Run(r *http.Request, c *C) {
+ s.match(r, c, false)
+}
+func (s stringPattern) match(r *http.Request, c *C, dryrun bool) bool {
+ path := r.URL.Path
+ var matches map[string]string
+ if !dryrun {
+ if s.wildcard {
+ matches = make(map[string]string, len(s.pats)+1)
+ } else if len(s.pats) != 0 {
+ matches = make(map[string]string, len(s.pats))
+ }
+ }
+ for i, pat := range s.pats {
+ sli := s.literals[i]
+ if !strings.HasPrefix(path, sli) {
+ return false
+ }
+ path = path[len(sli):]
+
+ m := 0
+ bc := s.breaks[i]
+ for ; m < len(path); m++ {
+ if path[m] == bc || path[m] == '/' {
+ break
+ }
+ }
+ if m == 0 {
+ // Empty strings are not matches, otherwise routes like
+ // "/:foo" would match the path "/"
+ return false
+ }
+ if !dryrun {
+ matches[pat] = path[:m]
+ }
+ path = path[m:]
+ }
+ // There's exactly one more literal than pat.
+ tail := s.literals[len(s.pats)]
+ if s.wildcard {
+ if !strings.HasPrefix(path, tail) {
+ return false
+ }
+ if !dryrun {
+ matches["*"] = path[len(tail)-1:]
+ }
+ } else if path != tail {
+ return false
+ }
+
+ if c == nil || dryrun {
+ return true
+ }
+
+ if c.URLParams == nil {
+ c.URLParams = matches
+ } else {
+ for k, v := range matches {
+ c.URLParams[k] = v
+ }
+ }
+ return true
+}
+
+func (s stringPattern) String() string {
+ return fmt.Sprintf("stringPattern(%q)", s.raw)
+}
+
+func (s stringPattern) Raw() string {
+ return s.raw
+}
+
+// "Break characters" are characters that can end patterns. They are not allowed
+// to appear in pattern names. "/" was chosen because it is the standard path
+// separator, and "." was chosen because it often delimits file extensions. ";"
+// and "," were chosen because Section 3.3 of RFC 3986 suggests their use.
+const bc = "/.;,"
+
+var patternRe = regexp.MustCompile(`[` + bc + `]:([^` + bc + `]+)`)
+
+func parseStringPattern(s string) stringPattern {
+ raw := s
+ var wildcard bool
+ if strings.HasSuffix(s, "/*") {
+ s = s[:len(s)-1]
+ wildcard = true
+ }
+
+ matches := patternRe.FindAllStringSubmatchIndex(s, -1)
+ pats := make([]string, len(matches))
+ breaks := make([]byte, len(matches))
+ literals := make([]string, len(matches)+1)
+ n := 0
+ for i, match := range matches {
+ a, b := match[2], match[3]
+ literals[i] = s[n : a-1] // Need to leave off the colon
+ pats[i] = s[a:b]
+ if b == len(s) {
+ breaks[i] = '/'
+ } else {
+ breaks[i] = s[b]
+ }
+ n = b
+ }
+ literals[len(matches)] = s[n:]
+ return stringPattern{
+ raw: raw,
+ pats: pats,
+ breaks: breaks,
+ literals: literals,
+ wildcard: wildcard,
+ }
+}
diff --git a/vendor/github.com/zenazn/goji/web/web.go b/vendor/github.com/zenazn/goji/web/web.go
new file mode 100644
index 0000000..21f6fcc
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/web/web.go
@@ -0,0 +1,112 @@
+/*
+Package web provides a fast and flexible middleware stack and mux.
+
+This package attempts to solve three problems that net/http does not. First, it
+allows you to specify flexible patterns, including routes with named parameters
+and regular expressions. Second, it allows you to write reconfigurable
+middleware stacks. And finally, it allows you to attach additional context to
+requests, in a manner that can be manipulated by both compliant middleware and
+handlers.
+*/
+package web
+
+import (
+ "net/http"
+)
+
+/*
+C is a request-local context object which is threaded through all compliant
+middleware layers and given to the final request handler.
+*/
+type C struct {
+ // URLParams is a map of variables extracted from the URL (typically
+ // from the path portion) during routing. See the documentation for the
+ // URL Pattern you are using (or the documentation for PatternType for
+ // the case of standard pattern types) for more information about how
+ // variables are extracted and named.
+ URLParams map[string]string
+ // Env is a free-form environment for storing request-local data. Keys
+ // may be arbitrary types that support equality, however package-private
+ // types with type-safe accessors provide a convenient way for packages
+ // to mediate access to their request-local data.
+ Env map[interface{}]interface{}
+}
+
+// Handler is similar to net/http's http.Handler, but also accepts a Goji
+// context object.
+type Handler interface {
+ ServeHTTPC(C, http.ResponseWriter, *http.Request)
+}
+
+// HandlerFunc is similar to net/http's http.HandlerFunc, but supports a context
+// object. Implements both http.Handler and Handler.
+type HandlerFunc func(C, http.ResponseWriter, *http.Request)
+
+// ServeHTTP implements http.Handler, allowing HandlerFunc's to be used with
+// net/http and other compliant routers. When used in this way, the underlying
+// function will be passed an empty context.
+func (h HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ h(C{}, w, r)
+}
+
+// ServeHTTPC implements Handler.
+func (h HandlerFunc) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {
+ h(c, w, r)
+}
+
+/*
+PatternType is the type denoting Patterns and types that Goji internally
+converts to Pattern (via the ParsePattern function). In order to provide an
+expressive API, this type is an alias for interface{} that is named for the
+purposes of documentation, however only the following concrete types are
+accepted:
+ - types that implement Pattern
+ - string, which is interpreted as a Sinatra-like URL pattern. In
+ particular, the following syntax is recognized:
+ - a path segment starting with a colon will match any
+ string placed at that position. e.g., "/:name" will match
+ "/carl", binding "name" to "carl".
+ - a pattern ending with "/*" will match any route with that
+ prefix. For instance, the pattern "/u/:name/*" will match
+ "/u/carl/" and "/u/carl/projects/123", but not "/u/carl"
+ (because there is no trailing slash). In addition to any names
+ bound in the pattern, the special key "*" is bound to the
+ unmatched tail of the match, but including the leading "/". So
+ for the two matching examples above, "*" would be bound to "/"
+ and "/projects/123" respectively.
+ Unlike http.ServeMux's patterns, string patterns support neither the
+ "rooted subtree" behavior nor Host-specific routes. Users who require
+ either of these features are encouraged to compose package http's mux
+ with the mux provided by this package.
+ - regexp.Regexp, which is assumed to be a Perl-style regular expression
+ that is anchored on the left (i.e., the beginning of the string). If
+ your regular expression is not anchored on the left, a
+ hopefully-identical left-anchored regular expression will be created
+ and used instead.
+
+ Capturing groups will be converted into bound URL parameters in
+ URLParams. If the capturing group is named, that name will be used;
+ otherwise the special identifiers "$1", "$2", etc. will be used.
+*/
+type PatternType interface{}
+
+/*
+HandlerType is the type of Handlers and types that Goji internally converts to
+Handler. In order to provide an expressive API, this type is an alias for
+interface{} that is named for the purposes of documentation, however only the
+following concrete types are accepted:
+ - types that implement http.Handler
+ - types that implement Handler
+ - func(http.ResponseWriter, *http.Request)
+ - func(web.C, http.ResponseWriter, *http.Request)
+*/
+type HandlerType interface{}
+
+/*
+MiddlewareType is the type of Goji middleware. In order to provide an expressive
+API, this type is an alias for interface{} that is named for the purposes of
+documentation, however only the following concrete types are accepted:
+ - func(http.Handler) http.Handler
+ - func(*web.C, http.Handler) http.Handler
+*/
+type MiddlewareType interface{}