summary refs log tree commit diff stats
path: root/lib/pure/httpclient.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure/httpclient.nim')
-rw-r--r--lib/pure/httpclient.nim54
1 files changed, 42 insertions, 12 deletions
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index 9799821ae..68adf5f49 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -432,16 +432,29 @@ proc generateHeaders(r: TURL, httpMethod: THttpMethod,
 type
   PAsyncHttpClient = ref object
     socket: PAsyncSocket
+    connected: bool
     currentURL: TURL ## Where we are currently connected.
     headers: PStringTable
     userAgent: string
 
 proc newAsyncHttpClient*(): PAsyncHttpClient =
   new result
-  result.socket = newAsyncSocket()
   result.headers = newStringTable(modeCaseInsensitive)
   result.userAgent = defUserAgent
 
+proc close*(client: PAsyncHttpClient) =
+  ## Closes any connections held by the HttpClient.
+  if client.connected:
+    client.socket.close()
+    client.connected = false
+
+proc recvFull(socket: PAsyncSocket, size: int): PFuture[string] {.async.} =
+  ## Ensures that all the data requested is read and returned.
+  result = ""
+  while true:
+    if size == result.len: break
+    result.add await socket.recv(size - result.len)
+
 proc parseChunks(client: PAsyncHttpClient): PFuture[string] {.async.} =
   result = ""
   var ri = 0
@@ -469,8 +482,8 @@ proc parseChunks(client: PAsyncHttpClient): PFuture[string] {.async.} =
         httpError("Invalid chunk size: " & chunkSizeStr)
       inc(i)
     if chunkSize <= 0: break
-    result.add await recv(client.socket, chunkSize)
-    discard await recv(client.socket, 2) # Skip \c\L
+    result.add await recvFull(client.socket, chunkSize)
+    discard await recvFull(client.socket, 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
   
@@ -485,9 +498,12 @@ proc parseBody(client: PAsyncHttpClient,
     var contentLengthHeader = headers["Content-Length"]
     if contentLengthHeader != "":
       var length = contentLengthHeader.parseint()
-      result = await client.socket.recv(length)
+      result = await client.socket.recvFull(length)
       if result == "":
-        httpError("Got disconnected while trying to recv body.")
+        httpError("Got disconnected while trying to read body.")
+      if result.len != length:
+        httpError("Received length doesn't match expected length. Wanted " &
+                  $length & " got " & $result.len)
     else:
       # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.4 TODO
       
@@ -496,7 +512,7 @@ proc parseBody(client: PAsyncHttpClient,
       if headers["Connection"] == "close":
         var buf = ""
         while True:
-          buf = await client.socket.recv(4000)
+          buf = await client.socket.recvFull(4000)
           if buf == "": break
           result.add(buf)
 
@@ -517,7 +533,11 @@ proc parseResponse(client: PAsyncHttpClient,
     if not parsedStatus:
       # Parse HTTP version info and status code.
       var le = skipIgnoreCase(line, "HTTP/", linei)
-      if le <= 0: httpError("invalid http version")
+      if le <= 0:
+        while true:
+          let nl = await client.socket.recvLine()
+          echo("Got another line: ", nl)
+        httpError("invalid http version, " & line.repr)
       inc(linei, le)
       le = skipIgnoreCase(line, "1.1", linei)
       if le > 0: result.version = "1.1"
@@ -550,16 +570,19 @@ proc parseResponse(client: PAsyncHttpClient,
 proc newConnection(client: PAsyncHttpClient, url: TURL) {.async.} =
   if client.currentURL.hostname != url.hostname or
       client.currentURL.scheme != url.scheme:
+    if client.connected: client.close()
+    client.socket = newAsyncSocket()
     if url.scheme == "https":
       assert false, "TODO SSL"
 
     # TODO: I should be able to write 'net.TPort' here...
     let port =
       if url.port == "": rawsockets.TPort(80)
-      else: rawsockets.TPort(url.port.parseInt) 
+      else: rawsockets.TPort(url.port.parseInt)
     
     await client.socket.connect(url.hostname, port)
     client.currentURL = url
+    client.connected = true
 
 proc request*(client: PAsyncHttpClient, url: string, httpMethod = httpGET,
               body = ""): PFuture[TResponse] {.async.} =
@@ -588,11 +611,18 @@ when isMainModule:
       echo("Body:\n")
       echo(resp.body)
 
-      var resp1 = await client.request("http://picheta.me/aboutme.html")
-      echo("Got response: ", resp1.status)
+      resp = await client.request("http://picheta.me/asfas.html")
+      echo("Got response: ", resp.status)
+
+      resp = await client.request("http://picheta.me/aboutme.html")
+      echo("Got response: ", resp.status)
+
+      resp = await client.request("http://nimrod-lang.org/")
+      echo("Got response: ", resp.status)
+
+      resp = await client.request("http://nimrod-lang.org/download.html")
+      echo("Got response: ", resp.status)
 
-      var resp2 = await client.request("http://picheta.me/aboutme.html")
-      echo("Got response: ", resp2.status)
     main()
     runForever()