router.go 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. package main
  2. import (
  3. "net/http"
  4. "regexp"
  5. "strings"
  6. nanoid "github.com/matoous/go-nanoid"
  7. )
  8. const (
  9. xRequestIDHeader = "X-Request-ID"
  10. )
  11. var (
  12. requestIDRe = regexp.MustCompile(`^[A-Za-z0-9_\-]+$`)
  13. )
  14. type routeHandler func(string, http.ResponseWriter, *http.Request)
  15. type panicHandler func(string, http.ResponseWriter, *http.Request, error)
  16. type route struct {
  17. Method string
  18. Prefix string
  19. Handler routeHandler
  20. }
  21. type router struct {
  22. Routes []*route
  23. PanicHandler panicHandler
  24. }
  25. func newRouter() *router {
  26. return &router{
  27. Routes: make([]*route, 0),
  28. }
  29. }
  30. func (r *router) Add(method, prefix string, handler routeHandler) {
  31. r.Routes = append(
  32. r.Routes,
  33. &route{Method: method, Prefix: prefix, Handler: handler},
  34. )
  35. }
  36. func (r *router) GET(prefix string, handler routeHandler) {
  37. r.Add(http.MethodGet, prefix, handler)
  38. }
  39. func (r *router) OPTIONS(prefix string, handler routeHandler) {
  40. r.Add(http.MethodOptions, prefix, handler)
  41. }
  42. func (r *router) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
  43. reqID := req.Header.Get(xRequestIDHeader)
  44. if len(reqID) == 0 || !requestIDRe.MatchString(reqID) {
  45. reqID, _ = nanoid.Nanoid()
  46. }
  47. rw.Header().Set("Server", "imgproxy")
  48. rw.Header().Set(xRequestIDHeader, reqID)
  49. defer func() {
  50. if rerr := recover(); rerr != nil {
  51. if err, ok := rerr.(error); ok && r.PanicHandler != nil {
  52. r.PanicHandler(reqID, rw, req, err)
  53. } else {
  54. panic(rerr)
  55. }
  56. }
  57. }()
  58. logRequest(reqID, req)
  59. for _, rr := range r.Routes {
  60. if rr.Method == req.Method && strings.HasPrefix(req.URL.Path, rr.Prefix) {
  61. rr.Handler(reqID, rw, req)
  62. return
  63. }
  64. }
  65. logWarning("Route for %s is not defined", req.URL.Path)
  66. rw.WriteHeader(404)
  67. }