castrum 0.9.3 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/Cargo.lock +2259 -0
  2. package/Cargo.toml +138 -0
  3. package/README.md +6 -0
  4. package/build.rs +5 -0
  5. package/castrum.linux-x64-gnu.node +0 -0
  6. package/dist/index.d.ts +1348 -1295
  7. package/dist/index.js +4307 -3776
  8. package/docs/API.md +1 -1
  9. package/docs/ARCHITECTURE.md +3 -2
  10. package/docs/CASE_STUDY.md +2 -2
  11. package/docs/ENVIRONMENT.md +16 -0
  12. package/docs/FFI_BUN_GUIDE.md +89 -6
  13. package/docs/REPO_MAP.md +2 -2
  14. package/index.ts +19 -25
  15. package/package.json +9 -1
  16. package/rust/crypto/aead.rs +378 -0
  17. package/rust/crypto/argon2.rs +265 -0
  18. package/rust/crypto/base64.rs +420 -0
  19. package/rust/crypto/bcrypt.rs +89 -0
  20. package/rust/crypto/cookie_sign.rs +169 -0
  21. package/rust/crypto/csrf.rs +106 -0
  22. package/rust/crypto/ed25519.rs +215 -0
  23. package/rust/crypto/hashing.rs +140 -0
  24. package/rust/crypto/hmac_sha256.rs +88 -0
  25. package/rust/crypto/jwt/api.rs +286 -0
  26. package/rust/crypto/jwt/mod.rs +22 -0
  27. package/rust/crypto/jwt/tests.rs +312 -0
  28. package/rust/crypto/jwt/token.rs +360 -0
  29. package/rust/crypto/mod.rs +27 -0
  30. package/rust/crypto/pbkdf2.rs +96 -0
  31. package/rust/crypto/random_token.rs +85 -0
  32. package/rust/crypto/session.rs +222 -0
  33. package/rust/ffi/crypto.rs +708 -0
  34. package/rust/ffi/hashing.rs +285 -0
  35. package/rust/ffi/http.rs +752 -0
  36. package/rust/ffi/ingress.rs +303 -0
  37. package/rust/ffi/json.rs +517 -0
  38. package/rust/ffi/jwt.rs +419 -0
  39. package/rust/ffi/metrics.rs +373 -0
  40. package/rust/ffi/mod.rs +68 -0
  41. package/rust/ffi/payload.rs +275 -0
  42. package/rust/ffi/probe.rs +60 -0
  43. package/rust/ffi/rate_limit.rs +77 -0
  44. package/rust/ffi/route.rs +85 -0
  45. package/rust/ffi/tests.rs +2333 -0
  46. package/rust/ffi/util.rs +152 -0
  47. package/rust/ffi/validators.rs +123 -0
  48. package/rust/http/accept.rs +606 -0
  49. package/rust/http/cookie_parser.rs +140 -0
  50. package/rust/http/etag.rs +289 -0
  51. package/rust/http/form.rs +132 -0
  52. package/rust/http/headers.rs +267 -0
  53. package/rust/http/http_date.rs +286 -0
  54. package/rust/http/http_parser.rs +252 -0
  55. package/rust/http/media_type.rs +355 -0
  56. package/rust/http/method.rs +200 -0
  57. package/rust/http/mime_lookup.rs +130 -0
  58. package/rust/http/mod.rs +32 -0
  59. package/rust/http/multipart.rs +685 -0
  60. package/rust/http/query_parser.rs +180 -0
  61. package/rust/http/url_codec.rs +296 -0
  62. package/rust/http/url_join.rs +324 -0
  63. package/rust/ingress/api.rs +355 -0
  64. package/rust/ingress/cors.rs +403 -0
  65. package/rust/ingress/ingress_constants.rs +209 -0
  66. package/rust/ingress/ip_trust.rs +312 -0
  67. package/rust/ingress/mod.rs +48 -0
  68. package/rust/ingress/native_route.rs +779 -0
  69. package/rust/ingress/options.rs +176 -0
  70. package/rust/ingress/output.rs +406 -0
  71. package/rust/ingress/packed.rs +191 -0
  72. package/rust/ingress/pipeline.rs +534 -0
  73. package/rust/ingress/proxy.rs +138 -0
  74. package/rust/ingress/rate_limit.rs +538 -0
  75. package/rust/ingress/terminal.rs +114 -0
  76. package/rust/ingress/tests.rs +566 -0
  77. package/rust/ingress/time.rs +70 -0
  78. package/rust/json/fast_schema/capture.rs +336 -0
  79. package/rust/json/fast_schema/compile.rs +556 -0
  80. package/rust/json/fast_schema/cursor.rs +368 -0
  81. package/rust/json/fast_schema/email.rs +282 -0
  82. package/rust/json/fast_schema/errors.rs +247 -0
  83. package/rust/json/fast_schema/mod.rs +57 -0
  84. package/rust/json/fast_schema/tests.rs +1143 -0
  85. package/rust/json/fast_schema/types.rs +140 -0
  86. package/rust/json/fast_schema/validate.rs +931 -0
  87. package/rust/json/json_ops.rs +121 -0
  88. package/rust/json/json_schema.rs +837 -0
  89. package/rust/json/json_ser.rs +881 -0
  90. package/rust/json/mod.rs +19 -0
  91. package/rust/json/napi_marshal.rs +318 -0
  92. package/rust/json/patch/api.rs +60 -0
  93. package/rust/json/patch/engine.rs +153 -0
  94. package/rust/json/patch/mod.rs +47 -0
  95. package/rust/json/patch/ops.rs +270 -0
  96. package/rust/json/patch/pointer.rs +49 -0
  97. package/rust/json/patch/tests.rs +499 -0
  98. package/rust/lib.rs +72 -0
  99. package/rust/metrics/api.rs +148 -0
  100. package/rust/metrics/mod.rs +21 -0
  101. package/rust/metrics/registry.rs +961 -0
  102. package/rust/panic_safety.rs +40 -0
  103. package/rust/payload/compress.rs +512 -0
  104. package/rust/payload/mod.rs +13 -0
  105. package/rust/payload/sse.rs +214 -0
  106. package/rust/payload/template.rs +253 -0
  107. package/rust/payload/websocket.rs +69 -0
  108. package/rust/payload/ws_frames.rs +354 -0
  109. package/rust/proptest_suite.rs +139 -0
  110. package/rust/selection.rs +95 -0
  111. package/rust/src/bin/native-bench.rs +495 -0
  112. package/rust/test_support.rs +71 -0
  113. package/rust/util/batch/api.rs +266 -0
  114. package/rust/util/batch/core.rs +473 -0
  115. package/rust/util/batch/mod.rs +23 -0
  116. package/rust/util/batch/tests.rs +445 -0
  117. package/rust/util/batch_core.rs +184 -0
  118. package/rust/util/bytes.rs +407 -0
  119. package/rust/util/mod.rs +34 -0
  120. package/rust/util/packed.rs +473 -0
  121. package/rust/util/text.rs +130 -0
  122. package/rust/util/threadpool.rs +128 -0
  123. package/rust/util/validation.rs +419 -0
  124. package/rust-toolchain.toml +17 -0
  125. package/scripts/postinstall.mjs +199 -0
  126. package/src/ingress/constants.ts +1 -1
  127. package/src/ingress/context.ts +2 -2
  128. package/src/ingress/decode/baked-result.ts +13 -13
  129. package/src/ingress/decode/fast-result.ts +17 -17
  130. package/src/ingress/errors.ts +7 -7
  131. package/src/ingress/headers/baked-templates.ts +1 -1
  132. package/src/ingress/headers/fast-templates.ts +3 -3
  133. package/src/ingress/index.ts +1 -1
  134. package/src/ingress/native-route.ts +9 -5
  135. package/src/ingress/options.ts +1 -1
  136. package/src/ingress/packing/gather-raw-headers.ts +1 -1
  137. package/src/ingress/packing/route-wire.ts +1 -5
  138. package/src/ingress/response/error-bodies.ts +5 -5
  139. package/src/ingress/response/terminal.ts +1 -1
  140. package/src/ingress/routes/echo.ts +4 -4
  141. package/src/ingress/routes/fallback.ts +2 -2
  142. package/src/ingress/routes/head.ts +2 -2
  143. package/src/ingress/routes/native.ts +2 -1
  144. package/src/ingress/routes/options.ts +1 -1
  145. package/src/ingress/routes/read.ts +2 -2
  146. package/src/ingress/server.ts +10 -6
  147. package/src/ingress/status.ts +7 -7
  148. package/src/integration/batch.ts +1 -1
  149. package/src/integration/index.ts +9 -9
  150. package/src/integration/pipeline.ts +6 -6
  151. package/src/integration/websocket.ts +2 -2
  152. package/src/loader/index.ts +1 -5
  153. package/src/loader/ops.ts +1 -1
  154. package/src/native/ffi/build/codecs.ts +104 -8
  155. package/src/native/ffi/build/compress.ts +14 -10
  156. package/src/native/ffi/build/instances.ts +40 -3
  157. package/src/native/ffi/build/metrics.ts +224 -0
  158. package/src/native/ffi/build/parse.ts +59 -23
  159. package/src/native/ffi/build/util.ts +14 -1
  160. package/src/native/ffi/build.ts +2 -0
  161. package/src/native/ffi/selftest.ts +10 -3
  162. package/src/native/ffi/types.ts +111 -9
  163. package/src/native/ffi.ts +87 -1
  164. package/src/native/index.ts +1 -1
  165. package/src/native/loader.ts +2 -2
  166. package/src/native/types/instances.ts +34 -0
  167. package/src/native/types.ts +11 -0
  168. package/src/runtime/codec.ts +53 -9
  169. package/src/runtime/types.ts +7 -0
  170. package/src/rust-ffi/batch/build.ts +16 -9
  171. package/src/rust-ffi/index.ts +7 -6
  172. package/src/rust-ffi/options.ts +1 -3
  173. package/src/rust-ffi/proven.ts +3 -3
  174. package/src/rust-ffi/scalar/crypto.ts +1 -1
  175. package/src/rust-ffi/scalar/factories.ts +73 -0
  176. package/src/rust-ffi/scalar/hashing.ts +1 -1
  177. package/src/rust-ffi/scalar/http.ts +17 -11
  178. package/src/rust-ffi/scalar/index.ts +2 -0
  179. package/src/rust-ffi/scalar/interface.ts +20 -0
  180. package/src/rust-ffi/scalar/payload.ts +11 -3
  181. package/src/rust-ffi/scalar/validation.ts +121 -0
  182. package/src/selection.ts +1 -1
  183. package/src/shared/bytes.ts +5 -4
  184. package/src/shared/codec.ts +18 -3
@@ -0,0 +1,200 @@
1
+ // rust/http/method.rs — HTTP method classification
2
+ // Uses the shared word-at-a-time byte helpers from `bytes.rs`.
3
+
4
+ use crate::util::bytes::{ascii_eq_ignore_case, load_u64_padded};
5
+
6
+ #[derive(Clone, Copy, PartialEq, Eq, Debug)]
7
+ pub enum MethodKind {
8
+ Get,
9
+ Head,
10
+ Post,
11
+ Put,
12
+ Patch,
13
+ Delete,
14
+ Options,
15
+ Other,
16
+ }
17
+
18
+ const METHOD_GET: u64 = u64::from_le_bytes(*b"GET\0\0\0\0\0");
19
+ const METHOD_HEAD: u64 = u64::from_le_bytes(*b"HEAD\0\0\0\0");
20
+ const METHOD_POST: u64 = u64::from_le_bytes(*b"POST\0\0\0\0");
21
+ const METHOD_PUT: u64 = u64::from_le_bytes(*b"PUT\0\0\0\0\0");
22
+ const METHOD_PATCH: u64 = u64::from_le_bytes(*b"PATCH\0\0\0");
23
+ const METHOD_DELETE: u64 = u64::from_le_bytes(*b"DELETE\0\0");
24
+ const METHOD_OPTIONS: u64 = u64::from_le_bytes(*b"OPTIONS\0");
25
+
26
+ impl MethodKind {
27
+ /// Classify HTTP method from a `&str` (case-sensitive, exact match).
28
+ #[allow(clippy::should_implement_trait)]
29
+ #[inline(always)]
30
+ pub fn from_str(method: &str) -> Self {
31
+ let bytes = method.as_bytes();
32
+ let loaded = load_u64_padded(bytes);
33
+
34
+ match bytes.len() {
35
+ 3 if loaded == METHOD_GET => Self::Get,
36
+ 3 if loaded == METHOD_PUT => Self::Put,
37
+ 4 if loaded == METHOD_POST => Self::Post,
38
+ 4 if loaded == METHOD_HEAD => Self::Head,
39
+ 5 if loaded == METHOD_PATCH => Self::Patch,
40
+ 6 if loaded == METHOD_DELETE => Self::Delete,
41
+ 7 if loaded == METHOD_OPTIONS => Self::Options,
42
+ _ => Self::Other,
43
+ }
44
+ }
45
+
46
+ /// Classify HTTP method from raw bytes, case-insensitively, with zero
47
+ /// allocation (delegates to the shared word-at-a-time comparator).
48
+ #[inline(always)]
49
+ pub fn from_bytes_ignore_case(bytes: &[u8]) -> Self {
50
+ if bytes.len() > 7 {
51
+ return Self::Other;
52
+ }
53
+ if ascii_eq_ignore_case(bytes, b"GET") {
54
+ Self::Get
55
+ } else if ascii_eq_ignore_case(bytes, b"HEAD") {
56
+ Self::Head
57
+ } else if ascii_eq_ignore_case(bytes, b"POST") {
58
+ Self::Post
59
+ } else if ascii_eq_ignore_case(bytes, b"PUT") {
60
+ Self::Put
61
+ } else if ascii_eq_ignore_case(bytes, b"PATCH") {
62
+ Self::Patch
63
+ } else if ascii_eq_ignore_case(bytes, b"DELETE") {
64
+ Self::Delete
65
+ } else if ascii_eq_ignore_case(bytes, b"OPTIONS") {
66
+ Self::Options
67
+ } else {
68
+ Self::Other
69
+ }
70
+ }
71
+
72
+ /// Classify from u8 discriminator (0-6).
73
+ #[inline(always)]
74
+ pub fn from_u8(v: u8) -> Self {
75
+ match v {
76
+ 0 => Self::Get,
77
+ 1 => Self::Head,
78
+ 2 => Self::Post,
79
+ 3 => Self::Put,
80
+ 4 => Self::Patch,
81
+ 5 => Self::Delete,
82
+ 6 => Self::Options,
83
+ _ => Self::Other,
84
+ }
85
+ }
86
+
87
+ /// Returns true if the method may carry a body.
88
+ #[inline(always)]
89
+ pub fn may_have_body(&self) -> bool {
90
+ matches!(self, Self::Post | Self::Put | Self::Patch | Self::Delete)
91
+ }
92
+
93
+ /// Returns u8 discriminant for packed format.
94
+ #[inline(always)]
95
+ pub fn to_u8(&self) -> u8 {
96
+ match self {
97
+ Self::Get => 0,
98
+ Self::Head => 1,
99
+ Self::Post => 2,
100
+ Self::Put => 3,
101
+ Self::Patch => 4,
102
+ Self::Delete => 5,
103
+ Self::Options => 6,
104
+ Self::Other => 7,
105
+ }
106
+ }
107
+ }
108
+
109
+ impl MethodKind {
110
+ /// Bitmask representation for fast CORS method matching.
111
+ #[inline(always)]
112
+ pub fn bit(&self) -> u16 {
113
+ 1u16 << (self.to_u8() as u16)
114
+ }
115
+ }
116
+
117
+ #[cfg(test)]
118
+ mod tests {
119
+ use super::MethodKind;
120
+
121
+ #[test]
122
+ fn method_from_bytes_ignore_case_upper() {
123
+ assert_eq!(MethodKind::from_bytes_ignore_case(b"GET"), MethodKind::Get);
124
+ assert_eq!(
125
+ MethodKind::from_bytes_ignore_case(b"HEAD"),
126
+ MethodKind::Head
127
+ );
128
+ assert_eq!(
129
+ MethodKind::from_bytes_ignore_case(b"POST"),
130
+ MethodKind::Post
131
+ );
132
+ assert_eq!(MethodKind::from_bytes_ignore_case(b"PUT"), MethodKind::Put);
133
+ assert_eq!(
134
+ MethodKind::from_bytes_ignore_case(b"PATCH"),
135
+ MethodKind::Patch
136
+ );
137
+ assert_eq!(
138
+ MethodKind::from_bytes_ignore_case(b"DELETE"),
139
+ MethodKind::Delete
140
+ );
141
+ assert_eq!(
142
+ MethodKind::from_bytes_ignore_case(b"OPTIONS"),
143
+ MethodKind::Options
144
+ );
145
+ }
146
+
147
+ #[test]
148
+ fn method_from_bytes_ignore_case_lower() {
149
+ assert_eq!(MethodKind::from_bytes_ignore_case(b"get"), MethodKind::Get);
150
+ assert_eq!(
151
+ MethodKind::from_bytes_ignore_case(b"post"),
152
+ MethodKind::Post
153
+ );
154
+ assert_eq!(
155
+ MethodKind::from_bytes_ignore_case(b"options"),
156
+ MethodKind::Options
157
+ );
158
+ }
159
+
160
+ #[test]
161
+ fn method_from_bytes_ignore_case_unknown() {
162
+ assert_eq!(
163
+ MethodKind::from_bytes_ignore_case(b"FETCH"),
164
+ MethodKind::Other
165
+ );
166
+ assert_eq!(MethodKind::from_bytes_ignore_case(b""), MethodKind::Other);
167
+ assert_eq!(
168
+ MethodKind::from_bytes_ignore_case(b"GETX"),
169
+ MethodKind::Other
170
+ );
171
+ assert_eq!(
172
+ MethodKind::from_bytes_ignore_case(b"POSTING"),
173
+ MethodKind::Other
174
+ );
175
+ }
176
+
177
+ #[test]
178
+ fn method_from_str_upper() {
179
+ assert_eq!(MethodKind::from_str("GET"), MethodKind::Get);
180
+ assert_eq!(MethodKind::from_str("DELETE"), MethodKind::Delete);
181
+ assert_eq!(MethodKind::from_str("WEIRD"), MethodKind::Other);
182
+ }
183
+
184
+ #[test]
185
+ fn method_bits_are_distinct() {
186
+ let bits: std::collections::HashSet<u16> = [
187
+ MethodKind::Get,
188
+ MethodKind::Head,
189
+ MethodKind::Post,
190
+ MethodKind::Put,
191
+ MethodKind::Patch,
192
+ MethodKind::Delete,
193
+ MethodKind::Options,
194
+ ]
195
+ .iter()
196
+ .map(|m| m.bit())
197
+ .collect();
198
+ assert_eq!(bits.len(), 7, "each method must have a unique bit");
199
+ }
200
+ }
@@ -0,0 +1,130 @@
1
+ // rust/http/mime_lookup.rs — file extension → MIME type (phf table).
2
+
3
+ use napi::bindgen_prelude::*;
4
+ use napi_derive::napi;
5
+ use phf::{phf_map, Map};
6
+
7
+ const OCTET_STREAM: &str = "application/octet-stream";
8
+
9
+ static MIME_TABLE: Map<&'static str, &'static str> = phf_map! {
10
+ "json" => "application/json",
11
+ "js" => "text/javascript",
12
+ "mjs" => "text/javascript",
13
+ "cjs" => "text/javascript",
14
+ "html" => "text/html",
15
+ "htm" => "text/html",
16
+ "css" => "text/css",
17
+ "csv" => "text/csv",
18
+ "xml" => "application/xml",
19
+ "txt" => "text/plain",
20
+ "png" => "image/png",
21
+ "jpg" => "image/jpeg",
22
+ "jpeg" => "image/jpeg",
23
+ "gif" => "image/gif",
24
+ "webp" => "image/webp",
25
+ "avif" => "image/avif",
26
+ "svg" => "image/svg+xml",
27
+ "ico" => "image/x-icon",
28
+ "pdf" => "application/pdf",
29
+ "zip" => "application/zip",
30
+ "gz" => "application/gzip",
31
+ "wasm" => "application/wasm",
32
+ "map" => "application/json",
33
+ "woff" => "font/woff",
34
+ "woff2" => "font/woff2",
35
+ "ttf" => "font/ttf",
36
+ "otf" => "font/otf",
37
+ "mp4" => "video/mp4",
38
+ "webm" => "video/webm",
39
+ "mp3" => "audio/mpeg",
40
+ "ogg" => "audio/ogg",
41
+ };
42
+
43
+ /// MIME lookup core over a raw byte slice. Shared by the scalar napi path and
44
+ /// the packed batch path. Unknown extensions fall back to mime_guess, then to
45
+ /// `application/octet-stream`.
46
+ pub fn mime_from_extension_bytes(ext: &[u8]) -> Vec<u8> {
47
+ let text = match std::str::from_utf8(ext) {
48
+ Ok(text) => text,
49
+ Err(_) => return OCTET_STREAM.as_bytes().to_vec(),
50
+ };
51
+
52
+ let text = text.trim_start_matches('.');
53
+
54
+ let mut stack = [0u8; 64];
55
+
56
+ let lower_owned: String;
57
+
58
+ let lower: &str = if text.len() <= stack.len() {
59
+ for (i, b) in text.bytes().enumerate() {
60
+ stack[i] = b.to_ascii_lowercase();
61
+ }
62
+
63
+ match std::str::from_utf8(&stack[..text.len()]) {
64
+ Ok(s) => s,
65
+ Err(_) => return OCTET_STREAM.as_bytes().to_vec(),
66
+ }
67
+ } else {
68
+ lower_owned = text.to_ascii_lowercase();
69
+ &lower_owned
70
+ };
71
+
72
+ if let Some(mime) = MIME_TABLE.get(lower) {
73
+ return mime.as_bytes().to_vec();
74
+ }
75
+
76
+ let guessed = mime_guess::from_ext(lower).first_or_octet_stream();
77
+ guessed.essence_str().as_bytes().to_vec()
78
+ }
79
+
80
+ #[napi]
81
+ pub fn mime_from_extension(ext: Uint8Array) -> Buffer {
82
+ Buffer::from(mime_from_extension_bytes(ext.as_ref()))
83
+ }
84
+
85
+ /// Packed MIME-lookup batch: `[u32 count]{[u32 len][ext]}` in →
86
+ /// `[u32 count]{[u32 len][mime]}` out (octet-stream for unknown/empty).
87
+ #[napi]
88
+ pub fn mime_from_extension_batch_packed(input: Uint8Array) -> Result<Buffer> {
89
+ crate::util::run_packed_batch(input.as_ref(), mime_from_extension_bytes).map(Buffer::from)
90
+ }
91
+
92
+ #[cfg(test)]
93
+ mod tests {
94
+ use super::*;
95
+
96
+ fn mime(input: &str) -> String {
97
+ let out = mime_from_extension(Uint8Array::new(input.as_bytes().to_vec()));
98
+ String::from_utf8_lossy(out.as_ref()).into_owned()
99
+ }
100
+
101
+ #[test]
102
+ fn known_extensions() {
103
+ assert_eq!(mime(".js"), "text/javascript");
104
+ assert_eq!(mime("json"), "application/json");
105
+ assert_eq!(mime(".html"), "text/html");
106
+ assert_eq!(mime(".png"), "image/png");
107
+ assert_eq!(mime(".pdf"), "application/pdf");
108
+ }
109
+
110
+ #[test]
111
+ fn case_insensitive() {
112
+ assert_eq!(mime(".JS"), "text/javascript");
113
+ assert_eq!(mime(".Jpg"), "image/jpeg");
114
+ }
115
+
116
+ #[test]
117
+ fn unknown_and_empty_fall_back_to_octet_stream() {
118
+ assert_eq!(mime(".xkrq"), "application/octet-stream");
119
+ assert_eq!(mime(""), "application/octet-stream");
120
+ }
121
+
122
+ #[test]
123
+ fn non_utf8_falls_back_to_octet_stream() {
124
+ let out = mime_from_extension(Uint8Array::new(vec![0xff, 0xfe, 0xfd]));
125
+ assert_eq!(
126
+ String::from_utf8_lossy(out.as_ref()),
127
+ "application/octet-stream"
128
+ );
129
+ }
130
+ }
@@ -0,0 +1,32 @@
1
+ //! HTTP wire formats & parsing (zero-alloc parsers + wire helpers).
2
+ //
3
+ // Zero-alloc parsers and wire-format helpers for the HTTP surface:
4
+ // - headers.rs zero-alloc packed-header parser (HeaderRefs)
5
+ // - method.rs HTTP method classification
6
+ // - http_parser.rs HTTP request line + headers → packed request
7
+ // - cookie_parser.rs cookie header → packed pairs
8
+ // - query_parser.rs query/form-string → packed pairs
9
+ // - form.rs x-www-form-urlencoded body parser (FormParser instance)
10
+ // - media_type.rs Content-Type parser + wildcard matching instances
11
+ // - url_codec.rs percent-encoding encode/decode
12
+ // - url_join.rs RFC 3986 url_resolve + query builder (UrlBuilder)
13
+ // - etag.rs HTTP cache semantics (etag / conditional / 304)
14
+ // - http_date.rs HTTP-date (IMF-fixdate) format + parse
15
+ // - accept.rs Accept-Encoding negotiation (AcceptNegotiator)
16
+ // - mime_lookup.rs extension → MIME type (phf table)
17
+ // - multipart.rs multipart/form-data parser (+ limits)
18
+
19
+ pub mod accept;
20
+ pub mod cookie_parser;
21
+ pub mod etag;
22
+ pub mod form;
23
+ pub mod headers;
24
+ pub mod http_date;
25
+ pub mod http_parser;
26
+ pub mod media_type;
27
+ pub mod method;
28
+ pub mod mime_lookup;
29
+ pub mod multipart;
30
+ pub mod query_parser;
31
+ pub mod url_codec;
32
+ pub mod url_join;