github.com/pbberlin/go-pwa@v0.0.0-20220328105622-7c26e0ca1ab8/cmd/server/main.go (about) 1 package main 2 3 import ( 4 "crypto/tls" 5 "fmt" 6 "log" 7 "net/http" 8 "path/filepath" 9 "strings" 10 11 "github.com/pbberlin/go-pwa/pkg/cfg" 12 "github.com/pbberlin/go-pwa/pkg/db" 13 "github.com/pbberlin/go-pwa/pkg/static" 14 "golang.org/x/crypto/acme/autocert" 15 ) 16 17 func stripPortFromHost(s string) string { 18 if strings.Contains(s, ":") { 19 return strings.Split(s, ":")[0] 20 } 21 return s 22 } 23 24 func redirectHTTP(w http.ResponseWriter, r *http.Request) { 25 26 if cfg.Get().AutoRedirectHTTP { 27 28 if r.Method != "GET" && r.Method != "HEAD" { 29 http.Error(w, "Use HTTPS", http.StatusBadRequest) 30 return 31 } 32 target := "https://" + stripPortFromHost(r.Host) + r.URL.RequestURI() 33 http.Redirect(w, r, target, http.StatusFound) 34 35 } else { 36 37 w.Header().Set("Content-Type", "text/plain") 38 fmt.Fprintf(w, "You asked for HTTP - please switch to HTTPS.\n") 39 40 } 41 42 } 43 44 func main() { 45 46 var err error 47 48 cfg.Headless(cfg.Load, "/config/load") 49 if false { 50 // omit while developing backend db 51 cfg.Headless(static.PrepareStatic, "/prepare-static") 52 } 53 54 db.Init() 55 56 mux := http.NewServeMux() 57 mux.HandleFunc("/", home) 58 mux.HandleFunc("/index.html", home) 59 mux.HandleFunc("/offline.html", offline) 60 mux.HandleFunc("/layout-template-for-js.html", layoutTemplateForJS) 61 62 mux.HandleFunc("/hello", plain) 63 mux.HandleFunc("/save-json", saveJson) 64 mux.HandleFunc("/golang-metrics", golangMetrics) 65 66 mux.HandleFunc("/config/load", func(w http.ResponseWriter, r *http.Request) { 67 cfg.Load(w, r) 68 static.PrepareStatic(w, r) 69 }) 70 71 static.Register(mux) 72 73 switch cfg.Get().ModeHTTPS { 74 75 case "https-localhost-cert": 76 // localhost development using https://github.com/FiloSottile/mkcert 77 err = http.ListenAndServeTLS( 78 ":443", 79 "./app-bucket/server-config/certs/server.pem", 80 "./app-bucket/server-config/certs/server.key", 81 mux, 82 ) 83 84 case "letsenrypt-simple": 85 lstnr := autocert.NewListener(cfg.Get().Domains...) 86 err = http.Serve(lstnr, mux) 87 88 case "letsenrypt-extended": 89 mgr := &autocert.Manager{ 90 Prompt: autocert.AcceptTOS, 91 HostPolicy: autocert.HostWhitelist(cfg.Get().Domains...), 92 Cache: autocert.DirCache(filepath.Join(cfg.Get().AppDir, "autocert")), 93 } 94 95 tlsCfg := &tls.Config{ 96 // ServerName: "xxxxxx", // should equal hostname 97 GetCertificate: mgr.GetCertificate, 98 MinVersion: tls.VersionTLS12, 99 // NextProtos: []string{ 100 // "h2", "http/1.1", // enable HTTP/2 101 // acme.ALPNProto, // enable tls-alpn ACME challenges 102 // }, 103 } 104 105 server := &http.Server{ 106 Addr: ":https", // same as :443 107 TLSConfig: tlsCfg, 108 Handler: mux, 109 } 110 111 if false { 112 // this would disable http/2 113 server.TLSNextProto = map[string]func(*http.Server, *tls.Conn, http.Handler){} 114 } 115 116 // http server 117 go func() { 118 if cfg.Get().AllowHTTP { 119 log.Fatal(http.ListenAndServe(":http", mux)) 120 } else { 121 log.Printf("serve HTTP, which will redirect automatically to HTTPS - %v", cfg.Get().Dms) 122 // h := mgr.HTTPHandler(nil) // argument nil would silently redirect 123 h := mgr.HTTPHandler(http.HandlerFunc(redirectHTTP)) 124 log.Fatal(http.ListenAndServe(":http", h)) 125 } 126 }() 127 128 log.Printf("serve HTTPS for %v", cfg.Get().Dms) 129 err = server.ListenAndServeTLS("", "") 130 131 } 132 133 if err != nil { 134 log.Fatal("https server failed: ", err) 135 } 136 }