goyave.dev/goyave/v5@v5.0.0-rc9.0.20240517145003-d3f977d0b9f3/request.go (about)

     1  package goyave
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"net/http"
     7  	"net/url"
     8  	"strings"
     9  	"time"
    10  
    11  	"goyave.dev/goyave/v5/lang"
    12  )
    13  
    14  type (
    15  	// ExtraBodyValidationRules the key used in `Context.Extra` to
    16  	// store the body validation rules.
    17  	ExtraBodyValidationRules struct{}
    18  
    19  	// ExtraQueryValidationRules the key used in `Context.Extra` to
    20  	// store the query validation rules.
    21  	ExtraQueryValidationRules struct{}
    22  
    23  	// ExtraValidationError the key used in `Context.Extra` to
    24  	// store the body validation errors.
    25  	ExtraValidationError struct{}
    26  
    27  	// ExtraQueryValidationError the key used in `Context.Extra` to
    28  	// store the query validation errors.
    29  	ExtraQueryValidationError struct{}
    30  )
    31  
    32  // Request represents an http request received by the server.
    33  type Request struct {
    34  	httpRequest *http.Request
    35  	Now         time.Time
    36  	Data        any
    37  	User        any
    38  	Query       map[string]any
    39  	Lang        *lang.Language
    40  
    41  	// Extra can be used to store any extra information related to the request.
    42  	// For example, the JWT middleware stores the token claim in the extras.
    43  	//
    44  	// The keys must be comparable and should not be of type
    45  	// string or any other built-in type to avoid collisions.
    46  	// To avoid allocating when assigning to an `interface{}`, context keys often have
    47  	// concrete type `struct{}`. Alternatively, exported context key variables' static
    48  	// type should be a pointer or interface.
    49  	Extra       map[any]any
    50  	Route       *Route
    51  	RouteParams map[string]string
    52  	cookies     []*http.Cookie
    53  }
    54  
    55  // NewRequest create a new Request from the given raw http request.
    56  // Initializes Now with the current time and Extra with a non-nil map.
    57  func NewRequest(httpRequest *http.Request) *Request {
    58  	return &Request{
    59  		httpRequest: httpRequest,
    60  		Now:         time.Now(),
    61  		Extra:       map[any]any{},
    62  		// Route is set by the router
    63  		// Lang is set inside the language middleware
    64  		// Query is set inside the parse request middleware
    65  	}
    66  }
    67  
    68  // Request return the raw http request.
    69  // Prefer using the "goyave.Request" accessors.
    70  func (r *Request) Request() *http.Request {
    71  	return r.httpRequest
    72  }
    73  
    74  // Method specifies the HTTP method (GET, POST, PUT, etc.).
    75  func (r *Request) Method() string {
    76  	return r.httpRequest.Method
    77  }
    78  
    79  // Protocol the protocol used by this request, "HTTP/1.1" for example.
    80  func (r *Request) Protocol() string {
    81  	return r.httpRequest.Proto
    82  }
    83  
    84  // URL specifies the URL being requested.
    85  func (r *Request) URL() *url.URL {
    86  	return r.httpRequest.URL
    87  }
    88  
    89  // Header contains the request header fields either received
    90  // by the server or to be sent by the client.
    91  // Header names are case-insensitive.
    92  //
    93  // If the raw request has the following header lines,
    94  //
    95  //	Host: example.com
    96  //	accept-encoding: gzip, deflate
    97  //	Accept-Language: en-us
    98  //	fOO: Bar
    99  //	foo: two
   100  //
   101  // then the header map will look like this:
   102  //
   103  //	Header = map[string][]string{
   104  //		"Accept-Encoding": {"gzip, deflate"},
   105  //		"Accept-Language": {"en-us"},
   106  //		"Foo": {"Bar", "two"},
   107  //	}
   108  func (r *Request) Header() http.Header {
   109  	return r.httpRequest.Header
   110  }
   111  
   112  // ContentLength records the length of the associated content.
   113  // The value -1 indicates that the length is unknown.
   114  func (r *Request) ContentLength() int64 {
   115  	return r.httpRequest.ContentLength
   116  }
   117  
   118  // RemoteAddress allows to record the network address that
   119  // sent the request, usually for logging.
   120  func (r *Request) RemoteAddress() string {
   121  	return r.httpRequest.RemoteAddr
   122  }
   123  
   124  // Cookies returns the HTTP cookies sent with the request.
   125  func (r *Request) Cookies() []*http.Cookie {
   126  	if r.cookies == nil {
   127  		r.cookies = r.httpRequest.Cookies()
   128  	}
   129  	return r.cookies
   130  }
   131  
   132  // Referrer returns the referring URL, if sent in the request.
   133  func (r *Request) Referrer() string {
   134  	return r.httpRequest.Referer()
   135  }
   136  
   137  // UserAgent returns the client's User-Agent, if sent in the request.
   138  func (r *Request) UserAgent() string {
   139  	return r.httpRequest.UserAgent()
   140  }
   141  
   142  // BasicAuth returns the username and password provided in the request's
   143  // Authorization header, if the request uses HTTP Basic Authentication.
   144  func (r *Request) BasicAuth() (username, password string, ok bool) {
   145  	return r.httpRequest.BasicAuth()
   146  }
   147  
   148  // BearerToken extract the auth token from the "Authorization" header.
   149  // Only takes tokens of type "Bearer".
   150  // Returns empty string if no token found or the header is invalid.
   151  func (r *Request) BearerToken() (string, bool) {
   152  	const schema = "Bearer "
   153  	header := r.Header().Get("Authorization")
   154  	if !strings.HasPrefix(header, schema) {
   155  		return "", false
   156  	}
   157  	return strings.TrimSpace(header[len(schema):]), true
   158  }
   159  
   160  // Body the request body.
   161  // Always non-nil, but will return EOF immediately when no body is present.
   162  // The server will close the request body so handlers don't need to.
   163  func (r *Request) Body() io.ReadCloser {
   164  	return r.httpRequest.Body
   165  }
   166  
   167  // Context returns the request's context. To change the context, use `WithContext`.
   168  //
   169  // The returned context is always non-nil; it defaults to the
   170  // background context.
   171  //
   172  // The context is canceled when the client's connection closes, the request is canceled (with HTTP/2),
   173  // or when the `ServeHTTP` method returns (after the finalization step of the request lifecycle).
   174  func (r *Request) Context() context.Context {
   175  	return r.httpRequest.Context()
   176  }
   177  
   178  // WithContext creates a shallow copy of the underlying `*http.Request` with
   179  // its context changed to `ctx` then returns itself.
   180  // The provided ctx must be non-nil.
   181  func (r *Request) WithContext(ctx context.Context) *Request {
   182  	r.httpRequest = r.httpRequest.WithContext(ctx)
   183  	return r
   184  }