package fasthttp
import (
"bytes"
"errors"
"io"
"sync"
"time"
)
var zeroTime time .Time
var (
CookieExpireDelete = time .Date (2009 , time .November , 10 , 23 , 0 , 0 , 0 , time .UTC )
CookieExpireUnlimited = zeroTime
)
type CookieSameSite int
const (
CookieSameSiteDisabled CookieSameSite = iota
CookieSameSiteDefaultMode
CookieSameSiteLaxMode
CookieSameSiteStrictMode
CookieSameSiteNoneMode
)
func AcquireCookie () *Cookie {
return cookiePool .Get ().(*Cookie )
}
func ReleaseCookie (c *Cookie ) {
c .Reset ()
cookiePool .Put (c )
}
var cookiePool = &sync .Pool {
New : func () interface {} {
return &Cookie {}
},
}
type Cookie struct {
noCopy noCopy
key []byte
value []byte
expire time .Time
maxAge int
domain []byte
path []byte
httpOnly bool
secure bool
sameSite CookieSameSite
bufKV argsKV
buf []byte
}
func (c *Cookie ) CopyTo (src *Cookie ) {
c .Reset ()
c .key = append (c .key , src .key ...)
c .value = append (c .value , src .value ...)
c .expire = src .expire
c .maxAge = src .maxAge
c .domain = append (c .domain , src .domain ...)
c .path = append (c .path , src .path ...)
c .httpOnly = src .httpOnly
c .secure = src .secure
c .sameSite = src .sameSite
}
func (c *Cookie ) HTTPOnly () bool {
return c .httpOnly
}
func (c *Cookie ) SetHTTPOnly (httpOnly bool ) {
c .httpOnly = httpOnly
}
func (c *Cookie ) Secure () bool {
return c .secure
}
func (c *Cookie ) SetSecure (secure bool ) {
c .secure = secure
}
func (c *Cookie ) SameSite () CookieSameSite {
return c .sameSite
}
func (c *Cookie ) SetSameSite (mode CookieSameSite ) {
c .sameSite = mode
if mode == CookieSameSiteNoneMode {
c .SetSecure (true )
}
}
func (c *Cookie ) Path () []byte {
return c .path
}
func (c *Cookie ) SetPath (path string ) {
c .buf = append (c .buf [:0 ], path ...)
c .path = normalizePath (c .path , c .buf )
}
func (c *Cookie ) SetPathBytes (path []byte ) {
c .buf = append (c .buf [:0 ], path ...)
c .path = normalizePath (c .path , c .buf )
}
func (c *Cookie ) Domain () []byte {
return c .domain
}
func (c *Cookie ) SetDomain (domain string ) {
c .domain = append (c .domain [:0 ], domain ...)
}
func (c *Cookie ) SetDomainBytes (domain []byte ) {
c .domain = append (c .domain [:0 ], domain ...)
}
func (c *Cookie ) MaxAge () int {
return c .maxAge
}
func (c *Cookie ) SetMaxAge (seconds int ) {
c .maxAge = seconds
}
func (c *Cookie ) Expire () time .Time {
expire := c .expire
if expire .IsZero () {
expire = CookieExpireUnlimited
}
return expire
}
func (c *Cookie ) SetExpire (expire time .Time ) {
c .expire = expire
}
func (c *Cookie ) Value () []byte {
return c .value
}
func (c *Cookie ) SetValue (value string ) {
c .value = append (c .value [:0 ], value ...)
}
func (c *Cookie ) SetValueBytes (value []byte ) {
c .value = append (c .value [:0 ], value ...)
}
func (c *Cookie ) Key () []byte {
return c .key
}
func (c *Cookie ) SetKey (key string ) {
c .key = append (c .key [:0 ], key ...)
}
func (c *Cookie ) SetKeyBytes (key []byte ) {
c .key = append (c .key [:0 ], key ...)
}
func (c *Cookie ) Reset () {
c .key = c .key [:0 ]
c .value = c .value [:0 ]
c .expire = zeroTime
c .maxAge = 0
c .domain = c .domain [:0 ]
c .path = c .path [:0 ]
c .httpOnly = false
c .secure = false
c .sameSite = CookieSameSiteDisabled
}
func (c *Cookie ) AppendBytes (dst []byte ) []byte {
if len (c .key ) > 0 {
dst = append (dst , c .key ...)
dst = append (dst , '=' )
}
dst = append (dst , c .value ...)
if c .maxAge > 0 {
dst = append (dst , ';' , ' ' )
dst = append (dst , strCookieMaxAge ...)
dst = append (dst , '=' )
dst = AppendUint (dst , c .maxAge )
} else if !c .expire .IsZero () {
c .bufKV .value = AppendHTTPDate (c .bufKV .value [:0 ], c .expire )
dst = append (dst , ';' , ' ' )
dst = append (dst , strCookieExpires ...)
dst = append (dst , '=' )
dst = append (dst , c .bufKV .value ...)
}
if len (c .domain ) > 0 {
dst = appendCookiePart (dst , strCookieDomain , c .domain )
}
if len (c .path ) > 0 {
dst = appendCookiePart (dst , strCookiePath , c .path )
}
if c .httpOnly {
dst = append (dst , ';' , ' ' )
dst = append (dst , strCookieHTTPOnly ...)
}
if c .secure {
dst = append (dst , ';' , ' ' )
dst = append (dst , strCookieSecure ...)
}
switch c .sameSite {
case CookieSameSiteDefaultMode :
dst = append (dst , ';' , ' ' )
dst = append (dst , strCookieSameSite ...)
case CookieSameSiteLaxMode :
dst = append (dst , ';' , ' ' )
dst = append (dst , strCookieSameSite ...)
dst = append (dst , '=' )
dst = append (dst , strCookieSameSiteLax ...)
case CookieSameSiteStrictMode :
dst = append (dst , ';' , ' ' )
dst = append (dst , strCookieSameSite ...)
dst = append (dst , '=' )
dst = append (dst , strCookieSameSiteStrict ...)
case CookieSameSiteNoneMode :
dst = append (dst , ';' , ' ' )
dst = append (dst , strCookieSameSite ...)
dst = append (dst , '=' )
dst = append (dst , strCookieSameSiteNone ...)
}
return dst
}
func (c *Cookie ) Cookie () []byte {
c .buf = c .AppendBytes (c .buf [:0 ])
return c .buf
}
func (c *Cookie ) String () string {
return string (c .Cookie ())
}
func (c *Cookie ) WriteTo (w io .Writer ) (int64 , error ) {
n , err := w .Write (c .Cookie ())
return int64 (n ), err
}
var errNoCookies = errors .New ("no cookies found" )
func (c *Cookie ) Parse (src string ) error {
c .buf = append (c .buf [:0 ], src ...)
return c .ParseBytes (c .buf )
}
func (c *Cookie ) ParseBytes (src []byte ) error {
c .Reset ()
var s cookieScanner
s .b = src
kv := &c .bufKV
if !s .next (kv ) {
return errNoCookies
}
c .key = append (c .key , kv .key ...)
c .value = append (c .value , kv .value ...)
for s .next (kv ) {
if len (kv .key ) != 0 {
switch kv .key [0 ] | 0x20 {
case 'm' :
if caseInsensitiveCompare (strCookieMaxAge , kv .key ) {
maxAge , err := ParseUint (kv .value )
if err != nil {
return err
}
c .maxAge = maxAge
}
case 'e' :
if caseInsensitiveCompare (strCookieExpires , kv .key ) {
v := b2s (kv .value )
exptime , err := time .ParseInLocation (time .RFC1123 , v , time .UTC )
if err != nil {
exptime , err = time .Parse ("Mon, 02-Jan-2006 15:04:05 MST" , v )
if err != nil {
return err
}
}
c .expire = exptime
}
case 'd' :
if caseInsensitiveCompare (strCookieDomain , kv .key ) {
c .domain = append (c .domain , kv .value ...)
}
case 'p' :
if caseInsensitiveCompare (strCookiePath , kv .key ) {
c .path = append (c .path , kv .value ...)
}
case 's' :
if caseInsensitiveCompare (strCookieSameSite , kv .key ) {
if len (kv .value ) > 0 {
switch kv .value [0 ] | 0x20 {
case 'l' :
if caseInsensitiveCompare (strCookieSameSiteLax , kv .value ) {
c .sameSite = CookieSameSiteLaxMode
}
case 's' :
if caseInsensitiveCompare (strCookieSameSiteStrict , kv .value ) {
c .sameSite = CookieSameSiteStrictMode
}
case 'n' :
if caseInsensitiveCompare (strCookieSameSiteNone , kv .value ) {
c .sameSite = CookieSameSiteNoneMode
}
}
}
}
}
} else if len (kv .value ) != 0 {
switch kv .value [0 ] | 0x20 {
case 'h' :
if caseInsensitiveCompare (strCookieHTTPOnly , kv .value ) {
c .httpOnly = true
}
case 's' :
if caseInsensitiveCompare (strCookieSecure , kv .value ) {
c .secure = true
} else if caseInsensitiveCompare (strCookieSameSite , kv .value ) {
c .sameSite = CookieSameSiteDefaultMode
}
}
}
}
return nil
}
func appendCookiePart(dst , key , value []byte ) []byte {
dst = append (dst , ';' , ' ' )
dst = append (dst , key ...)
dst = append (dst , '=' )
return append (dst , value ...)
}
func getCookieKey(dst , src []byte ) []byte {
n := bytes .IndexByte (src , '=' )
if n >= 0 {
src = src [:n ]
}
return decodeCookieArg (dst , src , false )
}
func appendRequestCookieBytes(dst []byte , cookies []argsKV ) []byte {
for i , n := 0 , len (cookies ); i < n ; i ++ {
kv := &cookies [i ]
if len (kv .key ) > 0 {
dst = append (dst , kv .key ...)
dst = append (dst , '=' )
}
dst = append (dst , kv .value ...)
if i +1 < n {
dst = append (dst , ';' , ' ' )
}
}
return dst
}
func appendResponseCookieBytes(dst []byte , cookies []argsKV ) []byte {
for i , n := 0 , len (cookies ); i < n ; i ++ {
kv := &cookies [i ]
dst = append (dst , kv .value ...)
if i +1 < n {
dst = append (dst , ';' , ' ' )
}
}
return dst
}
func parseRequestCookies(cookies []argsKV , src []byte ) []argsKV {
var s cookieScanner
s .b = src
var kv *argsKV
cookies , kv = allocArg (cookies )
for s .next (kv ) {
if len (kv .key ) > 0 || len (kv .value ) > 0 {
cookies , kv = allocArg (cookies )
}
}
return releaseArg (cookies )
}
type cookieScanner struct {
b []byte
}
func (s *cookieScanner ) next (kv *argsKV ) bool {
b := s .b
if len (b ) == 0 {
return false
}
isKey := true
k := 0
for i , c := range b {
switch c {
case '=' :
if isKey {
isKey = false
kv .key = decodeCookieArg (kv .key , b [:i ], false )
k = i + 1
}
case ';' :
if isKey {
kv .key = kv .key [:0 ]
}
kv .value = decodeCookieArg (kv .value , b [k :i ], true )
s .b = b [i +1 :]
return true
}
}
if isKey {
kv .key = kv .key [:0 ]
}
kv .value = decodeCookieArg (kv .value , b [k :], true )
s .b = b [len (b ):]
return true
}
func decodeCookieArg(dst , src []byte , skipQuotes bool ) []byte {
for len (src ) > 0 && src [0 ] == ' ' {
src = src [1 :]
}
for len (src ) > 0 && src [len (src )-1 ] == ' ' {
src = src [:len (src )-1 ]
}
if skipQuotes {
if len (src ) > 1 && src [0 ] == '"' && src [len (src )-1 ] == '"' {
src = src [1 : len (src )-1 ]
}
}
return append (dst [:0 ], src ...)
}
func caseInsensitiveCompare(a , b []byte ) bool {
if len (a ) != len (b ) {
return false
}
for i := 0 ; i < len (a ); i ++ {
if a [i ]|0x20 != b [i ]|0x20 {
return false
}
}
return true
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .