userver: userver/middlewares/pipeline.hpp Source File
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
pipeline.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/middlewares/pipeline.hpp
4/// @brief Lists all available middlewares and builds their order of execution.
5
6#include <unordered_map>
7
8#include <userver/components/component_base.hpp>
9#include <userver/components/component_fwd.hpp>
10#include <userver/utils/meta_light.hpp>
11#include <userver/yaml_config/fwd.hpp>
12
13#include <userver/middlewares/impl/middleware_pipeline_config.hpp>
14
15USERVER_NAMESPACE_BEGIN
16
17/// Pipeline to configure middlewares order and static configs.
18/// @see @ref scripts/docs/en/userver/grpc/middlewares_order.md
19/// @see @ref scripts/docs/en/userver/grpc/middlewares_configuration.md
20namespace middlewares {
21
22/// @brief The dependency type between middlewares.
23///
24/// Iff dependency type from 'X' to 'Y' is `kStrong` and 'Y' is disabled, middlewares pipeline will failure when start,
25/// otherwise (in `kWeak`) we ignore this dependency.
26enum class DependencyType {
27 kStrong = 0,
28 kWeak = 1,
29};
30
31namespace impl {
32
33struct Connect final {
34 std::string node_name{};
35 DependencyType type{DependencyType::kStrong};
36};
37
38struct MiddlewareDependency final {
39 std::string middleware_name{};
40 std::vector<Connect> befores{};
41 std::vector<Connect> afters{};
42 bool enabled{true};
43 std::optional<std::string> group{};
44};
45
46using Dependencies = std::unordered_map<std::string, MiddlewareDependency>;
47
48class MiddlewarePipeline final {
49public:
50 explicit MiddlewarePipeline(Dependencies&& deps);
51
52 std::vector<std::string> GetPerServiceMiddlewares(const MiddlewareRunnerConfig& config) const;
53
54 const MiddlewareOrderedList& GetOrderedList() const { return pipeline_; }
55
56private:
57 Dependencies deps_{};
58 MiddlewareOrderedList pipeline_{};
59};
60
61template <typename Group>
62std::string BeginOfGroup() {
63 return std::string{Group::kName} + "#begin";
64}
65
66template <typename Group>
67std::string EndOfGroup() {
68 return std::string{Group::kName} + "#end";
69}
70
71/// @brief A base component to create middlewares pipeline.
72///
73/// ## Static options:
74/// Name | Description | Default value
75/// ---- | ----------- | -------------
76/// middlewares | middlewares names and configs to use | `{}`
77///
78/// ## Config example:
79///
80/// @snippet grpc/functional_tests/middleware_server/static_config.yaml middleware pipeline component config
81
82class AnyMiddlewarePipelineComponent : public components::ComponentBase {
83public:
84 AnyMiddlewarePipelineComponent(
85 const components::ComponentConfig& config,
86 const components::ComponentContext& context,
87 impl::BasePipelineConfig&& base_config
88 );
89
90 static yaml_config::Schema GetStaticConfigSchema();
91
92 /// @cond
93 /// Only for internal use.
94 const impl::MiddlewarePipeline& GetPipeline() const;
95 /// @endcond
96
97private:
98 impl::MiddlewarePipeline pipeline_;
99};
100
101} // namespace impl
102
103/// @brief Specifies a middleware's order with respect to other middlewares.
104///
105/// If you don't care about the order in relative to others, ignore this and your middleware
106/// will be in the `kUser` group.
107/// Otherwise, pass an instance of this class to `MiddlewareComponentBase` in the constructor of your
108/// middleware component.
109class MiddlewareDependencyBuilder final {
110public:
111 /// @brief Make an empty dependency builder. For middlewares, `InGroup` must be called at some point.
112 MiddlewareDependencyBuilder() = default;
113
114 MiddlewareDependencyBuilder(const MiddlewareDependencyBuilder&) = default;
115 MiddlewareDependencyBuilder(MiddlewareDependencyBuilder&&) noexcept = default;
116 MiddlewareDependencyBuilder& operator=(const MiddlewareDependencyBuilder&) = default;
117 MiddlewareDependencyBuilder& operator=(MiddlewareDependencyBuilder&&) = default;
118
119 /// @brief Add dependency for your middleware. Your middleware will be before `MiddlewareBefore` in the pipeline
120 /// @param type is connection type between middlewares
121 template <typename MiddlewareBefore>
122 MiddlewareDependencyBuilder Before(DependencyType type = DependencyType::kStrong) &&;
123
124 /// @brief Add dependency for your middleware. Your middleware will be before `before` in the pipeline
125 /// @param type is connection type between middlewares
126 /// @param before is the middleware component name
127 MiddlewareDependencyBuilder Before(std::string_view before, DependencyType type = DependencyType::kStrong) &&;
128
129 /// @brief Add dependency for your middleware. Your middleware will be after `MiddlewareAfter` in the pipeline
130 /// @param type is connection type between middlewares
131 template <typename MiddlewareAfter>
132 MiddlewareDependencyBuilder After(DependencyType type = DependencyType::kStrong) &&;
133
134 /// @brief Add dependency for your middleware. Your middleware will be after `after` in the pipeline
135 /// @param type is connection type between middlewares
136 /// @param after is the middleware component name
137 MiddlewareDependencyBuilder After(std::string_view after, DependencyType type = DependencyType::kStrong) &&;
138
139 /// @brief Add dependency for your middleware. Your middleware will be in the `Group` group
140 template <typename Group>
141 MiddlewareDependencyBuilder InGroup() &&;
142
143 /// @cond
144 // Only for internal use.
145 impl::MiddlewareDependency ExtractDependency(std::string_view middleware_name) &&;
146 impl::MiddlewareDependency ExtractGroupDependency(std::string_view group_name) &&;
147 /// @endcond
148
149private:
150 void InUserGroupByDefault();
151
152 impl::MiddlewareDependency dep_{};
153};
154
155namespace impl {
156
157template <typename T>
158using IsGroup = decltype(T::kDependency);
159
160template <typename T>
161constexpr bool kIsGroup = std::is_same_v<meta::DetectedType<IsGroup, T>, const MiddlewareDependencyBuilder>;
162
163} // namespace impl
164
165template <typename MiddlewareBefore>
166MiddlewareDependencyBuilder MiddlewareDependencyBuilder::Before(DependencyType type) && {
167 if constexpr (impl::kIsGroup<MiddlewareBefore>) {
168 return std::move(*this).Before(impl::BeginOfGroup<MiddlewareBefore>(), type);
169 }
170 return std::move(*this).Before(MiddlewareBefore::kName, type);
171}
172
173template <typename MiddlewareAfter>
174MiddlewareDependencyBuilder MiddlewareDependencyBuilder::After(DependencyType type) && {
175 if constexpr (impl::kIsGroup<MiddlewareAfter>) {
176 return std::move(*this).After(impl::EndOfGroup<MiddlewareAfter>(), type);
177 }
178 return std::move(*this).After(MiddlewareAfter::kName, type);
179}
180
181template <typename Group>
182MiddlewareDependencyBuilder MiddlewareDependencyBuilder::InGroup() && {
183 dep_.group = Group::kName;
184 dep_.afters.push_back(impl::Connect{impl::BeginOfGroup<Group>(), DependencyType::kStrong});
185 return std::move(*this).Before(impl::EndOfGroup<Group>(), DependencyType::kWeak);
186}
187
188} // namespace middlewares
189
190USERVER_NAMESPACE_END