userver: userver/server/http/http_response.hpp Source File
Loading...
Searching...
No Matches
http_response.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/server/http/http_response.hpp
4/// @brief @copybrief server::http::HttpResponse
5
6#include <chrono>
7#include <string>
8#include <variant>
9
10#include <userver/compiler/impl/lifetime.hpp>
11#include <userver/concurrent/queue.hpp>
12#include <userver/engine/single_consumer_event.hpp>
13#include <userver/http/content_type.hpp>
14#include <userver/http/header_map.hpp>
15#include <userver/server/http/http_response_cookie.hpp>
16#include <userver/server/request/response_base.hpp>
17#include <userver/utils/impl/projecting_view.hpp>
18#include <userver/utils/str_icase.hpp>
19
20#include "http_status.hpp"
21
22USERVER_NAMESPACE_BEGIN
23
24namespace server::http {
25
26// RFC 9110 states that in case of missing Content-Type it may be assumed to be
27// application/octet-stream.
28//
29// text/plain was our first guess, but we should provide an encoding with that
30// type, which we do not know for sure. "application/octet-stream" has no
31// charset https://www.iana.org/assignments/media-types/application/octet-stream
32inline constexpr std::string_view kDefaultContentType = "application/octet-stream";
33
34class Http2ResponseWriter;
35
36namespace impl {
37
38void OutputHeader(USERVER_NAMESPACE::http::headers::HeadersString& header, std::string_view key, std::string_view val);
39
40/// @brief Event-like helper for waiting until HTTP headers can be sent.
41///
42/// For streamed responses this happens on the first body chunk (or when the
43/// handler finishes). It does not wait for the full response body.
44class HeadersEndEvent final {
45public:
46 explicit HeadersEndEvent(engine::SingleConsumerEvent& event) noexcept : event_(event) {}
47
48 [[nodiscard]] bool IsReady() const noexcept { return event_.IsReady(); }
49
50 /// @brief Satisfies @ref engine::Awaitable, for use with @ref engine::WaitAny.
51 [[nodiscard]] engine::AwaitableToken GetAwaitableToken() noexcept USERVER_IMPL_LIFETIME_BOUND {
52 return event_.GetAwaitableToken();
53 }
54
55 /// @returns whether headers became ready before deadline or cancellation.
56 [[nodiscard]] bool Wait() { return event_.WaitForEvent(); }
57
58private:
59 engine::SingleConsumerEvent& event_;
60};
61
62} // namespace impl
63
64class HttpRequest;
65
66/// @brief HTTP Response data
67class HttpResponse final : public request::ResponseBase {
68public:
69 using HeadersMap = USERVER_NAMESPACE::http::headers::HeaderMap;
70
71 using HeadersMapKeys = decltype(utils::impl::MakeKeysView(HeadersMap()));
72
73 using CookiesMap = Cookie::CookiesMap;
74
75 using CookiesMapKeys = decltype(utils::impl::MakeKeysView(CookiesMap()));
76
77 /// @cond
78 HttpResponse(const HttpRequest& request, request::ResponseDataAccounter& data_accounter);
79 HttpResponse(
80 const HttpRequest& request,
81 request::ResponseDataAccounter& data_accounter,
82 std::chrono::steady_clock::time_point now,
83 utils::StrCaseHash hasher
84 );
85 ~HttpResponse() override;
86
87 void SetSendFailed(std::chrono::steady_clock::time_point failure_time) override;
88 /// @endcond
89
90 /// @brief Add a new response header or rewrite an existing one.
91 /// @returns true if the header was set. Returns false if headers
92 /// were already sent for stream'ed response and the new header was not set,
93 /// or the header overrides system header.
94 bool SetHeader(std::string name, std::string value);
95
96 /// @brief Add a new response header or rewrite an existing one.
97 /// @returns true if the header was set. Returns false if headers
98 /// were already sent for stream'ed response and the new header was not set,
99 /// or the header overrides system header.
100 bool SetHeader(std::string_view name, std::string value);
101
102 /// @overload
103 bool SetHeader(const USERVER_NAMESPACE::http::headers::PredefinedHeader& header, std::string value);
104
105 /// @brief Add or rewrite the Content-Type header.
106 void SetContentType(const USERVER_NAMESPACE::http::ContentType& type);
107
108 /// @brief Add or rewrite the Content-Encoding header.
109 void SetContentEncoding(std::string encoding);
110
111 /// @brief Set the HTTP response status code.
112 /// @returns true if the status was set. Returns false if headers
113 /// were already sent for stream'ed response and the new status was not set.
114 bool SetStatus(HttpStatus status) noexcept;
115
116 /// @brief Set the end of system headers.
117 /// All headers written before this call are considered system; after - user.
118 /// User headers can't overwrite system headers.
120
121 /// @brief Remove all headers from response, except system headers.
122 /// @returns true if the headers were cleared. Returns false if headers
123 /// were already sent for stream'ed response and the headers were not cleared.
125
126 /// @brief Sets a cookie if it was not set before.
127 void SetCookie(Cookie cookie);
128
129 /// @brief Remove all cookies from response.
131
132 /// @return HTTP response status
133 HttpStatus GetStatus() const { return status_; }
134
135 /// @return List of HTTP system headers names.
136 HeadersMapKeys GetSystemHeaderNames() const;
137
138 /// @return List of HTTP user headers names.
139 HeadersMapKeys GetUserHeaderNames() const;
140
141 /// @return Value of the header with case insensitive name header_name, or an
142 /// empty string if no such header.
143 const std::string& GetHeader(std::string_view header_name) const;
144 /// @overload
145 const std::string& GetHeader(const USERVER_NAMESPACE::http::headers::PredefinedHeader& header_name) const;
146
147 /// @return true if header with case insensitive name header_name exists,
148 /// false otherwise.
149 bool HasHeader(std::string_view header_name) const;
150 /// @overload
151 bool HasHeader(const USERVER_NAMESPACE::http::headers::PredefinedHeader& header_name) const;
152
153 /// @return List of cookies names.
154 CookiesMapKeys GetCookieNames() const;
155
156 /// @return Value of the cookie with case sensitive name cookie_name, or an
157 /// empty string if no such cookie exists.
158 const Cookie& GetCookie(std::string_view cookie_name) const;
159
160 /// @cond
161 // TODO: server internals. remove from public interface
162
163 [[nodiscard]] impl::HeadersEndEvent FinishedSendingHeadersEvent() noexcept USERVER_IMPL_LIFETIME_BOUND {
164 return impl::HeadersEndEvent{headers_end_};
165 }
166
167 void SendResponse(engine::io::RwBase& socket) override;
168 /// @endcond
169
170 void SetStatusServiceUnavailable() override { SetStatus(HttpStatus::kServiceUnavailable); }
171 void SetStatusOk() override { SetStatus(HttpStatus::kOk); }
172 void SetStatusNotFound() override { SetStatus(HttpStatus::kNotFound); }
173
174 bool WaitForHeadersEnd() override;
175 void SetHeadersEnd() override;
176
177 /// @returns true if response headers were committed and cannot be changed.
178 [[nodiscard]] bool IsHeadersEnd() const noexcept { return headers_end_.IsReady(); }
179
180 using Queue = concurrent::StringStreamQueue;
181 using Producer = std::variant<std::monostate, Queue::Producer, impl::Http2StreamEventProducer>;
182
183 void SetStreamBody();
184 bool IsBodyStreamed() const override;
185 // Can be called only once
186 Producer GetBodyProducer();
187
188private:
189 friend class Http2ResponseWriter;
190
191 // Returns total size of the response
192 std::size_t SetBodyStreamed(engine::io::RwBase& socket, USERVER_NAMESPACE::http::headers::HeadersString& header);
193
194 // Returns total size of the response
195 std::size_t SetBodyNotStreamed(engine::io::RwBase& socket, USERVER_NAMESPACE::http::headers::HeadersString& header);
196
197 const HttpRequest& request_;
198 HttpStatus status_ = HttpStatus::kOk;
199 HeadersMap system_headers_;
200 HeadersMap user_headers_;
201 CookiesMap cookies_;
202
203 bool system_headers_ended_ = false;
204 engine::SingleConsumerEvent headers_end_{engine::SingleConsumerEvent::NoAutoReset()};
205 std::optional<Queue::Consumer> body_stream_;
206 Producer body_stream_producer_;
207 bool is_stream_body_{false};
208};
209
210void SetThrottleReason(http::HttpResponse& http_response, std::string log_reason, std::string http_header_reason);
211
212} // namespace server::http
213
214USERVER_NAMESPACE_END