package main import ( "database/sql" "encoding/json" "fmt" "log" "net/http" "path/filepath" "time" "github.com/NYTimes/gziphandler" "golang.org/x/crypto/acme/autocert" ) const AppName = `webscaffold` type WebSession struct { LastRefreshTime time.Time } type Application struct { c Config webfile http.Handler db *sql.DB sessions map[string]WebSession } func (this *Application) Start() error { log.Printf("Starting %s application...", AppName) // Connect to DB err := this.connectToDB() if err != nil { return err } // Resources this.webfile = http.FileServer(http.Dir(this.c.WebrootDir)) // Set up HTTP server if this.c.Autocert == "" { log.Printf("Starting web server (HTTP on %s)...", this.c.ListenAddress) return http.ListenAndServe(this.c.ListenAddress, this) // Plain HTTP } else { log.Printf("Starting web server (HTTPS as '%s' on :80 + :443)...", this.c.Autocert) return http.Serve(autocert.NewListener(this.c.Autocert), this) // HTTPS } } func (this *Application) respondWith(object interface{}, nerr netError) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // curry over extra arity if nerr != nil { nerr.Respond(w) return } // Plaintext response // Buffer the resulting json data, so that we can determine the final size jb, err := json.Marshal(object) if err != nil { systemError{err}.Respond(w) return } w.Header().Set(`Content-Type`, `application/json`) w.Header().Set(`Content-Length`, fmt.Sprintf("%d", len(jb))) w.WriteHeader(200) w.Write(jb) } } func (this *Application) ServeHTTP(w http.ResponseWriter, r *http.Request) { r.Header.Set(`Server`, AppName+`/1.0`) switch r.Method { case "GET": r.Header.Set(`X-Frame-Options`, `Deny`) if r.URL.Path == `/` { http.ServeFile(w, r, filepath.Join(this.c.WebrootDir, "index.html")) // n.b. no gzip } else { gziphandler.GzipHandler(this.webfile).ServeHTTP(w, r) } case "POST": if r.URL.Path == `/api/v1/login` { resp, err := this.apiLogin(r) gziphandler.GzipHandler(this.respondWith(resp, err)).ServeHTTP(w, r) } else { http.Error(w, "not found", 404) } } }