I've closed one of the two remaining open DoS protection features.
We pace all responses to their requests, and treat jrpc batched requests as individual cycles, reading only one at a time, making it inherently impossible to DoS the node by request flooding. One request, response completed, next request. We also gate the query of a next item on the completed send of its predecessor (e.g. block requests), flattening the peak cost of any single request down to its individual parts - even if those parts are a single logical message response. The caller is forced to read each portion before we query the next.
However, with full duplex we can send notifications, which are "responses" without a corresponding request. Even though we control the size of each response, we can't control the rate at which they are read. So notifications can backlog indefinitely, just du to slow reading. Some notifications can be dropped, but in the supported interfaces this really only applies to the bitcoind zeromq interface, since zmq adds a sequence identifier to broadcasts, allowing messages to be dropped when the high water mark (HWM) is reached. For others, correctness dictates that the channel must be dropped.
This implies some rational measure of backlog, which isn't always as straightforward as it may seem. For performance reasons we don't serialize objects to a buffer in order to send them. Sometimes the serialization happens directly from the source to the socket, as chunks are read by the recipient. And the backlogged closures have their own cost, which can be more than the message itself. But all backlog now includes size approximations sufficient for the DoS guard. So while a full-duplex channel is at its HWN it either starts dropping messages (zeromq) or it drops the channel (p2p, tcp-jrpc, ws).