diff options
Diffstat (limited to 'lib/pure/cookies.nim')
-rw-r--r--[-rwxr-xr-x] | lib/pure/cookies.nim | 84 |
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) |