summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/pure/asynchttpserver.nim21
-rw-r--r--lib/pure/httpclient.nim46
-rw-r--r--lib/pure/includes/asyncfutures.nim7
-rw-r--r--lib/pure/os.nim1
-rw-r--r--lib/pure/osproc.nim6
-rw-r--r--lib/pure/parsecfg.nim14
-rw-r--r--lib/pure/parsexml.nim4
-rw-r--r--lib/pure/strutils.nim10
-rw-r--r--lib/system.nim2
9 files changed, 82 insertions, 29 deletions
diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim
index 4c7037a4e..68da5f7c9 100644
--- a/lib/pure/asynchttpserver.nim
+++ b/lib/pure/asynchttpserver.nim
@@ -133,14 +133,21 @@ proc processClient(client: AsyncSocket, address: string,
     assert client != nil
     request.client = client
 
-    # First line - GET /path HTTP/1.1
-    lineFut.mget().setLen(0)
-    lineFut.clean()
-    await client.recvLineInto(lineFut) # TODO: Timeouts.
-    if lineFut.mget == "":
-      client.close()
-      return
+    # We should skip at least one empty line before the request
+    # https://tools.ietf.org/html/rfc7230#section-3.5
+    for i in 0..1:
+      lineFut.mget().setLen(0)
+      lineFut.clean()
+      await client.recvLineInto(lineFut) # TODO: Timeouts.
+
+      if lineFut.mget == "":
+        client.close()
+        return
 
+      if lineFut.mget != "\c\L":
+        break
+
+    # First line - GET /path HTTP/1.1
     var i = 0
     for linePart in lineFut.mget.split(' '):
       case i
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index c56d13b57..ba967b14f 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -117,7 +117,7 @@
 ## only basic authentication is supported at the moment.
 
 import net, strutils, uri, parseutils, strtabs, base64, os, mimetypes,
-  math, random, httpcore, times
+  math, random, httpcore, times, tables
 import asyncnet, asyncdispatch
 import nativesockets
 
@@ -989,8 +989,20 @@ proc newConnection(client: HttpClient | AsyncHttpClient,
     client.currentURL = url
     client.connected = true
 
+proc override(fallback, override: HttpHeaders): HttpHeaders =
+  # Right-biased map union for `HttpHeaders`
+  if override.isNil:
+    return fallback
+
+  result = newHttpHeaders()
+  # Copy by value
+  result.table[] = fallback.table[]
+  for k, vs in override.table:
+    result[k] = vs
+
 proc request*(client: HttpClient | AsyncHttpClient, url: string,
-              httpMethod: string, body = ""): Future[Response] {.multisync.} =
+              httpMethod: string, body = "",
+              headers: HttpHeaders = nil): Future[Response] {.multisync.} =
   ## Connects to the hostname specified by the URL and performs a request
   ## using the custom method string specified by ``httpMethod``.
   ##
@@ -1023,13 +1035,15 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string,
   else:
     await newConnection(client, connectionUrl)
 
-  if not client.headers.hasKey("user-agent") and client.userAgent != "":
-    client.headers["User-Agent"] = client.userAgent
+  let effectiveHeaders = client.headers.override(headers)
 
-  var headers = generateHeaders(requestUrl, httpMethod,
-                                client.headers, body, client.proxy)
+  if not effectiveHeaders.hasKey("user-agent") and client.userAgent != "":
+    effectiveHeaders["User-Agent"] = client.userAgent
 
-  await client.socket.send(headers)
+  var headersString = generateHeaders(requestUrl, httpMethod,
+                                      effectiveHeaders, body, client.proxy)
+
+  await client.socket.send(headersString)
   if body != "":
     await client.socket.send(body)
 
@@ -1040,7 +1054,8 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string,
   client.proxy = savedProxy
 
 proc request*(client: HttpClient | AsyncHttpClient, url: string,
-              httpMethod = HttpGET, body = ""): Future[Response] {.multisync.} =
+              httpMethod = HttpGET, body = "",
+              headers: HttpHeaders = nil): Future[Response] {.multisync.} =
   ## Connects to the hostname specified by the URL and performs a request
   ## using the method specified.
   ##
@@ -1050,7 +1065,8 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string,
   ##
   ## When a request is made to a different hostname, the current connection will
   ## be closed.
-  result = await request(client, url, $httpMethod, body)
+  result = await request(client, url, $httpMethod, body,
+                         headers = headers)
 
 proc get*(client: HttpClient | AsyncHttpClient,
           url: string): Future[Response] {.multisync.} =
@@ -1097,18 +1113,22 @@ proc post*(client: HttpClient | AsyncHttpClient, url: string, body = "",
     else:
       x
   var xb = mpBody.withNewLine() & body
+
+  var headers = newHttpHeaders()
   if multipart != nil:
-    client.headers["Content-Type"] = mpHeader.split(": ")[1]
-  client.headers["Content-Length"] = $len(xb)
+    headers["Content-Type"] = mpHeader.split(": ")[1]
+  headers["Content-Length"] = $len(xb)
 
-  result = await client.request(url, HttpPOST, xb)
+  result = await client.request(url, HttpPOST, xb,
+                                headers = headers)
   # Handle redirects.
   var lastURL = url
   for i in 1..client.maxRedirects:
     if result.status.redirection():
       let redirectTo = getNewLocation(lastURL, result.headers)
       var meth = if result.status != "307": HttpGet else: HttpPost
-      result = await client.request(redirectTo, meth, xb)
+      result = await client.request(redirectTo, meth, xb,
+                                    headers = headers)
       lastURL = redirectTo
 
 proc postContent*(client: HttpClient | AsyncHttpClient, url: string,
diff --git a/lib/pure/includes/asyncfutures.nim b/lib/pure/includes/asyncfutures.nim
index d78464a91..dfcfa37a0 100644
--- a/lib/pure/includes/asyncfutures.nim
+++ b/lib/pure/includes/asyncfutures.nim
@@ -246,6 +246,7 @@ proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
 proc all*[T](futs: varargs[Future[T]]): auto =
   ## Returns a future which will complete once
   ## all futures in ``futs`` complete.
+  ## If the argument is empty, the returned future completes immediately.
   ##
   ## If the awaited futures are not ``Future[void]``, the returned future
   ## will hold the values of all awaited futures in a sequence.
@@ -270,6 +271,9 @@ proc all*[T](futs: varargs[Future[T]]): auto =
           if completedFutures == totalFutures:
             retFuture.complete()
 
+    if totalFutures == 0:
+      retFuture.complete()
+
     return retFuture
 
   else:
@@ -292,4 +296,7 @@ proc all*[T](futs: varargs[Future[T]]): auto =
 
       setCallback(i)
 
+    if retValues.len == 0:
+      retFuture.complete(retValues)
+
     return retFuture
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 8f8f36b80..f077e798a 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -613,6 +613,7 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1",
       if bytesread != bufSize: break
     dealloc(buf)
     close(s)
+    flushFile(d)
     close(d)
 
 proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 44ec5b548..76bd2dfe1 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -841,15 +841,19 @@ elif not defined(useNimRtl):
       if data.workingDir.len > 0:
         setCurrentDir($data.workingDir)
       var pid: Pid
+      var err: OSErrorCode
 
       if data.optionPoUsePath:
         res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
+        if res != 0'i32: err = osLastError()
       else:
         res = posix_spawn(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
+        if res != 0'i32: err = osLastError()
 
       discard posix_spawn_file_actions_destroy(fops)
       discard posix_spawnattr_destroy(attr)
-      chck res
+      if res != 0'i32: raiseOSError(err)
+
       return pid
   else:
     proc startProcessAuxFork(data: StartProcessData): Pid =
diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim
index c648b0703..47670cc19 100644
--- a/lib/pure/parsecfg.nim
+++ b/lib/pure/parsecfg.nim
@@ -512,10 +512,16 @@ proc writeConfig*(dict: Config, filename: string) =
             kv = key
           if value != "": ## If the key is not empty
             if not allCharsInSet(value, SymChars):
-              kv.add(segmentChar)
-              kv.add("\"")
-              kv.add(replace(value))
-              kv.add("\"")
+              if find(value, '"') == -1:
+                kv.add(segmentChar)
+                kv.add("\"")
+                kv.add(replace(value))
+                kv.add("\"")
+              else:
+                kv.add(segmentChar)
+                kv.add("\"\"\"")
+                kv.add(replace(value))
+                kv.add("\"\"\"")
             else:
               kv.add(segmentChar)
               kv.add(value)
diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim
index aa4a13ecf..978c9c516 100644
--- a/lib/pure/parsexml.nim
+++ b/lib/pure/parsexml.nim
@@ -572,6 +572,10 @@ proc parseAttribute(my: var XmlParser) =
           inc(pos)
   else:
     markError(my, errQuoteExpected)
+    # error corrections: guess what was meant
+    while buf[pos] != '>' and buf[pos] > ' ':
+      add(my.b, buf[pos])
+      inc pos
   my.bufpos = pos
   parseWhitespace(my, skip=true)
 
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index bfc32bc71..129869373 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -179,9 +179,10 @@ proc isLowerAscii*(s: string): bool {.noSideEffect, procvar,
   if s.len() == 0:
     return false
 
-  result = true
   for c in s:
-    result = c.isLowerAscii() and result
+    if not c.isLowerAscii():
+      return false
+  true
 
 proc isUpperAscii*(s: string): bool {.noSideEffect, procvar,
   rtl, extern: "nsuIsUpperAsciiStr".}=
@@ -193,9 +194,10 @@ proc isUpperAscii*(s: string): bool {.noSideEffect, procvar,
   if s.len() == 0:
     return false
 
-  result = true
   for c in s:
-    result = c.isUpperAscii() and result
+    if not c.isUpperAscii():
+      return false
+  true
 
 proc toLowerAscii*(c: char): char {.noSideEffect, procvar,
   rtl, extern: "nsuToLowerAsciiChar".} =
diff --git a/lib/system.nim b/lib/system.nim
index d938e59bc..9547673a5 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -2629,6 +2629,8 @@ when not defined(JS): #and not defined(nimscript):
     FileMode* = enum           ## The file mode when opening a file.
       fmRead,                   ## Open the file for read access only.
       fmWrite,                  ## Open the file for write access only.
+                                ## If the file does not exist, it will be
+                                ## created.
       fmReadWrite,              ## Open the file for read and write access.
                                 ## If the file does not exist, it will be
                                 ## created.