parent
792e4a0392
commit
e718a4a058
3 changed files with 57 additions and 77 deletions
|
|
@ -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, "&")
|
of '&': add(dest, "&")
|
||||||
|
|
@ -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 ``"``
|
## * `"` is replaced by `"`
|
||||||
## * ``<`` is replaced by ``<``
|
## * `<` is replaced by `<`
|
||||||
## * ``>`` is replaced by ``>``
|
## * `>` is replaced by `>`
|
||||||
## * ``&`` is replaced by ``&``
|
## * `&` is replaced by `&`
|
||||||
## * 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
|
||||||
|
|
|
||||||
|
|
@ -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"
|
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue