Skip to content

Commit f8c46d8

Browse files
author
smorivan
committed
fix core: add test on HttpClient destructor awaiting pending requests
commit_hash:40d2a155ff1cf8d5e96b3dab4938fc474dd5199f
1 parent 8e41857 commit f8c46d8

File tree

10 files changed

+264
-0
lines changed

10 files changed

+264
-0
lines changed

.mapping.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,15 @@
714714
"core/functional_tests/http2server/tests/conftest.py":"taxi/uservices/userver/core/functional_tests/http2server/tests/conftest.py",
715715
"core/functional_tests/http2server/tests/test_http2_streaming.py":"taxi/uservices/userver/core/functional_tests/http2server/tests/test_http2_streaming.py",
716716
"core/functional_tests/http2server/tests/test_http2server.py":"taxi/uservices/userver/core/functional_tests/http2server/tests/test_http2server.py",
717+
"core/functional_tests/http_client_plugins/CMakeLists.txt":"taxi/uservices/userver/core/functional_tests/http_client_plugins/CMakeLists.txt",
718+
"core/functional_tests/http_client_plugins/config_vars.yaml":"taxi/uservices/userver/core/functional_tests/http_client_plugins/config_vars.yaml",
719+
"core/functional_tests/http_client_plugins/main.cpp":"taxi/uservices/userver/core/functional_tests/http_client_plugins/main.cpp",
720+
"core/functional_tests/http_client_plugins/src/echo_handler.hpp":"taxi/uservices/userver/core/functional_tests/http_client_plugins/src/echo_handler.hpp",
721+
"core/functional_tests/http_client_plugins/src/echo_handler_detach.hpp":"taxi/uservices/userver/core/functional_tests/http_client_plugins/src/echo_handler_detach.hpp",
722+
"core/functional_tests/http_client_plugins/src/plugin.hpp":"taxi/uservices/userver/core/functional_tests/http_client_plugins/src/plugin.hpp",
723+
"core/functional_tests/http_client_plugins/static_config.yaml":"taxi/uservices/userver/core/functional_tests/http_client_plugins/static_config.yaml",
724+
"core/functional_tests/http_client_plugins/tests/conftest.py":"taxi/uservices/userver/core/functional_tests/http_client_plugins/tests/conftest.py",
725+
"core/functional_tests/http_client_plugins/tests/test_plugin.py":"taxi/uservices/userver/core/functional_tests/http_client_plugins/tests/test_plugin.py",
717726
"core/functional_tests/https/CMakeLists.txt":"taxi/uservices/userver/core/functional_tests/https/CMakeLists.txt",
718727
"core/functional_tests/https/cert.crt":"taxi/uservices/userver/core/functional_tests/https/cert.crt",
719728
"core/functional_tests/https/httpserver_handlers.hpp":"taxi/uservices/userver/core/functional_tests/https/httpserver_handlers.hpp",
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
project(userver-core-tests-http-client-plugins CXX)
2+
3+
file(GLOB_RECURSE SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*pp")
4+
add_executable(${PROJECT_NAME} ${SOURCES} "main.cpp")
5+
target_include_directories(${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src")
6+
target_link_libraries(${PROJECT_NAME} userver::core)
7+
8+
userver_chaos_testsuite_add()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
echo-url: $mockserver/echo
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include <userver/clients/dns/component.hpp>
2+
#include <userver/clients/http/component.hpp>
3+
#include <userver/components/minimal_server_component_list.hpp>
4+
#include <userver/server/handlers/tests_control.hpp>
5+
#include <userver/testsuite/testsuite_support.hpp>
6+
#include <userver/utils/daemon_run.hpp>
7+
8+
#include <userver/utest/using_namespace_userver.hpp>
9+
10+
#include <echo_handler.hpp>
11+
#include <echo_handler_detach.hpp>
12+
#include <plugin.hpp>
13+
14+
int main(int argc, char* argv[]) {
15+
const auto component_list = components::MinimalServerComponentList()
16+
.Append<EchoHandler>()
17+
.Append<EchoHandlerDetach>()
18+
.Append<components::TestsuiteSupport>()
19+
.Append<server::handlers::TestsControl>()
20+
.Append<clients::dns::Component>()
21+
.Append<PluginComponent>()
22+
.Append<components::HttpClientCore>()
23+
.Append<components::HttpClient>();
24+
return utils::DaemonMain(argc, argv, component_list);
25+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#pragma once
2+
3+
#include <userver/clients/http/component.hpp>
4+
#include <userver/components/component.hpp>
5+
#include <userver/server/handlers/http_handler_base.hpp>
6+
#include <userver/utils/algo.hpp>
7+
#include <userver/yaml_config/merge_schemas.hpp>
8+
9+
class EchoHandler final : public server::handlers::HttpHandlerBase {
10+
public:
11+
static constexpr std::string_view kName = "handler-echo";
12+
13+
EchoHandler(const components::ComponentConfig& config, const components::ComponentContext& context)
14+
: HttpHandlerBase(config, context),
15+
echo_url_{config["echo-url"].As<std::string>()},
16+
http_client_(context.FindComponent<components::HttpClient>().GetHttpClient()) {}
17+
18+
std::string HandleRequestThrow(const server::http::HttpRequest&, server::request::RequestContext&) const override {
19+
auto response = http_client_.CreateRequest().get(echo_url_).perform();
20+
response->raise_for_status();
21+
UASSERT(utils::FindOptional(response->headers(), std::string_view{"additional-header"}) == "header-value");
22+
return {};
23+
}
24+
25+
static yaml_config::Schema GetStaticConfigSchema() {
26+
return yaml_config::MergeSchemas<server::handlers::HttpHandlerBase>(R"(
27+
type: object
28+
description: HTTP echo component
29+
additionalProperties: false
30+
properties:
31+
echo-url:
32+
type: string
33+
description: some other microservice listens on this URL
34+
)");
35+
}
36+
37+
private:
38+
const std::string echo_url_;
39+
clients::http::Client& http_client_;
40+
};
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#pragma once
2+
3+
#include <userver/clients/http/component.hpp>
4+
#include <userver/components/component.hpp>
5+
#include <userver/server/handlers/http_handler_base.hpp>
6+
#include <userver/yaml_config/merge_schemas.hpp>
7+
8+
class EchoHandlerDetach final : public server::handlers::HttpHandlerBase {
9+
public:
10+
static constexpr std::string_view kName = "handler-echo-detach";
11+
12+
EchoHandlerDetach(const components::ComponentConfig& config, const components::ComponentContext& context)
13+
: HttpHandlerBase(config, context),
14+
echo_url_{config["echo-url"].As<std::string>()},
15+
http_client_(context.FindComponent<components::HttpClient>().GetHttpClient()) {}
16+
17+
std::string HandleRequestThrow(const server::http::HttpRequest&, server::request::RequestContext&) const override {
18+
http_client_.CreateRequest().get(echo_url_).async_perform().Detach();
19+
return {};
20+
}
21+
22+
static yaml_config::Schema GetStaticConfigSchema() {
23+
return yaml_config::MergeSchemas<server::handlers::HttpHandlerBase>(R"(
24+
type: object
25+
description: HTTP echo detach component
26+
additionalProperties: false
27+
properties:
28+
echo-url:
29+
type: string
30+
description: some other microservice listens on this URL
31+
)");
32+
}
33+
34+
private:
35+
const std::string echo_url_;
36+
clients::http::Client& http_client_;
37+
};
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#pragma once
2+
3+
#include <userver/clients/http/plugin.hpp>
4+
#include <userver/clients/http/plugin_component.hpp>
5+
#include <userver/clients/http/response.hpp>
6+
#include <userver/components/component.hpp>
7+
#include <userver/yaml_config/merge_schemas.hpp>
8+
9+
class Plugin final : public clients::http::Plugin {
10+
public:
11+
Plugin(std::string key, std::string value)
12+
: clients::http::Plugin("add-header"),
13+
key_(std::make_unique<std::string>(std::move(key))),
14+
value_(std::make_unique<std::string>(std::move(value))) {}
15+
16+
void HookPerformRequest(clients::http::PluginRequest& request) override { request.SetHeader(*key_, *value_); }
17+
18+
void HookCreateSpan(clients::http::PluginRequest&, tracing::Span&) override {}
19+
20+
void HookOnCompleted(clients::http::PluginRequest&, clients::http::Response& response) override {
21+
response.headers().emplace(*key_, *value_);
22+
}
23+
24+
void HookOnError(clients::http::PluginRequest&, std::error_code) override {}
25+
26+
bool HookOnRetry(clients::http::PluginRequest&) override { return true; }
27+
28+
private:
29+
std::unique_ptr<std::string> key_;
30+
std::unique_ptr<std::string> value_;
31+
};
32+
33+
class PluginComponent final : public clients::http::plugin::ComponentBase {
34+
public:
35+
static constexpr std::string_view kName = "http-client-plugin-add-header";
36+
37+
PluginComponent(const components::ComponentConfig& config, const components::ComponentContext& context)
38+
: ComponentBase(config, context),
39+
plugin_(std::make_unique<Plugin>(config["key"].As<std::string>(), config["value"].As<std::string>())) {}
40+
41+
~PluginComponent() override = default;
42+
43+
clients::http::Plugin& GetPlugin() override { return *plugin_; }
44+
static yaml_config::Schema GetStaticConfigSchema() {
45+
return yaml_config::MergeSchemas<clients::http::plugin::ComponentBase>(R"(
46+
type: object
47+
description: Test plugin adding header to request and response
48+
additionalProperties: false
49+
properties:
50+
key:
51+
type: string
52+
description: header name
53+
value:
54+
type: string
55+
description: header value
56+
)");
57+
}
58+
59+
private:
60+
std::unique_ptr<Plugin> plugin_;
61+
};
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
components_manager:
2+
components:
3+
http-client-plugin-add-header:
4+
key: additional-header
5+
value: header-value
6+
handler-echo:
7+
echo-url: $echo-url
8+
path: /echo
9+
method: GET
10+
task_processor: main-task-processor
11+
handler-echo-detach:
12+
echo-url: $echo-url
13+
path: /echo-detach
14+
method: GET
15+
task_processor: main-task-processor
16+
17+
http-client:
18+
plugins:
19+
add-header: 1
20+
http-client-core:
21+
fs-task-processor: fs-task-processor
22+
23+
dns-client:
24+
fs-task-processor: fs-task-processor
25+
26+
testsuite-support:
27+
tests-control:
28+
# Some options from server::handlers::HttpHandlerBase
29+
path: /tests/{action}
30+
method: POST
31+
task_processor: main-task-processor
32+
33+
server:
34+
listener:
35+
port: 8089
36+
task_processor: main-task-processor
37+
logging:
38+
fs-task-processor: fs-task-processor
39+
loggers:
40+
default:
41+
file_path: '@stderr'
42+
level: info
43+
44+
task_processors:
45+
main-task-processor:
46+
worker_threads: 4
47+
48+
fs-task-processor:
49+
worker_threads: 4
50+
51+
default_task_processor: main-task-processor
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pytest_plugins = ['pytest_userver.plugins.core']
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import asyncio
2+
3+
import pytest
4+
5+
6+
async def test_plugin(service_client, mockserver):
7+
@mockserver.json_handler('/echo')
8+
async def _handler(request):
9+
assert request.headers['additional-header'] == 'header-value'
10+
return mockserver.make_response()
11+
12+
response = await service_client.get(
13+
'/echo',
14+
)
15+
assert _handler.times_called == 1
16+
assert response.status_code == 200
17+
18+
19+
@pytest.mark.uservice_oneshot
20+
async def test_await_detached_request(service_client, mockserver):
21+
@mockserver.json_handler('/echo')
22+
async def _handler(request):
23+
assert request.headers['additional-header'] == 'header-value'
24+
await asyncio.sleep(3)
25+
return mockserver.make_response()
26+
27+
response = await service_client.get(
28+
'/echo-detach',
29+
)
30+
assert response.status_code == 200
31+
# without awaiting detached requests in HttpClient there would be segfault at teardown

0 commit comments

Comments
 (0)