blob: 07d7bcdc145232002cc1d17a0980174f0864207a [file] [log] [blame]
Serge Bazanskide0330a2020-07-30 20:48:48 +02001package main
2
3import (
4 "context"
5 "flag"
6 "fmt"
7 "html/template"
8 "net"
9 "net/http"
10 "math/rand"
11 "strings"
12 "time"
13
14 "github.com/golang/glog"
15
16 mirko "code.hackerspace.pl/hscloud/go/mirko"
17 "code.hackerspace.pl/hscloud/hswaw/laserproxy/tpl"
18)
19
20var (
21 flagLaserAddress = "10.11.0.10"
22 flagLaserNetworkAddress = "10.11.0.1"
23 flagClientNetworkAddress = "10.8.1.2"
24 flagWebAddress = "127.0.0.1:8080"
25
26 tplIndex = template.Must(template.New("index.html").Parse(tpl.MustAssetString("index.html")))
27)
28
29type service struct {
30 lockCtrl chan *lockCtrl
31}
32
33func main() {
34 flag.StringVar(&flagLaserAddress, "laser_address", flagLaserAddress, "Address of Ruida controller on laser network")
35 flag.StringVar(&flagLaserNetworkAddress, "laser_network", flagLaserNetworkAddress, "Address of proxy on laser network")
36 flag.StringVar(&flagClientNetworkAddress, "client_network", flagClientNetworkAddress, "Address of proxy on client network")
37 flag.StringVar(&flagWebAddress, "web_address", flagWebAddress, "Address and port to listen on for public web connections")
38
39 flag.Parse()
40 m := mirko.New()
41 if err := m.Listen(); err != nil {
42 glog.Exitf("Could not listen: %v", err)
43 }
44
45 lisLaser, err := net.ListenPacket("udp", fmt.Sprintf("%s:40200", flagLaserNetworkAddress))
46 if err != nil {
47 glog.Fatalf("could not listen on laser network: %v", err)
48 }
49 defer lisLaser.Close()
50
51 lisClient, err := net.ListenPacket("udp", fmt.Sprintf("%s:50200", flagClientNetworkAddress))
52 if err != nil {
53 glog.Fatalf("could not listen on client network: %v", err)
54 }
55 defer lisClient.Close()
56
57 laserIP := net.ParseIP(flagLaserAddress)
58 if laserIP == nil {
59 glog.Fatalf("could not parse laser IP address %q", flagLaserAddress)
60 }
61 laserAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:50200", laserIP.String()))
62 if err != nil {
63 glog.Fatalf("could not make laser UDP address: %v", err)
64 }
65
66 ctx := m.Context()
67 s := &service{
68 lockCtrl: make(chan *lockCtrl),
69 }
70 updates := make(chan *lockUpdate)
71 go s.runProxy(ctx, updates, laserAddr, lisLaser, lisClient)
72 go s.runLocker(ctx, s.lockCtrl)
73 s.lockCtrl <- &lockCtrl{
74 subscribe: &lockCtrlSubscribe{
75 subscriber: updates,
76 },
77 }
78
79 mux := http.NewServeMux()
80 mux.HandleFunc("/", s.handlerIndex)
81 mux.HandleFunc("/take", s.handlerTake)
82 mux.HandleFunc("/release", s.handlerRelease)
83 mux.HandleFunc("/force", s.handlerForce)
84 httpSrv := &http.Server{Addr: flagWebAddress, Handler: mux}
85
86 glog.Infof("Listening for web connections on %q...", flagWebAddress)
87 go func() {
88 if err := httpSrv.ListenAndServe(); err != nil {
89 glog.Error(err)
90 }
91 }()
92
93 if err := m.Serve(); err != nil {
94 glog.Exitf("Could not run: %v", err)
95 }
96
97 glog.Info("Running.")
98 <-m.Done()
99 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
100 defer cancel()
101 httpSrv.Shutdown(ctx)
102}
103
104var (
105 sampleNames = []string{
106 "enleth", "radex", "qdot", "hans acker", "makłowicz",
107 }
108)
109
110func remoteAddr(r *http.Request) string {
111 if fwd := r.Header.Get("X-Forwarded-For"); fwd != "" {
112 return strings.Split(fwd, ":")[0]
113 }
114 return strings.Split(r.RemoteAddr, ":")[0]
115}
116
117func (s *service) handlerIndex(w http.ResponseWriter, r *http.Request) {
118 res := make(chan *lockResCurrent)
119 s.lockCtrl <- &lockCtrl{
120 getCurrent: &lockCtrlGetCurrent{
121 res: res,
122 },
123 }
124 cur := <-res
125
126 err := tplIndex.Execute(w, struct {
127 You bool
128 CurrentAddress string
129 CurrentNote string
130 CurrentDeadline string
131 SampleName string
132 }{
133 You: cur.addr == remoteAddr(r),
134 CurrentAddress: cur.addr,
135 CurrentNote: cur.note,
136 CurrentDeadline: fmt.Sprintf("%d minute(s)", int(cur.deadline.Sub(time.Now()).Minutes())),
137 SampleName: sampleNames[rand.Intn(len(sampleNames))],
138 })
139 if err != nil {
140 glog.Errorf("rendering template: %v", err)
141 }
142}
143
144func (s *service) handlerTake(w http.ResponseWriter, r *http.Request) {
145 if r.Method != "POST" {
146 return
147 }
148 r.ParseForm()
149 who := r.Form.Get("who")
150 prev := r.Form.Get("prev")
151 if who == "" {
152 fmt.Fprintf(w, "sorry, who are you? please specify a name")
153 return
154 }
155
156 res := make(chan bool)
157 take := &lockCtrlTake{
158 note: who,
159 addr: remoteAddr(r),
160 prev: prev,
161 res: res,
162 }
163 s.lockCtrl <- &lockCtrl{
164 take: take,
165 }
166 won := <-res
167
168 if won {
169 http.Redirect(w, r, "/", 302)
170 } else {
171 fmt.Fprintf(w, "lock not taken")
172 }
173}
174
175func (s *service) handlerRelease(w http.ResponseWriter, r *http.Request) {
176 if r.Method != "POST" {
177 return
178 }
179 res := make(chan struct{})
180 s.lockCtrl <- &lockCtrl{
181 release: &lockCtrlRelease{
182 addr: remoteAddr(r),
183 res: res,
184 },
185 }
186 <-res
187
188 http.Redirect(w, r, "/", 302)
189}
190
191func (s *service) handlerForce(w http.ResponseWriter, r *http.Request) {
192 if r.Method != "POST" {
193 return
194 }
195 res := make(chan struct{})
196 s.lockCtrl <- &lockCtrl{
197 release: &lockCtrlRelease{
198 force: true,
199 res: res,
200 },
201 }
202 <-res
203
204 http.Redirect(w, r, "/", 302)
205}