diff --git a/src/endpoint/context.rs b/src/endpoint/context.rs index 0c43e36..9b65e64 100644 --- a/src/endpoint/context.rs +++ b/src/endpoint/context.rs @@ -757,6 +757,7 @@ impl ContextInternal { let out = inner_lock.vm.take_output(); if let TakeOutputResult::Buffer(b) = out + && !b.is_empty() && !inner_lock.write.send(b) { // Nothing we can do anymore here diff --git a/src/endpoint/futures/async_result_poll.rs b/src/endpoint/futures/async_result_poll.rs index e5baa37..80faf7d 100644 --- a/src/endpoint/futures/async_result_poll.rs +++ b/src/endpoint/futures/async_result_poll.rs @@ -59,7 +59,11 @@ impl Future for VmAsyncResultPollFuture { let out = inner_lock.vm.take_output(); match out { TakeOutputResult::Buffer(b) => { - if !inner_lock.write.send(b) { + // Skip empty buffers: take_output returns an empty buffer when there's + // nothing to send (e.g. while replaying completed entries). Sending it + // would emit an empty HTTP/2 DATA frame per replayed await, which some + // proxies (e.g. Envoy) reject when consecutive. See sdk-rust#114. + if !b.is_empty() && !inner_lock.write.send(b) { return Poll::Ready(Err(ErrorInner::Suspended)); } } diff --git a/src/endpoint/futures/select_poll.rs b/src/endpoint/futures/select_poll.rs index 7ec52a2..ea35b9a 100644 --- a/src/endpoint/futures/select_poll.rs +++ b/src/endpoint/futures/select_poll.rs @@ -60,7 +60,11 @@ impl Future for VmSelectAsyncResultPollFuture { let out = inner_lock.vm.take_output(); match out { TakeOutputResult::Buffer(b) => { - if !inner_lock.write.send(b) { + // Skip empty buffers: take_output returns an empty buffer when there's + // nothing to send (e.g. while replaying completed entries). Sending it + // would emit an empty HTTP/2 DATA frame per replayed await, which some + // proxies (e.g. Envoy) reject when consecutive. See sdk-rust#114. + if !b.is_empty() && !inner_lock.write.send(b) { return Poll::Ready(Err(ErrorInner::Suspended)); } }