diff options
| -rw-r--r-- | main.c | 96 | 
1 files changed, 42 insertions, 54 deletions
| @@ -36,13 +36,13 @@ static inline int verbose(const char * format, ...) { | |||
| 36 | // }}} | 36 | // }}} | 
| 37 | 37 | ||
| 38 | typedef struct { | 38 | typedef struct { | 
| 39 | int newline_length; // lenght of one newline in bytes (\n has 1, CR/LF has 2) | 39 | int newline_length; // lenght of one newline in bytes (\n has 1, CR/LF has 2) | 
| 40 | char * method; | 40 | char * method; // GET/POST or something like that | 
| 41 | char * boundary; | 41 | char * url; // request URL (e.g. /index.html) | 
| 42 | size_t boundary_size; | 42 | char * boundary; // usually looks similar to ------1234 | 
| 43 | char * content_type; | 43 | size_t boundary_size; // size in bytes, calculated after first header complete; is an indicator for the first header | 
| 44 | char * content_disposition; | 44 | char * content_type; // sometimes 'text/html', also sometimes 'text/html; boundary=------1234' | 
| 45 | char * url; | 45 | char * content_disposition; // includes file names of uploaded files or field names with form-data (e.g. curl -F) | 
| 46 | } Http_Header; | 46 | } Http_Header; | 
| 47 | 47 | ||
| 48 | void send_answer(int fd_socket, Http_Header * http_header) { | 48 | void send_answer(int fd_socket, Http_Header * http_header) { | 
| @@ -57,24 +57,11 @@ void send_answer(int fd_socket, Http_Header * http_header) { | |||
| 57 | fstat(file, &stat); | 57 | fstat(file, &stat); | 
| 58 | sendfile (fileno(f), file, NULL, stat.st_size); | 58 | sendfile (fileno(f), file, NULL, stat.st_size); | 
| 59 | } else { | 59 | } else { | 
| 60 | fprintf(f, "could not open file \"%s\"\n", &http_header->url[1]); | 60 | if(http_header->url) // TODO: too dangerous to check that here, that is too late. | 
| 61 | fprintf(f, "could not open file \"%s\"\n", &http_header->url[1]); | ||
| 61 | } | 62 | } | 
| 62 | 63 | ||
| 63 | fclose(f); | 64 | fclose(f); | 
| 64 | |||
| 65 | // fputs("<html>", f); | ||
| 66 | // fputs("<head>", f); | ||
| 67 | // fputs("<title>test</title>", f); | ||
| 68 | // fputs("<style>label { display:block }</style>", f); | ||
| 69 | // fputs("</head>", f); | ||
| 70 | // fputs("<form action=\"/\" method=\"post\" enctype=\"multipart/form-data\">", f); | ||
| 71 | // fputs("<label>Line1<br/>\n", f); | ||
| 72 | // fputs("<textarea type=\"text\" name=\"text\"></textarea>", f); | ||
| 73 | // fputs("</label><br/>\n", f); | ||
| 74 | // fputs("<input type=\"file\" name=\"okay\" value=\"ok\" />", f); | ||
| 75 | // fputs("<input type=\"submit\" name=\"okay\" value=\"ok\" />", f); | ||
| 76 | // fputs("</form>", f); | ||
| 77 | // fputs("</html>", f); | ||
| 78 | } | 65 | } | 
| 79 | 66 | ||
| 80 | int read_everything(FILE * f_r, FILE * output) { | 67 | int read_everything(FILE * f_r, FILE * output) { | 
| @@ -106,10 +93,8 @@ void * next_customer(size_t new_socket) { | |||
| 106 | size_t output_buffer_length = 0; | 93 | size_t output_buffer_length = 0; | 
| 107 | FILE * output = open_memstream(&output_buffer, &output_buffer_length); | 94 | FILE * output = open_memstream(&output_buffer, &output_buffer_length); | 
| 108 | 95 | ||
| 109 | DEBUG("\n\n########################################## Content Reader [%d]\n", f_r); | 96 | read_everything(f_r, output); // TODO: catch return value and error handling | 
| 110 | read_everything(f_r, output); | ||
| 111 | shutdown(new_socket, SHUT_RD); // shutdown the reading half of the connection | 97 | shutdown(new_socket, SHUT_RD); // shutdown the reading half of the connection | 
| 112 | DEBUG("\n\n########################################## Content Parser\n"); | ||
| 113 | 98 | ||
| 114 | char * start = output_buffer; | 99 | char * start = output_buffer; | 
| 115 | char * end = NULL; | 100 | char * end = NULL; | 
| @@ -118,24 +103,25 @@ void * next_customer(size_t new_socket) { | |||
| 118 | Http_Header http_header = {0}; | 103 | Http_Header http_header = {0}; | 
| 119 | 104 | ||
| 120 | char * name = NULL; | 105 | char * name = NULL; | 
| 121 | while(NULL != (end = strpbrk(start, search))) { | 106 | while(NULL != (end = strpbrk(start, search))) { // TODO: try harder to break things (are SEGFAULTs possible?) | 
| 122 | 107 | ||
| 123 | size_t matchlen = strspn(end, search); | 108 | size_t matchlen = strspn(end, search); | 
| 124 | switch(end[0]) { | 109 | switch(end[0]) { | 
| 125 | case ':': | 110 | case ':': | 
| 126 | end[0] = '\0'; // {{{ remember header 'names' and search for the value | 111 | end[0] = '\0'; // {{{ remember header 'names' and search for the value | 
| 127 | end++; | 112 | end++; // jump over the colon | 
| 128 | 113 | ||
| 129 | name = start; | 114 | name = start; // remember, where name starts, will be important in the newline case | 
| 130 | 115 | ||
| 131 | if (0 == strcasecmp("Content-Type", start)) { | 116 | if (0 == strcasecmp("Content-Type", start)) { | 
| 132 | search = "\r\n;"; | 117 | search = "\r\n;"; // (more unlikely) also search for a semicolon in Content-Type: [...]; boundary=[...] | 
| 133 | } else { | 118 | } else { | 
| 134 | search = "\r\n"; | 119 | search = "\r\n"; // (likely) search for some kind of newline | 
| 135 | } // }}} | 120 | } // }}} | 
| 136 | break; | 121 | break; | 
| 137 | case ';': | 122 | case ';': | 
| 138 | start += strspn(start, "; "); // {{{ find the form-data boundary in the main header | 123 | // {{{ find the form-data boundary in the main header | 
| 124 | start += strspn(start, "; "); // remove spaces and semicolons (boundary check implicit; also stops at '\0') | ||
| 139 | 125 | ||
| 140 | const char s_multipart_form_data[] = "boundary="; | 126 | const char s_multipart_form_data[] = "boundary="; | 
| 141 | if(NULL == http_header.boundary && 0 < strcasecmp(start, s_multipart_form_data)) | 127 | if(NULL == http_header.boundary && 0 < strcasecmp(start, s_multipart_form_data)) | 
| @@ -248,14 +234,14 @@ void * next_customer(size_t new_socket) { | |||
| 248 | 234 | ||
| 249 | int serve(int server_fd) | 235 | int serve(int server_fd) | 
| 250 | { | 236 | { | 
| 251 | struct sockaddr_in address; | 237 | struct sockaddr_in address; | 
| 252 | socklen_t address_len = sizeof(address); | 238 | socklen_t address_len = sizeof(address); | 
| 253 | DEBUG("waiting for connections on server file descriptor %d", server_fd); | 239 | DEBUG("> Waiting for connections on server file descriptor %d\n", server_fd); | 
| 254 | 240 | ||
| 255 | size_t new_socket = -1; | 241 | size_t new_socket = -1; | 
| 256 | while(-1 != (new_socket = accept(server_fd, (struct sockaddr*) &address, &address_len))) | 242 | while(-1 != (new_socket = accept(server_fd, (struct sockaddr*) &address, &address_len))) | 
| 257 | { | 243 | { | 
| 258 | DEBUG("> Client %ld is connected via port %d", new_socket, address.sin_port); | 244 | DEBUG("> Client %ld is connected via port %d\n", new_socket, address.sin_port); | 
| 259 | 245 | ||
| 260 | // set non blocking mode... | 246 | // set non blocking mode... | 
| 261 | fcntl( | 247 | fcntl( | 
| @@ -269,33 +255,35 @@ int serve(int server_fd) | |||
| 269 | #ifdef VALGRIND | 255 | #ifdef VALGRIND | 
| 270 | break; // only run once, so that valgrind can test allocations&frees | 256 | break; // only run once, so that valgrind can test allocations&frees | 
| 271 | #endif | 257 | #endif | 
| 272 | } | 258 | } | 
| 273 | 259 | ||
| 274 | err(errno, "error serving"); | 260 | err(errno, "error serving"); | 
| 275 | close(server_fd); | 261 | close(server_fd); | 
| 276 | } | 262 | } | 
| 277 | 263 | ||
| 278 | #define PORT 8080 | 264 | int main(const int argc, char const * argv[]) { | 
| 279 | int main(int argc, char const *argv[]) | 265 | int server_fd = -1, opt = 1; | 
| 280 | { | 266 | |
| 281 | int server_fd = -1, opt = 1; | 267 | int port = atoi(argc > 1 ? argv[1] : "8080"); | 
| 268 | 0 == port ? port = 8080 : port; | ||
| 282 | 269 | ||
| 283 | struct sockaddr_in address = { | 270 | |
| 284 | .sin_family = AF_INET, | 271 | struct sockaddr_in address = { | 
| 285 | .sin_addr.s_addr = INADDR_ANY, | 272 | .sin_family = AF_INET, | 
| 286 | .sin_port = htons(PORT) | 273 | .sin_addr.s_addr = INADDR_ANY, | 
| 274 | .sin_port = htons(port) | ||
| 287 | }; | 275 | }; | 
| 288 | 276 | ||
| 289 | // I <3 C | 277 | // I <3 C | 
| 290 | 0 == (server_fd = socket(AF_INET, SOCK_STREAM, 0)) | 278 | 0 == (server_fd = socket(AF_INET, SOCK_STREAM, 0)) | 
| 291 | ? err(errno, NULL) | 279 | ? err(errno, NULL) | 
| 292 | : setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)) | 280 | : setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)) | 
| 293 | ? err(errno, "setsockopt failed on socket with fileno %d", server_fd) | 281 | ? err(errno, "setsockopt failed on socket with fileno %d", server_fd) | 
| 294 | : bind(server_fd, (struct sockaddr*) &address, sizeof(address)) | 282 | : bind(server_fd, (struct sockaddr*) &address, sizeof(address)) | 
| 295 | ? err(errno, NULL) | 283 | ? err(errno, NULL) | 
| 296 | : listen(server_fd, SOMAXCONN) | 284 | : listen(server_fd, SOMAXCONN) | 
| 297 | ? err(errno, NULL) | 285 | ? err(errno, NULL) | 
| 298 | : serve(server_fd) | 286 | : serve(server_fd) | 
| 299 | ? err(errno, NULL) | 287 | ? err(errno, NULL) | 
| 300 | : exit(EXIT_SUCCESS) | 288 | : exit(EXIT_SUCCESS) | 
| 301 | ; return EXIT_FAILURE; | 289 | ; return EXIT_FAILURE; | 
