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