gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // Copyright 2023 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package web
  4. import (
  5. "fmt"
  6. "net/http"
  7. "reflect"
  8. "code.gitea.io/gitea/modules/log"
  9. "code.gitea.io/gitea/modules/web/routing"
  10. "code.gitea.io/gitea/modules/web/types"
  11. )
  12. var responseStatusProviders = map[reflect.Type]func(req *http.Request) types.ResponseStatusProvider{}
  13. func RegisterResponseStatusProvider[T any](fn func(req *http.Request) types.ResponseStatusProvider) {
  14. responseStatusProviders[reflect.TypeFor[T]()] = fn
  15. }
  16. // responseWriter is a wrapper of http.ResponseWriter, to check whether the response has been written
  17. type responseWriter struct {
  18. respWriter http.ResponseWriter
  19. status int
  20. }
  21. var _ types.ResponseStatusProvider = (*responseWriter)(nil)
  22. func (r *responseWriter) WrittenStatus() int {
  23. return r.status
  24. }
  25. func (r *responseWriter) Header() http.Header {
  26. return r.respWriter.Header()
  27. }
  28. func (r *responseWriter) Write(bytes []byte) (int, error) {
  29. if r.status == 0 {
  30. r.status = http.StatusOK
  31. }
  32. return r.respWriter.Write(bytes)
  33. }
  34. func (r *responseWriter) WriteHeader(statusCode int) {
  35. r.status = statusCode
  36. r.respWriter.WriteHeader(statusCode)
  37. }
  38. var (
  39. httpReqType = reflect.TypeFor[*http.Request]()
  40. respWriterType = reflect.TypeFor[http.ResponseWriter]()
  41. )
  42. // preCheckHandler checks whether the handler is valid, developers could get first-time feedback, all mistakes could be found at startup
  43. func preCheckHandler(fn reflect.Value, argsIn []reflect.Value) {
  44. hasStatusProvider := false
  45. for _, argIn := range argsIn {
  46. if _, hasStatusProvider = argIn.Interface().(types.ResponseStatusProvider); hasStatusProvider {
  47. break
  48. }
  49. }
  50. if !hasStatusProvider {
  51. panic(fmt.Sprintf("handler should have at least one ResponseStatusProvider argument, but got %s", fn.Type()))
  52. }
  53. if fn.Type().NumOut() != 0 {
  54. panic(fmt.Sprintf("handler should have no return value other than registered ones, but got %s", fn.Type()))
  55. }
  56. }
  57. func prepareHandleArgsIn(resp http.ResponseWriter, req *http.Request, fn reflect.Value, fnInfo *routing.FuncInfo) []reflect.Value {
  58. defer func() {
  59. if err := recover(); err != nil {
  60. log.Error("unable to prepare handler arguments for %s: %v", fnInfo.String(), err)
  61. panic(err)
  62. }
  63. }()
  64. isPreCheck := req == nil
  65. argsIn := make([]reflect.Value, fn.Type().NumIn())
  66. for i := 0; i < fn.Type().NumIn(); i++ {
  67. argTyp := fn.Type().In(i)
  68. switch argTyp {
  69. case respWriterType:
  70. argsIn[i] = reflect.ValueOf(resp)
  71. case httpReqType:
  72. argsIn[i] = reflect.ValueOf(req)
  73. default:
  74. if argFn, ok := responseStatusProviders[argTyp]; ok {
  75. if isPreCheck {
  76. argsIn[i] = reflect.ValueOf(&responseWriter{})
  77. } else {
  78. argsIn[i] = reflect.ValueOf(argFn(req))
  79. }
  80. } else {
  81. panic(fmt.Sprintf("unsupported argument type: %s", argTyp))
  82. }
  83. }
  84. }
  85. return argsIn
  86. }
  87. func handleResponse(fn reflect.Value, ret []reflect.Value) {
  88. if len(ret) != 0 {
  89. panic(fmt.Sprintf("unsupported return values: %s", fn.Type()))
  90. }
  91. }
  92. func hasResponseBeenWritten(argsIn []reflect.Value) bool {
  93. for _, argIn := range argsIn {
  94. if statusProvider, ok := argIn.Interface().(types.ResponseStatusProvider); ok {
  95. if statusProvider.WrittenStatus() != 0 {
  96. return true
  97. }
  98. }
  99. }
  100. return false
  101. }
  102. func wrapHandlerProvider[T http.Handler](hp func(next http.Handler) T, funcInfo *routing.FuncInfo) func(next http.Handler) http.Handler {
  103. return func(next http.Handler) http.Handler {
  104. h := hp(next) // this handle could be dynamically generated, so we can't use it for debug info
  105. return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
  106. defer routing.RecordFuncInfo(req.Context(), funcInfo)()
  107. h.ServeHTTP(resp, req)
  108. })
  109. }
  110. }
  111. // toHandlerProvider converts a handler to a handler provider
  112. // A handler provider is a function that takes a "next" http.Handler, it can be used as a middleware
  113. func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
  114. funcInfo := routing.GetFuncInfo(handler)
  115. fn := reflect.ValueOf(handler)
  116. if fn.Type().Kind() != reflect.Func {
  117. panic(fmt.Sprintf("handler must be a function, but got %s", fn.Type()))
  118. }
  119. if hp, ok := handler.(func(next http.Handler) http.Handler); ok {
  120. return wrapHandlerProvider(hp, funcInfo)
  121. } else if hp, ok := handler.(func(http.Handler) http.HandlerFunc); ok {
  122. return wrapHandlerProvider(hp, funcInfo)
  123. }
  124. provider := func(next http.Handler) http.Handler {
  125. return http.HandlerFunc(func(respOrig http.ResponseWriter, req *http.Request) {
  126. // wrap the response writer to check whether the response has been written
  127. resp := respOrig
  128. if _, ok := resp.(types.ResponseStatusProvider); !ok {
  129. resp = &responseWriter{respWriter: resp}
  130. }
  131. // prepare the arguments for the handler and do pre-check
  132. argsIn := prepareHandleArgsIn(resp, req, fn, funcInfo)
  133. if req == nil {
  134. preCheckHandler(fn, argsIn)
  135. return // it's doing pre-check, just return
  136. }
  137. defer routing.RecordFuncInfo(req.Context(), funcInfo)()
  138. ret := fn.Call(argsIn)
  139. // handle the return value (no-op at the moment)
  140. handleResponse(fn, ret)
  141. // if the response has not been written, call the next handler
  142. if next != nil && !hasResponseBeenWritten(argsIn) {
  143. next.ServeHTTP(resp, req)
  144. }
  145. })
  146. }
  147. provider(nil).ServeHTTP(nil, nil) // do a pre-check to make sure all arguments and return values are supported
  148. return provider
  149. }