comparison src/core/http.cc @ 407:2ae34a90f8d4

http: roll our own URL encode/decode functions works for basically every kind of data
author Paper <paper@tflc.us>
date Wed, 21 Jan 2026 11:27:01 -0500
parents 811697ad826a
children 9323153786dc
comparison
equal deleted inserted replaced
406:31ce85df55a8 407:2ae34a90f8d4
8 8
9 namespace HTTP { 9 namespace HTTP {
10 10
11 std::string UrlEncode(const std::string &data) 11 std::string UrlEncode(const std::string &data)
12 { 12 {
13 /* why do I need to init curl just for this? wtf? */ 13 std::string res;
14 CURL *curl = curl_easy_init(); 14 std::size_t sz = data.size();
15 if (!curl) 15 std::size_t i;
16 return ""; /* no way! */ 16
17 17 // output string will always be at least data.size()
18 char *output = curl_easy_escape(curl, data.data(), data.size()); 18 // so reserve that much space beforehand
19 if (!output) { 19 res.reserve(sz);
20 curl_easy_cleanup(curl); 20
21 return ""; 21 for (i = 0; i < sz; i++) {
22 } 22 // This works correctly for UTF-8 because of the way
23 23 // the data is laid out.
24 std::string str(output); 24 static const char lut[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.!~*'()";
25 25 unsigned char c = data[i];
26 curl_free(output); 26
27 curl_easy_cleanup(curl); 27 if (std::memchr(lut, c, sizeof(lut) - 1)) {
28 28 res.push_back(c);
29 return str; 29 } else {
30 } 30 static const char lut[] = "0123456789ABCDEF";
31 31 res.push_back('%');
32 res.push_back(lut[c >> 4]);
33 res.push_back(lut[c & 15]);
34 }
35 }
36
37 return res;
38 }
39
40 // NOTE: This function is not guaranteed to return
41 // UTF-8, or even text at all.
32 std::string UrlDecode(const std::string &data) 42 std::string UrlDecode(const std::string &data)
33 { 43 {
34 CURL *curl = curl_easy_init(); 44 std::string res;
35 if (!curl) 45
36 return ""; 46 const char *ptr = data.data();
37 47 std::size_t len = data.size();
38 int outlength; 48
39 char *output = curl_easy_unescape(curl, data.data(), data.size(), &outlength); 49 // reserve space beforehand
40 if (!output) { 50 res.reserve(len);
41 curl_easy_cleanup(curl); 51
42 return ""; 52 while (len > 0) {
43 } 53 // find the next percent character
44 54 // there's probably a better way to do this!
45 std::string str(output, outlength); 55 const char *next = reinterpret_cast<const char *>(std::memchr(reinterpret_cast<const void *>(ptr), '%', len));
46 56
47 curl_free(output); 57 if (next) {
48 curl_easy_cleanup(curl); 58 res.insert(res.end(), ptr, next);
49 59 len = (next - ptr);
50 return str; 60 ptr = next;
61
62 // now process the two hex chars
63 if (len >= 3) {
64 unsigned char hi, lo;
65
66 auto hex_char_to_value = [](unsigned char x, unsigned char &v) {
67 if (x >= 'A' && x <= 'F') {
68 v = x - 'A' + 0xA;
69 return true;
70 }
71
72 if (x >= 'a' && x <= 'f') {
73 v = x - 'a' + 0xA;
74 return true;
75 }
76
77 if (x >= '0' && x <= '9') {
78 v = x - '0';
79 return true;
80 }
81
82 return false;
83 };
84
85 if (hex_char_to_value(ptr[1], hi) && hex_char_to_value(ptr[2], lo)) {
86 ptr += 3;
87 len -= 3;
88 } else {
89 len = 0;
90 }
91 } else {
92 // uh oh
93 len = 0;
94 }
95 } else {
96 res.insert(res.end(), ptr, ptr + len);
97 //not needed: ptr += len;
98 len = 0;
99 }
100 }
101
102 return res;
51 } 103 }
52 104
53 std::string EncodeParamsList(std::string base, const std::map<std::string, std::string> &params) 105 std::string EncodeParamsList(std::string base, const std::map<std::string, std::string> &params)
54 { 106 {
55 std::size_t count = 0; 107 std::size_t count = 0;