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.nim74
1 files changed, 38 insertions, 36 deletions
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index de1d332a3..54a8498fa 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -469,7 +469,7 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
   ## **Deprecated since version 0.15.0**: use ``HttpClient.request`` instead.
   var r = if proxy == nil: parseUri(url) else: proxy.url
   var hostUrl = if proxy == nil: r else: parseUri(url)
-  var headers = httpMethod.toUpper()
+  var headers = httpMethod.toUpperAscii()
   # TODO: Use generateHeaders further down once it supports proxies.
 
   var s = newSocket()
@@ -713,10 +713,10 @@ proc downloadFile*(url: string, outputFilename: string,
 proc generateHeaders(requestUrl: Uri, httpMethod: string,
                      headers: HttpHeaders, body: string, proxy: Proxy): string =
   # GET
-  result = httpMethod.toUpper()
+  result = httpMethod.toUpperAscii()
   result.add ' '
 
-  if proxy.isNil:
+  if proxy.isNil or (not proxy.isNil and requestUrl.scheme == "https"):
     # /path?query
     if requestUrl.path[0] != '/': result.add '/'
     result.add(requestUrl.path)
@@ -1048,7 +1048,11 @@ proc newConnection(client: HttpClient | AsyncHttpClient,
       client.currentURL.scheme != url.scheme or
       client.currentURL.port != url.port or
       (not client.connected):
-    let isSsl = url.scheme.toLowerAscii() == "https"
+    # Connect to proxy if specified
+    let connectionUrl =
+      if client.proxy.isNil: url else: client.proxy.url
+
+    let isSsl = connectionUrl.scheme.toLowerAscii() == "https"
 
     if isSsl and not defined(ssl):
       raise newException(HttpRequestError,
@@ -1056,31 +1060,55 @@ proc newConnection(client: HttpClient | AsyncHttpClient,
 
     if client.connected:
       client.close()
+      client.connected = false
 
     # TODO: I should be able to write 'net.Port' here...
     let port =
-      if url.port == "":
+      if connectionUrl.port == "":
         if isSsl:
           nativesockets.Port(443)
         else:
           nativesockets.Port(80)
-      else: nativesockets.Port(url.port.parseInt)
+      else: nativesockets.Port(connectionUrl.port.parseInt)
 
     when client is HttpClient:
-      client.socket = await net.dial(url.hostname, port)
+      client.socket = await net.dial(connectionUrl.hostname, port)
     elif client is AsyncHttpClient:
-      client.socket = await asyncnet.dial(url.hostname, port)
+      client.socket = await asyncnet.dial(connectionUrl.hostname, port)
     else: {.fatal: "Unsupported client type".}
 
     when defined(ssl):
       if isSsl:
         try:
           client.sslContext.wrapConnectedSocket(
-            client.socket, handshakeAsClient, url.hostname)
+            client.socket, handshakeAsClient, connectionUrl.hostname)
         except:
           client.socket.close()
           raise getCurrentException()
 
+    # If need to CONNECT through proxy
+    if url.scheme == "https" and not client.proxy.isNil:
+      when defined(ssl):
+        # Pass only host:port for CONNECT
+        var connectUrl = initUri()
+        connectUrl.hostname = url.hostname
+        connectUrl.port = if url.port != "": url.port else: "443"
+
+        let proxyHeaderString = generateHeaders(connectUrl, $HttpConnect, newHttpHeaders(), "", client.proxy)
+        await client.socket.send(proxyHeaderString)
+        let proxyResp = await parseResponse(client, false)
+
+        if not proxyResp.status.startsWith("200"):
+          raise newException(HttpRequestError,
+                            "The proxy server rejected a CONNECT request, " &
+                            "so a secure connection could not be established.")
+        client.sslContext.wrapConnectedSocket(
+          client.socket, handshakeAsClient, url.hostname)
+      else:
+        raise newException(HttpRequestError,
+        "SSL support is not available. Cannot connect over SSL.")
+
+    # May be connected through proxy but remember actual URL being accessed
     client.currentURL = url
     client.connected = true
 
@@ -1100,32 +1128,9 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: string,
                 headers: HttpHeaders = nil): Future[Response | AsyncResponse]
                 {.multisync.} =
   # Helper that actually makes the request. Does not handle redirects.
-  let connectionUrl =
-    if client.proxy.isNil: parseUri(url) else: client.proxy.url
   let requestUrl = parseUri(url)
 
-  let savedProxy = client.proxy # client's proxy may be overwritten.
-
-  if requestUrl.scheme == "https" and not client.proxy.isNil:
-    when defined(ssl):
-      client.proxy.url = connectionUrl
-      var connectUrl = requestUrl
-      connectUrl.scheme = "http"
-      connectUrl.port = "443"
-      let proxyResp = await requestAux(client, $connectUrl, $HttpConnect)
-
-      if not proxyResp.status.startsWith("200"):
-        raise newException(HttpRequestError,
-                           "The proxy server rejected a CONNECT request, " &
-                           "so a secure connection could not be established.")
-      client.sslContext.wrapConnectedSocket(
-        client.socket, handshakeAsClient, requestUrl.hostname)
-      client.proxy = nil
-    else:
-      raise newException(HttpRequestError,
-          "SSL support not available. Cannot connect to https site over proxy.")
-  else:
-    await newConnection(client, connectionUrl)
+  await newConnection(client, requestUrl)
 
   let effectiveHeaders = client.headers.override(headers)
 
@@ -1143,9 +1148,6 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: string,
                 client.getBody
   result = await parseResponse(client, getBody)
 
-  # Restore the clients proxy in case it was overwritten.
-  client.proxy = savedProxy
-
 proc request*(client: HttpClient | AsyncHttpClient, url: string,
               httpMethod: string, body = "",
               headers: HttpHeaders = nil): Future[Response | AsyncResponse]