Using the idle_timeout paramter during connection creation, the
connection is prevented from being closed server-side due to beeing idle
for too long. Internally, while waiting for an API request, if
idle_timeout reaches zero before a new request comes in, a hello request
is send as a keepalive measure.
This rewrites the logic to process requests to follow the I/O task
pattern. This makes it easier to implement things like keepalives as
well as dealing with dropped futures.
While all the examples in RFC 5730 use numbers, there is nothing
normative in section 2.9.2.3 that constrains msgID to be a number.
And if we look at the XML Schema, we find that msgID is defined
to be of type `token`, which seems to be defined as a string
that does not contain line feeds, carriage returns, tabs, leading
or trailing spaces or multiple spaces.
While we might define a more specific token type in the future,
for now sticking with just &str seems reasonable and this also
matches the type for `MessageQueue::id`.
The external client/connection interface expects that to complete
full request/response cycles. However, at await points the stack
could simply be dropped, meaning the connection is left in an
inconsistent state. One relatively likely scenario is that a
transaction might be dropped while waiting for a response from the
server. For example, this might happen if the connection was
initiated by a HTTP request which was canceled/aborted.
There are different failure modes which can result from similar
scenarios depending on during what await point the future was
dropped. Since it's relatively difficult to protect against
these scenarios and some of them might manifest in indirect ways
(for example, a deserialization error might happen because the
incoming response was for a different kind of request), this PR
takes the approach of tracking in the connection whether we're
(supposedly) at a point where the connection is ready to send
another request. If transact() is called while the connection is
not in such a state, the connection will transparently attempt
to reconnect to clean up any erroneous state.