userver: userver/server/http/http_request.hpp Source File
Loading...
Searching...
No Matches
http_request.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/server/http/http_request.hpp
4/// @brief @copybrief server::http::HttpRequest
5
6#include <chrono>
7#include <string>
8#include <type_traits>
9#include <vector>
10
11#include <userver/engine/task/task_processor_fwd.hpp>
12
13#include <userver/engine/io/sockaddr.hpp>
14#include <userver/http/url.hpp>
15#include <userver/server/http/form_data_arg.hpp>
16#include <userver/server/http/http_method.hpp>
17#include <userver/server/http/http_response.hpp>
18#include <userver/utils/datetime/wall_coarse_clock.hpp>
19#include <userver/utils/impl/internal_tag.hpp>
20#include <userver/utils/impl/transparent_hash.hpp>
21#include <userver/utils/str_icase.hpp>
22
23USERVER_NAMESPACE_BEGIN
24
25namespace server::handlers {
26class HttpHandlerBase;
27} // namespace server::handlers
28
29/// Server parts of the HTTP protocol implementation.
30namespace server::http {
31
32/// @brief HTTP Request data.
33/// @note do not create HttpRequest by hand in tests,
34/// use HttpRequestBuilder instead.
35class HttpRequest final {
36public:
37 using HeadersMap = USERVER_NAMESPACE::http::headers::HeaderMap;
38
39 using HeadersMapKeys = decltype(utils::impl::MakeKeysView(HeadersMap()));
40
41 using CookiesMap = std::unordered_map<std::string, std::string, utils::StrCaseHash>;
42
43 using CookiesMapKeys = decltype(utils::impl::MakeKeysView(CookiesMap()));
44
45 /// @cond
46 explicit HttpRequest(request::ResponseDataAccounter& data_accounter, utils::impl::InternalTag);
47 /// @endcond
48
49 HttpRequest(HttpRequest&&) = delete;
50 HttpRequest(const HttpRequest&) = delete;
51
52 ~HttpRequest();
53
54 /// @return HTTP method (e.g. GET/POST)
55 const HttpMethod& GetMethod() const;
56
57 /// @return HTTP method as a string (e.g. "GET")
58 const std::string& GetMethodStr() const;
59
60 /// @return Major version of HTTP. For example, for HTTP 1.0 it returns 1
61 int GetHttpMajor() const;
62
63 /// @return Minor version of HTTP. For example, for HTTP 1.0 it returns 0
64 int GetHttpMinor() const;
65
66 /// @brief Get HTTP request target as provided by the client (see
67 /// https://www.rfc-editor.org/rfc/rfc7230#section-5.3). May contain the whole URL, but usually it consists of path
68 /// and query string.
69 const std::string& GetUrl() const;
70
71 /// @brief Get the path part of HTTP request URL.
72 ///
73 /// Unlike @ref server::handlers::HandlerConfig::path, path parameters are not replaced with placeholders, this is
74 /// the original highly cardinal path.
75 const std::string& GetRequestPath() const;
76
77 /// @cond
78 std::chrono::duration<double> GetRequestTime() const;
79
80 std::chrono::duration<double> GetResponseTime() const;
81 /// @endcond
82
83 /// @return Host from the URL.
84 const std::string& GetHost() const;
85
86 /// @return Request remote (peer's) address
87 const engine::io::Sockaddr& GetRemoteAddress() const;
88
89 /// @return First argument value with name `arg_name` or an empty string if no
90 /// such argument.
91 /// Arguments are extracted from:
92 /// - query part of the URL,
93 /// - the HTTP body (only if `parse_args_from_body: true` for handler is set).
94 ///
95 /// In both cases, arg keys and values are url-decoded automatically when
96 /// parsing into the HttpRequest.
97 const std::string& GetArg(std::string_view arg_name) const;
98
99 /// @return Argument values with name `arg_name` or an empty vector if no
100 /// such argument.
101 /// Arguments are extracted from:
102 /// - query part of the URL,
103 /// - the HTTP body (only if `parse_args_from_body: true` for handler is set).
104 ///
105 /// In both cases, arg keys and values are url-decoded automatically when
106 /// parsing into the HttpRequest.
107 const std::vector<std::string>& GetArgVector(std::string_view arg_name) const;
108
109 /// @return true if argument with name arg_name exists, false otherwise.
110 /// Arguments are extracted from:
111 /// - query part of the URL,
112 /// - the HTTP body (only if `parse_args_from_body: true` for handler is set).
113 ///
114 /// In both cases, arg keys and values are url-decoded automatically when
115 /// parsing into the HttpRequest.
116 bool HasArg(std::string_view arg_name) const;
117
118 /// @return Count of arguments.
119 /// Arguments are extracted from:
120 /// - query part of the URL,
121 /// - the HTTP body (only if `parse_args_from_body: true` for handler is set).
122 size_t ArgCount() const;
123
124 /// @return List of names of arguments.
125 /// Arguments are extracted from:
126 /// - query part of the URL,
127 /// - the HTTP body (only if `parse_args_from_body: true` for handler is set).
128 std::vector<std::string> ArgNames() const;
129
130 /// @brief Reconstruct the request URL with selected query args masked.
131 ///
132 /// Iterates all query arguments, replacing each value with `"***"` when
133 /// `is_masked_arg_name(name)` returns true.
134 template <typename Predicate>
135 requires std::is_invocable_r_v<bool, Predicate, std::string_view>
136 std::string GetMaskedUrl(Predicate is_masked_arg_name) const {
137 const auto names = ArgNames();
138 std::vector<std::pair<std::string_view, std::string_view>> args;
139
140 // Common case: each arg vector size is 1, so hope we don't need more
141 args.reserve(names.size());
142
143 for (const auto& name : names) {
144 for (const auto& value : GetArgVector(name)) {
145 args.emplace_back(name, is_masked_arg_name(name) ? "***" : std::string_view{value});
146 }
147 }
148 return USERVER_NAMESPACE::http::MakeUrl(GetRequestPath(), args);
149 }
150
151 /// @return First argument value with name arg_name from multipart/form-data
152 /// request or an empty FormDataArg if no such argument.
153 const FormDataArg& GetFormDataArg(std::string_view arg_name) const;
154
155 /// @return Argument values with name arg_name from multipart/form-data
156 /// request or an empty FormDataArg if no such argument.
157 const std::vector<FormDataArg>& GetFormDataArgVector(std::string_view arg_name) const;
158
159 /// @return true if argument with name arg_name exists in multipart/form-data
160 /// request, false otherwise.
161 bool HasFormDataArg(std::string_view arg_name) const;
162
163 /// @return Count of multipart/form-data arguments.
164 size_t FormDataArgCount() const;
165
166 /// @return List of names of multipart/form-data arguments.
167 std::vector<std::string> FormDataArgNames() const;
168
169 /// @return Named argument from URL path with wildcards.
170 /// @note Path args are currently NOT url-decoded automatically.
171 const std::string& GetPathArg(std::string_view arg_name) const;
172
173 /// @return Argument from URL path with wildcards by its 0-based index.
174 /// @note Path args are currently NOT url-decoded automatically.
175 const std::string& GetPathArg(size_t index) const;
176
177 /// @return true if named argument from URL path with wildcards exists, false
178 /// otherwise.
179 bool HasPathArg(std::string_view arg_name) const;
180
181 /// @return true if argument with index from URL path with wildcards exists,
182 /// false otherwise.
183 bool HasPathArg(size_t index) const;
184
185 /// @return Number of wildcard arguments in URL path.
186 size_t PathArgCount() const;
187
188 /// @return Value of the header with case insensitive name header_name, or an
189 /// empty string if no such header.
190 const std::string& GetHeader(std::string_view header_name) const;
191
192 /// @overload
193 const std::string& GetHeader(const USERVER_NAMESPACE::http::headers::PredefinedHeader& header_name) const;
194
195 /// @return true if header with case insensitive name header_name exists,
196 /// false otherwise.
197 bool HasHeader(std::string_view header_name) const;
198
199 /// @overload
200 bool HasHeader(const USERVER_NAMESPACE::http::headers::PredefinedHeader& header_name) const;
201
202 /// @return Number of headers.
203 size_t HeaderCount() const;
204
205 /// Removes the header with case insensitive name header_name.
206 void RemoveHeader(std::string_view header_name);
207
208 /// @overload
209 void RemoveHeader(const USERVER_NAMESPACE::http::headers::PredefinedHeader& header_name);
210
211 /// @return List of headers names.
212 HeadersMapKeys GetHeaderNames() const;
213
214 /// @return HTTP headers.
215 const HeadersMap& GetHeaders() const;
216
217 /// @return Value of the cookie with case sensitive name cookie_name, or an
218 /// empty string if no such cookie exists.
219 const std::string& GetCookie(const std::string& cookie_name) const;
220
221 /// @return true if cookie with case sensitive name cookie_name exists, false
222 /// otherwise.
223 bool HasCookie(const std::string& cookie_name) const;
224
225 /// @return Number of cookies.
226 size_t CookieCount() const;
227
228 /// @return List of cookies names.
229 CookiesMapKeys GetCookieNames() const;
230
231 /// @return HTTP cookies.
232 const CookiesMap& RequestCookies() const;
233
234 /// @return HTTP body.
235 const std::string& RequestBody() const;
236
237 /// @return moved out HTTP body. `this` is modified.
238 std::string ExtractRequestBody();
239
240 /// @cond
241 void SetRequestBody(std::string body);
242 void ParseArgsFromBody();
243 bool IsFinal() const noexcept;
244 /// @endcond
245
246 /// @brief Set the response status code.
247 ///
248 /// Equivalent to this->GetHttpResponse().SetStatus(status).
249 void SetResponseStatus(HttpStatus status) const;
250
251 /// @return true if the body of the request is still compressed. In other
252 /// words returns true if the static option `decompress_request` of a handler
253 /// was set to `false` and this is a compressed request.
254 bool IsBodyCompressed() const;
255
256 HttpResponse& GetHttpResponse() const noexcept;
257
258 /// Get approximate time point of request handling start
259 std::chrono::steady_clock::time_point GetStartTime() const;
260
261 /// @cond
262 void MarkAsInternalServerError() const;
263
264 void SetStartSendResponseTime() noexcept;
265 void SetFinishSendResponseTime();
266
267 void WriteAccessLogs(
268 const logging::TextLoggerPtr& logger_access,
269 const logging::TextLoggerPtr& logger_access_tskv,
270 const std::string& remote_address
271 ) const;
272
273 void WriteAccessLog(
274 const logging::TextLoggerPtr& logger_access,
275 utils::datetime::WallCoarseClock::time_point tp,
276 const std::string& remote_address
277 ) const;
278
279 void WriteAccessTskvLog(
280 const logging::TextLoggerPtr& logger_access_tskv,
281 utils::datetime::WallCoarseClock::time_point tp,
282 const std::string& remote_address
283 ) const;
284
285 using UpgradeCallback = std::function<void(std::unique_ptr<engine::io::RwBase>&&, engine::io::Sockaddr&&)>;
286
287 bool IsUpgradeWebsocket() const;
288 void SetUpgradeWebsocket(UpgradeCallback cb) const;
289 void DoUpgrade(std::unique_ptr<engine::io::RwBase>&& socket, engine::io::Sockaddr&& peer_name) const;
290 /// @endcond
291
292private:
293 void SetPathArgs(std::vector<std::pair<std::string, std::string>> args);
294
295 void SetHttpHandler(const handlers::HttpHandlerBase& handler);
296 const handlers::HttpHandlerBase* GetHttpHandler() const;
297
298 void SetTaskProcessor(engine::TaskProcessor& task_processor);
299 engine::TaskProcessor* GetTaskProcessor() const;
300
301 // HTTP/2.0 only
302 void SetResponseStreamId(std::int32_t);
303 void SetStreamProducer(impl::Http2StreamEventProducer&& producer);
304
305 void SetTaskCreateTime();
306 void SetTaskStartTime();
307 void SetResponseNotifyTime();
308 void SetResponseNotifyTime(std::chrono::steady_clock::time_point now);
309
310 friend class HttpRequestBuilder;
311 friend class HttpRequestHandler;
312
313 struct Impl;
314 utils::FastPimpl<Impl, 1936, 16> pimpl_;
315};
316
317} // namespace server::http
318
319USERVER_NAMESPACE_END