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