tinywebd
changeset 1:f425a9805d17
foo
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Tue, 14 Apr 2015 08:32:51 +0300 |
parents | 9c9e24956d99 |
children | 7bb4c2a0a360 |
files | src/http.c src/http.h src/main.c |
diffstat | 3 files changed, 226 insertions(+), 8 deletions(-) [+] |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/http.c Tue Apr 14 08:32:51 2015 +0300 1.3 @@ -0,0 +1,29 @@ 1.4 +#include "http.h" 1.5 + 1.6 +const char *http_strmsg(int code) 1.7 +{ 1.8 + static const char **msgxxx[] = { 1.9 + 0, http_msg1xx, http_msg2xx, http_msg3xx, http_msg4xx, http_msg5xx 1.10 + }; 1.11 + static int msgcount[] = { 1.12 + 0, 1.13 + sizeof http_msg1xx / sizeof *http_msg1xx, 1.14 + sizeof http_msg2xx / sizeof *http_msg2xx, 1.15 + sizeof http_msg3xx / sizeof *http_msg3xx, 1.16 + sizeof http_msg4xx / sizeof *http_msg4xx, 1.17 + sizeof http_msg5xx / sizeof *http_msg5xx 1.18 + }; 1.19 + 1.20 + int type = code / 100; 1.21 + int idx = code % 100; 1.22 + 1.23 + if(type < 1 || type >= sizeof msgxxx / sizeof *msgxxx) { 1.24 + return "Invalid HTTP Status"; 1.25 + } 1.26 + 1.27 + if(idx < 0 || idx >= msgcount[type]) { 1.28 + return "Unknown HTTP Status"; 1.29 + } 1.30 + 1.31 + return msgxxx[type][idx]; 1.32 +}
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/src/http.h Tue Apr 14 08:32:51 2015 +0300 2.3 @@ -0,0 +1,103 @@ 2.4 +#ifndef HTTP_H_ 2.5 +#define HTTP_H_ 2.6 + 2.7 +enum http_method { 2.8 + HTTP_UNKNOWN, 2.9 + HTTP_OPTIONS, 2.10 + HTTP_GET, 2.11 + HTTP_HEAD, 2.12 + HTTP_POST, 2.13 + HTTP_PUT, 2.14 + HTTP_DELETE, 2.15 + HTTP_TRACE, 2.16 + HTTP_CONNECT, 2.17 + 2.18 + NUM_HTTP_METHODS 2.19 +}; 2.20 + 2.21 +const char *http_method_str[] = { 2.22 + "<unknown>", 2.23 + "OPTIONS", 2.24 + "GET", 2.25 + "HEAD", 2.26 + "POST", 2.27 + "PUT", 2.28 + "DELETE", 2.29 + "TRACE", 2.30 + "CONNECT", 2.31 + 0 2.32 +}; 2.33 + 2.34 + 2.35 +struct http_req_header { 2.36 + enum http_method method; 2.37 + char *uri; 2.38 + int ver_major, ver_minor; /* http version */ 2.39 + /* XXX cont. */ 2.40 +}; 2.41 + 2.42 + 2.43 +/* HTTP 1xx message strings */ 2.44 +const char *http_msg1xx[] = { 2.45 + "Continue", /* 100 */ 2.46 + "Switching Protocols" /* 101 */ 2.47 +}; 2.48 + 2.49 +/* HTTP 2xx message strings */ 2.50 +const char *http_msg2xx[] = { 2.51 + "OK", /* 200 */ 2.52 + "Created", /* 201 */ 2.53 + "Accepted", /* 202 */ 2.54 + "Non-Authoritative Information", /* 203 */ 2.55 + "No Content", /* 204 */ 2.56 + "Reset Content", /* 205 */ 2.57 + "Partial Content" /* 206 */ 2.58 +}; 2.59 + 2.60 +/* HTTP 3xx message strings */ 2.61 +const char *http_msg3xx[] = { 2.62 + "Multiple Choices", /* 300 */ 2.63 + "Moved Permanently", /* 301 */ 2.64 + "Found", /* 302 */ 2.65 + "See Other", /* 303 */ 2.66 + "Not Modified", /* 304 */ 2.67 + "Use Proxy", /* 305 */ 2.68 + "<unknown>", /* 306 is undefined? */ 2.69 + "Temporary Redirect" /* 307 */ 2.70 +}; 2.71 + 2.72 +/* HTTP 4xx error strings */ 2.73 +const char *http_msg4xx[] = { 2.74 + "Bad Request", /* 400 */ 2.75 + "Unauthorized", /* 401 */ 2.76 + "What the Fuck?", /* 402 */ 2.77 + "Forbidden", /* 403 */ 2.78 + "Not Found", /* 404 */ 2.79 + "Method Not Allowed", /* 405 */ 2.80 + "Not Acceptable", /* 406 */ 2.81 + "Proxy Authentication Required", /* 407 */ 2.82 + "Request Time-out", /* 408 */ 2.83 + "Conflict", /* 409 */ 2.84 + "Gone", /* 410 */ 2.85 + "Length Required", /* 411 */ 2.86 + "Precondition Failed", /* 412 */ 2.87 + "Request Entity Too Large", /* 413 */ 2.88 + "Request-URI Too Large", /* 414 */ 2.89 + "Unsupported Media Type", /* 415 */ 2.90 + "Request range not satisfiable", /* 416 */ 2.91 + "Expectation Failed" /* 417 */ 2.92 +}; 2.93 + 2.94 +/* HTTP 5xx error strings */ 2.95 +const char *http_msg5xx[] = { 2.96 + "Internal Server Error", /* 500 */ 2.97 + "Not Implemented", /* 501 */ 2.98 + "Bad Gateway", /* 502 */ 2.99 + "Service Unavailable", /* 503 */ 2.100 + "Gateway Time-out", /* 504 */ 2.101 + "HTTP Version not supported" /* 505 */ 2.102 +}; 2.103 + 2.104 +const char *http_strmsg(int code); 2.105 + 2.106 +#endif /* HTTP_H_ */
3.1 --- a/src/main.c Tue Apr 14 02:13:29 2015 +0300 3.2 +++ b/src/main.c Tue Apr 14 08:32:51 2015 +0300 3.3 @@ -8,6 +8,15 @@ 3.4 #include <sys/types.h> 3.5 #include <sys/socket.h> 3.6 #include <arpa/inet.h> 3.7 +#include "http.h" 3.8 + 3.9 +/* HTTP version */ 3.10 +#define HTTP_VER_MAJOR 1 3.11 +#define HTTP_VER_MINOR 1 3.12 +#define HTTP_VER_STR "1.1" 3.13 + 3.14 +/* maximum request length: 64mb */ 3.15 +#define MAX_REQ_LENGTH (65536 * 1024) 3.16 3.17 struct client { 3.18 int s; 3.19 @@ -19,6 +28,8 @@ 3.20 int start_server(void); 3.21 int accept_conn(int lis); 3.22 int handle_client(struct client *c); 3.23 +void respond_error(struct client *c, int errcode); 3.24 +int parse_args(int argc, char **argv); 3.25 3.26 static int lis; 3.27 static int port = 8080; 3.28 @@ -26,6 +37,10 @@ 3.29 3.30 int main(int argc, char **argv) 3.31 { 3.32 + if(parse_args(argc, argv) == -1) { 3.33 + return 1; 3.34 + } 3.35 + 3.36 if((lis = start_server()) == -1) { 3.37 return 1; 3.38 } 3.39 @@ -128,37 +143,108 @@ 3.40 return 0; 3.41 } 3.42 3.43 +void close_conn(struct client *c) 3.44 +{ 3.45 + close(c->s); 3.46 + c->s = -1; /* mark it for removal */ 3.47 + free(c->rcvbuf); 3.48 +} 3.49 + 3.50 +static char *skip_space(char *s, char *endp) 3.51 +{ 3.52 + while(s < endp && *s && isspace(*s)) 3.53 + ++s; 3.54 + return s; 3.55 +} 3.56 + 3.57 int handle_client(struct client *c) 3.58 { 3.59 + char *reqp, *hdrp, *bodyp, *endp, *eol; 3.60 + char req_type[32]; 3.61 static char buf[2048]; 3.62 int rdsz; 3.63 3.64 while((rdsz = recv(c->s, buf, sizeof buf, 0)) > 0) { 3.65 if(c->rcvbuf) { 3.66 int newsz = c->bufsz + rdsz; 3.67 - char *newbuf = realloc(buf, newsz); 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 fprintf(stderr, "failed to allocate %d byte buffer\n", newsz); 3.76 - /* TODO http error */ 3.77 - goto drop; 3.78 + respond_error(c, 503); 3.79 + return -1; 3.80 } 3.81 3.82 memcpy(newbuf + c->bufsz, buf, rdsz); 3.83 + newbuf[newsz] = 0; 3.84 3.85 c->rcvbuf = newbuf; 3.86 c->bufsz = newsz; 3.87 } 3.88 } 3.89 3.90 + endp = c->rcvbuf + c->bufsz; 3.91 + if((reqp = skip_space(buf, endp)) >= endp) { 3.92 + return 0; /* incomplete have to read more ... */ 3.93 + } 3.94 + 3.95 + 3.96 + /* we only support GET and HEAD at this point, so freak out on anything else */ 3.97 + 3.98 /* TODO: parse header, drop on invalid, determine if the request 3.99 * is complete and process 3.100 */ 3.101 +} 3.102 3.103 - if(rdsz == -1 && errno != EAGAIN) { 3.104 -drop: 3.105 - close(c->s); 3.106 - c->s = -1; 3.107 - free(c->rcvbuf); 3.108 +void respond_error(struct client *c, int errcode) 3.109 +{ 3.110 + char buf[512]; 3.111 + 3.112 + sprintf(buf, HTTP_VER_STR " %d %s\r\n\r\n", errcode, http_strmsg(errcode)); 3.113 + 3.114 + send(c->s, buf, strlen(buf), 0); 3.115 + close_conn(c); 3.116 +} 3.117 + 3.118 + 3.119 +static void print_help(const char *argv0) 3.120 +{ 3.121 + printf("Usage: %s [options]\n", argv0); 3.122 + printf("Options:\n"); 3.123 + printf(" -p <port> set the TCP/IP port number to use\n"); 3.124 + printf(" -h print usage help and exit\n"); 3.125 +} 3.126 + 3.127 +int parse_args(int argc, char **argv) 3.128 +{ 3.129 + int i; 3.130 + 3.131 + for(i=1; i<argc; i++) { 3.132 + if(argv[i][0] == '-' && argv[i][2] == 0) { 3.133 + switch(argv[i][1]) { 3.134 + case 'p': 3.135 + if((port = atoi(argv[++i])) == 0) { 3.136 + fprintf(stderr, "-p must be followed by a valid port number\n"); 3.137 + return -1; 3.138 + } 3.139 + break; 3.140 + 3.141 + case 'h': 3.142 + print_help(argv[0]); 3.143 + exit(0); 3.144 + 3.145 + default: 3.146 + fprintf(stderr, "unrecognized option: %s\n", argv[i]); 3.147 + return -1; 3.148 + } 3.149 + } else { 3.150 + fprintf(stderr, "unexpected argument: %s\n", argv[i]); 3.151 + return -1; 3.152 + } 3.153 } 3.154 return 0; 3.155 }