113 lines
2.3 KiB
Go
113 lines
2.3 KiB
Go
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:]
|
|
}
|