Skip to main content

hive_router_plan_executor/headers/
request.rs

1use hive_router_internal::expressions::ExecutableProgram;
2use http::HeaderMap;
3
4use crate::{
5    execution::client_request_details::ClientRequestDetails,
6    headers::{
7        errors::HeaderRuleRuntimeError,
8        expression::vrl_value_to_header_value,
9        plan::{
10            HeaderRulesPlan, RequestHeaderRule, RequestInsertExpression, RequestInsertStatic,
11            RequestPropagateNamed, RequestPropagateRegex, RequestRemoveNamed, RequestRemoveRegex,
12        },
13        sanitizer::{is_denied_header, is_never_join_header},
14    },
15};
16
17pub fn modify_subgraph_request_headers(
18    header_rule_plan: &HeaderRulesPlan,
19    subgraph_name: &str,
20    client_request: &ClientRequestDetails,
21    output_headers: &mut HeaderMap,
22) -> Result<(), HeaderRuleRuntimeError> {
23    let global_actions = &header_rule_plan.request.global;
24    let subgraph_actions = header_rule_plan.request.by_subgraph.get(subgraph_name);
25
26    let ctx = RequestExpressionContext {
27        subgraph_name,
28        client_request,
29    };
30
31    for action in global_actions
32        .iter()
33        .chain(subgraph_actions.into_iter().flatten())
34    {
35        action.apply_request_headers(&ctx, output_headers)?;
36    }
37
38    Ok(())
39}
40
41pub struct RequestExpressionContext<'a> {
42    pub subgraph_name: &'a str,
43    pub client_request: &'a ClientRequestDetails<'a>,
44}
45
46trait ApplyRequestHeader {
47    fn apply_request_headers(
48        &self,
49        ctx: &RequestExpressionContext,
50        output_headers: &mut HeaderMap,
51    ) -> Result<(), HeaderRuleRuntimeError>;
52}
53
54impl ApplyRequestHeader for RequestHeaderRule {
55    fn apply_request_headers(
56        &self,
57        ctx: &RequestExpressionContext,
58        output_headers: &mut HeaderMap,
59    ) -> Result<(), HeaderRuleRuntimeError> {
60        match self {
61            Self::PropagateNamed(data) => data.apply_request_headers(ctx, output_headers),
62            Self::PropagateRegex(data) => data.apply_request_headers(ctx, output_headers),
63            Self::InsertStatic(data) => data.apply_request_headers(ctx, output_headers),
64            Self::InsertExpression(data) => data.apply_request_headers(ctx, output_headers),
65            Self::RemoveNamed(data) => data.apply_request_headers(ctx, output_headers),
66            Self::RemoveRegex(data) => data.apply_request_headers(ctx, output_headers),
67        }
68    }
69}
70
71impl ApplyRequestHeader for RequestPropagateNamed {
72    fn apply_request_headers(
73        &self,
74        ctx: &RequestExpressionContext,
75        output_headers: &mut HeaderMap,
76    ) -> Result<(), HeaderRuleRuntimeError> {
77        let mut matched = false;
78
79        for header_name in &self.names {
80            if is_denied_header(header_name) {
81                continue;
82            }
83            if let Some(header_value) = ctx.client_request.headers.get(header_name) {
84                let destination_name = self.rename.as_ref().unwrap_or(header_name);
85                output_headers.append(destination_name, header_value.into());
86                matched = true;
87            }
88        }
89
90        if !matched {
91            // If no headers matched, and a default is provided, use it
92            if let (Some(default_value), Some(first_name)) = (&self.default, self.names.first()) {
93                let destination_name = self.rename.as_ref().unwrap_or(first_name);
94
95                if is_denied_header(destination_name) {
96                    return Ok(());
97                }
98
99                output_headers.append(destination_name, default_value.clone());
100            }
101        }
102
103        Ok(())
104    }
105}
106
107impl ApplyRequestHeader for RequestPropagateRegex {
108    fn apply_request_headers(
109        &self,
110        ctx: &RequestExpressionContext,
111        output_headers: &mut HeaderMap,
112    ) -> Result<(), HeaderRuleRuntimeError> {
113        for (header_name, header_value) in ctx.client_request.headers {
114            if is_denied_header(header_name) {
115                continue;
116            }
117
118            let header_bytes = header_name.as_str().as_bytes();
119
120            let Some(include_regex) = &self.include else {
121                continue;
122            };
123
124            if !include_regex.is_match(header_bytes) {
125                continue;
126            }
127
128            if self
129                .exclude
130                .as_ref()
131                .is_some_and(|regex| regex.is_match(header_bytes))
132            {
133                continue;
134            }
135
136            output_headers.append(header_name, header_value.into());
137        }
138
139        Ok(())
140    }
141}
142
143impl ApplyRequestHeader for RequestInsertStatic {
144    fn apply_request_headers(
145        &self,
146        _ctx: &RequestExpressionContext,
147        output_headers: &mut HeaderMap,
148    ) -> Result<(), HeaderRuleRuntimeError> {
149        if !is_denied_header(&self.name) {
150            if is_never_join_header(&self.name) {
151                output_headers.append(self.name.clone(), self.value.clone());
152            } else {
153                output_headers.insert(self.name.clone(), self.value.clone());
154            }
155        }
156
157        Ok(())
158    }
159}
160
161impl ApplyRequestHeader for RequestInsertExpression {
162    fn apply_request_headers(
163        &self,
164        ctx: &RequestExpressionContext,
165        output_headers: &mut HeaderMap,
166    ) -> Result<(), HeaderRuleRuntimeError> {
167        if is_denied_header(&self.name) {
168            return Ok(());
169        }
170        let value = self.expression.execute(ctx.into()).map_err(|err| {
171            HeaderRuleRuntimeError::ExpressionEvaluation(self.name.to_string(), Box::new(err.0))
172        })?;
173
174        if let Some(header_value) = vrl_value_to_header_value(value) {
175            if is_never_join_header(&self.name) {
176                output_headers.append(self.name.clone(), header_value);
177            } else {
178                output_headers.insert(self.name.clone(), header_value);
179            }
180        }
181
182        Ok(())
183    }
184}
185
186impl ApplyRequestHeader for RequestRemoveNamed {
187    fn apply_request_headers(
188        &self,
189        _ctx: &RequestExpressionContext,
190        output_headers: &mut HeaderMap,
191    ) -> Result<(), HeaderRuleRuntimeError> {
192        for header_name in &self.names {
193            if is_denied_header(header_name) {
194                continue;
195            }
196            output_headers.remove(header_name);
197        }
198
199        Ok(())
200    }
201}
202
203impl ApplyRequestHeader for RequestRemoveRegex {
204    fn apply_request_headers(
205        &self,
206        _ctx: &RequestExpressionContext,
207        output_headers: &mut HeaderMap,
208    ) -> Result<(), HeaderRuleRuntimeError> {
209        let mut headers_to_remove = Vec::new();
210        for header_name in output_headers.keys() {
211            if is_denied_header(header_name) {
212                continue;
213            }
214
215            if self.regex.is_match(header_name.as_str().as_bytes()) {
216                headers_to_remove.push(header_name.clone());
217            }
218        }
219
220        for header_name in headers_to_remove.iter() {
221            output_headers.remove(header_name);
222        }
223
224        Ok(())
225    }
226}