forked from perkeep/perkeep
-
Notifications
You must be signed in to change notification settings - Fork 0
/
listen.go
135 lines (119 loc) · 2.85 KB
/
listen.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package listen
import (
"errors"
"flag"
"fmt"
"net"
"os"
"strconv"
"strings"
)
// NewFlag returns a flag that implements the flag.Value interface.
func NewFlag(flagName, defaultValue string, serverType string) *Addr {
addr := &Addr{
s: defaultValue,
}
flag.Var(addr, flagName, Usage(serverType))
return addr
}
// Listen is a replacement for net.Listen that also respects runsit
// listeners: port, :port, ip:port, FD:<fd_num>, ADDR:<name> or <name>
// named ports.
// Listeners are always TCP.
func Listen(addr string) (net.Listener, error) {
a := &Addr{s: addr}
return a.Listen()
}
// Usage returns a descriptive usage message for a flag given the name
// of thing being addressed.
func Usage(name string) string {
if name == "" {
name = "Listen address"
}
if !strings.HasSuffix(name, " address") {
name += " address"
}
return name + "; may be port, :port, ip:port, FD:<fd_num>, or ADDR:<name> to use named runsit ports"
}
// Addr is a flag variable. Use like:
//
// var webPort listen.Addr
// flag.Var(&webPort, "web_addr", listen.Usage("Web server address"))
// flag.Parse()
// webListener, err := webPort.Listen()
type Addr struct {
s string
ln net.Listener
err error
}
func (a *Addr) String() string {
return a.s
}
// Set implements the flag.Value interface.
func (a *Addr) Set(v string) error {
a.s = v
// Try the requested port by runsit port name first.
fd, ok, err := namedPort(v)
if err != nil {
return err
}
if ok {
return a.listenOnFD(fd)
}
if strings.HasPrefix(v, "FD:") {
fdStr := v[len("FD:"):]
fd, err := strconv.ParseUint(fdStr, 10, 32)
if err != nil {
return fmt.Errorf("invalid file descriptor %q: %v", fdStr, err)
}
return a.listenOnFD(uintptr(fd))
}
ipPort := v
if isPort(v) {
ipPort = ":" + v
}
_, _, err = net.SplitHostPort(ipPort)
if err != nil {
return fmt.Errorf("invalid PORT or IP:PORT %q: %v", v, err)
}
a.ln, err = net.Listen("tcp", ipPort)
return err
}
func isPort(s string) bool {
_, err := strconv.ParseUint(s, 10, 16)
return err == nil
}
func (a *Addr) listenOnFD(fd uintptr) (err error) {
f := os.NewFile(fd, fmt.Sprintf("fd #%d from runsit parent", fd))
a.ln, err = net.FileListener(f)
return
}
func namedPort(name string) (fd uintptr, ok bool, err error) {
s := os.Getenv("RUNSIT_PORTFD_" + name)
if s == "" {
return
}
u64, err := strconv.ParseUint(s, 10, 32)
if err != nil {
return
}
return uintptr(u64), true, nil
}
var _ flag.Value = (*Addr)(nil)
// Listen returns the address's TCP listener.
func (a *Addr) Listen() (net.Listener, error) {
// Start the listener now, if there's a default
// and nothing's called Set yet.
if a.err == nil && a.ln == nil && a.s != "" {
if err := a.Set(a.s); err != nil {
return nil, err
}
}
if a.err != nil {
return nil, a.err
}
if a.ln != nil {
return a.ln, nil
}
return nil, errors.New("listen: no error or listener")
}