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