aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/zenazn/goji/web/middleware
diff options
context:
space:
mode:
authorFelix Hanley <felix@userspace.com.au>2016-12-05 08:16:58 +0000
committerFelix Hanley <felix@userspace.com.au>2016-12-05 08:16:58 +0000
commitb049991a46a2f619344bd6e915745703864d0134 (patch)
treeec1d3897a7b69c7c63a3774d4c42dfbb8cb46432 /vendor/github.com/zenazn/goji/web/middleware
parente1c3d6f7db06d592538f1162b2b6b9f1b6efa330 (diff)
downloadgo-dict2rest-b049991a46a2f619344bd6e915745703864d0134.tar.gz
go-dict2rest-b049991a46a2f619344bd6e915745703864d0134.tar.bz2
Clean up source structure and update vendor versionsHEADmaster
Diffstat (limited to 'vendor/github.com/zenazn/goji/web/middleware')
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/envinit.go27
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/logger.go92
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/middleware.go4
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/nocache.go55
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/options.go97
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/realip.go51
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/recoverer.go44
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/request_id.go88
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/subrouter.go65
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/terminal.go60
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/urlquery.go24
11 files changed, 607 insertions, 0 deletions
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)
+}