More style changes, fixed most of the compiler warnings.

This commit is contained in:
Emil Mikulic 2011-01-15 20:08:16 +11:00
parent 0442d41dab
commit 7343ecf35f

View File

@ -93,6 +93,7 @@ static const int debug = 1;
/* [<-] */ /* [<-] */
CTASSERT(sizeof(unsigned long long) >= sizeof(off_t)); CTASSERT(sizeof(unsigned long long) >= sizeof(off_t));
#define llu(x) ((unsigned long long)(x))
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__linux) #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__linux)
#include <err.h> #include <err.h>
@ -1091,7 +1092,7 @@ static void log_connection(const struct connection *conn) {
fprintf(logfile, "%lu\t%s\t%s\t%s\t%d\t%llu\t\"%s\"\t\"%s\"\n", fprintf(logfile, "%lu\t%s\t%s\t%s\t%d\t%llu\t\"%s\"\t\"%s\"\n",
(unsigned long int)now, inet_ntoa(inaddr), (unsigned long int)now, inet_ntoa(inaddr),
conn->method, conn->uri, conn->method, conn->uri,
conn->http_code, (unsigned long long)conn->total_sent, conn->http_code, llu(conn->total_sent),
(conn->referer == NULL)?"":conn->referer, (conn->referer == NULL)?"":conn->referer,
(conn->user_agent == NULL)?"":conn->user_agent (conn->user_agent == NULL)?"":conn->user_agent
); );
@ -1188,11 +1189,10 @@ static char *rfc1123_date(char *dest, const time_t when) {
* character it represents. Don't forget to free the return value. * character it represents. Don't forget to free the return value.
*/ */
static char *urldecode(const char *url) { static char *urldecode(const char *url) {
size_t len = strlen(url); size_t i, pos, len = strlen(url);
char *out = xmalloc(len+1); char *out = xmalloc(len+1);
int pos;
for (size_t i = 0, pos = 0; i < len; i++) { for (i = 0, pos = 0; i < len; i++) {
if ((url[i] == '%') && (i+2 < len) && if ((url[i] == '%') && (i+2 < len) &&
isxdigit(url[i+1]) && isxdigit(url[i+2])) { isxdigit(url[i+1]) && isxdigit(url[i+2])) {
/* decode %XX */ /* decode %XX */
@ -1214,12 +1214,12 @@ static char *urldecode(const char *url) {
return out; return out;
} }
/* --------------------------------------------------------------------------- /* A default reply for any (erroneous) occasion. */
* A default reply for any (erroneous) occasion.
*/
static void default_reply(struct connection *conn, static void default_reply(struct connection *conn,
const int errcode, const char *errname, const char *format, ...) const int errcode, const char *errname, const char *format, ...)
{ __printflike(4, 5);
static void default_reply(struct connection *conn,
const int errcode, const char *errname, const char *format, ...) {
char *reason, date[DATE_LEN]; char *reason, date[DATE_LEN];
va_list va; va_list va;
@ -1245,23 +1245,19 @@ static void default_reply(struct connection *conn,
"Date: %s\r\n" "Date: %s\r\n"
"Server: %s\r\n" "Server: %s\r\n"
"%s" /* keep-alive */ "%s" /* keep-alive */
"Content-Length: %d\r\n" "Content-Length: %llu\r\n"
"Content-Type: text/html\r\n" "Content-Type: text/html\r\n"
"\r\n", "\r\n",
errcode, errname, date, pkgname, keep_alive(conn), errcode, errname, date, pkgname, keep_alive(conn),
conn->reply_length); llu(conn->reply_length));
conn->reply_type = REPLY_GENERATED; conn->reply_type = REPLY_GENERATED;
conn->http_code = errcode; conn->http_code = errcode;
} }
/* ---------------------------------------------------------------------------
* Redirection.
*/
static void redirect(struct connection *conn, const char *format, ...) static void redirect(struct connection *conn, const char *format, ...)
{ __printflike(2, 3);
static void redirect(struct connection *conn, const char *format, ...) {
char *where, date[DATE_LEN]; char *where, date[DATE_LEN];
va_list va; va_list va;
@ -1287,76 +1283,69 @@ static void redirect(struct connection *conn, const char *format, ...)
"Server: %s\r\n" "Server: %s\r\n"
"Location: %s\r\n" "Location: %s\r\n"
"%s" /* keep-alive */ "%s" /* keep-alive */
"Content-Length: %d\r\n" "Content-Length: %llu\r\n"
"Content-Type: text/html\r\n" "Content-Type: text/html\r\n"
"\r\n", "\r\n",
date, pkgname, where, keep_alive(conn), conn->reply_length); date, pkgname, where, keep_alive(conn), llu(conn->reply_length));
free(where); free(where);
conn->reply_type = REPLY_GENERATED; conn->reply_type = REPLY_GENERATED;
conn->http_code = 301; conn->http_code = 301;
} }
/* Parses a single HTTP request field. Returns string from end of [field] to
/* ---------------------------------------------------------------------------
* Parses a single HTTP request field. Returns string from end of [field] to
* first \r, \n or end of request string. Returns NULL if [field] can't be * first \r, \n or end of request string. Returns NULL if [field] can't be
* matched. * matched.
* *
* You need to remember to deallocate the result. * You need to remember to deallocate the result.
* example: parse_field(conn, "Referer: "); * example: parse_field(conn, "Referer: ");
*/ */
static char *parse_field(const struct connection *conn, const char *field) static char *parse_field(const struct connection *conn, const char *field) {
{
size_t bound1, bound2; size_t bound1, bound2;
char *pos; char *pos;
/* find start */ /* find start */
pos = strstr(conn->request, field); pos = strstr(conn->request, field);
if (pos == NULL) return NULL; if (pos == NULL)
bound1 = pos - conn->request + strlen(field); return NULL;
assert(pos >= conn->request);
bound1 = (size_t)(pos - conn->request) + strlen(field);
/* find end */ /* find end */
for (bound2 = bound1; for (bound2 = bound1;
conn->request[bound2] != '\r' && (conn->request[bound2] != '\r') &&
bound2 < conn->request_length; bound2++) (bound2 < conn->request_length); bound2++)
; ;
/* copy to buffer */ /* copy to buffer */
return split_string(conn->request, bound1, bound2); return split_string(conn->request, bound1, bound2);
} }
/* Parse a Range: field into range_begin and range_end. Only handles the
/* ---------------------------------------------------------------------------
* Parse a Range: field into range_begin and range_end. Only handles the
* first range if a list is given. Sets range_{begin,end}_given to 1 if * first range if a list is given. Sets range_{begin,end}_given to 1 if
* either part of the range is given. * either part of the range is given.
*/ */
static void parse_range_field(struct connection *conn) static void parse_range_field(struct connection *conn) {
{
size_t bound1, bound2, len; size_t bound1, bound2, len;
char *range; char *range;
range = parse_field(conn, "Range: bytes="); range = parse_field(conn, "Range: bytes=");
if (range == NULL) return; if (range == NULL)
return;
len = strlen(range); len = strlen(range);
do /* break handling */ do {
{
/* parse number up to hyphen */ /* parse number up to hyphen */
bound1 = 0; bound1 = 0;
for (bound2=0; for (bound2=0;
isdigit( (int)range[bound2] ) && bound2 < len; isdigit((int)range[bound2]) && (bound2 < len);
bound2++) bound2++)
; ;
if (bound2 == len || range[bound2] != '-') if ((bound2 == len) || (range[bound2] != '-'))
break; /* there must be a hyphen here */ break; /* there must be a hyphen here */
if (bound1 != bound2) if (bound1 != bound2) {
{
conn->range_begin_given = 1; conn->range_begin_given = 1;
conn->range_begin = (off_t)strtoll(range+bound1, NULL, 10); conn->range_begin = (off_t)strtoll(range+bound1, NULL, 10);
} }
@ -1364,89 +1353,94 @@ static void parse_range_field(struct connection *conn)
/* parse number after hyphen */ /* parse number after hyphen */
bound2++; bound2++;
for (bound1=bound2; for (bound1=bound2;
isdigit( (int)range[bound2] ) && bound2 < len; isdigit((int)range[bound2]) && (bound2 < len);
bound2++) bound2++)
; ;
if (bound2 != len && range[bound2] != ',') if ((bound2 != len) && (range[bound2] != ','))
break; /* must be end of string or a list to be valid */ break; /* must be end of string or a list to be valid */
if (bound1 != bound2) if (bound1 != bound2) {
{
conn->range_end_given = 1; conn->range_end_given = 1;
conn->range_end = (off_t)strtoll(range+bound1, NULL, 10); conn->range_end = (off_t)strtoll(range+bound1, NULL, 10);
} }
} } while(0); /* break handling */
while(0); /* break handling */
free(range); free(range);
/* sanity check: begin <= end */ /* sanity check: begin <= end */
if (conn->range_begin_given && conn->range_end_given && if (conn->range_begin_given && conn->range_end_given &&
(conn->range_begin > conn->range_end)) (conn->range_begin > conn->range_end)) {
{
conn->range_begin_given = conn->range_end_given = 0; conn->range_begin_given = conn->range_end_given = 0;
} }
} }
/* Parse an HTTP request like "GET / HTTP/1.1" to get the method (GET), the
/* ---------------------------------------------------------------------------
* Parse an HTTP request like "GET / HTTP/1.1" to get the method (GET), the
* url (/), the referer (if given) and the user-agent (if given). Remember to * url (/), the referer (if given) and the user-agent (if given). Remember to
* deallocate all these buffers. The method will be returned in uppercase. * deallocate all these buffers. The method will be returned in uppercase.
*/ */
static int parse_request(struct connection *conn) static int parse_request(struct connection *conn) {
{
size_t bound1, bound2; size_t bound1, bound2;
char *tmp; char *tmp;
assert(conn->request_length == strlen(conn->request)); assert(conn->request_length == strlen(conn->request));
/* parse method */ /* parse method */
for (bound1 = 0; bound1 < conn->request_length && for (bound1 = 0;
conn->request[bound1] != ' '; bound1++) (bound1 < conn->request_length) &&
(conn->request[bound1] != ' ');
bound1++)
; ;
conn->method = split_string(conn->request, 0, bound1); conn->method = split_string(conn->request, 0, bound1);
strntoupper(conn->method, bound1); strntoupper(conn->method, bound1);
/* parse uri */ /* parse uri */
for (; bound1 < conn->request_length && for (;
conn->request[bound1] == ' '; bound1++) (bound1 < conn->request_length) &&
(conn->request[bound1] == ' ');
bound1++)
; ;
if (bound1 == conn->request_length) return 0; /* fail */ if (bound1 == conn->request_length)
return 0; /* fail */
for (bound2=bound1+1; bound2 < conn->request_length && for (bound2 = bound1 + 1;
conn->request[bound2] != ' ' && (bound2 < conn->request_length) &&
conn->request[bound2] != '\r'; bound2++) (conn->request[bound2] != ' ') &&
(conn->request[bound2] != '\r');
bound2++)
; ;
conn->uri = split_string(conn->request, bound1, bound2); conn->uri = split_string(conn->request, bound1, bound2);
/* parse protocol to determine conn_close */ /* parse protocol to determine conn_close */
if (conn->request[bound2] == ' ') if (conn->request[bound2] == ' ') {
{
char *proto; char *proto;
for (bound1 = bound2; bound1 < conn->request_length && for (bound1 = bound2;
conn->request[bound1] == ' '; bound1++) (bound1 < conn->request_length) &&
(conn->request[bound1] == ' ');
bound1++)
; ;
for (bound2=bound1+1; bound2 < conn->request_length && for (bound2 = bound1 + 1;
conn->request[bound2] != ' ' && (bound2 < conn->request_length) &&
conn->request[bound2] != '\r'; bound2++) (conn->request[bound2] != ' ') &&
(conn->request[bound2] != '\r');
bound2++)
; ;
proto = split_string(conn->request, bound1, bound2); proto = split_string(conn->request, bound1, bound2);
if (strcasecmp(proto, "HTTP/1.1") == 0) conn->conn_close = 0; if (strcasecmp(proto, "HTTP/1.1") == 0)
conn->conn_close = 0;
free(proto); free(proto);
} }
/* parse connection field */ /* parse connection field */
tmp = parse_field(conn, "Connection: "); tmp = parse_field(conn, "Connection: ");
if (tmp != NULL) if (tmp != NULL) {
{ if (strcasecmp(tmp, "close") == 0)
if (strcasecmp(tmp, "close") == 0) conn->conn_close = 1; conn->conn_close = 1;
else if (strcasecmp(tmp, "keep-alive") == 0) conn->conn_close = 0; else if (strcasecmp(tmp, "keep-alive") == 0)
conn->conn_close = 0;
free(tmp); free(tmp);
} }
@ -1457,13 +1451,7 @@ static int parse_request(struct connection *conn)
return 1; return 1;
} }
static int file_exists(const char *path) {
/* ---------------------------------------------------------------------------
* Check if a file exists.
*/
static int file_exists(const char *path)
{
struct stat filestat; struct stat filestat;
if ((stat(path, &filestat) == -1) && (errno = ENOENT)) if ((stat(path, &filestat) == -1) && (errno = ENOENT))
return 0; return 0;
@ -1471,27 +1459,21 @@ static int file_exists(const char *path)
return 1; return 1;
} }
struct dlent {
/* ---------------------------------------------------------------------------
* Make sorted list of files in a directory. Returns number of entries, or -1
* if error occurs.
*/
struct dlent
{
char *name; char *name;
int is_dir; int is_dir;
off_t size; off_t size;
}; };
static int dlent_cmp(const void *a, const void *b) static int dlent_cmp(const void *a, const void *b) {
{ return strcmp((*((const struct dlent * const *)a))->name,
return strcmp( (*((const struct dlent * const *)a))->name, (*((const struct dlent * const *)b))->name);
(*((const struct dlent * const *)b))->name );
} }
static ssize_t make_sorted_dirlist(const char *path, struct dlent ***output) /* Make sorted list of files in a directory. Returns number of entries, or -1
{ * if error occurs.
*/
static ssize_t make_sorted_dirlist(const char *path, struct dlent ***output) {
DIR *dir; DIR *dir;
struct dirent *ent; struct dirent *ent;
size_t entries = 0, pool = 0; size_t entries = 0, pool = 0;
@ -1500,67 +1482,57 @@ static ssize_t make_sorted_dirlist(const char *path, struct dlent ***output)
struct dlent **list = NULL; struct dlent **list = NULL;
dir = opendir(path); dir = opendir(path);
if (dir == NULL) return -1; if (dir == NULL)
return -1;
currname = xmalloc(strlen(path) + MAXNAMLEN + 1); currname = xmalloc(strlen(path) + MAXNAMLEN + 1);
/* construct list */ /* construct list */
while ((ent = readdir(dir)) != NULL) while ((ent = readdir(dir)) != NULL) {
{
struct stat s; struct stat s;
if (ent->d_name[0] == '.' && ent->d_name[1] == '\0') if ((ent->d_name[0] == '.') && (ent->d_name[1] == '\0'))
continue; /* skip "." */ continue; /* skip "." */
assert(strlen(ent->d_name) <= MAXNAMLEN); assert(strlen(ent->d_name) <= MAXNAMLEN);
sprintf(currname, "%s%s", path, ent->d_name); sprintf(currname, "%s%s", path, ent->d_name);
if (stat(currname, &s) == -1) if (stat(currname, &s) == -1)
continue; /* skip un-stat-able files */ continue; /* skip un-stat-able files */
if (entries == pool) {
if (entries == pool)
{
pool += POOL_INCR; pool += POOL_INCR;
list = xrealloc(list, sizeof(struct dlent*) * pool); list = xrealloc(list, sizeof(struct dlent*) * pool);
} }
list[entries] = xmalloc(sizeof(struct dlent)); list[entries] = xmalloc(sizeof(struct dlent));
list[entries]->name = xstrdup(ent->d_name); list[entries]->name = xstrdup(ent->d_name);
list[entries]->is_dir = S_ISDIR(s.st_mode); list[entries]->is_dir = S_ISDIR(s.st_mode);
list[entries]->size = s.st_size; list[entries]->size = s.st_size;
entries++; entries++;
} }
closedir(dir);
(void)closedir(dir); /* can't error out if opendir() succeeded */
free(currname); free(currname);
qsort(list, entries, sizeof(struct dlent*), dlent_cmp); qsort(list, entries, sizeof(struct dlent*), dlent_cmp);
*output = xrealloc(list, sizeof(struct dlent*) * entries); *output = xrealloc(list, sizeof(struct dlent*) * entries);
return entries; return (ssize_t)entries;
#undef POOL_INCR #undef POOL_INCR
} }
/* Cleanly deallocate a sorted list of directory files. */
static void cleanup_sorted_dirlist(struct dlent **list, const ssize_t size) {
/* ---------------------------------------------------------------------------
* Cleanly deallocate a sorted list of directory files.
*/
static void cleanup_sorted_dirlist(struct dlent **list, const ssize_t size)
{
ssize_t i; ssize_t i;
for (i=0; i<size; i++)
{ for (i = 0; i < size; i++) {
free(list[i]->name); free(list[i]->name);
free(list[i]); free(list[i]);
} }
} }
/* Should this character be urlencoded (according to RFC1738). /* Should this character be urlencoded (according to RFC1738).
* Contirubted by nf. * Contributed by nf.
*/ */
static int needs_urlencoding(char c) { static int needs_urlencoding(unsigned char c) {
int i; unsigned int i;
static const char bad[] = "<>\"%{}|^~[]`\\;:/?@#=&"; static const char bad[] = "<>\"%{}|^~[]`\\;:/?@#=&";
for (i=0; i<sizeof(bad)-1; i++) for (i = 0; i < sizeof(bad) - 1; i++)
if (c == bad[i]) if (c == bad[i])
return 1; return 1;
@ -1572,16 +1544,14 @@ static int needs_urlencoding(char c) {
} }
/* Encode filename to be an RFC1738-compliant URL part. /* Encode filename to be an RFC1738-compliant URL part.
* Contributed by nf * Contributed by nf.
*/ */
static void urlencode_filename(char *name, char *safe_url) { static void urlencode_filename(char *name, char *safe_url) {
static const char hex[] = "0123456789ABCDEF"; static const char hex[] = "0123456789ABCDEF";
int i, j; int i, j;
for (i = j = 0; name[i] != '\0'; i++) for (i = j = 0; name[i] != '\0'; i++) {
{ if (needs_urlencoding((unsigned char)name[i])) {
if (needs_urlencoding(name[i]))
{
safe_url[j++] = '%'; safe_url[j++] = '%';
safe_url[j++] = hex[(name[i] >> 4) & 0xF]; safe_url[j++] = hex[(name[i] >> 4) & 0xF];
safe_url[j++] = hex[ name[i] & 0xF]; safe_url[j++] = hex[ name[i] & 0xF];
@ -1589,15 +1559,10 @@ static void urlencode_filename(char *name, char *safe_url) {
else else
safe_url[j++] = name[i]; safe_url[j++] = name[i];
} }
safe_url[j] = '\0'; safe_url[j] = '\0';
} }
/* --------------------------------------------------------------------------- static void generate_dir_listing(struct connection *conn, const char *path) {
* Generate directory listing.
*/
static void generate_dir_listing(struct connection *conn, const char *path)
{
char date[DATE_LEN], *spaces; char date[DATE_LEN], *spaces;
struct dlent **list; struct dlent **list;
ssize_t listsize; ssize_t listsize;
@ -1606,17 +1571,16 @@ static void generate_dir_listing(struct connection *conn, const char *path)
struct apbuf *listing = make_apbuf(); struct apbuf *listing = make_apbuf();
listsize = make_sorted_dirlist(path, &list); listsize = make_sorted_dirlist(path, &list);
if (listsize == -1) if (listsize == -1) {
{
default_reply(conn, 500, "Internal Server Error", default_reply(conn, 500, "Internal Server Error",
"Couldn't list directory: %s", strerror(errno)); "Couldn't list directory: %s", strerror(errno));
return; return;
} }
for (i=0; i<listsize; i++) for (i=0; i<listsize; i++) {
{
size_t tmp = strlen(list[i]->name); size_t tmp = strlen(list[i]->name);
if (maxlen < tmp) maxlen = tmp; if (maxlen < tmp)
maxlen = tmp;
} }
append(listing, "<html>\n<head>\n <title>"); append(listing, "<html>\n<head>\n <title>");
@ -1628,8 +1592,7 @@ static void generate_dir_listing(struct connection *conn, const char *path)
spaces = xmalloc(maxlen); spaces = xmalloc(maxlen);
memset(spaces, ' ', maxlen); memset(spaces, ' ', maxlen);
for (i=0; i<listsize; i++) for (i=0; i<listsize; i++) {
{
/* If a filename is made up of entirely unsafe chars, /* If a filename is made up of entirely unsafe chars,
* the url would be three times its original length. * the url would be three times its original length.
*/ */
@ -1645,10 +1608,9 @@ static void generate_dir_listing(struct connection *conn, const char *path)
if (list[i]->is_dir) if (list[i]->is_dir)
append(listing, "/\n"); append(listing, "/\n");
else else {
{
appendl(listing, spaces, maxlen-strlen(list[i]->name)); appendl(listing, spaces, maxlen-strlen(list[i]->name));
appendf(listing, "%10lld\n", (long long)list[i]->size); appendf(listing, "%10llu\n", llu(list[i]->size));
} }
} }
@ -1675,22 +1637,17 @@ static void generate_dir_listing(struct connection *conn, const char *path)
"Date: %s\r\n" "Date: %s\r\n"
"Server: %s\r\n" "Server: %s\r\n"
"%s" /* keep-alive */ "%s" /* keep-alive */
"Content-Length: %u\r\n" "Content-Length: %llu\r\n"
"Content-Type: text/html\r\n" "Content-Type: text/html\r\n"
"\r\n", "\r\n",
date, pkgname, keep_alive(conn), conn->reply_length); date, pkgname, keep_alive(conn), llu(conn->reply_length));
conn->reply_type = REPLY_GENERATED; conn->reply_type = REPLY_GENERATED;
conn->http_code = 200; conn->http_code = 200;
} }
/* Process a GET/HEAD request. */
static void process_get(struct connection *conn) {
/* ---------------------------------------------------------------------------
* Process a GET/HEAD request
*/
static void process_get(struct connection *conn)
{
char *decoded_url, *target, *if_mod_since; char *decoded_url, *target, *if_mod_since;
char date[DATE_LEN], lastmod[DATE_LEN]; char date[DATE_LEN], lastmod[DATE_LEN];
const char *mimetype = NULL; const char *mimetype = NULL;
@ -1702,17 +1659,15 @@ static void process_get(struct connection *conn)
/* make sure it's safe */ /* make sure it's safe */
if (make_safe_uri(decoded_url) == NULL) { if (make_safe_uri(decoded_url) == NULL) {
default_reply(conn, 400, "Bad Request", default_reply(conn, 400, "Bad Request",
"You requested an invalid URI: %s", conn->uri); "You requested an invalid URI: %s", conn->uri);
free(decoded_url); free(decoded_url);
return; return;
} }
/* does it end in a slash? serve up url/index_name */ /* does it end in a slash? serve up url/index_name */
if (decoded_url[strlen(decoded_url)-1] == '/') if (decoded_url[strlen(decoded_url)-1] == '/') {
{
xasprintf(&target, "%s%s%s", wwwroot, decoded_url, index_name); xasprintf(&target, "%s%s%s", wwwroot, decoded_url, index_name);
if (!file_exists(target)) if (!file_exists(target)) {
{
free(target); free(target);
xasprintf(&target, "%s%s", wwwroot, decoded_url); xasprintf(&target, "%s%s", wwwroot, decoded_url);
generate_dir_listing(conn, target); generate_dir_listing(conn, target);
@ -1722,21 +1677,21 @@ static void process_get(struct connection *conn)
} }
mimetype = uri_content_type(index_name); mimetype = uri_content_type(index_name);
} }
else /* points to a file */ else {
{ /* points to a file */
xasprintf(&target, "%s%s", wwwroot, decoded_url); xasprintf(&target, "%s%s", wwwroot, decoded_url);
mimetype = uri_content_type(decoded_url); mimetype = uri_content_type(decoded_url);
} }
free(decoded_url); free(decoded_url);
if (debug) printf("uri=%s, target=%s, content-type=%s\n", if (debug)
conn->uri, target, mimetype); printf("uri=%s, target=%s, content-type=%s\n",
conn->uri, target, mimetype);
/* open file */ /* open file */
conn->reply_fd = open(target, O_RDONLY | O_NONBLOCK); conn->reply_fd = open(target, O_RDONLY | O_NONBLOCK);
free(target); free(target);
if (conn->reply_fd == -1) if (conn->reply_fd == -1) {
{
/* open() failed */ /* open() failed */
if (errno == EACCES) if (errno == EACCES)
default_reply(conn, 403, "Forbidden", default_reply(conn, 403, "Forbidden",
@ -1753,34 +1708,31 @@ static void process_get(struct connection *conn)
} }
/* stat the file */ /* stat the file */
if (fstat(conn->reply_fd, &filestat) == -1) if (fstat(conn->reply_fd, &filestat) == -1) {
{
default_reply(conn, 500, "Internal Server Error", default_reply(conn, 500, "Internal Server Error",
"fstat() failed: %s.", strerror(errno)); "fstat() failed: %s.", strerror(errno));
return; return;
} }
/* make sure it's a regular file */ /* make sure it's a regular file */
if (S_ISDIR(filestat.st_mode)) if (S_ISDIR(filestat.st_mode)) {
{
redirect(conn, "%s/", conn->uri); redirect(conn, "%s/", conn->uri);
return; return;
} }
else if (!S_ISREG(filestat.st_mode)) else if (!S_ISREG(filestat.st_mode)) {
{
default_reply(conn, 403, "Forbidden", "Not a regular file."); default_reply(conn, 403, "Forbidden", "Not a regular file.");
return; return;
} }
conn->reply_type = REPLY_FROMFILE; conn->reply_type = REPLY_FROMFILE;
(void) rfc1123_date(lastmod, filestat.st_mtime); rfc1123_date(lastmod, filestat.st_mtime);
/* check for If-Modified-Since, may not have to send */ /* check for If-Modified-Since, may not have to send */
if_mod_since = parse_field(conn, "If-Modified-Since: "); if_mod_since = parse_field(conn, "If-Modified-Since: ");
if (if_mod_since != NULL && if ((if_mod_since != NULL) &&
strcmp(if_mod_since, lastmod) == 0) (strcmp(if_mod_since, lastmod) == 0)) {
{ if (debug)
if (debug) printf("not modified since %s\n", if_mod_since); printf("not modified since %s\n", if_mod_since);
default_reply(conn, 304, "Not Modified", ""); default_reply(conn, 304, "Not Modified", "");
conn->header_only = 1; conn->header_only = 1;
free(if_mod_since); free(if_mod_since);
@ -1788,12 +1740,10 @@ static void process_get(struct connection *conn)
} }
free(if_mod_since); free(if_mod_since);
if (conn->range_begin_given || conn->range_end_given) if (conn->range_begin_given || conn->range_end_given) {
{
off_t from, to; off_t from, to;
if (conn->range_begin_given && conn->range_end_given) if (conn->range_begin_given && conn->range_end_given) {
{
/* 100-200 */ /* 100-200 */
from = conn->range_begin; from = conn->range_begin;
to = conn->range_end; to = conn->range_end;
@ -1802,14 +1752,12 @@ static void process_get(struct connection *conn)
if (to > (size_t)(filestat.st_size-1)) if (to > (size_t)(filestat.st_size-1))
to = filestat.st_size-1; to = filestat.st_size-1;
} }
else if (conn->range_begin_given && !conn->range_end_given) else if (conn->range_begin_given && !conn->range_end_given) {
{
/* 100- :: yields 100 to end */ /* 100- :: yields 100 to end */
from = conn->range_begin; from = conn->range_begin;
to = filestat.st_size-1; to = filestat.st_size-1;
} }
else if (!conn->range_begin_given && conn->range_end_given) else if (!conn->range_begin_given && conn->range_end_given) {
{
/* -200 :: yields last 200 */ /* -200 :: yields last 200 */
to = filestat.st_size-1; to = filestat.st_size-1;
from = to - conn->range_end + 1; from = to - conn->range_end + 1;
@ -1817,7 +1765,8 @@ static void process_get(struct connection *conn)
/* check for wrapping */ /* check for wrapping */
if (from > to) from = 0; if (from > to) from = 0;
} }
else errx(1, "internal error - from/to mismatch"); else
errx(1, "internal error - from/to mismatch");
conn->reply_start = from; conn->reply_start = from;
conn->reply_length = to - from + 1; conn->reply_length = to - from + 1;
@ -1827,80 +1776,68 @@ static void process_get(struct connection *conn)
"Date: %s\r\n" "Date: %s\r\n"
"Server: %s\r\n" "Server: %s\r\n"
"%s" /* keep-alive */ "%s" /* keep-alive */
"Content-Length: %lld\r\n" "Content-Length: %llu\r\n"
"Content-Range: bytes %lld-%lld/%lld\r\n" "Content-Range: bytes %llu-%llu/%llu\r\n"
"Content-Type: %s\r\n" "Content-Type: %s\r\n"
"Last-Modified: %s\r\n" "Last-Modified: %s\r\n"
"\r\n" "\r\n"
, ,
rfc1123_date(date, now), pkgname, keep_alive(conn), rfc1123_date(date, now), pkgname, keep_alive(conn),
(long long)conn->reply_length, (long long)from, (long long)to, llu(conn->reply_length), llu(from), llu(to),
(long long)filestat.st_size, llu(filestat.st_size), mimetype, lastmod
mimetype, lastmod
); );
conn->http_code = 206; conn->http_code = 206;
if (debug) printf("sending %lld-%lld/%lld\n", if (debug)
(long long)from, (long long)to, printf("sending %llu-%llu/%llu\n",
(long long)filestat.st_size); llu(from), llu(to), llu(filestat.st_size));
} }
else /* no range stuff */ else {
{ /* no range stuff */
conn->reply_length = filestat.st_size; conn->reply_length = filestat.st_size;
conn->header_length = xasprintf(&(conn->header), conn->header_length = xasprintf(&(conn->header),
"HTTP/1.1 200 OK\r\n" "HTTP/1.1 200 OK\r\n"
"Date: %s\r\n" "Date: %s\r\n"
"Server: %s\r\n" "Server: %s\r\n"
"%s" /* keep-alive */ "%s" /* keep-alive */
"Content-Length: %lld\r\n" "Content-Length: %llu\r\n"
"Content-Type: %s\r\n" "Content-Type: %s\r\n"
"Last-Modified: %s\r\n" "Last-Modified: %s\r\n"
"\r\n" "\r\n"
, ,
rfc1123_date(date, now), pkgname, keep_alive(conn), rfc1123_date(date, now), pkgname, keep_alive(conn),
(long long)conn->reply_length, mimetype, lastmod llu(conn->reply_length), mimetype, lastmod
); );
conn->http_code = 200; conn->http_code = 200;
} }
} }
/* Process a request: build the header and reply, advance state. */
static void process_request(struct connection *conn) {
/* ---------------------------------------------------------------------------
* Process a request: build the header and reply, advance state.
*/
static void process_request(struct connection *conn)
{
num_requests++; num_requests++;
if (!parse_request(conn)) if (!parse_request(conn)) {
{
default_reply(conn, 400, "Bad Request", default_reply(conn, 400, "Bad Request",
"You sent a request that the server couldn't understand."); "You sent a request that the server couldn't understand.");
} }
else if (strcmp(conn->method, "GET") == 0) else if (strcmp(conn->method, "GET") == 0) {
{
process_get(conn); process_get(conn);
} }
else if (strcmp(conn->method, "HEAD") == 0) else if (strcmp(conn->method, "HEAD") == 0) {
{
process_get(conn); process_get(conn);
conn->header_only = 1; conn->header_only = 1;
} }
else if (strcmp(conn->method, "OPTIONS") == 0 || else if ((strcmp(conn->method, "OPTIONS") == 0) ||
strcmp(conn->method, "POST") == 0 || (strcmp(conn->method, "POST") == 0) ||
strcmp(conn->method, "PUT") == 0 || (strcmp(conn->method, "PUT") == 0) ||
strcmp(conn->method, "DELETE") == 0 || (strcmp(conn->method, "DELETE") == 0) ||
strcmp(conn->method, "TRACE") == 0 || (strcmp(conn->method, "TRACE") == 0) ||
strcmp(conn->method, "CONNECT") == 0) (strcmp(conn->method, "CONNECT") == 0)) {
{
default_reply(conn, 501, "Not Implemented", default_reply(conn, 501, "Not Implemented",
"The method you specified (%s) is not implemented.", "The method you specified (%s) is not implemented.",
conn->method); conn->method);
} }
else else {
{
default_reply(conn, 400, "Bad Request", default_reply(conn, 400, "Bad Request",
"%s is not a valid HTTP/1.1 method.", conn->method); "%s is not a valid HTTP/1.1 method.", conn->method);
} }
/* advance state */ /* advance state */
@ -1911,21 +1848,17 @@ static void process_request(struct connection *conn)
conn->request = NULL; /* important: don't free it again later */ conn->request = NULL; /* important: don't free it again later */
} }
/* Receiving request. */
static void poll_recv_request(struct connection *conn) {
/* ---------------------------------------------------------------------------
* Receiving request.
*/
static void poll_recv_request(struct connection *conn)
{
#define BUFSIZE 65536 #define BUFSIZE 65536
char buf[BUFSIZE]; char buf[BUFSIZE];
ssize_t recvd; ssize_t recvd;
assert(conn->state == RECV_REQUEST); assert(conn->state == RECV_REQUEST);
recvd = recv(conn->socket, buf, BUFSIZE, 0); recvd = recv(conn->socket, buf, BUFSIZE, 0);
if (debug) printf("poll_recv_request(%d) got %d bytes\n", if (debug)
conn->socket, (int)recvd); printf("poll_recv_request(%d) got %d bytes\n",
conn->socket, (int)recvd);
if (recvd < 1) { if (recvd < 1) {
if (recvd == -1) { if (recvd == -1) {
if (errno == EAGAIN) { if (errno == EAGAIN) {
@ -1944,8 +1877,8 @@ static void poll_recv_request(struct connection *conn)
/* append to conn->request */ /* append to conn->request */
assert(recvd > 0); assert(recvd > 0);
conn->request = xrealloc(conn->request, conn->request = xrealloc(
conn->request_length + (size_t)recvd + 1); conn->request, conn->request_length + (size_t)recvd + 1);
memcpy(conn->request+conn->request_length, buf, (size_t)recvd); memcpy(conn->request+conn->request_length, buf, (size_t)recvd);
conn->request_length += (size_t)recvd; conn->request_length += (size_t)recvd;
conn->request[conn->request_length] = 0; conn->request[conn->request_length] = 0;
@ -1959,11 +1892,10 @@ static void poll_recv_request(struct connection *conn)
(memcmp(conn->request+conn->request_length-4, "\r\n\r\n", 4) == 0)) (memcmp(conn->request+conn->request_length-4, "\r\n\r\n", 4) == 0))
process_request(conn); process_request(conn);
/* die if it's too long */ /* die if it's too large */
if (conn->request_length > MAX_REQUEST_LENGTH) if (conn->request_length > MAX_REQUEST_LENGTH) {
{
default_reply(conn, 413, "Request Entity Too Large", default_reply(conn, 413, "Request Entity Too Large",
"Your request was dropped because it was too long."); "Your request was dropped because it was too long.");
conn->state = SEND_HEADER; conn->state = SEND_HEADER;
} }
@ -1974,23 +1906,21 @@ static void poll_recv_request(struct connection *conn)
poll_send_header(conn); poll_send_header(conn);
} }
/* Sending header. Assumes conn->header is not NULL. */
static void poll_send_header(struct connection *conn) {
/* ---------------------------------------------------------------------------
* Sending header. Assumes conn->header is not NULL.
*/
static void poll_send_header(struct connection *conn)
{
ssize_t sent; ssize_t sent;
assert(conn->state == SEND_HEADER); assert(conn->state == SEND_HEADER);
assert(conn->header_length == strlen(conn->header)); assert(conn->header_length == strlen(conn->header));
sent = send(conn->socket, conn->header + conn->header_sent, sent = send(conn->socket,
conn->header_length - conn->header_sent, 0); conn->header + conn->header_sent,
conn->header_length - conn->header_sent,
0);
conn->last_active = now; conn->last_active = now;
if (debug) printf("poll_send_header(%d) sent %d bytes\n", if (debug)
conn->socket, (int)sent); printf("poll_send_header(%d) sent %d bytes\n",
conn->socket, (int)sent);
/* handle any errors (-1) or closure (0) in send() */ /* handle any errors (-1) or closure (0) in send() */
if (sent < 1) { if (sent < 1) {
@ -2010,8 +1940,7 @@ static void poll_send_header(struct connection *conn)
total_out += (size_t)sent; total_out += (size_t)sent;
/* check if we're done sending header */ /* check if we're done sending header */
if (conn->header_sent == conn->header_length) if (conn->header_sent == conn->header_length) {
{
if (conn->header_only) if (conn->header_only)
conn->state = DONE; conn->state = DONE;
else { else {
@ -2024,17 +1953,13 @@ static void poll_send_header(struct connection *conn)
} }
} }
/* Send chunk on socket <s> from FILE *fp, starting at <ofs> and of size
/* ---------------------------------------------------------------------------
* Send chunk on socket <s> from FILE *fp, starting at <ofs> and of size
* <size>. Use sendfile() if possible since it's zero-copy on some platforms. * <size>. Use sendfile() if possible since it's zero-copy on some platforms.
* Returns the number of bytes sent, 0 on closure, -1 if send() failed, -2 if * Returns the number of bytes sent, 0 on closure, -1 if send() failed, -2 if
* read error. * read error.
*/ */
static ssize_t send_from_file(const int s, const int fd, static ssize_t send_from_file(const int s, const int fd,
off_t ofs, size_t size) off_t ofs, size_t size) {
{
#ifdef __FreeBSD__ #ifdef __FreeBSD__
off_t sent; off_t sent;
int ret = sendfile(fd, s, ofs, size, NULL, &sent, 0); int ret = sendfile(fd, s, ofs, size, NULL, &sent, 0);
@ -2065,20 +1990,18 @@ static ssize_t send_from_file(const int s, const int fd,
ssize_t numread; ssize_t numread;
#undef BUFSIZE #undef BUFSIZE
if (lseek(fd, ofs, SEEK_SET) == -1) err(1, "fseek(%d)", (int)ofs); if (lseek(fd, ofs, SEEK_SET) == -1)
err(1, "fseek(%d)", (int)ofs);
numread = read(fd, buf, amount); numread = read(fd, buf, amount);
if (numread == 0) if (numread == 0) {
{
fprintf(stderr, "premature eof on fd %d\n", fd); fprintf(stderr, "premature eof on fd %d\n", fd);
return -1; return -1;
} }
else if (numread == -1) else if (numread == -1) {
{
fprintf(stderr, "error reading on fd %d: %s", fd, strerror(errno)); fprintf(stderr, "error reading on fd %d: %s", fd, strerror(errno));
return -1; return -1;
} }
else if ((size_t)numread != amount) else if ((size_t)numread != amount) {
{
fprintf(stderr, "read %d bytes, expecting %u bytes on fd %d\n", fprintf(stderr, "read %d bytes, expecting %u bytes on fd %d\n",
numread, amount, fd); numread, amount, fd);
return -1; return -1;
@ -2089,26 +2012,20 @@ static ssize_t send_from_file(const int s, const int fd,
#endif #endif
} }
/* Sending reply. */
/* ---------------------------------------------------------------------------
* Sending reply.
*/
static void poll_send_reply(struct connection *conn) static void poll_send_reply(struct connection *conn)
{ {
ssize_t sent; ssize_t sent;
assert(conn->state == SEND_REPLY); assert(conn->state == SEND_REPLY);
assert(!conn->header_only); assert(!conn->header_only);
if (conn->reply_type == REPLY_GENERATED) if (conn->reply_type == REPLY_GENERATED) {
{
assert(conn->reply_length >= conn->reply_sent); assert(conn->reply_length >= conn->reply_sent);
sent = send(conn->socket, sent = send(conn->socket,
conn->reply + conn->reply_start + conn->reply_sent, conn->reply + conn->reply_start + conn->reply_sent,
(size_t)(conn->reply_length - conn->reply_sent), 0); (size_t)(conn->reply_length - conn->reply_sent), 0);
} }
else else {
{
errno = 0; errno = 0;
assert(conn->reply_length >= conn->reply_sent); assert(conn->reply_length >= conn->reply_sent);
sent = send_from_file(conn->socket, conn->reply_fd, sent = send_from_file(conn->socket, conn->reply_fd,
@ -2119,11 +2036,11 @@ static void poll_send_reply(struct connection *conn)
(long long)sent, errno, strerror(errno)); (long long)sent, errno, strerror(errno));
} }
conn->last_active = now; conn->last_active = now;
if (debug) printf("poll_send_reply(%d) sent %d: %lld+[%lld-%lld] of %lld\n", if (debug)
conn->socket, (int)sent, (long long)conn->reply_start, printf("poll_send_reply(%d) sent %d: %lld+[%lld-%lld] of %lld\n",
(long long)conn->reply_sent, conn->socket, (int)sent, llu(conn->reply_start),
(long long)(conn->reply_sent + sent - 1), llu(conn->reply_sent), llu(conn->reply_sent + sent - 1),
(long long)conn->reply_length); llu(conn->reply_length));
/* handle any errors (-1) or closure (0) in send() */ /* handle any errors (-1) or closure (0) in send() */
if (sent < 1) { if (sent < 1) {
@ -2136,9 +2053,9 @@ static void poll_send_reply(struct connection *conn)
if (debug) if (debug)
printf("send(%d) error: %s\n", conn->socket, strerror(errno)); printf("send(%d) error: %s\n", conn->socket, strerror(errno));
} }
else if (sent == 0) else if (sent == 0) {
{ if (debug)
if (debug) printf("send(%d) closure\n", conn->socket); printf("send(%d) closure\n", conn->socket);
} }
conn->conn_close = 1; conn->conn_close = 1;
conn->state = DONE; conn->state = DONE;
@ -2149,11 +2066,10 @@ static void poll_send_reply(struct connection *conn)
total_out += (size_t)sent; total_out += (size_t)sent;
/* check if we're done sending */ /* check if we're done sending */
if (conn->reply_sent == conn->reply_length) conn->state = DONE; if (conn->reply_sent == conn->reply_length)
conn->state = DONE;
} }
/* --------------------------------------------------------------------------- /* ---------------------------------------------------------------------------
* Main loop of the httpd - a select() and then delegation to accept * Main loop of the httpd - a select() and then delegation to accept
* connections, handle receiving of requests, and sending of replies. * connections, handle receiving of requests, and sending of replies.