Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/functional_tests/http2server/static_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ components_manager:

handler-http2:
path: /http2server
method: GET,POST,PUT,DELETE,HEAD
method: GET,POST,PUT,DELETE,HEAD,TRACE
task_processor: main-task-processor
throttling_enabled: false
max_request_size: 2097152 # 2Mib
Expand Down
19 changes: 19 additions & 0 deletions core/functional_tests/http2server/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,25 @@ async def head(
timeout,
)

async def trace(
self,
path,
params={},
headers={},
data=None,
json={},
timeout=DEFAULT_TIMEOUT,
) -> httpx.Response:
return await self._request(
'TRACE',
path,
params,
headers,
data,
json,
timeout,
)

async def _request(
self,
method,
Expand Down
13 changes: 13 additions & 0 deletions core/functional_tests/http2server/tests/test_high_level.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,19 @@ async def test_headers(http2_client):
assert hval == r.text


async def test_trace_is_routed(http2_client):
# TRACE arrives as a ':method' pseudo-header, so unlike HTTP/1.1 it never
# goes through llhttp; the handler must be reached over HTTP/2 as well.
hval = 'traced'
r = await http2_client.trace(
DEFAULT_PATH,
params={'type': 'echo-header'},
headers={'echo-header': hval},
)
assert 200 == r.status_code
assert hval == r.text


async def test_head_response_has_no_body(http2_client):
r = await http2_client.head(
DEFAULT_PATH,
Expand Down
2 changes: 1 addition & 1 deletion core/include/userver/clients/http/request.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class EasyWrapper;
} // namespace impl

/// @brief HTTP request method
enum class HttpMethod { kGet, kPost, kHead, kPut, kDelete, kPatch, kOptions };
enum class HttpMethod { kGet, kPost, kHead, kPut, kDelete, kPatch, kOptions, kTrace };

/// @brief Convert HTTP method enum value to string
std::string_view ToStringView(HttpMethod method);
Expand Down
1 change: 1 addition & 0 deletions core/include/userver/server/http/http_method.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ enum class HttpMethod {
kPatch,
kConnect,
kOptions,
kTrace,
kUnknown,
};

Expand Down
2 changes: 2 additions & 0 deletions core/src/clients/http/http_method_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ UTEST(ClientHttpMethodTest, Convert) {
EXPECT_EQ(ch::ToStringView(ch::HttpMethod::kPut), "PUT");
EXPECT_EQ(ch::ToStringView(ch::HttpMethod::kDelete), "DELETE");
EXPECT_EQ(ch::ToStringView(ch::HttpMethod::kOptions), "OPTIONS");
EXPECT_EQ(ch::ToStringView(ch::HttpMethod::kTrace), "TRACE");

EXPECT_EQ(ch::HttpMethodFromString("GET"), ch::HttpMethod::kGet);
EXPECT_EQ(ch::HttpMethodFromString("HEAD"), ch::HttpMethod::kHead);
Expand All @@ -25,6 +26,7 @@ UTEST(ClientHttpMethodTest, Convert) {
EXPECT_EQ(ch::HttpMethodFromString("PUT"), ch::HttpMethod::kPut);
EXPECT_EQ(ch::HttpMethodFromString("DELETE"), ch::HttpMethod::kDelete);
EXPECT_EQ(ch::HttpMethodFromString("OPTIONS"), ch::HttpMethod::kOptions);
EXPECT_EQ(ch::HttpMethodFromString("TRACE"), ch::HttpMethod::kTrace);

UEXPECT_THROW(ch::HttpMethodFromString("123"), std::runtime_error);
}
Expand Down
3 changes: 2 additions & 1 deletion core/src/clients/http/request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ constexpr utils::TrivialBiMap kHttpMethodMap([](auto selector) {
.Case(HttpMethod::kPut, "PUT")
.Case(HttpMethod::kPatch, "PATCH")
.Case(HttpMethod::kDelete, "DELETE")
.Case(HttpMethod::kOptions, "OPTIONS");
.Case(HttpMethod::kOptions, "OPTIONS")
.Case(HttpMethod::kTrace, "TRACE");
});

curl::easy::http_version_t ToNative(HttpVersion version) {
Expand Down
1 change: 1 addition & 0 deletions core/src/clients/http/request_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,7 @@ void RequestState::SetMethod(HttpMethod method) {
switch (method) {
case HttpMethod::kDelete:
case HttpMethod::kOptions:
case HttpMethod::kTrace:
easy().set_custom_request(ToString(method));
break;
case HttpMethod::kGet:
Expand Down
1 change: 1 addition & 0 deletions core/src/server/http/handler_methods.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ inline constexpr HttpMethod kHandlerMethods[] = {
HttpMethod::kDelete,
HttpMethod::kPatch,
HttpMethod::kOptions,
HttpMethod::kTrace,
HttpMethod::kUnknown
};

Expand Down
8 changes: 8 additions & 0 deletions core/src/server/http/http_method.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ struct HttpMethodStrings {
const std::string kConnect = "CONNECT";
const std::string kPatch = "PATCH";
const std::string kOptions = "OPTIONS";
const std::string kTrace = "TRACE";
const std::string kUnknown = "unknown";
};
// NOLINTEND(readability-identifier-naming)
Expand Down Expand Up @@ -81,6 +82,11 @@ HttpMethod HttpMethodFromString(std::string_view method_str) {
result = HttpMethod::kOptions;
}
break;
case 'T':
if (method_str == strings.kTrace) {
result = HttpMethod::kTrace;
}
break;
}
}

Expand Down Expand Up @@ -116,6 +122,8 @@ const std::string& ToString(HttpMethod method) noexcept {
return strings.kPatch;
case HttpMethod::kOptions:
return strings.kOptions;
case HttpMethod::kTrace:
return strings.kTrace;
case HttpMethod::kUnknown:
return strings.kUnknown;
}
Expand Down
51 changes: 51 additions & 0 deletions core/src/server/http/http_method_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include <userver/server/http/http_method.hpp>

#include <userver/utest/utest.hpp>

#include <server/http/handler_methods.hpp>

USERVER_NAMESPACE_BEGIN

namespace {
namespace sh = server::http;
}

UTEST(ServerHttpMethodTest, ToString) {
EXPECT_EQ(sh::ToString(sh::HttpMethod::kDelete), "DELETE");
EXPECT_EQ(sh::ToString(sh::HttpMethod::kGet), "GET");
EXPECT_EQ(sh::ToString(sh::HttpMethod::kHead), "HEAD");
EXPECT_EQ(sh::ToString(sh::HttpMethod::kPost), "POST");
EXPECT_EQ(sh::ToString(sh::HttpMethod::kPut), "PUT");
EXPECT_EQ(sh::ToString(sh::HttpMethod::kPatch), "PATCH");
EXPECT_EQ(sh::ToString(sh::HttpMethod::kConnect), "CONNECT");
EXPECT_EQ(sh::ToString(sh::HttpMethod::kOptions), "OPTIONS");
EXPECT_EQ(sh::ToString(sh::HttpMethod::kTrace), "TRACE");
}

UTEST(ServerHttpMethodTest, FromString) {
EXPECT_EQ(sh::HttpMethodFromString("DELETE"), sh::HttpMethod::kDelete);
EXPECT_EQ(sh::HttpMethodFromString("GET"), sh::HttpMethod::kGet);
EXPECT_EQ(sh::HttpMethodFromString("HEAD"), sh::HttpMethod::kHead);
EXPECT_EQ(sh::HttpMethodFromString("POST"), sh::HttpMethod::kPost);
EXPECT_EQ(sh::HttpMethodFromString("PUT"), sh::HttpMethod::kPut);
EXPECT_EQ(sh::HttpMethodFromString("PATCH"), sh::HttpMethod::kPatch);
EXPECT_EQ(sh::HttpMethodFromString("CONNECT"), sh::HttpMethod::kConnect);
EXPECT_EQ(sh::HttpMethodFromString("OPTIONS"), sh::HttpMethod::kOptions);
EXPECT_EQ(sh::HttpMethodFromString("TRACE"), sh::HttpMethod::kTrace);

UEXPECT_THROW(sh::HttpMethodFromString("TRAC"), std::runtime_error);
UEXPECT_THROW(sh::HttpMethodFromString("TRACER"), std::runtime_error);
UEXPECT_THROW(sh::HttpMethodFromString("trace"), std::runtime_error);
}

// TRACE must be registrable in a handler `method:` list, unlike CONNECT, which
// userver never routes to a handler.
UTEST(ServerHttpMethodTest, IsHandlerMethod) {
EXPECT_TRUE(sh::IsHandlerMethod(sh::HttpMethod::kGet));
EXPECT_TRUE(sh::IsHandlerMethod(sh::HttpMethod::kOptions));
EXPECT_TRUE(sh::IsHandlerMethod(sh::HttpMethod::kTrace));

EXPECT_FALSE(sh::IsHandlerMethod(sh::HttpMethod::kConnect));
}

USERVER_NAMESPACE_END
1 change: 1 addition & 0 deletions core/src/server/http/http_request_method_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ INSTANTIATE_UTEST_SUITE_P(
MethodsData{"CONNECT", HttpMethod::kConnect},
MethodsData{"PATCH", HttpMethod::kPatch},
MethodsData{"OPTIONS", HttpMethod::kOptions},
MethodsData{"TRACE", HttpMethod::kTrace},
MethodsData{"GE", HttpMethod::kUnknown},
MethodsData{"GETT", HttpMethod::kUnknown},
MethodsData{"get", HttpMethod::kUnknown},
Expand Down
2 changes: 2 additions & 0 deletions core/src/server/http/http_request_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ HttpMethod ConvertHttpMethod(llhttp_method method) {
return HttpMethod::kPatch;
case HTTP_OPTIONS:
return HttpMethod::kOptions;
case HTTP_TRACE:
return HttpMethod::kTrace;
default:
return HttpMethod::kUnknown;
}
Expand Down
2 changes: 2 additions & 0 deletions core/utest/src/utest/http_server_mock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ clients::http::HttpMethod ConvertHttpMethod(llhttp_method method) {
return clients::http::HttpMethod::kPatch;
case HTTP_OPTIONS:
return clients::http::HttpMethod::kOptions;
case HTTP_TRACE:
return clients::http::HttpMethod::kTrace;
default:
ADD_FAILURE() << "Unknown HTTP method " << method;
return clients::http::HttpMethod::kGet;
Expand Down
Loading