Compare commits
	
		
			No commits in common. "master" and "v0.3.1" have entirely different histories.
		
	
	
		
							
								
								
									
										72
									
								
								.travis.yml
								
								
								
								
							
							
						
						
									
										72
									
								
								.travis.yml
								
								
								
								
							|  | @ -1,61 +1,35 @@ | ||||||
| language: python | language: python | ||||||
| sudo: required |  | ||||||
| dist: xenial |  | ||||||
| 
 | 
 | ||||||
| addons: | python: | ||||||
|   apt: |  - 2.7 | ||||||
|     sources: |  | ||||||
|       - deadsnakes |  | ||||||
|     packages: |  | ||||||
|       - python2.4 |  | ||||||
|       - python2.5 |  | ||||||
|       - python2.6 |  | ||||||
|       - python3.2 |  | ||||||
|       - python3.3 |  | ||||||
| 
 | 
 | ||||||
| matrix: | env: | ||||||
|   include: |  - TOXENV=py24 | ||||||
|     - python: 2.7 |  - TOXENV=py25 | ||||||
|       env: TOXENV=flake8 |  - TOXENV=py26 | ||||||
|     - python: 2.7 |  - TOXENV=py27 | ||||||
|       env: TOXENV=py24 |  - TOXENV=py31 | ||||||
|     - python: 2.7 |  - TOXENV=py32 | ||||||
|       env: TOXENV=py25 |  - TOXENV=py33 | ||||||
|     - python: 2.7 |  - TOXENV=py34 | ||||||
|       env: TOXENV=py26 |  - TOXENV=pypy | ||||||
|     - python: 2.7 |  - TOXENV=flake8 | ||||||
|       env: TOXENV=py27 |  | ||||||
|     - python: 2.7 |  | ||||||
|       env: TOXENV=py32 |  | ||||||
|     - python: 2.7 |  | ||||||
|       env: TOXENV=py33 |  | ||||||
|     - python: 3.4 |  | ||||||
|       env: TOXENV=py34 |  | ||||||
|     - python: 3.5 |  | ||||||
|       env: TOXENV=py35 |  | ||||||
|     - python: 3.6 |  | ||||||
|       env: TOXENV=py36 |  | ||||||
|     - python: 3.7 |  | ||||||
|       env: TOXENV=py37 |  | ||||||
|     - python: 3.8-dev |  | ||||||
|       env: TOXENV=py38 |  | ||||||
|     - python: pypy |  | ||||||
|       env: TOXENV=pypy |  | ||||||
|       dist: trusty |  | ||||||
| 
 | 
 | ||||||
| before_install: | before_install: | ||||||
|   - if [[ $(echo "$TOXENV" | egrep -c "py35") != 0 ]]; then pyenv global system 3.5; fi; |  - 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: | install: | ||||||
|   - if [[ $(echo "$TOXENV" | egrep -c "py32") != 0 ]]; then pip install setuptools==17.1.1; fi; |  - 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]|py3[12])") != 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; | ||||||
|   - if [[ $(echo "$TOXENV" | egrep -c "(py26|py33)") != 0 ]]; then pip install virtualenv==15.2.0 tox==2.9.1; fi; |  | ||||||
|   - if [[ $(echo "$TOXENV" | egrep -c "(py2[456]|py3[123])") == 0 ]]; then pip install tox; fi; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| script: | script: | ||||||
|   - tox |  - tox | ||||||
| 
 | 
 | ||||||
| notifications: | notifications: | ||||||
|   email: |   email: | ||||||
|  |  | ||||||
							
								
								
									
										89
									
								
								README.rst
								
								
								
								
							
							
						
						
									
										89
									
								
								README.rst
								
								
								
								
							|  | @ -4,24 +4,20 @@ speedtest-cli | ||||||
| Command line interface for testing internet bandwidth using | Command line interface for testing internet bandwidth using | ||||||
| speedtest.net | speedtest.net | ||||||
| 
 | 
 | ||||||
| .. image:: https://img.shields.io/pypi/v/speedtest-cli.svg | .. image:: https://pypip.in/v/speedtest-cli/badge.png | ||||||
|         :target: https://pypi.python.org/pypi/speedtest-cli/ |         :target: https://pypi.python.org/pypi/speedtest-cli/ | ||||||
|         :alt: Latest Version |         :alt: Latest Version | ||||||
| .. image:: https://img.shields.io/travis/sivel/speedtest-cli.svg | .. image:: https://pypip.in/d/speedtest-cli/badge.png | ||||||
|         :target: https://pypi.python.org/pypi/speedtest-cli/ |         :target: https://pypi.python.org/pypi/speedtest-cli/ | ||||||
|         :alt: Travis |         :alt: Downloads | ||||||
| .. image:: https://img.shields.io/pypi/l/speedtest-cli.svg | .. image:: https://pypip.in/license/speedtest-cli/badge.png | ||||||
|         :target: https://pypi.python.org/pypi/speedtest-cli/ |         :target: https://pypi.python.org/pypi/speedtest-cli/ | ||||||
|         :alt: License |         :alt: License | ||||||
| 
 | 
 | ||||||
| Versions | Versions | ||||||
| -------- | -------- | ||||||
| 
 | 
 | ||||||
| speedtest-cli works with Python 2.4-3.7 | speedtest-cli works with Python 2.4-3.4 | ||||||
| 
 |  | ||||||
| .. image:: https://img.shields.io/pypi/pyversions/speedtest-cli.svg |  | ||||||
|         :target: https://pypi.python.org/pypi/speedtest-cli/ |  | ||||||
|         :alt: Versions |  | ||||||
| 
 | 
 | ||||||
| Installation | Installation | ||||||
| ------------ | ------------ | ||||||
|  | @ -51,22 +47,21 @@ or | ||||||
| :: | :: | ||||||
| 
 | 
 | ||||||
|     git clone https://github.com/sivel/speedtest-cli.git |     git clone https://github.com/sivel/speedtest-cli.git | ||||||
|     cd speedtest-cli |     python speedtest-cli/setup.py install | ||||||
|     python setup.py install |  | ||||||
| 
 | 
 | ||||||
| Just download (Like the way it used to be) | Just download (Like the way it used to be) | ||||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||||
| 
 | 
 | ||||||
| :: | :: | ||||||
| 
 | 
 | ||||||
|     wget -O speedtest-cli https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest.py |     wget -O speedtest-cli https://raw.github.com/sivel/speedtest-cli/master/speedtest_cli.py | ||||||
|     chmod +x speedtest-cli |     chmod +x speedtest-cli | ||||||
| 
 | 
 | ||||||
| or | or | ||||||
| 
 | 
 | ||||||
| :: | :: | ||||||
| 
 | 
 | ||||||
|     curl -Lo speedtest-cli https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest.py |     curl -o speedtest-cli https://raw.github.com/sivel/speedtest-cli/master/speedtest_cli.py | ||||||
|     chmod +x speedtest-cli |     chmod +x speedtest-cli | ||||||
| 
 | 
 | ||||||
| Usage | Usage | ||||||
|  | @ -75,62 +70,26 @@ Usage | ||||||
| :: | :: | ||||||
| 
 | 
 | ||||||
|     $ speedtest-cli -h |     $ speedtest-cli -h | ||||||
|     usage: speedtest-cli [-h] [--no-download] [--no-upload] [--single] [--bytes] |     usage: speedtest-cli [-h] [--bytes] [--share] [--simple] [--list] | ||||||
|                          [--share] [--simple] [--csv] |                          [--server SERVER] [--mini MINI] [--source SOURCE] | ||||||
|                          [--csv-delimiter CSV_DELIMITER] [--csv-header] [--json] |                          [--version] | ||||||
|                          [--list] [--server SERVER] [--exclude EXCLUDE] |      | ||||||
|                          [--mini MINI] [--source SOURCE] [--timeout TIMEOUT] |  | ||||||
|                          [--secure] [--no-pre-allocate] [--version] |  | ||||||
| 
 |  | ||||||
|     Command line interface for testing internet bandwidth using speedtest.net. |     Command line interface for testing internet bandwidth using speedtest.net. | ||||||
|     -------------------------------------------------------------------------- |     -------------------------------------------------------------------------- | ||||||
|     https://github.com/sivel/speedtest-cli |     https://github.com/sivel/speedtest-cli | ||||||
| 
 |      | ||||||
|     optional arguments: |     optional arguments: | ||||||
|       -h, --help            show this help message and exit |       -h, --help       show this help message and exit | ||||||
|       --no-download         Do not perform download test |       --bytes          Display values in bytes instead of bits. Does not affect | ||||||
|       --no-upload           Do not perform upload test |                        the image generated by --share | ||||||
|       --single              Only use a single connection instead of multiple. This |       --share          Generate and provide a URL to the speedtest.net share | ||||||
|                             simulates a typical file transfer. |                        results image | ||||||
|       --bytes               Display values in bytes instead of bits. Does not |       --simple         Suppress verbose output, only show basic information | ||||||
|                             affect the image generated by --share, nor output from |       --list           Display a list of speedtest.net servers sorted by distance | ||||||
|                             --json or --csv |       --server SERVER  Specify a server ID to test against | ||||||
|       --share               Generate and provide a URL to the speedtest.net share |       --mini MINI      URL of the Speedtest Mini server | ||||||
|                             results image, not displayed with --csv |       --source SOURCE  Source IP address to bind to | ||||||
|       --simple              Suppress verbose output, only show basic information |       --version        Show the version number and exit | ||||||
|       --csv                 Suppress verbose output, only show basic information |  | ||||||
|                             in CSV format. Speeds listed in bit/s and not affected |  | ||||||
|                             by --bytes |  | ||||||
|       --csv-delimiter CSV_DELIMITER |  | ||||||
|                             Single character delimiter to use in CSV output. |  | ||||||
|                             Default "," |  | ||||||
|       --csv-header          Print CSV headers |  | ||||||
|       --json                Suppress verbose output, only show basic information |  | ||||||
|                             in JSON format. Speeds listed in bit/s and not |  | ||||||
|                             affected by --bytes |  | ||||||
|       --list                Display a list of speedtest.net servers sorted by |  | ||||||
|                             distance |  | ||||||
|       --server SERVER       Specify a server ID to test against. Can be supplied |  | ||||||
|                             multiple times |  | ||||||
|       --exclude EXCLUDE     Exclude a server from selection. Can be supplied |  | ||||||
|                             multiple times |  | ||||||
|       --mini MINI           URL of the Speedtest Mini server |  | ||||||
|       --source SOURCE       Source IP address to bind to |  | ||||||
|       --timeout TIMEOUT     HTTP timeout in seconds. Default 10 |  | ||||||
|       --secure              Use HTTPS instead of HTTP when communicating with |  | ||||||
|                             speedtest.net operated servers |  | ||||||
|       --no-pre-allocate     Do not pre allocate upload data. Pre allocation is |  | ||||||
|                             enabled by default to improve upload performance. To |  | ||||||
|                             support systems with insufficient memory, use this |  | ||||||
|                             option to avoid a MemoryError |  | ||||||
|       --version             Show the version number and exit |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| Python API |  | ||||||
| ---------- |  | ||||||
| 
 |  | ||||||
| See the `wiki <https://github.com/sivel/speedtest-cli/wiki>`_. |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| Inconsistency | Inconsistency | ||||||
| ------------- | ------------- | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								setup.py
								
								
								
								
							
							
						
						
									
										16
									
								
								setup.py
								
								
								
								
							|  | @ -1,6 +1,6 @@ | ||||||
| #!/usr/bin/env python | #!/usr/bin/env python | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
| # Copyright 2012 Matt Martz | # Copyright 2012-2014 Matt Martz | ||||||
| # All Rights Reserved. | # All Rights Reserved. | ||||||
| # | # | ||||||
| #    Licensed under the Apache License, Version 2.0 (the "License"); you may | #    Licensed under the Apache License, Version 2.0 (the "License"); you may | ||||||
|  | @ -57,7 +57,7 @@ except: | ||||||
| 
 | 
 | ||||||
| setup( | setup( | ||||||
|     name='speedtest-cli', |     name='speedtest-cli', | ||||||
|     version=find_version('speedtest.py'), |     version=find_version('speedtest_cli.py'), | ||||||
|     description=('Command line interface for testing internet bandwidth using ' |     description=('Command line interface for testing internet bandwidth using ' | ||||||
|                  'speedtest.net'), |                  'speedtest.net'), | ||||||
|     long_description=long_description, |     long_description=long_description, | ||||||
|  | @ -66,11 +66,11 @@ setup( | ||||||
|     author_email='matt@sivel.net', |     author_email='matt@sivel.net', | ||||||
|     url='https://github.com/sivel/speedtest-cli', |     url='https://github.com/sivel/speedtest-cli', | ||||||
|     license='Apache License, Version 2.0', |     license='Apache License, Version 2.0', | ||||||
|     py_modules=['speedtest'], |     py_modules=['speedtest_cli'], | ||||||
|     entry_points={ |     entry_points={ | ||||||
|         'console_scripts': [ |         'console_scripts': [ | ||||||
|             'speedtest=speedtest:main', |             'speedtest=speedtest_cli:main', | ||||||
|             'speedtest-cli=speedtest:main' |             'speedtest-cli=speedtest_cli:main' | ||||||
|         ] |         ] | ||||||
|     }, |     }, | ||||||
|     classifiers=[ |     classifiers=[ | ||||||
|  | @ -89,11 +89,5 @@ setup( | ||||||
|         'Programming Language :: Python :: 3.2', |         'Programming Language :: Python :: 3.2', | ||||||
|         'Programming Language :: Python :: 3.3', |         'Programming Language :: Python :: 3.3', | ||||||
|         'Programming Language :: Python :: 3.4', |         'Programming Language :: Python :: 3.4', | ||||||
|         'Programming Language :: Python :: 3.5', |  | ||||||
|         'Programming Language :: Python :: 3.6', |  | ||||||
|         'Programming Language :: Python :: 3.7', |  | ||||||
|         'Programming Language :: Python :: 3.8', |  | ||||||
|         'Programming Language :: Python :: 3.9', |  | ||||||
|         'Programming Language :: Python :: 3.10', |  | ||||||
|     ] |     ] | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | @ -1,9 +1,9 @@ | ||||||
| .TH "speedtest-cli" 1 "2018-01-05" "speedtest-cli" | .TH "speedtest-cli" 1 "2014-04-23" "speedtest-cli" | ||||||
| .SH NAME | .SH NAME | ||||||
| speedtest\-cli \- Command line interface for testing internet bandwidth using speedtest.net | speedtest\-cli \- Test your bandwidth througput using speedtest.net | ||||||
| .SH SYNOPSIS | .SH SYNOPSIS | ||||||
| .B speedtest\-cli | .B speedtest\-cli | ||||||
| [OPTION...] | [OPTION]... | ||||||
| .SH DESCRIPTION | .SH DESCRIPTION | ||||||
| Speedtest.net is a web service for testing your broadband connection by downloading a file | Speedtest.net is a web service for testing your broadband connection by downloading a file | ||||||
| from a nearby speedtest.net server on the web. This tool allows you to access the service | from a nearby speedtest.net server on the web. This tool allows you to access the service | ||||||
|  | @ -23,29 +23,14 @@ Displays usage for the tool. | ||||||
| 
 | 
 | ||||||
| .B Options | .B Options | ||||||
| 
 | 
 | ||||||
| \fB\-\-no\-download\fR |  | ||||||
| .RS |  | ||||||
| Do not perform download test |  | ||||||
| .RE |  | ||||||
| 
 |  | ||||||
| \fB\-\-no\-upload\fR |  | ||||||
| .RS |  | ||||||
| Do not perform upload test |  | ||||||
| .RE |  | ||||||
| 
 |  | ||||||
| \fB\-\-single\fR |  | ||||||
| .RS |  | ||||||
| Only use a single connection instead of multiple. This simulates a typical file transfer. |  | ||||||
| .RE |  | ||||||
| 
 |  | ||||||
| \fB\-\-bytes\fR | \fB\-\-bytes\fR | ||||||
| .RS | .RS | ||||||
| Display values in bytes instead of bits. Does not affect the image generated by \-\-share, nor output from \-\-json or \-\-csv | Display values in bytes instead of bits. Does not affect the image generated by \-\-share | ||||||
| .RE | .RE | ||||||
| 
 | 
 | ||||||
| \fB\-\-share\fR | \fB\-\-share\fR | ||||||
| .RS | .RS | ||||||
| Generate and provide a URL to the speedtest.net share results image, not displayed with \-\-csv | Generate and provide a URL to the speedtest.net share results image | ||||||
| .RE | .RE | ||||||
| 
 | 
 | ||||||
| \fB\-\-simple\fR | \fB\-\-simple\fR | ||||||
|  | @ -53,26 +38,6 @@ Generate and provide a URL to the speedtest.net share results image, not display | ||||||
| Suppress verbose output, only show basic information | Suppress verbose output, only show basic information | ||||||
| .RE | .RE | ||||||
| 
 | 
 | ||||||
| \fB\-\-csv\fR |  | ||||||
| .RS |  | ||||||
| Suppress verbose output, only show basic information in CSV format. Speeds listed in bit/s and not affected by \-\-bytes |  | ||||||
| .RE |  | ||||||
| 
 |  | ||||||
| \fB\-\-csv\-delimiter CSV_DELIMITER\fR |  | ||||||
| .RS |  | ||||||
| Single character delimiter to use in CSV output. Default "," |  | ||||||
| .RE |  | ||||||
| 
 |  | ||||||
| \fB\-\-csv\-header\fR |  | ||||||
| .RS |  | ||||||
| Print CSV headers |  | ||||||
| .RE |  | ||||||
| 
 |  | ||||||
| \fB\-\-json\fR |  | ||||||
| .RS |  | ||||||
| Suppress verbose output, only show basic information in JSON format. Speeds listed in bit/s and not affected by \-\-bytes |  | ||||||
| .RE |  | ||||||
| 
 |  | ||||||
| \fB\-\-list\fR | \fB\-\-list\fR | ||||||
| .RS | .RS | ||||||
| Display a list of speedtest.net servers sorted by distance | Display a list of speedtest.net servers sorted by distance | ||||||
|  | @ -80,12 +45,7 @@ Display a list of speedtest.net servers sorted by distance | ||||||
| 
 | 
 | ||||||
| \fB\-\-server SERVER\fR | \fB\-\-server SERVER\fR | ||||||
| .RS | .RS | ||||||
| Specify a server ID to test against. Can be supplied multiple times | Specify a server ID to test against | ||||||
| .RE |  | ||||||
| 
 |  | ||||||
| \fB\-\-exclude EXCLUDE\fR |  | ||||||
| .RS |  | ||||||
| Exclude a server from selection. Can be supplied multiple times |  | ||||||
| .RE | .RE | ||||||
| 
 | 
 | ||||||
| \fB\-\-mini MINI\fR | \fB\-\-mini MINI\fR | ||||||
|  | @ -98,21 +58,6 @@ URL of the Speedtest Mini server | ||||||
| Source IP address to bind to | Source IP address to bind to | ||||||
| .RE | .RE | ||||||
| 
 | 
 | ||||||
| \fB\-\-timeout TIMEOUT\fR |  | ||||||
| .RS |  | ||||||
| HTTP timeout in seconds. Default 10 |  | ||||||
| .RE |  | ||||||
| 
 |  | ||||||
| \fB\-\-secure\fR |  | ||||||
| .RS |  | ||||||
| Use HTTPS instead of HTTP when communicating with speedtest.net operated servers |  | ||||||
| .RE |  | ||||||
| 
 |  | ||||||
| \fB\-\-no\-pre\-allocate\fR |  | ||||||
| .RS |  | ||||||
| Do not pre allocate upload data. Pre allocation is enabled by default to improve upload performance. To support systems with insufficient memory, use this option to avoid a MemoryError |  | ||||||
| .RE |  | ||||||
| 
 |  | ||||||
| \fB\-\-version\fR | \fB\-\-version\fR | ||||||
| .RS | .RS | ||||||
| Show the version number and exit | Show the version number and exit | ||||||
|  |  | ||||||
							
								
								
									
										2013
									
								
								speedtest.py
								
								
								
								
							
							
						
						
									
										2013
									
								
								speedtest.py
								
								
								
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,694 @@ | ||||||
|  | #!/usr/bin/env python | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Copyright 2012-2014 Matt Martz | ||||||
|  | # All Rights Reserved. | ||||||
|  | # | ||||||
|  | #    Licensed under the Apache License, Version 2.0 (the "License"); you may | ||||||
|  | #    not use this file except in compliance with the License. You may obtain | ||||||
|  | #    a copy of the License at | ||||||
|  | # | ||||||
|  | #         http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  | # | ||||||
|  | #    Unless required by applicable law or agreed to in writing, software | ||||||
|  | #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||||||
|  | #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||||||
|  | #    License for the specific language governing permissions and limitations | ||||||
|  | #    under the License. | ||||||
|  | 
 | ||||||
|  | __version__ = '0.3.1' | ||||||
|  | 
 | ||||||
|  | # Some global variables we use | ||||||
|  | source = None | ||||||
|  | shutdown_event = None | ||||||
|  | 
 | ||||||
|  | import os | ||||||
|  | import re | ||||||
|  | import sys | ||||||
|  | import math | ||||||
|  | import signal | ||||||
|  | import socket | ||||||
|  | import timeit | ||||||
|  | import threading | ||||||
|  | 
 | ||||||
|  | # Used for bound_interface | ||||||
|  | socket_socket = socket.socket | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     import xml.etree.cElementTree as ET | ||||||
|  | except ImportError: | ||||||
|  |     try: | ||||||
|  |         import xml.etree.ElementTree as ET | ||||||
|  |     except ImportError: | ||||||
|  |         from xml.dom import minidom as DOM | ||||||
|  |         ET = None | ||||||
|  | 
 | ||||||
|  | # Begin import game to handle Python 2 and Python 3 | ||||||
|  | try: | ||||||
|  |     from urllib2 import urlopen, Request, HTTPError, URLError | ||||||
|  | except ImportError: | ||||||
|  |     from urllib.request import urlopen, Request, HTTPError, URLError | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     from httplib import HTTPConnection, HTTPSConnection | ||||||
|  | except ImportError: | ||||||
|  |     from http.client import HTTPConnection, HTTPSConnection | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     from Queue import Queue | ||||||
|  | except ImportError: | ||||||
|  |     from queue import Queue | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     from urlparse import urlparse | ||||||
|  | except ImportError: | ||||||
|  |     from urllib.parse import urlparse | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     from urlparse import parse_qs | ||||||
|  | except ImportError: | ||||||
|  |     try: | ||||||
|  |         from urllib.parse import parse_qs | ||||||
|  |     except ImportError: | ||||||
|  |         from cgi import parse_qs | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     from hashlib import md5 | ||||||
|  | except ImportError: | ||||||
|  |     from md5 import md5 | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     from argparse import ArgumentParser as ArgParser | ||||||
|  | except ImportError: | ||||||
|  |     from optparse import OptionParser as ArgParser | ||||||
|  | 
 | ||||||
|  | try: | ||||||
|  |     import builtins | ||||||
|  | except ImportError: | ||||||
|  |     def print_(*args, **kwargs): | ||||||
|  |         """The new-style print function taken from | ||||||
|  |         https://pypi.python.org/pypi/six/ | ||||||
|  | 
 | ||||||
|  |         """ | ||||||
|  |         fp = kwargs.pop("file", sys.stdout) | ||||||
|  |         if fp is None: | ||||||
|  |             return | ||||||
|  | 
 | ||||||
|  |         def write(data): | ||||||
|  |             if not isinstance(data, basestring): | ||||||
|  |                 data = str(data) | ||||||
|  |             fp.write(data) | ||||||
|  | 
 | ||||||
|  |         want_unicode = False | ||||||
|  |         sep = kwargs.pop("sep", None) | ||||||
|  |         if sep is not None: | ||||||
|  |             if isinstance(sep, unicode): | ||||||
|  |                 want_unicode = True | ||||||
|  |             elif not isinstance(sep, str): | ||||||
|  |                 raise TypeError("sep must be None or a string") | ||||||
|  |         end = kwargs.pop("end", None) | ||||||
|  |         if end is not None: | ||||||
|  |             if isinstance(end, unicode): | ||||||
|  |                 want_unicode = True | ||||||
|  |             elif not isinstance(end, str): | ||||||
|  |                 raise TypeError("end must be None or a string") | ||||||
|  |         if kwargs: | ||||||
|  |             raise TypeError("invalid keyword arguments to print()") | ||||||
|  |         if not want_unicode: | ||||||
|  |             for arg in args: | ||||||
|  |                 if isinstance(arg, unicode): | ||||||
|  |                     want_unicode = True | ||||||
|  |                     break | ||||||
|  |         if want_unicode: | ||||||
|  |             newline = unicode("\n") | ||||||
|  |             space = unicode(" ") | ||||||
|  |         else: | ||||||
|  |             newline = "\n" | ||||||
|  |             space = " " | ||||||
|  |         if sep is None: | ||||||
|  |             sep = space | ||||||
|  |         if end is None: | ||||||
|  |             end = newline | ||||||
|  |         for i, arg in enumerate(args): | ||||||
|  |             if i: | ||||||
|  |                 write(sep) | ||||||
|  |             write(arg) | ||||||
|  |         write(end) | ||||||
|  | else: | ||||||
|  |     print_ = getattr(builtins, 'print') | ||||||
|  |     del builtins | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def bound_socket(*args, **kwargs): | ||||||
|  |     """Bind socket to a specified source IP address""" | ||||||
|  | 
 | ||||||
|  |     global source | ||||||
|  |     sock = socket_socket(*args, **kwargs) | ||||||
|  |     sock.bind((source, 0)) | ||||||
|  |     return sock | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def distance(origin, destination): | ||||||
|  |     """Determine distance between 2 sets of [lat,lon] in km""" | ||||||
|  | 
 | ||||||
|  |     lat1, lon1 = origin | ||||||
|  |     lat2, lon2 = destination | ||||||
|  |     radius = 6371  # km | ||||||
|  | 
 | ||||||
|  |     dlat = math.radians(lat2 - lat1) | ||||||
|  |     dlon = math.radians(lon2 - lon1) | ||||||
|  |     a = (math.sin(dlat / 2) * math.sin(dlat / 2) + math.cos(math.radians(lat1)) | ||||||
|  |          * math.cos(math.radians(lat2)) * math.sin(dlon / 2) | ||||||
|  |          * math.sin(dlon / 2)) | ||||||
|  |     c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) | ||||||
|  |     d = radius * c | ||||||
|  | 
 | ||||||
|  |     return d | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class FileGetter(threading.Thread): | ||||||
|  |     """Thread class for retrieving a URL""" | ||||||
|  | 
 | ||||||
|  |     def __init__(self, url, start): | ||||||
|  |         self.url = url | ||||||
|  |         self.result = None | ||||||
|  |         self.starttime = start | ||||||
|  |         threading.Thread.__init__(self) | ||||||
|  | 
 | ||||||
|  |     def run(self): | ||||||
|  |         self.result = [0] | ||||||
|  |         try: | ||||||
|  |             if (timeit.default_timer() - self.starttime) <= 10: | ||||||
|  |                 f = urlopen(self.url) | ||||||
|  |                 while 1 and not shutdown_event.isSet(): | ||||||
|  |                     self.result.append(len(f.read(10240))) | ||||||
|  |                     if self.result[-1] == 0: | ||||||
|  |                         break | ||||||
|  |                 f.close() | ||||||
|  |         except IOError: | ||||||
|  |             pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def downloadSpeed(files, quiet=False): | ||||||
|  |     """Function to launch FileGetter threads and calculate download speeds""" | ||||||
|  | 
 | ||||||
|  |     start = timeit.default_timer() | ||||||
|  | 
 | ||||||
|  |     def producer(q, files): | ||||||
|  |         for file in files: | ||||||
|  |             thread = FileGetter(file, start) | ||||||
|  |             thread.start() | ||||||
|  |             q.put(thread, True) | ||||||
|  |             if not quiet and not shutdown_event.isSet(): | ||||||
|  |                 sys.stdout.write('.') | ||||||
|  |                 sys.stdout.flush() | ||||||
|  | 
 | ||||||
|  |     finished = [] | ||||||
|  | 
 | ||||||
|  |     def consumer(q, total_files): | ||||||
|  |         while len(finished) < total_files: | ||||||
|  |             thread = q.get(True) | ||||||
|  |             while thread.isAlive(): | ||||||
|  |                 thread.join(timeout=0.1) | ||||||
|  |             finished.append(sum(thread.result)) | ||||||
|  |             del thread | ||||||
|  | 
 | ||||||
|  |     q = Queue(6) | ||||||
|  |     prod_thread = threading.Thread(target=producer, args=(q, files)) | ||||||
|  |     cons_thread = threading.Thread(target=consumer, args=(q, len(files))) | ||||||
|  |     start = timeit.default_timer() | ||||||
|  |     prod_thread.start() | ||||||
|  |     cons_thread.start() | ||||||
|  |     while prod_thread.isAlive(): | ||||||
|  |         prod_thread.join(timeout=0.1) | ||||||
|  |     while cons_thread.isAlive(): | ||||||
|  |         cons_thread.join(timeout=0.1) | ||||||
|  |     return (sum(finished) / (timeit.default_timer() - start)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class FilePutter(threading.Thread): | ||||||
|  |     """Thread class for putting a URL""" | ||||||
|  | 
 | ||||||
|  |     def __init__(self, url, start, size): | ||||||
|  |         self.url = url | ||||||
|  |         chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' | ||||||
|  |         data = chars * (int(round(int(size) / 36.0))) | ||||||
|  |         self.data = ('content1=%s' % data[0:int(size) - 9]).encode() | ||||||
|  |         del data | ||||||
|  |         self.result = None | ||||||
|  |         self.starttime = start | ||||||
|  |         threading.Thread.__init__(self) | ||||||
|  | 
 | ||||||
|  |     def run(self): | ||||||
|  |         try: | ||||||
|  |             if ((timeit.default_timer() - self.starttime) <= 10 and | ||||||
|  |                     not shutdown_event.isSet()): | ||||||
|  |                 f = urlopen(self.url, self.data) | ||||||
|  |                 f.read(11) | ||||||
|  |                 f.close() | ||||||
|  |                 self.result = len(self.data) | ||||||
|  |             else: | ||||||
|  |                 self.result = 0 | ||||||
|  |         except IOError: | ||||||
|  |             self.result = 0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def uploadSpeed(url, sizes, quiet=False): | ||||||
|  |     """Function to launch FilePutter threads and calculate upload speeds""" | ||||||
|  | 
 | ||||||
|  |     start = timeit.default_timer() | ||||||
|  | 
 | ||||||
|  |     def producer(q, sizes): | ||||||
|  |         for size in sizes: | ||||||
|  |             thread = FilePutter(url, start, size) | ||||||
|  |             thread.start() | ||||||
|  |             q.put(thread, True) | ||||||
|  |             if not quiet and not shutdown_event.isSet(): | ||||||
|  |                 sys.stdout.write('.') | ||||||
|  |                 sys.stdout.flush() | ||||||
|  | 
 | ||||||
|  |     finished = [] | ||||||
|  | 
 | ||||||
|  |     def consumer(q, total_sizes): | ||||||
|  |         while len(finished) < total_sizes: | ||||||
|  |             thread = q.get(True) | ||||||
|  |             while thread.isAlive(): | ||||||
|  |                 thread.join(timeout=0.1) | ||||||
|  |             finished.append(thread.result) | ||||||
|  |             del thread | ||||||
|  | 
 | ||||||
|  |     q = Queue(6) | ||||||
|  |     prod_thread = threading.Thread(target=producer, args=(q, sizes)) | ||||||
|  |     cons_thread = threading.Thread(target=consumer, args=(q, len(sizes))) | ||||||
|  |     start = timeit.default_timer() | ||||||
|  |     prod_thread.start() | ||||||
|  |     cons_thread.start() | ||||||
|  |     while prod_thread.isAlive(): | ||||||
|  |         prod_thread.join(timeout=0.1) | ||||||
|  |     while cons_thread.isAlive(): | ||||||
|  |         cons_thread.join(timeout=0.1) | ||||||
|  |     return (sum(finished) / (timeit.default_timer() - start)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def getAttributesByTagName(dom, tagName): | ||||||
|  |     """Retrieve an attribute from an XML document and return it in a | ||||||
|  |     consistent format | ||||||
|  | 
 | ||||||
|  |     Only used with xml.dom.minidom, which is likely only to be used | ||||||
|  |     with python versions older than 2.5 | ||||||
|  |     """ | ||||||
|  |     elem = dom.getElementsByTagName(tagName)[0] | ||||||
|  |     return dict(list(elem.attributes.items())) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def getConfig(): | ||||||
|  |     """Download the speedtest.net configuration and return only the data | ||||||
|  |     we are interested in | ||||||
|  |     """ | ||||||
|  | 
 | ||||||
|  |     uh = urlopen('http://www.speedtest.net/speedtest-config.php') | ||||||
|  |     configxml = [] | ||||||
|  |     while 1: | ||||||
|  |         configxml.append(uh.read(10240)) | ||||||
|  |         if len(configxml[-1]) == 0: | ||||||
|  |             break | ||||||
|  |     if int(uh.code) != 200: | ||||||
|  |         return None | ||||||
|  |     uh.close() | ||||||
|  |     try: | ||||||
|  |         try: | ||||||
|  |             root = ET.fromstring(''.encode().join(configxml)) | ||||||
|  |             config = { | ||||||
|  |                 'client': root.find('client').attrib, | ||||||
|  |                 'times': root.find('times').attrib, | ||||||
|  |                 'download': root.find('download').attrib, | ||||||
|  |                 'upload': root.find('upload').attrib} | ||||||
|  |         except AttributeError: | ||||||
|  |             root = DOM.parseString(''.join(configxml)) | ||||||
|  |             config = { | ||||||
|  |                 'client': getAttributesByTagName(root, 'client'), | ||||||
|  |                 'times': getAttributesByTagName(root, 'times'), | ||||||
|  |                 'download': getAttributesByTagName(root, 'download'), | ||||||
|  |                 'upload': getAttributesByTagName(root, 'upload')} | ||||||
|  |     except SyntaxError: | ||||||
|  |         print_('Failed to parse speedtest.net configuration') | ||||||
|  |         sys.exit(1) | ||||||
|  |     del root | ||||||
|  |     del configxml | ||||||
|  |     return config | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def closestServers(client, all=False): | ||||||
|  |     """Determine the 5 closest speedtest.net servers based on geographic | ||||||
|  |     distance | ||||||
|  |     """ | ||||||
|  | 
 | ||||||
|  |     uh = urlopen('http://www.speedtest.net/speedtest-servers-static.php') | ||||||
|  |     serversxml = [] | ||||||
|  |     while 1: | ||||||
|  |         serversxml.append(uh.read(10240)) | ||||||
|  |         if len(serversxml[-1]) == 0: | ||||||
|  |             break | ||||||
|  |     if int(uh.code) != 200: | ||||||
|  |         return None | ||||||
|  |     uh.close() | ||||||
|  |     try: | ||||||
|  |         try: | ||||||
|  |             root = ET.fromstring(''.encode().join(serversxml)) | ||||||
|  |             elements = root.getiterator('server') | ||||||
|  |         except AttributeError: | ||||||
|  |             root = DOM.parseString(''.join(serversxml)) | ||||||
|  |             elements = root.getElementsByTagName('server') | ||||||
|  |     except SyntaxError: | ||||||
|  |         print_('Failed to parse list of speedtest.net servers') | ||||||
|  |         sys.exit(1) | ||||||
|  |     servers = {} | ||||||
|  |     for server in elements: | ||||||
|  |         try: | ||||||
|  |             attrib = server.attrib | ||||||
|  |         except AttributeError: | ||||||
|  |             attrib = dict(list(server.attributes.items())) | ||||||
|  |         d = distance([float(client['lat']), float(client['lon'])], | ||||||
|  |                      [float(attrib.get('lat')), float(attrib.get('lon'))]) | ||||||
|  |         attrib['d'] = d | ||||||
|  |         if d not in servers: | ||||||
|  |             servers[d] = [attrib] | ||||||
|  |         else: | ||||||
|  |             servers[d].append(attrib) | ||||||
|  |     del root | ||||||
|  |     del serversxml | ||||||
|  |     del elements | ||||||
|  | 
 | ||||||
|  |     closest = [] | ||||||
|  |     for d in sorted(servers.keys()): | ||||||
|  |         for s in servers[d]: | ||||||
|  |             closest.append(s) | ||||||
|  |             if len(closest) == 5 and not all: | ||||||
|  |                 break | ||||||
|  |         else: | ||||||
|  |             continue | ||||||
|  |         break | ||||||
|  | 
 | ||||||
|  |     del servers | ||||||
|  |     return closest | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def getBestServer(servers): | ||||||
|  |     """Perform a speedtest.net latency request to determine which | ||||||
|  |     speedtest.net server has the lowest latency | ||||||
|  |     """ | ||||||
|  | 
 | ||||||
|  |     results = {} | ||||||
|  |     for server in servers: | ||||||
|  |         cum = [] | ||||||
|  |         url = '%s/latency.txt' % os.path.dirname(server['url']) | ||||||
|  |         urlparts = urlparse(url) | ||||||
|  |         for i in range(0, 3): | ||||||
|  |             try: | ||||||
|  |                 if urlparts[0] == 'https': | ||||||
|  |                     h = HTTPSConnection(urlparts[1]) | ||||||
|  |                 else: | ||||||
|  |                     h = HTTPConnection(urlparts[1]) | ||||||
|  |                 start = timeit.default_timer() | ||||||
|  |                 h.request("GET", urlparts[2]) | ||||||
|  |                 r = h.getresponse() | ||||||
|  |                 total = (timeit.default_timer() - start) | ||||||
|  |             except (HTTPError, URLError, socket.error): | ||||||
|  |                 cum.append(3600) | ||||||
|  |                 continue | ||||||
|  |             text = r.read(9) | ||||||
|  |             if int(r.status) == 200 and text == 'test=test'.encode(): | ||||||
|  |                 cum.append(total) | ||||||
|  |             else: | ||||||
|  |                 cum.append(3600) | ||||||
|  |             h.close() | ||||||
|  |         avg = round((sum(cum) / 6) * 1000, 3) | ||||||
|  |         results[avg] = server | ||||||
|  |     fastest = sorted(results.keys())[0] | ||||||
|  |     best = results[fastest] | ||||||
|  |     best['latency'] = fastest | ||||||
|  | 
 | ||||||
|  |     return best | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def ctrl_c(signum, frame): | ||||||
|  |     """Catch Ctrl-C key sequence and set a shutdown_event for our threaded | ||||||
|  |     operations | ||||||
|  |     """ | ||||||
|  | 
 | ||||||
|  |     global shutdown_event | ||||||
|  |     shutdown_event.set() | ||||||
|  |     raise SystemExit('\nCancelling...') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def version(): | ||||||
|  |     """Print the version""" | ||||||
|  | 
 | ||||||
|  |     raise SystemExit(__version__) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def speedtest(): | ||||||
|  |     """Run the full speedtest.net test""" | ||||||
|  | 
 | ||||||
|  |     global shutdown_event, source | ||||||
|  |     shutdown_event = threading.Event() | ||||||
|  | 
 | ||||||
|  |     signal.signal(signal.SIGINT, ctrl_c) | ||||||
|  | 
 | ||||||
|  |     description = ( | ||||||
|  |         'Command line interface for testing internet bandwidth using ' | ||||||
|  |         'speedtest.net.\n' | ||||||
|  |         '------------------------------------------------------------' | ||||||
|  |         '--------------\n' | ||||||
|  |         'https://github.com/sivel/speedtest-cli') | ||||||
|  | 
 | ||||||
|  |     parser = ArgParser(description=description) | ||||||
|  |     # Give optparse.OptionParser an `add_argument` method for | ||||||
|  |     # compatibility with argparse.ArgumentParser | ||||||
|  |     try: | ||||||
|  |         parser.add_argument = parser.add_option | ||||||
|  |     except AttributeError: | ||||||
|  |         pass | ||||||
|  |     parser.add_argument('--bytes', dest='units', action='store_const', | ||||||
|  |                         const=('bytes', 1), default=('bits', 8), | ||||||
|  |                         help='Display values in bytes instead of bits. Does ' | ||||||
|  |                              'not affect the image generated by --share') | ||||||
|  |     parser.add_argument('--share', action='store_true', | ||||||
|  |                         help='Generate and provide a URL to the speedtest.net ' | ||||||
|  |                              'share results image') | ||||||
|  |     parser.add_argument('--simple', action='store_true', | ||||||
|  |                         help='Suppress verbose output, only show basic ' | ||||||
|  |                              'information') | ||||||
|  |     parser.add_argument('--list', action='store_true', | ||||||
|  |                         help='Display a list of speedtest.net servers ' | ||||||
|  |                              'sorted by distance') | ||||||
|  |     parser.add_argument('--server', help='Specify a server ID to test against') | ||||||
|  |     parser.add_argument('--mini', help='URL of the Speedtest Mini server') | ||||||
|  |     parser.add_argument('--source', help='Source IP address to bind to') | ||||||
|  |     parser.add_argument('--version', action='store_true', | ||||||
|  |                         help='Show the version number and exit') | ||||||
|  | 
 | ||||||
|  |     options = parser.parse_args() | ||||||
|  |     if isinstance(options, tuple): | ||||||
|  |         args = options[0] | ||||||
|  |     else: | ||||||
|  |         args = options | ||||||
|  |     del options | ||||||
|  | 
 | ||||||
|  |     # Print the version and exit | ||||||
|  |     if args.version: | ||||||
|  |         version() | ||||||
|  | 
 | ||||||
|  |     # If specified bind to a specific IP address | ||||||
|  |     if args.source: | ||||||
|  |         source = args.source | ||||||
|  |         socket.socket = bound_socket | ||||||
|  | 
 | ||||||
|  |     if not args.simple: | ||||||
|  |         print_('Retrieving speedtest.net configuration...') | ||||||
|  |     try: | ||||||
|  |         config = getConfig() | ||||||
|  |     except URLError: | ||||||
|  |         print_('Cannot retrieve speedtest configuration') | ||||||
|  |         sys.exit(1) | ||||||
|  | 
 | ||||||
|  |     if not args.simple: | ||||||
|  |         print_('Retrieving speedtest.net server list...') | ||||||
|  |     if args.list or args.server: | ||||||
|  |         servers = closestServers(config['client'], True) | ||||||
|  |         if args.list: | ||||||
|  |             serverList = [] | ||||||
|  |             for server in servers: | ||||||
|  |                 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 | ||||||
|  |             sys.exit(0) | ||||||
|  |     else: | ||||||
|  |         servers = closestServers(config['client']) | ||||||
|  | 
 | ||||||
|  |     if not args.simple: | ||||||
|  |         print_('Testing from %(isp)s (%(ip)s)...' % config['client']) | ||||||
|  | 
 | ||||||
|  |     if args.server: | ||||||
|  |         try: | ||||||
|  |             best = getBestServer(filter(lambda x: x['id'] == args.server, | ||||||
|  |                                         servers)) | ||||||
|  |         except IndexError: | ||||||
|  |             print_('Invalid server ID') | ||||||
|  |             sys.exit(1) | ||||||
|  |     elif args.mini: | ||||||
|  |         name, ext = os.path.splitext(args.mini) | ||||||
|  |         if ext: | ||||||
|  |             url = os.path.dirname(args.mini) | ||||||
|  |         else: | ||||||
|  |             url = args.mini | ||||||
|  |         urlparts = urlparse(url) | ||||||
|  |         try: | ||||||
|  |             f = urlopen(args.mini) | ||||||
|  |         except: | ||||||
|  |             print_('Invalid Speedtest Mini URL') | ||||||
|  |             sys.exit(1) | ||||||
|  |         else: | ||||||
|  |             text = f.read() | ||||||
|  |             f.close() | ||||||
|  |         extension = re.findall('upload_extension: "([^"]+)"', text.decode()) | ||||||
|  |         if not extension: | ||||||
|  |             for ext in ['php', 'asp', 'aspx', 'jsp']: | ||||||
|  |                 try: | ||||||
|  |                     f = urlopen('%s/speedtest/upload.%s' % (args.mini, ext)) | ||||||
|  |                 except: | ||||||
|  |                     pass | ||||||
|  |                 else: | ||||||
|  |                     data = f.read().strip() | ||||||
|  |                     if (f.code == 200 and | ||||||
|  |                             len(data.splitlines()) == 1 and | ||||||
|  |                             re.match('size=[0-9]', data)): | ||||||
|  |                         extension = [ext] | ||||||
|  |                         break | ||||||
|  |         if not urlparts or not extension: | ||||||
|  |             print_('Please provide the full URL of your Speedtest Mini server') | ||||||
|  |             sys.exit(1) | ||||||
|  |         servers = [{ | ||||||
|  |             'sponsor': 'Speedtest Mini', | ||||||
|  |             'name': urlparts[1], | ||||||
|  |             'd': 0, | ||||||
|  |             'url': '%s/speedtest/upload.%s' % (url.rstrip('/'), extension[0]), | ||||||
|  |             'latency': 0, | ||||||
|  |             'id': 0 | ||||||
|  |         }] | ||||||
|  |         try: | ||||||
|  |             best = getBestServer(servers) | ||||||
|  |         except: | ||||||
|  |             best = servers[0] | ||||||
|  |     else: | ||||||
|  |         if not args.simple: | ||||||
|  |             print_('Selecting best server based on latency...') | ||||||
|  |         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) | ||||||
|  |     else: | ||||||
|  |         print_('Ping: %(latency)s ms' % best) | ||||||
|  | 
 | ||||||
|  |     sizes = [350, 500, 750, 1000, 1500, 2000, 2500, 3000, 3500, 4000] | ||||||
|  |     urls = [] | ||||||
|  |     for size in sizes: | ||||||
|  |         for i in range(0, 4): | ||||||
|  |             urls.append('%s/random%sx%s.jpg' % | ||||||
|  |                         (os.path.dirname(best['url']), size, size)) | ||||||
|  |     if not args.simple: | ||||||
|  |         print_('Testing download speed', end='') | ||||||
|  |     dlspeed = downloadSpeed(urls, args.simple) | ||||||
|  |     if not args.simple: | ||||||
|  |         print_() | ||||||
|  |     print_('Download: %0.2f M%s/s' % | ||||||
|  |            ((dlspeed / 1000 / 1000) * args.units[1], args.units[0])) | ||||||
|  | 
 | ||||||
|  |     sizesizes = [int(.25 * 1000 * 1000), int(.5 * 1000 * 1000)] | ||||||
|  |     sizes = [] | ||||||
|  |     for size in sizesizes: | ||||||
|  |         for i in range(0, 25): | ||||||
|  |             sizes.append(size) | ||||||
|  |     if not args.simple: | ||||||
|  |         print_('Testing upload speed', end='') | ||||||
|  |     ulspeed = uploadSpeed(best['url'], sizes, args.simple) | ||||||
|  |     if not args.simple: | ||||||
|  |         print_() | ||||||
|  |     print_('Upload: %0.2f M%s/s' % | ||||||
|  |            ((ulspeed / 1000 / 1000) * args.units[1], args.units[0])) | ||||||
|  | 
 | ||||||
|  |     if args.share and args.mini: | ||||||
|  |         print_('Cannot generate a speedtest.net share results image while ' | ||||||
|  |                'testing against a Speedtest Mini server') | ||||||
|  |     elif args.share: | ||||||
|  |         dlspeedk = int(round((dlspeed / 1000) * 8, 0)) | ||||||
|  |         ping = int(round(best['latency'], 0)) | ||||||
|  |         ulspeedk = int(round((ulspeed / 1000) * 8, 0)) | ||||||
|  | 
 | ||||||
|  |         # Build the request to send results back to speedtest.net | ||||||
|  |         # We use a list instead of a dict because the API expects parameters | ||||||
|  |         # in a certain order | ||||||
|  |         apiData = [ | ||||||
|  |             'download=%s' % dlspeedk, | ||||||
|  |             'ping=%s' % ping, | ||||||
|  |             'upload=%s' % ulspeedk, | ||||||
|  |             'promo=', | ||||||
|  |             'startmode=%s' % 'pingselect', | ||||||
|  |             'recommendedserverid=%s' % best['id'], | ||||||
|  |             'accuracy=%s' % 1, | ||||||
|  |             'serverid=%s' % best['id'], | ||||||
|  |             'hash=%s' % md5(('%s-%s-%s-%s' % | ||||||
|  |                              (ping, ulspeedk, dlspeedk, '297aae72')) | ||||||
|  |                             .encode()).hexdigest()] | ||||||
|  | 
 | ||||||
|  |         req = Request('http://www.speedtest.net/api/api.php', | ||||||
|  |                       data='&'.join(apiData).encode()) | ||||||
|  |         req.add_header('Referer', 'http://c.speedtest.net/flash/speedtest.swf') | ||||||
|  |         f = urlopen(req) | ||||||
|  |         response = f.read() | ||||||
|  |         code = f.code | ||||||
|  |         f.close() | ||||||
|  | 
 | ||||||
|  |         if int(code) != 200: | ||||||
|  |             print_('Could not submit results to speedtest.net') | ||||||
|  |             sys.exit(1) | ||||||
|  | 
 | ||||||
|  |         qsargs = parse_qs(response.decode()) | ||||||
|  |         resultid = qsargs.get('resultid') | ||||||
|  |         if not resultid or len(resultid) != 1: | ||||||
|  |             print_('Could not submit results to speedtest.net') | ||||||
|  |             sys.exit(1) | ||||||
|  | 
 | ||||||
|  |         print_('Share results: http://www.speedtest.net/result/%s.png' % | ||||||
|  |                resultid[0]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def main(): | ||||||
|  |     try: | ||||||
|  |         speedtest() | ||||||
|  |     except KeyboardInterrupt: | ||||||
|  |         print_('\nCancelling...') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     main() | ||||||
|  | 
 | ||||||
|  | # vim:ts=4:sw=4:expandtab | ||||||
|  | @ -1,37 +0,0 @@ | ||||||
| #!/usr/bin/env python |  | ||||||
| # -*- coding: utf-8 -*- |  | ||||||
| # Copyright 2018 Matt Martz |  | ||||||
| # All Rights Reserved. |  | ||||||
| # |  | ||||||
| #    Licensed under the Apache License, Version 2.0 (the "License"); you may |  | ||||||
| #    not use this file except in compliance with the License. You may obtain |  | ||||||
| #    a copy of the License at |  | ||||||
| # |  | ||||||
| #         http://www.apache.org/licenses/LICENSE-2.0 |  | ||||||
| # |  | ||||||
| #    Unless required by applicable law or agreed to in writing, software |  | ||||||
| #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |  | ||||||
| #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |  | ||||||
| #    License for the specific language governing permissions and limitations |  | ||||||
| #    under the License. |  | ||||||
| 
 |  | ||||||
| import sys |  | ||||||
| import subprocess |  | ||||||
| 
 |  | ||||||
| cmd = [sys.executable, 'speedtest.py', '--source', '127.0.0.1'] |  | ||||||
| 
 |  | ||||||
| p = subprocess.Popen( |  | ||||||
|     cmd, |  | ||||||
|     stdout=subprocess.PIPE, |  | ||||||
|     stderr=subprocess.PIPE |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| stdout, stderr = p.communicate() |  | ||||||
| 
 |  | ||||||
| if p.returncode != 1: |  | ||||||
|     raise SystemExit('%s did not fail with exit code 1' % ' '.join(cmd)) |  | ||||||
| 
 |  | ||||||
| if 'Invalid argument'.encode() not in stderr: |  | ||||||
|     raise SystemExit( |  | ||||||
|         '"Invalid argument" not found in stderr:\n%s' % stderr.decode() |  | ||||||
|     ) |  | ||||||
							
								
								
									
										12
									
								
								tox.ini
								
								
								
								
							
							
						
						
									
										12
									
								
								tox.ini
								
								
								
								
							|  | @ -4,22 +4,16 @@ skipsdist=true | ||||||
| [testenv] | [testenv] | ||||||
| commands = | commands = | ||||||
|     {envpython} -V |     {envpython} -V | ||||||
|     {envpython} -m compileall speedtest.py |     {envpython} speedtest_cli.py | ||||||
|     {envpython} speedtest.py |  | ||||||
|     {envpython} speedtest.py --source 172.17.0.1 |  | ||||||
|     {envpython} tests/scripts/source.py |  | ||||||
| 
 | 
 | ||||||
| [testenv:flake8] | [testenv:flake8] | ||||||
| basepython=python | basepython=python | ||||||
| deps=flake8 | deps=flake8 | ||||||
| commands = | commands = | ||||||
|     {envpython} -V |     {envpython} -V | ||||||
|     flake8 speedtest.py |     flake8 speedtest_cli.py | ||||||
| 
 | 
 | ||||||
| [testenv:pypy] | [testenv:pypy] | ||||||
| commands = | commands = | ||||||
|     pypy -V |     pypy -V | ||||||
|     pypy -m compileall speedtest.py |     pypy speedtest_cli.py | ||||||
|     pypy speedtest.py |  | ||||||
|     pypy speedtest.py --source 172.17.0.1 |  | ||||||
|     pypy tests/scripts/source.py |  | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue