gitea源码

router.go 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package web
  4. import (
  5. "net/http"
  6. "net/url"
  7. "reflect"
  8. "strings"
  9. "code.gitea.io/gitea/modules/htmlutil"
  10. "code.gitea.io/gitea/modules/reqctx"
  11. "code.gitea.io/gitea/modules/setting"
  12. "code.gitea.io/gitea/modules/web/middleware"
  13. "gitea.com/go-chi/binding"
  14. "github.com/go-chi/chi/v5"
  15. )
  16. // Bind binding an obj to a handler's context data
  17. func Bind[T any](_ T) http.HandlerFunc {
  18. return func(resp http.ResponseWriter, req *http.Request) {
  19. theObj := new(T) // create a new form obj for every request but not use obj directly
  20. data := middleware.GetContextData(req.Context())
  21. binding.Bind(req, theObj)
  22. SetForm(data, theObj)
  23. middleware.AssignForm(theObj, data)
  24. }
  25. }
  26. // SetForm set the form object
  27. func SetForm(dataStore reqctx.ContextDataProvider, obj any) {
  28. dataStore.GetData()["__form"] = obj
  29. }
  30. // GetForm returns the validate form information
  31. func GetForm(dataStore reqctx.RequestDataStore) any {
  32. return dataStore.GetData()["__form"]
  33. }
  34. // Router defines a route based on chi's router
  35. type Router struct {
  36. chiRouter *chi.Mux
  37. curGroupPrefix string
  38. curMiddlewares []any
  39. }
  40. // NewRouter creates a new route
  41. func NewRouter() *Router {
  42. r := chi.NewRouter()
  43. return &Router{chiRouter: r}
  44. }
  45. // Use supports two middlewares
  46. func (r *Router) Use(middlewares ...any) {
  47. for _, m := range middlewares {
  48. if m != nil {
  49. r.chiRouter.Use(toHandlerProvider(m))
  50. }
  51. }
  52. }
  53. // Group mounts a sub-Router along a `pattern` string.
  54. func (r *Router) Group(pattern string, fn func(), middlewares ...any) {
  55. previousGroupPrefix := r.curGroupPrefix
  56. previousMiddlewares := r.curMiddlewares
  57. r.curGroupPrefix += pattern
  58. r.curMiddlewares = append(r.curMiddlewares, middlewares...)
  59. fn()
  60. r.curGroupPrefix = previousGroupPrefix
  61. r.curMiddlewares = previousMiddlewares
  62. }
  63. func (r *Router) getPattern(pattern string) string {
  64. newPattern := r.curGroupPrefix + pattern
  65. if !strings.HasPrefix(newPattern, "/") {
  66. newPattern = "/" + newPattern
  67. }
  68. if newPattern == "/" {
  69. return newPattern
  70. }
  71. return strings.TrimSuffix(newPattern, "/")
  72. }
  73. func isNilOrFuncNil(v any) bool {
  74. if v == nil {
  75. return true
  76. }
  77. r := reflect.ValueOf(v)
  78. return r.Kind() == reflect.Func && r.IsNil()
  79. }
  80. func wrapMiddlewareAndHandler(curMiddlewares, h []any) ([]func(http.Handler) http.Handler, http.HandlerFunc) {
  81. handlerProviders := make([]func(http.Handler) http.Handler, 0, len(curMiddlewares)+len(h)+1)
  82. for _, m := range curMiddlewares {
  83. if !isNilOrFuncNil(m) {
  84. handlerProviders = append(handlerProviders, toHandlerProvider(m))
  85. }
  86. }
  87. if len(h) == 0 {
  88. panic("no endpoint handler provided")
  89. }
  90. for i, m := range h {
  91. if !isNilOrFuncNil(m) {
  92. handlerProviders = append(handlerProviders, toHandlerProvider(m))
  93. } else if i == len(h)-1 {
  94. panic("endpoint handler can't be nil")
  95. }
  96. }
  97. middlewares := handlerProviders[:len(handlerProviders)-1]
  98. handlerFunc := handlerProviders[len(handlerProviders)-1](nil).ServeHTTP
  99. mockPoint := RouterMockPoint(MockAfterMiddlewares)
  100. if mockPoint != nil {
  101. middlewares = append(middlewares, mockPoint)
  102. }
  103. return middlewares, handlerFunc
  104. }
  105. // Methods adds the same handlers for multiple http "methods" (separated by ",").
  106. // If any method is invalid, the lower level router will panic.
  107. func (r *Router) Methods(methods, pattern string, h ...any) {
  108. middlewares, handlerFunc := wrapMiddlewareAndHandler(r.curMiddlewares, h)
  109. fullPattern := r.getPattern(pattern)
  110. if strings.Contains(methods, ",") {
  111. methods := strings.SplitSeq(methods, ",")
  112. for method := range methods {
  113. r.chiRouter.With(middlewares...).Method(strings.TrimSpace(method), fullPattern, handlerFunc)
  114. }
  115. } else {
  116. r.chiRouter.With(middlewares...).Method(methods, fullPattern, handlerFunc)
  117. }
  118. }
  119. // Mount attaches another Router along ./pattern/*
  120. func (r *Router) Mount(pattern string, subRouter *Router) {
  121. subRouter.Use(r.curMiddlewares...)
  122. r.chiRouter.Mount(r.getPattern(pattern), subRouter.chiRouter)
  123. }
  124. // Any delegate requests for all methods
  125. func (r *Router) Any(pattern string, h ...any) {
  126. middlewares, handlerFunc := wrapMiddlewareAndHandler(r.curMiddlewares, h)
  127. r.chiRouter.With(middlewares...).HandleFunc(r.getPattern(pattern), handlerFunc)
  128. }
  129. // Delete delegate delete method
  130. func (r *Router) Delete(pattern string, h ...any) {
  131. r.Methods("DELETE", pattern, h...)
  132. }
  133. // Get delegate get method
  134. func (r *Router) Get(pattern string, h ...any) {
  135. r.Methods("GET", pattern, h...)
  136. }
  137. // Head delegate head method
  138. func (r *Router) Head(pattern string, h ...any) {
  139. r.Methods("HEAD", pattern, h...)
  140. }
  141. // Post delegate post method
  142. func (r *Router) Post(pattern string, h ...any) {
  143. r.Methods("POST", pattern, h...)
  144. }
  145. // Put delegate put method
  146. func (r *Router) Put(pattern string, h ...any) {
  147. r.Methods("PUT", pattern, h...)
  148. }
  149. // Patch delegate patch method
  150. func (r *Router) Patch(pattern string, h ...any) {
  151. r.Methods("PATCH", pattern, h...)
  152. }
  153. // ServeHTTP implements http.Handler
  154. func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  155. r.normalizeRequestPath(w, req, r.chiRouter)
  156. }
  157. // NotFound defines a handler to respond whenever a route could not be found.
  158. func (r *Router) NotFound(h http.HandlerFunc) {
  159. r.chiRouter.NotFound(h)
  160. }
  161. func (r *Router) normalizeRequestPath(resp http.ResponseWriter, req *http.Request, next http.Handler) {
  162. normalized := false
  163. normalizedPath := req.URL.EscapedPath()
  164. if normalizedPath == "" {
  165. normalizedPath, normalized = "/", true
  166. } else if normalizedPath != "/" {
  167. normalized = strings.HasSuffix(normalizedPath, "/")
  168. normalizedPath = strings.TrimRight(normalizedPath, "/")
  169. }
  170. removeRepeatedSlashes := strings.Contains(normalizedPath, "//")
  171. normalized = normalized || removeRepeatedSlashes
  172. // the following code block is a slow-path for replacing all repeated slashes "//" to one single "/"
  173. // if the path doesn't have repeated slashes, then no need to execute it
  174. if removeRepeatedSlashes {
  175. buf := &strings.Builder{}
  176. for i := 0; i < len(normalizedPath); i++ {
  177. if i == 0 || normalizedPath[i-1] != '/' || normalizedPath[i] != '/' {
  178. buf.WriteByte(normalizedPath[i])
  179. }
  180. }
  181. normalizedPath = buf.String()
  182. }
  183. // If the config tells Gitea to use a sub-url path directly without reverse proxy,
  184. // then we need to remove the sub-url path from the request URL path.
  185. // But "/v2" is special for OCI container registry, it should always be in the root of the site.
  186. if setting.UseSubURLPath {
  187. remainingPath, ok := strings.CutPrefix(normalizedPath, setting.AppSubURL+"/")
  188. if ok {
  189. normalizedPath = "/" + remainingPath
  190. } else if normalizedPath == setting.AppSubURL {
  191. normalizedPath = "/"
  192. } else if !strings.HasPrefix(normalizedPath+"/", "/v2/") {
  193. // do not respond to other requests, to simulate a real sub-path environment
  194. resp.Header().Add("Content-Type", "text/html; charset=utf-8")
  195. resp.WriteHeader(http.StatusNotFound)
  196. _, _ = resp.Write([]byte(htmlutil.HTMLFormat(`404 page not found, sub-path is: <a href="%s">%s</a>`, setting.AppSubURL, setting.AppSubURL)))
  197. return
  198. }
  199. normalized = true
  200. }
  201. // if the path is normalized, then fill it back to the request
  202. if normalized {
  203. decodedPath, err := url.PathUnescape(normalizedPath)
  204. if err != nil {
  205. http.Error(resp, "400 Bad Request: unable to unescape path "+normalizedPath, http.StatusBadRequest)
  206. return
  207. }
  208. req.URL.RawPath = normalizedPath
  209. req.URL.Path = decodedPath
  210. }
  211. next.ServeHTTP(resp, req)
  212. }
  213. // Combo delegates requests to Combo
  214. func (r *Router) Combo(pattern string, h ...any) *Combo {
  215. return &Combo{r, pattern, h}
  216. }
  217. // PathGroup creates a group of paths which could be matched by regexp.
  218. // It is only designed to resolve some special cases which chi router can't handle.
  219. // For most cases, it shouldn't be used because it needs to iterate all rules to find the matched one (inefficient).
  220. func (r *Router) PathGroup(pattern string, fn func(g *RouterPathGroup), h ...any) {
  221. g := &RouterPathGroup{r: r, pathParam: "*"}
  222. fn(g)
  223. r.Any(pattern, append(h, g.ServeHTTP)...)
  224. }