1use super::compression::{
26 CompressionEncoding, CompressionSettings, SingleMessageCompressionOverride, compress,
27};
28use super::{BufferSettings, DEFAULT_MAX_SEND_MESSAGE_SIZE, EncodeBuf, Encoder, HEADER_SIZE};
29use crate::Status;
30use bytes::{BufMut, Bytes, BytesMut};
31#[cfg(feature = "h2")]
32use h2::{Error as H2Error, Reason as H2Reason};
33use http::HeaderMap;
34use http_body::{Body, Frame};
35use pin_project::pin_project;
36use std::{
37 pin::Pin,
38 task::{Context, Poll, ready},
39};
40use tokio_stream::{Stream, StreamExt, adapters::Fuse};
41use tokio_util::sync::{CancellationToken, WaitForCancellationFutureOwned};
42
43#[pin_project(project = EncodedBytesProj)]
49#[derive(Debug)]
50struct EncodedBytes<T, U> {
51 #[pin]
52 source: Fuse<U>,
53 encoder: T,
54 compression_encoding: Option<CompressionEncoding>,
55 max_message_size: Option<usize>,
56 buf: BytesMut,
57 uncompression_buf: BytesMut,
58 error: Option<Status>,
59}
60
61impl<T: Encoder, U: Stream> EncodedBytes<T, U> {
62 fn new(
63 encoder: T,
64 source: U,
65 compression_encoding: Option<CompressionEncoding>,
66 compression_override: SingleMessageCompressionOverride,
67 max_message_size: Option<usize>,
68 ) -> Self {
69 let buffer_settings = encoder.buffer_settings();
70 let buf = BytesMut::with_capacity(buffer_settings.buffer_size);
71
72 let compression_encoding =
73 if compression_override == SingleMessageCompressionOverride::Disable {
74 None
75 } else {
76 compression_encoding
77 };
78
79 let uncompression_buf = if compression_encoding.is_some() {
80 BytesMut::with_capacity(buffer_settings.buffer_size)
81 } else {
82 BytesMut::new()
83 };
84
85 Self {
86 source: source.fuse(),
87 encoder,
88 compression_encoding,
89 max_message_size,
90 buf,
91 uncompression_buf,
92 error: None,
93 }
94 }
95}
96
97impl<T, U> Stream for EncodedBytes<T, U>
98where
99 T: Encoder<Error = Status>,
100 U: Stream<Item = Result<T::Item, Status>>,
101{
102 type Item = Result<Bytes, Status>;
103
104 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
105 let EncodedBytesProj {
106 mut source,
107 encoder,
108 compression_encoding,
109 max_message_size,
110 buf,
111 uncompression_buf,
112 error,
113 } = self.project();
114 let buffer_settings = encoder.buffer_settings();
115
116 if let Some(status) = error.take() {
117 return Poll::Ready(Some(Err(status)));
118 }
119
120 loop {
121 match source.as_mut().poll_next(cx) {
122 Poll::Pending if buf.is_empty() => {
123 return Poll::Pending;
124 }
125 Poll::Ready(None) if buf.is_empty() => {
126 return Poll::Ready(None);
127 }
128 Poll::Pending | Poll::Ready(None) => {
129 return Poll::Ready(Some(Ok(buf.split_to(buf.len()).freeze())));
130 }
131 Poll::Ready(Some(Ok(item))) => {
132 if let Err(status) = encode_item(
133 encoder,
134 buf,
135 uncompression_buf,
136 *compression_encoding,
137 *max_message_size,
138 buffer_settings,
139 item,
140 ) {
141 return Poll::Ready(Some(Err(status)));
142 }
143
144 if buf.len() >= buffer_settings.yield_threshold {
145 return Poll::Ready(Some(Ok(buf.split_to(buf.len()).freeze())));
146 }
147 }
148 Poll::Ready(Some(Err(status))) => {
149 if buf.is_empty() {
150 return Poll::Ready(Some(Err(status)));
151 }
152 *error = Some(status);
153 return Poll::Ready(Some(Ok(buf.split_to(buf.len()).freeze())));
154 }
155 }
156 }
157 }
158}
159
160fn encode_item<T>(
161 encoder: &mut T,
162 buf: &mut BytesMut,
163 uncompression_buf: &mut BytesMut,
164 compression_encoding: Option<CompressionEncoding>,
165 max_message_size: Option<usize>,
166 buffer_settings: BufferSettings,
167 item: T::Item,
168) -> Result<(), Status>
169where
170 T: Encoder<Error = Status>,
171{
172 let offset = buf.len();
173
174 buf.reserve(HEADER_SIZE);
175 buf.put_slice(&[0u8; HEADER_SIZE]);
176
177 if let Some(encoding) = compression_encoding {
178 uncompression_buf.clear();
179
180 encoder
181 .encode(item, &mut EncodeBuf::new(uncompression_buf))
182 .map_err(|err| Status::internal(format!("Error encoding: {err}")))?;
183
184 let uncompressed_len = uncompression_buf.len();
185
186 compress(
187 CompressionSettings {
188 encoding,
189 buffer_growth_interval: buffer_settings.buffer_size,
190 },
191 uncompression_buf,
192 buf,
193 uncompressed_len,
194 )
195 .map_err(|err| Status::internal(format!("Error compressing: {err}")))?;
196 } else {
197 encoder
198 .encode(item, &mut EncodeBuf::new(buf))
199 .map_err(|err| Status::internal(format!("Error encoding: {err}")))?;
200 }
201
202 finish_encoding(compression_encoding, max_message_size, &mut buf[offset..])
204}
205
206fn finish_encoding(
207 compression_encoding: Option<CompressionEncoding>,
208 max_message_size: Option<usize>,
209 buf: &mut [u8],
210) -> Result<(), Status> {
211 let len = buf.len() - HEADER_SIZE;
212 let limit = max_message_size.unwrap_or(DEFAULT_MAX_SEND_MESSAGE_SIZE);
213 if len > limit {
214 return Err(Status::out_of_range(format!(
215 "Error, encoded message length too large: found {len} bytes, the limit is: {limit} bytes"
216 )));
217 }
218
219 if len > u32::MAX as usize {
220 return Err(Status::resource_exhausted(format!(
221 "Cannot return body with more than 4GB of data but got {len} bytes"
222 )));
223 }
224 {
225 let mut buf = &mut buf[..HEADER_SIZE];
226 buf.put_u8(compression_encoding.is_some() as u8);
227 buf.put_u32(len as u32);
228 }
229
230 Ok(())
231}
232
233#[derive(Debug)]
234enum Role {
235 Client,
236 Server,
237}
238
239#[pin_project]
241#[derive(Debug)]
242pub struct EncodeBody<T, U> {
243 #[pin]
244 inner: EncodedBytes<T, U>,
245 state: EncodeState,
246 #[pin]
247 cancellation_fut: Option<WaitForCancellationFutureOwned>,
248}
249
250#[derive(Debug)]
251struct EncodeState {
252 error: Option<Status>,
253 role: Role,
254 is_end_stream: bool,
255}
256
257impl<T: Encoder, U: Stream> EncodeBody<T, U> {
258 pub fn new_client(
261 encoder: T,
262 source: U,
263 compression_encoding: Option<CompressionEncoding>,
264 max_message_size: Option<usize>,
265 ) -> Self {
266 Self::new_client_with_cancellation(
267 encoder,
268 source,
269 compression_encoding,
270 max_message_size,
271 None,
272 )
273 }
274
275 pub(crate) fn new_client_with_cancellation(
276 encoder: T,
277 source: U,
278 compression_encoding: Option<CompressionEncoding>,
279 max_message_size: Option<usize>,
280 cancellation_token: Option<CancellationToken>,
281 ) -> Self {
282 let cancellation_fut = cancellation_token.map(|c| c.cancelled_owned());
283 Self {
284 inner: EncodedBytes::new(
285 encoder,
286 source,
287 compression_encoding,
288 SingleMessageCompressionOverride::default(),
289 max_message_size,
290 ),
291 state: EncodeState {
292 error: None,
293 role: Role::Client,
294 is_end_stream: false,
295 },
296 cancellation_fut,
297 }
298 }
299
300 pub fn new_server(
303 encoder: T,
304 source: U,
305 compression_encoding: Option<CompressionEncoding>,
306 compression_override: SingleMessageCompressionOverride,
307 max_message_size: Option<usize>,
308 ) -> Self {
309 Self {
310 inner: EncodedBytes::new(
311 encoder,
312 source,
313 compression_encoding,
314 compression_override,
315 max_message_size,
316 ),
317 state: EncodeState {
318 error: None,
319 role: Role::Server,
320 is_end_stream: false,
321 },
322 cancellation_fut: None,
323 }
324 }
325}
326
327impl EncodeState {
328 fn trailers(&mut self) -> Option<Result<HeaderMap, Status>> {
329 match self.role {
330 Role::Client => None,
331 Role::Server => {
332 if self.is_end_stream {
333 return None;
334 }
335
336 self.is_end_stream = true;
337 let status = if let Some(status) = self.error.take() {
338 status
339 } else {
340 Status::ok("")
341 };
342 Some(status.to_header_map())
343 }
344 }
345 }
346}
347
348impl<T, U> Body for EncodeBody<T, U>
349where
350 T: Encoder<Error = Status>,
351 U: Stream<Item = Result<T::Item, Status>>,
352{
353 type Data = Bytes;
354 type Error = Status;
355
356 fn is_end_stream(&self) -> bool {
357 self.state.is_end_stream
358 }
359
360 fn poll_frame(
361 self: Pin<&mut Self>,
362 cx: &mut Context<'_>,
363 ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
364 let self_proj = self.project();
365
366 if let Some(cancellation_fut) = self_proj.cancellation_fut.as_pin_mut()
367 && let Poll::Ready(()) = cancellation_fut.poll(cx)
368 {
369 #[cfg(feature = "h2")]
370 let status = {
371 let mut status = Status::cancelled("client cancelled");
372 status.set_source(std::sync::Arc::new(H2Error::from(H2Reason::CANCEL)));
375 status
376 };
377
378 #[cfg(not(feature = "h2"))]
379 let status = Status::cancelled("client cancelled");
380 return Poll::Ready(Some(Err(status)));
381 }
382
383 match ready!(self_proj.inner.poll_next(cx)) {
384 Some(Ok(d)) => Some(Ok(Frame::data(d))).into(),
385 Some(Err(status)) => match self_proj.state.role {
386 Role::Client => Some(Err(status)).into(),
387 Role::Server => {
388 self_proj.state.is_end_stream = true;
389 Some(Ok(Frame::trailers(status.to_header_map()?))).into()
390 }
391 },
392 None => self_proj
393 .state
394 .trailers()
395 .map(|t| t.map(Frame::trailers))
396 .into(),
397 }
398 }
399}
400
401#[cfg(test)]
402mod tests {
403 use super::*;
404 use std::panic::catch_unwind;
405
406 struct PanickingEncoder;
407
408 impl Encoder for PanickingEncoder {
409 type Item = String;
410 type Error = Status;
411
412 fn encode(
413 &mut self,
414 _item: Self::Item,
415 _dst: &mut EncodeBuf<'_>,
416 ) -> Result<(), Self::Error> {
417 panic!("encoder deliberate panic for testing exception safety");
418 }
419 }
420
421 #[test]
422 fn encode_item_exception_safety_on_panic() {
423 let mut encoder = PanickingEncoder;
424 let mut buf = BytesMut::new();
425 let mut uncompression_buf = BytesMut::new();
426
427 let result = catch_unwind(std::panic::AssertUnwindSafe(|| {
428 encode_item(
429 &mut encoder,
430 &mut buf,
431 &mut uncompression_buf,
432 None,
433 None,
434 BufferSettings::default(),
435 "test".to_string(),
436 )
437 }));
438
439 assert!(result.is_err(), "Encoder panic should unwind correctly.");
440 assert_eq!(
442 buf.len(),
443 HEADER_SIZE,
444 "Buffer length should reflect reserved header bytes."
445 );
446 assert_eq!(
447 &buf[..],
448 &[0u8; HEADER_SIZE],
449 "Buffer must contain only initialized zero bytes."
450 );
451 }
452}