made some enhancements to httpd
This commit is contained in:
parent
a782229fa2
commit
681e50822c
@ -43,38 +43,47 @@ enum qse_pio_flag_t
|
||||
QSE_PIO_NOAUTOFLUSH = (1 << 2),
|
||||
|
||||
/** execute the command via a system shell
|
||||
* (/bin/sh on *nix, cmd.exe on windows) */
|
||||
* (/bin/sh on unix/linux, cmd.exe on windows and os2) */
|
||||
QSE_PIO_SHELL = (1 << 3),
|
||||
|
||||
/** indicate that the command to qse_pio_open() is a multi-byte string.
|
||||
* it is useful if #QSE_CHAR_IS_WCHAR is defined. */
|
||||
QSE_PIO_MBSCMD = (1 << 4),
|
||||
|
||||
/** don't attempt to close open file descriptors unknown to pio.
|
||||
* it is useful only on a unix-like systems where file descriptors
|
||||
* not set with FD_CLOEXEC are inherited by a child process.
|
||||
* you're advised to set this option if all normal file descriptors
|
||||
* in your application are open with FD_CLOEXEC set. it can skip
|
||||
* checking a bunch of file descriptors and arranging to close
|
||||
* them to prevent inheritance. */
|
||||
QSE_PIO_NOCLOEXEC = (1 << 5),
|
||||
|
||||
/** write to stdin of a child process */
|
||||
QSE_PIO_WRITEIN = (1 << 8),
|
||||
QSE_PIO_WRITEIN = (1 << 8),
|
||||
/** read stdout of a child process */
|
||||
QSE_PIO_READOUT = (1 << 9),
|
||||
QSE_PIO_READOUT = (1 << 9),
|
||||
/** read stderr of a child process */
|
||||
QSE_PIO_READERR = (1 << 10),
|
||||
QSE_PIO_READERR = (1 << 10),
|
||||
|
||||
/** redirect stderr to stdout (2>&1, require #QSE_PIO_READOUT) */
|
||||
QSE_PIO_ERRTOOUT = (1 << 11),
|
||||
QSE_PIO_ERRTOOUT = (1 << 11),
|
||||
/** redirect stdout to stderr (1>&2, require #QSE_PIO_READERR) */
|
||||
QSE_PIO_OUTTOERR = (1 << 12),
|
||||
QSE_PIO_OUTTOERR = (1 << 12),
|
||||
|
||||
/** redirect stdin to the null device (</dev/null, <NUL) */
|
||||
QSE_PIO_INTONUL = (1 << 13),
|
||||
QSE_PIO_INTONUL = (1 << 13),
|
||||
/** redirect stdin to the null device (>/dev/null, >NUL) */
|
||||
QSE_PIO_ERRTONUL = (1 << 14),
|
||||
QSE_PIO_ERRTONUL = (1 << 14),
|
||||
/** redirect stderr to the null device (2>/dev/null, 2>NUL) */
|
||||
QSE_PIO_OUTTONUL = (1 << 15),
|
||||
QSE_PIO_OUTTONUL = (1 << 15),
|
||||
|
||||
/** drop stdin */
|
||||
QSE_PIO_DROPIN = (1 << 16),
|
||||
QSE_PIO_DROPIN = (1 << 16),
|
||||
/** drop stdout */
|
||||
QSE_PIO_DROPOUT = (1 << 17),
|
||||
QSE_PIO_DROPOUT = (1 << 17),
|
||||
/** drop stderr */
|
||||
QSE_PIO_DROPERR = (1 << 18)
|
||||
QSE_PIO_DROPERR = (1 << 18)
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -43,6 +43,11 @@ enum qse_httpd_errnum_t
|
||||
};
|
||||
typedef enum qse_httpd_errnum_t qse_httpd_errnum_t;
|
||||
|
||||
enum qse_httpd_option_t
|
||||
{
|
||||
QSE_HTTPD_CGINOCLOEXEC = (1 << 0)
|
||||
};
|
||||
|
||||
typedef struct qse_httpd_cbs_t qse_httpd_cbs_t;
|
||||
struct qse_httpd_cbs_t
|
||||
{
|
||||
@ -77,10 +82,15 @@ typedef int (*qse_httpd_task_main_t) (
|
||||
|
||||
struct qse_httpd_task_t
|
||||
{
|
||||
/* you must not call another entask functions from within initailizer */
|
||||
/* you must not call another entask functions from within
|
||||
* an initailizer. you can call entask functions from within
|
||||
* a finalizer and a main function. */
|
||||
qse_httpd_task_init_t init;
|
||||
qse_httpd_task_fini_t fini;
|
||||
qse_httpd_task_main_t main;
|
||||
|
||||
qse_ubi_t trigger;
|
||||
|
||||
void* ctx;
|
||||
};
|
||||
|
||||
@ -105,6 +115,15 @@ void qse_httpd_close (
|
||||
qse_httpd_t* httpd
|
||||
);
|
||||
|
||||
int qse_httpd_getoption (
|
||||
qse_httpd_t* httpd
|
||||
);
|
||||
|
||||
void qse_httpd_setoption (
|
||||
qse_httpd_t* httpd,
|
||||
int option
|
||||
);
|
||||
|
||||
const qse_httpd_cbs_t* qse_httpd_getcbs (
|
||||
qse_httpd_t* httpd
|
||||
);
|
||||
|
@ -270,9 +270,10 @@ static int make_param (
|
||||
}
|
||||
|
||||
#if defined(QSE_CHAR_IS_MCHAR)
|
||||
if (mcmd && mcmd != cmd) param->mcmd = mcmd;
|
||||
if (mcmd && mcmd != (qse_mchar_t*)cmd) param->mcmd = mcmd;
|
||||
#else
|
||||
if (mcmd && mcmd != cmd && mcmd != param->fixed_mbuf) param->mcmd = mcmd;
|
||||
if (mcmd && mcmd != (qse_mchar_t*)cmd &&
|
||||
mcmd != param->fixed_mbuf) param->mcmd = mcmd;
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
@ -280,7 +281,8 @@ oops:
|
||||
#if defined(QSE_CHAR_IS_MCHAR)
|
||||
if (mcmd && mcmd != cmd) QSE_MMGR_FREE (pio->mmgr, mcmd);
|
||||
#else
|
||||
if (mcmd && mcmd != cmd && mcmd != param->fixed_mbuf) QSE_MMGR_FREE (pio->mmgr, mcmd);
|
||||
if (mcmd && mcmd != (qse_mchar_t*)cmd &&
|
||||
mcmd != param->fixed_mbuf) QSE_MMGR_FREE (pio->mmgr, mcmd);
|
||||
if (wcmd) QSE_MMGR_FREE (pio->mmgr, wcmd);
|
||||
#endif
|
||||
return -1;
|
||||
@ -288,7 +290,18 @@ oops:
|
||||
|
||||
static QSE_INLINE int is_fd_valid (int fd)
|
||||
{
|
||||
return fcntl (fd, F_GETFL) != -1 || errno != EBADF;
|
||||
return fcntl (fd, F_GETFD) != -1 || errno != EBADF;
|
||||
}
|
||||
|
||||
static QSE_INLINE int is_fd_valid_and_nocloexec (int fd)
|
||||
{
|
||||
int flags = fcntl (fd, F_GETFD);
|
||||
if (flags == -1)
|
||||
{
|
||||
if (errno == EBADF) return 0; /* invalid. return false */
|
||||
return -1; /* unknown. true but negative to indicate unknown */
|
||||
}
|
||||
return !(flags & FD_CLOEXEC)? 1: 0;
|
||||
}
|
||||
|
||||
static int get_highest_fd (void)
|
||||
@ -962,21 +975,21 @@ int qse_pio_init (
|
||||
if ((flags & QSE_PIO_DROPERR) && is_fd_valid(2) &&
|
||||
posix_spawn_file_actions_addclose (&fa, 2) != 0) goto oops;
|
||||
|
||||
if (!(flags & QSE_PIO_NOCLOEXEC))
|
||||
{
|
||||
int fd = get_highest_fd ();
|
||||
while (--fd > 2)
|
||||
{
|
||||
if (fd != handle[0] &&
|
||||
fd != handle[1] &&
|
||||
fd != handle[2] &&
|
||||
fd != handle[3] &&
|
||||
fd != handle[4] &&
|
||||
fd != handle[5])
|
||||
{
|
||||
/* closing attempt on a best-effort basis */
|
||||
if (is_fd_valid(fd) &&
|
||||
posix_spawn_file_actions_addclose (&fa, fd) != 0) goto oops;
|
||||
}
|
||||
if (fd == handle[0] || fd == handle[1] ||
|
||||
fd == handle[2] || fd == handle[3] ||
|
||||
fd == handle[4] || fd == handle[5]) continue;
|
||||
|
||||
/* closing attempt on a best-effort basis.
|
||||
* posix_spawn() fails if the file descriptor added
|
||||
* with addclose() is closed before posix_spawn().
|
||||
* addclose() if no FD_CLOEXEC is set or it's unknown. */
|
||||
if (is_fd_valid_and_nocloexec(fd) &&
|
||||
posix_spawn_file_actions_addclose (&fa, fd) != 0) goto oops;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1044,17 +1057,19 @@ int qse_pio_init (
|
||||
{
|
||||
/* child */
|
||||
qse_pio_hnd_t devnull = -1;
|
||||
int fd = get_highest_fd ();
|
||||
|
||||
/* don't close stdin/out/err and the pipes */
|
||||
while (--fd > 2)
|
||||
if (!(flags & QSE_PIO_NOCLOEXEC))
|
||||
{
|
||||
if (fd != handle[0] &&
|
||||
fd != handle[1] &&
|
||||
fd != handle[2] &&
|
||||
fd != handle[3] &&
|
||||
fd != handle[4] &&
|
||||
fd != handle[5]) QSE_CLOSE (fd);
|
||||
int fd = get_highest_fd ();
|
||||
|
||||
/* close all other unknown open handles except
|
||||
* stdin/out/err and the pipes. */
|
||||
while (--fd > 2)
|
||||
{
|
||||
if (fd != handle[0] && fd != handle[1] &&
|
||||
fd != handle[2] && fd != handle[3] &&
|
||||
fd != handle[4] && fd != handle[5]) QSE_CLOSE (fd);
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & QSE_PIO_WRITEIN)
|
||||
|
@ -99,16 +99,16 @@
|
||||
#endif
|
||||
|
||||
#if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(SYS_fstat64)
|
||||
# define QSE_FSTAT(path,stbuf) syscall(SYS_fstat64,path,stbuf)
|
||||
# define QSE_FSTAT(handle,stbuf) syscall(SYS_fstat64,handle,stbuf)
|
||||
typedef struct stat64 qse_fstat_t;
|
||||
#elif defined(SYS_fstat)
|
||||
# define QSE_FSTAT(path,stbuf) syscall(SYS_fstat,path,stbuf)
|
||||
# define QSE_FSTAT(handle,stbuf) syscall(SYS_fstat,handle,stbuf)
|
||||
typedef struct stat qse_fstat_t;
|
||||
#elif !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(HAVE_FSTAT64)
|
||||
# define QSE_FSTAT(path,stbuf) fstat64(path,stbuf)
|
||||
# define QSE_FSTAT(handle,stbuf) fstat64(handle,stbuf)
|
||||
typedef struct stat64 qse_fstat_t;
|
||||
#else
|
||||
# define QSE_FSTAT(path,stbuf) fstat(path,stbuf)
|
||||
# define QSE_FSTAT(handle,stbuf) fstat(handle,stbuf)
|
||||
typedef struct stat qse_fstat_t;
|
||||
#endif
|
||||
|
||||
@ -289,6 +289,12 @@
|
||||
typedef struct stat qse_lstat_t;
|
||||
#endif
|
||||
|
||||
#if defined(SYS_access)
|
||||
# define QSE_ACCESS(path,mode) syscall(SYS_access,path,mode)
|
||||
#else
|
||||
# define QSE_ACCESS(path,mode) access(path,mode)
|
||||
#endif
|
||||
|
||||
#if defined(SYS_rename)
|
||||
# define QSE_RENAME(oldpath,newpath) syscall(SYS_rename,oldpath,newpath)
|
||||
#else
|
||||
|
@ -111,6 +111,16 @@ void qse_httpd_stop (qse_httpd_t* httpd)
|
||||
httpd->stopreq = 1;
|
||||
}
|
||||
|
||||
int qse_httpd_getoption (qse_httpd_t* httpd)
|
||||
{
|
||||
return httpd->option;
|
||||
}
|
||||
|
||||
void qse_httpd_setoption (qse_httpd_t* httpd, int option)
|
||||
{
|
||||
httpd->option = option;
|
||||
}
|
||||
|
||||
const qse_httpd_cbs_t* qse_httpd_getcbs (qse_httpd_t* httpd)
|
||||
{
|
||||
return httpd->cbs;
|
||||
@ -573,13 +583,14 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: too many client?\n"));
|
||||
* arrival for wrong checksum, for example. */
|
||||
flag = fcntl (c, F_GETFL);
|
||||
if (flag >= 0) fcntl (c, F_SETFL, flag | O_NONBLOCK);
|
||||
fcntl (c, F_SETFD, FD_CLOEXEC);
|
||||
|
||||
flag = fcntl (c, F_GETFD);
|
||||
if (flag >= 0) fcntl (c, F_SETFD, flag | FD_CLOEXEC);
|
||||
|
||||
#if defined(HAVE_PTHREAD)
|
||||
if (httpd->threaded) pthread_mutex_lock (&httpd->client.mutex);
|
||||
#endif
|
||||
client = insert_into_client_array (httpd, c, &addr);
|
||||
|
||||
#if defined(HAVE_PTHREAD)
|
||||
if (httpd->threaded) pthread_mutex_unlock (&httpd->client.mutex);
|
||||
#endif
|
||||
@ -614,12 +625,12 @@ httpd->cbs.on_error (httpd, l).... */
|
||||
}
|
||||
|
||||
static int make_fd_set_from_client_array (
|
||||
qse_httpd_t* httpd, fd_set* r, fd_set* w)
|
||||
qse_httpd_t* httpd, fd_set* r, fd_set* w, int for_rdwr)
|
||||
{
|
||||
int fd, max = -1;
|
||||
client_array_t* ca = &httpd->client.array;
|
||||
|
||||
if (r)
|
||||
if (r && for_rdwr)
|
||||
{
|
||||
max = httpd->listener.max;
|
||||
*r = httpd->listener.set;
|
||||
@ -635,15 +646,48 @@ static int make_fd_set_from_client_array (
|
||||
{
|
||||
if (r && !ca->data[fd].bad)
|
||||
{
|
||||
FD_SET (ca->data[fd].handle.i, r);
|
||||
if (ca->data[fd].handle.i > max) max = ca->data[fd].handle.i;
|
||||
if (for_rdwr)
|
||||
{
|
||||
/* add a client-side handle to the read set */
|
||||
FD_SET (ca->data[fd].handle.i, r);
|
||||
if (ca->data[fd].handle.i > max) max = ca->data[fd].handle.i;
|
||||
}
|
||||
|
||||
if (ca->data[fd].task.queue.head &&
|
||||
ca->data[fd].task.queue.head->task.trigger.i >= 0)
|
||||
{
|
||||
/* if a trigger is available, add it to the read set also */
|
||||
FD_SET (ca->data[fd].task.queue.head->task.trigger.i, r);
|
||||
if (ca->data[fd].task.queue.head->task.trigger.i > max)
|
||||
max = ca->data[fd].task.queue.head->task.trigger.i;
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
if (w && (ca->data[fd].task.queue.count > 0 || ca->data[fd].bad))
|
||||
{
|
||||
/* add it to the set if it has a response to send */
|
||||
FD_SET (ca->data[fd].handle.i, w);
|
||||
if (ca->data[fd].handle.i > max) max = ca->data[fd].handle.i;
|
||||
}
|
||||
#endif
|
||||
if (w)
|
||||
{
|
||||
if (ca->data[fd].bad ||
|
||||
(ca->data[fd].task.queue.head &&
|
||||
ca->data[fd].task.queue.head->task.trigger.i <= -1))
|
||||
{
|
||||
/* add a client-side handle to the write set
|
||||
* if the client is already marked bad or
|
||||
* the current task enqueued didn't specify a trigger.
|
||||
*
|
||||
* if the task doesn't have a trigger, i perform
|
||||
* the task so long as the client side-handle is
|
||||
* available for writing in the main loop.
|
||||
*/
|
||||
FD_SET (ca->data[fd].handle.i, w);
|
||||
if (ca->data[fd].handle.i > max) max = ca->data[fd].handle.i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -680,14 +724,14 @@ static void* response_thread (void* arg)
|
||||
while (!httpd->stopreq)
|
||||
{
|
||||
int n, max, fd;
|
||||
fd_set w;
|
||||
fd_set r, w;
|
||||
struct timeval tv;
|
||||
|
||||
tv.tv_sec = 1;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
pthread_mutex_lock (&httpd->client.mutex);
|
||||
max = make_fd_set_from_client_array (httpd, QSE_NULL, &w);
|
||||
max = make_fd_set_from_client_array (httpd, &r, &w, 0);
|
||||
pthread_mutex_unlock (&httpd->client.mutex);
|
||||
|
||||
while (max == -1 && !httpd->stopreq)
|
||||
@ -702,14 +746,14 @@ static void* response_thread (void* arg)
|
||||
timeout.tv_nsec = now.tv_usec * 1000;
|
||||
|
||||
pthread_cond_timedwait (&httpd->client.cond, &httpd->client.mutex, &timeout);
|
||||
max = make_fd_set_from_client_array (httpd, QSE_NULL, &w);
|
||||
max = make_fd_set_from_client_array (httpd, &r, &w, 0);
|
||||
|
||||
pthread_mutex_unlock (&httpd->client.mutex);
|
||||
}
|
||||
|
||||
if (httpd->stopreq) break;
|
||||
|
||||
n = select (max + 1, QSE_NULL, &w, QSE_NULL, &tv);
|
||||
n = select (max + 1, &r, &w, QSE_NULL, &tv);
|
||||
if (n <= -1)
|
||||
{
|
||||
/*if (errno == EINTR) continue; */
|
||||
@ -728,21 +772,45 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure - %hs\n"), strerr
|
||||
|
||||
if (!client->htrd) continue;
|
||||
|
||||
/* ---------------------------------------------- */
|
||||
#if 0
|
||||
if (FD_ISSET(client->handle.i, &w))
|
||||
{
|
||||
if (client->bad)
|
||||
{
|
||||
/*send (client->handle, i, "INTERNAL SERVER ERROR..", ...);*/
|
||||
/*shutdown (client->handle.i, SHUT_RDWR);*/
|
||||
pthread_mutex_lock (&httpd->client.mutex);
|
||||
delete_from_client_array (httpd, fd);
|
||||
pthread_mutex_unlock (&httpd->client.mutex);
|
||||
}
|
||||
else if (client->task.queue.count > 0)
|
||||
{
|
||||
perform_task (httpd, client);
|
||||
if (client->task.queue.head->task.trigger.i <= -1 ||
|
||||
FD_ISSET(client->task.queue.head->task.trigger.i, &r))
|
||||
{
|
||||
perform_task (httpd, client);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (client->bad)
|
||||
{
|
||||
/*shutdown (client->handle.i, SHUT_RDWR);*/
|
||||
pthread_mutex_lock (&httpd->client.mutex);
|
||||
delete_from_client_array (httpd, fd);
|
||||
pthread_mutex_unlock (&httpd->client.mutex);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (client->task.queue.head->task.trigger.i <= -1 ||
|
||||
FD_ISSET(client->task.queue.head->task.trigger.i, &r))
|
||||
{
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
FD_ZERO (&w);
|
||||
FD_SET (client->handle.i, &w);
|
||||
n = select (max + 1, QSE_NULL, &w, QSE_NULL, &tv);
|
||||
if (n > 0 && FD_ISSET(client->handle.i, &w))
|
||||
perform_task (httpd, client);
|
||||
}
|
||||
}
|
||||
/* ---------------------------------------------- */
|
||||
}
|
||||
}
|
||||
|
||||
@ -760,7 +828,13 @@ reread:
|
||||
m = read (client->handle.i, buf, QSE_SIZEOF(buf));
|
||||
if (m <= -1)
|
||||
{
|
||||
if (errno != EINTR)
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
{
|
||||
/* nothing to read yet. */
|
||||
qse_fprintf (QSE_STDERR, QSE_T("Warning: Nothing to read from a client %d\n"), client->handle.i);
|
||||
return 0; /* return ok */
|
||||
}
|
||||
else if (errno != EINTR)
|
||||
{
|
||||
httpd->errnum = QSE_HTTPD_ESOCKET;
|
||||
qse_fprintf (QSE_STDERR, QSE_T("Error: failed to read from a client %d\n"), client->handle.i);
|
||||
@ -856,11 +930,12 @@ int qse_httpd_loop (qse_httpd_t* httpd, int threaded)
|
||||
#if defined(HAVE_PTHREAD)
|
||||
if (httpd->threaded) pthread_mutex_lock (&httpd->client.mutex);
|
||||
#endif
|
||||
max = make_fd_set_from_client_array (httpd, &r, &w);
|
||||
max = make_fd_set_from_client_array (httpd, &r, &w, 1);
|
||||
#if defined(HAVE_PTHREAD)
|
||||
if (httpd->threaded) pthread_mutex_unlock (&httpd->client.mutex);
|
||||
#endif
|
||||
|
||||
/*n = select (max + 1, &r, &w, QSE_NULL, &tv);*/
|
||||
n = select (max + 1, &r, &w, QSE_NULL, &tv);
|
||||
if (n <= -1)
|
||||
{
|
||||
@ -871,7 +946,10 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure\n"));
|
||||
/* break; */
|
||||
continue;
|
||||
}
|
||||
if (n == 0) continue;
|
||||
if (n == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* check the listener activity */
|
||||
accept_client_from_listeners (httpd, &r);
|
||||
@ -905,6 +983,9 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure\n"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------*/
|
||||
#if 0
|
||||
if (!httpd->threaded && FD_ISSET(client->handle.i, &w))
|
||||
{
|
||||
/* output is handled in the main loop if and
|
||||
@ -920,10 +1001,43 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure\n"));
|
||||
}
|
||||
else if (client->task.queue.count > 0)
|
||||
{
|
||||
perform_task (httpd, client);
|
||||
if (client->task.queue.head->task.trigger.i <= -1 ||
|
||||
FD_ISSET(client->task.queue.head->task.trigger.i, &r))
|
||||
{
|
||||
perform_task (httpd, client);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!httpd->threaded)
|
||||
{
|
||||
if (client->bad)
|
||||
{
|
||||
/*shutdown (client->handle.i, SHUT_RDWR);*/
|
||||
/*pthread_mutex_lock (&httpd->client.mutex);*/
|
||||
delete_from_client_array (httpd, fd);
|
||||
/*pthread_mutex_unlock (&httpd->client.mutex);*/
|
||||
}
|
||||
else if (client->task.queue.count > 0)
|
||||
{
|
||||
if (client->task.queue.head->task.trigger.i <= -1 ||
|
||||
FD_ISSET(client->task.queue.head->task.trigger.i, &r))
|
||||
{
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
FD_ZERO (&w);
|
||||
FD_SET (client->handle.i, &w);
|
||||
n = select (max + 1, QSE_NULL, &w, QSE_NULL, &tv);
|
||||
|
||||
/* TODO: logging if n == -1 */
|
||||
|
||||
if (n > 0 && FD_ISSET(client->handle.i, &w))
|
||||
perform_task (httpd, client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,6 +112,7 @@ struct qse_httpd_t
|
||||
qse_httpd_errnum_t errnum;
|
||||
qse_httpd_cbs_t* cbs;
|
||||
|
||||
int option;
|
||||
int stopreq;
|
||||
int threaded;
|
||||
|
||||
|
@ -25,12 +25,12 @@
|
||||
|
||||
#include "httpd.h"
|
||||
#include "../cmn/mem.h"
|
||||
#include "../cmn/syscall.h"
|
||||
#include <qse/cmn/str.h>
|
||||
#include <qse/cmn/chr.h>
|
||||
#include <qse/cmn/pio.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
@ -131,6 +131,7 @@ qse_httpd_task_t* qse_httpd_entaskdisconnect (
|
||||
|
||||
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
|
||||
task.main = task_main_disconnect;
|
||||
task.trigger.i = -1;
|
||||
|
||||
return qse_httpd_entask (httpd, client, pred, &task, 0);
|
||||
}
|
||||
@ -177,6 +178,7 @@ qse_httpd_task_t* qse_httpd_entaskstatictext (
|
||||
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
|
||||
task.main = task_main_statictext;
|
||||
task.ctx = (void*)text;
|
||||
task.trigger.i = -1;
|
||||
|
||||
return qse_httpd_entask (httpd, client, pred, &task, 0);
|
||||
}
|
||||
@ -247,6 +249,7 @@ qse_httpd_task_t* qse_httpd_entasktext (
|
||||
task.init = task_init_text;
|
||||
task.main = task_main_text;
|
||||
task.ctx = &data;
|
||||
task.trigger.i = -1;
|
||||
|
||||
return qse_httpd_entask (
|
||||
httpd, client, pred, &task, QSE_SIZEOF(data) + data.left);
|
||||
@ -397,8 +400,9 @@ qse_httpd_task_t* qse_httpd_entaskformat (
|
||||
task.fini = task_fini_format;
|
||||
task.main = task_main_format;
|
||||
task.ctx = &data;
|
||||
task.trigger.i = -1;
|
||||
|
||||
qse_printf (QSE_T("SEND: [%.*S]\n"), (int)l, buf);
|
||||
qse_printf (QSE_T("SEND: [%.*hs]\n"), (int)l, buf);
|
||||
return qse_httpd_entask (
|
||||
httpd, client, pred, &task, QSE_SIZEOF(data));
|
||||
}
|
||||
@ -502,7 +506,7 @@ static void task_fini_file (
|
||||
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
|
||||
{
|
||||
task_file_t* ctx = (task_file_t*)task->ctx;
|
||||
close (ctx->handle.i);
|
||||
QSE_CLOSE (ctx->handle.i);
|
||||
}
|
||||
|
||||
static int task_main_file (
|
||||
@ -567,6 +571,7 @@ qse_printf (QSE_T("Debug: sending file to %d\n"), client->handle.i);
|
||||
task.main = task_main_file;
|
||||
task.fini = task_fini_file;
|
||||
task.ctx = &data;
|
||||
task.trigger.i = -1;
|
||||
|
||||
return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(data));
|
||||
}
|
||||
@ -581,7 +586,8 @@ struct task_dir_t
|
||||
int footer_pending;
|
||||
struct dirent* dent;
|
||||
|
||||
qse_mchar_t buf[512];
|
||||
/*qse_mchar_t buf[4096];*/
|
||||
qse_mchar_t buf[512]; /* TOOD: increate size */
|
||||
qse_size_t bufpos;
|
||||
qse_size_t buflen;
|
||||
qse_size_t bufrem;
|
||||
@ -909,16 +915,13 @@ qse_httpd_task_t* qse_httpd_entaskdir (
|
||||
task.main = chunked? task_main_dir: task_main_dir_nochunk;
|
||||
task.fini = task_fini_dir;
|
||||
task.ctx = &handle;
|
||||
task.trigger.i = -1;
|
||||
|
||||
return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(task_dir_t));
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
typedef struct task_path_t task_path_t;
|
||||
struct task_path_t
|
||||
{
|
||||
@ -945,19 +948,27 @@ static QSE_INLINE int task_main_path_file (
|
||||
task_path_t* data = (task_path_t*)task->ctx;
|
||||
qse_httpd_task_t* x = task;
|
||||
qse_ubi_t handle;
|
||||
int oflags;
|
||||
|
||||
/* 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"), data->name);
|
||||
handle.i = open (data->name, O_RDONLY);
|
||||
|
||||
oflags = O_RDONLY;
|
||||
#if defined(O_LARGEFILE)
|
||||
oflags |= O_LARGEFILE;
|
||||
#endif
|
||||
handle.i = QSE_OPEN (data->name, oflags, 0);
|
||||
if (handle.i <= -1)
|
||||
{
|
||||
x = entask_error (httpd, client, x, 404, &data->version, data->keepalive);
|
||||
goto no_file_send;
|
||||
}
|
||||
fcntl (handle.i, F_SETFD, FD_CLOEXEC);
|
||||
oflags = QSE_FCNTL (handle.i, F_GETFD, 0);
|
||||
if (oflags >= 0)
|
||||
QSE_FCNTL (handle.i, F_SETFD, oflags | FD_CLOEXEC);
|
||||
|
||||
if (data->range.type != QSE_HTTP_RANGE_NONE)
|
||||
{
|
||||
@ -1147,9 +1158,9 @@ static int task_main_path (
|
||||
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
|
||||
{
|
||||
task_path_t* data = (task_path_t*)task->ctx;
|
||||
struct stat st;
|
||||
qse_lstat_t st;
|
||||
|
||||
if (lstat (data->name, &st) <= -1)
|
||||
if (QSE_LSTAT (data->name, &st) <= -1)
|
||||
{
|
||||
return (entask_error (httpd, client, task, 404, &data->version, data->keepalive) == QSE_NULL)? -1: 0;
|
||||
}
|
||||
@ -1198,6 +1209,7 @@ qse_httpd_task_t* qse_httpd_entaskpath (
|
||||
task.init = task_init_path;
|
||||
task.main = task_main_path;
|
||||
task.ctx = &data;
|
||||
task.trigger.i = -1;
|
||||
|
||||
return qse_httpd_entask (httpd, client, pred, &task,
|
||||
QSE_SIZEOF(task_path_t) + qse_mbslen(name) + 1);
|
||||
@ -1476,7 +1488,13 @@ 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) qse_pio_close (cgi->pio);
|
||||
if (cgi->pio)
|
||||
{
|
||||
/* 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);
|
||||
}
|
||||
if (cgi->res) qse_mbs_close (cgi->res);
|
||||
if (cgi->htrd) qse_htrd_close (cgi->htrd);
|
||||
qse_printf (QSE_T("task_fini_cgi\n"));
|
||||
@ -1604,6 +1622,7 @@ qse_printf (QSE_T("CGI FUCKED UP...RETURNING TOO MUCH DATA\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
qse_printf (QSE_T("CGI SEND [%.*hs]\n"), (int)cgi->buflen, cgi->buf);
|
||||
n = send (client->handle.i, cgi->buf, cgi->buflen, 0);
|
||||
if (n <= -1)
|
||||
{
|
||||
@ -1615,6 +1634,7 @@ qse_printf (QSE_T("CGI FUCKED UP...RETURNING TOO MUCH DATA\n"));
|
||||
QSE_MEMCPY (&cgi->buf[0], &cgi->buf[n], cgi->buflen - n);
|
||||
cgi->buflen -= n;
|
||||
|
||||
qse_printf (QSE_T("CGI SEND DONE\n"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -1684,7 +1704,6 @@ qse_printf (QSE_T("[cgi_2 ]\n"));
|
||||
/* end of output from cgi before it has seen a header.
|
||||
* the cgi script must be crooked. */
|
||||
/* TODO: logging */
|
||||
qse_pio_kill (cgi->pio);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -1723,9 +1742,17 @@ static int task_main_cgi (
|
||||
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
|
||||
{
|
||||
task_cgi_t* cgi = (task_cgi_t*)task->ctx;
|
||||
int pio_options;
|
||||
int error_code = 500;
|
||||
|
||||
if (cgi->init_failed) goto oops;
|
||||
|
||||
if (QSE_ACCESS (cgi->path, X_OK) == -1)
|
||||
{
|
||||
error_code = (errno == EACCES)? 403: 404;
|
||||
goto oops;
|
||||
}
|
||||
|
||||
if (cgi->nph)
|
||||
{
|
||||
/* i cannot know how long the content will be
|
||||
@ -1753,12 +1780,21 @@ static int task_main_cgi (
|
||||
if (cgi->res == QSE_NULL) goto oops;
|
||||
}
|
||||
|
||||
pio_options = QSE_PIO_READOUT | QSE_PIO_WRITEIN |
|
||||
QSE_PIO_ERRTONUL | QSE_PIO_MBSCMD;
|
||||
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,
|
||||
QSE_PIO_READOUT | QSE_PIO_WRITEIN | QSE_PIO_ERRTONUL | QSE_PIO_MBSCMD
|
||||
httpd->mmgr, 0, (const qse_char_t*)cgi->path,
|
||||
cgi->env, pio_options
|
||||
);
|
||||
if (cgi->pio == QSE_NULL) goto oops;
|
||||
|
||||
/* set the trigger that the main loop can use this
|
||||
* handle for multiplexing */
|
||||
task->trigger.i = qse_pio_gethandle (cgi->pio, QSE_PIO_OUT);
|
||||
|
||||
if (cgi->nph)
|
||||
{
|
||||
/* skip various header processing */
|
||||
@ -1783,7 +1819,7 @@ 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, error_code, &cgi->version, cgi->keepalive) == QSE_NULL)? -1: 0;
|
||||
}
|
||||
|
||||
/* TODO: global option or individual paramter for max cgi lifetime
|
||||
@ -1809,6 +1845,7 @@ qse_httpd_task_t* qse_httpd_entaskcgi (
|
||||
task.fini = task_fini_cgi;
|
||||
task.main = task_main_cgi;
|
||||
task.ctx = &arg;
|
||||
task.trigger.i = -1;
|
||||
|
||||
return qse_httpd_entask (
|
||||
httpd, client, pred, &task,
|
||||
@ -1835,6 +1872,7 @@ qse_httpd_task_t* qse_httpd_entasknph (
|
||||
task.fini = task_fini_cgi;
|
||||
task.main = task_main_cgi;
|
||||
task.ctx = &arg;
|
||||
task.trigger.i = -1;
|
||||
|
||||
return qse_httpd_entask (
|
||||
httpd, client, pred, &task,
|
||||
@ -1845,16 +1883,22 @@ qse_httpd_task_t* qse_httpd_entasknph (
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
#if 0
|
||||
typedef struct task_proxy_t task_proxy_t;
|
||||
struct task_proxy_t
|
||||
{
|
||||
}
|
||||
|
||||
qse_httpd_task_t* qse_httpd_entaskproxy (...)
|
||||
qse_httpd_task_t* qse_httpd_entaskproxy (
|
||||
qse_httpd_t* httpd,
|
||||
qse_httpd_client_t* client,
|
||||
const qse_httpd_task_t* pred,
|
||||
const qse_mchar_t* host,
|
||||
const qse_htre_t* req)
|
||||
{
|
||||
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
|
@ -282,6 +282,21 @@ static int test10 (void)
|
||||
}
|
||||
|
||||
static int test11 (void)
|
||||
{
|
||||
return pio1 (
|
||||
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
|
||||
(const qse_char_t*)"dir /a",
|
||||
#else
|
||||
(const qse_char_t*)"ls -laF",
|
||||
#endif
|
||||
QSE_NULL,
|
||||
QSE_PIO_MBSCMD|QSE_PIO_READOUT|QSE_PIO_WRITEIN|QSE_PIO_DROPERR|QSE_PIO_INTONUL|QSE_PIO_SHELL,
|
||||
QSE_PIO_OUT
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
static int test12 (void)
|
||||
{
|
||||
qse_env_t* env;
|
||||
int n;
|
||||
@ -307,7 +322,7 @@ static int test11 (void)
|
||||
return n;
|
||||
}
|
||||
|
||||
static int test12 (void)
|
||||
static int test13 (void)
|
||||
{
|
||||
qse_pio_t* pio;
|
||||
int x;
|
||||
@ -406,6 +421,7 @@ int main ()
|
||||
R (test10);
|
||||
R (test11);
|
||||
R (test12);
|
||||
R (test13);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -178,7 +178,7 @@ int httpd_main (int argc, qse_char_t* argv[])
|
||||
signal (SIGINT, sigint);
|
||||
signal (SIGPIPE, SIG_IGN);
|
||||
|
||||
n = qse_httpd_loop (httpd, 0);
|
||||
n = qse_httpd_loop (httpd, 1);
|
||||
|
||||
signal (SIGINT, SIG_DFL);
|
||||
signal (SIGPIPE, SIG_DFL);
|
||||
|
Loading…
x
Reference in New Issue
Block a user