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