mirror of
https://github.com/emikulic/darkhttpd.git
synced 2023-08-10 21:13:08 +03:00
. TODO: Added If-Modified-Since
. TODO: Marked off Actually serve files . Added min(a,b) macro . Moved header_only from process_get() to struct connection . MAX_REQUEST_LENGTH from 20000 to 4000 . xrealloc() to strlen+1 in urldecode() . Made default_reply() take variable arguments like printf . Mostly implemented process_get() . Handling of file-not-found . Handling of other fopen() errors . Header generation (except Content-Type) . Made default_replies more specific . poll_send_header() advances state to DONE if header_only . Completed poll_send_reply() (implemented REPLY_FROMFILE)
This commit is contained in:
parent
32b3855aeb
commit
02964cc5a6
@ -10,10 +10,11 @@
|
||||
/*
|
||||
* TODO:
|
||||
* . Ignore SIGPIPE.
|
||||
* . Actually serve files.
|
||||
* x Actually serve files.
|
||||
* . Generate directory entries.
|
||||
* . Log to file.
|
||||
* . Partial content.
|
||||
* . If-Modified-Since.
|
||||
* . Keep-alive connections.
|
||||
* . Chroot, set{uid|gid}.
|
||||
* . Port to Win32.
|
||||
@ -21,11 +22,14 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/queue.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -35,6 +39,10 @@
|
||||
/* for easy defusal */
|
||||
#define debugf printf
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ( ((a)<(b)) ? (a) : (b) )
|
||||
#endif
|
||||
|
||||
LIST_HEAD(conn_list_head, connection) connlist =
|
||||
LIST_HEAD_INITIALIZER(conn_list_head);
|
||||
|
||||
@ -58,7 +66,7 @@ struct connection
|
||||
|
||||
char *header;
|
||||
unsigned int header_sent, header_length;
|
||||
int header_dont_free;
|
||||
int header_dont_free, header_only;
|
||||
|
||||
enum { REPLY_GENERATED, REPLY_FROMFILE } reply_type;
|
||||
char *reply;
|
||||
@ -77,7 +85,7 @@ struct connection
|
||||
/* To prevent a malformed request from eating up too much memory, die once the
|
||||
* request exceeds this many bytes:
|
||||
*/
|
||||
#define MAX_REQUEST_LENGTH 20000
|
||||
#define MAX_REQUEST_LENGTH 4000
|
||||
|
||||
|
||||
|
||||
@ -242,9 +250,9 @@ static struct connection *new_connection(void)
|
||||
conn->request_length = 0;
|
||||
conn->header = NULL;
|
||||
conn->header_sent = conn->header_length = 0;
|
||||
conn->header_dont_free = 0; /* you'll want to, later */
|
||||
conn->header_dont_free = conn->header_only = 0;
|
||||
conn->reply = NULL;
|
||||
conn->reply_dont_free = 0; /* you'll want to, later */
|
||||
conn->reply_dont_free = 0;
|
||||
conn->reply_file = NULL;
|
||||
conn->reply_sent = conn->reply_length = 0;
|
||||
|
||||
@ -385,7 +393,7 @@ static char *urldecode(const char *url)
|
||||
}
|
||||
out[pos] = 0;
|
||||
|
||||
out = xrealloc(out, strlen(out)); /* dealloc what we don't need */
|
||||
out = xrealloc(out, strlen(out)+1); /* dealloc what we don't need */
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -395,8 +403,15 @@ static char *urldecode(const char *url)
|
||||
* A default reply for any (erroneous) occasion.
|
||||
*/
|
||||
static void default_reply(struct connection *conn,
|
||||
const int errcode, const char *errname, const char *reason)
|
||||
const int errcode, const char *errname, const char *format, ...)
|
||||
{
|
||||
char *reason;
|
||||
va_list va;
|
||||
|
||||
va_start(va, format);
|
||||
vasprintf(&reason, format, va);
|
||||
va_end(va);
|
||||
|
||||
conn->reply_length = asprintf(&(conn->reply),
|
||||
"<html><head><title>%d %s</title></head><body>\n"
|
||||
"<h1>%s</h1>\n" /* errname */
|
||||
@ -405,6 +420,7 @@ static void default_reply(struct connection *conn,
|
||||
"Generated by %s on %s\n"
|
||||
"</body></html>\n",
|
||||
errcode, errname, errname, reason, pkgname, rfc1123_date(time(NULL)));
|
||||
free(reason);
|
||||
|
||||
if (conn->reply == NULL) errx(1, "out of memory in asprintf()");
|
||||
|
||||
@ -453,13 +469,14 @@ static void parse_request(const char *req, const int length,
|
||||
/* ---------------------------------------------------------------------------
|
||||
* Process a GET/HEAD request
|
||||
*/
|
||||
static void process_get(struct connection *conn,
|
||||
const char *url, const int header_only)
|
||||
static void process_get(struct connection *conn, const char *url)
|
||||
{
|
||||
char *decoded_url, *target;
|
||||
char *decoded_url, *target, *tmp;
|
||||
struct stat filestat;
|
||||
|
||||
/* work out which file we're trying to get */
|
||||
/* work out path of file being requested */
|
||||
decoded_url = urldecode(url);
|
||||
/* FIXME: ensure this is a safe URL, i.e. no '../' */
|
||||
if (decoded_url[strlen(decoded_url)-1] == '/')
|
||||
{
|
||||
asprintf(&target, "%s%s%s", wwwroot, decoded_url, index_name);
|
||||
@ -469,13 +486,54 @@ static void process_get(struct connection *conn,
|
||||
asprintf(&target, "%s%s", wwwroot, decoded_url);
|
||||
}
|
||||
free(decoded_url);
|
||||
decoded_url = NULL;
|
||||
|
||||
debugf(">>>%s<<<\n", target);
|
||||
free(target);
|
||||
|
||||
/* FIXME */
|
||||
default_reply(conn, 200, "OK", "Nothing to see here. Move along.");
|
||||
conn->reply_file = fopen(target, "rb");
|
||||
free(target);
|
||||
if (conn->reply_file == NULL)
|
||||
{
|
||||
/* fopen() failed */
|
||||
if (errno == ENOENT)
|
||||
default_reply(conn, 404, "Not Found",
|
||||
"The URI you requested (%s) was not found.", url);
|
||||
else
|
||||
default_reply(conn, 403, "Forbidden",
|
||||
"The URI you requested (%s) cannot be returned.<br>\n"
|
||||
"%s.", /* reason why */
|
||||
url, strerror(errno));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* get information on the file */
|
||||
if (fstat(fileno(conn->reply_file), &filestat) == -1)
|
||||
{
|
||||
default_reply(conn, 500, "Internal Server Error",
|
||||
"fstat() failed: %s.", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
conn->reply_type = REPLY_FROMFILE;
|
||||
conn->reply_length = filestat.st_size;
|
||||
|
||||
asprintf(&tmp,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Date: %s\r\n"
|
||||
"Server: %s\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"Content-Type: text/plain\r\n" /* FIXME */
|
||||
, rfc1123_date(time(NULL)), pkgname, conn->reply_length
|
||||
);
|
||||
|
||||
conn->header_length = asprintf(&(conn->header),
|
||||
"%s"
|
||||
"Last-Modified: %s\r\n"
|
||||
"\r\n"
|
||||
, tmp, rfc1123_date(filestat.st_mtime)
|
||||
);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
|
||||
@ -490,9 +548,12 @@ static void process_request(struct connection *conn)
|
||||
debugf("method=``%s'', url=``%s''\n", method, url);
|
||||
|
||||
if (strcmp(method, "GET") == 0)
|
||||
process_get(conn, url, 0);
|
||||
process_get(conn, url);
|
||||
else if (strcmp(method, "HEAD") == 0)
|
||||
process_get(conn, url, 1);
|
||||
{
|
||||
process_get(conn, url);
|
||||
conn->header_only = 1;
|
||||
}
|
||||
else if (strcmp(method, "OPTIONS") == 0 ||
|
||||
strcmp(method, "POST") == 0 ||
|
||||
strcmp(method, "PUT") == 0 ||
|
||||
@ -500,10 +561,10 @@ static void process_request(struct connection *conn)
|
||||
strcmp(method, "TRACE") == 0 ||
|
||||
strcmp(method, "CONNECT") == 0)
|
||||
default_reply(conn, 501, "Not Implemented",
|
||||
"That method is not implemented.");
|
||||
"That method you specified (%s) is not implemented.", method);
|
||||
else
|
||||
default_reply(conn, 400, "Bad Request",
|
||||
"That method is not a valid HTTP/1.1 method.");
|
||||
"%s is not a valid HTTP/1.1 method.", method);
|
||||
|
||||
/* advance state */
|
||||
conn->state = SEND_HEADER;
|
||||
@ -541,8 +602,7 @@ static void poll_recv_request(struct connection *conn)
|
||||
#undef BUFSIZE
|
||||
|
||||
/* append to conn->request */
|
||||
conn->request = xrealloc(conn->request,
|
||||
conn->request_length + recvd + 1);
|
||||
conn->request = xrealloc(conn->request, conn->request_length+recvd+1);
|
||||
memcpy(conn->request+conn->request_length, buf, recvd);
|
||||
conn->request_length += recvd;
|
||||
conn->request[conn->request_length] = 0;
|
||||
@ -564,7 +624,7 @@ static void poll_recv_request(struct connection *conn)
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
* Sending header.
|
||||
* Sending header. Assumes conn->header is not NULL.
|
||||
*/
|
||||
static void poll_send_header(struct connection *conn)
|
||||
{
|
||||
@ -583,6 +643,10 @@ static void poll_send_header(struct connection *conn)
|
||||
{
|
||||
if (!conn->header_dont_free) free(conn->header);
|
||||
conn->header = NULL;
|
||||
|
||||
if (conn->header_only)
|
||||
conn->state = DONE;
|
||||
else
|
||||
conn->state = SEND_REPLY;
|
||||
}
|
||||
}
|
||||
@ -590,12 +654,34 @@ static void poll_send_header(struct connection *conn)
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
* Sending reply. (FIXME: FROM FILE)
|
||||
* Sending reply.
|
||||
*/
|
||||
static void poll_send_reply(struct connection *conn)
|
||||
{
|
||||
ssize_t sent = send(conn->socket, conn->reply + conn->reply_sent,
|
||||
ssize_t sent;
|
||||
if (conn->reply_type == REPLY_GENERATED)
|
||||
{
|
||||
sent = send(conn->socket, conn->reply + conn->reply_sent,
|
||||
conn->reply_length - conn->reply_sent, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* from file! */
|
||||
#define BUFSIZE 65000
|
||||
char buf[BUFSIZE];
|
||||
int amount = min(BUFSIZE, conn->reply_length - conn->reply_sent);
|
||||
#undef BUFSIZE
|
||||
|
||||
if (fseek(conn->reply_file, conn->reply_sent, SEEK_SET) == -1)
|
||||
err(1, "fseek(%d)", conn->reply_sent);
|
||||
|
||||
if (fread(buf, amount, 1, conn->reply_file) != 1)
|
||||
err(1, "fread()");
|
||||
|
||||
sent = send(conn->socket, buf, amount, 0);
|
||||
}
|
||||
|
||||
/* handle any errors in send() */
|
||||
if (sent == -1) err(1, "send()");
|
||||
if (sent == 0)
|
||||
{
|
||||
@ -607,8 +693,12 @@ static void poll_send_reply(struct connection *conn)
|
||||
/* check if we're done sending */
|
||||
if (conn->reply_sent == conn->reply_length)
|
||||
{
|
||||
if (!conn->reply_dont_free) free(conn->reply);
|
||||
if (!conn->reply_dont_free && conn->reply != NULL)
|
||||
{
|
||||
free(conn->reply);
|
||||
conn->reply = NULL;
|
||||
}
|
||||
if (conn->reply_file != NULL) fclose(conn->reply_file);
|
||||
conn->state = DONE;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user