/src/pdns/ext/yahttp/yahttp/utility.hpp
Line | Count | Source (jump to first uncovered line) |
1 | | #pragma once |
2 | | |
3 | | #ifndef YAHTTP_MAX_REQUEST_LINE_SIZE |
4 | 4.13k | #define YAHTTP_MAX_REQUEST_LINE_SIZE 8192 |
5 | | #endif |
6 | | |
7 | | #ifndef YAHTTP_MAX_REQUEST_FIELDS |
8 | 16.8k | #define YAHTTP_MAX_REQUEST_FIELDS 100 |
9 | | #endif |
10 | | |
11 | | namespace YaHTTP { |
12 | | static const char *MONTHS[] = {0,"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",0}; //<! List of months |
13 | | static const char *DAYS[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat",0}; //<! List of days |
14 | | |
15 | | bool isspace(char c); |
16 | | bool isspace(char c, const std::locale& loc); |
17 | | bool isxdigit(char c); |
18 | | bool isxdigit(char c, const std::locale& loc); |
19 | | bool isdigit(char c); |
20 | | bool isdigit(char c, const std::locale& loc); |
21 | | bool isalnum(char c); |
22 | | bool isalnum(char c, const std::locale& loc); |
23 | | |
24 | | /*! Case-Insensitive NULL safe comparator for string maps */ |
25 | | struct ASCIICINullSafeComparator { |
26 | 3.08M | bool operator() (const std::string& lhs, const std::string& rhs) const { |
27 | 3.08M | int v; |
28 | 3.08M | std::string::const_iterator lhi = lhs.begin(); |
29 | 3.08M | std::string::const_iterator rhi = rhs.begin(); |
30 | 6.13M | for(;lhi != lhs.end() && rhi != rhs.end(); lhi++, rhi++) |
31 | 3.97M | if ((v = ::tolower(*lhi) - ::tolower(*rhi)) != 0) return v<0; |
32 | 2.16M | if (lhi == lhs.end() && rhi != rhs.end()) return true; |
33 | 1.18M | if (lhi != lhs.end() && rhi == rhs.end()) return false; |
34 | 1.16M | return false; // they are equal |
35 | 1.18M | } |
36 | | }; |
37 | | |
38 | | typedef std::map<std::string,std::string,ASCIICINullSafeComparator> strstr_map_t; //<! String to String map |
39 | | |
40 | | /*! Represents a date/time with utc offset */ |
41 | | class DateTime { |
42 | | public: |
43 | | bool isSet; //<! if this is initialized yet |
44 | | |
45 | | int year; //<! year, 0 is year 0, not 1900 |
46 | | |
47 | | int month; //<! month, range 1-12 |
48 | | int day; //<! day, range 1-31 |
49 | | int wday; //<! week day, range 1-7 |
50 | | |
51 | | int hours; //<! hours, range 0-23 |
52 | | int minutes; //<! minutes, range 0-59 |
53 | | int seconds; //<! seconds, range 0-60 |
54 | | |
55 | | int utc_offset; //<! UTC offset with minutes (hhmm) |
56 | | |
57 | 1.18M | DateTime() { |
58 | 1.18M | initialize(); |
59 | 1.18M | }; //<! Construct and initialize |
60 | | |
61 | 1.18M | void initialize() { |
62 | 1.18M | isSet = false; |
63 | 1.18M | year = month = day = wday = hours = minutes = seconds = utc_offset = 0; |
64 | 1.18M | month = 1; // it's invalid otherwise |
65 | 1.18M | }; //<! Creates year 0 date |
66 | | |
67 | 0 | void setLocal() { |
68 | 0 | fromLocaltime(time((time_t*)NULL)); |
69 | 0 | }; //<! sets current local time |
70 | | |
71 | 0 | void setGm() { |
72 | 0 | fromGmtime(time((time_t*)NULL)); |
73 | 0 | }; //<! sets current gmtime (almost UTC) |
74 | | |
75 | 0 | void fromLocaltime(time_t t) { |
76 | 0 | #ifdef HAVE_LOCALTIME_R |
77 | 0 | struct tm tm; |
78 | 0 | localtime_r(&t, &tm); |
79 | 0 | fromTm(&tm); |
80 | 0 | #else |
81 | 0 | struct tm *tm; |
82 | 0 | #error define HAVE_LOCALTIME_R |
83 | 0 | tm = localtime(&t); // lgtm [cpp/potentially-dangerous-function] |
84 | 0 | fromTm(tm); |
85 | 0 | #endif |
86 | 0 | #ifndef HAVE_TM_GMTOFF |
87 | 0 | time_t t2; |
88 | 0 | # ifdef HAVE_LOCALTIME_R |
89 | 0 | gmtime_r(&t, &tm); |
90 | 0 | t2 = mktime(&tm); |
91 | 0 | # else |
92 | 0 | #error define HAVE_LOCALTIME_R |
93 | 0 | tm = gmtime(&t); // lgtm [cpp/potentially-dangerous-function] |
94 | 0 | t2 = mktime(tm); |
95 | 0 | # endif |
96 | 0 | this->utc_offset = ((t2-t)/10)*10; // removes any possible differences. |
97 | 0 | #endif |
98 | 0 | }; //<! uses localtime for time |
99 | | |
100 | 0 | void fromGmtime(time_t t) { |
101 | 0 | #ifdef HAVE_GMTIME_R |
102 | 0 | struct tm tm; |
103 | 0 | gmtime_r(&t, &tm); |
104 | 0 | fromTm(&tm); |
105 | 0 | #else |
106 | 0 | struct tm *tm; |
107 | 0 | #error define HAVE_GMTIME_R |
108 | 0 | tm = gmtime(&t);// lgtm [cpp/potentially-dangerous-function] |
109 | 0 | fromTm(tm); |
110 | 0 | #endif |
111 | 0 | #ifndef HAVE_TM_GMTOFF |
112 | 0 | this->utc_offset = 0; |
113 | 0 | #endif |
114 | 0 | }; //<! uses gmtime for time |
115 | | |
116 | 0 | void fromTm(const struct tm *tm) { |
117 | 0 | year = tm->tm_year + 1900; |
118 | 0 | month = tm->tm_mon + 1; |
119 | 0 | day = tm->tm_mday; |
120 | 0 | hours = tm->tm_hour; |
121 | 0 | minutes = tm->tm_min; |
122 | 0 | seconds = tm->tm_sec; |
123 | 0 | wday = tm->tm_wday; |
124 | | #ifdef HAVE_TM_GMTOFF |
125 | | utc_offset = tm->tm_gmtoff; |
126 | | #endif |
127 | 0 | isSet = true; |
128 | 0 | }; //<! parses date from struct tm |
129 | | |
130 | 0 | void validate() const { |
131 | 0 | if (wday < 0 || wday > 6) throw std::range_error("Invalid date"); |
132 | 0 | if (month < 1 || month > 12) throw std::range_error("Invalid date"); |
133 | 0 | if (year < 0) throw std::range_error("Invalid date"); |
134 | 0 | if (hours < 0 || hours > 23 || |
135 | 0 | minutes < 0 || minutes > 59 || |
136 | 0 | seconds < 0 || seconds > 60) throw std::range_error("Invalid date"); |
137 | 0 | }; //<! make sure we are within ranges (not a *REAL* validation, just range check) |
138 | | |
139 | 0 | std::string rfc_str() const { |
140 | 0 | std::ostringstream oss; |
141 | 0 | validate(); |
142 | 0 | oss << DAYS[wday] << ", " << std::setfill('0') << std::setw(2) << day << " " << MONTHS[month] << " " << |
143 | 0 | std::setfill('0') << std::setw(2) << year << " " << |
144 | 0 | std::setfill('0') << std::setw(2) << hours << ":" << |
145 | 0 | std::setfill('0') << std::setw(2) << minutes << ":" << |
146 | 0 | std::setfill('0') << std::setw(2) << seconds << " "; |
147 | 0 | if (utc_offset>=0) oss << "+"; |
148 | 0 | else oss << "-"; |
149 | 0 | int tmp_off = ( utc_offset < 0 ? utc_offset*-1 : utc_offset ); |
150 | 0 | oss << std::setfill('0') << std::setw(2) << (tmp_off/3600); |
151 | 0 | oss << std::setfill('0') << std::setw(2) << (tmp_off%3600)/60; |
152 | 0 |
|
153 | 0 | return oss.str(); |
154 | 0 | }; //<! converts this date into a RFC-822 format |
155 | | |
156 | 0 | std::string cookie_str() const { |
157 | 0 | std::ostringstream oss; |
158 | 0 | validate(); |
159 | 0 | oss << std::setfill('0') << std::setw(2) << day << "-" << MONTHS[month] << "-" << year << " " << |
160 | 0 | std::setfill('0') << std::setw(2) << hours << ":" << |
161 | 0 | std::setfill('0') << std::setw(2) << minutes << ":" << |
162 | 0 | std::setfill('0') << std::setw(2) << seconds << " GMT"; |
163 | 0 | return oss.str(); |
164 | 0 | }; //<! converts this date into a HTTP Cookie date |
165 | | |
166 | 0 | void parse822(const std::string &rfc822_date) { |
167 | 0 | struct tm tm; |
168 | 0 | const char *ptr; |
169 | 0 | #ifdef HAVE_TM_GMTOFF |
170 | 0 | if ( (ptr = strptime(rfc822_date.c_str(), "%a, %d %b %Y %T %z", &tm)) != NULL) { |
171 | 0 | #else |
172 | 0 | if ( (ptr = strptime(rfc822_date.c_str(), "%a, %d %b %Y %T", &tm)) != NULL) { |
173 | 0 | int sign; |
174 | 0 | // parse the timezone parameter |
175 | 0 | while(*ptr && YaHTTP::isspace(*ptr)) ptr++; |
176 | 0 | if (*ptr == '+') sign = 0; |
177 | 0 | else if (*ptr == '-') sign = -1; |
178 | 0 | else throw YaHTTP::ParseError("Unparseable date"); |
179 | 0 | ptr++; |
180 | 0 | utc_offset = ::atoi(ptr) * sign; |
181 | 0 | while(*ptr != '\0' && YaHTTP::isdigit(*ptr)) ptr++; |
182 | 0 | #endif |
183 | 0 | while(*ptr != '\0' && YaHTTP::isspace(*ptr)) ptr++; |
184 | 0 | if (*ptr != '\0') throw YaHTTP::ParseError("Unparseable date"); // must be final. |
185 | 0 | fromTm(&tm); |
186 | 0 | } else { |
187 | 0 | throw YaHTTP::ParseError("Unparseable date"); |
188 | 0 | } |
189 | 0 | }; //<! parses RFC-822 date |
190 | | |
191 | 0 | void parseCookie(const std::string &cookie_date) { |
192 | 0 | struct tm tm; |
193 | 0 | const char *ptr; |
194 | 0 | if ( (ptr = strptime(cookie_date.c_str(), "%d-%b-%Y %T", &tm)) != NULL |
195 | | #ifdef HAVE_TM_GMTOFF |
196 | | || (ptr = strptime(cookie_date.c_str(), "%d-%b-%Y %T %z", &tm)) != NULL |
197 | | || (ptr = strptime(cookie_date.c_str(), "%a, %d-%b-%Y %T %Z", &tm)) != NULL |
198 | | #endif |
199 | 0 | ) { |
200 | 0 | while(*ptr != '\0' && ( YaHTTP::isspace(*ptr) || YaHTTP::isalnum(*ptr) )) ptr++; |
201 | 0 | if (*ptr != '\0') throw YaHTTP::ParseError("Unparseable date (non-final)"); // must be final. |
202 | 0 | fromTm(&tm); |
203 | 0 | this->utc_offset = 0; |
204 | 0 | } else { |
205 | 0 | std::cout << cookie_date << std::endl; |
206 | 0 | throw YaHTTP::ParseError("Unparseable date (did not match pattern cookie)"); |
207 | 0 | } |
208 | 0 | }; //<! parses HTTP Cookie date |
209 | | |
210 | 0 | time_t unixtime() const { |
211 | 0 | struct tm tm; |
212 | 0 | tm.tm_year = year-1900; |
213 | 0 | tm.tm_mon = month-1; |
214 | 0 | tm.tm_mday = day; |
215 | 0 | tm.tm_hour = hours; |
216 | 0 | tm.tm_min = minutes; |
217 | 0 | tm.tm_sec = seconds; |
218 | 0 | tm.tm_isdst = 0; |
219 | 0 | #ifdef HAVE_TM_GMTOFF |
220 | 0 | tm.tm_gmtoff = utc_offset; |
221 | 0 | #endif |
222 | 0 | return mktime(&tm); |
223 | 0 | }; //<! returns this datetime as unixtime. will not work for dates before 1970/1/1 00:00:00 GMT |
224 | | }; |
225 | | |
226 | | /*! Various helpers needed in the code */ |
227 | | class Utility { |
228 | | public: |
229 | 2.37M | static std::string decodeURL(const std::string& component) { |
230 | 2.37M | std::string result = component; |
231 | 2.37M | size_t pos1,pos2; |
232 | 2.37M | pos2 = 0; |
233 | 2.91M | while((pos1 = result.find_first_of("%", pos2))!=std::string::npos) { |
234 | 546k | std::string code; |
235 | 546k | char a,b,c; |
236 | 546k | if (pos1 + 2 > result.length()) return result; // end of result |
237 | 539k | code = result.substr(pos1+1, 2); |
238 | 539k | a = std::tolower(code[0]); b = std::tolower(code[1]); |
239 | | |
240 | 539k | if ((( '0' > a || a > '9') && ('a' > a || a > 'f')) || |
241 | 539k | (( '0' > b || b > '9') && ('a' > b || b > 'f'))) { |
242 | 531k | pos2 = pos1+3; |
243 | 531k | continue; |
244 | 531k | } |
245 | | |
246 | 8.27k | if ('0' <= a && a <= '9') a = a - '0'; |
247 | 3.91k | else if ('a' <= a && a <= 'f') a = a - 'a' + 0x0a; |
248 | 8.27k | if ('0' <= b && b <= '9') b = b - '0'; |
249 | 3.93k | else if ('a' <= b && b <= 'f') b = b - 'a' + 0x0a; |
250 | | |
251 | 8.27k | c = (a<<4)+b; |
252 | 8.27k | result = result.replace(pos1,3,1,c); |
253 | 8.27k | pos2=pos1; |
254 | 8.27k | } |
255 | 2.36M | return result; |
256 | 2.37M | }; //<! Decodes %xx from string into bytes |
257 | | |
258 | 0 | static std::string encodeURL(const std::string& component, bool asUrl = true) { |
259 | 0 | std::string result = component; |
260 | 0 | std::string skip = "+-.:,&;_#%[]?/@(){}="; |
261 | 0 | char repl[3]; |
262 | 0 | size_t pos; |
263 | 0 | for(std::string::iterator iter = result.begin(); iter != result.end(); iter++) { |
264 | 0 | if (!YaHTTP::isalnum(*iter) && (!asUrl || skip.find(*iter) == std::string::npos)) { |
265 | | // replace with different thing |
266 | 0 | pos = std::distance(result.begin(), iter); |
267 | 0 | ::snprintf(repl,3,"%02x", static_cast<unsigned char>(*iter)); |
268 | 0 | result = result.replace(pos, 1, "%", 1).insert(pos+1, repl, 2); |
269 | 0 | iter = result.begin() + pos + 2; |
270 | 0 | } |
271 | 0 | } |
272 | 0 | return result; |
273 | 0 | }; //<! Escapes any characters into %xx representation when necessary, set asUrl to false to fully encode the url |
274 | | |
275 | 0 | static std::string encodeURL(const std::wstring& component, bool asUrl = true) { |
276 | 0 | unsigned char const *p = reinterpret_cast<unsigned char const*>(&component[0]); |
277 | 0 | std::size_t s = component.size() * sizeof((*component.begin())); |
278 | 0 | std::vector<unsigned char> vec(p, p+s); |
279 | 0 |
|
280 | 0 | std::ostringstream result; |
281 | 0 | std::string skip = "+-.,&;_#%[]?/@(){}="; |
282 | 0 | for(std::vector<unsigned char>::iterator iter = vec.begin(); iter != vec.end(); iter++) { |
283 | 0 | if (!YaHTTP::isalnum((char)*iter) && (!asUrl || skip.find((char)*iter) == std::string::npos)) { |
284 | 0 | // bit more complex replace |
285 | 0 | result << "%" << std::hex << std::setw(2) << std::setfill('0') << static_cast<unsigned int>(*iter); |
286 | 0 | } else result << (char)*iter; |
287 | 0 | } |
288 | 0 | return result.str(); |
289 | 0 | }; //<! Escapes any characters into %xx representation when necessary, set asUrl to false to fully encode the url, for wide strings, returns ordinary string |
290 | | |
291 | 0 | static std::string status2text(int status) { |
292 | 0 | switch(status) { |
293 | 0 | case 200: |
294 | 0 | return "OK"; |
295 | 0 | case 201: |
296 | 0 | return "Created"; |
297 | 0 | case 202: |
298 | 0 | return "Accepted"; |
299 | 0 | case 203: |
300 | 0 | return "Non-Authoritative Information"; |
301 | 0 | case 204: |
302 | 0 | return "No Content"; |
303 | 0 | case 205: |
304 | 0 | return "Reset Content"; |
305 | 0 | case 206: |
306 | 0 | return "Partial Content"; |
307 | 0 | case 300: |
308 | 0 | return "Multiple Choices"; |
309 | 0 | case 301: |
310 | 0 | return "Moved Permanently"; |
311 | 0 | case 302: |
312 | 0 | return "Found"; |
313 | 0 | case 303: |
314 | 0 | return "See Other"; |
315 | 0 | case 304: |
316 | 0 | return "Not Modified"; |
317 | 0 | case 305: |
318 | 0 | return "Use Proxy"; |
319 | 0 | case 307: |
320 | 0 | return "Temporary Redirect"; |
321 | 0 | case 400: |
322 | 0 | return "Bad Request"; |
323 | 0 | case 401: |
324 | 0 | return "Unauthorized"; |
325 | 0 | case 402: |
326 | 0 | return "Payment Required"; |
327 | 0 | case 403: |
328 | 0 | return "Forbidden"; |
329 | 0 | case 404: |
330 | 0 | return "Not Found"; |
331 | 0 | case 405: |
332 | 0 | return "Method Not Allowed"; |
333 | 0 | case 406: |
334 | 0 | return "Not Acceptable"; |
335 | 0 | case 407: |
336 | 0 | return "Proxy Authentication Required"; |
337 | 0 | case 408: |
338 | 0 | return "Request Time-out"; |
339 | 0 | case 409: |
340 | 0 | return "Conflict"; |
341 | 0 | case 410: |
342 | 0 | return "Gone"; |
343 | 0 | case 411: |
344 | 0 | return "Length Required"; |
345 | 0 | case 412: |
346 | 0 | return "Precondition Failed"; |
347 | 0 | case 413: |
348 | 0 | return "Request Entity Too Large"; |
349 | 0 | case 414: |
350 | 0 | return "Request-URI Too Large"; |
351 | 0 | case 415: |
352 | 0 | return "Unsupported Media Type"; |
353 | 0 | case 416: |
354 | 0 | return "Requested range not satisfiable"; |
355 | 0 | case 417: |
356 | 0 | return "Expectation Failed"; |
357 | 0 | case 422: |
358 | 0 | return "Unprocessable Entity"; |
359 | 0 | case 500: |
360 | 0 | return "Internal Server Error"; |
361 | 0 | case 501: |
362 | 0 | return "Not Implemented"; |
363 | 0 | case 502: |
364 | 0 | return "Bad Gateway"; |
365 | 0 | case 503: |
366 | 0 | return "Service Unavailable"; |
367 | 0 | case 504: |
368 | 0 | return "Gateway Time-out"; |
369 | 0 | case 505: |
370 | 0 | return "HTTP Version not supported"; |
371 | 0 | default: |
372 | 0 | return "Unknown Status"; |
373 | 0 | } |
374 | 0 | }; //<! static HTTP codes to text mappings |
375 | | |
376 | 4.13k | static strstr_map_t parseUrlParameters(const std::string& parameters) { |
377 | 4.13k | if (parameters.size() > YAHTTP_MAX_REQUEST_LINE_SIZE) { |
378 | 5 | return {}; |
379 | 5 | } |
380 | 4.12k | std::string::size_type pos = 0; |
381 | 4.12k | strstr_map_t parameter_map; |
382 | 21.0k | while (pos != std::string::npos) { |
383 | | // find next parameter start |
384 | 21.0k | std::string::size_type nextpos = parameters.find("&", pos); |
385 | 21.0k | std::string::size_type delim = parameters.find("=", pos); |
386 | 21.0k | if (delim > nextpos) { |
387 | 15.1k | delim = nextpos; |
388 | 15.1k | } |
389 | 21.0k | std::string key; |
390 | 21.0k | std::string value; |
391 | 21.0k | if (delim == std::string::npos) { |
392 | 3.87k | key = parameters.substr(pos); |
393 | 17.1k | } else { |
394 | 17.1k | key = parameters.substr(pos, delim-pos); |
395 | 17.1k | if (nextpos == std::string::npos) { |
396 | 121 | value = parameters.substr(delim+1); |
397 | 17.0k | } else { |
398 | 17.0k | value = parameters.substr(delim+1, nextpos-delim-1); |
399 | 17.0k | } |
400 | 17.1k | } |
401 | 21.0k | if (key.empty()) { |
402 | | // no parameters at all |
403 | 3.39k | break; |
404 | 3.39k | } |
405 | 17.6k | parameter_map[decodeURL(key)] = decodeURL(value); |
406 | 17.6k | if (nextpos == std::string::npos) { |
407 | | // no more parameters left |
408 | 726 | break; |
409 | 726 | } |
410 | 16.8k | if (parameter_map.size() >= YAHTTP_MAX_REQUEST_FIELDS) { |
411 | 5 | break; |
412 | 5 | } |
413 | | |
414 | 16.8k | pos = nextpos+1; |
415 | 16.8k | } |
416 | 4.12k | return parameter_map; |
417 | 4.13k | }; //<! parses URL parameters into string map |
418 | | |
419 | 130 | static bool iequals(const std::string& a, const std::string& b, size_t length) { |
420 | 130 | std::string::const_iterator ai, bi; |
421 | 130 | size_t i; |
422 | 3.50k | for(ai = a.begin(), bi = b.begin(), i = 0; ai != a.end() && bi != b.end() && i < length; ai++,bi++,i++) { |
423 | 3.38k | if (::toupper(*ai) != ::toupper(*bi)) return false; |
424 | 3.38k | } |
425 | | |
426 | 118 | if (ai == a.end() && bi == b.end()) return true; |
427 | 118 | if ((ai == a.end() && bi != b.end()) || |
428 | 118 | (ai != a.end() && bi == b.end())) return false; |
429 | | |
430 | 100 | return ::toupper(*ai) == ::toupper(*bi); |
431 | 118 | }; //<! case-insensitive comparison with length |
432 | | |
433 | 0 | static bool iequals(const std::string& a, const std::string& b) { |
434 | 0 | if (a.size() != b.size()) return false; |
435 | 0 | return iequals(a,b,a.size()); |
436 | 0 | }; //<! case-insensitive comparison |
437 | | |
438 | 26.3k | static void trimLeft(std::string &str) { |
439 | 26.3k | const std::locale &loc = std::locale::classic(); |
440 | 26.3k | std::string::iterator iter = str.begin(); |
441 | 27.6k | while(iter != str.end() && YaHTTP::isspace(*iter, loc)) iter++; |
442 | 26.3k | str.erase(str.begin(), iter); |
443 | 26.3k | }; //<! removes whitespace from left |
444 | | |
445 | 26.3k | static void trimRight(std::string &str) { |
446 | 26.3k | const std::locale &loc = std::locale::classic(); |
447 | 26.3k | std::string::reverse_iterator iter = str.rbegin(); |
448 | 29.1k | while(iter != str.rend() && YaHTTP::isspace(*iter, loc)) iter++; |
449 | 26.3k | str.erase(iter.base(), str.end()); |
450 | 26.3k | }; //<! removes whitespace from right |
451 | | |
452 | 26.3k | static void trim(std::string &str) { |
453 | 26.3k | trimLeft(str); |
454 | 26.3k | trimRight(str); |
455 | 26.3k | }; //<! removes whitespace from left and right |
456 | | |
457 | 0 | static std::string camelizeHeader(const std::string &str) { |
458 | 0 | std::string::const_iterator iter = str.begin(); |
459 | 0 | std::string result; |
460 | 0 | const std::locale &loc = std::locale::classic(); |
461 | |
|
462 | 0 | bool doNext = true; |
463 | |
|
464 | 0 | while(iter != str.end()) { |
465 | 0 | if (doNext) |
466 | 0 | result.insert(result.end(), std::toupper(*iter, loc)); |
467 | 0 | else |
468 | 0 | result.insert(result.end(), std::tolower(*iter, loc)); |
469 | 0 | doNext = (*(iter++) == '-'); |
470 | 0 | } |
471 | |
|
472 | 0 | return result; |
473 | 0 | }; //<! camelizes headers, such as, content-type => Content-Type |
474 | | }; |
475 | | }; |