# HG changeset patch # User John Tsiombikas # Date 1429186816 -10800 # Node ID 852a745503cfee8e5e0177257108198bda20135a # Parent 7bb4c2a0a360f5e8c2a56d1b9ac08005449e078d http header parsing, not tested diff -r 7bb4c2a0a360 -r 852a745503cf src/http.c --- a/src/http.c Wed Apr 15 23:44:22 2015 +0300 +++ b/src/http.c Thu Apr 16 15:20:16 2015 +0300 @@ -1,10 +1,98 @@ +#include +#include +#include +#include +#include #include "http.h" + +const char *http_method_str[] = { + "", + "OPTIONS", + "GET", + "HEAD", + "POST", + "PUT", + "DELETE", + "TRACE", + "CONNECT", + 0 +}; + + +/* HTTP 1xx message strings */ +const char *http_msg1xx[] = { + "Continue", /* 100 */ + "Switching Protocols" /* 101 */ +}; + +/* HTTP 2xx message strings */ +const char *http_msg2xx[] = { + "OK", /* 200 */ + "Created", /* 201 */ + "Accepted", /* 202 */ + "Non-Authoritative Information", /* 203 */ + "No Content", /* 204 */ + "Reset Content", /* 205 */ + "Partial Content" /* 206 */ +}; + +/* HTTP 3xx message strings */ +const char *http_msg3xx[] = { + "Multiple Choices", /* 300 */ + "Moved Permanently", /* 301 */ + "Found", /* 302 */ + "See Other", /* 303 */ + "Not Modified", /* 304 */ + "Use Proxy", /* 305 */ + "", /* 306 is undefined? */ + "Temporary Redirect" /* 307 */ +}; + +/* HTTP 4xx error strings */ +const char *http_msg4xx[] = { + "Bad Request", /* 400 */ + "Unauthorized", /* 401 */ + "What the Fuck?", /* 402 */ + "Forbidden", /* 403 */ + "Not Found", /* 404 */ + "Method Not Allowed", /* 405 */ + "Not Acceptable", /* 406 */ + "Proxy Authentication Required", /* 407 */ + "Request Time-out", /* 408 */ + "Conflict", /* 409 */ + "Gone", /* 410 */ + "Length Required", /* 411 */ + "Precondition Failed", /* 412 */ + "Request Entity Too Large", /* 413 */ + "Request-URI Too Large", /* 414 */ + "Unsupported Media Type", /* 415 */ + "Request range not satisfiable", /* 416 */ + "Expectation Failed" /* 417 */ +}; + +/* HTTP 5xx error strings */ +const char *http_msg5xx[] = { + "Internal Server Error", /* 500 */ + "Not Implemented", /* 501 */ + "Bad Gateway", /* 502 */ + "Service Unavailable", /* 503 */ + "Gateway Time-out", /* 504 */ + "HTTP Version not supported" /* 505 */ +}; + + +static enum http_method parse_method(const char *s); + + int http_parse_header(struct http_req_header *hdr, const char *buf, int bufsz) { int i, nlines = 0; - char *endhdr; char *rqline = 0; + char *method, *uri, *version, *ptr; + const char *startln, *endln; + + memset(hdr, 0, sizeof *hdr); for(i=1; i 4 && buf[i - 2] == '\n' && buf[i - 3] == '\r') { + hdr->body_offset = i + 1; + break; + } } } - if(!rqline) - return -1; + if(!rqline) { + return HTTP_HDR_PARTIAL; + } + ptr = rqline; + while(*ptr && isspace(*ptr)) ++ptr; + method = ptr; + /* parse the request line */ + while(*ptr && !isspace(*ptr)) ++ptr; + while(*ptr && isspace(*ptr)) *ptr++ = 0; + + uri = ptr; + while(*ptr && !isspace(*ptr)) ++ptr; + while(*ptr && isspace(*ptr)) *ptr++ = 0; + + version = ptr; + while(*ptr && !isspace(*ptr)) ++ptr; + while(*ptr && isspace(*ptr)) *ptr++ = 0; + + hdr->method = parse_method(method); + hdr->uri = strdup(uri); + if(sscanf(version, "HTTP/%d.%d", &hdr->ver_major, &hdr->ver_minor) != 2) { + fprintf(stderr, "warning: failed to parse HTTP version \"%s\"\n", version); + hdr->ver_major = 1; + hdr->ver_minor = 1; + } + + if(!(hdr->hdrfields = malloc(nlines * sizeof *hdr->hdrfields))) { + perror("failed to allocate memory for the header fields"); + return HTTP_HDR_NOMEM; + } + hdr->num_hdrfields = 0; + + startln = buf; + endln = buf; + for(i=1; ibody_offset; i++) { + if(buf[i] == '\n' && buf[i - 1] == '\r') { + int linesz; + endln = buf + i - 1; + linesz = endln - startln - 1; + + if(startln > buf) { /* skip first line */ + int idx = hdr->num_hdrfields++; + hdr->hdrfields[idx] = malloc(linesz + 1); + memcpy(hdr->hdrfields[idx], startln, linesz); + hdr->hdrfields[idx][linesz] = 0; + } + startln = endln = buf + i + 1; + } + } + + return HTTP_HDR_OK; +} + +void http_print_header(struct http_req_header *hdr) +{ + int i; + + printf("HTTP request header\n"); + printf(" method: %s\n", http_method_str[hdr->method]); + printf(" uri: %s\n", hdr->uri); + printf(" version: %d.%d\n", hdr->ver_major, hdr->ver_minor); + printf(" fields (%d):\n", hdr->num_hdrfields); + + for(i=0; inum_hdrfields; i++) { + printf(" %s\n", hdr->hdrfields[i]); + } + putchar('\n'); +} + +void http_destroy_header(struct http_req_header *hdr) +{ + int i; + + if(hdr->hdrfields) { + for(i=0; inum_hdrfields; i++) { + free(hdr->hdrfields[i]); + } + free(hdr->hdrfields); + } + free(hdr->uri); } const char *http_strmsg(int code) @@ -50,3 +221,14 @@ return msgxxx[type][idx]; } + +static enum http_method parse_method(const char *s) +{ + int i; + for(i=0; http_method_str[i]; i++) { + if(strcmp(s, http_method_str[i]) == 0) { + return (enum http_method)i; + } + } + return HTTP_UNKNOWN; +} diff -r 7bb4c2a0a360 -r 852a745503cf src/http.h --- a/src/http.h Wed Apr 15 23:44:22 2015 +0300 +++ b/src/http.h Thu Apr 16 15:20:16 2015 +0300 @@ -15,93 +15,24 @@ NUM_HTTP_METHODS }; -const char *http_method_str[] = { - "", - "OPTIONS", - "GET", - "HEAD", - "POST", - "PUT", - "DELETE", - "TRACE", - "CONNECT", - 0 -}; - - struct http_req_header { enum http_method method; char *uri; int ver_major, ver_minor; /* http version */ char **hdrfields; int num_hdrfields; + int body_offset; }; +#define HTTP_HDR_OK 0 +#define HTTP_HDR_INVALID -1 +#define HTTP_HDR_NOMEM -2 +#define HTTP_HDR_PARTIAL -3 int http_parse_header(struct http_req_header *hdr, const char *buf, int bufsz); +void http_print_header(struct http_req_header *hdr); +void http_destroy_header(struct http_req_header *hdr); const char *http_strmsg(int code); - -/* HTTP 1xx message strings */ -const char *http_msg1xx[] = { - "Continue", /* 100 */ - "Switching Protocols" /* 101 */ -}; - -/* HTTP 2xx message strings */ -const char *http_msg2xx[] = { - "OK", /* 200 */ - "Created", /* 201 */ - "Accepted", /* 202 */ - "Non-Authoritative Information", /* 203 */ - "No Content", /* 204 */ - "Reset Content", /* 205 */ - "Partial Content" /* 206 */ -}; - -/* HTTP 3xx message strings */ -const char *http_msg3xx[] = { - "Multiple Choices", /* 300 */ - "Moved Permanently", /* 301 */ - "Found", /* 302 */ - "See Other", /* 303 */ - "Not Modified", /* 304 */ - "Use Proxy", /* 305 */ - "", /* 306 is undefined? */ - "Temporary Redirect" /* 307 */ -}; - -/* HTTP 4xx error strings */ -const char *http_msg4xx[] = { - "Bad Request", /* 400 */ - "Unauthorized", /* 401 */ - "What the Fuck?", /* 402 */ - "Forbidden", /* 403 */ - "Not Found", /* 404 */ - "Method Not Allowed", /* 405 */ - "Not Acceptable", /* 406 */ - "Proxy Authentication Required", /* 407 */ - "Request Time-out", /* 408 */ - "Conflict", /* 409 */ - "Gone", /* 410 */ - "Length Required", /* 411 */ - "Precondition Failed", /* 412 */ - "Request Entity Too Large", /* 413 */ - "Request-URI Too Large", /* 414 */ - "Unsupported Media Type", /* 415 */ - "Request range not satisfiable", /* 416 */ - "Expectation Failed" /* 417 */ -}; - -/* HTTP 5xx error strings */ -const char *http_msg5xx[] = { - "Internal Server Error", /* 500 */ - "Not Implemented", /* 501 */ - "Bad Gateway", /* 502 */ - "Service Unavailable", /* 503 */ - "Gateway Time-out", /* 504 */ - "HTTP Version not supported" /* 505 */ -}; - #endif /* HTTP_H_ */ diff -r 7bb4c2a0a360 -r 852a745503cf src/main.c --- a/src/main.c Wed Apr 15 23:44:22 2015 +0300 +++ b/src/main.c Thu Apr 16 15:20:16 2015 +0300 @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include #include #include @@ -27,8 +29,11 @@ int start_server(void); int accept_conn(int lis); +void close_conn(struct client *c); int handle_client(struct client *c); +void do_get_head(struct client *c); void respond_error(struct client *c, int errcode); +void sighandler(int s); int parse_args(int argc, char **argv); static int lis; @@ -41,6 +46,10 @@ return 1; } + signal(SIGINT, sighandler); + signal(SIGTERM, sighandler); + signal(SIGQUIT, sighandler); + if((lis = start_server()) == -1) { return 1; } @@ -91,6 +100,8 @@ } } } + + return 0; /* unreachable */ } int start_server(void) @@ -150,30 +161,22 @@ free(c->rcvbuf); } -static char *skip_space(char *s, char *endp) -{ - while(s < endp && *s && isspace(*s)) - ++s; - return s; -} - int handle_client(struct client *c) { - char *reqp, *hdrp, *bodyp, *endp, *eol; - char req_type[32]; + struct http_req_header hdr; static char buf[2048]; - int rdsz; + int rdsz, status; while((rdsz = recv(c->s, buf, sizeof buf, 0)) > 0) { if(c->rcvbuf) { + char *newbuf; int newsz = c->bufsz + rdsz; if(newsz > MAX_REQ_LENGTH) { respond_error(c, 413); return -1; } - char *newbuf = realloc(buf, newsz + 1); - if(!newbuf) { + if(!(newbuf = realloc(buf, newsz + 1))) { fprintf(stderr, "failed to allocate %d byte buffer\n", newsz); respond_error(c, 503); return -1; @@ -187,17 +190,41 @@ } } - endp = c->rcvbuf + c->bufsz; - if((reqp = skip_space(buf, endp)) >= endp) { - return 0; /* incomplete have to read more ... */ + if((status = http_parse_header(&hdr, c->rcvbuf, c->bufsz)) != HTTP_HDR_OK) { + http_print_header(&hdr); + switch(status) { + case HTTP_HDR_INVALID: + respond_error(c, 400); + return -1; + + case HTTP_HDR_NOMEM: + respond_error(c, 503); + return -1; + + case HTTP_HDR_PARTIAL: + return 0; /* partial header, continue reading */ + } + } + http_print_header(&hdr); + + /* we only support GET and HEAD at this point, so freak out on anything else */ + switch(hdr.method) { + case HTTP_GET: + case HTTP_HEAD: + do_get_head(c); + break; + + default: + respond_error(c, 501); + return -1; } + close_conn(c); + return 0; +} - /* we only support GET and HEAD at this point, so freak out on anything else */ - - /* TODO: parse header, drop on invalid, determine if the request - * is complete and process - */ +void do_get_head(struct client *c) +{ } void respond_error(struct client *c, int errcode) @@ -210,6 +237,23 @@ close_conn(c); } +void sighandler(int s) +{ + if(s == SIGINT || s == SIGTERM || s == SIGQUIT) { + close(lis); + while(clist) { + struct client *c = clist; + clist = clist->next; + close_conn(c); + free(c); + } + clist = 0; + + printf("bye!\n"); + exit(0); + } +} + static void print_help(const char *argv0) {