changed to clear the on_write callbacks for completed write requestrs after having triggered timer callbacks collectively and also before triggering on_read() for each device

cleaned up debugging messages in dns-cli.c
experimenting with http server implementation
This commit is contained in:
2020-05-15 06:18:49 +00:00
parent cc74d317a2
commit 57ca185651
9 changed files with 522 additions and 277 deletions

View File

@ -172,7 +172,6 @@ if (mio_htre_getcontentlen(req) > 0)
* i don't want to delay this until the contents are received.
* if you don't like this behavior, you must implement your own
* callback function for request handling. */
#if 0
/* TODO support X-HTTP-Method-Override */
if (data.method == MIO_HTTP_POST)
@ -197,36 +196,22 @@ if (mio_htre_getcontentlen(req) > 0)
}
else
{
//mio_svc_htts_sendstatus (htts, csck, 500, mth, mio_htre_getversion(req), (req->flags & MIO_HTRE_ATTR_KEEPALIVE), MIO_NULL);
//return 0;
if (mth == MIO_HTTP_POST &&
!(req->flags & MIO_HTRE_ATTR_LENGTH) &&
!(req->flags & MIO_HTRE_ATTR_CHUNKED))
if (mth == MIO_HTTP_POST && !(req->flags & (MIO_HTRE_ATTR_LENGTH | MIO_HTRE_ATTR_CHUNKED)))
{
/* POST without Content-Length nor not chunked */
req->flags &= ~MIO_HTRE_ATTR_KEEPALIVE;
mio_htre_discardcontent (req);
mio_htre_discardcontent (req);
/* 411 Length Required - can't keep alive. Force disconnect */
mio_svc_htts_sendstatus (htts, csck, 411, mth, mio_htre_getversion(req), 0, MIO_NULL); /* TODO: error check... */
req->flags &= ~MIO_HTRE_ATTR_KEEPALIVE; /* to cause sendstatus() to close */
if (mio_svc_htts_sendstatus(htts, csck, req, 411, MIO_NULL) <= -1) mio_dev_sck_halt (csck); /*TODO: redo this sendstatus */
}
else
{
/* TODO: handle 100 continue??? */
if ((req->flags & MIO_HTRE_ATTR_EXPECT) &&
mio_comp_http_version_numbers(&req->version, 1, 1) >= 0 &&
mio_htre_getcontentlen(req) <= 0)
{
if (req->flags & MIO_HTRE_ATTR_EXPECT100)
{
mio_dev_sck_write(csck, "HTTP/1.1 100 Continue\r\n", 23, MIO_NULL, MIO_NULL);
}
else
{
}
}
const mio_bch_t* qpath = mio_htre_getqpath(req);
if (mio_svc_htts_docgi(htts, csck, req, "") <= -1)
{
mio_dev_sck_halt (csck);
}
#if 0
/*
if (mio_comp_bcstr(qpath, "/testfunc", 0) == 0)
{
@ -241,54 +226,8 @@ if (mio_htre_getcontentlen(req) > 0)
mio_htre_discardcontent (req);
mio_dev_sck_halt (csck);
}
/*
if (mio_comp_bcstr(qpath, "/mio.c", 0) == 0)
{
mio_svc_htts_sendfile (htts, csck, "/home/hyung-hwan/projects/mio/lib/mio.c", 200, mth, mio_htre_getversion(req), (req->flags & MIO_HTRE_ATTR_KEEPALIVE));
}
else
{
mio_svc_htts_sendstatus (htts, csck, 500, mth, mio_htre_getversion(req), (req->flags & MIO_HTRE_ATTR_KEEPALIVE), MIO_NULL);
}*/
}
#if 0
else if (server_xtn->makersrc(htts, client, req, &rsrc) <= -1)
{
/* failed to make a resource. just send the internal server error.
* the makersrc handler can return a negative number to return
* '500 Internal Server Error'. If it wants to return a specific
* error code, it should return 0 with the MIO_HTTPD_RSRC_ERROR
* resource. */
mio_htts_discardcontent (req);
task = mio_htts_entaskerror(htts, client, MIO_NULL, 500, req);
}
else
{
task = MIO_NULL;
if ((rsrc.flags & MIO_HTTPD_RSRC_100_CONTINUE) &&
(task = mio_htts_entaskcontinue(htts, client, task, req)) == MIO_NULL)
{
/* inject '100 continue' first if it is needed */
goto oops;
}
/* arrange the actual resource to be returned */
task = mio_htts_entaskrsrc(htts, client, task, &rsrc, req);
server_xtn->freersrc (htts, client, req, &rsrc);
/* if the resource is indicating to return an error,
* discard the contents since i won't return them */
if (rsrc.type == MIO_HTTPD_RSRC_ERROR)
{
mio_htre_discardcontent (req);
}
}
if (task == MIO_NULL) goto oops;
#endif
}
}
}
else
@ -296,38 +235,14 @@ if (mio_htre_getcontentlen(req) > 0)
/* contents are all received */
if (mth == MIO_HTTP_CONNECT)
{
MIO_DEBUG1 (htts->mio, "Switching HTRD to DUMMY for [%hs]\n", mio_htre_getqpath(req));
/* Switch the http reader to a dummy mode so that the subsqeuent
* input(request) is just treated as data to the request just
* completed */
mio_htrd_dummify (cli->htrd);
/* connect is not handled in the peek mode. create a proxy resource here */
////////
#if 0
if (server_xtn->makersrc(htts, client, req, &rsrc) <= -1)
{
MIO_DEBUG1 (htts->mio, "Cannot make resource for [%hs]\n", mio_htre_getqpath(req));
/* failed to make a resource. just send the internal server error.
* the makersrc handler can return a negative number to return
* '500 Internal Server Error'. If it wants to return a specific
* error code, it should return 0 with the MIO_HTTPD_RSRC_ERROR
* resource. */
task = mio_htts_entaskerror(htts, client, MIO_NULL, 500, req);
}
else
{
/* arrange the actual resource to be returned */
task = mio_htts_entaskrsrc (htts, client, MIO_NULL, &rsrc, req);
server_xtn->freersrc (htts, client, req, &rsrc);
}
if (task == MIO_NULL) goto oops;
#endif
////////
/* TODO: arrange to forward all raw bytes */
}
else if (req->flags & MIO_HTRE_ATTR_PROXIED)
{
@ -446,25 +361,52 @@ static void fini_client (mio_svc_htts_cli_t* cli)
/* ------------------------------------------------------------------------ */
static int client_on_read (mio_dev_sck_t* sck, const void* buf, mio_iolen_t len, const mio_skad_t* srcaddr)
static int listener_on_read (mio_dev_sck_t* sck, const void* buf, mio_iolen_t len, const mio_skad_t* srcaddr)
{
/* unlike the function name, this callback is set on both the listener and the client.
* however, it must never be triggered for the listener */
mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck);
printf ("** HTTS - client read %p %d -> htts:%p\n", sck, (int)len, cli->htts);
mio_oow_t rem;
int x;
MIO_ASSERT (sck->mio, sck != cli->htts->lsck);
printf ("** HTTS - client read socket(%p) %d -> htts:%p\n", sck, (int)len, cli->htts);
{
mio_iolen_t i;
for (i = 0; i < len; i++)
{
printf ("%c", ((const mio_uint8_t*)buf)[i]);
}
}
if (len <= 0)
{
mio_dev_sck_halt (sck);
}
else if (mio_htrd_feed(cli->htrd, buf, len) <= -1)
else if ((x = mio_htrd_feed(cli->htrd, buf, len, &rem)) <= -1)
{
/* in some cases, we may have to send some http response depending on the failure type */
/* BADRE -> bad request? */
printf ("** HTTS - client htrd feed failure socket(%p) - %d\n", sck, x);
/* TODO: either send bad request or server failure ... */
mio_dev_sck_halt (sck);
}
else if (rem > 0)
{
/* TODO: store the remaining data in the client's buffer */
}
return 0;
}
static int client_on_write (mio_dev_sck_t* sck, mio_iolen_t wrlen, void* wrctx, const mio_skad_t* dstaddr)
static int listener_on_write (mio_dev_sck_t* sck, mio_iolen_t wrlen, void* wrctx, const mio_skad_t* dstaddr)
{
mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck);
mio_svc_htts_rsrc_t* rsrc = (mio_svc_htts_rsrc_t*)wrctx;
MIO_ASSERT (sck->mio, sck != cli->htts->lsck);
if (rsrc)
{
int n;
@ -479,18 +421,7 @@ static int client_on_write (mio_dev_sck_t* sck, mio_iolen_t wrlen, void* wrctx,
}
return 0;
}
/* ------------------------------------------------------------------------ */
static int listener_on_read (mio_dev_sck_t* sck, const void* buf, mio_iolen_t len, const mio_skad_t* srcaddr)
{
return 0;
}
static int listener_on_write (mio_dev_sck_t* sck, mio_iolen_t wrlen, void* wrctx, const mio_skad_t* dstaddr)
{
return 0;
}
static void listener_on_connect (mio_dev_sck_t* sck)
@ -502,13 +433,9 @@ static void listener_on_connect (mio_dev_sck_t* sck)
/* accepted a new client */
MIO_DEBUG3 (sck->mio, "HTTS(%p) - accepted... %p %d \n", cli->htts, sck, sck->hnd);
/* the accepted socket inherits various event callbacks. switch some of them to avoid sharing */
sck->on_read = client_on_read;
sck->on_write = client_on_write;
if (init_client(cli, sck) <= -1)
{
MIO_INFO2 (sck->mio, "UNABLE TO INITIALIZE NEW CLIENT %p %d\n", sck, (int)sck->hnd);
MIO_DEBUG2 (sck->mio, "UNABLE TO INITIALIZE NEW CLIENT %p %d\n", sck, (int)sck->hnd);
mio_dev_sck_halt (sck);
}
}
@ -529,21 +456,21 @@ static void listener_on_disconnect (mio_dev_sck_t* sck)
{
case MIO_DEV_SCK_CONNECTING:
/* only for connecting sockets */
MIO_INFO1 (sck->mio, "OUTGOING SESSION DISCONNECTED - FAILED TO CONNECT (%d) TO REMOTE SERVER\n", (int)sck->hnd);
MIO_DEBUG1 (sck->mio, "OUTGOING SESSION DISCONNECTED - FAILED TO CONNECT (%d) TO REMOTE SERVER\n", (int)sck->hnd);
break;
case MIO_DEV_SCK_CONNECTING_SSL:
/* only for connecting sockets */
MIO_INFO1 (sck->mio, "OUTGOING SESSION DISCONNECTED - FAILED TO SSL-CONNECT (%d) TO REMOTE SERVER\n", (int)sck->hnd);
MIO_DEBUG1 (sck->mio, "OUTGOING SESSION DISCONNECTED - FAILED TO SSL-CONNECT (%d) TO REMOTE SERVER\n", (int)sck->hnd);
break;
case MIO_DEV_SCK_CONNECTED:
/* only for connecting sockets */
MIO_INFO1 (sck->mio, "OUTGOING CLIENT CONNECTION GOT TORN DOWN %p(%d).......\n", (int)sck->hnd);
MIO_DEBUG1 (sck->mio, "OUTGOING CLIENT CONNECTION GOT TORN DOWN %p(%d).......\n", (int)sck->hnd);
break;
case MIO_DEV_SCK_LISTENING:
MIO_INFO2 (sck->mio, "LISTNER SOCKET %p(%d) - SHUTTUING DOWN\n", sck, (int)sck->hnd);
MIO_DEBUG2 (sck->mio, "LISTNER SOCKET %p(%d) - SHUTTUING DOWN\n", sck, (int)sck->hnd);
break;
case MIO_DEV_SCK_ACCEPTING_SSL: /* special case. */
@ -552,17 +479,17 @@ static void listener_on_disconnect (mio_dev_sck_t* sck)
* the cli extension are is not initialized yet */
MIO_ASSERT (sck->mio, sck != cli->sck);
MIO_ASSERT (sck->mio, cli->sck == cli->htts->lsck); /* the field is a copy of the extension are of the listener socket. so it should point to the listner socket */
MIO_INFO2 (sck->mio, "LISTENER UNABLE TO SSL-ACCEPT CLIENT %p(%d) ....%p\n", sck, (int)sck->hnd);
MIO_DEBUG2 (sck->mio, "LISTENER UNABLE TO SSL-ACCEPT CLIENT %p(%d) ....%p\n", sck, (int)sck->hnd);
return;
case MIO_DEV_SCK_ACCEPTED:
/* only for sockets accepted by the listeners. will never come here because
* the disconnect call for such sockets have been changed in listener_on_connect() */
MIO_INFO2 (sck->mio, "ACCEPTED CLIENT SOCKET %p(%d) GOT DISCONNECTED.......\n", sck, (int)sck->hnd);
MIO_DEBUG2 (sck->mio, "ACCEPTED CLIENT SOCKET %p(%d) GOT DISCONNECTED.......\n", sck, (int)sck->hnd);
break;
default:
MIO_INFO2 (sck->mio, "SOCKET %p(%d) DISCONNECTED AFTER ALL.......\n", sck, (int)sck->hnd);
MIO_DEBUG2 (sck->mio, "SOCKET %p(%d) DISCONNECTED AFTER ALL.......\n", sck, (int)sck->hnd);
break;
}
@ -639,7 +566,7 @@ mio_svc_htts_t* mio_svc_htts_start (mio_t* mio, const mio_skad_t* bind_addr)
MIO_MEMSET (&info, 0, MIO_SIZEOF(info));
info.b.localaddr = *bind_addr;
info.b.options = MIO_DEV_SCK_BIND_REUSEADDR | MIO_DEV_SCK_BIND_REUSEPORT;
info.b.options |= MIO_DEV_SCK_BIND_SSL;
/*info.b.options |= MIO_DEV_SCK_BIND_SSL; */
info.b.ssl_certfile = "localhost.crt";
info.b.ssl_keyfile = "localhost.key";
if (mio_dev_sck_bind(htts->lsck, &info.b) <= -1) goto oops;
@ -657,7 +584,7 @@ mio_svc_htts_t* mio_svc_htts_start (mio_t* mio, const mio_skad_t* bind_addr)
MIO_SVCL_APPEND_SVC (&mio->actsvc, (mio_svc_t*)htts);
CLIL_INIT (&htts->cli);
MIO_DEBUG3 (mio, "STARTED SVC(HTTS) LISTENER %p LISTENER SOCKET %p(%d)\n", htts, htts->lsck, (int)htts->lsck->hnd);
MIO_DEBUG3 (mio, "HTTS - STARTED SERVICE %p - LISTENER SOCKET %p(%d)\n", htts, htts->lsck, (int)htts->lsck->hnd);
return htts;
oops:
@ -673,7 +600,7 @@ void mio_svc_htts_stop (mio_svc_htts_t* htts)
{
mio_t* mio = htts->mio;
MIO_DEBUG3 (mio, "STOPPING SVC(HTTS) %p LISTENER SOCKET %p(%d)\n", htts, htts->lsck, (int)(htts->lsck? htts->lsck->hnd: -1));
MIO_DEBUG3 (mio, "HTTS - STOPPING SERVICE %p - LISTENER SOCKET %p(%d)\n", htts, htts->lsck, (int)(htts->lsck? htts->lsck->hnd: -1));
/* htts->lsck may be null if the socket has been destroyed for operational error and
* forgotten in the disconnect callback thereafter */
@ -732,7 +659,7 @@ mio_svc_htts_rsrc_t* mio_svc_htts_rsrc_make (mio_svc_htts_t* htts, mio_dev_sck_t
void mio_svc_htts_rsrc_kill (mio_svc_htts_rsrc_t* rsrc)
{
printf ("RSRC KILL >>>>> %p\n", rsrc->htts);
printf ("RSRC KILL >>>>> htts=> %p\n", rsrc->htts);
mio_t* mio = rsrc->htts->mio;
if (rsrc->on_kill) rsrc->on_kill (rsrc);
@ -936,34 +863,99 @@ int mio_svc_htts_dofile (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t*
/* ----------------------------------------------------------------- */
struct cgi_state_t
{
/**** CHANGE THESE FIELDS AFTER RSRC CLEANUP */
mio_svc_htts_t* htts;
mio_svc_htts_rsrc_t* rsrc_prev;
mio_svc_htts_rsrc_t* rsrc_next;
mio_svc_htts_rsrc_on_kill_t on_kill;
/**** CHANGE THESE FIELDS AFTER RSRC CLEANUP */
mio_oow_t num_pending_writes_to_client;
mio_oow_t num_pending_writes_to_peer;
mio_dev_pro_t* peer;
mio_htrd_t* peer_htrd;
mio_svc_htts_cli_t* cli;
mio_htre_t* req;
mio_oow_t req_content_length;
mio_dev_sck_on_read_t cli_org_on_read;
mio_dev_sck_on_write_t cli_org_on_write;
};
typedef struct cgi_state_t cgi_state_t;
struct cgi_peer_xtn_t
{
cgi_state_t* state;
};
typedef struct cgi_peer_xtn_t cgi_peer_xtn_t;
static void cgi_state_on_kill (cgi_state_t* cgi_state)
{
printf ("**** CGI_STATE_ON_KILL \n");
if (cgi_state->peer)
{
mio_dev_pro_kill (cgi_state->peer);
cgi_state->peer = MIO_NULL;
}
if (cgi_state->peer_htrd)
{
mio_htrd_close (cgi_state->peer_htrd);
cgi_state->peer_htrd = MIO_NULL;
}
if (cgi_state->cli_org_on_read)
{
cgi_state->cli->sck->on_read = cgi_state->cli_org_on_read;
cgi_state->cli_org_on_read = MIO_NULL;
}
if (cgi_state->cli_org_on_write)
{
cgi_state->cli->sck->on_write = cgi_state->cli_org_on_write;
cgi_state->cli_org_on_write = MIO_NULL;
}
}
static void cgi_peer_on_close (mio_dev_pro_t* pro, mio_dev_pro_sid_t sid)
{
mio_t* mio = pro->mio;
cgi_peer_xtn_t* cgi_peer = mio_dev_pro_getxtn(pro);
if (sid == MIO_DEV_PRO_MASTER)
MIO_INFO1 (mio, "PROCESS(%d) CLOSE MASTER\n", (int)pro->child_pid);
{
MIO_DEBUG1 (mio, "PROCESS(%d) CLOSE MASTER\n", (int)pro->child_pid);
cgi_peer->state->peer = MIO_NULL; /* clear this peer from the state */
}
else
MIO_INFO2 (mio, "PROCESS(%d) CLOSE SLAVE[%d]\n", (int)pro->child_pid, sid);
{
MIO_DEBUG2 (mio, "PROCESS(%d) CLOSE SLAVE[%d]\n", (int)pro->child_pid, sid);
}
}
static int cgi_peer_on_read (mio_dev_pro_t* pro, mio_dev_pro_sid_t sid, const void* data, mio_iolen_t dlen)
{
mio_t* mio = pro->mio;
cgi_peer_xtn_t* cgi_peer = mio_dev_pro_getxtn(pro);
if (dlen <= -1)
{
MIO_INFO1 (mio, "PROCESS(%d): READ TIMED OUT...\n", (int)pro->child_pid);
MIO_DEBUG1 (mio, "PROCESS(%d): READ TIMED OUT...\n", (int)pro->child_pid);
mio_dev_pro_halt (pro);
return 0;
}
else if (dlen <= 0)
{
MIO_INFO1 (mio, "PROCESS(%d): EOF RECEIVED...\n", (int)pro->child_pid);
MIO_DEBUG1 (mio, "PROCESS(%d): EOF RECEIVED...\n", (int)pro->child_pid);
/* no outstanding request. but EOF */
mio_dev_pro_halt (pro);
return 0;
}
MIO_INFO5 (mio, "PROCESS(%d) READ DATA ON SLAVE[%d] len=%d [%.*hs]\n", (int)pro->child_pid, (int)sid, (int)dlen, dlen, (char*)data);
MIO_DEBUG5 (mio, "PROCESS(%d) READ DATA ON SLAVE[%d] len=%d [%.*hs]\n", (int)pro->child_pid, (int)sid, (int)dlen, dlen, (char*)data);
if (sid == MIO_DEV_PRO_OUT)
{
mio_dev_pro_read (pro, sid, 0);
@ -975,18 +967,34 @@ static int cgi_peer_on_read (mio_dev_pro_t* pro, mio_dev_pro_sid_t sid, const vo
static int cgi_peer_on_write (mio_dev_pro_t* pro, mio_iolen_t wrlen, void* wrctx)
{
mio_t* mio = pro->mio;
cgi_peer_xtn_t* cgi_peer = mio_dev_pro_getxtn(pro);
cgi_state_t* cgi_state = cgi_peer->state;
mio_ntime_t tmout;
if (wrlen <= -1)
{
MIO_INFO1 (mio, "PROCESS(%d): WRITE TIMED OUT...\n", (int)pro->child_pid);
MIO_DEBUG1 (mio, "PROCESS(%d): WRITE TIMED OUT...\n", (int)pro->child_pid);
mio_dev_pro_halt (pro);
return 0;
}
MIO_DEBUG2 (mio, "PROCESS(%d) wrote data of %d bytes\n", (int)pro->child_pid, (int)wrlen);
#if 0
/*mio_dev_pro_read (pro, MIO_DEV_PRO_OUT, 1);*/
MIO_INIT_NTIME (&tmout, 5, 0);
mio_dev_pro_timedread (pro, MIO_DEV_PRO_OUT, 1, &tmout);
#endif
#if 0
if (mio_dev_pro_write(pro, cgi_peer->cli->req, length, MIO_NULL) <= -1)
{
/* halt both peer and cli */
}
#endif
cgi_state->num_pending_writes_to_peer--;
return 0;
}
@ -994,84 +1002,211 @@ static int cgi_peer_on_write (mio_dev_pro_t* pro, mio_iolen_t wrlen, void* wrctx
static int cgi_client_on_read (mio_dev_sck_t* sck, const void* buf, mio_iolen_t len, const mio_skad_t* srcaddr)
{
mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck);
cgi_state_t* cgi_state = RSRCL_FIRST_RSRC(&cli->rsrc);
printf ("** HTTS - cgi client read %p %d -> htts:%p\n", sck, (int)len, cli->htts);
if (len <= 0)
if (len <= -1)
{
/* read error */
mio_dev_sck_halt (sck);
goto oops;
}
else if (mio_htrd_feed(cli->htrd, buf, len) <= -1)
else if (len == 0)
{
mio_dev_sck_halt (sck);
/* EOF */
}
else
{
#if 0
mio_oow_t rem;
if (mio_htrd_feed(cli->htrd, buf, len, &rem) <= -1) goto oops;
if (rem > 0)
{
/* TODO: there is left-over data */
}
#endif
cgi_state->num_pending_writes_to_peer++;
if (mio_dev_pro_write(cgi_state->peer, buf, len, MIO_NULL) <= -1)
{
cgi_state->num_pending_writes_to_peer--;
goto oops;
}
if (cgi_state->num_pending_writes_to_peer > 1)
{
if (mio_dev_sck_read(sck, 0) <= -1) goto oops; /* disable input watching */
}
}
return 0;
oops:
/* TODO: arrange to kill the entire cgi_state??? */
mio_dev_sck_halt (sck);
return 0;
}
static int cgi_client_on_write (mio_dev_sck_t* sck, mio_iolen_t wrlen, void* wrctx, const mio_skad_t* dstaddr)
{
mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck);
cgi_state_t* cgi_state = RSRCL_FIRST_RSRC(&cli->rsrc);
return 0;
}
static void cgi_client_on_disconnect (mio_dev_sck_t* sck)
static int get_request_content_length (mio_htre_t* req, mio_oow_t* len)
{
client_on_disconnect (sck);
}
if (req->state & (MIO_HTRE_COMPLETED | MIO_HTRE_DISCARDED))
{
/* no more content to receive */
*len = mio_htre_getcontentlen(req);
}
else if (req->flags & MIO_HTRE_ATTR_CHUNKED)
{
/* "Transfer-Encoding: chunked" take precedence over "Content-Length: XXX".
*
* [RFC7230]
* If a message is received with both a Transfer-Encoding and a
* Content-Length header field, the Transfer-Encoding overrides the
* Content-Length. */
typedef int (*mio_htre_concb_t) (
mio_htre_t* re,
const mio_bch_t* ptr,
mio_oow_t len,
void* ctx
);
int cgi_forward_client_to_peer (mio_htre_t* re, const mio_bch_t* ptr, mio_oow_t len, void* ctx)
{
mio_dev_pro_t* pro = (mio_dev_pro_t*)ctx;
return mio_dev_pro_write(pro, ptr, len, MIO_NULL);
/* TODO: if there are too many pending write requests, stop read event on the client */
return -1; /* unable to determine content-length in advance */
}
else if (req->flags & MIO_HTRE_ATTR_LENGTH)
{
*len = req->attr.content_length;
}
else
{
/* no content */
*len = 0;
}
return 0;
}
int mio_svc_htts_docgi (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* req, const mio_bch_t* docroot)
{
mio_dev_pro_t* pro = MIO_NULL;
mio_t* mio = htts->mio;
mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(csck);
cgi_state_t* cgi_state = MIO_NULL;
cgi_peer_xtn_t* cgi_peer;
mio_oow_t req_content_length;
mio_dev_pro_make_t mi;
if (get_request_content_length(req, &req_content_length) <= -1)
{
/* chunked - the length is not know in advance */
/* don't start the process yet. */
/* option 1. send 411 Length Required */
/* option 2. support chunked message box in the request */
/* option 3[non standard]. set CONTENT_LENGTH to -1 and indicate the end of INPUT by sending EOF */
req_content_length = MIO_TYPE_MAX(mio_oow_t); /* TODO: change type or add another variable ? */
}
MIO_MEMSET (&mi, 0, MIO_SIZEOF(mi));
mi.flags = MIO_DEV_PRO_READOUT /*| MIO_DEV_PRO_READERR*/ | MIO_DEV_PRO_WRITEIN /*| MIO_DEV_PRO_FORGET_CHILD*/;
mi.flags = MIO_DEV_PRO_READOUT | MIO_DEV_PRO_ERRTONUL | MIO_DEV_PRO_WRITEIN /*| MIO_DEV_PRO_FORGET_CHILD*/;
mi.cmd = mio_htre_getqpath(req); /* TODO: combine it with docroot */
mi.on_read = cgi_peer_on_read;
mi.on_write = cgi_peer_on_write;
mi.on_close = cgi_peer_on_close;
/* TODO: create cgi environment variables... */
pro = mio_dev_pro_make(htts->mio, 0, &mi);
if (MIO_UNLIKELY(!pro)) goto oops;
cgi_state = mio_callocmem(mio, MIO_SIZEOF(*cgi_state));
if (MIO_UNLIKELY(!cgi_state)) goto oops;
/* THE process device must be ready for I/O */
if (mio_htre_getcontentlen(req) > 0)
cgi_state->htts = htts; /*TODO: delete this field after rsrd renewal? */
cgi_state->on_kill = cgi_state_on_kill;
cgi_state->cli = cli;
/*cgi_state->num_pending_writes_to_client = 0;
cgi_state->num_pending_writes_to_peer = 0;*/
cgi_state->req = req;
cgi_state->req_content_length = req_content_length;
cgi_state->cli_org_on_read = csck->on_read;
cgi_state->cli_org_on_write = csck->on_write;
csck->on_read = cgi_client_on_read;
csck->on_write = cgi_client_on_write;
RSRCL_APPEND_RSRC (&cli->rsrc, cgi_state); /* attach it to the client information */
/* TODO: create cgi environment variables... */
/* TODO:
* never put Expect: 100-continue to environment variable
*/
cgi_state->peer = mio_dev_pro_make(mio, MIO_SIZEOF(*cgi_peer), &mi);
if (MIO_UNLIKELY(!cgi_state->peer)) goto oops;
cgi_peer = mio_dev_pro_getxtn(cgi_state->peer);
cgi_peer->state = cgi_state;
cgi_state->peer_htrd = mio_htrd_open(mio, MIO_SIZEOF(*cgi_peer));
if (MIO_UNLIKELY(!cgi_state->peer_htrd)) goto oops;
cgi_peer = mio_htrd_getxtn(cgi_state->peer_htrd);
cgi_peer->state = cgi_state;
/* TODO: any other handling before this? */
/* I CAN't SIMPLY WRITE LIKE THIS, the cgi must require this.
* send contents depending how cgi want Expect: continue handling to be handled */
if (req->flags & MIO_HTRE_ATTR_EXPECT100)
{
if (mio_dev_pro_write(pro, mio_htre_getcontentptr(req), mio_htre_getcontentlen(req), MIO_NULL) <= -1) goto oops;
/* TODO: Expect: 100-continue? who should handle this? cgi? or the http server? */
if (mio_comp_http_version_numbers(&req->version, 1, 1) >= 0 && req_content_length > 0)
{
/*
* Don't send 100 Continue if http verions is lower than 1.1
* [RFC7231]
* A server that receives a 100-continue expectation in an HTTP/1.0
* request MUST ignore that expectation.
*
* Don't send 100 Continue if expected content lenth is 0.
* [RFC7231]
* A server MAY omit sending a 100 (Continue) response if it has
* already received some or all of the message body for the
* corresponding request, or if the framing indicates that there is
* no message body.
*/
cgi_state->num_pending_writes_to_client++;
if (mio_dev_sck_write(csck, "HTTP/1.1 100 Continue\r\n", 23, MIO_NULL, MIO_NULL) <= -1)
{
cgi_state->num_pending_writes_to_client--;
goto oops;
}
}
}
else if (req->flags & MIO_HTRE_ATTR_EXPECT)
{
mio_htre_discardcontent (req);
req->flags &= ~MIO_HTRE_ATTR_KEEPALIVE; /* to cause sendstatus() to close */
if (mio_svc_htts_sendstatus(htts, csck, req, 417, MIO_NULL) <= -1) goto oops;
}
mio_htre_setconcb (req, cgi_forward_client_to_peer, pro);
if (req_content_length > 0)
{
if (mio_htre_getcontentlen(req) > 0)
{
cgi_state->num_pending_writes_to_peer++;
if (mio_dev_pro_write(cgi_state->peer, mio_htre_getcontentptr(req), mio_htre_getcontentlen(req), MIO_NULL) <= -1)
{
cgi_state->num_pending_writes_to_peer--;
goto oops;
}
}
}
#if 0
csck->on_read = cgi_client_on_read;
cssk->on_write = cgi_client_on_write;
csck->on_disconnect = cgi_client_on_disconnect;
#endif
#if 0
rsrc = mio_svc_htts_rsrc_make(htts, csck, rsrc_cgi_on_read, rsrc_cgi_on_write, rsrc_cgi_on_kill, MIO_SIZEOF(*rsrc_cgi));
if (MIO_UNLIKELY(!rsrc)) goto oops;
#endif
if (req->state & (MIO_HTRE_COMPLETED | MIO_HTRE_DISCARDED))
{
/* disable input watching from the client */
if (mio_dev_sck_read(csck, 0) <= -1) goto oops;
}
return 0;
oops:
if (pro) mio_dev_pro_kill (pro);
MIO_DEBUG2 (mio, "HTTS(%p) - FAILURE in docgi - socket(%p)\n", htts, csck);
cgi_state->on_kill (cgi_state); /* TODO: call rsrc_free... */
mio_dev_sck_halt (csck);
return -1;
}
@ -1213,13 +1348,14 @@ oops:
return -1;
}
int mio_svc_htts_sendstatus (mio_svc_htts_t* htts, mio_dev_sck_t* csck, int status_code, mio_http_method_t method, const mio_http_version_t* version, int keepalive, void* extra)
int mio_svc_htts_sendstatus (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* req, int status_code, void* extra)
{
/* TODO: change this to use send status */
mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(csck);
mio_bch_t text[1024]; /* TODO: make this buffer dynamic or scalable */
mio_bch_t dtbuf[64];
mio_oow_t x;
mio_http_version_t* version = mio_htre_getversion(req);
const mio_bch_t* extrapre = "";
const mio_bch_t* extrapst = "";
@ -1279,7 +1415,7 @@ int mio_svc_htts_sendstatus (mio_svc_htts_t* htts, mio_dev_sck_t* csck, int stat
version->major, version->minor, status_code, mio_http_status_to_bcstr(status_code),
htts->server_name,
dtbuf, /* DATE */
(keepalive? "keep-alive": "close"), /* connection */
((req->flags & MIO_HTRE_ATTR_KEEPALIVE)? "keep-alive": "close"), /* connection */
mio_count_bcstr(text), /* content length */
extrapre, extraval, extraval, text
);
@ -1292,7 +1428,8 @@ int mio_svc_htts_sendstatus (mio_svc_htts_t* htts, mio_dev_sck_t* csck, int stat
mio_dev_sck_halt (csck);
return -1;
}
else if (!keepalive)
if (!(req->flags & MIO_HTRE_ATTR_KEEPALIVE))
{
/* arrange to close the writing end */
if (mio_dev_sck_write(csck, MIO_NULL, 0, MIO_NULL, MIO_NULL) <= -1)