nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@3: #include nuclear@3: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@1: #include "http.h" nuclear@1: nuclear@1: /* HTTP version */ nuclear@1: #define HTTP_VER_MAJOR 1 nuclear@1: #define HTTP_VER_MINOR 1 nuclear@1: #define HTTP_VER_STR "1.1" nuclear@1: nuclear@1: /* maximum request length: 64mb */ nuclear@1: #define MAX_REQ_LENGTH (65536 * 1024) nuclear@0: nuclear@0: struct client { nuclear@0: int s; nuclear@0: char *rcvbuf; nuclear@0: int bufsz; nuclear@0: struct client *next; nuclear@0: }; nuclear@0: nuclear@0: int start_server(void); nuclear@0: int accept_conn(int lis); nuclear@3: void close_conn(struct client *c); nuclear@0: int handle_client(struct client *c); nuclear@3: void do_get_head(struct client *c); nuclear@1: void respond_error(struct client *c, int errcode); nuclear@3: void sighandler(int s); nuclear@1: int parse_args(int argc, char **argv); nuclear@0: nuclear@0: static int lis; nuclear@0: static int port = 8080; nuclear@0: static struct client *clist; nuclear@0: nuclear@0: int main(int argc, char **argv) nuclear@0: { nuclear@1: if(parse_args(argc, argv) == -1) { nuclear@1: return 1; nuclear@1: } nuclear@1: nuclear@3: signal(SIGINT, sighandler); nuclear@3: signal(SIGTERM, sighandler); nuclear@3: signal(SIGQUIT, sighandler); nuclear@3: nuclear@0: if((lis = start_server()) == -1) { nuclear@0: return 1; nuclear@0: } nuclear@0: nuclear@0: for(;;) { nuclear@0: struct client *c, dummy; nuclear@0: int maxfd = lis; nuclear@0: fd_set rdset; nuclear@0: nuclear@0: FD_ZERO(&rdset); nuclear@0: FD_SET(lis, &rdset); nuclear@0: nuclear@0: c = clist; nuclear@0: while(c) { nuclear@0: if(c->s > maxfd) { nuclear@0: maxfd = c->s; nuclear@0: } nuclear@0: FD_SET(c->s, &rdset); nuclear@0: c = c->next; nuclear@0: } nuclear@0: nuclear@0: while(select(maxfd + 1, &rdset, 0, 0, 0) == -1 && errno == EINTR); nuclear@0: nuclear@0: c = clist; nuclear@0: while(c) { nuclear@0: if(FD_ISSET(c->s, &rdset)) { nuclear@0: handle_client(c); nuclear@0: } nuclear@0: c = c->next; nuclear@0: } nuclear@0: nuclear@0: if(FD_ISSET(lis, &rdset)) { nuclear@0: accept_conn(lis); nuclear@0: } nuclear@0: nuclear@0: dummy.next = clist; nuclear@0: c = &dummy; nuclear@0: nuclear@0: while(c->next) { nuclear@0: struct client *n = c->next; nuclear@0: nuclear@0: if(n->s == -1) { nuclear@0: /* marked for removal */ nuclear@0: c->next = n->next; nuclear@0: free(n); nuclear@0: } else { nuclear@0: c = c->next; nuclear@0: } nuclear@0: } nuclear@4: clist = dummy.next; nuclear@0: } nuclear@3: nuclear@3: return 0; /* unreachable */ nuclear@0: } nuclear@0: nuclear@0: int start_server(void) nuclear@0: { nuclear@0: int s; nuclear@0: struct sockaddr_in sa; nuclear@0: nuclear@0: if((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) { nuclear@0: perror("failed to create listening socket"); nuclear@0: return -1; nuclear@0: } nuclear@0: fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK); nuclear@0: nuclear@0: memset(&sa, 0, sizeof sa); nuclear@0: sa.sin_family = AF_INET; nuclear@0: sa.sin_addr.s_addr = INADDR_ANY; nuclear@0: sa.sin_port = htons(port); nuclear@0: nuclear@0: if(bind(s, (struct sockaddr*)&sa, sizeof sa) == -1) { nuclear@0: fprintf(stderr, "failed to bind socket to port %d: %s\n", port, strerror(errno)); nuclear@0: return -1; nuclear@0: } nuclear@0: listen(s, 16); nuclear@0: nuclear@0: return s; nuclear@0: } nuclear@0: nuclear@0: int accept_conn(int lis) nuclear@0: { nuclear@0: int s; nuclear@0: struct client *c; nuclear@0: struct sockaddr_in addr; nuclear@0: socklen_t addr_sz = sizeof addr; nuclear@0: nuclear@0: if((s = accept(lis, (struct sockaddr*)&addr, &addr_sz)) == -1) { nuclear@0: perror("failed to accept incoming connection"); nuclear@0: return -1; nuclear@0: } nuclear@0: fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK); nuclear@0: nuclear@0: if(!(c = malloc(sizeof *c))) { nuclear@0: perror("failed to allocate memory while accepting connection"); nuclear@0: return -1; nuclear@0: } nuclear@0: c->s = s; nuclear@0: c->rcvbuf = 0; nuclear@0: c->bufsz = 0; nuclear@0: c->next = clist; nuclear@0: clist = c; nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@1: void close_conn(struct client *c) nuclear@1: { nuclear@1: close(c->s); nuclear@1: c->s = -1; /* mark it for removal */ nuclear@1: free(c->rcvbuf); nuclear@1: } nuclear@1: nuclear@0: int handle_client(struct client *c) nuclear@0: { nuclear@3: struct http_req_header hdr; nuclear@0: static char buf[2048]; nuclear@3: int rdsz, status; nuclear@0: nuclear@0: while((rdsz = recv(c->s, buf, sizeof buf, 0)) > 0) { nuclear@4: char *newbuf; nuclear@4: int newsz = c->bufsz + rdsz; nuclear@4: if(newsz > MAX_REQ_LENGTH) { nuclear@4: respond_error(c, 413); nuclear@4: return -1; nuclear@4: } nuclear@1: nuclear@4: if(!(newbuf = realloc(c->rcvbuf, newsz + 1))) { nuclear@4: fprintf(stderr, "failed to allocate %d byte buffer\n", newsz); nuclear@4: respond_error(c, 503); nuclear@4: return -1; nuclear@4: } nuclear@0: nuclear@4: memcpy(newbuf + c->bufsz, buf, rdsz); nuclear@4: newbuf[newsz] = 0; nuclear@0: nuclear@4: c->rcvbuf = newbuf; nuclear@4: c->bufsz = newsz; nuclear@0: } nuclear@0: nuclear@3: if((status = http_parse_header(&hdr, c->rcvbuf, c->bufsz)) != HTTP_HDR_OK) { nuclear@3: http_print_header(&hdr); nuclear@3: switch(status) { nuclear@3: case HTTP_HDR_INVALID: nuclear@3: respond_error(c, 400); nuclear@3: return -1; nuclear@3: nuclear@3: case HTTP_HDR_NOMEM: nuclear@3: respond_error(c, 503); nuclear@3: return -1; nuclear@3: nuclear@3: case HTTP_HDR_PARTIAL: nuclear@3: return 0; /* partial header, continue reading */ nuclear@3: } nuclear@3: } nuclear@3: http_print_header(&hdr); nuclear@3: nuclear@3: /* we only support GET and HEAD at this point, so freak out on anything else */ nuclear@3: switch(hdr.method) { nuclear@3: case HTTP_GET: nuclear@3: case HTTP_HEAD: nuclear@3: do_get_head(c); nuclear@3: break; nuclear@3: nuclear@3: default: nuclear@3: respond_error(c, 501); nuclear@3: return -1; nuclear@1: } nuclear@1: nuclear@3: close_conn(c); nuclear@3: return 0; nuclear@3: } nuclear@1: nuclear@3: void do_get_head(struct client *c) nuclear@3: { nuclear@1: } nuclear@0: nuclear@1: void respond_error(struct client *c, int errcode) nuclear@1: { nuclear@1: char buf[512]; nuclear@1: nuclear@1: sprintf(buf, HTTP_VER_STR " %d %s\r\n\r\n", errcode, http_strmsg(errcode)); nuclear@1: nuclear@1: send(c->s, buf, strlen(buf), 0); nuclear@1: close_conn(c); nuclear@1: } nuclear@1: nuclear@3: void sighandler(int s) nuclear@3: { nuclear@3: if(s == SIGINT || s == SIGTERM || s == SIGQUIT) { nuclear@3: close(lis); nuclear@3: while(clist) { nuclear@3: struct client *c = clist; nuclear@3: clist = clist->next; nuclear@3: close_conn(c); nuclear@3: free(c); nuclear@3: } nuclear@3: clist = 0; nuclear@3: nuclear@3: printf("bye!\n"); nuclear@3: exit(0); nuclear@3: } nuclear@3: } nuclear@3: nuclear@1: nuclear@1: static void print_help(const char *argv0) nuclear@1: { nuclear@1: printf("Usage: %s [options]\n", argv0); nuclear@1: printf("Options:\n"); nuclear@1: printf(" -p set the TCP/IP port number to use\n"); nuclear@1: printf(" -h print usage help and exit\n"); nuclear@1: } nuclear@1: nuclear@1: int parse_args(int argc, char **argv) nuclear@1: { nuclear@1: int i; nuclear@1: nuclear@1: for(i=1; i