sockets.recv optimizations; stdlib now supports taint mode
This commit is contained in:
parent
485c371942
commit
0f37d0e1f2
14 changed files with 112 additions and 74 deletions
|
|
@ -3402,3 +3402,25 @@ The interaction between threads and exceptions is simple: A *handled* exception
|
||||||
in one thread cannot affect any other thread. However, an *unhandled*
|
in one thread cannot affect any other thread. However, an *unhandled*
|
||||||
exception in one thread terminates the whole *process*!
|
exception in one thread terminates the whole *process*!
|
||||||
|
|
||||||
|
Taint mode
|
||||||
|
==========
|
||||||
|
|
||||||
|
The Nimrod compiler and standard library support a `taint mode`:idx:.
|
||||||
|
Input strings are declared with the `TaintedString`:idx: string type declared
|
||||||
|
in the ``system`` module.
|
||||||
|
|
||||||
|
If the taint mode is turned on (via the ``--taintMode:on`` command line
|
||||||
|
option) it is a distinct string type which helps to detect input
|
||||||
|
validation errors:
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
echo "your name: "
|
||||||
|
var name: TaintedString = stdin.readline
|
||||||
|
# it is safe here to output the name without any input validation, so
|
||||||
|
# we simply convert `name` to string to make the compiler happy:
|
||||||
|
echo "hi, ", name.string
|
||||||
|
|
||||||
|
If the taint mode is turned off, ``TaintedString`` is simply an alias for
|
||||||
|
``string``.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
## wanted functionality.
|
## wanted functionality.
|
||||||
|
|
||||||
when defined(Windows):
|
when defined(Windows):
|
||||||
proc ReadLineFromStdin*(prompt: string): string =
|
proc ReadLineFromStdin*(prompt: string): TaintedString =
|
||||||
## Reads a line from stdin.
|
## Reads a line from stdin.
|
||||||
stdout.write(prompt)
|
stdout.write(prompt)
|
||||||
result = readLine(stdin)
|
result = readLine(stdin)
|
||||||
|
|
@ -22,11 +22,11 @@ when defined(Windows):
|
||||||
else:
|
else:
|
||||||
import readline, history
|
import readline, history
|
||||||
|
|
||||||
proc ReadLineFromStdin*(prompt: string): string =
|
proc ReadLineFromStdin*(prompt: string): TaintedString =
|
||||||
var buffer = readline.readLine(prompt)
|
var buffer = readline.readLine(prompt)
|
||||||
if isNil(buffer): quit(0)
|
if isNil(buffer): quit(0)
|
||||||
result = $buffer
|
result = TaintedString($buffer)
|
||||||
if result.len > 0:
|
if result.string.len > 0:
|
||||||
add_history(buffer)
|
add_history(buffer)
|
||||||
readline.free(buffer)
|
readline.free(buffer)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,11 +50,11 @@ proc connect*(sock: var TSecureSocket, address: string,
|
||||||
|
|
||||||
result = SSL_get_verify_result(sock.ssl)
|
result = SSL_get_verify_result(sock.ssl)
|
||||||
|
|
||||||
proc recvLine*(sock: TSecureSocket, line: var string): bool =
|
proc recvLine*(sock: TSecureSocket, line: var TaintedString): bool =
|
||||||
## Acts in a similar fashion to the `recvLine` in the sockets module.
|
## Acts in a similar fashion to the `recvLine` in the sockets module.
|
||||||
## Returns false when no data is available to be read.
|
## Returns false when no data is available to be read.
|
||||||
## `Line` must be initialized and not nil!
|
## `Line` must be initialized and not nil!
|
||||||
setLen(line, 0)
|
setLen(line.string, 0)
|
||||||
while True:
|
while True:
|
||||||
var c: array[0..0, char]
|
var c: array[0..0, char]
|
||||||
var n = BIO_read(sock.bio, c, c.len)
|
var n = BIO_read(sock.bio, c, c.len)
|
||||||
|
|
@ -66,7 +66,7 @@ proc recvLine*(sock: TSecureSocket, line: var string): bool =
|
||||||
elif n <= 0:
|
elif n <= 0:
|
||||||
return False
|
return False
|
||||||
elif c[0] == '\L': return True
|
elif c[0] == '\L': return True
|
||||||
add(line, c)
|
add(line.string, c)
|
||||||
|
|
||||||
|
|
||||||
proc send*(sock: TSecureSocket, data: string) =
|
proc send*(sock: TSecureSocket, data: string) =
|
||||||
|
|
|
||||||
|
|
@ -43,19 +43,19 @@ proc URLretrieveStream*(url: string): PStream =
|
||||||
if easy_perform(hCurl) != E_OK: return nil
|
if easy_perform(hCurl) != E_OK: return nil
|
||||||
easy_cleanup(hCurl)
|
easy_cleanup(hCurl)
|
||||||
|
|
||||||
proc URLretrieveString*(url: string): string =
|
proc URLretrieveString*(url: string): TaintedString =
|
||||||
## retrieves the given `url` and returns the contents. Returns nil if an
|
## retrieves the given `url` and returns the contents. Returns nil if an
|
||||||
## error occurs.
|
## error occurs.
|
||||||
var stream = newStringStream()
|
var stream = newStringStream()
|
||||||
var hCurl = easy_init()
|
var hCurl = easy_init()
|
||||||
if hCurl == nil: return nil
|
if hCurl == nil: return
|
||||||
if easy_setopt(hCurl, OPT_URL, url) != E_OK: return nil
|
if easy_setopt(hCurl, OPT_URL, url) != E_OK: return
|
||||||
if easy_setopt(hCurl, OPT_WRITEFUNCTION,
|
if easy_setopt(hCurl, OPT_WRITEFUNCTION,
|
||||||
curlwrapperWrite) != E_OK: return nil
|
curlwrapperWrite) != E_OK: return
|
||||||
if easy_setopt(hCurl, OPT_WRITEDATA, stream) != E_OK: return nil
|
if easy_setopt(hCurl, OPT_WRITEDATA, stream) != E_OK: return
|
||||||
if easy_perform(hCurl) != E_OK: return nil
|
if easy_perform(hCurl) != E_OK: return
|
||||||
easy_cleanup(hCurl)
|
easy_cleanup(hCurl)
|
||||||
result = stream.data
|
result = stream.data.TaintedString
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
echo URLretrieveString("http://nimrod.ethexor.com/")
|
echo URLretrieveString("http://nimrod.ethexor.com/")
|
||||||
|
|
|
||||||
|
|
@ -111,19 +111,19 @@ proc getEncodedData(allowedMethods: set[TRequestMethod]): string =
|
||||||
of "POST":
|
of "POST":
|
||||||
if methodPost notin allowedMethods:
|
if methodPost notin allowedMethods:
|
||||||
cgiError("'REQUEST_METHOD' 'POST' is not supported")
|
cgiError("'REQUEST_METHOD' 'POST' is not supported")
|
||||||
var L = parseInt(getenv("CONTENT_LENGTH"))
|
var L = parseInt(getenv("CONTENT_LENGTH").string)
|
||||||
result = newString(L)
|
result = newString(L)
|
||||||
if readBuffer(stdin, addr(result[0]), L) != L:
|
if readBuffer(stdin, addr(result[0]), L) != L:
|
||||||
cgiError("cannot read from stdin")
|
cgiError("cannot read from stdin")
|
||||||
of "GET":
|
of "GET":
|
||||||
if methodGet notin allowedMethods:
|
if methodGet notin allowedMethods:
|
||||||
cgiError("'REQUEST_METHOD' 'GET' is not supported")
|
cgiError("'REQUEST_METHOD' 'GET' is not supported")
|
||||||
result = getenv("QUERY_STRING")
|
result = getenv("QUERY_STRING").string
|
||||||
else:
|
else:
|
||||||
if methodNone notin allowedMethods:
|
if methodNone notin allowedMethods:
|
||||||
cgiError("'REQUEST_METHOD' must be 'POST' or 'GET'")
|
cgiError("'REQUEST_METHOD' must be 'POST' or 'GET'")
|
||||||
|
|
||||||
iterator decodeData*(data: string): tuple[key, value: string] =
|
iterator decodeData*(data: string): 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.
|
## data consists of.
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
@ -160,13 +160,13 @@ iterator decodeData*(data: string): tuple[key, value: string] =
|
||||||
of '&', '\0': break
|
of '&', '\0': break
|
||||||
else: add(value, data[i])
|
else: add(value, data[i])
|
||||||
inc(i)
|
inc(i)
|
||||||
yield (name, value)
|
yield (name.TaintedString, value.TaintedString)
|
||||||
if data[i] == '&': inc(i)
|
if data[i] == '&': inc(i)
|
||||||
elif data[i] == '\0': break
|
elif data[i] == '\0': break
|
||||||
else: cgiError("'&' expected")
|
else: cgiError("'&' expected")
|
||||||
|
|
||||||
iterator decodeData*(allowedMethods: set[TRequestMethod] =
|
iterator decodeData*(allowedMethods: set[TRequestMethod] =
|
||||||
{methodNone, methodPost, methodGet}): tuple[key, value: string] =
|
{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, an `ECgi` exception is raised.
|
## `allowedMethods` set, an `ECgi` exception is raised.
|
||||||
|
|
@ -181,7 +181,7 @@ proc readData*(allowedMethods: set[TRequestMethod] =
|
||||||
## `allowedMethods` set, an `ECgi` exception is raised.
|
## `allowedMethods` set, an `ECgi` exception is raised.
|
||||||
result = newStringTable()
|
result = newStringTable()
|
||||||
for name, value in decodeData(allowedMethods):
|
for name, value in decodeData(allowedMethods):
|
||||||
result[name] = value
|
result[name.string] = value.string
|
||||||
|
|
||||||
proc validateData*(data: PStringTable, validKeys: openarray[string]) =
|
proc validateData*(data: PStringTable, validKeys: openarray[string]) =
|
||||||
## validates data; raises `ECgi` if this fails. This checks that each variable
|
## validates data; raises `ECgi` if this fails. This checks that each variable
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ proc fileError(msg: string) =
|
||||||
proc charAt(d: var string, i: var int, s: TSocket): char {.inline.} =
|
proc charAt(d: var string, i: var int, s: TSocket): char {.inline.} =
|
||||||
result = d[i]
|
result = d[i]
|
||||||
while result == '\0':
|
while result == '\0':
|
||||||
d = s.recv()
|
d = string(s.recv())
|
||||||
i = 0
|
i = 0
|
||||||
result = d[i]
|
result = d[i]
|
||||||
|
|
||||||
|
|
@ -98,7 +98,7 @@ proc parseChunks(d: var string, start: int, s: TSocket): string =
|
||||||
digitFound = true
|
digitFound = true
|
||||||
chunkSize = chunkSize shl 4 or (ord(d[i]) - ord('A') + 10)
|
chunkSize = chunkSize shl 4 or (ord(d[i]) - ord('A') + 10)
|
||||||
of '\0':
|
of '\0':
|
||||||
d = s.recv()
|
d = string(s.recv())
|
||||||
i = -1
|
i = -1
|
||||||
else: break
|
else: break
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
@ -123,7 +123,7 @@ proc parseChunks(d: var string, start: int, s: TSocket): string =
|
||||||
inc(L, bytesRead)
|
inc(L, bytesRead)
|
||||||
dec(missing, bytesRead)
|
dec(missing, bytesRead)
|
||||||
# next chunk:
|
# next chunk:
|
||||||
d = s.recv()
|
d = string(s.recv())
|
||||||
i = 0
|
i = 0
|
||||||
# skip trailing CR-LF:
|
# skip trailing CR-LF:
|
||||||
while charAt(d, i, s) in {'\C', '\L'}: inc(i)
|
while charAt(d, i, s) in {'\C', '\L'}: inc(i)
|
||||||
|
|
@ -139,7 +139,7 @@ proc parseBody(d: var string, start: int, s: TSocket,
|
||||||
var contentLengthHeader = headers["Content-Length"]
|
var contentLengthHeader = headers["Content-Length"]
|
||||||
if contentLengthHeader != "":
|
if contentLengthHeader != "":
|
||||||
var length = contentLengthHeader.parseint()
|
var length = contentLengthHeader.parseint()
|
||||||
while result.len() < length: result.add(s.recv())
|
while result.len() < length: result.add(s.recv.string)
|
||||||
else:
|
else:
|
||||||
# (http://tools.ietf.org/html/rfc2616#section-4.4) NR.4 TODO
|
# (http://tools.ietf.org/html/rfc2616#section-4.4) NR.4 TODO
|
||||||
|
|
||||||
|
|
@ -147,12 +147,12 @@ proc parseBody(d: var string, start: int, s: TSocket,
|
||||||
# (http://tools.ietf.org/html/rfc2616#section-4.4) NR.5
|
# (http://tools.ietf.org/html/rfc2616#section-4.4) NR.5
|
||||||
if headers["Connection"] == "close":
|
if headers["Connection"] == "close":
|
||||||
while True:
|
while True:
|
||||||
var moreData = recv(s)
|
var moreData = recv(s).string
|
||||||
if moreData.len == 0: break
|
if moreData.len == 0: break
|
||||||
result.add(moreData)
|
result.add(moreData)
|
||||||
|
|
||||||
proc parseResponse(s: TSocket): TResponse =
|
proc parseResponse(s: TSocket): TResponse =
|
||||||
var d = s.recv()
|
var d = s.recv.string
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
||||||
# Parse the version
|
# Parse the version
|
||||||
|
|
|
||||||
|
|
@ -113,11 +113,11 @@ proc executeCgi(client: TSocket, path, query: string, meth: TRequestMethod) =
|
||||||
env["REQUEST_METHOD"] = "GET"
|
env["REQUEST_METHOD"] = "GET"
|
||||||
env["QUERY_STRING"] = query
|
env["QUERY_STRING"] = query
|
||||||
of reqPost:
|
of reqPost:
|
||||||
var buf = ""
|
var buf = TaintedString""
|
||||||
var dataAvail = false
|
var dataAvail = false
|
||||||
while dataAvail:
|
while dataAvail:
|
||||||
dataAvail = recvLine(client, buf)
|
dataAvail = recvLine(client, buf)
|
||||||
var L = toLower(buf)
|
var L = toLower(buf.string)
|
||||||
if L.startsWith("content-length:"):
|
if L.startsWith("content-length:"):
|
||||||
var i = len("content-length:")
|
var i = len("content-length:")
|
||||||
while L[i] in Whitespace: inc(i)
|
while L[i] in Whitespace: inc(i)
|
||||||
|
|
@ -146,7 +146,7 @@ proc executeCgi(client: TSocket, path, query: string, meth: TRequestMethod) =
|
||||||
var outp = process.outputStream
|
var outp = process.outputStream
|
||||||
while running(process) or not outp.atEnd(outp):
|
while running(process) or not outp.atEnd(outp):
|
||||||
var line = outp.readLine()
|
var line = outp.readLine()
|
||||||
send(client, line)
|
send(client, line.string)
|
||||||
send(client, wwwNL)
|
send(client, wwwNL)
|
||||||
|
|
||||||
# --------------- Server Setup -----------------------------------------------
|
# --------------- Server Setup -----------------------------------------------
|
||||||
|
|
@ -154,10 +154,10 @@ proc executeCgi(client: TSocket, path, query: string, meth: TRequestMethod) =
|
||||||
proc acceptRequest(client: TSocket) =
|
proc acceptRequest(client: TSocket) =
|
||||||
var cgi = false
|
var cgi = false
|
||||||
var query = ""
|
var query = ""
|
||||||
var buf = ""
|
var buf = TaintedString""
|
||||||
discard recvLine(client, buf)
|
discard recvLine(client, buf)
|
||||||
var path = ""
|
var path = ""
|
||||||
var data = buf.split()
|
var data = buf.string.split()
|
||||||
var meth = reqGet
|
var meth = reqGet
|
||||||
|
|
||||||
var q = find(data[1], '?')
|
var q = find(data[1], '?')
|
||||||
|
|
@ -231,7 +231,7 @@ proc next*(s: var TServer) =
|
||||||
## proceed to the first/next request.
|
## proceed to the first/next request.
|
||||||
s.client = accept(s.socket)
|
s.client = accept(s.socket)
|
||||||
headers(s.client, "")
|
headers(s.client, "")
|
||||||
var data = recv(s.client)
|
var data = recv(s.client).string
|
||||||
#discard recvLine(s.client, data)
|
#discard recvLine(s.client, data)
|
||||||
|
|
||||||
var i = skipWhitespace(data)
|
var i = skipWhitespace(data)
|
||||||
|
|
|
||||||
|
|
@ -205,15 +205,15 @@ proc poll*(irc: var TIRC, ev: var TIRCEvent,
|
||||||
## This function should be called often as it also handles pinging
|
## This function should be called often as it also handles pinging
|
||||||
## the server.
|
## the server.
|
||||||
if not irc.connected: ev.typ = EvDisconnected
|
if not irc.connected: ev.typ = EvDisconnected
|
||||||
var line = ""
|
var line = TaintedString""
|
||||||
var socks = @[irc.sock]
|
var socks = @[irc.sock]
|
||||||
var ret = socks.select(timeout)
|
var ret = socks.select(timeout)
|
||||||
if socks.len() == 0 and ret == 1:
|
if socks.len() == 0 and ret == 1:
|
||||||
if irc.sock.recvLine(line):
|
if irc.sock.recvLine(line):
|
||||||
if line == "":
|
if line.string.len == 0:
|
||||||
ev.typ = EvDisconnected
|
ev.typ = EvDisconnected
|
||||||
else:
|
else:
|
||||||
ev = parseMessage(line)
|
ev = parseMessage(line.string)
|
||||||
if ev.cmd == MPing:
|
if ev.cmd == MPing:
|
||||||
irc.send("PONG " & ev.params[0])
|
irc.send("PONG " & ev.params[0])
|
||||||
if ev.cmd == MPong:
|
if ev.cmd == MPong:
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ type
|
||||||
|
|
||||||
proc execProcess*(command: string,
|
proc execProcess*(command: string,
|
||||||
options: set[TProcessOption] = {poStdErrToStdOut,
|
options: set[TProcessOption] = {poStdErrToStdOut,
|
||||||
poUseShell}): string {.
|
poUseShell}): TaintedString {.
|
||||||
rtl, extern: "nosp$1".}
|
rtl, extern: "nosp$1".}
|
||||||
## A convenience procedure that executes ``command`` with ``startProcess``
|
## A convenience procedure that executes ``command`` with ``startProcess``
|
||||||
## and returns its output as a string.
|
## and returns its output as a string.
|
||||||
|
|
@ -203,13 +203,13 @@ proc select*(readfds: var seq[PProcess], timeout = 500): int
|
||||||
when not defined(useNimRtl):
|
when not defined(useNimRtl):
|
||||||
proc execProcess(command: string,
|
proc execProcess(command: string,
|
||||||
options: set[TProcessOption] = {poStdErrToStdOut,
|
options: set[TProcessOption] = {poStdErrToStdOut,
|
||||||
poUseShell}): string =
|
poUseShell}): TaintedString =
|
||||||
var p = startProcessAux(command, options=options)
|
var p = startProcessAux(command, options=options)
|
||||||
var outp = outputStream(p)
|
var outp = outputStream(p)
|
||||||
result = ""
|
result = TaintedString""
|
||||||
while running(p) or not outp.atEnd(outp):
|
while running(p) or not outp.atEnd(outp):
|
||||||
result.add(outp.readLine())
|
result.string.add(outp.readLine().string)
|
||||||
result.add("\n")
|
result.string.add("\n")
|
||||||
outp.close(outp)
|
outp.close(outp)
|
||||||
close(p)
|
close(p)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ proc raiseNoOK(status: string) =
|
||||||
raise newException(EInvalidReply, "Expected \"OK\" got \"$1\"" % status)
|
raise newException(EInvalidReply, "Expected \"OK\" got \"$1\"" % status)
|
||||||
|
|
||||||
proc parseStatus(r: TRedis): TRedisStatus =
|
proc parseStatus(r: TRedis): TRedisStatus =
|
||||||
var line = r.socket.recv()
|
var line = r.socket.recv.string
|
||||||
|
|
||||||
if line[0] == '-':
|
if line[0] == '-':
|
||||||
raise newException(ERedis, strip(line))
|
raise newException(ERedis, strip(line))
|
||||||
|
|
@ -59,7 +59,7 @@ proc parseStatus(r: TRedis): TRedisStatus =
|
||||||
return line.substr(1, line.len-3) # Strip '+' and \c\L.
|
return line.substr(1, line.len-3) # Strip '+' and \c\L.
|
||||||
|
|
||||||
proc parseInteger(r: TRedis): TRedisInteger =
|
proc parseInteger(r: TRedis): TRedisInteger =
|
||||||
var line = r.socket.recv()
|
var line = r.socket.recv.string
|
||||||
|
|
||||||
if line[0] == '-':
|
if line[0] == '-':
|
||||||
raise newException(ERedis, strip(line))
|
raise newException(ERedis, strip(line))
|
||||||
|
|
@ -70,14 +70,14 @@ proc parseInteger(r: TRedis): TRedisInteger =
|
||||||
if parseBiggestInt(line, result, 1) == 0:
|
if parseBiggestInt(line, result, 1) == 0:
|
||||||
raise newException(EInvalidReply, "Unable to parse integer.")
|
raise newException(EInvalidReply, "Unable to parse integer.")
|
||||||
|
|
||||||
proc recv(sock: TSocket, size: int): string =
|
proc recv(sock: TSocket, size: int): TaintedString =
|
||||||
result = newString(size)
|
result = newString(size).TaintedString
|
||||||
if sock.recv(cstring(result), size) != size:
|
if sock.recv(cstring(result), size) != size:
|
||||||
raise newException(EInvalidReply, "recv failed")
|
raise newException(EInvalidReply, "recv failed")
|
||||||
|
|
||||||
proc parseBulk(r: TRedis, allowMBNil = False): TRedisString =
|
proc parseBulk(r: TRedis, allowMBNil = False): TRedisString =
|
||||||
var line = ""
|
var line = ""
|
||||||
if not r.socket.recvLine(line):
|
if not r.socket.recvLine(line.TaintedString):
|
||||||
raise newException(EInvalidReply, "recvLine failed")
|
raise newException(EInvalidReply, "recvLine failed")
|
||||||
|
|
||||||
# Error.
|
# Error.
|
||||||
|
|
@ -97,17 +97,17 @@ proc parseBulk(r: TRedis, allowMBNil = False): TRedisString =
|
||||||
return RedisNil
|
return RedisNil
|
||||||
|
|
||||||
var s = r.socket.recv(numBytes+2)
|
var s = r.socket.recv(numBytes+2)
|
||||||
result = strip(s)
|
result = strip(s.string)
|
||||||
|
|
||||||
proc parseMultiBulk(r: TRedis): TRedisList =
|
proc parseMultiBulk(r: TRedis): TRedisList =
|
||||||
var line = ""
|
var line = TaintedString""
|
||||||
if not r.socket.recvLine(line):
|
if not r.socket.recvLine(line):
|
||||||
raise newException(EInvalidReply, "recvLine failed")
|
raise newException(EInvalidReply, "recvLine failed")
|
||||||
|
|
||||||
if line[0] != '*':
|
if line.string[0] != '*':
|
||||||
raiseInvalidReply('*', line[0])
|
raiseInvalidReply('*', line.string[0])
|
||||||
|
|
||||||
var numElems = parseInt(line.substr(1))
|
var numElems = parseInt(line.string.substr(1))
|
||||||
if numElems == -1: return nil
|
if numElems == -1: return nil
|
||||||
result = @[]
|
result = @[]
|
||||||
for i in 1..numElems:
|
for i in 1..numElems:
|
||||||
|
|
@ -839,7 +839,7 @@ proc shutdown*(r: TRedis) =
|
||||||
## Synchronously save the dataset to disk and then shut down the server
|
## Synchronously save the dataset to disk and then shut down the server
|
||||||
r.sendCommand("SHUTDOWN")
|
r.sendCommand("SHUTDOWN")
|
||||||
var s = r.socket.recv()
|
var s = r.socket.recv()
|
||||||
if s != "": raise newException(ERedis, s)
|
if s.string.len != 0: raise newException(ERedis, s.string)
|
||||||
|
|
||||||
proc slaveof*(r: TRedis, host: string, port: string) =
|
proc slaveof*(r: TRedis, host: string, port: string) =
|
||||||
## Make the server a slave of another instance, or promote it as master
|
## Make the server a slave of another instance, or promote it as master
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,8 @@ proc debugSend(smtp: TSMTP, cmd: string) =
|
||||||
when not defined(noSSL):
|
when not defined(noSSL):
|
||||||
smtp.sslSock.send(cmd)
|
smtp.sslSock.send(cmd)
|
||||||
|
|
||||||
proc debugRecv(smtp: TSMTP): string =
|
proc debugRecv(smtp: TSMTP): TaintedString =
|
||||||
var line = ""
|
var line = TaintedString""
|
||||||
var ret = False
|
var ret = False
|
||||||
if not smtp.ssl:
|
if not smtp.ssl:
|
||||||
ret = smtp.sock.recvLine(line)
|
ret = smtp.sock.recvLine(line)
|
||||||
|
|
@ -69,11 +69,11 @@ proc debugRecv(smtp: TSMTP): string =
|
||||||
ret = smtp.sslSock.recvLine(line)
|
ret = smtp.sslSock.recvLine(line)
|
||||||
if ret:
|
if ret:
|
||||||
if smtp.debug:
|
if smtp.debug:
|
||||||
echo("S:" & line)
|
echo("S:" & line.string)
|
||||||
return line
|
return line
|
||||||
else:
|
else:
|
||||||
OSError()
|
OSError()
|
||||||
return ""
|
return TaintedString""
|
||||||
|
|
||||||
proc quitExcpt(smtp: TSMTP, msg: string) =
|
proc quitExcpt(smtp: TSMTP, msg: string) =
|
||||||
smtp.debugSend("QUIT")
|
smtp.debugSend("QUIT")
|
||||||
|
|
@ -81,8 +81,8 @@ proc quitExcpt(smtp: TSMTP, msg: string) =
|
||||||
|
|
||||||
proc checkReply(smtp: TSMTP, reply: string) =
|
proc checkReply(smtp: TSMTP, reply: string) =
|
||||||
var line = smtp.debugRecv()
|
var line = smtp.debugRecv()
|
||||||
if not line.startswith(reply):
|
if not line.string.startswith(reply):
|
||||||
quitExcpt(smtp, "Expected " & reply & " reply, got: " & line)
|
quitExcpt(smtp, "Expected " & reply & " reply, got: " & line.string)
|
||||||
|
|
||||||
proc connect*(address: string, port = 25,
|
proc connect*(address: string, port = 25,
|
||||||
ssl = false, debug = false): TSMTP =
|
ssl = false, debug = false): TSMTP =
|
||||||
|
|
|
||||||
|
|
@ -553,17 +553,28 @@ proc recv*(socket: TSocket): TaintedString =
|
||||||
## If socket is not a connectionless socket and socket is not connected
|
## If socket is not a connectionless socket and socket is not connected
|
||||||
## ``""`` will be returned.
|
## ``""`` will be returned.
|
||||||
const bufSize = 1000
|
const bufSize = 1000
|
||||||
var buf = newString(bufSize)
|
result = newStringOfCap(bufSize).TaintedString
|
||||||
result = TaintedString""
|
var pos = 0
|
||||||
while true:
|
while true:
|
||||||
var bytesRead = recv(socket, cstring(buf), bufSize-1)
|
var bytesRead = recv(socket, addr(string(result)[pos]), bufSize-1)
|
||||||
# Error
|
|
||||||
if bytesRead == -1: OSError()
|
if bytesRead == -1: OSError()
|
||||||
|
setLen(result.string, pos + bytesRead)
|
||||||
buf[bytesRead] = '\0' # might not be necessary
|
|
||||||
setLen(buf, bytesRead)
|
|
||||||
add(result.string, buf)
|
|
||||||
if bytesRead != bufSize-1: break
|
if bytesRead != bufSize-1: break
|
||||||
|
# increase capacity:
|
||||||
|
setLen(result.string, result.string.len + bufSize)
|
||||||
|
inc(pos, bytesRead)
|
||||||
|
when false:
|
||||||
|
var buf = newString(bufSize)
|
||||||
|
result = TaintedString""
|
||||||
|
while true:
|
||||||
|
var bytesRead = recv(socket, cstring(buf), bufSize-1)
|
||||||
|
# Error
|
||||||
|
if bytesRead == -1: OSError()
|
||||||
|
|
||||||
|
buf[bytesRead] = '\0' # might not be necessary
|
||||||
|
setLen(buf, bytesRead)
|
||||||
|
add(result.string, buf)
|
||||||
|
if bytesRead != bufSize-1: break
|
||||||
|
|
||||||
proc recvAsync*(socket: TSocket, s: var TaintedString): bool =
|
proc recvAsync*(socket: TSocket, s: var TaintedString): bool =
|
||||||
## receives all the data from a non-blocking socket. If socket is non-blocking
|
## receives all the data from a non-blocking socket. If socket is non-blocking
|
||||||
|
|
@ -572,11 +583,12 @@ proc recvAsync*(socket: TSocket, s: var TaintedString): bool =
|
||||||
## If socket is not a connectionless socket and socket is not connected
|
## If socket is not a connectionless socket and socket is not connected
|
||||||
## ``s`` will be set to ``""``.
|
## ``s`` will be set to ``""``.
|
||||||
const bufSize = 1000
|
const bufSize = 1000
|
||||||
var buf = newString(bufSize)
|
# ensure bufSize capacity:
|
||||||
s = ""
|
setLen(s.string, bufSize)
|
||||||
|
setLen(s.string, 0)
|
||||||
|
var pos = 0
|
||||||
while true:
|
while true:
|
||||||
var bytesRead = recv(socket, cstring(buf), bufSize-1)
|
var bytesRead = recv(socket, addr(string(s)[pos]), bufSize-1)
|
||||||
# Error
|
|
||||||
if bytesRead == -1:
|
if bytesRead == -1:
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
# TODO: Test on Windows
|
# TODO: Test on Windows
|
||||||
|
|
@ -588,11 +600,12 @@ proc recvAsync*(socket: TSocket, s: var TaintedString): bool =
|
||||||
if errno == EAGAIN or errno == EWOULDBLOCK:
|
if errno == EAGAIN or errno == EWOULDBLOCK:
|
||||||
return False
|
return False
|
||||||
else: OSError()
|
else: OSError()
|
||||||
|
|
||||||
buf[bytesRead] = '\0' # might not be necessary
|
setLen(s.string, pos + bytesRead)
|
||||||
setLen(buf, bytesRead)
|
|
||||||
add(s, buf)
|
|
||||||
if bytesRead != bufSize-1: break
|
if bytesRead != bufSize-1: break
|
||||||
|
# increase capacity:
|
||||||
|
setLen(s.string, s.string.len + bufSize)
|
||||||
|
inc(pos, bytesRead)
|
||||||
result = True
|
result = True
|
||||||
|
|
||||||
proc skip*(socket: TSocket) =
|
proc skip*(socket: TSocket) =
|
||||||
|
|
|
||||||
1
todo.txt
1
todo.txt
|
|
@ -1,7 +1,6 @@
|
||||||
Version 0.8.14
|
Version 0.8.14
|
||||||
==============
|
==============
|
||||||
|
|
||||||
- taint mode
|
|
||||||
- 'let x = y'; const ptr/ref
|
- 'let x = y'; const ptr/ref
|
||||||
- fix actors.nim
|
- fix actors.nim
|
||||||
- make threadvar efficient again on linux after testing
|
- make threadvar efficient again on linux after testing
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,10 @@ Compiler Additions
|
||||||
and Objective C somewhat easier.
|
and Objective C somewhat easier.
|
||||||
- Added a ``--nimcache:PATH`` configuration option for control over the output
|
- Added a ``--nimcache:PATH`` configuration option for control over the output
|
||||||
directory for generated code.
|
directory for generated code.
|
||||||
|
- The compiler and standard library now support a *taint mode*. Input strings
|
||||||
|
are declared with the ``TaintedString`` string type. If the taint
|
||||||
|
mode is turned on it is a distinct string type which helps to detect input
|
||||||
|
validation errors.
|
||||||
|
|
||||||
|
|
||||||
Library Additions
|
Library Additions
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue