package goxy import ( "crypto/tls" "net/http" "net/http/httputil" "net/url" ) type Server struct { DataFile string Routes Entries wwwServer http.Server tlsServer http.Server rpcServer http.Server } func NewServer(dataFile, listenWWW, listenTLS, listenRPC string) (*Server, error) { if listenRPC == "" { listenRPC = RPCPort } server := &Server{ DataFile: dataFile, Routes: make(Routes), Entries: make(Entries), wwwServer: http.Server{Addr: listenWWW}, tlsServer: http.Server{Addr: listenTLS}, rpcServer: http.Server{Addr: listenRPC}, } server.tlsServer.TLSConfig = &tls.Config{ GetCertificate: server.GetCertificate, } if dataFile != "" { server.Load(dataFile) } RegisterRPC(server) http.Handle("/debug/route", server) return server, server.UpdateMux() } func NewRedirect(host string) http.Handler { return http.RedirectHandler(host, http.StatusMovedPermanently) } func NewReverseProxy(target *url.URL) *httputil.ReverseProxy { return httputil.NewSingleHostReverseProxy(target) } // Update routes from in-memory state func (s *Server) UpdateMux() error { wwwMux := http.NewServeMux() tlsMux := http.NewServeMux() for host, r := range s.Routes { switch r.ServerName.Scheme { case "http", "": wwwMux.Handle(host, NewReverseProxy(r.Upstream)) case "https": wwwMux.Handle(host, NewRedirect("https://"+host)) tlsMux.Handle(host, NewReverseProxy(r.Upstream)) case "ws": wwwMux.Handle(host, NewWebSocketProxy(r.Upstream)) case "wss": wwwMux.Handle(host, NewRedirect("wss://"+host)) tlsMux.Handle(host, NewWebSocketProxy(r.Upstream)) } } s.wwwServer.Handler = wwwMux s.tlsServer.Handler = tlsMux return nil } func (s *Server) AddEntry(e Entry) error { r, err := NewRoute(e) if err != nil { return err } defer s.Save(s.DataFile) host := r.ServerName.Host + r.ServerName.Path s.Entries[host] = e s.Routes[host] = r return s.UpdateMux() } func (s *Server) DelEntry(host string) error { h, err := url.Parse(host) if err != nil { return err } if h.Path == "" { h.Path = "/" } host = h.Host + h.Path defer s.Save(s.DataFile) delete(s.Entries, host) delete(s.Routes, host) return s.UpdateMux() } func (s *Server) Start() error { errc := make(chan error) go func() { errc <- s.wwwServer.ListenAndServe() }() go func() { errc <- s.tlsServer.ListenAndServeTLS("", "") }() go func() { errc <- s.rpcServer.ListenAndServe() }() return <-errc }