657 lines
16 KiB
C
657 lines
16 KiB
C
/*
|
|
Copyright (c) 2016-2020 Chung, Hyung-Hwan. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
1. Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
|
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <hio-fcgi.h>
|
|
#include <hio-sck.h>
|
|
#include "hio-prv.h"
|
|
|
|
struct hio_svc_fcgic_t
|
|
{
|
|
HIO_SVC_HEADER;
|
|
|
|
int stopping;
|
|
int tmout_set;
|
|
hio_svc_fcgic_tmout_t tmout;
|
|
|
|
hio_svc_fcgic_conn_t* conns;
|
|
};
|
|
|
|
#define CONN_SESS_CAPA_MAX (65536)
|
|
#define CONN_SESS_INC (32)
|
|
#define INVALID_SID HIO_TYPE_MAX(hio_oow_t)
|
|
|
|
struct hio_svc_fcgic_conn_t
|
|
{
|
|
hio_svc_fcgic_t* fcgic;
|
|
hio_skad_t addr;
|
|
hio_dev_sck_t* dev;
|
|
int connected;
|
|
|
|
struct
|
|
{
|
|
hio_svc_fcgic_sess_t* ptr;
|
|
hio_oow_t capa;
|
|
hio_oow_t free; /* the index to the first free session slot */
|
|
} sess;
|
|
|
|
struct
|
|
{
|
|
enum
|
|
{
|
|
R_AWAITING_HEADER,
|
|
R_AWAITING_BODY
|
|
} state;
|
|
|
|
hio_uint8_t type;
|
|
hio_uint16_t id;
|
|
hio_uint16_t content_len;
|
|
hio_uint8_t padding_len;
|
|
|
|
hio_uint32_t body_len; /* content_len+ padding_len */
|
|
hio_uint8_t buf[65792]; /* TODO: make it smaller by fireing on_read more frequently? */
|
|
hio_oow_t len; /* current bufferred length */
|
|
} r; /* space to parse incoming reply header */
|
|
|
|
hio_svc_fcgic_conn_t* next;
|
|
};
|
|
|
|
struct hio_svc_fcgic_sess_t
|
|
{
|
|
int active;
|
|
hio_oow_t sid;
|
|
hio_svc_fcgic_conn_t* conn;
|
|
hio_svc_fcgic_on_read_t on_read;
|
|
hio_svc_fcgic_on_untie_t on_untie;
|
|
void* ctx;
|
|
};
|
|
|
|
struct fcgic_sck_xtn_t
|
|
{
|
|
hio_svc_fcgic_conn_t* conn;
|
|
};
|
|
typedef struct fcgic_sck_xtn_t fcgic_sck_xtn_t;
|
|
|
|
static int make_connection_socket (hio_svc_fcgic_conn_t* conn);
|
|
static void release_session (hio_svc_fcgic_sess_t* sess);
|
|
|
|
static void sck_on_disconnect (hio_dev_sck_t* sck)
|
|
{
|
|
fcgic_sck_xtn_t* sck_xtn = hio_dev_sck_getxtn(sck);
|
|
hio_svc_fcgic_conn_t* conn = sck_xtn->conn;
|
|
|
|
printf ("DISCONNECT SOCKET .................. sck->%p conn->%p\n", sck, conn);
|
|
|
|
if (conn)
|
|
{
|
|
/* TODO: arrange to create it again if the server is not closing... */
|
|
/* if (.... ) */
|
|
#if 0
|
|
if (sck->hio->stopreq == HIO_STOPREQ_NONE)
|
|
{
|
|
/* this may create a busy loop if the connection attempt fails repeatedly */
|
|
make_connection_socket(conn); /* don't care about failure for now */
|
|
}
|
|
#else
|
|
hio_oow_t i;
|
|
for (i = 0; i < conn->sess.capa; i++)
|
|
{
|
|
hio_svc_fcgic_sess_t* sess;
|
|
sess = &conn->sess.ptr[i];
|
|
if (sess->active)
|
|
{
|
|
release_session (sess); /* TODO: is this correct?? */
|
|
}
|
|
}
|
|
conn->dev = HIO_NULL;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void sck_on_connect (hio_dev_sck_t* sck)
|
|
{
|
|
fcgic_sck_xtn_t* sck_xtn = hio_dev_sck_getxtn(sck);
|
|
hio_svc_fcgic_conn_t* conn = sck_xtn->conn;
|
|
|
|
printf ("CONNECTED >>>>>>>>>>>>>>>>>>>>>>>>>>\n");
|
|
|
|
/* reinitialize the input parsing information */
|
|
HIO_MEMSET (&conn->r, 0, HIO_SIZEOF(conn->r));
|
|
conn->r.state = R_AWAITING_HEADER;
|
|
}
|
|
|
|
static int sck_on_write (hio_dev_sck_t* sck, hio_iolen_t wrlen, void* wrctx, const hio_skad_t* dstaddr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int sck_on_read (hio_dev_sck_t* sck, const void* data, hio_iolen_t dlen, const hio_skad_t* srcaddr)
|
|
{
|
|
fcgic_sck_xtn_t* sck_xtn = hio_dev_sck_getxtn(sck);
|
|
hio_svc_fcgic_conn_t* conn = sck_xtn->conn;
|
|
hio_t* hio = conn->fcgic->hio;
|
|
|
|
if (dlen <= -1)
|
|
{
|
|
/* error or timeout */
|
|
/* fire all related fcgi sessions?? -> handled on disconnect */
|
|
hio_dev_sck_halt (sck);
|
|
}
|
|
else if (dlen == 0)
|
|
{
|
|
/* EOF */
|
|
hio_dev_sck_halt (sck);
|
|
/* fire all related fcgi sessions?? -> handled on disconnect?? */
|
|
}
|
|
else
|
|
{
|
|
printf ("got DATA From FCGI CLIENT>... dlen=%d [%.*s]\n", (int)dlen, (int)dlen, data);
|
|
do
|
|
{
|
|
hio_iolen_t reqlen, cplen;
|
|
|
|
if (conn->r.state == R_AWAITING_HEADER)
|
|
{
|
|
hio_fcgi_record_header_t* h;
|
|
|
|
HIO_ASSERT (hio, conn->r.len < HIO_SIZEOF(*h));
|
|
|
|
reqlen = HIO_SIZEOF(*h) - conn->r.len;
|
|
cplen = (dlen > reqlen)? reqlen: dlen;
|
|
HIO_MEMCPY (&conn->r.buf[conn->r.len], data, cplen);
|
|
conn->r.len += cplen;
|
|
|
|
data += cplen;
|
|
dlen -= cplen;
|
|
|
|
if (conn->r.len < HIO_SIZEOF(*h))
|
|
{
|
|
/* not enough data to complete a header*/
|
|
HIO_ASSERT (hio, dlen == 0);
|
|
break;
|
|
}
|
|
|
|
/* header complted */
|
|
h = (hio_fcgi_record_header_t*)conn->r.buf;
|
|
conn->r.type = h->type;
|
|
conn->r.id = hio_ntoh16(h->id);
|
|
conn->r.content_len = hio_ntoh16(h->content_len);
|
|
conn->r.padding_len = h->padding_len;
|
|
|
|
conn->r.state = R_AWAITING_BODY;
|
|
conn->r.body_len = conn->r.content_len + conn->r.padding_len;
|
|
conn->r.len = 0; /* reset to 0 to use the buffer to hold body */
|
|
printf ("header completed remaining data %d expected body_len %d\n", (int)dlen, (int)conn->r.body_len);
|
|
|
|
/* the expected body length must not be too long */
|
|
HIO_ASSERT (hio, conn->r.body_len <= HIO_SIZEOF(conn->r.buf));
|
|
|
|
if (conn->r.type == HIO_FCGI_END_REQUEST && conn->r.content_len < HIO_SIZEOF(hio_fcgi_end_request_body_t))
|
|
{
|
|
/* invalid content_len encountered */
|
|
/* TODO: logging*/
|
|
hio_dev_sck_halt (sck);
|
|
goto done;
|
|
}
|
|
|
|
if (conn->r.content_len == 0)
|
|
{
|
|
printf ("fireing without body\n");
|
|
goto got_body;
|
|
}
|
|
}
|
|
else /* R_AWAITING_BODY */
|
|
{
|
|
hio_svc_fcgic_sess_t* sess;
|
|
|
|
reqlen = conn->r.body_len - conn->r.len;
|
|
cplen = (dlen > reqlen)? reqlen: dlen;
|
|
HIO_MEMCPY (&conn->r.buf[conn->r.len], data, cplen);
|
|
conn->r.len += cplen;
|
|
|
|
data += cplen;
|
|
dlen -= cplen;
|
|
|
|
if (conn->r.len < conn->r.body_len)
|
|
{
|
|
HIO_ASSERT (hio, dlen == 0);
|
|
break;
|
|
}
|
|
|
|
got_body:
|
|
sess = (conn->r.id >= 1 && conn->r.id <= conn->sess.capa)? &conn->sess.ptr[conn->r.id - 1]: HIO_NULL;
|
|
|
|
if (!sess || !sess->active)
|
|
{
|
|
/* discard the record. no associated sessoin or inactive session */
|
|
goto back_to_header;
|
|
}
|
|
|
|
/* the complete body is in conn->r.buf */
|
|
if (conn->r.type == HIO_FCGI_END_REQUEST)
|
|
{
|
|
hio_fcgi_end_request_body_t* erb = conn->r.buf;
|
|
|
|
if (erb->proto_status != HIO_FCGI_REQUEST_COMPLETE)
|
|
{
|
|
/* error */
|
|
hio_uint32_t app_status = hio_ntoh32(erb->app_status);
|
|
}
|
|
conn->r.content_len = 0; /* to indicate the end of input*/
|
|
}
|
|
else if (conn->r.content_len == 0 || conn->r.type != HIO_FCGI_STDOUT)
|
|
{
|
|
/* discard the record */
|
|
/* TODO: log stderr to a file?? or handle it differently according to options given - discard or write to file */
|
|
/* TODO: logging */
|
|
goto back_to_header;
|
|
}
|
|
|
|
HIO_ASSERT (hio, conn->r.content_len <= conn->r.len);
|
|
sess->on_read (sess, conn->r.buf, conn->r.content_len, sess->ctx); /* TODO: tell between stdout and stderr */
|
|
|
|
back_to_header:
|
|
conn->r.state = R_AWAITING_HEADER;
|
|
conn->r.len = 0;
|
|
conn->r.body_len = 0;
|
|
conn->r.content_len = 0;
|
|
conn->r.padding_len = 0;
|
|
|
|
printf ("body completed remaining data %d\n", (int)dlen);
|
|
}
|
|
} while (dlen > 0);
|
|
}
|
|
|
|
done:
|
|
return 0;
|
|
}
|
|
|
|
static int make_connection_socket (hio_svc_fcgic_conn_t* conn)
|
|
{
|
|
hio_t* hio = conn->fcgic->hio;
|
|
hio_dev_sck_t* sck;
|
|
hio_dev_sck_make_t mi;
|
|
hio_dev_sck_connect_t ci;
|
|
fcgic_sck_xtn_t* sck_xtn;
|
|
|
|
HIO_MEMSET (&mi, 0, HIO_SIZEOF(mi));
|
|
switch (hio_skad_get_family(&conn->addr))
|
|
{
|
|
case HIO_AF_INET:
|
|
mi.type = HIO_DEV_SCK_TCP4;
|
|
break;
|
|
|
|
case HIO_AF_INET6:
|
|
mi.type = HIO_DEV_SCK_TCP6;
|
|
break;
|
|
|
|
#if defined(HIO_AF_UNIX)
|
|
case HIO_AF_UNIX:
|
|
mi.type = HIO_DEV_SCK_UNIX;
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
hio_seterrnum (hio, HIO_EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
mi.options = HIO_DEV_SCK_MAKE_LENIENT;
|
|
mi.on_write = sck_on_write;
|
|
mi.on_read = sck_on_read;
|
|
mi.on_connect = sck_on_connect;
|
|
mi.on_disconnect = sck_on_disconnect;
|
|
|
|
sck = hio_dev_sck_make(hio, HIO_SIZEOF(*sck_xtn), &mi);
|
|
if (HIO_UNLIKELY(!sck)) return -1;
|
|
|
|
sck_xtn = hio_dev_sck_getxtn(sck);
|
|
sck_xtn->conn = conn;
|
|
|
|
HIO_MEMSET (&ci, 0, HIO_SIZEOF(ci));
|
|
ci.remoteaddr = conn->addr;
|
|
/*ci.connect_tmout.sec = 5; TODO: make this configurable */
|
|
|
|
if (hio_dev_sck_connect(sck, &ci) <= -1)
|
|
{
|
|
/* immediate failure */
|
|
sck_xtn->conn = HIO_NULL; /* disassociate the socket from the fcgi connection object */
|
|
hio_dev_sck_halt (sck);
|
|
return -1;
|
|
}
|
|
|
|
printf ("MAKING CONNECTION %p %p\n", conn->dev, sck);
|
|
HIO_ASSERT (hio, conn->dev == HIO_NULL);
|
|
|
|
conn->dev = sck;
|
|
return 0;
|
|
}
|
|
|
|
static hio_svc_fcgic_conn_t* get_connection (hio_svc_fcgic_t* fcgic, const hio_skad_t* fcgis_addr)
|
|
{
|
|
hio_t* hio = fcgic->hio;
|
|
hio_svc_fcgic_conn_t* conn = fcgic->conns;
|
|
|
|
/* TODO: speed up? how many conns would be configured? sequential search may be ok here */
|
|
while (conn)
|
|
{
|
|
if (hio_equal_skads(&conn->addr, fcgis_addr, 1))
|
|
{
|
|
if (conn->sess.free != INVALID_SID ||
|
|
conn->sess.capa <= (CONN_SESS_CAPA_MAX - CONN_SESS_INC))
|
|
{
|
|
/* the connection has room for more sessions */
|
|
if (!conn->dev) make_connection_socket(conn); /* conn->dev will still be null if connection fails*/
|
|
return conn;
|
|
}
|
|
}
|
|
conn = conn->next;
|
|
}
|
|
|
|
conn = hio_callocmem(hio, HIO_SIZEOF(*conn));
|
|
if (HIO_UNLIKELY(!conn)) return HIO_NULL;
|
|
|
|
conn->fcgic = fcgic;
|
|
conn->addr = *fcgis_addr;
|
|
conn->sess.capa = 0;
|
|
conn->sess.free = INVALID_SID;
|
|
|
|
if (make_connection_socket(conn) <= -1)
|
|
{
|
|
hio_freemem (hio, conn);
|
|
return HIO_NULL;
|
|
}
|
|
|
|
conn->next = fcgic->conns;
|
|
fcgic->conns = conn;
|
|
|
|
return conn;
|
|
}
|
|
|
|
static void free_connections (hio_svc_fcgic_t* fcgic)
|
|
{
|
|
hio_t* hio = fcgic->hio;
|
|
hio_svc_fcgic_conn_t* conn = fcgic->conns;
|
|
hio_svc_fcgic_conn_t* next;
|
|
|
|
while (conn)
|
|
{
|
|
next = conn->next;
|
|
if (conn->dev)
|
|
{
|
|
struct fcgic_sck_xtn_t* sck_xtn;
|
|
sck_xtn = hio_dev_sck_getxtn(conn->dev);
|
|
sck_xtn->conn = HIO_NULL;
|
|
hio_dev_sck_halt (conn->dev);
|
|
}
|
|
hio_freemem (hio, conn->sess.ptr);
|
|
hio_freemem (hio, conn);
|
|
conn = next;
|
|
}
|
|
}
|
|
|
|
static hio_svc_fcgic_sess_t* new_session (hio_svc_fcgic_t* fcgic, const hio_skad_t* fcgis_addr, hio_svc_fcgic_on_read_t on_read, hio_svc_fcgic_on_untie_t on_untie, void* ctx)
|
|
{
|
|
hio_t* hio = fcgic->hio;
|
|
hio_svc_fcgic_conn_t* conn;
|
|
hio_svc_fcgic_sess_t* sess;
|
|
|
|
conn = get_connection(fcgic, fcgis_addr);
|
|
if (HIO_UNLIKELY(!conn)) return HIO_NULL;
|
|
|
|
if (conn->sess.free == INVALID_SID)
|
|
{
|
|
hio_oow_t newcapa, i;
|
|
hio_svc_fcgic_sess_t* newptr;
|
|
|
|
newcapa = conn->sess.capa + CONN_SESS_INC;
|
|
newptr = hio_reallocmem(hio, conn->sess.ptr, HIO_SIZEOF(*sess) * newcapa);
|
|
if (HIO_UNLIKELY(!newptr)) return HIO_NULL;
|
|
|
|
for (i = conn->sess.capa ; i < newcapa; i++)
|
|
{
|
|
/* management records use 0 for requestId.
|
|
* but application records have a nonzero requestId. */
|
|
newptr[i].sid = i + 1;
|
|
newptr[i].conn = conn;
|
|
newptr[i].active = 0;
|
|
}
|
|
newptr[i - 1].sid = INVALID_SID;
|
|
conn->sess.free = conn->sess.capa;
|
|
|
|
conn->sess.capa = newcapa;
|
|
conn->sess.ptr = newptr;
|
|
}
|
|
|
|
sess = &conn->sess.ptr[conn->sess.free];
|
|
conn->sess.free = sess->sid;
|
|
|
|
sess->sid = conn->sess.free;
|
|
sess->on_read = on_read;
|
|
sess->on_untie = on_untie;
|
|
sess->active = 1;
|
|
sess->ctx = ctx;
|
|
HIO_ASSERT (hio, sess->conn == conn);
|
|
HIO_ASSERT (hio, sess->conn->fcgic == fcgic);
|
|
|
|
return sess;
|
|
}
|
|
|
|
static void release_session (hio_svc_fcgic_sess_t* sess)
|
|
{
|
|
if (sess->on_untie) sess->on_untie (sess, sess->ctx);
|
|
sess->active = 0;
|
|
sess->sid = sess->conn->sess.free;
|
|
sess->conn->sess.free = sess->sid;
|
|
}
|
|
|
|
hio_svc_fcgic_t* hio_svc_fcgic_start (hio_t* hio, const hio_svc_fcgic_tmout_t* tmout)
|
|
{
|
|
hio_svc_fcgic_t* fcgic = HIO_NULL;
|
|
|
|
fcgic = (hio_svc_fcgic_t*)hio_callocmem(hio, HIO_SIZEOF(*fcgic));
|
|
if (HIO_UNLIKELY(!fcgic)) goto oops;
|
|
|
|
fcgic->hio = hio;
|
|
fcgic->svc_stop = (hio_svc_stop_t)hio_svc_fcgic_stop;
|
|
|
|
if (tmout)
|
|
{
|
|
fcgic->tmout = *tmout;
|
|
fcgic->tmout_set = 1;
|
|
}
|
|
|
|
HIO_SVCL_APPEND_SVC (&hio->actsvc, (hio_svc_t*)fcgic);
|
|
HIO_DEBUG1 (hio, "FCGIC - STARTED SERVICE %p\n", fcgic);
|
|
return fcgic;
|
|
|
|
oops:
|
|
if (fcgic) hio_freemem (hio, fcgic);
|
|
return HIO_NULL;
|
|
}
|
|
|
|
void hio_svc_fcgic_stop (hio_svc_fcgic_t* fcgic)
|
|
{
|
|
hio_t* hio = fcgic->hio;
|
|
|
|
HIO_DEBUG1 (hio, "FCGIC - STOPPING SERVICE %p\n", fcgic);
|
|
fcgic->stopping = 1;
|
|
|
|
free_connections (fcgic);
|
|
|
|
HIO_SVCL_UNLINK_SVC (fcgic);
|
|
hio_freemem (hio, fcgic);
|
|
|
|
HIO_DEBUG1 (hio, "FCGIC - STOPPED SERVICE %p\n", fcgic);
|
|
}
|
|
|
|
hio_svc_fcgic_sess_t* hio_svc_fcgic_tie (hio_svc_fcgic_t* fcgic, const hio_skad_t* addr, hio_svc_fcgic_on_read_t on_read, hio_svc_fcgic_on_untie_t on_untie, void* ctx)
|
|
{
|
|
/* TODO: reference counting for safety?? */
|
|
return new_session(fcgic, addr, on_read, on_untie, ctx);
|
|
}
|
|
|
|
void hio_svc_fcgic_untie (hio_svc_fcgic_sess_t* sess)
|
|
{
|
|
/* TODO: reference counting for safety?? */
|
|
release_session (sess);
|
|
}
|
|
|
|
int hio_svc_fcgic_beginrequest (hio_svc_fcgic_sess_t* sess)
|
|
{
|
|
hio_iovec_t iov[2];
|
|
hio_fcgi_record_header_t h;
|
|
hio_fcgi_begin_request_body_t b;
|
|
|
|
if (!sess->conn->dev)
|
|
{
|
|
/* TODO: set error **/
|
|
return -1;
|
|
}
|
|
|
|
HIO_MEMSET (&h, 0, HIO_SIZEOF(h));
|
|
h.version = HIO_FCGI_VERSION;
|
|
h.type = HIO_FCGI_BEGIN_REQUEST;
|
|
h.id = h.id = hio_hton16(sess->sid);
|
|
h.content_len = hio_hton16(HIO_SIZEOF(b));
|
|
h.padding_len = 0;
|
|
|
|
HIO_MEMSET (&b, 0, HIO_SIZEOF(b));
|
|
b.role = HIO_CONST_HTON16(HIO_FCGI_ROLE_RESPONDER);
|
|
b.flags = HIO_FCGI_KEEP_CONN;
|
|
|
|
iov[0].iov_ptr = &h;
|
|
iov[0].iov_len = HIO_SIZEOF(h);
|
|
iov[1].iov_ptr = &b;
|
|
iov[1].iov_len = HIO_SIZEOF(b);
|
|
|
|
/* TODO: check if sess->conn->dev is still valid */
|
|
return hio_dev_sck_writev(sess->conn->dev, iov, 2, HIO_NULL, HIO_NULL);
|
|
}
|
|
|
|
int hio_svc_fcgic_writeparam (hio_svc_fcgic_sess_t* sess, const void* key, hio_iolen_t ksz, const void* val, hio_iolen_t vsz)
|
|
{
|
|
hio_iovec_t iov[4];
|
|
hio_fcgi_record_header_t h;
|
|
hio_uint8_t sz[8];
|
|
hio_oow_t szc = 0;
|
|
|
|
if (!sess->conn->dev)
|
|
{
|
|
/* TODO: set error **/
|
|
return -1;
|
|
}
|
|
|
|
/* TODO: buffer key value pairs. flush on the end of param of buffer full.
|
|
* can merge multipl key values pairs in one FCGI_PARAMS packets....
|
|
*/
|
|
HIO_MEMSET (&h, 0, HIO_SIZEOF(h));
|
|
h.version = HIO_FCGI_VERSION;
|
|
h.type = HIO_FCGI_PARAMS;
|
|
h.id = hio_hton16(sess->sid);
|
|
h.content_len = 0;
|
|
|
|
/* TODO: check ksz and vsz can't exceed max 32bit value. */
|
|
/* limit sizes to the max of the signed 32-bit interger
|
|
* the high-order bit is used as encoding marker (1-byte or 4-byte encoding).
|
|
* so a size can't hit the unsigned max. */
|
|
ksz &= HIO_TYPE_MAX(hio_int32_t);
|
|
vsz &= HIO_TYPE_MAX(hio_int32_t);
|
|
if (ksz > 0)
|
|
{
|
|
if (ksz > 0xFF)
|
|
{
|
|
sz[szc++] = (ksz >> 24) | 0x80;
|
|
sz[szc++] = (ksz >> 16) & 0xFF;
|
|
sz[szc++] = (ksz >> 8) & 0xFF;
|
|
sz[szc++] = ksz & 0xFF;
|
|
}
|
|
else
|
|
{
|
|
sz[szc++] = ksz;
|
|
}
|
|
|
|
if (vsz > 0xFF)
|
|
{
|
|
sz[szc++] = (vsz >> 24) | 0x80;
|
|
sz[szc++] = (vsz >> 16) & 0xFF;
|
|
sz[szc++] = (vsz >> 8) & 0xFF;
|
|
sz[szc++] = vsz & 0xFF;
|
|
}
|
|
else
|
|
{
|
|
sz[szc++] = vsz;
|
|
}
|
|
|
|
h.content_len = szc + ksz + vsz;
|
|
/* TODO: check content_len overflows... */
|
|
}
|
|
|
|
h.content_len = hio_hton16(h.content_len);
|
|
h.padding_len = 0;
|
|
|
|
/* TODO: some buffering of parameters??? if the key/value isn't long enough, it may trigger many system calls*/
|
|
iov[0].iov_ptr = &h;
|
|
iov[0].iov_len = HIO_SIZEOF(h);
|
|
if (ksz > 0)
|
|
{
|
|
iov[1].iov_ptr = sz;
|
|
iov[1].iov_len = szc;
|
|
iov[2].iov_ptr = key;
|
|
iov[2].iov_len = ksz;
|
|
iov[3].iov_ptr = val;
|
|
iov[3].iov_len = vsz;
|
|
}
|
|
|
|
return hio_dev_sck_writev(sess->conn->dev, iov, (ksz > 0? 4: 1), HIO_NULL, HIO_NULL);
|
|
}
|
|
|
|
int hio_svc_fcgic_writestdin (hio_svc_fcgic_sess_t* sess, const void* data, hio_iolen_t size)
|
|
{
|
|
hio_iovec_t iov[2];
|
|
hio_fcgi_record_header_t h;
|
|
|
|
if (!sess->conn->dev)
|
|
{
|
|
/* TODO: set error **/
|
|
return -1;
|
|
}
|
|
|
|
HIO_MEMSET (&h, 0, HIO_SIZEOF(h));
|
|
h.version = HIO_FCGI_VERSION;
|
|
h.type = HIO_FCGI_STDIN;
|
|
h.id = hio_hton16(sess->sid);
|
|
h.content_len = hio_hton16(size);
|
|
|
|
iov[0].iov_ptr = &h;
|
|
iov[0].iov_len = HIO_SIZEOF(h);
|
|
if (size > 0)
|
|
{
|
|
iov[1].iov_ptr = (void*)data;
|
|
iov[1].iov_len = size;
|
|
}
|
|
|
|
/* TODO: check if sess->conn->dev is still valid */
|
|
return hio_dev_sck_writev(sess->conn->dev, iov, (size > 0? 2: 1), HIO_NULL, HIO_NULL);
|
|
}
|