diff options
Diffstat (limited to 'vendor/github.com')
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{} |
