Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Appending the redirect urls stats. #17

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
258 changes: 147 additions & 111 deletions httpstat.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ def get(self, default=None):
"time_starttransfer": %{time_starttransfer},
"time_total": %{time_total},
"speed_download": %{speed_download},
"speed_upload": %{speed_upload}
"speed_upload": %{speed_upload},
"http_code": %{http_code},
"redirect_url": "%{redirect_url}"
}"""

https_template = """
Expand Down Expand Up @@ -112,6 +114,7 @@ def quit(s, code=0):
def print_help():
help = """
Usage: httpstat URL [CURL_OPTIONS]
httpstat -L URL [CURL_OPTIONS]
httpstat -h | --help
httpstat --version

Expand All @@ -122,6 +125,7 @@ def print_help():
CURL_OPTIONS any curl supported options, except for -w -D -o -S -s,
which are already used internally.
-h --help show this screen.
-L Follow redirects
--version show version.

Environments:
Expand Down Expand Up @@ -177,15 +181,20 @@ def main():
)

# get url
url = args[0]
if url in ['-h', '--help']:
arg_value, redirect_loop_print_stats = args[0], False
if arg_value in ['-h', '--help']:
print_help()
quit(None, 0)
elif url == '--version':
elif arg_value == '--version':
print('httpstat {}'.format(__version__))
quit(None, 0)

curl_args = args[1:]
elif arg_value == '-L':
url = args[1]
redirect_loop_print_stats=True
curl_args = args[2:]
else:
url = args[0]
curl_args = args[1:]

# check curl args
exclude_options = [
Expand All @@ -210,120 +219,146 @@ def main():
cmd_env.update(
LC_ALL='C',
)

cmd_core = [curl_bin, '-w', curl_format, '-D', headerf.name, '-o', bodyf.name, '-s', '-S']
cmd = cmd_core + curl_args + [url]
lg.debug('cmd: %s', cmd)
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=cmd_env)
out, err = p.communicate()
if PY3:
out, err = out.decode(), err.decode()

# print stderr
if p.returncode == 0:
print(grayscale[16](err))
else:
_cmd = list(cmd)
_cmd[2] = '<output-format>'
_cmd[4] = '<tempfile>'
_cmd[6] = '<tempfile>'
print('> {}'.format(' '.join(_cmd)))
quit(yellow('curl error: {}'.format(err)), p.returncode)

# parse output
try:
d = json.loads(out)
except ValueError as e:
print(yellow('Could not decode json: {}'.format(e)))
print('curl result:', p.returncode, grayscale[16](out), grayscale[16](err))
quit(None, 1)
for k in d:
if k.startswith('time_'):
d[k] = int(d[k] * 1000)

# calculate ranges
d.update(
range_dns=d['time_namelookup'],
range_connection=d['time_connect'] - d['time_namelookup'],
range_ssl=d['time_pretransfer'] - d['time_connect'],
range_server=d['time_starttransfer'] - d['time_pretransfer'],
range_transfer=d['time_total'] - d['time_starttransfer'],
)

# print header & body summary
with open(headerf.name, 'r') as f:
headers = f.read().strip()
# remove header file
lg.debug('rm header file %s', headerf.name)
os.remove(headerf.name)

for loop, line in enumerate(headers.split('\n')):
if loop == 0:
p1, p2 = tuple(line.split('/'))
print(green(p1) + grayscale[14]('/') + cyan(p2))

def run_command(cmd):
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=cmd_env)
out, err = p.communicate()
returncode = p.returncode

if PY3:
out, err = out.decode(), err.decode()

return out, err, returncode

def print_stats(out, err, returncode):
# print stderr
if returncode == 0:
print(grayscale[16](err))
else:
pos = line.find(':')
print(grayscale[14](line[:pos + 1]) + cyan(line[pos + 1:]))

print()

# body
if show_body:
body_limit = 1024
with open(bodyf.name, 'r') as f:
body = f.read().strip()
body_len = len(body)

if body_len > body_limit:
print(body[:body_limit] + cyan('...'))
print()
s = '{} is truncated ({} out of {})'.format(green('Body'), body_limit, body_len)
if save_body:
s += ', stored in: {}'.format(bodyf.name)
print(s)
_cmd = list(cmd)
_cmd[2] = '<output-format>'
_cmd[4] = '<tempfile>'
_cmd[6] = '<tempfile>'
print('> {}'.format(' '.join(_cmd)))
quit(yellow('curl error: {}'.format(err)), returncode)

# parse output

try:
d = json.loads(out)
except ValueError as e:
print(yellow('Could not decode json: {}'.format(e)))
print('curl result:', returncode, grayscale[16](out), grayscale[16](err))
quit(None, 1)
for k in d:
if k.startswith('time_'):
d[k] = int(d[k] * 1000)

# calculate ranges
d.update(
range_dns=d['time_namelookup'],
range_connection=d['time_connect'] - d['time_namelookup'],
range_ssl=d['time_pretransfer'] - d['time_connect'],
range_server=d['time_starttransfer'] - d['time_pretransfer'],
range_transfer=d['time_total'] - d['time_starttransfer'],
)
# print header & body summary
with open(headerf.name, 'r') as f:
headers = f.read().strip()
# remove header file
lg.debug('rm header file %s', headerf.name)
os.remove(headerf.name)

for loop, line in enumerate(headers.split('\n')):
if loop == 0:
p1, p2 = tuple(line.split('/'))
print(green(p1) + grayscale[14]('/') + cyan(p2))
else:
pos = line.find(':')
print(grayscale[14](line[:pos + 1]) + cyan(line[pos + 1:]))

print()

# body
if show_body:
body_limit = 1024
with open(bodyf.name, 'r') as f:
body = f.read().strip()
body_len = len(body)

if body_len > body_limit:
print(body[:body_limit] + cyan('...'))
print()
s = '{} is truncated ({} out of {})'.format(green('Body'), body_limit, body_len)
if save_body:
s += ', stored in: {}'.format(bodyf.name)
print(s)
else:
print(body)
else:
print(body)
else:
if save_body:
print('{} stored in: {}'.format(green('Body'), bodyf.name))
if save_body:
print('{} stored in: {}'.format(green('Body'), bodyf.name))

# remove body file
if not save_body:
lg.debug('rm body file %s', bodyf.name)
os.remove(bodyf.name)
# remove body file
if not save_body:
lg.debug('rm body file %s', bodyf.name)
os.remove(bodyf.name)

# print stat
if url.startswith('https://'):
template = https_template
# print stat
if url.startswith('https://'):
template = https_template
else:
template = http_template

# colorize template first line
tpl_parts = template.split('\n')
tpl_parts[0] = grayscale[16](tpl_parts[0])
template = '\n'.join(tpl_parts)

def fmta(s):
return cyan('{:^7}'.format(str(s) + 'ms'))

def fmtb(s):
return cyan('{:<7}'.format(str(s) + 'ms'))

stat = template.format(
# a
a0000=fmta(d['range_dns']),
a0001=fmta(d['range_connection']),
a0002=fmta(d['range_ssl']),
a0003=fmta(d['range_server']),
a0004=fmta(d['range_transfer']),
# b
b0000=fmtb(d['time_namelookup']),
b0001=fmtb(d['time_connect']),
b0002=fmtb(d['time_pretransfer']),
b0003=fmtb(d['time_starttransfer']),
b0004=fmtb(d['time_total']),
)
print()
print(stat)

def run_command_and_print_stats(cmd):
out, err, returncode = run_command(cmd)
print_stats(out, err, returncode)
return out

while redirect_loop_print_stats:
out = run_command_and_print_stats(cmd)
out = json.loads(out)
if int(out['http_code']) in range(201,400):
url = out['redirect_url']
cmd = cmd_core + curl_args + [url]
continue
else:
break
else:
template = http_template

# colorize template first line
tpl_parts = template.split('\n')
tpl_parts[0] = grayscale[16](tpl_parts[0])
template = '\n'.join(tpl_parts)

def fmta(s):
return cyan('{:^7}'.format(str(s) + 'ms'))

def fmtb(s):
return cyan('{:<7}'.format(str(s) + 'ms'))

stat = template.format(
# a
a0000=fmta(d['range_dns']),
a0001=fmta(d['range_connection']),
a0002=fmta(d['range_ssl']),
a0003=fmta(d['range_server']),
a0004=fmta(d['range_transfer']),
# b
b0000=fmtb(d['time_namelookup']),
b0001=fmtb(d['time_connect']),
b0002=fmtb(d['time_pretransfer']),
b0003=fmtb(d['time_starttransfer']),
b0004=fmtb(d['time_total']),
)
print()
print(stat)
run_command_and_print_stats(cmd)

# speed, originally bytes per second
if show_speed:
Expand All @@ -333,3 +368,4 @@ def fmtb(s):

if __name__ == '__main__':
main()