From aef4a78831c70b3b83b2392a0b4388c86261e9c1 Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Wed, 25 Mar 2015 09:43:20 -0500 Subject: [PATCH 01/12] missing httplib/http.client classes --- speedtest_cli.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/speedtest_cli.py b/speedtest_cli.py index 2beeb35..5e822f0 100755 --- a/speedtest_cli.py +++ b/speedtest_cli.py @@ -24,7 +24,7 @@ import socket import timeit import threading -__version__ = '0.3.2' +__version__ = '0.3.3a' # Some global variables we use user_agent = 'speedtest-cli/%s' % __version__ @@ -52,7 +52,15 @@ except ImportError: try: from httplib import HTTPConnection, HTTPSConnection except ImportError: - from http.client import HTTPConnection, HTTPSConnection + e_http_py2 = sys.exc_info() + try: + from http.client import HTTPConnection, HTTPSConnection + except ImportError: + e_http_py3 = sys.exc_info() + raise SystemExit('Your python installation is missing required HTTP ' + 'client classes:\n\n' + 'Python 2: %s\n' + 'Python 3: %s' % (e_http_py2[1], e_http_py3[1])) try: from Queue import Queue From ffd2c7f963cc9a7f9799ef35c41db28bbda3be09 Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Wed, 25 Mar 2015 09:57:00 -0500 Subject: [PATCH 02/12] Add some debugging for failed http requests using catch_request --- speedtest_cli.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/speedtest_cli.py b/speedtest_cli.py index 5e822f0..ca3ef6c 100755 --- a/speedtest_cli.py +++ b/speedtest_cli.py @@ -201,9 +201,10 @@ def catch_request(request): try: uh = urlopen(request) - return uh + return uh, False except (HTTPError, URLError, socket.error): - return False + e = sys.exc_info()[1] + return None, e class FileGetter(threading.Thread): @@ -349,9 +350,9 @@ def getConfig(): """ request = build_request('https://www.speedtest.net/speedtest-config.php') - uh = catch_request(request) - if uh is False: - print_('Could not retrieve speedtest.net configuration') + uh, e = catch_request(request) + if e: + print_('Could not retrieve speedtest.net configuration: %s' % e) sys.exit(1) configxml = [] while 1: @@ -393,12 +394,14 @@ def closestServers(client, all=False): 'https://www.speedtest.net/speedtest-servers-static.php', 'http://c.speedtest.net/speedtest-servers-static.php', ] + errors = [] servers = {} for url in urls: try: request = build_request(url) - uh = catch_request(request) - if uh is False: + uh, e = catch_request(request) + if e: + errors.append('%s' % e) raise SpeedtestCliServerListError serversxml = [] while 1: @@ -443,7 +446,8 @@ def closestServers(client, all=False): break if not servers: - print_('Failed to retrieve list of speedtest.net servers') + print_('Failed to retrieve list of speedtest.net servers:\n\n %s' % + '\n'.join(errors)) sys.exit(1) closest = [] @@ -739,9 +743,9 @@ def speedtest(): request = build_request('https://www.speedtest.net/api/api.php', data='&'.join(apiData).encode(), headers=headers) - f = catch_request(request) - if f is False: - print_('Could not submit results to speedtest.net') + f, e = catch_request(request) + if e: + print_('Could not submit results to speedtest.net: %s' % e) sys.exit(1) response = f.read() code = f.code From 3c04dfefd30c5c035d42b3928bcc5808860f2016 Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Wed, 15 Apr 2015 08:48:22 -0500 Subject: [PATCH 03/12] Switch back to http --- speedtest_cli.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/speedtest_cli.py b/speedtest_cli.py index ca3ef6c..60f3366 100755 --- a/speedtest_cli.py +++ b/speedtest_cli.py @@ -349,7 +349,7 @@ def getConfig(): we are interested in """ - request = build_request('https://www.speedtest.net/speedtest-config.php') + request = build_request('http://www.speedtest.net/speedtest-config.php') uh, e = catch_request(request) if e: print_('Could not retrieve speedtest.net configuration: %s' % e) @@ -391,7 +391,7 @@ def closestServers(client, all=False): """ urls = [ - 'https://www.speedtest.net/speedtest-servers-static.php', + 'http://www.speedtest.net/speedtest-servers-static.php', 'http://c.speedtest.net/speedtest-servers-static.php', ] errors = [] @@ -740,7 +740,7 @@ def speedtest(): .encode()).hexdigest()] headers = {'Referer': 'https://c.speedtest.net/flash/speedtest.swf'} - request = build_request('https://www.speedtest.net/api/api.php', + request = build_request('http://www.speedtest.net/api/api.php', data='&'.join(apiData).encode(), headers=headers) f, e = catch_request(request) From 075cfda9cf6fc6dbb8874a3639fecf14a87617d3 Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Wed, 15 Apr 2015 16:51:08 -0500 Subject: [PATCH 04/12] Default to http, add --secure to specify use of https --- speedtest_cli.py | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/speedtest_cli.py b/speedtest_cli.py index 60f3366..de9b1aa 100755 --- a/speedtest_cli.py +++ b/speedtest_cli.py @@ -30,6 +30,8 @@ __version__ = '0.3.3a' user_agent = 'speedtest-cli/%s' % __version__ source = None shutdown_event = None +scheme = 'http' + # Used for bound_interface socket_socket = socket.socket @@ -189,8 +191,13 @@ def build_request(url, data=None, headers={}): """ + if url[0] == ':': + schemed_url = '%s%s' % (scheme, url) + else: + schemed_url = url + headers['User-Agent'] = user_agent - return Request(url, data=data, headers=headers) + return Request(schemed_url, data=data, headers=headers) def catch_request(request): @@ -349,7 +356,7 @@ def getConfig(): we are interested in """ - request = build_request('http://www.speedtest.net/speedtest-config.php') + request = build_request('://www.speedtest.net/speedtest-config.php') uh, e = catch_request(request) if e: print_('Could not retrieve speedtest.net configuration: %s' % e) @@ -391,8 +398,8 @@ def closestServers(client, all=False): """ urls = [ - 'http://www.speedtest.net/speedtest-servers-static.php', - 'http://c.speedtest.net/speedtest-servers-static.php', + '://www.speedtest.net/speedtest-servers-static.php', + '://c.speedtest.net/speedtest-servers-static.php', ] errors = [] servers = {} @@ -522,7 +529,7 @@ def version(): def speedtest(): """Run the full speedtest.net test""" - global shutdown_event, source + global shutdown_event, source, scheme shutdown_event = threading.Event() signal.signal(signal.SIGINT, ctrl_c) @@ -559,6 +566,9 @@ def speedtest(): parser.add_argument('--source', help='Source IP address to bind to') parser.add_argument('--timeout', default=10, type=int, help='HTTP timeout in seconds. Default 10') + parser.add_argument('--secure', action='store_true', + help='Use HTTPS instead of HTTP when communicating' + 'with speedtest.net operated servers') parser.add_argument('--version', action='store_true', help='Show the version number and exit') @@ -580,6 +590,9 @@ def speedtest(): source = args.source socket.socket = bound_socket + if args.secure: + scheme = 'https' + if not args.simple: print_('Retrieving speedtest.net configuration...') try: @@ -739,8 +752,8 @@ def speedtest(): (ping, ulspeedk, dlspeedk, '297aae72')) .encode()).hexdigest()] - headers = {'Referer': 'https://c.speedtest.net/flash/speedtest.swf'} - request = build_request('http://www.speedtest.net/api/api.php', + headers = {'Referer': 'http://c.speedtest.net/flash/speedtest.swf'} + request = build_request('://www.speedtest.net/api/api.php', data='&'.join(apiData).encode(), headers=headers) f, e = catch_request(request) @@ -761,8 +774,8 @@ def speedtest(): print_('Could not submit results to speedtest.net') sys.exit(1) - print_('Share results: https://www.speedtest.net/result/%s.png' % - resultid[0]) + print_('Share results: %s://www.speedtest.net/result/%s.png' % + scheme, resultid[0]) def main(): From d1be67be48ae5e2d2964dff854766a57919cccf6 Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Mon, 20 Apr 2015 09:45:02 -0500 Subject: [PATCH 05/12] Attempt a Mozilla/5.0 compatible user-agent string --- speedtest_cli.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/speedtest_cli.py b/speedtest_cli.py index de9b1aa..feb8edc 100755 --- a/speedtest_cli.py +++ b/speedtest_cli.py @@ -22,12 +22,13 @@ import math import signal import socket import timeit +import platform import threading __version__ = '0.3.3a' # Some global variables we use -user_agent = 'speedtest-cli/%s' % __version__ +user_agent = None source = None shutdown_event = None scheme = 'http' @@ -184,6 +185,24 @@ def distance(origin, destination): return d +def build_user_agent(): + """Build a Mozilla/5.0 compatible User-Agent string""" + + global user_agent + if user_agent: + return user_agent + + ua_tuple = ( + 'Mozilla/5.0', + '(%s; U; %s; en-us)' % (platform.system(), platform.architecture()[0]), + 'Python/%s' % platform.python_version(), + '(KHTML, like Gecko)', + 'speedtest-cli/%s' % __version__ + ) + user_agent = ' '.join(ua_tuple) + return user_agent + + def build_request(url, data=None, headers={}): """Build a urllib2 request object @@ -196,7 +215,7 @@ def build_request(url, data=None, headers={}): else: schemed_url = url - headers['User-Agent'] = user_agent + headers['User-Agent'] = build_user_agent() return Request(schemed_url, data=data, headers=headers) @@ -585,6 +604,9 @@ def speedtest(): socket.setdefaulttimeout(args.timeout) + # Pre-cache the user agent string + build_user_agent() + # If specified bind to a specific IP address if args.source: source = args.source From 47c17d4a49cc91a2457cd46c2546329c21e30db3 Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Wed, 13 May 2015 11:35:01 -0500 Subject: [PATCH 06/12] Just use the global instead of calling the function again --- speedtest_cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/speedtest_cli.py b/speedtest_cli.py index feb8edc..8bdf86e 100755 --- a/speedtest_cli.py +++ b/speedtest_cli.py @@ -215,7 +215,7 @@ def build_request(url, data=None, headers={}): else: schemed_url = url - headers['User-Agent'] = build_user_agent() + headers['User-Agent'] = user_agent return Request(schemed_url, data=data, headers=headers) From 51d0d88b96e16b6be5ef4e75f1e6fd0a88639170 Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Fri, 15 May 2015 10:03:10 -0500 Subject: [PATCH 07/12] Bump to 0.3.3b --- speedtest_cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/speedtest_cli.py b/speedtest_cli.py index 8bdf86e..c42d81e 100755 --- a/speedtest_cli.py +++ b/speedtest_cli.py @@ -25,7 +25,7 @@ import timeit import platform import threading -__version__ = '0.3.3a' +__version__ = '0.3.3b' # Some global variables we use user_agent = None From 1e44e9e2f15efad0f157141da2e2da383896c551 Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Sun, 17 May 2015 19:50:05 -0500 Subject: [PATCH 08/12] Always encode server info --- speedtest_cli.py | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/speedtest_cli.py b/speedtest_cli.py index c42d81e..ba0f38c 100755 --- a/speedtest_cli.py +++ b/speedtest_cli.py @@ -633,16 +633,7 @@ def speedtest(): line = ('%(id)4s) %(sponsor)s (%(name)s, %(country)s) ' '[%(d)0.2f km]' % server) serverList.append(line) - # Python 2.7 and newer seem to be ok with the resultant encoding - # from parsing the XML, but older versions have some issues. - # This block should detect whether we need to encode or not - try: - unicode() - print_('\n'.join(serverList).encode('utf-8', 'ignore')) - except NameError: - print_('\n'.join(serverList)) - except IOError: - pass + print_('\n'.join(serverList).encode('utf-8', 'ignore')) sys.exit(0) else: servers = closestServers(config['client']) @@ -710,16 +701,8 @@ def speedtest(): best = getBestServer(servers) if not args.simple: - # Python 2.7 and newer seem to be ok with the resultant encoding - # from parsing the XML, but older versions have some issues. - # This block should detect whether we need to encode or not - try: - unicode() - print_(('Hosted by %(sponsor)s (%(name)s) [%(d)0.2f km]: ' - '%(latency)s ms' % best).encode('utf-8', 'ignore')) - except NameError: - print_('Hosted by %(sponsor)s (%(name)s) [%(d)0.2f km]: ' - '%(latency)s ms' % best) + print_(('Hosted by %(sponsor)s (%(name)s) [%(d)0.2f km]: ' + '%(latency)s ms' % best).encode('utf-8', 'ignore')) else: print_('Ping: %(latency)s ms' % best) From 1df3e76b19de6e369844ef54c2ce9b505544cf80 Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Wed, 20 May 2015 14:49:45 -0500 Subject: [PATCH 09/12] Switch to travis addons for installing deadsnakes --- .travis.yml | 20 +++++++++++--------- tox.ini | 4 ++-- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index ddaa43b..e5bd889 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,17 @@ language: python python: - 2.7 +addons: + apt: + sources: + - deadsnakes + packages: + - python2.4 + - python2.5 + - python2.6 + - python3.1 + - pypy + env: - TOXENV=py24 - TOXENV=py25 @@ -15,15 +26,6 @@ env: - TOXENV=pypy - TOXENV=flake8 -before_install: - - if [[ $(echo "$TOXENV" | egrep -c "(py2[45]|py3[14])") != 0 ]]; then sudo add-apt-repository -y ppa:fkrull/deadsnakes; fi; - - if [[ $(echo "$TOXENV" | egrep -c "(py2[45]|py3[14])") != 0 ]]; then sudo apt-get update -qq; fi; - - if [[ "$TOXENV" == "py24" ]]; then sudo apt-get install -y python2.4; fi; - - if [[ "$TOXENV" == "py25" ]]; then sudo apt-get install -y python2.5; fi; - - if [[ "$TOXENV" == "py31" ]]; then sudo apt-get install -y python3.1; fi; - - if [[ "$TOXENV" == "py34" ]]; then sudo apt-get install -y python3.4; fi; - - if [[ "$TOXENV" == "pypy" ]]; then sudo apt-get install -y pypy; fi; - install: - if [[ $(echo "$TOXENV" | egrep -c "(py2[45]|py31)") != 0 ]]; then pip install virtualenv==1.7.2 tox==1.3; fi; - if [[ $(echo "$TOXENV" | egrep -c "(py2[45]|py31)") == 0 ]]; then pip install tox; fi; diff --git a/tox.ini b/tox.ini index 8d0ebc2..bb37438 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ skipsdist=true [testenv] commands = {envpython} -V - {envpython} speedtest_cli.py + {envpython} -m compileall speedtest_cli.py [testenv:flake8] basepython=python @@ -16,4 +16,4 @@ commands = [testenv:pypy] commands = pypy -V - pypy speedtest_cli.py + pypy -m compileall speedtest_cli.py From bae642ccdef97b21698502023fc3821275ae814d Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Wed, 20 May 2015 14:52:35 -0500 Subject: [PATCH 10/12] Meant to add an additional line for compileall --- tox.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tox.ini b/tox.ini index bb37438..3a17c18 100644 --- a/tox.ini +++ b/tox.ini @@ -5,6 +5,7 @@ skipsdist=true commands = {envpython} -V {envpython} -m compileall speedtest_cli.py + {envpython} speedtest_cli.py [testenv:flake8] basepython=python @@ -17,3 +18,4 @@ commands = commands = pypy -V pypy -m compileall speedtest_cli.py + pypy speedtest_cli.py From 918e70e66d152d2ad405f302352ef419a9d69d52 Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Wed, 20 May 2015 18:00:19 -0500 Subject: [PATCH 11/12] Remove py31 --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e5bd889..e8b3a20 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,6 @@ addons: - python2.4 - python2.5 - python2.6 - - python3.1 - pypy env: @@ -19,7 +18,6 @@ env: - TOXENV=py25 - TOXENV=py26 - TOXENV=py27 - - TOXENV=py31 - TOXENV=py32 - TOXENV=py33 - TOXENV=py34 From 514b310484431f4c3aa6c8bc72d82f6d1163f11b Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Thu, 21 May 2015 14:31:05 -0500 Subject: [PATCH 12/12] Fix missing space --- speedtest_cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/speedtest_cli.py b/speedtest_cli.py index ba0f38c..5f343f6 100755 --- a/speedtest_cli.py +++ b/speedtest_cli.py @@ -586,7 +586,7 @@ def speedtest(): parser.add_argument('--timeout', default=10, type=int, help='HTTP timeout in seconds. Default 10') parser.add_argument('--secure', action='store_true', - help='Use HTTPS instead of HTTP when communicating' + help='Use HTTPS instead of HTTP when communicating ' 'with speedtest.net operated servers') parser.add_argument('--version', action='store_true', help='Show the version number and exit')