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; |