Pitfalls to avoid¶
These are corner cases which should not happen in the common usages, but if they do, what happens may be very confusing. Here are some tips.
When my application exits uncleanly, it still appears to be online to other resources¶
Congratulations! You are using a server with support for
Stream Management. As you might know, aioxmpp
transparently
and automatically uses Stream Management whenever it is available. This means
that aioxmpp.Client
instances must always be
aioxmpp.Client.stop()
-ed properly, so that the stream can be shut down
to prevent it from lingering on the server side. The preferred way to do this
is to use the aioxmpp.Client.connected()
asynchronous context manager:
client = aioxmpp.Client()
with client.connected() as stream:
# stream is the aioxmpp.stream.StanzaStream of the client
# do something
When the context manager is left (either with an exception or normally), the connection is closed cleanly.
If the context manager cannot be used, other means to ensure that
aioxmpp.Client.stop()
is called and the client is given enough time to
shut the connection down cleanly need to be applied. This can be done in the
following manner:
if client.running:
fut = asyncio.Future()
client.on_stopped.connect(fut, client.on_stopped.AUTO_FUTURE)
client.on_failure.connect(fut, client.on_failure.AUTO_FUTURE)
try:
yield from fut
except:
# we are shutting down, ignore any exceptions from on_failure
pass
Ensure that this snippet is executed before the application exits, even in the case that an error occurred.
Note
You may be asking “But why is the Connection Reset by Peer the server must be getting after my application crashed not enough?”. The whole reason for Stream Management is to make it possible for the server to ignore such errors which may very well occur if network connectivity is briefly interrupted (for example when switching between networks, or your ISP has a power failure, or you reboot your modem or something like that). Stream Management allows to resume an uncleanly closed stream up to a certain timeout (as (possibly dynamically) determined by the server). Making errors such as Connection Reset by Peer break such a stream would defeat the purpose of Stream Management.
Note
As of version 0.9, you can disable the resumption capaibility of Stream
Management using the Client.resumption_timeout
attribute. However,
that alone is no guarantee that sessions die quickly; it still depends a
lot on the way in which the network connection got interrupted and whether
or not the server is sending data to the (disconnected) client.
There are other timeouts, such as the ones from TCP, at play here which need to be tweaked properly on the server-side. How to do so is out of scope for aioxmpp.
(If you happen to find a client-side way, e.g. in another XMPP library, to achieve the behaviour of letting the session die quickly in case of a hard disconnect (e.g. a pulled cable), let me know. I’m quite convinced that this is impossible, so I’d like to be proven wrong.)
I am trying to connect to a bare IP and I get a DNS error¶
For example, when trying to connect to 192.168.122.1
, you may see:
Traceback (most recent call last):
File "/home/horazont/aioxmpp/aioxmpp/network.py", line 272, in repeated_query
raise_on_no_answer=False
File "/usr/lib/python3.4/asyncio/futures.py", line 388, in __iter__
yield self # This tells Task to wait for completion.
File "/usr/lib/python3.4/asyncio/tasks.py", line 286, in _wakeup
value = future.result()
File "/usr/lib/python3.4/asyncio/futures.py", line 277, in result
raise self._exception
File "/usr/lib/python3.4/concurrent/futures/thread.py", line 54, in run
result = self.fn(*self.args, **self.kwargs)
File "/home/horazont/.local/lib/python3.4/site-packages/dns/resolver.py", line 1051, in query
raise NXDOMAIN(qnames=qnames_to_try, responses=nxdomain_responses)
dns.resolver.NXDOMAIN: None of DNS query names exist: _xmpp-client._tcp.192.168.122.1., _xmpp-client._tcp.192.168.122.1.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/horazont/aioxmpp/aioxmpp/node.py", line 710, in _on_main_done
task.result()
File "/usr/lib/python3.4/asyncio/futures.py", line 277, in result
raise self._exception
File "/usr/lib/python3.4/asyncio/tasks.py", line 233, in _step
result = coro.throw(exc)
File "/home/horazont/aioxmpp/aioxmpp/node.py", line 868, in _main
yield from self._main_impl()
File "/home/horazont/aioxmpp/aioxmpp/node.py", line 830, in _main_impl
logger=self.logger)
File "/home/horazont/aioxmpp/aioxmpp/node.py", line 337, in connect_xmlstream
logger=logger,
File "/home/horazont/aioxmpp/aioxmpp/node.py", line 142, in discover_connectors
"xmpp-client",
File "/home/horazont/aioxmpp/aioxmpp/network.py", line 318, in lookup_srv
**kwargs)
File "/home/horazont/aioxmpp/aioxmpp/network.py", line 280, in repeated_query
"nameserver error, most likely DNSSEC validation failed",
aioxmpp.network.ValidationError: nameserver error, most likely DNSSEC validation failed
You should be using aioxmpp.Client.override_peer
or an equivalent
mechanism. Note that the exception will still occur if the connection attempt to
the override fails. Bare IPs as target hosts are generally not a good idea.