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