added qse_httpd_geterrnum()/qse_httpd_seterrnum().
added more error codes to httpd. added qse_pio_gethandleasubi(). enhanced sample file handlers enhanced qse_htre_completecontent()/qse_htre_discardcontent()/qse_htre_addcontent()/qse_htre_clear() fixed many cgi handling bugs
This commit is contained in:
parent
29a218eeb5
commit
3df521f7a9
@ -132,6 +132,8 @@ enum qse_pio_errnum_t
|
||||
QSE_PIO_ECHILD, /**< the child is not valid */
|
||||
QSE_PIO_EINTR, /**< interrupted */
|
||||
QSE_PIO_EPIPE, /**< broken pipe */
|
||||
QSE_PIO_EACCES, /**< access denied */
|
||||
QSE_PIO_ENOENT, /**< no such file */
|
||||
QSE_PIO_ESUBSYS /**< subsystem(system call) error */
|
||||
};
|
||||
typedef enum qse_pio_errnum_t qse_pio_errnum_t;
|
||||
@ -316,6 +318,16 @@ qse_pio_hnd_t qse_pio_gethandle (
|
||||
qse_pio_hid_t hid /**< handle ID */
|
||||
);
|
||||
|
||||
/**
|
||||
* The qse_pio_gethandleasubi() function gets a pipe handle wrapped
|
||||
* in the #qse_ubi_t type.
|
||||
* @return pipe handle
|
||||
*/
|
||||
qse_ubi_t qse_pio_gethandleasubi (
|
||||
qse_pio_t* pio, /**< pio object */
|
||||
qse_pio_hid_t hid /**< handle ID */
|
||||
);
|
||||
|
||||
/**
|
||||
* The qse_pio_getchild() function gets a process handle.
|
||||
* @return process handle
|
||||
|
@ -126,12 +126,6 @@ struct qse_htre_t
|
||||
#define qse_htre_setsmessagefromxstr(re,v) \
|
||||
qse_htre_setstrfromxstr((re),qse_htre_getsmessage(re),(v))
|
||||
|
||||
/* NOTE: setcontent() doesn't execute concb. use this with care */
|
||||
#define qse_htre_setcontentfromcstr(re,v) \
|
||||
qse_htre_setstrfromcstr((re),qse_htre_getcontent(re),(v))
|
||||
#define qse_htre_setcontentfromxstr(re,v) \
|
||||
qse_htre_setstrfromxstr((re),qse_htre_getcontent(re),(v))
|
||||
|
||||
typedef int (*qse_htre_header_walker_t) (
|
||||
qse_htre_t* re,
|
||||
const qse_mchar_t* key,
|
||||
|
@ -34,13 +34,15 @@ enum qse_httpd_errnum_t
|
||||
QSE_HTTPD_ENOERR,
|
||||
QSE_HTTPD_ENOMEM,
|
||||
QSE_HTTPD_EINVAL,
|
||||
QSE_HTTPD_ENOENT,
|
||||
QSE_HTTPD_EACCES,
|
||||
QSE_HTTPD_EINTERN,
|
||||
QSE_HTTPD_EIOMUX,
|
||||
QSE_HTTPD_ESUBSYS,
|
||||
QSE_HTTPD_ESOCKET,
|
||||
QSE_HTTPD_EDISCON, /* client disconnnected */
|
||||
QSE_HTTPD_EBADREQ, /* bad request */
|
||||
QSE_HTTPD_ETASK,
|
||||
QSE_HTTPD_ECOMCBS
|
||||
QSE_HTTPD_ETASK
|
||||
};
|
||||
typedef enum qse_httpd_errnum_t qse_httpd_errnum_t;
|
||||
|
||||
@ -184,6 +186,15 @@ void qse_httpd_close (
|
||||
qse_httpd_t* httpd
|
||||
);
|
||||
|
||||
qse_httpd_errnum_t qse_httpd_geterrnum (
|
||||
qse_httpd_t* httpd
|
||||
);
|
||||
|
||||
void qse_httpd_seterrnum (
|
||||
qse_httpd_t* httpd,
|
||||
qse_httpd_errnum_t errnum
|
||||
);
|
||||
|
||||
int qse_httpd_getoption (
|
||||
qse_httpd_t* httpd
|
||||
);
|
||||
@ -214,11 +225,6 @@ int qse_httpd_addlistener (
|
||||
const qse_char_t* uri
|
||||
);
|
||||
|
||||
void qse_httpd_markbadclient (
|
||||
qse_httpd_t* httpd,
|
||||
qse_httpd_client_t* client
|
||||
);
|
||||
|
||||
void qse_httpd_discardcontent (
|
||||
qse_httpd_t* httpd,
|
||||
qse_htre_t* req
|
||||
|
@ -288,6 +288,35 @@ oops:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int assert_executable (qse_pio_t* pio, const qse_mchar_t* path)
|
||||
{
|
||||
qse_lstat_t st;
|
||||
|
||||
if (QSE_ACCESS(path, X_OK) <= -1)
|
||||
{
|
||||
if (errno == EACCES) pio->errnum = QSE_PIO_EACCES;
|
||||
else if (errno == ENOENT) pio->errnum = QSE_PIO_ENOENT;
|
||||
else if (errno == ENOMEM) pio->errnum = QSE_PIO_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (QSE_LSTAT(path, &st) <= -1)
|
||||
{
|
||||
if (errno == EACCES) pio->errnum = QSE_PIO_EACCES;
|
||||
else if (errno == ENOENT) pio->errnum = QSE_PIO_ENOENT;
|
||||
else if (errno == ENOMEM) pio->errnum = QSE_PIO_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!S_ISREG(st.st_mode))
|
||||
{
|
||||
pio->errnum = QSE_PIO_EACCES;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static QSE_INLINE int is_fd_valid (int fd)
|
||||
{
|
||||
return fcntl (fd, F_GETFD) != -1 || errno != EBADF;
|
||||
@ -572,7 +601,26 @@ int qse_pio_init (
|
||||
);
|
||||
|
||||
QSE_MMGR_FREE (mmgr, dupcmd);
|
||||
if (x == FALSE) goto oops;
|
||||
if (x == FALSE)
|
||||
{
|
||||
DWORD e = GetLastError ();
|
||||
switch (e)
|
||||
{
|
||||
case ERROR_ACCESS_DENIED:
|
||||
pio->errnum = QSE_PIO_EACCES;
|
||||
break;
|
||||
|
||||
case ERROR_FILE_NOT_FOUND:
|
||||
case ERROR_PATH_NOT_FOUND:
|
||||
pio->errnum = QSE_PIO_ENOENT;
|
||||
break;
|
||||
|
||||
case ERROR_NOT_ENOUGH_MEMORY:
|
||||
case ERROR_OUTOFMEMORY:
|
||||
pio->errnum = QSE_PIO_ENOMEM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (windevnul != INVALID_HANDLE_VALUE)
|
||||
@ -871,6 +919,8 @@ int qse_pio_init (
|
||||
cmd_file
|
||||
);
|
||||
|
||||
/* TODO: translate error code ... */
|
||||
|
||||
QSE_MMGR_FREE (mmgr, cmd_line);
|
||||
cmd_line = QSE_NULL;
|
||||
|
||||
@ -994,6 +1044,16 @@ int qse_pio_init (
|
||||
}
|
||||
|
||||
if (make_param (pio, cmd, flags, ¶m) <= -1) goto oops;
|
||||
|
||||
/* check if the command(the command requested or /bin/sh) is
|
||||
* exectuable to return an error without trying to execute it
|
||||
* though this check alone isn't sufficient */
|
||||
if (assert_executable (pio, param.argv[0]) <= -1)
|
||||
{
|
||||
free_param (pio, ¶m);
|
||||
goto oops;
|
||||
}
|
||||
|
||||
spawn_ret = posix_spawn(
|
||||
&pid, param.argv[0], &fa, QSE_NULL, param.argv,
|
||||
(env? qse_env_getarr(env): environ));
|
||||
@ -1050,8 +1110,23 @@ int qse_pio_init (
|
||||
goto oops;
|
||||
}
|
||||
|
||||
if (make_param (pio, cmd, flags, ¶m) <= -1) goto oops;
|
||||
|
||||
/* check if the command(the command requested or /bin/sh) is
|
||||
* exectuable to return an error without trying to execute it
|
||||
* though this check alone isn't sufficient */
|
||||
if (assert_executable (pio, param.argv[0]) <= -1)
|
||||
{
|
||||
free_param (pio, ¶m);
|
||||
goto oops;
|
||||
}
|
||||
|
||||
pid = QSE_FORK();
|
||||
if (pid <= -1) goto oops;
|
||||
if (pid <= -1)
|
||||
{
|
||||
free_param (pio, ¶m);
|
||||
goto oops;
|
||||
}
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
@ -1145,7 +1220,7 @@ int qse_pio_init (
|
||||
if (flags & QSE_PIO_DROPOUT) QSE_CLOSE(1);
|
||||
if (flags & QSE_PIO_DROPERR) QSE_CLOSE(2);
|
||||
|
||||
if (make_param (pio, cmd, flags, ¶m) <= -1) goto child_oops;
|
||||
/*if (make_param (pio, cmd, flags, ¶m) <= -1) goto child_oops;*/
|
||||
QSE_EXECVE (param.argv[0], param.argv, (env? qse_env_getarr(env): environ));
|
||||
free_param (pio, ¶m);
|
||||
|
||||
@ -1155,6 +1230,7 @@ int qse_pio_init (
|
||||
}
|
||||
|
||||
/* parent */
|
||||
free_param (pio, ¶m);
|
||||
pio->child = pid;
|
||||
|
||||
if (flags & QSE_PIO_WRITEIN)
|
||||
@ -1337,6 +1413,8 @@ const qse_char_t* qse_pio_geterrmsg (qse_pio_t* pio)
|
||||
QSE_T("child process not valid"),
|
||||
QSE_T("interruped"),
|
||||
QSE_T("broken pipe"),
|
||||
QSE_T("access denied"),
|
||||
QSE_T("no such file"),
|
||||
QSE_T("systeam call error"),
|
||||
QSE_T("unknown error")
|
||||
};
|
||||
@ -1362,6 +1440,23 @@ qse_pio_hnd_t qse_pio_gethandle (qse_pio_t* pio, qse_pio_hid_t hid)
|
||||
return pio->pin[hid].handle;
|
||||
}
|
||||
|
||||
qse_ubi_t qse_pio_gethandleasubi (qse_pio_t* pio, qse_pio_hid_t hid)
|
||||
{
|
||||
qse_ubi_t handle;
|
||||
|
||||
#if defined(_WIN32)
|
||||
handle.ptr = pio->pin[hid].handle;
|
||||
#elif defined(__OS2__)
|
||||
handle.ul = pio->pin[hid].handle;
|
||||
#elif defined(__DOS__)
|
||||
handle.i = pio->pin[hid].handle;
|
||||
#else
|
||||
handle.i = pio->pin[hid].handle;
|
||||
#endif
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
qse_pio_pid_t qse_pio_getchild (qse_pio_t* pio)
|
||||
{
|
||||
return pio->child;
|
||||
|
@ -45,10 +45,14 @@ void qse_htre_fini (qse_htre_t* re)
|
||||
|
||||
void qse_htre_clear (qse_htre_t* re)
|
||||
{
|
||||
if (re->concb)
|
||||
if (!(re->state & QSE_HTRE_COMPLETED) &&
|
||||
!(re->state & QSE_HTRE_DISCARDED))
|
||||
{
|
||||
re->concb (re, QSE_NULL, 0, re->concb_ctx); /* indicate end of content */
|
||||
qse_htre_unsetconcb (re);
|
||||
if (re->concb)
|
||||
{
|
||||
re->concb (re, QSE_NULL, 0, re->concb_ctx); /* indicate end of content */
|
||||
qse_htre_unsetconcb (re);
|
||||
}
|
||||
}
|
||||
|
||||
QSE_MEMSET (&re->version, 0, QSE_SIZEOF(re->version));
|
||||
@ -117,25 +121,73 @@ int qse_htre_walkheaders (
|
||||
int qse_htre_addcontent (
|
||||
qse_htre_t* re, const qse_mchar_t* ptr, qse_size_t len)
|
||||
{
|
||||
if (re->state & (QSE_HTRE_COMPLETED | QSE_HTRE_DISCARDED)) return 0;
|
||||
/* see comments in qse_htre_discardcontent() */
|
||||
|
||||
/* if the callback is set, the content goes to the callback. */
|
||||
if (re->concb) return re->concb (re, ptr, len, re->concb_ctx);
|
||||
/* if the callback is not set, the contents goes to the internal buffer */
|
||||
if (qse_mbs_ncat (&re->content, ptr, len) == (qse_size_t)-1) return -1;
|
||||
if (re->state & (QSE_HTRE_COMPLETED | QSE_HTRE_DISCARDED)) return 0; /* skipped */
|
||||
|
||||
if (re->concb)
|
||||
{
|
||||
/* if the callback is set, the content goes to the callback. */
|
||||
if (re->concb (re, ptr, len, re->concb_ctx) <= -1) return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if the callback is not set, the contents goes to the internal buffer */
|
||||
if (qse_mbs_ncat (&re->content, ptr, len) == (qse_size_t)-1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1; /* added successfully */
|
||||
}
|
||||
|
||||
void qse_htre_completecontent (qse_htre_t* re)
|
||||
{
|
||||
re->state |= QSE_HTRE_COMPLETED;
|
||||
/* see comments in qse_htre_discardcontent() */
|
||||
|
||||
if (!(re->state & QSE_HTRE_COMPLETED) &&
|
||||
!(re->state & QSE_HTRE_DISCARDED))
|
||||
{
|
||||
re->state |= QSE_HTRE_COMPLETED;
|
||||
if (re->concb)
|
||||
{
|
||||
/* indicate end of content */
|
||||
re->concb (re, QSE_NULL, 0, re->concb_ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void qse_htre_discardcontent (qse_htre_t* re)
|
||||
{
|
||||
re->state |= QSE_HTRE_DISCARDED;
|
||||
qse_mbs_clear (&re->content);
|
||||
/* you can't discard this if it's completed.
|
||||
* you can't complete this if it's discarded
|
||||
* you can't add contents to this if it's completed or discarded
|
||||
*/
|
||||
|
||||
if (!(re->state & QSE_HTRE_COMPLETED) &&
|
||||
!(re->state & QSE_HTRE_DISCARDED))
|
||||
{
|
||||
re->state |= QSE_HTRE_DISCARDED;
|
||||
|
||||
/* qse_htre_addcontent()...
|
||||
* qse_thre_setconcb()...
|
||||
* qse_htre_discardcontent()... <-- POINT A.
|
||||
*
|
||||
* at point A, the content must contain something
|
||||
* and concb is also set. for simplicity,
|
||||
* clear the content buffer and invoke the callback
|
||||
*
|
||||
* likewise, you may produce many weird combinations
|
||||
* of these functions. however, these functions are
|
||||
* designed to serve a certain usage pattern not including
|
||||
* weird combinations.
|
||||
*/
|
||||
qse_mbs_clear (&re->content);
|
||||
if (re->concb)
|
||||
{
|
||||
/* indicate end of content */
|
||||
re->concb (re, QSE_NULL, 0, re->concb_ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void qse_htre_unsetconcb (qse_htre_t* re)
|
||||
|
@ -97,6 +97,16 @@ void qse_httpd_stop (qse_httpd_t* httpd)
|
||||
httpd->stopreq = 1;
|
||||
}
|
||||
|
||||
qse_httpd_errnum_t qse_httpd_geterrnum (qse_httpd_t* httpd)
|
||||
{
|
||||
return httpd->errnum;
|
||||
}
|
||||
|
||||
void qse_httpd_seterrnum (qse_httpd_t* httpd, qse_httpd_errnum_t errnum)
|
||||
{
|
||||
httpd->errnum = errnum;
|
||||
}
|
||||
|
||||
int qse_httpd_getoption (qse_httpd_t* httpd)
|
||||
{
|
||||
return httpd->option;
|
||||
@ -665,7 +675,7 @@ static void perform_task (qse_httpd_t* httpd, qse_httpd_client_t* client)
|
||||
{
|
||||
dequeue_task (httpd, client);
|
||||
/*shutdown (client->handle.i, SHUT_RDWR);*/
|
||||
client->bad = 1;
|
||||
client->bad = 1;
|
||||
}
|
||||
else if (n == 0)
|
||||
{
|
||||
@ -675,7 +685,7 @@ static void perform_task (qse_httpd_t* httpd, qse_httpd_client_t* client)
|
||||
|
||||
static int read_from_client (qse_httpd_t* httpd, qse_httpd_client_t* client)
|
||||
{
|
||||
qse_mchar_t buf[1024];
|
||||
qse_mchar_t buf[2048]; /* TODO: adjust this buffer size */
|
||||
qse_ssize_t m;
|
||||
|
||||
QSE_ASSERT (httpd->cbs->client.recv != QSE_NULL);
|
||||
@ -803,87 +813,87 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure\n"));
|
||||
QSE_ASSERT (httpd->cbs->client.accepted != QSE_NULL);
|
||||
int x = httpd->cbs->client.accepted (httpd, client); /* is this correct???? what if ssl handshaking got stalled because writing failed in SSL_accept()? */
|
||||
if (x >= 1) client->ready = 1;
|
||||
else if (x <= -1) goto bad_client;
|
||||
else if (x <= -1)
|
||||
{
|
||||
delete_from_client_array (httpd, fd);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: any way to suspend read while a request is being processed???
|
||||
if (read_from_client (httpd, client) <= -1)
|
||||
{
|
||||
bad_client:
|
||||
if (httpd->threaded)
|
||||
{
|
||||
/* let the writing part handle it,
|
||||
* probably in the next iteration */
|
||||
qse_httpd_markbadclient (httpd, client);
|
||||
shutdown (client->handle.i, SHUT_RDWR);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete_from_client_array (httpd, fd);
|
||||
continue; /* don't need to go to the writing part */
|
||||
}
|
||||
delete_from_client_array (httpd, fd);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!httpd->threaded)
|
||||
/* perform a client task enqued to a client */
|
||||
if (client->task.queue.head)
|
||||
{
|
||||
if (client->bad)
|
||||
qse_httpd_task_t* task;
|
||||
int perform = 0;
|
||||
|
||||
qse_printf (QSE_T(".....CLIENT %d HAS TASK\n"), fd);
|
||||
task = &client->task.queue.head->task;
|
||||
task->trigger_mask &=
|
||||
~(QSE_HTTPD_TASK_TRIGGER_READABLE |
|
||||
QSE_HTTPD_TASK_TRIGGER_RELAYABLE |
|
||||
QSE_HTTPD_TASK_TRIGGER_WRITABLE);
|
||||
|
||||
if (!(task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READ) &&
|
||||
!(task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_RELAY) &&
|
||||
!(task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITE))
|
||||
{
|
||||
/*shutdown (client->handle.i, SHUT_RDWR);*/
|
||||
delete_from_client_array (httpd, fd);
|
||||
qse_printf (QSE_T(".....NO TRIGGER ACTION....\n"));
|
||||
/* no trigger set. set the flag to
|
||||
* non-readable and non-writable */
|
||||
perform = 1;
|
||||
}
|
||||
else if (client->task.queue.head)
|
||||
else
|
||||
{
|
||||
qse_httpd_task_t* task;
|
||||
int perform = 0;
|
||||
|
||||
task = &client->task.queue.head->task;
|
||||
task->trigger_mask &=
|
||||
~(QSE_HTTPD_TASK_TRIGGER_READABLE |
|
||||
QSE_HTTPD_TASK_TRIGGER_RELAYABLE |
|
||||
QSE_HTTPD_TASK_TRIGGER_WRITABLE);
|
||||
|
||||
if (!(task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READ) &&
|
||||
!(task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_RELAY) &&
|
||||
!(task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITE))
|
||||
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READ)
|
||||
qse_printf (QSE_T(".....CLIENT %d HAS READ TREIGGER %d\n"), fd, task->trigger[0].i);
|
||||
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_RELAY)
|
||||
qse_printf (QSE_T(".....CLIENT %d HAS RELAY TREIGGER %d\n"), fd, task->trigger[1].i);
|
||||
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITE)
|
||||
qse_printf (QSE_T(".....CLIENT %d HAS WRITE TREIGGER %d\n"), fd, task->trigger[2].i);
|
||||
if ((task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READ) &&
|
||||
FD_ISSET(task->trigger[0].i, &r))
|
||||
{
|
||||
/* no trigger set. set the flag to
|
||||
* non-readable and non-writable */
|
||||
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_READABLE;
|
||||
perform = 1;
|
||||
qse_printf (QSE_T(".....TRIGGER READABLE....\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READ) &&
|
||||
FD_ISSET(task->trigger[0].i, &r))
|
||||
{
|
||||
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_READABLE;
|
||||
perform = 1;
|
||||
}
|
||||
|
||||
if ((task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_RELAY) &&
|
||||
FD_ISSET(task->trigger[1].i, &r))
|
||||
{
|
||||
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_RELAYABLE;
|
||||
perform = 1;
|
||||
}
|
||||
|
||||
if ((task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_RELAY) &&
|
||||
FD_ISSET(task->trigger[2].i, &w))
|
||||
{
|
||||
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_WRITABLE;
|
||||
perform = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (perform)
|
||||
if ((task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_RELAY) &&
|
||||
FD_ISSET(task->trigger[1].i, &r))
|
||||
{
|
||||
/* TODO: error handling -> writable() returns <= -1 */
|
||||
/* TODO: though the client side is not writable, can't i still exeucte the task?
|
||||
* if the task needs to transfer anything yet.. it can do that.
|
||||
* i probably need a new trigger type??? */
|
||||
if (httpd->cbs->mux.writable (httpd, client->handle, 0) >= 1)
|
||||
perform_task (httpd, client);
|
||||
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_RELAYABLE;
|
||||
perform = 1;
|
||||
qse_printf (QSE_T(".....TRIGGER RELAYABLE....\n"));
|
||||
}
|
||||
|
||||
if ((task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITE) &&
|
||||
FD_ISSET(task->trigger[2].i, &w))
|
||||
{
|
||||
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_WRITABLE;
|
||||
perform = 1;
|
||||
qse_printf (QSE_T(".....TRIGGER WRITABLE....\n"));
|
||||
}
|
||||
}
|
||||
|
||||
if (perform)
|
||||
{
|
||||
/* TODO: error handling -> writable() returns <= -1 */
|
||||
/* TODO: though the client side is not writable, can't i still exeucte the task?
|
||||
* if the task needs to transfer anything yet.. it can do that.
|
||||
* i probably need a new trigger type??? */
|
||||
if (httpd->cbs->mux.writable (httpd, client->handle, 0) >= 1)
|
||||
{
|
||||
perform_task (httpd, client);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1109,18 +1119,7 @@ qse_httpd_task_t* qse_httpd_entask (
|
||||
const qse_httpd_task_t* pred, const qse_httpd_task_t* task,
|
||||
qse_size_t xtnsize)
|
||||
{
|
||||
qse_httpd_task_t* ret;
|
||||
ret = enqueue_task (httpd, client, pred, task, xtnsize);
|
||||
if (ret == QSE_NULL) client->bad = 1; /* mark this client bad */
|
||||
return ret;
|
||||
}
|
||||
|
||||
void qse_httpd_markbadclient (qse_httpd_t* httpd, qse_httpd_client_t* client)
|
||||
{
|
||||
/* mark that something is wrong in processing requests from this client.
|
||||
* this client could be bad... or the system could encounter some errors
|
||||
* like memory allocation failure */
|
||||
client->bad = 1;
|
||||
return enqueue_task (httpd, client, pred, task, xtnsize);
|
||||
}
|
||||
|
||||
void qse_httpd_discardcontent (qse_httpd_t* httpd, qse_htre_t* req)
|
||||
|
@ -30,10 +30,6 @@
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#if defined(HAVE_PTHREAD)
|
||||
# include <pthread.h>
|
||||
#endif
|
||||
|
||||
#ifndef SHUT_RDWR
|
||||
# define SHUT_RDWR 2
|
||||
#endif
|
||||
@ -64,17 +60,14 @@ struct qse_httpd_client_t
|
||||
qse_ubi_t handle2;
|
||||
|
||||
int ready;
|
||||
int bad;
|
||||
int secure;
|
||||
int bad;
|
||||
sockaddr_t local_addr;
|
||||
sockaddr_t remote_addr;
|
||||
qse_htrd_t* htrd;
|
||||
|
||||
struct
|
||||
{
|
||||
#if defined(HAVE_PTHREAD)
|
||||
pthread_mutex_t mutex;
|
||||
#endif
|
||||
struct
|
||||
{
|
||||
int count;
|
||||
@ -120,23 +113,13 @@ struct qse_httpd_t
|
||||
int option;
|
||||
int stopreq;
|
||||
|
||||
int threaded;
|
||||
|
||||
struct
|
||||
{
|
||||
#if defined(HAVE_PTHREAD)
|
||||
int pfd[2];
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
#endif
|
||||
client_array_t array;
|
||||
} client;
|
||||
|
||||
struct
|
||||
{
|
||||
#if defined(HAVE_PTHREAD)
|
||||
pthread_mutex_t mutex;
|
||||
#endif
|
||||
listener_t* list;
|
||||
fd_set set;
|
||||
int max;
|
||||
|
@ -262,7 +262,7 @@ qse_httpd_task_t* qse_httpd_entaskformat (
|
||||
|
||||
capa = capa * 2;
|
||||
buf = (qse_mchar_t*) qse_httpd_allocmem (httpd, (capa + 1) * QSE_SIZEOF(*buf));
|
||||
if (buf == QSE_NULL) return QSE_NULL;
|
||||
if (buf == QSE_NULL) return QSE_NULL;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
@ -272,7 +272,8 @@ qse_httpd_task_t* qse_httpd_entaskformat (
|
||||
/* vsnprintf returns the number of characters that would
|
||||
* have been written not including the terminating '\0'
|
||||
* if the _data buffer were large enough */
|
||||
buf = (qse_mchar_t*) qse_httpd_allocmem (httpd, (bytes_req + 1) * QSE_SIZEOF(*buf));
|
||||
buf = (qse_mchar_t*) qse_httpd_allocmem (
|
||||
httpd, (bytes_req + 1) * QSE_SIZEOF(*buf));
|
||||
if (buf == QSE_NULL) return QSE_NULL;
|
||||
|
||||
va_start (ap, fmt);
|
||||
@ -287,7 +288,6 @@ qse_httpd_task_t* qse_httpd_entaskformat (
|
||||
{
|
||||
/* something got wrong ... */
|
||||
qse_httpd_freemem (httpd, buf);
|
||||
|
||||
httpd->errnum = QSE_HTTPD_EINTERN;
|
||||
return QSE_NULL;
|
||||
}
|
||||
@ -1116,17 +1116,17 @@ qse_httpd_task_t* qse_httpd_entaskpath (
|
||||
{
|
||||
qse_httpd_task_t task;
|
||||
task_path_t data;
|
||||
const qse_mchar_t* range;
|
||||
const qse_mchar_t* tmp;
|
||||
|
||||
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
|
||||
data.name = name;
|
||||
data.version = *qse_htre_getversion(req);
|
||||
data.keepalive = req->attr.keepalive;
|
||||
|
||||
range = qse_htre_getheaderval(req, QSE_MT("Range"));
|
||||
if (range)
|
||||
tmp = qse_htre_getheaderval(req, QSE_MT("Range"));
|
||||
if (tmp)
|
||||
{
|
||||
if (qse_parsehttprange (range, &data.range) <= -1)
|
||||
if (qse_parsehttprange (tmp, &data.range) <= -1)
|
||||
{
|
||||
return entask_error (httpd, client, pred, 416, &data.version, data.keepalive);
|
||||
}
|
||||
@ -1196,7 +1196,7 @@ static int task_main_fseg (
|
||||
httpd, client, ctx->handle, &ctx->offset, count);
|
||||
if (n <= -1)
|
||||
{
|
||||
// HANDLE EGAIN specially???
|
||||
/* HANDLE EGAIN specially??? */
|
||||
return -1; /* TODO: any logging */
|
||||
}
|
||||
|
||||
@ -1268,17 +1268,17 @@ static QSE_INLINE int task_main_file (
|
||||
/* TODO: if you should deal with files on a network-mounted drive,
|
||||
setting a trigger or non-blocking I/O are needed. */
|
||||
|
||||
/* when it comes to the file size, using fstat after opening
|
||||
* can be more accurate. but this function uses information
|
||||
* set into the task context before the call to this function */
|
||||
|
||||
qse_printf (QSE_T("opening file %hs\n"), file->path);
|
||||
|
||||
httpd->errnum = QSE_HTTPD_ENOERR;
|
||||
if (httpd->cbs->file.ropen (httpd, file->path, &handle, &filesize) <= -1)
|
||||
{
|
||||
/* TODO: depending on the error type, either 404 or 403??? */
|
||||
int http_errnum;
|
||||
http_errnum = (httpd->errnum == QSE_HTTPD_ENOENT)? 404:
|
||||
(httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
|
||||
x = entask_error (
|
||||
httpd, client, x, 404, &file->version, file->keepalive);
|
||||
httpd, client, x, http_errnum,
|
||||
&file->version, file->keepalive);
|
||||
goto no_file_send;
|
||||
}
|
||||
fileopen = 1;
|
||||
@ -1414,17 +1414,17 @@ qse_httpd_task_t* qse_httpd_entaskfile (
|
||||
{
|
||||
qse_httpd_task_t task;
|
||||
task_file_t data;
|
||||
const qse_mchar_t* range;
|
||||
const qse_mchar_t* tmp;
|
||||
|
||||
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
|
||||
data.path = path;
|
||||
data.version = *qse_htre_getversion(req);
|
||||
data.keepalive = req->attr.keepalive;
|
||||
|
||||
range = qse_htre_getheaderval(req, QSE_MT("Range"));
|
||||
if (range)
|
||||
tmp = qse_htre_getheaderval(req, QSE_MT("Range"));
|
||||
if (tmp)
|
||||
{
|
||||
if (qse_parsehttprange (range, &data.range) <= -1)
|
||||
if (qse_parsehttprange (tmp, &data.range) <= -1)
|
||||
{
|
||||
return qse_httpd_entaskerror (httpd, client, pred, 416, req);
|
||||
}
|
||||
@ -1433,6 +1433,14 @@ qse_httpd_task_t* qse_httpd_entaskfile (
|
||||
{
|
||||
data.range.type = QSE_HTTP_RANGE_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: If-Modified-Since...
|
||||
tmp = qse_htre_getheaderval(req, QSE_MT("If-Modified-Since"));
|
||||
if (tmp)
|
||||
{
|
||||
}
|
||||
*/
|
||||
|
||||
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
|
||||
task.init = task_init_file;
|
||||
@ -1463,12 +1471,13 @@ struct task_cgi_t
|
||||
int keepalive; /* taken from the request */
|
||||
int nph;
|
||||
|
||||
qse_env_t* env;
|
||||
qse_pio_t* pio;
|
||||
qse_htrd_t* htrd;
|
||||
qse_env_t* env;
|
||||
qse_pio_t pio;
|
||||
int pio_inited;
|
||||
|
||||
qse_htre_t* req; /* original request associated with this */
|
||||
qse_mbs_t* reqcon; /* content from the request */
|
||||
qse_mbs_t* reqfwdbuf; /* content from the request */
|
||||
int reqfwderr;
|
||||
|
||||
qse_mbs_t* res;
|
||||
@ -1807,89 +1816,136 @@ oops:
|
||||
static int cgi_snatch_content (
|
||||
qse_htre_t* req, const qse_mchar_t* ptr, qse_size_t len, void* ctx)
|
||||
{
|
||||
task_cgi_t* cgi = (task_cgi_t*)ctx;
|
||||
qse_httpd_task_t* task;
|
||||
task_cgi_t* cgi;
|
||||
|
||||
task = (qse_httpd_task_t*)ctx;
|
||||
cgi = (task_cgi_t*)task->ctx;
|
||||
|
||||
if (ptr) qse_printf (QSE_T("!!!SNATCHING [%.*hs]\n"), len, ptr);
|
||||
else qse_printf (QSE_T("!!!SNATCHING DONE\n"));
|
||||
|
||||
if (ptr == QSE_NULL)
|
||||
{
|
||||
/* request ended. this could be a real end or
|
||||
* abortion for an error */
|
||||
/*
|
||||
* this callback is called with ptr of QSE_NULL
|
||||
* when the request is completed or discarded.
|
||||
* and this indicates that there's nothing more to read
|
||||
* from the client side. this can happen when the end of
|
||||
* a request is seen or when an error occurs
|
||||
*/
|
||||
QSE_ASSERT (len == 0);
|
||||
cgi->req = QSE_NULL;
|
||||
/* TODO: probably need to add a write trigger....
|
||||
until cgi->reqcon is emptied...
|
||||
cannot clear triogters since there are jobs depending on readbility or realyableilityy
|
||||
*/
|
||||
|
||||
/* mark the there's nothing to read form the client side */
|
||||
cgi->req = QSE_NULL;
|
||||
|
||||
/* since there is no more to read from the client side.
|
||||
* the relay trigger is not needed any more. */
|
||||
task->trigger_mask &=
|
||||
~(QSE_HTTPD_TASK_TRIGGER_RELAY |
|
||||
QSE_HTTPD_TASK_TRIGGER_RELAYABLE);
|
||||
|
||||
if (QSE_MBS_LEN(cgi->reqfwdbuf) > 0 && cgi->pio_inited &&
|
||||
!(task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITE))
|
||||
{
|
||||
/* there's nothing more to read from the client side.
|
||||
* there's something to forward in the forwarding buffer.
|
||||
* but no write trigger is set. add the write trigger
|
||||
* for task invocation. */
|
||||
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_WRITE;
|
||||
task->trigger_mask &= ~QSE_HTTPD_TASK_TRIGGER_WRITABLE;
|
||||
task->trigger[2] = qse_pio_gethandleasubi (&cgi->pio, QSE_PIO_IN);
|
||||
}
|
||||
}
|
||||
else if (!cgi->reqfwderr)
|
||||
{
|
||||
/* push the contents to the own buffer */
|
||||
if (qse_mbs_ncat (cgi->reqcon, ptr, len) == (qse_size_t)-1)
|
||||
/* we can write to the child process if a forwarding error
|
||||
* didn't occur previously. we store data from the client side
|
||||
* to the forwaring buffer only if there's no such previous
|
||||
* error. if an error occurred, we simply drop the data. */
|
||||
if (qse_mbs_ncat (cgi->reqfwdbuf, ptr, len) == (qse_size_t)-1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
qse_printf (QSE_T("!!!SNACHED [%.*hs]\n"), len, ptr);
|
||||
qse_printf (QSE_T("!!!SNATCHED [%.*hs]\n"), len, ptr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cgi_forward_content (qse_httpd_t* httpd, qse_httpd_task_t* task)
|
||||
static void cgi_forward_content (
|
||||
qse_httpd_t* httpd, qse_httpd_task_t* task, int writable)
|
||||
{
|
||||
task_cgi_t* cgi = (task_cgi_t*)task->ctx;
|
||||
|
||||
QSE_ASSERT (cgi->reqcon != QSE_NULL);
|
||||
QSE_ASSERT (cgi->reqfwdbuf != QSE_NULL);
|
||||
|
||||
if (QSE_MBS_LEN(cgi->reqcon) > 0)
|
||||
if (QSE_MBS_LEN(cgi->reqfwdbuf) > 0)
|
||||
{
|
||||
/* there is something to forward in the forwarding buffer. */
|
||||
|
||||
if (cgi->reqfwderr)
|
||||
{
|
||||
qse_mbs_clear (cgi->reqcon);
|
||||
/* a forwarding error has occurred previously.
|
||||
* clear the forwarding buffer */
|
||||
qse_printf (QSE_T("FORWARD: CLEARING REQCON FOR ERROR\n"));
|
||||
qse_mbs_clear (cgi->reqfwdbuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
qse_ubi_t handle;
|
||||
/* normal forwarding */
|
||||
qse_ssize_t n;
|
||||
|
||||
/* TODO: handle = qse_pio_getubihandle(); */
|
||||
handle.i = qse_pio_gethandle (cgi->pio, QSE_PIO_IN);
|
||||
if (writable) goto forward;
|
||||
|
||||
n = httpd->cbs->mux.writable (httpd, handle, 0);
|
||||
n = httpd->cbs->mux.writable (
|
||||
httpd, qse_pio_gethandleasubi (&cgi->pio, QSE_PIO_IN), 0);
|
||||
if (n == 0) qse_printf (QSE_T("FORWARD: @@@@@@@@@NOT WRITABLE\n"));
|
||||
if (n >= 1)
|
||||
{
|
||||
forward:
|
||||
/* writable */
|
||||
qse_printf (QSE_T("@@@@@@@@@@WRITING[%.*hs]\n"),
|
||||
(int)QSE_MBS_LEN(cgi->reqcon),
|
||||
QSE_MBS_PTR(cgi->reqcon));
|
||||
qse_printf (QSE_T("FORWARD: @@@@@@@@@@WRITING[%.*hs]\n"),
|
||||
(int)QSE_MBS_LEN(cgi->reqfwdbuf),
|
||||
QSE_MBS_PTR(cgi->reqfwdbuf));
|
||||
n = qse_pio_write (
|
||||
cgi->pio, QSE_PIO_IN,
|
||||
QSE_MBS_PTR(cgi->reqcon),
|
||||
QSE_MBS_LEN(cgi->reqcon)
|
||||
&cgi->pio, QSE_PIO_IN,
|
||||
QSE_MBS_PTR(cgi->reqfwdbuf),
|
||||
QSE_MBS_LEN(cgi->reqfwdbuf)
|
||||
);
|
||||
/* TODO: improve performance.. instead of copying the remaing part to the head all the time..
|
||||
grow the buffer to a certain limit. */
|
||||
if (n > 0) qse_mbs_del (cgi->reqcon, 0, n);
|
||||
/* TODO: improve performance.. instead of copying the remaing part
|
||||
to the head all the time.. grow the buffer to a certain limit. */
|
||||
if (n > 0) qse_mbs_del (cgi->reqfwdbuf, 0, n);
|
||||
}
|
||||
|
||||
if (n <= -1)
|
||||
{
|
||||
qse_printf (QSE_T("@@@@@@@@WRITE TO CGI FAILED\n"));
|
||||
qse_printf (QSE_T("FORWARD: @@@@@@@@WRITE TO CGI FAILED\n"));
|
||||
/* TODO: logging ... */
|
||||
cgi->reqfwderr = 1;
|
||||
qse_mbs_clear (cgi->reqcon);
|
||||
if (cgi->req) qse_htre_discardcontent (cgi->req);
|
||||
qse_mbs_clear (cgi->reqfwdbuf);
|
||||
if (cgi->req)
|
||||
{
|
||||
qse_htre_discardcontent (cgi->req);
|
||||
/* NOTE: cgi->req may be set to QSE_NULL
|
||||
* in cgi_snatch_content() triggered by
|
||||
* qse_htre_discardcontent() */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (cgi->req == QSE_NULL)
|
||||
{
|
||||
/* no more request content */
|
||||
qse_printf (QSE_T("@@@@@@@@NOTHING MORE TO WRITE TO CGI\n"));
|
||||
/* there is nothing to read from the client side and
|
||||
* there is nothing more to forward in the forwarding buffer.
|
||||
* clear the relay and write triggers.
|
||||
*/
|
||||
qse_printf (QSE_T("FORWARD: @@@@@@@@NOTHING MORE TO WRITE TO CGI\n"));
|
||||
task->trigger_mask &=
|
||||
~(QSE_HTTPD_TASK_TRIGGER_RELAY |
|
||||
QSE_HTTPD_TASK_TRIGGER_RELAYABLE);
|
||||
QSE_HTTPD_TASK_TRIGGER_RELAYABLE |
|
||||
QSE_HTTPD_TASK_TRIGGER_WRITE |
|
||||
QSE_HTTPD_TASK_TRIGGER_WRITABLE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1950,14 +2006,14 @@ qse_printf (QSE_T("ZZZZZZZZZZZZZZZ\n"));
|
||||
{
|
||||
/* create a buffer to hold request content from the client
|
||||
* and copy content received already */
|
||||
cgi->reqcon = qse_mbs_open (httpd->mmgr, 0, (len < 512? 512: len));
|
||||
if (cgi->reqcon == QSE_NULL) goto oops;
|
||||
cgi->reqfwdbuf = qse_mbs_open (httpd->mmgr, 0, (len < 512? 512: len));
|
||||
if (cgi->reqfwdbuf == QSE_NULL) goto oops;
|
||||
|
||||
ptr = qse_htre_getcontentptr(arg->req);
|
||||
if (qse_mbs_ncpy (cgi->reqcon, ptr, len) == (qse_size_t)-1)
|
||||
if (qse_mbs_ncpy (cgi->reqfwdbuf, ptr, len) == (qse_size_t)-1)
|
||||
{
|
||||
qse_mbs_close (cgi->reqcon);
|
||||
cgi->reqcon = QSE_NULL;
|
||||
qse_mbs_close (cgi->reqfwdbuf);
|
||||
cgi->reqfwdbuf = QSE_NULL;
|
||||
goto oops;
|
||||
}
|
||||
|
||||
@ -1985,10 +2041,10 @@ qse_printf (QSE_T("HHHHHHHHHHHHHHHHhh %d\n"), (int)len);
|
||||
if the request is already set up with a callback, something will go wrong.
|
||||
*/
|
||||
/* set up a callback to be called when the request content
|
||||
* is fed to the htrd reader. qse_htre_addcontent() called
|
||||
* by htrd invokes this callback. */
|
||||
* is fed to the htrd reader. qse_htre_addcontent() that
|
||||
* htrd calls invokes this callback. */
|
||||
cgi->req = arg->req;
|
||||
qse_htre_setconcb (cgi->req, cgi_snatch_content, cgi);
|
||||
qse_htre_setconcb (cgi->req, cgi_snatch_content, task);
|
||||
|
||||
QSE_ASSERT (arg->req->attr.content_length_set);
|
||||
content_length = arg->req->attr.content_length;
|
||||
@ -2020,16 +2076,16 @@ static void task_fini_cgi (
|
||||
{
|
||||
task_cgi_t* cgi = (task_cgi_t*)task->ctx;
|
||||
if (cgi->env) qse_env_close (cgi->env);
|
||||
if (cgi->pio)
|
||||
if (cgi->pio_inited)
|
||||
{
|
||||
/* kill cgi in case it is still alive.
|
||||
* qse_pio_wait() in qse_pio_close() can block. */
|
||||
qse_pio_kill (cgi->pio);
|
||||
qse_pio_close (cgi->pio);
|
||||
qse_pio_kill (&cgi->pio);
|
||||
qse_pio_close (&cgi->pio);
|
||||
}
|
||||
if (cgi->res) qse_mbs_close (cgi->res);
|
||||
if (cgi->htrd) qse_htrd_close (cgi->htrd);
|
||||
if (cgi->reqcon) qse_mbs_close (cgi->reqcon);
|
||||
if (cgi->reqfwdbuf) qse_mbs_close (cgi->reqfwdbuf);
|
||||
if (cgi->req)
|
||||
{
|
||||
/* this task is destroyed but the request
|
||||
@ -2047,31 +2103,39 @@ static int task_main_cgi_5 (
|
||||
task_cgi_t* cgi = (task_cgi_t*)task->ctx;
|
||||
qse_ssize_t n;
|
||||
|
||||
QSE_ASSERT (cgi->pio != QSE_NULL);
|
||||
QSE_ASSERT (cgi->pio_inited);
|
||||
|
||||
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_RELAYABLE)
|
||||
{
|
||||
cgi_forward_content (httpd, task);
|
||||
/* if forwarding didn't finish, something is not really right...
|
||||
* so long as the output from CGI is finished, no more forwarding
|
||||
* is performed */
|
||||
cgi_forward_content (httpd, task, 0);
|
||||
}
|
||||
else if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)
|
||||
{
|
||||
cgi_forward_content (httpd, task, 1);
|
||||
}
|
||||
|
||||
qse_printf (QSE_T("task_main_cgi_5\n"));
|
||||
/* TODO: check if cgi outputs more than content-length if it is set... */
|
||||
httpd->errnum = QSE_HTTPD_ENOERR;
|
||||
n = httpd->cbs->client.send (httpd, client, cgi->buf, cgi->buflen);
|
||||
if (n <= -1)
|
||||
if (cgi->buflen > 0)
|
||||
{
|
||||
/* TODO: check if cgi outputs more than content-length if it is set... */
|
||||
httpd->errnum = QSE_HTTPD_ENOERR;
|
||||
n = httpd->cbs->client.send (httpd, client, cgi->buf, cgi->buflen);
|
||||
if (n <= -1)
|
||||
{
|
||||
/* can't return internal server error any more... */
|
||||
/* TODO: logging ... */
|
||||
return -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
QSE_MEMCPY (&cgi->buf[0], &cgi->buf[n], cgi->buflen - n);
|
||||
cgi->buflen -= n;
|
||||
}
|
||||
|
||||
QSE_MEMCPY (&cgi->buf[0], &cgi->buf[n], cgi->buflen - n);
|
||||
cgi->buflen -= n;
|
||||
|
||||
return (cgi->buflen > 0)? 1: 0;
|
||||
/* if forwarding didn't finish, something is not really right...
|
||||
* so long as the output from CGI is finished, no more forwarding
|
||||
* is performed */
|
||||
return (cgi->buflen > 0 || cgi->req ||
|
||||
(cgi->reqfwdbuf && QSE_MBS_LEN(cgi->reqfwdbuf) > 0))? 1: 0;
|
||||
}
|
||||
|
||||
static int task_main_cgi_4 (
|
||||
@ -2080,11 +2144,15 @@ static int task_main_cgi_4 (
|
||||
task_cgi_t* cgi = (task_cgi_t*)task->ctx;
|
||||
qse_ssize_t n;
|
||||
|
||||
QSE_ASSERT (cgi->pio != QSE_NULL);
|
||||
QSE_ASSERT (cgi->pio_inited);
|
||||
|
||||
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_RELAYABLE)
|
||||
{
|
||||
cgi_forward_content (httpd, task);
|
||||
cgi_forward_content (httpd, task, 0);
|
||||
}
|
||||
else if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)
|
||||
{
|
||||
cgi_forward_content (httpd, task, 1);
|
||||
}
|
||||
|
||||
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READABLE)
|
||||
@ -2110,7 +2178,7 @@ static int task_main_cgi_4 (
|
||||
/* <- can i make it non-block?? or use select??? pio_tryread()? */
|
||||
|
||||
n = qse_pio_read (
|
||||
cgi->pio, QSE_PIO_OUT,
|
||||
&cgi->pio, QSE_PIO_OUT,
|
||||
&cgi->buf[cgi->buflen + QSE_SIZEOF(chunklen) - 1],
|
||||
count - extra
|
||||
);
|
||||
@ -2157,7 +2225,7 @@ static int task_main_cgi_4 (
|
||||
{
|
||||
qse_printf (QSE_T("READING IN NON-CHUNKED MODE...\n"));
|
||||
n = qse_pio_read (
|
||||
cgi->pio, QSE_PIO_OUT,
|
||||
&cgi->pio, QSE_PIO_OUT,
|
||||
&cgi->buf[cgi->buflen],
|
||||
QSE_SIZEOF(cgi->buf) - cgi->buflen
|
||||
);
|
||||
@ -2226,7 +2294,11 @@ static int task_main_cgi_3 (
|
||||
|
||||
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_RELAYABLE)
|
||||
{
|
||||
cgi_forward_content (httpd, task);
|
||||
cgi_forward_content (httpd, task, 0);
|
||||
}
|
||||
else if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)
|
||||
{
|
||||
cgi_forward_content (httpd, task, 1);
|
||||
}
|
||||
|
||||
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READABLE)
|
||||
@ -2273,13 +2345,17 @@ static int task_main_cgi_2 (
|
||||
task_cgi_t* cgi = (task_cgi_t*)task->ctx;
|
||||
qse_ssize_t n;
|
||||
|
||||
QSE_ASSERT (cgi->pio != QSE_NULL);
|
||||
QSE_ASSERT (cgi->pio_inited);
|
||||
|
||||
qse_printf (QSE_T("[cgi_2 ]\n"));
|
||||
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_RELAYABLE)
|
||||
{
|
||||
qse_printf (QSE_T("[cgi_2 write]\n"));
|
||||
cgi_forward_content (httpd, task);
|
||||
cgi_forward_content (httpd, task, 0);
|
||||
}
|
||||
else if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)
|
||||
{
|
||||
cgi_forward_content (httpd, task, 1);
|
||||
}
|
||||
|
||||
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READABLE)
|
||||
@ -2287,7 +2363,7 @@ qse_printf (QSE_T("[cgi_2 write]\n"));
|
||||
qse_printf (QSE_T("[cgi_2 read]\n"));
|
||||
/* <- can i make it non-block?? or use select??? pio_tryread()? */
|
||||
n = qse_pio_read (
|
||||
cgi->pio, QSE_PIO_OUT,
|
||||
&cgi->pio, QSE_PIO_OUT,
|
||||
&cgi->buf[cgi->buflen],
|
||||
QSE_SIZEOF(cgi->buf) - cgi->buflen
|
||||
);
|
||||
@ -2359,6 +2435,7 @@ static int task_main_cgi (
|
||||
{
|
||||
task_cgi_t* cgi = (task_cgi_t*)task->ctx;
|
||||
int pio_options;
|
||||
int http_errnum = 500;
|
||||
|
||||
if (cgi->init_failed) goto oops;
|
||||
|
||||
@ -2398,39 +2475,67 @@ static int task_main_cgi (
|
||||
if (httpd->option & QSE_HTTPD_CGINOCLOEXEC)
|
||||
pio_options |= QSE_PIO_NOCLOEXEC;
|
||||
|
||||
cgi->pio = qse_pio_open (
|
||||
httpd->mmgr, 0, (const qse_char_t*)cgi->path,
|
||||
cgi->env, pio_options
|
||||
);
|
||||
if (cgi->pio == QSE_NULL) goto oops;
|
||||
|
||||
/* TODO: use a different field for different OS???
|
||||
HANDLE for win32???
|
||||
*/
|
||||
/* set the trigger that the main loop can use this
|
||||
* handle for multiplexing */
|
||||
task->trigger_mask = QSE_HTTPD_TASK_TRIGGER_READ;
|
||||
task->trigger[0].i = qse_pio_gethandle (cgi->pio, QSE_PIO_OUT);
|
||||
if (cgi->reqcon)
|
||||
if (qse_pio_init (
|
||||
&cgi->pio, httpd->mmgr, (const qse_char_t*)cgi->path,
|
||||
cgi->env, pio_options) <= -1)
|
||||
{
|
||||
/* not meaningful to check writability to the child process
|
||||
* in the main loop. it is checked in cgi_forward_content().
|
||||
* so the following 2 lines are commented out.
|
||||
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_WRITE;
|
||||
task->trigger[1].i = qse_pio_gethandle (cgi->pio, QSE_PIO_IN);*/
|
||||
qse_pio_errnum_t errnum;
|
||||
|
||||
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_RELAY;
|
||||
task->trigger[1].i = client->handle.i;
|
||||
errnum = qse_pio_geterrnum (&cgi->pio);
|
||||
|
||||
if (errnum == QSE_PIO_ENOENT) http_errnum = 404;
|
||||
else if (errnum == QSE_PIO_EACCES) http_errnum = 403;
|
||||
|
||||
goto oops;
|
||||
}
|
||||
|
||||
if (cgi->reqcon)
|
||||
cgi->pio_inited = 1;
|
||||
|
||||
/* set the trigger that the main loop can use this
|
||||
* handle for multiplexing
|
||||
*
|
||||
* it the output from the child is available, this task
|
||||
* writes it back to the client. so add a trigger for
|
||||
* checking the data availability from the child process */
|
||||
task->trigger_mask = QSE_HTTPD_TASK_TRIGGER_READ;
|
||||
task->trigger[0] = qse_pio_gethandleasubi (&cgi->pio, QSE_PIO_OUT);
|
||||
if (cgi->reqfwdbuf)
|
||||
{
|
||||
/* since i didn't set triggers in the initializer (task_init_cgi()),
|
||||
* it is possible that some contents has been read in already,
|
||||
* forward them first. cgi_forward_content() is called after
|
||||
* triggers are added above because cgi_forwrad_content()
|
||||
* manipulates triggers when a forwarding error occurs. */
|
||||
cgi_forward_content (httpd, task);
|
||||
/* the existence of the forwarding buffer leads to a trigger
|
||||
* for checking data availiability from the client side. */
|
||||
|
||||
if (cgi->req)
|
||||
{
|
||||
/* there are still things to forward from the client-side.
|
||||
* i can rely on this relay trigger for task invocation. */
|
||||
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_RELAY;
|
||||
task->trigger[1].i = client->handle.i;
|
||||
}
|
||||
else if (QSE_MBS_LEN(cgi->reqfwdbuf) > 0)
|
||||
{
|
||||
/* there's nothing more to read from the client side but
|
||||
* some contents are already read into the forwarding buffer.
|
||||
* this is possible because the main loop can still read
|
||||
* between the initializer function (task_init_cgi()) and
|
||||
* this function. so let's forward it initially. */
|
||||
qse_printf (QSE_T("FORWARDING INITIAL PART OF CONTENT...\n"));
|
||||
cgi_forward_content (httpd, task, 0);
|
||||
|
||||
/* if the initial forwarding clears the forwarding
|
||||
* buffer, there is nothing more to forward.
|
||||
* (nothing more to read from the client side, nothing
|
||||
* left in the forwarding buffer). if not, this task should
|
||||
* still be invoked for forwarding.
|
||||
*/
|
||||
if (QSE_MBS_LEN(cgi->reqfwdbuf) > 0)
|
||||
{
|
||||
/* since the buffer is not cleared, this task needs
|
||||
* a trigger for invocation. ask the main loop to
|
||||
* invoke this task so long as it is able to write
|
||||
* to the child process */
|
||||
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_WRITE;
|
||||
task->trigger[2] = qse_pio_gethandleasubi (&cgi->pio, QSE_PIO_IN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task->main = cgi->nph? task_main_cgi_4: task_main_cgi_2;
|
||||
@ -2451,7 +2556,9 @@ oops:
|
||||
cgi->htrd = QSE_NULL;
|
||||
}
|
||||
|
||||
return (entask_error (httpd, client, task, 500, &cgi->version, cgi->keepalive) == QSE_NULL)? -1: 0;
|
||||
return (entask_error (
|
||||
httpd, client, task, http_errnum,
|
||||
&cgi->version, cgi->keepalive) == QSE_NULL)? -1: 0;
|
||||
}
|
||||
|
||||
/* TODO: global option or individual paramter for max cgi lifetime
|
||||
|
@ -249,7 +249,13 @@ static int file_ropen (
|
||||
|
||||
qse_printf (QSE_T("opening file [%hs] for reading\n"), path);
|
||||
fd = open (path, flags, 0);
|
||||
if (fd <= -1) return -1;
|
||||
if (fd <= -1)
|
||||
{
|
||||
qse_httpd_seterrnum (httpd,
|
||||
(errno == ENOENT? QSE_HTTPD_ENOENT:
|
||||
errno == EACCES? QSE_HTTPD_EACCES: QSE_HTTPD_ESUBSYS));
|
||||
return -1;
|
||||
}
|
||||
|
||||
flags = fcntl (fd, F_GETFD);
|
||||
if (flags >= 0) fcntl (fd, F_SETFD, flags | FD_CLOEXEC);
|
||||
@ -257,12 +263,16 @@ qse_printf (QSE_T("opening file [%hs] for reading\n"), path);
|
||||
/* TODO: fstat64??? */
|
||||
if (fstat (fd, &st) <= -1)
|
||||
{
|
||||
qse_httpd_seterrnum (httpd,
|
||||
(errno == ENOENT? QSE_HTTPD_ENOENT:
|
||||
errno == EACCES? QSE_HTTPD_EACCES: QSE_HTTPD_ESUBSYS));
|
||||
close (fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode))
|
||||
if (!S_ISREG(st.st_mode))
|
||||
{
|
||||
qse_httpd_seterrnum (httpd, QSE_HTTPD_EACCES);
|
||||
close (fd);
|
||||
return -1;
|
||||
}
|
||||
@ -287,7 +297,13 @@ static int file_wopen (
|
||||
|
||||
qse_printf (QSE_T("opening file [%hs] for writing\n"), path);
|
||||
fd = open (path, flags, 0644);
|
||||
if (fd <= -1) return -1;
|
||||
if (fd <= -1)
|
||||
{
|
||||
qse_httpd_seterrnum (httpd,
|
||||
(errno == ENOENT? QSE_HTTPD_ENOENT:
|
||||
errno == EACCES? QSE_HTTPD_EACCES: QSE_HTTPD_ESUBSYS));
|
||||
return -1;
|
||||
}
|
||||
|
||||
handle->i = fd;
|
||||
return 0;
|
||||
@ -488,6 +504,7 @@ if (qse_htre_getcontentlen(req) > 0)
|
||||
qse_printf (QSE_T("CONTENT after discard = [%.*S]\n"), (int)qse_htre_getcontentlen(req), qse_htre_getcontentptr(req));
|
||||
}
|
||||
|
||||
|
||||
if (method == QSE_HTTP_GET || method == QSE_HTTP_POST)
|
||||
{
|
||||
const qse_mchar_t* qpath = qse_htre_getqpathptr(req);
|
||||
@ -583,7 +600,7 @@ qse_printf (QSE_T("Entasking chunked CGI...\n"));
|
||||
|
||||
oops:
|
||||
/*qse_httpd_markbadclient (httpd, client);*/
|
||||
return 0; /* TODO: return failure??? */
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int peek_request (
|
||||
|
Loading…
Reference in New Issue
Block a user