Hello asyncio developers,

the datagram transports provided by asyncio cover only some of what a
datagram socket can do. In particular, I'm missing a way to run
`recvmsg` when something is to be read, rather than `recvfrom` which is
sufficient to satisfy the datagram transport's interface.

Right now, the only way I see to get information about a message's
destination address (RECVPKTINFO) or details of ICMP errors (RECVERR)
out of a received package is reaching into asyncio internals in a way I
should not[1], which is bound to fail sooner or later, and especially
with alternative loop implementations.


Before I rush ahead and propose a concrete extension to
DatagramProtocol, I'd like to explore the options with you. Possible
paths I see out of this are:

* Export something like loop._add_reader as part of the public
  loop interface.

  Such a function would allow users to implement their own transports on
  anything that has a file handler than can become readable using the
  given selector.

  Adding this would require very little effort for loops that support
  that concept (eg. gbulb), and might be impossible for others -- where
  it's probably OK because the underlying platform might not even
  support other required concepts either. (Case in point: Windows
  doesn't implement RECVPKTINFO anyway.)

* Exporting a third line of protocols / transports

  Next to having a DatagramTransport / DatagramProtocol, there could be
  a DatagramRecvmsgTransport / DatagramRecvmsgProtocol with all their
  construction methods of loops.

  A DatagramRecvmsgProtocol would have a datagram_received(data, addr,
  flags, ancdata) callback (with two additional arguments compared to
  DatagramProtocol's), and an additional error_msg_received(addr, flags,
  ancdata) callback where requested error information is passed along.

  The transport would, in addition to .sendto(addr, data), offer a
  .sendmsg(data, ancdata, flags, address) method.

  This would mean considerable effort (and probably duplication) on the
  side of loop implementors, but solves the issue.

* Doing something fancy to make recvmsg an optional feature of datagram
  transports

  Asyncio could generally run `recvmsg` rather than `recvfrom` on
  datagram sockets, fetching the error queue if so indicated by the
  return values of recvmsg. Then it could send the resulting data to a
  datagram_msg_received(data, addr, flags, ancdata) callback on the
  DatagramProtocol, which -- unless overridden by the implementation --
  could fall back to DatagramProtocol's default handler, which would
  then throw away the ancdata and call the established callback of
  DatagramProtocol.

  I'm not sure whether this is possible within the API guarantees given
  in asyncio (eg. it would rely on protocols to inherit
  DatagramProtocol, and if someone already uses their own
  datagram_msg_received method, they'd accidentally overwrite something
  now) -- maybe it can be made to work.

  This would mean a little less effort for third party main loops, but
  would provide a neater interface IMO.


The only alternatives I see to adding any of that are doing as I do now
(hooking into the _add_reader of the individual loops) or moving
networking into a different thread that runs on a loop where I can do
that (which kind of defeats the point of asyncio).

Which of the sketched ways do you think are worth pursuing, or should
this all be handled differently?

Best regards
Christian

[1]: 
https://github.com/chrysn/aiocoap/blob/0d09b2eb92a12a01279883f75f1da32099b7559e/aiocoap/util/asyncio/recvmsg.py#L99

-- 
To use raw power is to make yourself infinitely vulnerable to greater powers.
  -- Bene Gesserit axiom

Attachment: signature.asc
Description: PGP signature

Reply via email to