package server import ( "net/http" "time" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" ) // What the JSON routes under /api/v1 and /ip answer a path none of them // matches, and a method they do not take, with. const ( notFoundMessage = "Not found" methodNotAllowedMessage = "Method not allowed" // jsonRoutesAllow is the Allow header of their 405 answer: they are all // GET routes, and GetHead serves HEAD on them as GET. jsonRoutesAllow = "GET, HEAD" ) // setupRoutes configures the HTTP routes func (s *Server) setupRoutes() { r := chi.NewRouter() // Middleware r.Use(DecodedPathMiddleware) r.Use(middleware.GetHead) // after DecodedPathMiddleware, to route by the same path r.Use(SecurityHeadersMiddleware) r.Use(middleware.RequestID) r.Use(RealIPMiddleware(s.trustedProxies)) r.Use(RequestLoggerMiddleware(s.logger.Logger)) // Structured request logging r.Use(middleware.Recoverer) r.Use(JSONResponseMiddleware) // Timeout must come after JSONResponseMiddleware, which sends its answer // when the handler returns and would send a 200 ahead of Timeout's 504 for // a handler that wrote nothing. const requestTimeout = 30 * time.Second // Increased from 8s for slow queries r.Use(middleware.Timeout(requestTimeout)) // Routes r.Get("/", s.handleIndex()) r.Get("/status", s.handleStatusHTML()) r.Get("/status.json", JSONValidationMiddleware(s.handleStatusJSON()).ServeHTTP) r.Get( "/.well-known/healthcheck.json", JSONValidationMiddleware(s.handleHealthCheck()).ServeHTTP, ) // AS and prefix detail pages r.Get("/as/{asn}", s.handleASDetail()) r.Get("/prefix/{prefix}/{len}", s.handlePrefixDetail()) r.Get("/prefixlength/{length}", s.handlePrefixLength()) r.Get("/prefixlength6/{length}", s.handlePrefixLength6()) // The JSON routes answer a path or method they do not take with the JSON // error body, as they answer everything else. jsonNotFound := func(w http.ResponseWriter, _ *http.Request) { s.writeJSONError(w, http.StatusNotFound, notFoundMessage) } jsonMethodNotAllowed := func(w http.ResponseWriter, _ *http.Request) { // chi's own 405 answer sets Allow; one set in its place must too. w.Header().Set("Allow", jsonRoutesAllow) s.writeJSONError(w, http.StatusMethodNotAllowed, methodNotAllowedMessage) } // IP info JSON endpoints (replaces old /ip redirect) r.Route("/ip", func(r chi.Router) { r.Use(JSONValidationMiddleware) r.NotFound(jsonNotFound) r.MethodNotAllowed(jsonMethodNotAllowed) r.Get("/", s.handleIPInfo()) // Client IP r.Get("/{addr}", s.handleIPInfo()) // Specified IP }) // API routes r.Route("/api/v1", func(r chi.Router) { r.Use(JSONValidationMiddleware) r.NotFound(jsonNotFound) r.MethodNotAllowed(jsonMethodNotAllowed) r.Get("/stats", s.handleStats()) r.Get("/ip/{ip}", s.handleIPLookup()) r.Get("/as/{asn}", s.handleASDetailJSON()) r.Get("/prefix/{prefix}/{len}", s.handlePrefixDetailJSON()) }) s.router = r }