summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorRyan Marcus <ryan@rmarcus.info>2016-07-27 17:02:53 -0400
committerRyan Marcus <ryan@rmarcus.info>2016-07-27 17:02:53 -0400
commit56387c61ddaf37962a9174c47716716b497b385d (patch)
tree627133adcecc2ac1fff85c86c5f17b8729228a79 /lib
parent27a40cd5a43175d273bf15af0ce0c269ea76a49b (diff)
downloadNim-56387c61ddaf37962a9174c47716716b497b385d.tar.gz
added code to send CONNECT request for https requests through proxies, fixes #4520
Diffstat (limited to 'lib')
-rw-r--r--lib/pure/httpclient.nim68
1 files changed, 52 insertions, 16 deletions
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index bc964861d..5fbb23430 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -387,6 +387,8 @@ proc format(p: MultipartData): tuple[header, body: string] =
     result.body.add("--" & bound & "\c\L" & s)
   result.body.add("--" & bound & "--\c\L")
 
+
+
 proc request*(url: string, httpMethod: string, extraHeaders = "",
               body = "", sslContext = defaultSSLContext, timeout = -1,
               userAgent = defUserAgent, proxy: Proxy = nil): Response =
@@ -399,6 +401,55 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
   var hostUrl = if proxy == nil: r else: parseUri(url)
   var headers = substr(httpMethod, len("http"))
   # TODO: Use generateHeaders further down once it supports proxies.
+  
+  var s = newSocket()
+  if s == nil: raiseOSError(osLastError())
+  var port = net.Port(80)
+  if r.scheme == "https":
+    when defined(ssl):
+      sslContext.wrapSocket(s)
+      port = net.Port(443)
+    else:
+      raise newException(HttpRequestError,
+                "SSL support is not available. Cannot connect over SSL.")
+  if r.port != "":
+    port = net.Port(r.port.parseInt)
+
+
+  # get the socket ready. If we are connecting through a proxy to SSL,
+  # send the appropiate CONNECT header. If not, simply connect to the proper
+  # host (which may still be the proxy, for normal HTTP)
+  if proxy != nil and hostUrl.scheme == "https":
+    var connectHeaders = "CONNECT "
+    let targetPort = if hostUrl.port == "": 443 else: hostUrl.port.parseInt
+    connectHeaders.add(hostUrl.hostname)
+    connectHeaders.add(":" & $targetPort)
+    connectHeaders.add(" HTTP/1.1\c\L")
+    connectHeaders.add("Host: " & hostUrl.hostname & ":" & $targetPort & "\c\L")
+    if proxy.auth != "":
+      let auth = base64.encode(proxy.auth, newline = "")
+      connectHeaders.add("Proxy-Authorization: basic " & auth & "\c\L")
+    connectHeaders.add("\c\L")
+    if timeout == -1:
+      s.connect(r.hostname, port)
+    else:
+      s.connect(r.hostname, port, timeout)
+
+    s.send(connectHeaders)
+    let connectResult = parseResponse(s, false, timeout)
+    if not connectResult.status.startsWith("200"):
+      raise newException(HttpRequestError,
+                         "The proxy server rejected a CONNECT request, " &
+                         "so a secure connection could not be established.")
+    sslContext.wrapConnectedSocket(s, handshakeAsClient)
+  else:
+    if timeout == -1:
+      s.connect(r.hostname, port)
+    else:
+      s.connect(r.hostname, port, timeout)
+
+
+  # now that the socket is ready, prepare the headers
   if proxy == nil:
     headers.add ' '
     if r.path[0] != '/': headers.add '/'
@@ -422,23 +473,8 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
     add(headers, "Proxy-Authorization: basic " & auth & "\c\L")
   add(headers, extraHeaders)
   add(headers, "\c\L")
-  var s = newSocket()
-  if s == nil: raiseOSError(osLastError())
-  var port = net.Port(80)
-  if r.scheme == "https":
-    when defined(ssl):
-      sslContext.wrapSocket(s)
-      port = net.Port(443)
-    else:
-      raise newException(HttpRequestError,
-                "SSL support is not available. Cannot connect over SSL.")
-  if r.port != "":
-    port = net.Port(r.port.parseInt)
 
-  if timeout == -1:
-    s.connect(r.hostname, port)
-  else:
-    s.connect(r.hostname, port, timeout)
+  # headers are ready. send them, await the result, and close the socket.
   s.send(headers)
   if body != "":
     s.send(body)