pax_global_header00006660000000000000000000000064134746317470014532gustar00rootroot0000000000000052 comment=a16712d37ba72246f71f9c8012974d46f8e61d16 goji-1.0.1/000077500000000000000000000000001347463174700124615ustar00rootroot00000000000000goji-1.0.1/.travis.yml000066400000000000000000000004711347463174700145740ustar00rootroot00000000000000language: go go: - "1.5.x" - "1.6.x" - "1.7.x" - "1.8.x" - "1.9.x" - "1.10.x" - "1.11.x" 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 ./... goji-1.0.1/LICENSE000066400000000000000000000021121347463174700134620ustar00rootroot00000000000000Copyright (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. goji-1.0.1/README.md000066400000000000000000000155031347463174700137440ustar00rootroot00000000000000Goji ==== [![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. goji-1.0.1/bind/000077500000000000000000000000001347463174700133755ustar00rootroot00000000000000goji-1.0.1/bind/bind.go000066400000000000000000000124361347463174700146460ustar00rootroot00000000000000/* Package bind provides a convenient way to bind to sockets. It exposes a flag in the default flag set named "bind" which provides syntax to bind TCP and UNIX sockets. It also supports binding to arbitrary file descriptors passed by a parent (for instance, systemd), and for binding to Einhorn sockets (including Einhorn ACK support). If the value passed to bind contains a colon, as in ":8000" or "127.0.0.1:9001", it will be treated as a TCP address. If it begins with a "/" or a ".", it will be treated as a path to a UNIX socket. If it begins with the string "fd@", as in "fd@3", it will be treated as a file descriptor (useful for use with systemd, for instance). If it begins with the string "einhorn@", as in "einhorn@0", the corresponding einhorn socket will be used. If an option is not explicitly passed, the implementation will automatically select between using "einhorn@0", "fd@3", and ":8000", depending on whether Einhorn or systemd (or neither) is detected. This package is a teensy bit magical, and goes out of its way to Do The Right Thing in many situations, including in both development and production. If you're looking for something less magical, you'd probably be better off just calling net.Listen() the old-fashioned way. */ package bind import ( "flag" "fmt" "log" "net" "os" "strconv" "strings" "sync" ) var bind string func init() { einhornInit() systemdInit() } // DefaultBind specifies the fallback used for WithFlag() if it is // unable to discover an environment hint (after checking $GOJI_BIND, // Einhorn, systemd, and $PORT). // // If DefaultBind is overridden, it must be set before calling // WithFlag(). var DefaultBind = ":8000" // WithFlag adds a standard flag to the global flag instance that // allows configuration of the default socket. Users who call // Default() must call this function before flags are parsed, for // example in an init() block. // // When selecting the default bind string, this function will examine // its environment for hints about what port to bind to, selecting the // GOJI_BIND environment variable, Einhorn, systemd, the PORT // environment variable, and the value of DefaultBind, in order. In // most cases, this means that the default behavior of the default // socket will be reasonable for use in your circumstance. func WithFlag() { s := Sniff() if s == "" { s = DefaultBind } flag.StringVar(&bind, "bind", s, `Address to bind on. If this value has a colon, as in ":8000" or "127.0.0.1:9001", it will be treated as a TCP address. If it begins with a "/" or a ".", it will be treated as a path to a UNIX socket. If it begins with the string "fd@", as in "fd@3", it will be treated as a file descriptor (useful for use with systemd, for instance). If it begins with the string "einhorn@", as in "einhorn@0", the corresponding einhorn socket will be used. If an option is not explicitly passed, the implementation will automatically select among "einhorn@0" (Einhorn), "fd@3" (systemd), and ":8000" (fallback) based on its environment.`) } // Sniff attempts to select a sensible default bind string by examining its // environment. It examines the GOJI_BIND environment variable, Einhorn, // systemd, and the PORT environment variable, in that order, selecting the // first plausible option. It returns the empty string if no sensible default // could be extracted from the environment. func Sniff() string { if bind := os.Getenv("GOJI_BIND"); bind != "" { return bind } else if usingEinhorn() { return "einhorn@0" } else if usingSystemd() { return "fd@3" } else if port := os.Getenv("PORT"); port != "" { return ":" + port } return "" } func listenTo(bind string) (net.Listener, error) { if strings.Contains(bind, ":") { return net.Listen("tcp", bind) } else if strings.HasPrefix(bind, ".") || strings.HasPrefix(bind, "/") { return net.Listen("unix", bind) } else if strings.HasPrefix(bind, "fd@") { fd, err := strconv.Atoi(bind[3:]) if err != nil { return nil, fmt.Errorf("error while parsing fd %v: %v", bind, err) } f := os.NewFile(uintptr(fd), bind) defer f.Close() return net.FileListener(f) } else if strings.HasPrefix(bind, "einhorn@") { fd, err := strconv.Atoi(bind[8:]) if err != nil { return nil, fmt.Errorf( "error while parsing einhorn %v: %v", bind, err) } return einhornBind(fd) } return nil, fmt.Errorf("error while parsing bind arg %v", bind) } // Socket parses and binds to the specified address. If Socket encounters an // error while parsing or binding to the given socket it will exit by calling // log.Fatal. func Socket(bind string) net.Listener { l, err := listenTo(bind) if err != nil { log.Fatal(err) } return l } // Default parses and binds to the default socket as given to us by the flag // module. If there was an error parsing or binding to that socket, Default will // exit by calling `log.Fatal`. func Default() net.Listener { return Socket(bind) } // I'm not sure why you'd ever want to call Ready() more than once, but we may // as well be safe against it... var ready sync.Once // Ready notifies the environment (for now, just Einhorn) that the process is // ready to receive traffic. Should be called at the last possible moment to // maximize the chances that a faulty process exits before signaling that it's // ready. func Ready() { ready.Do(func() { einhornAck() }) } goji-1.0.1/bind/einhorn.go000066400000000000000000000036361347463174700153760ustar00rootroot00000000000000// +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) } } goji-1.0.1/bind/einhorn_stub.go000066400000000000000000000004331347463174700164230ustar00rootroot00000000000000// +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 } goji-1.0.1/bind/systemd.go000066400000000000000000000012051347463174700154120ustar00rootroot00000000000000// +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 } goji-1.0.1/bind/systemd_stub.go000066400000000000000000000001471347463174700164530ustar00rootroot00000000000000// +build windows package bind func systemdInit() {} func usingSystemd() bool { return false } goji-1.0.1/default.go000066400000000000000000000072041347463174700144370ustar00rootroot00000000000000package 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) } goji-1.0.1/example/000077500000000000000000000000001347463174700141145ustar00rootroot00000000000000goji-1.0.1/example/.gitignore000066400000000000000000000000101347463174700160730ustar00rootroot00000000000000example goji-1.0.1/example/README.md000066400000000000000000000006441347463174700153770ustar00rootroot00000000000000Gritter ======= 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`. goji-1.0.1/example/main.go000066400000000000000000000134631347463174700153760ustar00rootroot00000000000000// 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) } goji-1.0.1/example/middleware.go000066400000000000000000000021241347463174700165570ustar00rootroot00000000000000package 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")) } goji-1.0.1/example/models.go000066400000000000000000000024761347463174700157370ustar00rootroot00000000000000package 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) } goji-1.0.1/goji.go000066400000000000000000000017521347463174700137450ustar00rootroot00000000000000/* 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 goji-1.0.1/graceful/000077500000000000000000000000001347463174700142515ustar00rootroot00000000000000goji-1.0.1/graceful/clone.go000066400000000000000000000002261347463174700157000ustar00rootroot00000000000000// +build !go1.6 package graceful import "crypto/tls" // see clone16.go func cloneTLSConfig(cfg *tls.Config) *tls.Config { c := *cfg return &c } goji-1.0.1/graceful/clone16.go000066400000000000000000000022701347463174700160500ustar00rootroot00000000000000// +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, } } goji-1.0.1/graceful/einhorn.go000066400000000000000000000007621347463174700162470ustar00rootroot00000000000000// +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) } goji-1.0.1/graceful/graceful.go000066400000000000000000000036271347463174700164000ustar00rootroot00000000000000/* 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" "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 { switch oe.Op { // backward compatibility: older golang returns AcceptEx on Windows. // Current golang returns "accept" consistently. It's platform independent. // See https://github.com/golang/go/commit/b0f4ee533a875c258ac1030ee382f0ffe2de304b case "AcceptEx": fallthrough case "accept": if oe.Err.Error() == errClosing { return nil } } } return err } goji-1.0.1/graceful/listener/000077500000000000000000000000001347463174700160765ustar00rootroot00000000000000goji-1.0.1/graceful/listener/conn.go000066400000000000000000000052201347463174700173610ustar00rootroot00000000000000package 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 } goji-1.0.1/graceful/listener/conn_test.go000066400000000000000000000111051347463174700204170ustar00rootroot00000000000000package 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) } } goji-1.0.1/graceful/listener/fake_test.go000066400000000000000000000037711347463174700204020ustar00rootroot00000000000000package 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{}{} } goji-1.0.1/graceful/listener/listener.go000066400000000000000000000120441347463174700202530ustar00rootroot00000000000000/* 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 } goji-1.0.1/graceful/listener/listener_test.go000066400000000000000000000067211347463174700213170ustar00rootroot00000000000000package 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?") } } goji-1.0.1/graceful/listener/race_test.go000066400000000000000000000041561347463174700204040ustar00rootroot00000000000000package 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) } } goji-1.0.1/graceful/listener/shard.go000066400000000000000000000036261347463174700175350ustar00rootroot00000000000000package 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() } goji-1.0.1/graceful/middleware.go000066400000000000000000000042701347463174700167200ustar00rootroot00000000000000// +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{} goji-1.0.1/graceful/middleware_test.go000066400000000000000000000027601347463174700177610ustar00rootroot00000000000000// +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) } goji-1.0.1/graceful/serve.go000066400000000000000000000014451347463174700157300ustar00rootroot00000000000000// +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) } goji-1.0.1/graceful/serve13.go000066400000000000000000000045251347463174700160760ustar00rootroot00000000000000// +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) } goji-1.0.1/graceful/server.go000066400000000000000000000062401347463174700161100ustar00rootroot00000000000000package 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) } goji-1.0.1/graceful/signal.go000066400000000000000000000131131347463174700160540ustar00rootroot00000000000000package 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(os.Signal), 0) var posthooks = make([]func(os.Signal), 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) } // PreHookWithSignal registers a function to be called before any of this // package's normal shutdown actions, which recieves the signal that caused the // shutdown (or nil for manual shutdowns). All listeners will be called in the // order they were added, from a single goroutine. func PreHookWithSignal(f func(os.Signal)) { mu.Lock() defer mu.Unlock() prehooks = append(prehooks, f) } // 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()) { PreHookWithSignal(func(_ os.Signal) { f() }) } // PostHookWithSignal registers a function to be called after all of this // package's normal shutdown actions, which receives the signal that caused the // shutdown (or nil for manual shutdowns). 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 PostHookWithSignal(f func(os.Signal)) { mu.Lock() defer mu.Unlock() posthooks = append(posthooks, 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()) { PostHookWithSignal(func(_ os.Signal) { f() }) } // Shutdown manually triggers a shutdown from your application. Like Wait, // blocks until all connections have gracefully shut down. func Shutdown() { shutdown(false, nil) } // 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, nil) } // 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 { sig := <-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, sig) }() } mu.Unlock() go shutdown(force, sig) last = now } } var preOnce, closeOnce, forceOnce, postOnce, notifyOnce sync.Once func shutdown(force bool, sig os.Signal) { preOnce.Do(func() { mu.Lock() defer mu.Unlock() for _, f := range prehooks { f(sig) } }) 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(sig) } }) 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) } } goji-1.0.1/serve.go000066400000000000000000000024451347463174700141410ustar00rootroot00000000000000// +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() } goji-1.0.1/serve_appengine.go000066400000000000000000000007261347463174700161670ustar00rootroot00000000000000// +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) } goji-1.0.1/web/000077500000000000000000000000001347463174700132365ustar00rootroot00000000000000goji-1.0.1/web/atomic.go000066400000000000000000000006011347463174700150360ustar00rootroot00000000000000// +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)) } goji-1.0.1/web/atomic_appengine.go000066400000000000000000000004051347463174700170660ustar00rootroot00000000000000// +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 } goji-1.0.1/web/bench_test.go000066400000000000000000000063341347463174700157110ustar00rootroot00000000000000// +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) } goji-1.0.1/web/bytecode_compiler.go000066400000000000000000000167051347463174700172660ustar00rootroot00000000000000package 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 } goji-1.0.1/web/bytecode_runner.go000066400000000000000000000025051347463174700167560ustar00rootroot00000000000000package 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++ } } } goji-1.0.1/web/chanpool.go000066400000000000000000000006731347463174700153760ustar00rootroot00000000000000// +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: } } goji-1.0.1/web/cpool.go000066400000000000000000000004511347463174700147010ustar00rootroot00000000000000// +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) } goji-1.0.1/web/example_test.go000066400000000000000000000037251347463174700162660ustar00rootroot00000000000000package 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) } goji-1.0.1/web/func_equal.go000066400000000000000000000022241347463174700157070ustar00rootroot00000000000000package 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() } goji-1.0.1/web/func_equal_test.go000066400000000000000000000030111347463174700167410ustar00rootroot00000000000000package 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(%p, %p) 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") } } goji-1.0.1/web/handler.go000066400000000000000000000021401347463174700151770ustar00rootroot00000000000000package 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") } } goji-1.0.1/web/match.go000066400000000000000000000037221347463174700146650ustar00rootroot00000000000000package 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 } } goji-1.0.1/web/match_test.go000066400000000000000000000017541347463174700157270ustar00rootroot00000000000000package 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) } } } goji-1.0.1/web/middleware.go000066400000000000000000000064371347463174700157140ustar00rootroot00000000000000package 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 } p := cs.pool cs.pool = nil p.release(cs) } 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 } goji-1.0.1/web/middleware/000077500000000000000000000000001347463174700153535ustar00rootroot00000000000000goji-1.0.1/web/middleware/envinit.go000066400000000000000000000012231347463174700173540ustar00rootroot00000000000000package 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} } goji-1.0.1/web/middleware/logger.go000066400000000000000000000044231347463174700171640ustar00rootroot00000000000000package 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()) } goji-1.0.1/web/middleware/middleware.go000066400000000000000000000001421347463174700200140ustar00rootroot00000000000000/* Package middleware provides several standard middleware implementations. */ package middleware goji-1.0.1/web/middleware/nocache.go000066400000000000000000000024151347463174700173040ustar00rootroot00000000000000package 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) } goji-1.0.1/web/middleware/nocache_test.go000066400000000000000000000006601347463174700203430ustar00rootroot00000000000000package 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) } } } goji-1.0.1/web/middleware/options.go000066400000000000000000000037561347463174700174100ustar00rootroot00000000000000package 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) } goji-1.0.1/web/middleware/options_test.go000066400000000000000000000055121347463174700204370ustar00rootroot00000000000000package 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) } } goji-1.0.1/web/middleware/realip.go000066400000000000000000000030331347463174700171550ustar00rootroot00000000000000package 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 } goji-1.0.1/web/middleware/recoverer.go000066400000000000000000000015461347463174700177040ustar00rootroot00000000000000package 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()) } goji-1.0.1/web/middleware/request_id.go000066400000000000000000000044751347463174700200600ustar00rootroot00000000000000package 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 "" } goji-1.0.1/web/middleware/subrouter.go000066400000000000000000000032351347463174700177370ustar00rootroot00000000000000package 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} } goji-1.0.1/web/middleware/subrouter_test.go000066400000000000000000000007731347463174700210020ustar00rootroot00000000000000package 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) } goji-1.0.1/web/middleware/terminal.go000066400000000000000000000033351347463174700175210ustar00rootroot00000000000000package 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) } } goji-1.0.1/web/middleware/urlquery.go000066400000000000000000000010631347463174700175720ustar00rootroot00000000000000package 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) } goji-1.0.1/web/middleware/urlquery_test.go000066400000000000000000000024551347463174700206370ustar00rootroot00000000000000package 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'}) }, ) } } goji-1.0.1/web/middleware12_test.go000066400000000000000000000021161347463174700171040ustar00rootroot00000000000000// +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") } } goji-1.0.1/web/middleware_test.go000066400000000000000000000111501347463174700167370ustar00rootroot00000000000000package web import ( "net/http" "net/http/httptest" "sync" "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") } func TestMStackRace(t *testing.T) { m := &mStack{pool: makeCPool()} var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go func() { for j := 0; j < 1000; j++ { m.release(m.alloc()) } wg.Done() }() } wg.Wait() } goji-1.0.1/web/mutil/000077500000000000000000000000001347463174700143705ustar00rootroot00000000000000goji-1.0.1/web/mutil/mutil.go000066400000000000000000000001541347463174700160510ustar00rootroot00000000000000// Package mutil contains various functions that are helpful when writing http // middleware. package mutil goji-1.0.1/web/mutil/writer_proxy.go000066400000000000000000000072151347463174700175010ustar00rootroot00000000000000// +build !go1.8 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) } type flushWriter struct { basicWriter } func (f *flushWriter) Flush() { fl := f.basicWriter.ResponseWriter.(http.Flusher) fl.Flush() } var ( _ http.CloseNotifier = &fancyWriter{} _ http.Flusher = &fancyWriter{} _ http.Hijacker = &fancyWriter{} _ io.ReaderFrom = &fancyWriter{} _ http.Flusher = &flushWriter{} ) goji-1.0.1/web/mutil/writer_proxy_go1_8.go000066400000000000000000000065751347463174700205060ustar00rootroot00000000000000// +build go1.8 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 { _, fl := w.(http.Flusher) _, hj := w.(http.Hijacker) _, rf := w.(io.ReaderFrom) bw := basicWriter{ResponseWriter: w} if fl && hj && rf { return &fancyWriter{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.Pusher, // 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) Push(target string, opts *http.PushOptions) error { return f.basicWriter.ResponseWriter.(http.Pusher).Push(target, opts) } 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.Pusher = &fancyWriter{} _ http.Flusher = &fancyWriter{} _ http.Hijacker = &fancyWriter{} _ io.ReaderFrom = &fancyWriter{} ) goji-1.0.1/web/mux.go000066400000000000000000000164731347463174700144110ustar00rootroot00000000000000package 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() } goji-1.0.1/web/mux_test.go000066400000000000000000000017151347463174700154410ustar00rootroot00000000000000package 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) } } goji-1.0.1/web/pattern.go000066400000000000000000000042661347463174700152520ustar00rootroot00000000000000package 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") } } goji-1.0.1/web/pattern_test.go000066400000000000000000000103361347463174700163040ustar00rootroot00000000000000package 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) } } goji-1.0.1/web/regexp_pattern.go000066400000000000000000000073001347463174700166140ustar00rootroot00000000000000package 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, } } goji-1.0.1/web/router.go000066400000000000000000000060101347463174700151020ustar00rootroot00000000000000package 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 } goji-1.0.1/web/router_middleware_test.go000066400000000000000000000013111347463174700203350ustar00rootroot00000000000000package 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) } } goji-1.0.1/web/router_test.go000066400000000000000000000172061347463174700161520ustar00rootroot00000000000000package 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) } } } goji-1.0.1/web/string_pattern.go000066400000000000000000000055251347463174700166370ustar00rootroot00000000000000package 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, } } goji-1.0.1/web/web.go000066400000000000000000000113321347463174700143420ustar00rootroot00000000000000/* 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{}