1use std::marker::PhantomData;
24
25use super::error::RestError;
26use nexus_net::buf::WriteBuf;
27
28pub struct Query;
34pub struct Headers;
36pub struct Ready;
38
39mod sealed {
40 pub trait Phase {}
41 impl Phase for super::Query {}
42 impl Phase for super::Headers {}
43 impl Phase for super::Ready {}
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum Method {
53 Get,
55 Post,
57 Put,
59 Delete,
61 Patch,
63}
64
65impl Method {
66 pub fn as_str(self) -> &'static str {
68 match self {
69 Self::Get => "GET",
70 Self::Post => "POST",
71 Self::Put => "PUT",
72 Self::Delete => "DELETE",
73 Self::Patch => "PATCH",
74 }
75 }
76}
77
78impl std::fmt::Display for Method {
79 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80 f.write_str(self.as_str())
81 }
82}
83
84#[derive(Clone)]
103pub struct Request<'a> {
104 data: &'a [u8],
105}
106
107impl<'a> Request<'a> {
108 pub fn as_bytes(&self) -> &[u8] {
110 self.data
111 }
112
113 pub fn into_bytes(self) -> &'a [u8] {
124 self.data
125 }
126
127 pub fn len(&self) -> usize {
129 self.data.len()
130 }
131
132 pub fn is_empty(&self) -> bool {
134 self.data.is_empty()
135 }
136}
137
138impl std::fmt::Debug for Request<'_> {
139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140 f.debug_struct("Request")
141 .field("len", &self.data.len())
142 .finish()
143 }
144}
145
146const UNRESERVED: [bool; 256] = {
152 let mut table = [false; 256];
153 let mut i = 0;
154 while i < 256 {
155 table[i] = matches!(
156 i as u8,
157 b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'.' | b'_' | b'~'
158 );
159 i += 1;
160 }
161 table
162};
163
164const HEX_UPPER: &[u8; 16] = b"0123456789ABCDEF";
165
166fn append_percent_encoded(buf: &mut WriteBuf, input: &[u8], error: &mut Option<RestError>) {
170 if error.is_some() {
171 return;
172 }
173 let mut i = 0;
174 while i < input.len() {
175 let run_start = i;
177 while i < input.len() && UNRESERVED[input[i] as usize] {
178 i += 1;
179 }
180 if i > run_start {
182 checked_append(buf, &input[run_start..i], error);
183 if error.is_some() {
184 return;
185 }
186 }
187 if i < input.len() {
189 let b = input[i];
190 checked_append(
191 buf,
192 &[
193 b'%',
194 HEX_UPPER[(b >> 4) as usize],
195 HEX_UPPER[(b & 0xf) as usize],
196 ],
197 error,
198 );
199 if error.is_some() {
200 return;
201 }
202 i += 1;
203 }
204 }
205}
206
207fn checked_append(buf: &mut WriteBuf, src: &[u8], error: &mut Option<RestError>) {
213 if error.is_some() {
214 return;
215 }
216 if src.len() > buf.tailroom() {
217 *error = Some(RestError::RequestTooLarge {
218 capacity: buf.len() + buf.tailroom(),
219 });
220 return;
221 }
222 buf.append(src);
223}
224
225fn has_crlf(s: &str) -> bool {
227 s.bytes().any(|b| b == b'\r' || b == b'\n')
228}
229
230fn write_usize_ascii(buf: &mut WriteBuf, n: usize, error: &mut Option<RestError>) {
232 if n == 0 {
233 checked_append(buf, b"0", error);
234 return;
235 }
236 let mut digits = [0u8; 20]; let mut i = 20;
238 let mut val = n;
239 while val > 0 {
240 i -= 1;
241 digits[i] = (val % 10) as u8 + b'0';
242 val /= 10;
243 }
244 checked_append(buf, &digits[i..], error);
245}
246
247fn seal_request_line(writer: &mut RequestWriter, error: &mut Option<RestError>) {
249 checked_append(&mut writer.write_buf, b" HTTP/1.1\r\n", error);
250 checked_append(&mut writer.write_buf, &writer.host_wire, error);
252 if !writer.default_headers_wire.is_empty() {
253 checked_append(&mut writer.write_buf, &writer.default_headers_wire, error);
254 }
255}
256
257fn write_body(buf: &mut WriteBuf, body: &[u8], error: &mut Option<RestError>) {
259 checked_append(buf, b"Content-Length: ", error);
260 write_usize_ascii(buf, body.len(), error);
261 checked_append(buf, b"\r\n\r\n", error);
262 checked_append(buf, body, error);
263}
264
265fn write_body_header(buf: &mut WriteBuf, body_len: usize, error: &mut Option<RestError>) {
267 checked_append(buf, b"Content-Length: ", error);
268 write_usize_ascii(buf, body_len, error);
269 checked_append(buf, b"\r\n\r\n", error);
270}
271
272const CL_PREFIX: &[u8] = b"Content-Length: ";
275const CL_PAD_LEN: usize = 20;
276const CL_SUFFIX: &[u8] = b"\r\n\r\n";
277fn write_content_length_placeholder(buf: &mut WriteBuf, error: &mut Option<RestError>) -> usize {
280 checked_append(buf, CL_PREFIX, error);
281 let num_offset = buf.len();
282 checked_append(buf, b"00000000000000000000", error); checked_append(buf, CL_SUFFIX, error);
284 num_offset
285}
286
287pub type BodyWriter<'a> = nexus_net::buf::WriteBufWriter<'a>;
292
293fn backfill_content_length(buf: &mut WriteBuf, num_offset: usize, body_len: usize) {
298 let mut digits = [0u8; 20];
300 let digit_len = if body_len == 0 {
301 digits[0] = b'0';
302 1
303 } else {
304 let mut val = body_len;
305 let mut i = 20;
306 while val > 0 {
307 i -= 1;
308 digits[i] = (val % 10) as u8 + b'0';
309 val /= 10;
310 }
311 let len = 20 - i;
312 digits.copy_within(i..20, 0);
313 len
314 };
315
316 let gap = CL_PAD_LEN - digit_len;
317
318 {
319 let data = buf.data_mut();
320
321 data[num_offset..num_offset + digit_len].copy_from_slice(&digits[..digit_len]);
323
324 let suffix_dst = num_offset + digit_len;
326 data[suffix_dst..suffix_dst + CL_SUFFIX.len()].copy_from_slice(CL_SUFFIX);
327
328 if gap > 0 {
330 let body_start = num_offset + CL_PAD_LEN + CL_SUFFIX.len();
331 let body_end = data.len();
332 if body_start < body_end {
333 data.copy_within(body_start..body_end, body_start - gap);
334 }
335 }
336 }
337
338 if gap > 0 {
340 buf.shrink_tail(gap);
341 }
342}
343
344pub struct RequestWriter {
370 write_buf: WriteBuf,
371 host_wire: Vec<u8>,
373 default_headers_wire: Vec<u8>,
375 base_path: Vec<u8>,
377}
378
379impl RequestWriter {
380 pub fn new(host: &str) -> Result<Self, RestError> {
389 if host.bytes().any(|b| b == b'\r' || b == b'\n') {
390 return Err(RestError::CrlfInjection);
391 }
392
393 let mut host_wire = Vec::with_capacity(host.len() + 32);
394 host_wire.extend_from_slice(b"Host: ");
395 host_wire.extend_from_slice(host.as_bytes());
396 host_wire.extend_from_slice(b"\r\nConnection: keep-alive\r\n");
397
398 Ok(Self {
399 write_buf: WriteBuf::new(32 * 1024, 0),
400 host_wire,
401 default_headers_wire: Vec::new(),
402 base_path: Vec::new(),
403 })
404 }
405
406 pub fn set_write_buffer_capacity(&mut self, capacity: usize) {
414 self.write_buf = WriteBuf::new(capacity, 0);
415 }
416
417 pub fn default_header(&mut self, name: &str, value: &str) -> Result<(), RestError> {
425 if has_crlf(name) || has_crlf(value) {
426 return Err(RestError::CrlfInjection);
427 }
428 self.default_headers_wire.extend_from_slice(name.as_bytes());
429 self.default_headers_wire.extend_from_slice(b": ");
430 self.default_headers_wire
431 .extend_from_slice(value.as_bytes());
432 self.default_headers_wire.extend_from_slice(b"\r\n");
433 Ok(())
434 }
435
436 pub fn set_base_path(&mut self, path: &str) -> Result<(), RestError> {
444 if has_crlf(path) {
445 return Err(RestError::CrlfInjection);
446 }
447 self.base_path = path.trim_end_matches('/').as_bytes().to_vec();
448 Ok(())
449 }
450
451 pub fn get(&mut self, path: &str) -> RequestBuilder<'_> {
457 self.request(Method::Get, path)
458 }
459
460 pub fn post(&mut self, path: &str) -> RequestBuilder<'_> {
462 self.request(Method::Post, path)
463 }
464
465 pub fn put(&mut self, path: &str) -> RequestBuilder<'_> {
467 self.request(Method::Put, path)
468 }
469
470 pub fn delete(&mut self, path: &str) -> RequestBuilder<'_> {
472 self.request(Method::Delete, path)
473 }
474
475 pub fn request(&mut self, method: Method, path: &str) -> RequestBuilder<'_> {
477 RequestBuilder::new(self, method, path)
478 }
479
480 pub fn get_raw(&mut self, path: &str) -> RequestBuilder<'_, Headers> {
488 self.request_raw(Method::Get, path)
489 }
490
491 pub fn post_raw(&mut self, path: &str) -> RequestBuilder<'_, Headers> {
493 self.request_raw(Method::Post, path)
494 }
495
496 pub fn put_raw(&mut self, path: &str) -> RequestBuilder<'_, Headers> {
498 self.request_raw(Method::Put, path)
499 }
500
501 pub fn delete_raw(&mut self, path: &str) -> RequestBuilder<'_, Headers> {
503 self.request_raw(Method::Delete, path)
504 }
505
506 pub fn request_raw(&mut self, method: Method, path: &str) -> RequestBuilder<'_, Headers> {
508 RequestBuilder::new_sealed(self, method, path)
509 }
510}
511
512#[must_use = "request must be finished with .finish()"]
522pub struct RequestBuilder<'a, P: sealed::Phase = Query> {
523 writer: &'a mut RequestWriter,
524 has_query: bool,
525 error: Option<RestError>,
526 _phase: PhantomData<P>,
527}
528
529impl<'a> RequestBuilder<'a, Query> {
534 pub(crate) fn new(writer: &'a mut RequestWriter, method: Method, path: &str) -> Self {
535 writer.write_buf.clear();
536 let mut error = if has_crlf(path) {
537 Some(RestError::CrlfInjection)
538 } else {
539 None
540 };
541 checked_append(
542 &mut writer.write_buf,
543 method.as_str().as_bytes(),
544 &mut error,
545 );
546 checked_append(&mut writer.write_buf, b" ", &mut error);
547 if !writer.base_path.is_empty() {
548 checked_append(&mut writer.write_buf, &writer.base_path, &mut error);
549 }
550 checked_append(&mut writer.write_buf, path.as_bytes(), &mut error);
551 Self {
552 writer,
553 has_query: path.contains('?'),
554 error,
555 _phase: PhantomData,
556 }
557 }
558
559 pub(crate) fn new_sealed(
560 writer: &'a mut RequestWriter,
561 method: Method,
562 path: &str,
563 ) -> RequestBuilder<'a, Headers> {
564 writer.write_buf.clear();
565 let mut error = if has_crlf(path) {
566 Some(RestError::CrlfInjection)
567 } else {
568 None
569 };
570 checked_append(
571 &mut writer.write_buf,
572 method.as_str().as_bytes(),
573 &mut error,
574 );
575 checked_append(&mut writer.write_buf, b" ", &mut error);
576 if !writer.base_path.is_empty() {
577 checked_append(&mut writer.write_buf, &writer.base_path, &mut error);
578 }
579 checked_append(&mut writer.write_buf, path.as_bytes(), &mut error);
580 seal_request_line(writer, &mut error);
581 RequestBuilder {
582 writer,
583 has_query: false,
584 error,
585 _phase: PhantomData,
586 }
587 }
588
589 pub fn query(mut self, key: &str, value: &str) -> Self {
591 let sep = if self.has_query { b"&" as &[u8] } else { b"?" };
592 checked_append(&mut self.writer.write_buf, sep, &mut self.error);
593 append_percent_encoded(&mut self.writer.write_buf, key.as_bytes(), &mut self.error);
594 checked_append(&mut self.writer.write_buf, b"=", &mut self.error);
595 append_percent_encoded(
596 &mut self.writer.write_buf,
597 value.as_bytes(),
598 &mut self.error,
599 );
600 self.has_query = true;
601 self
602 }
603
604 pub fn query_raw(mut self, key: &str, value: &str) -> Self {
608 if has_crlf(key) || has_crlf(value) {
609 self.error = Some(RestError::CrlfInjection);
610 return self;
611 }
612 let sep = if self.has_query { b"&" as &[u8] } else { b"?" };
613 checked_append(&mut self.writer.write_buf, sep, &mut self.error);
614 checked_append(&mut self.writer.write_buf, key.as_bytes(), &mut self.error);
615 checked_append(&mut self.writer.write_buf, b"=", &mut self.error);
616 checked_append(
617 &mut self.writer.write_buf,
618 value.as_bytes(),
619 &mut self.error,
620 );
621 self.has_query = true;
622 self
623 }
624
625 pub fn header(mut self, name: &str, value: &str) -> RequestBuilder<'a, Headers> {
627 seal_request_line(self.writer, &mut self.error);
628 let mut next = RequestBuilder {
629 writer: self.writer,
630 has_query: self.has_query,
631 error: self.error,
632 _phase: PhantomData,
633 };
634 next.append_header(name, value);
635 next
636 }
637
638 pub fn body(mut self, body: &[u8]) -> RequestBuilder<'a, Ready> {
640 seal_request_line(self.writer, &mut self.error);
641 write_body(&mut self.writer.write_buf, body, &mut self.error);
642 RequestBuilder {
643 writer: self.writer,
644 has_query: self.has_query,
645 error: self.error,
646 _phase: PhantomData,
647 }
648 }
649
650 pub fn body_writer<F, E>(mut self, f: F) -> RequestBuilder<'a, Ready>
662 where
663 F: FnOnce(&mut BodyWriter<'_>) -> Result<(), E>,
664 E: Into<Box<dyn std::error::Error + Send + Sync>>,
665 {
666 seal_request_line(self.writer, &mut self.error);
667 if self.error.is_some() {
668 return RequestBuilder {
669 writer: self.writer,
670 has_query: self.has_query,
671 error: self.error,
672 _phase: PhantomData,
673 };
674 }
675 let num_offset =
676 write_content_length_placeholder(&mut self.writer.write_buf, &mut self.error);
677 if self.error.is_some() {
678 return RequestBuilder {
679 writer: self.writer,
680 has_query: self.has_query,
681 error: self.error,
682 _phase: PhantomData,
683 };
684 }
685 let body_len = {
686 let mut bw = BodyWriter::new(&mut self.writer.write_buf);
687 if let Err(e) = f(&mut bw) {
688 self.error = Some(if self.writer.write_buf.tailroom() == 0 {
690 RestError::RequestTooLarge {
691 capacity: self.writer.write_buf.len() + self.writer.write_buf.tailroom(),
692 }
693 } else {
694 RestError::Io(std::io::Error::other(e))
695 });
696 0
697 } else {
698 bw.written()
699 }
700 }; if self.error.is_none() {
702 backfill_content_length(&mut self.writer.write_buf, num_offset, body_len);
703 }
704 RequestBuilder {
705 writer: self.writer,
706 has_query: self.has_query,
707 error: self.error,
708 _phase: PhantomData,
709 }
710 }
711
712 pub fn body_fixed(
729 mut self,
730 len: usize,
731 f: impl FnOnce(&mut [u8]),
732 ) -> RequestBuilder<'a, Ready> {
733 seal_request_line(self.writer, &mut self.error);
734 write_body_header(&mut self.writer.write_buf, len, &mut self.error);
736 if self.error.is_some() {
737 return RequestBuilder {
738 writer: self.writer,
739 has_query: self.has_query,
740 error: self.error,
741 _phase: PhantomData,
742 };
743 }
744 let buf = &mut self.writer.write_buf;
746 if len > buf.tailroom() {
747 self.error = Some(RestError::RequestTooLarge {
748 capacity: buf.len() + buf.tailroom(),
749 });
750 } else {
751 let start = buf.len();
752 buf.extend_zeroed(len);
754 let data = buf.data_mut();
755 f(&mut data[start..start + len]);
756 }
757 RequestBuilder {
758 writer: self.writer,
759 has_query: self.has_query,
760 error: self.error,
761 _phase: PhantomData,
762 }
763 }
764
765 pub fn finish(mut self) -> Result<Request<'a>, RestError> {
767 seal_request_line(self.writer, &mut self.error);
768 checked_append(&mut self.writer.write_buf, b"\r\n", &mut self.error);
769 if let Some(e) = self.error {
770 return Err(e);
771 }
772 Ok(Request {
773 data: self.writer.write_buf.data(),
774 })
775 }
776}
777
778impl<'a> RequestBuilder<'a, Headers> {
783 pub fn header(mut self, name: &str, value: &str) -> Self {
785 self.append_header(name, value);
786 self
787 }
788
789 pub fn body(mut self, body: &[u8]) -> RequestBuilder<'a, Ready> {
791 write_body(&mut self.writer.write_buf, body, &mut self.error);
792 RequestBuilder {
793 writer: self.writer,
794 has_query: self.has_query,
795 error: self.error,
796 _phase: PhantomData,
797 }
798 }
799
800 pub fn body_writer<F, E>(mut self, f: F) -> RequestBuilder<'a, Ready>
804 where
805 F: FnOnce(&mut BodyWriter<'_>) -> Result<(), E>,
806 E: Into<Box<dyn std::error::Error + Send + Sync>>,
807 {
808 if self.error.is_some() {
809 return RequestBuilder {
810 writer: self.writer,
811 has_query: self.has_query,
812 error: self.error,
813 _phase: PhantomData,
814 };
815 }
816 let num_offset =
817 write_content_length_placeholder(&mut self.writer.write_buf, &mut self.error);
818 if self.error.is_some() {
819 return RequestBuilder {
820 writer: self.writer,
821 has_query: self.has_query,
822 error: self.error,
823 _phase: PhantomData,
824 };
825 }
826 let body_len = {
827 let mut bw = BodyWriter::new(&mut self.writer.write_buf);
828 if let Err(e) = f(&mut bw) {
829 self.error = Some(if self.writer.write_buf.tailroom() == 0 {
831 RestError::RequestTooLarge {
832 capacity: self.writer.write_buf.len() + self.writer.write_buf.tailroom(),
833 }
834 } else {
835 RestError::Io(std::io::Error::other(e))
836 });
837 0
838 } else {
839 bw.written()
840 }
841 }; if self.error.is_none() {
843 backfill_content_length(&mut self.writer.write_buf, num_offset, body_len);
844 }
845 RequestBuilder {
846 writer: self.writer,
847 has_query: self.has_query,
848 error: self.error,
849 _phase: PhantomData,
850 }
851 }
852
853 pub fn body_fixed(
857 mut self,
858 len: usize,
859 f: impl FnOnce(&mut [u8]),
860 ) -> RequestBuilder<'a, Ready> {
861 write_body_header(&mut self.writer.write_buf, len, &mut self.error);
862 if self.error.is_some() {
863 return RequestBuilder {
864 writer: self.writer,
865 has_query: self.has_query,
866 error: self.error,
867 _phase: PhantomData,
868 };
869 }
870 let buf = &mut self.writer.write_buf;
871 if len > buf.tailroom() {
872 self.error = Some(RestError::RequestTooLarge {
873 capacity: buf.len() + buf.tailroom(),
874 });
875 } else {
876 let start = buf.len();
877 buf.extend_zeroed(len);
878 let data = buf.data_mut();
879 f(&mut data[start..start + len]);
880 }
881 RequestBuilder {
882 writer: self.writer,
883 has_query: self.has_query,
884 error: self.error,
885 _phase: PhantomData,
886 }
887 }
888
889 pub fn finish(mut self) -> Result<Request<'a>, RestError> {
891 checked_append(&mut self.writer.write_buf, b"\r\n", &mut self.error);
892 if let Some(e) = self.error {
893 return Err(e);
894 }
895 Ok(Request {
896 data: self.writer.write_buf.data(),
897 })
898 }
899
900 fn append_header(&mut self, name: &str, value: &str) {
901 if self.error.is_some() {
902 return;
903 }
904 if has_crlf(name) || has_crlf(value) {
905 self.error = Some(RestError::CrlfInjection);
906 return;
907 }
908 checked_append(&mut self.writer.write_buf, name.as_bytes(), &mut self.error);
909 checked_append(&mut self.writer.write_buf, b": ", &mut self.error);
910 checked_append(
911 &mut self.writer.write_buf,
912 value.as_bytes(),
913 &mut self.error,
914 );
915 checked_append(&mut self.writer.write_buf, b"\r\n", &mut self.error);
916 }
917}
918
919impl<'a> RequestBuilder<'a, Ready> {
924 pub fn finish(self) -> Result<Request<'a>, RestError> {
926 if let Some(e) = self.error {
927 return Err(e);
928 }
929 Ok(Request {
930 data: self.writer.write_buf.data(),
931 })
932 }
933}