summary refs log tree commit diff stats
path: root/lib/pure/httpclient.nim
diff options
context:
space:
mode:
authorDominik Picheta <dominikpicheta@googlemail.com>2012-12-23 11:22:42 +0000
committerDominik Picheta <dominikpicheta@googlemail.com>2012-12-23 11:22:42 +0000
commit3cbac135465d63a320ff5fcdb5d3ba4ba89da4c8 (patch)
treef3b2ad93d0f74d96a41f3692a924a82f9494061a /lib/pure/httpclient.nim
parent6cb8edfce94c9236dfd8a30380d7474a8ede6f87 (diff)
downloadNim-3cbac135465d63a320ff5fcdb5d3ba4ba89da4c8.tar.gz
Rewrote the implementation of parsing chunked transfer coding in
httpclient. Fixes #272.
Diffstat (limited to 'lib/pure/httpclient.nim')
-rwxr-xr-xlib/pure/httpclient.nim92
1 files changed, 36 insertions, 56 deletions
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index 20b448123..5be4af8a4 100755
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -80,62 +80,43 @@ proc fileError(msg: string) =
   e.msg = msg
   raise e
 
-proc charAt(d: var string, i: var int, s: TSocket): char {.inline.} = 
-  result = d[i]
-  while result == '\0':
-    d = string(s.recv())
-    i = 0
-    result = d[i]
-
 proc parseChunks(s: TSocket): string =
-  # get chunks:
-  var i = 0
   result = ""
-  var d = s.recv().string
+  var ri = 0
   while true:
+    var chunkSizeStr = ""
     var chunkSize = 0
-    var digitFound = false
-    while true: 
-      case d[i]
-      of '0'..'9': 
-        digitFound = true
-        chunkSize = chunkSize shl 4 or (ord(d[i]) - ord('0'))
-      of 'a'..'f': 
-        digitFound = true
-        chunkSize = chunkSize shl 4 or (ord(d[i]) - ord('a') + 10)
-      of 'A'..'F': 
-        digitFound = true
-        chunkSize = chunkSize shl 4 or (ord(d[i]) - ord('A') + 10)
-      of '\0': 
-        d = string(s.recv())
-        i = -1
-      else: break
-      inc(i)
-    if not digitFound: httpError("Chunksize expected")
+    if s.recvLine(chunkSizeStr):
+      var i = 0
+      if chunkSizeStr == "":
+        httpError("Server terminated connection prematurely")
+      while true:
+        case chunkSizeStr[i]
+        of '0'..'9':
+          chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0'))
+        of 'a'..'f':
+          chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10)
+        of 'A'..'F':
+          chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10)
+        of '\0':
+          break
+        of ';':
+          # http://tools.ietf.org/html/rfc2616#section-3.6.1
+          # We don't care about chunk-extensions.
+          break
+        else:
+          httpError("Invalid chunk size: " & chunkSizeStr)
+        inc(i)
     if chunkSize <= 0: break
-    while charAt(d, i, s) notin {'\C', '\L', '\0'}: inc(i)
-    if charAt(d, i, s) == '\C': inc(i)
-    if charAt(d, i, s) == '\L': inc(i)
-    else: httpError("CR-LF after chunksize expected")
-    
-    var x = substr(d, i, i+chunkSize-1)
-    var size = x.len
-    result.add(x)
-    inc(i, size)
-    if size < chunkSize:
-      # read in the rest:
-      var missing = chunkSize - size
-      var L = result.len
-      setLen(result, L + missing)    
-      while missing > 0:
-        var bytesRead = s.recv(addr(result[L]), missing)
-        inc(L, bytesRead)
-        dec(missing, bytesRead)
-      # next chunk:
-      d = string(s.recv())
-      i = 0
-    # skip trailing CR-LF:
-    while charAt(d, i, s) in {'\C', '\L'}: inc(i)
+    result.setLen(ri+chunkSize)
+    var bytesRead = 0
+    while bytesRead != chunkSize:
+      let ret = recv(s, addr(result[ri]), chunkSize-bytesRead)
+      ri += ret
+      bytesRead += ret
+    s.skip(2) # Skip \c\L
+    # Trailer headers will only be sent if the request specifies that we want
+    # them: http://tools.ietf.org/html/rfc2616#section-3.6.1
   
 proc parseBody(s: TSocket,
                headers: PStringTable): string =
@@ -250,7 +231,6 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
   ## | Requests ``url`` with the specified ``httpMethod``.
   ## | Extra headers can be specified and must be seperated by ``\c\L``
   var r = parseUrl(url)
-  
   var headers = substr($httpMethod, len("http"))
   headers.add(" /" & r.path & r.query)
 
@@ -285,7 +265,7 @@ proc redirection(status: string): bool =
       return True
   
 proc get*(url: string, maxRedirects = 5, sslContext: PSSLContext = defaultSSLContext): TResponse =
-  ## | GET's the ``url`` and returns a ``TResponse`` object
+  ## | GETs the ``url`` and returns a ``TResponse`` object
   ## | This proc also handles redirection
   result = request(url)
   for i in 1..maxRedirects:
@@ -295,7 +275,7 @@ proc get*(url: string, maxRedirects = 5, sslContext: PSSLContext = defaultSSLCon
       result = request(locationHeader, sslContext = sslContext)
       
 proc getContent*(url: string, sslContext: PSSLContext = defaultSSLContext): string =
-  ## | GET's the body and returns it as a string.
+  ## | GETs the body and returns it as a string.
   ## | Raises exceptions for the status codes ``4xx`` and ``5xx``
   var r = get(url, sslContext = sslContext)
   if r.status[0] in {'4','5'}:
@@ -305,7 +285,7 @@ proc getContent*(url: string, sslContext: PSSLContext = defaultSSLContext): stri
   
 proc post*(url: string, extraHeaders = "", body = "", 
            maxRedirects = 5, sslContext: PSSLContext = defaultSSLContext): TResponse =
-  ## | POST's ``body`` to the ``url`` and returns a ``TResponse`` object.
+  ## | POSTs ``body`` to the ``url`` and returns a ``TResponse`` object.
   ## | This proc adds the necessary Content-Length header.
   ## | This proc also handles redirection.
   var xh = extraHeaders & "Content-Length: " & $len(body) & "\c\L"
@@ -319,7 +299,7 @@ proc post*(url: string, extraHeaders = "", body = "",
   
 proc postContent*(url: string, extraHeaders = "", body = "",
                   sslContext: PSSLContext = defaultSSLContext): string =
-  ## | POST's ``body`` to ``url`` and returns the response's body as a string
+  ## | POSTs ``body`` to ``url`` and returns the response's body as a string
   ## | Raises exceptions for the status codes ``4xx`` and ``5xx``
   var r = post(url, extraHeaders, body)
   if r.status[0] in {'4','5'}: