blob: 90959aee27245add19827eeff4d1f887d0be5a06 [file] [log] [blame]
Serge Bazanski3fd70d82018-10-14 08:12:46 -07001package mirko
2
3import (
Serge Bazanskiaa81aa22018-10-14 08:36:05 -07004 "context"
Serge Bazanski3fd70d82018-10-14 08:12:46 -07005 "flag"
6 "fmt"
7 "net"
8 "net/http"
9 "time"
10
11 "code.hackerspace.pl/q3k/hspki"
12 "github.com/golang/glog"
13 "github.com/q3k/statusz"
14 "golang.org/x/net/trace"
15 "google.golang.org/grpc"
16 "google.golang.org/grpc/reflection"
17)
18
19var (
20 flagListenAddress string
21 flagDebugAddress string
Serge Bazanski446c9e12018-10-14 17:06:09 +010022 flagDebugAllowAll bool
Serge Bazanski3fd70d82018-10-14 08:12:46 -070023)
24
25func init() {
Serge Bazanski69de9cb2018-10-14 08:49:04 -070026 flag.StringVar(&flagListenAddress, "listen_address", "127.0.0.1:4200", "gRPC listen address")
27 flag.StringVar(&flagDebugAddress, "debug_address", "127.0.0.1:4201", "HTTP debug/status listen address")
Serge Bazanski446c9e12018-10-14 17:06:09 +010028 flag.StringVar(&flagDebugAllowAll, "debug_allow_all", false, "HTTP debug/status available to everyone")
Serge Bazanskiaa81aa22018-10-14 08:36:05 -070029 flag.Set("logtostderr", "true")
Serge Bazanski3fd70d82018-10-14 08:12:46 -070030}
31
32type Mirko struct {
33 grpcListen net.Listener
34 grpcServer *grpc.Server
35 httpListen net.Listener
36 httpServer *http.Server
37 httpMux *http.ServeMux
38}
39
40func New() *Mirko {
41 return &Mirko{}
42}
43
Serge Bazanski446c9e12018-10-14 17:06:09 +010044func authRequest(req *http.Request) (any, sensitive bool) {
45 host, _, err := net.SplitHostPort(req.RemoteAddr)
46 if err != nil {
47 host = req.RemoteAddr
48 }
49
50 if flagDebugAllowAll {
51 return true, true
52 }
53
54 switch host {
55 case "localhost", "127.0.0.1", "::1":
56 return true, true
57 default:
58 return false, false
59 }
60}
61
Serge Bazanski3fd70d82018-10-14 08:12:46 -070062func (m *Mirko) Listen() error {
63 grpc.EnableTracing = true
Serge Bazanski446c9e12018-10-14 17:06:09 +010064 trace.AuthRequest = authRequest
65
Serge Bazanski3fd70d82018-10-14 08:12:46 -070066 grpcLis, err := net.Listen("tcp", flagListenAddress)
67 if err != nil {
68 return fmt.Errorf("net.Listen: %v", err)
69 }
70 m.grpcListen = grpcLis
71 m.grpcServer = grpc.NewServer(hspki.WithServerHSPKI()...)
72 reflection.Register(m.grpcServer)
73
74 httpLis, err := net.Listen("tcp", flagDebugAddress)
75 if err != nil {
76 return fmt.Errorf("net.Listen: %v", err)
77 }
78
79 m.httpMux = http.NewServeMux()
80 // Canonical URLs
Serge Bazanski446c9e12018-10-14 17:06:09 +010081 m.httpMux.HandleFunc("/debug/status", func(w http.ResponseWriter, r *http.Request) {
82 any, sensitive := authRequest(r)
83 if !any {
84 http.Error(w, "not allowed", http.StatusUnauthorized)
85 return
86 }
87 statusz.StatusHandler(w, r)
88 })
Serge Bazanski3fd70d82018-10-14 08:12:46 -070089 m.httpMux.HandleFunc("/debug/requests", trace.Traces)
90
91 // -z legacy URLs
92 m.httpMux.HandleFunc("/statusz", func(w http.ResponseWriter, r *http.Request) {
93 http.Redirect(w, r, "/debug/status", http.StatusSeeOther)
94 })
95 m.httpMux.HandleFunc("/rpcz", func(w http.ResponseWriter, r *http.Request) {
96 http.Redirect(w, r, "/debug/requests", http.StatusSeeOther)
97 })
98 m.httpMux.HandleFunc("/requestz", func(w http.ResponseWriter, r *http.Request) {
99 http.Redirect(w, r, "/debug/requests", http.StatusSeeOther)
100 })
101
102 // root redirect
103 m.httpMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
104 http.Redirect(w, r, "/debug/status", http.StatusSeeOther)
105 })
106
107 m.httpListen = httpLis
108 m.httpServer = &http.Server{
109 Addr: flagDebugAddress,
110 Handler: m.httpMux,
111 }
112
113 return nil
114}
115
Serge Bazanskiaa81aa22018-10-14 08:36:05 -0700116func (m *Mirko) Trace(ctx context.Context, f string, args ...interface{}) {
117 tr, ok := trace.FromContext(ctx)
118 if !ok {
119 fmtd := fmt.Sprintf(f, args...)
120 glog.Warningf("No trace in %v: %s", ctx, fmtd)
121 return
122 }
123 tr.LazyPrintf(f, args...)
124}
125
Serge Bazanski3fd70d82018-10-14 08:12:46 -0700126func (m *Mirko) GRPC() *grpc.Server {
127 if m.grpcServer == nil {
128 panic("GRPC() called before Listen()")
129 }
130 return m.grpcServer
131}
132
133func (m *Mirko) HTTPMux() *http.ServeMux {
134 if m.httpMux == nil {
135 panic("HTTPMux() called before Listen()")
136 }
137 return m.httpMux
138}
139
140func (m *Mirko) Serve() error {
141 errs := make(chan error, 1)
142 go func() {
143 if err := m.grpcServer.Serve(m.grpcListen); err != nil {
144 errs <- err
145 }
146 }()
147 go func() {
148 if err := m.httpServer.Serve(m.httpListen); err != nil {
149 errs <- err
150 }
151 }()
152
153 ticker := time.NewTicker(1 * time.Second)
154 select {
155 case <-ticker.C:
156 glog.Infof("gRPC listening on %s", flagListenAddress)
157 glog.Infof("HTTP listening on %s", flagDebugAddress)
158 return nil
159 case err := <-errs:
160 return err
161 }
162}