Skip to content

Commit

Permalink
Merge pull request #241 from lzaoral/xmlrpc-fix-proxy-connections
Browse files Browse the repository at this point in the history
xmlrpc: make proxy connections compatible with Python 3
  • Loading branch information
rohanpm authored Dec 20, 2023
2 parents 7a25ecf + 7a22c72 commit 82f8936
Showing 1 changed file with 26 additions and 147 deletions.
173 changes: 26 additions & 147 deletions kobo/xmlrpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import six.moves.http_cookiejar as cookielib
import fcntl
import hashlib
import six.moves.http_client as httplib
import http.client as httplib
import os
import socket
import ssl
Expand Down Expand Up @@ -38,153 +38,31 @@
CONNECTION_LOCK = threading.Lock()


class TimeoutHTTPConnection(httplib.HTTPConnection):
def connect(self):
httplib.HTTPConnection.connect(self)
timeout = getattr(self, "_timeout", 0)
if timeout:
self.sock.settimeout(timeout)

class TimeoutHTTPProxyConnection(TimeoutHTTPConnection):
default_port = httplib.HTTPConnection.default_port

class HTTPProxyConnection(httplib.HTTPConnection):
def __init__(self, host, proxy, port=None, proxy_user=None, proxy_password=None, **kwargs):
TimeoutHTTPConnection.__init__(self, proxy, **kwargs)
self.proxy, self.proxy_port = self.host, self.port
self.set_host_and_port(host, port)
self.real_host, self.real_port = self.host, self.port
self.proxy_user = proxy_user
self.proxy_password = proxy_password

def connect(self):
# Connect to the proxy
self.set_host_and_port(self.proxy, self.proxy_port)
httplib.HTTPConnection.connect(self)
self.set_host_and_port(self.real_host, self.real_port)
timeout = getattr(self, "_timeout", 0)
if timeout:
self.sock.settimeout(timeout)

def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0):
host = self.real_host
if self.default_port != self.real_port:
host = host + ':' + str(self.real_port)
url = "http://%s%s" % (host, url)
httplib.HTTPConnection.putrequest(self, method, url)
self._add_auth_proxy_header()

def _add_auth_proxy_header(self):
if not self.proxy_user:
return
userpass = "%s:%s" % (self.proxy_user, self.proxy_password)
encode_func = base64.encodebytes if hasattr(base64, "encodebytes") else base64.encodestring
enc_userpass = encode_func(userpass).strip()
self.putheader("Proxy-Authorization", "Basic %s" % enc_userpass)

def set_host_and_port(self, host, port):
"""Due to httplib.py changes using set host & port method depends on package version"""
if hasattr(self, "_set_hostport"):
self._set_hostport(host, port)
else:
(self.host, self.port) = self._get_hostport(host, port)

class TimeoutHTTP(httplib.HTTPConnection):
_connection_class = TimeoutHTTPConnection

def set_timeout(self, timeout):
self._conn._timeout = timeout


class TimeoutProxyHTTP(TimeoutHTTP):
_connection_class = TimeoutHTTPProxyConnection

def __init__(self, host='', proxy='', port=None, strict=None,
proxy_user=None, proxy_password=None):
if port == 0:
port = None
self._setup(self._connection_class(host, proxy, port=port,
strict=strict,
proxy_user=proxy_user,
proxy_password=proxy_password))

def _setup(self, conn):
httplib.HTTP._setup(self, conn)
# XXX: Hack for python >= 2.7 where a _single_request method is used
# and the method needs a connection object with .getresponse() method
self.getresponse = conn.getresponse
super().__init__(proxy, **kwargs)
proxy_header = {}

if proxy_user:
userpass = f"{proxy_user}:{proxy_password}".encode()
enc_userpass = base64.encodebytes(userpass).strip()
proxy_header = {"Proxy-Authorization": "Basic %s" % enc_userpass}

class TimeoutHTTPSConnection(httplib.HTTPSConnection):
def connect(self):
httplib.HTTPSConnection.connect(self)
timeout = getattr(self, "_timeout", 0)
if timeout:
self.sock.settimeout(timeout)
self.set_tunnel(host, port, headers=proxy_header)


class TimeoutHTTPSProxyConnection(TimeoutHTTPProxyConnection):
default_port = httplib.HTTPSConnection.default_port

class HTTPSProxyConnection(httplib.HTTPSConnection):
def __init__(self, host, proxy, port=None, proxy_user=None,
proxy_password=None, cert_file=None, key_file=None, **kwargs):
TimeoutHTTPProxyConnection.__init__(self, host, proxy, port,
proxy_user, proxy_password, **kwargs)
self.cert_file = cert_file
self.key_file = key_file
self.connect()

def connect(self):
TimeoutHTTPProxyConnection.connect(self)
host = "%s:%s" % (self.real_host, self.real_port)
TimeoutHTTPConnection.putrequest(self, "CONNECT", host)
self._add_auth_proxy_header()
TimeoutHTTPConnection.endheaders(self)

class MyHTTPSResponse(httplib.HTTPResponse):
def begin(self):
httplib.HTTPResponse.begin(self)
self.will_close = 0

response_class = self.response_class
self.response_class = MyHTTPSResponse
response = httplib.HTTPConnection.getresponse(self)
self.response_class = response_class
response.close()
if response.status != 200:
self.close()
raise socket.error(1001, response.status, response.msg)

self.sock = ssl.wrap_socket(self.sock, keyfile=self.key_file, certfile=self.cert_file)

def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0):
return TimeoutHTTPConnection.putrequest(self, method, url)

proxy_password=None, **kwargs):
super().__init__(proxy, **kwargs)
proxy_header = {}

class TimeoutHTTPS(httplib.HTTPSConnection):
_connection_class = TimeoutHTTPSConnection
if proxy_user:
userpass = f"{proxy_user}:{proxy_password}".encode()
enc_userpass = base64.encodebytes(userpass).strip()
proxy_header = {"Proxy-Authorization": "Basic %s" % enc_userpass}

def set_timeout(self, timeout):
self._conn._timeout = timeout


class TimeoutProxyHTTPS(TimeoutHTTPS):
_connection_class = TimeoutHTTPSProxyConnection

def __init__(self, host='', proxy='', port=None, strict=None,
proxy_user=None, proxy_password=None, cert_file=None,
key_file=None):
if port == 0:
port = None
self._setup(self._connection_class(host, proxy, port=port,
strict=strict, proxy_user=proxy_user,
proxy_password=proxy_password, cert_file=cert_file,
key_file=key_file))

def _setup(self, conn):
httplib.HTTP._setup(self, conn)
# XXX: Hack for python >= 2.7 where a _single_request method is used
# and the method needs a connection object with .getresponse() method
self.getresponse = conn.getresponse
self.set_tunnel(host, port, headers=proxy_header)


class CookieResponse(object):
Expand Down Expand Up @@ -214,7 +92,7 @@ class CookieTransport(xmlrpclib.Transport):

def __init__(self, *args, **kwargs):
cookiejar = kwargs.pop("cookiejar", None)
self.timeout = kwargs.pop("timeout", int(os.environ.get("KOBO_XMLRPC_TIMEOUT", "0")))
self.timeout = kwargs.pop("timeout", None)
self.proxy_config = self._get_proxy(**kwargs)
self.no_proxy = os.environ.get("no_proxy", "").lower().split(',')
self.context = kwargs.pop('context', None)
Expand All @@ -227,6 +105,9 @@ def __init__(self, *args, **kwargs):

self.cookiejar = cookiejar or cookielib.CookieJar()

if self.timeout is None and "KOBO_XMLRPC_TIMEOUT" in os.environ:
self.timeout = int(os.environ["KOBO_XMLRPC_TIMEOUT"])

if hasattr(self.cookiejar, "load"):
if not os.path.exists(self.cookiejar.filename):
if hasattr(self.cookiejar, "save"):
Expand Down Expand Up @@ -289,13 +170,12 @@ def make_connection(self, host):
# Remove port from the host
host_ = host.split(':')[0]
else:
host_ = "%s:%s" % (host, TimeoutHTTPProxyConnection.default_port)
host_ = "%s:%s" % (host, HTTPProxyConnection.default_port)

if self.proxy_config["proxy"] and host not in self.no_proxy and host_ not in self.no_proxy:
CONNECTION_LOCK.acquire()
host, extra_headers, x509 = self.get_host_info(host)
conn = TimeoutProxyHTTPS(host, **self.proxy_config)
conn.set_timeout(self.timeout)
conn = HTTPProxyConnection(host, timeout=self.timeout, **self.proxy_config)
CONNECTION_LOCK.release()
return conn

Expand Down Expand Up @@ -556,13 +436,12 @@ def make_connection(self, host):
# Remove port from the host
host_ = host.split(':')[0]
else:
host_ = "%s:%s" % (host, TimeoutHTTPSProxyConnection.default_port)
host_ = "%s:%s" % (host, HTTPSProxyConnection.default_port)

if self.proxy_config["proxy"] and host not in self.no_proxy and host_ not in self.no_proxy:
CONNECTION_LOCK.acquire()
host, extra_headers, x509 = self.get_host_info(host)
conn = TimeoutProxyHTTPS(host, **self.proxy_config)
conn.set_timeout(self.timeout)
conn = HTTPSProxyConnection(host, timeout=self.timeout, **self.proxy_config)
CONNECTION_LOCK.release()
return conn

Expand Down

0 comments on commit 82f8936

Please sign in to comment.