Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stdlib] make cookies module modern #17116

Merged
merged 12 commits into from
Feb 24, 2021
41 changes: 28 additions & 13 deletions lib/pure/cookies.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,25 @@

## This module implements helper procs for parsing Cookies.

import strtabs, times
import std/[strtabs, times, options]


type
SameSite* {.pure.} = enum ## The SameSite cookie attribute.
## `Default` means that `setCookie`
## proc will not set `SameSite` attribute.
Default, None, Lax, Strict
Copy link
Member

@timotheecour timotheecour Feb 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std/compilesettings uses lower-case enum values, this is inconsistent; we should specify in nep1 which one is the "best practice"
followup: timotheecour#622 (comment)

Copy link
Member Author

@ringabout ringabout Feb 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is already specified in nep1: Non-pure enum values should use camelCase whereas pure enum values should use PascalCase.

image


proc parseCookies*(s: string): StringTableRef =
timotheecour marked this conversation as resolved.
Show resolved Hide resolved
## parses cookies into a string table.
## 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.
##
## Example:
##
## .. code-block::Nim
## doAssert parseCookies("a=1; foo=bar") == {"a": 1, "foo": "bar"}.newStringTable
runnableExamples:
import std/strtabs
let cookieJar = parseCookies("a=1; foo=bar")
assert cookieJar["a"] == "1"
assert cookieJar["foo"] == "bar"

result = newStringTable(modeCaseInsensitive)
var i = 0
Expand All @@ -39,9 +46,10 @@ proc parseCookies*(s: string): StringTableRef =

proc setCookie*(key, value: string, domain = "", path = "",
expires = "", noName = false,
secure = false, httpOnly = false): string =
secure = false, httpOnly = false,
maxAge = none(int), sameSite = SameSite.Default): string =
## Creates a command in the format of
## ``Set-Cookie: key=value; Domain=...; ...``
## `Set-Cookie: key=value; Domain=...; ...`
result = ""
if not noName: result.add("Set-Cookie: ")
result.add key & "=" & value
Expand All @@ -50,12 +58,19 @@ proc setCookie*(key, value: string, domain = "", 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)

if sameSite != SameSite.Default:
if sameSite == SameSite.None:
doAssert httpOnly, "Cookies with SameSite=None must specify the Secure attribute!"
ringabout marked this conversation as resolved.
Show resolved Hide resolved
result.add("; SameSite=" & $sameSite)

proc setCookie*(key, value: string, expires: DateTime|Time,
domain = "", path = "", noName = false,
secure = false, httpOnly = false): string =
secure = false, httpOnly = false,
maxAge = none(int), sameSite = SameSite.Default): string =
## Creates a command in the format of
## ``Set-Cookie: key=value; Domain=...; ...``
return setCookie(key, value, domain, path,
## `Set-Cookie: key=value; Domain=...; ...`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pre-existing but this overload (with differently placed params) seems like a bad design, instead we should expose a proc (eg toGmtString(t: DateTime|Time) and let users call:

setCookie(..., t.toGmtString, ...)

result = setCookie(key, value, domain, path,
format(expires.utc, "ddd',' dd MMM yyyy HH:mm:ss 'GMT'"),
noname, secure, httpOnly)
noname, secure, httpOnly, maxAge, sameSite)
7 changes: 6 additions & 1 deletion tests/stdlib/tcookies.nim
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import cookies, times, strtabs
discard """
targets: "c js"
"""


import std/[cookies, times, strtabs]

let expire = fromUnix(0) + 1.seconds

Expand Down