1 #ifndef SIMPLE_WEB_UTILITY_HPP
2 #define SIMPLE_WEB_UTILITY_HPP
4 #include "status_code.h"
9 #include <unordered_map>
12 inline bool case_insensitive_equal(
const std::string &str1,
const std::string &str2) noexcept {
13 return str1.size() == str2.size() &&
14 std::equal(str1.begin(), str1.end(), str2.begin(), [](
char a,
char b) {
15 return tolower(a) == tolower(b);
20 bool operator()(
const std::string &str1,
const std::string &str2)
const noexcept {
21 return case_insensitive_equal(str1, str2);
27 std::size_t operator()(
const std::string &str)
const noexcept {
31 h ^= hash(tolower(c)) + 0x9e3779b9 + (h << 6) + (h >> 2);
36 using CaseInsensitiveMultimap = std::unordered_multimap<std::string, std::string, CaseInsensitiveHash, CaseInsensitiveEqual>;
42 static std::string
encode(
const std::string &value) noexcept {
43 static auto hex_chars =
"0123456789ABCDEF";
46 result.reserve(value.size());
48 for(
auto &chr : value) {
51 else if(chr ==
'!' || chr ==
'#' || chr ==
'$' || (chr >=
'&' && chr <=
',') || (chr >=
'/' && chr <=
';') || chr ==
'=' || chr ==
'?' || chr ==
'@' || chr ==
'[' || chr ==
']')
52 result += std::string(
"%") + hex_chars[chr >> 4] + hex_chars[chr & 15];
61 static std::string
decode(
const std::string &value) noexcept {
63 result.reserve(value.size() / 3 + (value.size() % 3));
65 for(std::size_t i = 0; i < value.size(); ++i) {
67 if(chr ==
'%' && i + 2 < value.size()) {
68 auto hex = value.substr(i + 1, 2);
69 auto decoded_chr =
static_cast<char>(std::strtol(hex.c_str(),
nullptr, 16));
70 result += decoded_chr;
87 static std::string
create(
const CaseInsensitiveMultimap &fields) noexcept {
91 for(
auto &field : fields) {
92 result += (!first ?
"&" :
"") + field.first +
'=' +
Percent::encode(field.second);
100 static CaseInsensitiveMultimap
parse(
const std::string &query_string) noexcept {
101 CaseInsensitiveMultimap result;
103 if(query_string.empty())
106 std::size_t name_pos = 0;
107 auto name_end_pos = std::string::npos;
108 auto value_pos = std::string::npos;
109 for(std::size_t c = 0; c < query_string.size(); ++c) {
110 if(query_string[c] ==
'&') {
111 auto name = query_string.substr(name_pos, (name_end_pos == std::string::npos ? c : name_end_pos) - name_pos);
113 auto value = value_pos == std::string::npos ? std::string() : query_string.substr(value_pos, c - value_pos);
117 name_end_pos = std::string::npos;
118 value_pos = std::string::npos;
120 else if(query_string[c] ==
'=') {
125 if(name_pos < query_string.size()) {
126 auto name = query_string.substr(name_pos, name_end_pos - name_pos);
128 auto value = value_pos >= query_string.size() ? std::string() : query_string.substr(value_pos);
140 static CaseInsensitiveMultimap
parse(std::istream &stream) noexcept {
141 CaseInsensitiveMultimap result;
143 getline(stream, line);
144 std::size_t param_end;
145 while((param_end = line.find(
':')) != std::string::npos) {
146 std::size_t value_start = param_end + 1;
147 if(value_start < line.size()) {
148 if(line[value_start] ==
' ')
150 if(value_start < line.size())
151 result.emplace(line.substr(0, param_end), line.substr(value_start, line.size() - value_start - 1));
154 getline(stream, line);
163 static bool parse(std::istream &stream, std::string &method, std::string &path, std::string &query_string, std::string &version, CaseInsensitiveMultimap &header) noexcept {
166 getline(stream, line);
167 std::size_t method_end;
168 if((method_end = line.find(
' ')) != std::string::npos) {
169 method = line.substr(0, method_end);
171 std::size_t query_start = std::string::npos;
172 std::size_t path_and_query_string_end = std::string::npos;
173 for(std::size_t i = method_end + 1; i < line.size(); ++i) {
174 if(line[i] ==
'?' && (i + 1) < line.size())
176 else if(line[i] ==
' ') {
177 path_and_query_string_end = i;
181 if(path_and_query_string_end != std::string::npos) {
182 if(query_start != std::string::npos) {
183 path = line.substr(method_end + 1, query_start - method_end - 2);
184 query_string = line.substr(query_start, path_and_query_string_end - query_start);
187 path = line.substr(method_end + 1, path_and_query_string_end - method_end - 1);
189 std::size_t protocol_end;
190 if((protocol_end = line.find(
'/', path_and_query_string_end + 1)) != std::string::npos) {
191 if(line.compare(path_and_query_string_end + 1, protocol_end - path_and_query_string_end - 1,
"HTTP") != 0)
193 version = line.substr(protocol_end + 1, line.size() - protocol_end - 2);
212 static bool parse(std::istream &stream, std::string &version, std::string &status_code, CaseInsensitiveMultimap &header) noexcept {
215 getline(stream, line);
216 std::size_t version_end = line.find(
' ');
217 if(version_end != std::string::npos) {
219 version = line.substr(5, version_end - 5);
222 if((version_end + 1) < line.size())
223 status_code = line.substr(version_end + 1, line.size() - (version_end + 1) - 1);
239 static CaseInsensitiveMultimap
parse(
const std::string &line) {
240 CaseInsensitiveMultimap result;
242 std::size_t para_start_pos = 0;
243 std::size_t para_end_pos = std::string::npos;
244 std::size_t value_start_pos = std::string::npos;
245 for(std::size_t c = 0; c < line.size(); ++c) {
246 if(para_start_pos != std::string::npos) {
247 if(para_end_pos == std::string::npos) {
249 result.emplace(line.substr(para_start_pos, c - para_start_pos), std::string());
250 para_start_pos = std::string::npos;
252 else if(line[c] ==
'=')
256 if(value_start_pos == std::string::npos) {
257 if(line[c] ==
'"' && c + 1 < line.size())
258 value_start_pos = c + 1;
260 else if(line[c] ==
'"') {
261 result.emplace(line.substr(para_start_pos, para_end_pos - para_start_pos), line.substr(value_start_pos, c - value_start_pos));
262 para_start_pos = std::string::npos;
263 para_end_pos = std::string::npos;
264 value_start_pos = std::string::npos;
268 else if(line[c] !=
' ' && line[c] !=
';')
271 if(para_start_pos != std::string::npos && para_end_pos == std::string::npos)
272 result.emplace(line.substr(para_start_pos), std::string());
280 #include <emmintrin.h>
281 namespace SimpleWeb {
282 inline void spin_loop_pause() noexcept { _mm_pause(); }
285 #elif defined(_MSC_VER) && _MSC_VER >= 1800 && (defined(_M_X64) || defined(_M_IX86))
287 namespace SimpleWeb {
288 inline void spin_loop_pause() noexcept { _mm_pause(); }
291 namespace SimpleWeb {
292 inline void spin_loop_pause() noexcept {}
296 namespace SimpleWeb {
300 std::atomic<long> count;
305 std::atomic<long> &count;
306 SharedLock(std::atomic<long> &count) noexcept : count(count) {}
320 long expected = count;
321 while(expected >= 0 && !count.compare_exchange_weak(expected, expected + 1))
327 return std::unique_ptr<SharedLock>(
new SharedLock(count));
333 while(!count.compare_exchange_weak(expected, -1)) {
343 #endif // SIMPLE_WEB_UTILITY_HPP
static std::string create(const CaseInsensitiveMultimap &fields) noexcept
Returns query string created from given field names and values.
Definition: utility.h:87
static bool parse(std::istream &stream, std::string &version, std::string &status_code, CaseInsensitiveMultimap &header) noexcept
Parse status line and header fields.
Definition: utility.h:212
Definition: utility.h:303
Definition: utility.h:160
Definition: utility.h:209
Query string creation and parsing.
Definition: utility.h:84
void stop() noexcept
Blocks until all shared locks are released, then prevents future shared locks.
Definition: utility.h:331
static CaseInsensitiveMultimap parse(const std::string &query_string) noexcept
Returns query keys with percent-decoded values.
Definition: utility.h:100
std::unique_ptr< SharedLock > continue_lock() noexcept
Returns nullptr if scope should be exited, or a shared lock otherwise.
Definition: utility.h:319
static std::string decode(const std::string &value) noexcept
Returns percent-decoded string.
Definition: utility.h:61
static std::string encode(const std::string &value) noexcept
Returns percent-encoded string.
Definition: utility.h:42
Definition: utility.h:235
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.
Definition: utility.h:163
Makes it possible to for instance cancel Asio handlers without stopping asio::io_service.
Definition: utility.h:298
static CaseInsensitiveMultimap parse(const std::string &line)
Definition: utility.h:239
Percent encoding and decoding.
Definition: utility.h:39