diff --git a/qse/cmd/http/httpd-mime.conf b/qse/cmd/http/httpd-mime.conf new file mode 100644 index 00000000..fe95a290 --- /dev/null +++ b/qse/cmd/http/httpd-mime.conf @@ -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"; diff --git a/qse/cmd/http/httpd.c b/qse/cmd/http/httpd.c index 65092943..a038d664 100644 --- a/qse/cmd/http/httpd.c +++ b/qse/cmd/http/httpd.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -27,6 +28,7 @@ #else # include # include +# include #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[]) diff --git a/qse/cmd/http/httpd.conf b/qse/cmd/http/httpd.conf index 9ac4d0a3..57039422 100644 --- a/qse/cmd/http/httpd.conf +++ b/qse/cmd/http/httpd.conf @@ -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 = ""; + error-css = ""; +} + +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"; + # } + # } + #} } diff --git a/qse/include/qse/http/httpd.h b/qse/include/qse/http/httpd.h index a7551380..1f00c6f6 100644 --- a/qse/include/qse/http/httpd.h +++ b/qse/include/qse/http/httpd.h @@ -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; diff --git a/qse/lib/cmn/pio.c b/qse/lib/cmn/pio.c index 726c65a9..8474f203 100644 --- a/qse/lib/cmn/pio.c +++ b/qse/lib/cmn/pio.c @@ -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; } diff --git a/qse/lib/http/httpd-cgi.c b/qse/lib/http/httpd-cgi.c index 00695026..243fc83d 100644 --- a/qse/lib/http/httpd-cgi.c +++ b/qse/lib/http/httpd-cgi.c @@ -23,6 +23,7 @@ #include #include #include +#include #include /* 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 diff --git a/qse/lib/http/httpd-proxy.c b/qse/lib/http/httpd-proxy.c index 6ee6210f..6efa08f0 100644 --- a/qse/lib/http/httpd-proxy.c +++ b/qse/lib/http/httpd-proxy.c @@ -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; }; diff --git a/qse/lib/http/httpd-std.c b/qse/lib/http/httpd-std.c index 353e904a..e47378bd 100644 --- a/qse/lib/http/httpd-std.c +++ b/qse/lib/http/httpd-std.c @@ -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); diff --git a/qse/lib/http/httpd.c b/qse/lib/http/httpd.c index 78ebe8bb..b648e2bb 100644 --- a/qse/lib/http/httpd.c +++ b/qse/lib/http/httpd.c @@ -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]; } + + +/* --------------------------------------------------- */ + + diff --git a/qse/lib/xli/xli.c b/qse/lib/xli/xli.c index 61632721..d397dc7d 100644 --- a/qse/lib/xli/xli.c +++ b/qse/lib/xli/xli.c @@ -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);