nuclear@0: #include nuclear@0: #include nuclear@0: #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@0: int handle_client(struct client *c); nuclear@1: void respond_error(struct client *c, int errcode); 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@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@0: } 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@1: static char *skip_space(char *s, char *endp) nuclear@1: { nuclear@1: while(s < endp && *s && isspace(*s)) nuclear@1: ++s; nuclear@1: return s; nuclear@1: } nuclear@1: nuclear@0: int handle_client(struct client *c) nuclear@0: { nuclear@1: char *reqp, *hdrp, *bodyp, *endp, *eol; nuclear@1: char req_type[32]; nuclear@0: static char buf[2048]; nuclear@0: int rdsz; nuclear@0: nuclear@0: while((rdsz = recv(c->s, buf, sizeof buf, 0)) > 0) { nuclear@0: if(c->rcvbuf) { nuclear@0: int newsz = c->bufsz + rdsz; nuclear@1: if(newsz > MAX_REQ_LENGTH) { nuclear@1: respond_error(c, 413); nuclear@1: return -1; nuclear@1: } nuclear@1: nuclear@1: char *newbuf = realloc(buf, newsz + 1); nuclear@0: if(!newbuf) { nuclear@0: fprintf(stderr, "failed to allocate %d byte buffer\n", newsz); nuclear@1: respond_error(c, 503); nuclear@1: return -1; nuclear@0: } nuclear@0: nuclear@0: memcpy(newbuf + c->bufsz, buf, rdsz); nuclear@1: newbuf[newsz] = 0; nuclear@0: nuclear@0: c->rcvbuf = newbuf; nuclear@0: c->bufsz = newsz; nuclear@0: } nuclear@0: } nuclear@0: nuclear@1: endp = c->rcvbuf + c->bufsz; nuclear@1: if((reqp = skip_space(buf, endp)) >= endp) { nuclear@1: return 0; /* incomplete have to read more ... */ nuclear@1: } nuclear@1: nuclear@1: nuclear@1: /* we only support GET and HEAD at this point, so freak out on anything else */ nuclear@1: nuclear@0: /* TODO: parse header, drop on invalid, determine if the request nuclear@0: * is complete and process nuclear@0: */ 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@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