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_ */