Sourcemeta Core 0.0.0
Loading...
Searching...
No Matches
http_system.h
1#ifndef SOURCEMETA_CORE_HTTP_SYSTEM_H_
2#define SOURCEMETA_CORE_HTTP_SYSTEM_H_
3
4#ifndef SOURCEMETA_CORE_HTTP_EXPORT
5#include <sourcemeta/core/http_export.h>
6#endif
7
8#include <sourcemeta/core/crypto.h>
9#include <sourcemeta/core/http_aws_sigv4.h>
10#include <sourcemeta/core/http_method.h>
11#include <sourcemeta/core/http_status.h>
12#include <sourcemeta/core/http_syntax.h>
13#include <sourcemeta/core/text.h>
14
15#include <chrono> // std::chrono::milliseconds, std::chrono::seconds
16#include <cstddef> // std::size_t
17#include <optional> // std::optional
18#include <stdexcept> // std::runtime_error
19#include <string> // std::string
20#include <string_view> // std::string_view
21#include <utility> // std::move, std::pair
22#include <variant> // std::variant, std::visit
23#include <vector> // std::vector
24
25namespace sourcemeta::core {
26
27// Exporting symbols that depends on the standard C++ library is considered
28// safe.
29// https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-2-c4275?view=msvc-170&redirectedfrom=MSDN
30#if defined(_MSC_VER)
31#pragma warning(disable : 4251 4275)
32#endif
33
54 std::vector<std::pair<std::string, std::string>> headers;
56 std::string body;
58 std::string url;
59};
60
74class SOURCEMETA_CORE_HTTP_EXPORT HTTPSystemBackendError
75 : public std::runtime_error {
76public:
79 HTTPSystemBackendError(const std::string &message, std::string variable,
80 std::vector<std::string> paths)
81 : std::runtime_error{message}, variable_{std::move(variable)},
82 paths_{std::move(paths)} {}
83
85 [[nodiscard]] auto variable() const noexcept -> const std::string & {
86 return this->variable_;
87 }
88
90 [[nodiscard]] auto paths() const noexcept
91 -> const std::vector<std::string> & {
92 return this->paths_;
93 }
94
95private:
96 std::string variable_;
97 std::vector<std::string> paths_;
98};
99
117class SOURCEMETA_CORE_HTTP_EXPORT HTTPSystemRequest {
118public:
120 explicit HTTPSystemRequest(std::string url,
122 : url_{std::move(url)}, method_{method} {}
123
126 this->method_ = method;
127 return *this;
128 }
129
132 struct HeaderValue {
137 std::variant<std::string, SecureString> data;
138
140 [[nodiscard]] auto bytes() const -> std::string_view {
141 return std::visit(
142 [](const auto &value) -> std::string_view { return value; },
143 this->data);
144 }
145 };
146
150 auto header(std::string name, std::string value) -> HTTPSystemRequest & {
151 // RFC 9110 §5.5: "a recipient of CR, LF, or NUL within a field value MUST
152 // either reject the message or replace each of those characters with SP",
153 // so a header carrying one is not added as a header-injection defense
156 return *this;
157 }
158
159 this->headers_.emplace_back(std::move(name),
160 HeaderValue{.data = std::move(value)});
161 return *this;
162 }
163
168 auto header(std::string name, SecureString value) -> HTTPSystemRequest & {
169 // RFC 9110 §5.5: "a recipient of CR, LF, or NUL within a field value MUST
170 // either reject the message or replace each of those characters with SP",
171 // so a header carrying one is not added as a header-injection defense
174 return *this;
175 }
176
177 this->headers_.emplace_back(std::move(name),
178 HeaderValue{.data = std::move(value)});
179 return *this;
180 }
181
193 [[nodiscard]] auto header(const std::string_view name) const
194 -> std::optional<std::string_view> {
195 for (const auto &[key, value] : this->headers_) {
196 if (equals_ignore_case(key, name)) {
197 return value.bytes();
198 }
199 }
200
201 return std::nullopt;
202 }
203
205 [[nodiscard]] auto headers() const noexcept -> const auto & {
206 return this->headers_;
207 }
208
212 auto body(std::string data, std::string content_type) -> HTTPSystemRequest & {
213 // RFC 9110 §5.5: "a recipient of CR, LF, or NUL within a field value MUST
214 // either reject the message or replace each of those characters with SP",
215 // so a content type carrying one is refused, since a backend writes it
216 // straight into the Content-Type field on the wire
217 if (http_field_line_has_forbidden_byte(content_type)) {
218 return *this;
219 }
220
221 this->body_ =
222 Body{.data = std::move(data), .content_type = std::move(content_type)};
223 return *this;
224 }
225
231 auto body(SecureString data, std::string content_type)
232 -> HTTPSystemRequest & {
233 // RFC 9110 §5.5: "a recipient of CR, LF, or NUL within a field value MUST
234 // either reject the message or replace each of those characters with SP",
235 // so a content type carrying one is refused, since a backend writes it
236 // straight into the Content-Type field on the wire
237 if (http_field_line_has_forbidden_byte(content_type)) {
238 return *this;
239 }
240
241 this->body_ =
242 Body{.data = std::move(data), .content_type = std::move(content_type)};
243 return *this;
244 }
245
247 auto follow_redirects(const bool value) -> HTTPSystemRequest & {
248 this->follow_redirects_ = value;
249 return *this;
250 }
251
253 auto maximum_redirects(const std::size_t value) -> HTTPSystemRequest & {
254 this->maximum_redirects_ = value;
255 return *this;
256 }
257
259 auto timeout(const std::chrono::milliseconds value) -> HTTPSystemRequest & {
260 this->timeout_ = value;
261 return *this;
262 }
263
266 auto connect_timeout(const std::chrono::milliseconds value)
267 -> HTTPSystemRequest & {
268 this->connect_timeout_ = value;
269 return *this;
270 }
271
273 auto maximum_response_size(const std::size_t value) -> HTTPSystemRequest & {
274 this->maximum_response_size_ = value;
275 return *this;
276 }
277
294 auto sign_aws_sigv4(const HTTPAWSCredentials &credentials,
295 const std::string_view region,
296 const std::string_view service,
297 const std::chrono::system_clock::time_point moment =
298 std::chrono::system_clock::now())
300
306 [[nodiscard]] auto send() const -> HTTPResponse;
307
308private:
309 struct Body {
310 // A plain body carries no secret and keeps the ordinary string storage,
311 // while a body set from wiping storage stays in it, so the common request
312 // pays nothing and a secret one is never copied into an ordinary string
313 std::variant<std::string, SecureString> data;
314 std::string content_type;
315
316 [[nodiscard]] auto bytes() const -> std::string_view {
317 return std::visit(
318 [](const auto &value) -> std::string_view { return value; },
319 this->data);
320 }
321 };
322
323 std::string url_;
324 HTTPMethod method_;
325 std::vector<std::pair<std::string, HeaderValue>> headers_;
326 std::optional<Body> body_;
327 bool follow_redirects_{true};
328 std::size_t maximum_redirects_{20};
329 std::chrono::milliseconds timeout_{std::chrono::seconds{30}};
330 std::optional<std::chrono::milliseconds> connect_timeout_;
331 std::optional<std::size_t> maximum_response_size_;
332};
333
334#if defined(_MSC_VER)
335#pragma warning(default : 4251 4275)
336#endif
337
338} // namespace sourcemeta::core
339
340#endif
Definition crypto_secure.h:162
HTTPSystemBackendError(const std::string &message, std::string variable, std::vector< std::string > paths)
Definition http_system.h:79
auto header(const std::string_view name) const -> std::optional< std::string_view >
Definition http_system.h:193
std::string body
The response body, owned by this result.
Definition http_system.h:56
auto follow_redirects(const bool value) -> HTTPSystemRequest &
Set whether to follow redirects, on by default.
Definition http_system.h:247
auto method(const HTTPMethod method) -> HTTPSystemRequest &
Set the request method.
Definition http_system.h:125
auto maximum_redirects(const std::size_t value) -> HTTPSystemRequest &
Set the maximum number of redirects to follow, 20 by default.
Definition http_system.h:253
auto timeout(const std::chrono::milliseconds value) -> HTTPSystemRequest &
Set the total request timeout, 30 seconds by default.
Definition http_system.h:259
auto headers() const noexcept -> const auto &
Get the request headers configured so far, in the order they were added.
Definition http_system.h:205
auto variable() const noexcept -> const std::string &
Get the name of the environment variable that overrides the backend path.
Definition http_system.h:85
auto header(std::string name, std::string value) -> HTTPSystemRequest &
Definition http_system.h:150
std::string url
The effective URL after any followed redirects.
Definition http_system.h:58
auto connect_timeout(const std::chrono::milliseconds value) -> HTTPSystemRequest &
Definition http_system.h:266
HTTPStatus status
The response status code.
Definition http_system.h:48
auto header(std::string name, SecureString value) -> HTTPSystemRequest &
Definition http_system.h:168
auto maximum_response_size(const std::size_t value) -> HTTPSystemRequest &
Abort with an error if the response body exceeds this number of bytes.
Definition http_system.h:273
auto paths() const noexcept -> const std::vector< std::string > &
Get the paths that were searched while looking for the backend.
Definition http_system.h:90
auto send() const -> HTTPResponse
auto body(SecureString data, std::string content_type) -> HTTPSystemRequest &
Definition http_system.h:231
std::vector< std::pair< std::string, std::string > > headers
Definition http_system.h:54
auto sign_aws_sigv4(const HTTPAWSCredentials &credentials, const std::string_view region, const std::string_view service, const std::chrono::system_clock::time_point moment=std::chrono::system_clock::now()) -> HTTPSystemRequest &
auto body(std::string data, std::string content_type) -> HTTPSystemRequest &
Definition http_system.h:212
HTTPSystemRequest(std::string url, const HTTPMethod method=HTTPMethod::GET)
Construct a request for the given URL and method.
Definition http_system.h:120
HTTPMethod
Definition http_method.h:12
auto http_field_line_has_forbidden_byte(const std::string_view value) noexcept -> bool
Definition http_syntax.h:196
@ GET
Transfer a current representation of the target resource.
Definition http_method.h:14
Definition http_aws_sigv4.h:20
Definition http_system.h:46
Definition http_status.h:20
auto equals_ignore_case(const std::string_view left, const std::string_view right) noexcept -> bool
Definition text.h:838
std::variant< std::string, SecureString > data
Definition http_system.h:137
auto bytes() const -> std::string_view
Get a view of the held bytes.
Definition http_system.h:140