toys/simple-http-server-c/source/util.c

211 lines
5.4 KiB
C

#include "util.h"
#include <stdio.h>
#include <ctype.h> /**< isalnum isdigit */
#include <string.h> /**< strncmp */
#include <time.h> /**< strftime */
#include <sys/time.h> /**< struct timeval */
#include <sys/resource.h> /**< struct rusage, getrusage */
// timeval -> microseconds
static int64_t Tv2Ms(struct timeval *p)
{
return ((int64_t)p->tv_sec) * 1000000 + (int64_t)p->tv_usec;
}
/// Render seconds since 1970 as an RFC822 date string.
/// Return a pointer to that string in a static buffer.
static char *Rfc822Date(time_t t)
{
struct tm *tm;
static char date[100];
tm = gmtime(&t);
strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S GMT", tm);
return date;
}
char *Rfc822DateNow()
{
time_t now;
time(&now);
return Rfc822Date(now);
}
/// Parse an RFC822-formatted timestamp as we'd expect from HTTP and return
/// a Unix epoch time. <= zero is returned on failure.
time_t ParseRfc822Date(const char *zDate)
{
int mday, mon, year, yday, hour, min, sec;
char zIgnore[4];
char zMonth[4];
static const char *const azMonths[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
if (7 == sscanf(zDate, "%3[A-Za-z], %d %3[A-Za-z] %d %d:%d:%d", zIgnore,
&mday, zMonth, &year, &hour, &min, &sec))
{
if (year > 1900) year -= 1900;
for (mon = 0; mon < 12; mon++)
{
if(!strncmp(azMonths[mon], zMonth, 3))
{
int nDay;
int isLeapYr;
static int priorDays[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
isLeapYr = year % 4 == 0 && (year % 100 != 0 || (year + 300) % 400==0);
yday = priorDays[mon] + mday - 1;
if(isLeapYr && mon > 1) yday++;
nDay = (year - 70) * 365 + (year - 69) / 4 - year / 100 + (year + 300) / 400 + yday;
return ((time_t)(nDay * 24 + hour) * 60 + min) * 60 + sec;
}
}
}
return 0;
}
void LogPrint(int lineNum)
{
struct timeval now;
struct tm *ptm;
struct rusage self;
size_t sz;
char date[200];
gettimeofday(&now, 0);
ptm = localtime(&now.tv_sec);
strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", ptm);
getrusage(RUSAGE_SELF, &self);
/* Log record:
** (1) Date and time
** (2) IP address
** (3) URL being accessed
** (4) Referer
** (5) Reply status
** (6) Bytes received
** (7) Bytes sent
** (8) Self user time
** (9) Self system time
** (10) Total wall-clock time
** (11) Request number for same TCP/IP connection
** (12) User agent
** (13) Remote user
** (14) Bytes of URL that correspond to the SCRIPT_NAME
** (15) Line number in source file
*/
// fprintf(stdout, "%s,%s,\"%s://%s%s\",\"%s\","
// "%s,%d,%d,%lld,%lld,%lld,%lld,%lld,%d,\"%s\",\"%s\",%d,%d\n",
// zDate, zRemoteAddr, zHttpScheme, Escape(zHttpHost), Escape(zScript),
// Escape(zReferer), zReplyStatus, nIn, nOut,
// Tv2Ms(&self.ru_utime) - Tv2Ms(&priorSelf.ru_utime),
// Tv2Ms(&self.ru_stime) - Tv2Ms(&priorSelf.ru_stime),
// Tv2Ms(&now) - Tv2Ms(&beginTime),
// nRequest, Escape(zAgent), Escape(zRM),
// (int)(strlen(zHttpScheme)+strlen(zHttpHost)+strlen(zRealScript)+3),
// lineNum);
// priorSelf = self;
}
int HttpUrlEncodeWithLength(const char *src, size_t srcsize, char *dst, size_t dstsize)
{
static const char *kDontEscape = "._-$,;~()";
static const char *kHex = "0123456789abcdef";
size_t i = 0;
int ch;
char *pos;
const char *end;
if (dst == NULL || dstsize < 1)
{
return 0;
}
if (src == NULL || srcsize == 0)
{
dst[0] = 0;
return 0;
}
pos = dst;
end = dst + dstsize - 1;
while (i < srcsize && pos < end)
{
ch = src[i];
if (isalnum(ch) || strchr(kDontEscape, ch) != NULL)
{
*pos = (char)ch;
}
else if (pos + 2 < end)
{
pos[0] = '%';
pos[1] = kHex[ch >> 4];
pos[2] = kHex[ch & 0xf];
pos += 2;
}
else
{
break;
}
pos++;
i++;
}
*pos = 0;
return (i == srcsize) ? (int)(pos - dst) : -1;
}
int HttpUrlDecodeWithFormat(const char *src, char *dst, size_t dstsize, int is_form_url_encoded)
{
#define HEXTOI(x) (isdigit(x) ? ((x) - '0') : ((x) - 'W'))
int i = 0, j = 0, a, b;
int srcsize, dstlen;
if (dst == NULL || dstsize < 1)
{
return 0;
}
if (src == NULL)
{
dst[0] = 0;
return 0;
}
srcsize = (int)strlen(src);
dstlen = (int)dstsize;
while (i < srcsize && j < dstlen - 1)
{
if (i < srcsize - 2 && src[i] == '%' && isdigit(*(const uint8_t *)(src + i + 1)) && isxdigit(*(const uint8_t *)(src + i + 2)))
{
a = tolower(*(const uint8_t *)(src + i + 1));
b = tolower(*(const uint8_t *)(src + i + 2));
dst[j] = (char)((HEXTOI(a) << 4) | HEXTOI(b));
i += 2;
}
else if (is_form_url_encoded && src[i] == '+')
{
dst[j] = ' ';
}
else
{
dst[j] = src[i];
}
i++;
j++;
}
dst[j] = 0;
return (i >= srcsize) ? j : -1;
}