laserproxy: init
Change-Id: I1900be6eea2d702548279176d796c58c34952dc8
diff --git a/hswaw/laserproxy/main.go b/hswaw/laserproxy/main.go
new file mode 100644
index 0000000..07d7bcd
--- /dev/null
+++ b/hswaw/laserproxy/main.go
@@ -0,0 +1,205 @@
+package main
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "html/template"
+ "net"
+ "net/http"
+ "math/rand"
+ "strings"
+ "time"
+
+ "github.com/golang/glog"
+
+ mirko "code.hackerspace.pl/hscloud/go/mirko"
+ "code.hackerspace.pl/hscloud/hswaw/laserproxy/tpl"
+)
+
+var (
+ flagLaserAddress = "10.11.0.10"
+ flagLaserNetworkAddress = "10.11.0.1"
+ flagClientNetworkAddress = "10.8.1.2"
+ flagWebAddress = "127.0.0.1:8080"
+
+ tplIndex = template.Must(template.New("index.html").Parse(tpl.MustAssetString("index.html")))
+)
+
+type service struct {
+ lockCtrl chan *lockCtrl
+}
+
+func main() {
+ flag.StringVar(&flagLaserAddress, "laser_address", flagLaserAddress, "Address of Ruida controller on laser network")
+ flag.StringVar(&flagLaserNetworkAddress, "laser_network", flagLaserNetworkAddress, "Address of proxy on laser network")
+ flag.StringVar(&flagClientNetworkAddress, "client_network", flagClientNetworkAddress, "Address of proxy on client network")
+ flag.StringVar(&flagWebAddress, "web_address", flagWebAddress, "Address and port to listen on for public web connections")
+
+ flag.Parse()
+ m := mirko.New()
+ if err := m.Listen(); err != nil {
+ glog.Exitf("Could not listen: %v", err)
+ }
+
+ lisLaser, err := net.ListenPacket("udp", fmt.Sprintf("%s:40200", flagLaserNetworkAddress))
+ if err != nil {
+ glog.Fatalf("could not listen on laser network: %v", err)
+ }
+ defer lisLaser.Close()
+
+ lisClient, err := net.ListenPacket("udp", fmt.Sprintf("%s:50200", flagClientNetworkAddress))
+ if err != nil {
+ glog.Fatalf("could not listen on client network: %v", err)
+ }
+ defer lisClient.Close()
+
+ laserIP := net.ParseIP(flagLaserAddress)
+ if laserIP == nil {
+ glog.Fatalf("could not parse laser IP address %q", flagLaserAddress)
+ }
+ laserAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:50200", laserIP.String()))
+ if err != nil {
+ glog.Fatalf("could not make laser UDP address: %v", err)
+ }
+
+ ctx := m.Context()
+ s := &service{
+ lockCtrl: make(chan *lockCtrl),
+ }
+ updates := make(chan *lockUpdate)
+ go s.runProxy(ctx, updates, laserAddr, lisLaser, lisClient)
+ go s.runLocker(ctx, s.lockCtrl)
+ s.lockCtrl <- &lockCtrl{
+ subscribe: &lockCtrlSubscribe{
+ subscriber: updates,
+ },
+ }
+
+ mux := http.NewServeMux()
+ mux.HandleFunc("/", s.handlerIndex)
+ mux.HandleFunc("/take", s.handlerTake)
+ mux.HandleFunc("/release", s.handlerRelease)
+ mux.HandleFunc("/force", s.handlerForce)
+ httpSrv := &http.Server{Addr: flagWebAddress, Handler: mux}
+
+ glog.Infof("Listening for web connections on %q...", flagWebAddress)
+ go func() {
+ if err := httpSrv.ListenAndServe(); err != nil {
+ glog.Error(err)
+ }
+ }()
+
+ if err := m.Serve(); err != nil {
+ glog.Exitf("Could not run: %v", err)
+ }
+
+ glog.Info("Running.")
+ <-m.Done()
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ httpSrv.Shutdown(ctx)
+}
+
+var (
+ sampleNames = []string{
+ "enleth", "radex", "qdot", "hans acker", "makłowicz",
+ }
+)
+
+func remoteAddr(r *http.Request) string {
+ if fwd := r.Header.Get("X-Forwarded-For"); fwd != "" {
+ return strings.Split(fwd, ":")[0]
+ }
+ return strings.Split(r.RemoteAddr, ":")[0]
+}
+
+func (s *service) handlerIndex(w http.ResponseWriter, r *http.Request) {
+ res := make(chan *lockResCurrent)
+ s.lockCtrl <- &lockCtrl{
+ getCurrent: &lockCtrlGetCurrent{
+ res: res,
+ },
+ }
+ cur := <-res
+
+ err := tplIndex.Execute(w, struct {
+ You bool
+ CurrentAddress string
+ CurrentNote string
+ CurrentDeadline string
+ SampleName string
+ }{
+ You: cur.addr == remoteAddr(r),
+ CurrentAddress: cur.addr,
+ CurrentNote: cur.note,
+ CurrentDeadline: fmt.Sprintf("%d minute(s)", int(cur.deadline.Sub(time.Now()).Minutes())),
+ SampleName: sampleNames[rand.Intn(len(sampleNames))],
+ })
+ if err != nil {
+ glog.Errorf("rendering template: %v", err)
+ }
+}
+
+func (s *service) handlerTake(w http.ResponseWriter, r *http.Request) {
+ if r.Method != "POST" {
+ return
+ }
+ r.ParseForm()
+ who := r.Form.Get("who")
+ prev := r.Form.Get("prev")
+ if who == "" {
+ fmt.Fprintf(w, "sorry, who are you? please specify a name")
+ return
+ }
+
+ res := make(chan bool)
+ take := &lockCtrlTake{
+ note: who,
+ addr: remoteAddr(r),
+ prev: prev,
+ res: res,
+ }
+ s.lockCtrl <- &lockCtrl{
+ take: take,
+ }
+ won := <-res
+
+ if won {
+ http.Redirect(w, r, "/", 302)
+ } else {
+ fmt.Fprintf(w, "lock not taken")
+ }
+}
+
+func (s *service) handlerRelease(w http.ResponseWriter, r *http.Request) {
+ if r.Method != "POST" {
+ return
+ }
+ res := make(chan struct{})
+ s.lockCtrl <- &lockCtrl{
+ release: &lockCtrlRelease{
+ addr: remoteAddr(r),
+ res: res,
+ },
+ }
+ <-res
+
+ http.Redirect(w, r, "/", 302)
+}
+
+func (s *service) handlerForce(w http.ResponseWriter, r *http.Request) {
+ if r.Method != "POST" {
+ return
+ }
+ res := make(chan struct{})
+ s.lockCtrl <- &lockCtrl{
+ release: &lockCtrlRelease{
+ force: true,
+ res: res,
+ },
+ }
+ <-res
+
+ http.Redirect(w, r, "/", 302)
+}