aboutsummaryrefslogtreecommitdiff
path: root/README.md
diff options
context:
space:
mode:
authorFatih Arslan <ftharsln@gmail.com>2014-02-18 09:03:13 +0000
committerFatih Arslan <ftharsln@gmail.com>2014-02-18 09:03:13 +0000
commitbb4d8abda781e933f5fe3357874780b981d31a23 (patch)
treebabed68efb0aa09090116dac2dac9077900f68d5 /README.md
parentabda2caec38fae5f90a66c37462b6f60e291eca0 (diff)
downloadcolour-bb4d8abda781e933f5fe3357874780b981d31a23.tar.gz
colour-bb4d8abda781e933f5fe3357874780b981d31a23.tar.bz2
Add travis
Diffstat (limited to 'README.md')
-rw-r--r--README.md2
1 files changed, 1 insertions, 1 deletions
diff --git a/README.md b/README.md
index 65cd7e8..e1274a7 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Color [![GoDoc](https://godoc.org/github.com/fatih/color?status.png)](http://godoc.org/github.com/fatih/color)
+# Color [![GoDoc](https://godoc.org/github.com/fatih/color?status.png)](http://godoc.org/github.com/fatih/color) [![Build Status](https://travis-ci.org/fatih/color.png)](https://travis-ci.org/fatih/color)
Color let you use colorized outputs in terms of [ASCI Escape
Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors). The API can be
d class='add'>vendor/github.com/zenazn/goji/bind/systemd.go36
-rw-r--r--vendor/github.com/zenazn/goji/bind/systemd_stub.go6
-rw-r--r--vendor/github.com/zenazn/goji/default.go102
-rw-r--r--vendor/github.com/zenazn/goji/example/main.go177
-rw-r--r--vendor/github.com/zenazn/goji/example/middleware.go47
-rw-r--r--vendor/github.com/zenazn/goji/example/models.go49
-rw-r--r--vendor/github.com/zenazn/goji/goji.go36
-rw-r--r--vendor/github.com/zenazn/goji/graceful/clone.go11
-rw-r--r--vendor/github.com/zenazn/goji/graceful/clone16.go34
-rw-r--r--vendor/github.com/zenazn/goji/graceful/einhorn.go21
-rw-r--r--vendor/github.com/zenazn/goji/graceful/graceful.go62
-rw-r--r--vendor/github.com/zenazn/goji/graceful/listener/conn.go151
-rw-r--r--vendor/github.com/zenazn/goji/graceful/listener/listener.go178
-rw-r--r--vendor/github.com/zenazn/goji/graceful/listener/shard.go98
-rw-r--r--vendor/github.com/zenazn/goji/graceful/middleware.go103
-rw-r--r--vendor/github.com/zenazn/goji/graceful/serve.go33
-rw-r--r--vendor/github.com/zenazn/goji/graceful/serve13.go76
-rw-r--r--vendor/github.com/zenazn/goji/graceful/server.go108
-rw-r--r--vendor/github.com/zenazn/goji/graceful/signal.go197
-rw-r--r--vendor/github.com/zenazn/goji/serve.go64
-rw-r--r--vendor/github.com/zenazn/goji/serve_appengine.go23
-rw-r--r--vendor/github.com/zenazn/goji/web/atomic.go18
-rw-r--r--vendor/github.com/zenazn/goji/web/atomic_appengine.go14
-rw-r--r--vendor/github.com/zenazn/goji/web/bytecode_compiler.go265
-rw-r--r--vendor/github.com/zenazn/goji/web/bytecode_runner.go83
-rw-r--r--vendor/github.com/zenazn/goji/web/chanpool.go31
-rw-r--r--vendor/github.com/zenazn/goji/web/cpool.go23
-rw-r--r--vendor/github.com/zenazn/goji/web/func_equal.go32
-rw-r--r--vendor/github.com/zenazn/goji/web/handler.go42
-rw-r--r--vendor/github.com/zenazn/goji/web/match.go66
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware.go154
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/envinit.go27
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/logger.go92
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/middleware.go4
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/nocache.go55
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/options.go97
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/realip.go51
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/recoverer.go44
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/request_id.go88
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/subrouter.go65
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/terminal.go60
-rw-r--r--vendor/github.com/zenazn/goji/web/middleware/urlquery.go24
-rw-r--r--vendor/github.com/zenazn/goji/web/mutil/mutil.go3
-rw-r--r--vendor/github.com/zenazn/goji/web/mutil/writer_proxy.go139
-rw-r--r--vendor/github.com/zenazn/goji/web/mux.go213
-rw-r--r--vendor/github.com/zenazn/goji/web/pattern.go58
-rw-r--r--vendor/github.com/zenazn/goji/web/regexp_pattern.go149
-rw-r--r--vendor/github.com/zenazn/goji/web/router.go154
-rw-r--r--vendor/github.com/zenazn/goji/web/string_pattern.go137
-rw-r--r--vendor/github.com/zenazn/goji/web/web.go112
54 files changed, 4190 insertions, 0 deletions
diff --git a/vendor/github.com/zenazn/goji/LICENSE b/vendor/github.com/zenazn/goji/LICENSE
new file mode 100644
index 0000000..446aba0
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2014, 2015, 2016 Carl Jackson (carl@avtok.com)
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/zenazn/goji/bind/bind.go b/vendor/github.com/zenazn/goji/bind/bind.go
new file mode 100644
index 0000000..43d11dd
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/bind/bind.go
@@ -0,0 +1,155 @@
+/*
+Package bind provides a convenient way to bind to sockets. It exposes a flag in
+the default flag set named "bind" which provides syntax to bind TCP and UNIX
+sockets. It also supports binding to arbitrary file descriptors passed by a
+parent (for instance, systemd), and for binding to Einhorn sockets (including
+Einhorn ACK support).
+
+If the value passed to bind contains a colon, as in ":8000" or "127.0.0.1:9001",
+it will be treated as a TCP address. If it begins with a "/" or a ".", it will
+be treated as a path to a UNIX socket. If it begins with the string "fd@", as in
+"fd@3", it will be treated as a file descriptor (useful for use with systemd,
+for instance). If it begins with the string "einhorn@", as in "einhorn@0", the
+corresponding einhorn socket will be used.
+
+If an option is not explicitly passed, the implementation will automatically
+select between using "einhorn@0", "fd@3", and ":8000", depending on whether
+Einhorn or systemd (or neither) is detected.
+
+This package is a teensy bit magical, and goes out of its way to Do The Right
+Thing in many situations, including in both development and production. If
+you're looking for something less magical, you'd probably be better off just
+calling net.Listen() the old-fashioned way.
+*/
+package bind
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "net"
+ "os"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+var bind string
+
+func init() {
+ einhornInit()
+ systemdInit()
+}
+
+// DefaultBind specifies the fallback used for WithFlag() if it is
+// unable to discover an environment hint (after checking $GOJI_BIND,
+// Einhorn, systemd, and $PORT).
+//
+// If DefaultBind is overridden, it must be set before calling
+// WithFlag().
+var DefaultBind = ":8000"
+
+// WithFlag adds a standard flag to the global flag instance that
+// allows configuration of the default socket. Users who call
+// Default() must call this function before flags are parsed, for
+// example in an init() block.
+//
+// When selecting the default bind string, this function will examine
+// its environment for hints about what port to bind to, selecting the
+// GOJI_BIND environment variable, Einhorn, systemd, the PORT
+// environment variable, and the value of DefaultBind, in order. In
+// most cases, this means that the default behavior of the default
+// socket will be reasonable for use in your circumstance.
+func WithFlag() {
+ s := Sniff()
+ if s == "" {
+ s = DefaultBind
+ }
+ flag.StringVar(&bind, "bind", s,
+ `Address to bind on. If this value has a colon, as in ":8000" or
+ "127.0.0.1:9001", it will be treated as a TCP address. If it
+ begins with a "/" or a ".", it will be treated as a path to a
+ UNIX socket. If it begins with the string "fd@", as in "fd@3",
+ it will be treated as a file descriptor (useful for use with
+ systemd, for instance). If it begins with the string "einhorn@",
+ as in "einhorn@0", the corresponding einhorn socket will be
+ used. If an option is not explicitly passed, the implementation
+ will automatically select among "einhorn@0" (Einhorn), "fd@3"
+ (systemd), and ":8000" (fallback) based on its environment.`)
+}
+
+// Sniff attempts to select a sensible default bind string by examining its
+// environment. It examines the GOJI_BIND environment variable, Einhorn,
+// systemd, and the PORT environment variable, in that order, selecting the
+// first plausible option. It returns the empty string if no sensible default
+// could be extracted from the environment.
+func Sniff() string {
+ if bind := os.Getenv("GOJI_BIND"); bind != "" {
+ return bind
+ } else if usingEinhorn() {
+ return "einhorn@0"
+ } else if usingSystemd() {
+ return "fd@3"
+ } else if port := os.Getenv("PORT"); port != "" {
+ return ":" + port
+ }
+ return ""
+}
+
+func listenTo(bind string) (net.Listener, error) {
+ if strings.Contains(bind, ":") {
+ return net.Listen("tcp", bind)
+ } else if strings.HasPrefix(bind, ".") || strings.HasPrefix(bind, "/") {
+ return net.Listen("unix", bind)
+ } else if strings.HasPrefix(bind, "fd@") {
+ fd, err := strconv.Atoi(bind[3:])
+ if err != nil {
+ return nil, fmt.Errorf("error while parsing fd %v: %v",
+ bind, err)
+ }
+ f := os.NewFile(uintptr(fd), bind)
+ defer f.Close()
+ return net.FileListener(f)
+ } else if strings.HasPrefix(bind, "einhorn@") {
+ fd, err := strconv.Atoi(bind[8:])
+ if err != nil {
+ return nil, fmt.Errorf(
+ "error while parsing einhorn %v: %v", bind, err)
+ }
+ return einhornBind(fd)
+ }
+
+ return nil, fmt.Errorf("error while parsing bind arg %v", bind)
+}
+
+// Socket parses and binds to the specified address. If Socket encounters an
+// error while parsing or binding to the given socket it will exit by calling
+// log.Fatal.
+func Socket(bind string) net.Listener {
+ l, err := listenTo(bind)
+ if err != nil {
+ log.Fatal(err)
+ }
+ return l
+}
+
+// Default parses and binds to the default socket as given to us by the flag
+// module. If there was an error parsing or binding to that socket, Default will
+// exit by calling `log.Fatal`.
+func Default() net.Listener {
+ return Socket(bind)
+}
+
+// I'm not sure why you'd ever want to call Ready() more than once, but we may
+// as well be safe against it...
+var ready sync.Once
+
+// Ready notifies the environment (for now, just Einhorn) that the process is
+// ready to receive traffic. Should be called at the last possible moment to
+// maximize the chances that a faulty process exits before signaling that it's
+// ready.
+func Ready() {
+ ready.Do(func() {
+ einhornAck()
+ })
+}
diff --git a/vendor/github.com/zenazn/goji/bind/einhorn.go b/vendor/github.com/zenazn/goji/bind/einhorn.go
new file mode 100644
index 0000000..e695c0e
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/bind/einhorn.go
@@ -0,0 +1,91 @@
+// +build !windows
+
+package bind
+
+import (
+ "fmt"
+ "log"
+ "net"
+ "os"
+ "strconv"
+ "syscall"
+)
+
+const tooBigErr = "bind: einhorn@%d not found (einhorn only passed %d fds)"
+const bindErr = "bind: could not bind einhorn@%d: not running under einhorn"
+const einhornErr = "bind: einhorn environment initialization error"
+const ackErr = "bind: error ACKing to einhorn: %v"
+
+var einhornNumFds int
+
+func envInt(val string) (int, error) {
+ return strconv.Atoi(os.Getenv(val))
+}
+
+// Unfortunately this can't be a normal init function, because their execution
+// order is undefined, and we need to run before the init() in bind.go.
+func einhornInit() {
+ mpid, err := envInt("EINHORN_MASTER_PID")
+ if err != nil || mpid != os.Getppid() {
+ return
+ }
+
+ einhornNumFds, err = envInt("EINHORN_FD_COUNT")
+ if err != nil {
+ einhornNumFds = 0
+ return
+ }
+
+ // Prevent einhorn's fds from leaking to our children
+ for i := 0; i < einhornNumFds; i++ {
+ syscall.CloseOnExec(einhornFdMap(i))
+ }
+}
+
+func usingEinhorn() bool {
+ return einhornNumFds > 0
+}
+
+func einhornFdMap(n int) int {
+ name := fmt.Sprintf("EINHORN_FD_%d", n)
+ fno, err := envInt(name)
+ if err != nil {
+ log.Fatal(einhornErr)
+ }
+ return fno
+}
+
+func einhornBind(n int) (net.Listener, error) {
+ if !usingEinhorn() {
+ return nil, fmt.Errorf(bindErr, n)
+ }
+ if n >= einhornNumFds || n < 0 {
+ return nil, fmt.Errorf(tooBigErr, n, einhornNumFds)
+ }
+
+ fno := einhornFdMap(n)
+ f := os.NewFile(uintptr(fno), fmt.Sprintf("einhorn@%d", n))
+ defer f.Close()
+ return net.FileListener(f)
+}
+
+// Fun story: this is actually YAML, not JSON.
+const ackMsg = `{"command": "worker:ack", "pid": %d}` + "\n"
+
+func einhornAck() {
+ if !usingEinhorn() {
+ return
+ }
+ log.Print("bind: ACKing to einhorn")
+
+ ctl, err := net.Dial("unix", os.Getenv("EINHORN_SOCK_PATH"))
+ if err != nil {
+ log.Fatalf(ackErr, err)
+ }
+ defer ctl.Close()
+
+ _, err = fmt.Fprintf(ctl, ackMsg, os.Getpid())
+ if err != nil {
+ log.Fatalf(ackErr, err)
+ }
+}
diff --git a/vendor/github.com/zenazn/goji/bind/einhorn_stub.go b/vendor/github.com/zenazn/goji/bind/einhorn_stub.go
new file mode 100644
index 0000000..093707f
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/bind/einhorn_stub.go
@@ -0,0 +1,12 @@
+// +build windows
+
+package bind
+
+import (
+ "net"
+)
+
+func einhornInit() {}
+func einhornAck() {}
+func einhornBind(fd int) (net.Listener, error) { return nil, nil }
+func usingEinhorn() bool { return false }
diff --git a/vendor/github.com/zenazn/goji/bind/systemd.go b/vendor/github.com/zenazn/goji/bind/systemd.go
new file mode 100644
index 0000000..e7cd8e4
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/bind/systemd.go
@@ -0,0 +1,36 @@
+// +build !windows
+
+package bind
+
+import (
+ "os"
+ "syscall"
+)
+
+const systemdMinFd = 3
+
+var systemdNumFds int
+
+// Unfortunately this can't be a normal init function, because their execution
+// order is undefined, and we need to run before the init() in bind.go.
+func systemdInit() {
+ pid, err := envInt("LISTEN_PID")
+ if err != nil || pid != os.Getpid() {
+ return
+ }
+
+ systemdNumFds, err = envInt("LISTEN_FDS")
+ if err != nil {
+ systemdNumFds = 0
+ return
+ }
+
+ // Prevent fds from leaking to our children
+ for i := 0; i < systemdNumFds; i++ {
+ syscall.CloseOnExec(systemdMinFd + i)
+ }
+}
+
+func usingSystemd() bool {
+ return systemdNumFds > 0
+}
diff --git a/vendor/github.com/zenazn/goji/bind/systemd_stub.go b/vendor/github.com/zenazn/goji/bind/systemd_stub.go
new file mode 100644
index 0000000..4ad4d20
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/bind/systemd_stub.go
@@ -0,0 +1,6 @@
+// +build windows
+
+package bind
+
+func systemdInit() {}
+func usingSystemd() bool { return false }
diff --git a/vendor/github.com/zenazn/goji/default.go b/vendor/github.com/zenazn/goji/default.go
new file mode 100644
index 0000000..540e792
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/default.go
@@ -0,0 +1,102 @@
+package goji
+
+import (
+ "github.com/zenazn/goji/web"
+ "github.com/zenazn/goji/web/middleware"
+)
+
+// The default web.Mux.
+var DefaultMux *web.Mux
+
+func init() {
+ DefaultMux = web.New()
+
+ DefaultMux.Use(middleware.RequestID)
+ DefaultMux.Use(middleware.Logger)
+ DefaultMux.Use(middleware.Recoverer)
+ DefaultMux.Use(middleware.AutomaticOptions)
+}
+
+// Use appends the given middleware to the default Mux's middleware stack. See
+// the documentation for web.Mux.Use for more information.
+func Use(middleware web.MiddlewareType) {
+ DefaultMux.Use(middleware)
+}
+
+// Insert the given middleware into the default Mux's middleware stack. See the
+// documentation for web.Mux.Insert for more information.
+func Insert(middleware, before web.MiddlewareType) error {
+ return DefaultMux.Insert(middleware, before)
+}
+
+// Abandon removes the given middleware from the default Mux's middleware stack.
+// See the documentation for web.Mux.Abandon for more information.
+func Abandon(middleware web.MiddlewareType) error {
+ return DefaultMux.Abandon(middleware)
+}
+
+// Handle adds a route to the default Mux. See the documentation for web.Mux for
+// more information about what types this function accepts.
+func Handle(pattern web.PatternType, handler web.HandlerType) {
+ DefaultMux.Handle(pattern, handler)
+}
+
+// Connect adds a CONNECT route to the default Mux. See the documentation for
+// web.Mux for more information about what types this function accepts.
+func Connect(pattern web.PatternType, handler web.HandlerType) {
+ DefaultMux.Connect(pattern, handler)
+}
+
+// Delete adds a DELETE route to the default Mux. See the documentation for
+// web.Mux for more information about what types this function accepts.
+func Delete(pattern web.PatternType, handler web.HandlerType) {
+ DefaultMux.Delete(pattern, handler)
+}
+
+// Get adds a GET route to the default Mux. See the documentation for web.Mux for
+// more information about what types this function accepts.
+func Get(pattern web.PatternType, handler web.HandlerType) {
+ DefaultMux.Get(pattern, handler)
+}
+
+// Head adds a HEAD route to the default Mux. See the documentation for web.Mux
+// for more information about what types this function accepts.
+func Head(pattern web.PatternType, handler web.HandlerType) {
+ DefaultMux.Head(pattern, handler)
+}
+
+// Options adds a OPTIONS route to the default Mux. See the documentation for
+// web.Mux for more information about what types this function accepts.
+func Options(pattern web.PatternType, handler web.HandlerType) {
+ DefaultMux.Options(pattern, handler)
+}
+
+// Patch adds a PATCH route to the default Mux. See the documentation for web.Mux
+// for more information about what types this function accepts.
+func Patch(pattern web.PatternType, handler web.HandlerType) {
+ DefaultMux.Patch(pattern, handler)
+}
+
+// Post adds a POST route to the default Mux. See the documentation for web.Mux
+// for more information about what types this function accepts.
+func Post(pattern web.PatternType, handler web.HandlerType) {
+ DefaultMux.Post(pattern, handler)
+}
+
+// Put adds a PUT route to the default Mux. See the documentation for web.Mux for
+// more information about what types this function accepts.
+func Put(pattern web.PatternType, handler web.HandlerType) {
+ DefaultMux.Put(pattern, handler)
+}
+
+// Trace adds a TRACE route to the default Mux. See the documentation for
+// web.Mux for more information about what types this function accepts.
+func Trace(pattern web.PatternType, handler web.HandlerType) {
+ DefaultMux.Trace(pattern, handler)
+}
+
+// NotFound sets the NotFound handler for the default Mux. See the documentation
+// for web.Mux.NotFound for more information.
+func NotFound(handler web.HandlerType) {
+ DefaultMux.NotFound(handler)
+}
diff --git a/vendor/github.com/zenazn/goji/example/main.go b/vendor/github.com/zenazn/goji/example/main.go
new file mode 100644
index 0000000..b2bcf6f
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/example/main.go
@@ -0,0 +1,177 @@
+// Command example is a sample application built with Goji. Its goal is to give
+// you a taste for what Goji looks like in the real world by artificially using
+// all of its features.
+//
+// In particular, this is a complete working site for gritter.com, a site where
+// users can post 140-character "greets". Any resemblance to real websites,
+// alive or dead, is purely coincidental.
+package main
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "regexp"
+ "strconv"
+ "time"
+
+ "github.com/goji/param"
+ "github.com/zenazn/goji"
+ "github.com/zenazn/goji/web"
+ "github.com/zenazn/goji/web/middleware"
+)
+
+// Note: the code below cuts a lot of corners to make the example app simple.
+
+func main() {
+ // Add routes to the global handler
+ goji.Get("/", Root)
+ // Fully backwards compatible with net/http's Handlers
+ goji.Get("/greets", http.RedirectHandler("/", 301))
+ // Use your favorite HTTP verbs
+ goji.Post("/greets", NewGreet)
+ // Use Sinatra-style patterns in your URLs
+ goji.Get("/users/:name", GetUser)
+ // Goji also supports regular expressions with named capture groups.
+ goji.Get(regexp.MustCompile(`^/greets/(?P<id>\d+)$`), GetGreet)
+
+ // Middleware can be used to inject behavior into your app. The
+ // middleware for this application are defined in middleware.go, but you
+ // can put them wherever you like.
+ goji.Use(PlainText)
+
+ // If the patterns ends with "/*", the path is treated as a prefix, and
+ // can be used to implement sub-routes.
+ admin := web.New()
+ goji.Handle("/admin/*", admin)
+
+ // The standard SubRouter middleware helps make writing sub-routers
+ // easy. Ordinarily, Goji does not manipulate the request's URL.Path,
+ // meaning you'd have to repeat "/admin/" in each of the following
+ // routes. This middleware allows you to cut down on the repetition by
+ // eliminating the shared, already-matched prefix.
+ admin.Use(middleware.SubRouter)
+ // You can also easily attach extra middleware to sub-routers that are
+ // not present on the parent router. This one, for instance, presents a
+ // password prompt to users of the admin endpoints.
+ admin.Use(SuperSecure)
+
+ admin.Get("/", AdminRoot)
+ admin.Get("/finances", AdminFinances)
+
+ // Goji's routing, like Sinatra's, is exact: no effort is made to
+ // normalize trailing slashes.
+ goji.Get("/admin", http.RedirectHandler("/admin/", 301))
+
+ // Use a custom 404 handler
+ goji.NotFound(NotFound)
+
+ // Sometimes requests take a long time.
+ goji.Get("/waitforit", WaitForIt)
+
+ // Call Serve() at the bottom of your main() function, and it'll take
+ // care of everything else for you, including binding to a socket (with
+ // automatic support for systemd and Einhorn) and supporting graceful
+ // shutdown on SIGINT. Serve() is appropriate for both development and
+ // production.
+ goji.Serve()
+}
+
+// Root route (GET "/"). Print a list of greets.
+func Root(w http.ResponseWriter, r *http.Request) {
+ // In the real world you'd probably use a template or something.
+ io.WriteString(w, "Gritter\n======\n\n")
+ for i := len(Greets) - 1; i >= 0; i-- {
+ Greets[i].Write(w)
+ }
+}
+
+// NewGreet creates a new greet (POST "/greets"). Creates a greet and redirects
+// you to the created greet.
+//
+// To post a new greet, try this at a shell:
+// $ now=$(date +'%Y-%m-%dT%H:%M:%SZ')
+// $ curl -i -d "user=carl&message=Hello+World&time=$now" localhost:8000/greets
+func NewGreet(w http.ResponseWriter, r *http.Request) {
+ var greet Greet
+
+ // Parse the POST body into the Greet struct. The format is the same as
+ // is emitted by (e.g.) jQuery.param.
+ r.ParseForm()
+ err := param.Parse(r.Form, &greet)
+
+ if err != nil || len(greet.Message) > 140 {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ // We make no effort to prevent races against other insertions.
+ Greets = append(Greets, greet)
+ url := fmt.Sprintf("/greets/%d", len(Greets)-1)
+ http.Redirect(w, r, url, http.StatusCreated)
+}
+
+// GetUser finds a given user and her greets (GET "/user/:name")
+func GetUser(c web.C, w http.ResponseWriter, r *http.Request) {
+ io.WriteString(w, "Gritter\n======\n\n")
+ handle := c.URLParams["name"]
+ user, ok := Users[handle]
+ if !ok {
+ http.Error(w, http.StatusText(404), 404)
+ return
+ }
+
+ user.Write(w, handle)
+
+ io.WriteString(w, "\nGreets:\n")
+ for i := len(Greets) - 1; i >= 0; i-- {
+ if Greets[i].User == handle {
+ Greets[i].Write(w)
+ }
+ }
+}
+
+// GetGreet finds a particular greet by ID (GET "/greets/\d+"). Does no bounds
+// checking, so will probably panic.
+func GetGreet(c web.C, w http.ResponseWriter, r *http.Request) {
+ id, err := strconv.Atoi(c.URLParams["id"])
+ if err != nil {
+ http.Error(w, http.StatusText(404), 404)
+ return
+ }
+ // This will panic if id is too big. Try it out!
+ greet := Greets[id]
+
+ io.WriteString(w, "Gritter\n======\n\n")
+ greet.Write(w)
+}
+
+// WaitForIt is a particularly slow handler (GET "/waitforit"). Try loading this
+// endpoint and initiating a graceful shutdown (Ctrl-C) or Einhorn reload. The
+// old server will stop accepting new connections and will attempt to kill
+// outstanding idle (keep-alive) connections, but will patiently stick around
+// for this endpoint to finish. How kind of it!
+func WaitForIt(w http.ResponseWriter, r *http.Request) {
+ io.WriteString(w, "This is going to be legend... (wait for it)\n")
+ if fl, ok := w.(http.Flusher); ok {
+ fl.Flush()
+ }
+ time.Sleep(15 * time.Second)
+ io.WriteString(w, "...dary! Legendary!\n")
+}
+
+// AdminRoot is root (GET "/admin/root"). Much secret. Very administrate. Wow.
+func AdminRoot(w http.ResponseWriter, r *http.Request) {
+ io.WriteString(w, "Gritter\n======\n\nSuper secret admin page!\n")
+}
+
+// AdminFinances would answer the question 'How are we doing?'
+// (GET "/admin/finances")
+func AdminFinances(w http.ResponseWriter, r *http.Request) {
+ io.WriteString(w, "Gritter\n======\n\nWe're broke! :(\n")
+}
+
+// NotFound is a 404 handler.
+func NotFound(w http.ResponseWriter, r *http.Request) {
+ http.Error(w, "Umm... have you tried turning it off and on again?", 404)
+}
diff --git a/vendor/github.com/zenazn/goji/example/middleware.go b/vendor/github.com/zenazn/goji/example/middleware.go
new file mode 100644
index 0000000..9652ebb
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/example/middleware.go
@@ -0,0 +1,47 @@
+package main
+
+import (
+ "encoding/base64"
+ "net/http"
+ "strings"
+
+ "github.com/zenazn/goji/web"
+)
+
+// PlainText sets the content-type of responses to text/plain.
+func PlainText(h http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/plain")
+ h.ServeHTTP(w, r)
+ }
+ return http.HandlerFunc(fn)
+}
+
+// Nobody will ever guess this!
+const Password = "admin:admin"
+
+// SuperSecure is HTTP Basic Auth middleware for super-secret admin page. Shhhh!
+func SuperSecure(c *web.C, h http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ auth := r.Header.Get("Authorization")
+ if !strings.HasPrefix(auth, "Basic ") {
+ pleaseAuth(w)
+ return
+ }
+
+ password, err := base64.StdEncoding.DecodeString(auth[6:])
+ if err != nil || string(password) != Password {
+ pleaseAuth(w)
+ return
+ }
+
+ h.ServeHTTP(w, r)
+ }
+ return http.HandlerFunc(fn)
+}
+
+func pleaseAuth(w http.ResponseWriter) {
+ w.Header().Set("WWW-Authenticate", `Basic realm="Gritter"`)
+ w.WriteHeader(http.StatusUnauthorized)
+ w.Write([]byte("Go away!\n"))
+}
diff --git a/vendor/github.com/zenazn/goji/example/models.go b/vendor/github.com/zenazn/goji/example/models.go
new file mode 100644
index 0000000..4c34c08
--- /dev/null
+++ b/vendor/github.com/zenazn/goji/example/models.go
@@ -0,0 +1,49 @@
+package main
+
+import (
+ "fmt"
+ "io"
+ "time"
+)
+
+// A Greet is a 140-character micro-blogpost that has no resemblance whatsoever
+// to the noise a bird makes.
+type Greet struct {
+ User string `param:"user"`
+ Message string `param:"message"`
+ Time time.Time `param:"time"`
+}
+
+// Store all our greets in a big list in memory, because, let's be honest, who's
+// actually going to use a service that only allows you to post 140-character
+// messages?
+var Greets = []Greet{
+ {"carl", "Welcome to Gritter!", time.Now()},
+ {"alice", "Wanna know a secret?", time.Now()},
+ {"bob", "Okay!", time.Now()},
+ {"eve", "I'm listening...", time.Now()},
+}
+
+// Write out a representation of the greet
+func (g Greet) Write(w io.Writer) {