1
#pragma once
2

            
3
#include <memory>
4
#include <vector>
5

            
6
#include "envoy/common/pure.h"
7
#include "envoy/config/typed_config.h"
8

            
9
#include "source/common/common/fmt.h"
10
#include "source/common/common/utility.h"
11
#include "source/common/protobuf/protobuf.h"
12

            
13
#include "absl/strings/string_view.h"
14
#include "absl/types/optional.h"
15

            
16
namespace Envoy {
17
namespace StreamInfo {
18

            
19
class FilterState;
20

            
21
using FilterStateSharedPtr = std::shared_ptr<FilterState>;
22

            
23
// Objects stored in the filter state can optionally be shared between the
24
// upstream and downstream filter state. Note that sharing happens at the
25
// connection level and in some cases may significantly reduce performance by
26
// preventing pooling of multiple downstream requests to a single
27
// upstream connection.
28
enum class StreamSharingMayImpactPooling {
29
  // None implies the object is exclusive to the stream.
30
  None,
31

            
32
  // Mark a filter state object as shared with the upstream connection.
33
  // Shared filter state objects are copied by reference from the downstream
34
  // requests and connections to the upstream connection filter state. When
35
  // upstream connections are re-used between streams, the downstream objects
36
  // are captured for the first, initiating stream. To force distinct
37
  // upstream connections, the shared filter state object must implement the
38
  // hashing interface. Shared objects with distinct hashes will use distinct
39
  // upstream connections. Note that this affects connection pooling,
40
  // preventing any re-use of the upstream connections in the worst case.
41
  SharedWithUpstreamConnection,
42

            
43
  // Same as SharedWithUpstreamConnection, except that the filter state is
44
  // not transitively shared. The filter state is imported into the upstream
45
  // connection filter state as exclusive to the upstream connection.
46
  SharedWithUpstreamConnectionOnce,
47
};
48

            
49
/**
50
 * FilterState represents dynamically generated information regarding a stream (TCP or HTTP level)
51
 * or a connection by various filters in Envoy. FilterState can be write-once or write-many.
52
 */
53
class FilterState {
54
public:
55
  enum class StateType { ReadOnly, Mutable };
56

            
57
  // Objects stored in the FilterState may have different life span. Life span is what controls
58
  // how long an object stored in FilterState lives. Implementation of this interface actually
59
  // stores objects in a (reverse) tree manner - multiple FilterStateImpl with shorter life span may
60
  // share the same FilterStateImpl as parent, which may recursively share parent with other
61
  // FilterStateImpl at the same life span. This interface is supposed to be accessed at the leaf
62
  // level (FilterChain) for objects with any desired longer life span.
63
  //
64
  // - FilterChain has the shortest life span, which is as long as the filter chain lives.
65
  //
66
  // - Request is longer than FilterChain. When internal redirect is enabled, one
67
  //   downstream request may create multiple filter chains. Request allows an object to
68
  //   survive across filter chains for bookkeeping needs. This is not used for the upstream case.
69
  //
70
  // - Connection makes an object survive the entire duration of a connection.
71
  //   Any stream within this connection can see the same object.
72
  //
73
  // Note that order matters in this enum because it's assumed that life span grows as enum number
74
  // grows.
75
  //
76
  // Note that for more accurate book-keeping it is recommended to subscribe to
77
  // the stream callbacks instead of relying on the destruction of the filter
78
  // state.
79
  //
80
  // As a special case, objects that are marked as shared with the upstream
81
  // become bound to the upstream connection life span in addition to the
82
  // original stream life span. That means, for example, that a shared request
83
  // span object may outlive the original request when it is shared, because it
84
  // may be captured by an upstream connection for the original downstream
85
  // request, which remains open after the downstream request completes.
86
  enum LifeSpan { FilterChain, Request, Connection, TopSpan = Connection };
87

            
88
  class Object {
89
  public:
90
    using FieldType = absl::variant<absl::monostate, absl::string_view, int64_t>;
91

            
92
38290
    virtual ~Object() = default;
93

            
94
    /**
95
     * @return Protobuf::MessagePtr an unique pointer to the proto serialization of the filter
96
     * state. If returned message type is Protobuf::Any it will be directly used in protobuf
97
     * logging. nullptr if the filter state cannot be serialized or serialization is not supported.
98
     */
99
2
    virtual ProtobufTypes::MessagePtr serializeAsProto() const { return nullptr; }
100

            
101
    /**
102
     * @return absl::optional<std::string> a optional string to the serialization of the filter
103
     * state. No value if the filter state cannot be serialized or serialization is not supported.
104
     * This method can be used to get an unstructured serialization result.
105
     */
106
4
    virtual absl::optional<std::string> serializeAsString() const { return absl::nullopt; }
107

            
108
    /**
109
     * @return bool true if the object supports field access. False if the object does not support
110
     * field access. Default implementation returns false.
111
     */
112
8
    virtual bool hasFieldSupport() const { return false; }
113

            
114
    /**
115
     * @return FieldType a single state property or field value for a name.
116
     */
117
    virtual FieldType getField(absl::string_view) const { return absl::monostate{}; }
118
  };
119

            
120
  /**
121
   * Generic factory for filter state objects. The factory registry uses the
122
   * object data name as the index for the object factory. This factory should be used by the
123
   * dynamic extensions that cannot use the object constructors directly.
124
   */
125
  class ObjectFactory : public Config::UntypedFactory {
126
  public:
127
    // Config::UntypedFactory
128
11795
    std::string category() const override { return "filter_state.object"; }
129

            
130
    /**
131
     * @return std::unique_ptr<Object> from the serialized object data or nullptr if the input
132
     * is malformed.
133
     */
134
    virtual std::unique_ptr<Object> createFromBytes(absl::string_view data) const PURE;
135
  };
136

            
137
  struct FilterObject {
138
    std::shared_ptr<Object> data_;
139
    StateType state_type_{StateType::ReadOnly};
140
    StreamSharingMayImpactPooling stream_sharing_{StreamSharingMayImpactPooling::None};
141
    std::string name_;
142
  };
143

            
144
  using Objects = std::vector<FilterObject>;
145
  using ObjectsPtr = std::unique_ptr<Objects>;
146

            
147
393125
  virtual ~FilterState() = default;
148

            
149
  /**
150
   * @param data_name the name of the data being set.
151
   * @param data an owning pointer to the data to be stored.
152
   * @param state_type indicates whether the object is mutable or not.
153
   * @param life_span indicates the life span of the object: bound to the filter chain, a
154
   * request, or a connection.
155
   *
156
   * Note that it is an error to call setData() twice with the same
157
   * data_name, if the existing object is immutable. Similarly, it is an
158
   * error to call setData() with same data_name but different state_types
159
   * (mutable and readOnly, or readOnly and mutable) or different life_span.
160
   * This is to enforce a single authoritative source for each piece of
161
   * data stored in FilterState.
162
   */
163
  virtual void
164
  setData(absl::string_view data_name, std::shared_ptr<Object> data, StateType state_type,
165
          LifeSpan life_span = LifeSpan::FilterChain,
166
          StreamSharingMayImpactPooling stream_sharing = StreamSharingMayImpactPooling::None) PURE;
167

            
168
  /**
169
   * @param data_name the name of the data being looked up (mutable/readonly).
170
   * @return a typed pointer to the stored data or nullptr if the data does not exist or the data
171
   * type does not match the expected type.
172
   */
173
432857
  template <typename T> const T* getDataReadOnly(absl::string_view data_name) const {
174
432857
    return dynamic_cast<const T*>(getDataReadOnlyGeneric(data_name));
175
432857
  }
176

            
177
  /**
178
   * @param data_name the name of the data being looked up (mutable/readonly).
179
   * @return a const pointer to the stored data or nullptr if the data does not exist.
180
   */
181
  virtual const Object* getDataReadOnlyGeneric(absl::string_view data_name) const PURE;
182

            
183
  /**
184
   * @param data_name the name of the data being looked up (mutable/readonly).
185
   * @return a typed pointer to the stored data or nullptr if the data does not exist or the data
186
   * type does not match the expected type.
187
   */
188
4960
  template <typename T> T* getDataMutable(absl::string_view data_name) {
189
4960
    return dynamic_cast<T*>(getDataMutableGeneric(data_name));
190
4960
  }
191

            
192
  /**
193
   * @param data_name the name of the data being looked up (mutable/readonly).
194
   * @return a pointer to the stored data or nullptr if the data does not exist.
195
   */
196
  virtual Object* getDataMutableGeneric(absl::string_view data_name) PURE;
197

            
198
  /**
199
   * @param data_name the name of the data being looked up (mutable/readonly).
200
   * @return a shared pointer to the stored data or nullptr if the data does not exist.
201
   */
202
  virtual std::shared_ptr<Object> getDataSharedMutableGeneric(absl::string_view data_name) PURE;
203

            
204
  /**
205
   * @param data_name the name of the data being probed.
206
   * @return Whether data of the type and name specified exists in the
207
   * data store.
208
   */
209
32381
  template <typename T> bool hasData(absl::string_view data_name) const {
210
32381
    return getDataReadOnly<T>(data_name) != nullptr;
211
32381
  }
212

            
213
  /**
214
   * @param data_name the name of the data being probed.
215
   * @return Whether data of any type and the name specified exists in the
216
   * data store.
217
   */
218
  virtual bool hasDataWithName(absl::string_view data_name) const PURE;
219

            
220
  /**
221
   * @param life_span the LifeSpan above which data existence is checked.
222
   * @return whether data of any type exist with LifeSpan greater than life_span.
223
   */
224
  virtual bool hasDataAtOrAboveLifeSpan(LifeSpan life_span) const PURE;
225

            
226
  /**
227
   * @return the LifeSpan of objects stored by this instance. Objects with
228
   * LifeSpan longer than this are handled recursively.
229
   */
230
  virtual LifeSpan lifeSpan() const PURE;
231

            
232
  /**
233
   * @return the pointer of the parent FilterState that has longer life span. nullptr means this is
234
   * either the top LifeSpan or the parent is not yet created.
235
   */
236
  virtual FilterStateSharedPtr parent() const PURE;
237

            
238
  /**
239
   * @return filter objects that are shared with the upstream connection.
240
   **/
241
  virtual ObjectsPtr objectsSharedWithUpstreamConnection() const PURE;
242
};
243

            
244
} // namespace StreamInfo
245
} // namespace Envoy