simplifying http server code while debugging and enhancing it

This commit is contained in:
hyung-hwan 2020-05-18 08:59:25 +00:00
parent f6aee2c356
commit b687e97590
4 changed files with 292 additions and 314 deletions

View File

@ -10,6 +10,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <pthread.h> #include <pthread.h>
#define CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH
typedef struct mio_svc_htts_cli_t mio_svc_htts_cli_t; typedef struct mio_svc_htts_cli_t mio_svc_htts_cli_t;
struct mio_svc_htts_cli_t struct mio_svc_htts_cli_t
@ -120,7 +121,7 @@ static int test_func_handler (int rfd, int wfd)
return -1; return -1;
} }
static int process_request (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* req, int peek) static int process_request (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* req)
{ {
//server_xtn_t* server_xtn = GET_SERVER_XTN(htts, client->server); //server_xtn_t* server_xtn = GET_SERVER_XTN(htts, client->server);
//mio_htts_task_t* task; //mio_htts_task_t* task;
@ -133,18 +134,13 @@ static int process_request (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_
* any more. once it's decoded in the peek mode, * any more. once it's decoded in the peek mode,
* the decoded query path is made available in the * the decoded query path is made available in the
* non-peek mode as well */ * non-peek mode as well */
if (peek)
{
mio_htre_perdecqpath(req);
/* TODO: proper request logging */ mio_htre_perdecqpath(req);
/* TODO: proper request logging */
MIO_DEBUG2 (htts->mio, "[REQ] %s %s\n", mio_htre_getqmethodname(req), mio_htre_getqpath(req));
MIO_DEBUG2 (htts->mio, "[PEEK] %s %s\n", mio_htre_getqmethodname(req), mio_htre_getqpath(req));
}
else
{
MIO_DEBUG2 (htts->mio, "[NOPEEK] %s %s\n", mio_htre_getqmethodname(req), mio_htre_getqpath(req));
}
#if 0 #if 0
mio_printf (MIO_T("================================\n")); mio_printf (MIO_T("================================\n"));
@ -168,93 +164,71 @@ if (mio_htre_getcontentlen(req) > 0)
#endif #endif
mth = mio_htre_getqmethodtype(req); mth = mio_htre_getqmethodtype(req);
if (peek) /* determine what to do once the header fields are all received.
{ * i don't want to delay this until the contents are received.
/* determine what to do once the header fields are all received. * if you don't like this behavior, you must implement your own
* i don't want to delay this until the contents are received. * callback function for request handling. */
* if you don't like this behavior, you must implement your own
* callback function for request handling. */
#if 0 #if 0
/* TODO support X-HTTP-Method-Override */ /* TODO support X-HTTP-Method-Override */
if (data.method == MIO_HTTP_POST) if (data.method == MIO_HTTP_POST)
{
tmp = mio_htre_getheaderval(req, MIO_MT("X-HTTP-Method-Override"));
if (tmp)
{ {
tmp = mio_htre_getheaderval(req, MIO_MT("X-HTTP-Method-Override")); /*while (tmp->next) tmp = tmp->next;*/ /* get the last value */
if (tmp) data.method = mio_mbstohttpmethod (tmp->ptr);
{
/*while (tmp->next) tmp = tmp->next;*/ /* get the last value */
data.method = mio_mbstohttpmethod (tmp->ptr);
}
} }
}
#endif #endif
if (mth == MIO_HTTP_CONNECT) #if 0
if (mth == MIO_HTTP_CONNECT)
{
/* CONNECT method must not have content set.
* however, arrange to discard it if so.
*
* NOTE: CONNECT is implemented to ignore many headers like
* 'Expect: 100-continue' and 'Connection: keep-alive'. */
mio_htre_discardcontent (req);
}
else
{
/* this part can be check in actual mio_svc_htts_doXXX() functions.
* some doXXX handlers may not require length for POST.
* it may be able to simply accept till EOF? or treat as if CONTENT_LENGTH is 0*/
if (mth == MIO_HTTP_POST && !(req->flags & (MIO_HTRE_ATTR_LENGTH | MIO_HTRE_ATTR_CHUNKED)))
{ {
/* CONNECT method must not have content set. /* POST without Content-Length nor not chunked */
* however, arrange to discard it if so. mio_htre_discardcontent (req);
* /* 411 Length Required - can't keep alive. Force disconnect */
* NOTE: CONNECT is implemented to ignore many headers like req->flags &= ~MIO_HTRE_ATTR_KEEPALIVE; /* to cause sendstatus() to close */
* 'Expect: 100-continue' and 'Connection: keep-alive'. */ if (mio_svc_htts_sendstatus(htts, csck, req, 411, MIO_NULL) <= -1) mio_dev_sck_halt (csck); /*TODO: redo this sendstatus */
mio_htre_discardcontent (req);
} }
else else
{ {
if (mth == MIO_HTTP_POST && !(req->flags & (MIO_HTRE_ATTR_LENGTH | MIO_HTRE_ATTR_CHUNKED))) #endif
const mio_bch_t* qpath = mio_htre_getqpath(req);
if (mio_svc_htts_docgi(htts, csck, req, "") <= -1)
{ {
/* POST without Content-Length nor not chunked */ mio_dev_sck_halt (csck);
mio_htre_discardcontent (req);
/* 411 Length Required - can't keep alive. Force disconnect */
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
{
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 0
/* /*
if (mio_comp_bcstr(qpath, "/testfunc", 0) == 0) if (mio_comp_bcstr(qpath, "/testfunc", 0) == 0)
{ {
if (mio_svc_htts_sendcgi(htts, csck, test_func_handler, req) <= -1) if (mio_svc_htts_sendcgi(htts, csck, test_func_handler, req) <= -1)
{
mio_htre_discardcontent (req);
mio_dev_sck_halt (csck);
}
}
else */if (mio_svc_htts_sendfile(htts, csck, qpath, 200, mth, mio_htre_getversion(req), (req->flags & MIO_HTRE_ATTR_KEEPALIVE)) <= -1)
{ {
mio_htre_discardcontent (req); mio_htre_discardcontent (req);
mio_dev_sck_halt (csck); mio_dev_sck_halt (csck);
} }
#endif
} }
} else */if (mio_svc_htts_sendfile(htts, csck, qpath, 200, mth, mio_htre_getversion(req), (req->flags & MIO_HTRE_ATTR_KEEPALIVE)) <= -1)
} {
else mio_htre_discardcontent (req);
{ mio_dev_sck_halt (csck);
/* 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 */
/* TODO: arrange to forward all raw bytes */
}
else if (req->flags & MIO_HTRE_ATTR_PROXIED)
{
/* the contents should be proxied.
* do nothing locally */
}
else
{
/* when the request is handled locally,
* there's nothing special to do here */
} }
} }
@ -266,6 +240,7 @@ if (mio_htre_getcontentlen(req) > 0)
//// if (MIO_UNLIKELY(!task)) goto oops; //// if (MIO_UNLIKELY(!task)) goto oops;
} }
} }
#endif
return 0; return 0;
@ -278,20 +253,15 @@ static int client_htrd_peek_request (mio_htrd_t* htrd, mio_htre_t* req)
{ {
htrd_xtn_t* htrdxtn = (htrd_xtn_t*)mio_htrd_getxtn(htrd); htrd_xtn_t* htrdxtn = (htrd_xtn_t*)mio_htrd_getxtn(htrd);
mio_svc_htts_cli_t* sckxtn = (mio_svc_htts_cli_t*)mio_dev_sck_getxtn(htrdxtn->sck); mio_svc_htts_cli_t* sckxtn = (mio_svc_htts_cli_t*)mio_dev_sck_getxtn(htrdxtn->sck);
return process_request(sckxtn->htts, htrdxtn->sck, req, 1); return process_request(sckxtn->htts, htrdxtn->sck, req);
} }
static int client_htrd_poke_request (mio_htrd_t* htrd, mio_htre_t* req)
{
htrd_xtn_t* htrdxtn = (htrd_xtn_t*)mio_htrd_getxtn(htrd);
mio_svc_htts_cli_t* sckxtn = (mio_svc_htts_cli_t*)mio_dev_sck_getxtn(htrdxtn->sck);
return process_request(sckxtn->htts, htrdxtn->sck, req, 0);
}
static mio_htrd_recbs_t client_htrd_recbs = static mio_htrd_recbs_t client_htrd_recbs =
{ {
client_htrd_peek_request, client_htrd_peek_request,
client_htrd_poke_request MIO_NULL,
MIO_NULL
}; };
static int init_client (mio_svc_htts_cli_t* cli, mio_dev_sck_t* sck) static int init_client (mio_svc_htts_cli_t* cli, mio_dev_sck_t* sck)
@ -728,10 +698,11 @@ struct cgi_state_t
mio_htrd_t* peer_htrd; mio_htrd_t* peer_htrd;
mio_svc_htts_cli_t* cli; mio_svc_htts_cli_t* cli;
mio_http_version_t req_version; /* client request */ mio_http_version_t req_version; /* client request */
mio_oow_t req_content_length; /* client request content length */
cgi_state_res_mode_t res_mode_to_cli; unsigned int req_content_length_unlimited: 1;
unsigned int peer_eof: 1; unsigned int peer_eof: 1;
mio_oow_t req_content_length; /* client request content length */
cgi_state_res_mode_t res_mode_to_cli;
mio_dev_sck_on_read_t cli_org_on_read; mio_dev_sck_on_read_t cli_org_on_read;
mio_dev_sck_on_write_t cli_org_on_write; mio_dev_sck_on_write_t cli_org_on_write;
@ -759,6 +730,71 @@ static void cgi_state_halt (cgi_state_t* cgi_state)
} }
static int cgi_state_write_to_client (cgi_state_t* cgi_state, const void* data, mio_iolen_t dlen)
{
cgi_state->num_pending_writes_to_client++;
if (mio_dev_sck_write(cgi_state->cli->sck, data, dlen, MIO_NULL, MIO_NULL) <= -1)
{
cgi_state->num_pending_writes_to_client--;
return -1;
}
if (cgi_state->num_pending_writes_to_client > CGI_STATE_PENDING_IO_THRESHOLD)
{
if (mio_dev_pro_read(cgi_state->peer, MIO_DEV_PRO_OUT, 0) <= -1) return -1;
}
return 0;
}
static int cgi_state_writev_to_client (cgi_state_t* cgi_state, mio_iovec_t* iov, mio_iolen_t iovcnt)
{
cgi_state->num_pending_writes_to_client++;
if (mio_dev_sck_writev(cgi_state->cli->sck, iov, iovcnt, MIO_NULL, MIO_NULL) <= -1)
{
cgi_state->num_pending_writes_to_client--;
return -1;
}
if (cgi_state->num_pending_writes_to_client > CGI_STATE_PENDING_IO_THRESHOLD)
{
if (mio_dev_pro_read(cgi_state->peer, MIO_DEV_PRO_OUT, 0) <= -1) return -1;
}
return 0;
}
static int cgi_state_write_to_peer (cgi_state_t* cgi_state, const void* data, mio_iolen_t dlen)
{
cgi_state->num_pending_writes_to_peer++;
if (mio_dev_pro_write(cgi_state->peer, data, dlen, MIO_NULL) <= -1)
{
cgi_state->num_pending_writes_to_peer--;
return -1;
}
/* TODO: check if it's already finished or something.. */
if (cgi_state->num_pending_writes_to_peer > CGI_STATE_PENDING_IO_THRESHOLD)
{
if (mio_dev_sck_read(cgi_state->cli->sck, 0) <= -1) return -1;
}
return 0;
}
static int cgi_state_send_final_status_to_client (cgi_state_t* cgi_state, int status_code)
{
mio_svc_htts_cli_t* cli = cgi_state->cli;
mio_bch_t dtbuf[64];
mio_svc_htts_fmtgmtime (cli->htts, MIO_NULL, dtbuf, MIO_COUNTOF(dtbuf));
if (mio_becs_fmt(cli->sbuf, "HTTP/%d.%d %d %hs\r\nServer: %hs\r\nDate: %s\r\nConnection: close\r\nContent-Length: 0\r\n\r\n",
cgi_state->req_version.major, cgi_state->req_version.minor,
status_code, mio_http_status_to_bcstr(status_code),
cli->htts->server_name, dtbuf) == (mio_oow_t)-1) return -1;
return cgi_state_write_to_client(cgi_state, MIO_BECS_PTR(cli->sbuf), MIO_BECS_LEN(cli->sbuf));
}
static void cgi_state_on_kill (cgi_state_t* cgi_state) static void cgi_state_on_kill (cgi_state_t* cgi_state)
{ {
printf ("**** CGI_STATE_ON_KILL \n"); printf ("**** CGI_STATE_ON_KILL \n");
@ -792,7 +828,7 @@ printf ("**** CGI_STATE_ON_KILL \n");
cgi_state->cli_org_on_write = MIO_NULL; cgi_state->cli_org_on_write = MIO_NULL;
} }
cgi_state->cli->htrd->recbs.push_content = MIO_NULL; mio_htrd_setrecbs (cgi_state->cli->htrd, &client_htrd_recbs); /* restore the callbacks */
if (MIO_BECS_LEN(&cgi_state->cli->rbuf) > 0) if (MIO_BECS_LEN(&cgi_state->cli->rbuf) > 0)
{ {
@ -806,21 +842,6 @@ printf ("**** CGI_STATE_ON_KILL \n");
} }
} }
static int cgi_send_simple_status_to_client (cgi_state_t* cgi_state, int status_code)
{
mio_svc_htts_cli_t* cli = cgi_state->cli;
mio_bch_t dtbuf[64];
mio_svc_htts_fmtgmtime (cli->htts, MIO_NULL, dtbuf, MIO_COUNTOF(dtbuf));
if (mio_becs_fmt(cli->sbuf, "HTTP/%d.%d %d %hs\r\nServer: %hs\r\nDate: %s\r\nConnection: close\r\nContent-Length: 0\r\n\r\n",
cgi_state->req_version.major, cgi_state->req_version.minor,
status_code, mio_http_status_to_bcstr(status_code),
cli->htts->server_name, dtbuf) == (mio_oow_t)-1) return -1;
return mio_dev_sck_write(cli->sck, MIO_BECS_PTR(cli->sbuf), MIO_BECS_LEN(cli->sbuf), MIO_NULL, MIO_NULL);
}
static void cgi_peer_on_close (mio_dev_pro_t* pro, mio_dev_pro_sid_t sid) static void cgi_peer_on_close (mio_dev_pro_t* pro, mio_dev_pro_sid_t sid)
{ {
mio_t* mio = pro->mio; mio_t* mio = pro->mio;
@ -829,6 +850,7 @@ static void cgi_peer_on_close (mio_dev_pro_t* pro, mio_dev_pro_sid_t sid)
if (!cgi_state) return; /* cgi state already gone */ if (!cgi_state) return; /* cgi state already gone */
printf (">>>>>>>>> PEER ON CLOSE sid=%d\n", (int)sid);
printf (">> cgi_state %p\n", cgi_state); printf (">> cgi_state %p\n", cgi_state);
printf (">> cgi_state->peer %p\n", cgi_state->peer); printf (">> cgi_state->peer %p\n", cgi_state->peer);
printf (">> cgi_state->cli %p\n", cgi_state->cli); printf (">> cgi_state->cli %p\n", cgi_state->cli);
@ -844,9 +866,15 @@ printf (">> cgi_state->cli->htts %p\n", cgi_state->cli->htts);
MIO_ASSERT (mio, cgi_state->peer == pro); MIO_ASSERT (mio, cgi_state->peer == pro);
MIO_DEBUG4 (mio, "HTTS(%p) - peer %p(pid=%d) closing slave[%d]\n", cgi_state->cli->htts, pro, (int)pro->child_pid, sid); MIO_DEBUG4 (mio, "HTTS(%p) - peer %p(pid=%d) closing slave[%d]\n", cgi_state->cli->htts, pro, (int)pro->child_pid, sid);
if (sid == MIO_DEV_PRO_OUT) if (sid == MIO_DEV_PRO_OUT && !cgi_state->peer_eof)
{ {
/* check if it's sending... */
cgi_state->peer_eof = 1; cgi_state->peer_eof = 1;
if (cgi_state->res_mode_to_cli == CGI_STATE_RES_MODE_CHUNKED &&
cgi_state_write_to_client(cgi_state, "0\r\n\r\n", 5) <= -1)
{
cgi_state_halt (cgi_state);
}
} }
} }
} }
@ -859,6 +887,7 @@ static int cgi_peer_on_read (mio_dev_pro_t* pro, mio_dev_pro_sid_t sid, const vo
MIO_ASSERT (mio, sid == MIO_DEV_PRO_OUT); /* since MIO_DEV_PRO_ERRTONUL is used, there should be no input from MIO_DEV_PRO_ERR */ MIO_ASSERT (mio, sid == MIO_DEV_PRO_OUT); /* since MIO_DEV_PRO_ERRTONUL is used, there should be no input from MIO_DEV_PRO_ERR */
printf (">>>>>>>>> PEER ON READ %d\n", dlen);
if (dlen <= -1) if (dlen <= -1)
{ {
MIO_DEBUG3 (mio, "HTTPS(%p) - read error from peer %p(pid=%u)\n", cgi_state->cli->htts, pro, (unsigned int)pro->child_pid); MIO_DEBUG3 (mio, "HTTPS(%p) - read error from peer %p(pid=%u)\n", cgi_state->cli->htts, pro, (unsigned int)pro->child_pid);
@ -872,9 +901,8 @@ static int cgi_peer_on_read (mio_dev_pro_t* pro, mio_dev_pro_sid_t sid, const vo
MIO_DEBUG3 (mio, "HTTPS(%p) - EOF from peer %p(pid=%u)\n", cgi_state->cli->htts, pro, (unsigned int)pro->child_pid); MIO_DEBUG3 (mio, "HTTPS(%p) - EOF from peer %p(pid=%u)\n", cgi_state->cli->htts, pro, (unsigned int)pro->child_pid);
cgi_state->peer_eof = 1; cgi_state->peer_eof = 1;
/* don't care about cgi_state->num_pending_writes_to_client since this is the last read */ if (cgi_state->res_mode_to_cli == CGI_STATE_RES_MODE_CHUNKED &&
if (cgi_state->res_mode_to_cli == CGI_STATE_RES_MODE_CHUNKED && cgi_state_write_to_client(cgi_state, "0\r\n\r\n", 5) <= -1) goto oops;
mio_dev_sck_write(cgi_state->cli->sck, "0\r\n\r\n", 5, MIO_NULL, MIO_NULL) <= -1) goto oops;
} }
else else
{ {
@ -883,7 +911,7 @@ printf ( "QQQQQQQQQQQQQQQQQQ>>>>>>>>>>>>>>>>>> feeding to peer htrd.... dlen =>
if (mio_htrd_feed(cgi_state->peer_htrd, data, dlen, &rem) <= -1) if (mio_htrd_feed(cgi_state->peer_htrd, data, dlen, &rem) <= -1)
{ {
printf ( "FEEDING FAILURE TO PEER HTDDRD.. dlen => %d\n", (int)dlen); printf ( "FEEDING FAILURE TO PEER HTDDRD.. dlen => %d\n", (int)dlen);
cgi_send_simple_status_to_client (cgi_state, 500); /* don't care about error */ cgi_state_send_final_status_to_client (cgi_state, 500); /* don't care about error */
goto oops; goto oops;
} }
if (rem > 0) if (rem > 0)
@ -980,7 +1008,17 @@ printf ("CGI PEER HTRD PEEK...\n");
} }
if (mio_becs_cat(cli->sbuf, "\r\n") == (mio_oow_t)-1) return -1; if (mio_becs_cat(cli->sbuf, "\r\n") == (mio_oow_t)-1) return -1;
return mio_dev_sck_write(cli->sck, MIO_BECS_PTR(cli->sbuf), MIO_BECS_LEN(cli->sbuf), MIO_NULL, MIO_NULL); return cgi_state_write_to_client(cgi_state, MIO_BECS_PTR(cli->sbuf), MIO_BECS_LEN(cli->sbuf));
}
static int cgi_peer_htrd_poke (mio_htrd_t* htrd, mio_htre_t* req)
{
/* client request got completed */
cgi_peer_xtn_t* cgi_peer = mio_htrd_getxtn(htrd);
cgi_state_t* cgi_state = cgi_peer->state;
/* indicate EOF to the client */
return cgi_state_write_to_client(cgi_state, MIO_NULL, 0);
} }
static int cgi_peer_htrd_push_content (mio_htrd_t* htrd, mio_htre_t* req, const mio_bch_t* data, mio_oow_t dlen) static int cgi_peer_htrd_push_content (mio_htrd_t* htrd, mio_htre_t* req, const mio_bch_t* data, mio_oow_t dlen)
@ -1009,23 +1047,14 @@ static int cgi_peer_htrd_push_content (mio_htrd_t* htrd, mio_htre_t* req, const
iov[2].iov_ptr = "\r\n"; iov[2].iov_ptr = "\r\n";
iov[2].iov_len = 2; iov[2].iov_len = 2;
cgi_state->num_pending_writes_to_client++; if (cgi_state_writev_to_client(cgi_state, iov, MIO_COUNTOF(iov)) <= -1) goto oops;
if (mio_dev_sck_writev(cgi_state->cli->sck, iov, MIO_COUNTOF(iov), MIO_NULL, MIO_NULL) <= -1)
{
cgi_state->num_pending_writes_to_client--;
goto oops;
}
break; break;
} }
case CGI_STATE_RES_MODE_CLOSE: case CGI_STATE_RES_MODE_CLOSE:
case CGI_STATE_RES_MODE_LENGTH: case CGI_STATE_RES_MODE_LENGTH:
cgi_state->num_pending_writes_to_client++; if (cgi_state_write_to_client(cgi_state, data, dlen) <= -1) goto oops;
if (mio_dev_sck_write(cgi_state->cli->sck, data, dlen, MIO_NULL, MIO_NULL) <= -1) break;
{
cgi_state->num_pending_writes_to_client--;
goto oops;
}
} }
if (cgi_state->num_pending_writes_to_client > CGI_STATE_PENDING_IO_THRESHOLD) if (cgi_state->num_pending_writes_to_client > CGI_STATE_PENDING_IO_THRESHOLD)
@ -1042,10 +1071,22 @@ oops:
static mio_htrd_recbs_t cgi_peer_htrd_recbs = static mio_htrd_recbs_t cgi_peer_htrd_recbs =
{ {
cgi_peer_htrd_peek, cgi_peer_htrd_peek,
MIO_NULL, cgi_peer_htrd_poke,
cgi_peer_htrd_push_content cgi_peer_htrd_push_content
}; };
static int cgi_client_htrd_poke (mio_htrd_t* htrd, mio_htre_t* req)
{
/* client request got completed */
htrd_xtn_t* htrdxtn = mio_htrd_getxtn(htrd);
mio_dev_sck_t* sck = htrdxtn->sck;
mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck);
cgi_state_t* cgi_state = RSRCL_FIRST_RSRC(&cli->rsrc);
/* indicate EOF to the client peer */
return cgi_state_write_to_peer(cgi_state, MIO_NULL, 0);
}
static int cgi_client_htrd_push_content (mio_htrd_t* htrd, mio_htre_t* req, const mio_bch_t* data, mio_oow_t dlen) static int cgi_client_htrd_push_content (mio_htrd_t* htrd, mio_htre_t* req, const mio_bch_t* data, mio_oow_t dlen)
{ {
htrd_xtn_t* htrdxtn = mio_htrd_getxtn(htrd); htrd_xtn_t* htrdxtn = mio_htrd_getxtn(htrd);
@ -1054,25 +1095,7 @@ static int cgi_client_htrd_push_content (mio_htrd_t* htrd, mio_htre_t* req, cons
cgi_state_t* cgi_state = RSRCL_FIRST_RSRC(&cli->rsrc); cgi_state_t* cgi_state = RSRCL_FIRST_RSRC(&cli->rsrc);
MIO_ASSERT (sck->mio, cli->sck == sck); MIO_ASSERT (sck->mio, cli->sck == sck);
return cgi_state_write_to_peer(cgi_state, data, dlen);
cgi_state->num_pending_writes_to_peer++;
if (mio_dev_pro_write(cgi_state->peer, data, dlen, MIO_NULL) <= -1)
{
cgi_state->num_pending_writes_to_peer--;
goto oops;
}
#if 0
if (cgi_state->num_pending_writes_to_peer > CGI_STATE_PENDING_IO_THRESHOLD)
{
if (mio_dev_sck_read(cli->sck, 0) <= -1) goto oops; /* disable input watching */
}
#endif
return 0;
oops:
return -1;
} }
static int cgi_peer_on_write (mio_dev_pro_t* pro, mio_iolen_t wrlen, void* wrctx) static int cgi_peer_on_write (mio_dev_pro_t* pro, mio_iolen_t wrlen, void* wrctx)
@ -1088,13 +1111,28 @@ static int cgi_peer_on_write (mio_dev_pro_t* pro, mio_iolen_t wrlen, void* wrctx
MIO_DEBUG3 (mio, "HTTS(%p) - unable to write to peer %p(pid=%u)\n", cgi_state->cli->htts, pro, (int)pro->child_pid); MIO_DEBUG3 (mio, "HTTS(%p) - unable to write to peer %p(pid=%u)\n", cgi_state->cli->htts, pro, (int)pro->child_pid);
goto oops; goto oops;
} }
else if (wrlen == 0)
cgi_state->num_pending_writes_to_peer--;
if (cgi_state->num_pending_writes_to_peer == CGI_STATE_PENDING_IO_THRESHOLD)
{ {
// TODO: check if (cli has anything to read...) if so ... /* indicated EOF */
if (mio_dev_sck_read(cgi_state->cli->sck, 1) <= -1) goto oops; /* do nothing here as i didn't incremented num_pending_writes_to_peer when making the write request */
cgi_state->num_pending_writes_to_peer--;
MIO_ASSERT (mio, cgi_state->num_pending_writes_to_peer == 0);
MIO_DEBUG3 (mio, "HTTS(%p) - indicated EOF to peer %p(pid=%u)\n", cgi_state->cli->htts, pro, (int)pro->child_pid);
/* indicated EOF to the peer side. i need no more data from the client side.
* i don't need to enable input watching in the client side either */
} }
else
{
MIO_ASSERT (mio, cgi_state->num_pending_writes_to_peer > 0);
cgi_state->num_pending_writes_to_peer--;
if (cgi_state->num_pending_writes_to_peer == CGI_STATE_PENDING_IO_THRESHOLD)
{
// TODO: check if (cli has anything to read...) if so ...
if (mio_dev_sck_read(cgi_state->cli->sck, 1) <= -1) goto oops;
}
}
return 0; return 0;
oops: oops:
@ -1130,7 +1168,8 @@ printf ("** HTTS - cgi client read %p %d -> htts:%p\n", sck, (int)len, cli->h
{ {
/* EOF on the client side. arrange to close */ /* EOF on the client side. arrange to close */
printf ("asking pro to close...\n"); printf ("asking pro to close...\n");
if (mio_dev_pro_write(cgi_state->peer, MIO_NULL, 0, MIO_NULL) <= -1) goto oops; /* HAVE I ALREADY CLOSED IT??? Check conflict is the poke handler */
return cgi_state_write_to_peer(cgi_state, MIO_NULL, 0);
} }
else else
{ {
@ -1164,24 +1203,39 @@ static int cgi_client_on_write (mio_dev_sck_t* sck, mio_iolen_t wrlen, void* wrc
if (wrlen <= -1) if (wrlen <= -1)
{ {
MIO_DEBUG3 (mio, "HTTPS(%p) - unable to write to client socket %p(%d)\n", sck->mio, sck, (int)sck->hnd); MIO_DEBUG3 (mio, "HTTPS(%p) - unable to write to client %p(%d)\n", sck->mio, sck, (int)sck->hnd);
goto oops; goto oops;
} }
printf ("MANAGED TO WRITE TO CLIENT>... %d\n", (int)wrlen); if (wrlen == 0)
cgi_state->num_pending_writes_to_client--;
if (cgi_state->peer && cgi_state->num_pending_writes_to_client == CGI_STATE_PENDING_IO_THRESHOLD)
{ {
// TODO: check if there is anything to read... */ /* if the connect is keep-alive, this part may not be called */
if (mio_dev_pro_read(cgi_state->peer, MIO_DEV_PRO_OUT, 1) <= -1) goto oops; cgi_state->num_pending_writes_to_client--;
MIO_ASSERT (mio, cgi_state->num_pending_writes_to_client == 0);
MIO_DEBUG3 (mio, "HTTS(%p) - indicated EOF to client %p(%d)\n", cgi_state->cli->htts, sck, (int)sck->hnd);
/* since EOF has been indicated to the client, it must not write to the client any further.
* this also means that i don't need any data from the peer side either.
* i don't need to enable input watching on the peer side */
} }
else
/* in case the peer died before peer read handler reads EOF */
if (cgi_state->peer_eof && cgi_state->num_pending_writes_to_client <= 0)
{ {
/*if (no_more_input is to be read from the client)*/ MIO_ASSERT (mio, cgi_state->num_pending_writes_to_client > 0);
cgi_state_halt (cgi_state); // must not use this... must kill the state ...
/* finished writing all */ printf ("MANAGED TO WRITE TO CLIENT>... %d\n", (int)wrlen);
cgi_state->num_pending_writes_to_client--;
if (cgi_state->peer && cgi_state->num_pending_writes_to_client == CGI_STATE_PENDING_IO_THRESHOLD)
{
// TODO: check if there is anything to read... */
if (mio_dev_pro_read(cgi_state->peer, MIO_DEV_PRO_OUT, 1) <= -1) goto oops;
}
/* in case the peer died before peer read handler reads EOF */
if (cgi_state->peer_eof && cgi_state->num_pending_writes_to_client <= 0)
{
/*if (no_more_input is to be read from the client)*/
cgi_state_halt (cgi_state); // must not use this... must kill the state ...
/* finished writing all */
}
} }
return 0; return 0;
@ -1193,12 +1247,7 @@ oops:
static int get_request_content_length (mio_htre_t* req, mio_oow_t* len) static int get_request_content_length (mio_htre_t* req, mio_oow_t* len)
{ {
if (req->state & (MIO_HTRE_COMPLETED | MIO_HTRE_DISCARDED)) if (req->flags & MIO_HTRE_ATTR_CHUNKED)
{
/* 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". /* "Transfer-Encoding: chunked" take precedence over "Content-Length: XXX".
* *
@ -1206,7 +1255,6 @@ static int get_request_content_length (mio_htre_t* req, mio_oow_t* len)
* If a message is received with both a Transfer-Encoding and a * If a message is received with both a Transfer-Encoding and a
* Content-Length header field, the Transfer-Encoding overrides the * Content-Length header field, the Transfer-Encoding overrides the
* Content-Length. */ * Content-Length. */
return -1; /* unable to determine content-length in advance */ return -1; /* unable to determine content-length in advance */
} }
else if (req->flags & MIO_HTRE_ATTR_LENGTH) else if (req->flags & MIO_HTRE_ATTR_LENGTH)
@ -1215,7 +1263,7 @@ static int get_request_content_length (mio_htre_t* req, mio_oow_t* len)
} }
else else
{ {
/* no content */ /* If no Content-Length is specified in a request, it's Content-Length: 0 */
*len = 0; *len = 0;
} }
return 0; return 0;
@ -1227,19 +1275,11 @@ int mio_svc_htts_docgi (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* r
mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(csck); mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(csck);
cgi_state_t* cgi_state = MIO_NULL; cgi_state_t* cgi_state = MIO_NULL;
cgi_peer_xtn_t* cgi_peer; cgi_peer_xtn_t* cgi_peer;
mio_oow_t req_content_length;
mio_dev_pro_make_t mi; mio_dev_pro_make_t mi;
int enable_input;
if (get_request_content_length(req, &req_content_length) <= -1) /* ensure that you call this function before any contents is received */
{ MIO_ASSERT (mio, mio_htre_getcontentlen(req) == 0);
/* 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)); MIO_MEMSET (&mi, 0, MIO_SIZEOF(mi));
mi.flags = MIO_DEV_PRO_READOUT | MIO_DEV_PRO_ERRTONUL | 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*/;
@ -1258,7 +1298,9 @@ int mio_svc_htts_docgi (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* r
/*cgi_state->num_pending_writes_to_client = 0; /*cgi_state->num_pending_writes_to_client = 0;
cgi_state->num_pending_writes_to_peer = 0;*/ cgi_state->num_pending_writes_to_peer = 0;*/
cgi_state->req_version = *mio_htre_getversion(req); cgi_state->req_version = *mio_htre_getversion(req);
cgi_state->req_content_length = req_content_length;
cgi_state->req_content_length_unlimited = 0;
if (get_request_content_length(req, &cgi_state->req_content_length) <= -1) cgi_state->req_content_length_unlimited = 1;
cgi_state->cli_org_on_read = csck->on_read; cgi_state->cli_org_on_read = csck->on_read;
cgi_state->cli_org_on_write = csck->on_write; cgi_state->cli_org_on_write = csck->on_write;
@ -1283,14 +1325,27 @@ int mio_svc_htts_docgi (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* r
cgi_peer = mio_htrd_getxtn(cgi_state->peer_htrd); cgi_peer = mio_htrd_getxtn(cgi_state->peer_htrd);
cgi_peer->state = cgi_state; 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 !defined(CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH)
if (cgi_state->req_content_length_unlimited)
{
/* Transfer-Encoding is chunked. no content-length is known in advance. */
/* option 1. buffer contents. if it gets too large, send 413 Request Entity Too Large.
* option 2. send 411 Length Required immediately
* option 3. set Content-Length to -1 and use EOF to indicate the end of content [Non-Standard] */
if (cgi_state_send_final_status_to_client(cgi_state, 411) <= -1) goto oops;
}
#endif
if (req->flags & MIO_HTRE_ATTR_EXPECT100) if (req->flags & MIO_HTRE_ATTR_EXPECT100)
{ {
/* TODO: Expect: 100-continue? who should handle this? cgi? or the http server? */ /* 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) /* CAN I LET the cgi SCRIPT handle this? */
if (mio_comp_http_version_numbers(&req->version, 1, 1) >= 0 &&
(cgi_state->req_content_length_unlimited || cgi_state->req_content_length > 0))
{ {
/* /*
* Don't send 100 Continue if http verions is lower than 1.1 * Don't send 100 Continue if http verions is lower than 1.1
@ -1309,47 +1364,55 @@ int mio_svc_htts_docgi (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* r
mio_oow_t msglen; mio_oow_t msglen;
msglen = mio_fmttobcstr(mio, msgbuf, MIO_COUNTOF(msgbuf), "HTTP/%d.%d 100 Continue\r\n\r\n", cgi_state->req_version.major, cgi_state->req_version.minor); msglen = mio_fmttobcstr(mio, msgbuf, MIO_COUNTOF(msgbuf), "HTTP/%d.%d 100 Continue\r\n\r\n", cgi_state->req_version.major, cgi_state->req_version.minor);
cgi_state->num_pending_writes_to_client++; if (cgi_state_write_to_client(cgi_state, msgbuf, msglen) <= -1) goto oops;
if (mio_dev_sck_write(csck, msgbuf, msglen, MIO_NULL, MIO_NULL) <= -1)
{
cgi_state->num_pending_writes_to_client--;
goto oops;
}
} }
} }
else if (req->flags & MIO_HTRE_ATTR_EXPECT) else if (req->flags & MIO_HTRE_ATTR_EXPECT)
{ {
mio_htre_discardcontent (req); /* 417 Expectation Failed */
req->flags &= ~MIO_HTRE_ATTR_KEEPALIVE; /* to cause sendstatus() to close */ cgi_state_send_final_status_to_client(cgi_state, 417);
if (mio_svc_htts_sendstatus(htts, csck, req, 417, MIO_NULL) <= -1) goto oops; goto oops;
} }
if (req_content_length > 0) enable_input = 1;
#if defined(CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH)
if (cgi_state->req_content_length_unlimited)
{ {
if (mio_htre_getcontentlen(req) > 0) /* Transfer-Encoding is chunked. no content-length is known in advance. */
{
/* this means that it's called from the peek context */ /* option 1. buffer contents. if it gets too large, send 413 Request Entity Too Large.
cgi_state->num_pending_writes_to_peer++; * option 2. send 411 Length Required immediately
if (mio_dev_pro_write(cgi_state->peer, mio_htre_getcontentptr(req), mio_htre_getcontentlen(req), MIO_NULL) <= -1) * option 3. set Content-Length to -1 and use EOF to indicate the end of content [Non-Standard] */
{
cgi_state->num_pending_writes_to_peer--;
goto oops;
}
}
}
if (req->state & (MIO_HTRE_COMPLETED | MIO_HTRE_DISCARDED)) /* TODO: must set CONTENT-LENGTH to -1 before start the process */
{ /* change the callbacks to subscribe to contents to be uploaded */
/* disable input watching from the client */ cgi_state->cli->htrd->recbs.poke = cgi_client_htrd_poke;
if (mio_dev_sck_read(csck, 0) <= -1) goto oops; cgi_state->cli->htrd->recbs.push_content = cgi_client_htrd_push_content;
} }
else else
{ {
if (req_content_length > 0) #endif
if (cgi_state->req_content_length > 0)
{ {
/* change the callbacks to subscribe to contents to be uploaded */
cgi_state->cli->htrd->recbs.poke = cgi_client_htrd_poke;
cgi_state->cli->htrd->recbs.push_content = cgi_client_htrd_push_content; cgi_state->cli->htrd->recbs.push_content = cgi_client_htrd_push_content;
} }
else
{
/* no content to be uploaded from the client */
/* indicate EOF to the peer and disable input wathching from the client */
if (cgi_state_write_to_peer(cgi_state, MIO_NULL, 0) <= -1) goto oops;
enable_input = 0;
}
#if defined(CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH)
} }
#endif
/* TODO: store current input watching state and use it when destroying the cgi_state data */
if (mio_dev_sck_read(csck, enable_input) <= -1) goto oops;
/* this may change later if Content-Length is included in the cgi output */ /* this may change later if Content-Length is included in the cgi output */
cgi_state->res_mode_to_cli = (req->flags & MIO_HTRE_ATTR_KEEPALIVE)? CGI_STATE_RES_MODE_CHUNKED: CGI_STATE_RES_MODE_CLOSE; cgi_state->res_mode_to_cli = (req->flags & MIO_HTRE_ATTR_KEEPALIVE)? CGI_STATE_RES_MODE_CHUNKED: CGI_STATE_RES_MODE_CLOSE;
@ -1357,6 +1420,8 @@ int mio_svc_htts_docgi (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* r
oops: oops:
MIO_DEBUG2 (mio, "HTTS(%p) - FAILURE in docgi - socket(%p)\n", htts, csck); MIO_DEBUG2 (mio, "HTTS(%p) - FAILURE in docgi - socket(%p)\n", htts, csck);
/* TODO: can i call cgi_state_halt??? */
cgi_state->on_kill (cgi_state); /* TODO: call rsrc_free... */ cgi_state->on_kill (cgi_state); /* TODO: call rsrc_free... */
mio_dev_sck_halt (csck); mio_dev_sck_halt (csck);
return -1; return -1;
@ -1534,100 +1599,6 @@ oops:
return -1; return -1;
} }
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 = "";
const mio_bch_t* extraval = "";
text[0] = '\0';
switch (status_code)
{
#if 0
case 301: /* Moved Permanently */
case 302: /* Found */
case 303: /* See Other (since HTTP/1.1) */
case 307: /* Temporary Redirect (since HTTP/1.1) */
case 308: /* Permanent Redirect (Experimental RFC; RFC 7238) */
{
mio_svc_htts_rsrc_reloc_t* reloc;
reloc = (mio_htts_rsrc_reloc_t*)extra;
extrapre = "Location: ";
extrapst = (reloc->flags & MIO_SVC_HTTS_RSRC_RELOC_APPENDSLASH)? "/\r\n": "\r\n";
extraval = reloc->target;
break;
}
#endif
case 304:
case 200:
case 201:
case 202:
case 203:
case 204:
case 205:
case 206:
/* nothing to do */
break;
default:
#if 0
if (method != MIO_HTTP_HEAD &&
htts->opt.rcb.fmterr(htts, client, code, text, MIO_COUNTOF(text)) <= -1) return QSE_NULL;
#endif
if (status_code == 401)
{
extrapre = "WWW-Authenticate: Basic realm=\"";
extrapst = "\"\r\n";
extraval = (const mio_bch_t*)extra;
}
break;
}
mio_svc_htts_fmtgmtime(htts, MIO_NULL, dtbuf, MIO_COUNTOF(dtbuf));
x = mio_becs_fmt(cli->sbuf, "HTTP/%d.%d %d %s\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\nContent-Type: text/html\r\nContent-Length: %u\r\n%s%s%s\r\n%s",
version->major, version->minor, status_code, mio_http_status_to_bcstr(status_code),
htts->server_name,
dtbuf, /* DATE */
((req->flags & MIO_HTRE_ATTR_KEEPALIVE)? "keep-alive": "close"), /* connection */
mio_count_bcstr(text), /* content length */
extrapre, extraval, extraval, text
);
if (MIO_UNLIKELY(x == (mio_oow_t)-1)) return -1;
/* TODO: use timedwrite? */
if (mio_dev_sck_write(csck, MIO_BECS_PTR(cli->sbuf), MIO_BECS_LEN(cli->sbuf), MIO_NULL, MIO_NULL) <= -1)
{
mio_dev_sck_halt (csck);
return -1;
}
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)
{
mio_dev_sck_halt (csck);
return -1;
}
}
return 0;
}
void mio_svc_htts_fmtgmtime (mio_svc_htts_t* htts, const mio_ntime_t* nt, mio_bch_t* buf, mio_oow_t len) void mio_svc_htts_fmtgmtime (mio_svc_htts_t* htts, const mio_ntime_t* nt, mio_bch_t* buf, mio_oow_t len)
{ {
mio_ntime_t now; mio_ntime_t now;

View File

@ -335,14 +335,6 @@ MIO_EXPORT int mio_svc_htts_sendfile (
int keepalive int keepalive
); );
MIO_EXPORT int mio_svc_htts_sendstatus (
mio_svc_htts_t* htts,
mio_dev_sck_t* csck,
mio_htre_t* req,
int status_code,
void* extra
);
MIO_EXPORT void mio_svc_htts_fmtgmtime ( MIO_EXPORT void mio_svc_htts_fmtgmtime (
mio_svc_htts_t* htts, mio_svc_htts_t* htts,
const mio_ntime_t* nt, const mio_ntime_t* nt,

View File

@ -564,6 +564,13 @@ static int dev_pro_write_slave (mio_dev_t* dev, const void* data, mio_iolen_t* l
mio_dev_pro_slave_t* pro = (mio_dev_pro_slave_t*)dev; mio_dev_pro_slave_t* pro = (mio_dev_pro_slave_t*)dev;
ssize_t x; ssize_t x;
if (MIO_UNLIKELY(*len <= 0))
{
/* this is an EOF indicator */
mio_dev_halt (dev); /* halt this slave device */
return 1;
}
x = write(pro->pfd, data, *len); x = write(pro->pfd, data, *len);
if (x <= -1) if (x <= -1)
{ {
@ -582,6 +589,13 @@ static int dev_pro_writev_slave (mio_dev_t* dev, const mio_iovec_t* iov, mio_iol
mio_dev_pro_slave_t* pro = (mio_dev_pro_slave_t*)dev; mio_dev_pro_slave_t* pro = (mio_dev_pro_slave_t*)dev;
ssize_t x; ssize_t x;
if (MIO_UNLIKELY(*iovcnt <= 0))
{
/* this is an EOF indicator */
mio_dev_halt (dev); /* halt this slave device */
return 1;
}
x = writev(pro->pfd, iov, *iovcnt); x = writev(pro->pfd, iov, *iovcnt);
if (x <= -1) if (x <= -1)
{ {

View File

@ -9,3 +9,4 @@ while read x
do do
echo $x echo $x
done done
##echo "<<EOF>>"