update from Atlas

This commit is contained in:
Luciano Ramalho
2015-02-27 20:34:12 -03:00
parent 304d628066
commit 39e87de5cd
25 changed files with 2097 additions and 46 deletions

1
futures/countries/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
flags/

View File

@@ -0,0 +1,8 @@
AD AE AF AG AL AM AO AR AT AU AZ BA BB BD BE BF BG BH BI BJ BN BO BR BS BT
BW BY BZ CA CD CF CG CH CI CL CM CN CO CR CU CV CY CZ DE DJ DK DM DZ EC EE
EG ER ES ET FI FJ FM FR GA GB GD GE GH GM GN GQ GR GT GW GY HN HR HT HU ID
IE IL IN IQ IR IS IT JM JO JP KE KG KH KI KM KN KP KR KW KZ LA LB LC LI LK
LR LS LT LU LV LY MA MC MD ME MG MH MK ML MM MN MR MT MU MV MW MX MY MZ NA
NE NG NI NL NO NP NR NZ OM PA PE PG PH PK PL PT PW PY QA RO RS RU RW SA SB
SC SD SE SG SI SK SL SM SN SO SR SS ST SV SY SZ TD TG TH TJ TL TM TN TO TR
TT TV TW TZ UA UG US UY UZ VA VC VE VN VU WS YE ZA ZM ZW

View File

@@ -0,0 +1,148 @@
"""Utilities for second set of flag examples.
"""
import os
import time
import sys
import string
import argparse
from collections import namedtuple
from enum import Enum
Result = namedtuple('Result', 'status data')
Counts = namedtuple('Counts', 'ok not_found error')
Status = Enum('Status', 'ok not_found error')
POP20_CC = ('CN IN US ID BR PK NG BD RU JP '
'MX PH VN ET EG DE IR TR CD FR').split()
DEFAULT_CONCUR_REQ = 1
MAX_CONCUR_REQ = 1
SERVERS = {
'REMOTE': 'http://python.pro.br/fluent/data/flags',
'LOCAL': 'http://localhost:8001/flags',
'DELAY': 'http://localhost:8002/flags',
'ERROR': 'http://localhost:8003/flags',
}
DEFAULT_SERVER = 'LOCAL'
DEST_DIR = 'downloads/'
COUNTRY_CODES_FILE = 'country_codes.txt'
def save_flag(img, filename):
path = os.path.join(DEST_DIR, filename)
with open(path, 'wb') as fp:
fp.write(img)
def initial_report(cc_list, actual_req, server_label):
if len(cc_list) <= 10:
cc_msg = ', '.join(cc_list)
else:
cc_msg = 'from {} to {}'.format(cc_list[0], cc_list[-1])
print('{} site: {}'.format(server_label, SERVERS[server_label]))
msg = 'Searching for {} flag{}: {}'
plural = 's' if len(cc_list) != 1 else ''
print(msg.format(len(cc_list), plural, cc_msg))
plural = 's' if actual_req != 1 else ''
msg = '{} concurrent connection{} will be used.'
print(msg.format(actual_req, plural))
def final_report(cc_list, counts, start_time):
elapsed = time.time() - start_time
print('-' * 20)
msg = '{} flag{} downloaded.'
plural = 's' if counts.ok != 1 else ''
print(msg.format(counts.ok, plural))
if counts.not_found:
print(counts.not_found, 'not found.')
if counts.error:
plural = 's' if counts.error != 1 else ''
print('{} error{}.'.format(counts.error, plural))
print('Elapsed time: {:.2f}s'.format(elapsed))
def expand_cc_args(every_cc, all_cc, cc_args, limit):
codes = set()
A_Z = string.ascii_uppercase
if every_cc:
codes.update(a+b for a in A_Z for b in A_Z)
elif all_cc:
with open(COUNTRY_CODES_FILE) as fp:
text = fp.read()
codes.update(text.split())
else:
for cc in (c.upper() for c in cc_args):
if len(cc) == 1 and cc in A_Z:
codes.update(cc+c for c in A_Z)
elif len(cc) == 2 and all(c in A_Z for c in cc):
codes.add(cc)
else:
msg = 'each CC argument must be A to Z or AA to ZZ.'
raise ValueError('*** Usage error: '+msg)
return sorted(codes)[:limit]
def process_args(default_concur_req):
server_options = ', '.join(sorted(SERVERS))
parser = argparse.ArgumentParser(
description='Download flags for country codes. '
'Default: top 20 countries by population.')
parser.add_argument('cc', metavar='CC', nargs='*',
help='country code or 1st letter (eg. B for BA...BZ)')
parser.add_argument('-a', '--all', action='store_true',
help='get all available flags (AD to ZW)')
parser.add_argument('-e', '--every', action='store_true',
help='get flags for every possible code (AA...ZZ)')
parser.add_argument('-l', '--limit', metavar='N', type=int,
help='limit to N first codes', default=sys.maxsize)
parser.add_argument('-m', '--max_req', metavar='CONCURRENT', type=int,
default=default_concur_req,
help='maximum concurrent requests (default={})'
.format(default_concur_req))
parser.add_argument('-s', '--server', metavar='LABEL',
default=DEFAULT_SERVER,
help='Server to hit; one of {} (default={})'
.format(server_options, DEFAULT_SERVER))
parser.add_argument('-v', '--verbose', action='store_true',
help='output detailed progress info')
args = parser.parse_args()
if args.max_req < 1:
print('*** Usage error: --max_req CONCURRENT must be >= 1')
parser.print_usage()
sys.exit(1)
if args.limit < 1:
print('*** Usage error: --limit N must be >= 1')
parser.print_usage()
sys.exit(1)
args.server = args.server.upper()
if args.server not in SERVERS:
print('*** Usage error: --server LABEL must be one of',
server_options)
parser.print_usage()
sys.exit(1)
try:
cc_list = expand_cc_args(args.every, args.all, args.cc, args.limit)
except ValueError as exc:
print(exc.args[0])
parser.print_usage()
sys.exit(1)
if not cc_list:
cc_list = sorted(POP20_CC)
return args, cc_list
def main(download_many, default_concur_req, max_concur_req):
args, cc_list = process_args(default_concur_req)
actual_req = min(args.max_req, max_concur_req, len(cc_list))
initial_report(cc_list, actual_req, args.server)
base_url = SERVERS[args.server]
t0 = time.time()
counts = download_many(cc_list, base_url, args.verbose, actual_req)
final_report(cc_list, counts, t0)

View File

@@ -0,0 +1,62 @@
"""Download flags of top 20 countries by population
Sequential version
Sample run::
$ python3 flags.py
BD retrieved.
BR retrieved.
CD retrieved.
...
TR retrieved.
US retrieved.
VN retrieved.
20 flags downloaded in 10.16s
"""
import os
import time
import requests
POP20_CC = ('CN IN US ID BR PK NG BD RU JP '
'MX PH VN ET EG DE IR TR CD FR').split()
BASE_URL = 'http://python.pro.br/fluent/data/flags'
DEST_DIR = 'downloads/'
def save_flag(img, filename):
path = os.path.join(DEST_DIR, filename)
with open(path, 'wb') as fp:
fp.write(img)
def get_flag(cc):
url = '{}/{cc}/{cc}.gif'.format(BASE_URL, cc=cc.lower())
res = requests.get(url)
return res.content
def download_many(cc_list):
for cc in sorted(cc_list):
image = get_flag(cc)
print('{} retrieved.'.format(cc))
save_flag(image, cc.lower() + '.gif')
return len(cc_list)
def main(download_many):
t0 = time.time()
count = download_many(POP20_CC)
elapsed = time.time() - t0
msg = '{} flags downloaded in {:.2f}s'
print(msg.format(count, elapsed))
if __name__ == '__main__':
main(download_many)

BIN
futures/countries/flags.zip Normal file

Binary file not shown.

View File

@@ -0,0 +1,112 @@
"""Download flags of top 10 countries by population
asyncio version
Sample run::
$
"""
import asyncio
from collections import namedtuple
from enum import Enum
import aiohttp
from aiohttp import web
import tqdm
from flag_utils import main, save_flag, Counts
# default set low to avoid errors from remote site:
# 503 - Service Temporarily Unavailable
DEFAULT_CONCUR_REQ = 5
MAX_CONCUR_REQ = 1000
TIMEOUT = 120 # seconds
Status = Enum('Status', 'ok not_found error')
Result = namedtuple('Result', 'status data')
@asyncio.coroutine
def get_flag(base_url, cc):
url = '{}/{cc}/{cc}.gif'.format(base_url, cc=cc.lower())
res = yield from aiohttp.request('GET', url)
if res.status == 200:
image = yield from res.read()
return image
elif res.status == 404:
raise web.HTTPNotFound()
else:
raise aiohttp.errors.HttpProcessingError(
code=res.status, message=res.reason, headers=res.headers)
@asyncio.coroutine
def download_one(cc, base_url, semaphore, verbose):
try:
with (yield from semaphore):
image = yield from get_flag(base_url, cc)
except web.HTTPNotFound:
status = Status.not_found
msg = ''
except aiohttp.errors.HttpProcessingError as exc:
status = Status.error
msg = '{} failed: {exc.code} - {exc.message}'
msg = msg.format(cc, exc=exc)
except aiohttp.errors.ClientError as exc:
try:
context = exc.__context__.__class__.__name__
except AttributeError:
# we chain all exceptions, you should get original exception from __cause__
context = '(unknown context)'
msg = '{} failed: {}'.format(cc, context)
status = Status.error
else:
save_flag(image, cc.lower() + '.gif')
status = Status.ok
msg = 'OK'
if verbose and msg:
print(cc, msg)
return Result(status, cc)
@asyncio.coroutine
def downloader_coro(cc_list, base_url, verbose, max_req):
semaphore = asyncio.Semaphore(max_req)
to_do = [download_one(cc, base_url, semaphore, verbose) for cc in cc_list]
results = []
to_do_iter = asyncio.as_completed(to_do)
if not verbose:
to_do_iter = tqdm.tqdm(to_do_iter, total=len(cc_list))
for future in to_do_iter:
result = yield from future
results.append(result)
return results
def download_many(cc_list, base_url, verbose, max_req):
loop = asyncio.get_event_loop()
#loop.set_debug(True)
try:
coro = downloader_coro(cc_list, base_url, verbose, max_req)
done = loop.run_until_complete(coro)
except Exception as exc:
print('*' * 60)
print(exc)
print(vars(exc))
print('*' * 60)
counts = []
for status in Status:
counts.append(len([res for res in done
if res.status == status]))
loop.close()
return Counts(*counts)
if __name__ == '__main__':
main(download_many, DEFAULT_CONCUR_REQ, MAX_CONCUR_REQ)

View File

@@ -0,0 +1,100 @@
"""Download flags of top 10 countries by population
asyncio version
Sample run::
$
"""
import asyncio
from collections import namedtuple
from enum import Enum
import aiohttp
from aiohttp import web
from flag_utils import main, save_flag, Counts
# default set low to avoid errors from remote site:
# 503 - Service Temporarily Unavailable
DEFAULT_CONCUR_REQ = 5
MAX_CONCUR_REQ = 1000
TIMEOUT = 120 # seconds
Status = Enum('Status', 'ok not_found error')
Result = namedtuple('Result', 'status data')
@asyncio.coroutine
def get_flag(base_url, cc):
url = '{}/{cc}/{cc}.gif'.format(base_url, cc=cc.lower())
res = yield from aiohttp.request('GET', url)
if res.status == 200:
image = yield from res.read()
return image
elif res.status == 404:
raise web.HTTPNotFound()
else:
raise aiohttp.errors.HttpProcessingError(
code=res.status, message=res.reason, headers=res.headers)
@asyncio.coroutine
def download_one(cc, base_url, semaphore, verbose):
try:
with (yield from semaphore):
image = yield from get_flag(base_url, cc)
except web.HTTPNotFound:
status = Status.not_found
msg = ''
except aiohttp.errors.HttpProcessingError as exc:
status = Status.error
msg = '{} failed: {exc.code} - {exc.message}'
msg = msg.format(cc, exc=exc)
except aiohttp.errors.ClientError as exc:
try:
context = exc.__context__.__class__.__name__
except AttributeError:
# we chain all exceptions, you should get original exception from __cause__
context = '(unknown context)'
msg = '{} failed: {}'.format(cc, context)
status = Status.error
else:
save_flag(image, cc.lower() + '.gif')
status = Status.ok
msg = 'OK'
if verbose and msg:
print(cc, msg)
return Result(status, cc)
def download_many(cc_list, base_url, verbose, max_req):
semaphore = asyncio.Semaphore(max_req)
to_do = [download_one(cc, base_url, semaphore, verbose) for cc in cc_list]
loop = asyncio.get_event_loop()
#loop.set_debug(True)
try:
done, pending = loop.run_until_complete(asyncio.wait(to_do, timeout=TIMEOUT))
except Exception as exc:
print('*' * 60)
print(exc)
print(vars(exc))
print('*' * 60)
counts = []
for status in Status:
counts.append(len([task for task in done
if task.result().status == status]))
for task in pending:
task.cancel()
loop.close()
return Counts(*counts)
if __name__ == '__main__':
main(download_many, DEFAULT_CONCUR_REQ, MAX_CONCUR_REQ)

View File

@@ -0,0 +1,74 @@
"""Download flags of countries (with error handling).
Sequential version
Sample run::
$
"""
import requests
import tqdm
from flag_utils import main, save_flag, Counts, Status, Result
DEFAULT_CONCUR_REQ = 1
MAX_CONCUR_REQ = 1
def get_flag(base_url, cc):
url = '{}/{cc}/{cc}.gif'.format(base_url, cc=cc.lower())
res = requests.get(url)
if res.status_code != 200:
res.raise_for_status()
return res.content
def download_one(cc, base_url, verbose=False):
try:
image = get_flag(base_url, cc)
except requests.exceptions.HTTPError as exc:
res = exc.response
if res.status_code == 404:
status = Status.not_found
msg = ''
else:
status = Status.error
msg = 'error {res.status_code} - {res.reason}'
msg = msg.format(res=exc.response)
except requests.exceptions.ConnectionError as exc:
status = Status.error
msg = 'failed: {}'.format(cc, exc.args)
else:
save_flag(image, cc.lower() + '.gif')
status = Status.ok
msg = 'OK'
if verbose and msg:
print(cc, msg)
return Result(status, cc)
def download_many(cc_list, base_url, verbose, max_req):
counts = [0, 0, 0]
if not verbose:
cc_iter = tqdm.tqdm(sorted(cc_list))
else:
cc_iter = sorted(cc_list)
for cc in cc_iter:
try:
res = download_one(cc, base_url, verbose)
except Exception as exc:
msg = 'Unexpected exception for {}: {!r}'
print(msg.format(cc, exc))
else:
counts[res.status.value-1] += 1
return Counts(*counts)
if __name__ == '__main__':
main(download_many, DEFAULT_CONCUR_REQ, MAX_CONCUR_REQ)

View File

@@ -0,0 +1,42 @@
"""Download flags of top 10 countries by population
ThreadPool version
Sample run::
$
"""
from concurrent import futures
import tqdm
from flag_utils import main, Counts
from flags2_sequential import get_flag, download_one
DEFAULT_CONCUR_REQ = 30
MAX_CONCUR_REQ = 1000
def download_many(cc_list, base_url, verbose, concur_req):
with futures.ThreadPoolExecutor(concur_req) as executor:
to_do = [executor.submit(download_one, cc, base_url, verbose)
for cc in sorted(cc_list)]
counts = [0, 0, 0]
to_do_iter = futures.as_completed(to_do)
if not verbose:
to_do_iter = tqdm.tqdm(to_do_iter, total=len(cc_list))
for future in to_do_iter:
try:
res = future.result()
except Exception as exc:
print('*** Unexpected exception:', exc)
else:
counts[res.status.value-1] += 1
return Counts(*counts)
if __name__ == '__main__':
main(download_many, DEFAULT_CONCUR_REQ, MAX_CONCUR_REQ)

View File

@@ -0,0 +1,122 @@
"""Download flags of top 10 countries by population
asyncio version
Sample run::
$ python3 pop10_asyncio1.py
CN retrieved.
US retrieved.
BR retrieved.
NG retrieved.
PK retrieved.
RU retrieved.
ID retrieved.
IN retrieved.
BD retrieved.
JP retrieved.
10 flags downloaded in 0.45s
"""
import asyncio
from collections import namedtuple
from enum import Enum
import aiohttp
from aiohttp import web
from flags_sequential2 import BASE_URL
from flags_sequential2 import save_flag, main, Counts
MAX_TASKS = 100 if 'localhost' in BASE_URL else 5
TIMEOUT = 120 # seconds
Status = Enum('Status', 'ok not_found error')
Result = namedtuple('Result', 'status data')
@asyncio.coroutine
def http_get(url):
res = yield from aiohttp.request('GET', url)
if res.status == 200:
ctype = res.headers.get('Content-type', '').lower()
if 'json' in ctype or url.endswith('json'):
data = yield from res.json()
else:
data = yield from res.read()
return data
elif res.status == 404:
raise web.HTTPNotFound()
else:
raise aiohttp.errors.HttpProcessingError(
code=res.status, message=res.reason, headers=res.headers)
@asyncio.coroutine
def get_flag(cc):
url = '{}/{cc}/{cc}.gif'.format(BASE_URL, cc=cc.lower())
return (yield from http_get(url))
@asyncio.coroutine
def get_country(cc):
url = '{}/{cc}/metadata.json'.format(BASE_URL, cc=cc.lower())
metadata = yield from http_get(url)
return metadata['country']
@asyncio.coroutine
def download_one(cc, semaphore):
try:
with (yield from semaphore):
image = yield from get_flag(cc)
with (yield from semaphore):
country = yield from get_country(cc)
except web.HTTPNotFound:
status = Status.not_found
except aiohttp.errors.HttpProcessingError as exc:
msg = '{} failed: {exc.code} - {exc.message}'
print(msg.format(cc, exc=exc))
status = Status.error
except aiohttp.errors.ClientResponseError as exc:
try:
context = exc.__context__.__class__.__name__
except AttributeError:
context = '(unknown context)'
msg = '{} failed: {}'
print(msg.format(cc, context))
status = Status.error
else:
print('{} retrieved.'.format(cc.upper()))
country = country.replace(' ', '_')
save_flag(image, '{}-{}.gif'.format(country, cc))
status = Status.ok
return Result(status, cc)
def download_many(cc_list):
semaphore = asyncio.Semaphore(MAX_TASKS)
to_do = [download_one(cc, semaphore) for cc in cc_list]
loop = asyncio.get_event_loop()
#loop.set_debug(True)
try:
done, pending = loop.run_until_complete(asyncio.wait(to_do, timeout=TIMEOUT))
except Exception as exc:
print('*' * 60)
print(exc)
print(vars(exc))
print('*' * 60)
counts = []
for status in Status:
counts.append(len([task for task in done
if task.result().status == status]))
for task in pending:
task.cancel()
loop.close()
return Counts(*counts)
if __name__ == '__main__':
main(download_many)

View File

@@ -0,0 +1,77 @@
"""Download flags of top 10 countries by population
ThreadPool version
Sample run::
$ python3 pop10_threadpool1.py
BR retrieved.
PK retrieved.
BD retrieved.
JP retrieved.
CN retrieved.
IN retrieved.
RU retrieved.
NG retrieved.
US retrieved.
ID retrieved.
10 flags downloaded in 0.63s
"""
from concurrent import futures
from collections import namedtuple
from enum import Enum
import requests
from flags_sequential2 import BASE_URL
from flags_sequential2 import save_flag, get_flag, main, Counts
MAX_WORKERS = 200
Status = Enum('Status', 'ok not_found error')
Result = namedtuple('Result', 'status data')
def get_country(cc):
url = '{}/{cc}/metadata.json'.format(BASE_URL, cc=cc.lower())
res = requests.get(url)
if res.status_code != 200:
res.raise_for_status()
return res.json()['country']
def download_one(cc):
try:
image = get_flag(cc)
country = get_country(cc)
except requests.exceptions.HTTPError as exc:
res = exc.response
if res.status_code == 404:
status = Status.not_found
else:
msg = '{} failed: {res.status_code} - {res.reason}'
print(msg.format(cc, res=exc.response))
status = Status.error
else:
print('{} retrieved.'.format(cc))
country = country.replace(' ', '_')
save_flag(image, '{}-{}.gif'.format(country, cc))
status = Status.ok
return Result(status, cc)
def download_many(cc_list):
workers = min(len(cc_list), MAX_WORKERS)
with futures.ThreadPoolExecutor(workers) as executor:
res = executor.map(download_one, sorted(cc_list))
res = list(res)
counts = []
for status in Status:
counts.append(len([r for r in res if r.status == status]))
return Counts(*counts)
if __name__ == '__main__':
main(download_many)

View File

@@ -0,0 +1,45 @@
"""Download flags of top 20 countries by population
asyncio+aiottp version
Sample run::
$ python3 flags_asyncio0.py
EG retrieved.
BD retrieved.
JP retrieved.
...
CD retrieved.
PH retrieved.
ET retrieved.
20 flags downloaded in 1.05s
"""
import asyncio
import aiohttp
from flags import BASE_URL, save_flag, main
@asyncio.coroutine
def download_one(cc):
url = '{}/{cc}/{cc}.gif'.format(BASE_URL, cc=cc.lower())
res = yield from aiohttp.request('GET', url)
image = yield from res.read()
print('{} retrieved.'.format(cc))
save_flag(image, cc.lower() + '.gif')
return cc
def download_many(cc_list):
loop = asyncio.get_event_loop()
to_do = [download_one(cc) for cc in cc_list]
res, _ = loop.run_until_complete(asyncio.wait(to_do))
loop.close()
return len(res)
if __name__ == '__main__':
main(download_many)

View File

@@ -0,0 +1,51 @@
"""Download flags of top 20 countries by population
asyncio+aiottp version
Sample run::
$ python3 flags_asyncio.py
NG retrieved.
FR retrieved.
IN retrieved.
...
EG retrieved.
DE retrieved.
IR retrieved.
20 flags downloaded in 1.08s
"""
import asyncio
import aiohttp
from flags import BASE_URL, save_flag, main
@asyncio.coroutine
def get_flag(cc):
url = '{}/{cc}/{cc}.gif'.format(BASE_URL, cc=cc.lower())
res = yield from aiohttp.request('GET', url)
image = yield from res.read()
return image
@asyncio.coroutine
def download_one(cc):
image = yield from get_flag(cc)
print('{} retrieved.'.format(cc))
save_flag(image, cc.lower() + '.gif')
return cc
def download_many(cc_list):
loop = asyncio.get_event_loop()
to_do = [download_one(cc) for cc in cc_list]
res, _ = loop.run_until_complete(asyncio.wait(to_do))
loop.close()
return len(res)
if __name__ == '__main__':
main(download_many)

View File

@@ -0,0 +1,61 @@
"""Download flags of top 20 countries by population
asyncio+aiottp version
Sample run::
$ python3 flags_asyncio.py
NG retrieved.
FR retrieved.
IN retrieved.
...
EG retrieved.
DE retrieved.
IR retrieved.
20 flags downloaded in 1.08s
"""
import asyncio
import aiohttp
from flags import BASE_URL, save_flag, main
@asyncio.coroutine
def get_flag(cc):
url = '{}/{cc}/{cc}.gif'.format(BASE_URL, cc=cc.lower())
res = yield from aiohttp.request('GET', url)
image = yield from res.read()
return image
@asyncio.coroutine
def download_one(cc):
image = yield from get_flag(cc)
print('{} retrieved.'.format(cc))
save_flag(image, cc.lower() + '.gif')
return cc
@asyncio.coroutine
def downloader_coro(cc_list):
to_do = [download_one(cc) for cc in cc_list]
results = []
for future in asyncio.as_completed(to_do):
print(future)
result = yield from future
results.append(result)
return results
def download_many(cc_list):
loop = asyncio.get_event_loop()
results = loop.run_until_complete(downloader_coro(cc_list))
loop.close()
return len(results)
if __name__ == '__main__':
main(download_many)

View File

@@ -0,0 +1,42 @@
"""Download flags of top 20 countries by population
ThreadPool version
Sample run::
$ python3 flags_threadpool.py
BD retrieved.
EG retrieved.
CN retrieved.
...
PH retrieved.
US retrieved.
IR retrieved.
20 flags downloaded in 0.93s
"""
from concurrent import futures
from flags import save_flag, get_flag, main
MAX_WORKERS = 100
def download_one(cc):
image = get_flag(cc)
print('{} retrieved.'.format(cc.upper()))
save_flag(image, cc.lower() + '.gif')
return cc
def download_many(cc_list):
workers = min(len(cc_list), MAX_WORKERS)
with futures.ThreadPoolExecutor(workers) as executor:
res = executor.map(download_one, sorted(cc_list))
return len(list(res))
if __name__ == '__main__':
main(download_many)

View File

@@ -0,0 +1,4 @@
#!/bin/bash
vaurien --protocol http --backend localhost:8001 \
--proxy localhost:8002 \
--behavior 100:delay --behavior-delay-sleep .5

View File

@@ -0,0 +1,4 @@
#!/bin/bash
vaurien --protocol http --backend localhost:8001 \
--proxy localhost:8003 \
--behavior 25:error,50:delay --behavior-delay-sleep .5