tinywebd
changeset 3:852a745503cf
http header parsing, not tested
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Thu, 16 Apr 2015 15:20:16 +0300 |
parents | 7bb4c2a0a360 |
children | 9e054c002489 |
files | src/http.c src/http.h src/main.c |
diffstat | 3 files changed, 256 insertions(+), 99 deletions(-) [+] |
line diff
1.1 --- a/src/http.c Wed Apr 15 23:44:22 2015 +0300 1.2 +++ b/src/http.c Thu Apr 16 15:20:16 2015 +0300 1.3 @@ -1,10 +1,98 @@ 1.4 +#include <stdio.h> 1.5 +#include <stdlib.h> 1.6 +#include <string.h> 1.7 +#include <ctype.h> 1.8 +#include <alloca.h> 1.9 #include "http.h" 1.10 1.11 + 1.12 +const char *http_method_str[] = { 1.13 + "<unknown>", 1.14 + "OPTIONS", 1.15 + "GET", 1.16 + "HEAD", 1.17 + "POST", 1.18 + "PUT", 1.19 + "DELETE", 1.20 + "TRACE", 1.21 + "CONNECT", 1.22 + 0 1.23 +}; 1.24 + 1.25 + 1.26 +/* HTTP 1xx message strings */ 1.27 +const char *http_msg1xx[] = { 1.28 + "Continue", /* 100 */ 1.29 + "Switching Protocols" /* 101 */ 1.30 +}; 1.31 + 1.32 +/* HTTP 2xx message strings */ 1.33 +const char *http_msg2xx[] = { 1.34 + "OK", /* 200 */ 1.35 + "Created", /* 201 */ 1.36 + "Accepted", /* 202 */ 1.37 + "Non-Authoritative Information", /* 203 */ 1.38 + "No Content", /* 204 */ 1.39 + "Reset Content", /* 205 */ 1.40 + "Partial Content" /* 206 */ 1.41 +}; 1.42 + 1.43 +/* HTTP 3xx message strings */ 1.44 +const char *http_msg3xx[] = { 1.45 + "Multiple Choices", /* 300 */ 1.46 + "Moved Permanently", /* 301 */ 1.47 + "Found", /* 302 */ 1.48 + "See Other", /* 303 */ 1.49 + "Not Modified", /* 304 */ 1.50 + "Use Proxy", /* 305 */ 1.51 + "<unknown>", /* 306 is undefined? */ 1.52 + "Temporary Redirect" /* 307 */ 1.53 +}; 1.54 + 1.55 +/* HTTP 4xx error strings */ 1.56 +const char *http_msg4xx[] = { 1.57 + "Bad Request", /* 400 */ 1.58 + "Unauthorized", /* 401 */ 1.59 + "What the Fuck?", /* 402 */ 1.60 + "Forbidden", /* 403 */ 1.61 + "Not Found", /* 404 */ 1.62 + "Method Not Allowed", /* 405 */ 1.63 + "Not Acceptable", /* 406 */ 1.64 + "Proxy Authentication Required", /* 407 */ 1.65 + "Request Time-out", /* 408 */ 1.66 + "Conflict", /* 409 */ 1.67 + "Gone", /* 410 */ 1.68 + "Length Required", /* 411 */ 1.69 + "Precondition Failed", /* 412 */ 1.70 + "Request Entity Too Large", /* 413 */ 1.71 + "Request-URI Too Large", /* 414 */ 1.72 + "Unsupported Media Type", /* 415 */ 1.73 + "Request range not satisfiable", /* 416 */ 1.74 + "Expectation Failed" /* 417 */ 1.75 +}; 1.76 + 1.77 +/* HTTP 5xx error strings */ 1.78 +const char *http_msg5xx[] = { 1.79 + "Internal Server Error", /* 500 */ 1.80 + "Not Implemented", /* 501 */ 1.81 + "Bad Gateway", /* 502 */ 1.82 + "Service Unavailable", /* 503 */ 1.83 + "Gateway Time-out", /* 504 */ 1.84 + "HTTP Version not supported" /* 505 */ 1.85 +}; 1.86 + 1.87 + 1.88 +static enum http_method parse_method(const char *s); 1.89 + 1.90 + 1.91 int http_parse_header(struct http_req_header *hdr, const char *buf, int bufsz) 1.92 { 1.93 int i, nlines = 0; 1.94 - char *endhdr; 1.95 char *rqline = 0; 1.96 + char *method, *uri, *version, *ptr; 1.97 + const char *startln, *endln; 1.98 + 1.99 + memset(hdr, 0, sizeof *hdr); 1.100 1.101 for(i=1; i<bufsz; i++) { 1.102 if(buf[i] == '\n' && buf[i - 1] == '\r') { 1.103 @@ -14,13 +102,96 @@ 1.104 rqline[i - 1] = 0; 1.105 } 1.106 ++nlines; 1.107 + 1.108 + if(i > 4 && buf[i - 2] == '\n' && buf[i - 3] == '\r') { 1.109 + hdr->body_offset = i + 1; 1.110 + break; 1.111 + } 1.112 } 1.113 } 1.114 1.115 - if(!rqline) 1.116 - return -1; 1.117 + if(!rqline) { 1.118 + return HTTP_HDR_PARTIAL; 1.119 + } 1.120 1.121 + ptr = rqline; 1.122 + while(*ptr && isspace(*ptr)) ++ptr; 1.123 + method = ptr; 1.124 1.125 + /* parse the request line */ 1.126 + while(*ptr && !isspace(*ptr)) ++ptr; 1.127 + while(*ptr && isspace(*ptr)) *ptr++ = 0; 1.128 + 1.129 + uri = ptr; 1.130 + while(*ptr && !isspace(*ptr)) ++ptr; 1.131 + while(*ptr && isspace(*ptr)) *ptr++ = 0; 1.132 + 1.133 + version = ptr; 1.134 + while(*ptr && !isspace(*ptr)) ++ptr; 1.135 + while(*ptr && isspace(*ptr)) *ptr++ = 0; 1.136 + 1.137 + hdr->method = parse_method(method); 1.138 + hdr->uri = strdup(uri); 1.139 + if(sscanf(version, "HTTP/%d.%d", &hdr->ver_major, &hdr->ver_minor) != 2) { 1.140 + fprintf(stderr, "warning: failed to parse HTTP version \"%s\"\n", version); 1.141 + hdr->ver_major = 1; 1.142 + hdr->ver_minor = 1; 1.143 + } 1.144 + 1.145 + if(!(hdr->hdrfields = malloc(nlines * sizeof *hdr->hdrfields))) { 1.146 + perror("failed to allocate memory for the header fields"); 1.147 + return HTTP_HDR_NOMEM; 1.148 + } 1.149 + hdr->num_hdrfields = 0; 1.150 + 1.151 + startln = buf; 1.152 + endln = buf; 1.153 + for(i=1; i<hdr->body_offset; i++) { 1.154 + if(buf[i] == '\n' && buf[i - 1] == '\r') { 1.155 + int linesz; 1.156 + endln = buf + i - 1; 1.157 + linesz = endln - startln - 1; 1.158 + 1.159 + if(startln > buf) { /* skip first line */ 1.160 + int idx = hdr->num_hdrfields++; 1.161 + hdr->hdrfields[idx] = malloc(linesz + 1); 1.162 + memcpy(hdr->hdrfields[idx], startln, linesz); 1.163 + hdr->hdrfields[idx][linesz] = 0; 1.164 + } 1.165 + startln = endln = buf + i + 1; 1.166 + } 1.167 + } 1.168 + 1.169 + return HTTP_HDR_OK; 1.170 +} 1.171 + 1.172 +void http_print_header(struct http_req_header *hdr) 1.173 +{ 1.174 + int i; 1.175 + 1.176 + printf("HTTP request header\n"); 1.177 + printf(" method: %s\n", http_method_str[hdr->method]); 1.178 + printf(" uri: %s\n", hdr->uri); 1.179 + printf(" version: %d.%d\n", hdr->ver_major, hdr->ver_minor); 1.180 + printf(" fields (%d):\n", hdr->num_hdrfields); 1.181 + 1.182 + for(i=0; i<hdr->num_hdrfields; i++) { 1.183 + printf(" %s\n", hdr->hdrfields[i]); 1.184 + } 1.185 + putchar('\n'); 1.186 +} 1.187 + 1.188 +void http_destroy_header(struct http_req_header *hdr) 1.189 +{ 1.190 + int i; 1.191 + 1.192 + if(hdr->hdrfields) { 1.193 + for(i=0; i<hdr->num_hdrfields; i++) { 1.194 + free(hdr->hdrfields[i]); 1.195 + } 1.196 + free(hdr->hdrfields); 1.197 + } 1.198 + free(hdr->uri); 1.199 } 1.200 1.201 const char *http_strmsg(int code) 1.202 @@ -50,3 +221,14 @@ 1.203 1.204 return msgxxx[type][idx]; 1.205 } 1.206 + 1.207 +static enum http_method parse_method(const char *s) 1.208 +{ 1.209 + int i; 1.210 + for(i=0; http_method_str[i]; i++) { 1.211 + if(strcmp(s, http_method_str[i]) == 0) { 1.212 + return (enum http_method)i; 1.213 + } 1.214 + } 1.215 + return HTTP_UNKNOWN; 1.216 +}
2.1 --- a/src/http.h Wed Apr 15 23:44:22 2015 +0300 2.2 +++ b/src/http.h Thu Apr 16 15:20:16 2015 +0300 2.3 @@ -15,93 +15,24 @@ 2.4 NUM_HTTP_METHODS 2.5 }; 2.6 2.7 -const char *http_method_str[] = { 2.8 - "<unknown>", 2.9 - "OPTIONS", 2.10 - "GET", 2.11 - "HEAD", 2.12 - "POST", 2.13 - "PUT", 2.14 - "DELETE", 2.15 - "TRACE", 2.16 - "CONNECT", 2.17 - 0 2.18 -}; 2.19 - 2.20 - 2.21 struct http_req_header { 2.22 enum http_method method; 2.23 char *uri; 2.24 int ver_major, ver_minor; /* http version */ 2.25 char **hdrfields; 2.26 int num_hdrfields; 2.27 + int body_offset; 2.28 }; 2.29 2.30 +#define HTTP_HDR_OK 0 2.31 +#define HTTP_HDR_INVALID -1 2.32 +#define HTTP_HDR_NOMEM -2 2.33 +#define HTTP_HDR_PARTIAL -3 2.34 2.35 int http_parse_header(struct http_req_header *hdr, const char *buf, int bufsz); 2.36 +void http_print_header(struct http_req_header *hdr); 2.37 +void http_destroy_header(struct http_req_header *hdr); 2.38 2.39 const char *http_strmsg(int code); 2.40 2.41 - 2.42 -/* HTTP 1xx message strings */ 2.43 -const char *http_msg1xx[] = { 2.44 - "Continue", /* 100 */ 2.45 - "Switching Protocols" /* 101 */ 2.46 -}; 2.47 - 2.48 -/* HTTP 2xx message strings */ 2.49 -const char *http_msg2xx[] = { 2.50 - "OK", /* 200 */ 2.51 - "Created", /* 201 */ 2.52 - "Accepted", /* 202 */ 2.53 - "Non-Authoritative Information", /* 203 */ 2.54 - "No Content", /* 204 */ 2.55 - "Reset Content", /* 205 */ 2.56 - "Partial Content" /* 206 */ 2.57 -}; 2.58 - 2.59 -/* HTTP 3xx message strings */ 2.60 -const char *http_msg3xx[] = { 2.61 - "Multiple Choices", /* 300 */ 2.62 - "Moved Permanently", /* 301 */ 2.63 - "Found", /* 302 */ 2.64 - "See Other", /* 303 */ 2.65 - "Not Modified", /* 304 */ 2.66 - "Use Proxy", /* 305 */ 2.67 - "<unknown>", /* 306 is undefined? */ 2.68 - "Temporary Redirect" /* 307 */ 2.69 -}; 2.70 - 2.71 -/* HTTP 4xx error strings */ 2.72 -const char *http_msg4xx[] = { 2.73 - "Bad Request", /* 400 */ 2.74 - "Unauthorized", /* 401 */ 2.75 - "What the Fuck?", /* 402 */ 2.76 - "Forbidden", /* 403 */ 2.77 - "Not Found", /* 404 */ 2.78 - "Method Not Allowed", /* 405 */ 2.79 - "Not Acceptable", /* 406 */ 2.80 - "Proxy Authentication Required", /* 407 */ 2.81 - "Request Time-out", /* 408 */ 2.82 - "Conflict", /* 409 */ 2.83 - "Gone", /* 410 */ 2.84 - "Length Required", /* 411 */ 2.85 - "Precondition Failed", /* 412 */ 2.86 - "Request Entity Too Large", /* 413 */ 2.87 - "Request-URI Too Large", /* 414 */ 2.88 - "Unsupported Media Type", /* 415 */ 2.89 - "Request range not satisfiable", /* 416 */ 2.90 - "Expectation Failed" /* 417 */ 2.91 -}; 2.92 - 2.93 -/* HTTP 5xx error strings */ 2.94 -const char *http_msg5xx[] = { 2.95 - "Internal Server Error", /* 500 */ 2.96 - "Not Implemented", /* 501 */ 2.97 - "Bad Gateway", /* 502 */ 2.98 - "Service Unavailable", /* 503 */ 2.99 - "Gateway Time-out", /* 504 */ 2.100 - "HTTP Version not supported" /* 505 */ 2.101 -}; 2.102 - 2.103 #endif /* HTTP_H_ */
3.1 --- a/src/main.c Wed Apr 15 23:44:22 2015 +0300 3.2 +++ b/src/main.c Thu Apr 16 15:20:16 2015 +0300 3.3 @@ -1,6 +1,8 @@ 3.4 #include <stdio.h> 3.5 #include <stdlib.h> 3.6 #include <string.h> 3.7 +#include <ctype.h> 3.8 +#include <signal.h> 3.9 #include <errno.h> 3.10 #include <unistd.h> 3.11 #include <fcntl.h> 3.12 @@ -27,8 +29,11 @@ 3.13 3.14 int start_server(void); 3.15 int accept_conn(int lis); 3.16 +void close_conn(struct client *c); 3.17 int handle_client(struct client *c); 3.18 +void do_get_head(struct client *c); 3.19 void respond_error(struct client *c, int errcode); 3.20 +void sighandler(int s); 3.21 int parse_args(int argc, char **argv); 3.22 3.23 static int lis; 3.24 @@ -41,6 +46,10 @@ 3.25 return 1; 3.26 } 3.27 3.28 + signal(SIGINT, sighandler); 3.29 + signal(SIGTERM, sighandler); 3.30 + signal(SIGQUIT, sighandler); 3.31 + 3.32 if((lis = start_server()) == -1) { 3.33 return 1; 3.34 } 3.35 @@ -91,6 +100,8 @@ 3.36 } 3.37 } 3.38 } 3.39 + 3.40 + return 0; /* unreachable */ 3.41 } 3.42 3.43 int start_server(void) 3.44 @@ -150,30 +161,22 @@ 3.45 free(c->rcvbuf); 3.46 } 3.47 3.48 -static char *skip_space(char *s, char *endp) 3.49 -{ 3.50 - while(s < endp && *s && isspace(*s)) 3.51 - ++s; 3.52 - return s; 3.53 -} 3.54 - 3.55 int handle_client(struct client *c) 3.56 { 3.57 - char *reqp, *hdrp, *bodyp, *endp, *eol; 3.58 - char req_type[32]; 3.59 + struct http_req_header hdr; 3.60 static char buf[2048]; 3.61 - int rdsz; 3.62 + int rdsz, status; 3.63 3.64 while((rdsz = recv(c->s, buf, sizeof buf, 0)) > 0) { 3.65 if(c->rcvbuf) { 3.66 + char *newbuf; 3.67 int newsz = c->bufsz + rdsz; 3.68 if(newsz > MAX_REQ_LENGTH) { 3.69 respond_error(c, 413); 3.70 return -1; 3.71 } 3.72 3.73 - char *newbuf = realloc(buf, newsz + 1); 3.74 - if(!newbuf) { 3.75 + if(!(newbuf = realloc(buf, newsz + 1))) { 3.76 fprintf(stderr, "failed to allocate %d byte buffer\n", newsz); 3.77 respond_error(c, 503); 3.78 return -1; 3.79 @@ -187,17 +190,41 @@ 3.80 } 3.81 } 3.82 3.83 - endp = c->rcvbuf + c->bufsz; 3.84 - if((reqp = skip_space(buf, endp)) >= endp) { 3.85 - return 0; /* incomplete have to read more ... */ 3.86 + if((status = http_parse_header(&hdr, c->rcvbuf, c->bufsz)) != HTTP_HDR_OK) { 3.87 + http_print_header(&hdr); 3.88 + switch(status) { 3.89 + case HTTP_HDR_INVALID: 3.90 + respond_error(c, 400); 3.91 + return -1; 3.92 + 3.93 + case HTTP_HDR_NOMEM: 3.94 + respond_error(c, 503); 3.95 + return -1; 3.96 + 3.97 + case HTTP_HDR_PARTIAL: 3.98 + return 0; /* partial header, continue reading */ 3.99 + } 3.100 + } 3.101 + http_print_header(&hdr); 3.102 + 3.103 + /* we only support GET and HEAD at this point, so freak out on anything else */ 3.104 + switch(hdr.method) { 3.105 + case HTTP_GET: 3.106 + case HTTP_HEAD: 3.107 + do_get_head(c); 3.108 + break; 3.109 + 3.110 + default: 3.111 + respond_error(c, 501); 3.112 + return -1; 3.113 } 3.114 3.115 + close_conn(c); 3.116 + return 0; 3.117 +} 3.118 3.119 - /* we only support GET and HEAD at this point, so freak out on anything else */ 3.120 - 3.121 - /* TODO: parse header, drop on invalid, determine if the request 3.122 - * is complete and process 3.123 - */ 3.124 +void do_get_head(struct client *c) 3.125 +{ 3.126 } 3.127 3.128 void respond_error(struct client *c, int errcode) 3.129 @@ -210,6 +237,23 @@ 3.130 close_conn(c); 3.131 } 3.132 3.133 +void sighandler(int s) 3.134 +{ 3.135 + if(s == SIGINT || s == SIGTERM || s == SIGQUIT) { 3.136 + close(lis); 3.137 + while(clist) { 3.138 + struct client *c = clist; 3.139 + clist = clist->next; 3.140 + close_conn(c); 3.141 + free(c); 3.142 + } 3.143 + clist = 0; 3.144 + 3.145 + printf("bye!\n"); 3.146 + exit(0); 3.147 + } 3.148 +} 3.149 + 3.150 3.151 static void print_help(const char *argv0) 3.152 {