1 |
#include "freehttpd.h" |
2 |
#include "http_error.h" |
3 |
#include "log.h" |
4 |
#include "protocol.h" |
5 |
#include "request.h" |
6 |
#include "response.h" |
7 |
|
8 |
#include <arpa/inet.h> |
9 |
#include <dirent.h> |
10 |
#include <errno.h> |
11 |
#include <magic.h> |
12 |
#include <stdarg.h> |
13 |
#include <stdbool.h> |
14 |
#include <stdint.h> |
15 |
#include <stdio.h> |
16 |
#include <stdlib.h> |
17 |
#include <string.h> |
18 |
#include <sys/socket.h> |
19 |
#include <sys/stat.h> |
20 |
#include <sys/types.h> |
21 |
#include <time.h> |
22 |
#include <unistd.h> |
23 |
|
24 |
struct freehttpd |
25 |
{ |
26 |
int sockfd; |
27 |
magic_t magic; |
28 |
freehttpd_config_t *config; |
29 |
freehttpd_errdoc_tbl_t *errdoc_tbl; |
30 |
}; |
31 |
|
32 |
static freehttpd_config_t * |
33 |
freehttpd_config_init () |
34 |
{ |
35 |
struct freehttpd_config *config = calloc (1, sizeof (freehttpd_config_t)); |
36 |
|
37 |
if (config == NULL) |
38 |
return NULL; |
39 |
|
40 |
config->port = 80; |
41 |
config->addr = NULL; |
42 |
config->max_listen_queue = 5; |
43 |
config->max_method_len = 16; |
44 |
config->max_uri_len = 8192; |
45 |
config->max_version_len = 16; |
46 |
config->docroot = NULL; |
47 |
|
48 |
return config; |
49 |
} |
50 |
|
51 |
static void |
52 |
freehttpd_config_free (freehttpd_config_t *config) |
53 |
{ |
54 |
if (config == NULL) |
55 |
return; |
56 |
|
57 |
free (config->docroot); |
58 |
free (config->addr); |
59 |
free (config); |
60 |
} |
61 |
|
62 |
freehttpd_t * |
63 |
freehttpd_init (magic_t magic) |
64 |
{ |
65 |
freehttpd_t *freehttpd = calloc (1, sizeof (freehttpd_t)); |
66 |
|
67 |
if (freehttpd == NULL) |
68 |
return NULL; |
69 |
|
70 |
freehttpd->sockfd = -1; |
71 |
freehttpd->magic = magic; |
72 |
freehttpd->config = freehttpd_config_init (); |
73 |
freehttpd->errdoc_tbl = freehttpd_error_document_tbl_init (); |
74 |
|
75 |
freehttpd_error_document_load_defaults (freehttpd->errdoc_tbl); |
76 |
return freehttpd; |
77 |
} |
78 |
|
79 |
ecode_t |
80 |
freehttpd_setopt (freehttpd_t *freehttpd, freehttpd_opt_t opt, void *value) |
81 |
{ |
82 |
switch (opt) |
83 |
{ |
84 |
case FREEHTTPD_CONFIG_PORT: |
85 |
freehttpd->config->port = *(unsigned int *) value; |
86 |
break; |
87 |
|
88 |
case FREEHTTPD_CONFIG_ADDR: |
89 |
freehttpd->config->addr |
90 |
= value == NULL ? NULL : strdup ((const char *) value); |
91 |
break; |
92 |
|
93 |
case FREEHTTPD_CONFIG_MAX_LISTEN_QUEUE: |
94 |
freehttpd->config->max_listen_queue = *(unsigned int *) value; |
95 |
break; |
96 |
|
97 |
case FREEHTTPD_CONFIG_MAX_METHOD_LEN: |
98 |
freehttpd->config->max_method_len = *(size_t *) value; |
99 |
break; |
100 |
|
101 |
case FREEHTTPD_CONFIG_MAX_URI_LEN: |
102 |
freehttpd->config->max_uri_len = *(size_t *) value; |
103 |
break; |
104 |
|
105 |
case FREEHTTPD_CONFIG_MAX_VERSION_LEN: |
106 |
freehttpd->config->max_version_len = *(size_t *) value; |
107 |
break; |
108 |
|
109 |
case FREEHTTPD_CONFIG_DOCROOT: |
110 |
freehttpd->config->docroot |
111 |
= value == NULL ? NULL : strdup ((const char *) value); |
112 |
|
113 |
if (value != NULL) |
114 |
freehttpd->config->_docroot_length |
115 |
= strlen (freehttpd->config->docroot); |
116 |
break; |
117 |
|
118 |
default: |
119 |
return E_UNKNOWN_OPT; |
120 |
} |
121 |
|
122 |
return E_OK; |
123 |
} |
124 |
|
125 |
void |
126 |
freehttpd_free (freehttpd_t *freehttpd) |
127 |
{ |
128 |
if (freehttpd == NULL) |
129 |
return; |
130 |
|
131 |
freehttpd_error_document_tbl_free (freehttpd->errdoc_tbl); |
132 |
freehttpd_config_free (freehttpd->config); |
133 |
free (freehttpd); |
134 |
} |
135 |
|
136 |
static ecode_t |
137 |
freehttpd_create_socket (freehttpd_t *freehttpd) |
138 |
{ |
139 |
int sockfd = socket (AF_INET, SOCK_STREAM, 0); |
140 |
|
141 |
if (sockfd < 0) |
142 |
return E_SYSCALL_SOCKET; |
143 |
|
144 |
int opt = 1; |
145 |
|
146 |
if (setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0) |
147 |
return E_SYSCALL_SETSOCKOPT; |
148 |
|
149 |
freehttpd->sockfd = sockfd; |
150 |
return E_OK; |
151 |
} |
152 |
|
153 |
static struct sockaddr_in |
154 |
freehttpd_setup_addrinfo (freehttpd_t *freehttpd) |
155 |
{ |
156 |
struct sockaddr_in addr = { 0 }; |
157 |
const char *addr_host = freehttpd->config->addr; |
158 |
|
159 |
addr.sin_family = AF_INET; |
160 |
addr.sin_port = htons (freehttpd->config->port); |
161 |
addr.sin_addr.s_addr |
162 |
= addr_host == NULL ? INADDR_ANY : inet_addr (addr_host); |
163 |
|
164 |
return addr; |
165 |
} |
166 |
|
167 |
static ecode_t |
168 |
freehttpd_bind (freehttpd_t *freehttpd, struct sockaddr_in *addr_in) |
169 |
{ |
170 |
if (bind (freehttpd->sockfd, (struct sockaddr *) addr_in, sizeof (*addr_in)) |
171 |
< 0) |
172 |
return E_SYSCALL_BIND; |
173 |
|
174 |
return E_OK; |
175 |
} |
176 |
|
177 |
static ecode_t |
178 |
freehttpd_listen (freehttpd_t *freehttpd) |
179 |
{ |
180 |
unsigned int max_listen_queue = freehttpd->config->max_listen_queue; |
181 |
|
182 |
if (listen (freehttpd->sockfd, (int) max_listen_queue) < 0) |
183 |
return E_SYSCALL_LISTEN; |
184 |
|
185 |
return E_OK; |
186 |
} |
187 |
|
188 |
static ecode_t |
189 |
freehttpd_send_error (freehttpd_t *freehttpd, FILE *stream, int sockfd, |
190 |
freehttpd_status_t status, const char *version) |
191 |
{ |
192 |
freehttpd_response_t *response = freehttpd_response_init ( |
193 |
stream, version == NULL ? HTTP1_1 : version, 3, status); |
194 |
ecode_t ret = E_OK; |
195 |
|
196 |
if (response == NULL) |
197 |
return E_LIBC_MALLOC; |
198 |
|
199 |
if (stream == NULL) |
200 |
stream = fdopen (sockfd, "w"); |
201 |
|
202 |
if (stream == NULL) |
203 |
{ |
204 |
freehttpd_response_free (response); |
205 |
return E_LIBC_FDOPEN; |
206 |
} |
207 |
|
208 |
freehttpd_response_add_default_headers (response); |
209 |
|
210 |
const freehttpd_errdoc_t *doc |
211 |
= freehttpd_error_document_get (freehttpd->errdoc_tbl, status); |
212 |
|
213 |
if (doc == NULL) |
214 |
{ |
215 |
freehttpd_response_add_header (response, "Content-Length", 0, "0"); |
216 |
freehttpd_response_head_send (response); |
217 |
freehttpd_response_begin_end (response); |
218 |
return E_OK; |
219 |
} |
220 |
|
221 |
freehttpd_response_add_header (response, "Content-Type", 0, |
222 |
"text/html; charset=\"utf-8\""); |
223 |
freehttpd_response_add_header (response, "Content-Length", 0, "%lu", |
224 |
doc->document_length); |
225 |
|
226 |
ret = freehttpd_response_head_send (response); |
227 |
|
228 |
if (ret != E_OK) |
229 |
log_err (LOG_ERR "failed to send error response headers: %i\n", ret); |
230 |
|
231 |
ret = freehttpd_response_begin_body (response); |
232 |
|
233 |
if (ret != E_OK) |
234 |
log_err (LOG_ERR "failed to start sending response: %i\n", ret); |
235 |
|
236 |
if (freehttpd_response_write (response, doc->document, 1, |
237 |
doc->document_length) |
238 |
!= doc->document_length) |
239 |
log_err (LOG_ERR "failed to write error response: %i\n", ret); |
240 |
|
241 |
freehttpd_response_free (response); |
242 |
return ret; |
243 |
} |
244 |
|
245 |
static long |
246 |
fsize (FILE *file) |
247 |
{ |
248 |
long size; |
249 |
long pos = ftell (file); |
250 |
fseek (file, 0, SEEK_END); |
251 |
size = ftell (file); |
252 |
fseek (file, pos, SEEK_SET); |
253 |
return size; |
254 |
} |
255 |
|
256 |
static void |
257 |
iasprintf (char **strp, const char *fmt, ...) |
258 |
{ |
259 |
va_list ap; |
260 |
va_start (ap, fmt); |
261 |
(void) (vasprintf (strp, fmt, ap) + 1); |
262 |
va_end (ap); |
263 |
} |
264 |
|
265 |
/* |
266 |
static ecode_t |
267 |
freehttpd_respond_dindex (freehttpd_t *freehttpd, freehttpd_request_t *request, |
268 |
freehttpd_response_t *response, FILE *stream, |
269 |
const char *rpath) |
270 |
{ |
271 |
freehttpd_response_set_status (response, FREEHTTPD_STATUS_OK); |
272 |
ecode_t code = freehttpd_response_head_send (response); |
273 |
|
274 |
if (code != E_OK) |
275 |
return code; |
276 |
|
277 |
bool is_root = strcmp (freehttpd->config->docroot, rpath) == 0; |
278 |
bool http1_0 = strcmp (request->version, "1.0") == 0; |
279 |
|
280 |
fprintf (stream, "Content-Type: text/html; charset=\"utf-8\"\r\n"); |
281 |
|
282 |
if (http1_0) |
283 |
fprintf (stream, "Content-Length: -1\r\n"); |
284 |
else |
285 |
fprintf (stream, "Transfer-Encoding: chunked\r\n"); |
286 |
|
287 |
fprintf (stream, "\r\n"); |
288 |
|
289 |
DIR *dir = opendir (rpath); |
290 |
|
291 |
if (dir == NULL) |
292 |
return E_SYSCALL_READ; |
293 |
|
294 |
struct dirent *entry = NULL; |
295 |
char *out_buf = NULL; |
296 |
|
297 |
iasprintf ( |
298 |
&out_buf, |
299 |
"<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\r\n" |
300 |
"<html>\r\n" |
301 |
"<head>\r\n" |
302 |
"<title>Index of %s</title>\r\n" |
303 |
"</head>\r\n" |
304 |
"<body>\r\n" |
305 |
"<h1>Index of %s</h1>\r\n" |
306 |
"<table>\r\n" |
307 |
"<tr>\r\n" |
308 |
" <th>\r\n" |
309 |
" <img\r\n" |
310 |
" src=\"https://httpd.apache.org/icons/blank.gif\"\r\n" |
311 |
" alt=\"[ICO]\"\r\n" |
312 |
" />\r\n" |
313 |
" </th>\r\n" |
314 |
" <th><a href=\"?C=N;O=D\">Name</a></th>\r\n" |
315 |
" <th><a href=\"?C=M;O=A\">Last modified</a></th>\r\n" |
316 |
" <th><a href=\"?C=S;O=A\">Size</a></th>\r\n" |
317 |
" <th><a href=\"?C=D;O=A\">Description</a></th>\r\n" |
318 |
"</tr>\r\n" |
319 |
"<tr>\r\n" |
320 |
" <th colspan=\"5\"><hr /></th>\r\n" |
321 |
"</tr>\r\n", |
322 |
request->uri, request->uri); |
323 |
|
324 |
fprintf (stream, "%lx\r\n%s\r\n", strlen (out_buf), out_buf); |
325 |
free (out_buf); |
326 |
|
327 |
if (!is_root) |
328 |
{ |
329 |
iasprintf (&out_buf, |
330 |
"<tr>\r\n" |
331 |
"<td valign=\"top\">\r\n" |
332 |
"<img\r\n" |
333 |
"src=\"https://httpd.apache.org/icons/back.gif\"\r\n" |
334 |
"alt=\"[DIR]\"\r\n" |
335 |
"/>\r\n" |
336 |
"</td>\r\n" |
337 |
"<td><a href=\"..\">Parent Directory</a></td>\r\n" |
338 |
"<td> </td>\r\n" |
339 |
"<td align=\"right\"> - </td>\r\n" |
340 |
"<td> </td>\r\n" |
341 |
"</tr>\r\n"); |
342 |
|
343 |
fprintf (stream, "%lx\r\n%s\r\n", strlen (out_buf), out_buf); |
344 |
free (out_buf); |
345 |
} |
346 |
|
347 |
while ((entry = readdir (dir)) != NULL) |
348 |
{ |
349 |
if (strcmp (entry->d_name, ".") == 0 |
350 |
|| strcmp (entry->d_name, "..") == 0) |
351 |
continue; |
352 |
|
353 |
struct stat st = { 0 }; |
354 |
char path[PATH_MAX] = { 0 }; |
355 |
char mtime_str[64] = { 0 }; |
356 |
|
357 |
strcpy (path, rpath); |
358 |
strcat (path, "/"); |
359 |
strcat (path, entry->d_name); |
360 |
|
361 |
if (lstat (path, &st) < 0) |
362 |
continue; |
363 |
|
364 |
struct tm *mtime = localtime (&st.st_mtime); |
365 |
strftime (mtime_str, sizeof (mtime_str), "%Y-%m-%d %H:%M", mtime); |
366 |
|
367 |
iasprintf (&out_buf, |
368 |
"<tr>\r\n" |
369 |
"<td valign=\"top\">\r\n" |
370 |
"<img\r\n" |
371 |
"src=\"https://httpd.apache.org/icons/%s.gif\"\r\n" |
372 |
"alt=\"[DIR]\"\r\n" |
373 |
"/>\r\n" |
374 |
"</td>\r\n", |
375 |
S_ISDIR (st.st_mode) ? "folder" : "unknown"); |
376 |
|
377 |
fprintf (stream, "%lx\r\n%s\r\n", strlen (out_buf), out_buf); |
378 |
free (out_buf); |
379 |
|
380 |
const char *leading_slash = S_ISDIR (st.st_mode) ? "/" : ""; |
381 |
iasprintf (&out_buf, |
382 |
"<td><a href=\"%s%s%s%s\">%s%s</a></td>\r\n" |
383 |
"<td align=\"right\">%s</td>\r\n" |
384 |
"<td align=\"right\"> - </td>\r\n" |
385 |
"</tr>\r\n", |
386 |
request->uri, |
387 |
request->uri[request->uri_length - 1] == '/' ? "" : "/", |
388 |
entry->d_name, leading_slash, entry->d_name, |
389 |
leading_slash, mtime_str); |
390 |
|
391 |
fprintf (stream, "%lx\r\n%s\r\n", strlen (out_buf), out_buf); |
392 |
free (out_buf); |
393 |
} |
394 |
|
395 |
iasprintf (&out_buf, |
396 |
"<tr>\r\n" |
397 |
" <th colspan=\"5\"><hr /></th>\r\n" |
398 |
"</tr>\r\n" |
399 |
"</table><address>freehttpd/1.0.0-beta.1 (Ubuntu 24.04 " |
400 |
"LTS) Server at localhost Port 8080</address>\r\n" |
401 |
"</body>\r\n" |
402 |
"</html>\r\n"); |
403 |
|
404 |
fprintf (stream, "%lx\r\n%s\r\n", strlen (out_buf), out_buf); |
405 |
free (out_buf); |
406 |
|
407 |
fprintf (stream, "0\r\n\r\n"); |
408 |
closedir (dir); |
409 |
return E_OK; |
410 |
} |
411 |
*/ |
412 |
|
413 |
static ecode_t |
414 |
freehttpd_respond_http1x (freehttpd_t *freehttpd, freehttpd_request_t *request, |
415 |
freehttpd_response_t *response) |
416 |
{ |
417 |
return freehttpd_send_error (freehttpd, response->stream, 0, |
418 |
FREEHTTPD_STATUS_NOT_IMPLEMENTED, |
419 |
request->version); |
420 |
// return E_OK; |
421 |
} |
422 |
|
423 |
/* |
424 |
static ecode_t |
425 |
freehttpd_respond (freehttpd_t *freehttpd, freehttpd_request_t *request, |
426 |
freehttpd_response_t *response, FILE *stream) |
427 |
{ |
428 |
freehttpd_response_add_default_headers (response); |
429 |
|
430 |
const char *docroot = freehttpd->config->docroot; |
431 |
char *rpath = NULL; |
432 |
freehttpd_status_t status = FREEHTTPD_STATUS_OK; |
433 |
|
434 |
if (docroot == NULL) |
435 |
{ |
436 |
status = FREEHTTPD_STATUS_INTERNAL_SERVER_ERROR; |
437 |
goto freehttpd_respond_error; |
438 |
} |
439 |
|
440 |
char *path = request->path; |
441 |
|
442 |
if (path == NULL) |
443 |
{ |
444 |
status = FREEHTTPD_STATUS_INTERNAL_SERVER_ERROR; |
445 |
goto freehttpd_respond_error; |
446 |
} |
447 |
|
448 |
char *fs_path = NULL; |
449 |
|
450 |
if (asprintf (&fs_path, "%s%s", docroot, path) < 0) |
451 |
{ |
452 |
status = FREEHTTPD_STATUS_INTERNAL_SERVER_ERROR; |
453 |
return E_LIBC_MALLOC; |
454 |
} |
455 |
|
456 |
rpath = realpath (fs_path, NULL); |
457 |
|
458 |
if (rpath == NULL) |
459 |
{ |
460 |
status = FREEHTTPD_STATUS_NOT_FOUND; |
461 |
goto freehttpd_respond_error; |
462 |
} |
463 |
|
464 |
if (strncmp (docroot, rpath, freehttpd->config->_docroot_length) != 0) |
465 |
{ |
466 |
status = FREEHTTPD_STATUS_FORBIDDEN; |
467 |
goto freehttpd_respond_error; |
468 |
} |
469 |
|
470 |
log_msg (LOG_DEBUG "rpath: %s\n", rpath); |
471 |
struct stat st = { 0 }; |
472 |
|
473 |
if (lstat (rpath, &st) < 0) |
474 |
{ |
475 |
status = FREEHTTPD_STATUS_INTERNAL_SERVER_ERROR; |
476 |
goto freehttpd_respond_error; |
477 |
} |
478 |
|
479 |
if (S_ISDIR (st.st_mode)) |
480 |
{ |
481 |
ecode_t code = freehttpd_respond_dindex (freehttpd, request, |
482 |
response, stream, rpath); |
483 |
|
484 |
free (rpath); |
485 |
free (fs_path); |
486 |
return code; |
487 |
} |
488 |
|
489 |
FILE *file = fopen (rpath, "r"); |
490 |
|
491 |
if (file == NULL) |
492 |
{ |
493 |
switch (errno) |
494 |
{ |
495 |
case EACCES: |
496 |
case EISDIR: |
497 |
status = FREEHTTPD_STATUS_FORBIDDEN; |
498 |
break; |
499 |
case ENOENT: |
500 |
status = FREEHTTPD_STATUS_NOT_FOUND; |
501 |
break; |
502 |
default: |
503 |
status = FREEHTTPD_STATUS_INTERNAL_SERVER_ERROR; |
504 |
break; |
505 |
} |
506 |
|
507 |
goto freehttpd_respond_error; |
508 |
} |
509 |
|
510 |
const char *content_type = NULL; |
511 |
size_t rpath_len = strlen (rpath); |
512 |
|
513 |
if (rpath_len > 4 && strcmp (rpath + rpath_len - 4, ".css") == 0) |
514 |
content_type = "text/css"; |
515 |
else if (rpath_len > 5 && strcmp (rpath + rpath_len - 5, ".html") == 0) |
516 |
content_type = "text/html"; |
517 |
else if (rpath_len > 4 && strcmp (rpath + rpath_len - 3, ".js") == 0) |
518 |
content_type = "application/javascript"; |
519 |
else |
520 |
{ |
521 |
content_type = magic_descriptor (freehttpd->magic, fileno (file)); |
522 |
|
523 |
if (content_type == NULL) |
524 |
content_type = "application/octet-stream"; |
525 |
} |
526 |
|
527 |
fseek (file, 0, SEEK_SET); |
528 |
|
529 |
long file_size = fsize (file); |
530 |
char buffer[1024] = { 0 }; |
531 |
|
532 |
if (file_size == -1) |
533 |
{ |
534 |
status = FREEHTTPD_STATUS_INTERNAL_SERVER_ERROR; |
535 |
goto freehttpd_respond_error; |
536 |
} |
537 |
|
538 |
freehttpd_response_set_status (response, status); |
539 |
ecode_t code = freehttpd_response_head_send (response); |
540 |
|
541 |
if (code != E_OK) |
542 |
{ |
543 |
free (rpath); |
544 |
free (fs_path); |
545 |
fclose (file); |
546 |
return code; |
547 |
} |
548 |
|
549 |
char etag[128] = { 0 }; |
550 |
bool http1_0 = strcmp (request->version, "1.0") == 0; |
551 |
snprintf (etag, sizeof (etag), "\"%lx-%lx\"", st.st_mtime, file_size); |
552 |
|
553 |
fprintf (stream, "Content-Type: %s\r\n", content_type); |
554 |
|
555 |
if (http1_0) |
556 |
fprintf (stream, "Content-Length: %ld\r\n", file_size); |
557 |
else |
558 |
fprintf (stream, "Transfer-Encoding: chunked\r\n"); |
559 |
|
560 |
fprintf (stream, "ETag: %s\r\n", etag); |
561 |
fprintf (stream, "\r\n"); |
562 |
|
563 |
while (file_size > 0) |
564 |
{ |
565 |
size_t read_size = 0; |
566 |
|
567 |
errno = 0; |
568 |
|
569 |
if ((read_size = fread (buffer, 1, sizeof (buffer), file)) == 0 |
570 |
&& errno != 0) |
571 |
{ |
572 |
freehttpd_response_set_status ( |
573 |
response, FREEHTTPD_STATUS_INTERNAL_SERVER_ERROR); |
574 |
free (rpath); |
575 |
free (fs_path); |
576 |
fclose (file); |
577 |
return E_SYSCALL_READ; |
578 |
} |
579 |
|
580 |
if (!http1_0) |
581 |
fprintf (stream, "%lx\r\n", read_size); |
582 |
|
583 |
if (fwrite (buffer, 1, read_size, stream) != read_size) |
584 |
{ |
585 |
free (rpath); |
586 |
free (fs_path); |
587 |
fclose (file); |
588 |
return E_SYSCALL_WRITE; |
589 |
} |
590 |
|
591 |
if (!http1_0) |
592 |
fprintf (stream, "\r\n"); |
593 |
|
594 |
if (file_size >= (long int) read_size) |
595 |
file_size -= (long int) read_size; |
596 |
else |
597 |
file_size = 0; |
598 |
} |
599 |
|
600 |
if (!http1_0) |
601 |
fprintf (stream, "0\r\n\r\n"); |
602 |
|
603 |
fclose (file); |
604 |
freehttpd_respond_error: |
605 |
freehttpd_response_set_status (response, status); |
606 |
|
607 |
if (status != FREEHTTPD_STATUS_OK) |
608 |
freehttpd_send_error (stream, 0, status, request->version); |
609 |
|
610 |
free (rpath); |
611 |
free (fs_path); |
612 |
return E_OK; |
613 |
} |
614 |
*/ |
615 |
|
616 |
static ecode_t |
617 |
freehttpd_loop (freehttpd_t *freehttpd) |
618 |
{ |
619 |
while (true) |
620 |
{ |
621 |
struct sockaddr_in client_addr = { 0 }; |
622 |
socklen_t client_addr_len = sizeof (client_addr); |
623 |
int client_sockfd |
624 |
= accept (freehttpd->sockfd, (struct sockaddr *) &client_addr, |
625 |
&client_addr_len); |
626 |
|
627 |
if (client_sockfd < 0) |
628 |
return E_SYSCALL_ACCEPT; |
629 |
|
630 |
ecode_t code = E_OK; |
631 |
freehttpd_request_t *request |
632 |
= freehttpd_request_parse (freehttpd, client_sockfd, &code); |
633 |
|
634 |
if (code != E_OK) |
635 |
{ |
636 |
log_err (LOG_ERR "failed to parse request: %i\n", code); |
637 |
freehttpd_send_error (freehttpd, NULL, client_sockfd, |
638 |
FREEHTTPD_STATUS_BAD_REQUEST, |
639 |
HTTP1_1); |
640 |
continue; |
641 |
} |
642 |
|
643 |
FILE *stream = fdopen (client_sockfd, "w"); |
644 |
|
645 |
if (stream == NULL) |
646 |
{ |
647 |
log_err (LOG_ERR "failed to open stream\n"); |
648 |
freehttpd_send_error ( |
649 |
freehttpd, NULL, client_sockfd, |
650 |
FREEHTTPD_STATUS_INTERNAL_SERVER_ERROR, |
651 |
request->version); |
652 |
freehttpd_request_free (request); |
653 |
close (client_sockfd); |
654 |
continue; |
655 |
} |
656 |
|
657 |
freehttpd_response_t *response = freehttpd_response_init ( |
658 |
stream, request->version, request->version_length, |
659 |
FREEHTTPD_STATUS_OK); |
660 |
|
661 |
if (response == NULL) |
662 |
{ |
663 |
log_err (LOG_ERR "failed to init response\n"); |
664 |
freehttpd_send_error ( |
665 |
freehttpd, NULL, client_sockfd, |
666 |
FREEHTTPD_STATUS_INTERNAL_SERVER_ERROR, |
667 |
request->version); |
668 |
freehttpd_request_free (request); |
669 |
close (client_sockfd); |
670 |
continue; |
671 |
} |
672 |
|
673 |
code = freehttpd_respond_http1x (freehttpd, request, response); |
674 |
|
675 |
log_msg (LOG_INFO "%s %s HTTP/%s - %d %s\n", request->method, |
676 |
request->uri, request->version, response->status.code, |
677 |
response->status.text); |
678 |
|
679 |
if (code != E_OK) |
680 |
log_err (LOG_ERR "failed to send response: %i\n", code); |
681 |
|
682 |
fclose (stream); |
683 |
freehttpd_response_free (response); |
684 |
freehttpd_request_free (request); |
685 |
} |
686 |
|
687 |
return E_OK; |
688 |
} |
689 |
|
690 |
ecode_t |
691 |
freehttpd_start (freehttpd_t *restrict freehttpd) |
692 |
{ |
693 |
ecode_t code = E_OK; |
694 |
struct sockaddr_in addr_in; |
695 |
|
696 |
if ((code = freehttpd_create_socket (freehttpd)) != E_OK) |
697 |
return code; |
698 |
|
699 |
addr_in = freehttpd_setup_addrinfo (freehttpd); |
700 |
|
701 |
if ((code = freehttpd_bind (freehttpd, &addr_in)) != E_OK) |
702 |
return code; |
703 |
|
704 |
if ((code = freehttpd_listen (freehttpd)) != E_OK) |
705 |
return code; |
706 |
|
707 |
return freehttpd_loop (freehttpd); |
708 |
} |
709 |
|
710 |
const freehttpd_config_t * |
711 |
freehttpd_get_config (freehttpd_t *freehttpd) |
712 |
{ |
713 |
return freehttpd->config; |
714 |
} |