mirror of
https://gitlab.alpinelinux.org/alpine/aports.git
synced 2025-07-13 19:29:53 +03:00
553 lines
20 KiB
Diff
553 lines
20 KiB
Diff
Patch-Source: https://github.com/wikier/keepalive/pull/11
|
|
From 64393f6c5bf9c69d946b584fd664dd4df72604e6 Mon Sep 17 00:00:00 2001
|
|
From: Theodore Ni <3806110+tjni@users.noreply.github.com>
|
|
Date: Tue, 6 Jun 2023 21:51:59 -0700
|
|
Subject: [PATCH] Convert to Python 3 and drop support for Python 2.
|
|
|
|
---
|
|
README.md | 2 +-
|
|
keepalive/keepalive.py | 226 +++++++++++++++++------------------------
|
|
setup.py | 28 ++---
|
|
3 files changed, 103 insertions(+), 153 deletions(-)
|
|
|
|
diff --git a/keepalive/keepalive.py b/keepalive/keepalive.py
|
|
index c07ef81..43787c7 100644
|
|
--- a/keepalive/keepalive.py
|
|
+++ b/keepalive/keepalive.py
|
|
@@ -20,15 +20,15 @@
|
|
# Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko
|
|
# Copyright 2015 Sergio Fernández
|
|
|
|
-"""An HTTP handler for urllib2 that supports HTTP 1.1 and keepalive.
|
|
+"""An HTTP handler for urllib that supports HTTP 1.1 and keepalive.
|
|
|
|
->>> import urllib2
|
|
+>>> import urllib.request
|
|
>>> from keepalive import HTTPHandler
|
|
>>> keepalive_handler = HTTPHandler()
|
|
->>> opener = urllib2.build_opener(keepalive_handler)
|
|
->>> urllib2.install_opener(opener)
|
|
+>>> opener = urllib.request.build_opener(keepalive_handler)
|
|
+>>> urllib.request.install_opener(opener)
|
|
>>>
|
|
->>> fo = urllib2.urlopen('http://www.python.org')
|
|
+>>> fo = urllib.request.urlopen('http://www.python.org')
|
|
|
|
If a connection to a given host is requested, and all of the existing
|
|
connections are still in use, another connection will be opened. If
|
|
@@ -58,7 +58,7 @@
|
|
|
|
Upon a status of 200, the object returned has a few additional
|
|
attributes and methods, which should not be used if you want to
|
|
- remain consistent with the normal urllib2-returned objects:
|
|
+ remain consistent with the normal urllib-returned objects:
|
|
|
|
close_connection() - close the connection to the host
|
|
readlines() - you know, readlines()
|
|
@@ -73,55 +73,30 @@
|
|
|
|
Unfortunately, these are ONLY there if status == 200, so it's not
|
|
easy to distinguish between non-200 responses. The reason is that
|
|
- urllib2 tries to do clever things with error codes 301, 302, 401,
|
|
+ urllib tries to do clever things with error codes 301, 302, 401,
|
|
and 407, and it wraps the object upon return.
|
|
|
|
- For python versions earlier than 2.4, you can avoid this fancy error
|
|
- handling by setting the module-level global HANDLE_ERRORS to zero.
|
|
- You see, prior to 2.4, it's the HTTP Handler's job to determine what
|
|
- to handle specially, and what to just pass up. HANDLE_ERRORS == 0
|
|
- means "pass everything up". In python 2.4, however, this job no
|
|
- longer belongs to the HTTP Handler and is now done by a NEW handler,
|
|
- HTTPErrorProcessor. Here's the bottom line:
|
|
-
|
|
- python version < 2.4
|
|
- HANDLE_ERRORS == 1 (default) pass up 200, treat the rest as
|
|
- errors
|
|
- HANDLE_ERRORS == 0 pass everything up, error processing is
|
|
- left to the calling code
|
|
- python version >= 2.4
|
|
- HANDLE_ERRORS == 1 pass up 200, treat the rest as errors
|
|
- HANDLE_ERRORS == 0 (default) pass everything up, let the
|
|
- other handlers (specifically,
|
|
- HTTPErrorProcessor) decide what to do
|
|
-
|
|
- In practice, setting the variable either way makes little difference
|
|
- in python 2.4, so for the most consistent behavior across versions,
|
|
- you probably just want to use the defaults, which will give you
|
|
- exceptions on errors.
|
|
-
|
|
"""
|
|
|
|
# $Id: keepalive.py,v 1.17 2006/12/08 00:14:16 mstenner Exp $
|
|
|
|
-import urllib2
|
|
-import httplib
|
|
+import urllib.error
|
|
+import urllib.request
|
|
+import http.client
|
|
import socket
|
|
-import thread
|
|
+import _thread
|
|
|
|
DEBUG = None
|
|
|
|
import sys
|
|
-if sys.version_info < (2, 4): HANDLE_ERRORS = 1
|
|
-else: HANDLE_ERRORS = 0
|
|
-
|
|
+
|
|
class ConnectionManager:
|
|
"""
|
|
The connection manager must be able to:
|
|
* keep track of all existing
|
|
"""
|
|
def __init__(self):
|
|
- self._lock = thread.allocate_lock()
|
|
+ self._lock = _thread.allocate_lock()
|
|
self._hostmap = {} # map hosts to a list of connections
|
|
self._connmap = {} # map connections to host
|
|
self._readymap = {} # map connection to ready state
|
|
@@ -129,7 +104,7 @@ def __init__(self):
|
|
def add(self, host, connection, ready):
|
|
self._lock.acquire()
|
|
try:
|
|
- if not self._hostmap.has_key(host): self._hostmap[host] = []
|
|
+ if host not in self._hostmap: self._hostmap[host] = []
|
|
self._hostmap[host].append(connection)
|
|
self._connmap[connection] = host
|
|
self._readymap[connection] = ready
|
|
@@ -159,7 +134,7 @@ def get_ready_conn(self, host):
|
|
conn = None
|
|
self._lock.acquire()
|
|
try:
|
|
- if self._hostmap.has_key(host):
|
|
+ if host in self._hostmap:
|
|
for c in self._hostmap[host]:
|
|
if self._readymap[c]:
|
|
self._readymap[c] = 0
|
|
@@ -183,7 +158,7 @@ def __init__(self):
|
|
def open_connections(self):
|
|
"""return a list of connected hosts and the number of connections
|
|
to each. [('foo.com:80', 2), ('bar.org', 1)]"""
|
|
- return [(host, len(li)) for (host, li) in self._cm.get_all().items()]
|
|
+ return [(host, len(li)) for (host, li) in list(self._cm.get_all().items())]
|
|
|
|
def close_connection(self, host):
|
|
"""close connection(s) to <host>
|
|
@@ -195,7 +170,7 @@ def close_connection(self, host):
|
|
|
|
def close_all(self):
|
|
"""close all open connections"""
|
|
- for host, conns in self._cm.get_all().items():
|
|
+ for host, conns in list(self._cm.get_all().items()):
|
|
for h in conns:
|
|
self._cm.remove(h)
|
|
h.close()
|
|
@@ -213,7 +188,7 @@ def _remove_connection(self, host, connection, close=0):
|
|
def do_open(self, req):
|
|
host = req.host
|
|
if not host:
|
|
- raise urllib2.URLError('no host given')
|
|
+ raise urllib.error.URLError('no host given')
|
|
|
|
try:
|
|
h = self._cm.get_ready_conn(host)
|
|
@@ -237,8 +212,8 @@ def do_open(self, req):
|
|
self._cm.add(host, h, 0)
|
|
self._start_transaction(h, req)
|
|
r = h.getresponse()
|
|
- except (socket.error, httplib.HTTPException), err:
|
|
- raise urllib2.URLError(err)
|
|
+ except (socket.error, http.client.HTTPException) as err:
|
|
+ raise urllib.error.URLError(err)
|
|
|
|
if DEBUG: DEBUG.info("STATUS: %s, %s", r.status, r.reason)
|
|
|
|
@@ -255,11 +230,7 @@ def do_open(self, req):
|
|
r.headers = r.msg
|
|
r.msg = r.reason
|
|
|
|
- if r.status == 200 or not HANDLE_ERRORS:
|
|
- return r
|
|
- else:
|
|
- return self.parent.error('http', req, r,
|
|
- r.status, r.msg, r.headers)
|
|
+ return r
|
|
|
|
def _reuse_connection(self, h, req, host):
|
|
"""start the transaction with a re-used connection
|
|
@@ -273,7 +244,7 @@ def _reuse_connection(self, h, req, host):
|
|
r = h.getresponse()
|
|
# note: just because we got something back doesn't mean it
|
|
# worked. We'll check the version below, too.
|
|
- except (socket.error, httplib.HTTPException):
|
|
+ except (socket.error, http.client.HTTPException):
|
|
r = None
|
|
except:
|
|
# adding this block just in case we've missed
|
|
@@ -292,7 +263,7 @@ def _reuse_connection(self, h, req, host):
|
|
raise
|
|
|
|
if r is None or r.version == 9:
|
|
- # httplib falls back to assuming HTTP 0.9 if it gets a
|
|
+ # http.client falls back to assuming HTTP 0.9 if it gets a
|
|
# bad header back. This is most likely to happen if
|
|
# the socket has been closed by the server since we
|
|
# last used the connection.
|
|
@@ -312,22 +283,22 @@ def _start_transaction(self, h, req):
|
|
h.putrequest('POST', req.selector)
|
|
else:
|
|
h.putrequest('POST', req.get_selector())
|
|
- if not req.headers.has_key('Content-type'):
|
|
+ if 'Content-type' not in req.headers:
|
|
h.putheader('Content-type',
|
|
'application/x-www-form-urlencoded')
|
|
- if not req.headers.has_key('Content-length'):
|
|
+ if 'Content-length' not in req.headers:
|
|
h.putheader('Content-length', '%d' % len(data))
|
|
else:
|
|
if hasattr(req, 'selector'):
|
|
h.putrequest('GET', req.selector)
|
|
else:
|
|
h.putrequest('GET', req.get_selector())
|
|
- except (socket.error, httplib.HTTPException), err:
|
|
- raise urllib2.URLError(err)
|
|
+ except (socket.error, http.client.HTTPException) as err:
|
|
+ raise urllib.error.URLError(err)
|
|
|
|
for args in self.parent.addheaders:
|
|
h.putheader(*args)
|
|
- for k, v in req.headers.items():
|
|
+ for k, v in list(req.headers.items()):
|
|
h.putheader(k, v)
|
|
h.endheaders()
|
|
if req.data:
|
|
@@ -336,7 +307,7 @@ def _start_transaction(self, h, req):
|
|
def _get_connection(self, host):
|
|
return NotImplementedError
|
|
|
|
-class HTTPHandler(KeepAliveHandler, urllib2.HTTPHandler):
|
|
+class HTTPHandler(KeepAliveHandler, urllib.request.HTTPHandler):
|
|
def __init__(self):
|
|
KeepAliveHandler.__init__(self)
|
|
|
|
@@ -346,7 +317,7 @@ def http_open(self, req):
|
|
def _get_connection(self, host):
|
|
return HTTPConnection(host)
|
|
|
|
-class HTTPSHandler(KeepAliveHandler, urllib2.HTTPSHandler):
|
|
+class HTTPSHandler(KeepAliveHandler, urllib.request.HTTPSHandler):
|
|
def __init__(self, ssl_factory=None):
|
|
KeepAliveHandler.__init__(self)
|
|
if not ssl_factory:
|
|
@@ -364,7 +335,7 @@ def _get_connection(self, host):
|
|
try: return self._ssl_factory.get_https_connection(host)
|
|
except AttributeError: return HTTPSConnection(host)
|
|
|
|
-class HTTPResponse(httplib.HTTPResponse):
|
|
+class HTTPResponse(http.client.HTTPResponse):
|
|
# we need to subclass HTTPResponse in order to
|
|
# 1) add readline() and readlines() methods
|
|
# 2) add close_connection() methods
|
|
@@ -385,10 +356,7 @@ class HTTPResponse(httplib.HTTPResponse):
|
|
|
|
|
|
def __init__(self, sock, debuglevel=0, strict=0, method=None):
|
|
- if method: # the httplib in python 2.3 uses the method arg
|
|
- httplib.HTTPResponse.__init__(self, sock, debuglevel, method)
|
|
- else: # 2.2 doesn't
|
|
- httplib.HTTPResponse.__init__(self, sock, debuglevel)
|
|
+ http.client.HTTPResponse.__init__(self, sock, debuglevel, method)
|
|
self.fileno = sock.fileno
|
|
self.code = None
|
|
self._rbuf = b""
|
|
@@ -398,7 +366,7 @@ def __init__(self, sock, debuglevel=0, strict=0, method=None):
|
|
self._url = None # (same)
|
|
self._connection = None # (same)
|
|
|
|
- _raw_read = httplib.HTTPResponse.read
|
|
+ _raw_read = http.client.HTTPResponse.read
|
|
|
|
def close(self):
|
|
if self.fp:
|
|
@@ -462,11 +430,11 @@ def readlines(self, sizehint = 0):
|
|
return list
|
|
|
|
|
|
-class HTTPConnection(httplib.HTTPConnection):
|
|
+class HTTPConnection(http.client.HTTPConnection):
|
|
# use the modified response class
|
|
response_class = HTTPResponse
|
|
|
|
-class HTTPSConnection(httplib.HTTPSConnection):
|
|
+class HTTPSConnection(http.client.HTTPSConnection):
|
|
response_class = HTTPResponse
|
|
|
|
#########################################################################
|
|
@@ -474,81 +442,77 @@ class HTTPSConnection(httplib.HTTPSConnection):
|
|
#########################################################################
|
|
|
|
def error_handler(url):
|
|
- global HANDLE_ERRORS
|
|
- orig = HANDLE_ERRORS
|
|
keepalive_handler = HTTPHandler()
|
|
- opener = urllib2.build_opener(keepalive_handler)
|
|
- urllib2.install_opener(opener)
|
|
- pos = {0: 'off', 1: 'on'}
|
|
- for i in (0, 1):
|
|
- print " fancy error handling %s (HANDLE_ERRORS = %i)" % (pos[i], i)
|
|
- HANDLE_ERRORS = i
|
|
- try:
|
|
- fo = urllib2.urlopen(url)
|
|
- foo = fo.read()
|
|
- fo.close()
|
|
- try: status, reason = fo.status, fo.reason
|
|
- except AttributeError: status, reason = None, None
|
|
- except IOError, e:
|
|
- print " EXCEPTION: %s" % e
|
|
- raise
|
|
- else:
|
|
- print " status = %s, reason = %s" % (status, reason)
|
|
- HANDLE_ERRORS = orig
|
|
+ opener = urllib.request.build_opener(keepalive_handler)
|
|
+ urllib.request.install_opener(opener)
|
|
+ try:
|
|
+ fo = urllib.request.urlopen(url)
|
|
+ foo = fo.read()
|
|
+ fo.close()
|
|
+ try: status, reason = fo.status, fo.reason
|
|
+ except AttributeError: status, reason = None, None
|
|
+ except IOError as e:
|
|
+ print(" EXCEPTION: %s" % e)
|
|
+ raise
|
|
+ else:
|
|
+ print(" status = %s, reason = %s" % (status, reason))
|
|
hosts = keepalive_handler.open_connections()
|
|
- print "open connections:", hosts
|
|
+ print("open connections:", hosts)
|
|
keepalive_handler.close_all()
|
|
|
|
def continuity(url):
|
|
- import md5
|
|
+ import hashlib
|
|
format = '%25s: %s'
|
|
|
|
# first fetch the file with the normal http handler
|
|
- opener = urllib2.build_opener()
|
|
- urllib2.install_opener(opener)
|
|
- fo = urllib2.urlopen(url)
|
|
+ opener = urllib.request.build_opener()
|
|
+ urllib.request.install_opener(opener)
|
|
+ fo = urllib.request.urlopen(url)
|
|
foo = fo.read()
|
|
fo.close()
|
|
- m = md5.new(foo)
|
|
- print format % ('normal urllib', m.hexdigest())
|
|
+ m = hashlib.md5()
|
|
+ m.update(foo)
|
|
+ print(format % ('normal urllib', m.hexdigest()))
|
|
|
|
# now install the keepalive handler and try again
|
|
- opener = urllib2.build_opener(HTTPHandler())
|
|
- urllib2.install_opener(opener)
|
|
+ opener = urllib.request.build_opener(HTTPHandler())
|
|
+ urllib.request.install_opener(opener)
|
|
|
|
- fo = urllib2.urlopen(url)
|
|
+ fo = urllib.request.urlopen(url)
|
|
foo = fo.read()
|
|
fo.close()
|
|
- m = md5.new(foo)
|
|
- print format % ('keepalive read', m.hexdigest())
|
|
+ m = hashlib.md5()
|
|
+ m.update(foo)
|
|
+ print(format % ('keepalive read', m.hexdigest()))
|
|
|
|
- fo = urllib2.urlopen(url)
|
|
- foo = ''
|
|
+ fo = urllib.request.urlopen(url)
|
|
+ foo = b''
|
|
while 1:
|
|
f = fo.readline()
|
|
if f: foo = foo + f
|
|
else: break
|
|
fo.close()
|
|
- m = md5.new(foo)
|
|
- print format % ('keepalive readline', m.hexdigest())
|
|
+ m = hashlib.md5()
|
|
+ m.update(foo)
|
|
+ print(format % ('keepalive readline', m.hexdigest()))
|
|
|
|
def comp(N, url):
|
|
- print ' making %i connections to:\n %s' % (N, url)
|
|
+ print(' making %i connections to:\n %s' % (N, url))
|
|
|
|
sys.stdout.write(' first using the normal urllib handlers')
|
|
# first use normal opener
|
|
- opener = urllib2.build_opener()
|
|
- urllib2.install_opener(opener)
|
|
+ opener = urllib.request.build_opener()
|
|
+ urllib.request.install_opener(opener)
|
|
t1 = fetch(N, url)
|
|
- print ' TIME: %.3f s' % t1
|
|
+ print(' TIME: %.3f s' % t1)
|
|
|
|
sys.stdout.write(' now using the keepalive handler ')
|
|
# now install the keepalive handler and try again
|
|
- opener = urllib2.build_opener(HTTPHandler())
|
|
- urllib2.install_opener(opener)
|
|
+ opener = urllib.request.build_opener(HTTPHandler())
|
|
+ urllib.request.install_opener(opener)
|
|
t2 = fetch(N, url)
|
|
- print ' TIME: %.3f s' % t2
|
|
- print ' improvement factor: %.2f' % (t1/t2, )
|
|
+ print(' TIME: %.3f s' % t2)
|
|
+ print(' improvement factor: %.2f' % (t1/t2, ))
|
|
|
|
def fetch(N, url, delay=0):
|
|
import time
|
|
@@ -556,7 +520,7 @@ def fetch(N, url, delay=0):
|
|
starttime = time.time()
|
|
for i in range(N):
|
|
if delay and i > 0: time.sleep(delay)
|
|
- fo = urllib2.urlopen(url)
|
|
+ fo = urllib.request.urlopen(url)
|
|
foo = fo.read()
|
|
fo.close()
|
|
lens.append(len(foo))
|
|
@@ -566,7 +530,7 @@ def fetch(N, url, delay=0):
|
|
for i in lens[1:]:
|
|
j = j + 1
|
|
if not i == lens[0]:
|
|
- print "WARNING: inconsistent length on read %i: %i" % (j, i)
|
|
+ print("WARNING: inconsistent length on read %i: %i" % (j, i))
|
|
|
|
return diff
|
|
|
|
@@ -574,16 +538,16 @@ def test_timeout(url):
|
|
global DEBUG
|
|
dbbackup = DEBUG
|
|
class FakeLogger:
|
|
- def debug(self, msg, *args): print msg % args
|
|
+ def debug(self, msg, *args): print(msg % args)
|
|
info = warning = error = debug
|
|
DEBUG = FakeLogger()
|
|
- print " fetching the file to establish a connection"
|
|
- fo = urllib2.urlopen(url)
|
|
+ print(" fetching the file to establish a connection")
|
|
+ fo = urllib.request.urlopen(url)
|
|
data1 = fo.read()
|
|
fo.close()
|
|
|
|
i = 20
|
|
- print " waiting %i seconds for the server to close the connection" % i
|
|
+ print(" waiting %i seconds for the server to close the connection" % i)
|
|
while i > 0:
|
|
sys.stdout.write('\r %2i' % i)
|
|
sys.stdout.flush()
|
|
@@ -591,33 +555,33 @@ def debug(self, msg, *args): print msg % args
|
|
i -= 1
|
|
sys.stderr.write('\r')
|
|
|
|
- print " fetching the file a second time"
|
|
- fo = urllib2.urlopen(url)
|
|
+ print(" fetching the file a second time")
|
|
+ fo = urllib.request.urlopen(url)
|
|
data2 = fo.read()
|
|
fo.close()
|
|
|
|
if data1 == data2:
|
|
- print ' data are identical'
|
|
+ print(' data are identical')
|
|
else:
|
|
- print ' ERROR: DATA DIFFER'
|
|
+ print(' ERROR: DATA DIFFER')
|
|
|
|
DEBUG = dbbackup
|
|
|
|
|
|
def test(url, N=10):
|
|
- print "checking error hander (do this on a non-200)"
|
|
+ print("checking error hander (do this on a non-200)")
|
|
try: error_handler(url)
|
|
- except IOError, e:
|
|
- print "exiting - exception will prevent further tests"
|
|
+ except IOError as e:
|
|
+ print("exiting - exception will prevent further tests")
|
|
sys.exit()
|
|
- print
|
|
- print "performing continuity test (making sure stuff isn't corrupted)"
|
|
+ print()
|
|
+ print("performing continuity test (making sure stuff isn't corrupted)")
|
|
continuity(url)
|
|
- print
|
|
- print "performing speed comparison"
|
|
+ print()
|
|
+ print("performing speed comparison")
|
|
comp(N, url)
|
|
- print
|
|
- print "performing dropped-connection check"
|
|
+ print()
|
|
+ print("performing dropped-connection check")
|
|
test_timeout(url)
|
|
|
|
if __name__ == '__main__':
|
|
@@ -627,7 +591,7 @@ def test(url, N=10):
|
|
N = int(sys.argv[1])
|
|
url = sys.argv[2]
|
|
except:
|
|
- print "%s <integer> <url>" % sys.argv[0]
|
|
+ print("%s <integer> <url>" % sys.argv[0])
|
|
else:
|
|
test(url, N)
|
|
|
|
diff --git a/setup.py b/setup.py
|
|
index 4f06360..0f963c1 100644
|
|
--- a/setup.py
|
|
+++ b/setup.py
|
|
@@ -23,23 +23,13 @@
|
|
from setuptools import setup
|
|
import sys
|
|
|
|
-try:
|
|
- import six
|
|
- py3 = six.PY3
|
|
-except:
|
|
- py3 = sys.version_info[0] >= 3
|
|
-
|
|
# metadata
|
|
-if py3:
|
|
- import re
|
|
- _version_re = re.compile(r'__version__\s*=\s*"(.*)"')
|
|
- for line in open('keepalive/__init__.py', encoding='utf-8'):
|
|
- version_match = _version_re.match(line)
|
|
- if version_match:
|
|
- _version = version_match.group(1)
|
|
-else:
|
|
- import keepalive
|
|
- _version = keepalive.__version__
|
|
+import re
|
|
+_version_re = re.compile(r'__version__\s*=\s*"(.*)"')
|
|
+for line in open('keepalive/__init__.py', encoding='utf-8'):
|
|
+ version_match = _version_re.match(line)
|
|
+ if version_match:
|
|
+ _version = version_match.group(1)
|
|
|
|
setup(
|
|
name = 'keepalive',
|
|
@@ -62,14 +52,10 @@
|
|
'Topic :: Software Development :: Libraries :: Python Modules',
|
|
'License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)',
|
|
'Operating System :: OS Independent',
|
|
- 'Programming Language :: Python :: 2',
|
|
- 'Programming Language :: Python :: 2.6',
|
|
- 'Programming Language :: Python :: 2.7',
|
|
'Programming Language :: Python :: 3',
|
|
'Programming Language :: Python :: 3.3',
|
|
'Programming Language :: Python :: 3.4',
|
|
'Programming Language :: Python :: 3.5',
|
|
],
|
|
- keywords = 'python http urllib keepalive',
|
|
- use_2to3 = True
|
|
+ keywords = 'python http urllib keepalive'
|
|
)
|