From 650a75beea975e367e87d77052f01582166ffd56 Mon Sep 17 00:00:00 2001 From: def Date: Mon, 2 Mar 2015 01:51:43 +0100 Subject: Make uri module usable for faster URI parsing: - A version of parseUri that takes a uri as parameter and modifies it - Export initUri so you can use the new parseUri better - Avoid creating new strings --- lib/pure/uri.nim | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) (limited to 'lib/pure') diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index b0afb75f9..1890a9bf4 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -53,10 +53,10 @@ proc parseAuthority(authority: string, result: var Uri) = while true: case authority[i] of '@': - result.password = result.port - result.port = "" - result.username = result.hostname - result.hostname = "" + swap result.password, result.port + result.port.setLen(0) + swap result.username, result.hostname + result.hostname.setLen(0) inPort = false of ':': inPort = true @@ -75,7 +75,7 @@ proc parsePath(uri: string, i: var int, result: var Uri) = # The 'mailto' scheme's PATH actually contains the hostname/username if result.scheme.toLower == "mailto": parseAuthority(result.path, result) - result.path = "" + result.path.setLen(0) if uri[i] == '?': i.inc # Skip '?' @@ -85,13 +85,21 @@ proc parsePath(uri: string, i: var int, result: var Uri) = i.inc # Skip '#' i.inc parseUntil(uri, result.anchor, {}, i) -proc initUri(): Uri = +proc initUri*(): Uri = + ## Initializes a URI. result = Uri(scheme: "", username: "", password: "", hostname: "", port: "", path: "", query: "", anchor: "") -proc parseUri*(uri: string): Uri = - ## Parses a URI. - result = initUri() +proc resetUri(uri: var Uri) = + for f in uri.fields: + when f is string: + f.setLen(0) + else: + f = false + +proc parseUri*(uri: string, result: var Uri) = + ## Parses a URI. The `result` variable will be cleared before. + resetUri(result) var i = 0 @@ -105,7 +113,7 @@ proc parseUri*(uri: string): Uri = if uri[i] != ':': # Assume this is a reference URI (relative URI) i = 0 - result.scheme = "" + result.scheme.setLen(0) parsePath(uri, i, result) return i.inc # Skip ':' @@ -124,6 +132,11 @@ proc parseUri*(uri: string): Uri = # Path parsePath(uri, i, result) +proc parseUri*(uri: string): Uri = + ## Parses a URI and returns it. + result = initUri() + parseUri(uri, result) + proc removeDotSegments(path: string): string = var collection: seq[string] = @[] let endsWithSlash = path[path.len-1] == '/' -- cgit 1.4.1-2-gfad0 From 524b68d0edcadae340bf0d4406ba7cd49a478108 Mon Sep 17 00:00:00 2001 From: def Date: Mon, 2 Mar 2015 01:57:25 +0100 Subject: Make strtabs module usable when avoiding allocations - resetStringTable proc --- lib/pure/strtabs.nim | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'lib/pure') diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 727d5a386..05d8666c6 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -168,6 +168,12 @@ proc newStringTable*(mode: StringTableMode): StringTableRef {. result.counter = 0 newSeq(result.data, startSize) +proc resetStringTable*(s: var StringTableRef, mode: StringTableMode) = + ## resets a string table to be empty again. + s.mode = mode + s.counter = 0 + s.data.setLen(startSize) + proc newStringTable*(keyValuePairs: varargs[string], mode: StringTableMode): StringTableRef {. rtl, extern: "nst$1WithPairs".} = -- cgit 1.4.1-2-gfad0 From 5aab532c9262bdd062d3f225a9402ede189b7a9b Mon Sep 17 00:00:00 2001 From: def Date: Mon, 2 Mar 2015 02:17:19 +0100 Subject: Make asyncdispatch usable when preventing allocations - Added a recvInto proc that takes a cstring as argument and reads into it instead of returning a newly allocated string. This is pretty unnice because of code duplication with recv. Calling recvInto from recv is not a good solution because of the additional future that gets created. - Windows version is totally untested --- lib/pure/asyncdispatch.nim | 112 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) (limited to 'lib/pure') diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 8e0ac8d21..c30c5ad41 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -542,6 +542,94 @@ when defined(windows) or defined(nimdoc): retFuture.fail(newException(OSError, osErrorMsg(lastError))) return retFuture + proc recvInto*(socket: TAsyncFD, buf: cstring, size: int, + flags = {SocketFlag.SafeDisconn}): Future[int] = + ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``, which must + ## at least be of that size. Returned future will complete once all the + ## data requested is read, a part of the data has been read, or the socket + ## has disconnected in which case the future will complete with a value of + ## ``0``. + ## + ## **Warning**: The ``Peek`` socket flag is not supported on Windows. + + + # Things to note: + # * When WSARecv completes immediately then ``bytesReceived`` is very + # unreliable. + # * Still need to implement message-oriented socket disconnection, + # '\0' in the message currently signifies a socket disconnect. Who + # knows what will happen when someone sends that to our socket. + verifyPresence(socket) + assert SocketFlag.Peek notin flags, "Peek not supported on Windows." + + var retFuture = newFuture[int]("recvInto") + + buf[0] = '\0' + var dataBuf: TWSABuf + dataBuf.buf = buf + dataBuf.len = size + + var bytesReceived: Dword + var flagsio = flags.toOSFlags().Dword + var ol = PCustomOverlapped() + GC_ref(ol) + ol.data = TCompletionData(fd: socket, cb: + proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) = + if not retFuture.finished: + if errcode == OSErrorCode(-1): + if bytesCount == 0 and dataBuf.buf[0] == '\0': + retFuture.complete(0) + else: + retFuture.complete(bytesCount) + else: + if flags.isDisconnectionError(errcode): + retFuture.complete(0) + else: + retFuture.fail(newException(OSError, osErrorMsg(errcode))) + if dataBuf.buf != nil: + dataBuf.buf = nil + ) + + let ret = WSARecv(socket.SocketHandle, addr dataBuf, 1, addr bytesReceived, + addr flagsio, cast[POVERLAPPED](ol), nil) + if ret == -1: + let err = osLastError() + if err.int32 != ERROR_IO_PENDING: + if dataBuf.buf != nil: + dataBuf.buf = nil + GC_unref(ol) + if flags.isDisconnectionError(err): + retFuture.complete(0) + else: + retFuture.fail(newException(OSError, osErrorMsg(err))) + elif ret == 0 and bytesReceived == 0 and dataBuf.buf[0] == '\0': + # We have to ensure that the buffer is empty because WSARecv will tell + # us immediately when it was disconnected, even when there is still + # data in the buffer. + # We want to give the user as much data as we can. So we only return + # the empty string (which signals a disconnection) when there is + # nothing left to read. + retFuture.complete(0) + # TODO: "For message-oriented sockets, where a zero byte message is often + # allowable, a failure with an error code of WSAEDISCON is used to + # indicate graceful closure." + # ~ http://msdn.microsoft.com/en-us/library/ms741688%28v=vs.85%29.aspx + else: + # Request to read completed immediately. + # From my tests bytesReceived isn't reliable. + let realSize = + if bytesReceived == 0: + size + else: + bytesReceived + assert realSize <= size + retFuture.complete(realSize) + # We don't deallocate ``ol`` here because even though this completed + # immediately poll will still be notified about its completion and it will + # free ``ol``. + return retFuture + + proc recv*(socket: TAsyncFD, size: int, flags = {SocketFlag.SafeDisconn}): Future[string] = ## Reads **up to** ``size`` bytes from ``socket``. Returned future will @@ -949,6 +1037,30 @@ else: retFuture.fail(newException(OSError, osErrorMsg(lastError))) return retFuture + proc recvInto*(socket: TAsyncFD, buf: cstring, size: int, + flags = {SocketFlag.SafeDisconn}): Future[int] = + var retFuture = newFuture[int]("recvInto") + + proc cb(sock: TAsyncFD): bool = + result = true + let res = recv(sock.SocketHandle, buf, size.cint, + flags.toOSFlags()) + if res < 0: + let lastError = osLastError() + if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}: + if flags.isDisconnectionError(lastError): + retFuture.complete(0) + else: + retFuture.fail(newException(OSError, osErrorMsg(lastError))) + else: + result = false # We still want this callback to be called. + else: + retFuture.complete(res) + # TODO: The following causes a massive slowdown. + #if not cb(socket): + addRead(socket, cb) + return retFuture + proc recv*(socket: TAsyncFD, size: int, flags = {SocketFlag.SafeDisconn}): Future[string] = var retFuture = newFuture[string]("recv") -- cgit 1.4.1-2-gfad0 From 07a50caf64d1ed2891349cff9a22b53c4ef61c2d Mon Sep 17 00:00:00 2001 From: def Date: Mon, 2 Mar 2015 02:52:55 +0100 Subject: Make asyncnet usable when avoiding allocations. - readInto, readIntoBuf, are templates instead of procs now - New recvLineInto template that reads directly into a string instead of creating a new one. Used by recvLine proc now - Need fd and bufLen fields of AsyncSocketDesc exported because of the templates - recv returns a shallow string to prevent copying - This gives significant speedups, mostly by using templates instead of creating new Futures and waiting for them all the time. --- lib/pure/asyncnet.nim | 159 +++++++++++++++++++++++++++++++------------------- 1 file changed, 98 insertions(+), 61 deletions(-) (limited to 'lib/pure') diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index e7325e0d7..7e1ff5db4 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -69,13 +69,13 @@ type # TODO: I would prefer to just do: # AsyncSocket* {.borrow: `.`.} = distinct Socket. But that doesn't work. AsyncSocketDesc = object - fd: SocketHandle + fd*: SocketHandle closed: bool ## determines whether this socket has been closed case isBuffered: bool ## determines whether this socket is buffered. of true: buffer: array[0..BufferSize, char] currPos: int # current index in buffer - bufLen: int # current length of buffer + bufLen*: int # current length of buffer of false: nil case isSsl: bool of true: @@ -182,26 +182,30 @@ proc connect*(socket: AsyncSocket, address: string, port: Port, sslSetConnectState(socket.sslHandle) sslLoop(socket, flags, sslDoHandshake(socket.sslHandle)) -proc readInto(buf: cstring, size: int, socket: AsyncSocket, - flags: set[SocketFlag]): Future[int] {.async.} = +template readInto*(buf: cstring, size: int, socket: AsyncSocket, + flags: set[SocketFlag]): int = + ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``. Note that + ## this is a template and not a proc. + var res = 0 if socket.isSsl: when defined(ssl): # SSL mode. sslLoop(socket, flags, sslRead(socket.sslHandle, buf, size.cint)) - result = opResult + res = opResult else: - var data = await recv(socket.fd.TAsyncFD, size, flags) - if data.len != 0: - copyMem(buf, addr data[0], data.len) + var recvIntoFut = recvInto(socket.fd.TAsyncFD, buf, size, flags) + yield recvIntoFut # Not in SSL mode. - result = data.len + res = recvIntoFut.read() + res -proc readIntoBuf(socket: AsyncSocket, - flags: set[SocketFlag]): Future[int] {.async.} = - result = await readInto(addr socket.buffer[0], BufferSize, socket, flags) +template readIntoBuf(socket: AsyncSocket, + flags: set[SocketFlag]): int = + var size = readInto(addr socket.buffer[0], BufferSize, socket, flags) socket.currPos = 0 - socket.bufLen = result + socket.bufLen = size + size proc recv*(socket: AsyncSocket, size: int, flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} = @@ -222,10 +226,11 @@ proc recv*(socket: AsyncSocket, size: int, ## to be read then the future will complete with a value of ``""``. if socket.isBuffered: result = newString(size) + shallow(result) let originalBufPos = socket.currPos if socket.bufLen == 0: - let res = await socket.readIntoBuf(flags - {SocketFlag.Peek}) + let res = socket.readIntoBuf(flags - {SocketFlag.Peek}) if res == 0: result.setLen(0) return @@ -236,7 +241,7 @@ proc recv*(socket: AsyncSocket, size: int, if SocketFlag.Peek in flags: # We don't want to get another buffer if we're peeking. break - let res = await socket.readIntoBuf(flags - {SocketFlag.Peek}) + let res = socket.readIntoBuf(flags - {SocketFlag.Peek}) if res == 0: break @@ -251,7 +256,7 @@ proc recv*(socket: AsyncSocket, size: int, result.setLen(read) else: result = newString(size) - let read = await readInto(addr result[0], size, socket, flags) + let read = readInto(addr result[0], size, socket, flags) result.setLen(read) proc send*(socket: AsyncSocket, data: string, @@ -302,6 +307,81 @@ proc accept*(socket: AsyncSocket, retFut.complete(future.read.client) return retFut +template recvLineInto*(socket: AsyncSocket, resString: var string, + flags = {SocketFlag.SafeDisconn}) = + ## Reads a line of data from ``socket`` into ``resString``. + ## + ## If a full line is read ``\r\L`` is not + ## added to ``line``, however if solely ``\r\L`` is read then ``line`` + ## will be set to it. + ## + ## If the socket is disconnected, ``line`` will be set to ``""``. + ## + ## If the socket is disconnected in the middle of a line (before ``\r\L`` + ## is read) then line will be set to ``""``. + ## The partial line **will be lost**. + ## + ## **Warning**: The ``Peek`` flag is not yet implemented. + ## + ## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the + ## protocol uses ``\r\L`` to delimit a new line. + assert SocketFlag.Peek notin flags ## TODO: + + template addNLIfEmpty(): stmt = + if resString.len == 0: + resString.add("\c\L") + + block recvLineInto: + if socket.isBuffered: + if socket.bufLen == 0: + let res = socket.readIntoBuf(flags) + if res == 0: + break recvLineInto + + var lastR = false + while true: + if socket.currPos >= socket.bufLen: + let res = socket.readIntoBuf(flags) + if res == 0: + resString.setLen(0) + break recvLineInto + + case socket.buffer[socket.currPos] + of '\r': + lastR = true + addNLIfEmpty() + of '\L': + addNLIfEmpty() + socket.currPos.inc() + break recvLineInto + else: + if lastR: + socket.currPos.inc() + break recvLineInto + else: + resString.add socket.buffer[socket.currPos] + socket.currPos.inc() + else: + var c = "" + while true: + let recvFut = recv(socket, 1, flags) + yield recvFut + c = recvFut.read() + if c.len == 0: + resString.setLen(0) + break recvLineInto + if c == "\r": + let recvFut = recv(socket, 1, flags) # Skip \L + yield recvFut + c = recvFut.read() + assert c == "\L" + addNLIfEmpty() + break recvLineInto + elif c == "\L": + addNLIfEmpty() + break recvLineInto + add(resString, c) + proc recvLine*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} = ## Reads a line of data from ``socket``. Returned future will complete once @@ -325,52 +405,9 @@ proc recvLine*(socket: AsyncSocket, if result.len == 0: result.add("\c\L") assert SocketFlag.Peek notin flags ## TODO: - if socket.isBuffered: - result = "" - if socket.bufLen == 0: - let res = await socket.readIntoBuf(flags) - if res == 0: - return - var lastR = false - while true: - if socket.currPos >= socket.bufLen: - let res = await socket.readIntoBuf(flags) - if res == 0: - result = "" - break - - case socket.buffer[socket.currPos] - of '\r': - lastR = true - addNLIfEmpty() - of '\L': - addNLIfEmpty() - socket.currPos.inc() - return - else: - if lastR: - socket.currPos.inc() - return - else: - result.add socket.buffer[socket.currPos] - socket.currPos.inc() - else: - result = "" - var c = "" - while true: - c = await recv(socket, 1, flags) - if c.len == 0: - return "" - if c == "\r": - c = await recv(socket, 1, flags) # Skip \L - assert c == "\L" - addNLIfEmpty() - return - elif c == "\L": - addNLIfEmpty() - return - add(result.string, c) + result = "" + socket.recvLineInto(result, flags) proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} = ## Marks ``socket`` as accepting connections. -- cgit 1.4.1-2-gfad0 From 477b3594ebdeec0d8ecdf1f050a61af7e0f96cbf Mon Sep 17 00:00:00 2001 From: def Date: Mon, 2 Mar 2015 03:08:17 +0100 Subject: Speed up asynchttpserver significantly using all the previous changes - Export socket field of AsyncHttpServer and addHeaders proc for templates - Make respond a template instead of proc because of how often it's called. This means no more "await" when invoking it. - Optimize respond template with special case for empty headers and Content-Length entry - newRequest doesn't allocate a hostname and body anymore because they're copied in later - Major changes to processClient to prevent allocations and copies --- lib/pure/asynchttpserver.nim | 98 +++++++++++++++++++++++--------------------- lib/pure/asyncnet.nim | 2 +- 2 files changed, 53 insertions(+), 47 deletions(-) (limited to 'lib/pure') diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 64242234c..c288f6089 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -21,7 +21,7 @@ ## ## var server = newAsyncHttpServer() ## proc cb(req: Request) {.async.} = -## await req.respond(Http200, "Hello World") +## req.respond(Http200, "Hello World") ## ## asyncCheck server.serve(Port(8080), cb) ## runForever() @@ -38,7 +38,7 @@ type body*: string AsyncHttpServer* = ref object - socket: AsyncSocket + socket*: AsyncSocket reuseAddr: bool HttpCode* = enum @@ -99,7 +99,7 @@ proc newAsyncHttpServer*(reuseAddr = true): AsyncHttpServer = new result result.reuseAddr = reuseAddr -proc addHeaders(msg: var string, headers: StringTableRef) = +proc addHeaders*(msg: var string, headers: StringTableRef) = for k, v in headers: msg.add(k & ": " & v & "\c\L") @@ -109,22 +109,22 @@ proc sendHeaders*(req: Request, headers: StringTableRef): Future[void] = addHeaders(msg, headers) return req.client.send(msg) -proc respond*(req: Request, code: HttpCode, - content: string, headers = newStringTable()) {.async.} = +template respond*(req: Request, code: HttpCode, + content: string, headers: StringTableRef = nil) = ## Responds to the request with the specified ``HttpCode``, headers and ## content. ## - ## This procedure will **not** close the client socket. - var customHeaders = headers - customHeaders["Content-Length"] = $content.len + ## This template will **not** close the client socket. var msg = "HTTP/1.1 " & $code & "\c\L" - msg.addHeaders(customHeaders) - await req.client.send(msg & "\c\L" & content) + + if headers != nil: + msg.addHeaders(headers) + msg.add("Content-Length: " & $content.len & "\c\L\c\L") + msg.add(content) + result = req.client.send(msg) proc newRequest(): Request = result.headers = newStringTable(modeCaseInsensitive) - result.hostname = "" - result.body = "" proc parseHeader(line: string): tuple[key, value: string] = var i = 0 @@ -149,77 +149,83 @@ proc sendStatus(client: AsyncSocket, status: string): Future[void] = proc processClient(client: AsyncSocket, address: string, callback: proc (request: Request): Future[void] {.closure, gcsafe.}) {.async.} = + var request: Request + request.url = initUri() + request.headers = newStringTable(modeCaseInsensitive) + var line = newStringOfCap(80) + var key, value = "" + while not client.isClosed: # GET /path HTTP/1.1 # Header: val # \n - var request = newRequest() - request.hostname = address + request.headers.resetStringTable(modeCaseInsensitive) + request.hostname.shallowCopy(address) assert client != nil request.client = client # First line - GET /path HTTP/1.1 - let line = await client.recvLine() # TODO: Timeouts. + line.setLen(0) + client.recvLineInto(line) # TODO: Timeouts. if line == "": client.close() return - let lineParts = line.split(' ') - if lineParts.len != 3: - await request.respond(Http400, "Invalid request. Got: " & line) - continue - let reqMethod = lineParts[0] - let path = lineParts[1] - let protocol = lineParts[2] + var i = 0 + for linePart in line.split(' '): + case i + of 0: request.reqMethod.shallowCopy(linePart.normalize) + of 1: parseUri(linePart, request.url) + of 2: + try: + request.protocol = parseProtocol(linePart) + except ValueError: + request.respond(Http400, "Invalid request protocol. Got: " & + linePart) + continue + else: + request.respond(Http400, "Invalid request. Got: " & line) + continue + inc i # Headers - var i = 0 while true: i = 0 - let headerLine = await client.recvLine() - if headerLine == "": - client.close(); return - if headerLine == "\c\L": break - # TODO: Compiler crash - #let (key, value) = parseHeader(headerLine) - let kv = parseHeader(headerLine) - request.headers[kv.key] = kv.value + line.setLen(0) + client.recvLineInto(line) - request.reqMethod = reqMethod - request.url = parseUri(path) - try: - request.protocol = protocol.parseProtocol() - except ValueError: - asyncCheck request.respond(Http400, "Invalid request protocol. Got: " & - protocol) - continue + if line == "": + client.close(); return + if line == "\c\L": break + let (key, value) = parseHeader(line) + request.headers[key] = value - if reqMethod.normalize == "post": + if request.reqMethod == "post": # Check for Expect header if request.headers.hasKey("Expect"): if request.headers["Expect"].toLower == "100-continue": await client.sendStatus("100 Continue") else: await client.sendStatus("417 Expectation Failed") - + # Read the body # - Check for Content-length header if request.headers.hasKey("Content-Length"): var contentLength = 0 if parseInt(request.headers["Content-Length"], contentLength) == 0: - await request.respond(Http400, "Bad Request. Invalid Content-Length.") + request.respond(Http400, "Bad Request. Invalid Content-Length.") else: request.body = await client.recv(contentLength) assert request.body.len == contentLength else: - await request.respond(Http400, "Bad Request. No Content-Length.") + request.respond(Http400, "Bad Request. No Content-Length.") continue - case reqMethod.normalize + case request.reqMethod of "get", "post", "head", "put", "delete", "trace", "options", "connect", "patch": await callback(request) else: - await request.respond(Http400, "Invalid request method. Got: " & reqMethod) + request.respond(Http400, "Invalid request method. Got: " & request.reqMethod) # Persistent connections if (request.protocol == HttpVer11 and @@ -268,7 +274,7 @@ when isMainModule: #echo(req.headers) let headers = {"Date": "Tue, 29 Apr 2014 23:40:08 GMT", "Content-type": "text/plain; charset=utf-8"} - await req.respond(Http200, "Hello World", headers.newStringTable()) + req.respond(Http200, "Hello World", headers.newStringTable()) asyncCheck server.serve(Port(5555), cb) runForever() diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 7e1ff5db4..cb137cfe5 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -200,7 +200,7 @@ template readInto*(buf: cstring, size: int, socket: AsyncSocket, res = recvIntoFut.read() res -template readIntoBuf(socket: AsyncSocket, +template readIntoBuf*(socket: AsyncSocket, flags: set[SocketFlag]): int = var size = readInto(addr socket.buffer[0], BufferSize, socket, flags) socket.currPos = 0 -- cgit 1.4.1-2-gfad0 From 7b4724ea27f5b30cb60fa689a1d449f3290c06e4 Mon Sep 17 00:00:00 2001 From: def Date: Mon, 2 Mar 2015 14:54:11 +0100 Subject: PNimrodNode -> NimNode in asyncdispatch --- lib/pure/asyncdispatch.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/pure') diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index c30c5ad41..38f188c74 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -610,9 +610,9 @@ when defined(windows) or defined(nimdoc): # the empty string (which signals a disconnection) when there is # nothing left to read. retFuture.complete(0) - # TODO: "For message-oriented sockets, where a zero byte message is often - # allowable, a failure with an error code of WSAEDISCON is used to - # indicate graceful closure." + # TODO: "For message-oriented sockets, where a zero byte message is often + # allowable, a failure with an error code of WSAEDISCON is used to + # indicate graceful closure." # ~ http://msdn.microsoft.com/en-us/library/ms741688%28v=vs.85%29.aspx else: # Request to read completed immediately. -- cgit 1.4.1-2-gfad0 From 6830c655606c30cd80936bdb0695b5a6fb308118 Mon Sep 17 00:00:00 2001 From: def Date: Mon, 2 Mar 2015 15:25:36 +0100 Subject: Document asynchttpserver's respond template --- lib/pure/asynchttpserver.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/pure') diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index c288f6089..7d6d5ffc0 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -112,7 +112,7 @@ proc sendHeaders*(req: Request, headers: StringTableRef): Future[void] = template respond*(req: Request, code: HttpCode, content: string, headers: StringTableRef = nil) = ## Responds to the request with the specified ``HttpCode``, headers and - ## content. + ## content. This template returns a Future[void]. ## ## This template will **not** close the client socket. var msg = "HTTP/1.1 " & $code & "\c\L" -- cgit 1.4.1-2-gfad0 From 58c29c29aec3187659d31a92552e2db2b6b4f757 Mon Sep 17 00:00:00 2001 From: def Date: Mon, 2 Mar 2015 19:53:34 +0100 Subject: Remove unused newRequest proc --- lib/pure/asynchttpserver.nim | 3 --- 1 file changed, 3 deletions(-) (limited to 'lib/pure') diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 7d6d5ffc0..ff48a5761 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -123,9 +123,6 @@ template respond*(req: Request, code: HttpCode, msg.add(content) result = req.client.send(msg) -proc newRequest(): Request = - result.headers = newStringTable(modeCaseInsensitive) - proc parseHeader(line: string): tuple[key, value: string] = var i = 0 i = line.parseUntil(result.key, ':') -- cgit 1.4.1-2-gfad0 From 43ed83384c41a97f3b36c4569e1dfd1ec4f43226 Mon Sep 17 00:00:00 2001 From: def Date: Tue, 3 Mar 2015 21:59:42 +0100 Subject: Rename resetStringTable to clearStringTable --- lib/pure/asynchttpserver.nim | 2 +- lib/pure/strtabs.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/pure') diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index ff48a5761..5c32389cc 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -156,7 +156,7 @@ proc processClient(client: AsyncSocket, address: string, # GET /path HTTP/1.1 # Header: val # \n - request.headers.resetStringTable(modeCaseInsensitive) + request.headers.clearStringTable(modeCaseInsensitive) request.hostname.shallowCopy(address) assert client != nil request.client = client diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 05d8666c6..125bee706 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -168,7 +168,7 @@ proc newStringTable*(mode: StringTableMode): StringTableRef {. result.counter = 0 newSeq(result.data, startSize) -proc resetStringTable*(s: var StringTableRef, mode: StringTableMode) = +proc clearStringTable*(s: StringTableRef, mode: StringTableMode) = ## resets a string table to be empty again. s.mode = mode s.counter = 0 -- cgit 1.4.1-2-gfad0 From e127ed77b1ef94a551c86b6a78ae8a08dcbba159 Mon Sep 17 00:00:00 2001 From: def Date: Tue, 17 Mar 2015 18:15:47 +0100 Subject: Make recvLineInto a proc instead of template --- lib/pure/asynchttpserver.nim | 4 +- lib/pure/asyncnet.nim | 98 ++++++++++++++++++++++---------------------- 2 files changed, 50 insertions(+), 52 deletions(-) (limited to 'lib/pure') diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 5c32389cc..22ea5e4bb 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -163,7 +163,7 @@ proc processClient(client: AsyncSocket, address: string, # First line - GET /path HTTP/1.1 line.setLen(0) - client.recvLineInto(line) # TODO: Timeouts. + await client.recvLineInto(addr line) # TODO: Timeouts. if line == "": client.close() return @@ -189,7 +189,7 @@ proc processClient(client: AsyncSocket, address: string, while true: i = 0 line.setLen(0) - client.recvLineInto(line) + await client.recvLineInto(addr line) if line == "": client.close(); return diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index cb137cfe5..0c8ed8a08 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -307,8 +307,8 @@ proc accept*(socket: AsyncSocket, retFut.complete(future.read.client) return retFut -template recvLineInto*(socket: AsyncSocket, resString: var string, - flags = {SocketFlag.SafeDisconn}) = +proc recvLineInto*(socket: AsyncSocket, resString: ptr string, + flags = {SocketFlag.SafeDisconn}): Future[void] {.async.} = ## Reads a line of data from ``socket`` into ``resString``. ## ## If a full line is read ``\r\L`` is not @@ -326,61 +326,59 @@ template recvLineInto*(socket: AsyncSocket, resString: var string, ## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the ## protocol uses ``\r\L`` to delimit a new line. assert SocketFlag.Peek notin flags ## TODO: + result = newFuture[void]("asyncnet.recvLineInto") template addNLIfEmpty(): stmt = - if resString.len == 0: - resString.add("\c\L") + if resString[].len == 0: + resString[].add("\c\L") - block recvLineInto: - if socket.isBuffered: - if socket.bufLen == 0: + if socket.isBuffered: + if socket.bufLen == 0: + let res = socket.readIntoBuf(flags) + if res == 0: + return + + var lastR = false + while true: + if socket.currPos >= socket.bufLen: let res = socket.readIntoBuf(flags) if res == 0: - break recvLineInto - - var lastR = false - while true: - if socket.currPos >= socket.bufLen: - let res = socket.readIntoBuf(flags) - if res == 0: - resString.setLen(0) - break recvLineInto - - case socket.buffer[socket.currPos] - of '\r': - lastR = true - addNLIfEmpty() - of '\L': - addNLIfEmpty() + resString[].setLen(0) + return + + case socket.buffer[socket.currPos] + of '\r': + lastR = true + addNLIfEmpty() + of '\L': + addNLIfEmpty() + socket.currPos.inc() + return + else: + if lastR: socket.currPos.inc() - break recvLineInto + return else: - if lastR: - socket.currPos.inc() - break recvLineInto - else: - resString.add socket.buffer[socket.currPos] - socket.currPos.inc() - else: - var c = "" - while true: - let recvFut = recv(socket, 1, flags) - yield recvFut + resString[].add socket.buffer[socket.currPos] + socket.currPos.inc() + else: + var c = "" + while true: + let recvFut = recv(socket, 1, flags) + c = recvFut.read() + if c.len == 0: + resString[].setLen(0) + return + if c == "\r": + let recvFut = recv(socket, 1, flags) # Skip \L c = recvFut.read() - if c.len == 0: - resString.setLen(0) - break recvLineInto - if c == "\r": - let recvFut = recv(socket, 1, flags) # Skip \L - yield recvFut - c = recvFut.read() - assert c == "\L" - addNLIfEmpty() - break recvLineInto - elif c == "\L": - addNLIfEmpty() - break recvLineInto - add(resString, c) + assert c == "\L" + addNLIfEmpty() + return + elif c == "\L": + addNLIfEmpty() + return + add(resString[], c) proc recvLine*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} = @@ -407,7 +405,7 @@ proc recvLine*(socket: AsyncSocket, assert SocketFlag.Peek notin flags ## TODO: result = "" - socket.recvLineInto(result, flags) + await socket.recvLineInto(addr result, flags) proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} = ## Marks ``socket`` as accepting connections. -- cgit 1.4.1-2-gfad0 From 836819d6b6afd06abc49fb8c6065ee730c5abd96 Mon Sep 17 00:00:00 2001 From: def Date: Tue, 17 Mar 2015 18:16:39 +0100 Subject: Don't export readInto* templates --- lib/pure/asyncnet.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/pure') diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 0c8ed8a08..7fbcda599 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -182,7 +182,7 @@ proc connect*(socket: AsyncSocket, address: string, port: Port, sslSetConnectState(socket.sslHandle) sslLoop(socket, flags, sslDoHandshake(socket.sslHandle)) -template readInto*(buf: cstring, size: int, socket: AsyncSocket, +template readInto(buf: cstring, size: int, socket: AsyncSocket, flags: set[SocketFlag]): int = ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``. Note that ## this is a template and not a proc. @@ -200,7 +200,7 @@ template readInto*(buf: cstring, size: int, socket: AsyncSocket, res = recvIntoFut.read() res -template readIntoBuf*(socket: AsyncSocket, +template readIntoBuf(socket: AsyncSocket, flags: set[SocketFlag]): int = var size = readInto(addr socket.buffer[0], BufferSize, socket, flags) socket.currPos = 0 -- cgit 1.4.1-2-gfad0 From 134eb6e5820e8b71893d2382ec2c9627bd3c898b Mon Sep 17 00:00:00 2001 From: def Date: Tue, 17 Mar 2015 18:53:58 +0100 Subject: Move recvInto to asyncnet and don't export it --- lib/pure/asyncdispatch.nim | 112 --------------------------------------- lib/pure/asyncnet.nim | 128 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 120 insertions(+), 120 deletions(-) (limited to 'lib/pure') diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 38f188c74..8e0ac8d21 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -542,94 +542,6 @@ when defined(windows) or defined(nimdoc): retFuture.fail(newException(OSError, osErrorMsg(lastError))) return retFuture - proc recvInto*(socket: TAsyncFD, buf: cstring, size: int, - flags = {SocketFlag.SafeDisconn}): Future[int] = - ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``, which must - ## at least be of that size. Returned future will complete once all the - ## data requested is read, a part of the data has been read, or the socket - ## has disconnected in which case the future will complete with a value of - ## ``0``. - ## - ## **Warning**: The ``Peek`` socket flag is not supported on Windows. - - - # Things to note: - # * When WSARecv completes immediately then ``bytesReceived`` is very - # unreliable. - # * Still need to implement message-oriented socket disconnection, - # '\0' in the message currently signifies a socket disconnect. Who - # knows what will happen when someone sends that to our socket. - verifyPresence(socket) - assert SocketFlag.Peek notin flags, "Peek not supported on Windows." - - var retFuture = newFuture[int]("recvInto") - - buf[0] = '\0' - var dataBuf: TWSABuf - dataBuf.buf = buf - dataBuf.len = size - - var bytesReceived: Dword - var flagsio = flags.toOSFlags().Dword - var ol = PCustomOverlapped() - GC_ref(ol) - ol.data = TCompletionData(fd: socket, cb: - proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) = - if not retFuture.finished: - if errcode == OSErrorCode(-1): - if bytesCount == 0 and dataBuf.buf[0] == '\0': - retFuture.complete(0) - else: - retFuture.complete(bytesCount) - else: - if flags.isDisconnectionError(errcode): - retFuture.complete(0) - else: - retFuture.fail(newException(OSError, osErrorMsg(errcode))) - if dataBuf.buf != nil: - dataBuf.buf = nil - ) - - let ret = WSARecv(socket.SocketHandle, addr dataBuf, 1, addr bytesReceived, - addr flagsio, cast[POVERLAPPED](ol), nil) - if ret == -1: - let err = osLastError() - if err.int32 != ERROR_IO_PENDING: - if dataBuf.buf != nil: - dataBuf.buf = nil - GC_unref(ol) - if flags.isDisconnectionError(err): - retFuture.complete(0) - else: - retFuture.fail(newException(OSError, osErrorMsg(err))) - elif ret == 0 and bytesReceived == 0 and dataBuf.buf[0] == '\0': - # We have to ensure that the buffer is empty because WSARecv will tell - # us immediately when it was disconnected, even when there is still - # data in the buffer. - # We want to give the user as much data as we can. So we only return - # the empty string (which signals a disconnection) when there is - # nothing left to read. - retFuture.complete(0) - # TODO: "For message-oriented sockets, where a zero byte message is often - # allowable, a failure with an error code of WSAEDISCON is used to - # indicate graceful closure." - # ~ http://msdn.microsoft.com/en-us/library/ms741688%28v=vs.85%29.aspx - else: - # Request to read completed immediately. - # From my tests bytesReceived isn't reliable. - let realSize = - if bytesReceived == 0: - size - else: - bytesReceived - assert realSize <= size - retFuture.complete(realSize) - # We don't deallocate ``ol`` here because even though this completed - # immediately poll will still be notified about its completion and it will - # free ``ol``. - return retFuture - - proc recv*(socket: TAsyncFD, size: int, flags = {SocketFlag.SafeDisconn}): Future[string] = ## Reads **up to** ``size`` bytes from ``socket``. Returned future will @@ -1037,30 +949,6 @@ else: retFuture.fail(newException(OSError, osErrorMsg(lastError))) return retFuture - proc recvInto*(socket: TAsyncFD, buf: cstring, size: int, - flags = {SocketFlag.SafeDisconn}): Future[int] = - var retFuture = newFuture[int]("recvInto") - - proc cb(sock: TAsyncFD): bool = - result = true - let res = recv(sock.SocketHandle, buf, size.cint, - flags.toOSFlags()) - if res < 0: - let lastError = osLastError() - if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}: - if flags.isDisconnectionError(lastError): - retFuture.complete(0) - else: - retFuture.fail(newException(OSError, osErrorMsg(lastError))) - else: - result = false # We still want this callback to be called. - else: - retFuture.complete(res) - # TODO: The following causes a massive slowdown. - #if not cb(socket): - addRead(socket, cb) - return retFuture - proc recv*(socket: TAsyncFD, size: int, flags = {SocketFlag.SafeDisconn}): Future[string] = var retFuture = newFuture[string]("recv") diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 7fbcda599..840435aa4 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -24,7 +24,7 @@ ## ## Chat server ## ^^^^^^^^^^^ -## +## ## The following example demonstrates a simple chat server. ## ## .. code-block::nim @@ -182,6 +182,118 @@ proc connect*(socket: AsyncSocket, address: string, port: Port, sslSetConnectState(socket.sslHandle) sslLoop(socket, flags, sslDoHandshake(socket.sslHandle)) +when defined(windows): + proc recvInto(socket: TAsyncFD, buf: cstring, size: int, + flags = {SocketFlag.SafeDisconn}): Future[int] = + ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``, which must + ## at least be of that size. Returned future will complete once all the + ## data requested is read, a part of the data has been read, or the socket + ## has disconnected in which case the future will complete with a value of + ## ``0``. + ## + ## **Warning**: The ``Peek`` socket flag is not supported on Windows. + + + # Things to note: + # * When WSARecv completes immediately then ``bytesReceived`` is very + # unreliable. + # * Still need to implement message-oriented socket disconnection, + # '\0' in the message currently signifies a socket disconnect. Who + # knows what will happen when someone sends that to our socket. + verifyPresence(socket) + assert SocketFlag.Peek notin flags, "Peek not supported on Windows." + + var retFuture = newFuture[int]("recvInto") + + buf[0] = '\0' + var dataBuf: TWSABuf + dataBuf.buf = buf + dataBuf.len = size + + var bytesReceived: Dword + var flagsio = flags.toOSFlags().Dword + var ol = PCustomOverlapped() + GC_ref(ol) + ol.data = TCompletionData(fd: socket, cb: + proc (fd: TAsyncFD, bytesCount: Dword, errcode: OSErrorCode) = + if not retFuture.finished: + if errcode == OSErrorCode(-1): + if bytesCount == 0 and dataBuf.buf[0] == '\0': + retFuture.complete(0) + else: + retFuture.complete(bytesCount) + else: + if flags.isDisconnectionError(errcode): + retFuture.complete(0) + else: + retFuture.fail(newException(OSError, osErrorMsg(errcode))) + if dataBuf.buf != nil: + dataBuf.buf = nil + ) + + let ret = WSARecv(socket.SocketHandle, addr dataBuf, 1, addr bytesReceived, + addr flagsio, cast[POVERLAPPED](ol), nil) + if ret == -1: + let err = osLastError() + if err.int32 != ERROR_IO_PENDING: + if dataBuf.buf != nil: + dataBuf.buf = nil + GC_unref(ol) + if flags.isDisconnectionError(err): + retFuture.complete(0) + else: + retFuture.fail(newException(OSError, osErrorMsg(err))) + elif ret == 0 and bytesReceived == 0 and dataBuf.buf[0] == '\0': + # We have to ensure that the buffer is empty because WSARecv will tell + # us immediately when it was disconnected, even when there is still + # data in the buffer. + # We want to give the user as much data as we can. So we only return + # the empty string (which signals a disconnection) when there is + # nothing left to read. + retFuture.complete(0) + # TODO: "For message-oriented sockets, where a zero byte message is often + # allowable, a failure with an error code of WSAEDISCON is used to + # indicate graceful closure." + # ~ http://msdn.microsoft.com/en-us/library/ms741688%28v=vs.85%29.aspx + else: + # Request to read completed immediately. + # From my tests bytesReceived isn't reliable. + let realSize = + if bytesReceived == 0: + size + else: + bytesReceived + assert realSize <= size + retFuture.complete(realSize) + # We don't deallocate ``ol`` here because even though this completed + # immediately poll will still be notified about its completion and it will + # free ``ol``. + return retFuture +else: + proc recvInto(socket: TAsyncFD, buf: cstring, size: int, + flags = {SocketFlag.SafeDisconn}): Future[int] = + var retFuture = newFuture[int]("recvInto") + + proc cb(sock: TAsyncFD): bool = + result = true + let res = recv(sock.SocketHandle, buf, size.cint, + flags.toOSFlags()) + if res < 0: + let lastError = osLastError() + if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}: + if flags.isDisconnectionError(lastError): + retFuture.complete(0) + else: + retFuture.fail(newException(OSError, osErrorMsg(lastError))) + else: + result = false # We still want this callback to be called. + else: + retFuture.complete(res) + # TODO: The following causes a massive slowdown. + #if not cb(socket): + addRead(socket, cb) + return retFuture + template readInto(buf: cstring, size: int, socket: AsyncSocket, flags: set[SocketFlag]): int = ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``. Note that @@ -314,7 +426,7 @@ proc recvLineInto*(socket: AsyncSocket, resString: ptr string, ## If a full line is read ``\r\L`` is not ## added to ``line``, however if solely ``\r\L`` is read then ``line`` ## will be set to it. - ## + ## ## If the socket is disconnected, ``line`` will be set to ``""``. ## ## If the socket is disconnected in the middle of a line (before ``\r\L`` @@ -322,7 +434,7 @@ proc recvLineInto*(socket: AsyncSocket, resString: ptr string, ## The partial line **will be lost**. ## ## **Warning**: The ``Peek`` flag is not yet implemented. - ## + ## ## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the ## protocol uses ``\r\L`` to delimit a new line. assert SocketFlag.Peek notin flags ## TODO: @@ -388,7 +500,7 @@ proc recvLine*(socket: AsyncSocket, ## If a full line is read ``\r\L`` is not ## added to ``line``, however if solely ``\r\L`` is read then ``line`` ## will be set to it. - ## + ## ## If the socket is disconnected, ``line`` will be set to ``""``. ## ## If the socket is disconnected in the middle of a line (before ``\r\L`` @@ -396,7 +508,7 @@ proc recvLine*(socket: AsyncSocket, ## The partial line **will be lost**. ## ## **Warning**: The ``Peek`` flag is not yet implemented. - ## + ## ## **Warning**: ``recvLine`` on unbuffered sockets assumes that the protocol ## uses ``\r\L`` to delimit a new line. template addNLIfEmpty(): stmt = @@ -535,11 +647,11 @@ when isMainModule: proc (future: Future[void]) = echo("Send") client.close() - + var f = accept(sock) f.callback = onAccept - + var f = accept(sock) f.callback = onAccept runForever() - + -- cgit 1.4.1-2-gfad0 From 2410e667bcea042f4f851b903eac3398e9cc4bf9 Mon Sep 17 00:00:00 2001 From: def Date: Tue, 17 Mar 2015 22:05:10 +0100 Subject: Make respond a template again --- lib/pure/asynchttpserver.nim | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'lib/pure') diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 22ea5e4bb..7020dcbd3 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -99,7 +99,7 @@ proc newAsyncHttpServer*(reuseAddr = true): AsyncHttpServer = new result result.reuseAddr = reuseAddr -proc addHeaders*(msg: var string, headers: StringTableRef) = +proc addHeaders(msg: var string, headers: StringTableRef) = for k, v in headers: msg.add(k & ": " & v & "\c\L") @@ -109,8 +109,8 @@ proc sendHeaders*(req: Request, headers: StringTableRef): Future[void] = addHeaders(msg, headers) return req.client.send(msg) -template respond*(req: Request, code: HttpCode, - content: string, headers: StringTableRef = nil) = +proc respond*(req: Request, code: HttpCode, content: string, + headers: StringTableRef = nil): Future[void] = ## Responds to the request with the specified ``HttpCode``, headers and ## content. This template returns a Future[void]. ## @@ -177,11 +177,11 @@ proc processClient(client: AsyncSocket, address: string, try: request.protocol = parseProtocol(linePart) except ValueError: - request.respond(Http400, "Invalid request protocol. Got: " & - linePart) + asyncCheck request.respond(Http400, + "Invalid request protocol. Got: " & linePart) continue else: - request.respond(Http400, "Invalid request. Got: " & line) + await request.respond(Http400, "Invalid request. Got: " & line) continue inc i @@ -210,19 +210,19 @@ proc processClient(client: AsyncSocket, address: string, if request.headers.hasKey("Content-Length"): var contentLength = 0 if parseInt(request.headers["Content-Length"], contentLength) == 0: - request.respond(Http400, "Bad Request. Invalid Content-Length.") + await request.respond(Http400, "Bad Request. Invalid Content-Length.") else: request.body = await client.recv(contentLength) assert request.body.len == contentLength else: - request.respond(Http400, "Bad Request. No Content-Length.") + await request.respond(Http400, "Bad Request. No Content-Length.") continue case request.reqMethod of "get", "post", "head", "put", "delete", "trace", "options", "connect", "patch": await callback(request) else: - request.respond(Http400, "Invalid request method. Got: " & request.reqMethod) + await request.respond(Http400, "Invalid request method. Got: " & request.reqMethod) # Persistent connections if (request.protocol == HttpVer11 and @@ -250,7 +250,7 @@ proc serve*(server: AsyncHttpServer, port: Port, server.socket.setSockOpt(OptReuseAddr, true) server.socket.bindAddr(port, address) server.socket.listen() - + while true: # TODO: Causes compiler crash. #var (address, client) = await server.socket.acceptAddr() @@ -271,7 +271,7 @@ when isMainModule: #echo(req.headers) let headers = {"Date": "Tue, 29 Apr 2014 23:40:08 GMT", "Content-type": "text/plain; charset=utf-8"} - req.respond(Http200, "Hello World", headers.newStringTable()) + await req.respond(Http200, "Hello World", headers.newStringTable()) asyncCheck server.serve(Port(5555), cb) runForever() -- cgit 1.4.1-2-gfad0 From ee9499ac89e8e86f43637e49a84d1a2a753c0227 Mon Sep 17 00:00:00 2001 From: def Date: Tue, 17 Mar 2015 22:24:12 +0100 Subject: Some style cleanup --- lib/pure/asyncnet.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/pure') diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 840435aa4..fa67b212a 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -295,7 +295,7 @@ else: return retFuture template readInto(buf: cstring, size: int, socket: AsyncSocket, - flags: set[SocketFlag]): int = + flags: set[SocketFlag]): int = ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``. Note that ## this is a template and not a proc. var res = 0 @@ -420,7 +420,7 @@ proc accept*(socket: AsyncSocket, return retFut proc recvLineInto*(socket: AsyncSocket, resString: ptr string, - flags = {SocketFlag.SafeDisconn}): Future[void] {.async.} = + flags = {SocketFlag.SafeDisconn}) {.async.} = ## Reads a line of data from ``socket`` into ``resString``. ## ## If a full line is read ``\r\L`` is not @@ -490,7 +490,7 @@ proc recvLineInto*(socket: AsyncSocket, resString: ptr string, elif c == "\L": addNLIfEmpty() return - add(resString[], c) + resString[].add c proc recvLine*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} = -- cgit 1.4.1-2-gfad0 From 6523d8021145df5479e7e0e79999b43526b66679 Mon Sep 17 00:00:00 2001 From: def Date: Tue, 17 Mar 2015 22:48:34 +0100 Subject: Rename clearStringTable to clear --- lib/pure/asynchttpserver.nim | 2 +- lib/pure/strtabs.nim | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/pure') diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 7020dcbd3..52de9531e 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -156,7 +156,7 @@ proc processClient(client: AsyncSocket, address: string, # GET /path HTTP/1.1 # Header: val # \n - request.headers.clearStringTable(modeCaseInsensitive) + request.headers.clear(modeCaseInsensitive) request.hostname.shallowCopy(address) assert client != nil request.client = client diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 125bee706..7fdd994f2 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -168,7 +168,7 @@ proc newStringTable*(mode: StringTableMode): StringTableRef {. result.counter = 0 newSeq(result.data, startSize) -proc clearStringTable*(s: StringTableRef, mode: StringTableMode) = +proc clear*(s: StringTableRef, mode: StringTableMode) = ## resets a string table to be empty again. s.mode = mode s.counter = 0 @@ -233,7 +233,7 @@ proc `$`*(t: StringTableRef): string {.rtl, extern: "nstDollar".} = result = "{:}" else: result = "{" - for key, val in pairs(t): + for key, val in pairs(t): if result.len > 1: result.add(", ") result.add(key) result.add(": ") -- cgit 1.4.1-2-gfad0