engineioxide_core/payload/
mod.rs1use crate::{Packet, PacketParseError, ProtocolVersion, packet::PacketBuf};
4
5use bytes::Bytes;
6use futures_util::Stream;
7use http::HeaderValue;
8
9mod buf;
10mod decoder;
11mod encoder;
12
13const PACKET_SEPARATOR_V4: u8 = b'\x1e';
14#[cfg(feature = "v3")]
15const STRING_PACKET_SEPARATOR_V3: u8 = b':';
16#[cfg(feature = "v3")]
17const BINARY_PACKET_SEPARATOR_V3: u8 = 0xff;
18#[cfg(feature = "v3")]
19const STRING_PACKET_IDENTIFIER_V3: u8 = 0x00;
20#[cfg(feature = "v3")]
21const BINARY_PACKET_IDENTIFIER_V3: u8 = 0x01;
22#[cfg(feature = "v3")]
23const BINARY_CONTENT_TYPE: HeaderValue = HeaderValue::from_static("application/octet-stream");
24
25pub fn decoder(
27 body: impl http_body::Body<Error = impl std::fmt::Debug> + Unpin,
28 #[allow(unused)] content_type: Option<&HeaderValue>,
29 #[allow(unused)] protocol: ProtocolVersion,
30 max_payload: u64,
31) -> impl Stream<Item = Result<Packet, PacketParseError>> {
32 #[cfg(feature = "tracing")]
33 tracing::debug!(?content_type, %protocol, "decoding payload");
34
35 #[cfg(feature = "v3")]
36 {
37 use futures_util::future::Either;
38
39 let is_binary = content_type == Some(&BINARY_CONTENT_TYPE);
40 match protocol {
41 ProtocolVersion::V4 => Either::Left(decoder::v4_decoder(body, max_payload)),
42 ProtocolVersion::V3 if is_binary => {
43 Either::Right(Either::Left(decoder::v3_binary_decoder(body, max_payload)))
44 }
45 ProtocolVersion::V3 => {
46 Either::Right(Either::Right(decoder::v3_string_decoder(body, max_payload)))
47 }
48 }
49 }
50
51 #[cfg(not(feature = "v3"))]
52 {
53 decoder::v4_decoder(body, max_payload)
54 }
55}
56
57pub struct Payload {
59 pub data: Bytes,
61 pub has_binary: bool,
63 pub peeked: Option<PacketBuf>,
65}
66impl Payload {
67 pub fn new(data: Bytes, has_binary: bool) -> Self {
69 Self {
70 data,
71 has_binary,
72 peeked: None,
73 }
74 }
75}
76
77pub async fn encoder(
79 rx: impl Stream<Item = PacketBuf>,
80 #[allow(unused)] protocol: ProtocolVersion,
81 #[allow(unused)] supports_binary: bool,
82 max_payload: u64,
83) -> Payload {
84 let mut rx = std::pin::pin!(peekable::Peekable::new(rx));
85
86 #[cfg(feature = "v3")]
87 let mut payload = match protocol {
88 ProtocolVersion::V4 => encoder::v4_encoder(rx.as_mut(), max_payload).await,
89 ProtocolVersion::V3 if supports_binary => {
90 encoder::v3_binary_encoder(rx.as_mut(), max_payload).await
91 }
92 ProtocolVersion::V3 => encoder::v3_string_encoder(rx.as_mut(), max_payload).await,
93 };
94
95 #[cfg(not(feature = "v3"))]
96 let mut payload = encoder::v4_encoder(rx.as_mut(), max_payload).await;
97
98 payload.peeked = rx.take_peeked();
101
102 payload
103}
104
105pub fn packet_encoder(
107 packet: Packet,
108 #[allow(unused)] protocol: ProtocolVersion,
109 #[allow(unused)] supports_binary: bool,
110) -> Bytes {
111 #[cfg(feature = "v3")]
112 match protocol {
113 ProtocolVersion::V4 => packet.into(),
114 ProtocolVersion::V3 if supports_binary && packet.is_binary() => {
115 use bytes::BytesMut;
116
117 let size_hint = packet.get_size_hint(!supports_binary) + 2;
118 let mut bytes = BytesMut::with_capacity(size_hint);
119 encoder::v3_bin_packet_encoder(packet, &mut bytes);
120 bytes.freeze()
121 }
122 ProtocolVersion::V3 => {
123 use bytes::BytesMut;
124
125 let size_hint = packet.get_size_hint(!supports_binary) + 2;
126 let mut bytes = BytesMut::with_capacity(size_hint);
127 encoder::v3_string_packet_encoder(packet, &mut bytes);
128 bytes.freeze()
129 }
130 }
131
132 #[cfg(not(feature = "v3"))]
133 packet.into()
134}
135
136mod peekable {
137 use std::{
140 pin::Pin,
141 task::{Context, Poll},
142 };
143
144 use futures_util::{Stream, StreamExt as _, stream::Fuse};
145 use pin_project_lite::pin_project;
146
147 pin_project! {
148 #[derive(Debug)]
154 #[must_use = "streams do nothing unless polled"]
155 pub struct Peekable<St: Stream> {
156 #[pin]
157 stream: Fuse<St>,
158 peeked: Option<St::Item>,
159 }
160 }
161
162 impl<St: Stream> Peekable<St> {
163 pub(super) fn new(stream: St) -> Self {
164 Self {
165 stream: stream.fuse(),
166 peeked: None,
167 }
168 }
169
170 pub fn peek(self: Pin<&mut Self>) -> Peek<'_, St> {
173 Peek { inner: Some(self) }
174 }
175
176 pub fn take_peeked(self: Pin<&mut Self>) -> Option<St::Item> {
181 self.project().peeked.take()
182 }
183
184 pub fn poll_peek(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<&St::Item>> {
189 let mut this = self.project();
190
191 Poll::Ready(loop {
192 if this.peeked.is_some() {
193 break this.peeked.as_ref();
194 } else if let Some(item) = futures_util::ready!(this.stream.as_mut().poll_next(cx))
195 {
196 *this.peeked = Some(item);
197 } else {
198 break None;
199 }
200 })
201 }
202 }
203
204 impl<S: Stream> Stream for Peekable<S> {
205 type Item = S::Item;
206
207 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
208 let this = self.project();
209 if let Some(item) = this.peeked.take() {
210 return Poll::Ready(Some(item));
211 }
212 this.stream.poll_next(cx)
213 }
214
215 fn size_hint(&self) -> (usize, Option<usize>) {
216 let peek_len = usize::from(self.peeked.is_some());
217 let (lower, upper) = self.stream.size_hint();
218 let lower = lower.saturating_add(peek_len);
219 let upper = match upper {
220 Some(x) => x.checked_add(peek_len),
221 None => None,
222 };
223 (lower, upper)
224 }
225 }
226
227 pin_project! {
228 #[must_use = "futures do nothing unless polled"]
230 pub struct Peek<'a, St: Stream> {
231 inner: Option<Pin<&'a mut Peekable<St>>>,
232 }
233 }
234
235 impl<'a, St> Future for Peek<'a, St>
236 where
237 St: Stream,
238 {
239 type Output = Option<&'a St::Item>;
240
241 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
242 let inner = self.project().inner;
243 if let Some(peekable) = inner {
244 futures_util::ready!(peekable.as_mut().poll_peek(cx));
245
246 inner.take().unwrap().poll_peek(cx)
247 } else {
248 panic!("Peek polled after completion")
249 }
250 }
251 }
252}