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/concurrent/queue.hpp>
11#include <userver/engine/single_consumer_event.hpp>
12#include <userver/http/content_type.hpp>
13#include <userver/http/header_map.hpp>
14#include <userver/server/http/http_response_cookie.hpp>
15#include <userver/server/request/response_base.hpp>
16#include <userver/utils/impl/projecting_view.hpp>
17#include <userver/utils/str_icase.hpp>
18
19#include "http_status.hpp"
20
21USERVER_NAMESPACE_BEGIN
22
23namespace server::http {
24
25// RFC 9110 states that in case of missing Content-Type it may be assumed to be
26// application/octet-stream.
27//
28// text/plain was our first guess, but we should provide an encoding with that
29// type, which we do not know for sure. "application/octet-stream" has no
30// charset https://www.iana.org/assignments/media-types/application/octet-stream
31inline constexpr std::string_view kDefaultContentType = "application/octet-stream";
32
33class Http2ResponseWriter;
34
35namespace impl {
36
37void OutputHeader(USERVER_NAMESPACE::http::headers::HeadersString& header, std::string_view key, std::string_view val);
38
39} // namespace impl
40
41class HttpRequest;
42
43/// @brief HTTP Response data
44class HttpResponse final : public request::ResponseBase {
45public:
46 using HeadersMap = USERVER_NAMESPACE::http::headers::HeaderMap;
47
48 using HeadersMapKeys = decltype(utils::impl::MakeKeysView(HeadersMap()));
49
50 using CookiesMap = Cookie::CookiesMap;
51
52 using CookiesMapKeys = decltype(utils::impl::MakeKeysView(CookiesMap()));
53
54 /// @cond
55 HttpResponse(const HttpRequest& request, request::ResponseDataAccounter& data_accounter);
56 HttpResponse(
57 const HttpRequest& request,
58 request::ResponseDataAccounter& data_accounter,
59 std::chrono::steady_clock::time_point now,
60 utils::StrCaseHash hasher
61 );
62 ~HttpResponse() override;
63
64 void SetSendFailed(std::chrono::steady_clock::time_point failure_time) override;
65 /// @endcond
66
67 /// @brief Add a new response header or rewrite an existing one.
68 /// @returns true if the header was set. Returns false if headers
69 /// were already sent for stream'ed response and the new header was not set,
70 /// or the header overrides system header.
71 bool SetHeader(std::string name, std::string value);
72
73 /// @brief Add a new response header or rewrite an existing one.
74 /// @returns true if the header was set. Returns false if headers
75 /// were already sent for stream'ed response and the new header was not set,
76 /// or the header overrides system header.
77 bool SetHeader(std::string_view name, std::string value);
78
79 /// @overload
80 bool SetHeader(const USERVER_NAMESPACE::http::headers::PredefinedHeader& header, std::string value);
81
82 /// @brief Add or rewrite the Content-Type header.
83 void SetContentType(const USERVER_NAMESPACE::http::ContentType& type);
84
85 /// @brief Add or rewrite the Content-Encoding header.
86 void SetContentEncoding(std::string encoding);
87
88 /// @brief Set the HTTP response status code.
89 /// @returns true if the status was set. Returns false if headers
90 /// were already sent for stream'ed response and the new status was not set.
91 bool SetStatus(HttpStatus status);
92
93 /// @brief Set the end of system headers.
94 /// All headers written before this call are considered system; after - user.
95 /// User headers can't overwrite system headers.
97
98 /// @brief Remove all headers from response, except system headers.
99 /// @returns true if the headers were cleared. Returns false if headers
100 /// were already sent for stream'ed response and the headers were not cleared.
102
103 /// @brief Sets a cookie if it was not set before.
104 void SetCookie(Cookie cookie);
105
106 /// @brief Remove all cookies from response.
108
109 /// @return HTTP response status
110 HttpStatus GetStatus() const { return status_; }
111
112 /// @return List of HTTP system headers names.
113 HeadersMapKeys GetSystemHeaderNames() const;
114
115 /// @return List of HTTP user headers names.
116 HeadersMapKeys GetUserHeaderNames() const;
117
118 /// @return Value of the header with case insensitive name header_name, or an
119 /// empty string if no such header.
120 const std::string& GetHeader(std::string_view header_name) const;
121 /// @overload
122 const std::string& GetHeader(const USERVER_NAMESPACE::http::headers::PredefinedHeader& header_name) const;
123
124 /// @return true if header with case insensitive name header_name exists,
125 /// false otherwise.
126 bool HasHeader(std::string_view header_name) const;
127 /// @overload
128 bool HasHeader(const USERVER_NAMESPACE::http::headers::PredefinedHeader& header_name) const;
129
130 /// @return List of cookies names.
131 CookiesMapKeys GetCookieNames() const;
132
133 /// @return Value of the cookie with case sensitive name cookie_name, or an
134 /// empty string if no such cookie exists.
135 const Cookie& GetCookie(std::string_view cookie_name) const;
136
137 /// @cond
138 // TODO: server internals. remove from public interface
139 void SendResponse(engine::io::RwBase& socket) override;
140 /// @endcond
141
142 void SetStatusServiceUnavailable() override { SetStatus(HttpStatus::kServiceUnavailable); }
143 void SetStatusOk() override { SetStatus(HttpStatus::kOk); }
144 void SetStatusNotFound() override { SetStatus(HttpStatus::kNotFound); }
145
146 bool WaitForHeadersEnd() override;
147 void SetHeadersEnd() override;
148
149 using Queue = concurrent::StringStreamQueue;
150 using Producer = std::variant<std::monostate, Queue::Producer, impl::Http2StreamEventProducer>;
151
152 void SetStreamBody();
153 bool IsBodyStreamed() const override;
154 // Can be called only once
155 Producer GetBodyProducer();
156
157private:
158 friend class Http2ResponseWriter;
159
160 // Returns total size of the response
161 std::size_t SetBodyStreamed(engine::io::RwBase& socket, USERVER_NAMESPACE::http::headers::HeadersString& header);
162
163 // Returns total size of the response
164 std::size_t SetBodyNotStreamed(engine::io::RwBase& socket, USERVER_NAMESPACE::http::headers::HeadersString& header);
165
166 const HttpRequest& request_;
167 HttpStatus status_ = HttpStatus::kOk;
168 HeadersMap system_headers_;
169 HeadersMap user_headers_;
170 CookiesMap cookies_;
171
172 bool system_headers_ended_ = false;
173 engine::SingleConsumerEvent headers_end_{engine::SingleConsumerEvent::NoAutoReset()};
174 std::optional<Queue::Consumer> body_stream_;
175 Producer body_stream_producer_;
176 bool is_stream_body_{false};
177};
178
179void SetThrottleReason(http::HttpResponse& http_response, std::string log_reason, std::string http_header_reason);
180
181} // namespace server::http
182
183USERVER_NAMESPACE_END