summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--lib/pure/httpclient.nim32
-rw-r--r--lib/pure/net.nim40
-rw-r--r--lib/wrappers/openssl.nim13
3 files changed, 55 insertions, 30 deletions
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index 2b161778c..a7e1aeab2 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -88,10 +88,9 @@
 ## constructor should be used for this purpose. However,
 ## currently only basic authentication is supported.
 
-import sockets, strutils, parseurl, parseutils, strtabs, base64, os
+import net, strutils, uri, parseutils, strtabs, base64, os
 import asyncnet, asyncdispatch
 import rawsockets
-from net import nil
 
 type
   Response* = tuple[
@@ -101,7 +100,7 @@ type
     body: string]
 
   Proxy* = ref object
-    url*: Url
+    url*: Uri
     auth*: string
 
   ProtocolError* = object of IOError   ## exception that is raised when server
@@ -279,7 +278,7 @@ else:
 
 proc newProxy*(url: string, auth = ""): Proxy =
   ## Constructs a new ``TProxy`` object.
-  result = Proxy(url: parseUrl(url), auth: auth)
+  result = Proxy(url: parseUri(url), auth: auth)
 
 proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
               body = "",
@@ -290,7 +289,7 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
   ## | Extra headers can be specified and must be seperated by ``\c\L``
   ## | An optional timeout can be specified in miliseconds, if reading from the
   ## server takes longer than specified an ETimeout exception will be raised.
-  var r = if proxy == nil: parseUrl(url) else: proxy.url
+  var r = if proxy == nil: parseUri(url) else: proxy.url
   var headers = substr($httpMethod, len("http"))
   if proxy == nil:
     headers.add(" /" & r.path & r.query)
@@ -308,18 +307,18 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
   add(headers, extraHeaders)
   add(headers, "\c\L")
 
-  var s = socket()
-  if s == invalidSocket: raiseOSError(osLastError())
-  var port = sockets.Port(80)
+  var s = newSocket()
+  if s == nil: raiseOSError(osLastError())
+  var port = net.Port(80)
   if r.scheme == "https":
     when defined(ssl):
       sslContext.wrapSocket(s)
-      port = sockets.Port(443)
+      port = net.Port(443)
     else:
       raise newException(HttpRequestError,
                 "SSL support is not available. Cannot connect over SSL.")
   if r.port != "":
-    port = sockets.Port(r.port.parseInt)
+    port = net.Port(r.port.parseInt)
 
   if timeout == -1:
     s.connect(r.hostname, port)
@@ -342,9 +341,9 @@ proc getNewLocation(lastUrl: string, headers: StringTableRef): string =
   result = headers["Location"]
   if result == "": httpError("location header expected")
   # Relative URLs. (Not part of the spec, but soon will be.)
-  let r = parseURL(result)
+  let r = parseUri(result)
   if r.hostname == "" and r.path != "":
-    let origParsed = parseURL(lastUrl)
+    let origParsed = parseUri(lastUrl)
     result = origParsed.hostname & "/" & r.path
 
 proc get*(url: string, extraHeaders = "", maxRedirects = 5,
@@ -437,7 +436,7 @@ proc downloadFile*(url: string, outputFilename: string,
   else:
     fileError("Unable to open file")
 
-proc generateHeaders(r: Url, httpMethod: HttpMethod,
+proc generateHeaders(r: Uri, httpMethod: HttpMethod,
                      headers: StringTableRef): string =
   result = substr($httpMethod, len("http"))
   # TODO: Proxies
@@ -455,7 +454,7 @@ type
   AsyncHttpClient* = ref object
     socket: AsyncSocket
     connected: bool
-    currentURL: Url ## Where we are currently connected.
+    currentURL: Uri ## Where we are currently connected.
     headers*: StringTableRef
     maxRedirects: int
     userAgent: string
@@ -499,7 +498,6 @@ proc recvFull(socket: AsyncSocket, size: int): Future[string] {.async.} =
 
 proc parseChunks(client: AsyncHttpClient): Future[string] {.async.} =
   result = ""
-  var ri = 0
   while true:
     var chunkSize = 0
     var chunkSizeStr = await client.socket.recvLine()
@@ -607,7 +605,7 @@ proc parseResponse(client: AsyncHttpClient,
   else:
     result.body = ""
 
-proc newConnection(client: AsyncHttpClient, url: Url) {.async.} =
+proc newConnection(client: AsyncHttpClient, url: Uri) {.async.} =
   if client.currentURL.hostname != url.hostname or
       client.currentURL.scheme != url.scheme:
     if client.connected: client.close()
@@ -643,7 +641,7 @@ proc request*(client: AsyncHttpClient, url: string, httpMethod = httpGET,
   ## connection can be closed by using the ``close`` procedure.
   ##
   ## The returned future will complete once the request is completed.
-  let r = parseUrl(url)
+  let r = parseUri(url)
   await newConnection(client, r)
 
   if not client.headers.hasKey("user-agent") and client.userAgent != "":
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index 8afc6c5c5..28b84eb39 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -256,7 +256,21 @@ proc socketError*(socket: Socket, err: int = -1, async = false,
           else: raiseSSLError("Not enough data on socket.")
         of SSL_ERROR_WANT_X509_LOOKUP:
           raiseSSLError("Function for x509 lookup has been called.")
-        of SSL_ERROR_SYSCALL, SSL_ERROR_SSL:
+        of SSL_ERROR_SYSCALL:
+          var errStr = "IO error has occured "
+          let sslErr = ErrPeekLastError()
+          if sslErr == 0 and err == 0:
+            errStr.add "because an EOF was observed that violates the protocol"
+          elif sslErr == 0 and err == -1:
+            errStr.add "in the BIO layer"
+          else:
+            let errStr = $ErrErrorString(sslErr, nil)
+            raiseSSLError(errStr & ": " & errStr)
+          let osMsg = osErrorMsg osLastError()
+          if osMsg != "":
+            errStr.add ". The OS reports: " & osMsg
+          raise newException(OSError, errStr)
+        of SSL_ERROR_SSL:
           raiseSSLError()
         else: raiseSSLError("Unknown Error")
   
@@ -418,15 +432,21 @@ proc accept*(server: Socket, client: var Socket,
 
 proc close*(socket: Socket) =
   ## Closes a socket.
-  socket.fd.close()
-  when defined(ssl):
-    if socket.isSSL:
-      let res = SSLShutdown(socket.sslHandle)
-      if res == 0:
-        if SSLShutdown(socket.sslHandle) != 1:
-          socketError(socket)
-      elif res != 1:
-        socketError(socket)
+  try:
+    when defined(ssl):
+      if socket.isSSL:
+        ErrClearError()
+        # As we are closing the underlying socket immediately afterwards,
+        # it is valid, under the TLS standard, to perform a unidirectional
+        # shutdown i.e not wait for the peers "close notify" alert with a second
+        # call to SSLShutdown
+        let res = SSLShutdown(socket.sslHandle)
+        if res == 0:
+          discard
+        elif res != 1:
+          socketError(socket, res)
+  finally:
+    socket.fd.close()
 
 proc toCInt*(opt: SOBool): cint =
   ## Converts a ``SOBool`` into its Socket Option cint representation.
diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim
index 6e85fb9dd..ba25fbf1a 100644
--- a/lib/wrappers/openssl.nim
+++ b/lib/wrappers/openssl.nim
@@ -89,6 +89,8 @@ type
 {.deprecated: [PSSL: SslPtr, PSSL_CTX: SslCtx, PBIO: BIO].}
 
 const 
+  SSL_SENT_SHUTDOWN* = 1
+  SSL_RECEIVED_SHUTDOWN* = 2
   EVP_MAX_MD_SIZE* = 16 + 20
   SSL_ERROR_NONE* = 0
   SSL_ERROR_SSL* = 1
@@ -233,6 +235,8 @@ proc SSL_CTX_check_private_key*(ctx: SslCtx): cInt{.cdecl, dynlib: DLLSSLName,
 proc SSL_set_fd*(ssl: SslPtr, fd: SocketHandle): cint{.cdecl, dynlib: DLLSSLName, importc.}
 
 proc SSL_shutdown*(ssl: SslPtr): cInt{.cdecl, dynlib: DLLSSLName, importc.}
+proc SSL_set_shutdown*(ssl: SslPtr, mode: cint) {.cdecl, dynlib: DLLSSLName, importc: "SSL_set_shutdown".}
+proc SSL_get_shutdown*(ssl: SslPtr): cint {.cdecl, dynlib: DLLSSLName, importc: "SSL_get_shutdown".}
 proc SSL_connect*(ssl: SslPtr): cint{.cdecl, dynlib: DLLSSLName, importc.}
 proc SSL_read*(ssl: SslPtr, buf: pointer, num: int): cint{.cdecl, dynlib: DLLSSLName, importc.}
 proc SSL_write*(ssl: SslPtr, buf: cstring, num: int): cint{.cdecl, dynlib: DLLSSLName, importc.}
@@ -314,6 +318,11 @@ proc sslDoHandshake*(ssl: SslPtr): cint {.cdecl,
     dynlib: DLLSSLName, importc: "SSL_do_handshake".}
 
 
+
+proc ErrClearError*(){.cdecl, dynlib: DLLUtilName, importc: "ERR_clear_error".}
+proc ErrFreeStrings*(){.cdecl, dynlib: DLLUtilName, importc: "ERR_free_strings".}
+proc ErrRemoveState*(pid: cInt){.cdecl, dynlib: DLLUtilName, importc: "ERR_remove_state".}
+
 when true:
   discard
 else:
@@ -414,9 +423,7 @@ else:
     #  function ErrErrorString(e: cInt; buf: PChar): PChar;
   proc SSLeayversion*(t: cInt): cstring{.cdecl, dynlib: DLLUtilName, importc.}
 
-  proc ErrClearError*(){.cdecl, dynlib: DLLUtilName, importc.}
-  proc ErrFreeStrings*(){.cdecl, dynlib: DLLUtilName, importc.}
-  proc ErrRemoveState*(pid: cInt){.cdecl, dynlib: DLLUtilName, importc.}
+
   proc OPENSSLaddallalgorithms*(){.cdecl, dynlib: DLLUtilName, importc.}
   proc CRYPTOcleanupAllExData*(){.cdecl, dynlib: DLLUtilName, importc.}
   proc RandScreen*(){.cdecl, dynlib: DLLUtilName, importc.}