Skip to content

Commit

Permalink
feat server: allow request body moving out
Browse files Browse the repository at this point in the history
commit_hash:86bcb7d10acf464a468c10b2c04916403585eda0
  • Loading branch information
segoon committed Nov 29, 2024
1 parent dfb20c9 commit d1356ef
Show file tree
Hide file tree
Showing 34 changed files with 64 additions and 72 deletions.
2 changes: 1 addition & 1 deletion core/functional_tests/basic_chaos/httpclient_handlers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class StreamHandler : public server::handlers::HttpHandlerBase {

/// [HandleStreamRequest]
void HandleStreamRequest(
const server::http::HttpRequest& request,
server::http::HttpRequest& request,
server::request::RequestContext&,
server::http::ResponseBodyStream& response_body_stream
) const override {
Expand Down
2 changes: 1 addition & 1 deletion core/functional_tests/http2server/service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class HandlerHttp2 final : public server::handlers::HttpHandlerBase {
};

void HandleStreamRequest(
const server::http::HttpRequest& req,
server::http::HttpRequest& req,
server::request::RequestContext&,
server::http::ResponseBodyStream& stream
) const override {
Expand Down
2 changes: 1 addition & 1 deletion core/include/userver/server/handlers/handler_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class HandlerBase : public components::ComponentBase {

/// Parses request, executes processing routines, and fills response
/// accordingly. Does not throw.
virtual void HandleRequest(http::HttpRequest& request, request::RequestContext& context) const = 0;
virtual void PrepareAndHandleRequest(http::HttpRequest& request, request::RequestContext& context) const = 0;

/// Produces response to a request unrecognized by the protocol based on
/// provided generic response. Does not throw.
Expand Down
15 changes: 9 additions & 6 deletions core/include/userver/server/handlers/http_handler_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class HttpHandlerBase : public HandlerBase {

~HttpHandlerBase() override;

void HandleRequest(http::HttpRequest& request, request::RequestContext& context) const override;
void PrepareAndHandleRequest(http::HttpRequest& request, request::RequestContext& context) const override;

void ReportMalformedRequest(http::HttpRequest& request) const final;

Expand Down Expand Up @@ -119,11 +119,14 @@ class HttpHandlerBase : public HandlerBase {
protected:
[[noreturn]] void ThrowUnsupportedHttpMethod(const http::HttpRequest& request) const;

/// Same as `HandleRequest`.
virtual std::string HandleRequestThrow(const http::HttpRequest& request, request::RequestContext& context) const;

/// The core method for HTTP request handling.
/// `request` arg contains HTTP headers, full body, etc.
/// The method should return response body.
/// @note It is used only if IsStreamed() returned `false`.
virtual std::string HandleRequestThrow(const http::HttpRequest& request, request::RequestContext& context) const;
virtual std::string HandleRequest(http::HttpRequest& request, request::RequestContext& context) const;

/// The core method for HTTP request handling.
/// `request` arg contains HTTP headers, full body, etc.
Expand All @@ -137,12 +140,12 @@ class HttpHandlerBase : public HandlerBase {
/// in memory.
/// @note It is used only if IsStreamed() returned `true`.
virtual void
HandleStreamRequest(const server::http::HttpRequest&, server::request::RequestContext&, server::http::ResponseBodyStream&)
HandleStreamRequest(server::http::HttpRequest&, server::request::RequestContext&, server::http::ResponseBodyStream&)
const;

/// If IsStreamed() returns `true`, call HandleStreamRequest()
/// for request handling, HandleRequestThrow() is not called.
/// If it returns `false`, HandleRequestThrow() is called instead,
/// for request handling, HandleRequest() is not called.
/// If it returns `false`, HandleRequest() is called instead,
/// and HandleStreamRequest() is not called.
/// @note The default implementation returns the cached value of
/// "response-body-streamed" value from static config.
Expand Down Expand Up @@ -182,7 +185,7 @@ class HttpHandlerBase : public HandlerBase {

void HandleHttpRequest(http::HttpRequest& request, request::RequestContext& context) const;

void HandleRequestStream(const http::HttpRequest& http_request, request::RequestContext& context) const;
void HandleRequestStream(http::HttpRequest& http_request, request::RequestContext& context) const;

std::string GetRequestBodyForLoggingChecked(
const http::HttpRequest& request,
Expand Down
3 changes: 3 additions & 0 deletions core/include/userver/server/http/http_request.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ class HttpRequest final {
/// @return HTTP body.
const std::string& RequestBody() const;

/// @return moved out HTTP body. `this` is modified.
std::string ExtractRequestBody();

/// @cond
void SetRequestBody(std::string body);
void ParseArgsFromBody();
Expand Down
18 changes: 11 additions & 7 deletions core/src/server/handlers/http_handler_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,7 @@ HttpHandlerBase::HttpHandlerBase(

HttpHandlerBase::~HttpHandlerBase() { statistics_holder_.Unregister(); }

void HttpHandlerBase::HandleRequestStream(const http::HttpRequest& http_request, request::RequestContext& context)
const {
void HttpHandlerBase::HandleRequestStream(http::HttpRequest& http_request, request::RequestContext& context) const {
auto& response = http_request.GetHttpResponse();
const utils::ScopeGuard scope([&response] { response.SetHeadersEnd(); });

Expand Down Expand Up @@ -254,11 +253,11 @@ void HttpHandlerBase::HandleHttpRequest(http::HttpRequest& http_request, request
HandleRequestStream(http_request, context);
} else {
// !IsBodyStreamed()
response.SetData(HandleRequestThrow(http_request, context));
response.SetData(HandleRequest(http_request, context));
}
}

void HttpHandlerBase::HandleRequest(http::HttpRequest& http_request, request::RequestContext& context) const {
void HttpHandlerBase::PrepareAndHandleRequest(http::HttpRequest& http_request, request::RequestContext& context) const {
auto& response = http_request.GetHttpResponse();

context.GetInternalContext().SetConfigSnapshot(config_source_.GetSnapshot());
Expand Down Expand Up @@ -288,13 +287,18 @@ void HttpHandlerBase::ThrowUnsupportedHttpMethod(const http::HttpRequest& reques

std::string HttpHandlerBase::HandleRequestThrow(const http::HttpRequest&, request::RequestContext&) const {
throw std::runtime_error(
"non-stream HandleRequestThrow() is executed, but the handler doesn't "
"override HandleRequestThrow()."
"non-stream HandleRequest() is executed, but the handler doesn't "
"override HandleRequest()."
);
}

std::string HttpHandlerBase::HandleRequest(http::HttpRequest& request, request::RequestContext& context) const {
// Default implementation proxies the request to legacy HandleRequestThrow()
return HandleRequestThrow(request, context);
}

void HttpHandlerBase::
HandleStreamRequest(const server::http::HttpRequest&, server::request::RequestContext&, server::http::ResponseBodyStream&)
HandleStreamRequest(server::http::HttpRequest&, server::request::RequestContext&, server::http::ResponseBodyStream&)
const {
throw std::runtime_error(
"stream HandleStreamRequest() is executed, but the handler doesn't "
Expand Down
2 changes: 2 additions & 0 deletions core/src/server/http/http_request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ const HttpRequest::CookiesMap& HttpRequest::RequestCookies() const { return pimp

const std::string& HttpRequest::RequestBody() const { return pimpl_->request_body_; }

std::string HttpRequest::ExtractRequestBody() { return std::move(pimpl_->request_body_); }

void HttpRequest::SetRequestBody(std::string body) { pimpl_->request_body_ = std::move(body); }

void HttpRequest::ParseArgsFromBody() {
Expand Down
2 changes: 1 addition & 1 deletion core/src/server/http/http_request_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ engine::TaskWithResult<void> HttpRequestHandler::StartRequestTask(std::shared_pt
request->SetTaskStartTime();

request::RequestContext context;
handler->HandleRequest(*request, context);
handler->PrepareAndHandleRequest(*request, context);

const auto now = std::chrono::steady_clock::now();
request->SetResponseNotifyTime(now);
Expand Down
3 changes: 1 addition & 2 deletions samples/chaotic_service/src/hello_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ class Hello final : public server::handlers::HttpHandlerBase {
using HttpHandlerBase::HttpHandlerBase;

/// [Handler]
std::string HandleRequestThrow(const server::http::HttpRequest& request, server::request::RequestContext&)
const override {
std::string HandleRequest(server::http::HttpRequest& request, server::request::RequestContext&) const override {
request.GetHttpResponse().SetContentType(http::content_type::kApplicationJson);

auto request_json = formats::json::FromString(request.RequestBody());
Expand Down
5 changes: 2 additions & 3 deletions samples/clickhouse_service/clickhouse_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class HandlerDb final : public server::handlers::HttpHandlerBase {

HandlerDb(const components::ComponentConfig& config, const components::ComponentContext& context);

std::string HandleRequestThrow(const server::http::HttpRequest& request, server::request::RequestContext& context)
std::string HandleRequest(server::http::HttpRequest& request, server::request::RequestContext& context)
const override;

private:
Expand All @@ -45,8 +45,7 @@ HandlerDb::HandlerDb(const components::ComponentConfig& config, const components
: server::handlers::HttpHandlerBase{config, context},
clickhouse_{context.FindComponent<components::ClickHouse>("clickhouse-database").GetCluster()} {}

std::string HandlerDb::HandleRequestThrow(const server::http::HttpRequest& request, server::request::RequestContext&)
const {
std::string HandlerDb::HandleRequest(server::http::HttpRequest& request, server::request::RequestContext&) const {
const auto& limit = request.GetArg("limit");
// FP?: pfr magic
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
Expand Down
2 changes: 1 addition & 1 deletion samples/digest_auth_service/digest_auth_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class Hello final : public server::handlers::HttpHandlerBase {

using HttpHandlerBase::HttpHandlerBase;

std::string HandleRequestThrow(const server::http::HttpRequest&, server::request::RequestContext&) const override {
std::string HandleRequest(server::http::HttpRequest&, server::request::RequestContext&) const override {
return "Hello world";
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ GreeterHttpHandler::GreeterHttpHandler(
)
: HttpHandlerBase(config, context), grpc_greeter_client_(context.FindComponent<GreeterClient>()) {}

std::string
GreeterHttpHandler::HandleRequestThrow(const server::http::HttpRequest& request, server::request::RequestContext&)
std::string GreeterHttpHandler::HandleRequest(server::http::HttpRequest& request, server::request::RequestContext&)
const {
return grpc_greeter_client_.SayHello(request.RequestBody());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ class GreeterHttpHandler final : public server::handlers::HttpHandlerBase {

GreeterHttpHandler(const components::ComponentConfig& config, const components::ComponentContext& context);

std::string HandleRequestThrow(const server::http::HttpRequest& request, server::request::RequestContext&)
const override;
std::string HandleRequest(server::http::HttpRequest& request, server::request::RequestContext&) const override;

private:
GreeterClient& grpc_greeter_client_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ class CallGreeterClientTestHandler final : public server::handlers::HttpHandlerB
: HttpHandlerBase(config, context),
grpc_greeter_client_(context.FindComponent<GreeterClientComponent>().GetClient()) {}

std::string HandleRequestThrow(const server::http::HttpRequest& request, server::request::RequestContext&)
const override {
std::string HandleRequest(server::http::HttpRequest& request, server::request::RequestContext&) const override {
const auto& arg_case = request.GetArg("case");
request.GetHttpResponse().SetContentType(http::content_type::kTextPlain);

Expand Down
6 changes: 3 additions & 3 deletions samples/hello_service/src/hello_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

namespace samples::hello {

std::string HelloHandler::
HandleRequestThrow(const server::http::HttpRequest& request, server::request::RequestContext& /*request_context*/)
const {
std::string
HelloHandler::HandleRequest(server::http::HttpRequest& request, server::request::RequestContext& /*request_context*/)
const {
// Setting Content-Type: text/plain in a microservice response ensures
// the client interprets it as plain text, preventing misinterpretation or
// errors. Without this header, the client might assume a different format,
Expand Down
3 changes: 1 addition & 2 deletions samples/hello_service/src/hello_handler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ class HelloHandler final : public server::handlers::HttpHandlerBase {
// Component is valid after construction and is able to accept requests
using HttpHandlerBase::HttpHandlerBase;

std::string HandleRequestThrow(const server::http::HttpRequest& request, server::request::RequestContext&)
const override;
std::string HandleRequest(server::http::HttpRequest& request, server::request::RequestContext&) const override;
};

} // namespace samples::hello
3 changes: 1 addition & 2 deletions samples/http_caching/http_caching.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,7 @@ class GreetUser final : public server::handlers::HttpHandlerBase {
GreetUser(const components::ComponentConfig& config, const components::ComponentContext& context)
: HttpHandlerBase(config, context), cache_(context.FindComponent<HttpCachedTranslations>()) {}

std::string HandleRequestThrow(const server::http::HttpRequest& request, server::request::RequestContext&)
const override {
std::string HandleRequest(server::http::HttpRequest& request, server::request::RequestContext&) const override {
const auto cache_snapshot = cache_.Get();

using samples::http_cache::KeyLang;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class Handler final : public server::handlers::HttpHandlerBase {
public:
using HttpHandlerBase::HttpHandlerBase;

std::string HandleRequestThrow(const server::http::HttpRequest&, server::request::RequestContext&) const override {
std::string HandleRequest(server::http::HttpRequest&, server::request::RequestContext&) const override {
return "Hello world!\n";
}
};
Expand Down
3 changes: 1 addition & 2 deletions samples/https_service/hello_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ class Hello final : public server::handlers::HttpHandlerBase {
// Component is valid after construction and is able to accept requests
using HttpHandlerBase::HttpHandlerBase;

std::string HandleRequestThrow(const server::http::HttpRequest& request, server::request::RequestContext&)
const override {
std::string HandleRequest(server::http::HttpRequest& request, server::request::RequestContext&) const override {
request.GetHttpResponse().SetContentType(http::content_type::kTextPlain);
return "Hello world!\n";
}
Expand Down
3 changes: 1 addition & 2 deletions samples/mongo_service/mongo_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ class Translations final : public server::handlers::HttpHandlerBase {
Translations(const components::ComponentConfig& config, const components::ComponentContext& context)
: HttpHandlerBase(config, context), pool_(context.FindComponent<components::Mongo>("mongo-tr").GetPool()) {}

std::string HandleRequestThrow(const server::http::HttpRequest& request, server::request::RequestContext&)
const override {
std::string HandleRequest(server::http::HttpRequest& request, server::request::RequestContext&) const override {
request.GetHttpResponse().SetContentType(http::content_type::kApplicationJson);
if (request.GetMethod() == server::http::HttpMethod::kPatch) {
InsertNew(request);
Expand Down
6 changes: 2 additions & 4 deletions samples/multipart_service/service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,12 @@ class Multipart final : public server::handlers::HttpHandlerBase {
// Component is valid after construction and is able to accept requests
using HttpHandlerBase::HttpHandlerBase;

std::string HandleRequestThrow(const server::http::HttpRequest& req, server::request::RequestContext&)
const override;
std::string HandleRequest(server::http::HttpRequest& req, server::request::RequestContext&) const override;
};
/// [Multipart service sample - component]

/// [Multipart service sample - HandleRequestThrow]
std::string Multipart::HandleRequestThrow(const server::http::HttpRequest& req, server::request::RequestContext&)
const {
std::string Multipart::HandleRequest(server::http::HttpRequest& req, server::request::RequestContext&) const {
const auto content_type = http::ContentType(req.GetHeader(http::headers::kContentType));
if (content_type != "multipart/form-data") {
req.GetHttpResponse().SetStatus(server::http::HttpStatus::kBadRequest);
Expand Down
3 changes: 1 addition & 2 deletions samples/postgres_auth/postgres_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ class Hello final : public server::handlers::HttpHandlerBase {

using HttpHandlerBase::HttpHandlerBase;

std::string HandleRequestThrow(const server::http::HttpRequest& request, server::request::RequestContext& ctx)
const override {
std::string HandleRequest(server::http::HttpRequest& request, server::request::RequestContext& ctx) const override {
request.GetHttpResponse().SetContentType(http::content_type::kTextPlain);
return "Hello world, " + ctx.GetData<std::string>("name") + "!\n";
}
Expand Down
6 changes: 2 additions & 4 deletions samples/postgres_service/postgres_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ class KeyValue final : public server::handlers::HttpHandlerBase {

KeyValue(const components::ComponentConfig& config, const components::ComponentContext& context);

std::string HandleRequestThrow(const server::http::HttpRequest& request, server::request::RequestContext&)
const override;
std::string HandleRequest(server::http::HttpRequest& request, server::request::RequestContext&) const override;

private:
std::string GetValue(std::string_view key, const server::http::HttpRequest& request) const;
Expand All @@ -48,8 +47,7 @@ KeyValue::KeyValue(const components::ComponentConfig& config, const components::
/// [Postgres service sample - component constructor]

/// [Postgres service sample - HandleRequestThrow]
std::string KeyValue::HandleRequestThrow(const server::http::HttpRequest& request, server::request::RequestContext&)
const {
std::string KeyValue::HandleRequest(server::http::HttpRequest& request, server::request::RequestContext&) const {
const auto& key = request.GetArg("key");
if (key.empty()) {
throw server::handlers::ClientError(server::handlers::ExternalBody{"No 'key' query argument"});
Expand Down
12 changes: 4 additions & 8 deletions samples/redis_service/redis_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ class EvalSha final : public server::handlers::HttpHandlerBase {

EvalSha(const components::ComponentConfig& config, const components::ComponentContext& context);

std::string HandleRequestThrow(const server::http::HttpRequest& request, server::request::RequestContext&)
const override;
std::string HandleRequest(server::http::HttpRequest& request, server::request::RequestContext&) const override;

private:
std::string EvalShaRequest(const server::http::HttpRequest& request) const;
Expand All @@ -40,8 +39,7 @@ class KeyValue final : public server::handlers::HttpHandlerBase {

KeyValue(const components::ComponentConfig& config, const components::ComponentContext& context);

std::string HandleRequestThrow(const server::http::HttpRequest& request, server::request::RequestContext&)
const override;
std::string HandleRequest(server::http::HttpRequest& request, server::request::RequestContext&) const override;

private:
std::string GetValue(std::string_view key, const server::http::HttpRequest& request) const;
Expand All @@ -65,8 +63,7 @@ KeyValue::KeyValue(const components::ComponentConfig& config, const components::
/// [Redis service sample - component constructor]

/// [Redis service sample - HandleRequestThrow]
std::string
KeyValue::HandleRequestThrow(const server::http::HttpRequest& request, server::request::RequestContext& /*context*/)
std::string KeyValue::HandleRequest(server::http::HttpRequest& request, server::request::RequestContext& /*context*/)
const {
const auto& key = request.GetArg("key");
if (key.empty()) {
Expand Down Expand Up @@ -124,8 +121,7 @@ EvalSha::EvalSha(const components::ComponentConfig& config, const components::Co
: server::handlers::HttpHandlerBase(config, context),
redis_client_{context.FindComponent<components::Redis>("key-value-database").GetClient("taxi-tmp")} {}

std::string EvalSha::HandleRequestThrow(const server::http::HttpRequest& request, server::request::RequestContext&)
const {
std::string EvalSha::HandleRequest(server::http::HttpRequest& request, server::request::RequestContext&) const {
const auto& command = request.GetArg("command");
request.GetHttpResponse().SetContentType(http::content_type::kTextPlain);

Expand Down
4 changes: 2 additions & 2 deletions samples/testsuite-support/src/dynamic_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ DynamicConfig::DynamicConfig(const components::ComponentConfig& config, const co
: server::handlers::HttpHandlerBase(config, context),
config_source_(context.FindComponent<components::DynamicConfig>().GetSource()) {}

std::string DynamicConfig::HandleRequestThrow(
[[maybe_unused]] const server::http::HttpRequest& request,
std::string DynamicConfig::HandleRequest(
[[maybe_unused]] server::http::HttpRequest& request,
[[maybe_unused]] server::request::RequestContext& context
) const {
request.GetHttpResponse().SetContentType(http::content_type::kTextPlain);
Expand Down
Loading

0 comments on commit d1356ef

Please sign in to comment.