Sync to head of httplib2. Reviewed in http://codereview.appspot.com/5216044/
diff --git a/httplib2/socks.py b/httplib2/socks.py
index a458e1c..84cb93b 100644
--- a/httplib2/socks.py
+++ b/httplib2/socks.py
@@ -40,6 +40,7 @@
"""
+import base64
import socket
import struct
import sys
@@ -50,6 +51,7 @@
PROXY_TYPE_SOCKS4 = 1
PROXY_TYPE_SOCKS5 = 2
PROXY_TYPE_HTTP = 3
+PROXY_TYPE_HTTP_NO_TUNNEL = 4
_defaultproxy = None
_orgsocket = socket.socket
@@ -126,6 +128,7 @@
self.__proxy = (None, None, None, None, None, None)
self.__proxysockname = None
self.__proxypeername = None
+ self.__httptunnel = True
def __recvall(self, count):
"""__recvall(count) -> data
@@ -139,6 +142,41 @@
data = data + d
return data
+ def sendall(self, content, *args):
+ """ override socket.socket.sendall method to rewrite the header
+ for non-tunneling proxies if needed
+ """
+ if not self.__httptunnel:
+ content = self.__rewriteproxy(content)
+ return super(socksocket, self).sendall(content, *args)
+
+ def __rewriteproxy(self, header):
+ """ rewrite HTTP request headers to support non-tunneling proxies
+ (i.e. those which do not support the CONNECT method).
+ This only works for HTTP (not HTTPS) since HTTPS requires tunneling.
+ """
+ host, endpt = None, None
+ hdrs = header.split("\r\n")
+ for hdr in hdrs:
+ if hdr.lower().startswith("host:"):
+ host = hdr
+ elif hdr.lower().startswith("get") or hdr.lower().startswith("post"):
+ endpt = hdr
+ if host and endpt:
+ hdrs.remove(host)
+ hdrs.remove(endpt)
+ host = host.split(" ")[1]
+ endpt = endpt.split(" ")
+ if (self.__proxy[4] != None and self.__proxy[5] != None):
+ hdrs.insert(0, self.__getauthheader())
+ hdrs.insert(0, "Host: %s" % host)
+ hdrs.insert(0, "%s http://%s%s %s" % (endpt[0], host, endpt[1], endpt[2]))
+ return "\r\n".join(hdrs)
+
+ def __getauthheader(self):
+ auth = self.__proxy[4] + ":" + self.__proxy[5]
+ return "Proxy-Authorization: Basic " + base64.b64encode(auth)
+
def setproxy(self, proxytype=None, addr=None, port=None, rdns=True, username=None, password=None):
"""setproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
Sets the proxy to be used.
@@ -326,7 +364,12 @@
addr = socket.gethostbyname(destaddr)
else:
addr = destaddr
- self.sendall(("CONNECT " + addr + ":" + str(destport) + " HTTP/1.1\r\n" + "Host: " + destaddr + "\r\n\r\n").encode())
+ headers = ["CONNECT ", addr, ":", str(destport), " HTTP/1.1\r\n"]
+ headers += ["Host: ", destaddr, "\r\n"]
+ if (self.__proxy[4] != None and self.__proxy[5] != None):
+ headers += [self.__getauthheader(), "\r\n"]
+ headers.append("\r\n")
+ self.sendall("".join(headers).encode())
# We read the response until we get the string "\r\n\r\n"
resp = self.recv(1)
while resp.find("\r\n\r\n".encode()) == -1:
@@ -379,6 +422,16 @@
portnum = 8080
_orgsocket.connect(self,(self.__proxy[1], portnum))
self.__negotiatehttp(destpair[0], destpair[1])
+ elif self.__proxy[0] == PROXY_TYPE_HTTP_NO_TUNNEL:
+ if self.__proxy[2] != None:
+ portnum = self.__proxy[2]
+ else:
+ portnum = 8080
+ _orgsocket.connect(self,(self.__proxy[1],portnum))
+ if destpair[1] == 443:
+ self.__negotiatehttp(destpair[0],destpair[1])
+ else:
+ self.__httptunnel = False
elif self.__proxy[0] == None:
_orgsocket.connect(self, (destpair[0], destpair[1]))
else: