1use crate::bufvec::BufList;
14use bytes::{Buf, BufMut, Bytes, BytesMut};
15#[cfg(feature = "web_server")]
16use log::{debug, trace};
17use std::{iter::{Extend, FromIterator, IntoIterator}, fmt::Display};
18
19#[derive(Debug, Clone)]
21pub(crate) struct Header {
22 version: u8,
23 rtype: RecordType,
24 request_id: u16, content_length: u16,
26 padding_length: u8, }
29pub struct BeginRequestBody {
30 role: FastCGIRole,
31 flags: u8,
32 }
34pub struct EndRequestBody {
35 pub app_status: u32,
36 pub protocol_status: ProtoStatus,
37 }
39pub struct UnknownTypeBody {
40 pub rtype: u8,
41 }
43
44pub struct NameValuePair {
45 pub name_data: Bytes,
46 pub value_data: Bytes,
47}
48pub struct NVBody {
51 pairs: BufList<Bytes>,
52 len: u16,
53}
54pub struct NVBodyList {
57 bodies: Vec<NVBody>,
58}
59pub struct STDINBody {}
60pub enum Body {
61 BeginRequest(BeginRequestBody),
62 EndRequest(EndRequestBody),
63 UnknownType(UnknownTypeBody),
64 Params(NVBody),
65 StdIn(Bytes),
66 StdErr(Bytes),
67 StdOut(Bytes),
68 Abort,
69 GetValues(NVBody),
70 GetValuesResult(NVBody),
71}
72pub struct Record {
74 header: Header,
75 pub body: Body,
76}
77
78pub const LISTENSOCK_FILENO: u8 = 0;
80
81impl Body {
82 const MAX_LENGTH: usize = 0xffff;
84}
85
86impl Header {
87 #[cfg(feature = "codec")]
91 pub const HEADER_LEN: usize = 8;
92
93 const VERSION_1: u8 = 1;
95}
96
97impl Record {
98 pub const MGMT_REQUEST_ID: u16 = 0;
100}
101#[derive(Debug, Clone, Copy)]
103#[repr(u8)]
104pub enum RecordType {
105 BeginRequest = 1,
107 AbortRequest = 2,
109 EndRequest = 3,
111 Params = 4,
113 StdIn = 5,
115 StdOut = 6,
117 StdErr = 7,
119 Data = 8,
121 GetValues = 9,
124 GetValuesResult = 10,
127 #[cfg(feature = "web_server")]
129 UnknownType = 11,
130}
131impl TryFrom<u8> for RecordType {
132 type Error = u8;
133 fn try_from(value: u8) -> Result<Self, Self::Error> {
134 match value {
135 1 => Ok(RecordType::BeginRequest),
136 2 => Ok(RecordType::AbortRequest),
137 3 => Ok(RecordType::EndRequest),
138 4 => Ok(RecordType::Params),
139 5 => Ok(RecordType::StdIn),
140 6 => Ok(RecordType::StdOut),
141 7 => Ok(RecordType::StdErr),
142 8 => Ok(RecordType::Data),
143 9 => Ok(RecordType::GetValues),
144 10 => Ok(RecordType::GetValuesResult),
145 #[cfg(feature = "web_server")]
146 11 => Ok(RecordType::UnknownType),
147 o => Err(o),
148 }
149 }
150}
151impl Display for RecordType {
152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153 match self {
154 RecordType::BeginRequest => f.write_str("BeginRequest"),
156 RecordType::AbortRequest => f.write_str("AbortRequest"),
157 RecordType::EndRequest => f.write_str("EndRequest"),
158 RecordType::Params => f.write_str("Params"),
159 RecordType::StdIn => f.write_str("StdIn"),
160 RecordType::StdOut => f.write_str("StdOut"),
161 RecordType::StdErr => f.write_str("StdErr"),
162 RecordType::Data => f.write_str("Data"),
163 RecordType::GetValues => f.write_str("GetValues"),
164 RecordType::GetValuesResult => f.write_str("GetValuesResult"),
165 #[cfg(feature = "web_server")]
166 RecordType::UnknownType => f.write_str("UnknownType"),
167 }
168 }
169}
170impl BeginRequestBody {
171 pub const KEEP_CONN: u8 = 1;
173}
174#[repr(u16)]
176pub enum FastCGIRole {
177 Responder = 1,
179 Authorizer = 2,
181 Filter = 3
182}
183#[repr(u8)]
185pub enum ProtoStatus {
186 Complete = 0,
188 CantMpxCon = 1,
190 Overloaded = 2,
192 UnknownRole = 3,
194}
195impl TryFrom<u8> for ProtoStatus {
196 type Error = u8;
197 fn try_from(value: u8) -> Result<Self, Self::Error> {
198 match value {
199 0 => Ok(ProtoStatus::Complete),
200 1 => Ok(ProtoStatus::CantMpxCon),
201 2 => Ok(ProtoStatus::Overloaded),
202 3 => Ok(ProtoStatus::UnknownRole),
203 o => Err(o),
204 }
205 }
206}
207impl Display for ProtoStatus {
208 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209 match self {
210 ProtoStatus::Complete => f.write_str("Complete"),
211 ProtoStatus::CantMpxCon => f.write_str("CantMpxCon"),
212 ProtoStatus::Overloaded => f.write_str("Overloaded"),
213 ProtoStatus::UnknownRole => f.write_str("UnknownRole"),
214 }
215 }
216}
217pub const MAX_CONNS: &[u8] = b"MAX_CONNS";
222
223pub const MAX_REQS: &[u8] = b"MAX_REQS";
228
229pub const MPXS_CONNS: &[u8] = b"MPXS_CONNS";
234
235impl NameValuePair {
238 pub fn parse(data: &mut Bytes) -> NameValuePair {
241 let mut pos: usize = 0;
242 let key_length = NameValuePair::param_length(data, &mut pos);
243 let value_length = NameValuePair::param_length(data, &mut pos);
244 let key = data.slice(pos..pos + key_length);
245 pos += key_length;
246 let value = data.slice(pos..pos + value_length);
247 pos += value_length;
248 data.advance(pos);
249
250 NameValuePair {
251 name_data: key,
252 value_data: value,
253 }
254 }
255 pub fn new(name_data: Bytes, value_data: Bytes) -> NameValuePair {
256 NameValuePair {
257 name_data,
258 value_data,
259 }
260 }
261
262 fn param_length(data: &Bytes, pos: &mut usize) -> usize {
263 let mut length: usize = data[*pos] as usize;
264
265 if (length >> 7) == 1 {
266 length = (data.slice(*pos + 1..(*pos + 4)).get_u32() & 0x7FFFFFFF) as usize;
267
268 *pos += 4;
269 } else {
270 *pos += 1;
271 }
272
273 length
274 }
275 pub fn len(&self) -> usize {
276 let ln = self.name_data.len();
277 let lv = self.value_data.len();
278 let mut lf: usize = ln + lv + 2;
279 if ln > 0x7f {
280 lf += 3;
281 }
282 if lv > 0x7f {
283 lf += 3;
284 }
285 lf
286 }
287}
288
289impl STDINBody {
290 #[allow(clippy::new_ret_no_self)]
293 pub fn new(request_id: u16, b: &mut dyn Buf) -> Record {
294 let mut rec_data = b.take(Body::MAX_LENGTH);
295 let len = rec_data.remaining();
296
297 Record {
298 header: Header::new(RecordType::StdIn, request_id, len as u16),
299 body: Body::StdIn(rec_data.copy_to_bytes(len)),
300 }
301 }
302}
303
304impl Header {
305 pub fn new(rtype: RecordType, request_id: u16, len: u16) -> Header {
306 let mut pad: u8 = (len % 8) as u8;
307 if pad != 0 {
308 pad = 8 - pad;
309 }
310 Header {
311 version: Header::VERSION_1,
312 rtype,
313 request_id,
314 content_length: len,
315 padding_length: pad,
316 }
317 }
318 pub fn write_into<BM: BufMut>(self, data: &mut BM) {
319 data.put_u8(self.version);
320 data.put_u8(self.rtype as u8);
321 data.put_u16(self.request_id);
322 data.put_u16(self.content_length);
323 data.put_u8(self.padding_length);
324 data.put_u8(0); }
327 #[cfg(feature = "web_server")]
328 fn parse(data: &mut Bytes) -> Header {
329 let h = Header {
330 version: data.get_u8(),
331 rtype: data.get_u8().try_into().expect("not a fcgi type"),
332 request_id: data.get_u16(),
333 content_length: data.get_u16(),
334 padding_length: data.get_u8(),
335 };
336 data.advance(1); h
338 }
339 #[inline]
340 #[cfg(feature = "codec")]
341 pub fn get_padding(&self) -> u8 {
342 self.padding_length
343 }
344}
345
346impl BeginRequestBody {
347 #[allow(clippy::new_ret_no_self)]
349 pub fn new(role: FastCGIRole, flags: u8, request_id: u16) -> Record {
350 Record {
351 header: Header {
352 version: Header::VERSION_1,
353 rtype: RecordType::BeginRequest,
354 request_id,
355 content_length: 8,
356 padding_length: 0,
357 },
358 body: Body::BeginRequest(BeginRequestBody { role, flags }),
359 }
360 }
361}
362#[cfg(feature = "web_server")]
365pub(crate) struct RecordReader {
366 current: Option<Header>,
367}
368#[cfg(feature = "web_server")]
369impl RecordReader {
370 pub(crate) fn new() -> RecordReader {
371 RecordReader { current: None }
372 }
373 pub(crate) fn read(&mut self, data: &mut Bytes) -> Option<Record> {
374 let mut full_header = match self.current.take() {
375 Some(h) => h,
376 None => {
377 if data.remaining() < 8 {
379 return None;
380 }
381 debug!("new header");
382 Header::parse(data)
383 }
384 };
385 let mut body_len = full_header.content_length as usize;
386 let header = if data.remaining() < body_len {
387 let mut nh = full_header.clone();
388 body_len = data.remaining();
389 nh.content_length = body_len as u16;
390 nh.padding_length = 0;
391
392 full_header.content_length -= body_len as u16;
394 self.current = Some(full_header);
395 if body_len < 1 {
397 return None;
398 }
399
400 debug!("more later, now:");
401 nh
402 } else {
403 full_header
404 };
405
406 debug!("read type {}", header.rtype);
407 trace!("payload: {:?}", &data.slice(..body_len));
408 let body = data.slice(0..body_len);
409 data.advance(body_len);
410
411 if data.remaining() < header.padding_length as usize {
412 if body_len < 1 {
414 self.current = Some(header);
416 return None;
417 }
418 let mut nh = header.clone();
419 nh.content_length = 0;
420 self.current = Some(nh);
421 debug!("padding {} is still missing", header.padding_length);
422 } else {
423 data.advance(header.padding_length as usize);
424 }
425
426 let body = Record::parse_body(body, header.rtype);
427 Some(Record { header, body })
428 }
429}
430impl Record {
431 #[cfg(feature = "web_server")]
432 pub(crate) fn get_request_id(&self) -> u16 {
433 self.header.request_id
434 }
435 #[cfg(feature = "con_pool")]
438 pub(crate) fn read(data: &mut Bytes) -> Option<Record> {
439 if data.remaining() < 8 {
441 return None;
442 }
443 let header = Header::parse(&mut data.slice(..));
444 let len = header.content_length as usize + header.padding_length as usize;
445 if data.remaining() < len + 8 {
446 return None;
447 }
448 data.advance(8);
449 debug!("read type {}", header.rtype);
450 trace!(
451 "payload: {:?}",
452 &data.slice(..header.content_length as usize)
453 );
454 let body = data.slice(0..header.content_length as usize);
455 data.advance(len);
456 let body = Record::parse_body(body, header.rtype);
457 Some(Record { header, body })
458 }
459 #[cfg(feature = "web_server")]
460 fn parse_body(mut payload: Bytes, ptype: RecordType) -> Body {
461 match ptype {
462 RecordType::StdOut => Body::StdOut(payload),
463 RecordType::StdErr => Body::StdErr(payload),
464 RecordType::EndRequest => Body::EndRequest(EndRequestBody::parse(payload)),
465 RecordType::UnknownType => {
466 let rtype = payload.get_u8();
467 payload.advance(7);
468 Body::UnknownType(UnknownTypeBody { rtype })
469 }
470 RecordType::GetValuesResult => Body::GetValuesResult(NVBody::from_bytes(payload)),
471 RecordType::GetValues => Body::GetValues(NVBody::from_bytes(payload)),
472 RecordType::Params => Body::Params(NVBody::from_bytes(payload)),
473 _ => panic!("not impl"),
474 }
475 }
476 pub fn append<BM: BufMut>(self, buf: &mut BM) {
478 match self.body {
479 Body::BeginRequest(brb) => {
480 self.header.write_into(buf);
481 brb.write_into(buf);
482 }
483 Body::Params(nvs) | Body::GetValues(nvs) | Body::GetValuesResult(nvs) => {
484 let pad = self.header.padding_length as usize;
485 self.header.write_into(buf);
486 let kvp = nvs.drain().0;
487 buf.put(kvp);
488 unsafe {
489 buf.advance_mut(pad);
490 } }
492 Body::StdIn(b) => {
493 let pad = self.header.padding_length as usize;
494 self.header.write_into(buf);
495 if !b.has_remaining() {
496 debug_assert!(pad == 0);
498 return;
499 }
500 buf.put(b);
501 unsafe {
502 buf.advance_mut(pad);
503 } }
505 Body::Abort => {
506 self.header.write_into(buf);
507 }
508 _ => panic!("not impl"),
509 }
510 }
511}
512impl NVBody {
513 pub fn new() -> NVBody {
514 NVBody {
515 pairs: BufList::new(),
516 len: 0,
517 }
518 }
519 pub fn to_record(self, rtype: RecordType, request_id: u16) -> Record {
520 let mut pad: u8 = (self.len % 8) as u8;
521 if pad != 0 {
522 pad = 8 - pad;
523 }
524 Record {
525 header: Header {
526 version: Header::VERSION_1,
527 rtype,
528 request_id,
529 content_length: self.len,
530 padding_length: pad,
531 },
532 body: match rtype {
533 RecordType::Params => Body::Params(self),
534 RecordType::GetValues => Body::GetValues(self),
535 RecordType::GetValuesResult => Body::GetValuesResult(self),
536 _ => panic!("No valid type"),
537 },
538 }
539 }
540 pub fn fits(&self, pair: &NameValuePair) -> bool {
541 let l = pair.len() + self.len as usize;
542 l <= Body::MAX_LENGTH
543 }
544 pub fn add(&mut self, pair: NameValuePair) -> Result<(), ()> {
545 let l = pair.len() + self.len as usize;
546 if l > Body::MAX_LENGTH {
547 return Err(()); }
549 self.len = l as u16;
550
551 let mut ln = pair.name_data.len();
552 let mut lv = pair.value_data.len();
553 if ln + lv > Body::MAX_LENGTH {
554 return Err(()); }
556 let mut lf: usize = 2;
557 if ln > 0x7f {
558 if ln > 0x7fffffff {
559 return Err(());
560 }
561 lf += 3;
562 ln |= 0x80000000;
563 }
564 if lv > 0x7f {
565 if lv > 0x7fffffff {
566 return Err(());
567 }
568 lf += 3;
569 lv |= 0x80000000;
570 }
571 let mut data: BytesMut = BytesMut::with_capacity(lf);
572 if ln > 0x7f {
573 data.put_u32(ln as u32);
574 } else {
575 data.put_u8(ln as u8);
576 }
577 if lv > 0x7f {
578 data.put_u32(lv as u32);
579 } else {
580 data.put_u8(lv as u8);
581 }
582 self.pairs.push(data.freeze());
584 self.pairs.push(pair.name_data);
585 if lv > 0 {
586 self.pairs.push(pair.value_data);
587 }
588 Ok(())
589 }
590 #[cfg(feature = "web_server")]
591 pub(crate) fn from_bytes(buf: Bytes) -> NVBody {
592 let mut b = NVBody::new();
593 b.len = buf.remaining() as u16;
594 if b.len > 0 {
595 b.pairs.push(buf);
596 }
597 b
598 }
599 pub fn drain(mut self) -> NVDrain {
600 NVDrain(self.pairs.copy_to_bytes(self.len as usize))
601 }
602}
603pub struct NVDrain(Bytes);
604
605impl Iterator for NVDrain {
606 type Item = NameValuePair;
607
608 fn next(&mut self) -> Option<NameValuePair> {
610 if !self.0.has_remaining() {
611 return None;
612 }
613 Some(NameValuePair::parse(&mut self.0))
614 }
615
616 fn size_hint(&self) -> (usize, Option<usize>) {
617 (1, None)
618 }
619}
620
621impl NVBodyList {
622 pub fn new() -> NVBodyList {
623 NVBodyList {
624 bodies: vec![NVBody::new()],
625 }
626 }
627 pub fn add(&mut self, pair: NameValuePair) {
630 let mut nv = self.bodies.last_mut().unwrap(); if !nv.fits(&pair) {
632 let new = NVBody::new();
633 self.bodies.push(new);
634 nv = self.bodies.last_mut().unwrap(); }
636 nv.add(pair).expect("KVPair bigger that 0xFFFF");
637 }
638}
639
640impl FromIterator<(Bytes, Bytes)> for NVBodyList {
641 fn from_iter<T: IntoIterator<Item = (Bytes, Bytes)>>(iter: T) -> NVBodyList {
642 let mut nv = NVBodyList::new();
643 nv.extend(iter);
644 nv
645 }
646}
647
648impl Extend<(Bytes, Bytes)> for NVBodyList {
649 #[inline]
650 fn extend<T: IntoIterator<Item = (Bytes, Bytes)>>(&mut self, iter: T) {
651 for (k, v) in iter {
652 self.add(NameValuePair::new(k, v));
653 }
654 }
655}
656
657impl BeginRequestBody {
658 pub fn write_into<BM: BufMut>(self, data: &mut BM) {
659 data.put_u16(self.role as u16);
660 data.put_u8(self.flags);
661 data.put_slice(&[0; 5]); }
663}
664
665impl EndRequestBody {
666 #[cfg(feature = "web_server")]
667 fn parse(mut data: Bytes) -> EndRequestBody {
668 let b = EndRequestBody {
669 app_status: data.get_u32(),
670 protocol_status: data.get_u8().try_into().expect("not a fcgi status"),
671 };
672 data.advance(3); b
674 }
675}
676
677impl std::fmt::Debug for NameValuePair {
678 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
679 write!(f, "{:?} = {:?}", self.name_data, self.value_data)
680 }
681}
682
683#[test]
684fn encode_simple_get() {
685 let mut b = BytesMut::with_capacity(80);
686 BeginRequestBody::new(FastCGIRole::Responder, 0, 1).append(&mut b);
687 let mut nv = NVBody::new();
688 nv.add(NameValuePair::new(
689 Bytes::from(&b"SCRIPT_FILENAME"[..]),
690 Bytes::from(&b"/home/daniel/Public/test.php"[..]),
691 ))
692 .expect("record full");
693 nv.to_record(RecordType::Params, 1).append(&mut b);
694 NVBody::new().to_record(RecordType::Params, 1).append(&mut b);
695
696 let mut dst = [0; 80];
697 b.copy_to_slice(&mut dst);
698
699 let expected = b"\x01\x01\0\x01\0\x08\0\0\0\x01\0\0\0\0\0\0\x01\x04\0\x01\0-\x03\0\x0f\x1cSCRIPT_FILENAME/home/daniel/Public/test.php\x01\x04\0\x01\x04\0\x01\0\0\0\0";
700
701 assert_eq!(dst[..69], expected[..69]);
702 assert_eq!(dst[72..], expected[72..]);
704}
705#[test]
706fn encode_post() {
707 let mut b = BytesMut::with_capacity(104);
708 BeginRequestBody::new(FastCGIRole::Responder, 0, 1).append(&mut b);
709 let mut nv = NVBody::new();
710 nv.add(NameValuePair::new(
711 Bytes::from(&b"SCRIPT_FILENAME"[..]),
712 Bytes::from(&b"/home/daniel/Public/test.php"[..]),
713 ))
714 .expect("record full");
715 nv.to_record(RecordType::Params, 1).append(&mut b);
716 NVBody::new().to_record(RecordType::Params, 1).append(&mut b);
717 STDINBody::new(1, &mut Bytes::from(&b"a=b"[..])).append(&mut b);
718 STDINBody::new(1, &mut Bytes::new()).append(&mut b);
719
720 let mut dst = [0; 104];
721 b.copy_to_slice(&mut dst);
722 dst[69] = 0xFF;
724 dst[70] = 0xFF;
725 dst[71] = 0xFF;
726 dst[91] = 0xFF;
727 dst[92] = 0xFF;
728 dst[93] = 0xFF;
729 dst[94] = 0xFF;
730 dst[95] = 0xFF;
731 let expected = b"\x01\x01\0\x01\0\x08\0\0\0\x01\0\0\0\0\0\0\x01\x04\0\x01\0-\x03\0\x0f\x1cSCRIPT_FILENAME/home/daniel/Public/test.php\xff\xff\xff\x01\x04\0\x01\0\0\0\0\x01\x05\0\x01\0\x03\x05\0a=b\xff\xff\xff\xff\xff\x01\x05\0\x01\0\0\0\0";
732
733 assert_eq!(dst, &expected[..]);
734}
735
736#[test]
737fn encode_long_param() {
738 let mut b = BytesMut::with_capacity(190);
739 BeginRequestBody::new(FastCGIRole::Responder, 0, 1).append(&mut b);
740 let mut nv = NVBody::new();
741 nv.add(NameValuePair::new(
742 Bytes::from(&b"HTTP_ACCEPT"[..]),
743 Bytes::from(&b"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"[..]),
744 ))
745 .expect("record full");
746 nv.to_record(RecordType::Params, 1).append(&mut b);
747 NVBody::new().to_record(RecordType::Params, 1).append(&mut b);
748
749 let mut dst = [0; 184];
750 b.copy_to_slice(&mut dst);
751
752 dst[175]=1;
754
755 let expected = b"\x01\x01\0\x01\0\x08\0\0\0\x01\0\0\0\0\0\0\x01\x04\0\x01\0\x97\x01\0\x0b\x80\0\0\x87HTTP_ACCEPTtext/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\x01\x01\x04\0\x01\0\0\0\0";
756
757 assert_eq!(dst, expected[..]);
758}