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  }