diff --git a/src/copy.rs b/src/copy.rs index b6fe6859b..f37f4d976 100644 --- a/src/copy.rs +++ b/src/copy.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::proxy; use crate::proxy::ConnectionResult; use crate::proxy::Error::{BackendDisconnected, ClientDisconnected, ReceiveError, SendError}; use bytes::{Buf, Bytes, BytesMut}; @@ -149,18 +150,38 @@ where { let (mut rd, mut wd) = downstream.split_into_buffered_reader(); let (mut ru, mut wu) = upstream.split_into_buffered_reader(); - let downstream_to_upstream = async { - let res = copy_buf(&mut rd, &mut wu, stats, false).await; + let translate_error = |e: io::Error| { + SendError(Box::new(match e.kind() { + io::ErrorKind::NotConnected => BackendDisconnected, + io::ErrorKind::WriteZero => BackendDisconnected, + io::ErrorKind::UnexpectedEof => ClientDisconnected, + _ => e.into(), + })) + }; + let res = ignore_io_errors(copy_buf(&mut rd, &mut wu, stats, false).await) + .map_err(translate_error); trace!(?res, "send"); - ignore_shutdown_errors(shutdown(&mut wu).await)?; + ignore_shutdown_errors(shutdown(&mut wu).await) + .map_err(translate_error) + .map_err(|e| proxy::Error::ShutdownError(Box::new(e)))?; res }; let upstream_to_downstream = async { - let res = copy_buf(&mut ru, &mut wd, stats, true).await; + let translate_error = |e: io::Error| { + ReceiveError(Box::new(match e.kind() { + io::ErrorKind::NotConnected => ClientDisconnected, + io::ErrorKind::WriteZero => ClientDisconnected, + _ => e.into(), + })) + }; + let res = ignore_io_errors(copy_buf(&mut ru, &mut wd, stats, true).await) + .map_err(translate_error); trace!(?res, "receive"); - ignore_shutdown_errors(shutdown(&mut wd).await)?; + ignore_shutdown_errors(shutdown(&mut wd).await) + .map_err(translate_error) + .map_err(|e| proxy::Error::ShutdownError(Box::new(e)))?; res }; @@ -168,23 +189,33 @@ where let (sent, received) = tokio::join!(downstream_to_upstream, upstream_to_downstream); // Convert some error messages to easier to understand - let sent = sent - .map_err(|e| match e.kind() { - io::ErrorKind::NotConnected => BackendDisconnected, - io::ErrorKind::UnexpectedEof => ClientDisconnected, - _ => e.into(), - }) - .map_err(|e| SendError(Box::new(e)))?; - let received = received - .map_err(|e| match e.kind() { - io::ErrorKind::NotConnected => ClientDisconnected, - _ => e.into(), - }) - .map_err(|e| ReceiveError(Box::new(e)))?; + let sent = sent?; + let received = received?; trace!(sent, received, "copy complete"); Ok(()) } +// During copying, we may encounter errors from either side closing their connection. Typically, we +// get a fully graceful shutdown with no errors on either end, but can if one end sends a RST directly, +// or if we have other non-graceful behavior, we may see errors. This is generally ok - a TCP connection +// can close at any time, really. Avoid reporting these as errors, as generally users expect errors to +// occur only when we cannot connect to the backend at all. +fn ignore_io_errors(res: Result) -> Result { + use io::ErrorKind::*; + match &res { + Err(e) => match e.kind() { + NotConnected | UnexpectedEof | ConnectionReset | BrokenPipe => { + trace!(err=%e, "io terminated ungracefully"); + // Returning Default here is very hacky, but the data we are returning isn't critical so its no so bad to lose it. + // Changing this would require refactoring all the interfaces to always return the bytes written even on error. + Ok(Default::default()) + } + _ => res, + }, + _ => res, + } +} + // During shutdown, the other end may have already disconnected. That is fine, they shutdown for us. // Ignore it. fn ignore_shutdown_errors(res: Result<(), io::Error>) -> Result<(), io::Error> { diff --git a/src/proxy.rs b/src/proxy.rs index 945810a5c..b48f95b24 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -299,6 +299,9 @@ pub enum Error { #[error("io error: {0}")] Io(#[from] io::Error), + #[error("while closing connection: {0}")] + ShutdownError(Box), + #[error("destination disconnected before all data was written")] BackendDisconnected, #[error("receive: {0}")] diff --git a/src/proxy/inbound_passthrough.rs b/src/proxy/inbound_passthrough.rs index 2f9307569..5d297c45d 100644 --- a/src/proxy/inbound_passthrough.rs +++ b/src/proxy/inbound_passthrough.rs @@ -99,7 +99,7 @@ impl InboundPassthrough { } .in_current_span(); - assertions::size_between_ref(1500, 2750, &serve_client); + assertions::size_between_ref(1500, 3000, &serve_client); tokio::spawn(serve_client); } Err(e) => {