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