1
#pragma once
2

            
3
#include "envoy/matcher/matcher.h"
4
#include "envoy/server/factory_context.h"
5
#include "envoy/stream_info/stream_info.h"
6
#include "envoy/type/matcher/v3/status_code_input.pb.h"
7
#include "envoy/type/matcher/v3/status_code_input.pb.validate.h"
8

            
9
#include "source/common/http/header_utility.h"
10
#include "source/common/http/utility.h"
11

            
12
namespace Envoy {
13
namespace Http {
14
namespace Matching {
15

            
16
class HttpResponseStatusCodeInput : public Matcher::DataInput<HttpMatchingData> {
17
public:
18
700
  HttpResponseStatusCodeInput() = default;
19
699
  ~HttpResponseStatusCodeInput() override = default;
20

            
21
305
  Matcher::DataInputGetResult get(const HttpMatchingData& data) const override {
22
305
    const auto maybe_headers = data.responseHeaders();
23

            
24
305
    if (!maybe_headers) {
25
1
      return Matcher::DataInputGetResult::NoData(Matcher::DataAvailability::NotAvailable);
26
1
    }
27
304
    const auto maybe_status = Http::Utility::getResponseStatusOrNullopt(*maybe_headers);
28

            
29
304
    if (maybe_status.has_value()) {
30
303
      return Matcher::DataInputGetResult::CreateString(absl::StrCat(*maybe_status));
31
303
    }
32

            
33
1
    return Matcher::DataInputGetResult::NoData();
34
304
  }
35
};
36

            
37
inline constexpr absl::string_view Code1xx = "1xx";
38
inline constexpr absl::string_view Code2xx = "2xx";
39
inline constexpr absl::string_view Code3xx = "3xx";
40
inline constexpr absl::string_view Code4xx = "4xx";
41
inline constexpr absl::string_view Code5xx = "5xx";
42

            
43
class HttpResponseStatusCodeClassInput : public Matcher::DataInput<HttpMatchingData> {
44
public:
45
148
  HttpResponseStatusCodeClassInput() = default;
46
147
  ~HttpResponseStatusCodeClassInput() override = default;
47

            
48
155
  Matcher::DataInputGetResult get(const HttpMatchingData& data) const override {
49
155
    const auto maybe_headers = data.responseHeaders();
50
155
    if (!maybe_headers) {
51
1
      return Matcher::DataInputGetResult::NoData(Matcher::DataAvailability::NotAvailable);
52
1
    }
53

            
54
154
    const auto maybe_status = Http::Utility::getResponseStatusOrNullopt(*maybe_headers);
55
154
    if (maybe_status.has_value()) {
56
153
      if (*maybe_status >= 100 && *maybe_status < 200) {
57
1
        return Matcher::DataInputGetResult::CreateStringView(Code1xx);
58
1
      }
59
152
      if (*maybe_status >= 200 && *maybe_status < 300) {
60
13
        return Matcher::DataInputGetResult::CreateStringView(Code2xx);
61
13
      }
62
139
      if (*maybe_status >= 300 && *maybe_status < 400) {
63
1
        return Matcher::DataInputGetResult::CreateStringView(Code3xx);
64
1
      }
65
138
      if (*maybe_status >= 400 && *maybe_status < 500) {
66
26
        return Matcher::DataInputGetResult::CreateStringView(Code4xx);
67
26
      }
68
112
      if (*maybe_status >= 500 && *maybe_status < 600) {
69
110
        return Matcher::DataInputGetResult::CreateStringView(Code5xx);
70
110
      }
71
112
    }
72
3
    return Matcher::DataInputGetResult::NoData();
73
154
  }
74
};
75

            
76
template <class DataInputType, class ProtoType>
77
class HttpResponseStatusCodeInputFactoryBase : public Matcher::DataInputFactory<HttpMatchingData> {
78
public:
79
  explicit HttpResponseStatusCodeInputFactoryBase(const std::string& name)
80
24
      : name_("envoy.matching.inputs." + name) {}
81

            
82
459
  std::string name() const override { return name_; }
83

            
84
  Matcher::DataInputFactoryCb<HttpMatchingData>
85
862
  createDataInputFactoryCb(const Protobuf::Message&, ProtobufMessage::ValidationVisitor&) override {
86

            
87
862
    return [] { return std::make_unique<DataInputType>(); };
88
862
  };
89
877
  ProtobufTypes::MessagePtr createEmptyConfigProto() override {
90
877
    return std::make_unique<ProtoType>();
91
877
  }
92

            
93
private:
94
  const std::string name_;
95
};
96

            
97
class HttpResponseStatusCodeInputFactory
98
    : public HttpResponseStatusCodeInputFactoryBase<
99
          HttpResponseStatusCodeInput, envoy::type::matcher::v3::HttpResponseStatusCodeMatchInput> {
100
public:
101
  explicit HttpResponseStatusCodeInputFactory()
102
8
      : HttpResponseStatusCodeInputFactoryBase("status_code_input") {}
103
};
104

            
105
class HttpResponseStatusCodeClassInputFactory
106
    : public HttpResponseStatusCodeInputFactoryBase<
107
          HttpResponseStatusCodeClassInput,
108
          envoy::type::matcher::v3::HttpResponseStatusCodeClassMatchInput> {
109
public:
110
  explicit HttpResponseStatusCodeClassInputFactory()
111
8
      : HttpResponseStatusCodeInputFactoryBase("status_code_class_input") {}
112
};
113

            
114
inline constexpr absl::string_view LocalReplyTrue = "true";
115
inline constexpr absl::string_view LocalReplyFalse = "false";
116

            
117
class HttpResponseLocalReplyInput : public Matcher::DataInput<HttpMatchingData> {
118
public:
119
16
  HttpResponseLocalReplyInput() = default;
120
12
  ~HttpResponseLocalReplyInput() override = default;
121

            
122
19
  Matcher::DataInputGetResult get(const HttpMatchingData& data) const override {
123
19
    const auto& details = data.streamInfo().responseCodeDetails();
124
19
    if (!details.has_value()) {
125
1
      return Matcher::DataInputGetResult::NoData(Matcher::DataAvailability::NotAvailable);
126
1
    }
127
18
    if (details.value() == StreamInfo::ResponseCodeDetails::get().ViaUpstream) {
128
7
      return Matcher::DataInputGetResult::CreateStringView(LocalReplyFalse);
129
7
    }
130
11
    return Matcher::DataInputGetResult::CreateStringView(LocalReplyTrue);
131
18
  }
132
};
133

            
134
class HttpResponseLocalReplyInputFactory
135
    : public HttpResponseStatusCodeInputFactoryBase<
136
          HttpResponseLocalReplyInput, envoy::type::matcher::v3::HttpResponseLocalReplyMatchInput> {
137
public:
138
  explicit HttpResponseLocalReplyInputFactory()
139
8
      : HttpResponseStatusCodeInputFactoryBase("local_reply") {}
140
};
141

            
142
} // namespace Matching
143
} // namespace Http
144
} // namespace Envoy