[multistream-select] Require remaining negotiation data to be flushed. (#1781)

* Require remaining negotiation data to be flushed.

There appears to still be an edge-case whereby the
`remaining` data to send w.r.t. protocol negotiation to send
is successfully written before a `poll_read` on a `Negotiated` stream,
but where the subsequent `poll_flush()` is pending.
Now `remaining` is empty and the next `poll_read()`
will go straight to reading from the underlying
I/O stream, despite the flush not having happened
yet, which can lead to a form of deadlock during
protocol negotiation.

Rather than complicating the existing code further in
order to accommodate for this case, it seems preferable
to simplify the code by giving up on this optimisation
that only affects the last negotiation protocol message
sent by the "listener". So we give up on the ability
to combine data sent by the "listener" immediately
after protocol negotiation together with the final
negotiation frame in the same transport-level frame/packet.

* Update changelog.

* Add missing comma.
This commit is contained in:
Roman Borschel
2020-10-01 12:29:51 +02:00
committed by GitHub
parent c19344dee7
commit 8cec457b5e
6 changed files with 68 additions and 190 deletions

View File

@ -289,23 +289,16 @@ impl<R> MessageIO<R> {
MessageReader { inner: self.inner.into_reader() }
}
/// Drops the [`MessageIO`] resource, yielding the underlying I/O stream
/// together with the remaining write buffer containing the protocol
/// negotiation frame data that has not yet been written to the I/O stream.
///
/// The returned remaining write buffer may be prepended to follow-up
/// protocol data to send with a single `write`. Either way, if non-empty,
/// the write buffer _must_ eventually be written to the I/O stream
/// _before_ any follow-up data, in order for protocol negotiation to
/// complete cleanly.
/// Drops the [`MessageIO`] resource, yielding the underlying I/O stream.
///
/// # Panics
///
/// Panics if the read buffer is not empty, meaning that an incoming
/// protocol negotiation frame has been partially read. The read buffer
/// is guaranteed to be empty whenever `MessageIO::poll` returned
/// a message.
pub fn into_inner(self) -> (R, BytesMut) {
/// Panics if the read buffer or write buffer is not empty, meaning that an incoming
/// protocol negotiation frame has been partially read or an outgoing frame
/// has not yet been flushed. The read buffer is guaranteed to be empty whenever
/// `MessageIO::poll` returned a message. The write buffer is guaranteed to be empty
/// when the sink has been flushed.
pub fn into_inner(self) -> R {
self.inner.into_inner()
}
}
@ -365,19 +358,14 @@ impl<R> MessageReader<R> {
/// together with the remaining write buffer containing the protocol
/// negotiation frame data that has not yet been written to the I/O stream.
///
/// The returned remaining write buffer may be prepended to follow-up
/// protocol data to send with a single `write`. Either way, if non-empty,
/// the write buffer _must_ eventually be written to the I/O stream
/// _before_ any follow-up data, in order for protocol negotiation to
/// complete cleanly.
///
/// # Panics
///
/// Panics if the read buffer is not empty, meaning that an incoming
/// protocol negotiation frame has been partially read. The read buffer
/// is guaranteed to be empty whenever `MessageReader::poll` returned
/// a message.
pub fn into_inner(self) -> (R, BytesMut) {
/// Panics if the read buffer or write buffer is not empty, meaning that either
/// an incoming protocol negotiation frame has been partially read, or an
/// outgoing frame has not yet been flushed. The read buffer is guaranteed to
/// be empty whenever `MessageReader::poll` returned a message. The write
/// buffer is guaranteed to be empty whenever the sink has been flushed.
pub fn into_inner(self) -> R {
self.inner.into_inner()
}
}