written some cgi response chunking code

This commit is contained in:
hyung-hwan 2011-08-04 11:04:36 +00:00
parent 0998ae3b25
commit bd09e34df5
2 changed files with 106 additions and 46 deletions

View File

@ -671,6 +671,8 @@ struct task_cgi_t
qse_mbs_t* res; qse_mbs_t* res;
qse_mchar_t* res_ptr; qse_mchar_t* res_ptr;
qse_size_t res_left; qse_size_t res_left;
int chunked;
int sent;
qse_pio_t* pio; qse_pio_t* pio;
@ -693,7 +695,7 @@ int walk_cgi_headers (qse_htre_t* req, const qse_mchar_t* key, const qse_mchar_t
if (qse_mbs_cat (cgi->res, key) == (qse_size_t)-1) return -1; if (qse_mbs_cat (cgi->res, key) == (qse_size_t)-1) return -1;
if (qse_mbs_cat (cgi->res, QSE_MT(": ")) == (qse_size_t)-1) return -1; if (qse_mbs_cat (cgi->res, QSE_MT(": ")) == (qse_size_t)-1) return -1;
if (qse_mbs_cat (cgi->res, val) == (qse_size_t)-1) return -1; if (qse_mbs_cat (cgi->res, val) == (qse_size_t)-1) return -1;
if (qse_mbs_cat (cgi->res, QSE_MT("\r\n\r\n")) == (qse_size_t)-1) return -1; if (qse_mbs_cat (cgi->res, QSE_MT("\r\n")) == (qse_size_t)-1) return -1;
} }
return 0; return 0;
@ -713,8 +715,7 @@ static int cgi_htrd_handle_request (qse_htrd_t* htrd, qse_htre_t* req)
qse_mchar_t buf[128]; qse_mchar_t buf[128];
snprintf (buf, QSE_COUNTOF(buf), snprintf (buf, QSE_COUNTOF(buf),
QSE_MT("HTTP/%d.%d "), QSE_MT("HTTP/%d.%d "),
qse_htre_getmajorversion(req), 1,1 /* TODO: get the version from outer request....... */
qse_htre_getminorversion(req)
); );
if (qse_mbs_cat (cgi->res, buf) == (qse_size_t)-1) return -1; if (qse_mbs_cat (cgi->res, buf) == (qse_size_t)-1) return -1;
/* TODO: check the syntax of status value??? */ /* TODO: check the syntax of status value??? */
@ -726,28 +727,38 @@ static int cgi_htrd_handle_request (qse_htrd_t* htrd, qse_htre_t* req)
qse_mchar_t buf[128]; qse_mchar_t buf[128];
snprintf (buf, QSE_COUNTOF(buf), snprintf (buf, QSE_COUNTOF(buf),
QSE_MT("HTTP/%d.%d 200 OK\r\n"), QSE_MT("HTTP/%d.%d 200 OK\r\n"),
qse_htre_getmajorversion(req), 1,1 /* TODO: get the version from outer request....... */
qse_htre_getminorversion(req)
); );
if (qse_mbs_cat (cgi->res, buf) == (qse_size_t)-1) return -1; if (qse_mbs_cat (cgi->res, buf) == (qse_size_t)-1) return -1;
} }
if (!req->attr.content_length_set) if (!req->attr.content_length_set) cgi->chunked = 1;
qse_printf (QSE_T("req->attr.content_length_set = %d, req->attr.content_length = %d\n"), (int)req->attr.content_length_set, req->attr.content_length);
if (cgi->chunked)
{ {
if (qse_mbs_cat (cgi->res, QSE_MT("Transfer-Encoding: chunked\r\n")) == (qse_size_t)-1) return -1; if (qse_mbs_cat (cgi->res, QSE_MT("Transfer-Encoding: chunked\r\n")) == (qse_size_t)-1) return -1;
} }
if (qse_htre_walkheaders (req, walk_cgi_headers, cgi) <= -1) return -1; if (qse_htre_walkheaders (req, walk_cgi_headers, cgi) <= -1) return -1;
if (qse_mbs_ncat (cgi->res, QSE_MT("\r\n"), 2) == (qse_size_t)-1) return -1;
if (qse_htre_getcontentlen(req) > 0) if (qse_htre_getcontentlen(req) > 0)
{ {
if (!req->attr.content_length_set) if (cgi->chunked)
{ {
qse_mchar_t buf[64]; qse_mchar_t buf[64];
snprintf (buf, QSE_COUNTOF(buf), QSE_MT("%lX\r\n"), (unsigned long)qse_htre_getcontentlen(req)); snprintf (buf, QSE_COUNTOF(buf), QSE_MT("%lX\r\n"), (unsigned long)qse_htre_getcontentlen(req));
if (qse_mbs_cat (cgi->res, buf) == (qse_size_t)-1) return -1; if (qse_mbs_cat (cgi->res, buf) == (qse_size_t)-1) return -1;
} }
if (qse_mbs_ncat (cgi->res, qse_htre_getcontentptr(req), qse_htre_getcontentlen(req)) == (qse_size_t)-1) return -1; if (qse_mbs_ncat (cgi->res, qse_htre_getcontentptr(req), qse_htre_getcontentlen(req)) == (qse_size_t)-1) return -1;
if (cgi->chunked)
{
if (qse_mbs_ncat (cgi->res, QSE_MT("\r\n"), 2) == (qse_size_t)-1) return -1;
}
} }
return 0; return 0;
@ -790,11 +801,7 @@ static int task_main_cgi_5 (
QSE_ASSERT (cgi->pio != QSE_NULL); QSE_ASSERT (cgi->pio != QSE_NULL);
qse_printf (QSE_T("task_main_cgi_5\n")); qse_printf (QSE_T("task_main_cgi_5\n"));
{
char buf[64];
snprintf (buf, sizeof(buf), "%lX\r\n", cgi->buflen);
send (client->handle.i, buf, strlen(buf), 0);
}
/* TODO: check if cgi outputs more than content-length if it is set... */ /* TODO: check if cgi outputs more than content-length if it is set... */
n = send (client->handle.i, cgi->buf, cgi->buflen, 0); n = send (client->handle.i, cgi->buf, cgi->buflen, 0);
if (n <= -1) if (n <= -1)
@ -803,15 +810,11 @@ send (client->handle.i, buf, strlen(buf), 0);
/* TODO: logging ... */ /* TODO: logging ... */
return -1; return -1;
} }
send (client->handle.i, "\r\n", 2, 0);
QSE_MEMCPY (&cgi->buf[0], &cgi->buf[n], cgi->buflen - n); QSE_MEMCPY (&cgi->buf[0], &cgi->buf[n], cgi->buflen - n);
cgi->buflen -= n; cgi->buflen -= n;
if (cgi->buflen > 0) return 1; return (cgi->buflen > 0)? 1: 0;
send (client->handle.i, "0\r\n\r\n", 5, 0);
return 0;
} }
static int task_main_cgi_4 ( static int task_main_cgi_4 (
@ -821,14 +824,30 @@ static int task_main_cgi_4 (
qse_ssize_t n; qse_ssize_t n;
QSE_ASSERT (cgi->pio != QSE_NULL); QSE_ASSERT (cgi->pio != QSE_NULL);
/* this function assumes that the chunk length does not exceeded
* 4 hexadecimal digits. */
QSE_ASSERT (QSE_SIZEOF(cgi->buf) <= 0xFFFF);
qse_printf (QSE_T("task_main_cgi_4\n")); qse_printf (QSE_T("task_main_cgi_4\n"));
if (cgi->chunked)
{
qse_size_t count, extra;
qse_mchar_t chunklen[7];
extra = (QSE_SIZEOF(chunklen) - 1) + 2;
count = QSE_SIZEOF(cgi->buf) - cgi->buflen;
if (count > extra)
{
/* TODO: check if cgi outputs more than content-length if it is set... */ /* TODO: check if cgi outputs more than content-length if it is set... */
/* <- can i make it non-block?? or use select??? pio_tryread()? */ /* <- can i make it non-block?? or use select??? pio_tryread()? */
n = qse_pio_read ( n = qse_pio_read (
cgi->pio, cgi->pio,
&cgi->buf[cgi->buflen], &cgi->buf[cgi->buflen + QSE_SIZEOF(chunklen) - 1],
QSE_SIZEOF(cgi->buf) - cgi->buflen, count - extra,
QSE_PIO_OUT QSE_PIO_OUT
); );
if (n <= -1) if (n <= -1)
@ -839,24 +858,48 @@ qse_printf (QSE_T("task_main_cgi_4\n"));
} }
if (n == 0) if (n == 0)
{ {
if (cgi->buflen > 0) cgi->buf[cgi->buflen++] = QSE_MT('0');
{ cgi->buf[cgi->buflen++] = QSE_MT('\r');
task->main = task_main_cgi_4; cgi->buf[cgi->buflen++] = QSE_MT('\n');
cgi->buf[cgi->buflen++] = QSE_MT('\r');
cgi->buf[cgi->buflen++] = QSE_MT('\n');
task->main = task_main_cgi_5;
return task_main_cgi_5 (httpd, client, task); return task_main_cgi_5 (httpd, client, task);
} }
/* set the chunk length */
snprintf (chunklen, QSE_COUNTOF(chunklen), QSE_MT("%-4lX\r\n"), n);
QSE_MEMCPY (&cgi->buf[cgi->buflen], chunklen, QSE_SIZEOF(chunklen) - 1);
cgi->buflen += QSE_SIZEOF(chunklen) - 1 + n;
/* set the trailing CR & LF for a chunk */
cgi->buf[cgi->buflen++] = QSE_MT('\r');
cgi->buf[cgi->buflen++] = QSE_MT('\n');
}
}
else else
{ {
send (client->handle.i, "0\r\n\r\n", 5, 0); qse_printf (QSE_T("READING IN NON-CHUNKED MODE...\n"));
return 0; n = qse_pio_read (
cgi->pio,
&cgi->buf[cgi->buflen],
QSE_SIZEOF(cgi->buf) - cgi->buflen,
QSE_PIO_OUT
);
if (n <= -1)
{
/* can't return internal server error any more... */
/* TODO: loggig ... */
return -1;
} }
if (n == 0)
{
task->main = task_main_cgi_5;
return task_main_cgi_5 (httpd, client, task);
} }
cgi->buflen += n; cgi->buflen += n;
{
char buf[64];
snprintf (buf, sizeof(buf), "%lX\r\n", cgi->buflen);
send (client->handle.i, buf, strlen(buf), 0);
} }
n = send (client->handle.i, cgi->buf, cgi->buflen, 0); n = send (client->handle.i, cgi->buf, cgi->buflen, 0);
@ -866,7 +909,8 @@ send (client->handle.i, buf, strlen(buf), 0);
/* TODO: logging ... */ /* TODO: logging ... */
return -1; return -1;
} }
send (client->handle.i, "\r\n", 2, 0); cgi->sent += n;
qse_printf (QSE_T("READING IN NON-CHUNKED MODE...SENT %d so far\n"), cgi->sent);
QSE_MEMCPY (&cgi->buf[0], &cgi->buf[n], cgi->buflen - n); QSE_MEMCPY (&cgi->buf[0], &cgi->buf[n], cgi->buflen - n);
cgi->buflen -= n; cgi->buflen -= n;
@ -877,6 +921,9 @@ send (client->handle.i, "\r\n", 2, 0);
static int task_main_cgi_3 ( static int task_main_cgi_3 (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{ {
/* send the http initial line and headers built using the headers
* returned by CGI. it may include some contents as well */
task_cgi_t* cgi = (task_cgi_t*)task->ctx; task_cgi_t* cgi = (task_cgi_t*)task->ctx;
qse_ssize_t n; qse_ssize_t n;
qse_size_t count; qse_size_t count;
@ -908,11 +955,18 @@ qse_printf (QSE_T("task_main_cgi_3\n"));
static int task_main_cgi_2 ( static int task_main_cgi_2 (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{ {
/* several calls to this function will read output from the cgi
* until the end of header is reached. when the end is reached,
* it is possible that some contents are also read in.
* The callback function to qse_htrd_feed() handles this also.
*/
task_cgi_t* cgi = (task_cgi_t*)task->ctx; task_cgi_t* cgi = (task_cgi_t*)task->ctx;
qse_ssize_t n; qse_ssize_t n;
QSE_ASSERT (cgi->pio != QSE_NULL); QSE_ASSERT (cgi->pio != QSE_NULL);
qse_printf (QSE_T("[cgi_2 ]\n"));
/* <- can i make it non-block?? or use select??? pio_tryread()? */ /* <- can i make it non-block?? or use select??? pio_tryread()? */
n = qse_pio_read ( n = qse_pio_read (
cgi->pio, cgi->pio,
@ -937,6 +991,7 @@ static int task_main_cgi_2 (
cgi->buflen += n; cgi->buflen += n;
qse_printf (QSE_T("[feeding ]\n"));
if (qse_htrd_feed (cgi->htrd, cgi->buf, cgi->buflen) <= -1) if (qse_htrd_feed (cgi->htrd, cgi->buf, cgi->buflen) <= -1)
{ {
/* TODO: logging */ /* TODO: logging */
@ -1004,8 +1059,8 @@ qse_printf (QSE_T("internal server error....\n"));
{ {
} }
qse_printf (QSE_T("[calling cgi_2 ]\n"));
task->main = task_main_cgi_2; /* cause this function to be called subsequently */ task->main = task_main_cgi_2; /* cause this function to be called subsequently */
return task_main_cgi_2 (httpd, client, task); /* let me call it here once */ return task_main_cgi_2 (httpd, client, task); /* let me call it here once */
} }

View File

@ -61,6 +61,11 @@ qse_printf (QSE_T("content = [%.*S]\n"),
if (dot && qse_mbscmp (dot, QSE_MT(".cgi")) == 0) if (dot && qse_mbscmp (dot, QSE_MT(".cgi")) == 0)
{ {
static qse_http_version_t v1 = { 1, 0 };
/* persistent connection and cgi not compatible */
if (qse_comparehttpversions (qse_htre_getversion(req), &v1) <= 0)
req->attr.connection_close = 1;
qse_httpd_entaskcgi (httpd, client, QSE_NULL, QSE_T("/tmp/test.cgi")); qse_httpd_entaskcgi (httpd, client, QSE_NULL, QSE_T("/tmp/test.cgi"));
goto done; goto done;
} }