1use core::fmt;
4use std::time::Duration;
5
6use http_net::{
7 BodyPlan, Headers, Method, OutgoingBody, OwnedRequestHead, RequestHead, ResponseHeadRef,
8 Version,
9};
10
11use crate::Finished;
12use crate::client::{ClientInfo, IngressKind};
13use crate::config::{Limits, Profile, ProxyKind};
14use crate::engine::http1::engine_error;
15use crate::engine::{
16 DetachedRequest, EngineError, Family, HeadContext, Http1, OutBuf, RefusalKind, Service,
17};
18use crate::envelope::Envelope;
19use crate::forwarded::{self, FieldLines, Lines, WalkError};
20use crate::reply::Reply;
21use crate::service::{HeadAction, Outcome, UpgradeTarget, UpgradedConn, WorkClass};
22
23pub type HttpReply = Reply<HttpFamily>;
25
26#[derive(Clone, Copy, Debug, Default)]
29pub struct HttpFamily;
30
31#[derive(Clone, Copy)]
37pub struct HttpHead<'a> {
38 raw: RequestHead<'a>,
39 client: ClientInfo,
40 authority: Option<&'a str>,
41}
42
43impl<'a> HttpHead<'a> {
44 #[must_use]
46 pub fn method(&self) -> Method {
47 self.raw.method()
48 }
49
50 #[must_use]
52 pub fn method_str(&self) -> &'a str {
53 self.raw.method_str()
54 }
55
56 #[must_use]
58 pub fn target(&self) -> &'a str {
59 self.raw.target()
60 }
61
62 #[must_use]
64 pub fn path(&self) -> &'a str {
65 self.raw.path()
66 }
67
68 #[must_use]
70 pub fn query(&self) -> Option<&'a str> {
71 self.raw.query()
72 }
73
74 #[must_use]
76 pub fn version(&self) -> Version {
77 self.raw.version()
78 }
79
80 #[must_use]
82 pub fn header(&self, name: &str) -> Option<&'a [u8]> {
83 self.raw.header(name)
84 }
85
86 #[must_use]
88 pub fn header_str(&self, name: &str) -> Option<&'a str> {
89 self.raw.header_str(name)
90 }
91
92 pub fn header_all<'n>(&self, name: &'n str) -> impl Iterator<Item = &'a [u8]> + use<'a, 'n> {
94 self.raw.header_all(name)
95 }
96
97 #[must_use]
99 pub fn headers(&self) -> Fields<'a> {
100 Fields {
101 inner: FieldSource::Head(self.raw.headers()),
102 }
103 }
104
105 #[must_use]
107 pub fn authority(&self) -> Option<&'a str> {
108 self.authority
109 }
110
111 #[must_use]
113 pub fn client(&self) -> &ClientInfo {
114 &self.client
115 }
116
117 #[must_use]
119 pub fn content_length(&self) -> Option<u64> {
120 self.raw.content_length()
121 }
122
123 #[must_use]
125 pub fn expects_continue(&self) -> bool {
126 self.raw.expects_continue()
127 }
128
129 #[must_use]
131 pub fn is_upgrade(&self) -> bool {
132 self.raw.is_upgrade()
133 }
134}
135
136impl fmt::Debug for HttpHead<'_> {
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 f.debug_struct("HttpHead")
140 .field("head", &self.raw)
141 .field("client", &self.client)
142 .finish()
143 }
144}
145
146#[derive(Clone)]
148pub struct Fields<'a> {
149 inner: FieldSource<'a>,
150}
151
152#[derive(Clone)]
153enum FieldSource<'a> {
154 Head(Headers<'a>),
155 Copy(&'a [u8], core::slice::Iter<'a, (Span, Span)>),
156}
157
158impl<'a> Iterator for Fields<'a> {
159 type Item = (&'a str, &'a [u8]);
160 fn next(&mut self) -> Option<Self::Item> {
161 match &mut self.inner {
162 FieldSource::Head(h) => h.next(),
163 FieldSource::Copy(bytes, spans) => {
164 let (name, value) = spans.next()?;
165 let name = core::str::from_utf8(name.of(bytes)).unwrap_or("");
166 Some((name, value.of(bytes)))
167 }
168 }
169 }
170 fn size_hint(&self) -> (usize, Option<usize>) {
171 match &self.inner {
172 FieldSource::Head(h) => h.size_hint(),
173 FieldSource::Copy(_, spans) => spans.size_hint(),
174 }
175 }
176}
177
178impl ExactSizeIterator for Fields<'_> {}
179
180#[non_exhaustive]
182#[derive(Clone, Copy, Debug, PartialEq, Eq)]
183pub enum HttpBody<'a> {
184 Empty,
186 Full(&'a [u8]),
188 Chunk {
190 bytes: &'a [u8],
192 last: bool,
194 },
195}
196
197impl<'a> HttpBody<'a> {
198 #[must_use]
200 pub fn bytes(&self) -> &'a [u8] {
201 match self {
202 HttpBody::Empty => &[],
203 HttpBody::Full(b) | HttpBody::Chunk { bytes: b, .. } => b,
204 }
205 }
206}
207
208#[derive(Clone, Copy)]
210pub struct HttpRequest<'a> {
211 head: HttpHead<'a>,
212 body: &'a [u8],
213 chunk: Option<bool>,
215}
216
217impl<'a> HttpRequest<'a> {
218 #[must_use]
220 pub fn head(&self) -> &HttpHead<'a> {
221 &self.head
222 }
223
224 #[must_use]
226 pub fn method(&self) -> Method {
227 self.head.method()
228 }
229
230 #[must_use]
232 pub fn path(&self) -> &'a str {
233 self.head.path()
234 }
235
236 #[must_use]
238 pub fn query(&self) -> Option<&'a str> {
239 self.head.query()
240 }
241
242 #[must_use]
244 pub fn header(&self, name: &str) -> Option<&'a [u8]> {
245 self.head.header(name)
246 }
247
248 #[must_use]
250 pub fn client(&self) -> &ClientInfo {
251 self.head.client()
252 }
253
254 #[must_use]
256 pub fn body(&self) -> HttpBody<'a> {
257 match self.chunk {
258 Some(last) => HttpBody::Chunk {
259 bytes: self.body,
260 last,
261 },
262 None if self.body.is_empty() => HttpBody::Empty,
263 None => HttpBody::Full(self.body),
264 }
265 }
266
267 #[must_use]
269 pub fn body_bytes(&self) -> &'a [u8] {
270 self.body
271 }
272
273 #[must_use]
275 pub fn detach_head(&self) -> DetachedHead {
276 DetachedHead::of(&self.head)
277 }
278}
279
280struct OwnedRequest {
282 head: OwnedRequestHead,
283 client: ClientInfo,
284 authority: Option<Box<str>>,
285 body: Box<[u8]>,
286}
287
288impl DetachedRequest<HttpFamily> for OwnedRequest {
289 fn with(&self, call: &mut dyn FnMut(&HttpRequest<'_>)) {
290 call(&HttpRequest {
291 head: HttpHead {
292 raw: self.head.view(),
293 client: self.client,
294 authority: self.authority.as_deref(),
295 },
296 body: &self.body,
297 chunk: None,
298 });
299 }
300}
301
302#[derive(Clone, Copy, Debug)]
304struct Span {
305 start: u32,
306 len: u32,
307}
308
309impl Span {
310 fn of(self, bytes: &[u8]) -> &[u8] {
311 let start = self.start as usize;
312 bytes.get(start..start + self.len as usize).unwrap_or(&[])
313 }
314}
315
316pub struct DetachedHead {
329 bytes: Vec<u8>,
330 method: Method,
331 version: Version,
332 method_str: Span,
333 target: Span,
334 path: Span,
335 query: Option<Span>,
336 fields: Vec<(Span, Span)>,
337}
338
339const CREDENTIALS: [&str; 4] = [
341 "authorization",
342 "proxy-authorization",
343 "cookie",
344 "set-cookie",
345];
346
347impl DetachedHead {
348 fn of(head: &HttpHead<'_>) -> Self {
349 let mut bytes = Vec::with_capacity(512);
350 let mut put = |value: &[u8]| {
351 let start = bytes.len() as u32;
352 bytes.extend_from_slice(value);
353 Span {
354 start,
355 len: value.len() as u32,
356 }
357 };
358 let method_str = put(head.method_str().as_bytes());
359 let target = put(head.target().as_bytes());
360 let path = put(head.path().as_bytes());
361 let query = head.query().map(|q| put(q.as_bytes()));
362 let fields = head
363 .headers()
364 .map(|(name, value)| (put(name.as_bytes()), put(value)))
365 .collect();
366 DetachedHead {
367 bytes,
368 method: head.method(),
369 version: head.version(),
370 method_str,
371 target,
372 path,
373 query,
374 fields,
375 }
376 }
377
378 fn text(&self, span: Span) -> &str {
379 core::str::from_utf8(span.of(&self.bytes)).unwrap_or("")
380 }
381
382 #[must_use]
384 pub fn method(&self) -> Method {
385 self.method
386 }
387
388 #[must_use]
390 pub fn method_str(&self) -> &str {
391 self.text(self.method_str)
392 }
393
394 #[must_use]
396 pub fn target(&self) -> &str {
397 self.text(self.target)
398 }
399
400 #[must_use]
402 pub fn path(&self) -> &str {
403 self.text(self.path)
404 }
405
406 #[must_use]
408 pub fn query(&self) -> Option<&str> {
409 self.query.map(|q| self.text(q))
410 }
411
412 #[must_use]
414 pub fn version(&self) -> Version {
415 self.version
416 }
417
418 #[must_use]
420 pub fn header(&self, name: &str) -> Option<&[u8]> {
421 self.headers()
422 .find(|(n, _)| n.eq_ignore_ascii_case(name))
423 .map(|(_, value)| value)
424 }
425
426 pub fn header_all<'s>(&'s self, name: &'s str) -> impl Iterator<Item = &'s [u8]> + 's {
428 self.headers()
429 .filter(move |(n, _)| n.eq_ignore_ascii_case(name))
430 .map(|(_, value)| value)
431 }
432
433 #[must_use]
435 pub fn headers(&self) -> Fields<'_> {
436 Fields {
437 inner: FieldSource::Copy(&self.bytes, self.fields.iter()),
438 }
439 }
440}
441
442impl fmt::Debug for DetachedHead {
443 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
444 let mut d = f.debug_struct("DetachedHead");
445 d.field("method", &self.method_str())
446 .field("target", &self.target());
447 let fields: Vec<(&str, &str)> = self
448 .headers()
449 .map(|(n, v)| {
450 let secret = CREDENTIALS.iter().any(|c| n.eq_ignore_ascii_case(c));
451 let value = if secret {
452 "<redacted>"
453 } else {
454 core::str::from_utf8(v).unwrap_or("<binary>")
455 };
456 (n, value)
457 })
458 .collect();
459 d.field("fields", &fields).finish()
460 }
461}
462
463impl fmt::Debug for HttpRequest<'_> {
464 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
465 f.debug_struct("HttpRequest")
466 .field("head", &self.head)
467 .field("body_len", &self.body.len())
468 .finish()
469 }
470}
471
472const MAX_FIELDS: usize = 64;
474
475#[derive(Default)]
477pub struct HttpParts {
478 status: u16,
479 names: Vec<u8>,
480 values: Vec<u8>,
481 spans: Vec<(u32, u32, u32, u32)>,
482 body: Vec<u8>,
483 body_static: Option<&'static [u8]>,
484 close: bool,
485 retry_after: Option<u64>,
486 invalid: bool,
487 streaming: bool,
489 sent: u64,
491}
492
493impl HttpParts {
494 fn next_part(&mut self, wipe: bool) {
496 self.sent += self.body.len() as u64;
497 if wipe {
498 crate::engine::wipe_bytes(&mut self.values);
499 crate::engine::wipe_bytes(&mut self.body);
500 }
501 self.names.clear();
503 self.values.clear();
504 self.spans.clear();
505 self.body.clear();
506 }
507
508 fn clear(&mut self, wipe: bool) {
509 if wipe {
510 crate::engine::wipe_bytes(&mut self.values);
511 crate::engine::wipe_bytes(&mut self.body);
512 }
513 self.status = 0;
514 self.names.clear();
515 self.values.clear();
516 self.spans.clear();
517 self.body.clear();
518 self.body_static = None;
519 self.close = false;
520 self.retry_after = None;
521 self.invalid = false;
522 self.streaming = false;
523 self.sent = 0;
524 }
525}
526
527impl fmt::Debug for HttpParts {
528 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
529 let names: Vec<&str> = self
531 .spans
532 .iter()
533 .map(|&(s, e, _, _)| {
534 core::str::from_utf8(&self.names[s as usize..e as usize]).unwrap_or("?")
535 })
536 .collect();
537 f.debug_struct("HttpParts")
538 .field("status", &self.status)
539 .field("fields", &names)
540 .field(
541 "body_len",
542 &self.body_static.map_or(self.body.len(), <[u8]>::len),
543 )
544 .finish()
545 }
546}
547
548#[derive(Debug)]
550pub struct ReplyStream<'a> {
551 parts: &'a mut HttpParts,
552}
553
554impl ReplyStream<'_> {
555 pub fn send(&mut self, bytes: &[u8]) -> &mut Self {
557 self.parts.body.extend_from_slice(bytes);
558 self
559 }
560
561 #[must_use]
564 pub fn sent(&self) -> u64 {
565 self.parts.sent
566 }
567}
568
569impl Reply<HttpFamily> {
570 pub fn stream(&mut self) -> ReplyStream<'_> {
608 self.parts.streaming = true;
609 self.parts.body_static = None;
610 ReplyStream {
611 parts: &mut self.parts,
612 }
613 }
614
615 pub fn status(&mut self, status: u16) -> &mut Self {
617 self.parts.status = status;
618 self
619 }
620
621 pub fn header(&mut self, name: &str, value: impl AsRef<[u8]>) -> &mut Self {
625 let p = &mut self.parts;
626 if p.spans.len() >= MAX_FIELDS {
627 p.invalid = true;
628 return self;
629 }
630 let value = value.as_ref();
631 let (ns, vs) = (p.names.len() as u32, p.values.len() as u32);
632 p.names.extend_from_slice(name.as_bytes());
633 p.values.extend_from_slice(value);
634 p.spans
635 .push((ns, p.names.len() as u32, vs, p.values.len() as u32));
636 self
637 }
638
639 pub fn body(&mut self, bytes: &[u8]) -> &mut Self {
641 let p = &mut self.parts;
642 p.body.clear();
643 p.body.extend_from_slice(bytes);
644 p.body_static = None;
645 self
646 }
647
648 pub fn body_static(&mut self, bytes: &'static [u8]) -> &mut Self {
650 self.parts.body.clear();
651 self.parts.body_static = Some(bytes);
652 self
653 }
654
655 pub fn text(&mut self, status: u16, text: &str) -> &mut Self {
657 self.status(status)
658 .header("content-type", "text/plain; charset=utf-8")
659 .body(text.as_bytes())
660 }
661
662 pub fn html(&mut self, status: u16, html: &str) -> &mut Self {
664 self.status(status)
665 .header("content-type", "text/html; charset=utf-8")
666 .body(html.as_bytes())
667 }
668
669 pub fn close(&mut self) -> &mut Self {
671 self.parts.close = true;
672 self
673 }
674
675 pub fn retry_after(&mut self, after: Duration) -> &mut Self {
677 self.parts.retry_after = Some(after.as_secs().max(1));
678 self
679 }
680}
681
682impl<'h> FieldLines<'h> for RequestHead<'h> {
684 fn field_lines(&self, name: &str, out: &mut Lines<'h>) -> Result<(), WalkError> {
685 for v in self.header_all(name) {
686 out.push(v)?;
687 }
688 Ok(())
689 }
690}
691
692impl Family for HttpFamily {
693 type Engine = Http1;
694 type Head<'a> = HttpHead<'a>;
695 type Request<'a> = HttpRequest<'a>;
696 type Parts = HttpParts;
697
698 fn engine(&self, profile: &Profile, limits: &Limits) -> Http1 {
699 let engine_limits = limits
700 .engine
701 .with_max_body(limits.max_body.min(limits.engine.max_body()));
702 Http1::new(engine_limits, profile.wipe_buffers)
703 }
704
705 fn head<'a>(
706 &self,
707 raw: RequestHead<'a>,
708 cx: &HeadContext<'a>,
709 ) -> Result<(HttpHead<'a>, ClientInfo), EngineError> {
710 let base = *cx.client;
711 let ingress_error = EngineError {
712 kind: RefusalKind::Ingress,
713 status: 400,
714 };
715 let Some(proxy) = cx.proxy else {
716 return Ok((
717 HttpHead {
718 raw,
719 client: base,
720 authority: raw.host(),
721 },
722 base,
723 ));
724 };
725 match proxy.kind {
726 ProxyKind::Headers(source) => {
727 if let Some(client) = cx.resolved {
728 let host = if proxy.trust_host && client.ingress() == IngressKind::Forwarded {
733 forwarded::trusted_host(source, &raw)
734 } else {
735 None
736 };
737 let authority = host.or_else(|| raw.host());
738 return Ok((
739 HttpHead {
740 raw,
741 client,
742 authority,
743 },
744 client,
745 ));
746 }
747 let unix = base.ingress() == IngressKind::Unix;
750 let peer = match base.peer() {
751 Some(p) => Some(p),
752 None if unix => None,
753 None => return Err(ingress_error),
754 };
755 let rules = forwarded::Rules {
756 source,
757 trusted: &proxy.trusted,
758 max_hops: proxy.max_hops,
759 missing: proxy.missing,
760 unknown: proxy.unknown,
761 trust_scheme: proxy.trust_scheme,
762 trust_host: proxy.trust_host,
763 };
764 let found = forwarded::resolve_from(
765 peer.map(|p| p.ip()),
766 peer.map_or(0, |p| p.port()),
767 &rules,
768 &raw,
769 )
770 .map_err(|_| ingress_error)?;
771 let client = if found.forwarded {
772 base.behind_proxy(found.client, found.port, found.hops, IngressKind::Forwarded)
773 .with_scheme(found.scheme.unwrap_or(cx.declared_scheme))
774 } else {
775 base
776 };
777 let authority = found.host.or_else(|| raw.host());
778 Ok((
779 HttpHead {
780 raw,
781 client,
782 authority,
783 },
784 client,
785 ))
786 }
787 ProxyKind::ProxyProtocol(_) => {
788 let client = base;
790 let tlv = if proxy.trust_host {
791 cx.proxy_facts
792 .and_then(|f| f.authority())
793 .filter(|a| forwarded::valid_authority(a.as_bytes()))
794 } else {
795 None
796 };
797 Ok((
798 HttpHead {
799 raw,
800 client,
801 authority: tlv.or_else(|| raw.host()),
802 },
803 client,
804 ))
805 }
806 }
807 }
808
809 fn request<'a>(&self, head: HttpHead<'a>, body: &'a [u8]) -> HttpRequest<'a> {
810 HttpRequest {
811 head,
812 body,
813 chunk: None,
814 }
815 }
816
817 fn request_chunk<'a>(&self, head: HttpHead<'a>, body: &'a [u8], last: bool) -> HttpRequest<'a> {
818 HttpRequest {
819 head,
820 body,
821 chunk: Some(last),
822 }
823 }
824
825 fn reply_stream(&self, parts: &HttpParts) -> bool {
826 parts.streaming
827 }
828
829 fn respond_stream(
830 &self,
831 engine: &mut Http1,
832 parts: &mut HttpParts,
833 first: bool,
834 last: bool,
835 unix_secs: u64,
836 out: &mut OutBuf,
837 ) -> Result<u16, EngineError> {
838 let mut status = 0;
839 if first {
840 let (written, plan) = encode(engine, parts, true, unix_secs, out)?;
841 status = written;
842 if !matches!(plan, BodyPlan::Stream) {
843 parts.streaming = false;
845 return Ok(status);
846 }
847 }
848 if !parts.body.is_empty() {
849 engine
850 .conn
851 .send_body(&parts.body, &mut out.bytes)
852 .map_err(engine_error)?;
853 }
854 if last {
855 engine.conn.end_body(&mut out.bytes).map_err(engine_error)?;
856 }
857 Ok(status)
858 }
859
860 fn next_stream_part(&self, parts: &mut HttpParts, wipe: bool) {
861 parts.next_part(wipe);
862 }
863
864 fn detach(&self, request: &HttpRequest<'_>) -> Option<Box<dyn DetachedRequest<Self>>> {
865 Some(Box::new(OwnedRequest {
866 head: request.head.raw.to_owned(),
867 client: request.head.client,
868 authority: request.head.authority.map(Box::from),
869 body: Box::from(request.body),
870 }))
871 }
872
873 fn new_parts(&self) -> HttpParts {
874 HttpParts::default()
875 }
876
877 fn clear_parts(&self, parts: &mut HttpParts, wipe: bool) {
878 parts.clear(wipe);
879 }
880
881 fn respond(
882 &self,
883 engine: &mut Http1,
884 parts: &mut HttpParts,
885 unix_secs: u64,
886 out: &mut OutBuf,
887 ) -> Result<u16, EngineError> {
888 encode(engine, parts, false, unix_secs, out).map(|(status, _)| status)
889 }
890
891 fn upgrade(
892 &self,
893 engine: &mut Http1,
894 target: &UpgradeTarget,
895 unix_secs: u64,
896 out: &mut OutBuf,
897 ) -> Result<(), EngineError> {
898 let _ = (&engine, unix_secs, &out);
899 match *target {
900 #[cfg(feature = "ws")]
901 UpgradeTarget::WebSocket(ref t) => crate::ws::switch_http1(engine, t, unix_secs, out),
902 }
903 }
904}
905
906fn encode(
908 engine: &mut Http1,
909 parts: &HttpParts,
910 chunked: bool,
911 unix_secs: u64,
912 out: &mut OutBuf,
913) -> Result<(u16, BodyPlan), EngineError> {
914 if parts.invalid {
915 return Err(EngineError {
916 kind: RefusalKind::Internal,
917 status: 500,
918 });
919 }
920 let status = if parts.status == 0 { 200 } else { parts.status };
921 let mut fields: [(&str, &[u8]); MAX_FIELDS + 1] = [("", &[]); MAX_FIELDS + 1];
922 let mut n = 0;
923 for &(ns, ne, vs, ve) in &parts.spans {
924 let Ok(name) = core::str::from_utf8(&parts.names[ns as usize..ne as usize]) else {
925 return Err(EngineError {
926 kind: RefusalKind::Internal,
927 status: 500,
928 });
929 };
930 fields[n] = (name, &parts.values[vs as usize..ve as usize]);
931 n += 1;
932 }
933 let mut digits = [0u8; 20];
934 if let Some(secs) = parts.retry_after {
935 fields[n] = ("retry-after", format_secs(secs, &mut digits));
936 n += 1;
937 }
938 let body: &[u8] = match parts.body_static {
939 Some(b) => b,
940 None => &parts.body,
941 };
942 let outgoing = if chunked {
943 OutgoingBody::Chunked
944 } else if body.is_empty() {
945 OutgoingBody::Empty
946 } else {
947 OutgoingBody::Full(body.len() as u64)
948 };
949 let mut head = ResponseHeadRef::new(status, &fields[..n], outgoing);
950 head.close = parts.close;
951 let date = engine.dates.update(unix_secs);
952 let start = out.len();
953 match engine.conn.write_head(&head, Some(date), out) {
954 Ok(plan) => {
955 if matches!(plan, BodyPlan::Raw) {
956 out.push(body);
957 }
958 Ok((status, plan))
959 }
960 Err(e) => {
961 out.bytes.truncate(start);
962 let mut err = engine_error(e);
963 err.kind = RefusalKind::Internal;
964 err.status = 500;
965 Err(err)
966 }
967 }
968}
969
970fn format_secs(mut n: u64, buf: &mut [u8; 20]) -> &[u8] {
971 let mut i = buf.len();
972 loop {
973 i -= 1;
974 buf[i] = b'0' + (n % 10) as u8;
975 n /= 10;
976 if n == 0 {
977 break;
978 }
979 }
980 &buf[i..]
981}
982
983pub trait HttpService: Send + Sync {
986 const WORK: WorkClass = WorkClass::Quick;
988
989 fn head(&self, head: &HttpHead<'_>, env: &mut Envelope) -> HeadAction {
991 let _ = (head, env);
992 HeadAction::Proceed
993 }
994
995 fn call(&self, request: &HttpRequest<'_>, env: &Envelope, reply: &mut HttpReply) -> Outcome;
997
998 fn upgraded(&self, env: &Envelope, conn: UpgradedConn) {
1002 let _ = (env, conn);
1003 }
1004
1005 fn done(&self, env: &Envelope, finished: &Finished) {
1007 let _ = (env, finished);
1008 }
1009}
1010
1011impl<S: HttpService> Service<HttpFamily> for S {
1012 const WORK: WorkClass = <S as HttpService>::WORK;
1013 fn head(&self, head: &HttpHead<'_>, env: &mut Envelope) -> HeadAction {
1014 HttpService::head(self, head, env)
1015 }
1016 fn call(&self, request: &HttpRequest<'_>, env: &Envelope, reply: &mut HttpReply) -> Outcome {
1017 HttpService::call(self, request, env, reply)
1018 }
1019 fn upgraded(&self, env: &Envelope, conn: UpgradedConn) {
1020 HttpService::upgraded(self, env, conn);
1021 }
1022 fn done(&self, env: &Envelope, finished: &Finished) {
1023 HttpService::done(self, env, finished);
1024 }
1025}
1026
1027pub struct Handler<F> {
1029 f: F,
1030}
1031
1032pub fn http<A, F>(addr: A, handler: F) -> Result<crate::Running, crate::ServeError>
1053where
1054 A: std::net::ToSocketAddrs,
1055 F: Fn(&HttpRequest<'_>, &mut HttpReply) + Send + Sync + 'static,
1056{
1057 tier1(addr, None, handler)
1058}
1059
1060pub fn http_behind_proxy<A, F>(
1082 addr: A,
1083 trusted: &str,
1084 handler: F,
1085) -> Result<crate::Running, crate::ServeError>
1086where
1087 A: std::net::ToSocketAddrs,
1088 F: Fn(&HttpRequest<'_>, &mut HttpReply) + Send + Sync + 'static,
1089{
1090 use crate::error::{ConfigError, ConfigErrors, RuleId, Subject};
1091 let trusted = crate::TrustedProxies::parse(trusted).map_err(|e| {
1092 let error = match e {
1093 crate::AddrError::Empty => ConfigError::new(
1094 RuleId::TrustEmpty,
1095 Subject::Listener("http-0".to_owned()),
1096 "the trusted proxy list is empty",
1097 "list the proxies' addresses, for example \"127.0.0.1, ::1\"",
1098 ),
1099 _ => ConfigError::new(
1100 RuleId::TrustParse,
1101 Subject::Listener("http-0".to_owned()),
1102 "the trusted proxy list does not parse",
1103 "use addresses or CIDR blocks separated by commas, for example \"10.0.0.0/8\"",
1104 ),
1105 };
1106 crate::ServeError::Config(
1107 ConfigErrors::from_vec(vec![error]).unwrap_or_else(|| unreachable!()),
1108 )
1109 })?;
1110 tier1(addr, Some(trusted), handler)
1111}
1112
1113fn tier1<A, F>(
1115 addr: A,
1116 trusted: Option<crate::TrustedProxies>,
1117 handler: F,
1118) -> Result<crate::Running, crate::ServeError>
1119where
1120 A: std::net::ToSocketAddrs,
1121 F: Fn(&HttpRequest<'_>, &mut HttpReply) + Send + Sync + 'static,
1122{
1123 let addrs: Vec<std::net::SocketAddr> = addr
1124 .to_socket_addrs()
1125 .map(Iterator::collect)
1126 .unwrap_or_default();
1127 let spec = |name: &str, a: &[std::net::SocketAddr]| {
1128 let l = crate::ListenerSpec::tcp(name, a);
1129 match &trusted {
1130 None => l,
1131 Some(t) => l.ingress(crate::Ingress::Proxy(crate::ProxySource::forwarded_for(
1132 t.clone(),
1133 ))),
1134 }
1135 };
1136 let mut builder = crate::Server::builder();
1137 if addrs.is_empty() {
1138 builder = builder.listener(spec("http-0", &[]));
1140 }
1141 for (i, a) in addrs.iter().enumerate() {
1142 builder = builder.listener(spec(&format!("http-{i}"), core::slice::from_ref(a)));
1143 }
1144 builder.serve(Handler { f: handler })
1145}
1146
1147pub fn handler<F>(f: F) -> Handler<F>
1156where
1157 F: Fn(&HttpRequest<'_>, &mut HttpReply) + Send + Sync + 'static,
1158{
1159 Handler { f }
1160}
1161
1162impl<F> HttpService for Handler<F>
1163where
1164 F: Fn(&HttpRequest<'_>, &mut HttpReply) + Send + Sync + 'static,
1165{
1166 fn call(&self, request: &HttpRequest<'_>, _env: &Envelope, reply: &mut HttpReply) -> Outcome {
1167 (self.f)(request, reply);
1168 Outcome::Done
1169 }
1170}
1171
1172impl<F> fmt::Debug for Handler<F> {
1173 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1174 f.write_str("Handler")
1175 }
1176}
1177
1178#[cfg(test)]
1179mod tests {
1180 use super::*;
1181
1182 #[test]
1183 fn test_reply_debug_redacts_values() {
1184 let mut r = HttpReply::new(HttpParts::default());
1185 r.header("set-cookie", "sid=secret").text(200, "x");
1186 let text = format!("{r:?}");
1187 assert!(!text.contains("secret"));
1188 assert!(text.contains("set-cookie"));
1189 }
1190
1191 #[test]
1192 fn test_reply_too_many_fields_is_invalid() {
1193 let mut r = HttpReply::new(HttpParts::default());
1194 for _ in 0..=MAX_FIELDS {
1195 r.header("x-a", "1");
1196 }
1197 assert!(r.parts.invalid);
1198 }
1199
1200 #[test]
1201 fn test_parts_clear_wipes() {
1202 let mut r = HttpReply::new(HttpParts::default());
1203 r.header("x", "secret").body(b"secret");
1204 r.parts.clear(true);
1205 assert!(r.parts.values.is_empty() && r.parts.body.is_empty() && r.parts.spans.is_empty());
1206 }
1207}