Kurlyk
Loading...
Searching...
No Matches
http_parser.hpp
Go to the documentation of this file.
1#pragma once
2#ifndef KURLYK_HEADER_KURLYK_UTILS_HTTP_PARSER_HPP_INCLUDED
3#define KURLYK_HEADER_KURLYK_UTILS_HTTP_PARSER_HPP_INCLUDED
4
7
8namespace kurlyk {
9namespace utils {
10
17 const char *buffer,
18 const size_t& size,
19 std::string& key,
20 std::string& value) {
21 std::string header(buffer, size);
22 if (header.size() < 3) return;
23 std::size_t colon_pos = header.find_first_of(":");
24 std::size_t start_pos = colon_pos + 1;
25 if (colon_pos == std::string::npos || colon_pos == 0) return;
26 key = header.substr(0, colon_pos);
27 std::size_t end_pos = header.find_first_not_of(" ", start_pos);
28 value = (end_pos != std::string::npos) ? header.substr(end_pos) : std::string();
29
30 // Trim newline characters from value
31 while (!value.empty() && (value.back() == '\r' || value.back() == '\n')) {
32 value.pop_back();
33 }
34 }
35
36# ifdef KURLYK_USE_CURL
37
42 inline std::string to_query_string(
43 const QueryParams &query,
44 const std::string &prefix = std::string()) noexcept {
45 if (query.empty()) return std::string();
46 std::string query_string(prefix);
47 CURL *curl = curl_easy_init();
48 if (!curl) return query_string;
49
50 size_t index = 0;
51 const size_t index_end = query.size() - 1;
52 for(const auto& pair : query) {
53 char *escaped_key = curl_easy_escape(curl, pair.first.c_str(), pair.first.size());
54 if (escaped_key) {
55 query_string += std::string(escaped_key);
56 curl_free(escaped_key);
57 }
58 query_string += "=";
59 char *escaped_value = curl_easy_escape(curl, pair.second.c_str(), pair.second.size());
60 if(escaped_value) {
61 query_string += std::string(escaped_value);
62 curl_free(escaped_value);
63 }
64 if (index != index_end) query_string += "&";
65 ++index;
66 }
67 curl_easy_cleanup(curl);
68 return query_string;
69 }
70
71# else
72
77 inline std::string to_query_string(
78 const QueryParams &query,
79 const std::string &prefix = std::string()) noexcept {
80 std::string result(prefix);
81 bool first = true;
82
83 for (const auto &field : query) {
84 if (!first) {
85 result += '&';
86 }
87 result += field.first + '=' + percent_encode(field.second);
88 first = false;
89 }
90
91 return result;
92 }
93
94# endif
95
99 inline std::string to_cookie_string(const CaseInsensitiveMultimap& cookies) {
100 std::string result;
101
102 for (auto it = cookies.begin(); it != cookies.end(); ++it) {
103 if (it != cookies.begin()) {
104 result += "; ";
105 }
106 result += it->first + "=" + it->second;
107 }
108
109 return result;
110 }
111
115 inline std::string to_cookie_string(const Cookies& cookies) {
116 std::string result;
117
118 for (auto it = cookies.begin(); it != cookies.end(); ++it) {
119 if (it != cookies.begin()) {
120 result += "; ";
121 }
122 result += it->second.name + "=" + it->second.value;
123
124 if (!it->second.path.empty()) {
125 result += "; Path=" + it->second.path;
126 }
127
128 if (it->second.expiration_date != 0) {
129 result += "; Expires=" + std::to_string(it->second.expiration_date);
130 }
131 }
132
133 return result;
134 }
135
139 inline Cookies parse_cookie(std::string cookie) {
140 Cookies cookies;
141 std::vector<std::string> list_fragment;
142 cookie += ";";
143 const std::string secure_prefix("__Secure-");
144 const std::string host_prefix("__Host-");
145 std::size_t start_pos = 0;
146
147 for (;;) {
148 bool is_option = false;
149 std::size_t separator_pos = cookie.find_first_of("=;", start_pos);
150 if (separator_pos != std::string::npos) {
151 std::string name = cookie.substr(start_pos, (separator_pos - start_pos));
152
153 if (name.size() > host_prefix.size() && name.substr(0, 2) == "__") {
154 std::size_t prefix_pos = name.find_first_of("-");
155 if(prefix_pos != std::string::npos) {
156 name = name.substr(prefix_pos + 1, name.size() - prefix_pos - 1);
157 }
158 } else {
159 if (case_insensitive_equal(name, "expires") ||
160 case_insensitive_equal(name, "max-age") ||
161 case_insensitive_equal(name, "path") ||
162 case_insensitive_equal(name, "domain") ||
163 case_insensitive_equal(name, "samesite")) {
164 is_option = true;
165 } else if (case_insensitive_equal(name, "secure") ||
166 case_insensitive_equal(name, "httponly")) {
167 start_pos = (cookie[separator_pos] == ';') ? separator_pos + 2 : separator_pos + 1;
168 continue;
169 }
170 }
171
172 std::size_t end_pos = cookie.find("; ", separator_pos + 1);
173 if (end_pos == std::string::npos) {
174 std::string value = cookie.substr(separator_pos + 1, cookie.size() - separator_pos - 2);
175 Cookie cookie_obj{name, value};
176 if (!is_option) cookies.emplace(cookie_obj.name, cookie_obj);
177 break;
178 }
179 std::string value = cookie.substr(separator_pos + 1, end_pos - separator_pos - 1);
180 start_pos = end_pos + 2;
181 Cookie cookie_obj{name, value};
182 if (!is_option) cookies.emplace(cookie_obj.name, cookie_obj);
183 } else {
184 break;
185 }
186 }
187 return cookies;
188 }
189
190} // namespace utils
191} // namespace kurlyk
192
193#endif // KURLYK_HEADER_KURLYK_UTILS_HTTP_PARSER_HPP_INCLUDED
Represents an HTTP cookie.
Definition Cookie.hpp:12
std::string name
The name of the cookie.
Definition Cookie.hpp:14
std::unordered_multimap< std::string, std::string, CaseInsensitiveHash, CaseInsensitiveEqual > CaseInsensitiveMultimap
A case-insensitive unordered multimap for storing HTTP headers.
bool case_insensitive_equal(const std::string &str1, const std::string &str2) noexcept
Compares two strings case-insensitively.
std::string to_cookie_string(const CaseInsensitiveMultimap &cookies)
Converts a CaseInsensitiveMultimap to a string format suitable for HTTP Cookie headers.
void parse_http_header_pair(const char *buffer, const size_t &size, std::string &key, std::string &value)
Parses a header pair from a buffer.
std::string to_query_string(const QueryParams &query, const std::string &prefix=std::string()) noexcept
Converts a map of query parameters into a URL query string.
Cookies parse_cookie(std::string cookie)
Parses a cookie string into a Cookies object.
std::string percent_encode(const std::string &value) noexcept
Encodes a string using Percent Encoding according to RFC 3986.
Primary namespace for the Kurlyk library, encompassing initialization, request management,...
utils::CaseInsensitiveCookieMultimap Cookies
Alias for HTTP cookies, stored case-insensitively.
utils::CaseInsensitiveMultimap QueryParams
Alias for query parameters in HTTP requests, stored case-insensitively.