fix(http2): improve I/O errors emitted by H2Upgraded (#2598)
When a `CONNECT` over HTTP2 has been established, and the user tries to write data right when the peer closes the stream, it will no longer return as a "user error". The reset code is checked, and converted into an appropriate `io::ErrorKind`.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
use bytes::{Buf, Bytes};
|
use bytes::{Buf, Bytes};
|
||||||
use h2::{RecvStream, SendStream};
|
use h2::{Reason, RecvStream, SendStream};
|
||||||
use http::header::{HeaderName, CONNECTION, TE, TRAILER, TRANSFER_ENCODING, UPGRADE};
|
use http::header::{HeaderName, CONNECTION, TE, TRAILER, TRANSFER_ENCODING, UPGRADE};
|
||||||
use http::HeaderMap;
|
use http::HeaderMap;
|
||||||
use pin_project_lite::pin_project;
|
use pin_project_lite::pin_project;
|
||||||
@@ -313,7 +313,11 @@ where
|
|||||||
break buf;
|
break buf;
|
||||||
}
|
}
|
||||||
Some(Err(e)) => {
|
Some(Err(e)) => {
|
||||||
return Poll::Ready(Err(h2_to_io_error(e)));
|
return Poll::Ready(match e.reason() {
|
||||||
|
Some(Reason::NO_ERROR) | Some(Reason::CANCEL) => Ok(()),
|
||||||
|
Some(Reason::STREAM_CLOSED) => Err(io::ErrorKind::BrokenPipe.into()),
|
||||||
|
_ => Err(h2_to_io_error(e)),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -335,21 +339,36 @@ where
|
|||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
buf: &[u8],
|
buf: &[u8],
|
||||||
) -> Poll<Result<usize, io::Error>> {
|
) -> Poll<Result<usize, io::Error>> {
|
||||||
if let Poll::Ready(reset) = self.send_stream.poll_reset(cx) {
|
|
||||||
return Poll::Ready(Err(h2_to_io_error(match reset {
|
|
||||||
Ok(reason) => reason.into(),
|
|
||||||
Err(e) => e,
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
if buf.is_empty() {
|
if buf.is_empty() {
|
||||||
return Poll::Ready(Ok(0));
|
return Poll::Ready(Ok(0));
|
||||||
}
|
}
|
||||||
self.send_stream.reserve_capacity(buf.len());
|
self.send_stream.reserve_capacity(buf.len());
|
||||||
Poll::Ready(match ready!(self.send_stream.poll_capacity(cx)) {
|
|
||||||
None => Ok(0),
|
// We ignore all errors returned by `poll_capacity` and `write`, as we
|
||||||
Some(Ok(cnt)) => self.send_stream.write(&buf[..cnt], false).map(|()| cnt),
|
// will get the correct from `poll_reset` anyway.
|
||||||
Some(Err(e)) => Err(h2_to_io_error(e)),
|
let cnt = match ready!(self.send_stream.poll_capacity(cx)) {
|
||||||
})
|
None => Some(0),
|
||||||
|
Some(Ok(cnt)) => self
|
||||||
|
.send_stream
|
||||||
|
.write(&buf[..cnt], false)
|
||||||
|
.ok()
|
||||||
|
.map(|()| cnt),
|
||||||
|
Some(Err(_)) => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(cnt) = cnt {
|
||||||
|
return Poll::Ready(Ok(cnt));
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Ready(Err(h2_to_io_error(
|
||||||
|
match ready!(self.send_stream.poll_reset(cx)) {
|
||||||
|
Ok(Reason::NO_ERROR) | Ok(Reason::CANCEL) | Ok(Reason::STREAM_CLOSED) => {
|
||||||
|
return Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()))
|
||||||
|
}
|
||||||
|
Ok(reason) => reason.into(),
|
||||||
|
Err(e) => e,
|
||||||
|
},
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
||||||
|
|||||||
Reference in New Issue
Block a user