diff --git a/vendor/github.com/zenazn/goji/.travis.yml b/vendor/github.com/zenazn/goji/.travis.yml new file mode 100644 index 0000000..35410e2 --- /dev/null +++ b/vendor/github.com/zenazn/goji/.travis.yml @@ -0,0 +1,33 @@ +language: go + +matrix: + include: + - go: 1.2 + install: + - go get golang.org/x/tools/cmd/cover + - go list -f '{{range .Imports}}{{.}} {{end}}' ./... | xargs go get -v + - go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs go get -v + - go: 1.3 + install: + - go get golang.org/x/tools/cmd/cover + - go list -f '{{range .Imports}}{{.}} {{end}}' ./... | xargs go get -v + - go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs go get -v + - go: 1.4 + install: + - go get golang.org/x/tools/cmd/cover + - go list -f '{{range .Imports}}{{.}} {{end}}' ./... | xargs go get -v + - go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs go get -v + - go: 1.5 + install: + - go list -f '{{range .Imports}}{{.}} {{end}}' ./... | xargs go get -v + - go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs go get -v + - go: 1.6 + install: + - go list -f '{{range .Imports}}{{.}} {{end}}' ./... | xargs go get -v + - go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs go get -v + - go: tip + install: + - go list -f '{{range .Imports}}{{.}} {{end}}' ./... | xargs go get -v + - go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs go get -v +script: + - go test -cover ./... 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/README.md b/vendor/github.com/zenazn/goji/README.md new file mode 100644 index 0000000..82df6fe --- /dev/null +++ b/vendor/github.com/zenazn/goji/README.md @@ -0,0 +1,176 @@ +Goji +==== + +[![GoDoc](https://godoc.org/github.com/zenazn/goji/web?status.svg)](https://godoc.org/github.com/zenazn/goji/web) [![Build Status](https://travis-ci.org/zenazn/goji.svg?branch=master)](https://travis-ci.org/zenazn/goji) + +Goji is a minimalistic web framework that values composability and simplicity. + +This project has been superseded by a [new version of Goji][goji2] by the same +author, which has very similar primitives and semantics, but has been updated to +reflect several years of experience with this library and the surrounding Go +ecosystem. This project is still well-loved and well-maintained, and will be for +the foreseeable future, but new projects are encouraged to use `goji.io` +instead. + +[goji2]: https://goji.io + +Example +------- + +```go +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() +} +``` + +Goji also includes a [sample application][sample] in the `example` folder which +was artificially constructed to show off all of Goji's features. Check it out! + +[sample]: https://github.com/zenazn/goji/tree/master/example + + +Features +-------- + +* Compatible with `net/http` +* URL patterns (both Sinatra style `/foo/:bar` patterns and regular expressions, + as well as [custom patterns][pattern]) +* Reconfigurable middleware stack +* Context/environment object threaded through middleware and handlers +* Automatic support for [Einhorn][einhorn], systemd, and [more][bind] +* [Graceful shutdown][graceful], and zero-downtime graceful reload when combined + with Einhorn. +* High in antioxidants + +[einhorn]: https://github.com/stripe/einhorn +[bind]: http://godoc.org/github.com/zenazn/goji/bind +[graceful]: http://godoc.org/github.com/zenazn/goji/graceful +[pattern]: https://godoc.org/github.com/zenazn/goji/web#Pattern + + +Stability +--------- + +Goji's API is essentially frozen, and guarantees to never break compatibility +with existing code (under similar rules to the Go project's +[guidelines][compat]). Goji is suitable for use in production, and has served +billions of requests across several companies. + +[compat]: https://golang.org/doc/go1compat + + +Is it any good? +--------------- + +Maybe! + +There are [plenty][revel] of [other][gorilla] [good][pat] [Go][martini] +[web][gocraft] [frameworks][tiger] out there. Goji is by no means especially +novel, nor is it uniquely good. The primary difference between Goji and other +frameworks—and the primary reason I think Goji is any good—is its philosophy: + +Goji first of all attempts to be simple. It is of the Sinatra and Flask school +of web framework design, and not the Rails/Django one. If you want me to tell +you what directory you should put your models in, or if you want built-in flash +sessions, you won't have a good time with Goji. + +Secondly, Goji attempts to be composable. It is fully composable with net/http, +and can be used as a `http.Handler`, or can serve arbitrary `http.Handler`s. At +least a few HTTP frameworks share this property, and is not particularly novel. +The more interesting property in my mind is that Goji is fully composable with +itself: it defines an interface (`web.Handler`) which is both fully compatible +with `http.Handler` and allows Goji to perform a "protocol upgrade" of sorts +when it detects that it is talking to itself (or another `web.Handler` +compatible component). `web.Handler` is at the core of Goji's interfaces and is +what allows it to share request contexts across unrelated objects. + +Third, Goji is not magic. One of my favorite existing frameworks is +[Martini][martini], but I rejected it in favor of building Goji because I +thought it was too magical. Goji's web package does not use reflection at all, +which is not in itself a sign of API quality, but to me at least seems to +suggest it. + +Finally, Goji gives you enough rope to hang yourself with. One of my other +favorite libraries, [pat][pat], implements Sinatra-like routing in a +particularly elegant way, but because of its reliance on net/http's interfaces, +doesn't allow programmers to thread their own state through the request handling +process. Implementing arbitrary context objects was one of the primary +motivations behind abandoning pat to write Goji. + +[revel]: http://revel.github.io/ +[gorilla]: http://www.gorillatoolkit.org/ +[pat]: https://github.com/bmizerany/pat +[martini]: http://martini.codegangsta.io/ +[gocraft]: https://github.com/gocraft/web +[tiger]: https://github.com/rcrowley/go-tigertonic + + +Is it fast? +----------- + +[Yeah][bench1], [it is][bench2]. Goji is among the fastest HTTP routers out +there, and is very gentle on the garbage collector. + +But that's sort of missing the point. Almost all Go routers are fast enough for +almost all purposes. In my opinion, what matters more is how simple and flexible +the routing semantics are. + +Goji provides results indistinguishable from naively trying routes one after +another. This means that a route added before another route will be attempted +before that route as well. This is perhaps the most simple and most intuitive +interface a router can provide, and makes routes very easy to understand and +debug. + +Goji's router is also very flexible: in addition to the standard Sinatra-style +patterns and regular expression patterns, you can define [custom +patterns][pattern] to perform whatever custom matching logic you desire. Custom +patterns of course are fully compatible with the routing semantics above. + +It's easy (and quite a bit of fun!) to get carried away by microbenchmarks, but +at the end of the day you're not going to miss those extra hundred nanoseconds +on a request. What matters is that you aren't compromising on the API for a +handful of CPU cycles. + +[bench1]: https://gist.github.com/zenazn/c5c8528efe1a00634096 +[bench2]: https://github.com/julienschmidt/go-http-routing-benchmark + + +Third-Party Libraries +--------------------- + +Goji is already compatible with a great many third-party libraries that are +themselves compatible with `net/http`, however some library authors have gone +out of their way to include Goji compatibility specifically, perhaps by +integrating more tightly with Goji's `web.C` or by providing a custom pattern +type. An informal list of such libraries is maintained [on the wiki][third]; +feel free to add to it as you see fit. + +[third]: https://github.com/zenazn/goji/wiki/Third-Party-Libraries + + +Contributing +------------ + +Please do! I love pull requests, and I love pull requests that include tests +even more. Goji's core packages have pretty good code coverage (yay code +coverage gamification!), and if you have the time to write tests I'd like to +keep it that way. + +In addition to contributing code, I'd love to know what you think about Goji. +Please open an issue or send me an email with your thoughts; it'd mean a lot to +me. 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..8e783bf --- /dev/null +++ b/vendor/github.com/zenazn/goji/bind/bind.go @@ -0,0 +1,145 @@ +/* +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() +} + +// 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 port 8000, 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() { + defaultBind := ":8000" + if s := Sniff(); s != "" { + defaultBind = s + } + flag.StringVar(&bind, "bind", defaultBind, + `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/.gitignore b/vendor/github.com/zenazn/goji/example/.gitignore new file mode 100644 index 0000000..33a9488 --- /dev/null +++ b/vendor/github.com/zenazn/goji/example/.gitignore @@ -0,0 +1 @@ +example diff --git a/vendor/github.com/zenazn/goji/example/README.md b/vendor/github.com/zenazn/goji/example/README.md new file mode 100644 index 0000000..8d33103 --- /dev/null +++ b/vendor/github.com/zenazn/goji/example/README.md @@ -0,0 +1,10 @@ +Gritter +======= + +Gritter is an example application built using Goji, where people who have +nothing better to do can post short 140-character "greets." + +A good place to start is with `main.go`, which contains a well-commented +walkthrough of Goji's features. Gritter uses a couple custom middlewares, which +have been arbitrarily placed in `middleware.go`. Finally some uninteresting +"database models" live in `models.go`. 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\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/conn_test.go b/vendor/github.com/zenazn/goji/graceful/listener/conn_test.go new file mode 100644 index 0000000..ff26e32 --- /dev/null +++ b/vendor/github.com/zenazn/goji/graceful/listener/conn_test.go @@ -0,0 +1,198 @@ +package listener + +import ( + "io" + "strings" + "testing" + "time" +) + +func TestManualRead(t *testing.T) { + t.Parallel() + l, c, wc := singleConn(t, Manual) + + go c.AllowRead() + wc.Read(make([]byte, 1024)) + + if err := l.CloseIdle(); err != nil { + t.Fatalf("error closing idle connections: %v", err) + } + if !c.Closed() { + t.Error("Read() should not make connection not-idle") + } +} + +func TestAutomaticRead(t *testing.T) { + t.Parallel() + l, c, wc := singleConn(t, Automatic) + + go c.AllowRead() + wc.Read(make([]byte, 1024)) + + if err := l.CloseIdle(); err != nil { + t.Fatalf("error closing idle connections: %v", err) + } + if c.Closed() { + t.Error("expected Read() to mark connection as in-use") + } +} + +func TestDeadlineRead(t *testing.T) { + t.Parallel() + l, c, wc := singleConn(t, Deadline) + + go c.AllowRead() + if _, err := wc.Read(make([]byte, 1024)); err != nil { + t.Fatalf("error reading from connection: %v", err) + } + + if err := l.CloseIdle(); err != nil { + t.Fatalf("error closing idle connections: %v", err) + } + if c.Closed() { + t.Error("expected Read() to mark connection as in-use") + } +} + +func TestDisownedRead(t *testing.T) { + t.Parallel() + l, c, wc := singleConn(t, Deadline) + + if err := Disown(wc); err != nil { + t.Fatalf("unexpected error disowning conn: %v", err) + } + if err := l.Close(); err != nil { + t.Fatalf("unexpected error closing listener: %v", err) + } + if err := l.Drain(); err != nil { + t.Fatalf("unexpected error draining listener: %v", err) + } + + go c.AllowRead() + if _, err := wc.Read(make([]byte, 1024)); err != nil { + t.Fatalf("error reading from connection: %v", err) + } +} + +func TestCloseConn(t *testing.T) { + t.Parallel() + l, _, wc := singleConn(t, Deadline) + + if err := MarkInUse(wc); err != nil { + t.Fatalf("error marking conn in use: %v", err) + } + if err := wc.Close(); err != nil { + t.Errorf("error closing connection: %v", err) + } + // This will hang if wc.Close() doesn't un-track the connection + if err := l.Drain(); err != nil { + t.Errorf("error draining listener: %v", err) + } +} + +// Regression test for issue #130. +func TestDisownedClose(t *testing.T) { + t.Parallel() + _, c, wc := singleConn(t, Deadline) + + if err := Disown(wc); err != nil { + t.Fatalf("unexpected error disowning conn: %v", err) + } + if err := wc.Close(); err != nil { + t.Errorf("error closing connection: %v", err) + } + if !c.Closed() { + t.Errorf("connection didn't get closed") + } +} + +func TestManualReadDeadline(t *testing.T) { + t.Parallel() + l, c, wc := singleConn(t, Manual) + + if err := MarkInUse(wc); err != nil { + t.Fatalf("error marking connection in use: %v", err) + } + if err := wc.SetReadDeadline(time.Now()); err != nil { + t.Fatalf("error setting read deadline: %v", err) + } + if err := l.CloseIdle(); err != nil { + t.Fatalf("error closing idle connections: %v", err) + } + if c.Closed() { + t.Error("SetReadDeadline() should not mark manual conn as idle") + } +} + +func TestAutomaticReadDeadline(t *testing.T) { + t.Parallel() + l, c, wc := singleConn(t, Automatic) + + if err := MarkInUse(wc); err != nil { + t.Fatalf("error marking connection in use: %v", err) + } + if err := wc.SetReadDeadline(time.Now()); err != nil { + t.Fatalf("error setting read deadline: %v", err) + } + if err := l.CloseIdle(); err != nil { + t.Fatalf("error closing idle connections: %v", err) + } + if c.Closed() { + t.Error("SetReadDeadline() should not mark automatic conn as idle") + } +} + +func TestDeadlineReadDeadline(t *testing.T) { + t.Parallel() + l, c, wc := singleConn(t, Deadline) + + if err := MarkInUse(wc); err != nil { + t.Fatalf("error marking connection in use: %v", err) + } + if err := wc.SetReadDeadline(time.Now()); err != nil { + t.Fatalf("error setting read deadline: %v", err) + } + if err := l.CloseIdle(); err != nil { + t.Fatalf("error closing idle connections: %v", err) + } + if !c.Closed() { + t.Error("SetReadDeadline() should mark deadline conn as idle") + } +} + +type readerConn struct { + fakeConn +} + +func (rc *readerConn) ReadFrom(r io.Reader) (int64, error) { + return 123, nil +} + +func TestReadFrom(t *testing.T) { + t.Parallel() + + l := makeFakeListener("net.Listener") + wl := Wrap(l, Manual) + c := &readerConn{ + fakeConn{ + read: make(chan struct{}), + write: make(chan struct{}), + closed: make(chan struct{}), + me: fakeAddr{"tcp", "local"}, + you: fakeAddr{"tcp", "remote"}, + }, + } + + go l.Enqueue(c) + wc, err := wl.Accept() + if err != nil { + t.Fatalf("error accepting connection: %v", err) + } + + // The io.MultiReader is a convenient hack to ensure that we're using + // our ReadFrom, not strings.Reader's WriteTo. + r := io.MultiReader(strings.NewReader("hello world")) + if _, err := io.Copy(wc, r); err != nil { + t.Fatalf("error copying: %v", err) + } +} diff --git a/vendor/github.com/zenazn/goji/graceful/listener/fake_test.go b/vendor/github.com/zenazn/goji/graceful/listener/fake_test.go new file mode 100644 index 0000000..083f6a8 --- /dev/null +++ b/vendor/github.com/zenazn/goji/graceful/listener/fake_test.go @@ -0,0 +1,123 @@ +package listener + +import ( + "net" + "time" +) + +type fakeAddr struct { + network, addr string +} + +func (f fakeAddr) Network() string { + return f.network +} +func (f fakeAddr) String() string { + return f.addr +} + +type fakeListener struct { + ch chan net.Conn + closed chan struct{} + addr net.Addr +} + +func makeFakeListener(addr string) *fakeListener { + a := fakeAddr{"tcp", addr} + return &fakeListener{ + ch: make(chan net.Conn), + closed: make(chan struct{}), + addr: a, + } +} + +func (f *fakeListener) Accept() (net.Conn, error) { + select { + case c := <-f.ch: + return c, nil + case <-f.closed: + return nil, errClosing + } +} +func (f *fakeListener) Close() error { + close(f.closed) + return nil +} + +func (f *fakeListener) Addr() net.Addr { + return f.addr +} + +func (f *fakeListener) Enqueue(c net.Conn) { + f.ch <- c +} + +type fakeConn struct { + read, write, closed chan struct{} + me, you net.Addr +} + +func makeFakeConn(me, you string) *fakeConn { + return &fakeConn{ + read: make(chan struct{}), + write: make(chan struct{}), + closed: make(chan struct{}), + me: fakeAddr{"tcp", me}, + you: fakeAddr{"tcp", you}, + } +} + +func (f *fakeConn) Read(buf []byte) (int, error) { + select { + case <-f.read: + return len(buf), nil + case <-f.closed: + return 0, errClosing + } +} + +func (f *fakeConn) Write(buf []byte) (int, error) { + select { + case <-f.write: + return len(buf), nil + case <-f.closed: + return 0, errClosing + } +} + +func (f *fakeConn) Close() error { + close(f.closed) + return nil +} + +func (f *fakeConn) LocalAddr() net.Addr { + return f.me +} +func (f *fakeConn) RemoteAddr() net.Addr { + return f.you +} +func (f *fakeConn) SetDeadline(t time.Time) error { + return nil +} +func (f *fakeConn) SetReadDeadline(t time.Time) error { + return nil +} +func (f *fakeConn) SetWriteDeadline(t time.Time) error { + return nil +} + +func (f *fakeConn) Closed() bool { + select { + case <-f.closed: + return true + default: + return false + } +} + +func (f *fakeConn) AllowRead() { + f.read <- struct{}{} +} +func (f *fakeConn) AllowWrite() { + f.write <- struct{}{} +} 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/listener_test.go b/vendor/github.com/zenazn/goji/graceful/listener/listener_test.go new file mode 100644 index 0000000..dcefdaa --- /dev/null +++ b/vendor/github.com/zenazn/goji/graceful/listener/listener_test.go @@ -0,0 +1,156 @@ +package listener + +import ( + "net" + "testing" + "time" +) + +// Helper for tests acting on a single accepted connection +func singleConn(t *testing.T, m mode) (*T, *fakeConn, net.Conn) { + l := makeFakeListener("net.Listener") + wl := Wrap(l, m) + c := makeFakeConn("local", "remote") + + go l.Enqueue(c) + wc, err := wl.Accept() + if err != nil { + t.Fatalf("error accepting connection: %v", err) + } + return wl, c, wc +} + +func TestAddr(t *testing.T) { + t.Parallel() + l, c, wc := singleConn(t, Manual) + + if a := l.Addr(); a.String() != "net.Listener" { + t.Errorf("addr was %v, wanted net.Listener", a) + } + + if c.LocalAddr() != wc.LocalAddr() { + t.Errorf("local addresses don't match: %v, %v", c.LocalAddr(), + wc.LocalAddr()) + } + if c.RemoteAddr() != wc.RemoteAddr() { + t.Errorf("remote addresses don't match: %v, %v", c.RemoteAddr(), + wc.RemoteAddr()) + } +} + +func TestBasicCloseIdle(t *testing.T) { + t.Parallel() + l, c, _ := singleConn(t, Manual) + + if err := l.CloseIdle(); err != nil { + t.Fatalf("error closing idle connections: %v", err) + } + if !c.Closed() { + t.Error("idle connection not closed") + } +} + +func TestMark(t *testing.T) { + t.Parallel() + l, c, wc := singleConn(t, Manual) + + if err := MarkInUse(wc); err != nil { + t.Fatalf("error marking %v in-use: %v", wc, err) + } + if err := l.CloseIdle(); err != nil { + t.Fatalf("error closing idle connections: %v", err) + } + if c.Closed() { + t.Errorf("manually in-use connection was closed") + } + + if err := MarkIdle(wc); err != nil { + t.Fatalf("error marking %v idle: %v", wc, err) + } + if err := l.CloseIdle(); err != nil { + t.Fatalf("error closing idle connections: %v", err) + } + if !c.Closed() { + t.Error("manually idle connection was not closed") + } +} + +func TestDisown(t *testing.T) { + t.Parallel() + l, c, wc := singleConn(t, Manual) + + if err := Disown(wc); err != nil { + t.Fatalf("error disowning connection: %v", err) + } + if err := l.CloseIdle(); err != nil { + t.Fatalf("error closing idle connections: %v", err) + } + + if c.Closed() { + t.Errorf("disowned connection got closed") + } +} + +func TestDrain(t *testing.T) { + t.Parallel() + l, _, wc := singleConn(t, Manual) + + MarkInUse(wc) + start := time.Now() + go func() { + time.Sleep(50 * time.Millisecond) + MarkIdle(wc) + }() + if err := l.Drain(); err != nil { + t.Fatalf("error draining listener: %v", err) + } + end := time.Now() + if dt := end.Sub(start); dt < 50*time.Millisecond { + t.Errorf("expected at least 50ms wait, but got %v", dt) + } +} + +func TestDrainAll(t *testing.T) { + t.Parallel() + l, c, wc := singleConn(t, Manual) + + MarkInUse(wc) + if err := l.DrainAll(); err != nil { + t.Fatalf("error draining listener: %v", err) + } + if !c.Closed() { + t.Error("expected in-use connection to be closed") + } +} + +func TestErrors(t *testing.T) { + t.Parallel() + _, c, wc := singleConn(t, Manual) + if err := Disown(c); err == nil { + t.Error("expected error when disowning unmanaged net.Conn") + } + if err := MarkIdle(c); err == nil { + t.Error("expected error when marking unmanaged net.Conn idle") + } + if err := MarkInUse(c); err == nil { + t.Error("expected error when marking unmanaged net.Conn in use") + } + + if err := Disown(wc); err != nil { + t.Fatalf("unexpected error disowning socket: %v", err) + } + if err := Disown(wc); err == nil { + t.Error("expected error disowning socket twice") + } +} + +func TestClose(t *testing.T) { + t.Parallel() + l, c, _ := singleConn(t, Manual) + if err := l.Close(); err != nil { + t.Fatalf("error while closing listener: %v", err) + } + if c.Closed() { + t.Error("connection closed when listener was?") + } +} diff --git a/vendor/github.com/zenazn/goji/graceful/listener/race_test.go b/vendor/github.com/zenazn/goji/graceful/listener/race_test.go new file mode 100644 index 0000000..835d6b4 --- /dev/null +++ b/vendor/github.com/zenazn/goji/graceful/listener/race_test.go @@ -0,0 +1,103 @@ +package listener + +import ( + "fmt" + "math/rand" + "runtime" + "sync/atomic" + "testing" + "time" +) + +func init() { + // Just to make sure we get some variety + runtime.GOMAXPROCS(4 * runtime.NumCPU()) +} + +// Chosen by random die roll +const seed = 4611413766552969250 + +// This is mostly just fuzzing to see what happens. +func TestRace(t *testing.T) { + t.Parallel() + + l := makeFakeListener("net.Listener") + wl := Wrap(l, Automatic) + + var flag int32 + + go func() { + for i := 0; ; i++ { + laddr := fmt.Sprintf("local%d", i) + raddr := fmt.Sprintf("remote%d", i) + c := makeFakeConn(laddr, raddr) + go func() { + defer func() { + if r := recover(); r != nil { + if atomic.LoadInt32(&flag) != 0 { + return + } + panic(r) + } + }() + l.Enqueue(c) + }() + wc, err := wl.Accept() + if err != nil { + if atomic.LoadInt32(&flag) != 0 { + return + } + t.Fatalf("error accepting connection: %v", err) + } + + go func() { + for { + time.Sleep(50 * time.Millisecond) + c.AllowRead() + } + }() + + go func(i int64) { + rng := rand.New(rand.NewSource(i + seed)) + buf := make([]byte, 1024) + for j := 0; j < 1024; j++ { + if _, err := wc.Read(buf); err != nil { + if atomic.LoadInt32(&flag) != 0 { + // Peaceful; the connection has + // probably been closed while + // idle + return + } + t.Errorf("error reading in conn %d: %v", + i, err) + } + time.Sleep(time.Duration(rng.Intn(100)) * time.Millisecond) + // This one is to make sure the connection + // hasn't closed underneath us + if _, err := wc.Read(buf); err != nil { + t.Errorf("error reading in conn %d: %v", + i, err) + } + MarkIdle(wc) + time.Sleep(time.Duration(rng.Intn(100)) * time.Millisecond) + } + }(int64(i)) + + time.Sleep(time.Duration(i) * time.Millisecond / 2) + } + }() + + if testing.Short() { + time.Sleep(2 * time.Second) + } else { + time.Sleep(10 * time.Second) + } + start := time.Now() + atomic.StoreInt32(&flag, 1) + wl.Close() + wl.Drain() + end := time.Now() + if dt := end.Sub(start); dt > 300*time.Millisecond { + t.Errorf("took %v to drain; expected shorter", dt) + } +} 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/middleware_test.go b/vendor/github.com/zenazn/goji/graceful/middleware_test.go new file mode 100644 index 0000000..aa4e623 --- /dev/null +++ b/vendor/github.com/zenazn/goji/graceful/middleware_test.go @@ -0,0 +1,71 @@ +// +build !go1.3 + +package graceful + +import ( + "net/http" + "sync/atomic" + "testing" +) + +type fakeWriter http.Header + +func (f fakeWriter) Header() http.Header { + return http.Header(f) +} +func (f fakeWriter) Write(buf []byte) (int, error) { + return len(buf), nil +} +func (f fakeWriter) WriteHeader(status int) {} + +func testClose(t *testing.T, h http.Handler, expectClose bool) { + m := middleware(h) + r, _ := http.NewRequest("GET", "/", nil) + w := make(fakeWriter) + m.ServeHTTP(w, r) + + c, ok := w["Connection"] + if expectClose { + if !ok || len(c) != 1 || c[0] != "close" { + t.Fatal("Expected 'Connection: close'") + } + } else { + if ok { + t.Fatal("Did not expect Connection header") + } + } +} + +func TestNormal(t *testing.T) { + atomic.StoreInt32(&closing, 0) + h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte{}) + }) + testClose(t, h, false) +} + +func TestClose(t *testing.T) { + atomic.StoreInt32(&closing, 0) + h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + atomic.StoreInt32(&closing, 1) + }) + testClose(t, h, true) +} + +func TestCloseWriteHeader(t *testing.T) { + atomic.StoreInt32(&closing, 0) + h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + atomic.StoreInt32(&closing, 1) + w.WriteHeader(200) + }) + testClose(t, h, true) +} + +func TestCloseWrite(t *testing.T) { + atomic.StoreInt32(&closing, 0) + h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + atomic.StoreInt32(&closing, 1) + w.Write([]byte{}) + }) + testClose(t, h, true) +} 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/bench_test.go b/vendor/github.com/zenazn/goji/web/bench_test.go new file mode 100644 index 0000000..fc285db --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/bench_test.go @@ -0,0 +1,166 @@ +// +build go1.3 + +package web + +import ( + "crypto/rand" + "encoding/base64" + mrand "math/rand" + "net/http" + "testing" +) + +/* +The core benchmarks here are based on cypriss's mux benchmarks, which can be +found here: +https://github.com/cypriss/golang-mux-benchmark + +They happen to play very well into Goji's router's strengths. +*/ + +type nilRouter struct{} + +var helloWorld = []byte("Hello world!\n") + +func (_ nilRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) { + w.Write(helloWorld) +} + +type nilResponse struct{} + +func (_ nilResponse) Write(buf []byte) (int, error) { + return len(buf), nil +} +func (_ nilResponse) Header() http.Header { + return nil +} +func (_ nilResponse) WriteHeader(code int) { +} + +func trivialMiddleware(h http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + h.ServeHTTP(w, r) + } + return http.HandlerFunc(fn) +} + +var w nilResponse + +func addRoutes(m *Mux, prefix string) { + m.Get(prefix, nilRouter{}) + m.Post(prefix, nilRouter{}) + m.Get(prefix+"/:id", nilRouter{}) + m.Put(prefix+"/:id", nilRouter{}) + m.Delete(prefix+"/:id", nilRouter{}) +} + +func randString() string { + var buf [6]byte + rand.Reader.Read(buf[:]) + return base64.URLEncoding.EncodeToString(buf[:]) +} + +func genPrefixes(n int) []string { + p := make([]string, n) + for i := range p { + p[i] = "/" + randString() + } + return p +} + +func genRequests(prefixes []string) []*http.Request { + rs := make([]*http.Request, 5*len(prefixes)) + for i, prefix := range prefixes { + rs[5*i+0], _ = http.NewRequest("GET", prefix, nil) + rs[5*i+1], _ = http.NewRequest("POST", prefix, nil) + rs[5*i+2], _ = http.NewRequest("GET", prefix+"/foo", nil) + rs[5*i+3], _ = http.NewRequest("PUT", prefix+"/foo", nil) + rs[5*i+4], _ = http.NewRequest("DELETE", prefix+"/foo", nil) + } + return rs +} + +func permuteRequests(reqs []*http.Request) []*http.Request { + out := make([]*http.Request, len(reqs)) + perm := mrand.Perm(len(reqs)) + for i, req := range reqs { + out[perm[i]] = req + } + return out +} + +func benchN(b *testing.B, n int) { + m := New() + prefixes := genPrefixes(n) + for _, prefix := range prefixes { + addRoutes(m, prefix) + } + m.Compile() + reqs := permuteRequests(genRequests(prefixes)) + + b.ResetTimer() + b.ReportAllocs() + b.RunParallel(func(pb *testing.PB) { + i := 0 + for pb.Next() { + i++ + m.ServeHTTP(w, reqs[i%len(reqs)]) + } + }) +} + +func benchM(b *testing.B, n int) { + m := New() + m.Get("/", nilRouter{}) + for i := 0; i < n; i++ { + m.Use(trivialMiddleware) + } + r, _ := http.NewRequest("GET", "/", nil) + m.Compile() + + b.ResetTimer() + b.ReportAllocs() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + m.ServeHTTP(w, r) + } + }) +} + +func BenchmarkStatic(b *testing.B) { + m := New() + m.Get("/", nilRouter{}) + r, _ := http.NewRequest("GET", "/", nil) + m.Compile() + + b.ResetTimer() + b.ReportAllocs() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + m.ServeHTTP(w, r) + } + }) +} + +func BenchmarkRoute5(b *testing.B) { + benchN(b, 1) +} +func BenchmarkRoute50(b *testing.B) { + benchN(b, 10) +} +func BenchmarkRoute500(b *testing.B) { + benchN(b, 100) +} +func BenchmarkRoute5000(b *testing.B) { + benchN(b, 1000) +} + +func BenchmarkMiddleware1(b *testing.B) { + benchM(b, 1) +} +func BenchmarkMiddleware10(b *testing.B) { + benchM(b, 10) +} +func BenchmarkMiddleware100(b *testing.B) { + benchM(b, 100) +} 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/example_test.go b/vendor/github.com/zenazn/goji/web/example_test.go new file mode 100644 index 0000000..9dc8de2 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/example_test.go @@ -0,0 +1,69 @@ +package web_test + +import ( + "fmt" + "log" + "net/http" + "regexp" + + "github.com/zenazn/goji/web" + "github.com/zenazn/goji/web/middleware" +) + +func Example() { + m := web.New() + + // Use your favorite HTTP verbs and the interfaces you know and love + // from net/http: + m.Get("/hello", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Why hello there!\n") + }) + m.Post("/login", func(w http.ResponseWriter, r *http.Request) { + if r.FormValue("password") != "god" { + http.Error(w, "Hack the planet!", 401) + } + }) + + // Handlers can optionally take a context parameter, which contains + // (among other things) a set of bound parameters. + hello := func(c web.C, w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello, %s!\n", c.URLParams["name"]) + } + + // Bind parameters using pattern strings... + m.Get("/hello/:name", hello) + + // ...or use regular expressions if you need additional power. + bonjour := regexp.MustCompile(`^/bonjour/(?P[A-Za-z]+)$`) + m.Get(bonjour, hello) + + // Middleware are a great abstraction for performing logic on every + // request. Some middleware use the Goji context object to set + // request-scoped variables. + logger := func(h http.Handler) http.Handler { + wrap := func(w http.ResponseWriter, r *http.Request) { + log.Println("Before request") + h.ServeHTTP(w, r) + log.Println("After request") + } + return http.HandlerFunc(wrap) + } + auth := func(c *web.C, h http.Handler) http.Handler { + wrap := func(w http.ResponseWriter, r *http.Request) { + if cookie, err := r.Cookie("user"); err == nil { + c.Env["user"] = cookie.Value + } + h.ServeHTTP(w, r) + } + return http.HandlerFunc(wrap) + } + + // A Middleware stack is a flexible way to assemble the common + // components of your application, like request loggers and + // authentication. There is an ecosystem of open-source middleware for + // Goji, so there's a chance someone has already written the middleware + // you are looking for! + m.Use(middleware.EnvInit) + m.Use(logger) + m.Use(auth) +} 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/func_equal_test.go b/vendor/github.com/zenazn/goji/web/func_equal_test.go new file mode 100644 index 0000000..9080a5a --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/func_equal_test.go @@ -0,0 +1,84 @@ +package web + +import ( + "testing" +) + +// To tell you the truth, I'm not actually sure how many of these cases are +// needed. Presumably someone with more patience than I could comb through +// http://golang.org/s/go11func and figure out what all the different cases I +// ought to test are, but I think this test includes all the cases I care about +// and is at least reasonably thorough. + +func a() string { + return "A" +} +func b() string { + return "B" +} +func mkFn(s string) func() string { + return func() string { + return s + } +} + +var c = mkFn("C") +var d = mkFn("D") +var e = a +var f = c +var g = mkFn("D") + +type Type string + +func (t *Type) String() string { + return string(*t) +} + +var t1 = Type("hi") +var t2 = Type("bye") +var t1f = t1.String +var t2f = t2.String + +var funcEqualTests = []struct { + a, b func() string + result bool +}{ + {a, a, true}, + {a, b, false}, + {b, b, true}, + {a, c, false}, + {c, c, true}, + {c, d, false}, + {a, e, true}, + {a, f, false}, + {c, f, true}, + {e, f, false}, + {d, g, false}, + {t1f, t1f, true}, + {t1f, t2f, false}, +} + +func TestFuncEqual(t *testing.T) { + t.Parallel() + + for _, test := range funcEqualTests { + r := funcEqual(test.a, test.b) + if r != test.result { + t.Errorf("funcEqual(%v, %v) should have been %v", + test.a, test.b, test.result) + } + } + h := mkFn("H") + i := h + j := mkFn("H") + k := a + if !funcEqual(h, i) { + t.Errorf("h and i should have been equal") + } + if funcEqual(h, j) { + t.Errorf("h and j should not have been equal") + } + if !funcEqual(a, k) { + t.Errorf("a and k should have been equal") + } +} 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/match_test.go b/vendor/github.com/zenazn/goji/web/match_test.go new file mode 100644 index 0000000..aefff04 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/match_test.go @@ -0,0 +1,50 @@ +package web + +import ( + "net/http" + "regexp" + "testing" +) + +var rawPatterns = []PatternType{ + "/hello/:name", + regexp.MustCompile("^/hello/(?P[^/]+)$"), + testPattern{}, +} + +func TestRawPattern(t *testing.T) { + t.Parallel() + + for _, p := range rawPatterns { + m := Match{Pattern: ParsePattern(p)} + if rp := m.RawPattern(); rp != p { + t.Errorf("got %#v, expected %#v", rp, p) + } + } +} + +type httpHandlerOnly struct{} + +func (httpHandlerOnly) ServeHTTP(w http.ResponseWriter, r *http.Request) {} + +type handlerOnly struct{} + +func (handlerOnly) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) {} + +var rawHandlers = []HandlerType{ + func(w http.ResponseWriter, r *http.Request) {}, + func(c C, w http.ResponseWriter, r *http.Request) {}, + httpHandlerOnly{}, + handlerOnly{}, +} + +func TestRawHandler(t *testing.T) { + t.Parallel() + + for _, h := range rawHandlers { + m := Match{Handler: parseHandler(h)} + if rh := m.RawHandler(); !funcEqual(rh, h) { + t.Errorf("got %#v, expected %#v", rh, h) + } + } +} 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/nocache_test.go b/vendor/github.com/zenazn/goji/web/middleware/nocache_test.go new file mode 100644 index 0000000..1fb71f6 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/middleware/nocache_test.go @@ -0,0 +1,29 @@ +package middleware + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/zenazn/goji/web" +) + +func TestNoCache(t *testing.T) { + + rr := httptest.NewRecorder() + s := web.New() + + s.Use(NoCache) + r, err := http.NewRequest("GET", "/", nil) + if err != nil { + t.Fatal(err) + } + + s.ServeHTTP(rr, r) + + for k, v := range noCacheHeaders { + if rr.HeaderMap[k][0] != v { + t.Errorf("%s header not set by middleware.", k) + } + } +} 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/options_test.go b/vendor/github.com/zenazn/goji/web/middleware/options_test.go new file mode 100644 index 0000000..e962145 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/middleware/options_test.go @@ -0,0 +1,112 @@ +package middleware + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/zenazn/goji/web" +) + +func testOptions(r *http.Request, f func(*web.C, http.ResponseWriter, *http.Request)) *httptest.ResponseRecorder { + var c web.C + + h := func(w http.ResponseWriter, r *http.Request) { + f(&c, w, r) + } + m := AutomaticOptions(&c, http.HandlerFunc(h)) + w := httptest.NewRecorder() + m.ServeHTTP(w, r) + + return w +} + +var optionsTestEnv = map[interface{}]interface{}{ + web.ValidMethodsKey: []string{ + "hello", + "world", + }, +} + +func TestAutomaticOptions(t *testing.T) { + t.Parallel() + + // Shouldn't interfere with normal requests + r, _ := http.NewRequest("GET", "/", nil) + rr := testOptions(r, + func(c *web.C, w http.ResponseWriter, r *http.Request) { + w.Write([]byte{'h', 'i'}) + }, + ) + if rr.Code != http.StatusOK { + t.Errorf("status is %d, not 200", rr.Code) + } + if rr.Body.String() != "hi" { + t.Errorf("body was %q, should be %q", rr.Body.String(), "hi") + } + allow := rr.HeaderMap.Get("Allow") + if allow != "" { + t.Errorf("Allow header was set to %q, should be empty", allow) + } + + // If we respond non-404 to an OPTIONS request, also don't interfere + r, _ = http.NewRequest("OPTIONS", "/", nil) + rr = testOptions(r, + func(c *web.C, w http.ResponseWriter, r *http.Request) { + c.Env = optionsTestEnv + w.Write([]byte{'h', 'i'}) + }, + ) + if rr.Code != http.StatusOK { + t.Errorf("status is %d, not 200", rr.Code) + } + if rr.Body.String() != "hi" { + t.Errorf("body was %q, should be %q", rr.Body.String(), "hi") + } + allow = rr.HeaderMap.Get("Allow") + if allow != "" { + t.Errorf("Allow header was set to %q, should be empty", allow) + } + + // Provide options if we 404. Make sure we nom the output bytes + r, _ = http.NewRequest("OPTIONS", "/", nil) + rr = testOptions(r, + func(c *web.C, w http.ResponseWriter, r *http.Request) { + c.Env = optionsTestEnv + w.WriteHeader(http.StatusNotFound) + w.Write([]byte{'h', 'i'}) + }, + ) + if rr.Code != http.StatusOK { + t.Errorf("status is %d, not 200", rr.Code) + } + if rr.Body.Len() != 0 { + t.Errorf("body was %q, should be empty", rr.Body.String()) + } + allow = rr.HeaderMap.Get("Allow") + correctHeaders := "hello, world, OPTIONS" + if allow != "hello, world, OPTIONS" { + t.Errorf("Allow header should be %q, was %q", correctHeaders, + allow) + } + + // If we somehow 404 without giving a list of valid options, don't do + // anything + r, _ = http.NewRequest("OPTIONS", "/", nil) + rr = testOptions(r, + func(c *web.C, w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + w.Write([]byte{'h', 'i'}) + }, + ) + if rr.Code != http.StatusNotFound { + t.Errorf("status is %d, not 404", rr.Code) + } + if rr.Body.String() != "hi" { + t.Errorf("body was %q, should be %q", rr.Body.String(), "hi") + } + allow = rr.HeaderMap.Get("Allow") + if allow != "" { + t.Errorf("Allow header was set to %q, should be empty", allow) + } +} 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/subrouter_test.go b/vendor/github.com/zenazn/goji/web/middleware/subrouter_test.go new file mode 100644 index 0000000..c46c27e --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/middleware/subrouter_test.go @@ -0,0 +1,28 @@ +package middleware + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/zenazn/goji/web" +) + +func TestSubRouterMatch(t *testing.T) { + m := web.New() + m.Use(m.Router) + + m2 := web.New() + m2.Use(SubRouter) + m2.Get("/bar", func(w http.ResponseWriter, r *http.Request) {}) + + m.Get("/foo/*", m2) + + r, err := http.NewRequest("GET", "/foo/bar", nil) + if err != nil { + t.Fatal(err) + } + + // This function will recurse forever if SubRouter + Match didn't work. + m.ServeHTTP(httptest.NewRecorder(), r) +} 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/middleware/urlquery_test.go b/vendor/github.com/zenazn/goji/web/middleware/urlquery_test.go new file mode 100644 index 0000000..d9b7833 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/middleware/urlquery_test.go @@ -0,0 +1,53 @@ +package middleware + +import ( + "net/http" + "net/http/httptest" + "net/url" + "reflect" + "testing" + + "github.com/zenazn/goji/web" +) + +func testURLQuery(r *http.Request, f func(*web.C, http.ResponseWriter, *http.Request)) *httptest.ResponseRecorder { + var c web.C + + h := func(w http.ResponseWriter, r *http.Request) { + f(&c, w, r) + } + m := URLQuery(&c, http.HandlerFunc(h)) + w := httptest.NewRecorder() + m.ServeHTTP(w, r) + + return w +} + +func TestURLQuery(t *testing.T) { + type testcase struct { + url string + expectedParams url.Values + } + + // we're not testing url.Query() here, but rather that the results of the query + // appear in the context + testcases := []testcase{ + testcase{"/", url.Values{}}, + testcase{"/?a=1&b=2&a=3", url.Values{"a": []string{"1", "3"}, "b": []string{"2"}}}, + testcase{"/?x=1&y=2&z=3#freddyishere", url.Values{"x": []string{"1"}, "y": []string{"2"}, "z": []string{"3"}}}, + } + + for _, tc := range testcases { + r, _ := http.NewRequest("GET", tc.url, nil) + testURLQuery(r, + func(c *web.C, w http.ResponseWriter, r *http.Request) { + params := c.Env[URLQueryKey].(url.Values) + if !reflect.DeepEqual(params, tc.expectedParams) { + t.Errorf("GET %s, URLQuery middleware found %v, should be %v", tc.url, params, tc.expectedParams) + } + + w.Write([]byte{'h', 'i'}) + }, + ) + } +} diff --git a/vendor/github.com/zenazn/goji/web/middleware12_test.go b/vendor/github.com/zenazn/goji/web/middleware12_test.go new file mode 100644 index 0000000..fe5ca9b --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/middleware12_test.go @@ -0,0 +1,52 @@ +// +build !go1.3 + +package web + +import "testing" + +// These tests were pretty sketchtacular to start with, but they aren't even +// guaranteed to pass with Go 1.3's sync.Pool. Let's keep them here for now; if +// they start spuriously failing later we can delete them outright. + +func TestCaching(t *testing.T) { + ch := make(chan string) + st := makeStack(ch) + cs1 := st.alloc() + cs2 := st.alloc() + if cs1 == cs2 { + t.Fatal("cs1 and cs2 are the same") + } + st.release(cs2) + cs3 := st.alloc() + if cs2 != cs3 { + t.Fatalf("Expected cs2 to equal cs3") + } + st.release(cs1) + st.release(cs3) + cs4 := st.alloc() + cs5 := st.alloc() + if cs4 != cs1 { + t.Fatal("Expected cs4 to equal cs1") + } + if cs5 != cs3 { + t.Fatal("Expected cs5 to equal cs3") + } +} + +func TestInvalidation(t *testing.T) { + ch := make(chan string) + st := makeStack(ch) + cs1 := st.alloc() + cs2 := st.alloc() + st.release(cs1) + st.invalidate() + cs3 := st.alloc() + if cs3 == cs1 { + t.Fatal("Expected cs3 to be fresh, instead got cs1") + } + st.release(cs2) + cs4 := st.alloc() + if cs4 == cs2 { + t.Fatal("Expected cs4 to be fresh, instead got cs2") + } +} diff --git a/vendor/github.com/zenazn/goji/web/middleware_test.go b/vendor/github.com/zenazn/goji/web/middleware_test.go new file mode 100644 index 0000000..678ccaa --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/middleware_test.go @@ -0,0 +1,204 @@ +package web + +import ( + "net/http" + "net/http/httptest" + "testing" + "time" +) + +type iRouter func(*C, http.ResponseWriter, *http.Request) + +func (i iRouter) route(c *C, w http.ResponseWriter, r *http.Request) { + i(c, w, r) +} + +func makeStack(ch chan string) *mStack { + router := func(c *C, w http.ResponseWriter, r *http.Request) { + ch <- "router" + } + return &mStack{ + stack: make([]mLayer, 0), + pool: makeCPool(), + router: iRouter(router), + } +} + +func chanWare(ch chan string, s string) func(http.Handler) http.Handler { + return func(h http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + ch <- s + h.ServeHTTP(w, r) + } + return http.HandlerFunc(fn) + } +} + +func simpleRequest(ch chan string, st *mStack) { + defer func() { + ch <- "end" + }() + r, _ := http.NewRequest("GET", "/", nil) + w := httptest.NewRecorder() + cs := st.alloc() + defer st.release(cs) + + cs.ServeHTTP(w, r) +} + +func assertOrder(t *testing.T, ch chan string, strings ...string) { + for i, s := range strings { + var v string + select { + case v = <-ch: + case <-time.After(5 * time.Millisecond): + t.Fatalf("Expected %q as %d'th value, but timed out", s, + i+1) + } + if s != v { + t.Errorf("%d'th value was %q, expected %q", i+1, v, s) + } + } +} + +func TestSimple(t *testing.T) { + t.Parallel() + + ch := make(chan string) + st := makeStack(ch) + st.Use(chanWare(ch, "one")) + st.Use(chanWare(ch, "two")) + go simpleRequest(ch, st) + assertOrder(t, ch, "one", "two", "router", "end") +} + +func TestTypes(t *testing.T) { + t.Parallel() + + ch := make(chan string) + st := makeStack(ch) + st.Use(func(h http.Handler) http.Handler { + return h + }) + st.Use(func(c *C, h http.Handler) http.Handler { + return h + }) +} + +func TestAddMore(t *testing.T) { + t.Parallel() + + ch := make(chan string) + st := makeStack(ch) + st.Use(chanWare(ch, "one")) + go simpleRequest(ch, st) + assertOrder(t, ch, "one", "router", "end") + + st.Use(chanWare(ch, "two")) + go simpleRequest(ch, st) + assertOrder(t, ch, "one", "two", "router", "end") + + st.Use(chanWare(ch, "three")) + st.Use(chanWare(ch, "four")) + go simpleRequest(ch, st) + assertOrder(t, ch, "one", "two", "three", "four", "router", "end") +} + +func TestInsert(t *testing.T) { + t.Parallel() + + ch := make(chan string) + st := makeStack(ch) + one := chanWare(ch, "one") + two := chanWare(ch, "two") + st.Use(one) + st.Use(two) + go simpleRequest(ch, st) + assertOrder(t, ch, "one", "two", "router", "end") + + err := st.Insert(chanWare(ch, "sloth"), chanWare(ch, "squirrel")) + if err == nil { + t.Error("Expected error when referencing unknown middleware") + } + + st.Insert(chanWare(ch, "middle"), two) + err = st.Insert(chanWare(ch, "start"), one) + if err != nil { + t.Fatal(err) + } + go simpleRequest(ch, st) + assertOrder(t, ch, "start", "one", "middle", "two", "router", "end") +} + +func TestAbandon(t *testing.T) { + t.Parallel() + + ch := make(chan string) + st := makeStack(ch) + one := chanWare(ch, "one") + two := chanWare(ch, "two") + three := chanWare(ch, "three") + st.Use(one) + st.Use(two) + st.Use(three) + go simpleRequest(ch, st) + assertOrder(t, ch, "one", "two", "three", "router", "end") + + st.Abandon(two) + go simpleRequest(ch, st) + assertOrder(t, ch, "one", "three", "router", "end") + + err := st.Abandon(chanWare(ch, "panda")) + if err == nil { + t.Error("Expected error when deleting unknown middleware") + } + + st.Abandon(one) + st.Abandon(three) + go simpleRequest(ch, st) + assertOrder(t, ch, "router", "end") + + st.Use(one) + go simpleRequest(ch, st) + assertOrder(t, ch, "one", "router", "end") +} + +func TestContext(t *testing.T) { + router := func(c *C, w http.ResponseWriter, r *http.Request) { + if c.Env["reqID"].(int) != 2 { + t.Error("Request id was not 2 :(") + } + } + st := mStack{ + stack: make([]mLayer, 0), + pool: makeCPool(), + router: iRouter(router), + } + st.Use(func(c *C, h http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + if c.Env != nil || c.URLParams != nil { + t.Error("Expected a clean context") + } + c.Env = make(map[interface{}]interface{}) + c.Env["reqID"] = 1 + + h.ServeHTTP(w, r) + } + return http.HandlerFunc(fn) + }) + + st.Use(func(c *C, h http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + if c.Env == nil { + t.Error("Expected env from last middleware") + } + c.Env["reqID"] = c.Env["reqID"].(int) + 1 + + h.ServeHTTP(w, r) + } + return http.HandlerFunc(fn) + }) + ch := make(chan string) + go simpleRequest(ch, &st) + assertOrder(t, ch, "end") +} 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/mux_test.go b/vendor/github.com/zenazn/goji/web/mux_test.go new file mode 100644 index 0000000..f461452 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/mux_test.go @@ -0,0 +1,45 @@ +package web + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +// Sanity check types +var _ http.Handler = &Mux{} +var _ Handler = &Mux{} + +// There's... really not a lot to do here. + +func TestIfItWorks(t *testing.T) { + t.Parallel() + + m := New() + ch := make(chan string, 1) + + m.Get("/hello/:name", func(c C, w http.ResponseWriter, r *http.Request) { + greeting := "Hello " + if c.Env != nil { + if g, ok := c.Env["greeting"]; ok { + greeting = g.(string) + } + } + ch <- greeting + c.URLParams["name"] + }) + + r, _ := http.NewRequest("GET", "/hello/carl", nil) + m.ServeHTTP(httptest.NewRecorder(), r) + out := <-ch + if out != "Hello carl" { + t.Errorf(`Unexpected response %q, expected "Hello carl"`, out) + } + + r, _ = http.NewRequest("GET", "/hello/bob", nil) + env := map[interface{}]interface{}{"greeting": "Yo "} + m.ServeHTTPC(C{Env: env}, httptest.NewRecorder(), r) + out = <-ch + if out != "Yo bob" { + t.Errorf(`Unexpected response %q, expected "Yo bob"`, out) + } +} 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/pattern_test.go b/vendor/github.com/zenazn/goji/web/pattern_test.go new file mode 100644 index 0000000..8e8af29 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/pattern_test.go @@ -0,0 +1,188 @@ +package web + +import ( + "net/http" + "reflect" + "regexp" + "testing" +) + +func pt(url string, match bool, params map[string]string) patternTest { + req, err := http.NewRequest("GET", url, nil) + if err != nil { + panic(err) + } + + return patternTest{ + r: req, + match: match, + c: &C{}, + cout: &C{URLParams: params}, + } +} + +type patternTest struct { + r *http.Request + match bool + c *C + cout *C +} + +var patternTests = []struct { + pat Pattern + prefix string + tests []patternTest +}{ + // Regexp tests + {parseRegexpPattern(regexp.MustCompile("^/hello$")), + "/hello", []patternTest{ + pt("/hello", true, nil), + pt("/hell", false, nil), + pt("/hello/", false, nil), + pt("/hello/world", false, nil), + pt("/world", false, nil), + }}, + {parseRegexpPattern(regexp.MustCompile("^/hello/(?P[a-z]+)$")), + "/hello/", []patternTest{ + pt("/hello/world", true, map[string]string{ + "name": "world", + }), + pt("/hello/", false, nil), + pt("/hello/my/love", false, nil), + }}, + {parseRegexpPattern(regexp.MustCompile(`^/a(?P\d+)/b(?P\d+)/?$`)), + "/a", []patternTest{ + pt("/a1/b2", true, map[string]string{ + "a": "1", + "b": "2", + }), + pt("/a9001/b007/", true, map[string]string{ + "a": "9001", + "b": "007", + }), + pt("/a/b", false, nil), + pt("/a", false, nil), + pt("/squirrel", false, nil), + }}, + {parseRegexpPattern(regexp.MustCompile(`^/hello/([a-z]+)$`)), + "/hello/", []patternTest{ + pt("/hello/world", true, map[string]string{ + "$1": "world", + }), + pt("/hello/", false, nil), + }}, + {parseRegexpPattern(regexp.MustCompile("/hello")), + "/hello", []patternTest{ + pt("/hello", true, nil), + pt("/hell", false, nil), + pt("/hello/", true, nil), + pt("/hello/world", true, nil), + pt("/world/hello", false, nil), + }}, + + // String pattern tests + {parseStringPattern("/hello"), + "/hello", []patternTest{ + pt("/hello", true, nil), + pt("/hell", false, nil), + pt("/hello/", false, nil), + pt("/hello/world", false, nil), + }}, + {parseStringPattern("/hello/:name"), + "/hello/", []patternTest{ + pt("/hello/world", true, map[string]string{ + "name": "world", + }), + pt("/hello/my.world;wow", true, map[string]string{ + "name": "my.world;wow", + }), + pt("/hell", false, nil), + pt("/hello/", false, nil), + pt("/hello/my/love", false, nil), + }}, + {parseStringPattern("/a/:a/b/:b"), + "/a/", []patternTest{ + pt("/a/1/b/2", true, map[string]string{ + "a": "1", + "b": "2", + }), + pt("/a", false, nil), + pt("/a//b/", false, nil), + pt("/a/1/b/2/3", false, nil), + }}, + {parseStringPattern("/a/:b.:c"), + "/a/", []patternTest{ + pt("/a/cat.gif", true, map[string]string{ + "b": "cat", + "c": "gif", + }), + pt("/a/cat.tar.gz", true, map[string]string{ + "b": "cat", + "c": "tar.gz", + }), + pt("/a", false, nil), + pt("/a/cat", false, nil), + pt("/a/cat/gif", false, nil), + pt("/a/cat.", false, nil), + pt("/a/cat/dog.gif", false, nil), + }}, + + // String prefix tests + {parseStringPattern("/user/:user/*"), + "/user/", []patternTest{ + pt("/user/bob/", true, map[string]string{ + "user": "bob", + "*": "/", + }), + pt("/user/bob/friends/123", true, map[string]string{ + "user": "bob", + "*": "/friends/123", + }), + pt("/user/bob", false, nil), + pt("/user/", false, nil), + pt("/user//", false, nil), + }}, + {parseStringPattern("/user/:user/friends/*"), + "/user/", []patternTest{ + pt("/user/bob/friends/", true, map[string]string{ + "user": "bob", + "*": "/", + }), + pt("/user/bob/friends/123", true, map[string]string{ + "user": "bob", + "*": "/123", + }), + pt("/user/bob/enemies", false, nil), + }}, +} + +func TestPatterns(t *testing.T) { + t.Parallel() + + for _, pt := range patternTests { + p := pt.pat.Prefix() + if p != pt.prefix { + t.Errorf("Expected prefix %q for %v, got %q", pt.prefix, + pt.pat, p) + } else { + for _, test := range pt.tests { + runTest(t, pt.pat, test) + } + } + } +} + +func runTest(t *testing.T, p Pattern, test patternTest) { + result := p.Match(test.r, test.c) + if result != test.match { + t.Errorf("Expected match(%v, %#v) to return %v", p, + test.r.URL.Path, test.match) + return + } + p.Run(test.r, test.c) + + if !reflect.DeepEqual(test.c, test.cout) { + t.Errorf("Expected a context of %v, instead got %v", test.cout, + test.c) + } +} 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/router_middleware_test.go b/vendor/github.com/zenazn/goji/web/router_middleware_test.go new file mode 100644 index 0000000..05dc2fa --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/router_middleware_test.go @@ -0,0 +1,35 @@ +package web + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +func TestRouterMiddleware(t *testing.T) { + t.Parallel() + + m := New() + ch := make(chan string, 1) + m.Get("/a", chHandler(ch, "a")) + m.Get("/b", chHandler(ch, "b")) + m.Use(m.Router) + m.Use(func(c *C, h http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + m := GetMatch(*c) + if rp := m.RawPattern(); rp != "/a" { + t.Fatalf("RawPattern was not /a: %v", rp) + } + r.URL.Path = "/b" + h.ServeHTTP(w, r) + } + return http.HandlerFunc(fn) + }) + + r, _ := http.NewRequest("GET", "/a", nil) + w := httptest.NewRecorder() + m.ServeHTTP(w, r) + if v := <-ch; v != "a" { + t.Errorf("Routing was not frozen! %s", v) + } +} diff --git a/vendor/github.com/zenazn/goji/web/router_test.go b/vendor/github.com/zenazn/goji/web/router_test.go new file mode 100644 index 0000000..1d2c27e --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/router_test.go @@ -0,0 +1,326 @@ +package web + +import ( + "net/http" + "net/http/httptest" + "reflect" + "regexp" + "testing" + "time" +) + +// These tests can probably be DRY'd up a bunch + +func chHandler(ch chan string, s string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ch <- s + }) +} + +var methods = []string{"CONNECT", "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", + "POST", "PUT", "TRACE", "OTHER"} + +func TestMethods(t *testing.T) { + t.Parallel() + m := New() + ch := make(chan string, 1) + + m.Connect("/", chHandler(ch, "CONNECT")) + m.Delete("/", chHandler(ch, "DELETE")) + m.Head("/", chHandler(ch, "HEAD")) + m.Get("/", chHandler(ch, "GET")) + m.Options("/", chHandler(ch, "OPTIONS")) + m.Patch("/", chHandler(ch, "PATCH")) + m.Post("/", chHandler(ch, "POST")) + m.Put("/", chHandler(ch, "PUT")) + m.Trace("/", chHandler(ch, "TRACE")) + m.Handle("/", chHandler(ch, "OTHER")) + + for _, method := range methods { + r, _ := http.NewRequest(method, "/", nil) + w := httptest.NewRecorder() + m.ServeHTTP(w, r) + select { + case val := <-ch: + if val != method { + t.Errorf("Got %q, expected %q", val, method) + } + case <-time.After(5 * time.Millisecond): + t.Errorf("Timeout waiting for method %q", method) + } + } +} + +type testPattern struct{} + +func (t testPattern) Prefix() string { + return "" +} + +func (t testPattern) Match(r *http.Request, c *C) bool { + return true +} +func (t testPattern) Run(r *http.Request, c *C) { +} + +var _ Pattern = testPattern{} + +func TestPatternTypes(t *testing.T) { + t.Parallel() + m := New() + + m.Get("/hello/carl", http.NotFound) + m.Get("/hello/:name", http.NotFound) + m.Get(regexp.MustCompile(`^/hello/(?P.+)$`), http.NotFound) + m.Get(testPattern{}, http.NotFound) +} + +type testHandler chan string + +func (t testHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + t <- "http" +} +func (t testHandler) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) { + t <- "httpc" +} + +var testHandlerTable = map[string]string{ + "/a": "http fn", + "/b": "http handler", + "/c": "web fn", + "/d": "web handler", + "/e": "httpc", +} + +func TestHandlerTypes(t *testing.T) { + t.Parallel() + m := New() + ch := make(chan string, 1) + + m.Get("/a", func(w http.ResponseWriter, r *http.Request) { + ch <- "http fn" + }) + m.Get("/b", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ch <- "http handler" + })) + m.Get("/c", func(c C, w http.ResponseWriter, r *http.Request) { + ch <- "web fn" + }) + m.Get("/d", HandlerFunc(func(c C, w http.ResponseWriter, r *http.Request) { + ch <- "web handler" + })) + m.Get("/e", testHandler(ch)) + + for route, response := range testHandlerTable { + r, _ := http.NewRequest("GET", route, nil) + w := httptest.NewRecorder() + m.ServeHTTP(w, r) + select { + case resp := <-ch: + if resp != response { + t.Errorf("Got %q, expected %q", resp, response) + } + case <-time.After(5 * time.Millisecond): + t.Errorf("Timeout waiting for path %q", route) + } + + } +} + +// The idea behind this test is to comprehensively test if routes are being +// applied in the right order. We define a special pattern type that always +// matches so long as it's greater than or equal to the global test index. By +// incrementing this index, we can invalidate all routes up to some point, and +// therefore test the routing guarantee that Goji provides: for any path P, if +// both A and B match P, and if A was inserted before B, then Goji will route to +// A before it routes to B. +var rsRoutes = []string{ + "/", + "/a", + "/a", + "/b", + "/ab", + "/", + "/ba", + "/b", + "/a", +} + +var rsTests = []struct { + key string + results []int +}{ + {"/", []int{0, 5, 5, 5, 5, 5, -1, -1, -1, -1}}, + {"/a", []int{0, 1, 2, 5, 5, 5, 8, 8, 8, -1}}, + {"/b", []int{0, 3, 3, 3, 5, 5, 7, 7, -1, -1}}, + {"/ab", []int{0, 1, 2, 4, 4, 5, 8, 8, 8, -1}}, + {"/ba", []int{0, 3, 3, 3, 5, 5, 6, 7, -1, -1}}, + {"/c", []int{0, 5, 5, 5, 5, 5, -1, -1, -1, -1}}, + {"nope", []int{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}, +} + +type rsPattern struct { + i int + counter *int + prefix string + ichan chan int +} + +func (rs rsPattern) Prefix() string { + return rs.prefix +} +func (rs rsPattern) Match(_ *http.Request, _ *C) bool { + return rs.i >= *rs.counter +} +func (rs rsPattern) Run(_ *http.Request, _ *C) { +} + +func (rs rsPattern) ServeHTTP(_ http.ResponseWriter, _ *http.Request) { + rs.ichan <- rs.i +} + +var _ Pattern = rsPattern{} +var _ http.Handler = rsPattern{} + +func TestRouteSelection(t *testing.T) { + t.Parallel() + m := New() + counter := 0 + ichan := make(chan int, 1) + m.NotFound(func(w http.ResponseWriter, r *http.Request) { + ichan <- -1 + }) + + for i, s := range rsRoutes { + pat := rsPattern{ + i: i, + counter: &counter, + prefix: s, + ichan: ichan, + } + m.Get(pat, pat) + } + + for _, test := range rsTests { + var n int + for counter, n = range test.results { + r, _ := http.NewRequest("GET", test.key, nil) + w := httptest.NewRecorder() + m.ServeHTTP(w, r) + actual := <-ichan + if n != actual { + t.Errorf("Expected %q @ %d to be %d, got %d", + test.key, counter, n, actual) + } + } + } +} + +func TestNotFound(t *testing.T) { + t.Parallel() + m := New() + + r, _ := http.NewRequest("post", "/", nil) + w := httptest.NewRecorder() + m.ServeHTTP(w, r) + if w.Code != 404 { + t.Errorf("Expected 404, got %d", w.Code) + } + + m.NotFound(func(w http.ResponseWriter, r *http.Request) { + http.Error(w, "I'm a teapot!", http.StatusTeapot) + }) + + r, _ = http.NewRequest("POST", "/", nil) + w = httptest.NewRecorder() + m.ServeHTTP(w, r) + if w.Code != http.StatusTeapot { + t.Errorf("Expected a teapot, got %d", w.Code) + } +} + +func TestPrefix(t *testing.T) { + t.Parallel() + m := New() + ch := make(chan string, 1) + + m.Handle("/hello/*", func(w http.ResponseWriter, r *http.Request) { + ch <- r.URL.Path + }) + + r, _ := http.NewRequest("GET", "/hello/world", nil) + w := httptest.NewRecorder() + m.ServeHTTP(w, r) + select { + case val := <-ch: + if val != "/hello/world" { + t.Errorf("Got %q, expected /hello/world", val) + } + case <-time.After(5 * time.Millisecond): + t.Errorf("Timeout waiting for hello") + } +} + +var validMethodsTable = map[string][]string{ + "/hello/carl": {"DELETE", "GET", "HEAD", "PATCH", "POST", "PUT"}, + "/hello/bob": {"DELETE", "GET", "HEAD", "PATCH", "PUT"}, + "/hola/carl": {"DELETE", "GET", "HEAD", "PUT"}, + "/hola/bob": {"DELETE"}, + "/does/not/compute": {}, +} + +func TestValidMethods(t *testing.T) { + t.Parallel() + m := New() + ch := make(chan []string, 1) + + m.NotFound(func(c C, w http.ResponseWriter, r *http.Request) { + if c.Env == nil { + ch <- []string{} + return + } + methods, ok := c.Env[ValidMethodsKey] + if !ok { + ch <- []string{} + return + } + ch <- methods.([]string) + }) + + m.Get("/hello/carl", http.NotFound) + m.Post("/hello/carl", http.NotFound) + m.Head("/hello/bob", http.NotFound) + m.Get("/hello/:name", http.NotFound) + m.Put("/hello/:name", http.NotFound) + m.Patch("/hello/:name", http.NotFound) + m.Get("/:greet/carl", http.NotFound) + m.Put("/:greet/carl", http.NotFound) + m.Delete("/:greet/:anyone", http.NotFound) + + for path, eMethods := range validMethodsTable { + r, _ := http.NewRequest("BOGUS", path, nil) + m.ServeHTTP(httptest.NewRecorder(), r) + aMethods := <-ch + if !reflect.DeepEqual(eMethods, aMethods) { + t.Errorf("For %q, expected %v, got %v", path, eMethods, + aMethods) + } + } + + // This should also work when c.Env has already been initalized + m.Use(func(c *C, h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + c.Env = make(map[interface{}]interface{}) + h.ServeHTTP(w, r) + }) + }) + for path, eMethods := range validMethodsTable { + r, _ := http.NewRequest("BOGUS", path, nil) + m.ServeHTTP(httptest.NewRecorder(), r) + aMethods := <-ch + if !reflect.DeepEqual(eMethods, aMethods) { + t.Errorf("For %q, expected %v, got %v", path, eMethods, + aMethods) + } + } +} 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{}