diff options
| author | 2023-10-17 20:02:08 +0200 | |
|---|---|---|
| committer | 2023-10-17 20:02:08 +0200 | |
| commit | 6cd3e768908e99c9bfb5d4f896b9765a477e4214 (patch) | |
| tree | bbb5aef160cd5169a029da95c5f16fa5f80bac31 | |
| parent | Version 2.42 RELEASED. (diff) | |
| download | snac2-6cd3e768908e99c9bfb5d4f896b9765a477e4214.tar.gz snac2-6cd3e768908e99c9bfb5d4f896b9765a477e4214.tar.xz snac2-6cd3e768908e99c9bfb5d4f896b9765a477e4214.zip | |
Added FastCGI support.
| -rw-r--r-- | Makefile | 4 | ||||
| -rw-r--r-- | httpd.c | 22 | ||||
| -rw-r--r-- | snac.c | 1 | ||||
| -rw-r--r-- | utils.c | 3 | ||||
| -rw-r--r-- | xs_fcgi.h | 365 | ||||
| -rw-r--r-- | xs_version.h | 2 |
6 files changed, 389 insertions, 8 deletions
| @@ -43,13 +43,13 @@ html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \ | |||
| 43 | http.o: http.c xs.h xs_io.h xs_openssl.h xs_curl.h xs_time.h xs_json.h \ | 43 | http.o: http.c xs.h xs_io.h xs_openssl.h xs_curl.h xs_time.h xs_json.h \ |
| 44 | snac.h | 44 | snac.h |
| 45 | httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_httpd.h xs_mime.h \ | 45 | httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_httpd.h xs_mime.h \ |
| 46 | xs_time.h xs_openssl.h snac.h | 46 | xs_time.h xs_openssl.h xs_fcgi.h snac.h |
| 47 | main.o: main.c xs.h xs_io.h xs_json.h snac.h | 47 | main.o: main.c xs.h xs_io.h xs_json.h snac.h |
| 48 | mastoapi.o: mastoapi.c xs.h xs_openssl.h xs_json.h xs_io.h xs_time.h \ | 48 | mastoapi.o: mastoapi.c xs.h xs_openssl.h xs_json.h xs_io.h xs_time.h \ |
| 49 | xs_glob.h xs_set.h xs_random.h xs_url.h xs_mime.h xs_match.h snac.h | 49 | xs_glob.h xs_set.h xs_random.h xs_url.h xs_mime.h xs_match.h snac.h |
| 50 | snac.o: snac.c xs.h xs_io.h xs_unicode.h xs_json.h xs_curl.h xs_openssl.h \ | 50 | snac.o: snac.c xs.h xs_io.h xs_unicode.h xs_json.h xs_curl.h xs_openssl.h \ |
| 51 | xs_socket.h xs_url.h xs_httpd.h xs_mime.h xs_regex.h xs_set.h xs_time.h \ | 51 | xs_socket.h xs_url.h xs_httpd.h xs_mime.h xs_regex.h xs_set.h xs_time.h \ |
| 52 | xs_glob.h xs_random.h xs_match.h snac.h | 52 | xs_glob.h xs_random.h xs_match.h xs_fcgi.h snac.h |
| 53 | upgrade.o: upgrade.c xs.h xs_io.h xs_json.h xs_glob.h snac.h | 53 | upgrade.o: upgrade.c xs.h xs_io.h xs_json.h xs_glob.h snac.h |
| 54 | utils.o: utils.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h \ | 54 | utils.o: utils.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h \ |
| 55 | xs_random.h snac.h | 55 | xs_random.h snac.h |
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "xs_mime.h" | 9 | #include "xs_mime.h" |
| 10 | #include "xs_time.h" | 10 | #include "xs_time.h" |
| 11 | #include "xs_openssl.h" | 11 | #include "xs_openssl.h" |
| 12 | #include "xs_fcgi.h" | ||
| 12 | 13 | ||
| 13 | #include "snac.h" | 14 | #include "snac.h" |
| 14 | 15 | ||
| @@ -24,6 +25,8 @@ | |||
| 24 | #include <poll.h> | 25 | #include <poll.h> |
| 25 | #endif | 26 | #endif |
| 26 | 27 | ||
| 28 | int use_fcgi = 0; | ||
| 29 | |||
| 27 | int srv_running = 0; | 30 | int srv_running = 0; |
| 28 | 31 | ||
| 29 | /* nodeinfo 2.0 template */ | 32 | /* nodeinfo 2.0 template */ |
| @@ -199,8 +202,12 @@ void httpd_connection(FILE *f) | |||
| 199 | xs *etag = NULL; | 202 | xs *etag = NULL; |
| 200 | int p_size = 0; | 203 | int p_size = 0; |
| 201 | char *p; | 204 | char *p; |
| 205 | int fcgi_id; | ||
| 202 | 206 | ||
| 203 | req = xs_httpd_request(f, &payload, &p_size); | 207 | if (use_fcgi) |
| 208 | req = xs_fcgi_request(f, &payload, &p_size, &fcgi_id); | ||
| 209 | else | ||
| 210 | req = xs_httpd_request(f, &payload, &p_size); | ||
| 204 | 211 | ||
| 205 | if (req == NULL) { | 212 | if (req == NULL) { |
| 206 | /* probably because a timeout */ | 213 | /* probably because a timeout */ |
| @@ -330,7 +337,10 @@ void httpd_connection(FILE *f) | |||
| 330 | headers = xs_dict_append(headers, "access-control-allow-origin", "*"); | 337 | headers = xs_dict_append(headers, "access-control-allow-origin", "*"); |
| 331 | headers = xs_dict_append(headers, "access-control-allow-headers", "*"); | 338 | headers = xs_dict_append(headers, "access-control-allow-headers", "*"); |
| 332 | 339 | ||
| 333 | xs_httpd_response(f, status, headers, body, b_size); | 340 | if (use_fcgi) |
| 341 | xs_fcgi_response(f, status, headers, body, b_size, fcgi_id); | ||
| 342 | else | ||
| 343 | xs_httpd_response(f, status, headers, body, b_size); | ||
| 334 | 344 | ||
| 335 | fclose(f); | 345 | fclose(f); |
| 336 | 346 | ||
| @@ -550,6 +560,8 @@ void httpd(void) | |||
| 550 | char sem_name[24]; | 560 | char sem_name[24]; |
| 551 | sem_t anon_job_sem; | 561 | sem_t anon_job_sem; |
| 552 | 562 | ||
| 563 | use_fcgi = xs_type(xs_dict_get(srv_config, "fastcgi")) == XSTYPE_TRUE; | ||
| 564 | |||
| 553 | address = xs_dict_get(srv_config, "address"); | 565 | address = xs_dict_get(srv_config, "address"); |
| 554 | port = xs_number_str(xs_dict_get(srv_config, "port")); | 566 | port = xs_number_str(xs_dict_get(srv_config, "port")); |
| 555 | 567 | ||
| @@ -564,7 +576,8 @@ void httpd(void) | |||
| 564 | signal(SIGTERM, term_handler); | 576 | signal(SIGTERM, term_handler); |
| 565 | signal(SIGINT, term_handler); | 577 | signal(SIGINT, term_handler); |
| 566 | 578 | ||
| 567 | srv_log(xs_fmt("httpd start %s:%s %s", address, port, USER_AGENT)); | 579 | srv_log(xs_fmt("httpd%s start %s:%s %s", use_fcgi ? " (FastCGI)" : "", |
| 580 | address, port, USER_AGENT)); | ||
| 568 | 581 | ||
| 569 | /* show the number of usable file descriptors */ | 582 | /* show the number of usable file descriptors */ |
| 570 | struct rlimit r; | 583 | struct rlimit r; |
| @@ -651,5 +664,6 @@ void httpd(void) | |||
| 651 | 664 | ||
| 652 | xs *uptime = xs_str_time_diff(time(NULL) - start_time); | 665 | xs *uptime = xs_str_time_diff(time(NULL) - start_time); |
| 653 | 666 | ||
| 654 | srv_log(xs_fmt("httpd stop %s:%s (run time: %s)", address, port, uptime)); | 667 | srv_log(xs_fmt("httpd%s stop %s:%s (run time: %s)", use_fcgi ? " (FastCGI)" : "", |
| 668 | address, port, uptime)); | ||
| 655 | } | 669 | } |
| @@ -19,6 +19,7 @@ | |||
| 19 | #include "xs_glob.h" | 19 | #include "xs_glob.h" |
| 20 | #include "xs_random.h" | 20 | #include "xs_random.h" |
| 21 | #include "xs_match.h" | 21 | #include "xs_match.h" |
| 22 | #include "xs_fcgi.h" | ||
| 22 | 23 | ||
| 23 | #include "snac.h" | 24 | #include "snac.h" |
| 24 | 25 | ||
| @@ -29,7 +29,8 @@ static const char *default_srv_config = "{" | |||
| 29 | "\"admin_email\": \"\"," | 29 | "\"admin_email\": \"\"," |
| 30 | "\"admin_account\": \"\"," | 30 | "\"admin_account\": \"\"," |
| 31 | "\"title\": \"\"," | 31 | "\"title\": \"\"," |
| 32 | "\"short_description\": \"\"" | 32 | "\"short_description\": \"\"," |
| 33 | "\"fastcgi\": false" | ||
| 33 | "}"; | 34 | "}"; |
| 34 | 35 | ||
| 35 | static const char *default_css = | 36 | static const char *default_css = |
diff --git a/xs_fcgi.h b/xs_fcgi.h new file mode 100644 index 0000000..97f14d5 --- /dev/null +++ b/xs_fcgi.h | |||
| @@ -0,0 +1,365 @@ | |||
| 1 | /* copyright (c) 2022 - 2023 grunfink et al. / MIT license */ | ||
| 2 | |||
| 3 | /* | ||
| 4 | This is an intentionally-dead-simple FastCGI implementation; | ||
| 5 | only FCGI_RESPONDER type with *no* FCGI_KEEP_CON flag is supported. | ||
| 6 | This means only one simultaneous connection and no multiplexing. | ||
| 7 | It seems it's enough for nginx and OpenBSD's httpd, so here it goes. | ||
| 8 | Almost fully compatible with xs_httpd.h | ||
| 9 | */ | ||
| 10 | |||
| 11 | #ifndef _XS_FCGI_H | ||
| 12 | |||
| 13 | #define _XS_FCGI_H | ||
| 14 | |||
| 15 | xs_dict *xs_fcgi_request(FILE *f, xs_str **payload, int *p_size, int *id); | ||
| 16 | void xs_fcgi_response(FILE *f, int status, xs_dict *headers, xs_str *body, int b_size, int id); | ||
| 17 | |||
| 18 | |||
| 19 | #ifdef XS_IMPLEMENTATION | ||
| 20 | |||
| 21 | struct fcgi_record_header { | ||
| 22 | unsigned char version; | ||
| 23 | unsigned char type; | ||
| 24 | unsigned short id; | ||
| 25 | unsigned short content_len; | ||
| 26 | unsigned char padding_len; | ||
| 27 | unsigned char reserved; | ||
| 28 | } __attribute__((packed)); | ||
| 29 | |||
| 30 | /* version */ | ||
| 31 | |||
| 32 | #define FCGI_VERSION_1 1 | ||
| 33 | |||
| 34 | /* types */ | ||
| 35 | |||
| 36 | #define FCGI_BEGIN_REQUEST 1 | ||
| 37 | #define FCGI_ABORT_REQUEST 2 | ||
| 38 | #define FCGI_END_REQUEST 3 | ||
| 39 | #define FCGI_PARAMS 4 | ||
| 40 | #define FCGI_STDIN 5 | ||
| 41 | #define FCGI_STDOUT 6 | ||
| 42 | #define FCGI_STDERR 7 | ||
| 43 | #define FCGI_DATA 8 | ||
| 44 | #define FCGI_GET_VALUES 9 | ||
| 45 | #define FCGI_GET_VALUES_RESULT 10 | ||
| 46 | #define FCGI_UNKNOWN_TYPE 11 | ||
| 47 | #define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE) | ||
| 48 | |||
| 49 | struct fcgi_begin_request { | ||
| 50 | unsigned short role; | ||
| 51 | unsigned char flags; | ||
| 52 | unsigned char reserved[5]; | ||
| 53 | } __attribute__((packed)); | ||
| 54 | |||
| 55 | /* roles */ | ||
| 56 | |||
| 57 | #define FCGI_RESPONDER 1 | ||
| 58 | #define FCGI_AUTHORIZER 2 | ||
| 59 | #define FCGI_FILTER 3 | ||
| 60 | |||
| 61 | /* flags */ | ||
| 62 | |||
| 63 | #define FCGI_KEEP_CONN 1 | ||
| 64 | |||
| 65 | struct fcgi_end_request { | ||
| 66 | unsigned int app_status; | ||
| 67 | unsigned char protocol_status; | ||
| 68 | unsigned char reserved[3]; | ||
| 69 | } __attribute__((packed)); | ||
| 70 | |||
| 71 | /* protocol statuses */ | ||
| 72 | |||
| 73 | #define FCGI_REQUEST_COMPLETE 0 | ||
| 74 | #define FCGI_CANT_MPX_CONN 1 | ||
| 75 | #define FCGI_OVERLOADED 2 | ||
| 76 | #define FCGI_UNKNOWN_ROLE 3 | ||
| 77 | |||
| 78 | |||
| 79 | xs_dict *xs_fcgi_request(FILE *f, xs_str **payload, int *p_size, int *fcgi_id) | ||
| 80 | /* keeps receiving FCGI packets until a complete request is finished */ | ||
| 81 | { | ||
| 82 | unsigned char p_buf[100000]; | ||
| 83 | struct fcgi_record_header hdr; | ||
| 84 | struct fcgi_begin_request *breq = (struct fcgi_begin_request *)&p_buf; | ||
| 85 | unsigned char *buf = NULL; | ||
| 86 | int b_size = 0; | ||
| 87 | xs_dict *req = NULL; | ||
| 88 | unsigned char p_status = FCGI_REQUEST_COMPLETE; | ||
| 89 | xs *q_vars = NULL; | ||
| 90 | xs *p_vars = NULL; | ||
| 91 | |||
| 92 | *fcgi_id = -1; | ||
| 93 | |||
| 94 | for (;;) { | ||
| 95 | int sz, psz; | ||
| 96 | |||
| 97 | /* read the packet header */ | ||
| 98 | if (fread(&hdr, sizeof(hdr), 1, f) != 1) | ||
| 99 | break; | ||
| 100 | |||
| 101 | /* read the packet body */ | ||
| 102 | if ((psz = ntohs(hdr.content_len)) > 0) { | ||
| 103 | if ((sz = fread(p_buf, 1, psz, f)) != psz) | ||
| 104 | break; | ||
| 105 | } | ||
| 106 | |||
| 107 | /* read (and drop) the padding */ | ||
| 108 | if (hdr.padding_len > 0) | ||
| 109 | fread(p_buf + sz, 1, hdr.padding_len, f); | ||
| 110 | |||
| 111 | switch (hdr.type) { | ||
| 112 | case FCGI_BEGIN_REQUEST: | ||
| 113 | /* fail on unsupported roles */ | ||
| 114 | if (ntohs(breq->role) != FCGI_RESPONDER) { | ||
| 115 | p_status = FCGI_UNKNOWN_ROLE; | ||
| 116 | goto end; | ||
| 117 | } | ||
| 118 | |||
| 119 | /* fail on unsupported flags */ | ||
| 120 | if (breq->flags & FCGI_KEEP_CONN) { | ||
| 121 | p_status = FCGI_CANT_MPX_CONN; | ||
| 122 | goto end; | ||
| 123 | } | ||
| 124 | |||
| 125 | /* store the id for later */ | ||
| 126 | *fcgi_id = (int) hdr.id; | ||
| 127 | |||
| 128 | break; | ||
| 129 | |||
| 130 | case FCGI_PARAMS: | ||
| 131 | /* unknown id? fail */ | ||
| 132 | if (hdr.id != *fcgi_id) { | ||
| 133 | p_status = FCGI_CANT_MPX_CONN; | ||
| 134 | goto end; | ||
| 135 | } | ||
| 136 | |||
| 137 | if (psz) { | ||
| 138 | /* add to the buffer */ | ||
| 139 | buf = xs_realloc(buf, b_size + psz); | ||
| 140 | memcpy(buf + b_size, p_buf, psz); | ||
| 141 | b_size += psz; | ||
| 142 | } | ||
| 143 | else { | ||
| 144 | /* no size, so the packet is complete; process it */ | ||
| 145 | xs *cgi_vars = xs_dict_new(); | ||
| 146 | |||
| 147 | req = xs_dict_new(); | ||
| 148 | |||
| 149 | int offset = 0; | ||
| 150 | while (offset < b_size) { | ||
| 151 | unsigned int ksz = buf[offset++]; | ||
| 152 | |||
| 153 | if (ksz & 0x80) { | ||
| 154 | ksz &= 0x7f; | ||
| 155 | ksz = (ksz << 8) | buf[offset++]; | ||
| 156 | ksz = (ksz << 8) | buf[offset++]; | ||
| 157 | ksz = (ksz << 8) | buf[offset++]; | ||
| 158 | } | ||
| 159 | |||
| 160 | unsigned int vsz = buf[offset++]; | ||
| 161 | if (vsz & 0x80) { | ||
| 162 | vsz &= 0x7f; | ||
| 163 | vsz = (vsz << 8) | buf[offset++]; | ||
| 164 | vsz = (vsz << 8) | buf[offset++]; | ||
| 165 | vsz = (vsz << 8) | buf[offset++]; | ||
| 166 | } | ||
| 167 | |||
| 168 | /* get the key */ | ||
| 169 | xs *k = xs_str_new_sz((char *)&buf[offset], ksz); | ||
| 170 | offset += ksz; | ||
| 171 | |||
| 172 | /* get the value */ | ||
| 173 | xs *v = xs_str_new_sz((char *)&buf[offset], vsz); | ||
| 174 | offset += vsz; | ||
| 175 | |||
| 176 | cgi_vars = xs_dict_append(cgi_vars, k, v); | ||
| 177 | |||
| 178 | if (strcmp(k, "REQUEST_METHOD") == 0) | ||
| 179 | req = xs_dict_append(req, "method", v); | ||
| 180 | else | ||
| 181 | if (strcmp(k, "REQUEST_URI") == 0) { | ||
| 182 | xs *udp = xs_url_dec(v); | ||
| 183 | xs *pnv = xs_split_n(udp, "?", 1); | ||
| 184 | |||
| 185 | /* store the path */ | ||
| 186 | req = xs_dict_append(req, "path", xs_list_get(pnv, 0)); | ||
| 187 | |||
| 188 | /* get the variables */ | ||
| 189 | q_vars = xs_url_vars(xs_list_get(pnv, 1)); | ||
| 190 | } | ||
| 191 | else | ||
| 192 | if (xs_match(k, "CONTENT_TYPE|CONTENT_LENGTH|HTTP_*")) { | ||
| 193 | if (xs_startswith(k, "HTTP_")) | ||
| 194 | k = xs_crop_i(k, 5, 0); | ||
| 195 | |||
| 196 | k = xs_tolower_i(k); | ||
| 197 | k = xs_replace_i(k, "_", "-"); | ||
| 198 | |||
| 199 | req = xs_dict_append(req, k, v); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | req = xs_dict_append(req, "cgi_vars", cgi_vars); | ||
| 204 | |||
| 205 | buf = xs_free(buf); | ||
| 206 | b_size = 0; | ||
| 207 | } | ||
| 208 | |||
| 209 | break; | ||
| 210 | |||
| 211 | case FCGI_STDIN: | ||
| 212 | /* unknown id? fail */ | ||
| 213 | if (hdr.id != *fcgi_id) { | ||
| 214 | p_status = FCGI_CANT_MPX_CONN; | ||
| 215 | goto end; | ||
| 216 | } | ||
| 217 | |||
| 218 | if (psz) { | ||
| 219 | /* add to the buffer */ | ||
| 220 | buf = xs_realloc(buf, b_size + psz); | ||
| 221 | memcpy(buf + b_size, p_buf, psz); | ||
| 222 | b_size += psz; | ||
| 223 | } | ||
| 224 | else { | ||
| 225 | /* the packet is complete; fill the payload info and finish */ | ||
| 226 | *payload = (xs_str *)buf; | ||
| 227 | *p_size = b_size; | ||
| 228 | |||
| 229 | const char *ct = xs_dict_get(req, "content-type"); | ||
| 230 | |||
| 231 | if (*payload && ct && strcmp(ct, "application/x-www-form-urlencoded") == 0) { | ||
| 232 | xs *upl = xs_url_dec(*payload); | ||
| 233 | p_vars = xs_url_vars(upl); | ||
| 234 | } | ||
| 235 | else | ||
| 236 | if (*payload && ct && xs_startswith(ct, "multipart/form-data")) { | ||
| 237 | p_vars = xs_multipart_form_data(*payload, *p_size, ct); | ||
| 238 | } | ||
| 239 | else | ||
| 240 | p_vars = xs_dict_new(); | ||
| 241 | |||
| 242 | if (q_vars == NULL) | ||
| 243 | q_vars = xs_dict_new(); | ||
| 244 | |||
| 245 | req = xs_dict_append(req, "q_vars", q_vars); | ||
| 246 | req = xs_dict_append(req, "p_vars", p_vars); | ||
| 247 | |||
| 248 | /* disconnect the payload from the buf variable */ | ||
| 249 | buf = NULL; | ||
| 250 | |||
| 251 | goto end; | ||
| 252 | } | ||
| 253 | |||
| 254 | break; | ||
| 255 | } | ||
| 256 | } | ||
| 257 | |||
| 258 | end: | ||
| 259 | /* any kind of error? notify and cleanup */ | ||
| 260 | if (p_status != FCGI_REQUEST_COMPLETE) { | ||
| 261 | struct fcgi_end_request ereq = {0}; | ||
| 262 | |||
| 263 | /* complete the connection */ | ||
| 264 | ereq.app_status = 0; | ||
| 265 | ereq.protocol_status = p_status; | ||
| 266 | |||
| 267 | /* reuse header */ | ||
| 268 | hdr.type = FCGI_ABORT_REQUEST; | ||
| 269 | hdr.content_len = htons(sizeof(ereq)); | ||
| 270 | |||
| 271 | fwrite(&hdr, sizeof(hdr), 1, f); | ||
| 272 | fwrite(&ereq, sizeof(ereq), 1, f); | ||
| 273 | |||
| 274 | /* session closed */ | ||
| 275 | *fcgi_id = -1; | ||
| 276 | |||
| 277 | /* request dict is not valid */ | ||
| 278 | req = xs_free(req); | ||
| 279 | } | ||
| 280 | |||
| 281 | xs_free(buf); | ||
| 282 | return req; | ||
| 283 | } | ||
| 284 | |||
| 285 | |||
| 286 | void xs_fcgi_response(FILE *f, int status, xs_dict *headers, xs_str *body, int b_size, int fcgi_id) | ||
| 287 | /* writes an FCGI response */ | ||
| 288 | { | ||
| 289 | struct fcgi_record_header hdr = {0}; | ||
| 290 | struct fcgi_end_request ereq = {0}; | ||
| 291 | xs *out = xs_str_new(NULL); | ||
| 292 | xs_dict *p; | ||
| 293 | xs_str *k; | ||
| 294 | xs_str *v; | ||
| 295 | |||
| 296 | /* no previous id? it's an error */ | ||
| 297 | if (fcgi_id == -1) | ||
| 298 | return; | ||
| 299 | |||
| 300 | /* create the headers */ | ||
| 301 | { | ||
| 302 | xs *s1 = xs_fmt("status: %d\r\n", status); | ||
| 303 | out = xs_str_cat(out, s1); | ||
| 304 | } | ||
| 305 | |||
| 306 | p = headers; | ||
| 307 | while (xs_dict_iter(&p, &k, &v)) { | ||
| 308 | xs *s1 = xs_fmt("%s: %s\r\n", k, v); | ||
| 309 | out = xs_str_cat(out, s1); | ||
| 310 | } | ||
| 311 | |||
| 312 | if (b_size > 0) { | ||
| 313 | xs *s1 = xs_fmt("content-length: %d\r\n", b_size); | ||
| 314 | out = xs_str_cat(out, s1); | ||
| 315 | } | ||
| 316 | |||
| 317 | out = xs_str_cat(out, "\r\n"); | ||
| 318 | |||
| 319 | /* everything is text by now */ | ||
| 320 | int size = strlen(out); | ||
| 321 | |||
| 322 | /* add the body */ | ||
| 323 | if (body != NULL && b_size > 0) | ||
| 324 | out = xs_append_m(out, body, b_size); | ||
| 325 | |||
| 326 | /* now send all the STDOUT in packets */ | ||
| 327 | hdr.version = FCGI_VERSION_1; | ||
| 328 | hdr.type = FCGI_STDOUT; | ||
| 329 | hdr.id = fcgi_id; | ||
| 330 | |||
| 331 | size += b_size; | ||
| 332 | int offset = 0; | ||
| 333 | |||
| 334 | while (offset < size) { | ||
| 335 | int sz = size - offset; | ||
| 336 | if (sz > 0xffff) | ||
| 337 | sz = 0xffff; | ||
| 338 | |||
| 339 | hdr.content_len = htons(sz); | ||
| 340 | |||
| 341 | fwrite(&hdr, sizeof(hdr), 1, f); | ||
| 342 | fwrite(out + offset, 1, sz, f); | ||
| 343 | |||
| 344 | offset += sz; | ||
| 345 | } | ||
| 346 | |||
| 347 | /* final STDOUT packet with 0 size */ | ||
| 348 | hdr.content_len = 0; | ||
| 349 | fwrite(&hdr, sizeof(hdr), 1, f); | ||
| 350 | |||
| 351 | /* complete the connection */ | ||
| 352 | ereq.app_status = 0; | ||
| 353 | ereq.protocol_status = FCGI_REQUEST_COMPLETE; | ||
| 354 | |||
| 355 | hdr.type = FCGI_END_REQUEST; | ||
| 356 | hdr.content_len = htons(sizeof(ereq)); | ||
| 357 | |||
| 358 | fwrite(&hdr, sizeof(hdr), 1, f); | ||
| 359 | fwrite(&ereq, sizeof(ereq), 1, f); | ||
| 360 | } | ||
| 361 | |||
| 362 | |||
| 363 | #endif /* XS_IMPLEMENTATION */ | ||
| 364 | |||
| 365 | #endif /* XS_URL_H */ | ||
diff --git a/xs_version.h b/xs_version.h index a30fb2d..5fa20dd 100644 --- a/xs_version.h +++ b/xs_version.h | |||
| @@ -1 +1 @@ | |||
| /* a9cd3893c427bbcc478c5680245d435e415fd58a */ | /* b109eea00ddc0765929e36ed1ca6f3f697262bb2 */ | ||