ghttp_request.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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. "fmt"
  9. "net/http"
  10. "strings"
  11. "time"
  12. "github.com/gogf/gf/v2/internal/intlog"
  13. "github.com/gogf/gf/v2/os/gres"
  14. "github.com/gogf/gf/v2/os/gsession"
  15. "github.com/gogf/gf/v2/os/gtime"
  16. "github.com/gogf/gf/v2/os/gview"
  17. "github.com/gogf/gf/v2/text/gregex"
  18. "github.com/gogf/gf/v2/text/gstr"
  19. "github.com/gogf/gf/v2/util/guid"
  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 milliseconds.
  30. LeaveTime int64 // Request to end time in milliseconds.
  31. Middleware *middleware // Middleware manager.
  32. StaticFile *staticFile // Static file object for static file serving.
  33. // =================================================================================================================
  34. // Private attributes for internal usage purpose.
  35. // =================================================================================================================
  36. handlers []*HandlerItemParsed // All matched handlers containing handler, hook and middleware for this request.
  37. serveHandler *HandlerItemParsed // Real handler serving for this request, not hook or middleware.
  38. handlerResponse interface{} // Handler response object for Request/Response handler.
  39. hasHookHandler bool // A bool marking whether there's hook handler in the handlers for performance purpose.
  40. hasServeHandler bool // A bool marking whether there's serving handler in the handlers for performance purpose.
  41. parsedQuery bool // A bool marking whether the GET parameters parsed.
  42. parsedBody bool // A bool marking whether the request body parsed.
  43. parsedForm bool // A bool marking whether request Form parsed for HTTP method PUT, POST, PATCH.
  44. paramsMap map[string]interface{} // Custom parameters map.
  45. routerMap map[string]string // Router parameters map, which might be nil if there are no router parameters.
  46. queryMap map[string]interface{} // Query parameters map, which is nil if there's no query string.
  47. formMap map[string]interface{} // Form parameters map, which is nil if there's no form of data from the client.
  48. bodyMap map[string]interface{} // Body parameters map, which might be nil if their nobody content.
  49. error error // Current executing error of the request.
  50. exitAll bool // A bool marking whether current request is exited.
  51. parsedHost string // The parsed host name for current host used by GetHost function.
  52. clientIp string // The parsed client ip for current host used by GetClientIp function.
  53. bodyContent []byte // Request body content.
  54. isFileRequest bool // A bool marking whether current request is file serving.
  55. viewObject *gview.View // Custom template view engine object for this response.
  56. viewParams gview.Params // Custom template view variables for this response.
  57. originUrlPath string // Original URL path that passed from client.
  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. originUrlPath: r.URL.Path,
  73. }
  74. request.Cookie = GetCookie(request)
  75. request.Session = s.sessionManager.New(
  76. r.Context(),
  77. request.GetSessionId(),
  78. )
  79. request.Response.Request = request
  80. request.Middleware = &middleware{
  81. request: request,
  82. }
  83. // Custom session id creating function.
  84. err := request.Session.SetIdFunc(func(ttl time.Duration) string {
  85. var (
  86. address = request.RemoteAddr
  87. header = fmt.Sprintf("%v", request.Header)
  88. )
  89. intlog.Print(r.Context(), address, header)
  90. return guid.S([]byte(address), []byte(header))
  91. })
  92. if err != nil {
  93. panic(err)
  94. }
  95. // Remove char '/' in the tail of URI.
  96. if request.URL.Path != "/" {
  97. for len(request.URL.Path) > 0 && request.URL.Path[len(request.URL.Path)-1] == '/' {
  98. request.URL.Path = request.URL.Path[:len(request.URL.Path)-1]
  99. }
  100. }
  101. // Default URI value if it's empty.
  102. if request.URL.Path == "" {
  103. request.URL.Path = "/"
  104. }
  105. return request
  106. }
  107. // WebSocket upgrades current request as a websocket request.
  108. // It returns a new WebSocket object if success, or the error if failure.
  109. // Note that the request should be a websocket request, or it will surely fail upgrading.
  110. func (r *Request) WebSocket() (*WebSocket, error) {
  111. if conn, err := wsUpGrader.Upgrade(r.Response.Writer, r.Request, nil); err == nil {
  112. return &WebSocket{
  113. conn,
  114. }, nil
  115. } else {
  116. return nil, err
  117. }
  118. }
  119. // Exit exits executing of current HTTP handler.
  120. func (r *Request) Exit() {
  121. panic(exceptionExit)
  122. }
  123. // ExitAll exits executing of current and following HTTP handlers.
  124. func (r *Request) ExitAll() {
  125. r.exitAll = true
  126. panic(exceptionExitAll)
  127. }
  128. // ExitHook exits executing of current and following HTTP HOOK handlers.
  129. func (r *Request) ExitHook() {
  130. panic(exceptionExitHook)
  131. }
  132. // IsExited checks and returns whether current request is exited.
  133. func (r *Request) IsExited() bool {
  134. return r.exitAll
  135. }
  136. // GetHeader retrieves and returns the header value with given `key`.
  137. func (r *Request) GetHeader(key string) string {
  138. return r.Header.Get(key)
  139. }
  140. // GetHost returns current request host name, which might be a domain or an IP without port.
  141. func (r *Request) GetHost() string {
  142. if len(r.parsedHost) == 0 {
  143. array, _ := gregex.MatchString(`(.+):(\d+)`, r.Host)
  144. if len(array) > 1 {
  145. r.parsedHost = array[1]
  146. } else {
  147. r.parsedHost = r.Host
  148. }
  149. }
  150. return r.parsedHost
  151. }
  152. // IsFileRequest checks and returns whether current request is serving file.
  153. func (r *Request) IsFileRequest() bool {
  154. return r.isFileRequest
  155. }
  156. // IsAjaxRequest checks and returns whether current request is an AJAX request.
  157. func (r *Request) IsAjaxRequest() bool {
  158. return strings.EqualFold(r.Header.Get("X-Requested-With"), "XMLHttpRequest")
  159. }
  160. // GetClientIp returns the client ip of this request without port.
  161. // Note that this ip address might be modified by client header.
  162. func (r *Request) GetClientIp() string {
  163. if r.clientIp != "" {
  164. return r.clientIp
  165. }
  166. realIps := r.Header.Get("X-Forwarded-For")
  167. if realIps != "" && len(realIps) != 0 && !strings.EqualFold("unknown", realIps) {
  168. ipArray := strings.Split(realIps, ",")
  169. r.clientIp = ipArray[0]
  170. }
  171. if r.clientIp == "" {
  172. r.clientIp = r.Header.Get("Proxy-Client-IP")
  173. }
  174. if r.clientIp == "" {
  175. r.clientIp = r.Header.Get("WL-Proxy-Client-IP")
  176. }
  177. if r.clientIp == "" {
  178. r.clientIp = r.Header.Get("HTTP_CLIENT_IP")
  179. }
  180. if r.clientIp == "" {
  181. r.clientIp = r.Header.Get("HTTP_X_FORWARDED_FOR")
  182. }
  183. if r.clientIp == "" {
  184. r.clientIp = r.Header.Get("X-Real-IP")
  185. }
  186. if r.clientIp == "" {
  187. r.clientIp = r.GetRemoteIp()
  188. }
  189. return r.clientIp
  190. }
  191. // GetRemoteIp returns the ip from RemoteAddr.
  192. func (r *Request) GetRemoteIp() string {
  193. array, _ := gregex.MatchString(`(.+):(\d+)`, r.RemoteAddr)
  194. if len(array) > 1 {
  195. return strings.Trim(array[1], "[]")
  196. }
  197. return r.RemoteAddr
  198. }
  199. // GetUrl returns current URL of this request.
  200. func (r *Request) GetUrl() string {
  201. var (
  202. scheme = "http"
  203. proto = r.Header.Get("X-Forwarded-Proto")
  204. )
  205. if r.TLS != nil || gstr.Equal(proto, "https") {
  206. scheme = "https"
  207. }
  208. return fmt.Sprintf(`%s://%s%s`, scheme, r.Host, r.URL.String())
  209. }
  210. // GetSessionId retrieves and returns session id from cookie or header.
  211. func (r *Request) GetSessionId() string {
  212. id := r.Cookie.GetSessionId()
  213. if id == "" {
  214. id = r.Header.Get(r.Server.GetSessionIdName())
  215. }
  216. return id
  217. }
  218. // GetReferer returns referer of this request.
  219. func (r *Request) GetReferer() string {
  220. return r.Header.Get("Referer")
  221. }
  222. // GetError returns the error occurs in the procedure of the request.
  223. // It returns nil if there's no error.
  224. func (r *Request) GetError() error {
  225. return r.error
  226. }
  227. // SetError sets custom error for current request.
  228. func (r *Request) SetError(err error) {
  229. r.error = err
  230. }
  231. // ReloadParam is used for modifying request parameter.
  232. // Sometimes, we want to modify request parameters through middleware, but directly modifying Request.Body
  233. // is invalid, so it clears the parsed* marks of Request to make the parameters reparsed.
  234. func (r *Request) ReloadParam() {
  235. r.parsedBody = false
  236. r.parsedForm = false
  237. r.parsedQuery = false
  238. r.bodyContent = nil
  239. }
  240. // GetHandlerResponse retrieves and returns the handler response object and its error.
  241. func (r *Request) GetHandlerResponse() interface{} {
  242. return r.handlerResponse
  243. }
  244. // GetServeHandler retrieves and returns the user defined handler used to serve this request.
  245. func (r *Request) GetServeHandler() *HandlerItemParsed {
  246. return r.serveHandler
  247. }