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