This commit is contained in:
hominator 2014-10-07 12:48:59 +00:00
commit ba142a723c
2 changed files with 158 additions and 33 deletions

View File

@ -89,6 +89,7 @@ Usage
--server SERVER Specify a server ID to test against --server SERVER Specify a server ID to test against
--mini MINI URL of the Speedtest Mini server --mini MINI URL of the Speedtest Mini server
--source SOURCE Source IP address to bind to --source SOURCE Source IP address to bind to
--timeout TIMEOUT HTTP timeout in seconds. Default 10
--version Show the version number and exit --version Show the version number and exit
Inconsistency Inconsistency

View File

@ -18,6 +18,7 @@
__version__ = '0.3.1' __version__ = '0.3.1'
# Some global variables we use # Some global variables we use
user_agent = 'speedtest-cli/%s' % __version__
source = None source = None
shutdown_event = None shutdown_event = None
@ -165,6 +166,17 @@ def distance(origin, destination):
return d return d
def build_request(url, data=None, headers={}):
"""Build a urllib2 request object
This function automatically adds a User-Agent header to all requests
"""
headers['User-Agent'] = user_agent
return Request(url, data=data, headers=headers)
class FileGetter(threading.Thread): class FileGetter(threading.Thread):
"""Thread class for retrieving a URL""" """Thread class for retrieving a URL"""
@ -178,7 +190,8 @@ class FileGetter(threading.Thread):
self.result = [0] self.result = [0]
try: try:
if (timeit.default_timer() - self.starttime) <= 10: if (timeit.default_timer() - self.starttime) <= 10:
f = urlopen(self.url) request = build_request(self.url)
f = urlopen(request)
while 1 and not shutdown_event.isSet(): while 1 and not shutdown_event.isSet():
self.result.append(len(f.read(10240))) self.result.append(len(f.read(10240)))
if self.result[-1] == 0: if self.result[-1] == 0:
@ -242,7 +255,8 @@ class FilePutter(threading.Thread):
try: try:
if ((timeit.default_timer() - self.starttime) <= 10 and if ((timeit.default_timer() - self.starttime) <= 10 and
not shutdown_event.isSet()): not shutdown_event.isSet()):
f = urlopen(self.url, self.data) request = build_request(self.url, data=self.data)
f = urlopen(request)
f.read(11) f.read(11)
f.close() f.close()
self.result = len(self.data) self.result = len(self.data)
@ -305,7 +319,8 @@ def getConfig():
we are interested in we are interested in
""" """
uh = urlopen('http://www.speedtest.net/speedtest-config.php') request = build_request('http://www.speedtest.net/speedtest-config.php')
uh = urlopen(request)
configxml = [] configxml = []
while 1: while 1:
configxml.append(uh.read(10240)) configxml.append(uh.read(10240))
@ -337,12 +352,15 @@ def getConfig():
return config return config
def closestServers(client, all=False): def closestServers(client, numServers=5):
"""Determine the 5 closest speedtest.net servers based on geographic """Determine the closest speedtest.net servers based on geographic
distance distance. The default number of servers to return is 5. If the
number of servers is specified as 0 then all servers are returned.
""" """
uh = urlopen('http://www.speedtest.net/speedtest-servers-static.php') url = 'http://www.speedtest.net/speedtest-servers-static.php'
request = build_request(url)
uh = urlopen(request)
serversxml = [] serversxml = []
while 1: while 1:
serversxml.append(uh.read(10240)) serversxml.append(uh.read(10240))
@ -382,7 +400,7 @@ def closestServers(client, all=False):
for d in sorted(servers.keys()): for d in sorted(servers.keys()):
for s in servers[d]: for s in servers[d]:
closest.append(s) closest.append(s)
if len(closest) == 5 and not all: if len(closest) == numServers and numServers != 0:
break break
else: else:
continue continue
@ -408,8 +426,9 @@ def getBestServer(servers):
h = HTTPSConnection(urlparts[1]) h = HTTPSConnection(urlparts[1])
else: else:
h = HTTPConnection(urlparts[1]) h = HTTPConnection(urlparts[1])
headers = {'User-Agent': user_agent}
start = timeit.default_timer() start = timeit.default_timer()
h.request("GET", urlparts[2]) h.request("GET", urlparts[2], headers=headers)
r = h.getresponse() r = h.getresponse()
total = (timeit.default_timer() - start) total = (timeit.default_timer() - start)
except (HTTPError, URLError, socket.error): except (HTTPError, URLError, socket.error):
@ -469,7 +488,7 @@ def speedtest():
except AttributeError: except AttributeError:
pass pass
parser.add_argument('--bytes', dest='units', action='store_const', parser.add_argument('--bytes', dest='units', action='store_const',
const=('bytes', 1), default=('bits', 8), const=('byte', 1), default=('bit', 8),
help='Display values in bytes instead of bits. Does ' help='Display values in bytes instead of bits. Does '
'not affect the image generated by --share') 'not affect the image generated by --share')
parser.add_argument('--share', action='store_true', parser.add_argument('--share', action='store_true',
@ -478,12 +497,29 @@ def speedtest():
parser.add_argument('--simple', action='store_true', parser.add_argument('--simple', action='store_true',
help='Suppress verbose output, only show basic ' help='Suppress verbose output, only show basic '
'information') 'information')
parser.add_argument('--showconfig', action='store_true',
help='Display the client configuration')
parser.add_argument('--saveconfig', help='Specify a file to save the '
'speedtest.net configuration')
parser.add_argument('--loadconfig', help='Specify a file to load the '
'speedtest.net configuration')
parser.add_argument('--list', action='store_true', parser.add_argument('--list', action='store_true',
help='Display a list of speedtest.net servers ' help='Display a list of speedtest.net servers '
'sorted by distance') 'sorted by distance')
parser.add_argument('--listservers', action='store_true',
help='Display a list of speedtest.net servers '
'sorted by distance. Synonym for --list')
parser.add_argument('--saveservers', help='Specify a file to save the '
'speedtest.net servers list')
parser.add_argument('--loadservers', help='Specify a file of speedtest.net'
' servers to use instead of downloading the list')
parser.add_argument('--server', help='Specify a server ID to test against') parser.add_argument('--server', help='Specify a server ID to test against')
parser.add_argument('--saveresults', help='Specify a file to save the '
'speedtest.net results')
parser.add_argument('--mini', help='URL of the Speedtest Mini server') 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('--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('--version', action='store_true', parser.add_argument('--version', action='store_true',
help='Show the version number and exit') help='Show the version number and exit')
@ -498,24 +534,62 @@ def speedtest():
if args.version: if args.version:
version() version()
socket.setdefaulttimeout(args.timeout)
# If specified bind to a specific IP address # If specified bind to a specific IP address
if args.source: if args.source:
source = args.source source = args.source
socket.socket = bound_socket socket.socket = bound_socket
if not args.simple: # Retrieve speedtest configuration
print_('Retrieving speedtest.net configuration...') if args.loadconfig is None:
try: if not args.simple:
config = getConfig() print_('Retrieving speedtest.net configuration...')
except URLError: try:
print_('Cannot retrieve speedtest configuration') config = getConfig()
sys.exit(1) except URLError:
print_('Cannot retrieve speedtest configuration')
sys.exit(1)
if not args.simple: if args.saveconfig is not None:
print_('Retrieving speedtest.net server list...') if not args.simple:
if args.list or args.server: print_('Saving speedtest.net configuration to %s'
servers = closestServers(config['client'], True) % args.saveconfig)
if args.list: try:
configfile = open(args.saveconfig, 'w')
except:
print_('Unable to open configuration file %s'
% args.saveconfig)
sys.exit(1)
configfile.write(str(config))
configfile.close()
else:
if not args.simple:
print_('Loading speedtest.net configuration from %s'
% args.loadconfig)
try:
confFile = open(args.loadconfig, 'r')
except:
print_('Unable to open configuration file %s' % args.loadconfig)
sys.exit(1)
config = eval(confFile.read())
confFile.close()
if args.showconfig:
print_('Speedtest.net configuration:')
for configitem in config:
print_(' %s: %s' % (configitem, config[configitem]))
sys.exit(0)
# Retrieve speedtest server list
if (args.list or args.listservers or args.saveservers is not None
or (args.server and args.loadservers is None)):
if not args.simple:
print_('Retrieving speedtest.net server list...')
servers = closestServers(config['client'], 0) # return all servers
# Display the server list and exit
if args.list or args.listservers:
serverList = [] serverList = []
for server in servers: for server in servers:
line = ('%(id)4s) %(sponsor)s (%(name)s, %(country)s) ' line = ('%(id)4s) %(sponsor)s (%(name)s, %(country)s) '
@ -532,8 +606,38 @@ def speedtest():
except IOError: except IOError:
pass pass
sys.exit(0) sys.exit(0)
elif args.saveservers is not None:
if not args.simple:
print_('Saving speedtest.net server list to %s...'
% args.saveservers)
try:
svrFile = open(args.saveservers, 'w')
except:
print_('Unable to open server file')
sys.exit(1)
svrFile.write(str(servers))
svrFile.close()
print_('Done')
sys.exit(0)
elif args.loadservers is not None:
if not args.simple:
print_('Loading speedtest.net server list from %s'
% args.loadservers)
try:
svrFile = open(args.loadservers, 'r')
except:
print_('Unable to open server file')
sys.exit(1)
allServers = eval(svrFile.read())
svrFile.close()
if len(allServers) > 10:
servers = allServers[:5]
else:
servers = allServers
else: else:
servers = closestServers(config['client']) if not args.simple:
print_('Retrieving speedtest.net server list...')
servers = closestServers(config['client']) # return closest 5 servers
if not args.simple: if not args.simple:
print_('Testing from %(isp)s (%(ip)s)...' % config['client']) print_('Testing from %(isp)s (%(ip)s)...' % config['client'])
@ -553,7 +657,8 @@ def speedtest():
url = args.mini url = args.mini
urlparts = urlparse(url) urlparts = urlparse(url)
try: try:
f = urlopen(args.mini) request = build_request(args.mini)
f = urlopen(request)
except: except:
print_('Invalid Speedtest Mini URL') print_('Invalid Speedtest Mini URL')
sys.exit(1) sys.exit(1)
@ -564,7 +669,9 @@ def speedtest():
if not extension: if not extension:
for ext in ['php', 'asp', 'aspx', 'jsp']: for ext in ['php', 'asp', 'aspx', 'jsp']:
try: try:
f = urlopen('%s/speedtest/upload.%s' % (args.mini, ext)) request = build_request('%s/speedtest/upload.%s' %
(args.mini, ext))
f = urlopen(request)
except: except:
pass pass
else: else:
@ -617,10 +724,11 @@ def speedtest():
if not args.simple: if not args.simple:
print_('Testing download speed', end='') print_('Testing download speed', end='')
dlspeed = downloadSpeed(urls, args.simple) dlspeed = downloadSpeed(urls, args.simple)
dlspdstr = '%0.2f M%s/s' % ((dlspeed / 1000 / 1000)
* args.units[1], args.units[0])
if not args.simple: if not args.simple:
print_() print_()
print_('Download: %0.2f M%s/s' % print_('Download: %s' % dlspdstr)
((dlspeed / 1000 / 1000) * args.units[1], args.units[0]))
sizesizes = [int(.25 * 1000 * 1000), int(.5 * 1000 * 1000)] sizesizes = [int(.25 * 1000 * 1000), int(.5 * 1000 * 1000)]
sizes = [] sizes = []
@ -630,10 +738,25 @@ def speedtest():
if not args.simple: if not args.simple:
print_('Testing upload speed', end='') print_('Testing upload speed', end='')
ulspeed = uploadSpeed(best['url'], sizes, args.simple) ulspeed = uploadSpeed(best['url'], sizes, args.simple)
ulspdstr = '%0.2f M%s/s' % ((ulspeed / 1000 / 1000)
* args.units[1], args.units[0])
if not args.simple: if not args.simple:
print_() print_()
print_('Upload: %0.2f M%s/s' % print_('Upload: %s' % ulspdstr)
((ulspeed / 1000 / 1000) * args.units[1], args.units[0]))
# Save test results
if args.saveresults is not None:
if not args.simple:
print_('Saving test results to %s' % args.saveresults)
try:
resfile = open(args.saveresults, 'a')
except:
print_('Unable to open results file')
sys.exit(1)
resfile.write(('%(id)s,%(sponsor)s,%(name)s,%(country)s,'
'%(latency)s ms,' % best) + ('%s,%s\n'
% (dlspdstr, ulspdstr)))
resfile.close()
if args.share and args.mini: if args.share and args.mini:
print_('Cannot generate a speedtest.net share results image while ' print_('Cannot generate a speedtest.net share results image while '
@ -659,10 +782,11 @@ def speedtest():
(ping, ulspeedk, dlspeedk, '297aae72')) (ping, ulspeedk, dlspeedk, '297aae72'))
.encode()).hexdigest()] .encode()).hexdigest()]
req = Request('http://www.speedtest.net/api/api.php', headers = {'Referer': 'http://c.speedtest.net/flash/speedtest.swf'}
data='&'.join(apiData).encode()) request = build_request('http://www.speedtest.net/api/api.php',
req.add_header('Referer', 'http://c.speedtest.net/flash/speedtest.swf') data='&'.join(apiData).encode(),
f = urlopen(req) headers=headers)
f = urlopen(request)
response = f.read() response = f.read()
code = f.code code = f.code
f.close() f.close()