source: code/trunk/vendor/github.com/valyala/fasthttp/cookie.go@ 145

Last change on this file since 145 was 145, checked in by Izuru Yakumo, 22 months ago

Updated the Makefile and vendored depedencies

Signed-off-by: Izuru Yakumo <yakumo.izuru@…>

File size: 13.5 KB
Line 
1package fasthttp
2
3import (
4 "bytes"
5 "errors"
6 "io"
7 "sync"
8 "time"
9)
10
11var zeroTime time.Time
12
13var (
14 // CookieExpireDelete may be set on Cookie.Expire for expiring the given cookie.
15 CookieExpireDelete = time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
16
17 // CookieExpireUnlimited indicates that the cookie doesn't expire.
18 CookieExpireUnlimited = zeroTime
19)
20
21// CookieSameSite is an enum for the mode in which the SameSite flag should be set for the given cookie.
22// See https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00 for details.
23type CookieSameSite int
24
25const (
26 // CookieSameSiteDisabled removes the SameSite flag
27 CookieSameSiteDisabled CookieSameSite = iota
28 // CookieSameSiteDefaultMode sets the SameSite flag
29 CookieSameSiteDefaultMode
30 // CookieSameSiteLaxMode sets the SameSite flag with the "Lax" parameter
31 CookieSameSiteLaxMode
32 // CookieSameSiteStrictMode sets the SameSite flag with the "Strict" parameter
33 CookieSameSiteStrictMode
34 // CookieSameSiteNoneMode sets the SameSite flag with the "None" parameter
35 // see https://tools.ietf.org/html/draft-west-cookie-incrementalism-00
36 CookieSameSiteNoneMode
37)
38
39// AcquireCookie returns an empty Cookie object from the pool.
40//
41// The returned object may be returned back to the pool with ReleaseCookie.
42// This allows reducing GC load.
43func AcquireCookie() *Cookie {
44 return cookiePool.Get().(*Cookie)
45}
46
47// ReleaseCookie returns the Cookie object acquired with AcquireCookie back
48// to the pool.
49//
50// Do not access released Cookie object, otherwise data races may occur.
51func ReleaseCookie(c *Cookie) {
52 c.Reset()
53 cookiePool.Put(c)
54}
55
56var cookiePool = &sync.Pool{
57 New: func() interface{} {
58 return &Cookie{}
59 },
60}
61
62// Cookie represents HTTP response cookie.
63//
64// Do not copy Cookie objects. Create new object and use CopyTo instead.
65//
66// Cookie instance MUST NOT be used from concurrently running goroutines.
67type Cookie struct {
68 noCopy noCopy //nolint:unused,structcheck
69
70 key []byte
71 value []byte
72 expire time.Time
73 maxAge int
74 domain []byte
75 path []byte
76
77 httpOnly bool
78 secure bool
79 sameSite CookieSameSite
80
81 bufKV argsKV
82 buf []byte
83}
84
85// CopyTo copies src cookie to c.
86func (c *Cookie) CopyTo(src *Cookie) {
87 c.Reset()
88 c.key = append(c.key, src.key...)
89 c.value = append(c.value, src.value...)
90 c.expire = src.expire
91 c.maxAge = src.maxAge
92 c.domain = append(c.domain, src.domain...)
93 c.path = append(c.path, src.path...)
94 c.httpOnly = src.httpOnly
95 c.secure = src.secure
96 c.sameSite = src.sameSite
97}
98
99// HTTPOnly returns true if the cookie is http only.
100func (c *Cookie) HTTPOnly() bool {
101 return c.httpOnly
102}
103
104// SetHTTPOnly sets cookie's httpOnly flag to the given value.
105func (c *Cookie) SetHTTPOnly(httpOnly bool) {
106 c.httpOnly = httpOnly
107}
108
109// Secure returns true if the cookie is secure.
110func (c *Cookie) Secure() bool {
111 return c.secure
112}
113
114// SetSecure sets cookie's secure flag to the given value.
115func (c *Cookie) SetSecure(secure bool) {
116 c.secure = secure
117}
118
119// SameSite returns the SameSite mode.
120func (c *Cookie) SameSite() CookieSameSite {
121 return c.sameSite
122}
123
124// SetSameSite sets the cookie's SameSite flag to the given value.
125// set value CookieSameSiteNoneMode will set Secure to true also to avoid browser rejection
126func (c *Cookie) SetSameSite(mode CookieSameSite) {
127 c.sameSite = mode
128 if mode == CookieSameSiteNoneMode {
129 c.SetSecure(true)
130 }
131}
132
133// Path returns cookie path.
134func (c *Cookie) Path() []byte {
135 return c.path
136}
137
138// SetPath sets cookie path.
139func (c *Cookie) SetPath(path string) {
140 c.buf = append(c.buf[:0], path...)
141 c.path = normalizePath(c.path, c.buf)
142}
143
144// SetPathBytes sets cookie path.
145func (c *Cookie) SetPathBytes(path []byte) {
146 c.buf = append(c.buf[:0], path...)
147 c.path = normalizePath(c.path, c.buf)
148}
149
150// Domain returns cookie domain.
151//
152// The returned value is valid until the Cookie reused or released (ReleaseCookie).
153// Do not store references to the returned value. Make copies instead.
154func (c *Cookie) Domain() []byte {
155 return c.domain
156}
157
158// SetDomain sets cookie domain.
159func (c *Cookie) SetDomain(domain string) {
160 c.domain = append(c.domain[:0], domain...)
161}
162
163// SetDomainBytes sets cookie domain.
164func (c *Cookie) SetDomainBytes(domain []byte) {
165 c.domain = append(c.domain[:0], domain...)
166}
167
168// MaxAge returns the seconds until the cookie is meant to expire or 0
169// if no max age.
170func (c *Cookie) MaxAge() int {
171 return c.maxAge
172}
173
174// SetMaxAge sets cookie expiration time based on seconds. This takes precedence
175// over any absolute expiry set on the cookie
176//
177// Set max age to 0 to unset
178func (c *Cookie) SetMaxAge(seconds int) {
179 c.maxAge = seconds
180}
181
182// Expire returns cookie expiration time.
183//
184// CookieExpireUnlimited is returned if cookie doesn't expire
185func (c *Cookie) Expire() time.Time {
186 expire := c.expire
187 if expire.IsZero() {
188 expire = CookieExpireUnlimited
189 }
190 return expire
191}
192
193// SetExpire sets cookie expiration time.
194//
195// Set expiration time to CookieExpireDelete for expiring (deleting)
196// the cookie on the client.
197//
198// By default cookie lifetime is limited by browser session.
199func (c *Cookie) SetExpire(expire time.Time) {
200 c.expire = expire
201}
202
203// Value returns cookie value.
204//
205// The returned value is valid until the Cookie reused or released (ReleaseCookie).
206// Do not store references to the returned value. Make copies instead.
207func (c *Cookie) Value() []byte {
208 return c.value
209}
210
211// SetValue sets cookie value.
212func (c *Cookie) SetValue(value string) {
213 c.value = append(c.value[:0], value...)
214}
215
216// SetValueBytes sets cookie value.
217func (c *Cookie) SetValueBytes(value []byte) {
218 c.value = append(c.value[:0], value...)
219}
220
221// Key returns cookie name.
222//
223// The returned value is valid until the Cookie reused or released (ReleaseCookie).
224// Do not store references to the returned value. Make copies instead.
225func (c *Cookie) Key() []byte {
226 return c.key
227}
228
229// SetKey sets cookie name.
230func (c *Cookie) SetKey(key string) {
231 c.key = append(c.key[:0], key...)
232}
233
234// SetKeyBytes sets cookie name.
235func (c *Cookie) SetKeyBytes(key []byte) {
236 c.key = append(c.key[:0], key...)
237}
238
239// Reset clears the cookie.
240func (c *Cookie) Reset() {
241 c.key = c.key[:0]
242 c.value = c.value[:0]
243 c.expire = zeroTime
244 c.maxAge = 0
245 c.domain = c.domain[:0]
246 c.path = c.path[:0]
247 c.httpOnly = false
248 c.secure = false
249 c.sameSite = CookieSameSiteDisabled
250}
251
252// AppendBytes appends cookie representation to dst and returns
253// the extended dst.
254func (c *Cookie) AppendBytes(dst []byte) []byte {
255 if len(c.key) > 0 {
256 dst = append(dst, c.key...)
257 dst = append(dst, '=')
258 }
259 dst = append(dst, c.value...)
260
261 if c.maxAge > 0 {
262 dst = append(dst, ';', ' ')
263 dst = append(dst, strCookieMaxAge...)
264 dst = append(dst, '=')
265 dst = AppendUint(dst, c.maxAge)
266 } else if !c.expire.IsZero() {
267 c.bufKV.value = AppendHTTPDate(c.bufKV.value[:0], c.expire)
268 dst = append(dst, ';', ' ')
269 dst = append(dst, strCookieExpires...)
270 dst = append(dst, '=')
271 dst = append(dst, c.bufKV.value...)
272 }
273 if len(c.domain) > 0 {
274 dst = appendCookiePart(dst, strCookieDomain, c.domain)
275 }
276 if len(c.path) > 0 {
277 dst = appendCookiePart(dst, strCookiePath, c.path)
278 }
279 if c.httpOnly {
280 dst = append(dst, ';', ' ')
281 dst = append(dst, strCookieHTTPOnly...)
282 }
283 if c.secure {
284 dst = append(dst, ';', ' ')
285 dst = append(dst, strCookieSecure...)
286 }
287 switch c.sameSite {
288 case CookieSameSiteDefaultMode:
289 dst = append(dst, ';', ' ')
290 dst = append(dst, strCookieSameSite...)
291 case CookieSameSiteLaxMode:
292 dst = append(dst, ';', ' ')
293 dst = append(dst, strCookieSameSite...)
294 dst = append(dst, '=')
295 dst = append(dst, strCookieSameSiteLax...)
296 case CookieSameSiteStrictMode:
297 dst = append(dst, ';', ' ')
298 dst = append(dst, strCookieSameSite...)
299 dst = append(dst, '=')
300 dst = append(dst, strCookieSameSiteStrict...)
301 case CookieSameSiteNoneMode:
302 dst = append(dst, ';', ' ')
303 dst = append(dst, strCookieSameSite...)
304 dst = append(dst, '=')
305 dst = append(dst, strCookieSameSiteNone...)
306 }
307 return dst
308}
309
310// Cookie returns cookie representation.
311//
312// The returned value is valid until the Cookie reused or released (ReleaseCookie).
313// Do not store references to the returned value. Make copies instead.
314func (c *Cookie) Cookie() []byte {
315 c.buf = c.AppendBytes(c.buf[:0])
316 return c.buf
317}
318
319// String returns cookie representation.
320func (c *Cookie) String() string {
321 return string(c.Cookie())
322}
323
324// WriteTo writes cookie representation to w.
325//
326// WriteTo implements io.WriterTo interface.
327func (c *Cookie) WriteTo(w io.Writer) (int64, error) {
328 n, err := w.Write(c.Cookie())
329 return int64(n), err
330}
331
332var errNoCookies = errors.New("no cookies found")
333
334// Parse parses Set-Cookie header.
335func (c *Cookie) Parse(src string) error {
336 c.buf = append(c.buf[:0], src...)
337 return c.ParseBytes(c.buf)
338}
339
340// ParseBytes parses Set-Cookie header.
341func (c *Cookie) ParseBytes(src []byte) error {
342 c.Reset()
343
344 var s cookieScanner
345 s.b = src
346
347 kv := &c.bufKV
348 if !s.next(kv) {
349 return errNoCookies
350 }
351
352 c.key = append(c.key, kv.key...)
353 c.value = append(c.value, kv.value...)
354
355 for s.next(kv) {
356 if len(kv.key) != 0 {
357 // Case insensitive switch on first char
358 switch kv.key[0] | 0x20 {
359 case 'm':
360 if caseInsensitiveCompare(strCookieMaxAge, kv.key) {
361 maxAge, err := ParseUint(kv.value)
362 if err != nil {
363 return err
364 }
365 c.maxAge = maxAge
366 }
367
368 case 'e': // "expires"
369 if caseInsensitiveCompare(strCookieExpires, kv.key) {
370 v := b2s(kv.value)
371 // Try the same two formats as net/http
372 // See: https://github.com/golang/go/blob/00379be17e63a5b75b3237819392d2dc3b313a27/src/net/http/cookie.go#L133-L135
373 exptime, err := time.ParseInLocation(time.RFC1123, v, time.UTC)
374 if err != nil {
375 exptime, err = time.Parse("Mon, 02-Jan-2006 15:04:05 MST", v)
376 if err != nil {
377 return err
378 }
379 }
380 c.expire = exptime
381 }
382
383 case 'd': // "domain"
384 if caseInsensitiveCompare(strCookieDomain, kv.key) {
385 c.domain = append(c.domain, kv.value...)
386 }
387
388 case 'p': // "path"
389 if caseInsensitiveCompare(strCookiePath, kv.key) {
390 c.path = append(c.path, kv.value...)
391 }
392
393 case 's': // "samesite"
394 if caseInsensitiveCompare(strCookieSameSite, kv.key) {
395 if len(kv.value) > 0 {
396 // Case insensitive switch on first char
397 switch kv.value[0] | 0x20 {
398 case 'l': // "lax"
399 if caseInsensitiveCompare(strCookieSameSiteLax, kv.value) {
400 c.sameSite = CookieSameSiteLaxMode
401 }
402 case 's': // "strict"
403 if caseInsensitiveCompare(strCookieSameSiteStrict, kv.value) {
404 c.sameSite = CookieSameSiteStrictMode
405 }
406 case 'n': // "none"
407 if caseInsensitiveCompare(strCookieSameSiteNone, kv.value) {
408 c.sameSite = CookieSameSiteNoneMode
409 }
410 }
411 }
412 }
413 }
414
415 } else if len(kv.value) != 0 {
416 // Case insensitive switch on first char
417 switch kv.value[0] | 0x20 {
418 case 'h': // "httponly"
419 if caseInsensitiveCompare(strCookieHTTPOnly, kv.value) {
420 c.httpOnly = true
421 }
422
423 case 's': // "secure"
424 if caseInsensitiveCompare(strCookieSecure, kv.value) {
425 c.secure = true
426 } else if caseInsensitiveCompare(strCookieSameSite, kv.value) {
427 c.sameSite = CookieSameSiteDefaultMode
428 }
429 }
430 } // else empty or no match
431 }
432 return nil
433}
434
435func appendCookiePart(dst, key, value []byte) []byte {
436 dst = append(dst, ';', ' ')
437 dst = append(dst, key...)
438 dst = append(dst, '=')
439 return append(dst, value...)
440}
441
442func getCookieKey(dst, src []byte) []byte {
443 n := bytes.IndexByte(src, '=')
444 if n >= 0 {
445 src = src[:n]
446 }
447 return decodeCookieArg(dst, src, false)
448}
449
450func appendRequestCookieBytes(dst []byte, cookies []argsKV) []byte {
451 for i, n := 0, len(cookies); i < n; i++ {
452 kv := &cookies[i]
453 if len(kv.key) > 0 {
454 dst = append(dst, kv.key...)
455 dst = append(dst, '=')
456 }
457 dst = append(dst, kv.value...)
458 if i+1 < n {
459 dst = append(dst, ';', ' ')
460 }
461 }
462 return dst
463}
464
465// For Response we can not use the above function as response cookies
466// already contain the key= in the value.
467func appendResponseCookieBytes(dst []byte, cookies []argsKV) []byte {
468 for i, n := 0, len(cookies); i < n; i++ {
469 kv := &cookies[i]
470 dst = append(dst, kv.value...)
471 if i+1 < n {
472 dst = append(dst, ';', ' ')
473 }
474 }
475 return dst
476}
477
478func parseRequestCookies(cookies []argsKV, src []byte) []argsKV {
479 var s cookieScanner
480 s.b = src
481 var kv *argsKV
482 cookies, kv = allocArg(cookies)
483 for s.next(kv) {
484 if len(kv.key) > 0 || len(kv.value) > 0 {
485 cookies, kv = allocArg(cookies)
486 }
487 }
488 return releaseArg(cookies)
489}
490
491type cookieScanner struct {
492 b []byte
493}
494
495func (s *cookieScanner) next(kv *argsKV) bool {
496 b := s.b
497 if len(b) == 0 {
498 return false
499 }
500
501 isKey := true
502 k := 0
503 for i, c := range b {
504 switch c {
505 case '=':
506 if isKey {
507 isKey = false
508 kv.key = decodeCookieArg(kv.key, b[:i], false)
509 k = i + 1
510 }
511 case ';':
512 if isKey {
513 kv.key = kv.key[:0]
514 }
515 kv.value = decodeCookieArg(kv.value, b[k:i], true)
516 s.b = b[i+1:]
517 return true
518 }
519 }
520
521 if isKey {
522 kv.key = kv.key[:0]
523 }
524 kv.value = decodeCookieArg(kv.value, b[k:], true)
525 s.b = b[len(b):]
526 return true
527}
528
529func decodeCookieArg(dst, src []byte, skipQuotes bool) []byte {
530 for len(src) > 0 && src[0] == ' ' {
531 src = src[1:]
532 }
533 for len(src) > 0 && src[len(src)-1] == ' ' {
534 src = src[:len(src)-1]
535 }
536 if skipQuotes {
537 if len(src) > 1 && src[0] == '"' && src[len(src)-1] == '"' {
538 src = src[1 : len(src)-1]
539 }
540 }
541 return append(dst[:0], src...)
542}
543
544// caseInsensitiveCompare does a case insensitive equality comparison of
545// two []byte. Assumes only letters need to be matched.
546func caseInsensitiveCompare(a, b []byte) bool {
547 if len(a) != len(b) {
548 return false
549 }
550 for i := 0; i < len(a); i++ {
551 if a[i]|0x20 != b[i]|0x20 {
552 return false
553 }
554 }
555 return true
556}
Note: See TracBrowser for help on using the repository browser.