Skip to main content

milo_parser/
lib.rs

1#![allow(unused_imports)]
2
3extern crate alloc;
4
5use alloc::ffi::CString;
6use alloc::vec::Vec;
7use alloc::{boxed::Box, format};
8use core::cell::{Cell, RefCell};
9use core::ffi::{c_char, c_uchar, c_void};
10use core::fmt::Debug;
11use core::ptr;
12use core::str;
13use core::{slice, slice::from_raw_parts};
14
15use milo_macros::{callback, generate, next};
16
17#[repr(C)]
18#[derive(Clone, Debug)]
19pub struct Parser {
20  // User writable
21  pub autodetect: bool,
22  pub is_request: bool,
23  pub manage_unconsumed: bool,
24  pub continue_without_data: bool,
25  pub is_connect: bool,
26  pub skip_body: bool,
27  pub max_start_line_length: usize,
28  pub max_header_length: usize,
29  #[cfg(not(target_family = "wasm"))]
30  pub context: *mut c_void,
31  #[cfg(any(debug_assertions, feature = "debug"))]
32  pub debug: bool,
33
34  // Generic state
35  pub state: u8,
36  pub position: usize,
37  pub parsed: u64,
38  pub paused: bool,
39  pub error_code: u8,
40
41  // Current message flags
42  pub method: u8,
43  pub status: u32,
44  pub version_major: u8,
45  pub version_minor: u8,
46  pub content_length: u64,
47  pub chunk_size: u64,
48  pub remaining_content_length: u64,
49  pub remaining_chunk_size: u64,
50  pub has_content_length: bool,
51  pub has_transfer_encoding: bool,
52  pub has_chunked_transfer_encoding: bool,
53  pub has_connection_close: bool,
54  pub has_connection_upgrade: bool,
55  pub has_upgrade: bool,
56  pub has_trailers: bool,
57
58  // Callback handling
59  pub active_callbacks: u64,
60  #[cfg(not(target_family = "wasm"))]
61  pub callbacks: ParserCallbacks,
62
63  // WASM Specific
64  #[cfg(target_family = "wasm")]
65  pub ptr: *mut c_void,
66
67  // Complex data types - We need to split them in order to be exportable to C++
68  pub error_description: *const c_uchar,
69  pub error_description_len: u16,
70  pub unconsumed: *const c_uchar,
71  pub unconsumed_len: usize,
72}
73
74#[cfg(not(target_family = "wasm"))]
75mod native;
76
77#[cfg(not(target_family = "wasm"))]
78pub use crate::native::*;
79
80#[cfg(target_family = "wasm")]
81mod wasm;
82
83#[cfg(target_family = "wasm")]
84pub use crate::wasm::*;
85
86generate!();
87
88impl Parser {
89  /// Creates a new parser
90  pub fn new() -> Parser {
91    Parser {
92      // User writable
93      autodetect: true,
94      is_request: false,
95      manage_unconsumed: false,
96      continue_without_data: false,
97      is_connect: false,
98      skip_body: false,
99      max_start_line_length: 8192,
100      max_header_length: 8192,
101      #[cfg(any(debug_assertions, feature = "debug"))]
102      debug: false,
103      #[cfg(not(target_family = "wasm"))]
104      context: ptr::null_mut(),
105      // Generic state
106      state: STATE_START,
107      position: 0,
108      parsed: 0,
109      paused: false,
110      error_code: ERROR_NONE,
111      // Current message flags
112      method: 0,
113      status: 0,
114      version_major: 0,
115      version_minor: 0,
116      content_length: 0,
117      chunk_size: 0,
118      remaining_content_length: 0,
119      remaining_chunk_size: 0,
120      has_content_length: false,
121      has_transfer_encoding: false,
122      has_chunked_transfer_encoding: false,
123      has_connection_close: false,
124      has_connection_upgrade: false,
125      has_upgrade: false,
126      has_trailers: false,
127      // Callbacks handling
128      active_callbacks: 0,
129      #[cfg(not(target_family = "wasm"))]
130      callbacks: ParserCallbacks::new(),
131      // WASM Specific
132      #[cfg(target_family = "wasm")]
133      ptr: ptr::null_mut(),
134      // Complex data types
135      error_description: ptr::null(),
136      error_description_len: 0,
137      unconsumed: ptr::null(),
138      unconsumed_len: 0,
139    }
140  }
141
142  /// Resets a parser. The second parameters specifies if to also reset the
143  /// parsed counter.
144  ///
145  /// The following fields are not modified:
146  ///   * position
147  ///   * context
148  ///   * autodetect
149  ///   * is_request
150  ///   * manage_unconsumed
151  ///   * continue_without_data
152  ///   * context
153  pub fn reset(&mut self, keep_parsed: bool) {
154    self.state = STATE_START;
155    self.paused = false;
156
157    if !keep_parsed {
158      self.parsed = 0;
159    }
160
161    self.error_code = ERROR_NONE;
162
163    if self.error_description_len > 0 {
164      unsafe {
165        let _ = slice::from_raw_parts(self.error_description, self.error_description_len as usize);
166      }
167
168      self.error_description = ptr::null();
169      self.error_description_len = 0;
170    }
171
172    if self.unconsumed_len > 0 {
173      unsafe {
174        let _ = slice::from_raw_parts(self.unconsumed, self.unconsumed_len);
175      }
176
177      self.unconsumed = ptr::null();
178      self.unconsumed_len = 0;
179    }
180
181    self.clear();
182    callback!(on_reset);
183  }
184
185  /// Clears all values about the message in the parser.
186  pub fn clear(&mut self) {
187    self.is_connect = false;
188    self.method = 0;
189    self.status = 0;
190    self.version_major = 0;
191    self.version_minor = 0;
192    self.has_content_length = false;
193    self.has_transfer_encoding = false;
194    self.has_chunked_transfer_encoding = false;
195    self.has_connection_close = false;
196    self.has_connection_upgrade = false;
197    self.has_upgrade = false;
198    self.has_trailers = false;
199    self.content_length = 0;
200    self.chunk_size = 0;
201    self.remaining_content_length = 0;
202    self.remaining_chunk_size = 0;
203    self.skip_body = false;
204  }
205
206  /// Pauses the parser. It will have to be resumed via `resume`.
207  pub fn pause(&mut self) { self.paused = true; }
208
209  /// Resumes the parser.
210  pub fn resume(&mut self) { self.paused = false; }
211
212  /// Marks the parser as finished. Any new data received via `parse` will
213  /// put the parser in the error state.
214  pub fn finish(&mut self) {
215    match self.state {
216      // If the parser is one of the initial states, simply jump to finish
217      STATE_START | STATE_REQUEST_LINE | STATE_STATUS_LINE | STATE_FINISH => {
218        self.state = STATE_FINISH;
219      }
220      STATE_BODY_WITH_NO_LENGTH => {
221        // Notify that the message has been completed
222        callback!(on_message_complete);
223
224        // Set the state to be finished
225        self.state = STATE_FINISH;
226      }
227      STATE_ERROR => (),
228      // In another other state, this is an error
229      _ => {
230        self.fail(ERROR_UNEXPECTED_EOF, "Unexpected end of data");
231      }
232    }
233  }
234
235  /// Marks the parsing a failed, setting a error code and and error message.
236  ///
237  /// It always returns zero for internal use.
238  #[inline(always)]
239  pub fn fail(&mut self, code: u8, description: &str) {
240    let description_copy = description.to_string();
241    let (ptr, _, len) = description_copy.into_raw_parts();
242
243    self.state = STATE_ERROR;
244    self.error_code = code;
245    self.error_description = ptr;
246    self.error_description_len = len as u16;
247    callback!(on_error, 0, 0);
248  }
249
250  /// Returns the current parser's state as string.
251  pub fn state_str(&self) -> &str { States::try_from(self.state).unwrap().as_str() }
252
253  /// Returns the current parser's error state as string.
254  pub fn error_code_str(&self) -> &str { Errors::try_from(self.error_code).unwrap().as_str() }
255
256  /// Returns the current parser's error description as string.
257  pub fn error_description_str(&self) -> &str {
258    unsafe {
259      if self.error_description_len > 0 {
260        str::from_utf8_unchecked(from_raw_parts(
261          self.error_description,
262          self.error_description_len as usize,
263        ))
264      } else {
265        ""
266      }
267    }
268  }
269}
270
271impl Default for Parser {
272  fn default() -> Self { Self::new() }
273}
274
275mod matchers;
276mod parse;