33 #ifndef _IDENTT_HTTP_UTILITY_HPP_ 34 #define _IDENTT_HTTP_UTILITY_HPP_ 42 #include <unordered_map> 48 #if defined(__GNUC__) || defined(__clang__) 49 #define DEPRECATED __attribute__((deprecated)) 50 #elif defined(_MSC_VER) 51 #define DEPRECATED __declspec(deprecated) 57 #if __cplusplus > 201402L || _MSVC_LANG > 201402L 58 #include <string_view> 61 using string_view = std::string_view;
65 #include <boost/utility/string_ref.hpp> 68 using string_view = boost::string_ref;
76 inline bool case_insensitive_equal(
const std::string &str1,
const std::string &str2) noexcept
78 return str1.size() == str2.size() &&
79 std::equal(str1.begin(), str1.end(), str2.begin(), [](
char a,
char b) {
80 return tolower(a) == tolower(b);
85 bool operator()(
const std::string &str1,
const std::string &str2)
const noexcept
87 return case_insensitive_equal(str1, str2);
93 std::size_t operator()(
const std::string &str)
const noexcept
98 h ^= hash(tolower(c)) + 0x9e3779b9 + (h << 6) + (h >> 2);
103 using CaseInsensitiveMultimap = std::unordered_multimap<std::string, std::string, CaseInsensitiveHash, CaseInsensitiveEqual>;
109 static std::string
encode(
const std::string &value) noexcept
111 static auto hex_chars =
"0123456789ABCDEF";
114 result.reserve(value.size());
116 for(
auto &chr : value) {
117 if(!((chr >=
'0' && chr <=
'9') || (chr >=
'A' && chr <=
'Z') || (chr >=
'a' && chr <=
'z') || chr ==
'-' || chr ==
'.' || chr ==
'_' || chr ==
'~'))
118 result += std::string(
"%") + hex_chars[
static_cast<unsigned char>(chr) >> 4] + hex_chars[static_cast<unsigned char>(chr) & 15];
127 static std::string
decode(
const std::string &value) noexcept
130 result.reserve(value.size() / 3 + (value.size() % 3));
132 for(std::size_t i = 0; i < value.size(); ++i) {
133 auto &chr = value[i];
134 if(chr ==
'%' && i + 2 < value.size()) {
135 auto hex = value.substr(i + 1, 2);
136 auto decoded_chr =
static_cast<char>(std::strtol(hex.c_str(),
nullptr, 16));
137 result += decoded_chr;
154 static std::string
create(
const CaseInsensitiveMultimap &fields) noexcept
159 for(
auto &field : fields) {
160 result += (!first ?
"&" :
"") + field.first +
'=' +
Percent::encode(field.second);
168 static CaseInsensitiveMultimap
parse(
const std::string &query_string) noexcept
170 CaseInsensitiveMultimap result;
172 if(query_string.empty())
175 std::size_t name_pos = 0;
176 auto name_end_pos = std::string::npos;
177 auto value_pos = std::string::npos;
178 for(std::size_t c = 0; c < query_string.size(); ++c) {
179 if(query_string[c] ==
'&') {
180 auto name = query_string.substr(name_pos, (name_end_pos == std::string::npos ? c : name_end_pos) - name_pos);
182 auto value = value_pos == std::string::npos ? std::string() : query_string.substr(value_pos, c - value_pos);
186 name_end_pos = std::string::npos;
187 value_pos = std::string::npos;
189 else if(query_string[c] ==
'=') {
194 if(name_pos < query_string.size()) {
195 auto name = query_string.substr(name_pos, name_end_pos - name_pos);
197 auto value = value_pos >= query_string.size() ? std::string() : query_string.substr(value_pos);
209 static CaseInsensitiveMultimap
parse(std::istream &stream) noexcept
211 CaseInsensitiveMultimap result;
213 std::size_t param_end;
214 while(getline(stream, line) && (param_end = line.find(
':')) != std::string::npos) {
215 std::size_t value_start = param_end + 1;
216 while(value_start + 1 < line.size() && line[value_start] ==
' ')
218 if(value_start < line.size())
219 result.emplace(line.substr(0, param_end), line.substr(value_start, line.size() - value_start - (line.back() ==
'\r' ? 1 : 0)));
230 static CaseInsensitiveMultimap
parse(
const std::string &value)
232 CaseInsensitiveMultimap result;
234 std::size_t name_start_pos = std::string::npos;
235 std::size_t name_end_pos = std::string::npos;
236 std::size_t value_start_pos = std::string::npos;
237 for(std::size_t c = 0; c < value.size(); ++c) {
238 if(name_start_pos == std::string::npos) {
239 if(value[c] !=
' ' && value[c] !=
';')
243 if(name_end_pos == std::string::npos) {
244 if(value[c] ==
';') {
245 result.emplace(value.substr(name_start_pos, c - name_start_pos), std::string());
246 name_start_pos = std::string::npos;
248 else if(value[c] ==
'=')
252 if(value_start_pos == std::string::npos) {
253 if(value[c] ==
'"' && c + 1 < value.size())
254 value_start_pos = c + 1;
258 else if(value[c] ==
'"' || value[c] ==
';') {
259 result.emplace(value.substr(name_start_pos, name_end_pos - name_start_pos),
Percent::decode(value.substr(value_start_pos, c - value_start_pos)));
260 name_start_pos = std::string::npos;
261 name_end_pos = std::string::npos;
262 value_start_pos = std::string::npos;
267 if(name_start_pos != std::string::npos) {
268 if(name_end_pos == std::string::npos)
269 result.emplace(value.substr(name_start_pos), std::string());
270 else if(value_start_pos != std::string::npos) {
271 if(value.back() ==
'"')
272 result.emplace(value.substr(name_start_pos, name_end_pos - name_start_pos),
Percent::decode(value.substr(value_start_pos, value.size() - 1)));
274 result.emplace(value.substr(name_start_pos, name_end_pos - name_start_pos),
Percent::decode(value.substr(value_start_pos)));
297 static bool parse(std::istream &stream, std::string &method, std::string &path, std::string &query_string, std::string &version, CaseInsensitiveMultimap &header) noexcept
300 std::size_t method_end;
301 if(getline(stream, line) && (method_end = line.find(
' ')) != std::string::npos) {
302 method = line.substr(0, method_end);
304 std::size_t query_start = std::string::npos;
305 std::size_t path_and_query_string_end = std::string::npos;
306 for(std::size_t i = method_end + 1; i < line.size(); ++i) {
307 if(line[i] ==
'?' && (i + 1) < line.size())
309 else if(line[i] ==
' ') {
310 path_and_query_string_end = i;
314 if(path_and_query_string_end != std::string::npos) {
315 if(query_start != std::string::npos) {
316 path = line.substr(method_end + 1, query_start - method_end - 2);
317 query_string = line.substr(query_start, path_and_query_string_end - query_start);
320 path = line.substr(method_end + 1, path_and_query_string_end - method_end - 1);
322 std::size_t protocol_end;
323 if((protocol_end = line.find(
'/', path_and_query_string_end + 1)) != std::string::npos) {
324 if(line.compare(path_and_query_string_end + 1, protocol_end - path_and_query_string_end - 1,
"HTTP") != 0)
326 version = line.substr(protocol_end + 1, line.size() - protocol_end - 2);
353 static bool parse(std::istream &stream, std::string &version, std::string &status_code, CaseInsensitiveMultimap &header) noexcept
356 std::size_t version_end;
357 if(getline(stream, line) && (version_end = line.find(
' ')) != std::string::npos) {
359 version = line.substr(5, version_end - 5);
362 if((version_end + 1) < line.size())
363 status_code = line.substr(version_end + 1, line.size() - (version_end + 1) - (line.back() ==
'\r' ? 1 : 0));
379 static std::string
to_string(
const std::chrono::system_clock::time_point time_point) noexcept
381 static std::string result_cache;
382 static std::chrono::system_clock::time_point last_time_point;
384 static std::mutex mutex;
385 std::lock_guard<std::mutex> lock(mutex);
387 if(std::chrono::duration_cast<std::chrono::seconds>(time_point - last_time_point).count() == 0 && !result_cache.empty())
390 last_time_point = time_point;
395 auto time = std::chrono::system_clock::to_time_t(time_point);
397 #if defined(_MSC_VER) || defined(__MINGW32__) 398 if(gmtime_s(&tm, &time) != 0)
402 auto gmtime = gmtime_r(&time, &tm);
407 switch(gmtime->tm_wday) {
431 result += gmtime->tm_mday < 10 ?
'0' :
static_cast<char>(gmtime->tm_mday / 10 + 48);
432 result +=
static_cast<char>(gmtime->tm_mday % 10 + 48);
434 switch(gmtime->tm_mon) {
473 auto year = gmtime->tm_year + 1900;
474 result +=
static_cast<char>(year / 1000 + 48);
475 result +=
static_cast<char>((year / 100) % 10 + 48);
476 result +=
static_cast<char>((year / 10) % 10 + 48);
477 result +=
static_cast<char>(year % 10 + 48);
480 result += gmtime->tm_hour < 10 ?
'0' :
static_cast<char>(gmtime->tm_hour / 10 + 48);
481 result +=
static_cast<char>(gmtime->tm_hour % 10 + 48);
484 result += gmtime->tm_min < 10 ?
'0' :
static_cast<char>(gmtime->tm_min / 10 + 48);
485 result +=
static_cast<char>(gmtime->tm_min % 10 + 48);
488 result += gmtime->tm_sec < 10 ?
'0' :
static_cast<char>(gmtime->tm_sec / 10 + 48);
489 result +=
static_cast<char>(gmtime->tm_sec % 10 + 48);
493 result_cache = result;
502 #include <emmintrin.h> 506 inline void spin_loop_pause() noexcept
513 #elif defined(_MSC_VER) && _MSC_VER >= 1800 && (defined(_M_X64) || defined(_M_IX86)) 518 inline void spin_loop_pause() noexcept
527 inline void spin_loop_pause() noexcept {}
537 std::atomic<long> count;
542 std::atomic<long> &count;
543 SharedLock(std::atomic<long> &count) noexcept : count(count) {}
560 long expected = count;
561 while(expected >= 0 && !count.compare_exchange_weak(expected, expected + 1))
567 return std::unique_ptr<SharedLock>(
new SharedLock(count));
574 while(!count.compare_exchange_weak(expected, -1)) {
585 #endif // _IDENTT_HTTP_UTILITY_HPP_
Definition: Utility.hpp:91
Makes it possible to for instance cancel Asio handlers without stopping asio::io_service.
Definition: Utility.hpp:535
Date class working with formats specified in RFC 7231 Date/Time Formats.
Definition: Utility.hpp:376
Definition: Utility.hpp:540
static CaseInsensitiveMultimap parse(const std::string &query_string) noexcept
Returns query keys with percent-decoded values.
Definition: Utility.hpp:168
Definition: Utility.hpp:284
static std::string encode(const std::string &value) noexcept
Returns percent-encoded string.
Definition: Utility.hpp:109
void stop() noexcept
Blocks until all shared locks are released, then prevents future shared locks.
Definition: Utility.hpp:571
Definition: Utility.hpp:342
Definition: CryptoBase.hpp:49
static bool parse(std::istream &stream, std::string &version, std::string &status_code, CaseInsensitiveMultimap &header) noexcept
Parse status line and header fields from a response stream.
Definition: Utility.hpp:353
Definition: Utility.hpp:83
Query string creation and parsing.
Definition: Utility.hpp:151
static std::string to_string(const std::chrono::system_clock::time_point time_point) noexcept
Returns the given std::chrono::system_clock::time_point as a string with the following format: Wed...
Definition: Utility.hpp:379
static std::string create(const CaseInsensitiveMultimap &fields) noexcept
Returns query string created from given field names and values.
Definition: Utility.hpp:154
std::unique_ptr< SharedLock > continue_lock() noexcept
Returns nullptr if scope should be exited, or a shared lock otherwise.
Definition: Utility.hpp:558
Percent encoding and decoding.
Definition: Utility.hpp:106
static bool parse(std::istream &stream, std::string &method, std::string &path, std::string &query_string, std::string &version, CaseInsensitiveMultimap &header) noexcept
Parse request line and header fields from a request stream.
Definition: Utility.hpp:297
static std::string decode(const std::string &value) noexcept
Returns percent-decoded string.
Definition: Utility.hpp:127