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 */