2.76
xs_httpd.h
1/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */
2
3#ifndef _XS_HTTPD_H
4
5#define _XS_HTTPD_H
6
7xs_dict *xs_httpd_request(FILE *f, xs_str **payload, int *p_size);
8void xs_httpd_response(FILE *f, int status, const char *status_text, xs_dict *headers, xs_str *body, int b_size);
9
10
11#ifdef XS_IMPLEMENTATION
12
13xs_dict *xs_httpd_request(FILE *f, xs_str **payload, int *p_size)
14/* processes an httpd connection */
15{
16 xs *q_vars = NULL;
17 xs *p_vars = NULL;
18 xs *l1;
19 const char *v;
20 char *saveptr;
21
22 xs_socket_timeout(fileno(f), 2.0, 0.0);
23
24 /* read the first line and split it */
25 l1 = xs_strip_i(xs_readline(f));
26 char *raw_path;
27 const char *mtd;
28 const char *proto;
29
30 if (!(mtd = strtok_r(l1, " ", &saveptr)) ||
31 !(raw_path = strtok_r(NULL, " ", &saveptr)) ||
32 !(proto = strtok_r(NULL, " ", &saveptr)) ||
33 strtok_r(NULL, " ", &saveptr))
34 return NULL;
35
36 if (!xs_is_string(mtd) || !xs_is_string(raw_path) || !xs_is_string(proto))
37 return NULL;
38
39 xs_dict *req = xs_dict_new();
40
41 req = xs_dict_append(req, "method", mtd);
42 req = xs_dict_append(req, "raw_path", raw_path);
43 req = xs_dict_append(req, "proto", proto);
44
45 {
46 char *q = strchr(raw_path, '?');
47
48 /* get the variables */
49 if (q) {
50 *q++ = '\0';
51 q_vars = xs_url_vars(q);
52 }
53 /* store the path */
54 req = xs_dict_append(req, "path", raw_path);
55 }
56
57 /* read the headers */
58 for (;;) {
59 xs *l;
60
61 l = xs_strip_i(xs_readline(f));
62
63 /* done with the header? */
64 if (strcmp(l, "") == 0)
65 break;
66
67 /* split header and content */
68 char *cnt = strchr(l, ':');
69 if (!cnt)
70 continue;
71
72 *cnt++ = '\0';
73 cnt += strspn(cnt, " \r\n\t\v\f");
74 l = xs_rstrip_chars_i(l, " \r\n\t\v\f");
75
76 if (!xs_is_string(cnt))
77 continue;
78
79 req = xs_dict_append(req, xs_tolower_i(l), cnt);
80 }
81
82 xs_socket_timeout(fileno(f), 5.0, 0.0);
83
84 if ((v = xs_dict_get(req, "content-length")) != NULL) {
85 /* if it has a payload, load it */
86 *p_size = atoi(v);
87 *payload = xs_read(f, p_size);
88 }
89
90 v = xs_dict_get(req, "content-type");
91
92 if (*payload && v && strcmp(v, "application/x-www-form-urlencoded") == 0) {
93 p_vars = xs_url_vars(*payload);
94 }
95 else
96 if (*payload && v && xs_startswith(v, "multipart/form-data")) {
97 p_vars = xs_multipart_form_data(*payload, *p_size, v);
98 }
99 else
100 p_vars = xs_dict_new();
101
102 req = xs_dict_append(req, "q_vars", q_vars);
103 req = xs_dict_append(req, "p_vars", p_vars);
104
105 if (errno)
106 req = xs_free(req);
107
108 return req;
109}
110
111
112void xs_httpd_response(FILE *f, int status, const char *status_text, xs_dict *headers, xs_str *body, int b_size)
113/* sends an httpd response */
114{
115 xs *proto;
116 const xs_str *k;
117 const xs_val *v;
118
119 proto = xs_fmt("HTTP/1.1 %d %s", status, status_text);
120 fprintf(f, "%s\r\n", proto);
121
122 xs_dict_foreach(headers, k, v) {
123 fprintf(f, "%s: %s\r\n", k, v);
124 }
125
126 if (b_size != 0)
127 fprintf(f, "content-length: %d\r\n", b_size);
128
129 fprintf(f, "\r\n");
130
131 if (body != NULL && b_size != 0)
132 fwrite(body, b_size, 1, f);
133}
134
135
136#endif /* XS_IMPLEMENTATION */
137
138#endif /* XS_HTTPD_H */