mysten_network/request_log/
body.rs1use std::{
5 borrow::Cow,
6 pin::Pin,
7 task::{Context, Poll, ready},
8};
9
10use base64::Engine as _;
11use bytes::Bytes;
12use http_body::Body;
13use pin_project_lite::pin_project;
14use tracing::{Span, trace};
15
16use super::TARGET;
17
18const FRAME_PREFIX_LEN: usize = 5;
21
22const MAX_CAPTURED_MESSAGES: u32 = 1;
29
30pin_project! {
31 pub struct RequestLogBody<B> {
36 #[pin]
37 inner: B,
38 capture: Option<CaptureState>,
39 }
40}
41
42pub(crate) struct CaptureState {
45 span: Span,
46 path: String,
47 max_captured_message_size: usize,
48 parser: FrameParser,
49 message_index: u32,
50}
51
52enum FrameParser {
56 Prefix {
58 buf: [u8; FRAME_PREFIX_LEN],
59 filled: usize,
60 },
61 Message { remaining: usize, buf: Vec<u8> },
63 Skip { remaining: usize },
65 Stopped,
68}
69
70enum FrameEvent {
72 Message(Vec<u8>),
74 Skipped {
76 len: usize,
77 reason: Cow<'static, str>,
78 },
79}
80
81impl<B> RequestLogBody<B> {
82 pub(crate) fn new(inner: B, capture: Option<CaptureState>) -> Self {
83 Self { inner, capture }
84 }
85}
86
87impl CaptureState {
88 pub(crate) fn new(span: Span, path: String, max_captured_message_size: usize) -> Self {
89 Self {
90 span,
91 path,
92 max_captured_message_size,
93 parser: FrameParser::new(),
94 message_index: 0,
95 }
96 }
97
98 fn observe(&mut self, mut chunk: &[u8]) {
100 while !chunk.is_empty() {
101 if self.message_index >= MAX_CAPTURED_MESSAGES
108 && matches!(self.parser, FrameParser::Prefix { .. })
109 {
110 let _span = self.span.enter();
111 trace!(
112 target: TARGET,
113 method = %self.path,
114 message_count = self.message_index,
115 "Captured request (capture stopped: too many messages)",
116 );
117 self.parser = FrameParser::Stopped;
118 return;
119 }
120
121 let (consumed, completed) = self.parser.feed(chunk, self.max_captured_message_size);
122 chunk = &chunk[consumed..];
123
124 let Some(event) = completed else {
125 continue;
126 };
127
128 let _span = self.span.enter();
131
132 match event {
133 FrameEvent::Message(message) => trace!(
134 target: TARGET,
135 method = %self.path,
136 message_index = self.message_index,
137 payload = %base64::engine::general_purpose::STANDARD.encode(&message),
138 "Captured request",
139 ),
140 FrameEvent::Skipped { len, reason } => trace!(
141 target: TARGET,
142 method = %self.path,
143 message_index = self.message_index,
144 message_len = len,
145 skipped = %reason,
146 "Captured request (payload skipped)",
147 ),
148 }
149 self.message_index += 1;
150 }
151 }
152}
153
154impl FrameParser {
155 fn new() -> Self {
156 Self::Prefix {
157 buf: [0; FRAME_PREFIX_LEN],
158 filled: 0,
159 }
160 }
161
162 fn feed(&mut self, chunk: &[u8], max_message_size: usize) -> (usize, Option<FrameEvent>) {
166 match self {
167 Self::Prefix { buf, filled } => {
168 let n = (FRAME_PREFIX_LEN - *filled).min(chunk.len());
169 buf[*filled..*filled + n].copy_from_slice(&chunk[..n]);
170 *filled += n;
171 if *filled < FRAME_PREFIX_LEN {
172 return (n, None);
173 }
174
175 let flag = buf[0];
176 let len = u32::from_be_bytes(buf[1..].try_into().unwrap()) as usize;
177 if flag > 1 {
178 *self = Self::Stopped;
179 return (
180 n,
181 Some(FrameEvent::Skipped {
182 len,
183 reason: format!("invalid flag {flag}").into(),
184 }),
185 );
186 }
187 if flag == 1 {
188 *self = Self::skip(len);
189 return (
190 n,
191 Some(FrameEvent::Skipped {
192 len,
193 reason: "compressed message".into(),
194 }),
195 );
196 }
197 if len > max_message_size {
198 *self = Self::skip(len);
199 return (
200 n,
201 Some(FrameEvent::Skipped {
202 len,
203 reason: "message too large".into(),
204 }),
205 );
206 }
207 if len == 0 {
208 *self = Self::new();
209 return (n, Some(FrameEvent::Message(Vec::new())));
210 }
211 *self = Self::Message {
212 remaining: len,
213 buf: Vec::with_capacity((chunk.len() - n).min(len)),
218 };
219 (n, None)
220 }
221 Self::Message { remaining, buf } => {
222 let n = (*remaining).min(chunk.len());
223 buf.extend_from_slice(&chunk[..n]);
224 *remaining -= n;
225 if *remaining > 0 {
226 return (n, None);
227 }
228 let message = std::mem::take(buf);
229 *self = Self::new();
230 (n, Some(FrameEvent::Message(message)))
231 }
232 Self::Skip { remaining } => {
233 let n = (*remaining).min(chunk.len());
234 *remaining -= n;
235 if *remaining == 0 {
236 *self = Self::new();
237 }
238 (n, None)
239 }
240 Self::Stopped => (chunk.len(), None),
241 }
242 }
243
244 fn skip(len: usize) -> Self {
247 if len == 0 {
248 Self::new()
249 } else {
250 Self::Skip { remaining: len }
251 }
252 }
253}
254
255impl<B> Body for RequestLogBody<B>
256where
257 B: Body<Data = Bytes>,
258{
259 type Data = Bytes;
260 type Error = B::Error;
261
262 fn poll_frame(
263 self: Pin<&mut Self>,
264 cx: &mut Context<'_>,
265 ) -> Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
266 let this = self.project();
267 let result = ready!(this.inner.poll_frame(cx));
268
269 if let (Some(capture), Some(Ok(frame))) = (this.capture.as_mut(), result.as_ref())
270 && let Some(chunk) = frame.data_ref()
271 {
272 capture.observe(chunk.as_ref());
273 }
274
275 Poll::Ready(result)
276 }
277
278 fn is_end_stream(&self) -> bool {
279 self.inner.is_end_stream()
280 }
281
282 fn size_hint(&self) -> http_body::SizeHint {
283 self.inner.size_hint()
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290
291 fn parse(chunks: &[&[u8]], max: usize) -> Vec<Result<Vec<u8>, Cow<'static, str>>> {
294 let mut parser = FrameParser::new();
295 let mut events = Vec::new();
296 for mut chunk in chunks.iter().copied() {
297 while !chunk.is_empty() {
298 let (consumed, completed) = parser.feed(chunk, max);
299 chunk = &chunk[consumed..];
300 match completed {
301 Some(FrameEvent::Message(message)) => events.push(Ok(message)),
302 Some(FrameEvent::Skipped { reason, .. }) => events.push(Err(reason)),
303 None => {}
304 }
305 }
306 }
307 events
308 }
309
310 fn frame(flag: u8, message: &[u8]) -> Vec<u8> {
311 let mut frame = vec![flag];
312 frame.extend_from_slice(&(message.len() as u32).to_be_bytes());
313 frame.extend_from_slice(message);
314 frame
315 }
316
317 #[test]
318 fn single_frame_single_chunk() {
319 let events = parse(&[&frame(0, b"hello")], 1024);
320 assert_eq!(events, vec![Ok(b"hello".to_vec())]);
321 }
322
323 #[test]
324 fn empty_message() {
325 let events = parse(&[&frame(0, b"")], 1024);
326 assert_eq!(events, vec![Ok(Vec::new())]);
327 }
328
329 #[test]
330 fn frame_split_across_chunks() {
331 let bytes = frame(0, b"hello");
332 let chunks: Vec<&[u8]> = bytes.chunks(1).collect();
333 let events = parse(&chunks, 1024);
334 assert_eq!(events, vec![Ok(b"hello".to_vec())]);
335 }
336
337 #[test]
338 fn multiple_frames_in_one_chunk() {
339 let mut bytes = frame(0, b"one");
340 bytes.extend_from_slice(&frame(0, b"two"));
341 let events = parse(&[&bytes], 1024);
342 assert_eq!(events, vec![Ok(b"one".to_vec()), Ok(b"two".to_vec())]);
343 }
344
345 #[test]
346 fn oversized_message_skipped_next_frame_still_parses() {
347 let mut bytes = frame(0, b"too big for the cap");
348 bytes.extend_from_slice(&frame(0, b"ok"));
349 let events = parse(&[&bytes], 4);
350 assert_eq!(
351 events,
352 vec![Err("message too large".into()), Ok(b"ok".to_vec())]
353 );
354 }
355
356 #[test]
357 fn compressed_message_skipped_next_frame_still_parses() {
358 let mut bytes = frame(1, b"compressed");
359 bytes.extend_from_slice(&frame(0, b"ok"));
360 let events = parse(&[&bytes], 1024);
361 assert_eq!(
362 events,
363 vec![Err("compressed message".into()), Ok(b"ok".to_vec())]
364 );
365 }
366
367 #[test]
368 fn empty_compressed_message_skipped() {
369 let mut bytes = frame(1, b"");
370 bytes.extend_from_slice(&frame(0, b"ok"));
371 let events = parse(&[&bytes], 1024);
372 assert_eq!(
373 events,
374 vec![Err("compressed message".into()), Ok(b"ok".to_vec())]
375 );
376 }
377
378 #[test]
379 fn invalid_flag_poisons_parser() {
380 let mut bytes = frame(2, b"garbage");
381 bytes.extend_from_slice(&frame(0, b"never parsed"));
382 let events = parse(&[&bytes], 1024);
383 assert_eq!(events, vec![Err("invalid flag 2".into())]);
384 }
385
386 #[test]
387 fn truncated_trailing_bytes_emit_nothing() {
388 let bytes = frame(0, b"hello");
389 let events = parse(&[&bytes[..bytes.len() - 1]], 1024);
390 assert_eq!(events, Vec::new());
391 }
392}