aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/zenazn/goji/bind
diff options
context:
space:
mode:
authorFelix Hanley <felix@userspace.com.au>2016-12-05 08:16:58 +0000
committerFelix Hanley <felix@userspace.com.au>2016-12-05 08:16:58 +0000
commitb049991a46a2f619344bd6e915745703864d0134 (patch)
treeec1d3897a7b69c7c63a3774d4c42dfbb8cb46432 /vendor/github.com/zenazn/goji/bind
parente1c3d6f7db06d592538f1162b2b6b9f1b6efa330 (diff)
downloadgo-dict2rest-b049991a46a2f619344bd6e915745703864d0134.tar.gz
go-dict2rest-b049991a46a2f619344bd6e915745703864d0134.tar.bz2
Clean up source structure and update vendor versionsHEADmaster
Diffstat (limited to 'vendor/github.com/zenazn/goji/bind')
-rw-r--r--vendor/github.com/zenazn/goji/bind/bind.go155
-rw-r--r--vendor/github.com/zenazn/goji/bind/einhorn.go91
-rw-r--r--vendor/github.com/zenazn/goji/bind/einhorn_stub.go12
-rw-r--r--vendor/github.com/zenazn/goji/bind/systemd.go36
-rw-r--r--vendor/github.com/zenazn/goji/bind/systemd_stub.go6
5 files changed, 300 insertions, 0 deletions
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 }