summary refs log tree commit diff stats
path: root/lib/pure/cookies.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure/cookies.nim')
-rw-r--r--[-rwxr-xr-x]lib/pure/cookies.nim84
1 files changed, 53 insertions, 31 deletions
diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim
index d1cf36a87..f628aaf6b 100755..100644
--- a/lib/pure/cookies.nim
+++ b/lib/pure/cookies.nim
@@ -1,6 +1,6 @@
 #
 #
-#            Nimrod's Runtime Library
+#            Nim's Runtime Library
 #        (c) Copyright 2012 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
@@ -9,51 +9,73 @@
 
 ## This module implements helper procs for parsing Cookies.
 
-import strtabs, times
+import std/[strtabs, times, options]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
+type
+  SameSite* {.pure.} = enum ## The SameSite cookie attribute.
+                            ## `Default` means that `setCookie`
+                            ## proc will not set `SameSite` attribute.
+    Default, None, Lax, Strict
+
+proc parseCookies*(s: string): StringTableRef =
+  ## Parses cookies into a string table.
+  ##
+  ## The proc is meant to parse the Cookie header set by a client, not the
+  ## "Set-Cookie" header set by servers.
+  runnableExamples:
+    import std/strtabs
+    let cookieJar = parseCookies("a=1; foo=bar")
+    assert cookieJar["a"] == "1"
+    assert cookieJar["foo"] == "bar"
 
-proc parseCookies*(s: string): PStringTable = 
-  ## parses cookies into a string table.
   result = newStringTable(modeCaseInsensitive)
   var i = 0
   while true:
-    while s[i] == ' ' or s[i] == '\t': inc(i)
+    while i < s.len and (s[i] == ' ' or s[i] == '\t'): inc(i)
     var keystart = i
-    while s[i] != '=' and s[i] != '\0': inc(i)
+    while i < s.len and s[i] != '=': inc(i)
     var keyend = i-1
-    if s[i] == '\0': break
+    if i >= s.len: break
     inc(i) # skip '='
     var valstart = i
-    while s[i] != ';' and s[i] != '\0': inc(i)
+    while i < s.len and s[i] != ';': inc(i)
     result[substr(s, keystart, keyend)] = substr(s, valstart, i-1)
-    if s[i] == '\0': break
+    if i >= s.len: break
     inc(i) # skip ';'
 
-proc setCookie*(key, value: string, domain = "", path = "", 
-                expires = "", noName = false): string =
-  ## Creates a command in the format of 
-  ## ``Set-Cookie: key=value; Domain=...; ...``
+proc setCookie*(key, value: string, domain = "", path = "",
+                expires = "", noName = false,
+                secure = false, httpOnly = false,
+                maxAge = none(int), sameSite = SameSite.Default): string =
+  ## Creates a command in the format of
+  ## `Set-Cookie: key=value; Domain=...; ...`
+  ##
+  ## .. tip:: Cookies can be vulnerable. Consider setting `secure=true`, `httpOnly=true` and `sameSite=Strict`.
   result = ""
   if not noName: result.add("Set-Cookie: ")
   result.add key & "=" & value
   if domain != "": result.add("; Domain=" & domain)
   if path != "": result.add("; Path=" & path)
   if expires != "": result.add("; Expires=" & expires)
+  if secure: result.add("; Secure")
+  if httpOnly: result.add("; HttpOnly")
+  if maxAge.isSome: result.add("; Max-Age=" & $maxAge.unsafeGet)
 
-proc setCookie*(key, value: string, expires: TTimeInfo,
-                domain = "", path = "", noName = false): string =
-  ## Creates a command in the format of 
-  ## ``Set-Cookie: key=value; Domain=...; ...``
-  ##
-  ## **Note:** UTC is assumed as the timezone for ``expires``.
-  
-  return setCookie(key, value, domain, path,
-            format(expires, "ddd',' dd MMM yyyy HH:mm:ss 'UTC'"), noname)
-  
-when isMainModule:
-  var tim = TTime(int(getTime()) + 76 * (60 * 60 * 24))
-
-  echo(setCookie("test", "value", tim.getGMTime()))
-  
-  echo parseCookies("uid=1; kp=2")
-  
-                
\ No newline at end of file
+  if sameSite != SameSite.Default:
+    if sameSite == SameSite.None:
+      doAssert secure, "Cookies with SameSite=None must specify the Secure attribute!"
+    result.add("; SameSite=" & $sameSite)
+
+proc setCookie*(key, value: string, expires: DateTime|Time,
+                domain = "", path = "", noName = false,
+                secure = false, httpOnly = false,
+                maxAge = none(int), sameSite = SameSite.Default): string =
+  ## Creates a command in the format of
+  ## `Set-Cookie: key=value; Domain=...; ...`
+  result = setCookie(key, value, domain, path,
+                   format(expires.utc, "ddd',' dd MMM yyyy HH:mm:ss 'GMT'"),
+                   noName, secure, httpOnly, maxAge, sameSite)