Core functionality for new aiterp.net servers
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123 lines
3.5 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. package wrouter
  2. import (
  3. "fmt"
  4. "net/http"
  5. "strings"
  6. "time"
  7. "git.aiterp.net/gisle/wrouter/auth"
  8. )
  9. // Route is an interface for a request handler.
  10. type Route interface {
  11. Handle(path string, w http.ResponseWriter, req *http.Request, user *auth.User) bool
  12. }
  13. // Router is the main structure of the wrouter package. It routes requests to the appropriate
  14. // Route
  15. type Router struct {
  16. paths map[Route]string
  17. routes []Route
  18. }
  19. // Mount a router to the router, prefixing all the paths of it
  20. func (router *Router) Mount(path string, subRouter *Router) {
  21. for _, route := range subRouter.routes {
  22. router.Route(strings.Replace(path+subRouter.paths[route], "//", "/", 1), route)
  23. }
  24. }
  25. // Route mounts a route interface to a path
  26. func (router *Router) Route(path string, route Route) {
  27. if router.paths == nil {
  28. router.paths = make(map[Route]string, 16)
  29. }
  30. router.paths[route] = path
  31. router.routes = append(router.routes, route)
  32. }
  33. // ServeHTTP serves a HTTP request using the router's routes
  34. func (router *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  35. req.ParseForm()
  36. defer req.Body.Close()
  37. // Allow REST for clients of yore
  38. if req.Header.Get("X-Method") != "" {
  39. req.Method = strings.ToUpper(req.Header.Get("X-Method"))
  40. }
  41. // Resolve session cookies
  42. var user *auth.User
  43. var sess *auth.Session
  44. cookie, err := req.Cookie(auth.SessionCookieName)
  45. if cookie != nil && err == nil {
  46. sess = auth.FindSession(cookie.Value)
  47. if sess != nil {
  48. user, _ = auth.FindUser(sess.UserID)
  49. if user != nil {
  50. user.Session = sess
  51. http.SetCookie(w, &http.Cookie{Name: auth.SessionCookieName, Value: sess.ID, Expires: sess.Time.Add(auth.SessionMaxTime), Path: "/", HttpOnly: true})
  52. }
  53. }
  54. }
  55. for index, route := range router.routes {
  56. path := router.paths[route]
  57. if strings.HasPrefix(strings.ToLower(req.URL.Path), path) {
  58. // Just so the handler can replace the path properly in case of case
  59. // insensitive clients getting fancy on it.
  60. path = req.URL.Path[:len(path)]
  61. // Attach a little something for testing
  62. w.Header().Set("X-Route-Path", path)
  63. w.Header().Set("X-Route-Index", fmt.Sprint(index))
  64. if route.Handle(path, w, req, user) {
  65. if user != nil && user.LoggedOut() {
  66. auth.CloseSession(sess.ID)
  67. }
  68. return
  69. }
  70. }
  71. }
  72. w.Header().Set("Content-Type", "text/plain; charset=utf-8")
  73. w.WriteHeader(404)
  74. w.Write([]byte("Not Found: " + req.URL.Path))
  75. }
  76. // Resource is a shorthand for creating and adding a new REST resource to the router
  77. func (router *Router) Resource(mount string, list, create ResourceFunc, get, update, delete ResourceIDFunc) {
  78. router.Route(mount, NewResource(list, create, get, update, delete))
  79. }
  80. // Static is a shorthand for creating a static file server on the following path
  81. func (router *Router) Static(mount string, filePath string) {
  82. router.Route(mount, NewStatic(filePath))
  83. }
  84. // Function creates a simple bare-bones handler that just runs a function, prevents
  85. // the need for dummy interfaces
  86. func (router *Router) Function(mount string, function FunctionHandlerFunc) {
  87. router.Route(mount, &functionHandler{function})
  88. }
  89. // Listen creates a http.Server with some sane defaults and pointing to this structure
  90. // for request handling.
  91. func (router *Router) Listen(host string, port int) (*http.Server, error) {
  92. srv := &http.Server{
  93. Addr: fmt.Sprintf("%s:%d", host, port),
  94. ReadTimeout: 5 * time.Second,
  95. WriteTimeout: 10 * time.Second,
  96. IdleTimeout: 120 * time.Second,
  97. Handler: router,
  98. }
  99. return srv, srv.ListenAndServe()
  100. }