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