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  }