ghttp_request.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
  2. //
  3. // This Source Code Form is subject to the terms of the MIT License.
  4. // If a copy of the MIT was not distributed with this file,
  5. // You can obtain one at https://github.com/gogf/gf.
  6. package ghttp
  7. import (
  8. "context"
  9. "fmt"
  10. "net/http"
  11. "strings"
  12. "time"
  13. "github.com/gogf/gf/internal/intlog"
  14. "github.com/gogf/gf/os/gres"
  15. "github.com/gogf/gf/os/gsession"
  16. "github.com/gogf/gf/os/gview"
  17. "github.com/gogf/gf/util/guid"
  18. "github.com/gogf/gf/os/gtime"
  19. "github.com/gogf/gf/text/gregex"
  20. )
  21. // Request is the context object for a request.
  22. type Request struct {
  23. *http.Request
  24. Server *Server // Server.
  25. Cookie *Cookie // Cookie.
  26. Session *gsession.Session // Session.
  27. Response *Response // Corresponding Response of this request.
  28. Router *Router // Matched Router for this request. Note that it's not available in HOOK handler.
  29. EnterTime int64 // Request starting time in microseconds.
  30. LeaveTime int64 // Request ending time in microseconds.
  31. Middleware *middleware // Middleware manager.
  32. StaticFile *staticFile // Static file object for static file serving.
  33. context context.Context // Custom context for internal usage purpose.
  34. handlers []*handlerParsedItem // All matched handlers containing handler, hook and middleware for this request.
  35. handlerResponse handlerResponse // Handler response object and its error value.
  36. hasHookHandler bool // A bool marking whether there's hook handler in the handlers for performance purpose.
  37. hasServeHandler bool // A bool marking whether there's serving handler in the handlers for performance purpose.
  38. parsedQuery bool // A bool marking whether the GET parameters parsed.
  39. parsedBody bool // A bool marking whether the request body parsed.
  40. parsedForm bool // A bool marking whether request Form parsed for HTTP method PUT, POST, PATCH.
  41. paramsMap map[string]interface{} // Custom parameters map.
  42. routerMap map[string]string // Router parameters map, which might be nil if there're no router parameters.
  43. queryMap map[string]interface{} // Query parameters map, which is nil if there's no query string.
  44. formMap map[string]interface{} // Form parameters map, which is nil if there's no form data from client.
  45. bodyMap map[string]interface{} // Body parameters map, which might be nil if there're no body content.
  46. error error // Current executing error of the request.
  47. exit bool // A bool marking whether current request is exited.
  48. parsedHost string // The parsed host name for current host used by GetHost function.
  49. clientIp string // The parsed client ip for current host used by GetClientIp function.
  50. bodyContent []byte // Request body content.
  51. isFileRequest bool // A bool marking whether current request is file serving.
  52. viewObject *gview.View // Custom template view engine object for this response.
  53. viewParams gview.Params // Custom template view variables for this response.
  54. }
  55. type handlerResponse struct {
  56. Object interface{}
  57. Error error
  58. }
  59. // staticFile is the file struct for static file service.
  60. type staticFile struct {
  61. File *gres.File // Resource file object.
  62. Path string // File path.
  63. IsDir bool // Is directory.
  64. }
  65. // newRequest creates and returns a new request object.
  66. func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request {
  67. request := &Request{
  68. Server: s,
  69. Request: r,
  70. Response: newResponse(s, w),
  71. EnterTime: gtime.TimestampMilli(),
  72. }
  73. request.Cookie = GetCookie(request)
  74. request.Session = s.sessionManager.New(
  75. r.Context(),
  76. request.GetSessionId(),
  77. )
  78. request.Response.Request = request
  79. request.Middleware = &middleware{
  80. request: request,
  81. }
  82. // Custom session id creating function.
  83. err := request.Session.SetIdFunc(func(ttl time.Duration) string {
  84. var (
  85. address = request.RemoteAddr
  86. header = fmt.Sprintf("%v", request.Header)
  87. )
  88. intlog.Print(r.Context(), address, header)
  89. return guid.S([]byte(address), []byte(header))
  90. })
  91. if err != nil {
  92. panic(err)
  93. }
  94. return request
  95. }
  96. // WebSocket upgrades current request as a websocket request.
  97. // It returns a new WebSocket object if success, or the error if failure.
  98. // Note that the request should be a websocket request, or it will surely fail upgrading.
  99. func (r *Request) WebSocket() (*WebSocket, error) {
  100. if conn, err := wsUpGrader.Upgrade(r.Response.Writer, r.Request, nil); err == nil {
  101. return &WebSocket{
  102. conn,
  103. }, nil
  104. } else {
  105. return nil, err
  106. }
  107. }
  108. // Exit exits executing of current HTTP handler.
  109. func (r *Request) Exit() {
  110. panic(exceptionExit)
  111. }
  112. // ExitAll exits executing of current and following HTTP handlers.
  113. func (r *Request) ExitAll() {
  114. r.exit = true
  115. panic(exceptionExitAll)
  116. }
  117. // ExitHook exits executing of current and following HTTP HOOK handlers.
  118. func (r *Request) ExitHook() {
  119. panic(exceptionExitHook)
  120. }
  121. // IsExited checks and returns whether current request is exited.
  122. func (r *Request) IsExited() bool {
  123. return r.exit
  124. }
  125. // GetHeader retrieves and returns the header value with given <key>.
  126. func (r *Request) GetHeader(key string) string {
  127. return r.Header.Get(key)
  128. }
  129. // GetHost returns current request host name, which might be a domain or an IP without port.
  130. func (r *Request) GetHost() string {
  131. if len(r.parsedHost) == 0 {
  132. array, _ := gregex.MatchString(`(.+):(\d+)`, r.Host)
  133. if len(array) > 1 {
  134. r.parsedHost = array[1]
  135. } else {
  136. r.parsedHost = r.Host
  137. }
  138. }
  139. return r.parsedHost
  140. }
  141. // IsFileRequest checks and returns whether current request is serving file.
  142. func (r *Request) IsFileRequest() bool {
  143. return r.isFileRequest
  144. }
  145. // IsAjaxRequest checks and returns whether current request is an AJAX request.
  146. func (r *Request) IsAjaxRequest() bool {
  147. return strings.EqualFold(r.Header.Get("X-Requested-With"), "XMLHttpRequest")
  148. }
  149. // GetClientIp returns the client ip of this request without port.
  150. // Note that this ip address might be modified by client header.
  151. func (r *Request) GetClientIp() string {
  152. if len(r.clientIp) == 0 {
  153. realIps := r.Header.Get("X-Forwarded-For")
  154. if realIps != "" && len(realIps) != 0 && !strings.EqualFold("unknown", realIps) {
  155. ipArray := strings.Split(realIps, ",")
  156. r.clientIp = ipArray[0]
  157. }
  158. if r.clientIp == "" || strings.EqualFold("unknown", realIps) {
  159. r.clientIp = r.Header.Get("Proxy-Client-IP")
  160. }
  161. if r.clientIp == "" || strings.EqualFold("unknown", realIps) {
  162. r.clientIp = r.Header.Get("WL-Proxy-Client-IP")
  163. }
  164. if r.clientIp == "" || strings.EqualFold("unknown", realIps) {
  165. r.clientIp = r.Header.Get("HTTP_CLIENT_IP")
  166. }
  167. if r.clientIp == "" || strings.EqualFold("unknown", realIps) {
  168. r.clientIp = r.Header.Get("HTTP_X_FORWARDED_FOR")
  169. }
  170. if r.clientIp == "" || strings.EqualFold("unknown", realIps) {
  171. r.clientIp = r.Header.Get("X-Real-IP")
  172. }
  173. if r.clientIp == "" || strings.EqualFold("unknown", realIps) {
  174. r.clientIp = r.GetRemoteIp()
  175. }
  176. }
  177. return r.clientIp
  178. }
  179. // GetRemoteIp returns the ip from RemoteAddr.
  180. func (r *Request) GetRemoteIp() string {
  181. array, _ := gregex.MatchString(`(.+):(\d+)`, r.RemoteAddr)
  182. if len(array) > 1 {
  183. return array[1]
  184. }
  185. return r.RemoteAddr
  186. }
  187. // GetUrl returns current URL of this request.
  188. func (r *Request) GetUrl() string {
  189. scheme := "http"
  190. if r.TLS != nil {
  191. scheme = "https"
  192. }
  193. return fmt.Sprintf(`%s://%s%s`, scheme, r.Host, r.URL.String())
  194. }
  195. // GetSessionId retrieves and returns session id from cookie or header.
  196. func (r *Request) GetSessionId() string {
  197. id := r.Cookie.GetSessionId()
  198. if id == "" {
  199. id = r.Header.Get(r.Server.GetSessionIdName())
  200. }
  201. return id
  202. }
  203. // GetReferer returns referer of this request.
  204. func (r *Request) GetReferer() string {
  205. return r.Header.Get("Referer")
  206. }
  207. // GetError returns the error occurs in the procedure of the request.
  208. // It returns nil if there's no error.
  209. func (r *Request) GetError() error {
  210. return r.error
  211. }
  212. // ReloadParam is used for modifying request parameter.
  213. // Sometimes, we want to modify request parameters through middleware, but directly modifying Request.Body
  214. // is invalid, so it clears the parsed* marks to make the parameters re-parsed.
  215. func (r *Request) ReloadParam() {
  216. r.parsedBody = false
  217. r.parsedForm = false
  218. r.parsedQuery = false
  219. r.bodyContent = nil
  220. }
  221. // GetHandlerResponse retrieves and returns the handler response object and its error.
  222. func (r *Request) GetHandlerResponse() (res interface{}, err error) {
  223. return r.handlerResponse.Object, r.handlerResponse.Error
  224. }