mirror of
https://github.com/lxsang/antd-fcgi-plugin.git
synced 2024-12-25 20:58:23 +01:00
Add basic FastCGI support to AntHTTP (responder only)
This commit is contained in:
parent
1d3db5ea5f
commit
14e6f28767
109
.gitignore
vendored
109
.gitignore
vendored
@ -1,49 +1,76 @@
|
||||
# http://www.gnu.org/software/automake
|
||||
# Prerequisites
|
||||
plugins
|
||||
build
|
||||
*._*
|
||||
*.d
|
||||
*.deb
|
||||
.vscode
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
Makefile.in
|
||||
/ar-lib
|
||||
/mdate-sh
|
||||
/py-compile
|
||||
/test-driver
|
||||
/ylwrap
|
||||
# Linker output
|
||||
*.ilk
|
||||
*.map
|
||||
*.exp
|
||||
|
||||
# http://www.gnu.org/software/autoconf
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
autom4te.cache
|
||||
/autoscan.log
|
||||
/autoscan-*.log
|
||||
/aclocal.m4
|
||||
/compile
|
||||
/config.guess
|
||||
/config.h.in
|
||||
/config.log
|
||||
/config.status
|
||||
/config.sub
|
||||
/configure
|
||||
/configure.scan
|
||||
/depcomp
|
||||
/install-sh
|
||||
/missing
|
||||
/stamp-h1
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# https://www.gnu.org/software/libtool/
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
/ltmain.sh
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# http://www.gnu.org/software/texinfo
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
|
||||
/texinfo.tex
|
||||
|
||||
# http://www.gnu.org/software/m4/
|
||||
|
||||
m4/libtool.m4
|
||||
m4/ltoptions.m4
|
||||
m4/ltsugar.m4
|
||||
m4/ltversion.m4
|
||||
m4/lt~obsolete.m4
|
||||
|
||||
# Generated Makefile
|
||||
# (meta build system like autotools,
|
||||
# can automatically generate from config.status script
|
||||
# (which is called by configure script))
|
||||
# Kernel Module Compile Results
|
||||
*.mod*
|
||||
*.cmd
|
||||
modules.order
|
||||
Module.symvers
|
||||
Makefile.old
|
||||
dkms.con
|
||||
.DS_Store
|
||||
.*
|
||||
*.cache
|
||||
Makefile
|
||||
antd
|
||||
compile
|
||||
config.guess
|
||||
depcomp
|
||||
install-sh
|
||||
missing
|
||||
libtool
|
||||
config.log
|
||||
config.status
|
||||
config.sub
|
||||
configure
|
||||
aclocal.m4
|
||||
ltmain.sh
|
||||
Makefile.in
|
||||
# others
|
||||
3rd/lua-5.3.4/lua
|
||||
3rd/lua-5.3.4/luac
|
||||
|
19
Makefile.am
Normal file
19
Makefile.am
Normal file
@ -0,0 +1,19 @@
|
||||
AUTOMAKE_OPTIONS = foreign
|
||||
|
||||
|
||||
|
||||
# check for system
|
||||
if LINUX
|
||||
AM_CPPFLAGS = -Wl,--no-as-needed
|
||||
else
|
||||
AM_CPPFLAGS = -Wl,-undefined,dynamic_lookup
|
||||
endif
|
||||
|
||||
|
||||
AM_CPPFLAGS += -W -Wall -g -std=c99 -fPIC
|
||||
|
||||
lib_LTLIBRARIES = fcgi.la
|
||||
fcgi_la_LDFLAGS = -module -avoid-version -shared
|
||||
fcgi_la_SOURCES = fcgi.c proto.c
|
||||
|
||||
EXTRA_DIST = README.md
|
69
configure.ac
Normal file
69
configure.ac
Normal file
@ -0,0 +1,69 @@
|
||||
# initialise autoconf and set up some basic information about the program we’re packaging
|
||||
AC_INIT([fcgi], [0.1.0a], [xsang.le@gmail.com])
|
||||
|
||||
# We’re going to use automake for this project
|
||||
# [subdir-objects] if needed
|
||||
AM_INIT_AUTOMAKE()
|
||||
|
||||
# dependencies
|
||||
# C compiler
|
||||
AC_PROG_CC
|
||||
# libtool for linking
|
||||
AC_PROG_LIBTOOL
|
||||
|
||||
# check for lib antd
|
||||
AC_CHECK_HEADER([antd/plugin.h],[
|
||||
has_antd=yes
|
||||
# check if the library exists
|
||||
],[
|
||||
AC_MSG_ERROR([Unable to find antd, please install it first])
|
||||
])
|
||||
AC_CHECK_LIB([antd],[antd_send],[],[
|
||||
if test "$has_antd" = "yes"; then
|
||||
AC_MSG_ERROR([Unable to find antd shared library, please install it first])
|
||||
fi
|
||||
])
|
||||
|
||||
|
||||
# check for pthread
|
||||
AC_CHECK_LIB([pthread], [pthread_create], [], [
|
||||
AC_MSG_ERROR([libpthread is not found])])
|
||||
|
||||
AC_DEFINE([_GNU_SOURCE], [1],[Use GNU source])
|
||||
# AC_CANONICAL_HOST is needed to access the 'host_os' variable
|
||||
|
||||
|
||||
AC_CANONICAL_HOST
|
||||
build_linux=no
|
||||
build_windows=no
|
||||
build_mac=no
|
||||
# Detect the target system
|
||||
case "${host_os}" in
|
||||
linux*)
|
||||
AC_DEFINE([LINUX], [1],[Linux system])
|
||||
build_linux=yes
|
||||
;;
|
||||
darwin*)
|
||||
build_mac=yes
|
||||
AC_DEFINE([MACOS], [1],[MacOS system])
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR(["OS $host_os is not supported"])
|
||||
;;
|
||||
esac
|
||||
# case for window:
|
||||
# cygwin*|mingw*)
|
||||
# build_windows=yes
|
||||
# ;;
|
||||
# Pass the conditionals to automake
|
||||
AM_CONDITIONAL([LINUX], [test "$build_linux" = "yes"])
|
||||
AM_CONDITIONAL([WINDOWS], [test "$build_windows" = "yes"])
|
||||
AM_CONDITIONAL([OSX], [test "$build_mac" = "yes"])
|
||||
|
||||
# find a file called Makefile.in, substitute placeholders
|
||||
# like @PACKAGE_VERSION@ with values like 0.1.0a,
|
||||
# and write the results to Makefile.
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
|
||||
# output the script:
|
||||
AC_OUTPUT
|
649
fcgi.c
Normal file
649
fcgi.c
Normal file
@ -0,0 +1,649 @@
|
||||
#define PLUGIN_IMPLEMENT 1
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <antd/plugin.h>
|
||||
#include <antd/scheduler.h>
|
||||
#include <antd/utils.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <time.h>
|
||||
#include <poll.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/time.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
//#include <libgen.h>
|
||||
#include "proto.h"
|
||||
|
||||
#define MAX_PATH_LEN 108
|
||||
#define MAX_BACK_LOG 1024
|
||||
#define PROCESS_TIMEOUT 200u
|
||||
|
||||
#define FCGI_CLIENT_REQUEST_SENT (0)
|
||||
#define FCGI_CLIENT_WAIT_FOR_RESPONSE_HEADER (-1)
|
||||
#define FCGI_CLIENT_WAIT_FOR_RESPONSE_DATA (-2)
|
||||
|
||||
typedef struct {
|
||||
// FastCGI application path
|
||||
char app_bin[MAX_PATH_LEN];
|
||||
// TCP host or Unix domain socket
|
||||
char address[MAX_PATH_LEN];
|
||||
// only for TCP socket
|
||||
int port;
|
||||
// server fd
|
||||
// int fd;
|
||||
// pid of the application process
|
||||
// pid_t pid;
|
||||
} fcgi_config_t;
|
||||
|
||||
static fcgi_config_t g_config;
|
||||
|
||||
static int read_config()
|
||||
{
|
||||
char * tmp;
|
||||
(void*) memset(g_config.app_bin, 0, MAX_PATH_LEN);
|
||||
(void*) memset(g_config.address, 0, MAX_PATH_LEN);
|
||||
g_config.port = -1;
|
||||
//g_config.fd = -1;
|
||||
//g_config.pid = -1;
|
||||
regmatch_t regex_matches[3];
|
||||
// read plugin configuration
|
||||
if(!__plugin__.config)
|
||||
{
|
||||
PLUGIN_PANIC("No plugin configuration found. Please specify it it server config file");
|
||||
return -1;
|
||||
}
|
||||
tmp = (char*) dvalue(__plugin__.config, "socket");
|
||||
if(!tmp)
|
||||
{
|
||||
PLUGIN_PANIC("No socket configuration found (socket)");
|
||||
return -1;
|
||||
}
|
||||
if(strncmp(tmp,"unix:", 5) == 0)
|
||||
{
|
||||
if(strlen(tmp + 5) > MAX_PATH_LEN - 1)
|
||||
{
|
||||
PLUGIN_PANIC("socket configuration is too long: %s", tmp);
|
||||
return -1;
|
||||
}
|
||||
snprintf(g_config.address, MAX_PATH_LEN,"%s", tmp+5);
|
||||
LOG("Found Unix domain socket configuration: %s", g_config.address);
|
||||
}
|
||||
else if(regex_match("^([a-zA-Z0-9\\-_\\.]+):([0-9]+)$", tmp,3, regex_matches))
|
||||
{
|
||||
if(regex_matches[1].rm_eo - regex_matches[1].rm_so > MAX_PATH_LEN - 1)
|
||||
{
|
||||
PLUGIN_PANIC("socket configuration is too long: %s", tmp);
|
||||
return -1;
|
||||
}
|
||||
memcpy(g_config.address, tmp + regex_matches[2].rm_so, regex_matches[2].rm_eo - regex_matches[2].rm_so);
|
||||
g_config.port = atoi(g_config.address);
|
||||
(void*) memset(g_config.address, 0, MAX_PATH_LEN);
|
||||
memcpy(g_config.address, tmp + regex_matches[1].rm_so, regex_matches[1].rm_eo - regex_matches[1].rm_so);
|
||||
LOG("Found TCP socket configuration: %s:%d", g_config.address, g_config.port);
|
||||
}
|
||||
else
|
||||
{
|
||||
PLUGIN_PANIC("Unknown socket configuration: %s", tmp);
|
||||
return -1;
|
||||
}
|
||||
tmp = (char*) dvalue(__plugin__.config, "bin");
|
||||
if(!tmp)
|
||||
{
|
||||
PLUGIN_PANIC("No FastCGI application configuration found (bin)");
|
||||
return -1;
|
||||
}
|
||||
if(strlen(tmp) > MAX_PATH_LEN - 1)
|
||||
{
|
||||
PLUGIN_PANIC("Bin applicqtion configuration is too long: %s", tmp);
|
||||
return -1;
|
||||
}
|
||||
snprintf(g_config.app_bin, MAX_PATH_LEN,"%s", tmp);
|
||||
LOG("Binary application configuration: %s", g_config.app_bin);
|
||||
return 0;
|
||||
}
|
||||
static int open_un_socket()
|
||||
{
|
||||
struct sockaddr_un address;
|
||||
address.sun_family = AF_UNIX;
|
||||
// create the socket
|
||||
(void)strncpy(address.sun_path, g_config.address, sizeof(address.sun_path));
|
||||
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd == -1)
|
||||
{
|
||||
ERROR("Unable to create Unix domain socket: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if(connect(fd, (struct sockaddr*)(&address), sizeof(address)) == -1)
|
||||
{
|
||||
ERROR( "Unable to connect to socket '%s': %s", address.sun_path, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
LOG("Connected to FastCGI server at %s: %d", g_config.address, fd);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int open_tcp_socket()
|
||||
{
|
||||
struct sockaddr_in servaddr;
|
||||
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (fd == -1)
|
||||
{
|
||||
PLUGIN_PANIC("Cannot create TCP socket %s:d: %s",g_config.address, g_config.port, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
bzero(&servaddr, sizeof(servaddr));
|
||||
|
||||
// assign IP, PORT
|
||||
servaddr.sin_family = AF_INET;
|
||||
servaddr.sin_addr.s_addr = inet_addr(g_config.address);
|
||||
servaddr.sin_port = htons(g_config.port);
|
||||
|
||||
// connect the client socket to server socket
|
||||
if (connect(fd, (struct sockaddr*)&servaddr, sizeof(servaddr))!= 0) {
|
||||
ERROR( "Unable to connect to socket '%s:%d': %s", g_config.address, g_config.port, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
LOG("Connected to server: %s:%d at [%d]", g_config.address, g_config.port, fd);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int open_socket()
|
||||
{
|
||||
if(g_config.port != -1)
|
||||
{
|
||||
return open_tcp_socket();
|
||||
}
|
||||
else
|
||||
{
|
||||
return open_un_socket();
|
||||
}
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
use_raw_body();
|
||||
if(read_config() != 0)
|
||||
return;
|
||||
// create the socket
|
||||
//if(create_socket() != 0)
|
||||
// return;
|
||||
LOG("FastCGI init successful");
|
||||
|
||||
}
|
||||
void destroy()
|
||||
{
|
||||
/*if(g_config.fd)
|
||||
{
|
||||
LOG("Close socket: %d", g_config.fd);
|
||||
close(g_config.fd);
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
char* read_line(char** buff, int* size)
|
||||
{
|
||||
int i = 0;
|
||||
while(i <= *size-1 && (*buff)[i] != '\n') i++;
|
||||
if(i > 0 && i <= *size - 1)
|
||||
(*buff)[i] = '\0';
|
||||
char* line = *buff;
|
||||
*size = *size - i - 1;
|
||||
*buff = *buff + i+1;
|
||||
return line;
|
||||
}
|
||||
|
||||
static int read_header(antd_client_t* cl, antd_request_t* rq)
|
||||
{
|
||||
FCGI_Header header;
|
||||
antd_response_header_t rhd;
|
||||
rhd.header = dict();
|
||||
rhd.cookie = list_init();
|
||||
rhd.status = 200;
|
||||
char *k;
|
||||
char *v;
|
||||
int len, ret;
|
||||
regmatch_t matches[3];
|
||||
uint8_t * payload;
|
||||
char* line;
|
||||
char* ptr;
|
||||
while(cl->state == FCGI_CLIENT_WAIT_FOR_RESPONSE_HEADER)
|
||||
{
|
||||
ret = fcgi_read_header(cl,&header);
|
||||
if(ret < 0)
|
||||
{
|
||||
(void)fcgi_abort_request(cl, cl->sock);
|
||||
LOG("fcgi_read_header() on %d: %s", cl->sock, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
payload = fcgi_read_payload(cl, &header, &ret);
|
||||
switch(header.type)
|
||||
{
|
||||
case FCGI_STDOUT:
|
||||
// write data to the other side
|
||||
if(payload && ret > 0)
|
||||
{
|
||||
ptr = (char*)payload;
|
||||
while(ret > 0)
|
||||
{
|
||||
line = read_line(&ptr, &ret);
|
||||
trim(line, '\r');
|
||||
if(strlen(line) == 0)
|
||||
{
|
||||
cl->state = FCGI_CLIENT_WAIT_FOR_RESPONSE_DATA;
|
||||
// write out header and the rest of the data
|
||||
antd_send_header(rq->client, &rhd);
|
||||
if(ret > 0)
|
||||
{
|
||||
if(antd_send(rq->client,ptr, ret) != ret)
|
||||
{
|
||||
(void)fcgi_abort_request(cl, cl->sock);
|
||||
ERROR("Error atnd_send(): %s", strerror(errno));
|
||||
free(payload);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(ret < 0) break;
|
||||
if (regex_match("\\s*Status\\s*:\\s+([0-9]{3})\\s+([a-zA-Z0-9 ]*)", line, 3, matches))
|
||||
{
|
||||
len = matches[1].rm_eo - matches[1].rm_so;
|
||||
k = (char *)malloc(len + 1);
|
||||
memset(k, 0, len + 1);
|
||||
memcpy(k, line + matches[1].rm_so, len);
|
||||
rhd.status = atoi(k);
|
||||
LOG("Response status %d", rhd.status);
|
||||
free(k);
|
||||
}
|
||||
else if (regex_match("^([a-zA-Z0-9\\-]+)\\s*:\\s*(.*)$", line, 3, matches))
|
||||
{
|
||||
len = matches[1].rm_eo - matches[1].rm_so;
|
||||
k = (char *)malloc(len + 1);
|
||||
memset(k, 0, len + 1);
|
||||
memcpy(k, line + matches[1].rm_so, len);
|
||||
verify_header(k);
|
||||
len = matches[2].rm_eo - matches[2].rm_so;
|
||||
v = (char *)malloc(len + 1);
|
||||
memset(v, 0, len + 1);
|
||||
memcpy(v, line + matches[2].rm_so, len);
|
||||
LOG("Header [%s] -> [%s]", k, v);
|
||||
if (strcmp(k, "Set-Cookie") == 0)
|
||||
{
|
||||
list_put_ptr(&rhd.cookie, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
dput(rhd.header, k, v);
|
||||
}
|
||||
free(k);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("Ignore invalid header: %s", line);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FCGI_STDERR:
|
||||
if(payload && ret > 0)
|
||||
{
|
||||
ERROR("%s", (char*) payload);
|
||||
}
|
||||
break;
|
||||
case FCGI_END_REQUEST:
|
||||
LOG("End request received, this should not happen %d", cl->sock);
|
||||
// FCGI_EndRequestBody* body = (FCGI_EndRequestBody*) payload;
|
||||
if(payload) free(payload);
|
||||
return -1;
|
||||
default:
|
||||
LOG("Unsupported record type: 0x%02x", header.type);
|
||||
break;
|
||||
}
|
||||
if(payload) free(payload);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_data(antd_client_t* cl, antd_request_t* rq)
|
||||
{
|
||||
FCGI_Header header;
|
||||
int ret = fcgi_read_header(cl,&header);
|
||||
if(ret < 0)
|
||||
{
|
||||
(void)fcgi_abort_request(cl, cl->sock);
|
||||
LOG("fcgi_read_header() on %d: %s", cl->sock, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
uint8_t * payload = fcgi_read_payload(cl, &header, &ret);
|
||||
switch(header.type)
|
||||
{
|
||||
case FCGI_STDOUT:
|
||||
// write data to the other side
|
||||
if(payload && ret > 0)
|
||||
{
|
||||
if(antd_send(rq->client,payload, ret) != ret)
|
||||
{
|
||||
(void)fcgi_abort_request(cl, cl->sock);
|
||||
ERROR("Error atnd_send(): %s", strerror(errno));
|
||||
free(payload);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FCGI_STDERR:
|
||||
if(payload && ret > 0)
|
||||
{
|
||||
ERROR("%s", (char*) payload);
|
||||
}
|
||||
break;
|
||||
case FCGI_END_REQUEST:
|
||||
LOG("End request received, close connection %d", cl->sock);
|
||||
if(payload) free(payload);
|
||||
return -1;
|
||||
default:
|
||||
LOG("Unsupported record type: 0x%02x", header.type);
|
||||
break;
|
||||
}
|
||||
if(payload) free(payload);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *process(void *data)
|
||||
{
|
||||
antd_request_t *rq = (antd_request_t *)data;
|
||||
antd_client_t* cl = (antd_client_t* ) dvalue(rq->request, "FCGI_CL_DATA");
|
||||
struct pollfd pfds[2];
|
||||
pfds[0].fd = rq->client->sock;
|
||||
pfds[0].events = POLLIN;
|
||||
if(rq->client->ssl)
|
||||
{
|
||||
pfds[0].events = POLLIN | POLLOUT;
|
||||
}
|
||||
pfds[1].fd = cl->sock;
|
||||
pfds[1].events = POLLIN;
|
||||
if(cl->state == FCGI_CLIENT_REQUEST_SENT)
|
||||
{
|
||||
(void)fcgi_send_stdin(cl, cl->sock, NULL, 0, 0);
|
||||
if (ws_enable(rq->request))
|
||||
{
|
||||
cl->state = FCGI_CLIENT_WAIT_FOR_RESPONSE_DATA;
|
||||
}
|
||||
else
|
||||
{
|
||||
cl->state = FCGI_CLIENT_WAIT_FOR_RESPONSE_HEADER;
|
||||
}
|
||||
}
|
||||
|
||||
int rc = poll(pfds, 2, PROCESS_TIMEOUT);
|
||||
antd_task_t* task;
|
||||
uint8_t buff[BUFFLEN];
|
||||
int ret;
|
||||
switch (rc)
|
||||
{
|
||||
case -1:
|
||||
ERROR("Error on poll(): %s", strerror(errno));
|
||||
(void)fcgi_abort_request(cl, cl->sock);
|
||||
antd_close(cl);
|
||||
dput(rq->request, "FCGI_CL_DATA", NULL);
|
||||
return antd_create_task(NULL, data, NULL, rq->client->last_io);
|
||||
case 0:
|
||||
// time out
|
||||
task = antd_create_task(process, (void *)rq, NULL, time(NULL));
|
||||
antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE);
|
||||
antd_task_bind_event(task, cl->sock, 0, TASK_EVT_ON_READABLE);
|
||||
return task;
|
||||
// we have data
|
||||
default:
|
||||
// If data is on webserver
|
||||
if ((pfds[0].revents & POLLIN) || (rq->client->ssl && (pfds[0].revents & POLLOUT)) )
|
||||
{
|
||||
while((ret = antd_recv_upto(rq->client,buff, BUFFLEN)) > 0)
|
||||
{
|
||||
// write data to the application stdin
|
||||
if(fcgi_send_stdin(cl, cl->sock,buff, ret, (ret % 8 == 0)? 0 : 8 - (ret % 8) ) != 0)
|
||||
{
|
||||
ERROR("Error on fcgi_send_stdin(): %s", strerror(errno));
|
||||
(void)fcgi_abort_request(cl, cl->sock);
|
||||
antd_close(cl);
|
||||
dput(rq->request, "FCGI_CL_DATA", NULL);
|
||||
return antd_create_task(NULL, data, NULL, rq->client->last_io);
|
||||
}
|
||||
if(cl->state > 0)
|
||||
cl->state -= ret;
|
||||
LOG("sending %s: %d", buff, cl->state);
|
||||
}
|
||||
if(ret < 0)
|
||||
{
|
||||
LOG("antd_recv_upto() on %d: %s",rq->client->sock, strerror(errno));
|
||||
(void)fcgi_abort_request(cl, cl->sock);
|
||||
antd_close(cl);
|
||||
dput(rq->request, "FCGI_CL_DATA", NULL);
|
||||
return antd_create_task(NULL, data, NULL, rq->client->last_io);
|
||||
}
|
||||
}
|
||||
else if(pfds[0].revents &(POLLERR | POLLHUP))
|
||||
{
|
||||
ERROR("POLLERR or POLLHUP received on %d", rq->client->sock);
|
||||
(void)fcgi_abort_request(cl, cl->sock);
|
||||
antd_close(cl);
|
||||
dput(rq->request, "FCGI_CL_DATA", NULL);
|
||||
return antd_create_task(NULL, data, NULL, rq->client->last_io);
|
||||
}
|
||||
if(pfds[1].revents & POLLIN)
|
||||
{
|
||||
if(cl->state == FCGI_CLIENT_WAIT_FOR_RESPONSE_HEADER)
|
||||
{
|
||||
if(read_header(cl, rq) != 0)
|
||||
{
|
||||
antd_close(cl);
|
||||
dput(rq->request, "FCGI_CL_DATA", NULL);
|
||||
return antd_create_task(NULL, data, NULL, rq->client->last_io);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(read_data(cl,rq) != 0)
|
||||
{
|
||||
antd_close(cl);
|
||||
dput(rq->request, "FCGI_CL_DATA", NULL);
|
||||
return antd_create_task(NULL, data, NULL, rq->client->last_io);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(pfds[1].revents &(POLLERR | POLLHUP))
|
||||
{
|
||||
ERROR("POLLERR or POLLHUP received on %d", cl->sock);
|
||||
//(void)fcgi_abort_request(cl, cl->sock);
|
||||
antd_close(cl);
|
||||
dput(rq->request, "FCGI_CL_DATA", NULL);
|
||||
return antd_create_task(NULL, data, NULL, rq->client->last_io);
|
||||
}
|
||||
task = antd_create_task(process, (void *)rq, NULL, time(NULL));
|
||||
antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE);
|
||||
antd_task_bind_event(task, cl->sock, 0, TASK_EVT_ON_READABLE);
|
||||
return task;
|
||||
}
|
||||
}
|
||||
|
||||
static int send_request(antd_client_t *cl, antd_request_t* rq)
|
||||
{
|
||||
int ret = 0;
|
||||
char *tmp = NULL;
|
||||
char *sub = NULL;
|
||||
char *root;
|
||||
dictionary_t request = (dictionary_t)rq->request;
|
||||
dictionary_t header = (dictionary_t)dvalue(rq->request, "REQUEST_HEADER");
|
||||
ret += fcgi_begin_request(cl, cl->sock, FCGI_RESPONDER, 0);
|
||||
//ret += fcgi_send_param(cl, cl->sock, "", "");
|
||||
|
||||
ret += fcgi_send_param(cl, cl->sock, "GATEWAY_INTERFACE", "CGI/1.1");
|
||||
ret += fcgi_send_param(cl, cl->sock, "SERVER_SOFTWARE", SERVER_NAME);
|
||||
root = (char *)dvalue(request, "SERVER_WWW_ROOT");
|
||||
tmp = (char *)dvalue(request, "REQUEST_QUERY");
|
||||
|
||||
if (!tmp)
|
||||
{
|
||||
ret += fcgi_send_param(cl, cl->sock, "QUERY_STRING", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
ret += fcgi_send_param(cl, cl->sock, "REQUEST_URI", tmp);
|
||||
sub = strchr(tmp, '?');
|
||||
if (sub)
|
||||
{
|
||||
sub++;
|
||||
ret += fcgi_send_param(cl, cl->sock, "QUERY_STRING", sub);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret += fcgi_send_param(cl, cl->sock, "QUERY_STRING", "");
|
||||
}
|
||||
}
|
||||
tmp = (char *)dvalue(request, "METHOD");
|
||||
if (tmp)
|
||||
{
|
||||
ret += fcgi_send_param(cl, cl->sock, "REQUEST_METHOD", tmp);
|
||||
}
|
||||
tmp = (char *)dvalue(header, "Content-Type");
|
||||
if (tmp)
|
||||
{
|
||||
ret += fcgi_send_param(cl, cl->sock, "CONTENT_TYPE", tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret += fcgi_send_param(cl, cl->sock, "CONTENT_TYPE", "");
|
||||
}
|
||||
tmp = (char *)dvalue(header, "Content-Length");
|
||||
if (tmp)
|
||||
{
|
||||
cl->state = atoi(tmp);
|
||||
ret += fcgi_send_param(cl, cl->sock, "CONTENT_LENGTH", tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret += fcgi_send_param(cl, cl->sock, "CONTENT_LENGTH", "");
|
||||
}
|
||||
ret += fcgi_send_param(cl, cl->sock, "DOCUMENT_ROOT", root);
|
||||
tmp = (char *)dvalue(request, "REQUEST_PATH");
|
||||
if (tmp)
|
||||
{
|
||||
sub = tmp;
|
||||
while (*sub == '/')
|
||||
sub++;
|
||||
if (sub)
|
||||
{
|
||||
ret += fcgi_send_param(cl, cl->sock, "PATH_INFO", sub);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret += fcgi_send_param(cl, cl->sock, "PATH_INFO", "");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret += fcgi_send_param(cl, cl->sock, "PATH_INFO", "");
|
||||
}
|
||||
tmp = (char *)dvalue(request, "REMOTE_ADDR");
|
||||
if(tmp)
|
||||
{
|
||||
ret += fcgi_send_param(cl, cl->sock, "REMOTE_ADDR", tmp);
|
||||
ret += fcgi_send_param(cl, cl->sock, "REMOTE_HOST", tmp);
|
||||
|
||||
}
|
||||
ret += fcgi_send_param(cl, cl->sock, "SERVER_NAME", SERVER_NAME);
|
||||
ret += fcgi_send_param(cl, cl->sock, "SERVER_PORT", (char *)dvalue(request, "SERVER_PORT"));
|
||||
ret += fcgi_send_param(cl, cl->sock, "SERVER_PROTOCOL", "HTTP/1.1");
|
||||
// add remaining header to the vars
|
||||
chain_t it;
|
||||
for_each_assoc(it, header)
|
||||
{
|
||||
tmp = __s("HTTP_%s", it->key);
|
||||
char *s = tmp;
|
||||
while (*s)
|
||||
{
|
||||
if (*s == '-')
|
||||
*s = '_';
|
||||
else if (*s != '_')
|
||||
*s = toupper((char)*s);
|
||||
s++;
|
||||
}
|
||||
ret += fcgi_send_param(cl, cl->sock, tmp, (char *)it->value);
|
||||
free(tmp);
|
||||
}
|
||||
tmp = (char *)dvalue(request, "RESOURCE_PATH");
|
||||
if (tmp)
|
||||
{
|
||||
ret += fcgi_send_param(cl, cl->sock, "SCRIPT_NAME", basename(tmp));
|
||||
tmp = __s("%s/%s", root, tmp);
|
||||
ret += fcgi_send_param(cl, cl->sock, "SCRIPT_FILENAME", tmp);
|
||||
ret += fcgi_send_param(cl, cl->sock, "PATH_TRANSLATED", tmp);
|
||||
free(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret += fcgi_send_param(cl, cl->sock, "SCRIPT_FILENAME", "");
|
||||
ret += fcgi_send_param(cl, cl->sock, "PATH_TRANSLATED", "");
|
||||
ret += fcgi_send_param(cl, cl->sock, "SCRIPT_NAME", "");
|
||||
}
|
||||
// redirect status for php
|
||||
ret += fcgi_send_param(cl, cl->sock, "REDIRECT_STATUS", "200");
|
||||
ret += fcgi_send_param(cl, cl->sock, "", "");
|
||||
return ret;
|
||||
}
|
||||
|
||||
void* handle(void* data)
|
||||
{
|
||||
antd_request_t *rq = (antd_request_t *)data;
|
||||
// connect to socket
|
||||
int fd = open_socket();
|
||||
if(fd < 0)
|
||||
{
|
||||
antd_error(rq->client, 503, "Service unavailable");
|
||||
return antd_create_task(NULL, data, NULL, rq->client->last_io);
|
||||
}
|
||||
set_nonblock(fd);
|
||||
|
||||
// write all header to fastCGI server via params
|
||||
antd_client_t* cl = (antd_client_t*) malloc(sizeof(antd_client_t));
|
||||
(void)memset(cl, 0, sizeof(antd_client_t));
|
||||
cl->sock = fd;
|
||||
time(&cl->last_io);
|
||||
cl->ssl = NULL;
|
||||
// state is used to store content lenth of the current request
|
||||
cl->state = FCGI_CLIENT_REQUEST_SENT;
|
||||
cl->z_status = 0;
|
||||
cl->z_level = ANTD_CNONE;
|
||||
cl->zstream = NULL;
|
||||
rq->client->z_level = ANTD_CNONE;
|
||||
|
||||
// start the request
|
||||
|
||||
if(send_request(cl,rq) != 0)
|
||||
{
|
||||
ERROR("Unable to send request to application: %d", fd);
|
||||
antd_error(rq->client, 500, "Internal server error");
|
||||
(void)fcgi_abort_request(cl, cl->sock);
|
||||
antd_close(cl);
|
||||
return antd_create_task(NULL, data, NULL, rq->client->last_io);
|
||||
}
|
||||
|
||||
dput(rq->request, "FCGI_CL_DATA", cl);
|
||||
|
||||
antd_task_t* task = antd_create_task(process, (void *)rq, NULL, time(NULL));
|
||||
//antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE);
|
||||
//antd_task_bind_event(task, fd, 0, TASK_EVT_ON_READABLE);
|
||||
return task;
|
||||
}
|
279
proto.c
Normal file
279
proto.c
Normal file
@ -0,0 +1,279 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "proto.h"
|
||||
|
||||
#define PARAMS_LENGTH_11 ((0<<0) | (0<<1))
|
||||
#define PARAMS_LENGTH_14 ((0<<0) | (1<<1))
|
||||
#define PARAMS_LENGTH_41 ((1<<0) | (0<<1))
|
||||
#define PARAMS_LENGTH_44 ((1<<0) | (1<<1))
|
||||
|
||||
typedef struct {
|
||||
unsigned char nameLengthB0; /* nameLengthB0 >> 7 == 0 */
|
||||
unsigned char valueLengthB0; /* valueLengthB0 >> 7 == 0 */
|
||||
char value[0];
|
||||
} FCGI_NameValuePair11;
|
||||
|
||||
typedef struct {
|
||||
unsigned char nameLengthB0; /* nameLengthB0 >> 7 == 0 */
|
||||
unsigned char valueLengthB3; /* valueLengthB3 >> 7 == 1 */
|
||||
unsigned char valueLengthB2;
|
||||
unsigned char valueLengthB1;
|
||||
unsigned char valueLengthB0;
|
||||
char value[0];
|
||||
} FCGI_NameValuePair14;
|
||||
|
||||
typedef struct {
|
||||
unsigned char nameLengthB3; /* nameLengthB3 >> 7 == 1 */
|
||||
unsigned char nameLengthB2;
|
||||
unsigned char nameLengthB1;
|
||||
unsigned char nameLengthB0;
|
||||
unsigned char valueLengthB0; /* valueLengthB0 >> 7 == 0 */
|
||||
char value[0];
|
||||
} FCGI_NameValuePair41;
|
||||
|
||||
typedef struct {
|
||||
unsigned char nameLengthB3; /* nameLengthB3 >> 7 == 1 */
|
||||
unsigned char nameLengthB2;
|
||||
unsigned char nameLengthB1;
|
||||
unsigned char nameLengthB0;
|
||||
unsigned char valueLengthB3; /* valueLengthB3 >> 7 == 1 */
|
||||
unsigned char valueLengthB2;
|
||||
unsigned char valueLengthB1;
|
||||
unsigned char valueLengthB0;
|
||||
char value[0];
|
||||
} FCGI_NameValuePair44;
|
||||
|
||||
typedef union {
|
||||
FCGI_NameValuePair11 d11;
|
||||
FCGI_NameValuePair14 d14;
|
||||
FCGI_NameValuePair41 d41;
|
||||
FCGI_NameValuePair44 d44;
|
||||
} FCGI_Params_Body;
|
||||
|
||||
static int fcgi_send_record(antd_client_t* cl, FCGI_Header* header, uint8_t* buff, size_t len)
|
||||
{
|
||||
if(!header)
|
||||
{
|
||||
ERROR("Record header should not empty");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// send the header
|
||||
int ret = antd_send(cl, (uint8_t*)header, sizeof(FCGI_Header));
|
||||
if(ret != sizeof(FCGI_Header))
|
||||
{
|
||||
ERROR("fcgi_send_record: Unable to send record header, only %d of %d bytes sent: %s", ret, sizeof(FCGI_Header), strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if(!buff)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// send the data
|
||||
ret = antd_send(cl, (uint8_t*)buff, len);
|
||||
if(ret != (int)len)
|
||||
{
|
||||
ERROR("fcgi_send_record: Unable to send record data, only %d of %d bytes sent", ret, len);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fcgi_begin_request(antd_client_t* cl, uint16_t id, uint16_t role, uint8_t flags)
|
||||
{
|
||||
FCGI_BeginRequestRecord record;
|
||||
record.header.version = FCGI_VERSION_1;
|
||||
record.header.type = FCGI_BEGIN_REQUEST;
|
||||
record.header.requestIdB1 = id >> 8;
|
||||
record.header.requestIdB0 = id & 0xFF;
|
||||
record.header.contentLengthB1 = 0;
|
||||
record.header.contentLengthB0 = 8;
|
||||
record.header.paddingLength = 0;
|
||||
|
||||
record.body.roleB1 = role >> 8;
|
||||
record.body.roleB0 = role & 0xFF;
|
||||
record.body.flags = flags;
|
||||
|
||||
int ret = antd_send(cl, (uint8_t*)&record, sizeof(record));
|
||||
if(ret != sizeof(record))
|
||||
{
|
||||
ERROR("fcgi_begin_request: Unable to send record data, only %d of %d bytes sent", ret, sizeof(record));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fcgi_abort_request(antd_client_t* cl, uint16_t id)
|
||||
{
|
||||
FCGI_Header header;
|
||||
header.version = FCGI_VERSION_1;
|
||||
header.type = FCGI_BEGIN_REQUEST;
|
||||
header.requestIdB1 = id >> 8;
|
||||
header.requestIdB0 = id & 0xFF;
|
||||
header.contentLengthB1 = 0;
|
||||
header.contentLengthB0 = 0;
|
||||
header.paddingLength = 0;
|
||||
int ret = antd_send(cl, (uint8_t*)&header, sizeof(header));
|
||||
if(ret != sizeof(header))
|
||||
{
|
||||
ERROR("fcgi_abort_request: Unable to send record data, only %d of %d bytes sent", ret, sizeof(header));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fcgi_send_param(antd_client_t* cl, int id, const char* key, const char* value)
|
||||
{
|
||||
size_t k_length = strlen(key);
|
||||
size_t v_length = strlen(value);
|
||||
//LOG("sending [%s] -> [%s]", key, value);
|
||||
FCGI_Params_Body* body = NULL;
|
||||
uint8_t* buff = NULL;
|
||||
size_t clen = k_length + v_length;
|
||||
if(clen > 0)
|
||||
{
|
||||
size_t max_buff_len = sizeof(FCGI_Params_Body) + k_length + v_length + 8;
|
||||
|
||||
buff = (uint8_t*)malloc(max_buff_len);
|
||||
if(!buff)
|
||||
{
|
||||
ERROR("Unable to allocate PARAMS record buffer memory: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
body = (FCGI_Params_Body*) buff;
|
||||
|
||||
FCGI_Header header;
|
||||
header.version = FCGI_VERSION_1;
|
||||
header.type = FCGI_PARAMS;
|
||||
header.requestIdB1 = id >> 8;
|
||||
header.requestIdB0 = id & 0xFF;
|
||||
|
||||
if(clen > 0)
|
||||
{
|
||||
uint8_t encoding_type = (((k_length & 0xFF) >> 7) << 0) | (((v_length & 0xFF)>>7) << 1);
|
||||
switch(encoding_type)
|
||||
{
|
||||
case PARAMS_LENGTH_11:
|
||||
body->d11.nameLengthB0 = k_length;
|
||||
body->d11.valueLengthB0 = v_length;
|
||||
memcpy(body->d11.value, key, k_length);
|
||||
memcpy(body->d11.value+k_length, value, v_length);
|
||||
clen += 2;
|
||||
break;
|
||||
case PARAMS_LENGTH_14:
|
||||
body->d14.nameLengthB0 = k_length;
|
||||
body->d14.valueLengthB3 = (v_length >> 24) | 0x80;
|
||||
body->d14.valueLengthB2 = (v_length >> 16) & 0xFF;
|
||||
body->d14.valueLengthB1 = (v_length >> 8) & 0xFF;
|
||||
body->d14.valueLengthB0 = v_length & 0xFF;
|
||||
|
||||
memcpy(body->d14.value, key, k_length);
|
||||
memcpy(body->d14.value+k_length, value, v_length);
|
||||
clen += 5;
|
||||
break;
|
||||
case PARAMS_LENGTH_41:
|
||||
body->d41.valueLengthB0 = v_length;
|
||||
body->d41.nameLengthB3 = (k_length >> 24) | 0x80;
|
||||
body->d41.nameLengthB2 = (k_length >> 16) & 0xFF;
|
||||
body->d41.nameLengthB1 = (k_length >> 8) & 0xFF;
|
||||
body->d41.nameLengthB0 = k_length & 0xFF;
|
||||
memcpy(body->d41.value, key, k_length);
|
||||
memcpy(body->d41.value+k_length, value, v_length);
|
||||
clen += 5;
|
||||
break;
|
||||
case PARAMS_LENGTH_44:
|
||||
body->d44.nameLengthB3 = (k_length >> 24) | 0x80;
|
||||
body->d44.nameLengthB2 = (k_length >> 16) & 0xFF;
|
||||
body->d44.nameLengthB1 = (k_length >> 8) & 0xFF;
|
||||
body->d44.nameLengthB0 = k_length & 0xFF;
|
||||
body->d44.valueLengthB3 = (v_length >> 24) | 0x80;
|
||||
body->d44.valueLengthB2 = (v_length >> 16) & 0xFF;
|
||||
body->d44.valueLengthB1 = (v_length >> 8) & 0xFF;
|
||||
body->d44.valueLengthB0 = v_length & 0xFF;
|
||||
memcpy(body->d44.value, key, k_length);
|
||||
memcpy(body->d44.value+k_length, value, v_length);
|
||||
clen += 8;
|
||||
break;
|
||||
default:
|
||||
// this should never happends
|
||||
free(buff);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
header.contentLengthB1 = clen >> 8;
|
||||
header.contentLengthB0 = clen & 0xFF;
|
||||
header.paddingLength = (clen % 8 == 0)? 0 : 8 - (clen % 8);
|
||||
|
||||
// send the record
|
||||
int ret = fcgi_send_record(cl, &header, buff, clen + header.paddingLength);
|
||||
if(buff)
|
||||
free(buff);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int fcgi_send_stdin(antd_client_t* cl, int id, uint8_t* padded_data, size_t len, uint8_t paddlen)
|
||||
{
|
||||
FCGI_Header header;
|
||||
header.version = FCGI_VERSION_1;
|
||||
header.type = FCGI_STDIN;
|
||||
header.requestIdB1 = id >> 8;
|
||||
header.requestIdB0 = id & 0xFF;
|
||||
header.contentLengthB1 = len >> 8;
|
||||
header.contentLengthB0 = len & 0xFF;
|
||||
header.paddingLength = paddlen;
|
||||
// send the record
|
||||
return fcgi_send_record(cl, &header, padded_data, len + paddlen);
|
||||
}
|
||||
|
||||
|
||||
int fcgi_read_header(antd_client_t* cl, FCGI_Header* header)
|
||||
{
|
||||
uint8_t* buff = (uint8_t*) header;
|
||||
int ret = antd_recv(cl, buff, sizeof(FCGI_Header));
|
||||
if(ret != sizeof(FCGI_Header))
|
||||
{
|
||||
ERROR("Unable to read header: received %d bytes out of %d bytes", ret, sizeof(FCGI_Header));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fcgi_read_data(antd_client_t* cl, FCGI_Header* header, uint8_t* buffer)
|
||||
{
|
||||
int len = ((header->contentLengthB1 << 8) | header->contentLengthB0) + header->paddingLength;
|
||||
int ret = antd_recv(cl, buffer, len);
|
||||
if(ret != len)
|
||||
{
|
||||
ERROR("Unable to read record body: received %d bytes out of %d bytes", ret, len);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t* fcgi_read_payload(antd_client_t* cl, FCGI_Header* header, int* size)
|
||||
{
|
||||
int len = ((header->contentLengthB1 << 8) | header->contentLengthB0) + header->paddingLength;
|
||||
uint8_t* buff = (uint8_t*) malloc(len + 1);
|
||||
if(!buff)
|
||||
{
|
||||
ERROR("Unable to allocate buffer of size %d", len);
|
||||
return NULL;
|
||||
}
|
||||
int ret = antd_recv(cl, buff, len);
|
||||
if(ret != len)
|
||||
{
|
||||
ERROR("Unable to read record body: received %d bytes out of %d bytes", ret, len);
|
||||
free(buff);
|
||||
return NULL;
|
||||
}
|
||||
*size = len - header->paddingLength;
|
||||
buff[*size] = '\0';
|
||||
return buff;
|
||||
}
|
165
proto.h
Normal file
165
proto.h
Normal file
@ -0,0 +1,165 @@
|
||||
#ifndef PROTO_H
|
||||
#define PROTO_H
|
||||
#include <unistd.h>
|
||||
#include <antd/plugin.h>
|
||||
/*
|
||||
* Listening socket file number
|
||||
*/
|
||||
#define FCGI_LISTENSOCK_FILENO 0
|
||||
|
||||
typedef struct {
|
||||
unsigned char version;
|
||||
unsigned char type;
|
||||
unsigned char requestIdB1;
|
||||
unsigned char requestIdB0;
|
||||
unsigned char contentLengthB1;
|
||||
unsigned char contentLengthB0;
|
||||
unsigned char paddingLength;
|
||||
unsigned char reserved;
|
||||
} FCGI_Header;
|
||||
|
||||
/*
|
||||
* Number of bytes in a FCGI_Header. Future versions of the protocol
|
||||
* will not reduce this number.
|
||||
*/
|
||||
#define FCGI_HEADER_LEN 8
|
||||
|
||||
/*
|
||||
* Value for version component of FCGI_Header
|
||||
*/
|
||||
#define FCGI_VERSION_1 1
|
||||
|
||||
/*
|
||||
* Values for type component of FCGI_Header
|
||||
*/
|
||||
#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)
|
||||
|
||||
/*
|
||||
* Value for requestId component of FCGI_Header
|
||||
*/
|
||||
#define FCGI_NULL_REQUEST_ID 0
|
||||
|
||||
typedef struct {
|
||||
unsigned char roleB1;
|
||||
unsigned char roleB0;
|
||||
unsigned char flags;
|
||||
unsigned char reserved[5];
|
||||
} FCGI_BeginRequestBody;
|
||||
|
||||
typedef struct {
|
||||
FCGI_Header header;
|
||||
FCGI_BeginRequestBody body;
|
||||
} FCGI_BeginRequestRecord;
|
||||
|
||||
/*
|
||||
* Mask for flags component of FCGI_BeginRequestBody
|
||||
*/
|
||||
#define FCGI_KEEP_CONN 1
|
||||
|
||||
/*
|
||||
* Values for role component of FCGI_BeginRequestBody
|
||||
*/
|
||||
#define FCGI_RESPONDER 1
|
||||
#define FCGI_AUTHORIZER 2
|
||||
#define FCGI_FILTER 3
|
||||
|
||||
typedef struct {
|
||||
unsigned char appStatusB3;
|
||||
unsigned char appStatusB2;
|
||||
unsigned char appStatusB1;
|
||||
unsigned char appStatusB0;
|
||||
unsigned char protocolStatus;
|
||||
unsigned char reserved[3];
|
||||
} FCGI_EndRequestBody;
|
||||
|
||||
typedef struct {
|
||||
FCGI_Header header;
|
||||
FCGI_EndRequestBody body;
|
||||
} FCGI_EndRequestRecord;
|
||||
|
||||
/*
|
||||
* Values for protocolStatus component of FCGI_EndRequestBody
|
||||
*/
|
||||
#define FCGI_REQUEST_COMPLETE 0
|
||||
#define FCGI_CANT_MPX_CONN 1
|
||||
#define FCGI_OVERLOADED 2
|
||||
#define FCGI_UNKNOWN_ROLE 3
|
||||
|
||||
/*
|
||||
* Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT records
|
||||
*/
|
||||
#define FCGI_MAX_CONNS "FCGI_MAX_CONNS"
|
||||
#define FCGI_MAX_REQS "FCGI_MAX_REQS"
|
||||
#define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS"
|
||||
|
||||
typedef struct {
|
||||
unsigned char type;
|
||||
unsigned char reserved[7];
|
||||
} FCGI_UnknownTypeBody;
|
||||
|
||||
typedef struct {
|
||||
FCGI_Header header;
|
||||
FCGI_UnknownTypeBody body;
|
||||
} FCGI_UnknownTypeRecord;
|
||||
|
||||
/**
|
||||
* The role component sets the role the Web server expects the application to play. The currently-defined roles are:
|
||||
* FCGI_RESPONDER
|
||||
* FCGI_AUTHORIZER
|
||||
* FCGI_FILTER
|
||||
* The flags component contains a bit that controls connection shutdown:
|
||||
* flags & FCGI_KEEP_CONN: If zero, the application closes the connection
|
||||
* after responding to this request. If not zero, the application does not
|
||||
* close the connection after responding to this request; the Web server
|
||||
* retains responsibility for the connection
|
||||
*/
|
||||
int fcgi_begin_request(antd_client_t* cl, uint16_t id, uint16_t role, uint8_t flags);
|
||||
|
||||
/**
|
||||
* The Web server sends a FCGI_ABORT_REQUEST record to abort a request. After receiving {FCGI_ABORT_REQUEST, R},
|
||||
* the application responds as soon as possible with {FCGI_END_REQUEST, R, {FCGI_REQUEST_COMPLETE, appStatus}}.
|
||||
* This is truly a response from the application, not a low-level acknowledgement from the FastCGI library.
|
||||
* A Web server aborts a FastCGI request when an HTTP client closes its transport connection while the FastCGI request is running on behalf of that client.
|
||||
*/
|
||||
int fcgi_abort_request(antd_client_t* cl, uint16_t id);
|
||||
|
||||
/**
|
||||
* FCGI_PARAMS is a stream record type used in sending name-value pairs from the Web server to the application. The name-value pairs are sent down the stream one after the other, in no specified order.
|
||||
*/
|
||||
int fcgi_send_param(antd_client_t* cl, int id, const char* key, const char* value);
|
||||
/**
|
||||
* FCGI_STDIN is a stream record type used in sending arbitrary data from the Web server to the application.
|
||||
* FCGI_DATA is a second stream record type used to send additional data to the application.
|
||||
*
|
||||
* The param data should be padded buffer with size len + paddlen
|
||||
*/
|
||||
int fcgi_send_stdin(antd_client_t* cl, int id, uint8_t* padded_data, size_t len, uint8_t paddlen);
|
||||
|
||||
/**
|
||||
* Read record header from application
|
||||
*/
|
||||
int fcgi_read_header(antd_client_t* cl, FCGI_Header* header);
|
||||
|
||||
/**
|
||||
* Read error message from stderr
|
||||
*
|
||||
* It is the responisbility of the caller to make
|
||||
* sure that the buffer lenth is big enough for the
|
||||
* Content data + padded len
|
||||
*/
|
||||
int fcgi_read_data(antd_client_t* cl, FCGI_Header* header, uint8_t* buffer);
|
||||
|
||||
uint8_t* fcgi_read_payload(antd_client_t* cl, FCGI_Header* header, int* size);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user