diff options
Diffstat (limited to 'vendor/github.com/zenazn/goji/bind/bind.go')
| -rw-r--r-- | vendor/github.com/zenazn/goji/bind/bind.go | 155 |
1 files changed, 155 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() + }) +} |
