summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--config/nimdoc.cfg4
-rw-r--r--lib/js/dom.nim30
-rw-r--r--lib/posix/posix.nim9
-rw-r--r--lib/pure/asynchttpserver.nim34
-rw-r--r--lib/pure/asyncmacro.nim27
-rw-r--r--lib/pure/asyncnet.nim16
-rw-r--r--lib/pure/concurrency/threadpool.nim2
-rw-r--r--lib/pure/httpcore.nim4
-rw-r--r--lib/pure/net.nim11
-rw-r--r--tests/async/tioselectors.nim8
10 files changed, 97 insertions, 48 deletions
diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg
index 832b4ffd0..d2cfc9540 100644
--- a/config/nimdoc.cfg
+++ b/config/nimdoc.cfg
@@ -133,8 +133,8 @@ doc.file = """<?xml version="1.0" encoding="utf-8" ?>
 <link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
 
 <!-- Google fonts -->
-<link href='http://fonts.googleapis.com/css?family=Raleway:400,600,900' rel='stylesheet' type='text/css'/>
-<link href='http://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
+<link href='https://fonts.googleapis.com/css?family=Raleway:400,600,900' rel='stylesheet' type='text/css'/>
+<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
 
 <!-- CSS -->
 <title>$title</title>
diff --git a/lib/js/dom.nim b/lib/js/dom.nim
index 5a33dfa9f..a879ddb61 100644
--- a/lib/js/dom.nim
+++ b/lib/js/dom.nim
@@ -130,6 +130,16 @@ type
     readOnly*: bool
     options*: seq[OptionElement]
 
+  # https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
+  HtmlElement* = ref object of Element
+    contentEditable*: string
+    isContentEditable*: bool
+    dir*: string
+    offsetHeight*: int
+    offsetWidth*: int
+    offsetLeft*: int
+    offsetTop*: int
+
   LinkElement* = ref LinkObj
   LinkObj {.importc.} = object of ElementObj
     target*: cstring
@@ -270,6 +280,8 @@ type
     wordSpacing*: cstring
     zIndex*: int
 
+  # TODO: A lot of the fields in Event belong to a more specific type of event.
+  # TODO: Should we clean this up?
   Event* = ref EventObj
   EventObj {.importc.} = object of RootObj
     target*: Node
@@ -310,6 +322,20 @@ type
     SUBMIT*: int
     UNLOAD*: int
 
+  TouchList* {.importc.} = ref object of RootObj
+    length*: int
+
+  TouchEvent* {.importc.} = ref object of Event
+    changedTouches*, targetTouches*, touches*: TouchList
+
+  Touch* {.importc.} = ref object of RootObj
+    identifier*: int
+    screenX*, screenY*, clientX*, clientY*, pageX*, pageY*: int
+    target*: Element
+    radiusX*, radiusY*: int
+    rotationAngle*: int
+    force*: float
+
   Location* = ref LocationObj
   LocationObj {.importc.} = object of RootObj
     hash*: cstring
@@ -488,6 +514,10 @@ proc setAttribute*(s: Style, attr, value: cstring, caseSensitive=false)
 # Event "methods"
 proc preventDefault*(ev: Event)
 
+# TouchEvent "methods"
+proc identifiedTouch*(list: TouchList): Touch
+proc item*(list: TouchList, i: int): Touch
+
 {.pop.}
 
 var
diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim
index a18db2150..b29f43eb4 100644
--- a/lib/posix/posix.nim
+++ b/lib/posix/posix.nim
@@ -109,12 +109,17 @@ type
   Dirent* {.importc: "struct dirent",
              header: "<dirent.h>", final, pure.} = object ## dirent_t struct
     d_ino*: Ino  ## File serial number.
-    when defined(linux) or defined(macosx) or defined(bsd):
+    when defined(dragonfly):
+      # DragonflyBSD doesn't have `d_reclen` field.
+      d_type*: uint8 
+    elif defined(linux) or defined(macosx) or defined(freebsd) or
+         defined(netbsd) or defined(openbsd):
       d_reclen*: cshort ## Length of this record. (not POSIX)
       d_type*: int8 ## Type of file; not supported by all filesystem types.
                     ## (not POSIX)
-      when defined(linux) or defined(bsd):
+      when defined(linux) or defined(openbsd):
         d_off*: Off  ## Not an offset. Value that ``telldir()`` would return.
+
     d_name*: array[0..255, char] ## Name of entry.
 
   Tflock* {.importc: "struct flock", final, pure,
diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim
index 80aa8bb2c..8d059dbbc 100644
--- a/lib/pure/asynchttpserver.nim
+++ b/lib/pure/asynchttpserver.nim
@@ -36,6 +36,9 @@ import httpcore
 
 export httpcore except parseHeader
 
+const
+  maxLine = 8*1024
+
 # TODO: If it turns out that the decisions that asynchttpserver makes
 # explicitly, about whether to close the client sockets or upgrade them are
 # wrong, then add a return value which determines what to do for the callback.
@@ -97,6 +100,18 @@ proc respond*(req: Request, code: HttpCode, content: string,
 
   if headers != nil:
     msg.addHeaders(headers)
+  msg.add("Content-Length: ")
+  # this particular way saves allocations:
+  msg.add content.len
+  msg.add "\c\L\c\L"
+  msg.add(content)
+  result = req.client.send(msg)
+
+proc respondError(req: Request, code: HttpCode): Future[void] =
+  ## Responds to the request with the specified ``HttpCode``.
+  let content = $code
+  var msg = "HTTP/1.1 " & content & "\c\L"
+
   msg.add("Content-Length: " & $content.len & "\c\L\c\L")
   msg.add(content)
   result = req.client.send(msg)
@@ -139,12 +154,16 @@ proc processClient(client: AsyncSocket, address: string,
     for i in 0..1:
       lineFut.mget().setLen(0)
       lineFut.clean()
-      await client.recvLineInto(lineFut) # TODO: Timeouts.
+      await client.recvLineInto(lineFut, maxLength=maxLine) # TODO: Timeouts.
 
       if lineFut.mget == "":
         client.close()
         return
 
+      if lineFut.mget.len > maxLine:
+        await request.respondError(Http413)
+        client.close()
+        return
       if lineFut.mget != "\c\L":
         break
 
@@ -157,19 +176,17 @@ proc processClient(client: AsyncSocket, address: string,
           # TODO: this is likely slow.
           request.reqMethod = parseEnum[HttpMethod]("http" & linePart)
         except ValueError:
-          asyncCheck request.respond(Http400, "Invalid request method. Got: " &
-                                     linePart)
+          asyncCheck request.respondError(Http400)
           continue
       of 1: parseUri(linePart, request.url)
       of 2:
         try:
           request.protocol = parseProtocol(linePart)
         except ValueError:
-          asyncCheck request.respond(Http400,
-            "Invalid request protocol. Got: " & linePart)
+          asyncCheck request.respondError(Http400)
           continue
       else:
-        await request.respond(Http400, "Invalid request. Got: " & lineFut.mget)
+        await request.respondError(Http400)
         continue
       inc i
 
@@ -178,10 +195,13 @@ proc processClient(client: AsyncSocket, address: string,
       i = 0
       lineFut.mget.setLen(0)
       lineFut.clean()
-      await client.recvLineInto(lineFut)
+      await client.recvLineInto(lineFut, maxLength=maxLine)
 
       if lineFut.mget == "":
         client.close(); return
+      if lineFut.mget.len > maxLine:
+        await request.respondError(Http413)
+        client.close(); return
       if lineFut.mget == "\c\L": break
       let (key, value) = parseHeader(lineFut.mget)
       request.headers[key] = value
diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim
index f0837d67d..ce4c9a9c9 100644
--- a/lib/pure/asyncmacro.nim
+++ b/lib/pure/asyncmacro.nim
@@ -40,6 +40,8 @@ template createCb(retFutureSym, iteratorNameSym,
         else:
           next.callback = cb
     except:
+      futureVarCompletions
+
       if retFutureSym.finished:
         # Take a look at tasyncexceptions for the bug which this fixes.
         # That test explains it better than I can here.
@@ -47,7 +49,6 @@ template createCb(retFutureSym, iteratorNameSym,
       else:
         retFutureSym.fail(getCurrentException())
 
-      futureVarCompletions
   cb()
   #{.pop.}
 proc generateExceptionCheck(futSym,
@@ -123,12 +124,16 @@ template createVar(result: var NimNode, futSymName: string,
   result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y
   useVar(result, futSym, valueReceiver, rootReceiver, fromNode)
 
-proc createFutureVarCompletions(futureVarIdents: seq[NimNode]): NimNode
-                                {.compileTime.} =
-  result = newStmtList()
+proc createFutureVarCompletions(futureVarIdents: seq[NimNode],
+    fromNode: NimNode): NimNode {.compileTime.} =
+  result = newNimNode(nnkStmtList, fromNode)
   # Add calls to complete each FutureVar parameter.
   for ident in futureVarIdents:
     # Only complete them if they have not been completed already by the user.
+    # TODO: Once https://github.com/nim-lang/Nim/issues/5617 is fixed.
+    # TODO: Add line info to the complete() call!
+    # In the meantime, this was really useful for debugging :)
+    #result.add(newCall(newIdentNode("echo"), newStrLitNode(fromNode.lineinfo)))
     result.add newIfStmt(
       (
         newCall(newIdentNode("not"),
@@ -145,6 +150,10 @@ proc processBody(node, retFutureSym: NimNode,
   case node.kind
   of nnkReturnStmt:
     result = newNimNode(nnkStmtList, node)
+
+    # As I've painfully found out, the order here really DOES matter.
+    result.add createFutureVarCompletions(futureVarIdents, node)
+
     if node[0].kind == nnkEmpty:
       if not subTypeIsVoid:
         result.add newCall(newIdentNode("complete"), retFutureSym,
@@ -158,8 +167,6 @@ proc processBody(node, retFutureSym: NimNode,
       else:
         result.add newCall(newIdentNode("complete"), retFutureSym, x)
 
-    result.add createFutureVarCompletions(futureVarIdents)
-
     result.add newNimNode(nnkReturnStmt, node).add(newNilLit())
     return # Don't process the children of this return stmt
   of nnkCommand, nnkCall:
@@ -347,6 +354,8 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
                                     futureVarIdents, nil)
   # don't do anything with forward bodies (empty)
   if procBody.kind != nnkEmpty:
+    procBody.add(createFutureVarCompletions(futureVarIdents, nil))
+
     if not subtypeIsVoid:
       procBody.insert(0, newNimNode(nnkPragma).add(newIdentNode("push"),
         newNimNode(nnkExprColonExpr).add(newNimNode(nnkBracketExpr).add(
@@ -366,8 +375,6 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
       # -> complete(retFuture)
       procBody.add(newCall(newIdentNode("complete"), retFutureSym))
 
-    procBody.add(createFutureVarCompletions(futureVarIdents))
-
     var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")],
                                   procBody, nnkIteratorDef)
     closureIterator[4] = newNimNode(nnkPragma, prc[6]).add(newIdentNode("closure"))
@@ -377,7 +384,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
     #var cbName = newIdentNode("cb")
     var procCb = getAst createCb(retFutureSym, iteratorNameSym,
                          newStrLitNode(prc[0].getName),
-                         createFutureVarCompletions(futureVarIdents))
+                         createFutureVarCompletions(futureVarIdents, nil))
     outerProcBody.add procCb
 
     # -> return retFuture
@@ -398,7 +405,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
   if procBody.kind != nnkEmpty:
     result[6] = outerProcBody
   #echo(treeRepr(result))
-  #if prc[0].getName == "beta":
+  #if prc[0].getName == "recvLineInto":
   #  echo(toStrLit(result))
 
 macro async*(prc: untyped): untyped =
diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim
index 2d5c65001..1ec751a64 100644
--- a/lib/pure/asyncnet.nim
+++ b/lib/pure/asyncnet.nim
@@ -464,8 +464,7 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
   ## The partial line **will be lost**.
   ##
   ## The ``maxLength`` parameter determines the maximum amount of characters
-  ## that can be read before a ``ValueError`` is raised. This prevents Denial
-  ## of Service (DOS) attacks.
+  ## that can be read. ``resString`` will be truncated after that.
   ##
   ## **Warning**: The ``Peek`` flag is not yet implemented.
   ##
@@ -519,10 +518,7 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
       socket.currPos.inc()
 
       # Verify that this isn't a DOS attack: #3847.
-      if resString.mget.len > maxLength:
-        let msg = "recvLine received more than the specified `maxLength` " &
-                  "allowed."
-        raise newException(ValueError, msg)
+      if resString.mget.len > maxLength: break
   else:
     var c = ""
     while true:
@@ -546,10 +542,7 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
       resString.mget.add c
 
       # Verify that this isn't a DOS attack: #3847.
-      if resString.mget.len > maxLength:
-        let msg = "recvLine received more than the specified `maxLength` " &
-                  "allowed."
-        raise newException(ValueError, msg)
+      if resString.mget.len > maxLength: break
   resString.complete()
 
 proc recvLine*(socket: AsyncSocket,
@@ -569,8 +562,7 @@ proc recvLine*(socket: AsyncSocket,
   ## The partial line **will be lost**.
   ##
   ## The ``maxLength`` parameter determines the maximum amount of characters
-  ## that can be read before a ``ValueError`` is raised. This prevents Denial
-  ## of Service (DOS) attacks.
+  ## that can be read. The result is truncated after that.
   ##
   ## **Warning**: The ``Peek`` flag is not yet implemented.
   ##
diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim
index 8cdb83e19..f438a85e7 100644
--- a/lib/pure/concurrency/threadpool.nim
+++ b/lib/pure/concurrency/threadpool.nim
@@ -418,7 +418,7 @@ proc spawn*(call: expr): expr {.magic: "Spawn".}
 proc pinnedSpawn*(id: ThreadId; call: expr): expr {.magic: "Spawn".}
   ## always spawns a new task on the worker thread with ``id``, so that
   ## the 'call' is **always** executed on
-  ## the this thread. 'call' has to be proc call 'p(...)' where 'p'
+  ## the thread. 'call' has to be proc call 'p(...)' where 'p'
   ## is gcsafe and has a return type that is either 'void' or compatible
   ## with ``FlowVar[T]``.
 
diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim
index aa8f1958d..a5ab40ca4 100644
--- a/lib/pure/httpcore.nim
+++ b/lib/pure/httpcore.nim
@@ -285,7 +285,7 @@ proc `$`*(code: HttpCode): string =
 proc `==`*(a, b: HttpCode): bool {.borrow.}
 
 proc `==`*(rawCode: string, code: HttpCode): bool =
-  return rawCode.toLower() == ($code).toLower()
+  return cmpIgnoreCase(rawCode, $code) == 0
 
 proc is2xx*(code: HttpCode): bool =
   ## Determines whether ``code`` is a 2xx HTTP status code.
@@ -304,7 +304,7 @@ proc is5xx*(code: HttpCode): bool =
   return code.int in {500 .. 599}
 
 proc `$`*(httpMethod: HttpMethod): string =
-  return (system.`$`(httpMethod))[4 .. ^1].toUpper()
+  return (system.`$`(httpMethod))[4 .. ^1].toUpperAscii()
 
 when isMainModule:
   var test = newHttpHeaders()
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index 7f6783358..56f8b9399 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -1010,8 +1010,7 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1,
   ## the specified time an ETimeout exception will be raised.
   ##
   ## The ``maxLength`` parameter determines the maximum amount of characters
-  ## that can be read before a ``ValueError`` is raised. This prevents Denial
-  ## of Service (DOS) attacks.
+  ## that can be read. The result is truncated after that.
   ##
   ## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
 
@@ -1047,10 +1046,7 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1,
     add(line.string, c)
 
     # Verify that this isn't a DOS attack: #3847.
-    if line.string.len > maxLength:
-      let msg = "recvLine received more than the specified `maxLength` " &
-                "allowed."
-      raise newException(ValueError, msg)
+    if line.string.len > maxLength: break
 
 proc recvLine*(socket: Socket, timeout = -1,
                flags = {SocketFlag.SafeDisconn},
@@ -1069,8 +1065,7 @@ proc recvLine*(socket: Socket, timeout = -1,
   ## the specified time an ETimeout exception will be raised.
   ##
   ## The ``maxLength`` parameter determines the maximum amount of characters
-  ## that can be read before a ``ValueError`` is raised. This prevents Denial
-  ## of Service (DOS) attacks.
+  ## that can be read. The result is truncated after that.
   ##
   ## **Warning**: Only the ``SafeDisconn`` flag is currently supported.
   result = ""
diff --git a/tests/async/tioselectors.nim b/tests/async/tioselectors.nim
index 048111d03..5e5a18a1b 100644
--- a/tests/async/tioselectors.nim
+++ b/tests/async/tioselectors.nim
@@ -508,8 +508,8 @@ else:
     freeAddrInfo(aiList)
     # for some reason Windows select doesn't return both
     # descriptors from first call, so we need to make 2 calls
-    discard selector.select(100)
-    var rcm = selector.select(100)
+    discard selector.select(1000)
+    var rcm = selector.select(1000)
     assert(len(rcm) == 2)
 
     var sockAddress = SockAddr()
@@ -526,7 +526,7 @@ else:
 
     selector.updateHandle(client_socket, {Event.Read})
 
-    var rc2 = selector.select(100)
+    var rc2 = selector.select(1000)
     assert(len(rc2) == 1)
 
     var read_count = recv(server2_socket, addr buffer[0], 128, 0)
@@ -595,7 +595,7 @@ else:
     proc event_wait_thread(event: SelectEvent) {.thread.} =
       var selector = newSelector[int]()
       selector.registerEvent(event, 1)
-      var rc = selector.select(500)
+      var rc = selector.select(1500)
       if len(rc) == 1:
         inc(counter)
       selector.unregister(event)