vector/sources/util/http/
headers.rs1use bytes::Bytes;
2use vector_lib::lookup::path;
3use vector_lib::{
4 config::{LegacyKey, LogNamespace},
5 event::Event,
6};
7use warp::http::{HeaderMap, HeaderValue};
8
9use crate::event::Value;
10use crate::sources::http_server::HttpConfigParamKind;
11
12pub fn add_headers(
13 events: &mut [Event],
14 headers_config: &[HttpConfigParamKind],
15 headers: &HeaderMap,
16 log_namespace: LogNamespace,
17 source_name: &'static str,
18) {
19 for h in headers_config {
20 match h {
21 HttpConfigParamKind::Exact(header_name) => {
25 let value = headers.get(header_name).map(HeaderValue::as_bytes);
26
27 for event in events.iter_mut() {
28 if let Event::Log(log) = event {
29 log_namespace.insert_source_metadata(
30 source_name,
31 log,
32 Some(LegacyKey::InsertIfEmpty(path!(header_name))),
33 path!("headers", header_name),
34 Value::from(value.map(Bytes::copy_from_slice)),
35 );
36 }
37 }
38 }
39 HttpConfigParamKind::Glob(header_pattern) => {
42 for header_name in headers.keys() {
43 if header_pattern
44 .matches_with(header_name.as_str(), glob::MatchOptions::default())
45 {
46 let value = headers.get(header_name).map(HeaderValue::as_bytes);
47
48 for event in events.iter_mut() {
49 if let Event::Log(log) = event {
50 log_namespace.insert_source_metadata(
51 source_name,
52 log,
53 Some(LegacyKey::InsertIfEmpty(path!(header_name.as_str()))),
54 path!("headers", header_name.as_str()),
55 Value::from(value.map(Bytes::copy_from_slice)),
56 );
57 }
58 }
59 }
60 }
61 }
62 };
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use crate::event::LogEvent;
69 use crate::sources::{http_server::HttpConfigParamKind, util::add_headers};
70
71 use vector_lib::config::LogNamespace;
72 use vrl::{path, value};
73 use warp::http::HeaderMap;
74
75 #[test]
76 fn multiple_headers() {
77 let header_names = [
78 HttpConfigParamKind::Exact("Content-Type".into()),
79 HttpConfigParamKind::Exact("User-Agent".into()),
80 ];
81 let mut headers = HeaderMap::new();
82 headers.insert("Content-Type", "application/x-protobuf".parse().unwrap());
83 headers.insert("User-Agent", "Test".parse().unwrap());
84 headers.insert("Content-Encoding", "gzip".parse().unwrap());
85
86 let mut base_log = [LogEvent::from(value!({})).into()];
87 add_headers(
88 &mut base_log,
89 &header_names,
90 &headers,
91 LogNamespace::Legacy,
92 "test",
93 );
94 let mut namespaced_log = [LogEvent::from(value!({})).into()];
95 add_headers(
96 &mut namespaced_log,
97 &header_names,
98 &headers,
99 LogNamespace::Vector,
100 "test",
101 );
102
103 assert_eq!(
104 base_log[0].as_log().value(),
105 namespaced_log[0]
106 .metadata()
107 .value()
108 .get(path!("test", "headers"))
109 .unwrap()
110 );
111 }
112
113 #[test]
114 fn multiple_headers_wildcard() {
115 let header_names = [HttpConfigParamKind::Glob(
116 glob::Pattern::new("Content-*").unwrap(),
117 )];
118 let mut headers = HeaderMap::new();
119 headers.insert("Content-Type", "application/x-protobuf".parse().unwrap());
120 headers.insert("User-Agent", "Test".parse().unwrap());
121 headers.insert("Content-Encoding", "gzip".parse().unwrap());
122
123 let mut base_log = [LogEvent::from(value!({})).into()];
124 add_headers(
125 &mut base_log,
126 &header_names,
127 &headers,
128 LogNamespace::Legacy,
129 "test",
130 );
131 let mut namespaced_log = [LogEvent::from(value!({})).into()];
132 add_headers(
133 &mut namespaced_log,
134 &header_names,
135 &headers,
136 LogNamespace::Vector,
137 "test",
138 );
139
140 let log = base_log[0].as_log();
141 assert_eq!(
142 log.value(),
143 namespaced_log[0]
144 .metadata()
145 .value()
146 .get(path!("test", "headers"))
147 .unwrap(),
148 "Checking legacy and namespaced log contain headers string"
149 );
150 assert_eq!(
151 log["content-type"],
152 "application/x-protobuf".into(),
153 "Checking log contains Content-Type header"
154 );
155 assert!(
156 !log.contains("user-agent"),
157 "Checking log does not contain User-Agent header"
158 );
159 assert_eq!(
160 log["content-encoding"],
161 "gzip".into(),
162 "Checking log contains Content-Encoding header"
163 );
164 }
165}