Commit 6cbe39ea authored by Alexandre's avatar Alexandre
Browse files

Perform a first query to initiate curl multi

When using curl multi, we populate the multi with handles and then
call multi_perform which leads to curl establishing the multi
connection and sending tranfers once it is done. This looks
satfisfying enough if we don't look at the time values stored by curl
in each handles. In this case it appears that all the transfer time
are set to the time the curl multi was started meaning that all
transfers keep track of the connection time, this is normal for the
first transfer but unnecessary for all the following ones.

Therefore by sending a first transfer asking for the root NS, it
allows to open the connection without populating the resolver cache.
And thus keeps the curl timers more accurate.
parent 9e64c19d
......@@ -132,7 +132,7 @@ When repeating tests, you can add a delay between tests, with `--delay
N` or `-d N`, where N is the (possibly fractional) number of seconds
to wait.
### Mulitstreams
### Multistreams
When using Homer with DoH, the option `--multistreams` can be used
to specify that you want to take advantage of the HTTP/2 streams
......@@ -173,6 +173,9 @@ and [CURLINFO_PRETRANSFER_TIME](https://curl.haxx.se/libcurl/c/curl_easy_getinfo
Total elapsed time: 0.07 seconds (9.83 ms/request)
```
Finally note that when using multistreams an extra DNS request is sent
to initiate the connection. This request asks for the root NS.
### Monitoring with Nagios, Icinga, or similar software
......
......@@ -645,6 +645,7 @@ class ConnectionDoH(Connection):
if self.multistreams:
self.multi = self.create_multi()
self.all_handles = []
self.finished = { 'http': {} }
else:
self.curl_handle = create_handle(self)
......@@ -653,6 +654,23 @@ class ConnectionDoH(Connection):
multi.setopt(pycurl.M_MAX_HOST_CONNECTIONS, 1)
return multi
def init_multi(self):
# perform a first query alone
# to establish the connection and hence avoid starting
# the transfer of all the other queries simultaneously
# query the root NS because this should not impact the resover cache
if verbose:
print("Establishing multistreams connection...")
request = create_request('.', qtype='NS', dot=False)
try:
self.do_test(request, synchronous=False)
except (OpenSSL.SSL.Error, CustomException) as e:
ok = False
error(e)
self.perform_multi(silent=True)
self.all_handles = []
self.finished = { 'http': {} }
def end(self):
if not self.multistreams:
self.curl_handle.close()
......@@ -667,7 +685,7 @@ class ConnectionDoH(Connection):
h.close()
self.multi.remove_handle(h)
def perform_multi(self):
def perform_multi(self, silent=False):
while 1:
ret, num_handles = self.multi.perform()
if ret != pycurl.E_CALL_MULTI_PERFORM:
......@@ -681,13 +699,13 @@ class ConnectionDoH(Connection):
if not sync:
n, handle_pass, handle_fail = self.multi.info_read()
for handle in handle_pass:
self.read_result_handle(handle)
self.read_result_handle(handle, silent=silent)
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)
self.read_result_handle(handle, silent=silent)
def send(self, handle):
handle.buffer = io.BytesIO()
......@@ -718,16 +736,16 @@ class ConnectionDoH(Connection):
self.send(handle)
self.receive(handle)
def read_result_handle(self, handle):
def read_result_handle(self, handle, silent=False):
self.receive(handle)
handle.request.check_response()
if show_time:
if not silent and show_time:
self.print_time(handle)
try:
self.finished['http'][handle.request.rcode] += 1
except KeyError:
self.finished['http'][handle.request.rcode] = 1
if display_results:
if not silent and display_results:
print("Return code %s (%.2f ms):" % (handle.request.rcode,
(handle.time - handle.pretime) * 1000))
print(f"{handle.request.response}\n")
......@@ -1263,7 +1281,7 @@ for connectTo in ip_set:
input = open(ifile)
if not check:
if multistreams:
conn.finished = { 'http': {} }
conn.init_multi()
for i in range (0, tests):
if tests > 1 and (verbose or display_results):
print("\nTest %i" % i)
......@@ -1285,11 +1303,6 @@ 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)
......
Markdown is supported
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