diff options
| author | Max Christian Pohle | 2021-11-20 02:20:05 +0100 |
|---|---|---|
| committer | Max Christian Pohle | 2021-11-20 02:20:05 +0100 |
| commit | c9fd3111a73f3fa66b99b1819ecb8e54688b87d0 (patch) | |
| tree | a2fe86f996464b32a258d747889353981997eb58 | |
| parent | 076a37921f533b97413340288d56572e11e1395b (diff) | |
| download | ohmycgi-c9fd3111a73f3fa66b99b1819ecb8e54688b87d0.tar.bz2 ohmycgi-c9fd3111a73f3fa66b99b1819ecb8e54688b87d0.zip | |
The server actually serves
| -rw-r--r-- | Makefile | 7 | ||||
| -rw-r--r-- | main.c | 459 | ||||
| -rw-r--r-- | test.sh | 3 |
3 files changed, 139 insertions, 330 deletions
| @@ -1,3 +1,8 @@ | |||
| 1 | BUILD := debug | ||
| 2 | |||
| 3 | cflags.debug := -Wall -o0 -g -DDEBUG=1 | ||
| 4 | cflags.release := -os -s | ||
| 5 | CFLAGS := ${cflags.${BUILD}} | ||
| 1 | 6 | ||
| 2 | test: main | 7 | test: main |
| 3 | ./main | 8 | ./main |
| @@ -8,5 +13,5 @@ clean: | |||
| 8 | rm -f ./main | 13 | rm -f ./main |
| 9 | 14 | ||
| 10 | %: | 15 | %: |
| 11 | $(CC) -pthread -g -o $@ $@.c | 16 | $(CC) $(CFLAGS) -o $@ $@.c |
| 12 | 17 | ||
| @@ -1,139 +1,129 @@ | |||
| 1 | #include <asm-generic/errno-base.h> | 1 | /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * |
| 2 | #include <asm-generic/errno.h> | 2 | * OHMYCGI * |
| 3 | #include <netinet/in.h> | 3 | * 2021 by Max Christian Pohle <max@coderonline.de> * |
| 4 | #include <stdio.h> | 4 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ |
| 5 | #include <stdio_ext.h> | 5 | |
| 6 | // {{{ INCLUDES | ||
| 6 | #include <stdlib.h> | 7 | #include <stdlib.h> |
| 8 | #include <stdio.h> | ||
| 7 | #include <string.h> | 9 | #include <string.h> |
| 8 | #include <sys/socket.h> | ||
| 9 | #include <sys/epoll.h> | ||
| 10 | #include <sys/sendfile.h> | ||
| 11 | #include <fcntl.h> | ||
| 12 | #include <unistd.h> | 10 | #include <unistd.h> |
| 13 | #include <poll.h> | 11 | #include <sys/socket.h> |
| 14 | |||
| 15 | #include <err.h> | 12 | #include <err.h> |
| 16 | #include <errno.h> | 13 | #include <errno.h> |
| 17 | 14 | #include <arpa/inet.h> | |
| 18 | #include <pthread.h> | 15 | #include <fcntl.h> |
| 16 | #include <sys/sendfile.h> | ||
| 17 | #include <sys/stat.h> | ||
| 18 | // #include <pthread.h> // maybe later | ||
| 19 | // }}} | ||
| 20 | // {{{ MACROS | ||
| 21 | #define EWOULDBLOCK_DELAY 100 | ||
| 22 | #define READ_BUFFER_LENGTH 9000 // jumboframe? | ||
| 23 | #define POST_DATA_MAX_LENGTH 18000 | ||
| 24 | #define DEBUG_SLEEP_TIME 50000 | ||
| 25 | |||
| 26 | #ifndef DEBUG | ||
| 27 | #define DEBUG(X, ...) // (X, ...) | ||
| 28 | #else | ||
| 19 | #include <stdarg.h> | 29 | #include <stdarg.h> |
| 20 | 30 | static inline int verbose(const char * format, ...) { | |
| 21 | 31 | va_list va; va_start(va, format); usleep(DEBUG_SLEEP_TIME); return vprintf(format, va); | |
| 22 | void send_answer(FILE * f) { | ||
| 23 | fputs("HTTP/1.0 200 OK\n", f); | ||
| 24 | fputs("content-type: text/html\n\n", f); | ||
| 25 | fputs("<html>", f); | ||
| 26 | fputs("<head>", f); | ||
| 27 | fputs("<title>test</title>", f); | ||
| 28 | fputs("<style>label { display:block }</style>", f); | ||
| 29 | fputs("</head>", f); | ||
| 30 | fputs("<form action=\"/\" method=\"post\" enctype=\"multipart/form-data\">", f); | ||
| 31 | fputs("<label>Line1<br/>\n", f); | ||
| 32 | fputs("<textarea type=\"text\" name=\"text\"></textarea>", f); | ||
| 33 | fputs("</label><br/>\n", f); | ||
| 34 | fputs("<input type=\"file\" name=\"okay\" value=\"ok\" />", f); | ||
| 35 | fputs("<input type=\"submit\" name=\"okay\" value=\"ok\" />", f); | ||
| 36 | fputs("</form>", f); | ||
| 37 | fputs("</html>", f); | ||
| 38 | } | 32 | } |
| 33 | #undef DEBUG | ||
| 34 | #define DEBUG verbose | ||
| 35 | #endif | ||
| 36 | // }}} | ||
| 39 | 37 | ||
| 38 | typedef struct { | ||
| 39 | int newline_length; // lenght of one newline in bytes (\n has 1, CR/LF has 2) | ||
| 40 | char * method; | ||
| 41 | char * boundary; | ||
| 42 | size_t boundary_size; | ||
| 43 | char * content_type; | ||
| 44 | char * content_disposition; | ||
| 45 | char * url; | ||
| 46 | } Http_Header; | ||
| 40 | 47 | ||
| 41 | #define EWOULDBLOCK_DELAY 1000 | 48 | void send_answer(int fd_socket, Http_Header * http_header) { |
| 42 | #define READ_BUFFER_LENGTH 10 | 49 | FILE * f = fdopen((size_t) fd_socket, "w"); |
| 43 | #define DEBUG_SLEEP_TIME 50000 | 50 | fputs("HTTP/1.0 200 OK\n", f); |
| 51 | fputs("content-type: text/plain\n\n", f); | ||
| 52 | fflush(f); | ||
| 53 | |||
| 54 | int file = open(&http_header->url[1], O_RDONLY); | ||
| 55 | if(0 < file) { | ||
| 56 | struct stat stat; | ||
| 57 | fstat(file, &stat); | ||
| 58 | sendfile (fileno(f), file, NULL, stat.st_size); | ||
| 59 | } else { | ||
| 60 | fprintf(f, "could not open file \"%s\"\n", &http_header->url[1]); | ||
| 61 | } | ||
| 44 | 62 | ||
| 45 | static inline int verbose(const char * format, ...) { | 63 | fclose(f); |
| 46 | va_list va; va_start(va, format); usleep(DEBUG_SLEEP_TIME); return vprintf(format, va); | 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); | ||
| 47 | } | 78 | } |
| 48 | 79 | ||
| 49 | 80 | int read_everything(FILE * f_r, FILE * output) { | |
| 50 | void read_everything(FILE * f_r, FILE * output) { | ||
| 51 | const int read_buffer_length = READ_BUFFER_LENGTH; | 81 | const int read_buffer_length = READ_BUFFER_LENGTH; |
| 52 | char read_buffer[read_buffer_length]; | 82 | char read_buffer[read_buffer_length]; |
| 53 | for(size_t size = -2 ; ; size = fread(read_buffer, read_buffer_length, 1, f_r)) { | 83 | for(size_t size = -2 ; ; size = fread(read_buffer, 1, read_buffer_length, f_r)) { |
| 54 | if(-2 == size || (-1 == size && EWOULDBLOCK == errno)) { | 84 | if(-2 == size || (-1 == size && EWOULDBLOCK == errno)) { |
| 55 | usleep(EWOULDBLOCK_DELAY); // try again a little later | 85 | usleep(EWOULDBLOCK_DELAY); // try again a little later |
| 56 | continue; | 86 | continue; |
| 57 | } | 87 | } |
| 58 | 88 | ||
| 59 | fwrite(read_buffer, read_buffer_length, 1, output); | 89 | fwrite(read_buffer, read_buffer_length, 1, output); |
| 60 | fwrite(read_buffer, read_buffer_length, 1, stdout); | ||
| 61 | |||
| 62 | if (1 != size) { // I expect one nmemb of data | ||
| 63 | break; | ||
| 64 | } | ||
| 65 | } | ||
| 66 | fflush(stdout); | ||
| 67 | fflush(output); | ||
| 68 | } | ||
| 69 | 90 | ||
| 70 | void header_next(const char * key, const char * value) { | 91 | if (read_buffer_length > POST_DATA_MAX_LENGTH) |
| 71 | // this sample function will be removed later | 92 | return EXIT_FAILURE; |
| 72 | typedef struct { | ||
| 73 | const char * name; | ||
| 74 | const char * value; | ||
| 75 | } NameValue; | ||
| 76 | 93 | ||
| 77 | NameValue namevalue[] = { | 94 | if (size < read_buffer_length) { // I expect one nmemb of data |
| 78 | {"Content-Type", NULL}, | ||
| 79 | {"Content-Length", NULL} | ||
| 80 | }; | ||
| 81 | for(int i=0; i < sizeof(namevalue) / sizeof(NameValue); i++) { | ||
| 82 | // printf("next name: %s\n", namevalue[i].name); | ||
| 83 | if(NULL == namevalue[i].value && 0 == strcasecmp(namevalue[i].name, key)) { | ||
| 84 | namevalue[i].value = value; | ||
| 85 | break; | 95 | break; |
| 86 | } | 96 | } |
| 87 | } | 97 | } |
| 98 | fflush(output); | ||
| 99 | return EXIT_SUCCESS; | ||
| 88 | } | 100 | } |
| 89 | 101 | ||
| 90 | typedef struct { | ||
| 91 | int newline_length; // lenght of one newline in bytes (\n has 1, CR/LF has 2) | ||
| 92 | char * method; | ||
| 93 | char * boundary; | ||
| 94 | size_t boundary_size; | ||
| 95 | char * content_type; | ||
| 96 | char * content_disposition; | ||
| 97 | } Http_Header; | ||
| 98 | |||
| 99 | void * next_customer(size_t new_socket) { | 102 | void * next_customer(size_t new_socket) { |
| 100 | FILE * f_r = fdopen((size_t) new_socket, "rw"); | 103 | FILE * f_r = fdopen((size_t) new_socket, "r"); |
| 101 | 104 | ||
| 102 | char * output_buffer = NULL; | 105 | char * output_buffer = NULL; |
| 103 | size_t output_buffer_length = 0; | 106 | size_t output_buffer_length = 0; |
| 104 | FILE * output = open_memstream(&output_buffer, &output_buffer_length); | 107 | FILE * output = open_memstream(&output_buffer, &output_buffer_length); |
| 105 | 108 | ||
| 106 | verbose("\n\n########################################## Content Reader [%d]\n", f_r); | 109 | DEBUG("\n\n########################################## Content Reader [%d]\n", f_r); |
| 107 | read_everything(f_r, output); | 110 | read_everything(f_r, output); |
| 108 | shutdown(new_socket, SHUT_RD); // shutdown the reading half of the connection | 111 | shutdown(new_socket, SHUT_RD); // shutdown the reading half of the connection |
| 109 | verbose("\n\n########################################## Content Parser\n"); | 112 | DEBUG("\n\n########################################## Content Parser\n"); |
| 110 | |||
| 111 | // token parser... | ||
| 112 | char * key = output_buffer; | ||
| 113 | |||
| 114 | char * saveptr = NULL; | ||
| 115 | 113 | ||
| 116 | char * start = output_buffer; | 114 | char * start = output_buffer; |
| 117 | char * end = NULL; | 115 | char * end = NULL; |
| 118 | char * search = "\r\n"; | 116 | char * search = "\r\n"; |
| 119 | 117 | ||
| 120 | Http_Header http_header; | 118 | Http_Header http_header = {0}; |
| 121 | http_header.boundary_size = 0; | ||
| 122 | http_header.newline_length = 1; | ||
| 123 | http_header.method = NULL; | ||
| 124 | http_header.boundary = NULL; | ||
| 125 | http_header.content_disposition = NULL; | ||
| 126 | http_header.content_type = NULL; | ||
| 127 | 119 | ||
| 128 | char * name = NULL; | 120 | char * name = NULL; |
| 129 | // while(NULL != (end = strpbrk(start, search))) { | ||
| 130 | while(NULL != (end = strpbrk(start, search))) { | 121 | while(NULL != (end = strpbrk(start, search))) { |
| 131 | // verbose("\033[31m[%ld|%ld|%ld]\033[0m\n", start - output_buffer, end - start, end - output_buffer); | ||
| 132 | 122 | ||
| 133 | size_t matchlen = strspn(end, search); | 123 | size_t matchlen = strspn(end, search); |
| 134 | switch(end[0]) { | 124 | switch(end[0]) { |
| 135 | case ':': | 125 | case ':': |
| 136 | end[0] = '\0'; | 126 | end[0] = '\0'; // {{{ remember header 'names' and search for the value |
| 137 | end++; | 127 | end++; |
| 138 | 128 | ||
| 139 | name = start; | 129 | name = start; |
| @@ -142,52 +132,56 @@ void * next_customer(size_t new_socket) { | |||
| 142 | search = "\r\n;"; | 132 | search = "\r\n;"; |
| 143 | } else { | 133 | } else { |
| 144 | search = "\r\n"; | 134 | search = "\r\n"; |
| 145 | } | 135 | } // }}} |
| 146 | break; | 136 | break; |
| 147 | case ';': | 137 | case ';': |
| 138 | start += strspn(start, "; "); // {{{ find the form-data boundary in the main header | ||
| 139 | |||
| 140 | const char s_multipart_form_data[] = "boundary="; | ||
| 141 | if(NULL == http_header.boundary && 0 < strcasecmp(start, s_multipart_form_data)) | ||
| 148 | { | 142 | { |
| 149 | start += strspn(start, "; "); | 143 | http_header.boundary = end + sizeof(s_multipart_form_data) + 1; |
| 150 | 144 | http_header.boundary += strspn(http_header.boundary, "-"); | |
| 151 | const char s_multipart_form_data[] = "boundary="; | 145 | DEBUG("> Boundary found, now looking where it ends...\n"); |
| 152 | if(NULL == http_header.boundary && 0 < strcasecmp(start, s_multipart_form_data)) | 146 | search = "\r\n"; |
| 153 | { | 147 | continue; |
| 154 | http_header.boundary = end + sizeof(s_multipart_form_data) + 1; | 148 | } /// }}} |
| 155 | http_header.boundary += strspn(http_header.boundary, "-"); | 149 | break; |
| 156 | // verbose("GESCHAFFT %s [%ld] QQQQ %s\n", http_header.boundary, http_header.boundary_size, end); | ||
| 157 | search = "\r\n"; | ||
| 158 | continue; | ||
| 159 | } | ||
| 160 | break; | ||
| 161 | } | ||
| 162 | case '\r': // fallthrough | 150 | case '\r': // fallthrough |
| 163 | case '\n': | 151 | case '\n': |
| 152 | // {{{ newlines are special: sometimes content parts follow and sometimes headers, guess what... | ||
| 164 | end[0] = '\0'; | 153 | end[0] = '\0'; |
| 165 | search = ":"; // we will continue to search for headers | 154 | search = ":"; // we will continue to search for headers |
| 166 | |||
| 167 | |||
| 168 | if(NULL == name) { | 155 | if(NULL == name) { |
| 169 | if(NULL == http_header.method) { | 156 | if(NULL == http_header.method) { |
| 170 | verbose("[%ld]> HTTP REQUEST LINE :: %s \n", matchlen, start); | 157 | DEBUG("[%ld]> HTTP REQUEST LINE :: %s \n", matchlen, start); |
| 158 | end[0] = '\0'; | ||
| 159 | |||
| 160 | while(NULL != (start = memchr(start, ' ', end - start))) { | ||
| 161 | if(NULL == http_header.url) | ||
| 162 | http_header.url = ++start; | ||
| 163 | else | ||
| 164 | start[0] = '\0'; | ||
| 165 | } | ||
| 171 | http_header.method = start; | 166 | http_header.method = start; |
| 172 | http_header.newline_length = matchlen; | 167 | http_header.newline_length = matchlen; |
| 173 | } else { | 168 | } else { |
| 174 | verbose("[...]\n"); // if we want to intentially skip something, we land here by setting name = NUL; | 169 | DEBUG("[...]\n"); // if we want to intentially skip something, we land here by setting name = NUL; |
| 175 | break; | 170 | break; |
| 176 | } | 171 | } |
| 177 | } else { // we know that name is not NULL and can work with it | 172 | } else { // we know that name is not NULL and can work with it |
| 178 | if (0 == strcasecmp("Content-Disposition", name)) | 173 | if (0 == strcasecmp("Content-Disposition", name)) |
| 179 | { http_header.content_disposition = start; } | 174 | { http_header.content_disposition = start; } |
| 180 | } | 175 | } // }}} |
| 181 | 176 | DEBUG("\033[32m[%ld]> '% 20s' = '%s'\033[0m\n", matchlen, name, start); | |
| 182 | verbose("\033[32m[%ld]> '% 20s' = '%s'\033[0m\n", matchlen, name, start); | 177 | // {{{ check if a http header ended (e.g. two newlines) |
| 183 | |||
| 184 | if(matchlen > http_header.newline_length) { | 178 | if(matchlen > http_header.newline_length) { |
| 185 | verbose("> END HEADERS, because there were %d newlines; boundary='%s'[%ld]\n", matchlen / http_header.newline_length, http_header.boundary, http_header.boundary_size); | 179 | DEBUG("> END HEADERS, because there were %d newlines; boundary='%s'[%ld]\n", matchlen / http_header.newline_length, http_header.boundary, http_header.boundary_size); |
| 186 | end += matchlen; | 180 | end += matchlen; |
| 187 | 181 | ||
| 188 | // if it was the first header, we calculate the boundary size and expect more headers to come after a boundary | 182 | // if it was the first header, we calculate the boundary size and expect more headers to come after a boundary |
| 189 | if(http_header.boundary && http_header.boundary_size == 0) { | 183 | if(http_header.boundary && http_header.boundary_size == 0) { |
| 190 | verbose("================================================================================\n"); | 184 | DEBUG("================================================================================\n"); |
| 191 | http_header.boundary_size = strlen(http_header.boundary); | 185 | http_header.boundary_size = strlen(http_header.boundary); |
| 192 | // skip the first header and boundary... | 186 | // skip the first header and boundary... |
| 193 | start = end; | 187 | start = end; |
| @@ -200,95 +194,68 @@ void * next_customer(size_t new_socket) { | |||
| 200 | while(1) | 194 | while(1) |
| 201 | { | 195 | { |
| 202 | size_t size_remaining = (size_t) output_buffer_length - (end - output_buffer) - 1; | 196 | size_t size_remaining = (size_t) output_buffer_length - (end - output_buffer) - 1; |
| 203 | verbose("%ld remaining.\n", size_remaining); fflush(stdout); | 197 | DEBUG("%ld remaining.\n", size_remaining); |
| 204 | 198 | ||
| 205 | if(size_remaining <= 0) { | 199 | if(size_remaining <= 0) { |
| 206 | verbose("> not even the boundary would fit in that what is left.\n"); | 200 | DEBUG("> not even the boundary would fit in that what is left.\n"); |
| 207 | break; | 201 | break; |
| 208 | } | 202 | } |
| 209 | 203 | ||
| 210 | if(NULL == (end = memchr((void*) end, '-', size_remaining))) { | 204 | if(NULL == (end = memchr((void*) end, '-', size_remaining))) { |
| 211 | verbose("no further '-' found\n"); | 205 | DEBUG("no further '-' found\n"); |
| 212 | break; | 206 | break; |
| 213 | } | 207 | } |
| 214 | 208 | ||
| 215 | char * content_end = end - http_header.newline_length; | 209 | char * content_end = end - http_header.newline_length; |
| 216 | 210 | ||
| 217 | |||
| 218 | end += strspn(end, "-"); | 211 | end += strspn(end, "-"); |
| 219 | if(0 == strncmp(end, http_header.boundary, http_header.boundary_size)) { | 212 | if(0 == strncmp(end, http_header.boundary, http_header.boundary_size)) { |
| 220 | size_t file_size = content_end - content_start; | 213 | size_t file_size = content_end - content_start; |
| 221 | verbose("> Content ends here, size of the last file is %ld: {begin}", file_size); | 214 | DEBUG("> Content ends here, size of the last file is %ld.", file_size); |
| 222 | fwrite(content_start, file_size, 1, stdout); | ||
| 223 | verbose("{end}\n"); | ||
| 224 | |||
| 225 | |||
| 226 | // puts(" <[last 20 bytes]\n"); | ||
| 227 | |||
| 228 | 215 | ||
| 229 | end += http_header.boundary_size; | 216 | end += http_header.boundary_size; |
| 230 | matchlen = strspn(end, "\r\n"); | 217 | matchlen = strspn(end, "\r\n"); |
| 231 | // matchlen = http_header.boundary_size; | 218 | DEBUG("> end is at %p, matchlen is %ld\n", end, matchlen); |
| 232 | verbose("> end is at %p, matchlen is %ld\n", end, matchlen); | ||
| 233 | |||
| 234 | 219 | ||
| 235 | search = ":"; | 220 | search = ":"; |
| 236 | break; | 221 | break; |
| 237 | } else { | 222 | } else { |
| 238 | // printf("[%ld] kack: %c\n", size_remaining, p[0]); fflush(stdout); | ||
| 239 | end = end + 1; | 223 | end = end + 1; |
| 240 | } | 224 | } |
| 241 | } | 225 | } |
| 242 | } | 226 | } |
| 243 | /* | ||
| 244 | char * content_start = end; | ||
| 245 | content_start += strspn(content_start, "-"); | ||
| 246 | // verbose("CONTENT: %s\n", content_start); | ||
| 247 | if(0 <= strcmp(content_start, http_header.boundary)) { | ||
| 248 | verbose("MATCH for %s // %s\n", http_header.content_type, http_header.content_disposition); | ||
| 249 | |||
| 250 | search = "\r\n"; | ||
| 251 | matchlen = http_header.boundary_size; | ||
| 252 | name = NULL; | ||
| 253 | } else { | ||
| 254 | verbose("NO MATCH\n"); | ||
| 255 | } | ||
| 256 | */ | ||
| 257 | break; | 227 | break; |
| 258 | } | 228 | } // }}} if condition after a header |
| 259 | } | 229 | } // switch |
| 260 | 230 | ||
| 261 | // verbose("> end is at %p, matchlen is %ld\n", end, matchlen); | 231 | if(NULL == end) |
| 262 | start = end + matchlen; | 232 | break; |
| 263 | // fwrite(start, 42, 1, stdout); | 233 | else |
| 264 | // printf(" <[first 42 bytes of new start; matchlen was %ld; searching for %s\n", matchlen, search); | 234 | start = end + matchlen; |
| 265 | } | 235 | } |
| 266 | printf("last known position: %p / %p with %s\n", start, end, search); | ||
| 267 | 236 | ||
| 268 | if(http_header.boundary) | 237 | DEBUG("> sending answer..."); |
| 269 | verbose("http-boundary: %s", http_header.boundary); | 238 | send_answer(new_socket, &http_header); |
| 239 | DEBUG("> answer sent."); | ||
| 270 | 240 | ||
| 271 | verbose("> sending answer..."); | ||
| 272 | send_answer(f_r); | ||
| 273 | fclose(f_r); | 241 | fclose(f_r); |
| 274 | 242 | ||
| 275 | verbose("> answer sent."); | 243 | fclose(output); |
| 244 | free(output_buffer); | ||
| 276 | 245 | ||
| 277 | return NULL; | 246 | return NULL; |
| 278 | } | 247 | } |
| 279 | 248 | ||
| 280 | |||
| 281 | |||
| 282 | int serve(int server_fd) | 249 | int serve(int server_fd) |
| 283 | { | 250 | { |
| 284 | struct sockaddr_in address; | 251 | struct sockaddr_in address; |
| 285 | socklen_t address_len = sizeof(address); | 252 | socklen_t address_len = sizeof(address); |
| 286 | verbose("waiting for connections on server file descriptor %d", server_fd); | 253 | DEBUG("waiting for connections on server file descriptor %d", server_fd); |
| 287 | 254 | ||
| 288 | size_t new_socket = -1; | 255 | size_t new_socket = -1; |
| 289 | while(-1 != (new_socket = accept(server_fd, (struct sockaddr*) &address, &address_len))) | 256 | while(-1 != (new_socket = accept(server_fd, (struct sockaddr*) &address, &address_len))) |
| 290 | { | 257 | { |
| 291 | verbose("> Client %ld is connected via port %d", new_socket, address.sin_port); | 258 | DEBUG("> Client %ld is connected via port %d", new_socket, address.sin_port); |
| 292 | 259 | ||
| 293 | // set non blocking mode... | 260 | // set non blocking mode... |
| 294 | fcntl( | 261 | fcntl( |
| @@ -299,23 +266,13 @@ int serve(int server_fd) | |||
| 299 | 266 | ||
| 300 | next_customer(new_socket); | 267 | next_customer(new_socket); |
| 301 | 268 | ||
| 302 | /* | 269 | #ifdef VALGRIND |
| 303 | if(fork()) { | 270 | break; // only run once, so that valgrind can test allocations&frees |
| 304 | close(new_socket); | 271 | #endif |
| 305 | } else { | ||
| 306 | close(server_fd); // give the server free | ||
| 307 | next_customer(new_socket); | ||
| 308 | shutdown(new_socket, SHUT_RDWR); | ||
| 309 | exit(0); | ||
| 310 | } | ||
| 311 | */ | ||
| 312 | |||
| 313 | // pthread_t thread_id; | ||
| 314 | // pthread_create(&thread_id, NULL, next_customer, (void*) new_socket); | ||
| 315 | // pthread_join(thread_id, NULL); | ||
| 316 | } | 272 | } |
| 317 | 273 | ||
| 318 | err(errno, "error serving"); | 274 | err(errno, "error serving"); |
| 275 | close(server_fd); | ||
| 319 | } | 276 | } |
| 320 | 277 | ||
| 321 | #define PORT 8080 | 278 | #define PORT 8080 |
| @@ -341,163 +298,7 @@ int main(int argc, char const *argv[]) | |||
| 341 | : serve(server_fd) | 298 | : serve(server_fd) |
| 342 | ? err(errno, NULL) | 299 | ? err(errno, NULL) |
| 343 | : exit(EXIT_SUCCESS) | 300 | : exit(EXIT_SUCCESS) |
| 344 | ; | 301 | ; return EXIT_FAILURE; |
| 345 | return EXIT_FAILURE; | ||
| 346 | } | 302 | } |
| 347 | 303 | ||
| 348 | // void * next_customer_old(int new_socket) | ||
| 349 | // { | ||
| 350 | // char *hello = "Hello from server"; | ||
| 351 | // char buffer[1024] = {0}; | ||
| 352 | // int valread = read(new_socket, buffer, 1024); | ||
| 353 | // printf("%s %d\n", buffer, valread); | ||
| 354 | // | ||
| 355 | // send(new_socket, hello, strlen(hello), 0); | ||
| 356 | // | ||
| 357 | // printf("Hello message sent\n"); | ||
| 358 | // return NULL; | ||
| 359 | // } | ||
| 360 | |||
| 361 | // IDEA ... | ||
| 362 | // | ||
| 363 | // #define FOREACH_HEADER(HEADER) \ | ||
| 364 | // HEADER("Content-Type: multipart/form-data; boundary=") | ||
| 365 | // #define GENERATE_HEADERS(H) sizeof(H), H | ||
| 366 | // | ||
| 367 | // typedef struct { | ||
| 368 | // const int length; | ||
| 369 | // const char * string; | ||
| 370 | // } length_value; | ||
| 371 | // | ||
| 372 | // const length_value headers[] = { | ||
| 373 | // FOREACH_HEADER(GENERATE_HEADERS) | ||
| 374 | // }; | ||
| 375 | // | ||
| 376 | |||
| 377 | |||
| 378 | // READ WITH recv ... | ||
| 379 | /* | ||
| 380 | for(size_t size = -1 ; ; (size = recv(new_socket, read_buffer, read_buffer_length, 0))) { | ||
| 381 | if(-1 == size) { | ||
| 382 | usleep(10000); | ||
| 383 | printf("size: %ld\n", size); | ||
| 384 | if(EWOULDBLOCK == errno){ | ||
| 385 | continue; | ||
| 386 | } else if (size == 0 || size < read_buffer_length) { | ||
| 387 | break; | ||
| 388 | } | ||
| 389 | } else { | ||
| 390 | read_buffer[size] = '\0'; | ||
| 391 | |||
| 392 | fwrite(read_buffer, size, 1, output); | ||
| 393 | fwrite(read_buffer, size, 1, stdout); | ||
| 394 | |||
| 395 | // if(output_buffer_length > 4 | ||
| 396 | // && (strspn(&output_buffer[output_buffer_length - 2], "\n") == 2 | ||
| 397 | // || strspn(&output_buffer[output_buffer_length - 4], "\r\n") == 4)) | ||
| 398 | // break; | ||
| 399 | } | ||
| 400 | }*/ | ||
| 401 | |||
| 402 | |||
| 403 | // fully working implementation... | ||
| 404 | /* | ||
| 405 | void * next_customer(void * new_socket) { | ||
| 406 | |||
| 407 | const int MAX_HEADER_LINE_LENGTH = 1024; | ||
| 408 | const int MAX_BOUNDARY_LENGTH = 64; | ||
| 409 | |||
| 410 | char boundary[64] = {0}; | ||
| 411 | int boundary_size = 0; | ||
| 412 | |||
| 413 | char current_name[64] = {0}; | ||
| 414 | |||
| 415 | usleep(100); // wait to avoid poll, epoll or evil select | ||
| 416 | |||
| 417 | char * output_buffer = NULL; | ||
| 418 | size_t output_buffer_length = 0; | ||
| 419 | FILE * output = open_memstream(&output_buffer, &output_buffer_length); | ||
| 420 | |||
| 421 | FILE * f = fdopen((size_t) new_socket, "r"); | ||
| 422 | char buffer[MAX_HEADER_LINE_LENGTH]; | ||
| 423 | char * s = NULL; | ||
| 424 | while(1) { | ||
| 425 | // printf("[errno: %d] %s", errno, buffer); | ||
| 426 | s = fgets(buffer, MAX_HEADER_LINE_LENGTH, f); | ||
| 427 | |||
| 428 | if(boundary[0] != '\0' && buffer[0] == '-') { // first char minus. could be a new chunk of post data | ||
| 429 | int i = 0; | ||
| 430 | for(i = 1; i < MAX_HEADER_LINE_LENGTH; i++) { | ||
| 431 | if(buffer[i] != '-') { // skip all minus | ||
| 432 | if(0 == strncmp(&buffer[i], boundary, boundary_size)) { | ||
| 433 | fflush(output); | ||
| 434 | printf("> name='%s' value='%*s'\n", current_name, (int) output_buffer_length, output_buffer); | ||
| 435 | setenv(current_name, output_buffer, 0); | ||
| 436 | current_name[0] = '\0'; | ||
| 437 | rewind(output); | ||
| 438 | i = 0; | ||
| 439 | |||
| 440 | puts("================================================================================"); | ||
| 441 | } | ||
| 442 | break; | ||
| 443 | } | ||
| 444 | } | ||
| 445 | if (i == 0) continue; | ||
| 446 | } | ||
| 447 | |||
| 448 | |||
| 449 | if(!s || strspn(buffer, "\n\r") > 0) { // either LF or CR/LF (windows) | ||
| 450 | puts("END HEADER\n"); | ||
| 451 | // s != NULL ? break : continue; | ||
| 452 | if(!s) break; else continue; // either eof or only end of (chunk-)header | ||
| 453 | } | ||
| 454 | |||
| 455 | const char header_multipart[] = "Content-Type: multipart/form-data; boundary="; | ||
| 456 | if (!boundary[0] && 0 == strncasecmp(buffer, header_multipart, sizeof(header_multipart) - 1)) { | ||
| 457 | // we could potentially optimize this part with strspn and moving the buffer point, why not? | ||
| 458 | for(int i=sizeof(header_multipart); i<MAX_HEADER_LINE_LENGTH; i++) | ||
| 459 | if(buffer[i] != '-') { | ||
| 460 | strncpy(boundary, &buffer[i], 64); | ||
| 461 | boundary_size = strnlen(boundary, 64); | ||
| 462 | boundary[boundary_size] = '\0'; | ||
| 463 | printf("> [%s] %s\n", header_multipart, boundary); | ||
| 464 | break; | ||
| 465 | } | ||
| 466 | continue; | ||
| 467 | } | ||
| 468 | |||
| 469 | const char header_disposition[] = "Content-Disposition: form-data; "; | ||
| 470 | if (boundary[0] && 0 == strncasecmp(header_disposition, buffer, sizeof(header_disposition) - 1)) { | ||
| 471 | printf("> %s :: %s\n", header_disposition, buffer); | ||
| 472 | const char header_disposition_name[] = "name=\""; | ||
| 473 | s = strstr(&buffer[sizeof(header_disposition) - 1], header_disposition_name); | ||
| 474 | if(s) { | ||
| 475 | s += sizeof(header_disposition_name) - 1; | ||
| 476 | s[strcspn(s, "\"")] = '\0'; | ||
| 477 | strncpy(current_name, s, 64); | ||
| 478 | printf("> /%s.*%s/ :: \"%s\"\n", header_disposition, header_disposition_name, s); | ||
| 479 | } else { | ||
| 480 | warnx("string not found in \"%s\".", &buffer[sizeof(header_disposition)]); | ||
| 481 | } | ||
| 482 | continue; | ||
| 483 | }// else { printf("FUCK: %*s [%d/%ld]\n", (int) sizeof(header_disposition), buffer, strncasecmp(header_disposition, buffer, sizeof(header_disposition)), sizeof(header_disposition)); } | ||
| 484 | |||
| 485 | printf("> [unknown] %s", buffer); | ||
| 486 | fputs(buffer, output); | ||
| 487 | |||
| 488 | |||
| 489 | // I think another if could now search for "Content-Disposition: form-data; " and store the remaining fields | ||
| 490 | // somewhere, e.g. name="okay"; filename="1.gif". Also there may also be a content-type per chunk, which we | ||
| 491 | // could also add and erase, where I have currently NEXT CHUNK. | ||
| 492 | // After that the text output (printf("%s", buffer)) will only contain content without headers, so that printf | ||
| 493 | // can then be replaced with some fwrite to a memstream and we are done :) | ||
| 494 | } | ||
| 495 | |||
| 496 | puts("> sending answer..."); | ||
| 497 | send_answer(new_socket); | ||
| 498 | puts("> answer sent."); | ||
| 499 | |||
| 500 | return NULL; | ||
| 501 | } | ||
| 502 | */ | ||
| 503 | // vim: shiftwidth=2 tabstop=2 number foldmethod=marker | 304 | // vim: shiftwidth=2 tabstop=2 number foldmethod=marker |
| @@ -0,0 +1,3 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | |||
| 3 | (echo -e "POST / HTTP/1.1\nContent-Type: text/html; boundary=--1234\n\n--1234\nContent-Disposition: fooo\n\n" ; dd if=/dev/urandom) | nc localhost 8080 | ||
