# HG changeset patch # User John Tsiombikas # Date 1428989571 -10800 # Node ID f425a9805d179e7854b3a6d8f90e62464f78d7b7 # Parent 9c9e24956d99ac98d4bab6077820a3918499dd00 foo diff -r 9c9e24956d99 -r f425a9805d17 src/http.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/http.c Tue Apr 14 08:32:51 2015 +0300 @@ -0,0 +1,29 @@ +#include "http.h" + +const char *http_strmsg(int code) +{ + static const char **msgxxx[] = { + 0, http_msg1xx, http_msg2xx, http_msg3xx, http_msg4xx, http_msg5xx + }; + static int msgcount[] = { + 0, + sizeof http_msg1xx / sizeof *http_msg1xx, + sizeof http_msg2xx / sizeof *http_msg2xx, + sizeof http_msg3xx / sizeof *http_msg3xx, + sizeof http_msg4xx / sizeof *http_msg4xx, + sizeof http_msg5xx / sizeof *http_msg5xx + }; + + int type = code / 100; + int idx = code % 100; + + if(type < 1 || type >= sizeof msgxxx / sizeof *msgxxx) { + return "Invalid HTTP Status"; + } + + if(idx < 0 || idx >= msgcount[type]) { + return "Unknown HTTP Status"; + } + + return msgxxx[type][idx]; +} diff -r 9c9e24956d99 -r f425a9805d17 src/http.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/http.h Tue Apr 14 08:32:51 2015 +0300 @@ -0,0 +1,103 @@ +#ifndef HTTP_H_ +#define HTTP_H_ + +enum http_method { + HTTP_UNKNOWN, + HTTP_OPTIONS, + HTTP_GET, + HTTP_HEAD, + HTTP_POST, + HTTP_PUT, + HTTP_DELETE, + HTTP_TRACE, + HTTP_CONNECT, + + 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 */ + /* XXX cont. */ +}; + + +/* 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 */ +}; + +const char *http_strmsg(int code); + +#endif /* HTTP_H_ */ diff -r 9c9e24956d99 -r f425a9805d17 src/main.c --- a/src/main.c Tue Apr 14 02:13:29 2015 +0300 +++ b/src/main.c Tue Apr 14 08:32:51 2015 +0300 @@ -8,6 +8,15 @@ #include #include #include +#include "http.h" + +/* HTTP version */ +#define HTTP_VER_MAJOR 1 +#define HTTP_VER_MINOR 1 +#define HTTP_VER_STR "1.1" + +/* maximum request length: 64mb */ +#define MAX_REQ_LENGTH (65536 * 1024) struct client { int s; @@ -19,6 +28,8 @@ int start_server(void); int accept_conn(int lis); int handle_client(struct client *c); +void respond_error(struct client *c, int errcode); +int parse_args(int argc, char **argv); static int lis; static int port = 8080; @@ -26,6 +37,10 @@ int main(int argc, char **argv) { + if(parse_args(argc, argv) == -1) { + return 1; + } + if((lis = start_server()) == -1) { return 1; } @@ -128,37 +143,108 @@ return 0; } +void close_conn(struct client *c) +{ + close(c->s); + c->s = -1; /* mark it for removal */ + 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]; static char buf[2048]; int rdsz; while((rdsz = recv(c->s, buf, sizeof buf, 0)) > 0) { if(c->rcvbuf) { int newsz = c->bufsz + rdsz; - char *newbuf = realloc(buf, newsz); + if(newsz > MAX_REQ_LENGTH) { + respond_error(c, 413); + return -1; + } + + char *newbuf = realloc(buf, newsz + 1); if(!newbuf) { fprintf(stderr, "failed to allocate %d byte buffer\n", newsz); - /* TODO http error */ - goto drop; + respond_error(c, 503); + return -1; } memcpy(newbuf + c->bufsz, buf, rdsz); + newbuf[newsz] = 0; c->rcvbuf = newbuf; c->bufsz = newsz; } } + endp = c->rcvbuf + c->bufsz; + if((reqp = skip_space(buf, endp)) >= endp) { + return 0; /* incomplete have to read more ... */ + } + + + /* 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 */ +} - if(rdsz == -1 && errno != EAGAIN) { -drop: - close(c->s); - c->s = -1; - free(c->rcvbuf); +void respond_error(struct client *c, int errcode) +{ + char buf[512]; + + sprintf(buf, HTTP_VER_STR " %d %s\r\n\r\n", errcode, http_strmsg(errcode)); + + send(c->s, buf, strlen(buf), 0); + close_conn(c); +} + + +static void print_help(const char *argv0) +{ + printf("Usage: %s [options]\n", argv0); + printf("Options:\n"); + printf(" -p set the TCP/IP port number to use\n"); + printf(" -h print usage help and exit\n"); +} + +int parse_args(int argc, char **argv) +{ + int i; + + for(i=1; i