tinywebd

diff src/main.c @ 7:5ec50ca0d071

separated the server code
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 17 Apr 2015 11:45:08 +0300
parents def49a046566
children 121b991ccc1d
line diff
     1.1 --- a/src/main.c	Fri Apr 17 01:57:25 2015 +0300
     1.2 +++ b/src/main.c	Fri Apr 17 11:45:08 2015 +0300
     1.3 @@ -1,58 +1,18 @@
     1.4  #include <stdio.h>
     1.5  #include <stdlib.h>
     1.6  #include <string.h>
     1.7 -#include <ctype.h>
     1.8 +#include <errno.h>
     1.9  #include <signal.h>
    1.10 -#include <errno.h>
    1.11 -#include <unistd.h>
    1.12 -#include <fcntl.h>
    1.13 -#include <sys/stat.h>
    1.14 -#include <sys/select.h>
    1.15 -#include <sys/mman.h>
    1.16 -#include <sys/types.h>
    1.17 -#include <sys/socket.h>
    1.18 -#include <arpa/inet.h>
    1.19 -#include "http.h"
    1.20 -#include "mime.h"
    1.21 +#include "tinyweb.h"
    1.22  
    1.23 -/* HTTP version */
    1.24 -#define HTTP_VER_MAJOR	1
    1.25 -#define HTTP_VER_MINOR	1
    1.26 -#define HTTP_VER_STR	"1.1"
    1.27 -
    1.28 -/* maximum request length: 64mb */
    1.29 -#define MAX_REQ_LENGTH	(65536 * 1024)
    1.30 -
    1.31 -struct client {
    1.32 -	int s;
    1.33 -	char *rcvbuf;
    1.34 -	int bufsz;
    1.35 -	struct client *next;
    1.36 -};
    1.37 -
    1.38 -int start_server(void);
    1.39 -int accept_conn(int lis);
    1.40 -void close_conn(struct client *c);
    1.41 -int handle_client(struct client *c);
    1.42 -int do_get(struct client *c, const char *uri, int with_body);
    1.43 -void respond_error(struct client *c, int errcode);
    1.44 +int parse_args(int argc, char **argv);
    1.45  void sighandler(int s);
    1.46 -int parse_args(int argc, char **argv);
    1.47 -
    1.48 -static int lis;
    1.49 -static int port = 8080;
    1.50 -static struct client *clist;
    1.51 -
    1.52 -static const char *indexfiles[] = {
    1.53 -	"index.cgi",
    1.54 -	"index.html",
    1.55 -	"index.htm",
    1.56 -	0
    1.57 -};
    1.58  
    1.59  
    1.60  int main(int argc, char **argv)
    1.61  {
    1.62 +	int *sockets, num_sockets, sockets_arr_size = 0;
    1.63 +
    1.64  	if(parse_args(argc, argv) == -1) {
    1.65  		return 1;
    1.66  	}
    1.67 @@ -61,279 +21,47 @@
    1.68  	signal(SIGTERM, sighandler);
    1.69  	signal(SIGQUIT, sighandler);
    1.70  
    1.71 -	if((lis = start_server()) == -1) {
    1.72 -		return 1;
    1.73 -	}
    1.74 +	tw_start();
    1.75  
    1.76  	for(;;) {
    1.77 -		struct client *c, dummy;
    1.78 -		int maxfd = lis;
    1.79 +		int i;
    1.80  		fd_set rdset;
    1.81  
    1.82 +		num_sockets = tw_get_sockets(0);
    1.83 +		if(num_sockets > sockets_arr_size) {
    1.84 +			int newsz = sockets_arr_size ? sockets_arr_size * 2 : 16;
    1.85 +			int *newarr = realloc(sockets, newsz * sizeof *sockets);
    1.86 +			if(!newarr) {
    1.87 +				fprintf(stderr, "failed to allocate sockets array\n");
    1.88 +				tw_stop();
    1.89 +				return 1;
    1.90 +			}
    1.91 +			sockets = newarr;
    1.92 +			sockets_arr_size = newsz;
    1.93 +		}
    1.94 +		tw_get_sockets(sockets);
    1.95 +
    1.96  		FD_ZERO(&rdset);
    1.97 -		FD_SET(lis, &rdset);
    1.98 -
    1.99 -		c = clist;
   1.100 -		while(c) {
   1.101 -			if(c->s > maxfd) {
   1.102 -				maxfd = c->s;
   1.103 -			}
   1.104 -			FD_SET(c->s, &rdset);
   1.105 -			c = c->next;
   1.106 +		for(i=0; i<num_sockets; i++) {
   1.107 +			FD_SET(sockets[i], &rdset);
   1.108  		}
   1.109  
   1.110 -		while(select(maxfd + 1, &rdset, 0, 0, 0) == -1 && errno == EINTR);
   1.111 +		while(select(tw_get_maxfd() + 1, &rdset, 0, 0, 0) == -1 && errno == EINTR);
   1.112  
   1.113 -		c = clist;
   1.114 -		while(c) {
   1.115 -			if(FD_ISSET(c->s, &rdset)) {
   1.116 -				handle_client(c);
   1.117 -			}
   1.118 -			c = c->next;
   1.119 -		}
   1.120 -
   1.121 -		if(FD_ISSET(lis, &rdset)) {
   1.122 -			accept_conn(lis);
   1.123 -		}
   1.124 -
   1.125 -		dummy.next = clist;
   1.126 -		c = &dummy;
   1.127 -
   1.128 -		while(c->next) {
   1.129 -			struct client *n = c->next;
   1.130 -
   1.131 -			if(n->s == -1) {
   1.132 -				/* marked for removal */
   1.133 -				c->next = n->next;
   1.134 -				free(n);
   1.135 -			} else {
   1.136 -				c = c->next;
   1.137 +		for(i=0; i<num_sockets; i++) {
   1.138 +			if(FD_ISSET(sockets[i], &rdset)) {
   1.139 +				tw_handle_socket(sockets[i]);
   1.140  			}
   1.141  		}
   1.142 -		clist = dummy.next;
   1.143  	}
   1.144  
   1.145  	return 0;	/* unreachable */
   1.146  }
   1.147  
   1.148 -int start_server(void)
   1.149 -{
   1.150 -	int s;
   1.151 -	struct sockaddr_in sa;
   1.152 -
   1.153 -	if((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
   1.154 -		perror("failed to create listening socket");
   1.155 -		return -1;
   1.156 -	}
   1.157 -	fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
   1.158 -
   1.159 -	memset(&sa, 0, sizeof sa);
   1.160 -	sa.sin_family = AF_INET;
   1.161 -	sa.sin_addr.s_addr = INADDR_ANY;
   1.162 -	sa.sin_port = htons(port);
   1.163 -
   1.164 -	if(bind(s, (struct sockaddr*)&sa, sizeof sa) == -1) {
   1.165 -		fprintf(stderr, "failed to bind socket to port %d: %s\n", port, strerror(errno));
   1.166 -		return -1;
   1.167 -	}
   1.168 -	listen(s, 16);
   1.169 -
   1.170 -	return s;
   1.171 -}
   1.172 -
   1.173 -int accept_conn(int lis)
   1.174 -{
   1.175 -	int s;
   1.176 -	struct client *c;
   1.177 -	struct sockaddr_in addr;
   1.178 -	socklen_t addr_sz = sizeof addr;
   1.179 -
   1.180 -	if((s = accept(lis, (struct sockaddr*)&addr, &addr_sz)) == -1) {
   1.181 -		perror("failed to accept incoming connection");
   1.182 -		return -1;
   1.183 -	}
   1.184 -	fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
   1.185 -
   1.186 -	if(!(c = malloc(sizeof *c))) {
   1.187 -		perror("failed to allocate memory while accepting connection");
   1.188 -		return -1;
   1.189 -	}
   1.190 -	c->s = s;
   1.191 -	c->rcvbuf = 0;
   1.192 -	c->bufsz = 0;
   1.193 -	c->next = clist;
   1.194 -	clist = c;
   1.195 -	return 0;
   1.196 -}
   1.197 -
   1.198 -void close_conn(struct client *c)
   1.199 -{
   1.200 -	close(c->s);
   1.201 -	c->s = -1;	/* mark it for removal */
   1.202 -	free(c->rcvbuf);
   1.203 -}
   1.204 -
   1.205 -int handle_client(struct client *c)
   1.206 -{
   1.207 -	struct http_req_header hdr;
   1.208 -	static char buf[2048];
   1.209 -	int rdsz, status;
   1.210 -
   1.211 -	while((rdsz = recv(c->s, buf, sizeof buf, 0)) > 0) {
   1.212 -		char *newbuf;
   1.213 -		int newsz = c->bufsz + rdsz;
   1.214 -		if(newsz > MAX_REQ_LENGTH) {
   1.215 -			respond_error(c, 413);
   1.216 -			return -1;
   1.217 -		}
   1.218 -
   1.219 -		if(!(newbuf = realloc(c->rcvbuf, newsz + 1))) {
   1.220 -			fprintf(stderr, "failed to allocate %d byte buffer\n", newsz);
   1.221 -			respond_error(c, 503);
   1.222 -			return -1;
   1.223 -		}
   1.224 -
   1.225 -		memcpy(newbuf + c->bufsz, buf, rdsz);
   1.226 -		newbuf[newsz] = 0;
   1.227 -
   1.228 -		c->rcvbuf = newbuf;
   1.229 -		c->bufsz = newsz;
   1.230 -	}
   1.231 -
   1.232 -	if((status = http_parse_header(&hdr, c->rcvbuf, c->bufsz)) != HTTP_HDR_OK) {
   1.233 -		http_print_header(&hdr);
   1.234 -		switch(status) {
   1.235 -		case HTTP_HDR_INVALID:
   1.236 -			respond_error(c, 400);
   1.237 -			return -1;
   1.238 -
   1.239 -		case HTTP_HDR_NOMEM:
   1.240 -			respond_error(c, 503);
   1.241 -			return -1;
   1.242 -
   1.243 -		case HTTP_HDR_PARTIAL:
   1.244 -			return 0;	/* partial header, continue reading */
   1.245 -		}
   1.246 -	}
   1.247 -	http_print_header(&hdr);
   1.248 -
   1.249 -	/* we only support GET and HEAD at this point, so freak out on anything else */
   1.250 -	switch(hdr.method) {
   1.251 -	case HTTP_GET:
   1.252 -		do_get(c, hdr.uri, 1);
   1.253 -		break;
   1.254 -
   1.255 -	case HTTP_HEAD:
   1.256 -		do_get(c, hdr.uri, 0);
   1.257 -		break;
   1.258 -
   1.259 -	default:
   1.260 -		respond_error(c, 501);
   1.261 -		return -1;
   1.262 -	}
   1.263 -
   1.264 -	close_conn(c);
   1.265 -	return 0;
   1.266 -}
   1.267 -
   1.268 -int do_get(struct client *c, const char *uri, int with_body)
   1.269 -{
   1.270 -	const char *ptr;
   1.271 -	struct http_resp_header resp;
   1.272 -
   1.273 -	if((ptr = strstr(uri, "://"))) {
   1.274 -		/* skip the host part */
   1.275 -		if(!(uri = strchr(ptr + 3, '/'))) {
   1.276 -			respond_error(c, 404);
   1.277 -			return -1;
   1.278 -		}
   1.279 -		++uri;
   1.280 -	}
   1.281 -
   1.282 -	if(*uri) {
   1.283 -		struct stat st;
   1.284 -		char *path = 0;
   1.285 -		char *buf;
   1.286 -		const char *type;
   1.287 -		int fd, size;
   1.288 -
   1.289 -		if(stat(uri, &st) == -1) {
   1.290 -			respond_error(c, 404);
   1.291 -			return -1;
   1.292 -		}
   1.293 -
   1.294 -		if(S_ISDIR(st.st_mode)) {
   1.295 -			int i;
   1.296 -			path = alloca(strlen(uri) + 64);
   1.297 -
   1.298 -			for(i=0; indexfiles[i]; i++) {
   1.299 -				sprintf(path, "%s/%s", uri, indexfiles[i]);
   1.300 -				if(stat(path, &st) == 0 && !S_ISDIR(st.st_mode)) {
   1.301 -					break;
   1.302 -				}
   1.303 -			}
   1.304 -
   1.305 -			if(indexfiles[i] == 0) {
   1.306 -				respond_error(c, 404);
   1.307 -				return -1;
   1.308 -			}
   1.309 -		} else {
   1.310 -			path = (char*)uri;
   1.311 -		}
   1.312 -
   1.313 -		if((fd = open(path, O_RDONLY)) == -1) {
   1.314 -			respond_error(c, 403);
   1.315 -			return -1;
   1.316 -		}
   1.317 -
   1.318 -		/* construct response header */
   1.319 -		http_init_resp(&resp);
   1.320 -		http_add_resp_field(&resp, "Content-Length: %d", st.st_size);
   1.321 -		if((type = mime_type(path))) {
   1.322 -			http_add_resp_field(&resp, "Content-Type: %s", type);
   1.323 -		}
   1.324 -
   1.325 -		size = http_serialize_resp(&resp, 0);
   1.326 -		buf = alloca(size);
   1.327 -		http_serialize_resp(&resp, buf);
   1.328 -		send(c->s, buf, size, 0);
   1.329 -
   1.330 -		if(with_body) {
   1.331 -			char *cont = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
   1.332 -			if(cont == (void*)-1) {
   1.333 -				respond_error(c, 503);
   1.334 -				close(fd);
   1.335 -				return -1;
   1.336 -			}
   1.337 -		}
   1.338 -
   1.339 -		close(fd);
   1.340 -	}
   1.341 -	return 0;
   1.342 -}
   1.343 -
   1.344 -void respond_error(struct client *c, int errcode)
   1.345 -{
   1.346 -	char buf[512];
   1.347 -
   1.348 -	sprintf(buf, HTTP_VER_STR " %d %s\r\n\r\n", errcode, http_strmsg(errcode));
   1.349 -
   1.350 -	send(c->s, buf, strlen(buf), 0);
   1.351 -	close_conn(c);
   1.352 -}
   1.353 -
   1.354  void sighandler(int s)
   1.355  {
   1.356  	if(s == SIGINT || s == SIGTERM || s == SIGQUIT) {
   1.357 -		close(lis);
   1.358 -		while(clist) {
   1.359 -			struct client *c = clist;
   1.360 -			clist = clist->next;
   1.361 -			close_conn(c);
   1.362 -			free(c);
   1.363 -		}
   1.364 -		clist = 0;
   1.365 -
   1.366 +		tw_stop();
   1.367  		printf("bye!\n");
   1.368  		exit(0);
   1.369  	}
   1.370 @@ -356,9 +84,13 @@
   1.371  		if(argv[i][0] == '-' && argv[i][2] == 0) {
   1.372  			switch(argv[i][1]) {
   1.373  			case 'p':
   1.374 -				if((port = atoi(argv[++i])) == 0) {
   1.375 -					fprintf(stderr, "-p must be followed by a valid port number\n");
   1.376 -					return -1;
   1.377 +				{
   1.378 +					int port = atoi(argv[++i]);
   1.379 +					if(!port) {
   1.380 +						fprintf(stderr, "-p must be followed by a valid port number\n");
   1.381 +						return -1;
   1.382 +					}
   1.383 +					tw_set_port(port);
   1.384  				}
   1.385  				break;
   1.386