| /* |
| copyright: Boaz segev, 2016-2017 |
| license: MIT |
| |
| Feel free to copy, use and enjoy according to the license provided. |
| */ |
| #include "http.h" |
| |
| /** |
| A faster (yet less localized) alternative to `gmtime_r`. |
| |
| See the libc `gmtime_r` documentation for details. |
| |
| Falls back to `gmtime_r` for dates before epoch. |
| */ |
| struct tm *http_gmtime(const time_t *timer, struct tm *tmbuf) { |
| // static char* DAYS[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; |
| // static char * Months = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", |
| // "Jul", |
| // "Aug", "Sep", "Oct", "Nov", "Dec"}; |
| static uint8_t month_len[] = { |
| 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, // nonleap year |
| 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 // leap year |
| }; |
| if (*timer < 0) |
| return gmtime_r(timer, tmbuf); |
| ssize_t tmp; |
| tmbuf->tm_gmtoff = 0; |
| tmbuf->tm_zone = "UTC"; |
| tmbuf->tm_isdst = 0; |
| tmbuf->tm_year = 70; // tm_year == The number of years since 1900 |
| tmbuf->tm_mon = 0; |
| // for seconds up to weekdays, we build up, as small values clean up larger |
| // values. |
| tmp = ((ssize_t)*timer); |
| tmbuf->tm_sec = tmp % 60; |
| tmp = tmp / 60; |
| tmbuf->tm_min = tmp % 60; |
| tmp = tmp / 60; |
| tmbuf->tm_hour = tmp % 24; |
| tmp = tmp / 24; |
| // day of epoch was a thursday. Add + 3 so sunday == 0... |
| tmbuf->tm_wday = (tmp + 3) % 7; |
| // tmp == number of days since epoch |
| #define DAYS_PER_400_YEARS ((400 * 365) + 97) |
| while (tmp >= DAYS_PER_400_YEARS) { |
| tmbuf->tm_year += 400; |
| tmp -= DAYS_PER_400_YEARS; |
| } |
| #undef DAYS_PER_400_YEARS |
| #define DAYS_PER_100_YEARS ((100 * 365) + 24) |
| while (tmp >= DAYS_PER_100_YEARS) { |
| tmbuf->tm_year += 100; |
| tmp -= DAYS_PER_100_YEARS; |
| if (((tmbuf->tm_year / 100) & 3) == |
| 0) // leap century divisable by 400 => add leap |
| --tmp; |
| } |
| #undef DAYS_PER_100_YEARS |
| #define DAYS_PER_32_YEARS ((32 * 365) + 8) |
| while (tmp >= DAYS_PER_32_YEARS) { |
| tmbuf->tm_year += 32; |
| tmp -= DAYS_PER_32_YEARS; |
| } |
| #undef DAYS_PER_32_YEARS |
| #define DAYS_PER_8_YEARS ((8 * 365) + 2) |
| while (tmp >= DAYS_PER_8_YEARS) { |
| tmbuf->tm_year += 8; |
| tmp -= DAYS_PER_8_YEARS; |
| } |
| #undef DAYS_PER_8_YEARS |
| #define DAYS_PER_4_YEARS ((4 * 365) + 1) |
| while (tmp >= DAYS_PER_4_YEARS) { |
| tmbuf->tm_year += 4; |
| tmp -= DAYS_PER_4_YEARS; |
| } |
| #undef DAYS_PER_4_YEARS |
| while (tmp >= 365) { |
| tmbuf->tm_year += 1; |
| tmp -= 365; |
| if ((tmbuf->tm_year & 3) == 0) { // leap year |
| if (tmp > 0) { |
| --tmp; |
| continue; |
| } else { |
| tmp += 365; |
| --tmbuf->tm_year; |
| break; |
| } |
| } |
| } |
| tmbuf->tm_yday = tmp; |
| if ((tmbuf->tm_year & 3) == 1) { |
| // regular year |
| for (size_t i = 0; i < 12; i++) { |
| if (tmp < month_len[i]) |
| break; |
| tmp -= month_len[i]; |
| ++tmbuf->tm_mon; |
| } |
| } else { |
| // leap year |
| for (size_t i = 12; i < 24; i++) { |
| if (tmp < month_len[i]) |
| break; |
| tmp -= month_len[i]; |
| ++tmbuf->tm_mon; |
| } |
| } |
| tmbuf->tm_mday = tmp; |
| return tmbuf; |
| } |
| |
| static char *DAY_NAMES[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; |
| static char *MONTH_NAMES[] = {"Jan ", "Feb ", "Mar ", "Apr ", "May ", "Jun ", |
| "Jul ", "Aug ", "Sep ", "Oct ", "Nov ", "Dec "}; |
| static const char *GMT_STR = "GMT"; |
| |
| size_t http_date2str(char *target, struct tm *tmbuf) { |
| char *pos = target; |
| uint16_t tmp; |
| *(uint32_t *)pos = *((uint32_t *)DAY_NAMES[tmbuf->tm_wday]); |
| pos[3] = ','; |
| pos[4] = ' '; |
| pos += 5; |
| if (tmbuf->tm_mday < 10) { |
| *pos = '0' + tmbuf->tm_mday; |
| ++pos; |
| } else { |
| tmp = tmbuf->tm_mday / 10; |
| pos[0] = '0' + tmp; |
| pos[1] = '0' + (tmbuf->tm_mday - (tmp * 10)); |
| pos += 2; |
| } |
| *(pos++) = ' '; |
| *(uint32_t *)pos = *((uint32_t *)MONTH_NAMES[tmbuf->tm_mon]); |
| pos += 4; |
| // write year. |
| pos += http_ul2a(pos, tmbuf->tm_year + 1900); |
| *(pos++) = ' '; |
| tmp = tmbuf->tm_hour / 10; |
| pos[0] = '0' + tmp; |
| pos[1] = '0' + (tmbuf->tm_hour - (tmp * 10)); |
| pos[2] = ':'; |
| tmp = tmbuf->tm_min / 10; |
| pos[3] = '0' + tmp; |
| pos[4] = '0' + (tmbuf->tm_min - (tmp * 10)); |
| pos[5] = ':'; |
| tmp = tmbuf->tm_sec / 10; |
| pos[6] = '0' + tmp; |
| pos[7] = '0' + (tmbuf->tm_sec - (tmp * 10)); |
| pos += 8; |
| pos[0] = ' '; |
| *((uint32_t *)(pos + 1)) = *((uint32_t *)GMT_STR); |
| pos += 4; |
| return pos - target; |
| } |
| |
| /* Credit to Jonathan Leffler for the idea of a unified conditional */ |
| #define hex_val(c) \ |
| (((c) >= '0' && (c) <= '9') \ |
| ? ((c)-48) \ |
| : (((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F')) \ |
| ? (((c) | 32) - 87) \ |
| : ({ \ |
| return -1; \ |
| 0; \ |
| })) |
| ssize_t http_decode_url(char *dest, const char *url_data, size_t length) { |
| char *pos = dest; |
| const char *end = url_data + length; |
| while (url_data < end) { |
| if (*url_data == '+') { |
| // decode space |
| *(pos++) = ' '; |
| ++url_data; |
| } else if (*url_data == '%') { |
| // decode hex value |
| // this is a percent encoded value. |
| *(pos++) = (hex_val(url_data[1]) << 4) | hex_val(url_data[2]); |
| url_data += 3; |
| } else |
| *(pos++) = *(url_data++); |
| } |
| *pos = 0; |
| return pos - dest; |
| } |
| |
| ssize_t http_decode_url_unsafe(char *dest, const char *url_data) { |
| char *pos = dest; |
| while (*url_data) { |
| if (*url_data == '+') { |
| // decode space |
| *(pos++) = ' '; |
| ++url_data; |
| } else if (*url_data == '%') { |
| // decode hex value |
| // this is a percent encoded value. |
| *(pos++) = (hex_val(url_data[1]) << 4) | hex_val(url_data[2]); |
| url_data += 3; |
| } else |
| *(pos++) = *(url_data++); |
| } |
| *pos = 0; |
| return pos - dest; |
| } |
| #undef hex_val |