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