snac2

Fork of https://codeberg.org/grunfink/snac2
git clone https://git.inz.fi/snac2
Log | Files | Refs | README | LICENSE

xs_httpd.h (3368B)


      1 /* copyright (c) 2022 - 2025 grunfink et al. / MIT license */
      2 
      3 #ifndef _XS_HTTPD_H
      4 
      5 #define _XS_HTTPD_H
      6 
      7 xs_dict *xs_httpd_request(FILE *f, xs_str **payload, int *p_size);
      8 void 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 
     13 xs_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 
    112 void 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 */