1#include <userver/easy.hpp>
5#include <unordered_map>
8#include <boost/algorithm/string/replace.hpp>
9#include <boost/program_options.hpp>
11#include <fmt/format.h>
12#include <fmt/ranges.h>
14#include <userver/clients/dns/component.hpp>
15#include <userver/clients/http/component.hpp>
16#include <userver/clients/http/middlewares/pipeline_component.hpp>
17#include <userver/components/component_context.hpp>
18#include <userver/components/minimal_server_component_list.hpp>
19#include <userver/components/run.hpp>
20#include <userver/server/handlers/http_handler_base.hpp>
21#include <userver/storages/postgres/component.hpp>
22#include <userver/testsuite/testsuite_support.hpp>
23#include <userver/utils/daemon_run.hpp>
25USERVER_NAMESPACE_BEGIN
31constexpr std::string_view kConfigBase = R"~(# yaml
32components_manager:
33 task_processors: # Task processor is an executor for coroutine tasks
34 main-task-processor: # Make a task processor for CPU-bound coroutine tasks.
35 worker_threads: 4 # Process tasks in 4 threads.
36
37 fs-task-processor: # Make a separate task processor for filesystem bound tasks.
38 worker_threads: 1
39
40 default_task_processor: main-task-processor # Task processor in which components start.
41
42 components: # Configuring components that were registered via component_list)~";
44constexpr std::string_view kConfigServerTemplate = R"~(
45 server:
46 listener: # configuring the main listening socket...
47 port: {} # ...to listen on this port and...
48 task_processor: main-task-processor # ...process incoming requests on this task processor.
49)~";
51constexpr std::string_view kConfigLoggingTemplate = R"~(
52 logging:
53 fs-task-processor: fs-task-processor
54 loggers:
55 default:
56 file_path: '@stderr'
57 level: {}
58 overflow_behavior: discard # Drop logs if the system is too busy to write them down.
59)~";
61constexpr std::string_view kConfigHandlerTemplate{
62 "path: {0} # Registering handler by URL '{0}'.\n"
64 "task_processor: main-task-processor # Run it on CPU bound task processor\n"
68 std::unordered_map<std::string, HttpBase::Callback> http_functions;
70 std::string db_schema;
73SharedPyaload globals{};
79DependenciesBase::~DependenciesBase() =
default;
83class HttpBase::Handle
final :
public server::handlers::
HttpHandlerBase {
88 callback_{globals.http_functions.at(config
.Name())}
91 std::string HandleRequestThrow(
const server::
http::HttpRequest& request, server::
request::RequestContext&)
93 if (globals.default_content_type) {
96 return callback_(request, deps_);
100 const impl::DependenciesBase& deps_;
101 HttpBase::Callback& callback_;
104HttpBase::HttpBase(
int argc,
const char*
const argv[])
107 static_config_{kConfigBase},
111HttpBase::~HttpBase() {
112 static_config_.append(fmt::format(kConfigServerTemplate, port_));
113 static_config_.append(fmt::format(kConfigLoggingTemplate,
ToString(level_
)));
115 namespace po = boost::program_options;
116 po::variables_map vm;
118 std::string config_dump;
119 std::string schema_dump;
123 (
"dump-config", po::value(&config_dump)->implicit_value(
""),
"path to dump the server config")
124 (
"dump-db-schema", po::value(&schema_dump)->implicit_value(
""),
"path to dump the DB schema")
125 (
"config,c", po::value<std::string>(),
"path to server config")
129 po::store(po::parse_command_line(argc_, argv_, desc), vm);
132 if (vm.count(
"help")) {
133 std::cerr << desc <<
'\n';
137 if (vm.count(
"dump-config")) {
138 if (config_dump.empty()) {
139 std::cout << static_config_ << std::endl;
141 std::ofstream(config_dump) << static_config_;
146 if (vm.count(
"dump-db-schema")) {
147 if (schema_dump.empty()) {
148 std::cout << schema_dump << std::endl;
150 std::ofstream(schema_dump) << globals.db_schema;
167void HttpBase::
Route(std::string_view path, Callback&& func, std::initializer_list<server::
http::
HttpMethod> methods) {
168 auto component_name = fmt::format(
"{}-{}", path, fmt::join(methods,
","));
170 globals.http_functions.emplace(component_name, std::move(func));
172 AddComponentConfig(component_name, fmt::format(kConfigHandlerTemplate, path, fmt::join(methods,
",")));
175void HttpBase::AddComponentConfig(std::string_view component, std::string_view config) {
176 static_config_ += fmt::format(
"\n {}:", component);
177 if (config.empty()) {
178 static_config_ +=
" {}\n";
180 if (config.back() ==
'\n') {
181 config = std::string_view{config.data(), config.size() - 1};
183 static_config_ += boost::algorithm::replace_all_copy(
"\n" + std::string{config},
"\n",
"\n ");
184 static_config_ +=
'\n';
188void HttpBase::
DbSchema(std::string_view schema) { globals.db_schema = schema; }
190const std::string& HttpBase::
GetDbSchema()
noexcept {
return globals.db_schema; }
192void HttpBase::
Port(std::uint16_t port) { port_ = port; }
200 if (!db_schema.empty()) {
205void PgDep::RegisterOn(HttpBase& app) {
208 "dbconnection#env: POSTGRESQL\n"
209 "dbconnection#fallback: 'postgresql://testsuite@localhost:15433/postgres'\n"
210 "blocking_task_processor: fs-task-processor\n"
211 "dns_resolver: async\n"
226 "pool-statistics-disable: false\n"
227 "thread-name-prefix: http-client\n"
229 "fs-task-processor: fs-task-processor\n"
232 clients
::http
::MiddlewarePipelineComponent
>(clients
::http
::MiddlewarePipelineComponent
::kName, "");