diff options
-rw-r--r-- | lib/pure/httpserver.nim | 202 | ||||
-rw-r--r-- | lib/pure/memfiles.nim | 34 | ||||
-rw-r--r-- | lib/pure/redis.nim | 59 | ||||
-rw-r--r-- | tests/compile/tsockets.nim | 6 |
4 files changed, 150 insertions, 151 deletions
diff --git a/lib/pure/httpserver.nim b/lib/pure/httpserver.nim index 61399c2d8..e24709451 100644 --- a/lib/pure/httpserver.nim +++ b/lib/pure/httpserver.nim @@ -47,10 +47,11 @@ proc badRequest(client: TSocket) = send(client, "<p>Your browser sent a bad request, " & "such as a POST without a Content-Length.</p>" & wwwNL) -proc cannotExec(client: TSocket) = - send(client, "HTTP/1.1 500 Internal Server Error" & wwwNL) - sendTextContentType(client) - send(client, "<P>Error prohibited CGI execution." & wwwNL) +when false: + proc cannotExec(client: TSocket) = + send(client, "HTTP/1.1 500 Internal Server Error" & wwwNL) + sendTextContentType(client) + send(client, "<P>Error prohibited CGI execution." & wwwNL) proc headers(client: TSocket, filename: string) = # XXX could use filename to determine file type @@ -79,11 +80,11 @@ proc unimplemented(client: TSocket) = # ----------------- file serving --------------------------------------------- -proc discardHeaders(client: TSocket) = skip(client) +when false: + proc discardHeaders(client: TSocket) = skip(client) proc serveFile*(client: TSocket, filename: string) = ## serves a file to the client. - when false: discardHeaders(client) var f: TFile if open(f, filename): headers(client, filename) @@ -96,7 +97,7 @@ proc serveFile*(client: TSocket, filename: string) = if bytesread != bytesWritten: dealloc(buf) close(f) - OSError() + OSError(OSLastError()) if bytesread != bufSize: break dealloc(buf) close(f) @@ -104,108 +105,109 @@ proc serveFile*(client: TSocket, filename: string) = notFound(client) # ------------------ CGI execution ------------------------------------------- +when false: + # TODO: Fix this, or get rid of it. + type + TRequestMethod = enum reqGet, reqPost + + proc executeCgi(client: TSocket, path, query: string, meth: TRequestMethod) = + var env = newStringTable(modeCaseInsensitive) + var contentLength = -1 + case meth + of reqGet: + discardHeaders(client) + + env["REQUEST_METHOD"] = "GET" + env["QUERY_STRING"] = query + of reqPost: + 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: + badRequest(client) + return -type - TRequestMethod = enum reqGet, reqPost - -proc executeCgi(client: TSocket, path, query: string, meth: TRequestMethod) = - var env = newStringTable(modeCaseInsensitive) - var contentLength = -1 - case meth - of reqGet: - discardHeaders(client) - - env["REQUEST_METHOD"] = "GET" - env["QUERY_STRING"] = query - of reqPost: - 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: - badRequest(client) - return - - env["REQUEST_METHOD"] = "POST" - env["CONTENT_LENGTH"] = $contentLength + env["REQUEST_METHOD"] = "POST" + env["CONTENT_LENGTH"] = $contentLength - send(client, "HTTP/1.0 200 OK" & wwwNL) + send(client, "HTTP/1.0 200 OK" & wwwNL) - var process = startProcess(command=path, env=env) - if meth == reqPost: - # get from client and post to CGI program: - var buf = alloc(contentLength) - if recv(client, buf, contentLength) != contentLength: + var process = startProcess(command=path, env=env) + if meth == reqPost: + # get from client and post to CGI program: + var buf = alloc(contentLength) + if recv(client, buf, contentLength) != contentLength: + dealloc(buf) + OSError() + var inp = process.inputStream + inp.writeData(buf, contentLength) dealloc(buf) - OSError() - var inp = process.inputStream - inp.writeData(buf, contentLength) - dealloc(buf) - var outp = process.outputStream - var line = newStringOfCap(120).TaintedString - while true: - if outp.readLine(line): - send(client, line.string) - send(client, wwwNL) - elif not running(process): break - -# --------------- Server Setup ----------------------------------------------- - -proc acceptRequest(client: TSocket) = - var cgi = false - var query = "" - var buf = TaintedString"" - discard recvLine(client, buf) - var path = "" - var data = buf.string.split() - var meth = reqGet - - var q = find(data[1], '?') - - # 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: - cgi = true - query = data[1].substr(q+1) - elif cmpIgnoreCase(data[0], "POST") == 0: - cgi = true - meth = reqPost - else: - unimplemented(client) + var outp = process.outputStream + var line = newStringOfCap(120).TaintedString + while true: + if outp.readLine(line): + send(client, line.string) + send(client, wwwNL) + elif not running(process): break - if path[path.len-1] == '/' or existsDir(path): - path = path / "index.html" + # --------------- Server Setup ----------------------------------------------- - 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 + proc acceptRequest(client: TSocket) = + var cgi = false + var query = "" + var buf = TaintedString"" + discard recvLine(client, buf) + var path = "" + var data = buf.string.split() + var meth = reqGet + + var q = find(data[1], '?') + + # 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: - if {fpUserExec, fpGroupExec, fpOthersExec} * path.getFilePermissions != {}: + 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 - if not cgi: - serveFile(client, path) + query = data[1].substr(q+1) + 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: - 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 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 ## acquired that can be accessed later by the ``port`` proc. s.socket = socket(AF_INET) - if s.socket == InvalidSocket: OSError() + if s.socket == InvalidSocket: OSError(OSLastError()) bindAddr(s.socket, port) listen(s.socket) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 84a87ba15..9f4094b40 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -50,11 +50,11 @@ proc open*(filename: string, mode: TFileMode = fmRead, result.size = 0 when defined(windows): - template fail(msg: expr) = + template fail(errCode: TOSErrorCode, msg: expr) = rollback() if result.fHandle != 0: discard CloseHandle(result.fHandle) if result.mapHandle != 0: discard CloseHandle(result.mapHandle) - OSError() + OSError(errCode) # return false #raise newException(EIO, msg) @@ -74,7 +74,7 @@ proc open*(filename: string, mode: TFileMode = fmRead, result.fHandle = callCreateFile(CreateFileA, filename) if result.fHandle == INVALID_HANDLE_VALUE: - fail "error opening file" + fail(OSLastError(), "error opening file") if newFileSize != -1: var @@ -83,9 +83,10 @@ proc open*(filename: string, mode: TFileMode = fmRead, var status = SetFilePointer(result.fHandle, sizeLow, addr(sizeHigh), 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): - fail "error setting file size" + fail(lastErr, "error setting file size") # since the strings are always 'nil', we simply always call # CreateFileMappingW which should be slightly faster anyway: @@ -95,7 +96,7 @@ proc open*(filename: string, mode: TFileMode = fmRead, 0, 0, nil) if result.mapHandle == 0: - fail "error creating mapping" + fail(OSLastError(), "error creating mapping") result.mem = MapViewOfFileEx( result.mapHandle, @@ -106,22 +107,22 @@ proc open*(filename: string, mode: TFileMode = fmRead, nil) if result.mem == nil: - fail "error mapping view" + fail(OSLastError(), "error mapping view") var hi, low: int32 low = GetFileSize(result.fHandle, addr(hi)) if low == INVALID_FILE_SIZE: - fail "error getting file size" + fail(OSLastError(), "error getting file size") else: var fileSize = (int64(hi) shr 32) or low if mappedSize != -1: result.size = min(fileSize, mappedSize).int else: result.size = fileSize.int else: - template fail(msg: expr) = + template fail(errCode: TOSErrorCode, msg: expr) = rollback() if result.handle != 0: discard close(result.handle) - OSError() + OSError(errCode) var flags = if readonly: O_RDONLY else: O_RDWR @@ -132,11 +133,11 @@ proc open*(filename: string, mode: TFileMode = fmRead, if result.handle == -1: # XXX: errno is supposed to be set here # Is there an exception that wraps it? - fail "error opening file" + fail(OSLastError(), "error opening file") if newFileSize != -1: if ftruncate(result.handle, newFileSize) == -1: - fail "error setting file size" + fail(OSLastError(), "error setting file size") if mappedSize != -1: result.size = mappedSize @@ -147,7 +148,7 @@ proc open*(filename: string, mode: TFileMode = fmRead, # Why is mmap taking int anyway? result.size = int(stat.st_size) else: - fail "error getting file size" + fail(OSLastError(), "error getting file size") result.mem = mmap( nil, @@ -158,21 +159,24 @@ proc open*(filename: string, mode: TFileMode = fmRead, offset) if result.mem == cast[pointer](MAP_FAILED): - fail "file mapping failed" + fail(OSLastError(), "file mapping failed") proc close*(f: var TMemFile) = ## closes the memory mapped file `f`. All changes are written back to the ## file system, if `f` was opened with write access. var error = false + var lastErr: TOSErrorCode when defined(windows): if f.fHandle != INVALID_HANDLE_VALUE: + lastErr = OSLastError() error = UnmapViewOfFile(f.mem) == 0 error = (CloseHandle(f.mapHandle) == 0) or error error = (CloseHandle(f.fHandle) == 0) or error else: if f.handle != 0: + lastErr = OSLastError() error = munmap(f.mem, f.size) != 0 error = (close(f.handle) != 0) or error @@ -185,5 +189,5 @@ proc close*(f: var TMemFile) = else: f.handle = 0 - if error: OSError() + if error: OSError(lastErr) diff --git a/lib/pure/redis.nim b/lib/pure/redis.nim index bb6ea6768..8171dc12b 100644 --- a/lib/pure/redis.nim +++ b/lib/pure/redis.nim @@ -36,7 +36,7 @@ proc open*(host = "localhost", port = 6379.TPort): TRedis = ## Opens a connection to the redis server. result.socket = socket(buffered = false) if result.socket == InvalidSocket: - OSError() + OSError(OSLastError()) result.socket.connect(host, port) proc raiseInvalidReply(expected, got: char) = @@ -50,34 +50,31 @@ proc raiseNoOK(status: string) = proc parseStatus(r: TRedis): TRedisStatus = var line = "" - if r.socket.recvLine(line): - if line == "": - raise newException(ERedis, "Server closed connection prematurely") + r.socket.readLine(line) + if line == "": + raise newException(ERedis, "Server closed connection prematurely") + + if line[0] == '-': + raise newException(ERedis, strip(line)) + if line[0] != '+': + raiseInvalidReply('+', line[0]) - if line[0] == '-': - raise newException(ERedis, strip(line)) - if line[0] != '+': - raiseInvalidReply('+', line[0]) - - return line.substr(1) # Strip '+' - else: - OSError() + return line.substr(1) # Strip '+' proc parseInteger(r: TRedis): TRedisInteger = var line = "" - if r.socket.recvLine(line): - if line == "": - raise newException(ERedis, "Server closed connection prematurely") - - if line[0] == '-': - raise newException(ERedis, strip(line)) - if line[0] != ':': - raiseInvalidReply(':', line[0]) - - # Strip ':' - if parseBiggestInt(line, result, 1) == 0: - raise newException(EInvalidReply, "Unable to parse integer.") - else: OSError() + r.socket.readLine(line) + if line == "": + raise newException(ERedis, "Server closed connection prematurely") + + if line[0] == '-': + raise newException(ERedis, strip(line)) + if line[0] != ':': + raiseInvalidReply(':', line[0]) + + # Strip ':' + if parseBiggestInt(line, result, 1) == 0: + raise newException(EInvalidReply, "Unable to parse integer.") proc recv(sock: TSocket, size: int): TaintedString = result = newString(size).TaintedString @@ -86,8 +83,7 @@ proc recv(sock: TSocket, size: int): TaintedString = proc parseBulk(r: TRedis, allowMBNil = False): TRedisString = var line = "" - if not r.socket.recvLine(line.TaintedString): - raise newException(EInvalidReply, "recvLine failed") + r.socket.readLine(line.TaintedString) # Error. if line[0] == '-': @@ -110,8 +106,7 @@ proc parseBulk(r: TRedis, allowMBNil = False): TRedisString = proc parseMultiBulk(r: TRedis): TRedisList = var line = TaintedString"" - if not r.socket.recvLine(line): - raise newException(EInvalidReply, "recvLine failed") + r.socket.readLine(line) if 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 r.sendCommand("SHUTDOWN") var s = "".TaintedString - if r.socket.recvLine(s): - if s.string.len != 0: raise newException(ERedis, s.string) - else: - OSError() + r.socket.readLine(s) + if s.string.len != 0: raise newException(ERedis, s.string) proc slaveof*(r: TRedis, host: string, port: string) = ## Make the server a slave of another instance, or promote it as master diff --git a/tests/compile/tsockets.nim b/tests/compile/tsockets.nim index cc7d18b87..6078504f5 100644 --- a/tests/compile/tsockets.nim +++ b/tests/compile/tsockets.nim @@ -4,8 +4,8 @@ s = socket() s.connect("www.google.com", TPort(80)) -var recvData: string = "" -echo(s.recvLine(recvData)) -echo(recvData) +var data: string = "" +s.readLine(data) +echo(data) |