cookie.go 16 KB

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