github.com/gogf/gf/v2@v2.7.4/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/gogf/gf.
     6  
     7  package ghttp
     8  
     9  import (
    10  	"fmt"
    11  	"net/http"
    12  	"strings"
    13  	"time"
    14  
    15  	"github.com/gogf/gf/v2/internal/intlog"
    16  	"github.com/gogf/gf/v2/os/gres"
    17  	"github.com/gogf/gf/v2/os/gsession"
    18  	"github.com/gogf/gf/v2/os/gtime"
    19  	"github.com/gogf/gf/v2/os/gview"
    20  	"github.com/gogf/gf/v2/text/gregex"
    21  	"github.com/gogf/gf/v2/text/gstr"
    22  	"github.com/gogf/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  *gtime.Time       // Request starting time in milliseconds.
    34  	LeaveTime  *gtime.Time       // 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.Now(),
    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  //
   121  // Deprecated: will be removed in the future, please use third-party websocket library instead.
   122  func (r *Request) WebSocket() (*WebSocket, error) {
   123  	if conn, err := wsUpGrader.Upgrade(r.Response.Writer, r.Request, nil); err == nil {
   124  		return &WebSocket{
   125  			conn,
   126  		}, nil
   127  	} else {
   128  		return nil, err
   129  	}
   130  }
   131  
   132  // Exit exits executing of current HTTP handler.
   133  func (r *Request) Exit() {
   134  	panic(exceptionExit)
   135  }
   136  
   137  // ExitAll exits executing of current and following HTTP handlers.
   138  func (r *Request) ExitAll() {
   139  	r.exitAll = true
   140  	panic(exceptionExitAll)
   141  }
   142  
   143  // ExitHook exits executing of current and following HTTP HOOK handlers.
   144  func (r *Request) ExitHook() {
   145  	panic(exceptionExitHook)
   146  }
   147  
   148  // IsExited checks and returns whether current request is exited.
   149  func (r *Request) IsExited() bool {
   150  	return r.exitAll
   151  }
   152  
   153  // GetHeader retrieves and returns the header value with given `key`.
   154  func (r *Request) GetHeader(key string) string {
   155  	return r.Header.Get(key)
   156  }
   157  
   158  // GetHost returns current request host name, which might be a domain or an IP without port.
   159  func (r *Request) GetHost() string {
   160  	if len(r.parsedHost) == 0 {
   161  		array, _ := gregex.MatchString(`(.+):(\d+)`, r.Host)
   162  		if len(array) > 1 {
   163  			r.parsedHost = array[1]
   164  		} else {
   165  			r.parsedHost = r.Host
   166  		}
   167  	}
   168  	return r.parsedHost
   169  }
   170  
   171  // IsFileRequest checks and returns whether current request is serving file.
   172  func (r *Request) IsFileRequest() bool {
   173  	return r.isFileRequest
   174  }
   175  
   176  // IsAjaxRequest checks and returns whether current request is an AJAX request.
   177  func (r *Request) IsAjaxRequest() bool {
   178  	return strings.EqualFold(r.Header.Get("X-Requested-With"), "XMLHttpRequest")
   179  }
   180  
   181  // GetClientIp returns the client ip of this request without port.
   182  // Note that this ip address might be modified by client header.
   183  func (r *Request) GetClientIp() string {
   184  	if r.clientIp != "" {
   185  		return r.clientIp
   186  	}
   187  	realIps := r.Header.Get("X-Forwarded-For")
   188  	if realIps != "" && len(realIps) != 0 && !strings.EqualFold("unknown", realIps) {
   189  		ipArray := strings.Split(realIps, ",")
   190  		r.clientIp = ipArray[0]
   191  	}
   192  	if r.clientIp == "" {
   193  		r.clientIp = r.Header.Get("Proxy-Client-IP")
   194  	}
   195  	if r.clientIp == "" {
   196  		r.clientIp = r.Header.Get("WL-Proxy-Client-IP")
   197  	}
   198  	if r.clientIp == "" {
   199  		r.clientIp = r.Header.Get("HTTP_CLIENT_IP")
   200  	}
   201  	if r.clientIp == "" {
   202  		r.clientIp = r.Header.Get("HTTP_X_FORWARDED_FOR")
   203  	}
   204  	if r.clientIp == "" {
   205  		r.clientIp = r.Header.Get("X-Real-IP")
   206  	}
   207  	if r.clientIp == "" {
   208  		r.clientIp = r.GetRemoteIp()
   209  	}
   210  	return r.clientIp
   211  }
   212  
   213  // GetRemoteIp returns the ip from RemoteAddr.
   214  func (r *Request) GetRemoteIp() string {
   215  	array, _ := gregex.MatchString(`(.+):(\d+)`, r.RemoteAddr)
   216  	if len(array) > 1 {
   217  		return strings.Trim(array[1], "[]")
   218  	}
   219  	return r.RemoteAddr
   220  }
   221  
   222  // GetSchema returns the schema of this request.
   223  func (r *Request) GetSchema() string {
   224  	var (
   225  		scheme = "http"
   226  		proto  = r.Header.Get("X-Forwarded-Proto")
   227  	)
   228  	if r.TLS != nil || gstr.Equal(proto, "https") {
   229  		scheme = "https"
   230  	}
   231  	return scheme
   232  }
   233  
   234  // GetUrl returns current URL of this request.
   235  func (r *Request) GetUrl() string {
   236  	var (
   237  		scheme = "http"
   238  		proto  = r.Header.Get("X-Forwarded-Proto")
   239  	)
   240  
   241  	if r.TLS != nil || gstr.Equal(proto, "https") {
   242  		scheme = "https"
   243  	}
   244  	return fmt.Sprintf(`%s://%s%s`, scheme, r.Host, r.URL.String())
   245  }
   246  
   247  // GetSessionId retrieves and returns session id from cookie or header.
   248  func (r *Request) GetSessionId() string {
   249  	id := r.Cookie.GetSessionId()
   250  	if id == "" {
   251  		id = r.Header.Get(r.Server.GetSessionIdName())
   252  	}
   253  	return id
   254  }
   255  
   256  // GetReferer returns referer of this request.
   257  func (r *Request) GetReferer() string {
   258  	return r.Header.Get("Referer")
   259  }
   260  
   261  // GetError returns the error occurs in the procedure of the request.
   262  // It returns nil if there's no error.
   263  func (r *Request) GetError() error {
   264  	return r.error
   265  }
   266  
   267  // SetError sets custom error for current request.
   268  func (r *Request) SetError(err error) {
   269  	r.error = err
   270  }
   271  
   272  // ReloadParam is used for modifying request parameter.
   273  // Sometimes, we want to modify request parameters through middleware, but directly modifying Request.Body
   274  // is invalid, so it clears the parsed* marks of Request to make the parameters reparsed.
   275  func (r *Request) ReloadParam() {
   276  	r.parsedBody = false
   277  	r.parsedForm = false
   278  	r.parsedQuery = false
   279  	r.bodyContent = nil
   280  }
   281  
   282  // GetHandlerResponse retrieves and returns the handler response object and its error.
   283  func (r *Request) GetHandlerResponse() interface{} {
   284  	return r.handlerResponse
   285  }
   286  
   287  // GetServeHandler retrieves and returns the user defined handler used to serve this request.
   288  func (r *Request) GetServeHandler() *HandlerItemParsed {
   289  	return r.serveHandler
   290  }