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 <limits>
8#include <memory>
9#include <string>
10#include <string_view>
11#include <variant>
12
13#include <userver/engine/single_consumer_event.hpp>
14#include <userver/http/content_type.hpp>
15#include <userver/http/header_map.hpp>
16#include <userver/server/http/http_response_cookie.hpp>
17#include <userver/utils/impl/projecting_view.hpp>
18#include <userver/utils/str_icase.hpp>
19
20#include "http_status.hpp"
21
22// TODO remove extra include.
23#include <userver/compiler/impl/lifetime.hpp>
24#include <userver/concurrent/queue.hpp>
25#include <userver/concurrent/striped_counter.hpp>
26#include <userver/utils/fast_pimpl.hpp>
27
28USERVER_NAMESPACE_BEGIN
29
30namespace server::request {
31class ResponseDataAccounter;
32} // namespace server::request
33
34namespace server::http {
35
36// RFC 9110 states that in case of missing Content-Type it may be assumed to be
37// application/octet-stream.
38//
39// text/plain was our first guess, but we should provide an encoding with that
40// type, which we do not know for sure. "application/octet-stream" has no
41// charset https://www.iana.org/assignments/media-types/application/octet-stream
42inline constexpr std::string_view kDefaultContentType = "application/octet-stream";
43
44class Http2ResponseWriter;
45class HttpResponseImpl;
46
47namespace impl {
48
49class ChunkStorage final {
50public:
51 ChunkStorage() = default;
52 explicit ChunkStorage(std::string data);
53 explicit ChunkStorage(std::shared_ptr<const std::string> data);
54
55 ChunkStorage(const ChunkStorage&) = delete;
56 ChunkStorage(ChunkStorage&&) noexcept = default;
57 ChunkStorage& operator=(const ChunkStorage&) = delete;
58 ChunkStorage& operator=(ChunkStorage&&) noexcept = default;
59
60 bool Empty() const noexcept;
61 std::size_t Size() const noexcept;
62 std::string_view View() const noexcept;
63 const std::string& AsString() const;
64
65private:
66 std::variant<std::string, std::shared_ptr<const std::string>> storage_{};
67};
68
69} // namespace impl
70
71class HttpRequest;
72
73/// @brief HTTP Response data
75public:
76 HttpResponse(const HttpResponse&) = delete;
77 HttpResponse(HttpResponse&&) = delete;
78
79 using HeadersMap = USERVER_NAMESPACE::http::headers::HeaderMap;
80
81 using HeadersMapKeys = decltype(utils::impl::MakeKeysView(HeadersMap()));
82
83 using CookiesMap = Cookie::CookiesMap;
84
85 using CookiesMapKeys = decltype(utils::impl::MakeKeysView(CookiesMap()));
86
87 void SetData(std::string data);
88 /// @brief Sets response body without copying, keeping @a data alive.
89 /// Useful for serving cached static content.
90 void SetSharedData(std::shared_ptr<const std::string> data);
91 const std::string& GetData() const;
92
93 /// @brief Add a new response header or rewrite an existing one.
94 /// @returns true if the header was set. Returns false if headers
95 /// were already sent for stream'ed response and the new header was not set,
96 /// or the header overrides system header.
97 bool SetHeader(std::string name, std::string value);
98
99 /// @brief Add a new response header or rewrite an existing one.
100 /// @returns true if the header was set. Returns false if headers
101 /// were already sent for stream'ed response and the new header was not set,
102 /// or the header overrides system header.
103 bool SetHeader(std::string_view name, std::string value);
104
105 /// @overload
106 bool SetHeader(const USERVER_NAMESPACE::http::headers::PredefinedHeader& header, std::string value);
107
108 /// @brief Add or rewrite the Content-Type header.
109 void SetContentType(const USERVER_NAMESPACE::http::ContentType& type);
110
111 /// @brief Add or rewrite the Content-Encoding header.
112 void SetContentEncoding(std::string encoding);
113
114 /// @brief Set the HTTP response status code.
115 /// @returns true if the status was set. Returns false if headers
116 /// were already sent for stream'ed response and the new status was not set.
117 bool SetStatus(HttpStatus status) noexcept;
118
119 /// @brief Remove all headers from response, except system headers.
120 /// @returns true if the headers were cleared. Returns false if headers
121 /// were already sent for stream'ed response and the headers were not cleared.
123
124 /// @brief Sets a cookie if it was not set before.
125 void SetCookie(Cookie cookie);
126
127 /// @brief Remove all cookies from response.
129
130 /// @return HTTP response status
131 HttpStatus GetStatus() const noexcept { return status_; }
132
133 /// @return List of HTTP system headers names.
134 HeadersMapKeys GetSystemHeaderNames() const;
135
136 /// @return List of HTTP user headers names.
137 HeadersMapKeys GetUserHeaderNames() const;
138
139 /// @return Value of the header with case insensitive name header_name, or an
140 /// empty string if no such header.
141 const std::string& GetHeader(std::string_view header_name) const;
142 /// @overload
143 const std::string& GetHeader(const USERVER_NAMESPACE::http::headers::PredefinedHeader& header_name) const;
144
145 /// @return true if header with case insensitive name header_name exists,
146 /// false otherwise.
147 bool HasHeader(std::string_view header_name) const;
148 /// @overload
149 bool HasHeader(const USERVER_NAMESPACE::http::headers::PredefinedHeader& header_name) const;
150
151 /// @return List of cookies names.
152 CookiesMapKeys GetCookieNames() const;
153
154 /// @return Value of the cookie with case sensitive name cookie_name, or an
155 /// empty string if no such cookie exists.
156 const Cookie& GetCookie(std::string_view cookie_name) const;
157
158 void SetStatusServiceUnavailable() { SetStatus(HttpStatus::kServiceUnavailable); }
159 void SetStatusOk() { SetStatus(HttpStatus::kOk); }
160 void SetStatusNotFound() { SetStatus(HttpStatus::kNotFound); }
161
162 bool WaitForHeadersEnd();
163 void SetHeadersEnd();
164
165 /// @returns true if response headers were committed and cannot be changed.
166 [[nodiscard]] bool IsHeadersEnd() const noexcept { return headers_end_.IsReady(); }
167
168 [[nodiscard]] bool IsBodyStreamed() const noexcept { return is_stream_body_; }
169
170 [[nodiscard]] bool IsSent() const noexcept { return is_sent_; }
171
172protected:
173 HttpResponse(const HttpRequest& request, request::ResponseDataAccounter& data_accounter);
174 HttpResponse(
175 const HttpRequest& request,
176 request::ResponseDataAccounter& data_accounter,
177 std::chrono::steady_clock::time_point now,
178 utils::StrCaseHash hasher
179 );
180 ~HttpResponse() noexcept;
181
182private:
183 friend class HttpResponseImpl;
184 friend class Http2ResponseWriter;
185
186 void StoreData(impl::ChunkStorage data);
187
188 [[nodiscard]] bool IsHeadersMutable() const noexcept { return !headers_end_.IsReady(); }
189
190 static constexpr auto kUnset = std::chrono::steady_clock::time_point::min();
191
192 request::ResponseDataAccounter& accounter_;
193 impl::ChunkStorage data_;
194 std::chrono::steady_clock::time_point create_time_;
195 std::chrono::steady_clock::time_point ready_time_{kUnset};
196 std::size_t accounted_size_ = 0;
197 std::size_t bytes_sent_ = 0;
198 bool is_sent_ = false;
199
200 const bool is_final_;
201 bool is_head_request_;
202 const std::uint8_t http_major_;
203 const std::uint8_t http_minor_;
204 HttpStatus status_ = HttpStatus::kOk;
205 HeadersMap system_headers_;
206 HeadersMap user_headers_;
207 CookiesMap cookies_;
208
209 bool is_stream_body_{false};
210 bool system_headers_ended_ = false;
211 engine::SingleConsumerEvent headers_end_{engine::SingleConsumerEvent::NoAutoReset()};
212};
213
214void SetThrottleReason(http::HttpResponse& http_response, std::string log_reason, std::string http_header_reason);
215
216} // namespace server::http
217
218USERVER_NAMESPACE_END