From b049991a46a2f619344bd6e915745703864d0134 Mon Sep 17 00:00:00 2001 From: Felix Hanley Date: Mon, 5 Dec 2016 15:16:58 +0700 Subject: Clean up source structure and update vendor versions --- .../zenazn/goji/web/middleware/envinit.go | 27 ++++++ .../zenazn/goji/web/middleware/logger.go | 92 ++++++++++++++++++++ .../zenazn/goji/web/middleware/middleware.go | 4 + .../zenazn/goji/web/middleware/nocache.go | 55 ++++++++++++ .../zenazn/goji/web/middleware/options.go | 97 ++++++++++++++++++++++ .../zenazn/goji/web/middleware/realip.go | 51 ++++++++++++ .../zenazn/goji/web/middleware/recoverer.go | 44 ++++++++++ .../zenazn/goji/web/middleware/request_id.go | 88 ++++++++++++++++++++ .../zenazn/goji/web/middleware/subrouter.go | 65 +++++++++++++++ .../zenazn/goji/web/middleware/terminal.go | 60 +++++++++++++ .../zenazn/goji/web/middleware/urlquery.go | 24 ++++++ 11 files changed, 607 insertions(+) create mode 100644 vendor/github.com/zenazn/goji/web/middleware/envinit.go create mode 100644 vendor/github.com/zenazn/goji/web/middleware/logger.go create mode 100644 vendor/github.com/zenazn/goji/web/middleware/middleware.go create mode 100644 vendor/github.com/zenazn/goji/web/middleware/nocache.go create mode 100644 vendor/github.com/zenazn/goji/web/middleware/options.go create mode 100644 vendor/github.com/zenazn/goji/web/middleware/realip.go create mode 100644 vendor/github.com/zenazn/goji/web/middleware/recoverer.go create mode 100644 vendor/github.com/zenazn/goji/web/middleware/request_id.go create mode 100644 vendor/github.com/zenazn/goji/web/middleware/subrouter.go create mode 100644 vendor/github.com/zenazn/goji/web/middleware/terminal.go create mode 100644 vendor/github.com/zenazn/goji/web/middleware/urlquery.go (limited to 'vendor/github.com/zenazn/goji/web/middleware') 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) +} -- cgit v1.2.3