snac2/xs_httpd.h
2022-09-19 20:41:11 +02:00

185 lines
4.0 KiB
C

/* copyright (c) 2022 grunfink - MIT license */
#ifndef _XS_HTTPD_H
#define _XS_HTTPD_H
d_char *xs_url_dec(char *str);
d_char *xs_url_vars(char *str);
d_char *xs_httpd_request(FILE *f);
void xs_httpd_response(FILE *f, int status, d_char *headers, char *body, int b_size);
#ifdef XS_IMPLEMENTATION
d_char *xs_url_dec(char *str)
/* decodes an URL */
{
d_char *s = xs_str_new(NULL);
while (*str) {
if (*str == '%') {
int i;
if (sscanf(str + 1, "%02x", &i) == 1) {
unsigned char uc = i;
s = xs_append_m(s, (char *)&uc, 1);
str += 2;
}
}
else
if (*str == '+')
s = xs_append_m(s, " ", 1);
else
s = xs_append_m(s, str, 1);
str++;
}
return s;
}
d_char *xs_url_vars(char *str)
/* parse url variables */
{
d_char *vars;
vars = xs_dict_new();
if (str != NULL) {
char *v, *l;
xs *args;
/* split by arguments */
args = xs_split(str, "&");
l = args;
while (xs_list_iter(&l, &v)) {
xs *kv = xs_splitn(v, "=", 2);
if (xs_list_len(kv) == 2)
vars = xs_dict_append(vars,
xs_list_get(kv, 0), xs_list_get(kv, 1));
}
}
return vars;
}
d_char *xs_httpd_request(FILE *f)
/* processes an httpd connection */
{
xs *headers = NULL;
xs *q_vars = NULL;
xs *p_vars = NULL;
d_char *req = NULL;
xs *l1, *l2;
char *v;
xs_socket_timeout(fileno(f), 2.0, 0.0);
/* read the first line and split it */
l1 = xs_strip(xs_readline(f));
l2 = xs_split(l1, " ");
if (xs_list_len(l2) != 3) {
/* error or timeout */
return NULL;
}
headers = xs_dict_new();
headers = xs_dict_append(headers, "method", xs_list_get(l2, 0));
headers = xs_dict_append(headers, "proto", xs_list_get(l2, 2));
{
/* split the path with its optional variables */
xs *udp = xs_url_dec(xs_list_get(l2, 1));
xs *pnv = xs_splitn(udp, "?", 1);
/* store the path */
headers = xs_dict_append(headers, "path", xs_list_get(pnv, 0));
/* get the variables */
q_vars = xs_url_vars(xs_list_get(pnv, 1));
}
/* read the headers */
for (;;) {
xs *l, *p = NULL;
l = xs_strip(xs_readline(f));
/* done with the header? */
if (strcmp(l, "") == 0)
break;
/* split header and content */
p = xs_splitn(l, ": ", 1);
if (xs_list_len(p) == 2)
headers = xs_dict_append(headers,
xs_tolower(xs_list_get(p, 0)), xs_list_get(p, 1));
}
xs_socket_timeout(fileno(f), 5.0, 0.0);
/* does it have a payload with form urlencoded variables? */
v = xs_dict_get(headers, "content-type");
if (v && strcmp(v, "application/x-www-form-urlencoded") == 0) {
if ((v = xs_dict_get(headers, "content-length")) != NULL) {
int cl = atoi(v);
xs *payload;
if ((payload = xs_read(f, cl)) != NULL) {
xs *upl = xs_url_dec(payload);
p_vars = xs_url_vars(upl);
}
}
}
else
p_vars = xs_dict_new();
if (errno == 0) {
req = xs_dict_new();
req = xs_dict_append(req, "headers", headers);
req = xs_dict_append(req, "q_vars", q_vars);
req = xs_dict_append(req, "p_vars", p_vars);
}
return req;
}
void xs_httpd_response(FILE *f, int status, d_char *headers, char *body, int b_size)
/* sends an httpd response */
{
xs *proto;
char *p, *k, *v;
proto = xs_fmt("HTTP/1.1 %d", status);
fprintf(f, "%s\r\n", proto);
p = headers;
while (xs_dict_iter(&p, &k, &v)) {
fprintf(f, "%s: %s\r\n", k, v);
}
if (b_size != 0)
fprintf(f, "content-length: %d\r\n", b_size);
fprintf(f, "\r\n");
if (body != NULL && b_size != 0)
fwrite(body, b_size, 1, f);
}
#endif /* XS_IMPLEMENTATION */
#endif /* XS_HTTPD_H */