added file-access and dir-access to cmd/http/httpd.c

This commit is contained in:
hyung-hwan 2013-02-21 15:00:08 +00:00
parent b677b4a892
commit b420a7c0dc
10 changed files with 721 additions and 188 deletions

View File

@ -0,0 +1,23 @@
###############################################
# this file defines various mime types that
# can be @included in a mime block.
#
# file entries are inspected first.
# then suffix entries are inspected.
# 'other' comes the last.
###############################################
suffix ".htm" = "text/html";
suffix ".html" = "text/html";
suffix ".txt" = "text/plain";
suffix ".css" = "text/css";
suffix ".xml" = "text/xml";
suffix ".js" = "application/javascript";
suffix ".jpg" = "image/jpeg";
suffix ".png" = "image/png";
# use file to set the type for a particular file
#file "x.jpg" = "specific mime type";
# other can override the built-in default.
#other = "default mime type";

View File

@ -8,6 +8,7 @@
#include <qse/cmn/mbwc.h>
#include <qse/cmn/time.h>
#include <qse/cmn/path.h>
#include <qse/cmn/opt.h>
#include <signal.h>
#include <locale.h>
@ -27,6 +28,7 @@
#else
# include <unistd.h>
# include <errno.h>
# include <fcntl.h>
#endif
#if defined(HAVE_SSL)
@ -38,17 +40,34 @@
/* --------------------------------------------------------------------- */
static qse_httpd_t* g_httpd = QSE_NULL;
static const qse_char_t* g_cfgfile = QSE_NULL;
static int g_daemon = 0;
static void sigint (int sig)
/* --------------------------------------------------------------------- */
typedef struct httpd_xtn_t httpd_xtn_t;
struct httpd_xtn_t
{
const qse_char_t* cfgfile;
qse_xli_t* xli;
qse_httpd_impede_t orgimpede;
int impede_code;
};
/* --------------------------------------------------------------------- */
static void sig_stop (int sig)
{
if (g_httpd) qse_httpd_stop (g_httpd);
}
static void sighup (int sig)
static void sig_reconf (int sig)
{
if (g_httpd)
{
/* arrange to intefere with httpd to perform reconfiguration */
httpd_xtn_t* httpd_xtn;
httpd_xtn = qse_httpd_getxtnstd (g_httpd);
httpd_xtn->impede_code = sig;
qse_httpd_impede (g_httpd);
}
}
@ -59,15 +78,25 @@ static void setup_signal_handlers ()
#if defined(SIGINT)
qse_memset (&act, 0, QSE_SIZEOF(act));
act.sa_handler = sigint;
act.sa_handler = sig_stop;
sigaction (SIGINT, &act, QSE_NULL);
#endif
#if defined(SIGTERM)
qse_memset (&act, 0, QSE_SIZEOF(act));
act.sa_handler = sig_stop;
sigaction (SIGTERM, &act, QSE_NULL);
#endif
#if defined(SIGHUP)
qse_memset (&act, 0, QSE_SIZEOF(act));
act.sa_handler = sighup;
act.sa_handler = sig_reconf;
sigaction (SIGHUP, &act, QSE_NULL);
#endif
#if defined(SIGUSR1)
qse_memset (&act, 0, QSE_SIZEOF(act));
act.sa_handler = sig_reconf;
sigaction (SIGUSR1, &act, QSE_NULL);
#endif
#if defined(SIGPIPE)
qse_memset (&act, 0, QSE_SIZEOF(act));
@ -85,12 +114,22 @@ static void restore_signal_handlers ()
act.sa_handler = SIG_DFL;
sigaction (SIGINT, &act, QSE_NULL);
#endif
#if defined(SIGTERM)
qse_memset (&act, 0, QSE_SIZEOF(act));
act.sa_handler = SIG_DFL;
sigaction (SIGTERM, &act, QSE_NULL);
#endif
#if defined(SIGHUP)
qse_memset (&act, 0, QSE_SIZEOF(act));
act.sa_handler = SIG_DFL;
sigaction (SIGHUP, &act, QSE_NULL);
#endif
#if defined(SIGUSR1)
qse_memset (&act, 0, QSE_SIZEOF(act));
act.sa_handler = SIG_DFL;
sigaction (SIGUSR1, &act, QSE_NULL);
#endif
#if defined(SIGPIPE)
qse_memset (&act, 0, QSE_SIZEOF(act));
@ -99,6 +138,43 @@ static void restore_signal_handlers ()
#endif
}
static int daemonize (int devnull)
{
#if defined(HAVE_FORK)
switch (fork())
{
case -1: return -1;
case 0: break; /* child */
default: _exit (0); /* parent */
}
if (setsid () <= -1) return -1;
/*umask (0);*/
chdir ("/");
if (devnull)
{
/* redirect stdin/out/err to /dev/null */
int fd = open ("/dev/null", O_RDWR);
if (fd >= 0)
{
dup2 (fd, 0);
dup2 (fd, 1);
dup2 (fd, 2);
close (fd);
}
}
return 0;
#else
return -1;
#endif
}
/* --------------------------------------------------------------------- */
enum
@ -117,8 +193,7 @@ struct cgi_t
{
enum {
CGI_SUFFIX,
CGI_FILE,
CGI_NAME,
CGI_MAX
} type;
@ -133,8 +208,8 @@ struct mime_t
{
enum {
MIME_SUFFIX,
MIME_FILE,
MIME_NAME,
MIME_OTHER,
MIME_MAX
} type;
@ -144,6 +219,23 @@ struct mime_t
struct mime_t* next;
};
struct access_t
{
/* TODO: support more types like ACCESS_GLOB
not-only the base name, find a way to use query path or xpath */
enum {
ACCESS_SUFFIX,
ACCESS_NAME,
ACCESS_OTHER,
ACCESS_MAX
} type;
qse_mchar_t* spec;
int value;
struct access_t* next;
};
typedef struct server_xtn_t server_xtn_t;
struct server_xtn_t
{
@ -176,15 +268,14 @@ struct server_xtn_t
struct mime_t* head;
struct mime_t* tail;
} mime[MIME_MAX];
struct
{
struct access_t* head;
struct access_t* tail;
} access[2][ACCESS_MAX];
};
typedef struct httpd_xtn_t httpd_xtn_t;
struct httpd_xtn_t
{
const qse_char_t* cfgfile;
qse_xli_t* xli;
qse_httpd_impede_t orgimpede;
};
static int make_resource (
qse_httpd_t* httpd, qse_httpd_client_t* client,
@ -253,7 +344,7 @@ static void free_resource (
static void clear_server_config (qse_httpd_t* httpd, qse_httpd_server_t* server)
{
server_xtn_t* server_xtn;
qse_size_t i;
qse_size_t i, j;
server_xtn = qse_httpd_getserverstdxtn (httpd, server);
@ -306,6 +397,25 @@ static void clear_server_config (qse_httpd_t* httpd, qse_httpd_server_t* server)
server_xtn->mime[i].head = QSE_NULL;
server_xtn->mime[i].tail = QSE_NULL;
}
for (j = 0; j < QSE_COUNTOF(server_xtn->access); j++)
{
for (i = 0; i < QSE_COUNTOF(server_xtn->access[j]); i++)
{
struct access_t* access = server_xtn->access[j][i].head;
while (access)
{
struct access_t* x = access;
access = x->next;
if (x->spec) qse_httpd_freemem (httpd, x->spec);
if (x) qse_httpd_freemem (httpd, x);
}
server_xtn->access[j][i].head = QSE_NULL;
server_xtn->access[j][i].tail = QSE_NULL;
}
}
}
static void detach_server (qse_httpd_t* httpd, qse_httpd_server_t* server)
@ -369,8 +479,8 @@ static int query_server (
struct cgi_t* cgi;
for (cgi = server_xtn->cgi[i].head; cgi; cgi = cgi->next)
{
if ((cgi->type == MIME_SUFFIX && qse_mbsend (xpath_base, cgi->spec)) ||
(cgi->type == MIME_FILE && qse_mbscmp (xpath_base, cgi->spec) == 0))
if ((cgi->type == CGI_SUFFIX && qse_mbsend (xpath_base, cgi->spec)) ||
(cgi->type == CGI_NAME && qse_mbscmp (xpath_base, cgi->spec) == 0))
{
scgi->cgi = 1;
scgi->nph = cgi->nph;
@ -386,7 +496,7 @@ static int query_server (
case QSE_HTTPD_SERVERSTD_MIME:
{
qse_size_t i;
qse_mchar_t* xpath_base;
const qse_mchar_t* xpath_base;
xpath_base = qse_mbsbasename (xpath);
@ -397,7 +507,8 @@ static int query_server (
for (mime = server_xtn->mime[i].head; mime; mime = mime->next)
{
if ((mime->type == MIME_SUFFIX && qse_mbsend (xpath_base, mime->spec)) ||
(mime->type == MIME_FILE && qse_mbscmp (xpath_base, mime->spec) == 0))
(mime->type == MIME_NAME && qse_mbscmp (xpath_base, mime->spec) == 0) ||
mime->type == MIME_OTHER)
{
*(const qse_mchar_t**)result = mime->value;
return 0;
@ -406,6 +517,35 @@ static int query_server (
}
return 0;
}
case QSE_HTTPD_SERVERSTD_DIRACC:
case QSE_HTTPD_SERVERSTD_FILEACC:
{
qse_size_t i;
const qse_mchar_t* xpath_base;
int id;
id = (code == QSE_HTTPD_SERVERSTD_DIRACC)? 0: 1;
xpath_base = qse_mbsbasename (xpath);
*(int*)result = 200;
for (i = 0; i < QSE_COUNTOF(server_xtn->access[id]); i++)
{
struct access_t* access;
for (access = server_xtn->access[id][i].head; access; access = access->next)
{
if ((access->type == ACCESS_SUFFIX && qse_mbsend (xpath_base, access->spec)) ||
(access->type == ACCESS_NAME && qse_mbscmp (xpath_base, access->spec) == 0) ||
access->type == ACCESS_OTHER)
{
*(int*)result = access->value;
return 0;
}
}
}
return 0;
}
}
return server_xtn->orgquery (httpd, server, req, xpath, code, result);
@ -498,9 +638,9 @@ static int load_server_config (
{
type = CGI_SUFFIX;
}
else if (qse_strcmp (pair->key, QSE_T("file")) == 0)
else if (qse_strcmp (pair->key, QSE_T("name")) == 0)
{
type = CGI_FILE;
type = CGI_NAME;
}
else continue;
@ -570,13 +710,14 @@ static int load_server_config (
if (atom->type != QSE_XLI_PAIR) continue;
pair = (qse_xli_pair_t*)atom;
if (pair->key && pair->name && pair->val->type == QSE_XLI_STR)
if (pair->key && pair->val->type == QSE_XLI_STR)
{
struct mime_t* mime;
int type;
if (qse_strcmp (pair->key, QSE_T("suffix")) == 0) type = MIME_SUFFIX;
else if (qse_strcmp (pair->key, QSE_T("file")) == 0) type = MIME_FILE;
if (qse_strcmp (pair->key, QSE_T("suffix")) == 0 && pair->name) type = MIME_SUFFIX;
else if (qse_strcmp (pair->key, QSE_T("name")) == 0 && pair->name) type = MIME_NAME;
else if (qse_strcmp (pair->key, QSE_T("other")) == 0 && !pair->name) type = MIME_OTHER;
else continue;
mime = qse_httpd_callocmem (httpd, QSE_SIZEOF(*mime));
@ -594,20 +735,16 @@ static int load_server_config (
qse_printf (QSE_T("ERROR: memory failure in copying mime\n"));
return -1;
}
if (pair->val->type == QSE_XLI_STR)
mime->value = qse_httpd_strtombsdup (httpd, ((qse_xli_str_t*)pair->val)->ptr);
if (!mime->value)
{
mime->value = qse_httpd_strtombsdup (httpd, ((qse_xli_str_t*)pair->val)->ptr);
if (!mime->value)
{
qse_httpd_freemem (httpd, mime->spec);
qse_httpd_freemem (httpd, mime);
qse_printf (QSE_T("ERROR: memory failure in copying mime\n"));
return -1;
}
/* TODO: more sanity check */
qse_httpd_freemem (httpd, mime->spec);
qse_httpd_freemem (httpd, mime);
qse_printf (QSE_T("ERROR: memory failure in copying mime\n"));
return -1;
}
if (server_xtn->mime[type].tail)
server_xtn->mime[type].tail->next = mime;
else
@ -617,6 +754,76 @@ static int load_server_config (
}
}
for (i = 0; i < 2; i++)
{
static struct
{
const qse_char_t* x;
const qse_char_t* y;
} acc_items[] =
{
{ QSE_T("host['*'].location['/'].dir-access"), QSE_T("default.dir-access") },
{ QSE_T("host['*'].location['/'].file-access"), QSE_T("default.file-access") },
};
pair = qse_xli_findpairbyname (httpd_xtn->xli, list, acc_items[i].x);
if (!pair) pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, acc_items[i].y);
if (pair && pair->val->type == QSE_XLI_LIST)
{
qse_xli_list_t* acclist = (qse_xli_list_t*)pair->val;
for (atom = acclist->head; atom; atom = atom->next)
{
if (atom->type != QSE_XLI_PAIR) continue;
pair = (qse_xli_pair_t*)atom;
if (pair->key && pair->val->type == QSE_XLI_STR)
{
struct access_t* acc;
const qse_char_t* tmp;
int type, value;
if (qse_strcmp (pair->key, QSE_T("suffix")) == 0 && pair->name) type = ACCESS_SUFFIX;
else if (qse_strcmp (pair->key, QSE_T("name")) == 0 && pair->name) type = ACCESS_NAME;
else if (qse_strcmp (pair->key, QSE_T("other")) == 0 && !pair->name) type = ACCESS_OTHER;
else continue;
tmp = ((qse_xli_str_t*)pair->val)->ptr;
if (qse_strcmp (tmp, QSE_T("noent")) == 0) value = 404;
else if (qse_strcmp (tmp, QSE_T("forbid")) == 0) value = 403;
else if (qse_strcmp (tmp, QSE_T("ok")) == 0) value = 200;
else continue;
/* TODO: more sanity check */
acc = qse_httpd_callocmem (httpd, QSE_SIZEOF(*acc));
if (acc == QSE_NULL)
{
qse_printf (QSE_T("ERROR: memory failure in copying acc\n"));
return -1;
}
acc->type = type;
if (pair->name)
{
acc->spec = qse_httpd_strtombsdup (httpd, pair->name);
if (!acc->spec)
{
qse_httpd_freemem (httpd, acc);
qse_printf (QSE_T("ERROR: memory failure in copying access\n"));
return -1;
}
}
acc->value = value;
if (server_xtn->access[i][type].tail)
server_xtn->access[i][type].tail->next = acc;
else
server_xtn->access[i][type].head = acc;
server_xtn->access[i][type].tail = acc;
}
}
}
}
/* perform more sanity check */
if (qse_mbschr (server_xtn->scfg[SCFG_AUTH], QSE_MT(':')) == QSE_NULL)
{
@ -844,58 +1051,171 @@ static void impede_httpd (qse_httpd_t* httpd)
if (httpd_xtn->orgimpede) httpd_xtn->orgimpede (httpd);
}
static void logact_httpd (qse_httpd_t* httpd, qse_httpd_act_t* act)
static void logact_httpd (qse_httpd_t* httpd, const qse_httpd_act_t* act)
{
httpd_xtn_t* httpd_xtn;
qse_char_t tmp[256];
qse_char_t tmp[128], tmp2[128], tmp3[128];
httpd_xtn = qse_httpd_getxtnstd (httpd);
switch (act->code)
{
case QSE_HTTPD_ACCEPT_CLIENT:
case QSE_HTTPD_CATCH_MERRMSG:
qse_printf (QSE_T("ERROR: %hs\n"), act->u.merrmsg);
break;
case QSE_HTTPD_CATCH_MDBGMSG:
qse_printf (QSE_T("DEBUG: %hs\n"), act->u.mdbgmsg);
break;
case QSE_HTTPD_ACCEPT_CLIENT:
qse_nwadtostr (&act->u.client->local_addr, tmp, QSE_COUNTOF(tmp), QSE_NWADTOSTR_ALL);
qse_nwadtostr (&act->u.client->orgdst_addr, tmp2, QSE_COUNTOF(tmp2), QSE_NWADTOSTR_ALL);
qse_nwadtostr (&act->u.client->remote_addr, tmp3, QSE_COUNTOF(tmp3), QSE_NWADTOSTR_ALL);
qse_printf (QSE_T("accepted client %s(%s) from %s\n"), tmp, tmp2, tmp3);
case QSE_HTTPD_PURGE_CLIENT:
qse_nwadtostr (&act->u.client->remote_addr, tmp, QSE_COUNTOF(tmp), QSE_NWADTOSTR_ALL);
qse_printf (QSE_T("purged client from %s\n"), tmp);
qse_printf (QSE_T("purged client - %s\n"), tmp);
break;
case QSE_HTTPD_READERR_CLIENT:
qse_nwadtostr (&act->u.client->remote_addr, tmp, QSE_COUNTOF(tmp), QSE_NWADTOSTR_ALL);
qse_printf (QSE_T("failed to read client - %s\n"), tmp);
break;
}
}
/* --------------------------------------------------------------------- */
static void print_version (void)
{
qse_printf (QSE_T("QSEHTTPD version %hs\n"), QSE_PACKAGE_VERSION);
}
static void print_usage (QSE_FILE* out, int argc, qse_char_t* argv[])
{
const qse_char_t* b = qse_basename (argv[0]);
qse_fprintf (out, QSE_T("USAGE: %s [options] -c file\n"), b);
qse_fprintf (out, QSE_T(" %s [options] --config-file file\n"), b);
qse_fprintf (out, QSE_T("options as follows:\n"));
qse_fprintf (out, QSE_T(" -h/--help show this message\n"));
qse_fprintf (out, QSE_T(" --version show version\n"));
qse_fprintf (out, QSE_T(" -c/--config-file file specify a configuration file\n"));
qse_fprintf (out, QSE_T(" -d/--daemon run in the background\n"));
}
static int handle_args (int argc, qse_char_t* argv[])
{
static qse_opt_lng_t lng[] =
{
{ QSE_T(":config-file"), QSE_T('c') },
{ QSE_T("daemon"), QSE_T('d') },
{ QSE_T("help"), QSE_T('h') },
{ QSE_T("version"), QSE_T('\0') },
{ QSE_NULL, QSE_T('\0') }
};
static qse_opt_t opt =
{
QSE_T("c:dh"),
lng
};
qse_cint_t c;
while ((c = qse_getopt (argc, argv, &opt)) != QSE_CHAR_EOF)
{
switch (c)
{
default:
goto wrongusage;
case QSE_T('?'):
qse_fprintf (QSE_STDERR,
QSE_T("ERROR: bad option - %c\n"),
opt.opt
);
goto wrongusage;
case QSE_T(':'):
qse_fprintf (QSE_STDERR,
QSE_T("ERROR: bad parameter for %c\n"),
opt.opt
);
goto wrongusage;
case QSE_T('c'):
g_cfgfile = opt.arg;
break;
case QSE_T('d'):
g_daemon = 1;
break;
case QSE_T('h'):
print_usage (QSE_STDOUT, argc, argv);
return 0;
case QSE_T('\0'):
{
if (qse_strcmp(opt.lngopt, QSE_T("version")) == 0)
{
print_version ();
return 0;
}
break;
}
}
}
if (opt.ind < argc || g_cfgfile == QSE_NULL) goto wrongusage;
return 1;
wrongusage:
print_usage (QSE_STDERR, argc, argv);
return -1;
}
static int httpd_main (int argc, qse_char_t* argv[])
{
qse_httpd_t* httpd = QSE_NULL;
httpd_xtn_t* httpd_xtn;
qse_ntime_t tmout;
int trait, ret = -1;
int trait, ret;
qse_httpd_rcb_t rcb;
if (argc != 2)
{
/* TODO: proper check... */
qse_fprintf (QSE_STDERR, QSE_T("Usage: %s -f config-file\n"), argv[0]);
goto oops;
}
ret = handle_args (argc, argv);
if (ret <= -1) return -1;
if (ret == 0) return 0;
httpd = qse_httpd_openstd (QSE_SIZEOF(httpd_xtn_t));
if (httpd == QSE_NULL)
{
qse_fprintf (QSE_STDERR, QSE_T("Cannot open httpd\n"));
qse_fprintf (QSE_STDERR, QSE_T("ERROR: Cannot open httpd\n"));
goto oops;
}
httpd_xtn = qse_httpd_getxtnstd (httpd);
httpd_xtn->cfgfile = argv[1];
httpd_xtn->cfgfile = g_cfgfile;
if (load_config (httpd) <= -1) goto oops;
if (g_daemon)
{
if (daemonize (1) <= -1)
{
qse_fprintf (QSE_STDERR, QSE_T("ERROR: Cannot daemonize\n"));
goto oops;
}
}
g_httpd = httpd;
setup_signal_handlers ();
qse_httpd_getopt (httpd, QSE_HTTPD_TRAIT, &trait);
trait |= QSE_HTTPD_CGIERRTONUL | QSE_HTTPD_ENABLELOG;
trait |= QSE_HTTPD_CGIERRTONUL | QSE_HTTPD_LOGACT;
qse_httpd_setopt (httpd, QSE_HTTPD_TRAIT, &trait);
tmout.sec = 10;
@ -921,7 +1241,7 @@ static int httpd_main (int argc, qse_char_t* argv[])
oops:
if (httpd) qse_httpd_close (httpd);
return ret;
return -1;
}
int qse_main (int argc, qse_achar_t* argv[])

View File

@ -2,43 +2,119 @@
# this is a sample configuration file for qsehttpd.
#
server {
bind = "0.0.0.0:80";
default {
# the default name is used in http headers and in pages
# generated by httpd.
name = "QSEHTTPD v1";
ssl {
certificate {
private = "xxxx";
public = "xxxx";
}
}
docroot = "/";
root = "/home/www/default";
option = "xxxx,xxx,xxxx";
realm {
name = "xxxxxx";
password = "zzzzzzzzzzzzzzzz";
}
realm = "default realm";
auth = "username:password";
index = "index.ant",
"index.html",
"index.cgi";
cgi {
suffix ".ant" = "xxxxx";
name "t3.nph" = "nph";
suffix ".cgi";
suffix ".nph";
suffix ".ant" = "nph", "";
suffix ".awk" = "cgi", "/usr/bin/qseawk -f";
# glob is not supported yet
# glob "x*.xxx";
}
mime {
suffix ".jpg" = "image/picture";
suffix ".txt" = "text/plain";
@include "httpd-mime.conf";
}
# virtual host
host "www.google.com" {
root = "/home/www/google";
option = "xxx, xxx, xxxx";
realm {
name = "www.google.com";
password = "zzzzzzzzzzzzzzzz";
}
# control access to directories
dir-access {
# suffix ".xxxx" = ok;
# name "xxxxx" = ok;
other = noent;
}
# control access to normal files.
# cgi scripts are not control by these.
file-access {
suffix ".html" = ok;
suffix ".css" = ok;
suffix ".js" = ok;
suffix ".png" = ok;
suffix ".jpg" = ok;
other = noent;
}
dir-css = "<style type='text/css'>body { background-color:#d0e4fe; font-size: 0.9em; } div.header { font-weight: bold; margin-bottom: 5px; } div.footer { border-top: 1px solid #99AABB; text-align: right; } table { font-size: 0.9em; } td { white-space: nowrap; } td.size { text-align: right; }</style>";
error-css = "<style type='text/css'>body { background-color:#d0e4fe; font-size: 0.9em; } div.header { font-weight: bold; margin-bottom: 5px; } div.footer { border-top: 1px solid #99AABB; text-align: right; }</style>";
}
server {
bind = "0.0.0.0:1999";
# ssl is not supported yet.
#ssl {
# certificate {
# private = "xxxx";
# public = "xxxx";
# }
#}
host "*" {
location "/" {
# the location-specific name is used in pages
# generated by httpd, not in http headers.
# you can't override the default name for use in
# http headers.
#name = "QSEHTTPD v1";
# uncomment the followng block to override the default.
#docroot = "/var/www";
# uncomment the followng block to override the default.
# if you want to disable authentication while the default
# enables it, don't put a value like 'realm;'
#realm = "default realm";
#auth = "username:password";
# uncomment the following block to override the default
#index = "index.cgi", "index.html";
# uncomment the following block to override the default
#cgi {
# suffix ".cgi";
# suffix ".awk" = "/usr/bin/qseawk -f";
#}
# uncomment the following block to override the default.
#mime {
# suffix ".htm" = "text/html";
# suffix ".html" = "text/html";
# suffix ".txt" = "text/html";
# suffix ".css" = "text/css";
# suffix ".xml" = "text/xml";
# suffix ".js" = "application/javascript";
# suffix ".jpg" = "image/jpeg";
# suffix ".png" = "image/png";
#}
}
# other locations than / are not supported yet.
#location "/help" {
#}
}
# virtual hosts are not supported yet.
#host "www.google.com" {
# location "/" {
# docroot = "/home/www/google";
# auth {
# realm = "jjjjjjjj";
# name = "www.google.com";
# password = "zzzzzzzzzzzzzzzz";
# }
# }
#}
}

View File

@ -75,7 +75,7 @@ enum qse_httpd_trait_t
QSE_HTTPD_CGIERRTONUL = (1 << 1),
QSE_HTTPD_CGINOCLOEXEC = (1 << 2),
QSE_HTTPD_CGINOCHUNKED = (1 << 3),
QSE_HTTPD_ENABLELOG = (1 << 4)
QSE_HTTPD_LOGACT = (1 << 4)
};
typedef enum qse_httpd_trait_t qse_httpd_trait_t;
@ -115,8 +115,8 @@ typedef struct qse_httpd_dirent_t qse_httpd_dirent_t;
struct qse_httpd_dirent_t
{
qse_mchar_t* name;
qse_httpd_stat_t stat;
const qse_mchar_t* name;
qse_httpd_stat_t stat;
};
typedef struct qse_httpd_scb_t qse_httpd_scb_t;
@ -266,8 +266,11 @@ typedef void (*qse_httpd_impede_t) (
enum qse_httpd_act_code_t
{
QSE_HTTPD_CATCH_MERRMSG,
QSE_HTTPD_CATCH_MDBGMSG,
QSE_HTTPD_ACCEPT_CLIENT,
QSE_HTTPD_PURGE_CLIENT,
QSE_HTTPD_READERR_CLIENT
};
typedef enum qse_httpd_act_code_t qse_httpd_act_code_t;
@ -277,6 +280,8 @@ struct qse_httpd_act_t
union
{
qse_httpd_client_t* client;
qse_mchar_t merrmsg[128];
qse_mchar_t mdbgmsg[128];
} u;
};
typedef struct qse_httpd_act_t qse_httpd_act_t;

View File

@ -385,8 +385,13 @@ static int get_highest_fd (void)
#endif
}
else fd = rlim.rlim_max;
if (fd == -1) fd = 1024; /* fallback */
/* F_MAXFD is the highest fd. but RLIMIT_NOFILE and
* _SC_OPEN_MAX returnes the maximum number of file
* descriptors. make adjustment */
if (fd > 0) fd--;
}
if (fd == -1) fd = 1024; /* fallback */
return fd;
}

View File

@ -23,6 +23,7 @@
#include <qse/cmn/str.h>
#include <qse/cmn/pio.h>
#include <qse/cmn/fmt.h>
#include <qse/cmn/path.h>
#include <stdio.h> /* TODO: remove this */
@ -196,6 +197,17 @@ static int cgi_capture_script_header (qse_htre_t* req, const qse_mchar_t* key, c
return 0;
}
static void log_cgi_script_error (task_cgi_t* cgi, const qse_mchar_t* shortmsg)
{
qse_httpd_act_t msg;
qse_size_t pos = 0;
msg.code = QSE_HTTPD_CATCH_MERRMSG;
pos += qse_mbsxcpy (&msg.u.merrmsg[pos], QSE_COUNTOF(msg.u.merrmsg) - pos, shortmsg);
pos += qse_mbsxcpy (&msg.u.merrmsg[pos], QSE_COUNTOF(msg.u.merrmsg) - pos, cgi->script);
cgi->httpd->opt.rcb.logact (cgi->httpd, &msg);
}
static int cgi_htrd_peek_script_output (qse_htrd_t* htrd, qse_htre_t* req)
{
cgi_script_htrd_xtn_t* xtn;
@ -355,8 +367,9 @@ static int cgi_htrd_peek_script_output (qse_htrd_t* htrd, qse_htre_t* req)
if ((cgi->resflags & CGI_RES_SCRIPT_LENGTH) &&
cgi->script_output_received > cgi->script_output_length)
{
/* TODO: cgi returning too much data... something is wrong in CGI */
qse_printf (QSE_T("CGI SCRIPT FUCKED - RETURNING TOO MUCH...\n"));
/* cgi returning too much data... something is wrong in CGI */
if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT)
log_cgi_script_error (cgi, QSE_MT("cgi redundant output - "));
cgi->httpd->errnum = QSE_HTTPD_EINVAL; /* TODO: change it to a better error code */
return -1;
}
@ -501,8 +514,10 @@ static int cgi_snatch_client_input (
task = (qse_httpd_task_t*)ctx;
cgi = (task_cgi_t*)task->ctx;
#if 0
if (ptr) qse_printf (QSE_T("!!!CGI SNATCHING [%.*hs]\n"), len, ptr);
else qse_printf (QSE_T("!!!CGI SNATCHING DONE\n"));
#endif
QSE_ASSERT (cgi->req);
QSE_ASSERT (!(cgi->reqflags & CGI_REQ_GOTALL));
@ -587,7 +602,9 @@ else qse_printf (QSE_T("!!!CGI SNATCHING DONE\n"));
/* output pipe to child */
task->trigger[1].mask = QSE_HTTPD_TASK_TRIGGER_WRITE;
#if 0
qse_printf (QSE_T("!!!CGI SNATCHED [%.*hs]\n"), len, ptr);
#endif
}
return 0;
@ -608,7 +625,9 @@ static void cgi_forward_client_input_to_script (
{
/* a forwarding error has occurred previously.
* clear the forwarding buffer */
#if 0
qse_printf (QSE_T("FORWARD: CLEARING REQCON FOR ERROR\n"));
#endif
qse_mbs_clear (cgi->reqfwdbuf);
}
else
@ -624,9 +643,11 @@ qse_printf (QSE_T("FORWARD: CLEARING REQCON FOR ERROR\n"));
{
forward:
/* writable */
#if 0
qse_printf (QSE_T("FORWARD: @@@@@@@@@@WRITING[%.*hs]\n"),
(int)QSE_MBS_LEN(cgi->reqfwdbuf),
QSE_MBS_PTR(cgi->reqfwdbuf));
#endif
n = qse_pio_write (
&cgi->pio, QSE_PIO_IN,
QSE_MBS_PTR(cgi->reqfwdbuf),
@ -647,8 +668,9 @@ to the head all the time.. grow the buffer to a certain limit. */
if (n <= -1)
{
qse_printf (QSE_T("FORWARD: @@@@@@@@WRITE TO CGI FAILED\n"));
/* TODO: logging ... */
if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT)
log_cgi_script_error (cgi, QSE_MT("cgi pio write error - "));
cgi->reqflags |= CGI_REQ_FWDERR;
qse_mbs_clear (cgi->reqfwdbuf);
@ -679,7 +701,9 @@ qse_printf (QSE_T("FORWARD: @@@@@@@@WRITE TO CGI FAILED\n"));
* there is nothing more to forward in the forwarding buffer.
* clear the relay and write triggers for the time being.
*/
#if 0
qse_printf (QSE_T("FORWARD: @@@@@@@@NOTHING MORE TO WRITE TO CGI\n"));
#endif
QSE_ASSERT (cgi->req == QSE_NULL);
/* mark the end of input to the child explicitly. */
@ -919,8 +943,6 @@ static void task_fini_cgi (
QSE_ASSERT (!(cgi->reqflags & CGI_REQ_GOTALL));
qse_htre_unsetconcb (cgi->req);
}
qse_printf (QSE_T("task_fini_cgi\n"));
}
static QSE_INLINE qse_ssize_t cgi_read_script_output_to_buffer (
@ -934,6 +956,10 @@ static QSE_INLINE qse_ssize_t cgi_read_script_output_to_buffer (
QSE_SIZEOF(cgi->buf) - cgi->buflen
);
if (n > 0) cgi->buflen += n;
if (n <= -1 && cgi->httpd->opt.trait & QSE_HTTPD_LOGACT)
log_cgi_script_error (cgi, QSE_MT("cgi pio read error - "));
return n;
}
@ -942,13 +968,16 @@ static QSE_INLINE qse_ssize_t cgi_write_script_output_to_client (
{
qse_ssize_t n;
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->opt.scb.client.send (httpd, client, cgi->buf, cgi->buflen);
if (n > 0)
{
QSE_MEMCPY (&cgi->buf[0], &cgi->buf[n], cgi->buflen - n);
cgi->buflen -= n;
}
if (n <= -1 && cgi->httpd->opt.trait & QSE_HTTPD_LOGACT)
log_cgi_script_error (cgi, QSE_MT("cgi write error to client - "));
return n;
}
@ -971,13 +1000,11 @@ static int task_main_cgi_5 (
if (!(task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_WRITE) ||
(task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE))
{
qse_printf (QSE_T("task_main_cgi_5 about to write %d bytes\n"), (int)cgi->buflen);
if (cgi->buflen > 0)
{
if (cgi_write_script_output_to_client (httpd, client, cgi) <= -1)
{
/* can't return internal server error any more... */
/* TODO: logging ... */
/* can't return internal server error any more... */
return -1;
}
}
@ -1024,11 +1051,7 @@ static int task_main_cgi_4_nph (
}
QSE_ASSERT (cgi->buflen > 0);
if (cgi_write_script_output_to_client (httpd, client, cgi) <= -1)
{
/* TODO: logging ... */
return -1;
}
if (cgi_write_script_output_to_client (httpd, client, cgi) <= -1) return -1;
}
return 1;
@ -1043,8 +1066,10 @@ static int task_main_cgi_4 (
QSE_ASSERT (!cgi->nph);
QSE_ASSERT (cgi->pio_inited);
#if 0
qse_printf (QSE_T("task_main_cgi_4 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask=%d\n"),
task->trigger[0].mask, task->trigger[1].mask, task->trigger[2].mask);
#endif
if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE)
{
@ -1057,7 +1082,6 @@ qse_printf (QSE_T("task_main_cgi_4 trigger[0].mask=%d trigger[1].mask=%d trigger
if (task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_READABLE)
{
qse_printf (QSE_T("TASK_MAIN_CGI_4\n"));
if (cgi->resflags & CGI_RES_CLIENT_CHUNK)
{
qse_size_t count, extra;
@ -1068,7 +1092,6 @@ qse_printf (QSE_T("TASK_MAIN_CGI_4\n"));
#define CHLEN_RESERVE 6
qse_printf (QSE_T("READING CHUNKED MODE...\n"));
extra = CHLEN_RESERVE + 2;
count = QSE_SIZEOF(cgi->buf) - cgi->buflen;
if (count > extra)
@ -1080,7 +1103,8 @@ qse_printf (QSE_T("TASK_MAIN_CGI_4\n"));
);
if (n <= -1)
{
/* TODO: logging ... */
if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT)
log_cgi_script_error (cgi, QSE_MT("cgi pio read error - "));
return -1;
}
if (n == 0)
@ -1121,15 +1145,15 @@ qse_printf (QSE_T("TASK_MAIN_CGI_4\n"));
if ((cgi->resflags & CGI_RES_SCRIPT_LENGTH) &&
cgi->script_output_received > cgi->script_output_length)
{
/* TODO: cgi returning too much data... something is wrong in CGI */
qse_printf (QSE_T("CGI FUCKED UP...RETURNING TOO MUCH DATA\n"));
/* cgi returning too much data... something is wrong in CGI */
if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT)
log_cgi_script_error (cgi, QSE_MT("cgi redundant output - "));
return -1;
}
}
}
else
{
qse_printf (QSE_T("READING IN NON-CHUNKED MODE...\n"));
if (cgi->buflen < QSE_SIZEOF(cgi->buf))
{
n = cgi_read_script_output_to_buffer (httpd, client, cgi);
@ -1147,9 +1171,9 @@ qse_printf (QSE_T("TASK_MAIN_CGI_4\n"));
if ((cgi->resflags & CGI_RES_SCRIPT_LENGTH) &&
cgi->script_output_received > cgi->script_output_length)
{
/* TODO: logging */
/* TODO: cgi returning too much data... something is wrong in CGI */
qse_printf (QSE_T("CGI FUCKED UP...RETURNING TOO MUCH DATA\n"));
/* cgi returning too much data... something is wrong in CGI */
if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT)
log_cgi_script_error (cgi, QSE_MT("cgi redundant output - "));
return -1;
}
}
@ -1159,11 +1183,7 @@ qse_printf (QSE_T("TASK_MAIN_CGI_4\n"));
* side is writable. it should be safe to write whenever
* this task function is called. */
QSE_ASSERT (cgi->buflen > 0);
if (cgi_write_script_output_to_client (httpd, client, cgi) <= -1)
{
/* TODO: logging ... */
return -1;
}
if (cgi_write_script_output_to_client (httpd, client, cgi) <= -1) return -1;
}
return 1;
@ -1181,8 +1201,10 @@ static int task_main_cgi_3 (
QSE_ASSERT (!cgi->nph);
#if 0
qse_printf (QSE_T("task_main_cgi_3 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask=%d\n"),
task->trigger[0].mask, task->trigger[1].mask, task->trigger[2].mask);
#endif
if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE)
{
cgi_forward_client_input_to_script (httpd, task, 0);
@ -1200,15 +1222,13 @@ qse_printf (QSE_T("task_main_cgi_3 trigger[0].mask=%d trigger[1].mask=%d trigger
count = MAX_SEND_SIZE;
if (count >= cgi->res_left) count = cgi->res_left;
qse_printf (QSE_T("[cgi_3 sending %d bytes]\n"), (int)count);
if (count > 0)
{
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->opt.scb.client.send (httpd, client, cgi->res_ptr, count);
if (n <= -1)
{
qse_printf (QSE_T("[cgi-3 send failure....\n"));
if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT)
log_cgi_script_error (cgi, QSE_MT("cgi initial write error to client - "));
return -1;
}
@ -1223,7 +1243,6 @@ qse_printf (QSE_T("[cgi-3 send failure....\n"));
if ((cgi->resflags & CGI_RES_SCRIPT_LENGTH) &&
cgi->script_output_received >= cgi->script_output_length)
{
qse_printf (QSE_T("[switching to cgi-5....\n"));
/* if a cgi script specified the content length
* and it has emitted as much as the length,
* i don't wait for the script to finish.
@ -1236,7 +1255,6 @@ qse_printf (QSE_T("[switching to cgi-5....\n"));
}
else
{
qse_printf (QSE_T("[switching to cgi-4....\n"));
task->main = task_main_cgi_4;
task->trigger[2].mask &= ~QSE_HTTPD_TASK_TRIGGER_WRITE;
}
@ -1262,12 +1280,13 @@ static int task_main_cgi_2 (
QSE_ASSERT (!cgi->nph);
QSE_ASSERT (cgi->pio_inited);
#if 0
qse_printf (QSE_T("task_main_cgi_2 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask=%d\n"),
task->trigger[0].mask, task->trigger[1].mask, task->trigger[2].mask);
#endif
if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE)
{
qse_printf (QSE_T("[cgi_2 write]\n"));
cgi_forward_client_input_to_script (httpd, task, 0);
}
else if (task->trigger[1].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)
@ -1277,7 +1296,6 @@ qse_printf (QSE_T("[cgi_2 write]\n"));
if (task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_READABLE)
{
qse_printf (QSE_T("[cgi_2 read]\n"));
n = qse_pio_read (
&cgi->pio, QSE_PIO_OUT,
&cgi->buf[cgi->buflen],
@ -1286,25 +1304,24 @@ qse_printf (QSE_T("[cgi_2 read]\n"));
if (n <= -1)
{
/* can't return internal server error any more... */
/* TODO: logging ... */
if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT)
log_cgi_script_error (cgi, QSE_MT("cgi pio read error - "));
goto oops;
}
if (n == 0)
{
/* end of output from cgi before it has seen a header.
* the cgi script must be crooked. */
/* TODO: logging */
qse_printf (QSE_T("#####PREMATURE EOF FROM CHILD\n"));
if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT)
log_cgi_script_error (cgi, QSE_MT("cgi premature eof - "));
goto oops;
}
cgi->buflen += n;
qse_printf (QSE_T("#####CGI FEED [%.*hs]\n"), (int)cgi->buflen, cgi->buf);
if (qse_htrd_feed (cgi->script_htrd, cgi->buf, cgi->buflen) <= -1)
{
/* TODO: logging */
qse_printf (QSE_T("#####INVALID HEADER FROM FROM CHILD [%.*hs]\n"), (int)cgi->buflen, cgi->buf);
if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT)
log_cgi_script_error (cgi, QSE_MT("cgi feed error - "));
goto oops;
}
@ -1324,7 +1341,9 @@ qse_printf (QSE_T("#####INVALID HEADER FROM FROM CHILD [%.*hs]\n"), (int)cgi->bu
cgi->res_ptr = QSE_MBS_PTR(cgi->res);
cgi->res_left = QSE_MBS_LEN(cgi->res);
#if 0
qse_printf (QSE_T("TRAILING DATA=[%.*hs]\n"), (int)QSE_MBS_LEN(cgi->res), QSE_MBS_PTR(cgi->res));
#endif
task->main = task_main_cgi_3;
task->trigger[2].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE;
return 1;
@ -1447,7 +1466,6 @@ static int task_main_cgi (
* 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_client_input_to_script (httpd, task, 0);
/* if the initial forwarding clears the forwarding

View File

@ -29,8 +29,8 @@
typedef struct task_proxy_arg_t task_proxy_arg_t;
struct task_proxy_arg_t
{
qse_nwad_t* peer_nwad;
qse_nwad_t* peer_local;
const qse_nwad_t* peer_nwad;
const qse_nwad_t* peer_local;
qse_htre_t* req;
};

View File

@ -531,12 +531,15 @@ struct httpd_xtn_t
#if defined(HAVE_SSL)
static int init_xtn_ssl (
httpd_xtn_t* xtn,
qse_httpd_t* httpd,
const qse_mchar_t* pemfile,
const qse_mchar_t* keyfile/*,
const qse_mchar_t* chainfile*/)
{
SSL_CTX* ctx;
httpd_xtn_t* xtn;
xtn = (httpd_xtn_t*)qse_httpd_getxtn (httpd);
ctx = SSL_CTX_new (SSLv23_server_method());
if (ctx == QSE_NULL) return -1;
@ -548,10 +551,14 @@ static int init_xtn_ssl (
SSL_CTX_check_private_key (ctx) == 0 /*||
SSL_CTX_use_certificate_chain_file (ctx, chainfile) == 0*/)
{
qse_mchar_t buf[128];
ERR_error_string_n(ERR_get_error(), buf, QSE_COUNTOF(buf));
/* TODO: logging */
qse_fprintf (QSE_STDERR, QSE_T("Error: %hs\n"), buf);
if (httpd->opt.trait & QSE_HTTPD_LOGACT)
{
qse_httpd_act_t msg;
msg.code = QSE_HTTPD_CATCH_MERRMSG;
ERR_error_string_n (ERR_get_error(), msg.u.merrmsg, QSE_COUNTOF(msg.u.merrmsg));
httpd->opt.rcb.logact (httpd, &msg);
}
SSL_CTX_free (ctx);
return -1;
}
@ -600,7 +607,7 @@ qse_httpd_t* qse_httpd_openstdwithmmgr (qse_mmgr_t* mmgr, qse_size_t xtnsize)
xtn = (httpd_xtn_t*)qse_httpd_getxtn (httpd);
#if defined(HAVE_SSL)
/*init_xtn_ssl (xtn, "http01.pem", "http01.key");*/
/*init_xtn_ssl (httpd, "http01.pem", "http01.key");*/
#endif
set_httpd_callbacks (httpd);
@ -1409,7 +1416,16 @@ static int file_ropen (
}
handle->ptr = fio;
qse_printf (QSE_T("opened rfile [%hs][%p][%p]\n"), path, handle->ptr, fio->handle);
if (httpd->opt.trait & QSE_HTTPD_LOGACT)
{
qse_httpd_act_t msg;
qse_size_t pos;
msg.code = QSE_HTTPD_CATCH_MDBGMSG;
pos = qse_mbscpy (msg.u.mdbgmsg, QSE_MT("ropened file "));
qse_mbsxcpy (&msg.u.mdbgmsg[pos], QSE_COUNTOF(msg.u.mdbgmsg) - pos, path);
httpd->opt.rcb.logact (httpd, &msg);
}
return 0;
}
@ -1438,13 +1454,22 @@ static int file_wopen (
}
handle->ptr = fio;
qse_printf (QSE_T("opened wfile [%hs][%p][%p]\n"), path, handle->ptr, fio->handle);
if (httpd->opt.trait & QSE_HTTPD_LOGACT)
{
qse_httpd_act_t msg;
qse_size_t pos;
msg.code = QSE_HTTPD_CATCH_MDBGMSG;
pos = qse_mbscpy (msg.u.mdbgmsg, QSE_MT("wopened file "));
qse_mbsxcpy (&msg.u.mdbgmsg[pos], QSE_COUNTOF(msg.u.mdbgmsg) - pos, path);
httpd->opt.rcb.logact (httpd, &msg);
}
return 0;
}
static void file_close (qse_httpd_t* httpd, qse_ubi_t handle)
{
qse_printf (QSE_T("closed file....%p\n"), handle.ptr);
qse_fio_fini (handle.ptr);
QSE_MMGR_FREE (httpd->mmgr, handle.ptr);
}
@ -1512,7 +1537,16 @@ static int dir_open (qse_httpd_t* httpd, const qse_mchar_t* path, qse_ubi_t* han
return -1;
}
qse_printf (QSE_T("OPENED DIRECTORY [%hs]\n"), path);
if (httpd->opt.trait & QSE_HTTPD_LOGACT)
{
qse_httpd_act_t msg;
qse_size_t pos;
msg.code = QSE_HTTPD_CATCH_MDBGMSG;
pos = qse_mbscpy (msg.u.mdbgmsg, QSE_MT("opened dir "));
qse_mbsxcpy (&msg.u.mdbgmsg[pos], QSE_COUNTOF(msg.u.mdbgmsg) - pos, path);
httpd->opt.rcb.logact (httpd, &msg);
}
handle->ptr = d;
return 0;
}
@ -1693,7 +1727,7 @@ static int client_accepted (qse_httpd_t* httpd, qse_httpd_client_t* client)
{
/* delayed initialization of ssl */
/* TODO: certificate from options */
if (init_xtn_ssl (xtn, "http01.pem", "http01.key") <= -1)
if (init_xtn_ssl (httpd, "http01.pem", "http01.key") <= -1)
{
return -1;
}
@ -1711,9 +1745,6 @@ static int client_accepted (qse_httpd_t* httpd, qse_httpd_client_t* client)
if (ssl == QSE_NULL) return -1;
client->handle2.ptr = ssl;
qse_printf (QSE_T("SSL ACCEPTING %d\n"), client->handle.i);
qse_fflush (QSE_STDOUT);
if (SSL_set_fd (ssl, client->handle.i) == 0)
{
/* don't free ssl here since client_closed()
@ -1727,18 +1758,25 @@ qse_fflush (QSE_STDOUT);
ret = SSL_accept (ssl);
if (ret <= 0)
{
if (SSL_get_error(ssl,ret) == SSL_ERROR_WANT_READ)
int err;
if ((err = SSL_get_error(ssl,ret)) == SSL_ERROR_WANT_READ)
{
/* handshaking isn't complete. */
return 0;
}
qse_fprintf (QSE_STDERR, QSE_T("Error: SSL ACCEPT ERROR\n"));
if (httpd->opt.trait & QSE_HTTPD_LOGACT)
{
qse_httpd_act_t msg;
msg.code = QSE_HTTPD_CATCH_MERRMSG;
ERR_error_string_n (err, msg.u.merrmsg, QSE_COUNTOF(msg.u.merrmsg));
httpd->opt.rcb.logact (httpd, &msg);
}
/* SSL_free (ssl); */
return -1;
}
qse_printf (QSE_T("SSL ACCEPTED %d\n"), client->handle.i);
qse_fflush (QSE_STDOUT);
#else
qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL);
return -1;
@ -1763,6 +1801,7 @@ static void client_closed (qse_httpd_t* httpd, qse_httpd_client_t* client)
}
/* ------------------------------------------------------------------- */
#if 0
static qse_htb_walk_t walk (qse_htb_t* htb, qse_htb_pair_t* pair, void* ctx)
{
qse_htre_hdrval_t* val;
@ -1775,6 +1814,7 @@ qse_printf (QSE_T("HEADER OK %d[%hs] %d[%hs]\n"), (int)QSE_HTB_KLEN(pair), QSE_
}
return QSE_HTB_WALK_FORWARD;
}
#endif
static int process_request (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req, int peek)
@ -1796,6 +1836,7 @@ static int process_request (
* non-peek mode as well */
if (peek) qse_perdechttpstr (qse_htre_getqpath(req), qse_htre_getqpath(req));
#if 0
qse_printf (QSE_T("================================\n"));
qse_printf (QSE_T("[%lu] %hs REQUEST ==> [%hs] version[%d.%d %hs] method[%hs]\n"),
(unsigned long)time(NULL),
@ -1814,6 +1855,7 @@ if (qse_htre_getcontentlen(req) > 0)
{
qse_printf (QSE_T("CONTENT [%.*S]\n"), (int)qse_htre_getcontentlen(req), qse_htre_getcontentptr(req));
}
#endif
if (peek)
{
@ -2044,11 +2086,12 @@ static void impede_httpd (qse_httpd_t* httpd)
/* do nothing */
}
static void logact_httpd (qse_httpd_t* httpd, qse_httpd_act_t* act)
static void logact_httpd (qse_httpd_t* httpd, const qse_httpd_act_t* act)
{
/* do nothing */
}
static qse_httpd_scb_t httpd_system_callbacks =
{
/* server */
@ -2341,11 +2384,11 @@ static int make_resource (
QSE_MEMSET (target, 0, QSE_SIZEOF(*target));
qse_printf (QSE_T(">>> MAKING RESOURCE [%hs]\n"), tmp.qpath);
server_xtn = qse_httpd_getserverxtn (httpd, client->server);
if (server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_DOCROOT, &tmp.docroot) <= -1 ||
server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_REALM, &tmp.realm) <= -1 ||
if (server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_DOCROOT, &tmp.docroot) <= -1) return -1;
if (server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_REALM, &tmp.realm) <= -1 ||
server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_AUTH, &tmp.auth) <= -1 ||
server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_INDEX, &tmp.index) <= -1)
{
@ -2455,8 +2498,19 @@ auth_ok:
}
}
target->type = QSE_HTTPD_RSRC_DIR;
target->u.dir.path = tmp.xpath;
/* it is a directory - should i allow it? */
if (server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_DIRACC, &target->u.err.code) <= -1) target->u.err.code = 500;
if (target->u.err.code != 200)
{
target->type = QSE_HTTPD_RSRC_ERR;
/* free xpath since it won't be used */
QSE_MMGR_FREE (httpd->mmgr, tmp.xpath);
}
else
{
target->type = QSE_HTTPD_RSRC_DIR;
target->u.dir.path = tmp.xpath;
}
}
else
{
@ -2478,14 +2532,25 @@ auth_ok:
}
if (n >= 1) return 0;
/* fall back to a normal file. */
target->type = QSE_HTTPD_RSRC_FILE;
target->u.file.path = tmp.xpath;
if (server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_MIME, &target->u.file.mime) <= -1)
/* check file's access permission */
if (server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_FILEACC, &target->u.err.code) <= -1) target->u.err.code = 500;
if (target->u.err.code != 200)
{
/* don't care about failure */
target->u.file.mime = QSE_NULL;
target->type = QSE_HTTPD_RSRC_ERR;
/* free xpath since it won't be used */
QSE_MMGR_FREE (httpd->mmgr, tmp.xpath);
}
else
{
/* fall back to a normal file. */
target->type = QSE_HTTPD_RSRC_FILE;
target->u.file.path = tmp.xpath;
if (server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_MIME, &target->u.file.mime) <= -1)
{
/* don't care about failure */
target->u.file.mime = QSE_NULL;
}
}
}
@ -2581,6 +2646,12 @@ static int query_server (
*(const qse_mchar_t**)result = QSE_NULL;
return 0;
case QSE_HTTPD_SERVERSTD_DIRACC:
case QSE_HTTPD_SERVERSTD_FILEACC:
*(int*)result = 200;
return 0;
}
qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL);

View File

@ -457,7 +457,7 @@ static void purge_client (qse_httpd_t* httpd, qse_httpd_client_t* client)
prev = client->prev;
next = client->next;
if (httpd->opt.trait & QSE_HTTPD_ENABLELOG)
if (httpd->opt.trait & QSE_HTTPD_LOGACT)
{
qse_httpd_act_t msg;
msg.code = QSE_HTTPD_PURGE_CLIENT;
@ -562,14 +562,13 @@ qse_printf (QSE_T("MUX ADDHND CLIENT READ %d\n"), client->handle.i);
httpd->client.list.tail = client;
}
{
/* TODO: proper logging */
qse_char_t tmp[128], tmp2[128], tmp3[128];
qse_nwadtostr (&client->local_addr, tmp, QSE_COUNTOF(tmp), QSE_NWADTOSTR_ALL);
qse_nwadtostr (&client->orgdst_addr, tmp2, QSE_COUNTOF(tmp2), QSE_NWADTOSTR_ALL);
qse_nwadtostr (&client->remote_addr, tmp3, QSE_COUNTOF(tmp3), QSE_NWADTOSTR_ALL);
qse_printf (QSE_T("connection %d accepted %s(%s from %s\n"), client->handle.i, tmp, tmp2, tmp3);
}
if (httpd->opt.trait & QSE_HTTPD_LOGACT)
{
qse_httpd_act_t msg;
msg.code = QSE_HTTPD_ACCEPT_CLIENT;
msg.u.client = client;
httpd->opt.rcb.logact (httpd, &msg);
}
}
return 0;
}
@ -733,7 +732,6 @@ reread:
if (httpd->errnum == QSE_HTTPD_EAGAIN)
{
/* nothing to read yet. */
qse_printf (QSE_T("Warning: Nothing to read from a client %d\n"), client->handle.i);
return 0; /* return ok */
}
else if (httpd->errnum == QSE_HTTPD_EINTR)
@ -743,14 +741,22 @@ qse_printf (QSE_T("Warning: Nothing to read from a client %d\n"), client->handle
else
{
/* TOOD: if (httpd->errnum == QSE_HTTPD_ENOERR) httpd->errnum = QSE_HTTPD_ECALLBACK; */
qse_printf (QSE_T("Error: failed to read from a client %d\n"), client->handle.i);
/* TODO: find a way to disconnect */
if (httpd->opt.trait & QSE_HTTPD_LOGACT)
{
qse_httpd_act_t msg;
msg.code = QSE_HTTPD_READERR_CLIENT;
msg.u.client = client;
httpd->opt.rcb.logact (httpd, &msg);
}
/* TODO: find a way to disconnect */
return -1;
}
}
else if (m == 0)
{
#if 0
qse_printf (QSE_T("Debug: connection closed %d\n"), client->handle.i);
#endif
/* reading from the client returned 0. this typically
* happens when the client closes the connection or
* shutdown the writing half of the socket. it's
@ -764,17 +770,22 @@ qse_printf (QSE_T("Debug: connection closed %d\n"), client->handle.i);
/* there is still more tasks to finish and
* http reader is not waiting for any more feeds. */
client->status |= CLIENT_MUTE;
#if 0
qse_printf (QSE_T(">>>>> Marking client %d as MUTE\n"), client->handle.i);
#endif
return 0;
}
else
{
#if 0
qse_printf (QSE_T(">>>>> Returning failure for client %d\n"), client->handle.i);
#endif
httpd->errnum = QSE_HTTPD_EDISCON;
return -1;
}
}
#if 0
qse_printf (QSE_T("!!!!!FEEDING %d from %d ["), (int)m, (int)client->handle.i);
#if !defined(__WATCOMC__)
{
@ -783,6 +794,7 @@ for (i = 0; i < m; i++) qse_printf (QSE_T("%hc"), buf[i]);
}
#endif
qse_printf (QSE_T("]\n"));
#endif
/* qse_htrd_feed() may call the request callback
* multiple times. that's because we don't know
@ -798,17 +810,21 @@ qse_printf (QSE_T("]\n"));
else httpd->errnum = QSE_HTTPD_ENOMEM; /* TODO: better translate error code */
}
#if 0
qse_printf (QSE_T("Error: http error while processing %d ["), (int)client->handle.i);
{
int i;
for (i = 0; i < m; i++) qse_printf (QSE_T("%hc"), buf[i]);
}
qse_printf (QSE_T("]\n"));
#endif
return -1;
}
#if 0
qse_printf (QSE_T("!!!!!FEEDING OK OK OK OK %d from %d\n"), (int)m, (int)client->handle.i);
#endif
if (client->status & CLIENT_PENDING)
{
@ -840,7 +856,6 @@ static int invoke_client_task (
int n, trigger_fired, client_handle_writable;
/* TODO: handle comparison callback ... */
qse_printf (QSE_T("INVOKE CLIENT TASK..........\n"));
if (handle.i == client->handle.i && (mask & QSE_HTTPD_MUX_READ)) /* TODO: no direct comparision */
{
if (!(client->status & CLIENT_MUTE) &&
@ -850,7 +865,6 @@ qse_printf (QSE_T("INVOKE CLIENT TASK..........\n"));
* purge the client in perform_client_task().
* thus the following line isn't necessary.
*if (httpd->errnum == QSE_HTTPD_EDISCON) return 0;*/
qse_printf (QSE_T("ERROR: read from client [%d] failed...\n"), (int)handle.i);
return -1;
}
}
@ -862,7 +876,6 @@ qse_printf (QSE_T("ERROR: read from client [%d] failed...\n"), (int)handle.i);
if (client->status & CLIENT_MUTE)
{
/* handle this delayed client disconnection */
qse_printf (QSE_T("ERROR: mute client got no more task [%d] failed...\n"), (int)client->handle.i);
return -1;
}
@ -908,7 +921,6 @@ qse_printf (QSE_T("ERROR: mute client got no more task [%d] failed...\n"), (int)
}
n = task->main (httpd, client, task);
qse_printf (QSE_T("task returend %d\n"), n);
if (n <= -1) return -1;
else if (n == 0)
{
@ -1118,17 +1130,12 @@ static int perform_client_task (
qse_gettime (&client->last_active); /* TODO: error check??? */
move_client_to_tail (httpd, client);
if (invoke_client_task (httpd, client, handle, mask) <= -1)
{
qse_printf (QSE_T("OOPS AFTER CLIENT TASK BAD XXXXXXXXXXXXXX [%d]\n"), (int)handle.i);
goto oops;
}
if (invoke_client_task (httpd, client, handle, mask) <= -1) goto oops;
}
return 0;
oops:
qse_printf (QSE_T("MARKING BAD XXXXXXXXXXXXXX [%d]\n"), (int)handle.i);
/*purge_client (httpd, client);*/
client->status |= CLIENT_BAD;
client->bad_next = httpd->client.bad;
@ -1336,3 +1343,8 @@ const qse_mchar_t* qse_httpd_fmtgmtimetobb (
qse_fmthttptime (nt, httpd->gtbuf[idx], QSE_COUNTOF(httpd->gtbuf[idx]));
return httpd->gtbuf[idx];
}
/* --------------------------------------------------- */

View File

@ -235,13 +235,16 @@ qse_xli_pair_t* qse_xli_insertpair (
if (pair == QSE_NULL) return QSE_NULL;
kptr = (qse_char_t*)(pair + 1);
nptr = kptr + klen + 1;
qse_strcpy (kptr, key);
if (name) qse_strcpy (nptr, name);
pair->type = QSE_XLI_PAIR;
pair->key = kptr;
pair->name = nptr;
if (name)
{
nptr = kptr + klen + 1;
qse_strcpy (nptr, name);
pair->name = nptr;
}
pair->val = value; /* this assumes it points to a dynamically allocated atom */
insert_atom (xli, parent, peer, (qse_xli_atom_t*)pair);