title: HTTPS proxy tunneling with the ssl module
body: <p>I'd like to manually (using the <a href="http://docs.python.org/library/socket.html" rel="noreferrer">socket</a> and <a href="http://docs.python.org/library/ssl.html" rel="noreferrer">ssl</a> modules) make an <code>HTTPS</code> request through a proxy which itself uses <code>HTTPS</code>.</p>
  
  <p>I can perform the initial <code>CONNECT</code> exchange just fine:</p>
  
  <pre><code>import ssl, socket
  
  PROXY_ADDR = ("proxy-addr", 443)
  CONNECT = "CONNECT example.com:443 HTTP/1.1\r\n\r\n"
  
  sock = socket.create_connection(PROXY_ADDR)
  sock = ssl.wrap_socket(sock)
  sock.sendall(CONNECT)
  s = ""
  while s[-4:] != "\r\n\r\n":
      s += sock.recv(1)
  print repr(s)
  </code></pre>
  
  <p>The above code prints <code>HTTP/1.1 200 Connection established</code> plus some headers, which is what I expect.  So now I should be ready to make the request, e.g.</p>
  
  <pre><code>sock.sendall("GET / HTTP/1.1\r\n\r\n")
  </code></pre>
  
  <p>but the above code returns</p>
  
  <pre><code>&lt;!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"&gt;
  &lt;html&gt;&lt;head&gt;
  &lt;title&gt;400 Bad Request&lt;/title&gt;
  &lt;/head&gt;&lt;body&gt;
  &lt;h1&gt;Bad Request&lt;/h1&gt;
  &lt;p&gt;Your browser sent a request that this server could not understand.&lt;br /&gt;
  Reason: You're speaking plain HTTP to an SSL-enabled server port.&lt;br /&gt;
  Instead use the HTTPS scheme to access this URL, please.&lt;br /&gt;
  &lt;/body&gt;&lt;/html&gt;
  </code></pre>
  
  <p>This makes sense too, since I still need to do an SSL handshake with the <code>example.com</code> server to which I'm tunneling.  However, if instead of immediately sending the <code>GET</code> request I say</p>
  
  <pre><code>sock = ssl.wrap_socket(sock)
  </code></pre>
  
  <p>to do the handshake with the remote server, then I get an exception:</p>
  
  <pre><code>Traceback (most recent call last):
    File "so_test.py", line 18, in &lt;module&gt;
      ssl.wrap_socket(sock)
    File "/usr/lib/python2.6/ssl.py", line 350, in wrap_socket
      suppress_ragged_eofs=suppress_ragged_eofs)
    File "/usr/lib/python2.6/ssl.py", line 118, in __init__
      self.do_handshake()
    File "/usr/lib/python2.6/ssl.py", line 293, in do_handshake
      self._sslobj.do_handshake()
  ssl.SSLError: [Errno 1] _ssl.c:480: error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol
  </code></pre>
  
  <p>So how can I do the SSL handshake with the remote <code>example.com</code> server?</p>
  
  <p>EDIT: I'm pretty sure that no additional data is available before my second call to <code>wrap_socket</code> because calling <code>sock.recv(1)</code> blocks indefinitely.</p>