1use std::{collections::HashMap, net::SocketAddr};
2
3use bytes::{Bytes, BytesMut};
4use chrono::Utc;
5use http::StatusCode;
6use http_serde;
7use tokio_util::codec::Decoder as _;
8use vector_lib::{
9 codecs::decoding::{DeserializerConfig, FramingConfig},
10 config::{DataType, LegacyKey, LogNamespace},
11 configurable::configurable_component,
12 lookup::{lookup_v2::OptionalValuePath, owned_value_path, path},
13 schema::Definition,
14};
15use vrl::value::{Kind, kind::Collection};
16use warp::http::HeaderMap;
17
18use crate::{
19 codecs::{Decoder, DecodingConfig},
20 common::http::{ErrorMessage, server_auth::HttpServerAuthConfig},
21 config::{
22 GenerateConfig, Resource, SourceAcknowledgementsConfig, SourceConfig, SourceContext,
23 SourceOutput,
24 },
25 event::Event,
26 http::KeepaliveConfig,
27 serde::{bool_or_struct, default_decoding},
28 sources::util::{
29 HttpSource,
30 http::{HttpMethod, add_headers, add_query_parameters},
31 },
32 tls::TlsEnableableConfig,
33};
34
35#[configurable_component(source("http", "Host an HTTP endpoint to receive logs."))]
37#[configurable(metadata(deprecated))]
38#[derive(Clone, Debug)]
39pub struct HttpConfig(SimpleHttpConfig);
40
41impl GenerateConfig for HttpConfig {
42 fn generate_config() -> serde_json::Value {
43 <SimpleHttpConfig as GenerateConfig>::generate_config()
44 }
45}
46
47#[async_trait::async_trait]
48#[typetag::serde(name = "http")]
49impl SourceConfig for HttpConfig {
50 async fn build(&self, cx: SourceContext) -> vector_lib::Result<super::Source> {
51 self.0.build(cx).await
52 }
53
54 fn outputs(&self, global_log_namespace: LogNamespace) -> Vec<SourceOutput> {
55 self.0.outputs(global_log_namespace)
56 }
57
58 fn resources(&self) -> Vec<Resource> {
59 self.0.resources()
60 }
61
62 fn can_acknowledge(&self) -> bool {
63 self.0.can_acknowledge()
64 }
65}
66
67#[configurable_component(source("http_server", "Host an HTTP endpoint to receive logs."))]
69#[derive(Clone, Debug)]
70pub struct SimpleHttpConfig {
71 #[configurable(metadata(docs::examples = "0.0.0.0:80"))]
75 #[configurable(metadata(docs::examples = "localhost:80"))]
76 address: SocketAddr,
77
78 #[serde(default)]
86 #[configurable(metadata(docs::examples = "User-Agent"))]
87 #[configurable(metadata(docs::examples = "X-My-Custom-Header"))]
88 #[configurable(metadata(docs::examples = "X-*"))]
89 #[configurable(metadata(docs::examples = "*"))]
90 headers: Vec<String>,
91
92 #[serde(default)]
100 #[configurable(metadata(docs::examples = "application"))]
101 #[configurable(metadata(docs::examples = "source"))]
102 #[configurable(metadata(docs::examples = "param*"))]
103 #[configurable(metadata(docs::examples = "*"))]
104 query_parameters: Vec<String>,
105
106 #[configurable(derived)]
115 auth: Option<HttpServerAuthConfig>,
116
117 #[serde(default = "crate::serde::default_true")]
125 strict_path: bool,
126
127 #[serde(default = "default_path")]
129 #[configurable(metadata(docs::examples = "/event/path"))]
130 #[configurable(metadata(docs::examples = "/logs"))]
131 path: String,
132
133 #[serde(default = "default_path_key")]
135 #[configurable(metadata(docs::examples = "vector_http_path"))]
136 path_key: OptionalValuePath,
137
138 #[serde(default = "default_host_key")]
140 #[configurable(metadata(docs::examples = "hostname"))]
141 host_key: OptionalValuePath,
142
143 #[serde(default = "default_http_method")]
145 method: HttpMethod,
146
147 #[configurable(metadata(docs::examples = 202))]
149 #[configurable(metadata(docs::numeric_type = "uint"))]
150 #[serde(with = "http_serde::status_code")]
151 #[serde(default = "default_http_response_code")]
152 response_code: StatusCode,
153
154 #[configurable(derived)]
155 tls: Option<TlsEnableableConfig>,
156
157 #[configurable(derived)]
158 framing: Option<FramingConfig>,
159
160 #[configurable(derived)]
161 decoding: Option<DeserializerConfig>,
162
163 #[configurable(derived)]
164 #[serde(default, deserialize_with = "bool_or_struct")]
165 acknowledgements: SourceAcknowledgementsConfig,
166
167 #[configurable(metadata(docs::hidden))]
169 #[serde(default)]
170 log_namespace: Option<bool>,
171
172 #[configurable(derived)]
173 #[serde(default)]
174 keepalive: KeepaliveConfig,
175}
176
177impl SimpleHttpConfig {
178 fn schema_definition(&self, log_namespace: LogNamespace) -> Definition {
180 let mut schema_definition = self
181 .decoding
182 .as_ref()
183 .unwrap_or(&default_decoding())
184 .schema_definition(log_namespace)
185 .with_source_metadata(
186 SimpleHttpConfig::NAME,
187 self.path_key.path.clone().map(LegacyKey::InsertIfEmpty),
188 &owned_value_path!("path"),
189 Kind::bytes(),
190 None,
191 )
192 .with_source_metadata(
194 SimpleHttpConfig::NAME,
195 None,
196 &owned_value_path!("headers"),
197 Kind::object(Collection::empty().with_unknown(Kind::bytes())).or_undefined(),
198 None,
199 )
200 .with_source_metadata(
202 SimpleHttpConfig::NAME,
203 None,
204 &owned_value_path!("query_parameters"),
205 Kind::object(Collection::empty().with_unknown(Kind::bytes())).or_undefined(),
206 None,
207 )
208 .with_source_metadata(
209 SimpleHttpConfig::NAME,
210 self.host_key.path.clone().map(LegacyKey::Overwrite),
211 &owned_value_path!("host"),
212 Kind::bytes().or_undefined(),
213 None,
214 )
215 .with_standard_vector_source_metadata();
216
217 if log_namespace == LogNamespace::Legacy {
219 let unknown_kind = if matches!(self.auth, Some(HttpServerAuthConfig::Custom { .. })) {
222 Kind::any()
223 } else {
224 Kind::bytes()
225 };
226 schema_definition = schema_definition.unknown_fields(unknown_kind);
227 }
228
229 schema_definition
230 }
231
232 fn get_decoding_config(&self) -> crate::Result<DecodingConfig> {
233 let decoding = self.decoding.clone().unwrap_or_else(default_decoding);
234 let framing = self
235 .framing
236 .clone()
237 .unwrap_or_else(|| decoding.default_stream_framing());
238
239 Ok(DecodingConfig::new(
240 framing,
241 decoding,
242 self.log_namespace.unwrap_or(false).into(),
243 ))
244 }
245}
246
247impl Default for SimpleHttpConfig {
248 fn default() -> Self {
249 Self {
250 address: "0.0.0.0:8080".parse().unwrap(),
251 headers: Vec::new(),
252 query_parameters: Vec::new(),
253 tls: None,
254 auth: None,
255 path: default_path(),
256 path_key: default_path_key(),
257 host_key: default_host_key(),
258 method: default_http_method(),
259 response_code: default_http_response_code(),
260 strict_path: true,
261 framing: None,
262 decoding: Some(default_decoding()),
263 acknowledgements: SourceAcknowledgementsConfig::default(),
264 log_namespace: None,
265 keepalive: KeepaliveConfig::default(),
266 }
267 }
268}
269
270impl_generate_config_from_default!(SimpleHttpConfig);
271
272const fn default_http_method() -> HttpMethod {
273 HttpMethod::Post
274}
275
276fn default_path() -> String {
277 "/".to_string()
278}
279
280fn default_path_key() -> OptionalValuePath {
281 OptionalValuePath::from(owned_value_path!("path"))
282}
283
284fn default_host_key() -> OptionalValuePath {
285 OptionalValuePath::none()
286}
287
288const fn default_http_response_code() -> StatusCode {
289 StatusCode::OK
290}
291
292pub fn remove_duplicates(mut list: Vec<String>, list_name: &str) -> Vec<String> {
294 list.sort();
295
296 let mut dedup = false;
297 for (idx, name) in list.iter().enumerate() {
298 if idx < list.len() - 1 && list[idx] == list[idx + 1] {
299 warn!(
300 "`{}` configuration contains duplicate entry for `{}`. Removing duplicate.",
301 list_name, name
302 );
303 dedup = true;
304 }
305 }
306
307 if dedup {
308 list.dedup();
309 }
310 list
311}
312
313fn socket_addr_to_ip_string(addr: &SocketAddr) -> String {
315 addr.ip().to_string()
316}
317
318#[derive(Clone)]
319pub enum HttpConfigParamKind {
320 Glob(glob::Pattern),
321 Exact(String),
322}
323
324pub fn build_param_matcher(list: &[String]) -> crate::Result<Vec<HttpConfigParamKind>> {
325 list.iter()
326 .map(|s| match s.contains('*') {
327 true => Ok(HttpConfigParamKind::Glob(glob::Pattern::new(s)?)),
328 false => Ok(HttpConfigParamKind::Exact(s.to_string())),
329 })
330 .collect::<crate::Result<Vec<HttpConfigParamKind>>>()
331}
332
333#[async_trait::async_trait]
334#[typetag::serde(name = "http_server")]
335impl SourceConfig for SimpleHttpConfig {
336 async fn build(&self, cx: SourceContext) -> crate::Result<super::Source> {
337 let log_namespace = cx.log_namespace(self.log_namespace);
338 let decoder = self
339 .get_decoding_config()?
340 .build()?
341 .with_log_namespace(log_namespace);
342
343 let source = SimpleHttpSource {
344 headers: build_param_matcher(&remove_duplicates(self.headers.clone(), "headers"))?,
345 query_parameters: build_param_matcher(&remove_duplicates(
346 self.query_parameters.clone(),
347 "query_parameters",
348 ))?,
349 path_key: self.path_key.clone(),
350 host_key: self.host_key.clone(),
351 decoder,
352 log_namespace,
353 };
354 source.run(
355 self.address,
356 self.path.as_str(),
357 self.method,
358 self.response_code,
359 self.strict_path,
360 self.tls.as_ref(),
361 self.auth.as_ref(),
362 cx,
363 self.acknowledgements,
364 self.keepalive.clone(),
365 )
366 }
367
368 fn outputs(&self, global_log_namespace: LogNamespace) -> Vec<SourceOutput> {
369 let log_namespace = global_log_namespace.merge(self.log_namespace);
372
373 let schema_definition = self.schema_definition(log_namespace);
374
375 vec![SourceOutput::new_maybe_logs(
376 self.decoding
377 .as_ref()
378 .map(|d| d.output_type())
379 .unwrap_or(DataType::Log),
380 schema_definition,
381 )]
382 }
383
384 fn resources(&self) -> Vec<Resource> {
385 vec![Resource::tcp(self.address)]
386 }
387
388 fn can_acknowledge(&self) -> bool {
389 true
390 }
391}
392
393#[derive(Clone)]
394struct SimpleHttpSource {
395 headers: Vec<HttpConfigParamKind>,
396 query_parameters: Vec<HttpConfigParamKind>,
397 path_key: OptionalValuePath,
398 host_key: OptionalValuePath,
399 decoder: Decoder,
400 log_namespace: LogNamespace,
401}
402
403impl HttpSource for SimpleHttpSource {
404 fn log_namespace(&self) -> LogNamespace {
405 self.log_namespace
406 }
407
408 fn name() -> &'static str {
409 SimpleHttpConfig::NAME
410 }
411
412 fn enrich_events(
415 &self,
416 events: &mut [Event],
417 request_path: &str,
418 headers: &HeaderMap,
419 query_parameters: &HashMap<String, String>,
420 source_ip: Option<&SocketAddr>,
421 ) {
422 let now = Utc::now();
423 for event in events.iter_mut() {
424 match event {
425 Event::Log(log) => {
426 self.log_namespace.insert_source_metadata(
428 SimpleHttpConfig::NAME,
429 log,
430 self.path_key.path.as_ref().map(LegacyKey::InsertIfEmpty),
431 path!("path"),
432 request_path.to_owned(),
433 );
434
435 self.log_namespace.insert_standard_vector_source_metadata(
436 log,
437 SimpleHttpConfig::NAME,
438 now,
439 );
440
441 if let Some(addr) = source_ip {
442 self.log_namespace.insert_source_metadata(
443 SimpleHttpConfig::NAME,
444 log,
445 self.host_key.path.as_ref().map(LegacyKey::Overwrite),
446 path!("host"),
447 socket_addr_to_ip_string(addr),
448 );
449 }
450 }
451 _ => {
452 continue;
453 }
454 }
455 }
456
457 add_headers(
458 events,
459 &self.headers,
460 headers,
461 self.log_namespace,
462 SimpleHttpConfig::NAME,
463 );
464
465 add_query_parameters(
466 events,
467 &self.query_parameters,
468 query_parameters,
469 self.log_namespace,
470 SimpleHttpConfig::NAME,
471 );
472 }
473
474 fn build_events(
475 &self,
476 body: Bytes,
477 _header_map: &HeaderMap,
478 _query_parameters: &HashMap<String, String>,
479 _request_path: &str,
480 ) -> Result<Vec<Event>, ErrorMessage> {
481 let mut decoder = self.decoder.clone();
482 let mut events = Vec::new();
483 let mut bytes = BytesMut::new();
484 bytes.extend_from_slice(&body);
485
486 loop {
487 match decoder.decode_eof(&mut bytes) {
488 Ok(Some((next, _))) => {
489 events.extend(next);
490 }
491 Ok(None) => break,
492 Err(error) => {
493 return Err(ErrorMessage::new(
496 StatusCode::BAD_REQUEST,
497 format!("Failed decoding body: {error}"),
498 ));
499 }
500 }
501 }
502
503 Ok(events)
504 }
505
506 fn enable_source_ip(&self) -> bool {
507 self.host_key.path.is_some()
508 }
509}
510
511#[cfg(test)]
512mod tests {
513 use std::{io::Write, net::SocketAddr, str::FromStr};
514
515 use flate2::{
516 Compression,
517 write::{GzEncoder, ZlibEncoder},
518 };
519 use futures::Stream;
520 use headers::{Authorization, authorization::Credentials};
521 use http::{HeaderMap, Method, StatusCode, Uri, header::AUTHORIZATION};
522 use similar_asserts::assert_eq;
523 use vector_lib::{
524 codecs::{
525 BytesDecoderConfig, JsonDeserializerConfig,
526 decoding::{DeserializerConfig, FramingConfig},
527 },
528 config::LogNamespace,
529 event::LogEvent,
530 lookup::{
531 OwnedTargetPath, PathPrefix, event_path, lookup_v2::OptionalValuePath,
532 owned_value_path, path,
533 },
534 schema::Definition,
535 };
536 use vrl::{
537 path::ValuePath as _,
538 value::{Kind, ObjectMap, kind::Collection},
539 };
540
541 use super::{SimpleHttpConfig, remove_duplicates};
542 use crate::{
543 SourceSender,
544 common::http::server_auth::HttpServerAuthConfig,
545 components::validation::prelude::*,
546 config::{SourceConfig, SourceContext, log_schema},
547 event::{Event, EventStatus, Value},
548 sources::http_server::HttpMethod,
549 test_util::{
550 addr::next_addr,
551 components::{self, HTTP_PUSH_SOURCE_TAGS, assert_source_compliance},
552 spawn_collect_n, wait_for_tcp,
553 },
554 };
555
556 #[test]
557 fn generate_config() {
558 crate::test_util::test_generate_config::<SimpleHttpConfig>();
559 }
560
561 #[allow(clippy::too_many_arguments)]
562 async fn source<'a>(
563 headers: Vec<String>,
564 query_parameters: Vec<String>,
565 path_key: &'a str,
566 host_key: &'a str,
567 path: &'a str,
568 method: &'a str,
569 response_code: StatusCode,
570 auth: Option<HttpServerAuthConfig>,
571 strict_path: bool,
572 status: EventStatus,
573 acknowledgements: bool,
574 framing: Option<FramingConfig>,
575 decoding: Option<DeserializerConfig>,
576 ) -> (impl Stream<Item = Event> + 'a, SocketAddr) {
577 let (sender, recv) = SourceSender::new_test_finalize(status);
578 let (_guard, address) = next_addr();
579 let path = path.to_owned();
580 let host_key = OptionalValuePath::from(owned_value_path!(host_key));
581 let path_key = OptionalValuePath::from(owned_value_path!(path_key));
582 let context = SourceContext::new_test(sender, None);
583 let method = match Method::from_str(method).unwrap() {
584 Method::GET => HttpMethod::Get,
585 Method::POST => HttpMethod::Post,
586 _ => HttpMethod::Post,
587 };
588
589 tokio::spawn(async move {
590 SimpleHttpConfig {
591 address,
592 headers,
593 query_parameters,
594 response_code,
595 tls: None,
596 auth,
597 strict_path,
598 path_key,
599 host_key,
600 path,
601 method,
602 framing,
603 decoding,
604 acknowledgements: acknowledgements.into(),
605 log_namespace: None,
606 keepalive: Default::default(),
607 }
608 .build(context)
609 .await
610 .unwrap()
611 .await
612 .unwrap();
613 });
614 wait_for_tcp(address).await;
615 (recv, address)
616 }
617
618 async fn send(address: SocketAddr, body: &str) -> u16 {
619 reqwest::Client::new()
620 .post(format!("http://{address}/"))
621 .body(body.to_owned())
622 .send()
623 .await
624 .unwrap()
625 .status()
626 .as_u16()
627 }
628
629 async fn send_with_headers(address: SocketAddr, body: &str, headers: HeaderMap) -> u16 {
630 reqwest::Client::new()
631 .post(format!("http://{address}/"))
632 .headers(headers)
633 .body(body.to_owned())
634 .send()
635 .await
636 .unwrap()
637 .status()
638 .as_u16()
639 }
640
641 async fn send_with_query(address: SocketAddr, body: &str, query: &str) -> u16 {
642 reqwest::Client::new()
643 .post(format!("http://{address}?{query}"))
644 .body(body.to_owned())
645 .send()
646 .await
647 .unwrap()
648 .status()
649 .as_u16()
650 }
651
652 async fn send_with_path(address: SocketAddr, body: &str, path: &str) -> u16 {
653 reqwest::Client::new()
654 .post(format!("http://{address}{path}"))
655 .body(body.to_owned())
656 .send()
657 .await
658 .unwrap()
659 .status()
660 .as_u16()
661 }
662
663 async fn send_request(address: SocketAddr, method: &str, body: &str, path: &str) -> u16 {
664 let method = Method::from_bytes(method.to_owned().as_bytes()).unwrap();
665 reqwest::Client::new()
666 .request(method, format!("http://{address}{path}"))
667 .body(body.to_owned())
668 .send()
669 .await
670 .unwrap()
671 .status()
672 .as_u16()
673 }
674
675 async fn send_bytes(address: SocketAddr, body: Vec<u8>, headers: HeaderMap) -> u16 {
676 reqwest::Client::new()
677 .post(format!("http://{address}/"))
678 .headers(headers)
679 .body(body)
680 .send()
681 .await
682 .unwrap()
683 .status()
684 .as_u16()
685 }
686
687 async fn spawn_ok_collect_n(
688 send: impl std::future::Future<Output = u16> + Send + 'static,
689 rx: impl Stream<Item = Event> + Unpin,
690 n: usize,
691 ) -> Vec<Event> {
692 spawn_collect_n(async move { assert_eq!(200, send.await) }, rx, n).await
693 }
694
695 #[tokio::test]
696 async fn http_multiline_text() {
697 let mut events = assert_source_compliance(&HTTP_PUSH_SOURCE_TAGS, async move {
698 let body = "test body\ntest body 2";
699
700 let (rx, addr) = source(
701 vec![],
702 vec![],
703 "http_path",
704 "remote_ip",
705 "/",
706 "POST",
707 StatusCode::OK,
708 None,
709 true,
710 EventStatus::Delivered,
711 true,
712 None,
713 None,
714 )
715 .await;
716
717 spawn_ok_collect_n(send(addr, body), rx, 2).await
718 })
719 .await;
720
721 {
722 let event = events.remove(0);
723 let log = event.as_log();
724 assert_eq!(*log.get_message().unwrap(), "test body".into());
725 assert!(log.get_timestamp().is_some());
726 assert_eq!(
727 *log.get_source_type().unwrap(),
728 SimpleHttpConfig::NAME.into()
729 );
730 assert_eq!(log["http_path"], "/".into());
731 assert_event_metadata(log).await;
732 }
733 {
734 let event = events.remove(0);
735 let log = event.as_log();
736 assert_eq!(*log.get_message().unwrap(), "test body 2".into());
737 assert_event_metadata(log).await;
738 }
739 }
740
741 #[tokio::test]
742 async fn http_multiline_text2() {
743 let body = "test body\ntest body 2\n";
745
746 let mut events = assert_source_compliance(&HTTP_PUSH_SOURCE_TAGS, async move {
747 let (rx, addr) = source(
748 vec![],
749 vec![],
750 "http_path",
751 "remote_ip",
752 "/",
753 "POST",
754 StatusCode::OK,
755 None,
756 true,
757 EventStatus::Delivered,
758 true,
759 None,
760 None,
761 )
762 .await;
763
764 spawn_ok_collect_n(send(addr, body), rx, 2).await
765 })
766 .await;
767
768 {
769 let event = events.remove(0);
770 let log = event.as_log();
771 assert_eq!(*log.get_message().unwrap(), "test body".into());
772 assert_event_metadata(log).await;
773 }
774 {
775 let event = events.remove(0);
776 let log = event.as_log();
777 assert_eq!(*log.get_message().unwrap(), "test body 2".into());
778 assert_event_metadata(log).await;
779 }
780 }
781
782 #[tokio::test]
783 async fn http_bytes_codec_preserves_newlines() {
784 let body = "foo\nbar";
785
786 let mut events = assert_source_compliance(&HTTP_PUSH_SOURCE_TAGS, async move {
787 let (rx, addr) = source(
788 vec![],
789 vec![],
790 "http_path",
791 "remote_ip",
792 "/",
793 "POST",
794 StatusCode::OK,
795 None,
796 true,
797 EventStatus::Delivered,
798 true,
799 Some(BytesDecoderConfig::new().into()),
800 None,
801 )
802 .await;
803
804 spawn_ok_collect_n(send(addr, body), rx, 1).await
805 })
806 .await;
807
808 assert_eq!(events.len(), 1);
809
810 {
811 let event = events.remove(0);
812 let log = event.as_log();
813 assert_eq!(*log.get_message().unwrap(), "foo\nbar".into());
814 assert_event_metadata(log).await;
815 }
816 }
817
818 #[tokio::test]
819 async fn http_json_parsing() {
820 let mut events = assert_source_compliance(&HTTP_PUSH_SOURCE_TAGS, async {
821 let (rx, addr) = source(
822 vec![],
823 vec![],
824 "http_path",
825 "remote_ip",
826 "/",
827 "POST",
828 StatusCode::OK,
829 None,
830 true,
831 EventStatus::Delivered,
832 true,
833 None,
834 Some(JsonDeserializerConfig::default().into()),
835 )
836 .await;
837
838 spawn_collect_n(
839 async move {
840 assert_eq!(400, send(addr, "{").await); assert_eq!(400, send(addr, r#"{"key"}"#).await); assert_eq!(200, send(addr, "{}").await); assert_eq!(200, send(addr, "[{},{},{}]").await);
845 },
846 rx,
847 2,
848 )
849 .await
850 })
851 .await;
852
853 assert!(events.remove(1).as_log().get_timestamp().is_some());
854 assert!(events.remove(0).as_log().get_timestamp().is_some());
855 }
856
857 #[tokio::test]
858 async fn http_json_values() {
859 let mut events = assert_source_compliance(&HTTP_PUSH_SOURCE_TAGS, async {
860 let (rx, addr) = source(
861 vec![],
862 vec![],
863 "http_path",
864 "remote_ip",
865 "/",
866 "POST",
867 StatusCode::OK,
868 None,
869 true,
870 EventStatus::Delivered,
871 true,
872 None,
873 Some(JsonDeserializerConfig::default().into()),
874 )
875 .await;
876
877 spawn_collect_n(
878 async move {
879 assert_eq!(200, send(addr, r#"[{"key":"value"}]"#).await);
880 assert_eq!(200, send(addr, r#"{"key2":"value2"}"#).await);
881 },
882 rx,
883 2,
884 )
885 .await
886 })
887 .await;
888
889 {
890 let event = events.remove(0);
891 let log = event.as_log();
892 assert_eq!(log["key"], "value".into());
893 assert_event_metadata(log).await;
894 }
895 {
896 let event = events.remove(0);
897 let log = event.as_log();
898 assert_eq!(log["key2"], "value2".into());
899 assert_event_metadata(log).await;
900 }
901 }
902
903 #[tokio::test]
904 async fn http_json_dotted_keys() {
905 let mut events = assert_source_compliance(&HTTP_PUSH_SOURCE_TAGS, async {
906 let (rx, addr) = source(
907 vec![],
908 vec![],
909 "http_path",
910 "remote_ip",
911 "/",
912 "POST",
913 StatusCode::OK,
914 None,
915 true,
916 EventStatus::Delivered,
917 true,
918 None,
919 Some(JsonDeserializerConfig::default().into()),
920 )
921 .await;
922
923 spawn_collect_n(
924 async move {
925 assert_eq!(200, send(addr, r#"[{"dotted.key":"value"}]"#).await);
926 assert_eq!(
927 200,
928 send(addr, r#"{"nested":{"dotted.key2":"value2"}}"#).await
929 );
930 },
931 rx,
932 2,
933 )
934 .await
935 })
936 .await;
937
938 {
939 let event = events.remove(0);
940 let log = event.as_log();
941 assert_eq!(
942 log.get(event_path!("dotted.key")).unwrap(),
943 &Value::from("value")
944 );
945 }
946 {
947 let event = events.remove(0);
948 let log = event.as_log();
949 let mut map = ObjectMap::new();
950 map.insert("dotted.key2".into(), Value::from("value2"));
951 assert_eq!(log["nested"], map.into());
952 }
953 }
954
955 #[tokio::test]
956 async fn http_ndjson() {
957 let mut events = assert_source_compliance(&HTTP_PUSH_SOURCE_TAGS, async {
958 let (rx, addr) = source(
959 vec![],
960 vec![],
961 "http_path",
962 "remote_ip",
963 "/",
964 "POST",
965 StatusCode::OK,
966 None,
967 true,
968 EventStatus::Delivered,
969 true,
970 None,
971 Some(JsonDeserializerConfig::default().into()),
972 )
973 .await;
974
975 spawn_collect_n(
976 async move {
977 assert_eq!(
978 200,
979 send(addr, r#"[{"key1":"value1"},{"key2":"value2"}]"#).await
980 );
981
982 assert_eq!(
983 200,
984 send(addr, "{\"key1\":\"value1\"}\n\n{\"key2\":\"value2\"}").await
985 );
986 },
987 rx,
988 4,
989 )
990 .await
991 })
992 .await;
993
994 {
995 let event = events.remove(0);
996 let log = event.as_log();
997 assert_eq!(log["key1"], "value1".into());
998 assert_event_metadata(log).await;
999 }
1000 {
1001 let event = events.remove(0);
1002 let log = event.as_log();
1003 assert_eq!(log["key2"], "value2".into());
1004 assert_event_metadata(log).await;
1005 }
1006 {
1007 let event = events.remove(0);
1008 let log = event.as_log();
1009 assert_eq!(log["key1"], "value1".into());
1010 assert_event_metadata(log).await;
1011 }
1012 {
1013 let event = events.remove(0);
1014 let log = event.as_log();
1015 assert_eq!(log["key2"], "value2".into());
1016 assert_event_metadata(log).await;
1017 }
1018 }
1019
1020 async fn assert_event_metadata(log: &LogEvent) {
1021 assert!(log.get_timestamp().is_some());
1022
1023 let source_type_key_value = log
1024 .get((PathPrefix::Event, log_schema().source_type_key().unwrap()))
1025 .unwrap()
1026 .as_str()
1027 .unwrap();
1028 assert_eq!(source_type_key_value, SimpleHttpConfig::NAME);
1029 assert_eq!(log["http_path"], "/".into());
1030 }
1031
1032 #[tokio::test]
1033 async fn http_headers() {
1034 let mut events = assert_source_compliance(&HTTP_PUSH_SOURCE_TAGS, async {
1035 let mut headers = HeaderMap::new();
1036 headers.insert("User-Agent", "test_client".parse().unwrap());
1037 headers.insert("Upgrade-Insecure-Requests", "false".parse().unwrap());
1038 headers.insert("X-Test-Header", "true".parse().unwrap());
1039
1040 let (rx, addr) = source(
1041 vec![
1042 "User-Agent".to_string(),
1043 "Upgrade-Insecure-Requests".to_string(),
1044 "X-*".to_string(),
1045 "AbsentHeader".to_string(),
1046 ],
1047 vec![],
1048 "http_path",
1049 "remote_ip",
1050 "/",
1051 "POST",
1052 StatusCode::OK,
1053 None,
1054 true,
1055 EventStatus::Delivered,
1056 true,
1057 None,
1058 Some(JsonDeserializerConfig::default().into()),
1059 )
1060 .await;
1061
1062 spawn_ok_collect_n(
1063 send_with_headers(addr, "{\"key1\":\"value1\"}", headers),
1064 rx,
1065 1,
1066 )
1067 .await
1068 })
1069 .await;
1070
1071 {
1072 let event = events.remove(0);
1073 let log = event.as_log();
1074 assert_eq!(log["key1"], "value1".into());
1075 assert_eq!(log["\"User-Agent\""], "test_client".into());
1076 assert_eq!(log["\"Upgrade-Insecure-Requests\""], "false".into());
1077 assert_eq!(log["\"x-test-header\""], "true".into());
1078 assert_eq!(log["AbsentHeader"], Value::Null);
1079 assert_event_metadata(log).await;
1080 }
1081 }
1082
1083 #[tokio::test]
1084 async fn http_headers_wildcard() {
1085 let mut events = assert_source_compliance(&HTTP_PUSH_SOURCE_TAGS, async {
1086 let mut headers = HeaderMap::new();
1087 headers.insert("User-Agent", "test_client".parse().unwrap());
1088 headers.insert("X-Case-Sensitive-Value", "CaseSensitive".parse().unwrap());
1089 headers.insert("key1", "value_from_header".parse().unwrap());
1091
1092 let (rx, addr) = source(
1093 vec!["*".to_string()],
1094 vec![],
1095 "http_path",
1096 "remote_ip",
1097 "/",
1098 "POST",
1099 StatusCode::OK,
1100 None,
1101 true,
1102 EventStatus::Delivered,
1103 true,
1104 None,
1105 Some(JsonDeserializerConfig::default().into()),
1106 )
1107 .await;
1108
1109 spawn_ok_collect_n(
1110 send_with_headers(addr, "{\"key1\":\"value1\"}", headers),
1111 rx,
1112 1,
1113 )
1114 .await
1115 })
1116 .await;
1117
1118 {
1119 let event = events.remove(0);
1120 let log = event.as_log();
1121 assert_eq!(log["key1"], "value1".into());
1122 assert_eq!(log["\"user-agent\""], "test_client".into());
1123 assert_eq!(log["\"x-case-sensitive-value\""], "CaseSensitive".into());
1124 assert_event_metadata(log).await;
1125 }
1126 }
1127
1128 #[tokio::test]
1129 async fn http_query() {
1130 let mut events = assert_source_compliance(&HTTP_PUSH_SOURCE_TAGS, async {
1131 let (rx, addr) = source(
1132 vec![],
1133 vec![
1134 "source".to_string(),
1135 "region".to_string(),
1136 "absent".to_string(),
1137 ],
1138 "http_path",
1139 "remote_ip",
1140 "/",
1141 "POST",
1142 StatusCode::OK,
1143 None,
1144 true,
1145 EventStatus::Delivered,
1146 true,
1147 None,
1148 Some(JsonDeserializerConfig::default().into()),
1149 )
1150 .await;
1151
1152 spawn_ok_collect_n(
1153 send_with_query(addr, "{\"key1\":\"value1\"}", "source=staging®ion=gb"),
1154 rx,
1155 1,
1156 )
1157 .await
1158 })
1159 .await;
1160
1161 {
1162 let event = events.remove(0);
1163 let log = event.as_log();
1164 assert_eq!(log["key1"], "value1".into());
1165 assert_eq!(log["source"], "staging".into());
1166 assert_eq!(log["region"], "gb".into());
1167 assert_eq!(log["absent"], Value::Null);
1168 assert_event_metadata(log).await;
1169 }
1170 }
1171
1172 #[tokio::test]
1173 async fn http_query_wildcard() {
1174 let mut events = assert_source_compliance(&HTTP_PUSH_SOURCE_TAGS, async {
1175 let (rx, addr) = source(
1176 vec![],
1177 vec!["*".to_string()],
1178 "http_path",
1179 "remote_ip",
1180 "/",
1181 "POST",
1182 StatusCode::OK,
1183 None,
1184 true,
1185 EventStatus::Delivered,
1186 true,
1187 None,
1188 Some(JsonDeserializerConfig::default().into()),
1189 )
1190 .await;
1191
1192 spawn_ok_collect_n(
1193 send_with_query(
1194 addr,
1195 "{\"key1\":\"value1\",\"key2\":\"value2\"}",
1196 "source=staging®ion=gb&key1=value_from_query",
1197 ),
1198 rx,
1199 1,
1200 )
1201 .await
1202 })
1203 .await;
1204
1205 {
1206 let event = events.remove(0);
1207 let log = event.as_log();
1208 assert_eq!(log["key1"], "value_from_query".into());
1209 assert_eq!(log["key2"], "value2".into());
1210 assert_eq!(log["source"], "staging".into());
1211 assert_eq!(log["region"], "gb".into());
1212 assert_event_metadata(log).await;
1213 }
1214 }
1215
1216 #[tokio::test]
1217 async fn http_gzip_deflate() {
1218 let mut events = assert_source_compliance(&HTTP_PUSH_SOURCE_TAGS, async {
1219 let body = "test body";
1220
1221 let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
1222 encoder.write_all(body.as_bytes()).unwrap();
1223 let body = encoder.finish().unwrap();
1224
1225 let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
1226 encoder.write_all(body.as_slice()).unwrap();
1227 let body = encoder.finish().unwrap();
1228
1229 let mut headers = HeaderMap::new();
1230 headers.insert("Content-Encoding", "gzip, deflate".parse().unwrap());
1231
1232 let (rx, addr) = source(
1233 vec![],
1234 vec![],
1235 "http_path",
1236 "remote_ip",
1237 "/",
1238 "POST",
1239 StatusCode::OK,
1240 None,
1241 true,
1242 EventStatus::Delivered,
1243 true,
1244 None,
1245 None,
1246 )
1247 .await;
1248
1249 spawn_ok_collect_n(send_bytes(addr, body, headers), rx, 1).await
1250 })
1251 .await;
1252
1253 {
1254 let event = events.remove(0);
1255 let log = event.as_log();
1256 assert_eq!(*log.get_message().unwrap(), "test body".into());
1257 assert_event_metadata(log).await;
1258 }
1259 }
1260
1261 #[tokio::test]
1262 async fn http_rejects_gzip_bomb_with_413() {
1263 let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
1266 let chunk = [0u8; 8 * 1024];
1267 for _ in 0..(200 * 1024 * 1024 / chunk.len()) {
1268 encoder.write_all(&chunk).unwrap();
1269 }
1270 let body = encoder.finish().unwrap();
1271
1272 let mut headers = HeaderMap::new();
1273 headers.insert("Content-Encoding", "gzip".parse().unwrap());
1274
1275 components::init_test();
1276 let (_rx, addr) = source(
1277 vec![],
1278 vec![],
1279 "http_path",
1280 "remote_ip",
1281 "/",
1282 "POST",
1283 StatusCode::OK,
1284 None,
1285 true,
1286 EventStatus::Delivered,
1287 true,
1288 None,
1289 None,
1290 )
1291 .await;
1292
1293 assert_eq!(413, send_bytes(addr, body, headers).await);
1294 }
1295
1296 #[tokio::test]
1297 async fn http_path() {
1298 let mut events = assert_source_compliance(&HTTP_PUSH_SOURCE_TAGS, async {
1299 let (rx, addr) = source(
1300 vec![],
1301 vec![],
1302 "vector_http_path",
1303 "vector_remote_ip",
1304 "/event/path",
1305 "POST",
1306 StatusCode::OK,
1307 None,
1308 true,
1309 EventStatus::Delivered,
1310 true,
1311 None,
1312 Some(JsonDeserializerConfig::default().into()),
1313 )
1314 .await;
1315
1316 spawn_ok_collect_n(
1317 send_with_path(addr, "{\"key1\":\"value1\"}", "/event/path"),
1318 rx,
1319 1,
1320 )
1321 .await
1322 })
1323 .await;
1324
1325 {
1326 let event = events.remove(0);
1327 let log = event.as_log();
1328 assert_eq!(log["key1"], "value1".into());
1329 assert_eq!(log["vector_http_path"], "/event/path".into());
1330 assert!(log.get_timestamp().is_some());
1331 assert_eq!(
1332 *log.get_source_type().unwrap(),
1333 SimpleHttpConfig::NAME.into()
1334 );
1335 }
1336 }
1337
1338 #[tokio::test]
1339 async fn http_path_no_restriction() {
1340 let mut events = assert_source_compliance(&HTTP_PUSH_SOURCE_TAGS, async {
1341 let (rx, addr) = source(
1342 vec![],
1343 vec![],
1344 "vector_http_path",
1345 "vector_remote_ip",
1346 "/event",
1347 "POST",
1348 StatusCode::OK,
1349 None,
1350 false,
1351 EventStatus::Delivered,
1352 true,
1353 None,
1354 Some(JsonDeserializerConfig::default().into()),
1355 )
1356 .await;
1357
1358 spawn_collect_n(
1359 async move {
1360 assert_eq!(
1361 200,
1362 send_with_path(addr, "{\"key1\":\"value1\"}", "/event/path1").await
1363 );
1364 assert_eq!(
1365 200,
1366 send_with_path(addr, "{\"key2\":\"value2\"}", "/event/path2").await
1367 );
1368 },
1369 rx,
1370 2,
1371 )
1372 .await
1373 })
1374 .await;
1375
1376 {
1377 let event = events.remove(0);
1378 let log = event.as_log();
1379 assert_eq!(log["key1"], "value1".into());
1380 assert_eq!(log["vector_http_path"], "/event/path1".into());
1381 assert!(log.get_timestamp().is_some());
1382 assert_eq!(
1383 *log.get_source_type().unwrap(),
1384 SimpleHttpConfig::NAME.into()
1385 );
1386 }
1387 {
1388 let event = events.remove(0);
1389 let log = event.as_log();
1390 assert_eq!(log["key2"], "value2".into());
1391 assert_eq!(log["vector_http_path"], "/event/path2".into());
1392 assert!(log.get_timestamp().is_some());
1393 assert_eq!(
1394 *log.get_source_type().unwrap(),
1395 SimpleHttpConfig::NAME.into()
1396 );
1397 }
1398 }
1399
1400 #[tokio::test]
1401 async fn http_wrong_path() {
1402 components::init_test();
1403 let (_rx, addr) = source(
1404 vec![],
1405 vec![],
1406 "vector_http_path",
1407 "vector_remote_ip",
1408 "/",
1409 "POST",
1410 StatusCode::OK,
1411 None,
1412 true,
1413 EventStatus::Delivered,
1414 true,
1415 None,
1416 Some(JsonDeserializerConfig::default().into()),
1417 )
1418 .await;
1419
1420 assert_eq!(
1421 404,
1422 send_with_path(addr, "{\"key1\":\"value1\"}", "/event/path").await
1423 );
1424 }
1425
1426 #[tokio::test]
1427 async fn http_status_code() {
1428 assert_source_compliance(&HTTP_PUSH_SOURCE_TAGS, async move {
1429 let (rx, addr) = source(
1430 vec![],
1431 vec![],
1432 "http_path",
1433 "remote_ip",
1434 "/",
1435 "POST",
1436 StatusCode::ACCEPTED,
1437 None,
1438 true,
1439 EventStatus::Delivered,
1440 true,
1441 None,
1442 None,
1443 )
1444 .await;
1445
1446 spawn_collect_n(
1447 async move {
1448 assert_eq!(
1449 StatusCode::ACCEPTED,
1450 send(addr, "{\"key1\":\"value1\"}").await
1451 );
1452 },
1453 rx,
1454 1,
1455 )
1456 .await;
1457 })
1458 .await;
1459 }
1460
1461 #[tokio::test]
1462 async fn http_delivery_failure() {
1463 assert_source_compliance(&HTTP_PUSH_SOURCE_TAGS, async {
1464 let (rx, addr) = source(
1465 vec![],
1466 vec![],
1467 "http_path",
1468 "remote_ip",
1469 "/",
1470 "POST",
1471 StatusCode::OK,
1472 None,
1473 true,
1474 EventStatus::Rejected,
1475 true,
1476 None,
1477 None,
1478 )
1479 .await;
1480
1481 spawn_collect_n(
1482 async move {
1483 assert_eq!(400, send(addr, "test body\n").await);
1484 },
1485 rx,
1486 1,
1487 )
1488 .await;
1489 })
1490 .await;
1491 }
1492
1493 #[tokio::test]
1494 async fn ignores_disabled_acknowledgements() {
1495 let events = assert_source_compliance(&HTTP_PUSH_SOURCE_TAGS, async {
1496 let (rx, addr) = source(
1497 vec![],
1498 vec![],
1499 "http_path",
1500 "remote_ip",
1501 "/",
1502 "POST",
1503 StatusCode::OK,
1504 None,
1505 true,
1506 EventStatus::Rejected,
1507 false,
1508 None,
1509 None,
1510 )
1511 .await;
1512
1513 spawn_collect_n(
1514 async move {
1515 assert_eq!(200, send(addr, "test body\n").await);
1516 },
1517 rx,
1518 1,
1519 )
1520 .await
1521 })
1522 .await;
1523
1524 assert_eq!(events.len(), 1);
1525 }
1526
1527 #[tokio::test]
1528 async fn http_get_method() {
1529 components::init_test();
1530 let (_rx, addr) = source(
1531 vec![],
1532 vec![],
1533 "http_path",
1534 "remote_ip",
1535 "/",
1536 "GET",
1537 StatusCode::OK,
1538 None,
1539 true,
1540 EventStatus::Delivered,
1541 true,
1542 None,
1543 None,
1544 )
1545 .await;
1546
1547 assert_eq!(200, send_request(addr, "GET", "", "/").await);
1548 }
1549
1550 #[tokio::test]
1551 async fn returns_401_when_required_auth_is_missing() {
1552 components::init_test();
1553 let (_rx, addr) = source(
1554 vec![],
1555 vec![],
1556 "http_path",
1557 "remote_ip",
1558 "/",
1559 "GET",
1560 StatusCode::OK,
1561 Some(HttpServerAuthConfig::Basic {
1562 username: "test".to_string(),
1563 password: "test".to_string().into(),
1564 }),
1565 true,
1566 EventStatus::Delivered,
1567 true,
1568 None,
1569 None,
1570 )
1571 .await;
1572
1573 assert_eq!(401, send_request(addr, "GET", "", "/").await);
1574 }
1575
1576 #[tokio::test]
1577 async fn returns_401_when_required_auth_is_wrong() {
1578 components::init_test();
1579 let (_rx, addr) = source(
1580 vec![],
1581 vec![],
1582 "http_path",
1583 "remote_ip",
1584 "/",
1585 "POST",
1586 StatusCode::OK,
1587 Some(HttpServerAuthConfig::Basic {
1588 username: "test".to_string(),
1589 password: "test".to_string().into(),
1590 }),
1591 true,
1592 EventStatus::Delivered,
1593 true,
1594 None,
1595 None,
1596 )
1597 .await;
1598
1599 let mut headers = HeaderMap::new();
1600 headers.insert(
1601 AUTHORIZATION,
1602 Authorization::basic("wrong", "test").0.encode(),
1603 );
1604 assert_eq!(401, send_with_headers(addr, "", headers).await);
1605 }
1606
1607 #[tokio::test]
1608 async fn http_get_with_correct_auth() {
1609 components::init_test();
1610 let (_rx, addr) = source(
1611 vec![],
1612 vec![],
1613 "http_path",
1614 "remote_ip",
1615 "/",
1616 "POST",
1617 StatusCode::OK,
1618 Some(HttpServerAuthConfig::Basic {
1619 username: "test".to_string(),
1620 password: "test".to_string().into(),
1621 }),
1622 true,
1623 EventStatus::Delivered,
1624 true,
1625 None,
1626 None,
1627 )
1628 .await;
1629
1630 let mut headers = HeaderMap::new();
1631 headers.insert(
1632 AUTHORIZATION,
1633 Authorization::basic("test", "test").0.encode(),
1634 );
1635 assert_eq!(200, send_with_headers(addr, "", headers).await);
1636 }
1637
1638 #[test]
1639 fn output_schema_definition_vector_namespace() {
1640 let config = SimpleHttpConfig {
1641 log_namespace: Some(true),
1642 ..Default::default()
1643 };
1644
1645 let definitions = config
1646 .outputs(LogNamespace::Vector)
1647 .remove(0)
1648 .schema_definition(true);
1649
1650 let expected_definition =
1651 Definition::new_with_default_metadata(Kind::bytes(), [LogNamespace::Vector])
1652 .with_meaning(OwnedTargetPath::event_root(), "message")
1653 .with_metadata_field(
1654 &owned_value_path!("vector", "source_type"),
1655 Kind::bytes(),
1656 None,
1657 )
1658 .with_metadata_field(
1659 &owned_value_path!(SimpleHttpConfig::NAME, "path"),
1660 Kind::bytes(),
1661 None,
1662 )
1663 .with_metadata_field(
1664 &owned_value_path!(SimpleHttpConfig::NAME, "headers"),
1665 Kind::object(Collection::empty().with_unknown(Kind::bytes())).or_undefined(),
1666 None,
1667 )
1668 .with_metadata_field(
1669 &owned_value_path!(SimpleHttpConfig::NAME, "query_parameters"),
1670 Kind::object(Collection::empty().with_unknown(Kind::bytes())).or_undefined(),
1671 None,
1672 )
1673 .with_metadata_field(
1674 &owned_value_path!(SimpleHttpConfig::NAME, "host"),
1675 Kind::bytes().or_undefined(),
1676 None,
1677 )
1678 .with_metadata_field(
1679 &owned_value_path!("vector", "ingest_timestamp"),
1680 Kind::timestamp(),
1681 None,
1682 );
1683
1684 assert_eq!(definitions, Some(expected_definition))
1685 }
1686
1687 #[test]
1688 fn output_schema_definition_legacy_namespace() {
1689 let config = SimpleHttpConfig::default();
1690
1691 let definitions = config
1692 .outputs(LogNamespace::Legacy)
1693 .remove(0)
1694 .schema_definition(true);
1695
1696 let expected_definition = Definition::new_with_default_metadata(
1697 Kind::object(Collection::empty()),
1698 [LogNamespace::Legacy],
1699 )
1700 .with_event_field(
1701 &owned_value_path!("message"),
1702 Kind::bytes(),
1703 Some("message"),
1704 )
1705 .with_event_field(&owned_value_path!("source_type"), Kind::bytes(), None)
1706 .with_event_field(&owned_value_path!("timestamp"), Kind::timestamp(), None)
1707 .with_event_field(&owned_value_path!("path"), Kind::bytes(), None)
1708 .with_event_field(
1709 &owned_value_path!("host"),
1710 Kind::bytes().or_undefined(),
1711 None,
1712 )
1713 .unknown_fields(Kind::bytes());
1714
1715 assert_eq!(definitions, Some(expected_definition))
1716 }
1717
1718 #[test]
1719 fn validate_remove_duplicates() {
1720 let mut list = vec![
1721 "a".to_owned(),
1722 "b".to_owned(),
1723 "c".to_owned(),
1724 "d".to_owned(),
1725 ];
1726
1727 {
1729 let list_dedup = remove_duplicates(list.clone(), "foo");
1730
1731 assert_eq!(list, list_dedup);
1732 }
1733
1734 list.push("b".to_owned());
1735
1736 {
1738 let list_dedup = remove_duplicates(list.clone(), "foo");
1739 assert_eq!(
1740 vec![
1741 "a".to_owned(),
1742 "b".to_owned(),
1743 "c".to_owned(),
1744 "d".to_owned()
1745 ],
1746 list_dedup
1747 );
1748 }
1749 }
1750
1751 #[test]
1752 fn inject_auth_enrichment_does_not_clobber_vector_namespace_builtin_fields() {
1753 use crate::{codecs::DecodingConfig, sources::util::HttpSource as _};
1754 use vector_lib::codecs::BytesDeserializerConfig;
1755 use vrl::value::KeyString;
1756
1757 let decoder = DecodingConfig::new(
1758 BytesDecoderConfig::new().into(),
1759 BytesDeserializerConfig::new().into(),
1760 LogNamespace::Vector,
1761 )
1762 .build()
1763 .unwrap()
1764 .with_log_namespace(LogNamespace::Vector);
1765
1766 let source = super::SimpleHttpSource {
1767 headers: vec![],
1768 query_parameters: vec![],
1769 path_key: OptionalValuePath::none(),
1770 host_key: OptionalValuePath::none(),
1771 decoder,
1772 log_namespace: LogNamespace::Vector,
1773 };
1774
1775 let mut log = LogEvent::default();
1776 log.insert(
1778 (
1779 PathPrefix::Metadata,
1780 path!(SimpleHttpConfig::NAME).concat(path!("path")),
1781 ),
1782 "/real/path",
1783 );
1784
1785 let mut events = vec![Event::Log(log)];
1786 let mut enrichment = ObjectMap::new();
1787 enrichment.insert(KeyString::from("path"), Value::from("/clobbered"));
1789 enrichment.insert(KeyString::from("tenant_id"), Value::from("t-123"));
1790
1791 source.inject_auth_enrichment(&mut events, enrichment);
1792
1793 let Event::Log(log) = &events[0] else {
1794 panic!("expected log event");
1795 };
1796 assert_eq!(
1797 log.get((
1798 PathPrefix::Metadata,
1799 path!(SimpleHttpConfig::NAME).concat(path!("path")),
1800 )),
1801 Some(&Value::from("/real/path")),
1802 "auth enrichment must not overwrite built-in source metadata"
1803 );
1804 assert_eq!(
1805 log.get((
1806 PathPrefix::Metadata,
1807 path!(SimpleHttpConfig::NAME).concat(path!("tenant_id")),
1808 )),
1809 Some(&Value::from("t-123")),
1810 "new auth enrichment field must be injected"
1811 );
1812 }
1813
1814 #[test]
1815 fn inject_auth_enrichment_does_not_overwrite_existing_metadata_in_vector_namespace() {
1816 use crate::{codecs::DecodingConfig, sources::util::HttpSource as _};
1817 use vector_lib::codecs::BytesDeserializerConfig;
1818 use vrl::value::KeyString;
1819
1820 let decoder = DecodingConfig::new(
1821 BytesDecoderConfig::new().into(),
1822 BytesDeserializerConfig::new().into(),
1823 LogNamespace::Vector,
1824 )
1825 .build()
1826 .unwrap()
1827 .with_log_namespace(LogNamespace::Vector);
1828
1829 let source = super::SimpleHttpSource {
1830 headers: vec![],
1831 query_parameters: vec![],
1832 path_key: OptionalValuePath::none(),
1833 host_key: OptionalValuePath::none(),
1834 decoder,
1835 log_namespace: LogNamespace::Vector,
1836 };
1837
1838 let mut log = LogEvent::default();
1839 log.insert(
1841 (
1842 PathPrefix::Metadata,
1843 path!(SimpleHttpConfig::NAME).concat(path!("tenant_id")),
1844 ),
1845 "existing",
1846 );
1847
1848 let mut events = vec![Event::Log(log)];
1849 let mut enrichment = ObjectMap::new();
1850 enrichment.insert(KeyString::from("tenant_id"), Value::from("auth-value"));
1851
1852 source.inject_auth_enrichment(&mut events, enrichment);
1853
1854 let Event::Log(log) = &events[0] else {
1855 panic!("expected log event");
1856 };
1857 assert_eq!(
1858 log.get((
1859 PathPrefix::Metadata,
1860 path!(SimpleHttpConfig::NAME).concat(path!("tenant_id")),
1861 )),
1862 Some(&Value::from("existing")),
1863 "auth enrichment must not overwrite already-present metadata keys"
1864 );
1865 }
1866
1867 #[test]
1868 fn inject_auth_enrichment_applies_to_non_log_events_in_vector_namespace() {
1869 use crate::{codecs::DecodingConfig, sources::util::HttpSource as _};
1870 use vector_lib::{
1871 codecs::BytesDeserializerConfig,
1872 event::{Metric, MetricKind, MetricValue},
1873 };
1874 use vrl::value::KeyString;
1875
1876 let decoder = DecodingConfig::new(
1877 BytesDecoderConfig::new().into(),
1878 BytesDeserializerConfig::new().into(),
1879 LogNamespace::Vector,
1880 )
1881 .build()
1882 .unwrap()
1883 .with_log_namespace(LogNamespace::Vector);
1884
1885 let source = super::SimpleHttpSource {
1886 headers: vec![],
1887 query_parameters: vec![],
1888 path_key: OptionalValuePath::none(),
1889 host_key: OptionalValuePath::none(),
1890 decoder,
1891 log_namespace: LogNamespace::Vector,
1892 };
1893
1894 let metric = Metric::new(
1895 "requests",
1896 MetricKind::Incremental,
1897 MetricValue::Counter { value: 1.0 },
1898 );
1899 let mut events = vec![Event::Metric(metric)];
1900
1901 let mut enrichment = ObjectMap::new();
1902 enrichment.insert(KeyString::from("tenant_id"), Value::from("t-456"));
1903
1904 source.inject_auth_enrichment(&mut events, enrichment);
1905
1906 let Event::Metric(metric) = &events[0] else {
1907 panic!("expected metric event");
1908 };
1909 assert_eq!(
1910 metric
1911 .metadata()
1912 .value()
1913 .get(path!(SimpleHttpConfig::NAME).concat(path!("tenant_id")),),
1914 Some(&Value::from("t-456")),
1915 "auth enrichment must be written to non-log event metadata"
1916 );
1917 }
1918
1919 impl ValidatableComponent for SimpleHttpConfig {
1920 fn validation_configuration() -> ValidationConfiguration {
1921 let config = Self {
1922 decoding: Some(DeserializerConfig::Json(Default::default())),
1923 ..Default::default()
1924 };
1925
1926 let log_namespace: LogNamespace = config.log_namespace.unwrap_or(false).into();
1927
1928 let listen_addr_http = format!("http://{}/", config.address);
1929 let uri = Uri::try_from(&listen_addr_http).expect("should not fail to parse URI");
1930
1931 let external_resource = ExternalResource::new(
1932 ResourceDirection::Push,
1933 HttpResourceConfig::from_parts(uri, Some(config.method.into())),
1934 config
1935 .get_decoding_config()
1936 .expect("should not fail to get decoding config"),
1937 );
1938
1939 ValidationConfiguration::from_source(
1940 Self::NAME,
1941 log_namespace,
1942 vec![ComponentTestCaseConfig::from_source(
1943 config,
1944 None,
1945 Some(external_resource),
1946 )],
1947 )
1948 }
1949 }
1950
1951 register_validatable_component!(SimpleHttpConfig);
1952}