userver: userver/middlewares/runner.hpp Source File
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
runner.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/middlewares/runner.hpp
4/// @brief @copybrief middlewares::RunnerComponentBase
5
6#include <vector>
7
8#include <userver/components/component_base.hpp>
9#include <userver/components/component_config.hpp>
10#include <userver/components/component_context.hpp>
11#include <userver/utils/assert.hpp>
12#include <userver/utils/impl/internal_tag.hpp>
13#include <userver/yaml_config/merge_schemas.hpp>
14
15#include <userver/middlewares/impl/middleware_pipeline_config.hpp>
16#include <userver/middlewares/impl/simple_middleware_pipeline.hpp>
17#include <userver/middlewares/pipeline.hpp>
18
19USERVER_NAMESPACE_BEGIN
20
21namespace middlewares {
22
23namespace impl {
24
25yaml_config::YamlConfig ValidateAndMergeMiddlewareConfigs(
26 const formats::yaml::Value& global,
27 const yaml_config::YamlConfig& local,
28 yaml_config::Schema schema
29);
30
31/// Make the default builder for `middlewares::groups::User` group.
32MiddlewareDependencyBuilder MakeDefaultUserDependency();
33
34class WithMiddlewareDependencyComponentBase : public components::ComponentBase {
35public:
36 WithMiddlewareDependencyComponentBase(
37 const components::ComponentConfig& config,
38 const components::ComponentContext& context
39
40 )
41 : components::ComponentBase(config, context) {}
42
43 virtual const middlewares::impl::MiddlewareDependency& GetMiddlewareDependency(utils::impl::InternalTag) const = 0;
44};
45
46} // namespace impl
47
48/// @ingroup userver_base_classes
49///
50/// @brief Base class for middleware factory component.
51template <typename MiddlewareBaseType, typename HandlerInfo>
52class MiddlewareFactoryComponentBase : public impl::WithMiddlewareDependencyComponentBase {
53public:
54 using MiddlewareBase = MiddlewareBaseType;
55
56 MiddlewareFactoryComponentBase(
57 const components::ComponentConfig& config,
58 const components::ComponentContext& context,
59 MiddlewareDependencyBuilder&& builder = impl::MakeDefaultUserDependency()
60 )
61 : impl::WithMiddlewareDependencyComponentBase(config, context),
62 global_config_(config.As<formats::yaml::Value>()),
63 dependency_(std::move(builder).Extract(config.Name())) {}
64
65 /// @brief Returns a middleware according to the component's settings.
66 ///
67 /// @param info is a handler info for the middleware.
68 /// @param middleware_config config for the middleware.
69 ///
70 /// @warning Don't store `info` by reference. `info` object will be dropped after the `CreateMiddleware` call.
71 virtual std::shared_ptr<MiddlewareBase>
72 CreateMiddleware(const HandlerInfo& info, const yaml_config::YamlConfig& middleware_config) const = 0;
73
74 /// @brief This method should return the schema of a middleware configuration.
75 /// Always write `return GetStaticConfigSchema();` in this method.
76 virtual yaml_config::Schema GetMiddlewareConfigSchema() const { return GetStaticConfigSchema(); }
77
78 static yaml_config::Schema GetStaticConfigSchema() {
79 return yaml_config::MergeSchemas<components::ComponentBase>(R"(
80type: object
81description: base class for grpc-server middleware
82additionalProperties: false
83properties:
84 enabled:
85 type: string
86 description: the flag to enable/disable middleware in the pipeline
87 defaultDescription: true
88)");
89 }
90
91 /// @cond
92 /// Only for internal use.
93 const middlewares::impl::MiddlewareDependency& GetMiddlewareDependency(utils::impl::InternalTag) const override {
94 return dependency_;
95 }
96
97 const formats::yaml::Value& GetGlobalConfig(utils::impl::InternalTag) const { return global_config_; }
98 /// @endcond
99
100private:
101 const formats::yaml::Value global_config_;
102 middlewares::impl::MiddlewareDependency dependency_;
103};
104
105/// @brief Base class for a component that runs middlewares.
106///
107/// There are a local and global configs of middlewares.
108/// Global config of middleware is a classic config in `components_manager.components`.
109/// You can override the global config for the specific service/client by the local config in the config of this
110/// component: see the 'middlewares' option.
111///
112/// `RunnerComponentBase` creates middleware instances using `MiddlewareFactoryComponentBase`.
113/// The Ordered list of middlewares `RunnerComponentBase` takes from Pipeline component.
114/// So, 'Pipeline' is responsible for the order of middlewares. `RunnerComponentBase` is responsible for creating
115/// middlewares and overriding configs.
116template <typename MiddlewareBase, typename HandlerInfo>
118 public impl::PipelineCreatorInterface<MiddlewareBase, HandlerInfo> {
119public:
120 static yaml_config::Schema GetStaticConfigSchema() {
121 return yaml_config::MergeSchemas<components::ComponentBase>(R"(
122type: object
123description: base class for all the gRPC service components
124additionalProperties: false
125properties:
126 disable-user-pipeline-middlewares:
127 type: boolean
128 description: flag to disable groups::User middlewares from pipeline
129 defaultDescription: false
130 disable-all-pipeline-middlewares:
131 type: boolean
132 description: flag to disable all middlewares from pipline
133 defaultDescription: false
134 middlewares:
135 type: object
136 description: overloads of configs of middlewares per service
137 additionalProperties:
138 type: object
139 description: a middleware config
140 additionalProperties: true
141 properties:
142 enabled:
143 type: boolean
144 description: enable middleware in the list
145 properties: {}
146)");
147 }
148
149protected:
150 using MiddlewareFactory = MiddlewareFactoryComponentBase<MiddlewareBase, HandlerInfo>;
151
152 RunnerComponentBase(
153 const components::ComponentConfig& config,
154 const components::ComponentContext& context,
155 std::string_view pipeline_name
156 );
157
158 /// @cond
159 /// Only for internal use.
160 std::vector<std::shared_ptr<MiddlewareBase>> CreateMiddlewares(const HandlerInfo& info) const override;
161 /// @endcond
162
163private:
164 struct MiddlewareInfo final {
165 const MiddlewareFactory* factory{nullptr};
166 yaml_config::YamlConfig local_config{};
167 };
168
169 std::vector<MiddlewareInfo> middleware_infos_{};
170};
171
172template <typename MiddlewareBase, typename HandlerInfo>
173RunnerComponentBase<MiddlewareBase, HandlerInfo>::RunnerComponentBase(
174 const components::ComponentConfig& config,
175 const components::ComponentContext& context,
176 std::string_view pipeline_name
177)
178 : components::ComponentBase(config, context) {
179 const auto& middlewares = config["middlewares"];
180 const auto& pipeline = context.FindComponent<impl::AnyMiddlewarePipelineComponent>(pipeline_name).GetPipeline();
181 for (const auto& mid : pipeline.GetPerServiceMiddlewares(config.As<impl::MiddlewareRunnerConfig>())) {
182 const auto* factory = context.FindComponentOptional<MiddlewareFactory>(mid);
183 UINVARIANT(factory != nullptr, fmt::format("The middleware '{}' must exist", mid));
184 middleware_infos_.push_back(MiddlewareInfo{factory, middlewares[mid]});
185 }
186}
187
188template <typename MiddlewareBase, typename HandlerInfo>
189std::vector<std::shared_ptr<MiddlewareBase>> RunnerComponentBase<MiddlewareBase, HandlerInfo>::CreateMiddlewares(
190 const HandlerInfo& info
191) const {
192 std::vector<std::shared_ptr<MiddlewareBase>> middlewares{};
193 middlewares.reserve(middleware_infos_.size());
194 for (const auto& [factory, local_config] : middleware_infos_) {
195 auto config = impl::ValidateAndMergeMiddlewareConfigs(
196 factory->GetGlobalConfig(utils::impl::InternalTag{}), local_config, factory->GetMiddlewareConfigSchema()
197 );
198 middlewares.push_back(factory->CreateMiddleware(info, config));
199 }
200 return middlewares;
201}
202
203namespace impl {
204
205/// @brief A short-cut for defining a middleware-factory.
206///
207/// `MiddlewareBase` type is a interface of middleware, so `Middleware` type is a implementation of this interface.
208/// Your middleware will be created with a default constructor and will be in `middlewares::groups::User` group.
209template <typename MiddlewareBase, typename Middleware, typename HandlerInfo>
210class SimpleMiddlewareFactoryComponent final : public MiddlewareFactoryComponentBase<MiddlewareBase, HandlerInfo> {
211public:
212 static constexpr std::string_view kName = Middleware::kName;
213
214 SimpleMiddlewareFactoryComponent(
215 const components::ComponentConfig& config,
216 const components::ComponentContext& context
217 )
218 : MiddlewareFactoryComponentBase<MiddlewareBase, HandlerInfo>(
219 config,
220 context,
221 middlewares::MiddlewareDependencyBuilder{Middleware::kDependency}
222 ) {}
223
224private:
225 std::shared_ptr<MiddlewareBase> CreateMiddleware(const HandlerInfo&, const yaml_config::YamlConfig&)
226 const override {
227 return std::make_shared<Middleware>();
228 }
229};
230
231} // namespace impl
232
233} // namespace middlewares
234
235template <typename MiddlewareBase, typename Middleware, typename HandlerInfo>
236inline constexpr bool components::kHasValidate<
237 middlewares::impl::SimpleMiddlewareFactoryComponent<MiddlewareBase, Middleware, HandlerInfo>> = true;
238
239USERVER_NAMESPACE_END