diff --git a/internal/http/http.go b/internal/http/http.go index 08e4bf8f..735bf17a 100644 --- a/internal/http/http.go +++ b/internal/http/http.go @@ -7,11 +7,12 @@ import ( "os" "github.com/go-chi/chi" + "github.com/go-chi/chi/middleware" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "demodesk/neko/internal/http/endpoint" - "demodesk/neko/internal/http/middleware" + customMiddleware "demodesk/neko/internal/http/middleware" "demodesk/neko/internal/types" "demodesk/neko/internal/types/config" ) @@ -24,12 +25,12 @@ type Server struct { } func New(conf *config.Server, webSocketHandler types.WebSocketHandler) *Server { - logger := log.With().Str("module", "webrtc").Logger() + logger := log.With().Str("module", "http").Logger() router := chi.NewRouter() - // router.Use(middleware.Recoverer) // Recover from panics without crashing server + router.Use(middleware.Recoverer) // Recover from panics without crashing server router.Use(middleware.RequestID) // Create a request ID for each request - router.Use(middleware.Logger) // Log API request calls + router.Use(customMiddleware.Logger) // Log API request calls using custom logger function router.Get("/ws", func(w http.ResponseWriter, r *http.Request) { webSocketHandler.Upgrade(w, r) @@ -51,7 +52,7 @@ func New(conf *config.Server, webSocketHandler types.WebSocketHandler) *Server { } })) - server := &http.Server{ + http := &http.Server{ Addr: conf.Bind, Handler: router, } @@ -59,7 +60,7 @@ func New(conf *config.Server, webSocketHandler types.WebSocketHandler) *Server { return &Server{ logger: logger, router: router, - http: server, + http: http, conf: conf, } } diff --git a/internal/http/middleware/middleware.go b/internal/http/middleware/middleware.go deleted file mode 100644 index 1d0ca508..00000000 --- a/internal/http/middleware/middleware.go +++ /dev/null @@ -1,12 +0,0 @@ -package middleware - -// contextKey is a value for use with context.WithValue. It's used as -// a pointer so it fits in an interface{} without allocation. This technique -// for defining context keys was copied from Go 1.7's new use of context in net/http. -type ctxKey struct { - name string -} - -func (k *ctxKey) String() string { - return "neko/ctx/" + k.name -} diff --git a/internal/http/middleware/recover.go b/internal/http/middleware/recover.go deleted file mode 100644 index f90eb9a5..00000000 --- a/internal/http/middleware/recover.go +++ /dev/null @@ -1,24 +0,0 @@ -package middleware - -// The original work was derived from Goji's middleware, source: -// https://github.com/zenazn/goji/tree/master/web/middleware - -import ( - "net/http" - - "demodesk/neko/internal/http/endpoint" -) - -func Recoverer(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - defer func() { - if rvr := recover(); rvr != nil { - endpoint.WriteError(w, r, rvr) - } - }() - - next.ServeHTTP(w, r) - } - - return http.HandlerFunc(fn) -} diff --git a/internal/http/middleware/request.go b/internal/http/middleware/request.go deleted file mode 100644 index c9634bf8..00000000 --- a/internal/http/middleware/request.go +++ /dev/null @@ -1,89 +0,0 @@ -package middleware - -import ( - "context" - "crypto/rand" - "encoding/base64" - "fmt" - "net/http" - "os" - "strings" - "sync/atomic" -) - -// Key to use when setting the request ID. -type ctxKeyRequestID int - -// RequestIDKey is the key that holds the unique request ID in a request context. -const RequestIDKey ctxKeyRequestID = 0 - -var prefix string -var reqid uint64 - -// A quick note on the statistics here: we're trying to calculate the chance that -// two randomly generated base62 prefixes will collide. We use the formula from -// http://en.wikipedia.org/wiki/Birthday_problem -// -// P[m, n] \approx 1 - e^{-m^2/2n} -// -// We ballpark an upper bound for $m$ by imagining (for whatever reason) a server -// that restarts every second over 10 years, for $m = 86400 * 365 * 10 = 315360000$ -// -// For a $k$ character base-62 identifier, we have $n(k) = 62^k$ -// -// Plugging this in, we find $P[m, n(10)] \approx 5.75%$, which is good enough for -// our purposes, and is surely more than anyone would ever need in practice -- a -// process that is rebooted a handful of times a day for a hundred years has less -// than a millionth of a percent chance of generating two colliding IDs. - -func init() { - hostname, err := os.Hostname() - if hostname == "" || err != nil { - hostname = "localhost" - } - var buf [12]byte - var b64 string - for len(b64) < 10 { - rand.Read(buf[:]) - b64 = base64.StdEncoding.EncodeToString(buf[:]) - b64 = strings.NewReplacer("+", "", "/", "").Replace(b64) - } - - prefix = fmt.Sprintf("%s/%s", hostname, b64[0:10]) -} - -// RequestID is a middleware that injects a request ID into the context of each -// request. A request ID is a string of the form "host.example.com/random-0001", -// where "random" is a base62 random string that uniquely identifies this go -// process, and where the last number is an atomically incremented request -// counter. -func RequestID(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - requestID := r.Header.Get("X-Request-Id") - if requestID == "" { - myid := atomic.AddUint64(&reqid, 1) - requestID = fmt.Sprintf("%s-%06d", prefix, myid) - } - ctx = context.WithValue(ctx, RequestIDKey, requestID) - next.ServeHTTP(w, r.WithContext(ctx)) - } - return http.HandlerFunc(fn) -} - -// GetReqID returns a request ID from the given context if one is present. -// Returns the empty string if a request ID cannot be found. -func GetReqID(ctx context.Context) string { - if ctx == nil { - return "" - } - if reqID, ok := ctx.Value(RequestIDKey).(string); ok { - return reqID - } - return "" -} - -// NextRequestID generates the next request ID in the sequence. -func NextRequestID() uint64 { - return atomic.AddUint64(&reqid, 1) -} diff --git a/internal/http/response/response.go b/internal/http/response/response.go deleted file mode 100644 index 09ec1102..00000000 --- a/internal/http/response/response.go +++ /dev/null @@ -1,32 +0,0 @@ -package response - -import ( - "encoding/json" - "net/http" - - "demodesk/neko/internal/http/endpoint" -) - -// JSON encodes data to rw in JSON format. Returns a pointer to a -// HandlerError if encoding fails. -func JSON(w http.ResponseWriter, data interface{}, status int) error { - w.WriteHeader(status) - w.Header().Set("Content-Type", "application/json") - - err := json.NewEncoder(w).Encode(data) - if err != nil { - return &endpoint.HandlerError{ - Status: http.StatusInternalServerError, - Message: "unable to write JSON response", - Err: err, - } - } - - return nil -} - -// Empty merely sets the response code to NoContent (204). -func Empty(w http.ResponseWriter) error { - w.WriteHeader(http.StatusNoContent) - return nil -}