package main import ( "context" "encoding/json" "log" "net/http" "path" "strings" "time" "midnadimple.com/fog/internal/store" ) type application struct { config config store store.Storage v1 *v1Handler } type config struct { addr string db dbConfig } type dbConfig struct { addr string maxOpenConns int maxIdleConns int maxIdleTime time.Duration } func (h *application) ServeHTTP(w http.ResponseWriter, r *http.Request) { wWrapper := &wrappedResponseWriter{w, http.StatusOK} w = wWrapper // fullpath middleware ctx := context.WithValue(r.Context(), "fullPath", r.URL.Path) // logger middleware startTime := time.Now() defer func() { log.Printf("request at %s, responded with status %d in %dms", ctx.Value("fullPath"), wWrapper.statusCode, time.Since(startTime).Milliseconds()) }() // recoverer middleware defer func() { err := recover() if err != nil { log.Println(err) jsonBody, _ := json.Marshal(map[string]string{ "error": "There was an internal server error", }) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusInternalServerError) w.Write(jsonBody) } }() // timeout middleware ctx, cancel := context.WithTimeout(ctx, time.Minute) defer func() { cancel() if ctx.Err() == context.DeadlineExceeded { w.WriteHeader(http.StatusGatewayTimeout) } }() r = r.WithContext(ctx) var head string head, r.URL.Path = shiftPath(r.URL.Path) switch head { case "v1": h.v1.ServeHTTP(w, r) default: http.Error(w, "Not Found", http.StatusNotFound) } } func (app *application) run() error { app.v1 = &v1Handler{ healthCheck: &healthCheckHandler{}, } srv := &http.Server{ Addr: app.config.addr, Handler: app, WriteTimeout: time.Second * 30, ReadTimeout: time.Second * 10, IdleTimeout: time.Minute, } log.Printf("server has started at %s", app.config.addr) return srv.ListenAndServe() } // ShiftPath splits off the first component of p, which will be cleaned of // relative components before processing. head will never contain a slash and // tail will always be a rooted path without trailing slash. func shiftPath(p string) (head, tail string) { p = path.Clean("/" + p) i := strings.Index(p[1:], "/") + 1 if i <= 0 { return p[1:], "/" } return p[1:i], p[i:] }