gf/net/ghttp/ghttp_request.go

267 lines
9.4 KiB
Go
Raw Normal View History

2021-01-17 21:46:25 +08:00
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
2017-12-29 16:03:30 +08:00
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
2017-12-31 18:19:58 +08:00
2017-11-23 10:21:28 +08:00
package ghttp
import (
"context"
"fmt"
"net/http"
"strings"
"time"
2021-10-11 21:41:56 +08:00
"github.com/gogf/gf/v2/internal/intlog"
"github.com/gogf/gf/v2/os/gres"
"github.com/gogf/gf/v2/os/gsession"
"github.com/gogf/gf/v2/os/gtime"
2021-11-13 23:23:55 +08:00
"github.com/gogf/gf/v2/os/gview"
2021-10-11 21:41:56 +08:00
"github.com/gogf/gf/v2/text/gregex"
2021-11-13 23:23:55 +08:00
"github.com/gogf/gf/v2/util/guid"
2017-11-23 10:21:28 +08:00
)
// Request is the context object for a request.
type Request struct {
2019-06-19 09:06:52 +08:00
*http.Request
2021-10-13 22:28:49 +08:00
Server *Server // Server.
Cookie *Cookie // Cookie.
Session *gsession.Session // Session.
Response *Response // Corresponding Response of this request.
Router *Router // Matched Router for this request. Note that it's not available in HOOK handler.
EnterTime int64 // Request starting time in microseconds.
LeaveTime int64 // Request ending time in microseconds.
Middleware *middleware // Middleware manager.
StaticFile *staticFile // Static file object for static file serving.
// =================================================================================================================
// Private attributes for internal usage purpose.
// =================================================================================================================
context context.Context // Custom context for internal usage purpose.
handlers []*handlerParsedItem // All matched handlers containing handler, hook and middleware for this request.
2021-10-13 22:28:49 +08:00
handlerResponse handlerResponse // Handler response object and its error value for Request/Response handler.
hasHookHandler bool // A bool marking whether there's hook handler in the handlers for performance purpose.
hasServeHandler bool // A bool marking whether there's serving handler in the handlers for performance purpose.
parsedQuery bool // A bool marking whether the GET parameters parsed.
parsedBody bool // A bool marking whether the request body parsed.
parsedForm bool // A bool marking whether request Form parsed for HTTP method PUT, POST, PATCH.
paramsMap map[string]interface{} // Custom parameters map.
routerMap map[string]string // Router parameters map, which might be nil if there're no router parameters.
queryMap map[string]interface{} // Query parameters map, which is nil if there's no query string.
2019-12-01 14:07:36 +08:00
formMap map[string]interface{} // Form parameters map, which is nil if there's no form data from client.
bodyMap map[string]interface{} // Body parameters map, which might be nil if there're no body content.
error error // Current executing error of the request.
exit bool // A bool marking whether current request is exited.
parsedHost string // The parsed host name for current host used by GetHost function.
clientIp string // The parsed client ip for current host used by GetClientIp function.
bodyContent []byte // Request body content.
isFileRequest bool // A bool marking whether current request is file serving.
2019-12-10 21:14:15 +08:00
viewObject *gview.View // Custom template view engine object for this response.
viewParams gview.Params // Custom template view variables for this response.
2021-10-13 22:28:49 +08:00
originUrlPath string // Original URL path that passed from client.
}
type handlerResponse struct {
Object interface{}
Error error
}
// staticFile is the file struct for static file service.
type staticFile struct {
File *gres.File // Resource file object.
Path string // File path.
IsDir bool // Is directory.
}
// newRequest creates and returns a new request object.
2018-04-11 16:06:45 +08:00
func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request {
2019-06-19 09:06:52 +08:00
request := &Request{
2021-10-13 22:28:49 +08:00
Server: s,
Request: r,
Response: newResponse(s, w),
EnterTime: gtime.TimestampMilli(),
originUrlPath: r.URL.Path,
2019-06-19 09:06:52 +08:00
}
request.Cookie = GetCookie(request)
request.Session = s.sessionManager.New(
r.Context(),
request.GetSessionId(),
)
request.Response.Request = request
2021-01-19 19:18:39 +08:00
request.Middleware = &middleware{
request: request,
}
// Custom session id creating function.
err := request.Session.SetIdFunc(func(ttl time.Duration) string {
var (
address = request.RemoteAddr
2020-05-27 11:26:05 +08:00
header = fmt.Sprintf("%v", request.Header)
)
intlog.Print(r.Context(), address, header)
2020-05-27 11:26:05 +08:00
return guid.S([]byte(address), []byte(header))
})
if err != nil {
panic(err)
}
2021-10-13 22:28:49 +08:00
// Remove char '/' in the tail of URI.
if request.URL.Path != "/" {
for len(request.URL.Path) > 0 && request.URL.Path[len(request.URL.Path)-1] == '/' {
request.URL.Path = request.URL.Path[:len(request.URL.Path)-1]
}
}
// Default URI value if it's empty.
if request.URL.Path == "" {
request.URL.Path = "/"
}
2019-06-19 09:06:52 +08:00
return request
2018-04-11 16:06:45 +08:00
}
// WebSocket upgrades current request as a websocket request.
// It returns a new WebSocket object if success, or the error if failure.
// Note that the request should be a websocket request, or it will surely fail upgrading.
func (r *Request) WebSocket() (*WebSocket, error) {
if conn, err := wsUpGrader.Upgrade(r.Response.Writer, r.Request, nil); err == nil {
2019-06-19 09:06:52 +08:00
return &WebSocket{
conn,
}, nil
} else {
return nil, err
}
}
// Exit exits executing of current HTTP handler.
2018-04-16 16:50:24 +08:00
func (r *Request) Exit() {
2020-12-14 13:26:48 +08:00
panic(exceptionExit)
}
// ExitAll exits executing of current and following HTTP handlers.
func (r *Request) ExitAll() {
2019-06-19 09:06:52 +08:00
r.exit = true
2020-12-14 13:26:48 +08:00
panic(exceptionExitAll)
2018-04-16 16:50:24 +08:00
}
// ExitHook exits executing of current and following HTTP HOOK handlers.
func (r *Request) ExitHook() {
2020-12-14 13:26:48 +08:00
panic(exceptionExitHook)
}
// IsExited checks and returns whether current request is exited.
2018-04-16 16:50:26 +08:00
func (r *Request) IsExited() bool {
2019-06-19 09:06:52 +08:00
return r.exit
2018-04-16 16:50:24 +08:00
}
// GetHeader retrieves and returns the header value with given `key`.
func (r *Request) GetHeader(key string) string {
return r.Header.Get(key)
}
// GetHost returns current request host name, which might be a domain or an IP without port.
2018-04-25 17:11:34 +08:00
func (r *Request) GetHost() string {
2019-06-19 09:06:52 +08:00
if len(r.parsedHost) == 0 {
array, _ := gregex.MatchString(`(.+):(\d+)`, r.Host)
if len(array) > 1 {
r.parsedHost = array[1]
} else {
r.parsedHost = r.Host
}
}
return r.parsedHost
2018-04-25 17:11:34 +08:00
}
// IsFileRequest checks and returns whether current request is serving file.
func (r *Request) IsFileRequest() bool {
2019-06-19 09:06:52 +08:00
return r.isFileRequest
}
// IsAjaxRequest checks and returns whether current request is an AJAX request.
func (r *Request) IsAjaxRequest() bool {
2019-06-19 09:06:52 +08:00
return strings.EqualFold(r.Header.Get("X-Requested-With"), "XMLHttpRequest")
2018-08-22 19:56:01 +08:00
}
// GetClientIp returns the client ip of this request without port.
// Note that this ip address might be modified by client header.
2018-04-20 15:43:02 +08:00
func (r *Request) GetClientIp() string {
2019-06-19 09:06:52 +08:00
if len(r.clientIp) == 0 {
realIps := r.Header.Get("X-Forwarded-For")
if realIps != "" && len(realIps) != 0 && !strings.EqualFold("unknown", realIps) {
ipArray := strings.Split(realIps, ",")
2020-06-11 09:49:16 +08:00
r.clientIp = ipArray[0]
2020-06-10 10:44:41 +08:00
}
if r.clientIp == "" || strings.EqualFold("unknown", realIps) {
2020-06-11 09:49:16 +08:00
r.clientIp = r.Header.Get("Proxy-Client-IP")
2020-06-10 10:44:41 +08:00
}
if r.clientIp == "" || strings.EqualFold("unknown", realIps) {
2020-06-11 09:49:16 +08:00
r.clientIp = r.Header.Get("WL-Proxy-Client-IP")
2020-06-10 10:44:41 +08:00
}
if r.clientIp == "" || strings.EqualFold("unknown", realIps) {
2020-06-11 09:49:16 +08:00
r.clientIp = r.Header.Get("HTTP_CLIENT_IP")
2020-06-10 10:44:41 +08:00
}
if r.clientIp == "" || strings.EqualFold("unknown", realIps) {
2020-06-11 09:49:16 +08:00
r.clientIp = r.Header.Get("HTTP_X_FORWARDED_FOR")
2020-06-10 10:44:41 +08:00
}
if r.clientIp == "" || strings.EqualFold("unknown", realIps) {
2020-06-11 09:49:16 +08:00
r.clientIp = r.Header.Get("X-Real-IP")
2020-06-10 10:44:41 +08:00
}
if r.clientIp == "" || strings.EqualFold("unknown", realIps) {
r.clientIp = r.GetRemoteIp()
2020-06-10 10:44:41 +08:00
}
}
2020-06-11 09:49:16 +08:00
return r.clientIp
2020-06-10 10:44:41 +08:00
}
// GetRemoteIp returns the ip from RemoteAddr.
func (r *Request) GetRemoteIp() string {
array, _ := gregex.MatchString(`(.+):(\d+)`, r.RemoteAddr)
if len(array) > 1 {
2021-12-03 16:07:04 +08:00
return strings.Trim(array[1], "[]")
}
return r.RemoteAddr
}
// GetUrl returns current URL of this request.
func (r *Request) GetUrl() string {
scheme := "http"
if r.TLS != nil {
scheme = "https"
}
return fmt.Sprintf(`%s://%s%s`, scheme, r.Host, r.URL.String())
}
// GetSessionId retrieves and returns session id from cookie or header.
func (r *Request) GetSessionId() string {
id := r.Cookie.GetSessionId()
if id == "" {
id = r.Header.Get(r.Server.GetSessionIdName())
}
return id
}
// GetReferer returns referer of this request.
func (r *Request) GetReferer() string {
2019-06-19 09:06:52 +08:00
return r.Header.Get("Referer")
}
// GetError returns the error occurs in the procedure of the request.
// It returns nil if there's no error.
func (r *Request) GetError() error {
return r.error
}
// ReloadParam is used for modifying request parameter.
// Sometimes, we want to modify request parameters through middleware, but directly modifying Request.Body
// is invalid, so it clears the parsed* marks to make the parameters re-parsed.
func (r *Request) ReloadParam() {
r.parsedBody = false
r.parsedForm = false
r.parsedQuery = false
r.bodyContent = nil
}
// GetHandlerResponse retrieves and returns the handler response object and its error.
func (r *Request) GetHandlerResponse() (res interface{}, err error) {
return r.handlerResponse.Object, r.handlerResponse.Error
}