Commit bbeaed93 authored by alex's avatar alex
Browse files

Merge branch 'streams-time' into 'streams'

Calculate time for each requests.
Closes #26

See merge request bortzmeyer/homer!13
parents 977866fd dd4a0655
......@@ -146,12 +146,29 @@ In order to focus on the time per request, you can suppress the
output by using the option `--silent`.
This option only works with `--multistreams`.
Finally, two modes are available. By default each response is read,
Two modes are available. By default each response is read,
checked and displayed as soon as it is received.
You can use `--sync` to delay this processing after the last transfer.
In that case the DNS responses are displayed in the same order as they
were sent.
An option `--time` allows to display the time taken by each transfer.
This is based on [libcurl time values](https://curl.haxx.se/libcurl/c/curl_easy_getinfo.html#TIMES)
[CURLINFO_TOTAL_TIME](https://curl.haxx.se/libcurl/c/curl_easy_getinfo.html#CURLINFOTOTALTIME)
and [CURLINFO_PRETRANSFER_TIME](https://curl.haxx.se/libcurl/c/curl_easy_getinfo.html#CURLINFOPRETRANSFERTIME)
```
% ./homer.py --multistreams --file input_file --repeat 5 --silent --time https://doh.powerdns.org
0 36.165 ms 44.773 ms 8.608 ms
1 0.142 ms 8.580 ms 8.438 ms
3 0.095 ms 9.223 ms 9.128 ms
2 0.103 ms 10.282 ms 10.179 ms
4 0.104 ms 10.068 ms 9.964 ms
Total elapsed time: 0.07 seconds (9.26 ms/request)
OK
```
### Monitoring with Nagios, Icinga, or similar software
......
......@@ -55,6 +55,7 @@ connectTo = None
multistreams = False
sync = False
silent = False
show_time = False
check = False
mandatory_level = None
check_additional = True
......@@ -548,6 +549,10 @@ class ConnectionDoH(Connection):
self.read_result_handle(handle)
if ret != pycurl.E_CALL_MULTI_PERFORM:
break
if not sync:
n, handle_pass, handle_fail = self.multi.info_read()
for handle in handle_pass:
self.read_result_handle(handle)
def send(self, handle):
handle.buffer = io.BytesIO()
......@@ -562,6 +567,8 @@ class ConnectionDoH(Connection):
body = handle.buffer.getvalue()
body_size = len(body)
http_code = handle.getinfo(pycurl.RESPONSE_CODE)
handle.time = handle.getinfo(pycurl.TOTAL_TIME)
handle.pretime = handle.getinfo(pycurl.PRETRANSFER_TIME)
try:
content_type = handle.getinfo(pycurl.CONTENT_TYPE)
except TypeError: # This is the exception we get if there is no Content-Type: (for intance in response to HEAD requests)
......@@ -579,8 +586,15 @@ class ConnectionDoH(Connection):
def read_result_handle(self, handle):
self.receive(handle)
handle.request.check_response()
if show_time:
print(f'{handle.request.i:3d}', end=' ')
print(f'{handle.pretime * 1000:8.3f} ms', end=' ')
print(f'{handle.time * 1000:8.3f} ms', end=' ')
print(f'{(handle.time - handle.pretime) * 1000:8.3f} ms')
if not silent:
print("Return code %s:\n%s\n" % (handle.request.rcode, handle.request.response))
print("Return code %s (%.2f ms):" % (handle.request.rcode,
(handle.time - handle.pretime) * 1000))
print(f"{handle.request.response}\n")
handle.close()
self.multi.remove_handle(handle)
......@@ -824,7 +838,7 @@ if not monitoring:
optlist, args = getopt.getopt (sys.argv[1:], "hvPkeV:r:f:d:t46",
["help", "verbose", "dot", "head",
"insecure", "POST", "vhost=", "multistreams",
"sync", "silent",
"sync", "silent", "time",
"dnssec", "noedns", "ecs", "repeat=", "file=", "delay=",
"key=", "nosni",
"v4only", "v6only",
......@@ -851,6 +865,8 @@ if not monitoring:
sync = True
elif option == "--silent":
silent = True
elif option == "--time":
show_time = True
elif option == "--dnssec":
dnssec = True
elif option == "--nosni":
......@@ -910,6 +926,9 @@ if not monitoring:
if silent and not multistreams:
usage("--silent cannot be used without --multistreams")
sys.exit(1)
if show_time and not multistreams:
usage("--time cannot be used without --multistreams")
sys.exit(1)
if not edns and not no_ecs:
usage("ECS requires EDNS")
sys.exit(1)
......@@ -1075,6 +1094,7 @@ for connectTo in ip_set:
if ifile is not None:
name, rtype = get_next_domain(input)
request = create_request(name, qtype=rtype, use_edns=edns, want_dnssec=dnssec, dot=dot)
request.i = i
if not dot:
request.head = head
request.post = post
......@@ -1088,23 +1108,32 @@ for connectTo in ip_set:
if not print_result(conn, request):
ok = False
if tests > 1 and i == 0:
if multistreams: # do the first query alone
# to establish the connection and hence avoid starting
# the transfer of all the other queries
conn.perform_multi()
conn.first_handle = conn.all_handles[0]
start2 = time.time()
if delay is not None:
time.sleep(delay)
if multistreams:
conn.perform_multi()
if sync:
print()
conn.read_results()
else:
ok = run_check(conn) and ok # need to run run_check first
stop = time.time()
if tests > 1:
extra = ", %.2f ms/request if we ignore the first one" % ((stop-start2)*1000/(tests-1))
if tests > 1 and not multistreams:
extra = " , %.2f ms/request if we ignore the first one" % ((stop-start2)*1000/(tests-1))
else:
extra = ""
if not monitoring and (not check or verbose):
print("\nTotal elapsed time: %.2f seconds (%.2f ms/request %s)" % (stop-start, (stop-start)*1000/tests, extra))
time_tot = stop - start
if multistreams:
time_per_request = sum(handle.time - handle.pretime for handle in conn.all_handles) / tests * 1000
else:
time_per_request = time_tot / tests * 1000
print("\nTotal elapsed time: %.2f seconds (%.2f ms/request%s)" % (time_tot, time_per_request, extra))
if ifile is not None:
input.close()
conn.end()
......
......@@ -1041,28 +1041,36 @@ tests:
stderr: ''
partstdout: 'NOERR'
# This test might fail
# this is due to the fact that it will check that libcurl
# uses HTTP/2 streams or sometimes the connection is
# available very quickly making unnecessary to use
# different streams
- exe : './homer.py'
name: '[doh] Multistreams with 2 requests'
name: '[doh] Multistreams with 3 requests (can fail)'
markers:
- 'doh'
- 'streams'
- 'xfail'
args:
- '-v'
- '--multistreams'
- '--file'
- 'input_file'
- '--repeat'
- '2'
- 'https://doh.bortzmeyer.fr'
- '3'
- 'https://doh.powerdns.org'
retcode: 0
partstderr: 'Multiplexed connection found'
partstdout: 'Return code 200'
# same as previous test
- exe : './homer.py'
name: '[doh] Multistreams with 5 requests'
markers:
- 'doh'
- 'streams'
- 'xfail'
args:
- '-v'
- '--multistreams'
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment