1use core::{mem, ops::Deref};
2
3use bytes::{Buf, Bytes, BytesMut};
4use bytestring::ByteString;
5
6use crate::{
7 MessageBase, ServerMessage, StatusCode, Subject, SubscriptionId,
8 error::ServerError,
9 headers::{
10 HeaderMap, HeaderName, HeaderValue,
11 error::{HeaderNameValidateError, HeaderValueValidateError},
12 },
13 status_code::StatusCodeError,
14 util::{self, CrlfFinder, ParseUintError},
15};
16
17pub use self::framed::{FrameDecoderError, decode_frame};
18pub use self::stream::StreamDecoder;
19
20use super::ServerOp;
21
22mod framed;
23mod stream;
24
25const MAX_HEAD_LEN: usize = 128 * 1024;
32
33#[derive(Debug)]
34pub(super) enum DecoderStatus {
35 ControlLine {
36 last_bytes_read: usize,
37 },
38 Headers {
39 subscription_id: SubscriptionId,
40 subject: Subject,
41 reply_subject: Option<Subject>,
42 header_len: usize,
43 payload_len: usize,
44 },
45 Payload {
46 subscription_id: SubscriptionId,
47 subject: Subject,
48 reply_subject: Option<Subject>,
49 status_code: Option<StatusCode>,
50 status_description: Option<ByteString>,
51 headers: HeaderMap,
52 payload_len: usize,
53 },
54 Poisoned,
55}
56
57pub(super) trait BytesLike: Buf + Deref<Target = [u8]> {
58 fn len(&self) -> usize {
59 Buf::remaining(self)
60 }
61
62 fn split_to(&mut self, at: usize) -> Bytes {
63 self.copy_to_bytes(at)
64 }
65}
66
67impl BytesLike for Bytes {}
68impl BytesLike for BytesMut {}
69
70pub(super) fn decode(
71 crlf: &CrlfFinder,
72 status: &mut DecoderStatus,
73 read_buf: &mut impl BytesLike,
74) -> Result<Option<ServerOp>, DecoderError> {
75 loop {
76 match status {
77 DecoderStatus::ControlLine { last_bytes_read } => {
78 if read_buf.starts_with(b"+OK\r\n") {
79 debug_assert_eq!(
81 *last_bytes_read, 0,
82 "we shouldn't have handled any bytes before"
83 );
84 read_buf.advance("+OK\r\n".len());
85 return Ok(Some(ServerOp::Success));
86 }
87
88 if *last_bytes_read == read_buf.len() {
89 return Ok(None);
91 }
92
93 let Some(control_line_len) = crlf.find(read_buf) else {
94 return if read_buf.len() < MAX_HEAD_LEN {
95 *last_bytes_read = read_buf.len();
96 Ok(None)
97 } else {
98 Err(DecoderError::HeadTooLong {
99 len: read_buf.len(),
100 })
101 };
102 };
103
104 let mut control_line = read_buf.split_to(control_line_len + "\r\n".len());
105 control_line.truncate(control_line.len() - 2);
106
107 return if control_line.starts_with(b"MSG ") {
108 *status = decode_msg(control_line)?;
109 continue;
110 } else if control_line.starts_with(b"HMSG ") {
111 *status = decode_hmsg(control_line)?;
112 continue;
113 } else if control_line.starts_with(b"PING") {
114 Ok(Some(ServerOp::Ping))
115 } else if control_line.starts_with(b"PONG") {
116 Ok(Some(ServerOp::Pong))
117 } else if control_line.starts_with(b"+OK") {
118 Ok(Some(ServerOp::Success))
120 } else if control_line.starts_with(b"-ERR ") {
121 control_line.advance("-ERR ".len());
122 if !control_line.starts_with(b"'") || !control_line.ends_with(b"'") {
123 return Err(DecoderError::InvalidErrorMessage);
124 }
125
126 control_line.advance(1);
127 control_line.truncate(control_line.len() - 1);
128 let raw_message = ByteString::try_from(control_line)
129 .map_err(|_| DecoderError::InvalidErrorMessage)?;
130 let error = ServerError::parse(raw_message);
131 Ok(Some(ServerOp::Error { error }))
132 } else if let Some(info) = control_line.strip_prefix(b"INFO ") {
133 let info = serde_json::from_slice(info).map_err(DecoderError::InvalidInfo)?;
134 Ok(Some(ServerOp::Info { info }))
135 } else {
136 Err(DecoderError::InvalidCommand)
137 };
138 }
139 DecoderStatus::Headers { header_len, .. } => {
140 if read_buf.len() < *header_len {
141 return Ok(None);
142 }
143
144 decode_headers(crlf, read_buf, status)?;
145 }
146 DecoderStatus::Payload { payload_len, .. } => {
147 if read_buf.len() < *payload_len + "\r\n".len() {
148 return Ok(None);
149 }
150
151 let DecoderStatus::Payload {
152 subscription_id,
153 subject,
154 reply_subject,
155 status_code,
156 status_description,
157 headers,
158 payload_len,
159 } = mem::replace(status, DecoderStatus::ControlLine { last_bytes_read: 0 })
160 else {
161 unreachable!()
162 };
163
164 let payload = read_buf.split_to(payload_len);
165 read_buf.advance("\r\n".len());
166 let message = ServerMessage {
167 status_code,
168 status_description,
169 subscription_id,
170 base: MessageBase {
171 subject,
172 reply_subject,
173 headers,
174 payload,
175 },
176 };
177 return Ok(Some(ServerOp::Message { message }));
178 }
179 DecoderStatus::Poisoned => return Err(DecoderError::Poisoned),
180 }
181 }
182}
183
184fn decode_msg(mut control_line: Bytes) -> Result<DecoderStatus, DecoderError> {
185 control_line.advance("MSG ".len());
186
187 let mut chunks = util::split_spaces(control_line);
188 let (subject, subscription_id, reply_subject, payload_len) = match (
189 chunks.next(),
190 chunks.next(),
191 chunks.next(),
192 chunks.next(),
193 chunks.next(),
194 ) {
195 (Some(subject), Some(subscription_id), Some(reply_subject), Some(payload_len), None) => {
196 (subject, subscription_id, Some(reply_subject), payload_len)
197 }
198 (Some(subject), Some(subscription_id), Some(payload_len), None, None) => {
199 (subject, subscription_id, None, payload_len)
200 }
201 _ => return Err(DecoderError::InvalidMsgArgsCount),
202 };
203 let subject = Subject::from_dangerous_value(
204 subject
205 .try_into()
206 .map_err(|_| DecoderError::SubjectInvalidUtf8)?,
207 );
208 let subscription_id =
209 SubscriptionId::from_ascii_bytes(&subscription_id).map_err(DecoderError::SubscriptionId)?;
210 let reply_subject = reply_subject
211 .map(|reply_subject| {
212 ByteString::try_from(reply_subject).map_err(|_| DecoderError::ReplySubjectInvalidUtf8)
213 })
214 .transpose()?
215 .map(Subject::from_dangerous_value);
216 let payload_len =
217 util::parse_usize(&payload_len).map_err(DecoderError::InvalidPayloadLength)?;
218 Ok(DecoderStatus::Payload {
219 subscription_id,
220 subject,
221 reply_subject,
222 status_code: None,
223 status_description: None,
224 headers: HeaderMap::new(),
225 payload_len,
226 })
227}
228
229fn decode_hmsg(mut control_line: Bytes) -> Result<DecoderStatus, DecoderError> {
230 control_line.advance("HMSG ".len());
231 let mut chunks = util::split_spaces(control_line);
232
233 let (subject, subscription_id, reply_subject, header_len, total_len) = match (
234 chunks.next(),
235 chunks.next(),
236 chunks.next(),
237 chunks.next(),
238 chunks.next(),
239 chunks.next(),
240 ) {
241 (
242 Some(subject),
243 Some(subscription_id),
244 Some(reply_to),
245 Some(header_len),
246 Some(total_len),
247 None,
248 ) => (
249 subject,
250 subscription_id,
251 Some(reply_to),
252 header_len,
253 total_len,
254 ),
255 (Some(subject), Some(subscription_id), Some(header_len), Some(total_len), None, None) => {
256 (subject, subscription_id, None, header_len, total_len)
257 }
258 _ => return Err(DecoderError::InvalidHmsgArgsCount),
259 };
260
261 let subject = Subject::from_dangerous_value(
262 subject
263 .try_into()
264 .map_err(|_| DecoderError::SubjectInvalidUtf8)?,
265 );
266 let subscription_id =
267 SubscriptionId::from_ascii_bytes(&subscription_id).map_err(DecoderError::SubscriptionId)?;
268 let reply_subject = reply_subject
269 .map(|reply_subject| {
270 ByteString::try_from(reply_subject).map_err(|_| DecoderError::ReplySubjectInvalidUtf8)
271 })
272 .transpose()?
273 .map(Subject::from_dangerous_value);
274 let header_len = util::parse_usize(&header_len).map_err(DecoderError::InvalidHeaderLength)?;
275 let total_len = util::parse_usize(&total_len).map_err(DecoderError::InvalidPayloadLength)?;
276
277 let payload_len = total_len
278 .checked_sub(header_len)
279 .ok_or(DecoderError::InvalidTotalLength)?;
280
281 Ok(DecoderStatus::Headers {
282 subscription_id,
283 subject,
284 reply_subject,
285 header_len,
286 payload_len,
287 })
288}
289
290fn decode_headers(
291 crlf: &CrlfFinder,
292 read_buf: &mut impl BytesLike,
293 status: &mut DecoderStatus,
294) -> Result<(), DecoderError> {
295 let DecoderStatus::Headers {
296 subscription_id,
297 subject,
298 reply_subject,
299 header_len,
300 payload_len,
301 } = mem::replace(status, DecoderStatus::Poisoned)
302 else {
303 unreachable!()
304 };
305
306 let header = read_buf.split_to(header_len);
307 let mut lines = util::lines_iter(crlf, header);
308 let head = lines.next().ok_or(DecoderError::MissingHead)?;
309 let status_line = head
310 .strip_prefix(b"NATS/1.0")
311 .ok_or(DecoderError::InvalidHead)?;
312 let status_code = if let Some(status_code) = status_line.get(1..4) {
313 Some(StatusCode::from_ascii_bytes(status_code).map_err(DecoderError::StatusCode)?)
314 } else {
315 None
316 };
317 let status_description = match status_line.get(4..) {
318 Some([separator, description @ ..]) => {
319 if !separator.is_ascii_whitespace() {
320 return Err(DecoderError::InvalidHead);
321 }
322
323 match description.trim_ascii() {
324 [] => None,
325 description => Some(
326 ByteString::try_from(head.slice_ref(description))
328 .map_err(|_| DecoderError::StatusDescriptionInvalidUtf8)?,
329 ),
330 }
331 }
332 _ => None,
333 };
334
335 let headers = lines
336 .filter(|line| !line.is_empty())
337 .map(|mut line| {
338 let i = memchr::memchr(b':', &line).ok_or(DecoderError::InvalidHeaderLine)?;
339
340 let name = line.split_to(i);
341 line.advance(":".len());
342 if line.first().is_some_and(u8::is_ascii_whitespace) {
343 line.advance(1);
345 }
346 let value = line;
347
348 let name = HeaderName::try_from(
349 ByteString::try_from(name).map_err(|_| DecoderError::HeaderNameInvalidUtf8)?,
350 )
351 .map_err(DecoderError::HeaderName)?;
352 let value = HeaderValue::from_bytes(&value).map_err(DecoderError::HeaderValue)?;
353 Ok((name, value))
354 })
355 .collect::<Result<_, _>>()?;
356
357 *status = DecoderStatus::Payload {
358 subscription_id,
359 subject,
360 reply_subject,
361 status_code,
362 status_description,
363 headers,
364 payload_len,
365 };
366 Ok(())
367}
368
369#[derive(Debug, thiserror::Error)]
370pub enum DecoderError {
371 #[error("The head exceeded the maximum head length (len {len} maximum {MAX_HEAD_LEN}")]
372 HeadTooLong { len: usize },
373 #[error("Invalid command")]
374 InvalidCommand,
375 #[error("MSG command has an unexpected number of arguments")]
376 InvalidMsgArgsCount,
377 #[error("HMSG command has an unexpected number of arguments")]
378 InvalidHmsgArgsCount,
379 #[error("The subject isn't valid utf-8")]
380 SubjectInvalidUtf8,
381 #[error("The reply subject isn't valid utf-8")]
382 ReplySubjectInvalidUtf8,
383 #[error("Couldn't parse the Subscription ID")]
384 SubscriptionId(#[source] ParseUintError),
385 #[error("Couldn't parse the length of the header")]
386 InvalidHeaderLength(#[source] ParseUintError),
387 #[error("Couldn't parse the length of the payload")]
388 InvalidPayloadLength(#[source] ParseUintError),
389 #[error("The total length is greater than the header length")]
390 InvalidTotalLength,
391 #[error("HMSG is missing head")]
392 MissingHead,
393 #[error("HMSG has an invalid head")]
394 InvalidHead,
395 #[error("HMSG header line is missing ': '")]
396 InvalidHeaderLine,
397 #[error("Couldn't parse the status code")]
398 StatusCode(#[source] StatusCodeError),
399 #[error("The status description isn't valid utf-8")]
400 StatusDescriptionInvalidUtf8,
401 #[error("The header name isn't valid utf-8")]
402 HeaderNameInvalidUtf8,
403 #[error("The header name couldn't be parsed")]
404 HeaderName(#[source] HeaderNameValidateError),
405 #[error("The header value couldn't be parsed")]
406 HeaderValue(#[source] HeaderValueValidateError),
407 #[error("INFO command JSON payload couldn't be deserialized")]
408 InvalidInfo(#[source] serde_json::Error),
409 #[error("-ERR command message couldn't be deserialized")]
410 InvalidErrorMessage,
411 #[error("The decoder was poisoned")]
412 Poisoned,
413}