Fixed OSError + recvLine deprecation warnings.

This commit is contained in:
Dominik Picheta 2013-06-27 15:06:30 +01:00
commit a9f2c3ffaf
4 changed files with 143 additions and 144 deletions

View file

@ -47,10 +47,11 @@ proc badRequest(client: TSocket) =
send(client, "<p>Your browser sent a bad request, " & send(client, "<p>Your browser sent a bad request, " &
"such as a POST without a Content-Length.</p>" & wwwNL) "such as a POST without a Content-Length.</p>" & wwwNL)
proc cannotExec(client: TSocket) = when false:
send(client, "HTTP/1.1 500 Internal Server Error" & wwwNL) proc cannotExec(client: TSocket) =
sendTextContentType(client) send(client, "HTTP/1.1 500 Internal Server Error" & wwwNL)
send(client, "<P>Error prohibited CGI execution." & wwwNL) sendTextContentType(client)
send(client, "<P>Error prohibited CGI execution." & wwwNL)
proc headers(client: TSocket, filename: string) = proc headers(client: TSocket, filename: string) =
# XXX could use filename to determine file type # XXX could use filename to determine file type
@ -79,11 +80,11 @@ proc unimplemented(client: TSocket) =
# ----------------- file serving --------------------------------------------- # ----------------- file serving ---------------------------------------------
proc discardHeaders(client: TSocket) = skip(client) when false:
proc discardHeaders(client: TSocket) = skip(client)
proc serveFile*(client: TSocket, filename: string) = proc serveFile*(client: TSocket, filename: string) =
## serves a file to the client. ## serves a file to the client.
when false: discardHeaders(client)
var f: TFile var f: TFile
if open(f, filename): if open(f, filename):
headers(client, filename) headers(client, filename)
@ -96,7 +97,7 @@ proc serveFile*(client: TSocket, filename: string) =
if bytesread != bytesWritten: if bytesread != bytesWritten:
dealloc(buf) dealloc(buf)
close(f) close(f)
OSError() OSError(OSLastError())
if bytesread != bufSize: break if bytesread != bufSize: break
dealloc(buf) dealloc(buf)
close(f) close(f)
@ -104,108 +105,109 @@ proc serveFile*(client: TSocket, filename: string) =
notFound(client) notFound(client)
# ------------------ CGI execution ------------------------------------------- # ------------------ CGI execution -------------------------------------------
when false:
# TODO: Fix this, or get rid of it.
type
TRequestMethod = enum reqGet, reqPost
type proc executeCgi(client: TSocket, path, query: string, meth: TRequestMethod) =
TRequestMethod = enum reqGet, reqPost var env = newStringTable(modeCaseInsensitive)
var contentLength = -1
case meth
of reqGet:
discardHeaders(client)
proc executeCgi(client: TSocket, path, query: string, meth: TRequestMethod) = env["REQUEST_METHOD"] = "GET"
var env = newStringTable(modeCaseInsensitive) env["QUERY_STRING"] = query
var contentLength = -1 of reqPost:
case meth var buf = TaintedString""
of reqGet: var dataAvail = false
discardHeaders(client) while dataAvail:
dataAvail = recvLine(client, buf) # TODO: This is incorrect.
var L = toLower(buf.string)
if L.startsWith("content-length:"):
var i = len("content-length:")
while L[i] in Whitespace: inc(i)
contentLength = parseInt(substr(L, i))
env["REQUEST_METHOD"] = "GET" if contentLength < 0:
env["QUERY_STRING"] = query badRequest(client)
of reqPost: return
var buf = TaintedString""
var dataAvail = false
while dataAvail:
dataAvail = recvLine(client, buf) # TODO: This is incorrect.
var L = toLower(buf.string)
if L.startsWith("content-length:"):
var i = len("content-length:")
while L[i] in Whitespace: inc(i)
contentLength = parseInt(substr(L, i))
if contentLength < 0: env["REQUEST_METHOD"] = "POST"
badRequest(client) env["CONTENT_LENGTH"] = $contentLength
return
env["REQUEST_METHOD"] = "POST" send(client, "HTTP/1.0 200 OK" & wwwNL)
env["CONTENT_LENGTH"] = $contentLength
send(client, "HTTP/1.0 200 OK" & wwwNL) var process = startProcess(command=path, env=env)
if meth == reqPost:
var process = startProcess(command=path, env=env) # get from client and post to CGI program:
if meth == reqPost: var buf = alloc(contentLength)
# get from client and post to CGI program: if recv(client, buf, contentLength) != contentLength:
var buf = alloc(contentLength) dealloc(buf)
if recv(client, buf, contentLength) != contentLength: OSError()
var inp = process.inputStream
inp.writeData(buf, contentLength)
dealloc(buf) dealloc(buf)
OSError()
var inp = process.inputStream
inp.writeData(buf, contentLength)
dealloc(buf)
var outp = process.outputStream var outp = process.outputStream
var line = newStringOfCap(120).TaintedString var line = newStringOfCap(120).TaintedString
while true: while true:
if outp.readLine(line): if outp.readLine(line):
send(client, line.string) send(client, line.string)
send(client, wwwNL) send(client, wwwNL)
elif not running(process): break elif not running(process): break
# --------------- Server Setup ----------------------------------------------- # --------------- Server Setup -----------------------------------------------
proc acceptRequest(client: TSocket) = proc acceptRequest(client: TSocket) =
var cgi = false var cgi = false
var query = "" var query = ""
var buf = TaintedString"" var buf = TaintedString""
discard recvLine(client, buf) discard recvLine(client, buf)
var path = "" var path = ""
var data = buf.string.split() var data = buf.string.split()
var meth = reqGet var meth = reqGet
var q = find(data[1], '?') var q = find(data[1], '?')
# extract path # extract path
if q >= 0:
# strip "?..." from path, this may be found in both POST and GET
path = "." & data[1].substr(0, q-1)
else:
path = "." & data[1]
# path starts with "/", by adding "." in front of it we serve files from cwd
if cmpIgnoreCase(data[0], "GET") == 0:
if q >= 0: if q >= 0:
# strip "?..." from path, this may be found in both POST and GET
path = "." & data[1].substr(0, q-1)
else:
path = "." & data[1]
# path starts with "/", by adding "." in front of it we serve files from cwd
if cmpIgnoreCase(data[0], "GET") == 0:
if q >= 0:
cgi = true
query = data[1].substr(q+1)
elif cmpIgnoreCase(data[0], "POST") == 0:
cgi = true cgi = true
query = data[1].substr(q+1) meth = reqPost
elif cmpIgnoreCase(data[0], "POST") == 0:
cgi = true
meth = reqPost
else:
unimplemented(client)
if path[path.len-1] == '/' or existsDir(path):
path = path / "index.html"
if not ExistsFile(path):
discardHeaders(client)
notFound(client)
else:
when defined(Windows):
var ext = splitFile(path).ext.toLower
if ext == ".exe" or ext == ".cgi":
# XXX: extract interpreter information here?
cgi = true
else: else:
if {fpUserExec, fpGroupExec, fpOthersExec} * path.getFilePermissions != {}: unimplemented(client)
cgi = true
if not cgi: if path[path.len-1] == '/' or existsDir(path):
serveFile(client, path) path = path / "index.html"
if not ExistsFile(path):
discardHeaders(client)
notFound(client)
else: else:
executeCgi(client, path, query, meth) when defined(Windows):
var ext = splitFile(path).ext.toLower
if ext == ".exe" or ext == ".cgi":
# XXX: extract interpreter information here?
cgi = true
else:
if {fpUserExec, fpGroupExec, fpOthersExec} * path.getFilePermissions != {}:
cgi = true
if not cgi:
serveFile(client, path)
else:
executeCgi(client, path, query, meth)
type type
TServer* = object of TObject ## contains the current server state TServer* = object of TObject ## contains the current server state
@ -226,7 +228,7 @@ proc open*(s: var TServer, port = TPort(80)) =
## creates a new server at port `port`. If ``port == 0`` a free port is ## creates a new server at port `port`. If ``port == 0`` a free port is
## acquired that can be accessed later by the ``port`` proc. ## acquired that can be accessed later by the ``port`` proc.
s.socket = socket(AF_INET) s.socket = socket(AF_INET)
if s.socket == InvalidSocket: OSError() if s.socket == InvalidSocket: OSError(OSLastError())
bindAddr(s.socket, port) bindAddr(s.socket, port)
listen(s.socket) listen(s.socket)

View file

@ -50,11 +50,11 @@ proc open*(filename: string, mode: TFileMode = fmRead,
result.size = 0 result.size = 0
when defined(windows): when defined(windows):
template fail(msg: expr) = template fail(errCode: TOSErrorCode, msg: expr) =
rollback() rollback()
if result.fHandle != 0: discard CloseHandle(result.fHandle) if result.fHandle != 0: discard CloseHandle(result.fHandle)
if result.mapHandle != 0: discard CloseHandle(result.mapHandle) if result.mapHandle != 0: discard CloseHandle(result.mapHandle)
OSError() OSError(errCode)
# return false # return false
#raise newException(EIO, msg) #raise newException(EIO, msg)
@ -74,7 +74,7 @@ proc open*(filename: string, mode: TFileMode = fmRead,
result.fHandle = callCreateFile(CreateFileA, filename) result.fHandle = callCreateFile(CreateFileA, filename)
if result.fHandle == INVALID_HANDLE_VALUE: if result.fHandle == INVALID_HANDLE_VALUE:
fail "error opening file" fail(OSLastError(), "error opening file")
if newFileSize != -1: if newFileSize != -1:
var var
@ -83,9 +83,10 @@ proc open*(filename: string, mode: TFileMode = fmRead,
var status = SetFilePointer(result.fHandle, sizeLow, addr(sizeHigh), var status = SetFilePointer(result.fHandle, sizeLow, addr(sizeHigh),
FILE_BEGIN) FILE_BEGIN)
if (status == INVALID_SET_FILE_POINTER and GetLastError() != NO_ERROR) or let lastErr = OSLastError()
if (status == INVALID_SET_FILE_POINTER and lastErr.int32 != NO_ERROR) or
(SetEndOfFile(result.fHandle) == 0): (SetEndOfFile(result.fHandle) == 0):
fail "error setting file size" fail(lastErr, "error setting file size")
# since the strings are always 'nil', we simply always call # since the strings are always 'nil', we simply always call
# CreateFileMappingW which should be slightly faster anyway: # CreateFileMappingW which should be slightly faster anyway:
@ -95,7 +96,7 @@ proc open*(filename: string, mode: TFileMode = fmRead,
0, 0, nil) 0, 0, nil)
if result.mapHandle == 0: if result.mapHandle == 0:
fail "error creating mapping" fail(OSLastError(), "error creating mapping")
result.mem = MapViewOfFileEx( result.mem = MapViewOfFileEx(
result.mapHandle, result.mapHandle,
@ -106,22 +107,22 @@ proc open*(filename: string, mode: TFileMode = fmRead,
nil) nil)
if result.mem == nil: if result.mem == nil:
fail "error mapping view" fail(OSLastError(), "error mapping view")
var hi, low: int32 var hi, low: int32
low = GetFileSize(result.fHandle, addr(hi)) low = GetFileSize(result.fHandle, addr(hi))
if low == INVALID_FILE_SIZE: if low == INVALID_FILE_SIZE:
fail "error getting file size" fail(OSLastError(), "error getting file size")
else: else:
var fileSize = (int64(hi) shr 32) or low var fileSize = (int64(hi) shr 32) or low
if mappedSize != -1: result.size = min(fileSize, mappedSize).int if mappedSize != -1: result.size = min(fileSize, mappedSize).int
else: result.size = fileSize.int else: result.size = fileSize.int
else: else:
template fail(msg: expr) = template fail(errCode: TOSErrorCode, msg: expr) =
rollback() rollback()
if result.handle != 0: discard close(result.handle) if result.handle != 0: discard close(result.handle)
OSError() OSError(errCode)
var flags = if readonly: O_RDONLY else: O_RDWR var flags = if readonly: O_RDONLY else: O_RDWR
@ -132,11 +133,11 @@ proc open*(filename: string, mode: TFileMode = fmRead,
if result.handle == -1: if result.handle == -1:
# XXX: errno is supposed to be set here # XXX: errno is supposed to be set here
# Is there an exception that wraps it? # Is there an exception that wraps it?
fail "error opening file" fail(OSLastError(), "error opening file")
if newFileSize != -1: if newFileSize != -1:
if ftruncate(result.handle, newFileSize) == -1: if ftruncate(result.handle, newFileSize) == -1:
fail "error setting file size" fail(OSLastError(), "error setting file size")
if mappedSize != -1: if mappedSize != -1:
result.size = mappedSize result.size = mappedSize
@ -147,7 +148,7 @@ proc open*(filename: string, mode: TFileMode = fmRead,
# Why is mmap taking int anyway? # Why is mmap taking int anyway?
result.size = int(stat.st_size) result.size = int(stat.st_size)
else: else:
fail "error getting file size" fail(OSLastError(), "error getting file size")
result.mem = mmap( result.mem = mmap(
nil, nil,
@ -158,21 +159,24 @@ proc open*(filename: string, mode: TFileMode = fmRead,
offset) offset)
if result.mem == cast[pointer](MAP_FAILED): if result.mem == cast[pointer](MAP_FAILED):
fail "file mapping failed" fail(OSLastError(), "file mapping failed")
proc close*(f: var TMemFile) = proc close*(f: var TMemFile) =
## closes the memory mapped file `f`. All changes are written back to the ## closes the memory mapped file `f`. All changes are written back to the
## file system, if `f` was opened with write access. ## file system, if `f` was opened with write access.
var error = false var error = false
var lastErr: TOSErrorCode
when defined(windows): when defined(windows):
if f.fHandle != INVALID_HANDLE_VALUE: if f.fHandle != INVALID_HANDLE_VALUE:
lastErr = OSLastError()
error = UnmapViewOfFile(f.mem) == 0 error = UnmapViewOfFile(f.mem) == 0
error = (CloseHandle(f.mapHandle) == 0) or error error = (CloseHandle(f.mapHandle) == 0) or error
error = (CloseHandle(f.fHandle) == 0) or error error = (CloseHandle(f.fHandle) == 0) or error
else: else:
if f.handle != 0: if f.handle != 0:
lastErr = OSLastError()
error = munmap(f.mem, f.size) != 0 error = munmap(f.mem, f.size) != 0
error = (close(f.handle) != 0) or error error = (close(f.handle) != 0) or error
@ -185,5 +189,5 @@ proc close*(f: var TMemFile) =
else: else:
f.handle = 0 f.handle = 0
if error: OSError() if error: OSError(lastErr)

View file

@ -36,7 +36,7 @@ proc open*(host = "localhost", port = 6379.TPort): TRedis =
## Opens a connection to the redis server. ## Opens a connection to the redis server.
result.socket = socket(buffered = false) result.socket = socket(buffered = false)
if result.socket == InvalidSocket: if result.socket == InvalidSocket:
OSError() OSError(OSLastError())
result.socket.connect(host, port) result.socket.connect(host, port)
proc raiseInvalidReply(expected, got: char) = proc raiseInvalidReply(expected, got: char) =
@ -50,34 +50,31 @@ proc raiseNoOK(status: string) =
proc parseStatus(r: TRedis): TRedisStatus = proc parseStatus(r: TRedis): TRedisStatus =
var line = "" var line = ""
if r.socket.recvLine(line): r.socket.readLine(line)
if line == "": if line == "":
raise newException(ERedis, "Server closed connection prematurely") raise newException(ERedis, "Server closed connection prematurely")
if line[0] == '-':
raise newException(ERedis, strip(line))
if line[0] != '+':
raiseInvalidReply('+', line[0])
if line[0] == '-': return line.substr(1) # Strip '+'
raise newException(ERedis, strip(line))
if line[0] != '+':
raiseInvalidReply('+', line[0])
return line.substr(1) # Strip '+'
else:
OSError()
proc parseInteger(r: TRedis): TRedisInteger = proc parseInteger(r: TRedis): TRedisInteger =
var line = "" var line = ""
if r.socket.recvLine(line): r.socket.readLine(line)
if line == "": if line == "":
raise newException(ERedis, "Server closed connection prematurely") raise newException(ERedis, "Server closed connection prematurely")
if line[0] == '-': if line[0] == '-':
raise newException(ERedis, strip(line)) raise newException(ERedis, strip(line))
if line[0] != ':': if line[0] != ':':
raiseInvalidReply(':', line[0]) raiseInvalidReply(':', line[0])
# Strip ':' # Strip ':'
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.")
else: OSError()
proc recv(sock: TSocket, size: int): TaintedString = proc recv(sock: TSocket, size: int): TaintedString =
result = newString(size).TaintedString result = newString(size).TaintedString
@ -86,8 +83,7 @@ proc recv(sock: TSocket, size: int): TaintedString =
proc parseBulk(r: TRedis, allowMBNil = False): TRedisString = proc parseBulk(r: TRedis, allowMBNil = False): TRedisString =
var line = "" var line = ""
if not r.socket.recvLine(line.TaintedString): r.socket.readLine(line.TaintedString)
raise newException(EInvalidReply, "recvLine failed")
# Error. # Error.
if line[0] == '-': if line[0] == '-':
@ -110,8 +106,7 @@ proc parseBulk(r: TRedis, allowMBNil = False): TRedisString =
proc parseMultiBulk(r: TRedis): TRedisList = proc parseMultiBulk(r: TRedis): TRedisList =
var line = TaintedString"" var line = TaintedString""
if not r.socket.recvLine(line): r.socket.readLine(line)
raise newException(EInvalidReply, "recvLine failed")
if line.string[0] != '*': if line.string[0] != '*':
raiseInvalidReply('*', line.string[0]) raiseInvalidReply('*', line.string[0])
@ -848,10 +843,8 @@ 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 = "".TaintedString var s = "".TaintedString
if r.socket.recvLine(s): r.socket.readLine(s)
if s.string.len != 0: raise newException(ERedis, s.string) if s.string.len != 0: raise newException(ERedis, s.string)
else:
OSError()
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

View file

@ -4,8 +4,8 @@ s = socket()
s.connect("www.google.com", TPort(80)) s.connect("www.google.com", TPort(80))
var recvData: string = "" var data: string = ""
echo(s.recvLine(recvData)) s.readLine(data)
echo(recvData) echo(data)