summary refs log tree commit diff stats
path: root/lib/pure/cgi.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure/cgi.nim')
-rw-r--r--[-rwxr-xr-x]lib/pure/cgi.nim456
1 files changed, 195 insertions, 261 deletions
diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim
index 490ae926d..034f224ac 100755..100644
--- a/lib/pure/cgi.nim
+++ b/lib/pure/cgi.nim
@@ -1,381 +1,315 @@
 #
 #
-#            Nimrod's Runtime Library
-#        (c) Copyright 2010 Andreas Rumpf
+#            Nim's Runtime Library
+#        (c) Copyright 2012 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
 #
 
-## This module implements helper procs for CGI applictions. Example:
-## 
-## .. code-block:: Nimrod
+## This module implements helper procs for CGI applications. Example:
 ##
-##    import strtabs, cgi
+##   ```Nim
+##   import std/[strtabs, cgi]
 ##
-##    # Fill the values when debugging:
-##    when debug: 
-##      setTestData("name", "Klaus", "password", "123456")
-##    # read the data into `myData`
-##    var myData = readData()
-##    # check that the data's variable names are "name" or "passwort" 
-##    validateData(myData, "name", "password")
-##    # start generating content:
-##    writeContentType()
-##    # generate content:
-##    write(stdout, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n")
-##    write(stdout, "<html><head><title>Test</title></head><body>\n")
-##    writeln(stdout, "your name: " & myData["name"])
-##    writeln(stdout, "your password: " & myData["password"])
-##    writeln(stdout, "</body></html>")
-
-import strutils, os, strtabs
-
-proc URLencode*(s: string): string =
-  ## Encodes a value to be HTTP safe: This means that characters in the set
-  ## ``{'A'..'Z', 'a'..'z', '0'..'9', '_'}`` are carried over to the result,
-  ## a space is converted to ``'+'`` and every other character is encoded as
-  ## ``'%xx'`` where ``xx`` denotes its hexadecimal value. 
-  result = ""
-  for i in 0..s.len-1:
-    case s[i]
-    of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i])
-    of ' ': add(result, '+')
-    else: 
-      add(result, '%')
-      add(result, toHex(ord(s[i]), 2))
-
-proc handleHexChar(c: char, x: var int) {.inline.} = 
-  case c
-  of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
-  of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
-  of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
-  else: assert(false)
-
-proc URLdecode*(s: string): string = 
-  ## Decodes a value from its HTTP representation: This means that a ``'+'`` 
-  ## is converted to a space, ``'%xx'`` (where ``xx`` denotes a hexadecimal
-  ## value) is converted to the character with ordinal number ``xx``, and  
-  ## and every other character is carried over. 
-  result = ""
-  var i = 0
-  while i < s.len:
-    case s[i]
-    of '%': 
-      var x = 0
-      handleHexChar(s[i+1], x)
-      handleHexChar(s[i+2], x)
-      inc(i, 2)
-      add(result, chr(x))
-    of '+': add(result, ' ')
-    else: add(result, s[i])
-    inc(i)
-
-proc addXmlChar(dest: var string, c: Char) {.inline.} = 
+##   # Fill the values when debugging:
+##   when debug:
+##     setTestData("name", "Klaus", "password", "123456")
+##   # read the data into `myData`
+##   var myData = readData()
+##   # check that the data's variable names are "name" or "password"
+##   validateData(myData, "name", "password")
+##   # start generating content:
+##   writeContentType()
+##   # generate content:
+##   write(stdout, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n")
+##   write(stdout, "<html><head><title>Test</title></head><body>\n")
+##   writeLine(stdout, "your name: " & myData["name"])
+##   writeLine(stdout, "your password: " & myData["password"])
+##   writeLine(stdout, "</body></html>")
+##   ```
+
+import std/[strutils, os, strtabs, cookies, uri]
+export uri.encodeUrl, uri.decodeUrl
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
+
+
+proc addXmlChar(dest: var string, c: char) {.inline.} =
   case c
   of '&': add(dest, "&amp;")
   of '<': add(dest, "&lt;")
   of '>': add(dest, "&gt;")
   of '\"': add(dest, "&quot;")
   else: add(dest, c)
-  
-proc XMLencode*(s: string): string = 
+
+proc xmlEncode*(s: string): string =
   ## Encodes a value to be XML safe:
-  ## * ``"`` is replaced by ``&quot;``
-  ## * ``<`` is replaced by ``&lt;``
-  ## * ``>`` is replaced by ``&gt;``
-  ## * ``&`` is replaced by ``&amp;``
+  ## * `"` is replaced by `&quot;`
+  ## * `<` is replaced by `&lt;`
+  ## * `>` is replaced by `&gt;`
+  ## * `&` is replaced by `&amp;`
   ## * every other character is carried over.
-  result = ""
+  result = newStringOfCap(s.len + s.len shr 2)
   for i in 0..len(s)-1: addXmlChar(result, s[i])
 
 type
-  ECgi* = object of EIO  ## the exception that is raised, if a CGI error occurs
-  TRequestMethod* = enum ## the used request method
-    methodNone,          ## no REQUEST_METHOD environment variable
-    methodPost,          ## query uses the POST method
-    methodGet            ## query uses the GET method
-
-proc cgiError*(msg: string) {.noreturn.} = 
-  ## raises an ECgi exception with message `msg`.
-  var e: ref ECgi
-  new(e)
-  e.msg = msg
-  raise e
-
-proc getEncodedData(allowedMethods: set[TRequestMethod]): string = 
-  case getenv("REQUEST_METHOD") 
-  of "POST": 
-    if methodPost notin allowedMethods: 
+  CgiError* = object of IOError ## Exception that is raised if a CGI error occurs.
+  RequestMethod* = enum ## The used request method.
+    methodNone,         ## no REQUEST_METHOD environment variable
+    methodPost,         ## query uses the POST method
+    methodGet           ## query uses the GET method
+
+proc cgiError*(msg: string) {.noreturn.} =
+  ## Raises a `CgiError` exception with message `msg`.
+  raise newException(CgiError, msg)
+
+proc getEncodedData(allowedMethods: set[RequestMethod]): string =
+  case getEnv("REQUEST_METHOD")
+  of "POST":
+    if methodPost notin allowedMethods:
       cgiError("'REQUEST_METHOD' 'POST' is not supported")
-    var L = parseInt(getenv("CONTENT_LENGTH"))
+    var L = parseInt(getEnv("CONTENT_LENGTH"))
+    if L == 0:
+      return ""
     result = newString(L)
     if readBuffer(stdin, addr(result[0]), L) != L:
       cgiError("cannot read from stdin")
   of "GET":
-    if methodGet notin allowedMethods: 
+    if methodGet notin allowedMethods:
       cgiError("'REQUEST_METHOD' 'GET' is not supported")
-    result = getenv("QUERY_STRING")
-  else: 
+    result = getEnv("QUERY_STRING")
+  else:
     if methodNone notin allowedMethods:
       cgiError("'REQUEST_METHOD' must be 'POST' or 'GET'")
 
-iterator decodeData*(data: string): tuple[key, value: string] = 
+iterator decodeData*(data: string): tuple[key, value: string] =
   ## Reads and decodes CGI data and yields the (name, value) pairs the
   ## data consists of.
-  var i = 0
-  var name = ""
-  var value = ""
-  # decode everything in one pass:
-  while data[i] != '\0':
-    setLen(name, 0) # reuse memory
-    while true:
-      case data[i]
-      of '\0': break
-      of '%': 
-        var x = 0
-        handleHexChar(data[i+1], x)
-        handleHexChar(data[i+2], x)
-        inc(i, 2)
-        add(name, chr(x))
-      of '+': add(name, ' ')
-      of '=', '&': break
-      else: add(name, data[i])
-      inc(i)
-    if data[i] != '=': cgiError("'=' expected")
-    inc(i) # skip '='
-    setLen(value, 0) # reuse memory
-    while true:
-      case data[i]
-      of '%': 
-        var x = 0
-        handleHexChar(data[i+1], x)
-        handleHexChar(data[i+2], x)
-        inc(i, 2)
-        add(value, chr(x))
-      of '+': add(value, ' ')
-      of '&', '\0': break
-      else: add(value, data[i])
-      inc(i)
-    yield (name, value)
-    if data[i] == '&': inc(i)
-    elif data[i] == '\0': break
-    else: cgiError("'&' expected")
- 
-iterator decodeData*(allowedMethods: set[TRequestMethod] = 
-       {methodNone, methodPost, methodGet}): tuple[key, value: string] = 
+  for (key, value) in uri.decodeQuery(data):
+    yield (key, value)
+
+iterator decodeData*(allowedMethods: set[RequestMethod] =
+       {methodNone, methodPost, methodGet}): tuple[key, value: string] =
   ## Reads and decodes CGI data and yields the (name, value) pairs the
   ## data consists of. If the client does not use a method listed in the
-  ## `allowedMethods` set, an `ECgi` exception is raised.
-  var data = getEncodedData(allowedMethods)
-  if not isNil(data): 
-    for key, value in decodeData(data):
-      yield (key, value)
-
-proc readData*(allowedMethods: set[TRequestMethod] = 
-               {methodNone, methodPost, methodGet}): PStringTable = 
-  ## Read CGI data. If the client does not use a method listed in the
-  ## `allowedMethods` set, an `ECgi` exception is raised.
+  ## `allowedMethods` set, a `CgiError` exception is raised.
+  let data = getEncodedData(allowedMethods)
+  for (key, value) in uri.decodeQuery(data):
+    yield (key, value)
+
+proc readData*(allowedMethods: set[RequestMethod] =
+               {methodNone, methodPost, methodGet}): StringTableRef =
+  ## Reads CGI data. If the client does not use a method listed in the
+  ## `allowedMethods` set, a `CgiError` exception is raised.
   result = newStringTable()
-  for name, value in decodeData(allowedMethods): 
+  for name, value in decodeData(allowedMethods):
     result[name] = value
-  
-proc validateData*(data: PStringTable, validKeys: openarray[string]) = 
-  ## validates data; raises `ECgi` if this fails. This checks that each variable
+
+proc readData*(data: string): StringTableRef =
+  ## Reads CGI data from a string.
+  result = newStringTable()
+  for name, value in decodeData(data):
+    result[name] = value
+
+proc validateData*(data: StringTableRef, validKeys: varargs[string]) =
+  ## Validates data; raises `CgiError` if this fails. This checks that each variable
   ## name of the CGI `data` occurs in the `validKeys` array.
   for key, val in pairs(data):
-    if find(validKeys, key) < 0: 
+    if find(validKeys, key) < 0:
       cgiError("unknown variable name: " & key)
 
 proc getContentLength*(): string =
-  ## returns contents of the ``CONTENT_LENGTH`` environment variable
-  return getenv("CONTENT_LENGTH")
+  ## Returns contents of the `CONTENT_LENGTH` environment variable.
+  return getEnv("CONTENT_LENGTH")
 
 proc getContentType*(): string =
-  ## returns contents of the ``CONTENT_TYPE`` environment variable
-  return getenv("CONTENT_Type")
+  ## Returns contents of the `CONTENT_TYPE` environment variable.
+  return getEnv("CONTENT_Type")
 
 proc getDocumentRoot*(): string =
-  ## returns contents of the ``DOCUMENT_ROOT`` environment variable
-  return getenv("DOCUMENT_ROOT")
+  ## Returns contents of the `DOCUMENT_ROOT` environment variable.
+  return getEnv("DOCUMENT_ROOT")
 
 proc getGatewayInterface*(): string =
-  ## returns contents of the ``GATEWAY_INTERFACE`` environment variable
-  return getenv("GATEWAY_INTERFACE")
+  ## Returns contents of the `GATEWAY_INTERFACE` environment variable.
+  return getEnv("GATEWAY_INTERFACE")
 
 proc getHttpAccept*(): string =
-  ## returns contents of the ``HTTP_ACCEPT`` environment variable
-  return getenv("HTTP_ACCEPT")
+  ## Returns contents of the `HTTP_ACCEPT` environment variable.
+  return getEnv("HTTP_ACCEPT")
 
 proc getHttpAcceptCharset*(): string =
-  ## returns contents of the ``HTTP_ACCEPT_CHARSET`` environment variable
-  return getenv("HTTP_ACCEPT_CHARSET")
+  ## Returns contents of the `HTTP_ACCEPT_CHARSET` environment variable.
+  return getEnv("HTTP_ACCEPT_CHARSET")
 
 proc getHttpAcceptEncoding*(): string =
-  ## returns contents of the ``HTTP_ACCEPT_ENCODING`` environment variable
-  return getenv("HTTP_ACCEPT_ENCODING")
+  ## Returns contents of the `HTTP_ACCEPT_ENCODING` environment variable.
+  return getEnv("HTTP_ACCEPT_ENCODING")
 
 proc getHttpAcceptLanguage*(): string =
-  ## returns contents of the ``HTTP_ACCEPT_LANGUAGE`` environment variable
-  return getenv("HTTP_ACCEPT_LANGUAGE")
+  ## Returns contents of the `HTTP_ACCEPT_LANGUAGE` environment variable.
+  return getEnv("HTTP_ACCEPT_LANGUAGE")
 
 proc getHttpConnection*(): string =
-  ## returns contents of the ``HTTP_CONNECTION`` environment variable
-  return getenv("HTTP_CONNECTION")
+  ## Returns contents of the `HTTP_CONNECTION` environment variable.
+  return getEnv("HTTP_CONNECTION")
 
 proc getHttpCookie*(): string =
-  ## returns contents of the ``HTTP_COOKIE`` environment variable
-  return getenv("HTTP_COOKIE")
+  ## Returns contents of the `HTTP_COOKIE` environment variable.
+  return getEnv("HTTP_COOKIE")
 
 proc getHttpHost*(): string =
-  ## returns contents of the ``HTTP_HOST`` environment variable
-  return getenv("HTTP_HOST")
+  ## Returns contents of the `HTTP_HOST` environment variable.
+  return getEnv("HTTP_HOST")
 
 proc getHttpReferer*(): string =
-  ## returns contents of the ``HTTP_REFERER`` environment variable
-  return getenv("HTTP_REFERER")
+  ## Returns contents of the `HTTP_REFERER` environment variable.
+  return getEnv("HTTP_REFERER")
 
 proc getHttpUserAgent*(): string =
-  ## returns contents of the ``HTTP_USER_AGENT`` environment variable
-  return getenv("HTTP_USER_AGENT")
+  ## Returns contents of the `HTTP_USER_AGENT` environment variable.
+  return getEnv("HTTP_USER_AGENT")
 
 proc getPathInfo*(): string =
-  ## returns contents of the ``PATH_INFO`` environment variable
-  return getenv("PATH_INFO")
+  ## Returns contents of the `PATH_INFO` environment variable.
+  return getEnv("PATH_INFO")
 
 proc getPathTranslated*(): string =
-  ## returns contents of the ``PATH_TRANSLATED`` environment variable
-  return getenv("PATH_TRANSLATED")
+  ## Returns contents of the `PATH_TRANSLATED` environment variable.
+  return getEnv("PATH_TRANSLATED")
 
 proc getQueryString*(): string =
-  ## returns contents of the ``QUERY_STRING`` environment variable
-  return getenv("QUERY_STRING")
+  ## Returns contents of the `QUERY_STRING` environment variable.
+  return getEnv("QUERY_STRING")
 
 proc getRemoteAddr*(): string =
-  ## returns contents of the ``REMOTE_ADDR`` environment variable
-  return getenv("REMOTE_ADDR")
+  ## Returns contents of the `REMOTE_ADDR` environment variable.
+  return getEnv("REMOTE_ADDR")
 
 proc getRemoteHost*(): string =
-  ## returns contents of the ``REMOTE_HOST`` environment variable
-  return getenv("REMOTE_HOST")
+  ## Returns contents of the `REMOTE_HOST` environment variable.
+  return getEnv("REMOTE_HOST")
 
 proc getRemoteIdent*(): string =
-  ## returns contents of the ``REMOTE_IDENT`` environment variable
-  return getenv("REMOTE_IDENT")
+  ## Returns contents of the `REMOTE_IDENT` environment variable.
+  return getEnv("REMOTE_IDENT")
 
 proc getRemotePort*(): string =
-  ## returns contents of the ``REMOTE_PORT`` environment variable
-  return getenv("REMOTE_PORT")
+  ## Returns contents of the `REMOTE_PORT` environment variable.
+  return getEnv("REMOTE_PORT")
 
 proc getRemoteUser*(): string =
-  ## returns contents of the ``REMOTE_USER`` environment variable
-  return getenv("REMOTE_USER")
+  ## Returns contents of the `REMOTE_USER` environment variable.
+  return getEnv("REMOTE_USER")
 
 proc getRequestMethod*(): string =
-  ## returns contents of the ``REQUEST_METHOD`` environment variable
-  return getenv("REQUEST_METHOD")
+  ## Returns contents of the `REQUEST_METHOD` environment variable.
+  return getEnv("REQUEST_METHOD")
 
 proc getRequestURI*(): string =
-  ## returns contents of the ``REQUEST_URI`` environment variable
-  return getenv("REQUEST_URI")
+  ## Returns contents of the `REQUEST_URI` environment variable.
+  return getEnv("REQUEST_URI")
 
 proc getScriptFilename*(): string =
-  ## returns contents of the ``SCRIPT_FILENAME`` environment variable
-  return getenv("SCRIPT_FILENAME")
+  ## Returns contents of the `SCRIPT_FILENAME` environment variable.
+  return getEnv("SCRIPT_FILENAME")
 
 proc getScriptName*(): string =
-  ## returns contents of the ``SCRIPT_NAME`` environment variable
-  return getenv("SCRIPT_NAME")
+  ## Returns contents of the `SCRIPT_NAME` environment variable.
+  return getEnv("SCRIPT_NAME")
 
 proc getServerAddr*(): string =
-  ## returns contents of the ``SERVER_ADDR`` environment variable
-  return getenv("SERVER_ADDR")
+  ## Returns contents of the `SERVER_ADDR` environment variable.
+  return getEnv("SERVER_ADDR")
 
 proc getServerAdmin*(): string =
-  ## returns contents of the ``SERVER_ADMIN`` environment variable
-  return getenv("SERVER_ADMIN")
+  ## Returns contents of the `SERVER_ADMIN` environment variable.
+  return getEnv("SERVER_ADMIN")
 
 proc getServerName*(): string =
-  ## returns contents of the ``SERVER_NAME`` environment variable
-  return getenv("SERVER_NAME")
+  ## Returns contents of the `SERVER_NAME` environment variable.
+  return getEnv("SERVER_NAME")
 
 proc getServerPort*(): string =
-  ## returns contents of the ``SERVER_PORT`` environment variable
-  return getenv("SERVER_PORT")
+  ## Returns contents of the `SERVER_PORT` environment variable.
+  return getEnv("SERVER_PORT")
 
 proc getServerProtocol*(): string =
-  ## returns contents of the ``SERVER_PROTOCOL`` environment variable
-  return getenv("SERVER_PROTOCOL")
+  ## Returns contents of the `SERVER_PROTOCOL` environment variable.
+  return getEnv("SERVER_PROTOCOL")
 
 proc getServerSignature*(): string =
-  ## returns contents of the ``SERVER_SIGNATURE`` environment variable
-  return getenv("SERVER_SIGNATURE")
+  ## Returns contents of the `SERVER_SIGNATURE` environment variable.
+  return getEnv("SERVER_SIGNATURE")
 
 proc getServerSoftware*(): string =
-  ## returns contents of the ``SERVER_SOFTWARE`` environment variable
-  return getenv("SERVER_SOFTWARE")
+  ## Returns contents of the `SERVER_SOFTWARE` environment variable.
+  return getEnv("SERVER_SOFTWARE")
 
-proc setTestData*(keysvalues: openarray[string]) = 
-  ## fills the appropriate environment variables to test your CGI application.
+proc setTestData*(keysvalues: varargs[string]) =
+  ## Fills the appropriate environment variables to test your CGI application.
   ## This can only simulate the 'GET' request method. `keysvalues` should
   ## provide embedded (name, value)-pairs. Example:
-  ##
-  ## .. code-block:: Nimrod
-  ##    setTestData("name", "Hanz", "password", "12345")
-  putenv("REQUEST_METHOD", "GET")
+  ##   ```Nim
+  ##   setTestData("name", "Hanz", "password", "12345")
+  ##   ```
+  putEnv("REQUEST_METHOD", "GET")
   var i = 0
   var query = ""
   while i < keysvalues.len:
-    add(query, URLencode(keysvalues[i]))
+    add(query, encodeUrl(keysvalues[i]))
     add(query, '=')
-    add(query, URLencode(keysvalues[i+1]))
+    add(query, encodeUrl(keysvalues[i+1]))
     add(query, '&')
     inc(i, 2)
-  putenv("QUERY_STRING", query)
-
-proc writeContentType*() = 
-  ## call this before starting to send your HTML data to `stdout`. This
-  ## implements this part of the CGI protocol: 
-  ##
-  ## .. code-block:: Nimrod
-  ##     write(stdout, "Content-type: text/html\n\n")
-  ## 
-  ## It also modifies the debug stack traces so that they contain
-  ## ``<br />`` and are easily readable in a browser.  
+  putEnv("QUERY_STRING", query)
+
+proc writeContentType*() =
+  ## Calls this before starting to send your HTML data to `stdout`. This
+  ## implements this part of the CGI protocol:
+  ##   ```Nim
+  ##   write(stdout, "Content-type: text/html\n\n")
+  ##   ```
   write(stdout, "Content-type: text/html\n\n")
-  system.stackTraceNewLine = "<br />\n"
-  
-proc setCookie*(name, value: string) = 
+
+proc resetForStacktrace() =
+  stdout.write """<!--: spam
+Content-Type: text/html
+
+<body bgcolor=#f0f0f8><font color=#f0f0f8 size=-5> -->
+<body bgcolor=#f0f0f8><font color=#f0f0f8 size=-5> --> -->
+</font> </font> </font> </script> </object> </blockquote> </pre>
+</table> </table> </table> </table> </table> </font> </font> </font>
+"""
+
+proc writeErrorMessage*(data: string) =
+  ## Tries to reset browser state and writes `data` to stdout in
+  ## <plaintext> tag.
+  resetForStacktrace()
+  # We use <plaintext> here, instead of escaping, so stacktrace can
+  # be understood by human looking at source.
+  stdout.write("<plaintext>\n")
+  stdout.write(data)
+
+proc setStackTraceStdout*() =
+  ## Makes Nim output stacktraces to stdout, instead of server log.
+  errorMessageWriter = writeErrorMessage
+
+proc setCookie*(name, value: string) =
   ## Sets a cookie.
   write(stdout, "Set-Cookie: ", name, "=", value, "\n")
 
 var
-  cookies: PStringTable = nil
+  gcookies {.threadvar.}: StringTableRef
 
-proc parseCookies(s: string): PStringTable = 
-  result = newStringTable(modeCaseInsensitive)
-  var i = 0
-  while true:
-    while s[i] == ' ' or s[i] == '\t': inc(i)
-    var keystart = i
-    while s[i] != '=' and s[i] != '\0': inc(i)
-    var keyend = i-1
-    if s[i] == '\0': break
-    inc(i) # skip '='
-    var valstart = i
-    while s[i] != ';' and s[i] != '\0': inc(i)
-    result[copy(s, keystart, keyend)] = copy(s, valstart, i-1)
-    if s[i] == '\0': break
-    inc(i) # skip ';'
-    
-proc getCookie*(name: string): string = 
+proc getCookie*(name: string): string =
   ## Gets a cookie. If no cookie of `name` exists, "" is returned.
-  if cookies == nil: cookies = parseCookies(getHttpCookie())
-  result = cookies[name]
+  if gcookies == nil: gcookies = parseCookies(getHttpCookie())
+  result = gcookies.getOrDefault(name)
 
-proc existsCookie*(name: string): bool = 
+proc existsCookie*(name: string): bool =
   ## Checks if a cookie of `name` exists.
-  if cookies == nil: cookies = parseCookies(getHttpCookie())
-  result = hasKey(cookies, name)
-
-
+  if gcookies == nil: gcookies = parseCookies(getHttpCookie())
+  result = hasKey(gcookies, name)