cookies.nim 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2012 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This module implements helper procs for parsing Cookies.
  10. import strtabs, times, options
  11. when defined(nimPreviewSlimSystem):
  12. import std/assertions
  13. type
  14. SameSite* {.pure.} = enum ## The SameSite cookie attribute.
  15. ## `Default` means that `setCookie`
  16. ## proc will not set `SameSite` attribute.
  17. Default, None, Lax, Strict
  18. proc parseCookies*(s: string): StringTableRef =
  19. ## Parses cookies into a string table.
  20. ##
  21. ## The proc is meant to parse the Cookie header set by a client, not the
  22. ## "Set-Cookie" header set by servers.
  23. runnableExamples:
  24. import std/strtabs
  25. let cookieJar = parseCookies("a=1; foo=bar")
  26. assert cookieJar["a"] == "1"
  27. assert cookieJar["foo"] == "bar"
  28. result = newStringTable(modeCaseInsensitive)
  29. var i = 0
  30. while true:
  31. while i < s.len and (s[i] == ' ' or s[i] == '\t'): inc(i)
  32. var keystart = i
  33. while i < s.len and s[i] != '=': inc(i)
  34. var keyend = i-1
  35. if i >= s.len: break
  36. inc(i) # skip '='
  37. var valstart = i
  38. while i < s.len and s[i] != ';': inc(i)
  39. result[substr(s, keystart, keyend)] = substr(s, valstart, i-1)
  40. if i >= s.len: break
  41. inc(i) # skip ';'
  42. proc setCookie*(key, value: string, domain = "", path = "",
  43. expires = "", noName = false,
  44. secure = false, httpOnly = false,
  45. maxAge = none(int), sameSite = SameSite.Default): string =
  46. ## Creates a command in the format of
  47. ## `Set-Cookie: key=value; Domain=...; ...`
  48. ##
  49. ## .. tip:: Cookies can be vulnerable. Consider setting `secure=true`, `httpOnly=true` and `sameSite=Strict`.
  50. result = ""
  51. if not noName: result.add("Set-Cookie: ")
  52. result.add key & "=" & value
  53. if domain != "": result.add("; Domain=" & domain)
  54. if path != "": result.add("; Path=" & path)
  55. if expires != "": result.add("; Expires=" & expires)
  56. if secure: result.add("; Secure")
  57. if httpOnly: result.add("; HttpOnly")
  58. if maxAge.isSome: result.add("; Max-Age=" & $maxAge.unsafeGet)
  59. if sameSite != SameSite.Default:
  60. if sameSite == SameSite.None:
  61. doAssert secure, "Cookies with SameSite=None must specify the Secure attribute!"
  62. result.add("; SameSite=" & $sameSite)
  63. proc setCookie*(key, value: string, expires: DateTime|Time,
  64. domain = "", path = "", noName = false,
  65. secure = false, httpOnly = false,
  66. maxAge = none(int), sameSite = SameSite.Default): string =
  67. ## Creates a command in the format of
  68. ## `Set-Cookie: key=value; Domain=...; ...`
  69. result = setCookie(key, value, domain, path,
  70. format(expires.utc, "ddd',' dd MMM yyyy HH:mm:ss 'GMT'"),
  71. noName, secure, httpOnly, maxAge, sameSite)