mirror of
https://codeberg.org/grunfink/snac2.git
synced 2024-11-25 22:45:05 +00:00
Added FastCGI support.
This commit is contained in:
parent
d17aa86c5b
commit
6cd3e76890
6 changed files with 389 additions and 8 deletions
4
Makefile
4
Makefile
|
@ -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 \
|
||||||
http.o: http.c xs.h xs_io.h xs_openssl.h xs_curl.h xs_time.h xs_json.h \
|
http.o: http.c xs.h xs_io.h xs_openssl.h xs_curl.h xs_time.h xs_json.h \
|
||||||
snac.h
|
snac.h
|
||||||
httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_httpd.h xs_mime.h \
|
httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_httpd.h xs_mime.h \
|
||||||
xs_time.h xs_openssl.h snac.h
|
xs_time.h xs_openssl.h xs_fcgi.h snac.h
|
||||||
main.o: main.c xs.h xs_io.h xs_json.h snac.h
|
main.o: main.c xs.h xs_io.h xs_json.h snac.h
|
||||||
mastoapi.o: mastoapi.c xs.h xs_openssl.h xs_json.h xs_io.h xs_time.h \
|
mastoapi.o: mastoapi.c xs.h xs_openssl.h xs_json.h xs_io.h xs_time.h \
|
||||||
xs_glob.h xs_set.h xs_random.h xs_url.h xs_mime.h xs_match.h snac.h
|
xs_glob.h xs_set.h xs_random.h xs_url.h xs_mime.h xs_match.h snac.h
|
||||||
snac.o: snac.c xs.h xs_io.h xs_unicode.h xs_json.h xs_curl.h xs_openssl.h \
|
snac.o: snac.c xs.h xs_io.h xs_unicode.h xs_json.h xs_curl.h xs_openssl.h \
|
||||||
xs_socket.h xs_url.h xs_httpd.h xs_mime.h xs_regex.h xs_set.h xs_time.h \
|
xs_socket.h xs_url.h xs_httpd.h xs_mime.h xs_regex.h xs_set.h xs_time.h \
|
||||||
xs_glob.h xs_random.h xs_match.h snac.h
|
xs_glob.h xs_random.h xs_match.h xs_fcgi.h snac.h
|
||||||
upgrade.o: upgrade.c xs.h xs_io.h xs_json.h xs_glob.h snac.h
|
upgrade.o: upgrade.c xs.h xs_io.h xs_json.h xs_glob.h snac.h
|
||||||
utils.o: utils.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h \
|
utils.o: utils.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h \
|
||||||
xs_random.h snac.h
|
xs_random.h snac.h
|
||||||
|
|
18
httpd.c
18
httpd.c
|
@ -9,6 +9,7 @@
|
||||||
#include "xs_mime.h"
|
#include "xs_mime.h"
|
||||||
#include "xs_time.h"
|
#include "xs_time.h"
|
||||||
#include "xs_openssl.h"
|
#include "xs_openssl.h"
|
||||||
|
#include "xs_fcgi.h"
|
||||||
|
|
||||||
#include "snac.h"
|
#include "snac.h"
|
||||||
|
|
||||||
|
@ -24,6 +25,8 @@
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
int use_fcgi = 0;
|
||||||
|
|
||||||
int srv_running = 0;
|
int srv_running = 0;
|
||||||
|
|
||||||
/* nodeinfo 2.0 template */
|
/* nodeinfo 2.0 template */
|
||||||
|
@ -199,7 +202,11 @@ void httpd_connection(FILE *f)
|
||||||
xs *etag = NULL;
|
xs *etag = NULL;
|
||||||
int p_size = 0;
|
int p_size = 0;
|
||||||
char *p;
|
char *p;
|
||||||
|
int fcgi_id;
|
||||||
|
|
||||||
|
if (use_fcgi)
|
||||||
|
req = xs_fcgi_request(f, &payload, &p_size, &fcgi_id);
|
||||||
|
else
|
||||||
req = xs_httpd_request(f, &payload, &p_size);
|
req = xs_httpd_request(f, &payload, &p_size);
|
||||||
|
|
||||||
if (req == NULL) {
|
if (req == NULL) {
|
||||||
|
@ -330,6 +337,9 @@ void httpd_connection(FILE *f)
|
||||||
headers = xs_dict_append(headers, "access-control-allow-origin", "*");
|
headers = xs_dict_append(headers, "access-control-allow-origin", "*");
|
||||||
headers = xs_dict_append(headers, "access-control-allow-headers", "*");
|
headers = xs_dict_append(headers, "access-control-allow-headers", "*");
|
||||||
|
|
||||||
|
if (use_fcgi)
|
||||||
|
xs_fcgi_response(f, status, headers, body, b_size, fcgi_id);
|
||||||
|
else
|
||||||
xs_httpd_response(f, status, headers, body, b_size);
|
xs_httpd_response(f, status, headers, body, b_size);
|
||||||
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
@ -550,6 +560,8 @@ void httpd(void)
|
||||||
char sem_name[24];
|
char sem_name[24];
|
||||||
sem_t anon_job_sem;
|
sem_t anon_job_sem;
|
||||||
|
|
||||||
|
use_fcgi = xs_type(xs_dict_get(srv_config, "fastcgi")) == XSTYPE_TRUE;
|
||||||
|
|
||||||
address = xs_dict_get(srv_config, "address");
|
address = xs_dict_get(srv_config, "address");
|
||||||
port = xs_number_str(xs_dict_get(srv_config, "port"));
|
port = xs_number_str(xs_dict_get(srv_config, "port"));
|
||||||
|
|
||||||
|
@ -564,7 +576,8 @@ void httpd(void)
|
||||||
signal(SIGTERM, term_handler);
|
signal(SIGTERM, term_handler);
|
||||||
signal(SIGINT, term_handler);
|
signal(SIGINT, term_handler);
|
||||||
|
|
||||||
srv_log(xs_fmt("httpd start %s:%s %s", address, port, USER_AGENT));
|
srv_log(xs_fmt("httpd%s start %s:%s %s", use_fcgi ? " (FastCGI)" : "",
|
||||||
|
address, port, USER_AGENT));
|
||||||
|
|
||||||
/* show the number of usable file descriptors */
|
/* show the number of usable file descriptors */
|
||||||
struct rlimit r;
|
struct rlimit r;
|
||||||
|
@ -651,5 +664,6 @@ void httpd(void)
|
||||||
|
|
||||||
xs *uptime = xs_str_time_diff(time(NULL) - start_time);
|
xs *uptime = xs_str_time_diff(time(NULL) - start_time);
|
||||||
|
|
||||||
srv_log(xs_fmt("httpd stop %s:%s (run time: %s)", address, port, uptime));
|
srv_log(xs_fmt("httpd%s stop %s:%s (run time: %s)", use_fcgi ? " (FastCGI)" : "",
|
||||||
|
address, port, uptime));
|
||||||
}
|
}
|
||||||
|
|
1
snac.c
1
snac.c
|
@ -19,6 +19,7 @@
|
||||||
#include "xs_glob.h"
|
#include "xs_glob.h"
|
||||||
#include "xs_random.h"
|
#include "xs_random.h"
|
||||||
#include "xs_match.h"
|
#include "xs_match.h"
|
||||||
|
#include "xs_fcgi.h"
|
||||||
|
|
||||||
#include "snac.h"
|
#include "snac.h"
|
||||||
|
|
||||||
|
|
3
utils.c
3
utils.c
|
@ -29,7 +29,8 @@ static const char *default_srv_config = "{"
|
||||||
"\"admin_email\": \"\","
|
"\"admin_email\": \"\","
|
||||||
"\"admin_account\": \"\","
|
"\"admin_account\": \"\","
|
||||||
"\"title\": \"\","
|
"\"title\": \"\","
|
||||||
"\"short_description\": \"\""
|
"\"short_description\": \"\","
|
||||||
|
"\"fastcgi\": false"
|
||||||
"}";
|
"}";
|
||||||
|
|
||||||
static const char *default_css =
|
static const char *default_css =
|
||||||
|
|
365
xs_fcgi.h
Normal file
365
xs_fcgi.h
Normal file
|
@ -0,0 +1,365 @@
|
||||||
|
/* copyright (c) 2022 - 2023 grunfink et al. / MIT license */
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is an intentionally-dead-simple FastCGI implementation;
|
||||||
|
only FCGI_RESPONDER type with *no* FCGI_KEEP_CON flag is supported.
|
||||||
|
This means only one simultaneous connection and no multiplexing.
|
||||||
|
It seems it's enough for nginx and OpenBSD's httpd, so here it goes.
|
||||||
|
Almost fully compatible with xs_httpd.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _XS_FCGI_H
|
||||||
|
|
||||||
|
#define _XS_FCGI_H
|
||||||
|
|
||||||
|
xs_dict *xs_fcgi_request(FILE *f, xs_str **payload, int *p_size, int *id);
|
||||||
|
void xs_fcgi_response(FILE *f, int status, xs_dict *headers, xs_str *body, int b_size, int id);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef XS_IMPLEMENTATION
|
||||||
|
|
||||||
|
struct fcgi_record_header {
|
||||||
|
unsigned char version;
|
||||||
|
unsigned char type;
|
||||||
|
unsigned short id;
|
||||||
|
unsigned short content_len;
|
||||||
|
unsigned char padding_len;
|
||||||
|
unsigned char reserved;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/* version */
|
||||||
|
|
||||||
|
#define FCGI_VERSION_1 1
|
||||||
|
|
||||||
|
/* types */
|
||||||
|
|
||||||
|
#define FCGI_BEGIN_REQUEST 1
|
||||||
|
#define FCGI_ABORT_REQUEST 2
|
||||||
|
#define FCGI_END_REQUEST 3
|
||||||
|
#define FCGI_PARAMS 4
|
||||||
|
#define FCGI_STDIN 5
|
||||||
|
#define FCGI_STDOUT 6
|
||||||
|
#define FCGI_STDERR 7
|
||||||
|
#define FCGI_DATA 8
|
||||||
|
#define FCGI_GET_VALUES 9
|
||||||
|
#define FCGI_GET_VALUES_RESULT 10
|
||||||
|
#define FCGI_UNKNOWN_TYPE 11
|
||||||
|
#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
|
||||||
|
|
||||||
|
struct fcgi_begin_request {
|
||||||
|
unsigned short role;
|
||||||
|
unsigned char flags;
|
||||||
|
unsigned char reserved[5];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/* roles */
|
||||||
|
|
||||||
|
#define FCGI_RESPONDER 1
|
||||||
|
#define FCGI_AUTHORIZER 2
|
||||||
|
#define FCGI_FILTER 3
|
||||||
|
|
||||||
|
/* flags */
|
||||||
|
|
||||||
|
#define FCGI_KEEP_CONN 1
|
||||||
|
|
||||||
|
struct fcgi_end_request {
|
||||||
|
unsigned int app_status;
|
||||||
|
unsigned char protocol_status;
|
||||||
|
unsigned char reserved[3];
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/* protocol statuses */
|
||||||
|
|
||||||
|
#define FCGI_REQUEST_COMPLETE 0
|
||||||
|
#define FCGI_CANT_MPX_CONN 1
|
||||||
|
#define FCGI_OVERLOADED 2
|
||||||
|
#define FCGI_UNKNOWN_ROLE 3
|
||||||
|
|
||||||
|
|
||||||
|
xs_dict *xs_fcgi_request(FILE *f, xs_str **payload, int *p_size, int *fcgi_id)
|
||||||
|
/* keeps receiving FCGI packets until a complete request is finished */
|
||||||
|
{
|
||||||
|
unsigned char p_buf[100000];
|
||||||
|
struct fcgi_record_header hdr;
|
||||||
|
struct fcgi_begin_request *breq = (struct fcgi_begin_request *)&p_buf;
|
||||||
|
unsigned char *buf = NULL;
|
||||||
|
int b_size = 0;
|
||||||
|
xs_dict *req = NULL;
|
||||||
|
unsigned char p_status = FCGI_REQUEST_COMPLETE;
|
||||||
|
xs *q_vars = NULL;
|
||||||
|
xs *p_vars = NULL;
|
||||||
|
|
||||||
|
*fcgi_id = -1;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
int sz, psz;
|
||||||
|
|
||||||
|
/* read the packet header */
|
||||||
|
if (fread(&hdr, sizeof(hdr), 1, f) != 1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* read the packet body */
|
||||||
|
if ((psz = ntohs(hdr.content_len)) > 0) {
|
||||||
|
if ((sz = fread(p_buf, 1, psz, f)) != psz)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read (and drop) the padding */
|
||||||
|
if (hdr.padding_len > 0)
|
||||||
|
fread(p_buf + sz, 1, hdr.padding_len, f);
|
||||||
|
|
||||||
|
switch (hdr.type) {
|
||||||
|
case FCGI_BEGIN_REQUEST:
|
||||||
|
/* fail on unsupported roles */
|
||||||
|
if (ntohs(breq->role) != FCGI_RESPONDER) {
|
||||||
|
p_status = FCGI_UNKNOWN_ROLE;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fail on unsupported flags */
|
||||||
|
if (breq->flags & FCGI_KEEP_CONN) {
|
||||||
|
p_status = FCGI_CANT_MPX_CONN;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* store the id for later */
|
||||||
|
*fcgi_id = (int) hdr.id;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FCGI_PARAMS:
|
||||||
|
/* unknown id? fail */
|
||||||
|
if (hdr.id != *fcgi_id) {
|
||||||
|
p_status = FCGI_CANT_MPX_CONN;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (psz) {
|
||||||
|
/* add to the buffer */
|
||||||
|
buf = xs_realloc(buf, b_size + psz);
|
||||||
|
memcpy(buf + b_size, p_buf, psz);
|
||||||
|
b_size += psz;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* no size, so the packet is complete; process it */
|
||||||
|
xs *cgi_vars = xs_dict_new();
|
||||||
|
|
||||||
|
req = xs_dict_new();
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
while (offset < b_size) {
|
||||||
|
unsigned int ksz = buf[offset++];
|
||||||
|
|
||||||
|
if (ksz & 0x80) {
|
||||||
|
ksz &= 0x7f;
|
||||||
|
ksz = (ksz << 8) | buf[offset++];
|
||||||
|
ksz = (ksz << 8) | buf[offset++];
|
||||||
|
ksz = (ksz << 8) | buf[offset++];
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int vsz = buf[offset++];
|
||||||
|
if (vsz & 0x80) {
|
||||||
|
vsz &= 0x7f;
|
||||||
|
vsz = (vsz << 8) | buf[offset++];
|
||||||
|
vsz = (vsz << 8) | buf[offset++];
|
||||||
|
vsz = (vsz << 8) | buf[offset++];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get the key */
|
||||||
|
xs *k = xs_str_new_sz((char *)&buf[offset], ksz);
|
||||||
|
offset += ksz;
|
||||||
|
|
||||||
|
/* get the value */
|
||||||
|
xs *v = xs_str_new_sz((char *)&buf[offset], vsz);
|
||||||
|
offset += vsz;
|
||||||
|
|
||||||
|
cgi_vars = xs_dict_append(cgi_vars, k, v);
|
||||||
|
|
||||||
|
if (strcmp(k, "REQUEST_METHOD") == 0)
|
||||||
|
req = xs_dict_append(req, "method", v);
|
||||||
|
else
|
||||||
|
if (strcmp(k, "REQUEST_URI") == 0) {
|
||||||
|
xs *udp = xs_url_dec(v);
|
||||||
|
xs *pnv = xs_split_n(udp, "?", 1);
|
||||||
|
|
||||||
|
/* store the path */
|
||||||
|
req = xs_dict_append(req, "path", xs_list_get(pnv, 0));
|
||||||
|
|
||||||
|
/* get the variables */
|
||||||
|
q_vars = xs_url_vars(xs_list_get(pnv, 1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (xs_match(k, "CONTENT_TYPE|CONTENT_LENGTH|HTTP_*")) {
|
||||||
|
if (xs_startswith(k, "HTTP_"))
|
||||||
|
k = xs_crop_i(k, 5, 0);
|
||||||
|
|
||||||
|
k = xs_tolower_i(k);
|
||||||
|
k = xs_replace_i(k, "_", "-");
|
||||||
|
|
||||||
|
req = xs_dict_append(req, k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req = xs_dict_append(req, "cgi_vars", cgi_vars);
|
||||||
|
|
||||||
|
buf = xs_free(buf);
|
||||||
|
b_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FCGI_STDIN:
|
||||||
|
/* unknown id? fail */
|
||||||
|
if (hdr.id != *fcgi_id) {
|
||||||
|
p_status = FCGI_CANT_MPX_CONN;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (psz) {
|
||||||
|
/* add to the buffer */
|
||||||
|
buf = xs_realloc(buf, b_size + psz);
|
||||||
|
memcpy(buf + b_size, p_buf, psz);
|
||||||
|
b_size += psz;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* the packet is complete; fill the payload info and finish */
|
||||||
|
*payload = (xs_str *)buf;
|
||||||
|
*p_size = b_size;
|
||||||
|
|
||||||
|
const char *ct = xs_dict_get(req, "content-type");
|
||||||
|
|
||||||
|
if (*payload && ct && strcmp(ct, "application/x-www-form-urlencoded") == 0) {
|
||||||
|
xs *upl = xs_url_dec(*payload);
|
||||||
|
p_vars = xs_url_vars(upl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (*payload && ct && xs_startswith(ct, "multipart/form-data")) {
|
||||||
|
p_vars = xs_multipart_form_data(*payload, *p_size, ct);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
p_vars = xs_dict_new();
|
||||||
|
|
||||||
|
if (q_vars == NULL)
|
||||||
|
q_vars = xs_dict_new();
|
||||||
|
|
||||||
|
req = xs_dict_append(req, "q_vars", q_vars);
|
||||||
|
req = xs_dict_append(req, "p_vars", p_vars);
|
||||||
|
|
||||||
|
/* disconnect the payload from the buf variable */
|
||||||
|
buf = NULL;
|
||||||
|
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
/* any kind of error? notify and cleanup */
|
||||||
|
if (p_status != FCGI_REQUEST_COMPLETE) {
|
||||||
|
struct fcgi_end_request ereq = {0};
|
||||||
|
|
||||||
|
/* complete the connection */
|
||||||
|
ereq.app_status = 0;
|
||||||
|
ereq.protocol_status = p_status;
|
||||||
|
|
||||||
|
/* reuse header */
|
||||||
|
hdr.type = FCGI_ABORT_REQUEST;
|
||||||
|
hdr.content_len = htons(sizeof(ereq));
|
||||||
|
|
||||||
|
fwrite(&hdr, sizeof(hdr), 1, f);
|
||||||
|
fwrite(&ereq, sizeof(ereq), 1, f);
|
||||||
|
|
||||||
|
/* session closed */
|
||||||
|
*fcgi_id = -1;
|
||||||
|
|
||||||
|
/* request dict is not valid */
|
||||||
|
req = xs_free(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
xs_free(buf);
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void xs_fcgi_response(FILE *f, int status, xs_dict *headers, xs_str *body, int b_size, int fcgi_id)
|
||||||
|
/* writes an FCGI response */
|
||||||
|
{
|
||||||
|
struct fcgi_record_header hdr = {0};
|
||||||
|
struct fcgi_end_request ereq = {0};
|
||||||
|
xs *out = xs_str_new(NULL);
|
||||||
|
xs_dict *p;
|
||||||
|
xs_str *k;
|
||||||
|
xs_str *v;
|
||||||
|
|
||||||
|
/* no previous id? it's an error */
|
||||||
|
if (fcgi_id == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* create the headers */
|
||||||
|
{
|
||||||
|
xs *s1 = xs_fmt("status: %d\r\n", status);
|
||||||
|
out = xs_str_cat(out, s1);
|
||||||
|
}
|
||||||
|
|
||||||
|
p = headers;
|
||||||
|
while (xs_dict_iter(&p, &k, &v)) {
|
||||||
|
xs *s1 = xs_fmt("%s: %s\r\n", k, v);
|
||||||
|
out = xs_str_cat(out, s1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b_size > 0) {
|
||||||
|
xs *s1 = xs_fmt("content-length: %d\r\n", b_size);
|
||||||
|
out = xs_str_cat(out, s1);
|
||||||
|
}
|
||||||
|
|
||||||
|
out = xs_str_cat(out, "\r\n");
|
||||||
|
|
||||||
|
/* everything is text by now */
|
||||||
|
int size = strlen(out);
|
||||||
|
|
||||||
|
/* add the body */
|
||||||
|
if (body != NULL && b_size > 0)
|
||||||
|
out = xs_append_m(out, body, b_size);
|
||||||
|
|
||||||
|
/* now send all the STDOUT in packets */
|
||||||
|
hdr.version = FCGI_VERSION_1;
|
||||||
|
hdr.type = FCGI_STDOUT;
|
||||||
|
hdr.id = fcgi_id;
|
||||||
|
|
||||||
|
size += b_size;
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
while (offset < size) {
|
||||||
|
int sz = size - offset;
|
||||||
|
if (sz > 0xffff)
|
||||||
|
sz = 0xffff;
|
||||||
|
|
||||||
|
hdr.content_len = htons(sz);
|
||||||
|
|
||||||
|
fwrite(&hdr, sizeof(hdr), 1, f);
|
||||||
|
fwrite(out + offset, 1, sz, f);
|
||||||
|
|
||||||
|
offset += sz;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* final STDOUT packet with 0 size */
|
||||||
|
hdr.content_len = 0;
|
||||||
|
fwrite(&hdr, sizeof(hdr), 1, f);
|
||||||
|
|
||||||
|
/* complete the connection */
|
||||||
|
ereq.app_status = 0;
|
||||||
|
ereq.protocol_status = FCGI_REQUEST_COMPLETE;
|
||||||
|
|
||||||
|
hdr.type = FCGI_END_REQUEST;
|
||||||
|
hdr.content_len = htons(sizeof(ereq));
|
||||||
|
|
||||||
|
fwrite(&hdr, sizeof(hdr), 1, f);
|
||||||
|
fwrite(&ereq, sizeof(ereq), 1, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* XS_IMPLEMENTATION */
|
||||||
|
|
||||||
|
#endif /* XS_URL_H */
|
|
@ -1 +1 @@
|
||||||
/* a9cd3893c427bbcc478c5680245d435e415fd58a */
|
/* b109eea00ddc0765929e36ed1ca6f3f697262bb2 */
|
||||||
|
|
Loading…
Reference in a new issue