vrl/stdlib/
parse_aws_alb_log.rs

1use crate::compiler::prelude::*;
2use nom::{
3    IResult, Parser,
4    branch::alt,
5    bytes::complete::{tag, take_while1},
6    character::complete::char,
7    combinator::map_res,
8    sequence::{delimited, preceded},
9};
10use std::collections::BTreeMap;
11
12fn parse_aws_alb_log(bytes: Value, strict_mode: Option<Value>) -> Resolved {
13    let bytes = bytes.try_bytes()?;
14    let strict_mode = strict_mode
15        .map(Value::try_boolean)
16        .transpose()?
17        .unwrap_or(true);
18    parse_log(&String::from_utf8_lossy(&bytes), strict_mode)
19}
20
21#[derive(Clone, Copy, Debug)]
22pub struct ParseAwsAlbLog;
23
24impl Function for ParseAwsAlbLog {
25    fn identifier(&self) -> &'static str {
26        "parse_aws_alb_log"
27    }
28
29    fn examples(&self) -> &'static [Example] {
30        &[
31            Example {
32                title: "valid",
33                source: r#"parse_aws_alb_log!(s'http 2025-01-01T00:00:00.000000Z a/b 1.1.1.1:1 - 0.000 0.000 0.000 200 200 1 2 "GET http://a/ HTTP/1.1" "u" - - arn:a "Root=1-1-1" "-" "-" 0 2025-01-01T00:00:00.000000Z "f" "-" "-" "-" "-" "-" "-" TID_x')"#,
34                result: Ok(
35                    r#"{ "actions_executed": "f", "chosen_cert_arn": null, "classification": null, "classification_reason": null, "client_host": "1.1.1.1:1", "domain_name": null, "elb": "a/b", "elb_status_code": "200", "error_reason": null, "matched_rule_priority": "0", "received_bytes": 1, "redirect_url": null, "request_creation_time": "2025-01-01T00:00:00.000000Z", "request_method": "GET", "request_processing_time": 0.0, "request_protocol": "HTTP/1.1", "request_url": "http://a/", "response_processing_time": 0.0, "sent_bytes": 2, "ssl_cipher": null, "ssl_protocol": null, "target_group_arn": "arn:a", "target_host": null, "target_port_list": [], "target_processing_time": 0.0, "target_status_code": "200", "target_status_code_list": [], "timestamp": "2025-01-01T00:00:00.000000Z", "trace_id": "Root=1-1-1", "type": "http", "user_agent": "u", "traceability_id": "TID_x" }"#,
36                ),
37            },
38            Example {
39                title: "ignores trailing fields when strict_mode is false",
40                source: r#"parse_aws_alb_log!(s'http 2025-01-01T00:00:00.000000Z a/b 1.1.1.1:1 - 0.000 0.000 0.000 200 200 1 2 "GET http://a/ HTTP/1.1" "u" - - arn:a "Root=1-1-1" "-" "-" 0 2025-01-01T00:00:00.000000Z "f" "-" "-" "-" "-" "-" "-" TID_x "-" "-" "-"', strict_mode: false)"#,
41                result: Ok(
42                    r#"{ "actions_executed": "f", "chosen_cert_arn": null, "classification": null, "classification_reason": null, "client_host": "1.1.1.1:1", "domain_name": null, "elb": "a/b", "elb_status_code": "200", "error_reason": null, "matched_rule_priority": "0", "received_bytes": 1, "redirect_url": null, "request_creation_time": "2025-01-01T00:00:00.000000Z", "request_method": "GET", "request_processing_time": 0.0, "request_protocol": "HTTP/1.1", "request_url": "http://a/", "response_processing_time": 0.0, "sent_bytes": 2, "ssl_cipher": null, "ssl_protocol": null, "target_group_arn": "arn:a", "target_host": null, "target_port_list": [], "target_processing_time": 0.0, "target_status_code": "200", "target_status_code_list": [], "timestamp": "2025-01-01T00:00:00.000000Z", "trace_id": "Root=1-1-1", "type": "http", "user_agent": "u", "traceability_id": "TID_x" }"#,
43                ),
44            },
45        ]
46    }
47
48    fn compile(
49        &self,
50        _state: &state::TypeState,
51        _ctx: &mut FunctionCompileContext,
52        arguments: ArgumentList,
53    ) -> Compiled {
54        let value = arguments.required("value");
55        let strict_mode = arguments.optional("strict_mode");
56
57        Ok(ParseAwsAlbLogFn::new(value, strict_mode).as_expr())
58    }
59
60    fn parameters(&self) -> &'static [Parameter] {
61        &[
62            Parameter {
63                keyword: "value",
64                kind: kind::BYTES,
65                required: true,
66            },
67            Parameter {
68                keyword: "strict_mode",
69                kind: kind::BOOLEAN,
70                required: false,
71            },
72        ]
73    }
74}
75
76#[derive(Debug, Clone)]
77struct ParseAwsAlbLogFn {
78    value: Box<dyn Expression>,
79    strict_mode: Option<Box<dyn Expression>>,
80}
81
82impl ParseAwsAlbLogFn {
83    fn new(value: Box<dyn Expression>, strict_mode: Option<Box<dyn Expression>>) -> Self {
84        Self { value, strict_mode }
85    }
86}
87
88impl FunctionExpression for ParseAwsAlbLogFn {
89    fn resolve(&self, ctx: &mut Context) -> Resolved {
90        let bytes = self.value.resolve(ctx)?;
91        let strict_mode = self
92            .strict_mode
93            .as_ref()
94            .map(|expr| expr.resolve(ctx))
95            .transpose()?;
96        parse_aws_alb_log(bytes, strict_mode)
97    }
98
99    fn type_def(&self, _: &state::TypeState) -> TypeDef {
100        TypeDef::object(inner_kind()).fallible(/* log parsing error */)
101    }
102}
103
104fn inner_kind() -> BTreeMap<Field, Kind> {
105    BTreeMap::from([
106        (
107            Field::from("actions_executed"),
108            Kind::bytes() | Kind::null(),
109        ),
110        (Field::from("chosen_cert_arn"), Kind::bytes() | Kind::null()),
111        (
112            Field::from("classification_reason"),
113            Kind::bytes() | Kind::null(),
114        ),
115        (Field::from("classification"), Kind::bytes() | Kind::null()),
116        (Field::from("client_host"), Kind::bytes()),
117        (Field::from("domain_name"), Kind::bytes() | Kind::null()),
118        (Field::from("elb_status_code"), Kind::bytes()),
119        (Field::from("elb"), Kind::bytes()),
120        (Field::from("error_reason"), Kind::bytes() | Kind::null()),
121        (
122            Field::from("matched_rule_priority"),
123            Kind::bytes() | Kind::null(),
124        ),
125        (Field::from("received_bytes"), Kind::integer()),
126        (Field::from("redirect_url"), Kind::bytes() | Kind::null()),
127        (Field::from("request_creation_time"), Kind::bytes()),
128        (Field::from("request_method"), Kind::bytes()),
129        (Field::from("request_processing_time"), Kind::float()),
130        (Field::from("request_protocol"), Kind::bytes()),
131        (Field::from("request_url"), Kind::bytes()),
132        (Field::from("response_processing_time"), Kind::float()),
133        (Field::from("sent_bytes"), Kind::integer()),
134        (Field::from("ssl_cipher"), Kind::bytes() | Kind::null()),
135        (Field::from("ssl_protocol"), Kind::bytes() | Kind::null()),
136        (Field::from("target_group_arn"), Kind::bytes()),
137        (Field::from("target_host"), Kind::bytes() | Kind::null()),
138        (
139            Field::from("target_port_list"),
140            Kind::bytes() | Kind::null(),
141        ),
142        (Field::from("target_processing_time"), Kind::float()),
143        (
144            Field::from("target_status_code_list"),
145            Kind::bytes() | Kind::null(),
146        ),
147        (
148            Field::from("target_status_code"),
149            Kind::bytes() | Kind::null(),
150        ),
151        (Field::from("timestamp"), Kind::bytes()),
152        (Field::from("trace_id"), Kind::bytes()),
153        (Field::from("type"), Kind::bytes()),
154        (Field::from("user_agent"), Kind::bytes()),
155        (Field::from("traceability_id"), Kind::bytes() | Kind::null()),
156    ])
157}
158
159#[allow(clippy::too_many_lines)]
160fn parse_log(mut input: &str, strict_mode: bool) -> ExpressionResult<Value> {
161    let mut log = BTreeMap::<KeyString, Value>::new();
162
163    macro_rules! get_value {
164        ($name:expr_2021, $parser:expr_2021, $err:ty) => {{
165            let result: IResult<_, _, $err> = $parser.parse(input);
166            match result {
167                Ok((rest, value)) => {
168                    input = rest;
169                    value
170                }
171                Err(error) => {
172                    return Err(format!("failed to get field `{}`: {}", $name, error).into());
173                }
174            }
175        }};
176        ($name:expr_2021, $parser:expr_2021) => {
177            get_value!($name, $parser, (&str, nom::error::ErrorKind))
178        };
179    }
180    macro_rules! field_raw {
181        ($name:expr_2021, $parser:expr_2021, $err:ty) => {
182            log.insert(
183                $name.into(),
184                match get_value!($name, $parser, $err).into() {
185                    Value::Bytes(bytes) if bytes == "-" => Value::Null,
186                    value => value,
187                },
188            )
189        };
190        ($name:expr_2021, $parser:expr_2021) => {
191            field_raw!($name, $parser, (&str, nom::error::ErrorKind))
192        };
193    }
194    macro_rules! field {
195        ($name:expr_2021, $($pattern:pat_param)|+) => {
196            field_raw!($name, preceded(char(' '), take_while1(|c| matches!(c, $($pattern)|+))))
197        };
198    }
199    macro_rules! field_parse {
200        ($name:expr_2021, $($pattern:pat_param)|+, $type:ty) => {
201            field_raw!($name, map_res(preceded(char(' '), take_while1(|c| matches!(c, $($pattern)|+))), |s: &str| s.parse::<$type>()))
202        };
203    }
204    macro_rules! field_tid {
205        ($name:expr_2021) => {
206            log.insert(
207                $name.into(),
208                match get_value!($name, take_tid_or_nothing) {
209                    Some(value) => Value::Bytes(value.to_owned().into()),
210                    None => Value::Null,
211                },
212            )
213        };
214    }
215
216    field_raw!("type", take_while1(|c| matches!(c, 'a'..='z' | '0'..='9')));
217    field!("timestamp", '0'..='9' | '.' | '-' | ':' | 'T' | 'Z');
218    field_raw!("elb", take_anything);
219    field!("client_host", '0'..='9' | 'a'..='f' | '.' | ':' | '-');
220    field!("target_host", '0'..='9' | 'a'..='f' | '.' | ':' | '-');
221    field_parse!(
222        "request_processing_time",
223        '0'..='9' | '.' | '-',
224        NotNan<f64>
225    );
226    field_parse!("target_processing_time", '0'..='9' | '.' | '-', NotNan<f64>);
227    field_parse!(
228        "response_processing_time",
229        '0'..='9' | '.' | '-',
230        NotNan<f64>
231    );
232    field!("elb_status_code", '0'..='9' | '-');
233    field!("target_status_code", '0'..='9' | '-');
234    field_parse!("received_bytes", '0'..='9' | '-', i64);
235    field_parse!("sent_bytes", '0'..='9' | '-', i64);
236    let request = get_value!("request", take_quoted1);
237    let mut iter = request.splitn(2, ' ');
238    log.insert(
239        "request_method".to_owned().into(),
240        match iter.next().unwrap().into() {
241            Value::Bytes(bytes) if bytes == "-" => Value::Null,
242            value => value,
243        },
244    ); // split always have at least 1 item
245    match iter.next() {
246        Some(value) => {
247            let mut iter = value.rsplitn(2, ' ');
248            log.insert(
249                "request_protocol".to_owned().into(),
250                match iter.next().unwrap().into() {
251                    Value::Bytes(bytes) if bytes == "-" => Value::Null,
252                    value => value,
253                },
254            ); // same as previous one
255            match iter.next() {
256                Some(value) => log.insert("request_url".into(), value.into()),
257                None => return Err("failed to get field `request_url`".into()),
258            }
259        }
260        None => return Err("failed to get field `request_url`".into()),
261    };
262
263    field_raw!("user_agent", take_quoted1);
264    field_raw!("ssl_cipher", take_anything);
265    field_raw!("ssl_protocol", take_anything);
266    field_raw!("target_group_arn", take_anything);
267    field_raw!("trace_id", take_quoted1);
268    field_raw!("domain_name", take_quoted1);
269    field_raw!("chosen_cert_arn", take_quoted1);
270    field!("matched_rule_priority", '0'..='9' | '-');
271    field!(
272        "request_creation_time",
273        '0'..='9' | '.' | '-' | ':' | 'T' | 'Z'
274    );
275    field_raw!("actions_executed", take_quoted1);
276    field_raw!("redirect_url", take_quoted1);
277    field_raw!("error_reason", take_quoted1);
278    field_raw!(
279        "target_port_list",
280        take_maybe_quoted_list(|c| matches!(c, '0'..='9' | 'a'..='f' | '.' | ':' | '-')),
281        ()
282    );
283    field_raw!(
284        "target_status_code_list",
285        take_maybe_quoted_list(|c| c.is_ascii_digit()),
286        ()
287    );
288    field_raw!("classification", take_quoted1);
289    field_raw!("classification_reason", take_quoted1);
290    field_tid!("traceability_id");
291
292    if input.is_empty() {
293        Ok(log.into())
294    } else if strict_mode {
295        Err(format!(r#"Log should be fully consumed: "{input}""#).into())
296    } else {
297        // Ignore any trailing, unknown fields when not in strict mode
298        Ok(log.into())
299    }
300}
301
302type SResult<'a, O> = IResult<&'a str, O, (&'a str, nom::error::ErrorKind)>;
303
304fn take_anything(input: &str) -> SResult<'_, &str> {
305    preceded(char(' '), take_while1(|c| c != ' ')).parse(input)
306}
307
308fn take_quoted1(input: &str) -> SResult<'_, String> {
309    delimited(tag(" \""), until_quote, char('"')).parse(input)
310}
311
312fn take_tid_or_nothing(input: &str) -> SResult<'_, Option<&str>> {
313    if input.starts_with(" TID_") {
314        let (rest, value) = take_anything(input)?;
315        Ok((rest, Some(value)))
316    } else {
317        Ok((input, None))
318    }
319}
320
321fn until_quote(input: &str) -> SResult<'_, String> {
322    let mut ret = String::new();
323    let mut skip_delimiter = false;
324    for (i, ch) in input.char_indices() {
325        if ch == '\\' && !skip_delimiter {
326            skip_delimiter = true;
327        } else if ch == '"' && !skip_delimiter {
328            return Ok((&input[i..], ret));
329        } else {
330            ret.push(ch);
331            skip_delimiter = false;
332        }
333    }
334    Err(nom::Err::Incomplete(nom::Needed::Unknown))
335}
336
337fn take_maybe_quoted_list<'a>(
338    cond: impl Fn(char) -> bool + Clone,
339) -> impl Parser<&'a str, Error = (), Output = Vec<&'a str>> {
340    alt((
341        map_res(tag(r#" "-""#), |_| {
342            Ok::<_, std::convert::Infallible>(vec![])
343        }),
344        map_res(
345            delimited(tag(" \""), take_while1(cond.clone()), char('"')),
346            |v: &str| Ok::<_, std::convert::Infallible>(vec![v]),
347        ),
348        map_res(preceded(char(' '), take_while1(cond)), |v: &str| {
349            Ok::<_, std::convert::Infallible>(vec![v])
350        }),
351    ))
352}
353
354#[cfg(test)]
355mod tests {
356    use super::*;
357    use crate::value;
358
359    test_function![
360        parse_aws_alb_log => ParseAwsAlbLog;
361
362        one {
363            args: func_args![value: r#"http 2018-07-02T22:23:00.186641Z app/my-loadbalancer/50dc6c495c0c9188 192.168.131.39:2817 10.0.0.1:80 0.000 0.001 0.000 200 200 34 366 "GET http://www.example.com:80/ HTTP/1.1" "curl/7.46.0" - - arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067 "Root=1-58337262-36d228ad5d99923122bbe354" "-" "-" 0 2018-07-02T22:22:48.364000Z "forward" "-" "-" 10.0.0.1:80 200 "-" "-" TID_dc57cebed65b444ebc8177bb698fe166"#],
364            want: Ok(value!({actions_executed: "forward",
365                             chosen_cert_arn: null,
366                             classification: null,
367                             classification_reason: null,
368                             client_host: "192.168.131.39:2817",
369                             domain_name: null,
370                             elb: "app/my-loadbalancer/50dc6c495c0c9188",
371                             elb_status_code: "200",
372                             error_reason: null,
373                             matched_rule_priority: "0",
374                             received_bytes: 34,
375                             redirect_url: null,
376                             request_creation_time: "2018-07-02T22:22:48.364000Z",
377                             request_method: "GET",
378                             request_processing_time: 0.0,
379                             request_protocol: "HTTP/1.1",
380                             request_url: "http://www.example.com:80/",
381                             response_processing_time: 0.0,
382                             sent_bytes: 366,
383                             ssl_cipher: null,
384                             ssl_protocol: null,
385                             target_group_arn: "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067",
386                             target_host: "10.0.0.1:80",
387                             target_port_list: ["10.0.0.1:80"],
388                             target_processing_time: 0.001,
389                             target_status_code: "200",
390                             target_status_code_list: ["200"],
391                             timestamp: "2018-07-02T22:23:00.186641Z",
392                             trace_id: "Root=1-58337262-36d228ad5d99923122bbe354",
393                             type: "http",
394                             user_agent: "curl/7.46.0",
395                             traceability_id: "TID_dc57cebed65b444ebc8177bb698fe166"
396
397
398            })),
399            tdef: TypeDef::object(inner_kind()).fallible(),
400        }
401
402        two {
403            args: func_args![value: r#"https 2018-07-02T22:23:00.186641Z app/my-loadbalancer/50dc6c495c0c9188 192.168.131.39:2817 10.0.0.1:80 0.086 0.048 0.037 200 200 0 57 "GET https://www.example.com:443/ HTTP/1.1" "curl/7.46.0" ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067 "Root=1-58337281-1d84f3d73c47ec4e58577259" "www.example.com" "arn:aws:acm:us-east-2:123456789012:certificate/12345678-1234-1234-1234-123456789012" 1 2018-07-02T22:22:48.364000Z "authenticate,forward" "-" "-" 10.0.0.1:80 200 "-" "-" TID_dc57cebed65b444ebc8177bb698fe166"#],
404            want: Ok(value! ({actions_executed: "authenticate,forward",
405                              chosen_cert_arn: "arn:aws:acm:us-east-2:123456789012:certificate/12345678-1234-1234-1234-123456789012",
406                              classification: null,
407                              classification_reason: null,
408                              client_host: "192.168.131.39:2817",
409                              domain_name: "www.example.com",
410                              elb: "app/my-loadbalancer/50dc6c495c0c9188",
411                              elb_status_code: "200",
412                              error_reason: null,
413                              matched_rule_priority: "1",
414                              received_bytes: 0,
415                              redirect_url: null,
416                              request_creation_time: "2018-07-02T22:22:48.364000Z",
417                              request_method: "GET",
418                              request_processing_time: 0.086,
419                              request_protocol: "HTTP/1.1",
420                              request_url: "https://www.example.com:443/",
421                              response_processing_time: 0.037,
422                              sent_bytes: 57,
423                              ssl_cipher: "ECDHE-RSA-AES128-GCM-SHA256",
424                              ssl_protocol: "TLSv1.2",
425                              target_group_arn: "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067",
426                              target_host: "10.0.0.1:80",
427                              target_port_list: ["10.0.0.1:80"],
428                              target_processing_time: 0.048,
429                              target_status_code: "200",
430                              target_status_code_list: ["200"],
431                              timestamp: "2018-07-02T22:23:00.186641Z",
432                              trace_id: "Root=1-58337281-1d84f3d73c47ec4e58577259",
433                              type: "https",
434                              user_agent: "curl/7.46.0",
435                              traceability_id: "TID_dc57cebed65b444ebc8177bb698fe166"})),
436            tdef: TypeDef::object(inner_kind()).fallible(),
437        }
438
439        three {
440            args: func_args![value: r#"h2 2018-07-02T22:23:00.186641Z app/my-loadbalancer/50dc6c495c0c9188 10.0.1.252:48160 10.0.0.66:9000 0.000 0.002 0.000 200 200 5 257 "GET https://10.0.2.105:773/ HTTP/2.0" "curl/7.46.0" ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067 "Root=1-58337327-72bd00b0343d75b906739c42" "-" "-" 1 2018-07-02T22:22:48.364000Z "redirect" "https://example.com:80/" "-" 10.0.0.66:9000 200 "-" "-" TID_dc57cebed65b444ebc8177bb698fe166"#],
441            want: Ok(value! ({actions_executed: "redirect",
442                              chosen_cert_arn: null,
443                              classification: null,
444                              classification_reason: null,
445                              client_host: "10.0.1.252:48160",
446                              domain_name: null,
447                              elb: "app/my-loadbalancer/50dc6c495c0c9188",
448                              elb_status_code: "200",
449                              error_reason: null,
450                              matched_rule_priority: "1",
451                              received_bytes: 5,
452                              redirect_url: "https://example.com:80/",
453                              request_creation_time: "2018-07-02T22:22:48.364000Z",
454                              request_method: "GET",
455                              request_processing_time: 0.0,
456                              request_protocol: "HTTP/2.0",
457                              request_url: "https://10.0.2.105:773/",
458                              response_processing_time: 0.0,
459                              sent_bytes: 257,
460                              ssl_cipher: "ECDHE-RSA-AES128-GCM-SHA256",
461                              ssl_protocol: "TLSv1.2",
462                              target_group_arn: "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067",
463                              target_host: "10.0.0.66:9000",
464                              target_port_list: ["10.0.0.66:9000"],
465                              target_processing_time: 0.002,
466                              target_status_code: "200",
467                              target_status_code_list: ["200"],
468                              timestamp: "2018-07-02T22:23:00.186641Z",
469                              trace_id: "Root=1-58337327-72bd00b0343d75b906739c42",
470                              type: "h2",
471                              user_agent: "curl/7.46.0",
472                              traceability_id: "TID_dc57cebed65b444ebc8177bb698fe166"})),
473            tdef: TypeDef::object(inner_kind()).fallible(),
474        }
475
476        four {
477            args: func_args![value: r#"ws 2018-07-02T22:23:00.186641Z app/my-loadbalancer/50dc6c495c0c9188 10.0.0.140:40914 10.0.1.192:8010 0.001 0.003 0.000 101 101 218 587 "GET http://10.0.0.30:80/ HTTP/1.1" "-" - - arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067 "Root=1-58337364-23a8c76965a2ef7629b185e3" "-" "-" 1 2018-07-02T22:22:48.364000Z "forward" "-" "-" 10.0.1.192:8010 101 "-" "-" TID_dc57cebed65b444ebc8177bb698fe166"#],
478            want: Ok(value!({actions_executed: "forward",
479                             chosen_cert_arn: null,
480                             classification: null,
481                             classification_reason: null,
482                             client_host: "10.0.0.140:40914",
483                             domain_name: null,
484                             elb: "app/my-loadbalancer/50dc6c495c0c9188",
485                             elb_status_code: "101",
486                             error_reason: null,
487                             matched_rule_priority: "1",
488                             received_bytes: 218,
489                             redirect_url: null,
490                             request_creation_time: "2018-07-02T22:22:48.364000Z",
491                             request_method: "GET",
492                             request_processing_time: 0.001,
493                             request_protocol: "HTTP/1.1",
494                             request_url: "http://10.0.0.30:80/",
495                             response_processing_time: 0.0,
496                             sent_bytes: 587,
497                             ssl_cipher: null,
498                             ssl_protocol: null,
499                             target_group_arn: "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067",
500                             target_host: "10.0.1.192:8010",
501                             target_port_list: ["10.0.1.192:8010"],
502                             target_processing_time: 0.003,
503                             target_status_code: "101",
504                             target_status_code_list: ["101"],
505                             timestamp: "2018-07-02T22:23:00.186641Z",
506                             trace_id: "Root=1-58337364-23a8c76965a2ef7629b185e3",
507                             type: "ws",
508                             user_agent: null,
509                             traceability_id: "TID_dc57cebed65b444ebc8177bb698fe166"})),
510            tdef: TypeDef::object(inner_kind()).fallible(),
511        }
512
513        five {
514            args: func_args![value: r#"wss 2018-07-02T22:23:00.186641Z app/my-loadbalancer/50dc6c495c0c9188 10.0.0.140:44244 10.0.0.171:8010 0.000 0.001 0.000 101 101 218 786 "GET https://10.0.0.30:443/ HTTP/1.1" "-" ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067 "Root=1-58337364-23a8c76965a2ef7629b185e3" "-" "-" 1 2018-07-02T22:22:48.364000Z "forward" "-" "-" 10.0.0.171:8010 101 "-" "-" TID_dc57cebed65b444ebc8177bb698fe166"#],
515            want: Ok(value! ({actions_executed: "forward",
516                              chosen_cert_arn: null,
517                              classification: null,
518                              classification_reason: null,
519                              client_host: "10.0.0.140:44244",
520                              domain_name: null,
521                              elb: "app/my-loadbalancer/50dc6c495c0c9188",
522                              elb_status_code: "101",
523                              error_reason: null,
524                              matched_rule_priority: "1",
525                              received_bytes: 218,
526                              redirect_url: null,
527                              request_creation_time: "2018-07-02T22:22:48.364000Z",
528                              request_method: "GET",
529                              request_processing_time: 0.0,
530                              request_protocol: "HTTP/1.1",
531                              request_url: "https://10.0.0.30:443/",
532                              response_processing_time: 0.0,
533                              sent_bytes: 786,
534                              ssl_cipher: "ECDHE-RSA-AES128-GCM-SHA256",
535                              ssl_protocol: "TLSv1.2",
536                              target_group_arn: "arn:aws:elasticloadbalancing:us-west-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067",
537                              target_host: "10.0.0.171:8010",
538                              target_port_list: ["10.0.0.171:8010"],
539                              target_processing_time: 0.001,
540                              target_status_code: "101",
541                              target_status_code_list: ["101"],
542                              timestamp: "2018-07-02T22:23:00.186641Z",
543                              trace_id: "Root=1-58337364-23a8c76965a2ef7629b185e3",
544                              type: "wss",
545                              user_agent: null,
546                              traceability_id: "TID_dc57cebed65b444ebc8177bb698fe166"})),
547            tdef: TypeDef::object(inner_kind()).fallible(),
548        }
549
550        six {
551            args: func_args![value: r#"http 2018-11-30T22:23:00.186641Z app/my-loadbalancer/50dc6c495c0c9188 192.168.131.39:2817 - 0.000 0.001 0.000 200 200 34 366 "GET http://www.example.com:80/ HTTP/1.1" "curl/7.46.0" - - arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067 "Root=1-58337364-23a8c76965a2ef7629b185e3" "-" "-" 0 2018-11-30T22:22:48.364000Z "forward" "-" "-" "-" "-" "-" "-" TID_dc57cebed65b444ebc8177bb698fe166"#],
552            want: Ok(value! ({actions_executed: "forward",
553                              chosen_cert_arn: null,
554                              classification: null,
555                              classification_reason: null,
556                              client_host: "192.168.131.39:2817",
557                              domain_name: null,
558                              elb: "app/my-loadbalancer/50dc6c495c0c9188",
559                              elb_status_code: "200",
560                              error_reason: null,
561                              matched_rule_priority: "0",
562                              received_bytes: 34,
563                              redirect_url: null,
564                              request_creation_time: "2018-11-30T22:22:48.364000Z",
565                              request_method: "GET",
566                              request_processing_time: 0.0,
567                              request_protocol: "HTTP/1.1",
568                              request_url: "http://www.example.com:80/",
569                              response_processing_time: 0.0,
570                              sent_bytes: 366,
571                              ssl_cipher: null,
572                              ssl_protocol: null,
573                              target_group_arn: "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067",
574                              target_host: null,
575                              target_port_list: [],
576                              target_processing_time: 0.001,
577                              target_status_code: "200",
578                              target_status_code_list: [],
579                              timestamp: "2018-11-30T22:23:00.186641Z",
580                              trace_id: "Root=1-58337364-23a8c76965a2ef7629b185e3",
581                              type: "http",
582                              user_agent: "curl/7.46.0",
583                              traceability_id: "TID_dc57cebed65b444ebc8177bb698fe166"})),
584            tdef: TypeDef::object(inner_kind()).fallible(),
585        }
586
587        seven {
588            args: func_args![value: r#"http 2018-11-30T22:23:00.186641Z app/my-loadbalancer/50dc6c495c0c9188 192.168.131.39:2817 - 0.000 0.001 0.000 502 - 34 366 "GET http://www.example.com:80/ HTTP/1.1" "curl/7.46.0" - - arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067 "Root=1-58337364-23a8c76965a2ef7629b185e3" "-" "-" 0 2018-11-30T22:22:48.364000Z "forward" "-" "LambdaInvalidResponse" "-" "-" "-" "-" TID_dc57cebed65b444ebc8177bb698fe166"#],
589            want: Ok(value!({actions_executed: "forward",
590                             chosen_cert_arn: null,
591                             classification: null,
592                             classification_reason: null,
593                             client_host: "192.168.131.39:2817",
594                             domain_name: null,
595                             elb: "app/my-loadbalancer/50dc6c495c0c9188",
596                             elb_status_code: "502",
597                             error_reason: "LambdaInvalidResponse",
598                             matched_rule_priority: "0",
599                             received_bytes: 34,
600                             redirect_url: null,
601                             request_creation_time: "2018-11-30T22:22:48.364000Z",
602                             request_method: "GET",
603                             request_processing_time: 0.0,
604                             request_protocol: "HTTP/1.1",
605                             request_url: "http://www.example.com:80/",
606                             response_processing_time: 0.0,
607                             sent_bytes: 366,
608                             ssl_cipher: null,
609                             ssl_protocol: null,
610                             target_group_arn: "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067",
611                             target_host: null,
612                             target_port_list: [],
613                             target_processing_time: 0.001,
614                             target_status_code: null,
615                             target_status_code_list: [],
616                             timestamp: "2018-11-30T22:23:00.186641Z",
617                             trace_id: "Root=1-58337364-23a8c76965a2ef7629b185e3",
618                             type: "http",
619                             user_agent: "curl/7.46.0",
620                             traceability_id: "TID_dc57cebed65b444ebc8177bb698fe166"})),
621            tdef: TypeDef::object(inner_kind()).fallible(),
622        }
623
624        eight {
625            args: func_args![value: r#"https 2021-03-16T20:20:00.135052Z app/awseb-AWSEB-1MVD8OW91UMOH/a32a5528b8fdaa6b 10.209.14.140:50599 10.119.5.47:80 0.001 0.052 0.000 200 200 589 2084 "POST https://test.domain.com:443/api/deposits/transactions:search?detailsLevel=FULL&offset=0&limit=50 HTTP/1.1" "User 1.0" ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 arn:aws:elasticloadbalancing:us-east-1:755269215481:targetgroup/awseb-AWSEB-91MZX0WA1A0F/5a03cc723870f039 "Root=1-605112f0-31f367be4fd3da651daa4157" "some.domain.com" "arn:aws:acm:us-east-1:765229915481:certificate/d8450a8a-b4f6-4714-8535-17c625c36899" 0 2021-03-16T20:20:00.081000Z "waf,forward" "-" "-" "10.229.5.47:80" "200" "-" "-" TID_dc57cebed65b444ebc8177bb698fe166"#],
626            want: Ok(value!({type: "https",
627                             timestamp: "2021-03-16T20:20:00.135052Z",
628                             elb: "app/awseb-AWSEB-1MVD8OW91UMOH/a32a5528b8fdaa6b",
629                             client_host: "10.209.14.140:50599",
630                             target_host: "10.119.5.47:80",
631                             request_processing_time: 0.001,
632                             target_processing_time: 0.052,
633                             response_processing_time: 0.0,
634                             elb_status_code: "200",
635                             target_status_code: "200",
636                             received_bytes: 589,
637                             sent_bytes: 2084,
638                             request_method: "POST",
639                             request_url: "https://test.domain.com:443/api/deposits/transactions:search?detailsLevel=FULL&offset=0&limit=50",
640                             request_protocol: "HTTP/1.1",
641                             user_agent: "User 1.0",
642                             ssl_cipher: "ECDHE-RSA-AES128-GCM-SHA256",
643                             ssl_protocol: "TLSv1.2",
644                             target_group_arn: "arn:aws:elasticloadbalancing:us-east-1:755269215481:targetgroup/awseb-AWSEB-91MZX0WA1A0F/5a03cc723870f039",
645                             trace_id: "Root=1-605112f0-31f367be4fd3da651daa4157",
646                             domain_name: "some.domain.com",
647                             chosen_cert_arn: "arn:aws:acm:us-east-1:765229915481:certificate/d8450a8a-b4f6-4714-8535-17c625c36899",
648                             matched_rule_priority: "0",
649                             request_creation_time: "2021-03-16T20:20:00.081000Z",
650                             actions_executed: "waf,forward",
651                             redirect_url: null,
652                             error_reason: null,
653                             target_port_list: ["10.229.5.47:80"],
654                             target_status_code_list: ["200"],
655                             classification: null,
656                             classification_reason: null,
657                             traceability_id: "TID_dc57cebed65b444ebc8177bb698fe166"})),
658            tdef: TypeDef::object(inner_kind()).fallible(),
659        }
660
661        nine {
662            args: func_args![value: r#"http 2021-03-17T18:54:14.216357Z app/awseb-AWSEB-1KGU6EBAG3FAD/7f0dc2b05788640f 113.241.19.90:15070 - -1 -1 -1 400 - 0 272 "- http://awseb-awseb-1kgu6fbag3fad-640112591.us-east-1.elb.amazonaws.com:80- -" "-" - - - "-" "-" "-" - 2021-03-17T18:54:13.967000Z "-" "-" "-" "-" "-" "-" "-" TID_dc57cebed65b444ebc8177bb698fe166"#],
663            want: Ok(value!({type: "http",
664                             timestamp: "2021-03-17T18:54:14.216357Z",
665                             elb: "app/awseb-AWSEB-1KGU6EBAG3FAD/7f0dc2b05788640f",
666                             client_host: "113.241.19.90:15070",
667                             target_host: null,
668                             request_processing_time: (-1.0),
669                             target_processing_time: (-1.0),
670                             response_processing_time: (-1.0),
671                             elb_status_code: "400",
672                             target_status_code: null,
673                             received_bytes: 0,
674                             sent_bytes: 272,
675                             request_method: null,
676                             request_url: "http://awseb-awseb-1kgu6fbag3fad-640112591.us-east-1.elb.amazonaws.com:80-",
677                             request_protocol: null,
678                             user_agent: null,
679                             ssl_cipher: null,
680                             ssl_protocol: null,
681                             target_group_arn: null,
682                             trace_id: null,
683                             domain_name: null,
684                             chosen_cert_arn: null,
685                             matched_rule_priority: null,
686                             request_creation_time: "2021-03-17T18:54:13.967000Z",
687                             actions_executed: null,
688                             redirect_url: null,
689                             error_reason: null,
690                             target_port_list: [],
691                             target_status_code_list: [],
692                             classification: null,
693                             classification_reason: null,
694                             traceability_id: "TID_dc57cebed65b444ebc8177bb698fe166"})),
695            tdef: TypeDef::object(inner_kind()).fallible(),
696        }
697
698        ten {
699            args: func_args![value: r#"http 2021-03-18T04:00:26.920977Z app/awseb-AWSEB-1KGU6EBAG3FAD/7f0dc2b05788640f 31.211.20.175:57720 - -1 -1 -1 400 - 191 272 "POST http://awseb-awseb-1kgu6fbag3fad-640112591.us-east-1.elb.amazonaws.com:80/cgi-bin/login.cgi HTTP/1.1" "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0" - - - "-" "-" "-" - 2021-03-18T04:00:26.599000Z "-" "-" "-" "-" "-" "-" "-" TID_dc57cebed65b444ebc8177bb698fe166"#],
700            want: Ok(value!({type: "http",
701                             timestamp: "2021-03-18T04:00:26.920977Z",
702                             elb: "app/awseb-AWSEB-1KGU6EBAG3FAD/7f0dc2b05788640f",
703                             client_host: "31.211.20.175:57720",
704                             target_host: null,
705                             request_processing_time: (-1.0),
706                             target_processing_time: (-1.0),
707                             response_processing_time: (-1.0),
708                             elb_status_code: "400",
709                             target_status_code: null,
710                             received_bytes: 191,
711                             sent_bytes: 272,
712                             request_method: "POST",
713                             request_url: "http://awseb-awseb-1kgu6fbag3fad-640112591.us-east-1.elb.amazonaws.com:80/cgi-bin/login.cgi",
714                             request_protocol: "HTTP/1.1",
715                             user_agent: "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0",
716                             ssl_cipher: null,
717                             ssl_protocol: null,
718                             target_group_arn: null,
719                             trace_id: null,
720                             domain_name: null,
721                             chosen_cert_arn: null,
722                             matched_rule_priority: null,
723                             request_creation_time: "2021-03-18T04:00:26.599000Z",
724                             actions_executed: null,
725                             redirect_url: null,
726                             error_reason: null,
727                             target_port_list: [],
728                             target_status_code_list: [],
729                             classification: null,
730                             classification_reason: null,
731                             traceability_id: "TID_dc57cebed65b444ebc8177bb698fe166"})),
732            tdef: TypeDef::object(inner_kind()).fallible(),
733        }
734
735        eleven {
736            args: func_args![value: r#"https 2021-03-16T20:20:00.135052Z app/awseb-AWSEB-1MVD8OW91UMOH/a32a5528b8fdaa6b 2601:6bbc:c529:9dad:6bbc:c529:9dad:6bbc:50599 fd6d:6bbc:c529:6::face:ed83:f46:80 0.001 0.052 0.000 200 200 589 2084 "POST https://test.domain.com:443/api/deposits/transactions:search?detailsLevel=FULL&offset=0&limit=50 HTTP/1.1" "User 1.0" ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 arn:aws:elasticloadbalancing:us-east-1:755269215481:targetgroup/awseb-AWSEB-91MZX0WA1A0F/5a03cc723870f039 "Root=1-605112f0-31f367be4fd3da651daa4157" "some.domain.com" "arn:aws:acm:us-east-1:765229915481:certificate/d8450a8a-b4f6-4714-8535-17c625c36899" 0 2021-03-16T20:20:00.081000Z "waf,forward" "-" "-" "fd6d:6bbc:c529:27ff:b::dead:ed84:80" "200" "-" "-" TID_dc57cebed65b444ebc8177bb698fe166"#],
737            want: Ok(value!({type: "https",
738                             timestamp: "2021-03-16T20:20:00.135052Z",
739                             elb: "app/awseb-AWSEB-1MVD8OW91UMOH/a32a5528b8fdaa6b",
740                             client_host: "2601:6bbc:c529:9dad:6bbc:c529:9dad:6bbc:50599",
741                             target_host: "fd6d:6bbc:c529:6::face:ed83:f46:80",
742                             request_processing_time: 0.001,
743                             target_processing_time: 0.052,
744                             response_processing_time: 0.0,
745                             elb_status_code: "200",
746                             target_status_code: "200",
747                             received_bytes: 589,
748                             sent_bytes: 2084,
749                             request_method: "POST",
750                             request_url: "https://test.domain.com:443/api/deposits/transactions:search?detailsLevel=FULL&offset=0&limit=50",
751                             request_protocol: "HTTP/1.1",
752                             user_agent: "User 1.0",
753                             ssl_cipher: "ECDHE-RSA-AES128-GCM-SHA256",
754                             ssl_protocol: "TLSv1.2",
755                             target_group_arn: "arn:aws:elasticloadbalancing:us-east-1:755269215481:targetgroup/awseb-AWSEB-91MZX0WA1A0F/5a03cc723870f039",
756                             trace_id: "Root=1-605112f0-31f367be4fd3da651daa4157",
757                             domain_name: "some.domain.com",
758                             chosen_cert_arn: "arn:aws:acm:us-east-1:765229915481:certificate/d8450a8a-b4f6-4714-8535-17c625c36899",
759                             matched_rule_priority: "0",
760                             request_creation_time: "2021-03-16T20:20:00.081000Z",
761                             actions_executed: "waf,forward",
762                             redirect_url: null,
763                             error_reason: null,
764                             target_port_list: ["fd6d:6bbc:c529:27ff:b::dead:ed84:80"],
765                             target_status_code_list: ["200"],
766                             classification: null,
767                             classification_reason: null,
768                             traceability_id: "TID_dc57cebed65b444ebc8177bb698fe166"})),
769            tdef: TypeDef::object(inner_kind()).fallible(),
770        }
771        twelve {
772            args: func_args![value: r#"https 2021-03-16T20:20:00.135052Z app/awseb-AWSEB-1MVD8OW91UMOH/a32a5528b8fdaa6b 2601:6bbc:c529:9dad:6bbc:c529:9dad:6bbc:50599 fd6d:6bbc:c529:6::face:ed83:f46:80 0.001 0.052 0.000 200 200 589 2084 "POST https://test.domain.com:443/api/deposits/transactions:search?detailsLevel=FULL&offset=0&limit=50 HTTP/1.1" "User 1.0" ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 arn:aws:elasticloadbalancing:us-east-1:755269215481:targetgroup/awseb-AWSEB-91MZX0WA1A0F/5a03cc723870f039 "Root=1-605112f0-31f367be4fd3da651daa4157" "some.domain.com" "arn:aws:acm:us-east-1:765229915481:certificate/d8450a8a-b4f6-4714-8535-17c625c36899" 0 2021-03-16T20:20:00.081000Z "waf,forward" "-" "-" "fd6d:6bbc:c529:27ff:b::dead:ed84:80" "200" "-" "-""#],
773            want: Ok(value!({type: "https",
774                             timestamp: "2021-03-16T20:20:00.135052Z",
775                             elb: "app/awseb-AWSEB-1MVD8OW91UMOH/a32a5528b8fdaa6b",
776                             client_host: "2601:6bbc:c529:9dad:6bbc:c529:9dad:6bbc:50599",
777                             target_host: "fd6d:6bbc:c529:6::face:ed83:f46:80",
778                             request_processing_time: 0.001,
779                             target_processing_time: 0.052,
780                             response_processing_time: 0.0,
781                             elb_status_code: "200",
782                             target_status_code: "200",
783                             received_bytes: 589,
784                             sent_bytes: 2084,
785                             request_method: "POST",
786                             request_url: "https://test.domain.com:443/api/deposits/transactions:search?detailsLevel=FULL&offset=0&limit=50",
787                             request_protocol: "HTTP/1.1",
788                             user_agent: "User 1.0",
789                             ssl_cipher: "ECDHE-RSA-AES128-GCM-SHA256",
790                             ssl_protocol: "TLSv1.2",
791                             target_group_arn: "arn:aws:elasticloadbalancing:us-east-1:755269215481:targetgroup/awseb-AWSEB-91MZX0WA1A0F/5a03cc723870f039",
792                             trace_id: "Root=1-605112f0-31f367be4fd3da651daa4157",
793                             domain_name: "some.domain.com",
794                             chosen_cert_arn: "arn:aws:acm:us-east-1:765229915481:certificate/d8450a8a-b4f6-4714-8535-17c625c36899",
795                             matched_rule_priority: "0",
796                             request_creation_time: "2021-03-16T20:20:00.081000Z",
797                             actions_executed: "waf,forward",
798                             redirect_url: null,
799                             error_reason: null,
800                             target_port_list: ["fd6d:6bbc:c529:27ff:b::dead:ed84:80"],
801                             target_status_code_list: ["200"],
802                             classification: null,
803                             classification_reason: null,
804                             traceability_id: null})),
805                tdef: TypeDef::object(inner_kind()).fallible(),
806            }
807            thirteen {
808                args: func_args![value: r#"http 2018-07-02T22:23:00.186641Z app/my-loadbalancer/50dc6c495c0c9188 192.168.131.39:2817 10.0.0.1:80 0.000 0.001 0.000 200 200 34 366 "GET http://www.example.com:80/ HTTP/1.1" "curl/7.46.0" - - arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067 "Root=1-58337262-36d228ad5d99923122bbe354" "-" "-" 0 2018-07-02T22:22:48.364000Z "forward" "-" "-" 10.0.0.1:80 200 "-" "-" TID_dc57cebed65b444ebc8177bb698fe166 "-" "-" "-""#, strict_mode: false],
809                want: Ok(value!({actions_executed: "forward",
810                                 chosen_cert_arn: null,
811                                 classification: null,
812                                 classification_reason: null,
813                                 client_host: "192.168.131.39:2817",
814                                 domain_name: null,
815                                 elb: "app/my-loadbalancer/50dc6c495c0c9188",
816                                 elb_status_code: "200",
817                                 error_reason: null,
818                                 matched_rule_priority: "0",
819                                 received_bytes: 34,
820                                 redirect_url: null,
821                                 request_creation_time: "2018-07-02T22:22:48.364000Z",
822                                 request_method: "GET",
823                                 request_processing_time: 0.0,
824                                 request_protocol: "HTTP/1.1",
825                                 request_url: "http://www.example.com:80/",
826                                 response_processing_time: 0.0,
827                                 sent_bytes: 366,
828                                 ssl_cipher: null,
829                                 ssl_protocol: null,
830                                 target_group_arn: "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-targets/73e2d6bc24d8a067",
831                                 target_host: "10.0.0.1:80",
832                                 target_port_list: ["10.0.0.1:80"],
833                                 target_processing_time: 0.001,
834                                 target_status_code: "200",
835                                 target_status_code_list: ["200"],
836                                 timestamp: "2018-07-02T22:23:00.186641Z",
837                                 trace_id: "Root=1-58337262-36d228ad5d99923122bbe354",
838                                 type: "http",
839                                 user_agent: "curl/7.46.0",
840                                 traceability_id: "TID_dc57cebed65b444ebc8177bb698fe166"
841                })),
842                tdef: TypeDef::object(inner_kind()).fallible(),
843            }
844    ];
845}