Skip to main content

vector/sources/fluent/
mod.rs

1use std::{collections::HashMap, io, net::SocketAddr, time::Duration};
2
3use base64::prelude::{BASE64_STANDARD, Engine as _};
4use bytes::{Buf, Bytes, BytesMut};
5use chrono::Utc;
6use rmp_serde::{Deserializer, Serializer, decode};
7use serde::{Deserialize, Serialize};
8use smallvec::{SmallVec, smallvec};
9use tokio_util::codec::Decoder;
10use vector_lib::{
11    codecs::{BytesDeserializerConfig, StreamDecodingError},
12    config::{LegacyKey, LogNamespace},
13    configurable::configurable_component,
14    ipallowlist::IpAllowlistConfig,
15    lookup::{OwnedValuePath, lookup_v2::parse_value_path, metadata_path, owned_value_path, path},
16    schema::Definition,
17};
18use vrl::value::{Kind, Value, kind::Collection};
19
20use super::util::decompression::{CappedDecoder, max_decompressed_size_bytes};
21use super::util::net::{SocketListenAddr, TcpSource, TcpSourceAck, TcpSourceAcker};
22use crate::{
23    config::{
24        DataType, GenerateConfig, Resource, SourceAcknowledgementsConfig, SourceConfig,
25        SourceContext, SourceOutput, log_schema,
26    },
27    event::{Event, LogEvent},
28    internal_events::{FluentMessageDecodeError, FluentMessageReceived},
29    serde::bool_or_struct,
30    tcp::TcpKeepaliveConfig,
31    tls::{MaybeTlsSettings, TlsSourceConfig},
32};
33
34mod message;
35use self::message::{FluentEntry, FluentMessage, FluentRecord, FluentTag, FluentTimestamp};
36
37/// Configuration for the `fluent` source.
38#[configurable_component(source("fluent", "Collect logs from a Fluentd or Fluent Bit agent."))]
39#[derive(Clone, Debug)]
40pub struct FluentConfig {
41    #[serde(flatten)]
42    mode: FluentMode,
43
44    /// The namespace to use for logs. This overrides the global setting.
45    #[configurable(metadata(docs::hidden))]
46    #[serde(default)]
47    log_namespace: Option<bool>,
48}
49
50/// Listening mode for the `fluent` source.
51#[configurable_component(no_deser)]
52#[derive(Clone, Debug)]
53#[serde(tag = "mode", rename_all = "snake_case")]
54#[configurable(metadata(docs::enum_tag_description = "The type of socket to use."))]
55#[allow(clippy::large_enum_variant)] // just used for configuration
56pub enum FluentMode {
57    /// Listen on TCP port
58    Tcp(FluentTcpConfig),
59
60    /// Listen on unix stream socket
61    #[cfg(unix)]
62    Unix(FluentUnixConfig),
63}
64
65/// Serde doesn't provide a way to specify a default tagged variant when deserializing
66/// So we use a somewhat arcane setup with an untagged and tagged versions to allow
67/// users to not have to specify mode = tcp
68///
69/// See [serde-rs/serde#2231](https://github.com/serde-rs/serde/issues/2231)
70mod deser {
71    use super::*;
72
73    #[allow(clippy::large_enum_variant)]
74    #[derive(Deserialize)]
75    #[serde(tag = "mode")]
76    enum FluentModeTagged {
77        #[serde(rename = "tcp")]
78        Tcp(FluentTcpConfig),
79
80        #[cfg(unix)]
81        #[serde(rename = "unix")]
82        Unix(FluentUnixConfig),
83    }
84
85    #[derive(Deserialize)]
86    #[serde(untagged)]
87    enum FluentModeDe {
88        Tagged(FluentModeTagged),
89
90        // Note: this must be last as serde attempts variants in order
91        Untagged(FluentTcpConfig),
92    }
93
94    impl<'de> Deserialize<'de> for FluentMode {
95        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
96        where
97            D: serde::Deserializer<'de>,
98        {
99            Ok(match FluentModeDe::deserialize(deserializer)? {
100                FluentModeDe::Tagged(FluentModeTagged::Tcp(config)) => FluentMode::Tcp(config),
101                #[cfg(unix)]
102                FluentModeDe::Tagged(FluentModeTagged::Unix(config)) => FluentMode::Unix(config),
103                FluentModeDe::Untagged(config) => FluentMode::Tcp(config),
104            })
105        }
106    }
107
108    #[cfg(test)]
109    mod tests {
110        use super::*;
111
112        #[test]
113        fn test_tcp_default_mode() {
114            let json_data = serde_json::json!({
115                "address": "0.0.0.0:2020",
116                "connection_limit": 2
117            });
118
119            let parsed: FluentConfig = serde_json::from_value(json_data).unwrap();
120            assert!(matches!(parsed.mode, FluentMode::Tcp(c) if c.connection_limit.unwrap() == 2));
121        }
122
123        #[test]
124        fn test_tcp_explicit_mode() {
125            let json_data = serde_json::json!({
126                "mode": "tcp",
127                "address": "0.0.0.0:2020",
128                "connection_limit": 2
129            });
130
131            let parsed: FluentConfig = serde_json::from_value(json_data).unwrap();
132            assert!(matches!(parsed.mode, FluentMode::Tcp(c) if c.connection_limit.unwrap() == 2));
133        }
134
135        #[test]
136        fn test_invalid_unix_mode() {
137            let json_data = serde_json::json!({
138                "mode": "unix",
139                "address": "0.0.0.0:2020",
140                "connection_limit": 2
141            });
142
143            assert!(serde_json::from_value::<FluentConfig>(json_data).is_err());
144        }
145
146        #[cfg(unix)]
147        #[test]
148        fn test_valid_unix_mode() {
149            let json_data = serde_json::json!({
150                "mode": "unix",
151                "path": "/foo"
152            });
153
154            let parsed: FluentConfig = serde_json::from_value(json_data).unwrap();
155            assert!(
156                matches!(parsed.mode, FluentMode::Unix(c) if c.path.to_string_lossy() == "/foo")
157            );
158        }
159    }
160}
161
162/// Configuration for the `fluent` TCP source.
163#[configurable_component]
164#[derive(Clone, Debug)]
165#[serde(deny_unknown_fields)]
166pub struct FluentTcpConfig {
167    #[configurable(derived)]
168    address: SocketListenAddr,
169
170    /// The maximum number of TCP connections that are allowed at any given time.
171    #[configurable(metadata(docs::type_unit = "connections"))]
172    connection_limit: Option<u32>,
173
174    #[configurable(derived)]
175    keepalive: Option<TcpKeepaliveConfig>,
176
177    #[configurable(derived)]
178    pub permit_origin: Option<IpAllowlistConfig>,
179
180    /// The size of the receive buffer used for each connection.
181    ///
182    /// This generally should not need to be changed.
183    #[configurable(metadata(docs::type_unit = "bytes"))]
184    #[configurable(metadata(docs::examples = 65536))]
185    receive_buffer_bytes: Option<usize>,
186
187    #[configurable(derived)]
188    tls: Option<TlsSourceConfig>,
189
190    #[configurable(derived)]
191    #[serde(default, deserialize_with = "bool_or_struct")]
192    acknowledgements: SourceAcknowledgementsConfig,
193}
194
195impl FluentTcpConfig {
196    fn build(
197        &self,
198        cx: SourceContext,
199        log_namespace: LogNamespace,
200    ) -> crate::Result<super::Source> {
201        let source = FluentSource::new(log_namespace);
202        let shutdown_secs = Duration::from_secs(30);
203        let tls_config = self.tls.as_ref().map(|tls| tls.tls_config.clone());
204        let tls_client_metadata_key = self
205            .tls
206            .as_ref()
207            .and_then(|tls| tls.client_metadata_key.clone())
208            .and_then(|k| k.path);
209        let tls = MaybeTlsSettings::from_config(tls_config.as_ref(), true)?;
210        source.run(
211            self.address,
212            self.keepalive,
213            shutdown_secs,
214            tls,
215            None, // tls_reloader: not wired for this source
216            tls_client_metadata_key,
217            self.receive_buffer_bytes,
218            None,
219            cx,
220            self.acknowledgements,
221            self.connection_limit,
222            self.permit_origin.clone().map(Into::into),
223            FluentConfig::NAME,
224            log_namespace,
225        )
226    }
227}
228
229/// Configuration for the `fluent` unix socket source.
230#[configurable_component]
231#[derive(Clone, Debug)]
232#[serde(deny_unknown_fields)]
233#[cfg(unix)]
234pub struct FluentUnixConfig {
235    /// The Unix socket path.
236    ///
237    /// This should be an absolute path.
238    #[configurable(metadata(docs::examples = "/path/to/socket"))]
239    pub path: std::path::PathBuf,
240
241    /// Unix file mode bits to be applied to the unix socket file as its designated file permissions.
242    ///
243    /// Note: The file mode value can be specified in any numeric format supported by your configuration
244    /// language, but it is most intuitive to use an octal number.
245    #[configurable(metadata(docs::examples = 0o777))]
246    #[configurable(metadata(docs::examples = 0o600))]
247    #[configurable(metadata(docs::examples = 508))]
248    pub socket_file_mode: Option<u32>,
249}
250
251#[cfg(unix)]
252impl FluentUnixConfig {
253    fn build(
254        &self,
255        cx: SourceContext,
256        log_namespace: LogNamespace,
257    ) -> crate::Result<super::Source> {
258        let source = FluentSource::new(log_namespace);
259
260        crate::sources::util::build_unix_stream_source(
261            self.path.clone(),
262            self.socket_file_mode,
263            source.decoder(),
264            move |events, host| source.handle_events_impl(events, host.into()),
265            cx.shutdown,
266            cx.out,
267        )
268    }
269}
270
271impl GenerateConfig for FluentConfig {
272    fn generate_config() -> toml::Value {
273        toml::Value::try_from(Self {
274            mode: FluentMode::Tcp(FluentTcpConfig {
275                address: SocketListenAddr::SocketAddr("0.0.0.0:24224".parse().unwrap()),
276                keepalive: None,
277                permit_origin: None,
278                tls: None,
279                receive_buffer_bytes: None,
280                acknowledgements: Default::default(),
281                connection_limit: Some(2),
282            }),
283            log_namespace: None,
284        })
285        .unwrap()
286    }
287}
288
289#[async_trait::async_trait]
290#[typetag::serde(name = "fluent")]
291impl SourceConfig for FluentConfig {
292    async fn build(&self, cx: SourceContext) -> crate::Result<super::Source> {
293        let log_namespace = cx.log_namespace(self.log_namespace);
294        match &self.mode {
295            FluentMode::Tcp(t) => t.build(cx, log_namespace),
296            #[cfg(unix)]
297            FluentMode::Unix(u) => u.build(cx, log_namespace),
298        }
299    }
300
301    fn outputs(&self, global_log_namespace: LogNamespace) -> Vec<SourceOutput> {
302        let log_namespace = global_log_namespace.merge(self.log_namespace);
303        let schema_definition = self.schema_definition(log_namespace);
304
305        vec![SourceOutput::new_maybe_logs(
306            DataType::Log,
307            schema_definition,
308        )]
309    }
310
311    fn resources(&self) -> Vec<Resource> {
312        match &self.mode {
313            FluentMode::Tcp(tcp) => vec![tcp.address.as_tcp_resource()],
314            #[cfg(unix)]
315            FluentMode::Unix(_) => vec![],
316        }
317    }
318
319    fn can_acknowledge(&self) -> bool {
320        matches!(self.mode, FluentMode::Tcp(_))
321    }
322}
323
324impl FluentConfig {
325    /// Builds the `schema::Definition` for this source using the provided `LogNamespace`.
326    fn schema_definition(&self, log_namespace: LogNamespace) -> Definition {
327        // `host_key` is only inserted if not present already.
328        let host_key = log_schema()
329            .host_key()
330            .cloned()
331            .map(LegacyKey::InsertIfEmpty);
332
333        let tag_key = parse_value_path("tag").ok().map(LegacyKey::Overwrite);
334
335        let tls_client_metadata_path = match &self.mode {
336            FluentMode::Tcp(tcp) => tcp
337                .tls
338                .as_ref()
339                .and_then(|tls| tls.client_metadata_key.as_ref())
340                .and_then(|k| k.path.clone())
341                .map(LegacyKey::Overwrite),
342            #[cfg(unix)]
343            FluentMode::Unix(_) => None,
344        };
345
346        // There is a global and per-source `log_namespace` config.
347        // The source config overrides the global setting and is merged here.
348        let mut schema_definition = BytesDeserializerConfig
349            .schema_definition(log_namespace)
350            .with_standard_vector_source_metadata()
351            .with_source_metadata(
352                FluentConfig::NAME,
353                host_key,
354                &owned_value_path!("host"),
355                Kind::bytes(),
356                Some("host"),
357            )
358            .with_source_metadata(
359                FluentConfig::NAME,
360                tag_key,
361                &owned_value_path!("tag"),
362                Kind::bytes(),
363                None,
364            )
365            .with_source_metadata(
366                FluentConfig::NAME,
367                None,
368                &owned_value_path!("timestamp"),
369                Kind::timestamp(),
370                Some("timestamp"),
371            )
372            // for metadata that is added to the events dynamically from the FluentRecord
373            .with_source_metadata(
374                FluentConfig::NAME,
375                None,
376                &owned_value_path!("record"),
377                Kind::object(Collection::empty().with_unknown(Kind::bytes())).or_undefined(),
378                None,
379            )
380            .with_source_metadata(
381                Self::NAME,
382                tls_client_metadata_path,
383                &owned_value_path!("tls_client_metadata"),
384                Kind::object(Collection::empty().with_unknown(Kind::bytes())).or_undefined(),
385                None,
386            );
387
388        // for metadata that is added to the events dynamically
389        if log_namespace == LogNamespace::Legacy {
390            schema_definition = schema_definition.unknown_fields(Kind::bytes());
391        }
392
393        schema_definition
394    }
395}
396
397#[derive(Debug, Clone)]
398struct FluentSource {
399    log_namespace: LogNamespace,
400    legacy_host_key_path: Option<OwnedValuePath>,
401}
402
403impl FluentSource {
404    fn new(log_namespace: LogNamespace) -> Self {
405        Self {
406            log_namespace,
407            legacy_host_key_path: log_schema().host_key().cloned(),
408        }
409    }
410
411    fn handle_events_impl(&self, events: &mut [Event], host: Value) {
412        for event in events {
413            let log = event.as_mut_log();
414
415            let legacy_host_key = self
416                .legacy_host_key_path
417                .as_ref()
418                .map(LegacyKey::InsertIfEmpty);
419
420            self.log_namespace.insert_source_metadata(
421                FluentConfig::NAME,
422                log,
423                legacy_host_key,
424                path!("host"),
425                host.clone(),
426            );
427        }
428    }
429}
430
431impl TcpSource for FluentSource {
432    type Error = DecodeError;
433    type Item = FluentFrame;
434    type Decoder = FluentDecoder;
435    type Acker = FluentAcker;
436
437    fn decoder(&self) -> Self::Decoder {
438        FluentDecoder::new(self.log_namespace)
439    }
440
441    fn handle_events(&self, events: &mut [Event], host: SocketAddr) {
442        self.handle_events_impl(events, host.ip().to_string().into())
443    }
444
445    fn build_acker(&self, frame: &[Self::Item]) -> Self::Acker {
446        FluentAcker::new(frame)
447    }
448}
449
450#[derive(Debug)]
451pub enum DecodeError {
452    IO(io::Error),
453    Decode(decode::Error),
454    UnknownCompression(String),
455    UnexpectedValue(rmpv::Value),
456    /// The buffered frame grew past the maximum allowed size before a complete
457    /// message could be decoded. Emitted to bound memory when a peer declares an
458    /// oversized msgpack array/map/string and streams the bytes to force
459    /// unbounded buffering.
460    FrameTooLarge {
461        size: usize,
462        max: usize,
463    },
464}
465
466impl std::fmt::Display for DecodeError {
467    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
468        match self {
469            DecodeError::IO(err) => write!(f, "{err}"),
470            DecodeError::Decode(err) => write!(f, "{err}"),
471            DecodeError::UnknownCompression(compression) => {
472                write!(f, "unknown compression: {compression}")
473            }
474            DecodeError::UnexpectedValue(value) => {
475                write!(f, "unexpected msgpack value, ignoring: {value}")
476            }
477            DecodeError::FrameTooLarge { size, max } => {
478                write!(
479                    f,
480                    "fluent frame exceeds maximum size before decoding: {size} bytes buffered, limit is {max} bytes"
481                )
482            }
483        }
484    }
485}
486
487impl StreamDecodingError for DecodeError {
488    fn can_continue(&self) -> bool {
489        match self {
490            DecodeError::IO(_) => false,
491            DecodeError::Decode(_) => true,
492            DecodeError::UnknownCompression(_) => true,
493            DecodeError::UnexpectedValue(_) => true,
494            // An oversized partial frame has no framing boundary to resync on, so
495            // the connection must be dropped rather than re-decoded in a loop.
496            DecodeError::FrameTooLarge { .. } => false,
497        }
498    }
499}
500
501impl From<io::Error> for DecodeError {
502    fn from(e: io::Error) -> Self {
503        DecodeError::IO(e)
504    }
505}
506
507impl From<decode::Error> for DecodeError {
508    fn from(e: decode::Error) -> Self {
509        DecodeError::Decode(e)
510    }
511}
512
513#[derive(Debug, Clone)]
514struct FluentDecoder {
515    log_namespace: LogNamespace,
516    /// Maximum number of bytes that may be buffered while waiting for a complete
517    /// frame. Bounds memory against a peer that declares an oversized msgpack
518    /// structure and streams the bytes to force unbounded buffering.
519    max_frame_size: usize,
520}
521
522impl FluentDecoder {
523    fn new(log_namespace: LogNamespace) -> Self {
524        Self {
525            log_namespace,
526            max_frame_size: max_decompressed_size_bytes(),
527        }
528    }
529
530    fn handle_message(
531        &mut self,
532        message: Result<FluentMessage, DecodeError>,
533        byte_size: usize,
534    ) -> Result<Option<(FluentFrame, usize)>, DecodeError> {
535        let log_namespace = &self.log_namespace;
536
537        match message? {
538            FluentMessage::Message(tag, timestamp, record) => {
539                let event = Event::from(FluentEvent {
540                    tag,
541                    timestamp,
542                    record,
543                    log_namespace,
544                });
545                let frame = FluentFrame {
546                    events: smallvec![event],
547                    chunk: None,
548                };
549                Ok(Some((frame, byte_size)))
550            }
551            FluentMessage::MessageWithOptions(tag, timestamp, record, options) => {
552                let event = Event::from(FluentEvent {
553                    tag,
554                    timestamp,
555                    record,
556                    log_namespace,
557                });
558                let frame = FluentFrame {
559                    events: smallvec![event],
560                    chunk: options.chunk,
561                };
562                Ok(Some((frame, byte_size)))
563            }
564            FluentMessage::Forward(tag, entries) => {
565                let events = entries
566                    .into_iter()
567                    .map(|FluentEntry(timestamp, record)| {
568                        Event::from(FluentEvent {
569                            tag: tag.clone(),
570                            timestamp,
571                            record,
572                            log_namespace,
573                        })
574                    })
575                    .collect();
576                let frame = FluentFrame {
577                    events,
578                    chunk: None,
579                };
580                Ok(Some((frame, byte_size)))
581            }
582            FluentMessage::ForwardWithOptions(tag, entries, options) => {
583                let events = entries
584                    .into_iter()
585                    .map(|FluentEntry(timestamp, record)| {
586                        Event::from(FluentEvent {
587                            tag: tag.clone(),
588                            timestamp,
589                            record,
590                            log_namespace,
591                        })
592                    })
593                    .collect();
594                let frame = FluentFrame {
595                    events,
596                    chunk: options.chunk,
597                };
598                Ok(Some((frame, byte_size)))
599            }
600            FluentMessage::PackedForward(tag, bin) => {
601                let mut buf = BytesMut::from(&bin[..]);
602
603                let mut events = smallvec![];
604                while let Some(FluentEntry(timestamp, record)) =
605                    FluentEntryStreamDecoder.decode(&mut buf)?
606                {
607                    events.push(Event::from(FluentEvent {
608                        tag: tag.clone(),
609                        timestamp,
610                        record,
611                        log_namespace,
612                    }));
613                }
614                let frame = FluentFrame {
615                    events,
616                    chunk: None,
617                };
618                Ok(Some((frame, byte_size)))
619            }
620            FluentMessage::PackedForwardWithOptions(tag, bin, options) => {
621                let buf = match options.compressed.as_deref() {
622                    // Cap the decompressed output so a `gzip` bomb in a single
623                    // `PackedForward` message cannot drive unbounded allocation.
624                    Some("gzip") => CappedDecoder::gzip(io::Cursor::new(bin.into_vec()))
625                        .decompress()
626                        .map_err(Into::into),
627                    Some("text") | None => Ok(bin.into_vec()),
628                    Some(s) => Err(DecodeError::UnknownCompression(s.to_owned())),
629                }?;
630
631                let mut buf = BytesMut::from(&buf[..]);
632
633                let mut events = smallvec![];
634                while let Some(FluentEntry(timestamp, record)) =
635                    FluentEntryStreamDecoder.decode(&mut buf)?
636                {
637                    events.push(Event::from(FluentEvent {
638                        tag: tag.clone(),
639                        timestamp,
640                        record,
641                        log_namespace,
642                    }));
643                }
644                let frame = FluentFrame {
645                    events,
646                    chunk: options.chunk,
647                };
648                Ok(Some((frame, byte_size)))
649            }
650            FluentMessage::Heartbeat(rmpv::Value::Nil) => Ok(None),
651            FluentMessage::Heartbeat(value) => Err(DecodeError::UnexpectedValue(value)),
652        }
653    }
654}
655
656impl Decoder for FluentDecoder {
657    type Item = (FluentFrame, usize);
658    type Error = DecodeError;
659
660    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
661        loop {
662            if src.is_empty() {
663                return Ok(None);
664            }
665
666            let (byte_size, res) = {
667                let mut des = Deserializer::new(io::Cursor::new(&src[..]));
668
669                let res = Deserialize::deserialize(&mut des).map_err(DecodeError::Decode);
670
671                // check for unexpected EOF to indicate that we need more data
672                if let Err(DecodeError::Decode(
673                    decode::Error::InvalidDataRead(ref custom)
674                    | decode::Error::InvalidMarkerRead(ref custom),
675                )) = res
676                    && custom.kind() == io::ErrorKind::UnexpectedEof
677                {
678                    // We need more bytes before a full message can be decoded. Bound
679                    // the buffer so a peer cannot force unbounded memory growth by
680                    // declaring a huge msgpack array/map/string and streaming the
681                    // bytes: if the frame has already grown past the limit without
682                    // yielding a complete message, drop the connection.
683                    if src.len() > self.max_frame_size {
684                        return Err(DecodeError::FrameTooLarge {
685                            size: src.len(),
686                            max: self.max_frame_size,
687                        });
688                    }
689                    return Ok(None);
690                }
691
692                (des.position() as usize, res)
693            };
694
695            src.advance(byte_size);
696
697            let maybe_item = self.handle_message(res, byte_size).inspect_err(|error| {
698                let base64_encoded_message = BASE64_STANDARD.encode(&src[..]);
699                emit!(FluentMessageDecodeError {
700                    error,
701                    base64_encoded_message
702                });
703            })?;
704            if let Some(item) = maybe_item {
705                return Ok(Some(item));
706            }
707        }
708    }
709}
710
711/// Decoder for decoding MessagePackEventStream which are just a stream of Entries
712#[derive(Clone, Debug)]
713struct FluentEntryStreamDecoder;
714
715impl Decoder for FluentEntryStreamDecoder {
716    type Item = FluentEntry;
717    type Error = DecodeError;
718
719    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
720        if src.is_empty() {
721            return Ok(None);
722        }
723        let (byte_size, res) = {
724            let mut des = Deserializer::new(io::Cursor::new(&src[..]));
725
726            // attempt to parse, if we get unexpected EOF, we need more data
727            let res = Deserialize::deserialize(&mut des).map_err(DecodeError::Decode);
728
729            if let Err(DecodeError::Decode(decode::Error::InvalidDataRead(ref custom))) = res
730                && custom.kind() == io::ErrorKind::UnexpectedEof
731            {
732                return Ok(None);
733            }
734
735            let byte_size = des.position();
736
737            emit!(FluentMessageReceived { byte_size });
738
739            (byte_size as usize, res)
740        };
741
742        src.advance(byte_size);
743
744        res
745    }
746}
747
748struct FluentAcker {
749    chunks: Vec<String>,
750}
751
752impl FluentAcker {
753    fn new(frames: &[FluentFrame]) -> Self {
754        Self {
755            chunks: frames.iter().filter_map(|f| f.chunk.clone()).collect(),
756        }
757    }
758}
759
760impl TcpSourceAcker for FluentAcker {
761    fn build_ack(self, ack: TcpSourceAck) -> Option<Bytes> {
762        if self.chunks.is_empty() {
763            return None;
764        }
765
766        let mut buf = Vec::new();
767        let mut ser = Serializer::new(&mut buf);
768        let mut ack_map = HashMap::new();
769
770        for chunk in self.chunks {
771            ack_map.clear();
772            if let TcpSourceAck::Ack = ack {
773                ack_map.insert("ack", chunk);
774            };
775            ack_map.serialize(&mut ser).unwrap();
776        }
777        Some(buf.into())
778    }
779}
780
781/// Normalized fluent message.
782#[derive(Debug, PartialEq)]
783struct FluentEvent<'a> {
784    tag: FluentTag,
785    timestamp: FluentTimestamp,
786    record: FluentRecord,
787    log_namespace: &'a LogNamespace,
788}
789
790impl From<FluentEvent<'_>> for Event {
791    fn from(frame: FluentEvent) -> Event {
792        LogEvent::from(frame).into()
793    }
794}
795
796struct FluentFrame {
797    events: SmallVec<[Event; 1]>,
798    chunk: Option<String>,
799}
800
801impl From<FluentFrame> for SmallVec<[Event; 1]> {
802    fn from(frame: FluentFrame) -> Self {
803        frame.events
804    }
805}
806
807impl From<FluentEvent<'_>> for LogEvent {
808    fn from(frame: FluentEvent) -> LogEvent {
809        let FluentEvent {
810            tag,
811            timestamp,
812            record,
813            log_namespace,
814        } = frame;
815
816        let mut log = LogEvent::default();
817
818        log_namespace.insert_vector_metadata(
819            &mut log,
820            log_schema().source_type_key(),
821            path!("source_type"),
822            Bytes::from_static(FluentConfig::NAME.as_bytes()),
823        );
824
825        match log_namespace {
826            LogNamespace::Vector => {
827                log.insert(metadata_path!(FluentConfig::NAME, "timestamp"), timestamp);
828                log.insert(metadata_path!("vector", "ingest_timestamp"), Utc::now());
829            }
830            LogNamespace::Legacy => {
831                log.maybe_insert(log_schema().timestamp_key_target_path(), timestamp);
832            }
833        }
834
835        log_namespace.insert_source_metadata(
836            FluentConfig::NAME,
837            &mut log,
838            Some(LegacyKey::Overwrite(path!("tag"))),
839            path!("tag"),
840            tag,
841        );
842
843        for (key, value) in record.into_iter() {
844            let value: Value = value.into();
845            log_namespace.insert_source_metadata(
846                FluentConfig::NAME,
847                &mut log,
848                Some(LegacyKey::Overwrite(path!(key.as_str()))),
849                path!("record", key.as_str()),
850                value,
851            );
852        }
853        log
854    }
855}
856
857#[cfg(test)]
858mod tests {
859    use bytes::BytesMut;
860    use chrono::{DateTime, Utc};
861    use rmp_serde::Serializer;
862    use serde::Serialize;
863    use tokio::{
864        io::{AsyncReadExt, AsyncWriteExt},
865        time::{Duration, error::Elapsed, timeout},
866    };
867    use tokio_util::codec::Decoder;
868    use vector_lib::{assert_event_data_eq, lookup::OwnedTargetPath, schema::Definition};
869    use vrl::event_path;
870    use vrl::value::{ObjectMap, Value, kind::Collection};
871
872    use super::{message::FluentMessageOptions, *};
873    use crate::{
874        SourceSender,
875        config::{SourceConfig, SourceContext},
876        event::EventStatus,
877        test_util::{self, addr::next_addr, trace_init, wait_for_tcp},
878    };
879
880    #[test]
881    fn generate_config() {
882        crate::test_util::test_generate_config::<FluentConfig>();
883    }
884
885    // useful references for msgpack:
886    // Spec: https://github.com/msgpack/msgpack/blob/master/spec.md
887    // Encode to array of bytes: https://kawanet.github.io/msgpack-lite/
888    // Decode base64: https://toolslick.com/conversion/data/messagepack-to-json
889
890    fn mock_event(name: &str, timestamp: &str) -> Event {
891        Event::Log(LogEvent::from(ObjectMap::from([
892            ("message".into(), Value::from(name)),
893            (
894                log_schema().source_type_key().unwrap().to_string().into(),
895                Value::from(FluentConfig::NAME),
896            ),
897            ("tag".into(), Value::from("tag.name")),
898            (
899                "timestamp".into(),
900                Value::Timestamp(DateTime::parse_from_rfc3339(timestamp).unwrap().into()),
901            ),
902        ])))
903    }
904
905    #[test]
906    fn decode_message_mode() {
907        //[
908        //  "tag.name",
909        //  1441588984,
910        //  {"message": "bar"},
911        //]
912        let message: Vec<u8> = vec![
913            147, 168, 116, 97, 103, 46, 110, 97, 109, 101, 206, 85, 236, 230, 248, 129, 167, 109,
914            101, 115, 115, 97, 103, 101, 163, 98, 97, 114,
915        ];
916
917        let expected = mock_event("bar", "2015-09-07T01:23:04Z");
918        let got = decode_all(message.clone()).unwrap();
919        assert_event_data_eq!(got.0[0], expected);
920        assert_eq!(got.1, message.len());
921    }
922
923    #[test]
924    fn decode_message_mode_with_options() {
925        //[
926        //  "tag.name",
927        //   1441588984,
928        //   { "message": "bar" },
929        //   { "size": 1 }
930        //]
931        let message: Vec<u8> = vec![
932            148, 168, 116, 97, 103, 46, 110, 97, 109, 101, 206, 85, 236, 230, 248, 129, 167, 109,
933            101, 115, 115, 97, 103, 101, 163, 98, 97, 114, 129, 164, 115, 105, 122, 101, 1,
934        ];
935
936        let expected = mock_event("bar", "2015-09-07T01:23:04Z");
937        let got = decode_all(message.clone()).unwrap();
938        assert_eq!(got.1, message.len());
939        assert_event_data_eq!(got.0[0], expected);
940    }
941
942    #[test]
943    fn decode_forward_mode() {
944        //[
945        //    "tag.name",
946        //    [
947        //        [1441588984, {"message": "foo"}],
948        //        [1441588985, {"message": "bar"}],
949        //        [1441588986, {"message": "baz"}]
950        //    ]
951        //]
952        let message: Vec<u8> = vec![
953            146, 168, 116, 97, 103, 46, 110, 97, 109, 101, 147, 146, 206, 85, 236, 230, 248, 129,
954            167, 109, 101, 115, 115, 97, 103, 101, 163, 102, 111, 111, 146, 206, 85, 236, 230, 249,
955            129, 167, 109, 101, 115, 115, 97, 103, 101, 163, 98, 97, 114, 146, 206, 85, 236, 230,
956            250, 129, 167, 109, 101, 115, 115, 97, 103, 101, 163, 98, 97, 122,
957        ];
958
959        let expected = [
960            mock_event("foo", "2015-09-07T01:23:04Z"),
961            mock_event("bar", "2015-09-07T01:23:05Z"),
962            mock_event("baz", "2015-09-07T01:23:06Z"),
963        ];
964        let got = decode_all(message.clone()).unwrap();
965
966        assert_eq!(got.1, message.len());
967        assert_event_data_eq!(got.0[0], expected[0]);
968        assert_event_data_eq!(got.0[1], expected[1]);
969        assert_event_data_eq!(got.0[2], expected[2]);
970    }
971
972    #[test]
973    fn decode_forward_mode_with_options() {
974        //[
975        //    "tag.name",
976        //    [
977        //        [1441588984, {"message": "foo"}],
978        //        [1441588985, {"message": "bar"}],
979        //        [1441588986, {"message": "baz"}]
980        //    ],
981        //    {"size": 3}
982        //]
983        let message: Vec<u8> = vec![
984            147, 168, 116, 97, 103, 46, 110, 97, 109, 101, 147, 146, 206, 85, 236, 230, 248, 129,
985            167, 109, 101, 115, 115, 97, 103, 101, 163, 102, 111, 111, 146, 206, 85, 236, 230, 249,
986            129, 167, 109, 101, 115, 115, 97, 103, 101, 163, 98, 97, 114, 146, 206, 85, 236, 230,
987            250, 129, 167, 109, 101, 115, 115, 97, 103, 101, 163, 98, 97, 122, 129, 164, 115, 105,
988            122, 101, 3,
989        ];
990
991        let expected = [
992            mock_event("foo", "2015-09-07T01:23:04Z"),
993            mock_event("bar", "2015-09-07T01:23:05Z"),
994            mock_event("baz", "2015-09-07T01:23:06Z"),
995        ];
996
997        let got = decode_all(message.clone()).unwrap();
998
999        assert_eq!(got.1, message.len());
1000
1001        assert_event_data_eq!(got.0[0], expected[0]);
1002        assert_event_data_eq!(got.0[1], expected[1]);
1003        assert_event_data_eq!(got.0[2], expected[2]);
1004    }
1005
1006    #[test]
1007    fn decode_packed_forward_mode() {
1008        //[
1009        //    "tag.name",
1010        //    <packed messages>
1011        //]
1012        //
1013        //With packed messages as bin:
1014        // [1441588984, {"message": "foo"}]
1015        // [1441588985, {"message": "bar"}]
1016        // [1441588986, {"message": "baz"}]
1017        let message: Vec<u8> = vec![
1018            147, 168, 116, 97, 103, 46, 110, 97, 109, 101, 196, 57, 146, 206, 85, 236, 230, 248,
1019            129, 167, 109, 101, 115, 115, 97, 103, 101, 163, 102, 111, 111, 146, 206, 85, 236, 230,
1020            249, 129, 167, 109, 101, 115, 115, 97, 103, 101, 163, 98, 97, 114, 146, 206, 85, 236,
1021            230, 250, 129, 167, 109, 101, 115, 115, 97, 103, 101, 163, 98, 97, 122, 129, 167, 109,
1022            101, 115, 115, 97, 103, 101, 163, 102, 111, 111,
1023        ];
1024
1025        let expected = [
1026            mock_event("foo", "2015-09-07T01:23:04Z"),
1027            mock_event("bar", "2015-09-07T01:23:05Z"),
1028            mock_event("baz", "2015-09-07T01:23:06Z"),
1029        ];
1030
1031        let got = decode_all(message.clone()).unwrap();
1032
1033        assert_eq!(got.1, message.len());
1034        assert_event_data_eq!(got.0[0], expected[0]);
1035        assert_event_data_eq!(got.0[1], expected[1]);
1036        assert_event_data_eq!(got.0[2], expected[2]);
1037    }
1038
1039    //  TODO
1040    #[test]
1041    fn decode_compressed_packed_forward_mode() {
1042        //[
1043        //    "tag.name",
1044        //    <packed messages>,
1045        //    {"compressed": "gzip"}
1046        //]
1047        //
1048        //With gzip'd packed messages as bin:
1049        // [1441588984, {"message": "foo"}]
1050        // [1441588985, {"message": "bar"}]
1051        // [1441588986, {"message": "baz"}]
1052        let message: Vec<u8> = vec![
1053            147, 168, 116, 97, 103, 46, 110, 97, 109, 101, 196, 55, 31, 139, 8, 0, 245, 10, 168,
1054            96, 0, 3, 155, 116, 46, 244, 205, 179, 31, 141, 203, 115, 83, 139, 139, 19, 211, 83,
1055            23, 167, 229, 231, 79, 2, 9, 253, 68, 8, 37, 37, 22, 129, 133, 126, 33, 11, 85, 1, 0,
1056            53, 3, 158, 28, 57, 0, 0, 0, 129, 170, 99, 111, 109, 112, 114, 101, 115, 115, 101, 100,
1057            164, 103, 122, 105, 112,
1058        ];
1059
1060        let expected = [
1061            mock_event("foo", "2015-09-07T01:23:04Z"),
1062            mock_event("bar", "2015-09-07T01:23:05Z"),
1063            mock_event("baz", "2015-09-07T01:23:06Z"),
1064        ];
1065
1066        let got = decode_all(message.clone()).unwrap();
1067
1068        assert_eq!(got.1, message.len());
1069        assert_event_data_eq!(got.0[0], expected[0]);
1070        assert_event_data_eq!(got.0[1], expected[1]);
1071        assert_event_data_eq!(got.0[2], expected[2]);
1072    }
1073
1074    fn decode_all(message: Vec<u8>) -> Result<(SmallVec<[Event; 1]>, usize), DecodeError> {
1075        let mut buf = BytesMut::from(&message[..]);
1076
1077        let mut decoder = FluentDecoder::new(LogNamespace::default());
1078
1079        let (frame, byte_size) = decoder.decode(&mut buf)?.unwrap();
1080        Ok((frame.into(), byte_size))
1081    }
1082
1083    #[test]
1084    fn decode_incomplete_frame_requests_more_data() {
1085        // An array of 2 elements (`0x92`) with a tag string declaring 16 bytes
1086        // (`0xb0`) but only 4 bytes provided: a valid, incomplete frame. The
1087        // decoder should ask for more data rather than erroring.
1088        let partial: Vec<u8> = vec![0x92, 0xb0, b't', b'a', b'g'];
1089        let mut buf = BytesMut::from(&partial[..]);
1090        let mut decoder = FluentDecoder::new(LogNamespace::default());
1091        assert!(matches!(decoder.decode(&mut buf), Ok(None)));
1092        // The buffer is retained so more bytes can complete the frame.
1093        assert_eq!(buf.len(), partial.len());
1094    }
1095
1096    #[test]
1097    fn decode_oversized_frame_is_rejected() {
1098        // Same shape as above (a 2-element array whose string is declared far
1099        // larger than what has arrived), but with a decoder whose frame cap is
1100        // tiny. Once the buffer grows past the cap without yielding a complete
1101        // message, the decoder must refuse to keep buffering and signal a
1102        // non-recoverable error so the connection is dropped.
1103        let max_frame_size = 8;
1104        let partial: Vec<u8> = vec![0x92, 0xb0, b't', b'a', b'g', b'.', b'n', b'a', b'm', b'e'];
1105        assert!(partial.len() > max_frame_size);
1106
1107        let mut buf = BytesMut::from(&partial[..]);
1108        let mut decoder = FluentDecoder {
1109            log_namespace: LogNamespace::default(),
1110            max_frame_size,
1111        };
1112
1113        let error = match decoder.decode(&mut buf) {
1114            Err(error) => error,
1115            Ok(_) => panic!("expected FrameTooLarge error, got Ok"),
1116        };
1117        assert!(
1118            matches!(error, DecodeError::FrameTooLarge { size, max } if size == partial.len() && max == max_frame_size),
1119            "unexpected error: {error:?}"
1120        );
1121        // A frame-too-large error must terminate the connection.
1122        assert!(!error.can_continue());
1123    }
1124
1125    #[tokio::test]
1126    async fn ack_delivered_without_chunk() {
1127        let (result, output) = check_acknowledgements(EventStatus::Delivered, false).await;
1128        assert!(result.is_err()); // the `_` inside this error is `Elapsed`
1129        assert!(output.is_empty());
1130    }
1131
1132    #[tokio::test]
1133    async fn ack_delivered_with_chunk() {
1134        let (result, output) = check_acknowledgements(EventStatus::Delivered, true).await;
1135        assert_eq!(result.unwrap().unwrap(), output.len());
1136        let expected: Vec<u8> = vec![0x81, 0xa3, 0x61, 0x63]; // { "ack": ...
1137        assert_eq!(output[..expected.len()], expected);
1138    }
1139
1140    #[tokio::test]
1141    async fn ack_failed_without_chunk() {
1142        let (result, output) = check_acknowledgements(EventStatus::Rejected, false).await;
1143        assert_eq!(result.unwrap().unwrap(), output.len());
1144        assert!(output.is_empty());
1145    }
1146
1147    #[tokio::test]
1148    async fn ack_failed_with_chunk() {
1149        let (result, output) = check_acknowledgements(EventStatus::Rejected, true).await;
1150        assert_eq!(result.unwrap().unwrap(), output.len());
1151        let expected: Vec<u8> = vec![0x80]; // { }
1152        assert_eq!(output, expected);
1153    }
1154
1155    async fn check_acknowledgements(
1156        status: EventStatus,
1157        with_chunk: bool,
1158    ) -> (Result<Result<usize, std::io::Error>, Elapsed>, Bytes) {
1159        trace_init();
1160
1161        let (sender, recv) = SourceSender::new_test_finalize(status);
1162        let (_guard, address) = next_addr();
1163        let source = FluentConfig {
1164            mode: FluentMode::Tcp(FluentTcpConfig {
1165                address: address.into(),
1166                tls: None,
1167                keepalive: None,
1168                permit_origin: None,
1169                receive_buffer_bytes: None,
1170                acknowledgements: true.into(),
1171                connection_limit: None,
1172            }),
1173            log_namespace: None,
1174        }
1175        .build(SourceContext::new_test(sender, None))
1176        .await
1177        .unwrap();
1178        tokio::spawn(source);
1179        wait_for_tcp(address).await;
1180
1181        let msg = uuid::Uuid::new_v4().to_string();
1182        let tag = uuid::Uuid::new_v4().to_string();
1183        let req = build_req(&tag, &[("field", &msg)], with_chunk);
1184
1185        let sender = tokio::spawn(async move {
1186            let mut socket = tokio::net::TcpStream::connect(address).await.unwrap();
1187            socket.write_all(&req).await.unwrap();
1188
1189            let mut output = BytesMut::new();
1190            (
1191                timeout(Duration::from_millis(250), socket.read_buf(&mut output)).await,
1192                output,
1193            )
1194        });
1195        let events = test_util::collect_n(recv, 1).await;
1196        let (result, output) = sender.await.unwrap();
1197
1198        assert_eq!(events.len(), 1);
1199        let log = events[0].as_log();
1200        assert_eq!(log.get(event_path!("field")).unwrap(), &msg.into());
1201        assert!(matches!(
1202            log.get(event_path!("host")).unwrap(),
1203            Value::Bytes(_)
1204        ));
1205        assert!(matches!(
1206            log.get(event_path!("timestamp")).unwrap(),
1207            Value::Timestamp(_)
1208        ));
1209        assert_eq!(log.get(event_path!("tag")).unwrap(), &tag.into());
1210
1211        (result, output.into())
1212    }
1213
1214    fn build_req(tag: &str, fields: &[(&str, &str)], with_chunk: bool) -> Vec<u8> {
1215        let mut record = FluentRecord::default();
1216        for (tag, value) in fields {
1217            record.insert((*tag).into(), rmpv::Value::String((*value).into()).into());
1218        }
1219        let chunk = with_chunk.then(|| BASE64_STANDARD.encode(uuid::Uuid::new_v4().as_bytes()));
1220        let req = FluentMessage::MessageWithOptions(
1221            tag.into(),
1222            FluentTimestamp::Unix(Utc::now()),
1223            record,
1224            FluentMessageOptions {
1225                chunk,
1226                ..Default::default()
1227            },
1228        );
1229        let mut buf = Vec::new();
1230        req.serialize(&mut Serializer::new(&mut buf)).unwrap();
1231        buf
1232    }
1233
1234    #[test]
1235    fn output_schema_definition_vector_namespace() {
1236        let config = FluentConfig {
1237            mode: FluentMode::Tcp(FluentTcpConfig {
1238                address: SocketListenAddr::SocketAddr("0.0.0.0:24224".parse().unwrap()),
1239                tls: None,
1240                keepalive: None,
1241                permit_origin: None,
1242                receive_buffer_bytes: None,
1243                acknowledgements: false.into(),
1244                connection_limit: None,
1245            }),
1246            log_namespace: Some(true),
1247        };
1248
1249        let definitions = config
1250            .outputs(LogNamespace::Vector)
1251            .remove(0)
1252            .schema_definition(true);
1253
1254        let expected_definition =
1255            Definition::new_with_default_metadata(Kind::bytes(), [LogNamespace::Vector])
1256                .with_meaning(OwnedTargetPath::event_root(), "message")
1257                .with_metadata_field(
1258                    &owned_value_path!("vector", "source_type"),
1259                    Kind::bytes(),
1260                    None,
1261                )
1262                .with_metadata_field(&owned_value_path!("fluent", "tag"), Kind::bytes(), None)
1263                .with_metadata_field(
1264                    &owned_value_path!("fluent", "timestamp"),
1265                    Kind::timestamp(),
1266                    Some("timestamp"),
1267                )
1268                .with_metadata_field(
1269                    &owned_value_path!("fluent", "record"),
1270                    Kind::object(Collection::empty().with_unknown(Kind::bytes())).or_undefined(),
1271                    None,
1272                )
1273                .with_metadata_field(
1274                    &owned_value_path!("vector", "ingest_timestamp"),
1275                    Kind::timestamp(),
1276                    None,
1277                )
1278                .with_metadata_field(
1279                    &owned_value_path!("fluent", "host"),
1280                    Kind::bytes(),
1281                    Some("host"),
1282                )
1283                .with_metadata_field(
1284                    &owned_value_path!("fluent", "tls_client_metadata"),
1285                    Kind::object(Collection::empty().with_unknown(Kind::bytes())).or_undefined(),
1286                    None,
1287                );
1288
1289        assert_eq!(definitions, Some(expected_definition))
1290    }
1291
1292    #[test]
1293    fn output_schema_definition_legacy_namespace() {
1294        let config = FluentConfig {
1295            mode: FluentMode::Tcp(FluentTcpConfig {
1296                address: SocketListenAddr::SocketAddr("0.0.0.0:24224".parse().unwrap()),
1297                tls: None,
1298                keepalive: None,
1299                permit_origin: None,
1300                receive_buffer_bytes: None,
1301                acknowledgements: false.into(),
1302                connection_limit: None,
1303            }),
1304            log_namespace: None,
1305        };
1306
1307        let definitions = config
1308            .outputs(LogNamespace::Legacy)
1309            .remove(0)
1310            .schema_definition(true);
1311
1312        let expected_definition = Definition::new_with_default_metadata(
1313            Kind::object(Collection::empty()),
1314            [LogNamespace::Legacy],
1315        )
1316        .with_event_field(
1317            &owned_value_path!("message"),
1318            Kind::bytes(),
1319            Some("message"),
1320        )
1321        .with_event_field(&owned_value_path!("source_type"), Kind::bytes(), None)
1322        .with_event_field(&owned_value_path!("tag"), Kind::bytes(), None)
1323        .with_event_field(&owned_value_path!("timestamp"), Kind::timestamp(), None)
1324        .with_event_field(&owned_value_path!("host"), Kind::bytes(), Some("host"))
1325        .unknown_fields(Kind::bytes());
1326
1327        assert_eq!(definitions, Some(expected_definition))
1328    }
1329}
1330
1331#[cfg(all(test, feature = "fluent-integration-tests"))]
1332mod integration_tests {
1333    use std::{fs::File, io::Write, net::SocketAddr, time::Duration};
1334
1335    use futures::Stream;
1336    use tokio::time::sleep;
1337    use vector_lib::event::{Event, EventStatus};
1338    use vrl::event_path;
1339
1340    use crate::{
1341        SourceSender,
1342        config::{SourceConfig, SourceContext},
1343        docker::Container,
1344        sources::fluent::{FluentConfig, FluentMode, FluentTcpConfig},
1345        test_util::{
1346            addr::{PortGuard, next_addr, next_addr_for_ip},
1347            collect_ready,
1348            components::{SOCKET_PUSH_SOURCE_TAGS, assert_source_compliance},
1349            random_string, wait_for_tcp,
1350        },
1351    };
1352
1353    const FLUENT_BIT_IMAGE: &str = "fluent/fluent-bit";
1354    const FLUENT_BIT_TAG: &str = "1.7";
1355    const FLUENTD_IMAGE: &str = "fluent/fluentd";
1356    const FLUENTD_TAG: &str = "v1.12";
1357
1358    fn make_file(name: &str, content: &str) -> tempfile::TempDir {
1359        let dir = tempfile::tempdir().unwrap();
1360        let mut file = File::create(dir.path().join(name)).unwrap();
1361        write!(&mut file, "{content}").unwrap();
1362        dir
1363    }
1364
1365    #[tokio::test]
1366    async fn fluentbit() {
1367        test_fluentbit(EventStatus::Delivered).await;
1368    }
1369
1370    #[tokio::test]
1371    async fn fluentbit_rejection() {
1372        test_fluentbit(EventStatus::Rejected).await;
1373    }
1374
1375    async fn test_fluentbit(status: EventStatus) {
1376        assert_source_compliance(&SOCKET_PUSH_SOURCE_TAGS, async move {
1377            let (_guard, test_address) = next_addr();
1378            let (out, source_address, _guard) = source(status).await;
1379
1380            let dir = make_file(
1381                "fluent-bit.conf",
1382                &format!(
1383                    r#"
1384[SERVICE]
1385    Grace      0
1386    Flush      1
1387    Daemon     off
1388
1389[INPUT]
1390    Name       http
1391    Host       {listen_host}
1392    Port       {listen_port}
1393
1394[OUTPUT]
1395    Name          forward
1396    Match         *
1397    Host          host.docker.internal
1398    Port          {send_port}
1399    Require_ack_response true
1400    "#,
1401                    listen_host = test_address.ip(),
1402                    listen_port = test_address.port(),
1403                    send_port = source_address.port(),
1404                ),
1405            );
1406
1407            let msg = random_string(64);
1408            let body = serde_json::json!({ "message": msg });
1409
1410            let events = Container::new(FLUENT_BIT_IMAGE, FLUENT_BIT_TAG)
1411                .bind(dir.path().display(), "/fluent-bit/etc")
1412                .run(async move {
1413                    wait_for_tcp(test_address).await;
1414                    reqwest::Client::new()
1415                        .post(format!("http://{test_address}/"))
1416                        .header("content-type", "application/json")
1417                        .body(body.to_string())
1418                        .send()
1419                        .await
1420                        .unwrap();
1421                    sleep(Duration::from_secs(2)).await;
1422
1423                    collect_ready(out).await
1424                })
1425                .await;
1426
1427            assert_eq!(events.len(), 1);
1428            let log = events[0].as_log();
1429            assert_eq!(log["tag"], "http.0".into());
1430            assert_eq!(log["message"], msg.into());
1431            assert!(log.get(event_path!("timestamp")).is_some());
1432            assert!(log.get(event_path!("host")).is_some());
1433        })
1434        .await;
1435    }
1436
1437    #[tokio::test]
1438    async fn fluentd() {
1439        test_fluentd(EventStatus::Delivered, "").await;
1440    }
1441
1442    #[tokio::test]
1443    async fn fluentd_gzip() {
1444        test_fluentd(EventStatus::Delivered, "compress gzip").await;
1445    }
1446
1447    #[tokio::test]
1448    async fn fluentd_rejection() {
1449        test_fluentd(EventStatus::Rejected, "").await;
1450    }
1451
1452    async fn test_fluentd(status: EventStatus, options: &str) {
1453        assert_source_compliance(&SOCKET_PUSH_SOURCE_TAGS, async move {
1454            let (_guard, test_address) = next_addr();
1455            let (out, source_address, _guard) = source(status).await;
1456
1457            let config = format!(
1458                r#"
1459<source>
1460  @type http
1461  bind {http_host}
1462  port {http_port}
1463</source>
1464
1465<match *>
1466  @type forward
1467  <server>
1468    name  local
1469    host  host.docker.internal
1470    port  {port}
1471  </server>
1472  <buffer>
1473    flush_mode immediate
1474  </buffer>
1475  require_ack_response true
1476  ack_response_timeout 1
1477  {options}
1478</match>
1479"#,
1480                http_host = test_address.ip(),
1481                http_port = test_address.port(),
1482                port = source_address.port(),
1483                options = options
1484            );
1485
1486            let dir = make_file("fluent.conf", &config);
1487
1488            let msg = random_string(64);
1489            let body = serde_json::json!({ "message": msg });
1490
1491            let events = Container::new(FLUENTD_IMAGE, FLUENTD_TAG)
1492                .bind(dir.path().display(), "/fluentd/etc")
1493                .run(async move {
1494                    wait_for_tcp(test_address).await;
1495                    reqwest::Client::new()
1496                        .post(format!("http://{test_address}/"))
1497                        .header("content-type", "application/json")
1498                        .body(body.to_string())
1499                        .send()
1500                        .await
1501                        .unwrap();
1502                    sleep(Duration::from_secs(2)).await;
1503                    collect_ready(out).await
1504                })
1505                .await;
1506
1507            assert_eq!(events.len(), 1);
1508            assert_eq!(events[0].as_log()["tag"], "".into());
1509            assert_eq!(events[0].as_log()["message"], msg.into());
1510            assert!(events[0].as_log().get(event_path!("timestamp")).is_some());
1511            assert!(events[0].as_log().get(event_path!("host")).is_some());
1512        })
1513        .await;
1514    }
1515
1516    async fn source(
1517        status: EventStatus,
1518    ) -> (impl Stream<Item = Event> + Unpin, SocketAddr, PortGuard) {
1519        let (sender, recv) = SourceSender::new_test_finalize(status);
1520        let (_guard, address) =
1521            next_addr_for_ip(std::net::IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED));
1522        tokio::spawn(async move {
1523            FluentConfig {
1524                mode: FluentMode::Tcp(FluentTcpConfig {
1525                    address: address.into(),
1526                    tls: None,
1527                    keepalive: None,
1528                    permit_origin: None,
1529                    receive_buffer_bytes: None,
1530                    acknowledgements: false.into(),
1531                    connection_limit: None,
1532                }),
1533                log_namespace: None,
1534            }
1535            .build(SourceContext::new_test(sender, None))
1536            .await
1537            .unwrap()
1538            .await
1539            .unwrap()
1540        });
1541        wait_for_tcp(address).await;
1542        (recv, address, _guard)
1543    }
1544}