I'm writing a reverse proxy in go. I already have a basic setup working, but I want to be able to listen on a port and have it use TLS or not depending on the host. This is my current code:
var sitesManager *sites.SiteManager = sites.CreateSiteManager()func handleRequest(res http.ResponseWriter, req *http.Request) { ctx := req.Context() svrAddr := ctx.Value(http.LocalAddrContextKey).(net.Addr) host := strings.Split(req.Host, ":")[0] port := strings.Split(svrAddr.String(), ":")[1] origin := host +":" + port site, exists := sitesManager.GetSite(origin) if !exists { http.Error(res, "Site not found", http.StatusNotFound) return } site.HandleRequest(res, req)}func main() { manager := &autocert.Manager{ Cache: autocert.DirCache("certsCache"), Prompt: autocert.AcceptTOS, HostPolicy: sitesManager.HostPolicy(), } registerSite("example.com:80", &sites.RedirectSite{ Destination: "https://example.com", HTTPS: true, }, manager) registerSite("example.com:443", &sites.ReverseProxySite{ Destination: "http://localhost:8000", HTTPS: true, }, manager) handler := http.HandlerFunc(handleRequest) err := http.ListenAndServe(":80", manager.HTTPHandler(handler)) if err != nil { log.Fatalf("HTTPS server failed: %v", err) }}func registerSite(origin string, site sites.Site, certManager *autocert.Manager) { portString := strings.Split(origin, ":")[1] port, _ := strconv.Atoi(portString) portExists := sitesManager.IsPortRegistered(port) defer sitesManager.RegisterSite(origin, site) if portExists { return } if site.ShouldUseTLS() { go func() { tlsServer := &http.Server{ Addr: ":" + portString, Handler: http.HandlerFunc(handleRequest), TLSConfig: certManager.TLSConfig(), } err := tlsServer.ListenAndServeTLS("", "") if err != nil { log.Fatalf("HTTPS server failed: %v", err) } }() } else { go func() { err := http.ListenAndServe(":" + portString, http.HandlerFunc(handleRequest)) if err != nil { log.Fatalf("HTTP server failed: %v", err) } }() }}
The site manager is just contains a RWMutex and a few maps to handle routing and ensure that servers are started correctly.Right now something like this would not work correctly:
registerSite("example1.com:8000", &sites.ReverseProxySite{ Destination: "localhost:3000", HTTPS: false,}, manager)registerSite("example2.com:8000", &sites.ReverseProxySite{ Destination: "localhost:3001", HTTPS: true,}, manager)
Is it possible to listen on same port for both http and https?