follow #15860 clean cgi module (#16487)

* follow #15860  clean cgi module

* follow #15860  clean cgi module
This commit is contained in:
flywind 2020-12-27 12:46:21 -06:00 • committed by GitHub
commit e718a4a058
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 57 additions and 77 deletions

View file

@ -29,13 +29,10 @@
## writeLine(stdout, "your password: " & myData["password"]) ## writeLine(stdout, "your password: " & myData["password"])
## writeLine(stdout, "</body></html>") ## writeLine(stdout, "</body></html>")
import strutils, os, strtabs, cookies, uri import std/[strutils, os, strtabs, cookies, uri]
export uri.encodeUrl, uri.decodeUrl export uri.encodeUrl, uri.decodeUrl
import std/private/decode_helpers
proc addXmlChar(dest: var string, c: char) {.inline.} = proc addXmlChar(dest: var string, c: char) {.inline.} =
case c case c
of '&': add(dest, "&amp;") of '&': add(dest, "&amp;")
@ -46,23 +43,23 @@ proc addXmlChar(dest: var string, c: char) {.inline.} =
proc xmlEncode*(s: string): string = proc xmlEncode*(s: string): string =
## Encodes a value to be XML safe: ## Encodes a value to be XML safe:
## * ``"`` is replaced by ``&quot;`` ## * `"` is replaced by `&quot;`
## * ``<`` is replaced by ``&lt;`` ## * `<` is replaced by `&lt;`
## * ``>`` is replaced by ``&gt;`` ## * `>` is replaced by `&gt;`
## * ``&`` is replaced by ``&amp;`` ## * `&` is replaced by `&amp;`
## * every other character is carried over. ## * every other character is carried over.
result = newStringOfCap(s.len + s.len shr 2) result = newStringOfCap(s.len + s.len shr 2)
for i in 0..len(s)-1: addXmlChar(result, s[i]) for i in 0..len(s)-1: addXmlChar(result, s[i])
type type
CgiError* = object of IOError ## Exception that is raised if a CGI error occurs CgiError* = object of IOError ## Exception that is raised if a CGI error occurs.
RequestMethod* = enum ## the used request method RequestMethod* = enum ## The used request method.
methodNone, ## no REQUEST_METHOD environment variable methodNone, ## no REQUEST_METHOD environment variable
methodPost, ## query uses the POST method methodPost, ## query uses the POST method
methodGet ## query uses the GET method methodGet ## query uses the GET method
proc cgiError*(msg: string) {.noreturn.} = proc cgiError*(msg: string) {.noreturn.} =
## Raises a ``CgiError`` exception with message `msg`. ## Raises a `CgiError` exception with message `msg`.
raise newException(CgiError, msg) raise newException(CgiError, msg)
proc getEncodedData(allowedMethods: set[RequestMethod]): string = proc getEncodedData(allowedMethods: set[RequestMethod]): string =
@ -97,7 +94,7 @@ iterator decodeData*(allowedMethods: set[RequestMethod] =
{methodNone, methodPost, methodGet}): tuple[key, value: TaintedString] = {methodNone, methodPost, methodGet}): tuple[key, value: TaintedString] =
## Reads and decodes CGI data and yields the (name, value) pairs the ## 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 ## data consists of. If the client does not use a method listed in the
## `allowedMethods` set, a ``CgiError`` exception is raised. ## `allowedMethods` set, a `CgiError` exception is raised.
let data = getEncodedData(allowedMethods) let data = getEncodedData(allowedMethods)
try: try:
for (key, value) in uri.decodeQuery(data): for (key, value) in uri.decodeQuery(data):
@ -107,155 +104,155 @@ iterator decodeData*(allowedMethods: set[RequestMethod] =
proc readData*(allowedMethods: set[RequestMethod] = proc readData*(allowedMethods: set[RequestMethod] =
{methodNone, methodPost, methodGet}): StringTableRef = {methodNone, methodPost, methodGet}): StringTableRef =
## Read CGI data. If the client does not use a method listed in the ## Reads 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.
result = newStringTable() result = newStringTable()
for name, value in decodeData(allowedMethods): for name, value in decodeData(allowedMethods):
result[name.string] = value.string result[name.string] = value.string
proc readData*(data: string): StringTableRef = proc readData*(data: string): StringTableRef =
## Read CGI data from a string. ## Reads CGI data from a string.
result = newStringTable() result = newStringTable()
for name, value in decodeData(data): for name, value in decodeData(data):
result[name.string] = value.string result[name.string] = value.string
proc validateData*(data: StringTableRef, validKeys: varargs[string]) = proc validateData*(data: StringTableRef, validKeys: varargs[string]) =
## validates data; raises `ECgi` if this fails. This checks that each variable ## Validates data; raises `CgiError` if this fails. This checks that each variable
## name of the CGI `data` occurs in the `validKeys` array. ## name of the CGI `data` occurs in the `validKeys` array.
for key, val in pairs(data): for key, val in pairs(data):
if find(validKeys, key) < 0: if find(validKeys, key) < 0:
cgiError("unknown variable name: " & key) cgiError("unknown variable name: " & key)
proc getContentLength*(): string = proc getContentLength*(): string =
## returns contents of the ``CONTENT_LENGTH`` environment variable ## Returns contents of the `CONTENT_LENGTH` environment variable.
return getEnv("CONTENT_LENGTH").string return getEnv("CONTENT_LENGTH").string
proc getContentType*(): string = proc getContentType*(): string =
## returns contents of the ``CONTENT_TYPE`` environment variable ## Returns contents of the `CONTENT_TYPE` environment variable.
return getEnv("CONTENT_Type").string return getEnv("CONTENT_Type").string
proc getDocumentRoot*(): string = proc getDocumentRoot*(): string =
## returns contents of the ``DOCUMENT_ROOT`` environment variable ## Returns contents of the `DOCUMENT_ROOT` environment variable.
return getEnv("DOCUMENT_ROOT").string return getEnv("DOCUMENT_ROOT").string
proc getGatewayInterface*(): string = proc getGatewayInterface*(): string =
## returns contents of the ``GATEWAY_INTERFACE`` environment variable ## Returns contents of the `GATEWAY_INTERFACE` environment variable.
return getEnv("GATEWAY_INTERFACE").string return getEnv("GATEWAY_INTERFACE").string
proc getHttpAccept*(): string = proc getHttpAccept*(): string =
## returns contents of the ``HTTP_ACCEPT`` environment variable ## Returns contents of the `HTTP_ACCEPT` environment variable.
return getEnv("HTTP_ACCEPT").string return getEnv("HTTP_ACCEPT").string
proc getHttpAcceptCharset*(): string = proc getHttpAcceptCharset*(): string =
## returns contents of the ``HTTP_ACCEPT_CHARSET`` environment variable ## Returns contents of the `HTTP_ACCEPT_CHARSET` environment variable.
return getEnv("HTTP_ACCEPT_CHARSET").string return getEnv("HTTP_ACCEPT_CHARSET").string
proc getHttpAcceptEncoding*(): string = proc getHttpAcceptEncoding*(): string =
## returns contents of the ``HTTP_ACCEPT_ENCODING`` environment variable ## Returns contents of the `HTTP_ACCEPT_ENCODING` environment variable.
return getEnv("HTTP_ACCEPT_ENCODING").string return getEnv("HTTP_ACCEPT_ENCODING").string
proc getHttpAcceptLanguage*(): string = proc getHttpAcceptLanguage*(): string =
## returns contents of the ``HTTP_ACCEPT_LANGUAGE`` environment variable ## Returns contents of the `HTTP_ACCEPT_LANGUAGE` environment variable.
return getEnv("HTTP_ACCEPT_LANGUAGE").string return getEnv("HTTP_ACCEPT_LANGUAGE").string
proc getHttpConnection*(): string = proc getHttpConnection*(): string =
## returns contents of the ``HTTP_CONNECTION`` environment variable ## Returns contents of the `HTTP_CONNECTION` environment variable.
return getEnv("HTTP_CONNECTION").string return getEnv("HTTP_CONNECTION").string
proc getHttpCookie*(): string = proc getHttpCookie*(): string =
## returns contents of the ``HTTP_COOKIE`` environment variable ## Returns contents of the `HTTP_COOKIE` environment variable.
return getEnv("HTTP_COOKIE").string return getEnv("HTTP_COOKIE").string
proc getHttpHost*(): string = proc getHttpHost*(): string =
## returns contents of the ``HTTP_HOST`` environment variable ## Returns contents of the `HTTP_HOST` environment variable.
return getEnv("HTTP_HOST").string return getEnv("HTTP_HOST").string
proc getHttpReferer*(): string = proc getHttpReferer*(): string =
## returns contents of the ``HTTP_REFERER`` environment variable ## Returns contents of the `HTTP_REFERER` environment variable.
return getEnv("HTTP_REFERER").string return getEnv("HTTP_REFERER").string
proc getHttpUserAgent*(): string = proc getHttpUserAgent*(): string =
## returns contents of the ``HTTP_USER_AGENT`` environment variable ## Returns contents of the `HTTP_USER_AGENT` environment variable.
return getEnv("HTTP_USER_AGENT").string return getEnv("HTTP_USER_AGENT").string
proc getPathInfo*(): string = proc getPathInfo*(): string =
## returns contents of the ``PATH_INFO`` environment variable ## Returns contents of the `PATH_INFO` environment variable.
return getEnv("PATH_INFO").string return getEnv("PATH_INFO").string
proc getPathTranslated*(): string = proc getPathTranslated*(): string =
## returns contents of the ``PATH_TRANSLATED`` environment variable ## Returns contents of the `PATH_TRANSLATED` environment variable.
return getEnv("PATH_TRANSLATED").string return getEnv("PATH_TRANSLATED").string
proc getQueryString*(): string = proc getQueryString*(): string =
## returns contents of the ``QUERY_STRING`` environment variable ## Returns contents of the `QUERY_STRING` environment variable.
return getEnv("QUERY_STRING").string return getEnv("QUERY_STRING").string
proc getRemoteAddr*(): string = proc getRemoteAddr*(): string =
## returns contents of the ``REMOTE_ADDR`` environment variable ## Returns contents of the `REMOTE_ADDR` environment variable.
return getEnv("REMOTE_ADDR").string return getEnv("REMOTE_ADDR").string
proc getRemoteHost*(): string = proc getRemoteHost*(): string =
## returns contents of the ``REMOTE_HOST`` environment variable ## Returns contents of the `REMOTE_HOST` environment variable.
return getEnv("REMOTE_HOST").string return getEnv("REMOTE_HOST").string
proc getRemoteIdent*(): string = proc getRemoteIdent*(): string =
## returns contents of the ``REMOTE_IDENT`` environment variable ## Returns contents of the `REMOTE_IDENT` environment variable.
return getEnv("REMOTE_IDENT").string return getEnv("REMOTE_IDENT").string
proc getRemotePort*(): string = proc getRemotePort*(): string =
## returns contents of the ``REMOTE_PORT`` environment variable ## Returns contents of the `REMOTE_PORT` environment variable.
return getEnv("REMOTE_PORT").string return getEnv("REMOTE_PORT").string
proc getRemoteUser*(): string = proc getRemoteUser*(): string =
## returns contents of the ``REMOTE_USER`` environment variable ## Returns contents of the `REMOTE_USER` environment variable.
return getEnv("REMOTE_USER").string return getEnv("REMOTE_USER").string
proc getRequestMethod*(): string = proc getRequestMethod*(): string =
## returns contents of the ``REQUEST_METHOD`` environment variable ## Returns contents of the `REQUEST_METHOD` environment variable.
return getEnv("REQUEST_METHOD").string return getEnv("REQUEST_METHOD").string
proc getRequestURI*(): string = proc getRequestURI*(): string =
## returns contents of the ``REQUEST_URI`` environment variable ## Returns contents of the `REQUEST_URI` environment variable.
return getEnv("REQUEST_URI").string return getEnv("REQUEST_URI").string
proc getScriptFilename*(): string = proc getScriptFilename*(): string =
## returns contents of the ``SCRIPT_FILENAME`` environment variable ## Returns contents of the `SCRIPT_FILENAME` environment variable.
return getEnv("SCRIPT_FILENAME").string return getEnv("SCRIPT_FILENAME").string
proc getScriptName*(): string = proc getScriptName*(): string =
## returns contents of the ``SCRIPT_NAME`` environment variable ## Returns contents of the `SCRIPT_NAME` environment variable.
return getEnv("SCRIPT_NAME").string return getEnv("SCRIPT_NAME").string
proc getServerAddr*(): string = proc getServerAddr*(): string =
## returns contents of the ``SERVER_ADDR`` environment variable ## Returns contents of the `SERVER_ADDR` environment variable.
return getEnv("SERVER_ADDR").string return getEnv("SERVER_ADDR").string
proc getServerAdmin*(): string = proc getServerAdmin*(): string =
## returns contents of the ``SERVER_ADMIN`` environment variable ## Returns contents of the `SERVER_ADMIN` environment variable.
return getEnv("SERVER_ADMIN").string return getEnv("SERVER_ADMIN").string
proc getServerName*(): string = proc getServerName*(): string =
## returns contents of the ``SERVER_NAME`` environment variable ## Returns contents of the `SERVER_NAME` environment variable.
return getEnv("SERVER_NAME").string return getEnv("SERVER_NAME").string
proc getServerPort*(): string = proc getServerPort*(): string =
## returns contents of the ``SERVER_PORT`` environment variable ## Returns contents of the `SERVER_PORT` environment variable.
return getEnv("SERVER_PORT").string return getEnv("SERVER_PORT").string
proc getServerProtocol*(): string = proc getServerProtocol*(): string =
## returns contents of the ``SERVER_PROTOCOL`` environment variable ## Returns contents of the `SERVER_PROTOCOL` environment variable.
return getEnv("SERVER_PROTOCOL").string return getEnv("SERVER_PROTOCOL").string
proc getServerSignature*(): string = proc getServerSignature*(): string =
## returns contents of the ``SERVER_SIGNATURE`` environment variable ## Returns contents of the `SERVER_SIGNATURE` environment variable.
return getEnv("SERVER_SIGNATURE").string return getEnv("SERVER_SIGNATURE").string
proc getServerSoftware*(): string = proc getServerSoftware*(): string =
## returns contents of the ``SERVER_SOFTWARE`` environment variable ## Returns contents of the `SERVER_SOFTWARE` environment variable.
return getEnv("SERVER_SOFTWARE").string return getEnv("SERVER_SOFTWARE").string
proc setTestData*(keysvalues: varargs[string]) = proc setTestData*(keysvalues: varargs[string]) =
## fills the appropriate environment variables to test your CGI application. ## Fills the appropriate environment variables to test your CGI application.
## This can only simulate the 'GET' request method. `keysvalues` should ## This can only simulate the 'GET' request method. `keysvalues` should
## provide embedded (name, value)-pairs. Example: ## provide embedded (name, value)-pairs. Example:
## ##
@ -273,7 +270,7 @@ proc setTestData*(keysvalues: varargs[string]) =
putEnv("QUERY_STRING", query) putEnv("QUERY_STRING", query)
proc writeContentType*() = proc writeContentType*() =
## call this before starting to send your HTML data to `stdout`. This ## Calls this before starting to send your HTML data to `stdout`. This
## implements this part of the CGI protocol: ## implements this part of the CGI protocol:
## ##
## .. code-block:: Nim ## .. code-block:: Nim

View file

@ -1,8 +1,4 @@
discard """ import std/[cgi, strtabs]
output: "works"
"""
import cgi, strtabs
proc handleRequest(query: string): StringTableRef = proc handleRequest(query: string): StringTableRef =
iterator foo(): StringTableRef {.closure.} = iterator foo(): StringTableRef {.closure.} =
@ -26,4 +22,3 @@ proc main =
quit "but now a leak" quit "but now a leak"
main() main()
echo "works"

View file

@ -1,25 +1,10 @@
discard """ import std/unittest
output: ''' import std/[cgi, strtabs, sugar]
[Suite] Test cgi module block: # Test cgi module
(key: "a", value: "1")
(key: "b", value: "0")
(key: "c", value: "3")
(key: "d", value: "")
(key: "e", value: "")
(key: "a", value: "5")
(key: "a", value: "t e x t")
(key: "e", value: "http://w3schools.com/my test.asp?name=ståle&car=saab")
'''
"""
import unittest
import cgi, strtabs
suite "Test cgi module":
const queryString = "foo=bar&фу=бар&checked=✓&list=1,2,3&with_space=text%20with%20space" const queryString = "foo=bar&фу=бар&checked=✓&list=1,2,3&with_space=text%20with%20space"
test "test query parsing with readData": block: # test query parsing with readData
let parsedQuery = readData(queryString) let parsedQuery = readData(queryString)
check parsedQuery["foo"] == "bar" check parsedQuery["foo"] == "bar"
@ -34,5 +19,8 @@ suite "Test cgi module":
# bug #15369 # bug #15369
let queryString = "a=1&b=0&c=3&d&e&a=5&a=t%20e%20x%20t&e=http%3A%2F%2Fw3schools.com%2Fmy%20test.asp%3Fname%3Dst%C3%A5le%26car%3Dsaab" let queryString = "a=1&b=0&c=3&d&e&a=5&a=t%20e%20x%20t&e=http%3A%2F%2Fw3schools.com%2Fmy%20test.asp%3Fname%3Dst%C3%A5le%26car%3Dsaab"
for pair in decodeData(queryString): doAssert collect(for pair in decodeData(queryString): pair) ==
echo pair @[("a", "1"), ("b", "0"), ("c", "3"),
("d", ""),("e", ""), ("a", "5"), ("a", "t e x t"),
("e", "http://w3schools.com/my test.asp?name=ståle&car=saab")
]