From 0824f4bcd9a31add8e7cfa1662d2cb40900de073 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Sun, 27 Mar 2016 20:44:30 +0200 Subject: Encapsulate in package --- Dockerfile | 2 +- cmd/goxy/main.go | 28 +++++++++++++++++ cmd/goxyctl/main.go | 73 ++++++++++++++++++++++++++++++++++++++++++++ data.go | 26 ---------------- goxyctl/main.go | 87 ----------------------------------------------------- main.go | 34 --------------------- route.go | 42 +++++++++++++------------- rpc.go | 33 ++++++++++---------- server.go | 52 ++++++++++++++++++++++++++++++++ ws.go | 2 +- 10 files changed, 192 insertions(+), 187 deletions(-) create mode 100644 cmd/goxy/main.go create mode 100644 cmd/goxyctl/main.go delete mode 100644 data.go delete mode 100644 goxyctl/main.go delete mode 100644 main.go create mode 100644 server.go diff --git a/Dockerfile b/Dockerfile index ae1638b..343a2e3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM golang ADD . /go/src/dim13.org/goxy -RUN go install dim13.org/goxy +RUN go install dim13.org/goxy/cmd/goxy VOLUME /go/src/dim13.org/goxy/data WORKDIR /go/src/dim13.org/goxy ENTRYPOINT /go/bin/goxy diff --git a/cmd/goxy/main.go b/cmd/goxy/main.go new file mode 100644 index 0000000..fc122e0 --- /dev/null +++ b/cmd/goxy/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "flag" + "log" + "net/http" + + "dim13.org/goxy" + + _ "net/http/pprof" +) + +var data = flag.String("data", "data/goxy.gob", "persistent storage file") + +func main() { + flag.Parse() + + server, err := goxy.NewServer(*data) + if err != nil { + log.Fatal(err) + } + + errc := make(chan error, 3) + go func() { errc <- server.ListenAndServe() }() + go func() { errc <- server.ListenAndServeTLS("", "") }() + go func() { errc <- http.ListenAndServe(":http-alt", nil) }() + log.Fatal(<-errc) +} diff --git a/cmd/goxyctl/main.go b/cmd/goxyctl/main.go new file mode 100644 index 0000000..1057fd4 --- /dev/null +++ b/cmd/goxyctl/main.go @@ -0,0 +1,73 @@ +package main + +import ( + "flag" + "io/ioutil" + "log" + "net/rpc" + + "dim13.org/goxy" +) + +var ( + rpcserver = flag.String("server", ":http-alt", "RPC Server port") + servername = flag.String("host", "", "ServerName") + upstream = flag.String("upstream", "", "Upstream URL") + keyfile = flag.String("key", "", "TLS Key file") + certfile = flag.String("cert", "", "TLS Cert file") + remove = flag.Bool("remove", false, "Remove route") +) + +func loadCert(certFile, keyFile string) ([]byte, []byte) { + if certFile == "" || keyFile == "" { + return nil, nil + } + cert, err := ioutil.ReadFile(certFile) + if err != nil { + log.Fatal(err) + } + key, err := ioutil.ReadFile(keyFile) + if err != nil { + log.Fatal(err) + } + return cert, key +} + +func send(server string, e goxy.Entry, del bool) error { + client, err := rpc.DialHTTP("tcp", server) + if err != nil { + return err + } + defer client.Close() + + switch { + case e.ServerName != "" && e.Upstream != "": + log.Println("Add", e) + return client.Call("GoXY.Add", e, nil) + case e.ServerName != "" && del: + log.Println("Del", e) + return client.Call("GoXY.Del", e, nil) + default: + var r goxy.Route + err = client.Call("GoXY.List", struct{}{}, &r) + if err != nil { + return err + } + for k, v := range r { + log.Println(k, v) + } + } + return nil +} + +func main() { + var e goxy.Entry + flag.Parse() + + e.ServerName, e.Upstream = *servername, *upstream + e.Cert, e.Key = loadCert(*certfile, *keyfile) + + if err := send(*rpcserver, e, *remove); err != nil { + log.Fatal(err) + } +} diff --git a/data.go b/data.go deleted file mode 100644 index 474afae..0000000 --- a/data.go +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import ( - "encoding/gob" - "os" -) - -// Save routes to persistent file -func (r Route) Save(fname string) error { - fd, err := os.Create(fname) - if err != nil { - return err - } - defer fd.Close() - return gob.NewEncoder(fd).Encode(r) -} - -// Load routes from persistent file -func (r *Route) Load(fname string) error { - fd, err := os.Open(fname) - if err != nil { - return err - } - defer fd.Close() - return gob.NewDecoder(fd).Decode(r) -} diff --git a/goxyctl/main.go b/goxyctl/main.go deleted file mode 100644 index 5fbc1e8..0000000 --- a/goxyctl/main.go +++ /dev/null @@ -1,87 +0,0 @@ -package main - -import ( - "flag" - "io/ioutil" - "log" - "net/rpc" -) - -var ( - rpcserver = flag.String("server", ":http-alt", "RPC Server port") - servername = flag.String("host", "", "ServerName") - upstream = flag.String("upstream", "", "Upstream URL") - keyfile = flag.String("key", "", "TLS Key file") - certfile = flag.String("cert", "", "TLS Cert file") - remove = flag.Bool("remove", false, "Remove route") -) - -// Entry contains routing settings -type Entry struct { - ServerName string - Upstream string - Cert []byte - Key []byte -} - -func (e Entry) String() string { - ret := e.ServerName + " → " + e.Upstream - if e.Cert != nil && e.Key != nil { - ret += " with TLS" - } - return ret -} - -func loadCert(certFile, keyFile string) ([]byte, []byte) { - if certFile == "" || keyFile == "" { - return nil, nil - } - cert, err := ioutil.ReadFile(certFile) - if err != nil { - log.Fatal(err) - } - key, err := ioutil.ReadFile(keyFile) - if err != nil { - log.Fatal(err) - } - return cert, key -} - -func send(server string, e Entry, del bool) error { - client, err := rpc.DialHTTP("tcp", server) - if err != nil { - return err - } - defer client.Close() - - switch { - case e.ServerName != "" && e.Upstream != "": - log.Println("Add", e) - return client.Call("GoXY.Add", e, nil) - case e.ServerName != "" && del: - log.Println("Del", e) - return client.Call("GoXY.Del", e, nil) - default: - var r []Entry - err = client.Call("GoXY.List", struct{}{}, &r) - if err != nil { - return err - } - for _, e := range r { - log.Println(e) - } - } - return nil -} - -func main() { - var e Entry - flag.Parse() - - e.ServerName, e.Upstream = *servername, *upstream - e.Cert, e.Key = loadCert(*certfile, *keyfile) - - if err := send(*rpcserver, e, *remove); err != nil { - log.Fatal(err) - } -} diff --git a/main.go b/main.go deleted file mode 100644 index 21e68da..0000000 --- a/main.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "crypto/tls" - "flag" - "log" - "net/http" - - _ "net/http/pprof" -) - -var ( - data = flag.String("data", "data/goxy.gob", "persistent storage file") - route = make(Route) - server = http.Server{ - Handler: http.NewServeMux(), - TLSConfig: &tls.Config{GetCertificate: route.GetCertificate}, - } -) - -func main() { - flag.Parse() - if err := route.Load(*data); err != nil { - log.Println(err) - } - if err := route.Restore(); err != nil { - log.Fatal(err) - } - errc := make(chan error) - go func() { errc <- server.ListenAndServe() }() - go func() { errc <- server.ListenAndServeTLS("", "") }() - go func() { errc <- http.ListenAndServe(":http-alt", nil) }() - log.Fatal(<-errc) -} diff --git a/route.go b/route.go index a695355..2fc38b3 100644 --- a/route.go +++ b/route.go @@ -1,4 +1,4 @@ -package main +package goxy import ( "crypto/tls" @@ -12,21 +12,29 @@ import ( // Route defines a set of routes including correspondent TLS certificates type Route map[string]Entry +// GetCertificate returns certificate for SNI negotiation +func (r Route) GetCertificate(h *tls.ClientHelloInfo) (*tls.Certificate, error) { + if e, ok := r[h.ServerName]; ok && e.cert != nil { + return e.cert, nil + } + return nil, errors.New("no cert for " + h.ServerName) +} + // Entry holds routing settings type Entry struct { ServerName string Upstream string - Cert []byte - Key []byte - cert *tls.Certificate + Cert []byte // PEM + Key []byte // PEM + cert *tls.Certificate // Parsed } -// GetCertificate returns certificate for SNI negotiation -func (r Route) GetCertificate(h *tls.ClientHelloInfo) (*tls.Certificate, error) { - if e, ok := r[h.ServerName]; ok && e.cert != nil { - return e.cert, nil +func (e Entry) String() string { + ret := e.ServerName + " → " + e.Upstream + if e.cert != nil { + ret += " with TLS" } - return nil, errors.New("no cert for " + h.ServerName) + return ret } func NewReverseProxy(target *url.URL) *httputil.ReverseProxy { @@ -39,16 +47,16 @@ func NewReverseProxy(target *url.URL) *httputil.ReverseProxy { } // Restore and update routes from in-memory state -func (r Route) Restore() error { +func (s *Server) Restore() error { mux := http.NewServeMux() - for k, v := range route { + for k, v := range s.Route { if v.Cert != nil && v.Key != nil { cert, err := tls.X509KeyPair(v.Cert, v.Key) if err != nil { return err } v.cert = &cert - r[k] = v + s.Route[k] = v } up, err := url.Parse(v.Upstream) if err != nil { @@ -65,14 +73,6 @@ func (r Route) Restore() error { mux.Handle(v.ServerName, NewReverseProxy(up)) } } - server.Handler = mux + s.Server.Handler = mux return nil } - -func (e Entry) String() string { - ret := e.ServerName + " → " + e.Upstream - if e.cert != nil { - ret += " with TLS" - } - return ret -} diff --git a/rpc.go b/rpc.go index 62a6a3f..bf7b36d 100644 --- a/rpc.go +++ b/rpc.go @@ -1,38 +1,37 @@ -package main +package goxy import ( "log" "net/rpc" ) -// GoXY defines RPC interface -type GoXY struct{} +type GoXY struct { + Server *Server +} -func init() { - rpc.Register(GoXY{}) +func Register(s *Server) { + rpc.Register(&GoXY{s}) rpc.HandleHTTP() } // Add adds a new route -func (GoXY) Add(e Entry, _ *struct{}) error { +func (s *GoXY) Add(e Entry, _ *struct{}) error { log.Println("Add route", e) - defer route.Save(*data) - route[e.ServerName] = e - return route.Restore() + defer s.Server.Save() + s.Server.Route[e.ServerName] = e + return s.Server.Restore() } // Del removes a route -func (GoXY) Del(e Entry, _ *struct{}) error { +func (s *GoXY) Del(e Entry, _ *struct{}) error { log.Println("Del route", e) - defer route.Save(*data) - delete(route, e.ServerName) - return route.Restore() + defer s.Server.Save() + delete(s.Server.Route, e.ServerName) + return s.Server.Restore() } // List routes -func (GoXY) List(_ struct{}, r *[]Entry) error { - for _, v := range route { - *r = append(*r, v) - } +func (s GoXY) List(_ struct{}, r *Route) error { + *r = s.Server.Route return nil } diff --git a/server.go b/server.go new file mode 100644 index 0000000..3d6651f --- /dev/null +++ b/server.go @@ -0,0 +1,52 @@ +package goxy + +import ( + "crypto/tls" + "encoding/gob" + "log" + "net/http" + "os" +) + +type Server struct { + Route + http.Server + Data string +} + +func NewServer(fname string) (*Server, error) { + r := make(Route) + s := http.Server{ + Handler: http.NewServeMux(), + TLSConfig: &tls.Config{GetCertificate: r.GetCertificate}, + } + server := &Server{Route: r, Server: s, Data: fname} + if err := server.Load(); err != nil { + log.Println(err) + } + if err := server.Restore(); err != nil { + return nil, err + } + Register(server) + return server, nil +} + +// Save routes to persistent file +func (s Server) Save() error { + fd, err := os.Create(s.Data) + if err != nil { + return err + } + defer fd.Close() + return gob.NewEncoder(fd).Encode(s.Route) +} + +// Load routes from persistent file +func (s *Server) Load() error { + fd, err := os.Open(s.Data) + if err != nil { + return err + } + defer fd.Close() + return gob.NewDecoder(fd).Decode(&s.Route) +} diff --git a/ws.go b/ws.go index a5c5d2f..b0199e8 100644 --- a/ws.go +++ b/ws.go @@ -1,4 +1,4 @@ -package main +package goxy import ( "io" -- cgit v1.2.3