1
#include "source/extensions/formatter/cel/cel.h"
2

            
3
#include "source/common/config/metadata.h"
4
#include "source/common/formatter/substitution_formatter.h"
5
#include "source/common/http/utility.h"
6
#include "source/common/protobuf/utility.h"
7

            
8
#if defined(USE_CEL_PARSER)
9
#include "eval/public/value_export_util.h"
10
#include "parser/parser.h"
11
#endif
12

            
13
namespace Envoy {
14
namespace Extensions {
15
namespace Formatter {
16

            
17
namespace Expr = Filters::Common::Expr;
18

            
19
CELFormatter::CELFormatter(const ::Envoy::LocalInfo::LocalInfo& local_info,
20
                           Expr::BuilderInstanceSharedConstPtr expr_builder,
21
                           const cel::expr::Expr& input_expr, absl::optional<size_t>& max_length,
22
                           bool typed)
23
91
    : local_info_(local_info), max_length_(max_length), compiled_expr_([&]() {
24
91
        auto compiled_expr = Expr::CompiledExpression::Create(expr_builder, input_expr);
25
91
        if (!compiled_expr.ok()) {
26
1
          throw EnvoyException(
27
1
              absl::StrCat("failed to create an expression: ", compiled_expr.status().message()));
28
1
        }
29
90
        return std::move(compiled_expr.value());
30
91
      }()),
31
91
      typed_(typed) {}
32

            
33
absl::optional<std::string> CELFormatter::format(const Envoy::Formatter::Context& context,
34
72
                                                 const StreamInfo::StreamInfo& stream_info) const {
35
72
  Protobuf::Arena arena;
36
72
  auto eval_status =
37
72
      compiled_expr_.evaluate(arena, &local_info_, stream_info, context.requestHeaders().ptr(),
38
72
                              context.responseHeaders().ptr(), context.responseTrailers().ptr());
39
72
  if (!eval_status.has_value() || eval_status.value().IsError()) {
40
18
    return absl::nullopt;
41
18
  }
42
54
  const auto result = Expr::print(eval_status.value());
43
54
  if (max_length_) {
44
4
    return result.substr(0, max_length_.value());
45
4
  }
46

            
47
50
  return result;
48
54
}
49

            
50
Protobuf::Value CELFormatter::formatValue(const Envoy::Formatter::Context& context,
51
40
                                          const StreamInfo::StreamInfo& stream_info) const {
52
40
  if (typed_) {
53
18
    Protobuf::Arena arena;
54
18
    auto eval_status =
55
18
        compiled_expr_.evaluate(arena, &local_info_, stream_info, context.requestHeaders().ptr(),
56
18
                                context.responseHeaders().ptr(), context.responseTrailers().ptr());
57
18
    if (!eval_status.has_value() || eval_status.value().IsError()) {
58
5
      return ValueUtil::nullValue();
59
5
    }
60

            
61
13
    Protobuf::Value proto_value;
62
13
    if (!ExportAsProtoValue(eval_status.value(), &proto_value).ok()) {
63
1
      return ValueUtil::nullValue();
64
1
    }
65

            
66
12
    if (max_length_ && proto_value.kind_case() == Protobuf::Value::kStringValue) {
67
2
      proto_value.set_string_value(proto_value.string_value().substr(0, max_length_.value()));
68
2
    }
69
12
    return proto_value;
70
22
  } else {
71
22
    auto result = format(context, stream_info);
72
22
    if (!result.has_value()) {
73
6
      return ValueUtil::nullValue();
74
6
    }
75
16
    return ValueUtil::stringValue(result.value());
76
22
  }
77
40
}
78

            
79
::Envoy::Formatter::FormatterProviderPtr
80
CELFormatterCommandParser::parse(absl::string_view command, absl::string_view subcommand,
81
483
                                 absl::optional<size_t> max_length) const {
82
483
#if defined(USE_CEL_PARSER)
83
483
  if (command == "CEL" || command == "TYPED_CEL") {
84
93
    auto parse_status = google::api::expr::parser::Parse(subcommand);
85
93
    if (!parse_status.ok()) {
86
2
      throw EnvoyException("Not able to parse expression: " + parse_status.status().ToString());
87
2
    }
88
91
    Server::Configuration::ServerFactoryContext& context =
89
91
        Server::Configuration::ServerFactoryContextInstance::get();
90
91
    return std::make_unique<CELFormatter>(
91
91
        context.localInfo(), Extensions::Filters::Common::Expr::getBuilder(context),
92
91
        parse_status.value().expr(), max_length, command == "TYPED_CEL");
93
93
  }
94

            
95
390
  return nullptr;
96
#else
97
  throw EnvoyException("CEL is not available for use in this environment.");
98
#endif
99
483
}
100

            
101
} // namespace Formatter
102
} // namespace Extensions
103
} // namespace Envoy