tinywebd

changeset 1:f425a9805d17

foo
author John Tsiombikas <nuclear@member.fsf.org>
date Tue, 14 Apr 2015 08:32:51 +0300
parents 9c9e24956d99
children 7bb4c2a0a360
files src/http.c src/http.h src/main.c
diffstat 3 files changed, 226 insertions(+), 8 deletions(-) [+]
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/http.c	Tue Apr 14 08:32:51 2015 +0300
     1.3 @@ -0,0 +1,29 @@
     1.4 +#include "http.h"
     1.5 +
     1.6 +const char *http_strmsg(int code)
     1.7 +{
     1.8 +	static const char **msgxxx[] = {
     1.9 +		0, http_msg1xx, http_msg2xx, http_msg3xx, http_msg4xx, http_msg5xx
    1.10 +	};
    1.11 +	static int msgcount[] = {
    1.12 +		0,
    1.13 +		sizeof http_msg1xx / sizeof *http_msg1xx,
    1.14 +		sizeof http_msg2xx / sizeof *http_msg2xx,
    1.15 +		sizeof http_msg3xx / sizeof *http_msg3xx,
    1.16 +		sizeof http_msg4xx / sizeof *http_msg4xx,
    1.17 +		sizeof http_msg5xx / sizeof *http_msg5xx
    1.18 +	};
    1.19 +
    1.20 +	int type = code / 100;
    1.21 +	int idx = code % 100;
    1.22 +
    1.23 +	if(type < 1 || type >= sizeof msgxxx / sizeof *msgxxx) {
    1.24 +		return "Invalid HTTP Status";
    1.25 +	}
    1.26 +
    1.27 +	if(idx < 0 || idx >= msgcount[type]) {
    1.28 +		return "Unknown HTTP Status";
    1.29 +	}
    1.30 +
    1.31 +	return msgxxx[type][idx];
    1.32 +}
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/src/http.h	Tue Apr 14 08:32:51 2015 +0300
     2.3 @@ -0,0 +1,103 @@
     2.4 +#ifndef HTTP_H_
     2.5 +#define HTTP_H_
     2.6 +
     2.7 +enum http_method {
     2.8 +	HTTP_UNKNOWN,
     2.9 +	HTTP_OPTIONS,
    2.10 +	HTTP_GET,
    2.11 +	HTTP_HEAD,
    2.12 +	HTTP_POST,
    2.13 +	HTTP_PUT,
    2.14 +	HTTP_DELETE,
    2.15 +	HTTP_TRACE,
    2.16 +	HTTP_CONNECT,
    2.17 +
    2.18 +	NUM_HTTP_METHODS
    2.19 +};
    2.20 +
    2.21 +const char *http_method_str[] = {
    2.22 +	"<unknown>",
    2.23 +	"OPTIONS",
    2.24 +	"GET",
    2.25 +	"HEAD",
    2.26 +	"POST",
    2.27 +	"PUT",
    2.28 +	"DELETE",
    2.29 +	"TRACE",
    2.30 +	"CONNECT",
    2.31 +	0
    2.32 +};
    2.33 +
    2.34 +
    2.35 +struct http_req_header {
    2.36 +	enum http_method method;
    2.37 +	char *uri;
    2.38 +	int ver_major, ver_minor;	/* http version */
    2.39 +	/* XXX cont. */
    2.40 +};
    2.41 +
    2.42 +
    2.43 +/* HTTP 1xx message strings */
    2.44 +const char *http_msg1xx[] = {
    2.45 +	"Continue",					/* 100 */
    2.46 +	"Switching Protocols"		/* 101 */
    2.47 +};
    2.48 +
    2.49 +/* HTTP 2xx message strings */
    2.50 +const char *http_msg2xx[] = {
    2.51 +	"OK",						/* 200 */
    2.52 +	"Created",					/* 201 */
    2.53 +	"Accepted",					/* 202 */
    2.54 +	"Non-Authoritative Information",	/* 203 */
    2.55 +	"No Content",				/* 204 */
    2.56 +	"Reset Content",			/* 205 */
    2.57 +	"Partial Content"			/* 206 */
    2.58 +};
    2.59 +
    2.60 +/* HTTP 3xx message strings */
    2.61 +const char *http_msg3xx[] = {
    2.62 +	"Multiple Choices",			/* 300 */
    2.63 +	"Moved Permanently",		/* 301 */
    2.64 +	"Found",					/* 302 */
    2.65 +	"See Other",				/* 303 */
    2.66 +	"Not Modified",				/* 304 */
    2.67 +	"Use Proxy",				/* 305 */
    2.68 +	"<unknown>",				/* 306 is undefined? */
    2.69 +	"Temporary Redirect"		/* 307 */
    2.70 +};
    2.71 +
    2.72 +/* HTTP 4xx error strings */
    2.73 +const char *http_msg4xx[] = {
    2.74 +	"Bad Request",				/* 400 */
    2.75 +	"Unauthorized",				/* 401 */
    2.76 +	"What the Fuck?",			/* 402 */
    2.77 +	"Forbidden",				/* 403 */
    2.78 +	"Not Found",				/* 404 */
    2.79 +	"Method Not Allowed",		/* 405 */
    2.80 +	"Not Acceptable",			/* 406 */
    2.81 +	"Proxy Authentication Required",	/* 407 */
    2.82 +	"Request Time-out",			/* 408 */
    2.83 +	"Conflict",					/* 409 */
    2.84 +	"Gone",						/* 410 */
    2.85 +	"Length Required",			/* 411 */
    2.86 +	"Precondition Failed",		/* 412 */
    2.87 +	"Request Entity Too Large", /* 413 */
    2.88 +	"Request-URI Too Large",	/* 414 */
    2.89 +	"Unsupported Media Type",	/* 415 */
    2.90 +	"Request range not satisfiable", /* 416 */
    2.91 +	"Expectation Failed"		/* 417 */
    2.92 +};
    2.93 +
    2.94 +/* HTTP 5xx error strings */
    2.95 +const char *http_msg5xx[] = {
    2.96 +	"Internal Server Error",	/* 500 */
    2.97 +	"Not Implemented",			/* 501 */
    2.98 +	"Bad Gateway",				/* 502 */
    2.99 +	"Service Unavailable",		/* 503 */
   2.100 +	"Gateway Time-out",			/* 504 */
   2.101 +	"HTTP Version not supported"	/* 505 */
   2.102 +};
   2.103 +
   2.104 +const char *http_strmsg(int code);
   2.105 +
   2.106 +#endif	/* HTTP_H_ */
     3.1 --- a/src/main.c	Tue Apr 14 02:13:29 2015 +0300
     3.2 +++ b/src/main.c	Tue Apr 14 08:32:51 2015 +0300
     3.3 @@ -8,6 +8,15 @@
     3.4  #include <sys/types.h>
     3.5  #include <sys/socket.h>
     3.6  #include <arpa/inet.h>
     3.7 +#include "http.h"
     3.8 +
     3.9 +/* HTTP version */
    3.10 +#define HTTP_VER_MAJOR	1
    3.11 +#define HTTP_VER_MINOR	1
    3.12 +#define HTTP_VER_STR	"1.1"
    3.13 +
    3.14 +/* maximum request length: 64mb */
    3.15 +#define MAX_REQ_LENGTH	(65536 * 1024)
    3.16  
    3.17  struct client {
    3.18  	int s;
    3.19 @@ -19,6 +28,8 @@
    3.20  int start_server(void);
    3.21  int accept_conn(int lis);
    3.22  int handle_client(struct client *c);
    3.23 +void respond_error(struct client *c, int errcode);
    3.24 +int parse_args(int argc, char **argv);
    3.25  
    3.26  static int lis;
    3.27  static int port = 8080;
    3.28 @@ -26,6 +37,10 @@
    3.29  
    3.30  int main(int argc, char **argv)
    3.31  {
    3.32 +	if(parse_args(argc, argv) == -1) {
    3.33 +		return 1;
    3.34 +	}
    3.35 +
    3.36  	if((lis = start_server()) == -1) {
    3.37  		return 1;
    3.38  	}
    3.39 @@ -128,37 +143,108 @@
    3.40  	return 0;
    3.41  }
    3.42  
    3.43 +void close_conn(struct client *c)
    3.44 +{
    3.45 +	close(c->s);
    3.46 +	c->s = -1;	/* mark it for removal */
    3.47 +	free(c->rcvbuf);
    3.48 +}
    3.49 +
    3.50 +static char *skip_space(char *s, char *endp)
    3.51 +{
    3.52 +	while(s < endp && *s && isspace(*s))
    3.53 +		++s;
    3.54 +	return s;
    3.55 +}
    3.56 +
    3.57  int handle_client(struct client *c)
    3.58  {
    3.59 +	char *reqp, *hdrp, *bodyp, *endp, *eol;
    3.60 +	char req_type[32];
    3.61  	static char buf[2048];
    3.62  	int rdsz;
    3.63  
    3.64  	while((rdsz = recv(c->s, buf, sizeof buf, 0)) > 0) {
    3.65  		if(c->rcvbuf) {
    3.66  			int newsz = c->bufsz + rdsz;
    3.67 -			char *newbuf = realloc(buf, newsz);
    3.68 +			if(newsz > MAX_REQ_LENGTH) {
    3.69 +				respond_error(c, 413);
    3.70 +				return -1;
    3.71 +			}
    3.72 +
    3.73 +			char *newbuf = realloc(buf, newsz + 1);
    3.74  			if(!newbuf) {
    3.75  				fprintf(stderr, "failed to allocate %d byte buffer\n", newsz);
    3.76 -				/* TODO http error */
    3.77 -				goto drop;
    3.78 +				respond_error(c, 503);
    3.79 +				return -1;
    3.80  			}
    3.81  
    3.82  			memcpy(newbuf + c->bufsz, buf, rdsz);
    3.83 +			newbuf[newsz] = 0;
    3.84  
    3.85  			c->rcvbuf = newbuf;
    3.86  			c->bufsz = newsz;
    3.87  		}
    3.88  	}
    3.89  
    3.90 +	endp = c->rcvbuf + c->bufsz;
    3.91 +	if((reqp = skip_space(buf, endp)) >= endp) {
    3.92 +		return 0;	/* incomplete have to read more ... */
    3.93 +	}
    3.94 +
    3.95 +
    3.96 +	/* we only support GET and HEAD at this point, so freak out on anything else */
    3.97 +
    3.98  	/* TODO: parse header, drop on invalid, determine if the request
    3.99  	 * is complete and process
   3.100  	 */
   3.101 +}
   3.102  
   3.103 -	if(rdsz == -1 && errno != EAGAIN) {
   3.104 -drop:
   3.105 -		close(c->s);
   3.106 -		c->s = -1;
   3.107 -		free(c->rcvbuf);
   3.108 +void respond_error(struct client *c, int errcode)
   3.109 +{
   3.110 +	char buf[512];
   3.111 +
   3.112 +	sprintf(buf, HTTP_VER_STR " %d %s\r\n\r\n", errcode, http_strmsg(errcode));
   3.113 +
   3.114 +	send(c->s, buf, strlen(buf), 0);
   3.115 +	close_conn(c);
   3.116 +}
   3.117 +
   3.118 +
   3.119 +static void print_help(const char *argv0)
   3.120 +{
   3.121 +	printf("Usage: %s [options]\n", argv0);
   3.122 +	printf("Options:\n");
   3.123 +	printf(" -p <port>  set the TCP/IP port number to use\n");
   3.124 +	printf(" -h         print usage help and exit\n");
   3.125 +}
   3.126 +
   3.127 +int parse_args(int argc, char **argv)
   3.128 +{
   3.129 +	int i;
   3.130 +
   3.131 +	for(i=1; i<argc; i++) {
   3.132 +		if(argv[i][0] == '-' && argv[i][2] == 0) {
   3.133 +			switch(argv[i][1]) {
   3.134 +			case 'p':
   3.135 +				if((port = atoi(argv[++i])) == 0) {
   3.136 +					fprintf(stderr, "-p must be followed by a valid port number\n");
   3.137 +					return -1;
   3.138 +				}
   3.139 +				break;
   3.140 +
   3.141 +			case 'h':
   3.142 +				print_help(argv[0]);
   3.143 +				exit(0);
   3.144 +
   3.145 +			default:
   3.146 +				fprintf(stderr, "unrecognized option: %s\n", argv[i]);
   3.147 +				return -1;
   3.148 +			}
   3.149 +		} else {
   3.150 +			fprintf(stderr, "unexpected argument: %s\n", argv[i]);
   3.151 +			return -1;
   3.152 +		}
   3.153  	}
   3.154  	return 0;
   3.155  }