tinywebd

changeset 7:5ec50ca0d071

separated the server code
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 17 Apr 2015 11:45:08 +0300
parents 4f191dbfac7e
children 121b991ccc1d
files src/http.c src/http.h src/logger.c src/logger.h src/main.c src/mime.c src/tinyweb.c src/tinyweb.h
diffstat 8 files changed, 506 insertions(+), 325 deletions(-) [+]
line diff
     1.1 --- a/src/http.c	Fri Apr 17 01:57:25 2015 +0300
     1.2 +++ b/src/http.c	Fri Apr 17 11:45:08 2015 +0300
     1.3 @@ -5,9 +5,10 @@
     1.4  #include <ctype.h>
     1.5  #include <alloca.h>
     1.6  #include "http.h"
     1.7 +#include "logger.h"
     1.8  
     1.9  
    1.10 -const char *http_method_str[] = {
    1.11 +static const char *http_method_str[] = {
    1.12  	"<unknown>",
    1.13  	"OPTIONS",
    1.14  	"GET",
    1.15 @@ -22,13 +23,13 @@
    1.16  
    1.17  
    1.18  /* HTTP 1xx message strings */
    1.19 -const char *http_msg1xx[] = {
    1.20 +static const char *http_msg1xx[] = {
    1.21  	"Continue",					/* 100 */
    1.22  	"Switching Protocols"		/* 101 */
    1.23  };
    1.24  
    1.25  /* HTTP 2xx message strings */
    1.26 -const char *http_msg2xx[] = {
    1.27 +static const char *http_msg2xx[] = {
    1.28  	"OK",						/* 200 */
    1.29  	"Created",					/* 201 */
    1.30  	"Accepted",					/* 202 */
    1.31 @@ -39,7 +40,7 @@
    1.32  };
    1.33  
    1.34  /* HTTP 3xx message strings */
    1.35 -const char *http_msg3xx[] = {
    1.36 +static const char *http_msg3xx[] = {
    1.37  	"Multiple Choices",			/* 300 */
    1.38  	"Moved Permanently",		/* 301 */
    1.39  	"Found",					/* 302 */
    1.40 @@ -51,7 +52,7 @@
    1.41  };
    1.42  
    1.43  /* HTTP 4xx error strings */
    1.44 -const char *http_msg4xx[] = {
    1.45 +static const char *http_msg4xx[] = {
    1.46  	"Bad Request",				/* 400 */
    1.47  	"Unauthorized",				/* 401 */
    1.48  	"What the Fuck?",			/* 402 */
    1.49 @@ -73,7 +74,7 @@
    1.50  };
    1.51  
    1.52  /* HTTP 5xx error strings */
    1.53 -const char *http_msg5xx[] = {
    1.54 +static const char *http_msg5xx[] = {
    1.55  	"Internal Server Error",	/* 500 */
    1.56  	"Not Implemented",			/* 501 */
    1.57  	"Bad Gateway",				/* 502 */
    1.58 @@ -86,7 +87,7 @@
    1.59  static enum http_method parse_method(const char *s);
    1.60  
    1.61  
    1.62 -int http_parse_header(struct http_req_header *hdr, const char *buf, int bufsz)
    1.63 +int http_parse_request(struct http_req_header *hdr, const char *buf, int bufsz)
    1.64  {
    1.65  	int i, nlines = 0;
    1.66  	char *rqline = 0;
    1.67 @@ -167,23 +168,23 @@
    1.68  	return HTTP_HDR_OK;
    1.69  }
    1.70  
    1.71 -void http_print_header(struct http_req_header *hdr)
    1.72 +void http_log_request(struct http_req_header *hdr)
    1.73  {
    1.74  	int i;
    1.75  
    1.76 -	printf("HTTP request header\n");
    1.77 -	printf(" method: %s\n", http_method_str[hdr->method]);
    1.78 -	printf(" uri: %s\n", hdr->uri);
    1.79 -	printf(" version: %d.%d\n", hdr->ver_major, hdr->ver_minor);
    1.80 -	printf(" fields (%d):\n", hdr->num_hdrfields);
    1.81 +	logmsg("HTTP request header\n");
    1.82 +	logmsg(" method: %s\n", http_method_str[hdr->method]);
    1.83 +	logmsg(" uri: %s\n", hdr->uri);
    1.84 +	logmsg(" version: %d.%d\n", hdr->ver_major, hdr->ver_minor);
    1.85 +	logmsg(" fields (%d):\n", hdr->num_hdrfields);
    1.86  
    1.87  	for(i=0; i<hdr->num_hdrfields; i++) {
    1.88 -		printf("   %s\n", hdr->hdrfields[i]);
    1.89 +		logmsg("   %s\n", hdr->hdrfields[i]);
    1.90  	}
    1.91 -	putchar('\n');
    1.92 +	logmsg("\n");
    1.93  }
    1.94  
    1.95 -void http_destroy_header(struct http_req_header *hdr)
    1.96 +void http_destroy_request(struct http_req_header *hdr)
    1.97  {
    1.98  	int i;
    1.99  
   1.100 @@ -211,7 +212,7 @@
   1.101  {
   1.102  	int sz;
   1.103  	va_list ap;
   1.104 -	char *field, *newarr, tmp;
   1.105 +	char *field, **newarr, tmp;
   1.106  
   1.107  	va_start(ap, fmt);
   1.108  	sz = vsnprintf(&tmp, 0, fmt, ap);
   1.109 @@ -229,7 +230,9 @@
   1.110  		free(field);
   1.111  		return -1;
   1.112  	}
   1.113 -	resp->fields[resp->num_fields++] = newarr;
   1.114 +	resp->fields = newarr;
   1.115 +
   1.116 +	resp->fields[resp->num_fields++] = field;
   1.117  	return 0;
   1.118  }
   1.119  
     2.1 --- a/src/http.h	Fri Apr 17 01:57:25 2015 +0300
     2.2 +++ b/src/http.h	Fri Apr 17 11:45:08 2015 +0300
     2.3 @@ -36,9 +36,9 @@
     2.4  #define HTTP_HDR_NOMEM		-2
     2.5  #define HTTP_HDR_PARTIAL	-3
     2.6  
     2.7 -int http_parse_header(struct http_req_header *hdr, const char *buf, int bufsz);
     2.8 -void http_print_header(struct http_req_header *hdr);
     2.9 -void http_destroy_header(struct http_req_header *hdr);
    2.10 +int http_parse_request(struct http_req_header *hdr, const char *buf, int bufsz);
    2.11 +void http_log_request(struct http_req_header *hdr);
    2.12 +void http_destroy_request(struct http_req_header *hdr);
    2.13  
    2.14  int http_init_resp(struct http_resp_header *resp);
    2.15  int http_add_resp_field(struct http_resp_header *resp, const char *fmt, ...);
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/src/logger.c	Fri Apr 17 11:45:08 2015 +0300
     3.3 @@ -0,0 +1,34 @@
     3.4 +#include <stdio.h>
     3.5 +#include <stdlib.h>
     3.6 +#include <string.h>
     3.7 +#include <stdarg.h>
     3.8 +#include <errno.h>
     3.9 +#include "logger.h"
    3.10 +
    3.11 +static FILE *logfile;
    3.12 +
    3.13 +int set_log_file(const char *fname)
    3.14 +{
    3.15 +	FILE *fp;
    3.16 +
    3.17 +	if(!(fp = fopen(fname, "w"))) {
    3.18 +		fprintf(stderr, "failed to open logfile: %s: %s\n", fname, strerror(errno));
    3.19 +		return -1;
    3.20 +	}
    3.21 +	setvbuf(fp, 0, _IONBF, 0);
    3.22 +	logfile = fp;
    3.23 +	return 0;
    3.24 +}
    3.25 +
    3.26 +void logmsg(const char *fmt, ...)
    3.27 +{
    3.28 +	va_list ap;
    3.29 +
    3.30 +	if(!logfile) {
    3.31 +		logfile = stderr;
    3.32 +	}
    3.33 +
    3.34 +	va_start(ap, fmt);
    3.35 +	vfprintf(logfile, fmt, ap);
    3.36 +	va_end(ap);
    3.37 +}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/src/logger.h	Fri Apr 17 11:45:08 2015 +0300
     4.3 @@ -0,0 +1,8 @@
     4.4 +#ifndef LOGGER_H_
     4.5 +#define LOGGER_H_
     4.6 +
     4.7 +int set_log_file(const char *fname);
     4.8 +
     4.9 +void logmsg(const char *fname, ...);
    4.10 +
    4.11 +#endif	/* LOGGER_H_ */
     5.1 --- a/src/main.c	Fri Apr 17 01:57:25 2015 +0300
     5.2 +++ b/src/main.c	Fri Apr 17 11:45:08 2015 +0300
     5.3 @@ -1,58 +1,18 @@
     5.4  #include <stdio.h>
     5.5  #include <stdlib.h>
     5.6  #include <string.h>
     5.7 -#include <ctype.h>
     5.8 +#include <errno.h>
     5.9  #include <signal.h>
    5.10 -#include <errno.h>
    5.11 -#include <unistd.h>
    5.12 -#include <fcntl.h>
    5.13 -#include <sys/stat.h>
    5.14 -#include <sys/select.h>
    5.15 -#include <sys/mman.h>
    5.16 -#include <sys/types.h>
    5.17 -#include <sys/socket.h>
    5.18 -#include <arpa/inet.h>
    5.19 -#include "http.h"
    5.20 -#include "mime.h"
    5.21 +#include "tinyweb.h"
    5.22  
    5.23 -/* HTTP version */
    5.24 -#define HTTP_VER_MAJOR	1
    5.25 -#define HTTP_VER_MINOR	1
    5.26 -#define HTTP_VER_STR	"1.1"
    5.27 -
    5.28 -/* maximum request length: 64mb */
    5.29 -#define MAX_REQ_LENGTH	(65536 * 1024)
    5.30 -
    5.31 -struct client {
    5.32 -	int s;
    5.33 -	char *rcvbuf;
    5.34 -	int bufsz;
    5.35 -	struct client *next;
    5.36 -};
    5.37 -
    5.38 -int start_server(void);
    5.39 -int accept_conn(int lis);
    5.40 -void close_conn(struct client *c);
    5.41 -int handle_client(struct client *c);
    5.42 -int do_get(struct client *c, const char *uri, int with_body);
    5.43 -void respond_error(struct client *c, int errcode);
    5.44 +int parse_args(int argc, char **argv);
    5.45  void sighandler(int s);
    5.46 -int parse_args(int argc, char **argv);
    5.47 -
    5.48 -static int lis;
    5.49 -static int port = 8080;
    5.50 -static struct client *clist;
    5.51 -
    5.52 -static const char *indexfiles[] = {
    5.53 -	"index.cgi",
    5.54 -	"index.html",
    5.55 -	"index.htm",
    5.56 -	0
    5.57 -};
    5.58  
    5.59  
    5.60  int main(int argc, char **argv)
    5.61  {
    5.62 +	int *sockets, num_sockets, sockets_arr_size = 0;
    5.63 +
    5.64  	if(parse_args(argc, argv) == -1) {
    5.65  		return 1;
    5.66  	}
    5.67 @@ -61,279 +21,47 @@
    5.68  	signal(SIGTERM, sighandler);
    5.69  	signal(SIGQUIT, sighandler);
    5.70  
    5.71 -	if((lis = start_server()) == -1) {
    5.72 -		return 1;
    5.73 -	}
    5.74 +	tw_start();
    5.75  
    5.76  	for(;;) {
    5.77 -		struct client *c, dummy;
    5.78 -		int maxfd = lis;
    5.79 +		int i;
    5.80  		fd_set rdset;
    5.81  
    5.82 +		num_sockets = tw_get_sockets(0);
    5.83 +		if(num_sockets > sockets_arr_size) {
    5.84 +			int newsz = sockets_arr_size ? sockets_arr_size * 2 : 16;
    5.85 +			int *newarr = realloc(sockets, newsz * sizeof *sockets);
    5.86 +			if(!newarr) {
    5.87 +				fprintf(stderr, "failed to allocate sockets array\n");
    5.88 +				tw_stop();
    5.89 +				return 1;
    5.90 +			}
    5.91 +			sockets = newarr;
    5.92 +			sockets_arr_size = newsz;
    5.93 +		}
    5.94 +		tw_get_sockets(sockets);
    5.95 +
    5.96  		FD_ZERO(&rdset);
    5.97 -		FD_SET(lis, &rdset);
    5.98 -
    5.99 -		c = clist;
   5.100 -		while(c) {
   5.101 -			if(c->s > maxfd) {
   5.102 -				maxfd = c->s;
   5.103 -			}
   5.104 -			FD_SET(c->s, &rdset);
   5.105 -			c = c->next;
   5.106 +		for(i=0; i<num_sockets; i++) {
   5.107 +			FD_SET(sockets[i], &rdset);
   5.108  		}
   5.109  
   5.110 -		while(select(maxfd + 1, &rdset, 0, 0, 0) == -1 && errno == EINTR);
   5.111 +		while(select(tw_get_maxfd() + 1, &rdset, 0, 0, 0) == -1 && errno == EINTR);
   5.112  
   5.113 -		c = clist;
   5.114 -		while(c) {
   5.115 -			if(FD_ISSET(c->s, &rdset)) {
   5.116 -				handle_client(c);
   5.117 -			}
   5.118 -			c = c->next;
   5.119 -		}
   5.120 -
   5.121 -		if(FD_ISSET(lis, &rdset)) {
   5.122 -			accept_conn(lis);
   5.123 -		}
   5.124 -
   5.125 -		dummy.next = clist;
   5.126 -		c = &dummy;
   5.127 -
   5.128 -		while(c->next) {
   5.129 -			struct client *n = c->next;
   5.130 -
   5.131 -			if(n->s == -1) {
   5.132 -				/* marked for removal */
   5.133 -				c->next = n->next;
   5.134 -				free(n);
   5.135 -			} else {
   5.136 -				c = c->next;
   5.137 +		for(i=0; i<num_sockets; i++) {
   5.138 +			if(FD_ISSET(sockets[i], &rdset)) {
   5.139 +				tw_handle_socket(sockets[i]);
   5.140  			}
   5.141  		}
   5.142 -		clist = dummy.next;
   5.143  	}
   5.144  
   5.145  	return 0;	/* unreachable */
   5.146  }
   5.147  
   5.148 -int start_server(void)
   5.149 -{
   5.150 -	int s;
   5.151 -	struct sockaddr_in sa;
   5.152 -
   5.153 -	if((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
   5.154 -		perror("failed to create listening socket");
   5.155 -		return -1;
   5.156 -	}
   5.157 -	fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
   5.158 -
   5.159 -	memset(&sa, 0, sizeof sa);
   5.160 -	sa.sin_family = AF_INET;
   5.161 -	sa.sin_addr.s_addr = INADDR_ANY;
   5.162 -	sa.sin_port = htons(port);
   5.163 -
   5.164 -	if(bind(s, (struct sockaddr*)&sa, sizeof sa) == -1) {
   5.165 -		fprintf(stderr, "failed to bind socket to port %d: %s\n", port, strerror(errno));
   5.166 -		return -1;
   5.167 -	}
   5.168 -	listen(s, 16);
   5.169 -
   5.170 -	return s;
   5.171 -}
   5.172 -
   5.173 -int accept_conn(int lis)
   5.174 -{
   5.175 -	int s;
   5.176 -	struct client *c;
   5.177 -	struct sockaddr_in addr;
   5.178 -	socklen_t addr_sz = sizeof addr;
   5.179 -
   5.180 -	if((s = accept(lis, (struct sockaddr*)&addr, &addr_sz)) == -1) {
   5.181 -		perror("failed to accept incoming connection");
   5.182 -		return -1;
   5.183 -	}
   5.184 -	fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
   5.185 -
   5.186 -	if(!(c = malloc(sizeof *c))) {
   5.187 -		perror("failed to allocate memory while accepting connection");
   5.188 -		return -1;
   5.189 -	}
   5.190 -	c->s = s;
   5.191 -	c->rcvbuf = 0;
   5.192 -	c->bufsz = 0;
   5.193 -	c->next = clist;
   5.194 -	clist = c;
   5.195 -	return 0;
   5.196 -}
   5.197 -
   5.198 -void close_conn(struct client *c)
   5.199 -{
   5.200 -	close(c->s);
   5.201 -	c->s = -1;	/* mark it for removal */
   5.202 -	free(c->rcvbuf);
   5.203 -}
   5.204 -
   5.205 -int handle_client(struct client *c)
   5.206 -{
   5.207 -	struct http_req_header hdr;
   5.208 -	static char buf[2048];
   5.209 -	int rdsz, status;
   5.210 -
   5.211 -	while((rdsz = recv(c->s, buf, sizeof buf, 0)) > 0) {
   5.212 -		char *newbuf;
   5.213 -		int newsz = c->bufsz + rdsz;
   5.214 -		if(newsz > MAX_REQ_LENGTH) {
   5.215 -			respond_error(c, 413);
   5.216 -			return -1;
   5.217 -		}
   5.218 -
   5.219 -		if(!(newbuf = realloc(c->rcvbuf, newsz + 1))) {
   5.220 -			fprintf(stderr, "failed to allocate %d byte buffer\n", newsz);
   5.221 -			respond_error(c, 503);
   5.222 -			return -1;
   5.223 -		}
   5.224 -
   5.225 -		memcpy(newbuf + c->bufsz, buf, rdsz);
   5.226 -		newbuf[newsz] = 0;
   5.227 -
   5.228 -		c->rcvbuf = newbuf;
   5.229 -		c->bufsz = newsz;
   5.230 -	}
   5.231 -
   5.232 -	if((status = http_parse_header(&hdr, c->rcvbuf, c->bufsz)) != HTTP_HDR_OK) {
   5.233 -		http_print_header(&hdr);
   5.234 -		switch(status) {
   5.235 -		case HTTP_HDR_INVALID:
   5.236 -			respond_error(c, 400);
   5.237 -			return -1;
   5.238 -
   5.239 -		case HTTP_HDR_NOMEM:
   5.240 -			respond_error(c, 503);
   5.241 -			return -1;
   5.242 -
   5.243 -		case HTTP_HDR_PARTIAL:
   5.244 -			return 0;	/* partial header, continue reading */
   5.245 -		}
   5.246 -	}
   5.247 -	http_print_header(&hdr);
   5.248 -
   5.249 -	/* we only support GET and HEAD at this point, so freak out on anything else */
   5.250 -	switch(hdr.method) {
   5.251 -	case HTTP_GET:
   5.252 -		do_get(c, hdr.uri, 1);
   5.253 -		break;
   5.254 -
   5.255 -	case HTTP_HEAD:
   5.256 -		do_get(c, hdr.uri, 0);
   5.257 -		break;
   5.258 -
   5.259 -	default:
   5.260 -		respond_error(c, 501);
   5.261 -		return -1;
   5.262 -	}
   5.263 -
   5.264 -	close_conn(c);
   5.265 -	return 0;
   5.266 -}
   5.267 -
   5.268 -int do_get(struct client *c, const char *uri, int with_body)
   5.269 -{
   5.270 -	const char *ptr;
   5.271 -	struct http_resp_header resp;
   5.272 -
   5.273 -	if((ptr = strstr(uri, "://"))) {
   5.274 -		/* skip the host part */
   5.275 -		if(!(uri = strchr(ptr + 3, '/'))) {
   5.276 -			respond_error(c, 404);
   5.277 -			return -1;
   5.278 -		}
   5.279 -		++uri;
   5.280 -	}
   5.281 -
   5.282 -	if(*uri) {
   5.283 -		struct stat st;
   5.284 -		char *path = 0;
   5.285 -		char *buf;
   5.286 -		const char *type;
   5.287 -		int fd, size;
   5.288 -
   5.289 -		if(stat(uri, &st) == -1) {
   5.290 -			respond_error(c, 404);
   5.291 -			return -1;
   5.292 -		}
   5.293 -
   5.294 -		if(S_ISDIR(st.st_mode)) {
   5.295 -			int i;
   5.296 -			path = alloca(strlen(uri) + 64);
   5.297 -
   5.298 -			for(i=0; indexfiles[i]; i++) {
   5.299 -				sprintf(path, "%s/%s", uri, indexfiles[i]);
   5.300 -				if(stat(path, &st) == 0 && !S_ISDIR(st.st_mode)) {
   5.301 -					break;
   5.302 -				}
   5.303 -			}
   5.304 -
   5.305 -			if(indexfiles[i] == 0) {
   5.306 -				respond_error(c, 404);
   5.307 -				return -1;
   5.308 -			}
   5.309 -		} else {
   5.310 -			path = (char*)uri;
   5.311 -		}
   5.312 -
   5.313 -		if((fd = open(path, O_RDONLY)) == -1) {
   5.314 -			respond_error(c, 403);
   5.315 -			return -1;
   5.316 -		}
   5.317 -
   5.318 -		/* construct response header */
   5.319 -		http_init_resp(&resp);
   5.320 -		http_add_resp_field(&resp, "Content-Length: %d", st.st_size);
   5.321 -		if((type = mime_type(path))) {
   5.322 -			http_add_resp_field(&resp, "Content-Type: %s", type);
   5.323 -		}
   5.324 -
   5.325 -		size = http_serialize_resp(&resp, 0);
   5.326 -		buf = alloca(size);
   5.327 -		http_serialize_resp(&resp, buf);
   5.328 -		send(c->s, buf, size, 0);
   5.329 -
   5.330 -		if(with_body) {
   5.331 -			char *cont = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
   5.332 -			if(cont == (void*)-1) {
   5.333 -				respond_error(c, 503);
   5.334 -				close(fd);
   5.335 -				return -1;
   5.336 -			}
   5.337 -		}
   5.338 -
   5.339 -		close(fd);
   5.340 -	}
   5.341 -	return 0;
   5.342 -}
   5.343 -
   5.344 -void respond_error(struct client *c, int errcode)
   5.345 -{
   5.346 -	char buf[512];
   5.347 -
   5.348 -	sprintf(buf, HTTP_VER_STR " %d %s\r\n\r\n", errcode, http_strmsg(errcode));
   5.349 -
   5.350 -	send(c->s, buf, strlen(buf), 0);
   5.351 -	close_conn(c);
   5.352 -}
   5.353 -
   5.354  void sighandler(int s)
   5.355  {
   5.356  	if(s == SIGINT || s == SIGTERM || s == SIGQUIT) {
   5.357 -		close(lis);
   5.358 -		while(clist) {
   5.359 -			struct client *c = clist;
   5.360 -			clist = clist->next;
   5.361 -			close_conn(c);
   5.362 -			free(c);
   5.363 -		}
   5.364 -		clist = 0;
   5.365 -
   5.366 +		tw_stop();
   5.367  		printf("bye!\n");
   5.368  		exit(0);
   5.369  	}
   5.370 @@ -356,9 +84,13 @@
   5.371  		if(argv[i][0] == '-' && argv[i][2] == 0) {
   5.372  			switch(argv[i][1]) {
   5.373  			case 'p':
   5.374 -				if((port = atoi(argv[++i])) == 0) {
   5.375 -					fprintf(stderr, "-p must be followed by a valid port number\n");
   5.376 -					return -1;
   5.377 +				{
   5.378 +					int port = atoi(argv[++i]);
   5.379 +					if(!port) {
   5.380 +						fprintf(stderr, "-p must be followed by a valid port number\n");
   5.381 +						return -1;
   5.382 +					}
   5.383 +					tw_set_port(port);
   5.384  				}
   5.385  				break;
   5.386  
     6.1 --- a/src/mime.c	Fri Apr 17 01:57:25 2015 +0300
     6.2 +++ b/src/mime.c	Fri Apr 17 11:45:08 2015 +0300
     6.3 @@ -33,7 +33,7 @@
     6.4  
     6.5  	if(types) return 0;
     6.6  
     6.7 -	if(rb_init(types, RB_KEY_STRING) == -1) {
     6.8 +	if((types = rb_create(RB_KEY_STRING))) {
     6.9  		return -1;
    6.10  	}
    6.11  	rb_set_delete_func(types, del_func, 0);
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/src/tinyweb.c	Fri Apr 17 11:45:08 2015 +0300
     7.3 @@ -0,0 +1,388 @@
     7.4 +#include <stdio.h>
     7.5 +#include <stdlib.h>
     7.6 +#include <string.h>
     7.7 +#include <ctype.h>
     7.8 +#include <errno.h>
     7.9 +#include <unistd.h>
    7.10 +#include <fcntl.h>
    7.11 +#include <sys/stat.h>
    7.12 +#include <sys/select.h>
    7.13 +#include <sys/mman.h>
    7.14 +#include <sys/types.h>
    7.15 +#include <sys/socket.h>
    7.16 +#include <arpa/inet.h>
    7.17 +#include "tinyweb.h"
    7.18 +#include "http.h"
    7.19 +#include "mime.h"
    7.20 +#include "logger.h"
    7.21 +
    7.22 +/* HTTP version */
    7.23 +#define HTTP_VER_MAJOR	1
    7.24 +#define HTTP_VER_MINOR	1
    7.25 +#define HTTP_VER_STR	"1.1"
    7.26 +
    7.27 +/* maximum request length: 64mb */
    7.28 +#define MAX_REQ_LENGTH	(65536 * 1024)
    7.29 +
    7.30 +struct client {
    7.31 +	int s;
    7.32 +	char *rcvbuf;
    7.33 +	int bufsz;
    7.34 +	struct client *next;
    7.35 +};
    7.36 +
    7.37 +static int accept_conn(int lis);
    7.38 +static void close_conn(struct client *c);
    7.39 +static int handle_client(struct client *c);
    7.40 +static int do_get(struct client *c, const char *uri, int with_body);
    7.41 +static void respond_error(struct client *c, int errcode);
    7.42 +
    7.43 +static int lis = -1;
    7.44 +static int maxfd;
    7.45 +static int port = 8080;
    7.46 +static struct client *clist;
    7.47 +static int num_clients;
    7.48 +
    7.49 +static const char *indexfiles[] = {
    7.50 +	"index.cgi",
    7.51 +	"index.html",
    7.52 +	"index.htm",
    7.53 +	0
    7.54 +};
    7.55 +
    7.56 +void tw_set_port(int p)
    7.57 +{
    7.58 +	port = p;
    7.59 +}
    7.60 +
    7.61 +int tw_set_root(const char *path)
    7.62 +{
    7.63 +	return chdir(path);
    7.64 +}
    7.65 +
    7.66 +int tw_set_logfile(const char *fname)
    7.67 +{
    7.68 +	return set_log_file(fname);
    7.69 +}
    7.70 +
    7.71 +int tw_start(void)
    7.72 +{
    7.73 +	int s;
    7.74 +	struct sockaddr_in sa;
    7.75 +
    7.76 +	logmsg("starting server ...\n");
    7.77 +
    7.78 +	if(lis != -1) {
    7.79 +		logmsg("can't start tinyweb server: already running!\n");
    7.80 +		return -1;
    7.81 +	}
    7.82 +
    7.83 +	if((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
    7.84 +		logmsg("failed to create listening socket: %s\n", strerror(errno));
    7.85 +		return -1;
    7.86 +	}
    7.87 +	fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
    7.88 +
    7.89 +	memset(&sa, 0, sizeof sa);
    7.90 +	sa.sin_family = AF_INET;
    7.91 +	sa.sin_addr.s_addr = INADDR_ANY;
    7.92 +	sa.sin_port = htons(port);
    7.93 +
    7.94 +	if(bind(s, (struct sockaddr*)&sa, sizeof sa) == -1) {
    7.95 +		logmsg("failed to bind socket to port %d: %s\n", port, strerror(errno));
    7.96 +		return -1;
    7.97 +	}
    7.98 +	listen(s, 16);
    7.99 +
   7.100 +	lis = s;
   7.101 +	return s;
   7.102 +}
   7.103 +
   7.104 +int tw_stop(void)
   7.105 +{
   7.106 +	if(lis == -1) {
   7.107 +		return -1;
   7.108 +	}
   7.109 +
   7.110 +	logmsg("stopping server...\n");
   7.111 +
   7.112 +	close(lis);
   7.113 +	lis = -1;
   7.114 +
   7.115 +	while(clist) {
   7.116 +		struct client *c = clist;
   7.117 +		clist = clist->next;
   7.118 +		close_conn(c);
   7.119 +		free(c);
   7.120 +	}
   7.121 +	clist = 0;
   7.122 +
   7.123 +	return 0;
   7.124 +}
   7.125 +
   7.126 +int tw_get_sockets(int *socks)
   7.127 +{
   7.128 +	struct client *c, dummy;
   7.129 +
   7.130 +	if(!socks) {
   7.131 +		/* just return the count */
   7.132 +		return num_clients + 1;	/* +1 for the listening socket */
   7.133 +	}
   7.134 +
   7.135 +	/* first cleanup the clients marked for removal */
   7.136 +	dummy.next = clist;
   7.137 +	c = &dummy;
   7.138 +
   7.139 +	while(c->next) {
   7.140 +		struct client *n = c->next;
   7.141 +
   7.142 +		if(n->s == -1) {
   7.143 +			/* marked for removal */
   7.144 +			c->next = n->next;
   7.145 +			free(n);
   7.146 +			--num_clients;
   7.147 +		} else {
   7.148 +			c = c->next;
   7.149 +		}
   7.150 +	}
   7.151 +	clist = dummy.next;
   7.152 +
   7.153 +	/* go through the client list and populate the array */
   7.154 +	maxfd = lis;
   7.155 +	*socks++ = lis;
   7.156 +
   7.157 +	c = clist;
   7.158 +	while(c) {
   7.159 +		*socks++ = c->s;
   7.160 +		if(c->s > maxfd) {
   7.161 +			maxfd = c->s;
   7.162 +		}
   7.163 +		c = c->next;
   7.164 +	}
   7.165 +	return num_clients + 1;	/* +1 for the listening socket */
   7.166 +}
   7.167 +
   7.168 +int tw_get_maxfd(void)
   7.169 +{
   7.170 +	return maxfd;
   7.171 +}
   7.172 +
   7.173 +int tw_handle_socket(int s)
   7.174 +{
   7.175 +	struct client *c;
   7.176 +
   7.177 +	if(s == lis) {
   7.178 +		return accept_conn(s);
   7.179 +	}
   7.180 +
   7.181 +	/* find which client corresponds to this socket */
   7.182 +	c = clist;
   7.183 +	while(c) {
   7.184 +		if(c->s == s) {
   7.185 +			return handle_client(c);
   7.186 +		}
   7.187 +		c = c->next;
   7.188 +	}
   7.189 +
   7.190 +	logmsg("socket %d doesn't correspond to any client\n");
   7.191 +	return -1;
   7.192 +}
   7.193 +
   7.194 +static int accept_conn(int lis)
   7.195 +{
   7.196 +	int s;
   7.197 +	struct client *c;
   7.198 +	struct sockaddr_in addr;
   7.199 +	socklen_t addr_sz = sizeof addr;
   7.200 +
   7.201 +	if((s = accept(lis, (struct sockaddr*)&addr, &addr_sz)) == -1) {
   7.202 +		logmsg("failed to accept incoming connection: %s\n", strerror(errno));
   7.203 +		return -1;
   7.204 +	}
   7.205 +	fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
   7.206 +
   7.207 +	if(!(c = malloc(sizeof *c))) {
   7.208 +		logmsg("failed to allocate memory while accepting connection: %s\n", strerror(errno));
   7.209 +		return -1;
   7.210 +	}
   7.211 +	c->s = s;
   7.212 +	c->rcvbuf = 0;
   7.213 +	c->bufsz = 0;
   7.214 +	c->next = clist;
   7.215 +	clist = c;
   7.216 +	return 0;
   7.217 +}
   7.218 +
   7.219 +static void close_conn(struct client *c)
   7.220 +{
   7.221 +	close(c->s);
   7.222 +	c->s = -1;	/* mark it for removal */
   7.223 +	free(c->rcvbuf);
   7.224 +	c->rcvbuf = 0;
   7.225 +}
   7.226 +
   7.227 +static int handle_client(struct client *c)
   7.228 +{
   7.229 +	struct http_req_header hdr;
   7.230 +	static char buf[2048];
   7.231 +	int rdsz, status;
   7.232 +
   7.233 +	while((rdsz = recv(c->s, buf, sizeof buf, 0)) > 0) {
   7.234 +		char *newbuf;
   7.235 +		int newsz = c->bufsz + rdsz;
   7.236 +		if(newsz > MAX_REQ_LENGTH) {
   7.237 +			respond_error(c, 413);
   7.238 +			return -1;
   7.239 +		}
   7.240 +
   7.241 +		if(!(newbuf = realloc(c->rcvbuf, newsz + 1))) {
   7.242 +			logmsg("failed to allocate %d byte buffer\n", newsz);
   7.243 +			respond_error(c, 503);
   7.244 +			return -1;
   7.245 +		}
   7.246 +
   7.247 +		memcpy(newbuf + c->bufsz, buf, rdsz);
   7.248 +		newbuf[newsz] = 0;
   7.249 +
   7.250 +		c->rcvbuf = newbuf;
   7.251 +		c->bufsz = newsz;
   7.252 +	}
   7.253 +
   7.254 +	if((status = http_parse_request(&hdr, c->rcvbuf, c->bufsz)) != HTTP_HDR_OK) {
   7.255 +		http_log_request(&hdr);
   7.256 +		switch(status) {
   7.257 +		case HTTP_HDR_INVALID:
   7.258 +			respond_error(c, 400);
   7.259 +			return -1;
   7.260 +
   7.261 +		case HTTP_HDR_NOMEM:
   7.262 +			respond_error(c, 503);
   7.263 +			return -1;
   7.264 +
   7.265 +		case HTTP_HDR_PARTIAL:
   7.266 +			return 0;	/* partial header, continue reading */
   7.267 +		}
   7.268 +	}
   7.269 +	http_log_request(&hdr);
   7.270 +
   7.271 +	/* we only support GET and HEAD at this point, so freak out on anything else */
   7.272 +	switch(hdr.method) {
   7.273 +	case HTTP_GET:
   7.274 +		if(do_get(c, hdr.uri, 1) == -1) {
   7.275 +			return -1;
   7.276 +		}
   7.277 +		break;
   7.278 +
   7.279 +	case HTTP_HEAD:
   7.280 +		if(do_get(c, hdr.uri, 0) == -1) {
   7.281 +			return -1;
   7.282 +		}
   7.283 +		break;
   7.284 +
   7.285 +	default:
   7.286 +		respond_error(c, 501);
   7.287 +		return -1;
   7.288 +	}
   7.289 +
   7.290 +	close_conn(c);
   7.291 +	return 0;
   7.292 +}
   7.293 +
   7.294 +static int do_get(struct client *c, const char *uri, int with_body)
   7.295 +{
   7.296 +	const char *ptr;
   7.297 +	struct http_resp_header resp;
   7.298 +
   7.299 +	if((ptr = strstr(uri, "://"))) {
   7.300 +		uri = ptr + 3;
   7.301 +	}
   7.302 +
   7.303 +	/* skip the host part and the first slash if it exists */
   7.304 +	if((ptr = strchr(uri, '/'))) {
   7.305 +		uri = ptr + 1;
   7.306 +	}
   7.307 +
   7.308 +	if(*uri) {
   7.309 +		struct stat st;
   7.310 +		char *path = 0;
   7.311 +		char *rsphdr;
   7.312 +		const char *type;
   7.313 +		int fd, rspsize;
   7.314 +
   7.315 +		if(stat(uri, &st) == -1) {
   7.316 +			respond_error(c, 404);
   7.317 +			return -1;
   7.318 +		}
   7.319 +
   7.320 +		if(S_ISDIR(st.st_mode)) {
   7.321 +			int i;
   7.322 +			path = alloca(strlen(uri) + 64);
   7.323 +
   7.324 +			for(i=0; indexfiles[i]; i++) {
   7.325 +				sprintf(path, "%s/%s", uri, indexfiles[i]);
   7.326 +				if(stat(path, &st) == 0 && !S_ISDIR(st.st_mode)) {
   7.327 +					break;
   7.328 +				}
   7.329 +			}
   7.330 +
   7.331 +			if(indexfiles[i] == 0) {
   7.332 +				respond_error(c, 404);
   7.333 +				return -1;
   7.334 +			}
   7.335 +		} else {
   7.336 +			path = (char*)uri;
   7.337 +		}
   7.338 +
   7.339 +		if((fd = open(path, O_RDONLY)) == -1) {
   7.340 +			respond_error(c, 403);
   7.341 +			return -1;
   7.342 +		}
   7.343 +
   7.344 +		/* construct response header */
   7.345 +		http_init_resp(&resp);
   7.346 +		http_add_resp_field(&resp, "Content-Length: %d", st.st_size);
   7.347 +		if((type = mime_type(path))) {
   7.348 +			http_add_resp_field(&resp, "Content-Type: %s", type);
   7.349 +		}
   7.350 +
   7.351 +		rspsize = http_serialize_resp(&resp, 0);
   7.352 +		rsphdr = alloca(rspsize);
   7.353 +		http_serialize_resp(&resp, rsphdr);
   7.354 +
   7.355 +		if(with_body) {
   7.356 +			int cont_left = st.st_size;
   7.357 +			char *cont = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
   7.358 +			if(cont == (void*)-1) {
   7.359 +				respond_error(c, 503);
   7.360 +				close(fd);
   7.361 +				return -1;
   7.362 +			}
   7.363 +			ptr = cont;
   7.364 +
   7.365 +			send(c->s, rsphdr, rspsize, 0);
   7.366 +			while(cont_left > 0) {
   7.367 +				int sz = cont_left < 4096 ? cont_left : 4096;
   7.368 +				send(c->s, ptr, sz, 0);
   7.369 +				ptr += sz;
   7.370 +			}
   7.371 +
   7.372 +			munmap(cont, st.st_size);
   7.373 +		} else {
   7.374 +			send(c->s, rsphdr, rspsize, 0);
   7.375 +		}
   7.376 +
   7.377 +		close(fd);
   7.378 +	}
   7.379 +	return 0;
   7.380 +}
   7.381 +
   7.382 +static void respond_error(struct client *c, int errcode)
   7.383 +{
   7.384 +	char buf[512];
   7.385 +
   7.386 +	sprintf(buf, "HTTP/" HTTP_VER_STR " %d %s\r\n\r\n", errcode, http_strmsg(errcode));
   7.387 +
   7.388 +	send(c->s, buf, strlen(buf), 0);
   7.389 +	close_conn(c);
   7.390 +}
   7.391 +
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/src/tinyweb.h	Fri Apr 17 11:45:08 2015 +0300
     8.3 @@ -0,0 +1,16 @@
     8.4 +#ifndef TINYWEB_H_
     8.5 +#define TINYWEB_H_
     8.6 +
     8.7 +void tw_set_port(int port);
     8.8 +int tw_set_root(const char *path);
     8.9 +int tw_set_logfile(const char *fname);
    8.10 +
    8.11 +int tw_start(void);
    8.12 +int tw_stop(void);
    8.13 +
    8.14 +int tw_get_sockets(int *socks);
    8.15 +int tw_get_maxfd(void);
    8.16 +int tw_handle_socket(int s);
    8.17 +
    8.18 +
    8.19 +#endif	/* TINYWEB_H_ */