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