diff options
author | Krzysztof Krzyżaniak <eloy@debian.org> | 2006-01-16 19:08:37 +0000 |
---|---|---|
committer | Krzysztof Krzyżaniak <eloy@debian.org> | 2006-01-16 19:08:37 +0000 |
commit | f40f3dcc4b1cbbbd3bcf640e6d852786bd546d12 (patch) | |
tree | f6c6501b996665c51a3504bf5327b2753d3e048c /src | |
parent | ae6e5f21be48a40bc2a29518dc655245408cf972 (diff) | |
download | lighttpd-f40f3dcc4b1cbbbd3bcf640e6d852786bd546d12.tar.gz |
eloy: new upstream versiondebian/1.4.9-1
Diffstat (limited to 'src')
37 files changed, 1389 insertions, 641 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 8caace2..7e9fc9e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -68,6 +68,12 @@ src += $(common_src) common_libadd = endif +lib_LTLIBRARIES += mod_evasive.la +mod_evasive_la_SOURCES = mod_evasive.c +mod_evasive_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined +mod_evasive_la_LIBADD = $(common_libadd) + + lib_LTLIBRARIES += mod_webdav.la mod_webdav_la_SOURCES = mod_webdav.c mod_webdav_la_CFLAGS = $(XML_CFLAGS) diff --git a/src/Makefile.in b/src/Makefile.in index e892092..cc6fab9 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -16,7 +16,7 @@ -SOURCES = $(liblightcomp_la_SOURCES) $(mod_access_la_SOURCES) $(mod_accesslog_la_SOURCES) $(mod_alias_la_SOURCES) $(mod_auth_la_SOURCES) $(mod_cgi_la_SOURCES) $(mod_cml_la_SOURCES) $(mod_compress_la_SOURCES) $(mod_dirlisting_la_SOURCES) $(mod_evhost_la_SOURCES) $(mod_expire_la_SOURCES) $(mod_fastcgi_la_SOURCES) $(mod_indexfile_la_SOURCES) $(mod_mysql_vhost_la_SOURCES) $(mod_proxy_la_SOURCES) $(mod_redirect_la_SOURCES) $(mod_rewrite_la_SOURCES) $(mod_rrdtool_la_SOURCES) $(mod_scgi_la_SOURCES) $(mod_secdownload_la_SOURCES) $(mod_setenv_la_SOURCES) $(mod_simple_vhost_la_SOURCES) $(mod_ssi_la_SOURCES) $(mod_staticfile_la_SOURCES) $(mod_status_la_SOURCES) $(mod_trigger_b4_dl_la_SOURCES) $(mod_userdir_la_SOURCES) $(mod_usertrack_la_SOURCES) $(mod_webdav_la_SOURCES) $(lemon_SOURCES) $(lighttpd_SOURCES) $(proc_open_SOURCES) $(spawn_fcgi_SOURCES) +SOURCES = $(liblightcomp_la_SOURCES) $(mod_access_la_SOURCES) $(mod_accesslog_la_SOURCES) $(mod_alias_la_SOURCES) $(mod_auth_la_SOURCES) $(mod_cgi_la_SOURCES) $(mod_cml_la_SOURCES) $(mod_compress_la_SOURCES) $(mod_dirlisting_la_SOURCES) $(mod_evasive_la_SOURCES) $(mod_evhost_la_SOURCES) $(mod_expire_la_SOURCES) $(mod_fastcgi_la_SOURCES) $(mod_indexfile_la_SOURCES) $(mod_mysql_vhost_la_SOURCES) $(mod_proxy_la_SOURCES) $(mod_redirect_la_SOURCES) $(mod_rewrite_la_SOURCES) $(mod_rrdtool_la_SOURCES) $(mod_scgi_la_SOURCES) $(mod_secdownload_la_SOURCES) $(mod_setenv_la_SOURCES) $(mod_simple_vhost_la_SOURCES) $(mod_ssi_la_SOURCES) $(mod_staticfile_la_SOURCES) $(mod_status_la_SOURCES) $(mod_trigger_b4_dl_la_SOURCES) $(mod_userdir_la_SOURCES) $(mod_usertrack_la_SOURCES) $(mod_webdav_la_SOURCES) $(lemon_SOURCES) $(lighttpd_SOURCES) $(proc_open_SOURCES) $(spawn_fcgi_SOURCES) srcdir = @srcdir@ top_srcdir = @top_srcdir@ @@ -145,6 +145,9 @@ mod_dirlisting_la_DEPENDENCIES = $(am__DEPENDENCIES_2) \ $(am__DEPENDENCIES_1) am_mod_dirlisting_la_OBJECTS = mod_dirlisting.lo mod_dirlisting_la_OBJECTS = $(am_mod_dirlisting_la_OBJECTS) +mod_evasive_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_evasive_la_OBJECTS = mod_evasive.lo +mod_evasive_la_OBJECTS = $(am_mod_evasive_la_OBJECTS) mod_evhost_la_DEPENDENCIES = $(am__DEPENDENCIES_2) am_mod_evhost_la_OBJECTS = mod_evhost.lo mod_evhost_la_OBJECTS = $(am_mod_evhost_la_OBJECTS) @@ -282,23 +285,7 @@ SOURCES = $(liblightcomp_la_SOURCES) $(mod_access_la_SOURCES) \ $(mod_accesslog_la_SOURCES) $(mod_alias_la_SOURCES) \ $(mod_auth_la_SOURCES) $(mod_cgi_la_SOURCES) \ $(mod_cml_la_SOURCES) $(mod_compress_la_SOURCES) \ - $(mod_dirlisting_la_SOURCES) $(mod_evhost_la_SOURCES) \ - $(mod_expire_la_SOURCES) $(mod_fastcgi_la_SOURCES) \ - $(mod_indexfile_la_SOURCES) $(mod_mysql_vhost_la_SOURCES) \ - $(mod_proxy_la_SOURCES) $(mod_redirect_la_SOURCES) \ - $(mod_rewrite_la_SOURCES) $(mod_rrdtool_la_SOURCES) \ - $(mod_scgi_la_SOURCES) $(mod_secdownload_la_SOURCES) \ - $(mod_setenv_la_SOURCES) $(mod_simple_vhost_la_SOURCES) \ - $(mod_ssi_la_SOURCES) $(mod_staticfile_la_SOURCES) \ - $(mod_status_la_SOURCES) $(mod_trigger_b4_dl_la_SOURCES) \ - $(mod_userdir_la_SOURCES) $(mod_usertrack_la_SOURCES) \ - $(mod_webdav_la_SOURCES) $(lemon_SOURCES) $(lighttpd_SOURCES) \ - $(proc_open_SOURCES) $(spawn_fcgi_SOURCES) -DIST_SOURCES = $(am__liblightcomp_la_SOURCES_DIST) \ - $(mod_access_la_SOURCES) $(mod_accesslog_la_SOURCES) \ - $(mod_alias_la_SOURCES) $(mod_auth_la_SOURCES) \ - $(mod_cgi_la_SOURCES) $(mod_cml_la_SOURCES) \ - $(mod_compress_la_SOURCES) $(mod_dirlisting_la_SOURCES) \ + $(mod_dirlisting_la_SOURCES) $(mod_evasive_la_SOURCES) \ $(mod_evhost_la_SOURCES) $(mod_expire_la_SOURCES) \ $(mod_fastcgi_la_SOURCES) $(mod_indexfile_la_SOURCES) \ $(mod_mysql_vhost_la_SOURCES) $(mod_proxy_la_SOURCES) \ @@ -309,8 +296,26 @@ DIST_SOURCES = $(am__liblightcomp_la_SOURCES_DIST) \ $(mod_staticfile_la_SOURCES) $(mod_status_la_SOURCES) \ $(mod_trigger_b4_dl_la_SOURCES) $(mod_userdir_la_SOURCES) \ $(mod_usertrack_la_SOURCES) $(mod_webdav_la_SOURCES) \ - $(lemon_SOURCES) $(am__lighttpd_SOURCES_DIST) \ - $(proc_open_SOURCES) $(spawn_fcgi_SOURCES) + $(lemon_SOURCES) $(lighttpd_SOURCES) $(proc_open_SOURCES) \ + $(spawn_fcgi_SOURCES) +DIST_SOURCES = $(am__liblightcomp_la_SOURCES_DIST) \ + $(mod_access_la_SOURCES) $(mod_accesslog_la_SOURCES) \ + $(mod_alias_la_SOURCES) $(mod_auth_la_SOURCES) \ + $(mod_cgi_la_SOURCES) $(mod_cml_la_SOURCES) \ + $(mod_compress_la_SOURCES) $(mod_dirlisting_la_SOURCES) \ + $(mod_evasive_la_SOURCES) $(mod_evhost_la_SOURCES) \ + $(mod_expire_la_SOURCES) $(mod_fastcgi_la_SOURCES) \ + $(mod_indexfile_la_SOURCES) $(mod_mysql_vhost_la_SOURCES) \ + $(mod_proxy_la_SOURCES) $(mod_redirect_la_SOURCES) \ + $(mod_rewrite_la_SOURCES) $(mod_rrdtool_la_SOURCES) \ + $(mod_scgi_la_SOURCES) $(mod_secdownload_la_SOURCES) \ + $(mod_setenv_la_SOURCES) $(mod_simple_vhost_la_SOURCES) \ + $(mod_ssi_la_SOURCES) $(mod_staticfile_la_SOURCES) \ + $(mod_status_la_SOURCES) $(mod_trigger_b4_dl_la_SOURCES) \ + $(mod_userdir_la_SOURCES) $(mod_usertrack_la_SOURCES) \ + $(mod_webdav_la_SOURCES) $(lemon_SOURCES) \ + $(am__lighttpd_SOURCES_DIST) $(proc_open_SOURCES) \ + $(spawn_fcgi_SOURCES) HEADERS = $(noinst_HEADERS) ETAGS = etags CTAGS = ctags @@ -480,20 +485,24 @@ spawn_fcgi_SOURCES = spawn-fcgi.c #mod_httptls_la_SOURCES = mod_httptls.c #mod_httptls_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined #mod_httptls_la_LIBADD = $(common_libadd) -lib_LTLIBRARIES = $(am__append_1) mod_webdav.la mod_cml.la \ - mod_trigger_b4_dl.la mod_mysql_vhost.la mod_cgi.la mod_scgi.la \ - mod_staticfile.la mod_dirlisting.la mod_indexfile.la \ - mod_setenv.la mod_alias.la mod_userdir.la mod_rrdtool.la \ - mod_usertrack.la mod_proxy.la mod_ssi.la mod_secdownload.la \ - mod_expire.la mod_evhost.la mod_simple_vhost.la mod_fastcgi.la \ - mod_access.la mod_compress.la mod_auth.la mod_rewrite.la \ - mod_redirect.la mod_status.la mod_accesslog.la +lib_LTLIBRARIES = $(am__append_1) mod_evasive.la mod_webdav.la \ + mod_cml.la mod_trigger_b4_dl.la mod_mysql_vhost.la mod_cgi.la \ + mod_scgi.la mod_staticfile.la mod_dirlisting.la \ + mod_indexfile.la mod_setenv.la mod_alias.la mod_userdir.la \ + mod_rrdtool.la mod_usertrack.la mod_proxy.la mod_ssi.la \ + mod_secdownload.la mod_expire.la mod_evhost.la \ + mod_simple_vhost.la mod_fastcgi.la mod_access.la \ + mod_compress.la mod_auth.la mod_rewrite.la mod_redirect.la \ + mod_status.la mod_accesslog.la @NO_RDYNAMIC_TRUE@liblightcomp_la_SOURCES = $(common_src) @NO_RDYNAMIC_TRUE@liblightcomp_la_CFLAGS = $(AM_CFLAGS) $(FAM_CFLAGS) @NO_RDYNAMIC_TRUE@liblightcomp_la_LDFLAGS = -avoid-version -no-undefined @NO_RDYNAMIC_TRUE@liblightcomp_la_LIBADD = $(PCRE_LIB) $(SSL_LIB) $(FAM_LIBS) @NO_RDYNAMIC_FALSE@common_libadd = @NO_RDYNAMIC_TRUE@common_libadd = liblightcomp.la +mod_evasive_la_SOURCES = mod_evasive.c +mod_evasive_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined +mod_evasive_la_LIBADD = $(common_libadd) mod_webdav_la_SOURCES = mod_webdav.c mod_webdav_la_CFLAGS = $(XML_CFLAGS) mod_webdav_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined @@ -693,6 +702,8 @@ mod_compress.la: $(mod_compress_la_OBJECTS) $(mod_compress_la_DEPENDENCIES) $(LINK) -rpath $(libdir) $(mod_compress_la_LDFLAGS) $(mod_compress_la_OBJECTS) $(mod_compress_la_LIBADD) $(LIBS) mod_dirlisting.la: $(mod_dirlisting_la_OBJECTS) $(mod_dirlisting_la_DEPENDENCIES) $(LINK) -rpath $(libdir) $(mod_dirlisting_la_LDFLAGS) $(mod_dirlisting_la_OBJECTS) $(mod_dirlisting_la_LIBADD) $(LIBS) +mod_evasive.la: $(mod_evasive_la_OBJECTS) $(mod_evasive_la_DEPENDENCIES) + $(LINK) -rpath $(libdir) $(mod_evasive_la_LDFLAGS) $(mod_evasive_la_OBJECTS) $(mod_evasive_la_LIBADD) $(LIBS) mod_evhost.la: $(mod_evhost_la_OBJECTS) $(mod_evhost_la_DEPENDENCIES) $(LINK) -rpath $(libdir) $(mod_evhost_la_LDFLAGS) $(mod_evhost_la_OBJECTS) $(mod_evhost_la_LIBADD) $(LIBS) mod_expire.la: $(mod_expire_la_OBJECTS) $(mod_expire_la_DEPENDENCIES) @@ -897,6 +908,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_cml_la-mod_cml_lua.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_compress.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_dirlisting.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_evasive.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_evhost.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_expire.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_fastcgi.Plo@am__quote@ @@ -260,7 +260,7 @@ typedef struct { unsigned short use_ipv6; unsigned short is_ssl; unsigned short allow_http11; - unsigned short force_lower_case; /* if the FS is case-insensitive, force all files to lower-case */ + unsigned short force_lowercase_filenames; /* if the FS is case-insensitive, force all files to lower-case */ unsigned short max_request_size; unsigned short kbytes_per_second; /* connection kb/s limit */ @@ -289,7 +289,21 @@ typedef struct { #endif } specific_config; -typedef enum { CON_STATE_CONNECT, CON_STATE_REQUEST_START, CON_STATE_READ, CON_STATE_REQUEST_END, CON_STATE_READ_POST, CON_STATE_HANDLE_REQUEST, CON_STATE_RESPONSE_START, CON_STATE_WRITE, CON_STATE_RESPONSE_END, CON_STATE_ERROR, CON_STATE_CLOSE } connection_state_t; +/* the order of the items should be the same as they are processed + * read before write as we use this later */ +typedef enum { + CON_STATE_CONNECT, + CON_STATE_REQUEST_START, + CON_STATE_READ, + CON_STATE_REQUEST_END, + CON_STATE_READ_POST, + CON_STATE_HANDLE_REQUEST, + CON_STATE_RESPONSE_START, + CON_STATE_WRITE, + CON_STATE_RESPONSE_END, + CON_STATE_ERROR, + CON_STATE_CLOSE +} connection_state_t; typedef enum { COND_RESULT_UNSET, COND_RESULT_FALSE, COND_RESULT_TRUE } cond_result_t; typedef struct { @@ -459,6 +473,7 @@ typedef struct { STAT_CACHE_ENGINE_SIMPLE, STAT_CACHE_ENGINE_FAM } stat_cache_engine; + unsigned short enable_cores; } server_config; typedef struct { @@ -558,6 +573,21 @@ typedef struct server { connections *fdwaitqueue; stat_cache *stat_cache; + + /** + * The status array can carry all the status information you want + * the key to the array is <module-prefix>.<name> + * and the values are counters + * + * example: + * fastcgi.backends = 10 + * fastcgi.active-backends = 6 + * fastcgi.backend.<key>.load = 24 + * fastcgi.backend.<key>.... + * + * fastcgi.backend.<key>.disconnects = ... + */ + array *status; fdevent_handler_t event_handler; diff --git a/src/buffer.c b/src/buffer.c index cd4a72a..40b8cb9 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -685,6 +685,28 @@ const char encoded_chars_html[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* F0 - FF */ }; +const char encoded_chars_minimal_xml[] = { + /* + 0 1 2 3 4 5 6 7 8 9 A B C D E F + */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 2F & */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 30 - 3F < > */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 - 5F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 70 - 7F DEL */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */ +}; + const char encoded_chars_hex[] = { /* 0 1 2 3 4 5 6 7 8 9 A B C D E F @@ -731,6 +753,9 @@ int buffer_append_string_encoded(buffer *b, const char *s, size_t s_len, buffer_ case ENCODING_HTML: map = encoded_chars_html; break; + case ENCODING_MINIMAL_XML: + map = encoded_chars_minimal_xml; + break; case ENCODING_HEX: map = encoded_chars_hex; break; @@ -749,6 +774,7 @@ int buffer_append_string_encoded(buffer *b, const char *s, size_t s_len, buffer_ d_len += 3; break; case ENCODING_HTML: + case ENCODING_MINIMAL_XML: d_len += 6; break; case ENCODING_HEX: @@ -774,6 +800,7 @@ int buffer_append_string_encoded(buffer *b, const char *s, size_t s_len, buffer_ d[d_len++] = hex_chars[(*ds) & 0x0F]; break; case ENCODING_HTML: + case ENCODING_MINIMAL_XML: d[d_len++] = '&'; d[d_len++] = '#'; d[d_len++] = 'x'; diff --git a/src/buffer.h b/src/buffer.h index c304d76..3ca22e5 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -88,6 +88,7 @@ typedef enum { ENCODING_REL_URI, /* for coding a rel-uri (/with space/and%percent) nicely as part of a href */ ENCODING_REL_URI_PART, /* same as ENC_REL_URL plus coding / too as %2F */ ENCODING_HTML, /* & becomes & and so on */ + ENCODING_MINIMAL_XML, /* minimal encoding for xml */ ENCODING_HEX /* encode string as hex */ } buffer_encoding_t; diff --git a/src/configfile.c b/src/configfile.c index e9080c3..6f13cd6 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -44,7 +44,7 @@ static int config_insert(server *srv) { { "server.max-request-size", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 12 */ { "server.max-worker", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 13 */ { "server.document-root", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 14 */ - { "server.force-lower-case-files", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 15 */ + { "server.force-lowercase-filenames", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 15 */ { "debug.log-condition-handling", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 16 */ { "server.max-keep-alive-requests", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 17 */ { "server.name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 18 */ @@ -79,6 +79,7 @@ static int config_insert(server *srv) { { "server.max-connections", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 42 */ { "server.network-backend", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 43 */ { "server.upload-dirs", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 44 */ + { "server.core-files", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 45 */ { "server.host", "use server.bind instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, { "server.docroot", "use server.document-root instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, @@ -88,6 +89,7 @@ static int config_insert(server *srv) { { "server.userid", "use server.username instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, { "server.groupid", "use server.groupname instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, { "server.use-keep-alive", "use server.max-keep-alive-requests = 0 instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, + { "server.force-lower-case-files", "use server.force-lowercase-filenames instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; @@ -116,6 +118,7 @@ static int config_insert(server *srv) { cv[41].destination = stat_cache_string; cv[43].destination = srv->srvconf.network_backend; cv[44].destination = srv->srvconf.upload_tempdirs; + cv[45].destination = &(srv->srvconf.enable_cores); cv[42].destination = &(srv->srvconf.max_conns); cv[12].destination = &(srv->srvconf.max_request_size); @@ -147,7 +150,7 @@ static int config_insert(server *srv) { s->kbytes_per_second = 0; s->allow_http11 = 1; s->range_requests = 1; - s->force_lower_case = 0; + s->force_lowercase_filenames = 0; s->global_kbytes_per_second = 0; s->global_bytes_per_second_cnt = 0; s->global_bytes_per_second_cnt_ptr = &s->global_bytes_per_second_cnt; @@ -160,7 +163,7 @@ static int config_insert(server *srv) { /* 13 max-worker */ cv[14].destination = s->document_root; - cv[15].destination = &(s->force_lower_case); + cv[15].destination = &(s->force_lowercase_filenames); cv[16].destination = &(s->log_condition_handling); cv[17].destination = &(s->max_keep_alive_requests); cv[18].destination = s->server_name; @@ -244,7 +247,7 @@ int config_setup_connection(server *srv, connection *con) { PATCH(log_file_not_found); PATCH(range_requests); - PATCH(force_lower_case); + PATCH(force_lowercase_filenames); PATCH(is_ssl); PATCH(ssl_pemfile); @@ -316,8 +319,8 @@ int config_patch_connection(server *srv, connection *con, comp_key_t comp) { PATCH(log_file_not_found); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.protocol-http11"))) { PATCH(allow_http11); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.force-lower-case-files"))) { - PATCH(force_lower_case); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.force-lowercase-filenames"))) { + PATCH(force_lowercase_filenames); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.kbytes-per-second"))) { PATCH(global_kbytes_per_second); PATCH(global_bytes_per_second_cnt); @@ -1085,7 +1088,7 @@ int config_set_defaults(server *srv) { * an other filename, no need to stat(), * just assume it is case-sensitive. */ - s->force_lower_case = 0; + s->force_lowercase_filenames = 0; } else if (0 == stat(srv->tmp_buf->ptr, &st2)) { /* upper case exists too, doesn't the FS handle this ? */ @@ -1095,7 +1098,7 @@ int config_set_defaults(server *srv) { if (st1.st_ino == st2.st_ino) { /* upper and lower have the same inode -> case-insensitve FS */ - s->force_lower_case = 1; + s->force_lowercase_filenames = 1; } } } diff --git a/src/configparser.c b/src/configparser.c index 68d9c62..2ce169a 100644 --- a/src/configparser.c +++ b/src/configparser.c @@ -1194,8 +1194,8 @@ static void yy_reduce( dc->string = buffer_init_buffer(rvalue); } #else - fprintf(stderr, "regex conditionals are not allowed as pcre-support" \ - "is missing: $%s[%s]\n", + fprintf(stderr, "can't handle '$%s[%s] =~ ...' as you compiled without pcre support. \n" + "(perhaps just a missing pcre-devel package ?) \n", yymsp[-5].minor.yy0->ptr, yymsp[-3].minor.yy1->ptr); ctx->ok = 0; #endif diff --git a/src/configparser.y b/src/configparser.y index 4ca4fa0..767024f 100644 --- a/src/configparser.y +++ b/src/configparser.y @@ -447,8 +447,8 @@ context ::= DOLLAR SRVVARNAME(B) LBRACKET stringop(C) RBRACKET cond(E) expressio dc->string = buffer_init_buffer(rvalue); } #else - fprintf(stderr, "regex conditionals are not allowed as pcre-support" \ - "is missing: $%s[%s]\n", + fprintf(stderr, "can't handle '$%s[%s] =~ ...' as you compiled without pcre support. \n" + "(perhaps just a missing pcre-devel package ?) \n", B->ptr, C->ptr); ctx->ok = 0; #endif diff --git a/src/connections.c b/src/connections.c index acd8880..ea3a66c 100644 --- a/src/connections.c +++ b/src/connections.c @@ -352,7 +352,13 @@ static int connection_handle_write_prepare(server *srv, connection *con) { case HTTP_METHOD_PROPPATCH: break; case HTTP_METHOD_OPTIONS: - if (con->uri.path->ptr[0] != '*') { + /* + * 400 is coming from the request-parser BEFORE uri.path is set + * 403 is from the response handler when noone else catched it + * + * */ + if (con->uri.path->used && + con->uri.path->ptr[0] != '*') { response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST")); con->http_status = 200; @@ -364,6 +370,7 @@ static int connection_handle_write_prepare(server *srv, connection *con) { default: switch(con->http_status) { case 400: /* bad request */ + case 414: /* overload request header */ case 505: /* unknown protocol */ case 207: /* this was webdav */ break; @@ -864,6 +871,7 @@ int connection_handle_read_state(server *srv, connection *con) { c->next = cq->unused; cq->unused = c; + cq->unused_chunks++; c = cq->first; } else if (c->next && c->next->mem->used == 0) { @@ -876,6 +884,7 @@ int connection_handle_read_state(server *srv, connection *con) { fc->next = cq->unused; cq->unused = fc; + cq->unused_chunks++; /* the last node was empty */ if (c->next == NULL) { @@ -981,11 +990,11 @@ int connection_handle_read_state(server *srv, connection *con) { if (h_term) { connection_set_state(srv, con, CON_STATE_REQUEST_END); } else if (con->request.request->used > 64 * 1024) { - log_error_write(srv, __FILE__, __LINE__, "sd", "http-header larger then 64k -> disconnected", chunkqueue_length(cq)); + log_error_write(srv, __FILE__, __LINE__, "s", "oversized request-header -> sending Status 414"); con->http_status = 414; /* Request-URI too large */ con->keep_alive = 0; - connection_set_state(srv, con, CON_STATE_REQUEST_END); + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); } break; case CON_STATE_READ_POST: diff --git a/src/http_auth.c b/src/http_auth.c index 478a2f7..9976c15 100644 --- a/src/http_auth.c +++ b/src/http_auth.c @@ -350,19 +350,25 @@ static int http_auth_match_rules(server *srv, mod_auth_plugin_data *p, const cha /* from r to r + r_len is a rule */ if (0 == strncmp(r, "valid-user", r_len)) { - log_error_write(srv, __FILE__, __LINE__, "s", "valid-user cannot be combined with other require rules"); + log_error_write(srv, __FILE__, __LINE__, "sb", + "parsing the 'require' section in 'auth.require' failed: valid-user cannot be combined with other require rules", + require->value); return -1; } /* search for = in the rules */ if (NULL == (eq = strchr(r, '='))) { - log_error_write(srv, __FILE__, __LINE__, "s", "= is missing"); + log_error_write(srv, __FILE__, __LINE__, "sb", + "parsing the 'require' section in 'auth.require' failed: a = is missing", + require->value); return -1; } /* = out of range */ if (eq > r + r_len) { - log_error_write(srv, __FILE__, __LINE__, "s", "= out of range"); + log_error_write(srv, __FILE__, __LINE__, "sb", + "parsing the 'require' section in 'auth.require' failed: = out of range", + require->value); return -1; } diff --git a/src/http_auth_digest.c b/src/http_auth_digest.c index 8f7086f..e440430 100644 --- a/src/http_auth_digest.c +++ b/src/http_auth_digest.c @@ -17,85 +17,3 @@ void CvtHex(IN HASH Bin, OUT HASHHEX Hex) { Hex[HASHHEXLEN] = '\0'; } -/* calculate H(A1) as per spec */ -void DigestCalcHA1( - IN char * pszAlg, - IN char * pszUserName, - IN char * pszRealm, - IN char * pszPassword, - IN char * pszNonce, - IN char * pszCNonce, - OUT HASHHEX SessionKey - ) -{ - MD5_CTX Md5Ctx; - HASH HA1; - - MD5_Init(&Md5Ctx); - MD5_Update(&Md5Ctx, (unsigned char *)pszUserName, strlen(pszUserName)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)pszRealm, strlen(pszRealm)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)pszPassword, strlen(pszPassword)); - MD5_Final(HA1, &Md5Ctx); - if (strcasecmp(pszAlg, "md5-sess") == 0) { - MD5_Init(&Md5Ctx); - MD5_Update(&Md5Ctx, (unsigned char *)HA1, HASHLEN); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)pszNonce, strlen(pszNonce)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)pszCNonce, strlen(pszCNonce)); - MD5_Final(HA1, &Md5Ctx); - } - CvtHex(HA1, SessionKey); -} - -/* calculate request-digest/response-digest as per HTTP Digest spec */ -void DigestCalcResponse( - IN HASHHEX HA1, /* H(A1) */ - IN char * pszNonce, /* nonce from server */ - IN char * pszNonceCount, /* 8 hex digits */ - IN char * pszCNonce, /* client nonce */ - IN char * pszQop, /* qop-value: "", "auth", "auth-int" */ - IN char * pszMethod, /* method from the request */ - IN char * pszDigestUri, /* requested URL */ - IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */ - OUT HASHHEX Response /* request-digest or response-digest */ - ) -{ - MD5_CTX Md5Ctx; - HASH HA2; - HASH RespHash; - HASHHEX HA2Hex; - - /* calculate H(A2) */ - MD5_Init(&Md5Ctx); - MD5_Update(&Md5Ctx, (unsigned char *)pszMethod, strlen(pszMethod)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)pszDigestUri, strlen(pszDigestUri)); - if (strcasecmp(pszQop, "auth-int") == 0) { - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)HEntity, HASHHEXLEN); - }; - MD5_Final(HA2, &Md5Ctx); - CvtHex(HA2, HA2Hex); - - /* calculate response */ - MD5_Init(&Md5Ctx); - MD5_Update(&Md5Ctx, (unsigned char *)HA1, HASHHEXLEN); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)pszNonce, strlen(pszNonce)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - if (*pszQop) { - MD5_Update(&Md5Ctx, (unsigned char *)pszNonceCount, strlen(pszNonceCount)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)pszCNonce, strlen(pszCNonce)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)pszQop, strlen(pszQop)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - } - MD5_Update(&Md5Ctx, (unsigned char *)HA2Hex, HASHHEXLEN); - MD5_Final(RespHash, &Md5Ctx); - CvtHex(RespHash, Response); -} - diff --git a/src/http_auth_digest.h b/src/http_auth_digest.h index 3f11d70..8bffce4 100644 --- a/src/http_auth_digest.h +++ b/src/http_auth_digest.h @@ -16,30 +16,6 @@ typedef char HASHHEX[HASHHEXLEN+1]; #endif #define OUT -/* calculate H(A1) as per HTTP Digest spec */ -void DigestCalcHA1( - IN char * pszAlg, - IN char * pszUserName, - IN char * pszRealm, - IN char * pszPassword, - IN char * pszNonce, - IN char * pszCNonce, - OUT HASHHEX SessionKey - ); - -/* calculate request-digest/response-digest as per HTTP Digest spec */ -void DigestCalcResponse( - IN HASHHEX HA1, /* H(A1) */ - IN char * pszNonce, /* nonce from server */ - IN char * pszNonceCount, /* 8 hex digits */ - IN char * pszCNonce, /* client nonce */ - IN char * pszQop, /* qop-value: "", "auth", "auth-int" */ - IN char * pszMethod, /* method from the request */ - IN char * pszDigestUri, /* requested URL */ - IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */ - OUT HASHHEX Response /* request-digest or response-digest */ - ); - void CvtHex( IN HASH Bin, OUT HASHHEX Hex diff --git a/src/keyvalue.c b/src/keyvalue.c index c438a43..b26588f 100644 --- a/src/keyvalue.c +++ b/src/keyvalue.c @@ -29,6 +29,7 @@ static keyvalue http_methods[] = { { HTTP_METHOD_CHECKIN, "CHECKIN" }, { HTTP_METHOD_UNCHECKOUT, "UNCHECKOUT" }, { HTTP_METHOD_VERSION_CONTROL, "VERSION-CONTROL" }, + { HTTP_METHOD_CONNECT, "CONNECT" }, { HTTP_METHOD_UNSET, NULL } }; diff --git a/src/keyvalue.h b/src/keyvalue.h index 7c78037..e1c940f 100644 --- a/src/keyvalue.h +++ b/src/keyvalue.h @@ -27,7 +27,8 @@ typedef enum { HTTP_METHOD_CHECKIN, HTTP_METHOD_VERSION_CONTROL, HTTP_METHOD_UNCHECKOUT, - HTTP_METHOD_LABEL + HTTP_METHOD_LABEL, + HTTP_METHOD_CONNECT } http_method_t; typedef enum { HTTP_VERSION_UNSET = -1, HTTP_VERSION_1_0, HTTP_VERSION_1_1 } http_version_t; diff --git a/src/mod_access.c b/src/mod_access.c index aa8d16f..f3f7071 100644 --- a/src/mod_access.c +++ b/src/mod_access.c @@ -129,11 +129,21 @@ URIHANDLER_FUNC(mod_access_uri_handler) { if (ct_len > s_len) continue; if (ds->value->used == 0) continue; - - if (0 == strncmp(con->uri.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) { - con->http_status = 403; + + /* if we have a case-insensitive FS we have to lower-case the URI here too */ + + if (con->conf.force_lowercase_filenames) { + if (0 == strncasecmp(con->uri.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) { + con->http_status = 403; - return HANDLER_FINISHED; + return HANDLER_FINISHED; + } + } else { + if (0 == strncmp(con->uri.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) { + con->http_status = 403; + + return HANDLER_FINISHED; + } } } diff --git a/src/mod_auth.c b/src/mod_auth.c index 703107c..9b791d4 100644 --- a/src/mod_auth.c +++ b/src/mod_auth.c @@ -193,11 +193,23 @@ static handler_t mod_auth_uri_handler(server *srv, connection *con, void *p_d) { /* search auth-directives for path */ for (k = 0; k < p->conf.auth_require->used; k++) { - if (p->conf.auth_require->data[k]->key->used == 0) continue; - - if (0 == strncmp(con->uri.path->ptr, p->conf.auth_require->data[k]->key->ptr, p->conf.auth_require->data[k]->key->used - 1)) { - auth_required = 1; - break; + buffer *req = p->conf.auth_require->data[k]->key; + + if (req->used == 0) continue; + if (con->uri.path->used < req->used) continue; + + /* if we have a case-insensitive FS we have to lower-case the URI here too */ + + if (con->conf.force_lowercase_filenames) { + if (0 == strncasecmp(con->uri.path->ptr, req->ptr, req->used - 1)) { + auth_required = 1; + break; + } + } else { + if (0 == strncmp(con->uri.path->ptr, req->ptr, req->used - 1)) { + auth_required = 1; + break; + } } } diff --git a/src/mod_cgi.c b/src/mod_cgi.c index d747ccf..9480032 100644 --- a/src/mod_cgi.c +++ b/src/mod_cgi.c @@ -5,7 +5,6 @@ #include <sys/socket.h> #include <sys/wait.h> #include <sys/mman.h> -#include <sys/fcntl.h> #include <netinet/in.h> @@ -22,6 +21,7 @@ #include <assert.h> #include <stdio.h> +#include <fcntl.h> #include "server.h" #include "keyvalue.h" @@ -1278,7 +1278,7 @@ int mod_cgi_plugin_init(plugin *p) { p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("cgi"); - p->handle_connection_close = cgi_connection_close_callback; + p->connection_reset = cgi_connection_close_callback; p->handle_subrequest_start = cgi_is_handled; p->handle_subrequest = mod_cgi_handle_subrequest; #if 0 diff --git a/src/mod_cml.c b/src/mod_cml.c index def16c7..0be9747 100644 --- a/src/mod_cml.c +++ b/src/mod_cml.c @@ -47,6 +47,7 @@ FREE_FUNC(mod_cml_free) { buffer_free(s->ext); buffer_free(s->mc_namespace); + buffer_free(s->power_magnet); array_free(s->mc_hosts); #if defined(HAVE_MEMCACHE_H) @@ -78,6 +79,7 @@ SETDEFAULTS_FUNC(mod_cml_set_defaults) { { "cml.extension", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ { "cml.memcache-hosts", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ { "cml.memcache-namespace", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "cml.power-magnet", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; @@ -92,6 +94,7 @@ SETDEFAULTS_FUNC(mod_cml_set_defaults) { s->ext = buffer_init(); s->mc_hosts = array_init(); s->mc_namespace = buffer_init(); + s->power_magnet = buffer_init(); #if defined(HAVE_MEMCACHE_H) s->mc = NULL; #endif @@ -99,6 +102,7 @@ SETDEFAULTS_FUNC(mod_cml_set_defaults) { cv[0].destination = s->ext; cv[1].destination = s->mc_hosts; cv[2].destination = s->mc_namespace; + cv[3].destination = s->power_magnet; p->config_storage[i] = s; @@ -144,6 +148,7 @@ static int mod_cml_patch_connection(server *srv, connection *con, plugin_data *p PATCH(mc); #endif PATCH(mc_namespace); + PATCH(power_magnet); /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { @@ -165,6 +170,8 @@ static int mod_cml_patch_connection(server *srv, connection *con, plugin_data *p #endif } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cml.memcache-namespace"))) { PATCH(mc_namespace); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cml.power-magnet"))) { + PATCH(power_magnet); } } } @@ -289,36 +296,11 @@ int cache_get_session_id(server *srv, connection *con, plugin_data *p) { } - -URIHANDLER_FUNC(mod_cml_is_handled) { - int ct_len, s_len; +int cache_call_lua(server *srv, connection *con, plugin_data *p, buffer *cml_file) { buffer *b; char *c; - buffer *fn = con->physical.path; - plugin_data *p = p_d; int ret; - - if (fn->used == 0) return HANDLER_ERROR; - - mod_cml_patch_connection(srv, con, p); - - buffer_reset(p->basedir); - buffer_reset(p->baseurl); - buffer_reset(p->session_id); - buffer_reset(p->trigger_handler); - - if (buffer_is_empty(p->conf.ext)) return HANDLER_GO_ON; - - ct_len = p->conf.ext->used - 1; - s_len = fn->used - 1; - - if (s_len < ct_len) return HANDLER_GO_ON; - - if (0 != strncmp(fn->ptr + s_len - ct_len, p->conf.ext->ptr, ct_len)) { - /* not my job */ - return HANDLER_GO_ON; - } - + /* cleanup basedir */ b = p->baseurl; buffer_copy_string_buffer(b, con->uri.path); @@ -330,7 +312,7 @@ URIHANDLER_FUNC(mod_cml_is_handled) { } b = p->basedir; - buffer_copy_string_buffer(b, fn); + buffer_copy_string_buffer(b, con->physical.path); for (c = b->ptr + b->used - 1; c > b->ptr && *c != '/'; c--); if (*c == '/') { @@ -338,6 +320,7 @@ URIHANDLER_FUNC(mod_cml_is_handled) { *(c+1) = '\0'; } + /* prepare variables * - session-id * - cookie-based @@ -346,9 +329,82 @@ URIHANDLER_FUNC(mod_cml_is_handled) { cache_get_session_id(srv, con, p); - ret = cache_parse_lua(srv, con, p, fn); + return cache_parse_lua(srv, con, p, cml_file); + +} + +URIHANDLER_FUNC(mod_cml_power_magnet) { + plugin_data *p = p_d; + + mod_cml_patch_connection(srv, con, p); + + buffer_reset(p->basedir); + buffer_reset(p->baseurl); + buffer_reset(p->session_id); + buffer_reset(p->trigger_handler); + + if (buffer_is_empty(p->conf.power_magnet)) return HANDLER_GO_ON; + + /* + * power-magnet: + * cml.power-magnet = server.docroot + "/rewrite.cml" + * + * is called on EACH request, take the original REQUEST_URI and modifies the + * request header as neccesary. + * + * First use: + * if file_exists("/maintainance.html") { + * output_include = ( "/maintainance.html" ) + * return CACHE_HIT + * } + * + * as we only want to rewrite HTML like requests we should cover it in a conditional + * + * */ + + switch(cache_call_lua(srv, con, p, p->conf.power_magnet)) { + case -1: + /* error */ + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "cache-error"); + } + con->http_status = 500; + return HANDLER_COMEBACK; + case 0: + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "cache-hit"); + } + /* cache-hit */ + buffer_reset(con->physical.path); + return HANDLER_FINISHED; + case 1: + /* cache miss */ + return HANDLER_GO_ON; + default: + con->http_status = 500; + return HANDLER_COMEBACK; + } +} + +URIHANDLER_FUNC(mod_cml_is_handled) { + plugin_data *p = p_d; + + if (buffer_is_empty(con->physical.path)) return HANDLER_ERROR; + + mod_cml_patch_connection(srv, con, p); + + buffer_reset(p->basedir); + buffer_reset(p->baseurl); + buffer_reset(p->session_id); + buffer_reset(p->trigger_handler); + + if (buffer_is_empty(p->conf.ext)) return HANDLER_GO_ON; - switch(ret) { + if (!buffer_is_equal_right_len(con->physical.path, p->conf.ext, p->conf.ext->used - 1)) { + return HANDLER_GO_ON; + } + + switch(cache_call_lua(srv, con, p, con->physical.path)) { case -1: /* error */ if (con->conf.log_request_handling) { @@ -369,9 +425,10 @@ URIHANDLER_FUNC(mod_cml_is_handled) { } /* cache miss */ return HANDLER_COMEBACK; + default: + con->http_status = 500; + return HANDLER_COMEBACK; } - - return 0; } int mod_cml_plugin_init(plugin *p) { @@ -383,6 +440,7 @@ int mod_cml_plugin_init(plugin *p) { p->set_defaults = mod_cml_set_defaults; p->handle_subrequest_start = mod_cml_is_handled; + p->handle_physical = mod_cml_power_magnet; p->data = NULL; diff --git a/src/mod_cml.h b/src/mod_cml.h index 9b09877..a2e9df4 100644 --- a/src/mod_cml.h +++ b/src/mod_cml.h @@ -22,6 +22,7 @@ typedef struct { #if defined(HAVE_MEMCACHE_H) struct memcache *mc; #endif + buffer *power_magnet; } plugin_config; typedef struct { diff --git a/src/mod_cml_lua.c b/src/mod_cml_lua.c index 5023b43..4e780e1 100644 --- a/src/mod_cml_lua.c +++ b/src/mod_cml_lua.c @@ -247,19 +247,19 @@ int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) { response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b)); } - if (!lua_to_c_is_table(L, "output_include")) { - log_error_write(srv, __FILE__, __LINE__, "s", - "output_include is missing or not a table"); - ret = -1; - - goto error; - } - if (ret == 0) { /* up to now it is a cache-hit, check if all files exist */ int curelem; time_t mtime = 0; + + if (!lua_to_c_is_table(L, "output_include")) { + log_error_write(srv, __FILE__, __LINE__, "s", + "output_include is missing or not a table"); + ret = -1; + + goto error; + } lua_pushstring(L, "output_include"); @@ -376,13 +376,7 @@ int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) { } } - if (ret == 1 && buffer_is_empty(p->trigger_handler)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "cache-miss, but not trigger_handler set"); - ret = -1; - } - - if (ret == 1) { + if (ret == 1 && !buffer_is_empty(p->trigger_handler)) { /* cache-miss */ buffer_copy_string_buffer(con->uri.path, p->baseurl); buffer_append_string_buffer(con->uri.path, p->trigger_handler); diff --git a/src/mod_dirlisting.c b/src/mod_dirlisting.c index 9b06fdb..69eb1e9 100644 --- a/src/mod_dirlisting.c +++ b/src/mod_dirlisting.c @@ -51,6 +51,9 @@ typedef struct { unsigned short dir_listing; unsigned short hide_dot_files; unsigned short show_readme; + unsigned short hide_readme_file; + unsigned short show_header; + unsigned short hide_header_file; excludes_buffer *excludes; @@ -62,6 +65,7 @@ typedef struct { PLUGIN_DATA; buffer *tmp_buf; + buffer *content_charset; plugin_config **config_storage; @@ -144,7 +148,9 @@ INIT_FUNC(mod_dirlisting_init) { plugin_data *p; p = calloc(1, sizeof(*p)); + p->tmp_buf = buffer_init(); + p->content_charset = buffer_init(); return p; } @@ -174,6 +180,7 @@ FREE_FUNC(mod_dirlisting_free) { } buffer_free(p->tmp_buf); + buffer_free(p->content_charset); free(p); @@ -228,13 +235,16 @@ SETDEFAULTS_FUNC(mod_dirlisting_set_defaults) { size_t i = 0; config_values_t cv[] = { - { "dir-listing.exclude", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ - { "dir-listing.activate", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ - { "dir-listing.hide-dotfiles", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ - { "dir-listing.external-css", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ - { "dir-listing.encoding", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ - { "dir-listing.show-readme", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ - { "server.dir-listing", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 6 */ + { "dir-listing.exclude", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "dir-listing.activate", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "dir-listing.hide-dotfiles", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "dir-listing.external-css", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { "dir-listing.encoding", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ + { "dir-listing.show-readme", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ + { "dir-listing.hide-readme-file", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 6 */ + { "dir-listing.show-header", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ + { "dir-listing.hide-header-file", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ + { "server.dir-listing", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 9 */ { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; @@ -253,6 +263,9 @@ SETDEFAULTS_FUNC(mod_dirlisting_set_defaults) { s->external_css = buffer_init(); s->hide_dot_files = 0; s->show_readme = 0; + s->hide_readme_file = 0; + s->show_header = 0; + s->hide_header_file = 0; s->encoding = buffer_init(); cv[0].destination = s->excludes; @@ -261,7 +274,10 @@ SETDEFAULTS_FUNC(mod_dirlisting_set_defaults) { cv[3].destination = s->external_css; cv[4].destination = s->encoding; cv[5].destination = &(s->show_readme); - cv[6].destination = &(s->dir_listing); /* old name */ + cv[6].destination = &(s->hide_readme_file); + cv[7].destination = &(s->show_header); + cv[8].destination = &(s->hide_header_file); + cv[9].destination = &(s->dir_listing); /* old name */ p->config_storage[i] = s; ca = ((data_config *)srv->config_context->data[i])->value; @@ -287,6 +303,9 @@ static int mod_dirlisting_patch_connection(server *srv, connection *con, plugin_ PATCH(hide_dot_files); PATCH(encoding); PATCH(show_readme); + PATCH(hide_readme_file); + PATCH(show_header); + PATCH(hide_header_file); PATCH(excludes); /* skip the first, the global context */ @@ -312,6 +331,12 @@ static int mod_dirlisting_patch_connection(server *srv, connection *con, plugin_ PATCH(encoding); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("dir-listing.show-readme"))) { PATCH(show_readme); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("dir-listing.hide-readme-file"))) { + PATCH(hide_readme_file); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("dir-listing.show-header"))) { + PATCH(show_header); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("dir-listing.hide-header-file"))) { + PATCH(hide_header_file); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("dir-listing.excludes"))) { PATCH(excludes); } @@ -414,7 +439,7 @@ static void http_list_directory_header(server *srv, connection *con, plugin_data "<head>\n" "<title>Index of " ); - buffer_append_string_encoded(out, CONST_BUF_LEN(con->uri.path), ENCODING_HTML); + buffer_append_string_encoded(out, CONST_BUF_LEN(con->uri.path), ENCODING_MINIMAL_XML); BUFFER_APPEND_STRING_CONST(out, "</title>\n"); if (p->conf.external_css->used > 1) { @@ -461,8 +486,27 @@ static void http_list_directory_header(server *srv, connection *con, plugin_data ); } - BUFFER_APPEND_STRING_CONST(out, "</head>\n<body>\n<h2>Index of "); - buffer_append_string_encoded(out, CONST_BUF_LEN(con->uri.path), ENCODING_HTML); + BUFFER_APPEND_STRING_CONST(out, "</head>\n<body>\n"); + + /* HEADER.txt */ + if (p->conf.show_header) { + stream s; + /* if we have a HEADER file, display it in <pre class="header"></pre> */ + + buffer_copy_string_buffer(p->tmp_buf, con->physical.path); + BUFFER_APPEND_SLASH(p->tmp_buf); + BUFFER_APPEND_STRING_CONST(p->tmp_buf, "HEADER.txt"); + + if (-1 != stream_open(&s, p->tmp_buf)) { + BUFFER_APPEND_STRING_CONST(out, "<pre class=\"header\">"); + buffer_append_string_encoded(out, s.start, s.size, ENCODING_MINIMAL_XML); + BUFFER_APPEND_STRING_CONST(out, "</pre>"); + } + stream_close(&s); + } + + BUFFER_APPEND_STRING_CONST(out, "<h2>Index of "); + buffer_append_string_encoded(out, CONST_BUF_LEN(con->uri.path), ENCODING_MINIMAL_XML); BUFFER_APPEND_STRING_CONST(out, "</h2>\n" "<div class=\"list\">\n" @@ -504,7 +548,7 @@ static void http_list_directory_footer(server *srv, connection *con, plugin_data if (-1 != stream_open(&s, p->tmp_buf)) { BUFFER_APPEND_STRING_CONST(out, "<pre class=\"readme\">"); - buffer_append_string_encoded(out, s.start, s.size, ENCODING_HTML); + buffer_append_string_encoded(out, s.start, s.size, ENCODING_MINIMAL_XML); BUFFER_APPEND_STRING_CONST(out, "</pre>"); } stream_close(&s); @@ -602,6 +646,15 @@ static int http_list_directory(server *srv, connection *con, plugin_data *p, buf continue; } + if (p->conf.hide_readme_file) { + if (strcmp(dent->d_name, "README.txt") == 0) + continue; + } + if (p->conf.hide_header_file) { + if (strcmp(dent->d_name, "HEADER.txt") == 0) + continue; + } + /* compare d_name against excludes array * elements, skipping any that match. */ @@ -691,7 +744,7 @@ static int http_list_directory(server *srv, connection *con, plugin_data *p, buf BUFFER_APPEND_STRING_CONST(out, "<tr><td class=\"n\"><a href=\""); buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_REL_URI_PART); BUFFER_APPEND_STRING_CONST(out, "/\">"); - buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_HTML); + buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML); BUFFER_APPEND_STRING_CONST(out, "</a>/</td><td class=\"m\">"); buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1); BUFFER_APPEND_STRING_CONST(out, "</td><td class=\"s\">- </td><td class=\"t\">Directory</td></tr>\n"); @@ -747,7 +800,7 @@ static int http_list_directory(server *srv, connection *con, plugin_data *p, buf BUFFER_APPEND_STRING_CONST(out, "<tr><td class=\"n\"><a href=\""); buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_REL_URI_PART); BUFFER_APPEND_STRING_CONST(out, "\">"); - buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_HTML); + buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML); BUFFER_APPEND_STRING_CONST(out, "</a></td><td class=\"m\">"); buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1); BUFFER_APPEND_STRING_CONST(out, "</td><td class=\"s\">"); @@ -764,7 +817,16 @@ static int http_list_directory(server *srv, connection *con, plugin_data *p, buf free(path); http_list_directory_footer(srv, con, p, out); - response_header_insert(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); + + /* Insert possible charset to Content-Type */ + if (buffer_is_empty(p->conf.encoding)) { + response_header_insert(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); + } else { + buffer_copy_string(p->content_charset, "text/html; charset="); + buffer_append_string_buffer(p->content_charset, p->conf.encoding); + response_header_insert(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->content_charset)); + } + con->file_finished = 1; return 0; diff --git a/src/mod_evasive.c b/src/mod_evasive.c new file mode 100644 index 0000000..b9d19ca --- /dev/null +++ b/src/mod_evasive.c @@ -0,0 +1,178 @@ +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +#include "base.h" +#include "log.h" +#include "buffer.h" + +#include "plugin.h" + +#include "inet_ntop_cache.h" + +/** + * mod_evasive + * + * we indent to implement all features the mod_evasive from apache has + * + * - limit of connections per IP + * - provide a list of block-listed ip/networks (no access) + * - provide a white-list of ips/network which is not affected by the limit + * (hmm, conditionals might be enough) + * - provide a bandwidth limiter per IP + * + * started by: + * - w1zzard@techpowerup.com + */ + +typedef struct { + unsigned short max_conns; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +INIT_FUNC(mod_evasive_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + return p; +} + +FREE_FUNC(mod_evasive_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + free(s); + } + free(p->config_storage); + } + + free(p); + + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_evasive_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "evasive.max-conns-per-ip", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->max_conns = 0; + + cv[0].destination = &(s->max_conns); + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_evasive_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(max_conns); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.max-conns-per-ip"))) { + PATCH(max_conns); + } + } + } + + return 0; +} +#undef PATCH + +URIHANDLER_FUNC(mod_evasive_uri_handler) { + plugin_data *p = p_d; + size_t conns_by_ip = 0; + size_t j; + + if (con->uri.path->used == 0) return HANDLER_GO_ON; + + mod_evasive_patch_connection(srv, con, p); + + /* no limit set, nothing to block */ + if (p->conf.max_conns == 0) return HANDLER_GO_ON; + + for (j = 0; j < srv->conns->used; j++) { + connection *c = srv->conns->ptr[j]; + + /* check if other connections are already actively serving data for the same IP + * we can only ban connections which are already behind the 'read request' state + * */ + if (c->dst_addr.ipv4.sin_addr.s_addr == con->dst_addr.ipv4.sin_addr.s_addr && + c->state > CON_STATE_REQUEST_END) { + conns_by_ip++; + + if (conns_by_ip > p->conf.max_conns) { + log_error_write(srv, __FILE__, __LINE__, "ss", + inet_ntop_cache_get_ip(srv, &(con->dst_addr)), + "turned away. Too many connections."); + + con->http_status = 403; + return HANDLER_FINISHED; + } + } + } + + return HANDLER_GO_ON; +} + + +int mod_evasive_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("evasive"); + + p->init = mod_evasive_init; + p->set_defaults = mod_evasive_set_defaults; + p->handle_uri_clean = mod_evasive_uri_handler; + p->cleanup = mod_evasive_free; + + p->data = NULL; + + return 0; +} diff --git a/src/mod_fastcgi.c b/src/mod_fastcgi.c index 890062c..8b1be0a 100644 --- a/src/mod_fastcgi.c +++ b/src/mod_fastcgi.c @@ -69,20 +69,24 @@ typedef struct fcgi_proc { size_t requests; /* see max_requests */ struct fcgi_proc *prev, *next; /* see first */ - time_t disable_ts; /* replace by host->something */ + time_t disabled_until; /* this proc is disabled until, use something else until than */ int is_local; - enum { PROC_STATE_UNSET, /* init-phase */ - PROC_STATE_RUNNING, /* alive */ - PROC_STATE_DIED_WAIT_FOR_PID, - PROC_STATE_KILLED, /* was killed as we don't have the load anymore */ - PROC_STATE_DIED, /* marked as dead, should be restarted */ - PROC_STATE_DISABLED /* proc disabled as it resulted in an error */ + enum { + PROC_STATE_UNSET, /* init-phase */ + PROC_STATE_RUNNING, /* alive */ + PROC_STATE_DIED_WAIT_FOR_PID, + PROC_STATE_KILLED, /* was killed as we don't have the load anymore */ + PROC_STATE_DIED, /* marked as dead, should be restarted */ + PROC_STATE_DISABLED /* proc disabled as it resulted in an error */ } state; } fcgi_proc; typedef struct { + /* the key that is used to reference this value */ + buffer *id; + /* list of processes handling this extension * sorted by lowest load * @@ -300,6 +304,8 @@ typedef struct { buffer *path; buffer *parse_response; + + buffer *statuskey; plugin_config **config_storage; @@ -307,13 +313,19 @@ typedef struct { } plugin_data; /* connection specific data */ -typedef enum { FCGI_STATE_INIT, FCGI_STATE_CONNECT, FCGI_STATE_PREPARE_WRITE, - FCGI_STATE_WRITE, FCGI_STATE_READ +typedef enum { + FCGI_STATE_UNSET, + FCGI_STATE_INIT, + FCGI_STATE_CONNECT_DELAYED, + FCGI_STATE_PREPARE_WRITE, + FCGI_STATE_WRITE, + FCGI_STATE_READ } fcgi_connection_state_t; typedef struct { fcgi_proc *proc; fcgi_extension_host *host; + fcgi_extension *ext; fcgi_connection_state_t state; time_t state_timestamp; @@ -325,8 +337,6 @@ typedef struct { buffer *response_header; - int delayed; /* flag to mark that the connect() is delayed */ - size_t request_id; int fd; /* fd to the fastcgi process */ int fde_ndx; /* index into the fd-event buffer */ @@ -348,7 +358,74 @@ static handler_t fcgi_handle_fdevent(void *s, void *ctx, int revents); int fcgi_proclist_sort_down(server *srv, fcgi_extension_host *host, fcgi_proc *proc); +data_integer *status_counter_get_counter(server *srv, const char *s, size_t len) { + data_integer *di; + + if (NULL == (di = (data_integer *)array_get_element(srv->status, s))) { + /* not found, create it */ + + if (NULL == (di = (data_integer *)array_get_unused_element(srv->status, TYPE_INTEGER))) { + di = data_integer_init(); + } + buffer_copy_string_len(di->key, s, len); + di->value = 0; + + array_insert_unique(srv->status, (data_unset *)di); + } + return di; +} + +/* dummies of the statistic framework functions + * they will be moved to a statistics.c later */ +int status_counter_inc(server *srv, const char *s, size_t len) { + data_integer *di = status_counter_get_counter(srv, s, len); + + di->value++; + + return 0; +} + +int status_counter_dec(server *srv, const char *s, size_t len) { + data_integer *di = status_counter_get_counter(srv, s, len); + + if (di->value > 0) di->value--; + + return 0; +} + +int status_counter_set(server *srv, const char *s, size_t len, int val) { + data_integer *di = status_counter_get_counter(srv, s, len); + + di->value = val; + return 0; +} + +int fastcgi_status_copy_procname(buffer *b, fcgi_extension_host *host, fcgi_proc *proc) { + buffer_copy_string(b, "fastcgi.backend."); + buffer_append_string_buffer(b, host->id); + buffer_append_string(b, "."); + buffer_append_long(b, proc->id); + + return 0; +} + +int fastcgi_status_init(server *srv, buffer *b, fcgi_extension_host *host, fcgi_proc *proc) { +#define CLEAN(x) \ + fastcgi_status_copy_procname(b, host, proc); \ + buffer_append_string(b, x); \ + status_counter_set(srv, CONST_BUF_LEN(b), 0); + + CLEAN(".disabled"); + CLEAN(".died"); + CLEAN(".overloaded"); + CLEAN(".connected"); + CLEAN(".load"); + +#undef CLEAN + + return 0; +} static handler_ctx * handler_ctx_init() { handler_ctx * hctx; @@ -366,8 +443,6 @@ static handler_ctx * handler_ctx_init() { hctx->fd = -1; - hctx->delayed = 0; - hctx->reconnects = 0; hctx->send_content_body = 1; @@ -413,6 +488,7 @@ fcgi_extension_host *fastcgi_host_init() { f = calloc(1, sizeof(*f)); + f->id = buffer_init(); f->host = buffer_init(); f->unixsocket = buffer_init(); f->docroot = buffer_init(); @@ -427,6 +503,7 @@ fcgi_extension_host *fastcgi_host_init() { void fastcgi_host_free(fcgi_extension_host *h) { if (!h) return; + buffer_free(h->id); buffer_free(h->host); buffer_free(h->unixsocket); buffer_free(h->docroot); @@ -540,6 +617,8 @@ INIT_FUNC(mod_fastcgi_init) { p->path = buffer_init(); p->parse_response = buffer_init(); + + p->statuskey = buffer_init(); return p; } @@ -556,6 +635,7 @@ FREE_FUNC(mod_fastcgi_free) { buffer_free(p->fcgi_env); buffer_free(p->path); buffer_free(p->parse_response); + buffer_free(p->statuskey); if (p->config_storage) { size_t i, j, n; @@ -824,6 +904,7 @@ static int fcgi_spawn_connection(server *srv, switch ((child = fork())) { case 0: { size_t i = 0; + char *c; char_array env; char_array arg; @@ -888,6 +969,20 @@ static int fcgi_spawn_connection(server *srv, parse_binpath(&arg, host->bin_path); + /* chdir into the base of the bin-path, + * search for the last / */ + if (NULL != (c = strrchr(arg.ptr[0], '/'))) { + *c = '\0'; + + /* change to the physical directory */ + if (-1 == chdir(arg.ptr[0])) { + *c = '/'; + log_error_write(srv, __FILE__, __LINE__, "sss", "chdir failed:", strerror(errno), arg.ptr[0]); + } + *c = '/'; + } + + /* exec the cgi */ execve(arg.ptr[0], arg.ptr, env.ptr); @@ -1058,7 +1153,7 @@ SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) { for (n = 0; n < da_ext->value->used; n++) { data_array *da_host = (data_array *)da_ext->value->data[n]; - fcgi_extension_host *df; + fcgi_extension_host *host; config_values_t fcv[] = { { "host", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ @@ -1094,54 +1189,56 @@ SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) { return HANDLER_ERROR; } - df = fastcgi_host_init(); + host = fastcgi_host_init(); - df->check_local = 1; - df->min_procs = 4; - df->max_procs = 4; - df->max_load_per_proc = 1; - df->idle_timeout = 60; - df->mode = FCGI_RESPONDER; - df->disable_time = 60; - df->break_scriptfilename_for_php = 0; - df->allow_xsendfile = 0; /* handle X-LIGHTTPD-send-file */ + buffer_copy_string_buffer(host->id, da_host->key); + + host->check_local = 1; + host->min_procs = 4; + host->max_procs = 4; + host->max_load_per_proc = 1; + host->idle_timeout = 60; + host->mode = FCGI_RESPONDER; + host->disable_time = 60; + host->break_scriptfilename_for_php = 0; + host->allow_xsendfile = 0; /* handle X-LIGHTTPD-send-file */ - fcv[0].destination = df->host; - fcv[1].destination = df->docroot; + fcv[0].destination = host->host; + fcv[1].destination = host->docroot; fcv[2].destination = fcgi_mode; - fcv[3].destination = df->unixsocket; - fcv[4].destination = df->bin_path; + fcv[3].destination = host->unixsocket; + fcv[4].destination = host->bin_path; - fcv[5].destination = &(df->check_local); - fcv[6].destination = &(df->port); - fcv[7].destination = &(df->min_procs); - fcv[8].destination = &(df->max_procs); - fcv[9].destination = &(df->max_load_per_proc); - fcv[10].destination = &(df->idle_timeout); - fcv[11].destination = &(df->disable_time); + fcv[5].destination = &(host->check_local); + fcv[6].destination = &(host->port); + fcv[7].destination = &(host->min_procs); + fcv[8].destination = &(host->max_procs); + fcv[9].destination = &(host->max_load_per_proc); + fcv[10].destination = &(host->idle_timeout); + fcv[11].destination = &(host->disable_time); - fcv[12].destination = df->bin_env; - fcv[13].destination = df->bin_env_copy; - fcv[14].destination = &(df->break_scriptfilename_for_php); - fcv[15].destination = &(df->allow_xsendfile); - fcv[16].destination = df->strip_request_uri; + fcv[12].destination = host->bin_env; + fcv[13].destination = host->bin_env_copy; + fcv[14].destination = &(host->break_scriptfilename_for_php); + fcv[15].destination = &(host->allow_xsendfile); + fcv[16].destination = host->strip_request_uri; if (0 != config_insert_values_internal(srv, da_host->value, fcv)) { return HANDLER_ERROR; } - if ((!buffer_is_empty(df->host) || df->port) && - !buffer_is_empty(df->unixsocket)) { + if ((!buffer_is_empty(host->host) || host->port) && + !buffer_is_empty(host->unixsocket)) { log_error_write(srv, __FILE__, __LINE__, "s", "either host+port or socket"); return HANDLER_ERROR; } - if (!buffer_is_empty(df->unixsocket)) { + if (!buffer_is_empty(host->unixsocket)) { /* unix domain socket */ - if (df->unixsocket->used > UNIX_PATH_MAX - 2) { + if (host->unixsocket->used > UNIX_PATH_MAX - 2) { log_error_write(srv, __FILE__, __LINE__, "s", "path of the unixdomain socket is too large"); return HANDLER_ERROR; @@ -1149,8 +1246,8 @@ SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) { } else { /* tcp/ip */ - if (buffer_is_empty(df->host) && - buffer_is_empty(df->bin_path)) { + if (buffer_is_empty(host->host) && + buffer_is_empty(host->bin_path)) { log_error_write(srv, __FILE__, __LINE__, "sbbbs", "missing key (string):", da->key, @@ -1159,7 +1256,7 @@ SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) { "host"); return HANDLER_ERROR; - } else if (df->port == 0) { + } else if (host->port == 0) { log_error_write(srv, __FILE__, __LINE__, "sbbbs", "missing key (short):", da->key, @@ -1170,37 +1267,37 @@ SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) { } } - if (!buffer_is_empty(df->bin_path)) { + if (!buffer_is_empty(host->bin_path)) { /* a local socket + self spawning */ size_t pno; /* HACK: just to make sure the adaptive spawing is disabled */ - df->min_procs = df->max_procs; + host->min_procs = host->max_procs; - if (df->min_procs > df->max_procs) df->max_procs = df->min_procs; - if (df->max_load_per_proc < 1) df->max_load_per_proc = 0; + if (host->min_procs > host->max_procs) host->max_procs = host->min_procs; + if (host->max_load_per_proc < 1) host->max_load_per_proc = 0; if (s->debug) { log_error_write(srv, __FILE__, __LINE__, "ssbsdsbsdsd", "--- fastcgi spawning local", - "\n\tproc:", df->bin_path, - "\n\tport:", df->port, - "\n\tsocket", df->unixsocket, - "\n\tmin-procs:", df->min_procs, - "\n\tmax-procs:", df->max_procs); + "\n\tproc:", host->bin_path, + "\n\tport:", host->port, + "\n\tsocket", host->unixsocket, + "\n\tmin-procs:", host->min_procs, + "\n\tmax-procs:", host->max_procs); } - for (pno = 0; pno < df->min_procs; pno++) { + for (pno = 0; pno < host->min_procs; pno++) { fcgi_proc *proc; proc = fastcgi_process_init(); - proc->id = df->num_procs++; - df->max_id++; + proc->id = host->num_procs++; + host->max_id++; - if (buffer_is_empty(df->unixsocket)) { - proc->port = df->port + pno; + if (buffer_is_empty(host->unixsocket)) { + proc->port = host->port + pno; } else { - buffer_copy_string_buffer(proc->socket, df->unixsocket); + buffer_copy_string_buffer(proc->socket, host->unixsocket); buffer_append_string(proc->socket, "-"); buffer_append_long(proc->socket, pno); } @@ -1208,49 +1305,53 @@ SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) { if (s->debug) { log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd", "--- fastcgi spawning", - "\n\tport:", df->port, - "\n\tsocket", df->unixsocket, - "\n\tcurrent:", pno, "/", df->min_procs); + "\n\tport:", host->port, + "\n\tsocket", host->unixsocket, + "\n\tcurrent:", pno, "/", host->min_procs); } - if (fcgi_spawn_connection(srv, p, df, proc)) { + if (fcgi_spawn_connection(srv, p, host, proc)) { log_error_write(srv, __FILE__, __LINE__, "s", "[ERROR]: spawning fcgi failed."); return HANDLER_ERROR; } + + fastcgi_status_init(srv, p->statuskey, host, proc); - proc->next = df->first; - if (df->first) df->first->prev = proc; + proc->next = host->first; + if (host->first) host->first->prev = proc; - df->first = proc; + host->first = proc; } } else { - fcgi_proc *fp; + fcgi_proc *proc; - fp = fastcgi_process_init(); - fp->id = df->num_procs++; - df->max_id++; - df->active_procs++; - fp->state = PROC_STATE_RUNNING; + proc = fastcgi_process_init(); + proc->id = host->num_procs++; + host->max_id++; + host->active_procs++; + proc->state = PROC_STATE_RUNNING; - if (buffer_is_empty(df->unixsocket)) { - fp->port = df->port; + if (buffer_is_empty(host->unixsocket)) { + proc->port = host->port; } else { - buffer_copy_string_buffer(fp->socket, df->unixsocket); + buffer_copy_string_buffer(proc->socket, host->unixsocket); } - df->first = fp; + fastcgi_status_init(srv, p->statuskey, host, proc); + + host->first = proc; - df->min_procs = 1; - df->max_procs = 1; + host->min_procs = 1; + host->max_procs = 1; } if (!buffer_is_empty(fcgi_mode)) { if (strcmp(fcgi_mode->ptr, "responder") == 0) { - df->mode = FCGI_RESPONDER; + host->mode = FCGI_RESPONDER; } else if (strcmp(fcgi_mode->ptr, "authorizer") == 0) { - df->mode = FCGI_AUTHORIZER; - if (buffer_is_empty(df->docroot)) { + host->mode = FCGI_AUTHORIZER; + if (buffer_is_empty(host->docroot)) { log_error_write(srv, __FILE__, __LINE__, "s", "ERROR: docroot is required for authorizer mode."); return HANDLER_ERROR; @@ -1261,9 +1362,9 @@ SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) { fcgi_mode, "(ignored, mode set to responder)"); } } - + /* if extension already exists, take it */ - fastcgi_extension_insert(s->exts, da_ext->key, df); + fastcgi_extension_insert(s->exts, da_ext->key, host); } } } @@ -1327,7 +1428,6 @@ static int fcgi_requestid_del(server *srv, plugin_data *p, size_t request_id) { return 0; } - void fcgi_connection_close(server *srv, handler_ctx *hctx) { plugin_data *p; connection *con; @@ -1360,6 +1460,13 @@ void fcgi_connection_close(server *srv, handler_ctx *hctx) { /* after the connect the process gets a load */ hctx->proc->load--; + status_counter_dec(srv, CONST_STR_LEN("fastcgi.active-requests")); + + fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc); + buffer_append_string(p->statuskey, ".load"); + + status_counter_set(srv, CONST_BUF_LEN(p->statuskey), hctx->proc->load); + if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "sddb", "release proc:", @@ -1397,11 +1504,13 @@ static int fcgi_reconnect(server *srv, handler_ctx *hctx) { * we have a connection but the child died by some other reason * */ - - fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); - fdevent_unregister(srv->ev, hctx->fd); - close(hctx->fd); - srv->cur_fds--; + + if (hctx->fd != -1) { + fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); + fdevent_unregister(srv->ev, hctx->fd); + close(hctx->fd); + srv->cur_fds--; + } fcgi_requestid_del(srv, p, hctx->request_id); @@ -1416,9 +1525,14 @@ static int fcgi_reconnect(server *srv, handler_ctx *hctx) { hctx->fd, hctx->proc->pid, hctx->proc->socket); } - - hctx->proc->load--; - fcgi_proclist_sort_down(srv, hctx->host, hctx->proc); + + if (hctx->proc) { + hctx->proc->load--; + fcgi_proclist_sort_down(srv, hctx->host, hctx->proc); + } + + /* perhaps another host gives us more luck */ + hctx->host = NULL; return 0; } @@ -1491,7 +1605,15 @@ static int fcgi_header(FCGI_Header * header, unsigned char type, size_t request_ * 1 not connected yet */ -static int fcgi_establish_connection(server *srv, handler_ctx *hctx) { +typedef enum { + CONNECTION_UNSET, + CONNECTION_OK, + CONNECTION_DELAYED, /* retry after event, take same host */ + CONNECTION_OVERLOADED, /* disable for 1 seconds, take another backend */ + CONNECTION_DEAD /* disable for 60 seconds, take another backend */ +} connection_result_t; + +static connection_result_t fcgi_establish_connection(server *srv, handler_ctx *hctx) { struct sockaddr *fcgi_addr; struct sockaddr_in fcgi_addr_in; #ifdef HAVE_SYS_UN_H @@ -1540,57 +1662,37 @@ static int fcgi_establish_connection(server *srv, handler_ctx *hctx) { errno == EALREADY || errno == EINTR) { if (hctx->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "sd", - "connect delayed, will continue later:", fcgi_fd); + log_error_write(srv, __FILE__, __LINE__, "sb", + "connect delayed, will continue later:", host->host); } - return 1; + return CONNECTION_DELAYED; } else if (errno == EAGAIN) { -#if 0 - if(hctx->delayed == 0) { - log_error_write(srv, __FILE__, __LINE__, "sdsdsdb", - "need reconnect, will continue later:", fcgi_fd, - "reconnects:", hctx->reconnects, - "load:", host->load, - host->unixsocket); + if (hctx->conf.debug) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "This means that the you have more incoming requests than your fastcgi-backend can handle in parallel. " + "Perhaps it helps to spawn more fastcgi backend or php-children, if not decrease server.max-connections." + "The load for this fastcgi backend is:", proc->load); } -#endif - hctx->reconnects++; - return -1; + + return CONNECTION_OVERLOADED; } else { - log_error_write(srv, __FILE__, __LINE__, "sdsddb", - "connect failed:", fcgi_fd, - strerror(errno), errno, + log_error_write(srv, __FILE__, __LINE__, "ssdb", + "connect failed:", + strerror(errno), proc->port, proc->socket); -#if 0 - if (errno == EAGAIN) { - log_error_write(srv, __FILE__, __LINE__, "sd", - "This means that the you have more incoming requests than your fastcgi-backend can handle in parallel. " - "Perhaps it helps to spawn more fastcgi backend or php-children, if not decrease server.max-connections." - "The load for this fastcgi backend is:", proc->load); - } -#endif - - return -1; + return CONNECTION_DEAD; } } -#if 0 - if(hctx->delayed == 1) { - log_error_write(srv, __FILE__, __LINE__, "sdsdsdb", - "reconnected:", fcgi_fd, - "reconnects:", hctx->reconnects, - "load:", host->load, - host->unixsocket); - } -#endif + hctx->reconnects = 0; if (hctx->conf.debug > 1) { log_error_write(srv, __FILE__, __LINE__, "sd", "connect succeeded: ", fcgi_fd); } - return 0; + return CONNECTION_OK; } static int fcgi_env_add_request_headers(server *srv, connection *con, plugin_data *p) { @@ -1639,9 +1741,15 @@ static int fcgi_env_add_request_headers(server *srv, connection *con, plugin_dat buffer_prepare_append(srv->tmp_buf, ds->key->used + 2); for (j = 0; j < ds->key->used - 1; j++) { - srv->tmp_buf->ptr[srv->tmp_buf->used++] = - isalpha((unsigned char)ds->key->ptr[j]) ? - toupper((unsigned char)ds->key->ptr[j]) : '_'; + char c = '_'; + if (light_isalpha(ds->key->ptr[j])) { + /* upper-case */ + c = ds->key->ptr[j] & ~32; + } else if (light_isdigit(ds->key->ptr[j])) { + /* copy */ + c = ds->key->ptr[j]; + } + srv->tmp_buf->ptr[srv->tmp_buf->used++] = c; } srv->tmp_buf->ptr[srv->tmp_buf->used++] = '\0'; @@ -2156,7 +2264,7 @@ static int fastcgi_get_packet(server *srv, handler_ctx *hctx, fastcgi_response_p /* no header */ buffer_free(packet->b); - log_error_write(srv, __FILE__, __LINE__, "s", "FastCGI: header to small"); + log_error_write(srv, __FILE__, __LINE__, "s", "FastCGI: header too small"); return -1; } @@ -2260,11 +2368,12 @@ static int fcgi_demux_response(server *srv, handler_ctx *hctx) { b->used = r + 1; /* one extra for the fake \0 */ b->ptr[b->used - 1] = '\0'; } else { - log_error_write(srv, __FILE__, __LINE__, "ssdsdsd", + log_error_write(srv, __FILE__, __LINE__, "ssdsbsbsd", "unexpected end-of-file (perhaps the fastcgi process died):", "pid:", proc->pid, - "fcgi-fd:", fcgi_fd, - "remote-fd:", con->fd); + "socket:", proc->socket, + "host:", host->host, + "port:", proc->port); return -1; } @@ -2279,9 +2388,6 @@ static int fcgi_demux_response(server *srv, handler_ctx *hctx) { /* check if we have at least one packet */ if (0 != fastcgi_get_packet(srv, hctx, &packet)) { /* no full packet */ - - hctx->delayed = 1; - break; } @@ -2546,7 +2652,7 @@ static int fcgi_restart_dead_procs(server *srv, plugin_data *p, fcgi_extension_h proc->pid); } - if (0 == proc->is_local) { + if (!proc->is_local) { /* * external servers might get disabled * @@ -2554,10 +2660,15 @@ static int fcgi_restart_dead_procs(server *srv, plugin_data *p, fcgi_extension_h */ if ((proc->state == PROC_STATE_DISABLED) && - (srv->cur_ts - proc->disable_ts > host->disable_time)) { + (srv->cur_ts > proc->disabled_until)) { proc->state = PROC_STATE_RUNNING; host->active_procs++; + fastcgi_status_copy_procname(p->statuskey, host, proc); + buffer_append_string(p->statuskey, ".disabled"); + + status_counter_set(srv, CONST_BUF_LEN(p->statuskey), 0); + log_error_write(srv, __FILE__, __LINE__, "sbdb", "fcgi-server re-enabled:", host->host, host->port, @@ -2626,7 +2737,6 @@ static int fcgi_restart_dead_procs(server *srv, plugin_data *p, fcgi_extension_h return 0; } - static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) { plugin_data *p = hctx->plugin_data; fcgi_extension_host *host= hctx->host; @@ -2645,10 +2755,60 @@ static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) { host->unixsocket->used); return HANDLER_ERROR; } + + /* we can't handle this in the switch as we have to fall through in it */ + if (hctx->state == FCGI_STATE_CONNECT_DELAYED) { + int socket_error; + socklen_t socket_error_len = sizeof(socket_error); + + /* try to finish the connect() */ + if (0 != getsockopt(hctx->fd, SOL_SOCKET, SO_ERROR, &socket_error, &socket_error_len)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "getsockopt failed:", strerror(errno)); + + return HANDLER_ERROR; + } + if (socket_error != 0) { + if (!hctx->proc->is_local || p->conf.debug) { + /* local procs get restarted */ + + log_error_write(srv, __FILE__, __LINE__, "sssd", + "establishing connection failed:", strerror(socket_error), + "port:", hctx->proc->port); + } + hctx->proc->disabled_until = srv->cur_ts + 10; + + fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc); + buffer_append_string(p->statuskey, ".died"); + + status_counter_inc(srv, CONST_BUF_LEN(p->statuskey)); + + return HANDLER_ERROR; + } + /* go on with preparing the request */ + hctx->state = FCGI_STATE_PREPARE_WRITE; + } + switch(hctx->state) { + case FCGI_STATE_CONNECT_DELAYED: + /* should never happen */ + break; case FCGI_STATE_INIT: + /* do we have a running process for this host (max-procs) ? */ + + for (hctx->proc = hctx->host->first; + hctx->proc && hctx->proc->state != PROC_STATE_RUNNING; + hctx->proc = hctx->proc->next); + + /* all childs are dead */ + if (hctx->proc == NULL) { + hctx->fde_ndx = -1; + + return HANDLER_ERROR; + } + ret = host->unixsocket->used ? AF_UNIX : AF_INET; if (-1 == (hctx->fd = socket(ret, SOCK_STREAM, 0))) { @@ -2672,80 +2832,88 @@ static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) { if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) { log_error_write(srv, __FILE__, __LINE__, "ss", - "fcntl failed: ", strerror(errno)); + "fcntl failed:", strerror(errno)); return HANDLER_ERROR; } - - /* fall through */ - case FCGI_STATE_CONNECT: - if (hctx->state == FCGI_STATE_INIT || hctx->delayed == 1) { - for (hctx->proc = hctx->host->first; - hctx->proc && hctx->proc->state != PROC_STATE_RUNNING; - hctx->proc = hctx->proc->next); - - /* all childs are dead */ - if (hctx->proc == NULL) { - hctx->fde_ndx = -1; - - return HANDLER_ERROR; - } - if (hctx->proc->is_local) { - hctx->pid = hctx->proc->pid; - } + if (hctx->proc->is_local) { + hctx->pid = hctx->proc->pid; + } - switch (fcgi_establish_connection(srv, hctx)) { - case 1: - fcgi_set_state(srv, hctx, FCGI_STATE_CONNECT); - - /* connection is in progress, wait for an event and call getsockopt() below */ - - fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); - - hctx->delayed = 0; - return HANDLER_WAIT_FOR_EVENT; - case -1: - /* if ECONNREFUSED/EAGAIN re-try connect() */ - - fcgi_set_state(srv, hctx, FCGI_STATE_CONNECT); - hctx->delayed = 1; - return HANDLER_WAIT_FOR_EVENT; - default: - /* everything is ok, go on */ - break; - } + switch (fcgi_establish_connection(srv, hctx)) { + case CONNECTION_DELAYED: + /* connection is in progress, wait for an event and call getsockopt() below */ + + fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); + + fcgi_set_state(srv, hctx, FCGI_STATE_CONNECT_DELAYED); + return HANDLER_WAIT_FOR_EVENT; + case CONNECTION_OVERLOADED: + /* cool down the backend, it is overloaded + * -> EAGAIN */ - } else { - int socket_error; - socklen_t socket_error_len = sizeof(socket_error); + log_error_write(srv, __FILE__, __LINE__, "ssdsdb", + "backend is overloaded, we disable it for a 2 seconds and send the request to another backend instead:" + "reconnects:", hctx->reconnects, + "load:", host->load, + host->unixsocket); + + + hctx->proc->disabled_until = srv->cur_ts + 2; + + fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc); + buffer_append_string(p->statuskey, ".overloaded"); + + status_counter_inc(srv, CONST_BUF_LEN(p->statuskey)); - /* try to finish the connect() */ - if (0 != getsockopt(hctx->fd, SOL_SOCKET, SO_ERROR, &socket_error, &socket_error_len)) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "getsockopt failed:", strerror(errno)); - - return HANDLER_ERROR; - } - if (socket_error != 0) { - if (!hctx->proc->is_local || p->conf.debug) { - /* local procs get restarted */ - - log_error_write(srv, __FILE__, __LINE__, "ss", - "establishing connection failed:", strerror(socket_error), - "port:", hctx->proc->port); - } - - return HANDLER_ERROR; - } + return HANDLER_ERROR; + case CONNECTION_DEAD: + /* we got a hard error from the backend like + * - ECONNREFUSED for tcp-ip sockets + * - ENOENT for unix-domain-sockets + * + * for check if the host is back in 10 seconds + * */ + + hctx->proc->disabled_until = srv->cur_ts + 10; + + fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc); + buffer_append_string(p->statuskey, ".died"); + + status_counter_inc(srv, CONST_BUF_LEN(p->statuskey)); + + return HANDLER_ERROR; + case CONNECTION_OK: + /* everything is ok, go on */ + + fcgi_set_state(srv, hctx, FCGI_STATE_PREPARE_WRITE); + + break; + case CONNECTION_UNSET: + break; } + case FCGI_STATE_PREPARE_WRITE: /* ok, we have the connection */ hctx->proc->load++; hctx->proc->last_used = srv->cur_ts; hctx->got_proc = 1; - + + status_counter_inc(srv, CONST_STR_LEN("fastcgi.requests")); + status_counter_inc(srv, CONST_STR_LEN("fastcgi.active-requests")); + + fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc); + buffer_append_string(p->statuskey, ".connected"); + + status_counter_inc(srv, CONST_BUF_LEN(p->statuskey)); + + fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc); + buffer_append_string(p->statuskey, ".load"); + + status_counter_set(srv, CONST_BUF_LEN(p->statuskey), hctx->proc->load); + if (p->conf.debug) { log_error_write(srv, __FILE__, __LINE__, "sddbdd", "got proc:", @@ -2766,9 +2934,7 @@ static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) { "fcgi-request is already in use:", hctx->request_id); } - fcgi_set_state(srv, hctx, FCGI_STATE_PREPARE_WRITE); /* fall through */ - case FCGI_STATE_PREPARE_WRITE: fcgi_create_env(srv, hctx, hctx->request_id); fcgi_set_state(srv, hctx, FCGI_STATE_WRITE); @@ -2847,6 +3013,9 @@ static handler_t fcgi_write_request(server *srv, handler_ctx *hctx) { return HANDLER_WAIT_FOR_EVENT; } + +/* might be called on fdevent after a connect() is delay too + * */ SUBREQUEST_FUNC(mod_fastcgi_handle_subrequest) { plugin_data *p = p_d; @@ -2858,83 +3027,118 @@ SUBREQUEST_FUNC(mod_fastcgi_handle_subrequest) { /* not my job */ if (con->mode != p->id) return HANDLER_GO_ON; + + /* we don't have a host yet, choose one + * -> this happens in the first round + * and when the host died and we have to select a new one */ + if (hctx->host == NULL) { + size_t k; + int ndx, used = -1; + + /* get best server */ + for (k = 0, ndx = -1; k < hctx->ext->used; k++) { + host = hctx->ext->hosts[k]; + + /* we should have at least one proc that can do somthing */ + if (host->active_procs == 0) continue; + + if (used == -1 || host->load < used) { + used = host->load; + + ndx = k; + } + } + /* found a server */ + if (ndx == -1) { + /* all hosts are down */ + + fcgi_connection_close(srv, hctx); + + con->http_status = 500; + con->mode = DIRECT; + + return HANDLER_FINISHED; + } + + host = hctx->ext->hosts[ndx]; + + /* + * if check-local is disabled, use the uri.path handler + * + */ + + /* init handler-context */ + hctx->host = host; + hctx->proc = NULL; + } else { + host = hctx->host; + } + /* ok, create the request */ switch(fcgi_write_request(srv, hctx)) { case HANDLER_ERROR: proc = hctx->proc; host = hctx->host; -#if 0 - if (proc && - 0 == proc->is_local && - proc->state != PROC_STATE_DISABLED) { - /* only disable remote servers as we don't manage them*/ - - log_error_write(srv, __FILE__, __LINE__, "sbdb", "fcgi-server disabled:", - host->host, - proc->port, - proc->socket); - - /* disable this server */ - proc->disable_ts = srv->cur_ts; - proc->state = PROC_STATE_DISABLED; - host->active_procs--; - } -#endif - if (hctx->state == FCGI_STATE_INIT || - hctx->state == FCGI_STATE_CONNECT) { + hctx->state == FCGI_STATE_CONNECT_DELAYED) { /* connect() or getsockopt() failed, * restart the request-handling */ - if (proc && proc->is_local) { + if (proc) { + if (proc->is_local) { - if (p->conf.debug) { - log_error_write(srv, __FILE__, __LINE__, "sbdb", "connect() to fastcgi failed, restarting the request-handling:", + if (p->conf.debug) { + log_error_write(srv, __FILE__, __LINE__, "sbdb", + "connect() to fastcgi failed, restarting the request-handling:", host->host, proc->port, proc->socket); - } + } - /* - * several hctx might reference the same proc - * - * Only one of them should mark the proc as dead all the other - * ones should just take a new one. - * - * If a new proc was started with the old struct this might lead - * the mark a perfect proc as dead otherwise - * - */ - if (proc->state == PROC_STATE_RUNNING && - hctx->pid == proc->pid) { - proc->state = PROC_STATE_DIED_WAIT_FOR_PID; + /* + * several hctx might reference the same proc + * + * Only one of them should mark the proc as dead all the other + * ones should just take a new one. + * + * If a new proc was started with the old struct this might lead + * the mark a perfect proc as dead otherwise + * + */ + if (proc->state == PROC_STATE_RUNNING && + hctx->pid == proc->pid) { + proc->state = PROC_STATE_DIED_WAIT_FOR_PID; + } + } else { + proc->state = PROC_STATE_DISABLED; } + host->active_procs--; + fastcgi_status_copy_procname(p->statuskey, hctx->host, hctx->proc); + buffer_append_string(p->statuskey, ".disabled"); + + status_counter_set(srv, CONST_BUF_LEN(p->statuskey), 1); } + fcgi_restart_dead_procs(srv, p, host); - - fcgi_connection_close(srv, hctx); - - buffer_reset(con->physical.path); - con->mode = DIRECT; - joblist_append(srv, con); /* really ? */ - /* mis-using HANDLER_WAIT_FOR_FD to break out of the loop - * and hope that the childs will be restarted - * - */ - - /* we might get into a LOOP here - * - * but how to handle this ? - * - * if we enter a endless loop, we will burn the CPU - * - * let this handle by the loop-detection - */ + /* cleanup this request and let the request handler start this request again */ + if (hctx->reconnects < 5) { + fcgi_reconnect(srv, hctx); + joblist_append(srv, con); /* in case we come from the event-handler */ + + return HANDLER_WAIT_FOR_FD; + } else { + fcgi_connection_close(srv, hctx); - return HANDLER_WAIT_FOR_FD; + buffer_reset(con->physical.path); + con->mode = DIRECT; + con->http_status = 500; + joblist_append(srv, con); /* in case we come from the event-handler */ + + return HANDLER_FINISHED; + } } else { fcgi_connection_close(srv, hctx); @@ -3054,7 +3258,7 @@ static handler_t fcgi_handle_fdevent(void *s, void *ctx, int revents) { fcgi_reconnect(srv, hctx); log_error_write(srv, __FILE__, __LINE__, "ssdsd", - "response not sent, request not sent, reconnection.", + "response not received, request not sent, reconnecting.", "connection-fd:", con->fd, "fcgi-fd:", hctx->fd); @@ -3062,7 +3266,7 @@ static handler_t fcgi_handle_fdevent(void *s, void *ctx, int revents) { } log_error_write(srv, __FILE__, __LINE__, "sosdsd", - "response not sent, request sent:", hctx->wb->bytes_out, + "response not received, request sent:", hctx->wb->bytes_out, "connection-fd:", con->fd, "fcgi-fd:", hctx->fd); @@ -3093,7 +3297,7 @@ static handler_t fcgi_handle_fdevent(void *s, void *ctx, int revents) { } if (revents & FDEVENT_OUT) { - if (hctx->state == FCGI_STATE_CONNECT || + if (hctx->state == FCGI_STATE_CONNECT_DELAYED || hctx->state == FCGI_STATE_WRITE) { /* we are allowed to send something out * @@ -3110,7 +3314,7 @@ static handler_t fcgi_handle_fdevent(void *s, void *ctx, int revents) { /* perhaps this issue is already handled */ if (revents & FDEVENT_HUP) { - if (hctx->state == FCGI_STATE_CONNECT) { + if (hctx->state == FCGI_STATE_CONNECT_DELAYED) { /* getoptsock will catch this one (right ?) * * if we are in connect we might get a EINPROGRESS @@ -3193,16 +3397,15 @@ static int fcgi_patch_connection(server *srv, connection *con, plugin_data *p) { static handler_t fcgi_check_extension(server *srv, connection *con, void *p_d, int uri_path_handler) { plugin_data *p = p_d; size_t s_len; - int used = -1; - int ndx; size_t k; buffer *fn; fcgi_extension *extension = NULL; + fcgi_extension_host *host = NULL; /* Possibly, we processed already this request */ if (con->file_started == 1) return HANDLER_GO_ON; - - fn = con->uri.path; + + fn = uri_path_handler ? con->uri.path : con->physical.path; if (fn->used == 0) { return HANDLER_ERROR; @@ -3237,124 +3440,118 @@ static handler_t fcgi_check_extension(server *srv, connection *con, void *p_d, i if (k == p->conf.exts->used) { return HANDLER_GO_ON; } - + /* get best server */ - for (k = 0, ndx = -1; k < extension->used; k++) { - fcgi_extension_host *host = extension->hosts[k]; - + for (k = 0; k < extension->used; k++) { + host = extension->hosts[k]; + /* we should have at least one proc that can do somthing */ - if (host->active_procs == 0) continue; + if (host->active_procs == 0) { + host = NULL; - if (used == -1 || host->load < used) { - used = host->load; - - ndx = k; + continue; } + + /* we found one host that is alive */ + break; } - /* found a server */ - if (ndx != -1) { - fcgi_extension_host *host = extension->hosts[ndx]; + if (!host) { + /* sorry, we don't have a server alive for this ext */ + buffer_reset(con->physical.path); + con->http_status = 500; - /* - * if check-local is disabled, use the uri.path handler - * - */ + log_error_write(srv, __FILE__, __LINE__, "sb", + "no fcgi-handler found for:", + fn); - /* init handler-context */ - if (uri_path_handler) { - if (host->check_local == 0) { - handler_ctx *hctx; - char *pathinfo; - - hctx = handler_ctx_init(); - - hctx->remote_conn = con; - hctx->plugin_data = p; - hctx->host = host; - hctx->proc = NULL; + return HANDLER_FINISHED; + } - hctx->conf.exts = p->conf.exts; - hctx->conf.debug = p->conf.debug; - - con->plugin_ctx[p->id] = hctx; - - host->load++; - - con->mode = p->id; - - if (con->conf.log_request_handling) { - log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_fastcgi"); - } - - /* the prefix is the SCRIPT_NAME, - * everthing from start to the next slash - * this is important for check-local = "disable" - * - * if prefix = /admin.fcgi - * - * /admin.fcgi/foo/bar - * - * SCRIPT_NAME = /admin.fcgi - * PATH_INFO = /foo/bar - * - * if prefix = /fcgi-bin/ - * - * /fcgi-bin/foo/bar - * - * SCRIPT_NAME = /fcgi-bin/foo - * PATH_INFO = /bar - * - */ - - /* the rewrite is only done for /prefix/? matches */ - if (extension->key->ptr[0] == '/' && - con->uri.path->used > extension->key->used && - NULL != (pathinfo = strchr(con->uri.path->ptr + extension->key->used - 1, '/'))) { - /* rewrite uri.path and pathinfo */ - - buffer_copy_string(con->request.pathinfo, pathinfo); - - con->uri.path->used -= con->request.pathinfo->used - 1; - con->uri.path->ptr[con->uri.path->used - 1] = '\0'; - } - } - return HANDLER_GO_ON; - } else { + /* + * if check-local is disabled, use the uri.path handler + * + */ + + /* init handler-context */ + if (uri_path_handler) { + if (host->check_local == 0) { handler_ctx *hctx; + char *pathinfo; + hctx = handler_ctx_init(); hctx->remote_conn = con; hctx->plugin_data = p; - hctx->host = host; - hctx->proc = NULL; - + hctx->proc = NULL; + hctx->ext = extension; + + hctx->conf.exts = p->conf.exts; hctx->conf.debug = p->conf.debug; - + con->plugin_ctx[p->id] = hctx; - - host->load++; - + con->mode = p->id; - + if (con->conf.log_request_handling) { - log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_fastcgi"); + log_error_write(srv, __FILE__, __LINE__, "s", + "handling it in mod_fastcgi"); } + + /* the prefix is the SCRIPT_NAME, + * everthing from start to the next slash + * this is important for check-local = "disable" + * + * if prefix = /admin.fcgi + * + * /admin.fcgi/foo/bar + * + * SCRIPT_NAME = /admin.fcgi + * PATH_INFO = /foo/bar + * + * if prefix = /fcgi-bin/ + * + * /fcgi-bin/foo/bar + * + * SCRIPT_NAME = /fcgi-bin/foo + * PATH_INFO = /bar + * + */ - return HANDLER_GO_ON; + /* the rewrite is only done for /prefix/? matches */ + if (extension->key->ptr[0] == '/' && + con->uri.path->used > extension->key->used && + NULL != (pathinfo = strchr(con->uri.path->ptr + extension->key->used - 1, '/'))) { + /* rewrite uri.path and pathinfo */ + + buffer_copy_string(con->request.pathinfo, pathinfo); + + con->uri.path->used -= con->request.pathinfo->used - 1; + con->uri.path->ptr[con->uri.path->used - 1] = '\0'; + } } } else { - /* no handler found */ - buffer_reset(con->physical.path); - con->http_status = 500; + handler_ctx *hctx; + hctx = handler_ctx_init(); - log_error_write(srv, __FILE__, __LINE__, "sb", - "no fcgi-handler found for:", - fn); + hctx->remote_conn = con; + hctx->plugin_data = p; + hctx->proc = NULL; + hctx->ext = extension; - return HANDLER_FINISHED; + hctx->conf.exts = p->conf.exts; + hctx->conf.debug = p->conf.debug; + + con->plugin_ctx[p->id] = hctx; + + con->mode = p->id; + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_fastcgi"); + } } + return HANDLER_GO_ON; } @@ -3380,7 +3577,7 @@ JOBLIST_FUNC(mod_fastcgi_handle_joblist) { fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); break; - case FCGI_STATE_CONNECT: + case FCGI_STATE_CONNECT_DELAYED: case FCGI_STATE_WRITE: fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); @@ -3598,7 +3795,7 @@ TRIGGER_FUNC(mod_fastcgi_handle_trigger) { int mod_fastcgi_plugin_init(plugin *p) { - p->version = LIGHTTPD_VERSION_ID; + p->version = LIGHTTPD_VERSION_ID; p->name = buffer_init_string("fastcgi"); p->init = mod_fastcgi_init; diff --git a/src/mod_mysql_vhost.c b/src/mod_mysql_vhost.c index ebe7657..bf13e09 100644 --- a/src/mod_mysql_vhost.c +++ b/src/mod_mysql_vhost.c @@ -413,7 +413,7 @@ int mod_mysql_vhost_plugin_init(plugin *p) { p->init = mod_mysql_vhost_init; p->cleanup = mod_mysql_vhost_cleanup; - p->handle_connection_close = mod_mysql_vhost_handle_connection_close; + p->handle_request_done = mod_mysql_vhost_handle_connection_close; p->set_defaults = mod_mysql_vhost_set_defaults; p->handle_docroot = mod_mysql_vhost_handle_docroot; diff --git a/src/mod_proxy.c b/src/mod_proxy.c index c6a4660..6baf459 100644 --- a/src/mod_proxy.c +++ b/src/mod_proxy.c @@ -439,7 +439,12 @@ static int proxy_create_env(server *srv, handler_ctx *hctx) { BUFFER_APPEND_STRING_CONST(b, " HTTP/1.0\r\n"); proxy_append_header(con, "X-Forwarded-For", (char *)inet_ntop_cache_get_ip(srv, &(con->dst_addr))); - proxy_set_header(con, "X-Host", con->request.http_host->ptr); + /* http_host is NOT is just a pointer to a buffer + * which is NULL if it is not set */ + if (con->request.http_host && + !buffer_is_empty(con->request.http_host)) { + proxy_set_header(con, "X-Host", con->request.http_host->ptr); + } proxy_set_header(con, "X-Forwarded-Proto", con->conf.is_ssl ? "https" : "http"); /* request header */ diff --git a/src/mod_scgi.c b/src/mod_scgi.c index 06713e9..a27b28a 100644 --- a/src/mod_scgi.c +++ b/src/mod_scgi.c @@ -2677,7 +2677,7 @@ static handler_t scgi_check_extension(server *srv, connection *con, void *p_d, i /* Possibly, we processed already this request */ if (con->file_started == 1) return HANDLER_GO_ON; - fn = con->uri.path; + fn = uri_path_handler ? con->uri.path : con->physical.path; if (fn->used == 0) { return HANDLER_ERROR; diff --git a/src/mod_secure_download.c b/src/mod_secure_download.c index 5139507..1ea5a50 100644 --- a/src/mod_secure_download.c +++ b/src/mod_secure_download.c @@ -37,7 +37,7 @@ typedef struct { buffer *secret; buffer *uri_prefix; - time_t timeout; + unsigned short timeout; } plugin_config; typedef struct { diff --git a/src/mod_setenv.c b/src/mod_setenv.c index 001b238..9501554 100644 --- a/src/mod_setenv.c +++ b/src/mod_setenv.c @@ -12,6 +12,10 @@ /* plugin config for all request/connections */ typedef struct { + int handled; /* make sure that we only apply the headers once */ +} handler_ctx; + +typedef struct { array *request_header; array *response_header; @@ -26,6 +30,21 @@ typedef struct { plugin_config conf; } plugin_data; +static handler_ctx * handler_ctx_init() { + handler_ctx * hctx; + + hctx = calloc(1, sizeof(*hctx)); + + hctx->handled = 0; + + return hctx; +} + +static void handler_ctx_free(handler_ctx *hctx) { + free(hctx); +} + + /* init the plugin data */ INIT_FUNC(mod_setenv_init) { plugin_data *p; @@ -140,7 +159,22 @@ static int mod_setenv_patch_connection(server *srv, connection *con, plugin_data URIHANDLER_FUNC(mod_setenv_uri_handler) { plugin_data *p = p_d; size_t k; - + handler_ctx *hctx; + + if (con->plugin_ctx[p->id]) { + hctx = con->plugin_ctx[p->id]; + } else { + hctx = handler_ctx_init(); + + con->plugin_ctx[p->id] = hctx; + } + + if (hctx->handled) { + return HANDLER_GO_ON; + } + + hctx->handled = 1; + mod_setenv_patch_connection(srv, con, p); for (k = 0; k < p->conf.request_header->used; k++) { @@ -181,6 +215,19 @@ URIHANDLER_FUNC(mod_setenv_uri_handler) { return HANDLER_GO_ON; } +REQUESTDONE_FUNC(mod_setenv_reset) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (con->plugin_ctx[p->id]) { + handler_ctx_free(con->plugin_ctx[p->id]); + con->plugin_ctx[p->id] = NULL; + } + + return HANDLER_GO_ON; +} + /* this function is called at dlopen() time and inits the callbacks */ int mod_setenv_plugin_init(plugin *p) { @@ -192,6 +239,8 @@ int mod_setenv_plugin_init(plugin *p) { p->set_defaults = mod_setenv_set_defaults; p->cleanup = mod_setenv_free; + p->handle_request_done = mod_setenv_reset; + p->data = NULL; return 0; diff --git a/src/mod_staticfile.c b/src/mod_staticfile.c index 6496689..cbc443d 100644 --- a/src/mod_staticfile.c +++ b/src/mod_staticfile.c @@ -372,17 +372,11 @@ URIHANDLER_FUNC(mod_staticfile_subrequest) { /* ignore certain extensions */ for (k = 0; k < p->conf.exclude_ext->used; k++) { - int ct_len; - ds = (data_string *)p->conf.exclude_ext->data[k]; - - ct_len = ds->value->used - 1; - - if (ct_len > s_len) continue; if (ds->value->used == 0) continue; - - if (0 == strncmp(con->uri.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) { + + if (buffer_is_equal_right_len(con->physical.path, ds->value, ds->value->used - 1)) { return HANDLER_GO_ON; } } @@ -446,13 +440,27 @@ URIHANDLER_FUNC(mod_staticfile_subrequest) { if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) { return HANDLER_FINISHED; } else if (con->request.http_range && con->conf.range_requests) { - /* content prepared, I'm done */ - con->file_finished = 1; + int do_range_request = 1; + /* check if we have a conditional GET */ + + if (NULL != (ds = array_get_element(con->request.headers, "If-Range"))) { + /* if the value is the same as our ETag, we do a Range-request, + * otherwise a full 200 */ + + if (!buffer_is_equal(ds->value, con->physical.etag)) { + do_range_request = 0; + } + } + + if (do_range_request) { + /* content prepared, I'm done */ + con->file_finished = 1; - if (0 == http_response_parse_range(srv, con, p)) { - con->http_status = 206; + if (0 == http_response_parse_range(srv, con, p)) { + con->http_status = 206; + } + return HANDLER_FINISHED; } - return HANDLER_FINISHED; } /* if we are still here, prepare body */ diff --git a/src/mod_status.c b/src/mod_status.c index f69a1f4..f5a35e9 100644 --- a/src/mod_status.c +++ b/src/mod_status.c @@ -22,6 +22,8 @@ typedef struct { buffer *config_url; buffer *status_url; + buffer *statistics_url; + int sort; } plugin_config; @@ -84,6 +86,7 @@ FREE_FUNC(mod_status_free) { plugin_config *s = p->config_storage[i]; buffer_free(s->status_url); + buffer_free(s->statistics_url); buffer_free(s->config_url); free(s); @@ -104,7 +107,8 @@ SETDEFAULTS_FUNC(mod_status_set_defaults) { config_values_t cv[] = { { "status.status-url", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, { "status.config-url", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, - { "status.enable-sort", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, + { "status.enable-sort", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, + { "status.statistics-url", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } }; @@ -119,10 +123,12 @@ SETDEFAULTS_FUNC(mod_status_set_defaults) { s->config_url = buffer_init(); s->status_url = buffer_init(); s->sort = 1; + s->statistics_url = buffer_init(); cv[0].destination = s->status_url; cv[1].destination = s->config_url; cv[2].destination = &(s->sort); + cv[3].destination = s->statistics_url; p->config_storage[i] = s; @@ -575,6 +581,40 @@ static handler_t mod_status_handle_server_status_text(server *srv, connection *c return 0; } +static handler_t mod_status_handle_server_statistics(server *srv, connection *con, void *p_d) { + plugin_data *p = p_d; + buffer *b, *m = p->module_list; + size_t i; + array *st = srv->status; + + if (0 == st->used) { + /* we have nothing to send */ + con->http_status = 204; + con->file_finished = 1; + + return HANDLER_FINISHED; + } + + b = chunkqueue_get_append_buffer(con->write_queue); + + for (i = 0; i < st->used; i++) { + size_t ndx = st->sorted[i]; + + buffer_append_string_buffer(b, st->data[ndx]->key); + buffer_append_string(b, ": "); + buffer_append_long(b, ((data_integer *)(st->data[ndx]))->value); + buffer_append_string(b, "\n"); + } + + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain")); + + con->http_status = 200; + con->file_finished = 1; + + return HANDLER_FINISHED; +} + + static handler_t mod_status_handle_server_status(server *srv, connection *con, void *p_d) { if (buffer_is_equal_string(con->uri.query, CONST_STR_LEN("auto"))) { @@ -638,9 +678,9 @@ static handler_t mod_status_handle_server_config(server *srv, connection *con, v mod_status_header_append(b, "Server-Features"); #ifdef HAVE_PCRE_H - mod_status_row_append(b, "Rewrite Engine", "enabled"); + mod_status_row_append(b, "RegEx Conditionals", "enabled"); #else - mod_status_row_append(b, "Rewrite Engine", "disabled - pcre missing"); + mod_status_row_append(b, "RegEx Conditionals", "disabled - pcre missing"); #endif mod_status_header_append(b, "Network Engine"); @@ -692,6 +732,7 @@ static int mod_status_patch_connection(server *srv, connection *con, plugin_data PATCH(status_url); PATCH(config_url); PATCH(sort); + PATCH(statistics_url); /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { @@ -711,6 +752,8 @@ static int mod_status_patch_connection(server *srv, connection *con, plugin_data PATCH(config_url); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.enable-sort"))) { PATCH(sort); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.statistics-url"))) { + PATCH(statistics_url); } } } @@ -729,6 +772,9 @@ static handler_t mod_status_handler(server *srv, connection *con, void *p_d) { } else if (!buffer_is_empty(p->conf.config_url) && buffer_is_equal(p->conf.config_url, con->uri.path)) { return mod_status_handle_server_config(srv, con, p_d); + } else if (!buffer_is_empty(p->conf.statistics_url) && + buffer_is_equal(p->conf.statistics_url, con->uri.path)) { + return mod_status_handle_server_statistics(srv, con, p_d); } return HANDLER_GO_ON; diff --git a/src/mod_webdav.c b/src/mod_webdav.c index 0e7a682..3306c73 100644 --- a/src/mod_webdav.c +++ b/src/mod_webdav.c @@ -291,6 +291,7 @@ static int mod_webdav_patch_connection(server *srv, connection *con, plugin_data PATCH(enabled); PATCH(is_readonly); + PATCH(log_xml); #ifdef USE_PROPPATCH PATCH(sql); @@ -1175,19 +1176,23 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { d.rel_path = buffer_init(); while(NULL != (de = readdir(dir))) { - if ((de->d_name[0] == '.' && de->d_name[1] == '\0') || - (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')) { + if (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0') { continue; /* ignore the parent dir */ } buffer_copy_string_buffer(d.path, dst->path); BUFFER_APPEND_SLASH(d.path); - buffer_append_string(d.path, de->d_name); - + buffer_copy_string_buffer(d.rel_path, dst->rel_path); BUFFER_APPEND_SLASH(d.rel_path); - buffer_append_string(d.rel_path, de->d_name); + + if (de->d_name[0] == '.' && de->d_name[1] == '\0') { + /* don't append the . */ + } else { + buffer_append_string(d.path, de->d_name); + buffer_append_string(d.rel_path, de->d_name); + } buffer_reset(prop_200); buffer_reset(prop_404); @@ -1247,6 +1252,9 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { free(req_props); } + buffer_free(prop_200); + buffer_free(prop_404); + buffer_append_string(b,"</D:multistatus>\n"); if (p->conf.log_xml) { @@ -1531,10 +1539,10 @@ URIHANDLER_FUNC(mod_webdav_subrequest_handler) { buffer_path_simplify(p->uri.path, p->tmp_buf); /* we now have a URI which is clean. transform it into a physical path */ - buffer_copy_string_buffer(p->physical.doc_root, con->conf.document_root); + buffer_copy_string_buffer(p->physical.doc_root, con->physical.doc_root); buffer_copy_string_buffer(p->physical.rel_path, p->uri.path); - if (con->conf.force_lower_case) { + if (con->conf.force_lowercase_filenames) { buffer_to_lower(p->physical.rel_path); } diff --git a/src/network.c b/src/network.c index 40e9bba..922009f 100644 --- a/src/network.c +++ b/src/network.c @@ -71,6 +71,7 @@ int network_server_init(server *srv, buffer *host_token, specific_config *s) { const char *host; buffer *b; int is_unix_domain_socket = 0; + int fd; #ifdef SO_ACCEPTFILTER struct accept_filter_arg afa; @@ -254,6 +255,33 @@ int network_server_init(server *srv, buffer *host_token, specific_config *s) { addr_len = strlen(host) + sizeof(srv_socket->addr.un.sun_family); #endif + /* check if the socket exists and try to connect to it. */ + if (-1 != (fd = connect(srv_socket->fd, (struct sockaddr *) &(srv_socket->addr), addr_len))) { + close(fd); + + log_error_write(srv, __FILE__, __LINE__, "ss", + "server socket is still in use:", + host); + + + return -1; + } + + /* connect failed */ + switch(errno) { + case ECONNREFUSED: + unlink(host); + break; + case ENOENT: + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sds", + "testing socket failed:", + host, strerror(errno)); + + return -1; + } + break; default: addr_len = 0; @@ -262,7 +290,18 @@ int network_server_init(server *srv, buffer *host_token, specific_config *s) { } if (0 != bind(srv_socket->fd, (struct sockaddr *) &(srv_socket->addr), addr_len)) { - log_error_write(srv, __FILE__, __LINE__, "sds", "can't bind to port", port, strerror(errno)); + switch(srv_socket->addr.plain.sa_family) { + case AF_UNIX: + log_error_write(srv, __FILE__, __LINE__, "sds", + "can't bind to socket:", + host, strerror(errno)); + break; + default: + log_error_write(srv, __FILE__, __LINE__, "ssds", + "can't bind to port:", + host, port, strerror(errno)); + break; + } return -1; } diff --git a/src/network_linux_sendfile.c b/src/network_linux_sendfile.c index 5628a94..5752385 100644 --- a/src/network_linux_sendfile.c +++ b/src/network_linux_sendfile.c @@ -173,6 +173,11 @@ int network_write_chunkqueue_linuxsendfile(server *srv, connection *con, int fd, return -1; } } + + if (r == 0) { + /* we got a event to write put we couldn't. remote side closed ? */ + return -2; + } c->offset += r; cq->bytes_out += r; diff --git a/src/plugin.c b/src/plugin.c index b375017..e74d8b0 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -66,6 +66,7 @@ static void plugin_free(plugin *p) { /*if (RUNNING_ON_VALGRIND) use_dlclose = 0;*/ #endif +#ifndef LIGHTTPD_STATIC if (use_dlclose && p->lib) { #ifdef __WIN32 FreeLibrary(p->lib); @@ -73,6 +74,7 @@ static void plugin_free(plugin *p) { dlclose(p->lib); #endif } +#endif free(p); } @@ -100,7 +102,23 @@ static int plugins_register(server *srv, plugin *p) { * */ +#ifdef LIGHTTPD_STATIC +int plugins_load(server *srv) { + plugin *p; +#define PLUGIN_INIT(x)\ + p = plugin_init(); \ + if (x ## _plugin_init(p)) { \ + log_error_write(srv, __FILE__, __LINE__, "ss", #x, "plugin init failed" ); \ + plugin_free(p); \ + return -1;\ + }\ + plugins_register(srv, p); + +#include "plugin-static.h" + return 0; +} +#else int plugins_load(server *srv) { plugin *p; int (*init)(plugin *pl); @@ -205,6 +223,7 @@ int plugins_load(server *srv) { return 0; } +#endif #define PLUGIN_TO_SLOT(x, y) \ handler_t plugins_call_##y(server *srv, connection *con) {\ diff --git a/src/request.c b/src/request.c index 0935725..db58671 100644 --- a/src/request.c +++ b/src/request.c @@ -791,6 +791,12 @@ int http_request_parse(server *srv, connection *con) { * -> (10.4.18) 417 (close) * * (not handled at all yet, we always send 417 here) + * + * What has to be added ? + * 1. handling of chunked request body + * 2. out-of-order sending from the HTTP/1.1 100 Continue + * header + * */ con->http_status = 417; @@ -815,9 +821,14 @@ int http_request_parse(server *srv, connection *con) { return 0; } } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-Modified-Since")))) { - /* if dup, only the first one will survive */ + /* Proxies sometimes send dup headers + * if they are the same we ignore the second + * if not, we raise an error */ if (!con->request.http_if_modified_since) { con->request.http_if_modified_since = ds->value->ptr; + } else if (0 == strcasecmp(con->request.http_if_modified_since, + ds->value->ptr)) { + /* ignore it if they are the same */ } else { con->http_status = 400; con->keep_alive = 0; @@ -963,22 +974,25 @@ int http_request_parse(server *srv, connection *con) { return 0; } - - /* check if we have read post data */ - if (con->request.http_method == HTTP_METHOD_POST - || (con->request.http_method != HTTP_METHOD_GET - && con->request.http_method != HTTP_METHOD_HEAD - && con->request.http_method != HTTP_METHOD_OPTIONS - && con_length_set)) { -#if 0 - if (con->request.http_content_type == NULL) { + switch(con->request.http_method) { + case HTTP_METHOD_GET: + case HTTP_METHOD_HEAD: + case HTTP_METHOD_OPTIONS: + /* content-length is forbidden for those */ + if (con_length_set && con->request.content_length != 0) { + /* content-length is missing */ log_error_write(srv, __FILE__, __LINE__, "s", - "Content-Length request, but content-type not set"); + "GET/HEAD/OPTIONS with content-length -> 400"); + con->keep_alive = 0; + + con->http_status = 400; + return 0; } -#endif - - if (con_length_set == 0) { + break; + case HTTP_METHOD_POST: + /* content-length is required for them */ + if (!con_length_set) { /* content-length is missing */ log_error_write(srv, __FILE__, __LINE__, "s", "POST-request, but content-length missing -> 411"); @@ -986,8 +1000,17 @@ int http_request_parse(server *srv, connection *con) { con->http_status = 411; return 0; + } - + break; + default: + /* the may have a content-length */ + break; + } + + + /* check if we have read post data */ + if (con_length_set) { /* don't handle more the SSIZE_MAX bytes in content-length */ if (con->request.content_length > SSIZE_MAX) { con->http_status = 413; diff --git a/src/response.c b/src/response.c index 2617842..d955cec 100644 --- a/src/response.c +++ b/src/response.c @@ -133,6 +133,18 @@ handler_t http_response_prepare(server *srv, connection *con) { /* no decision yet, build conf->filename */ if (con->mode == DIRECT && con->physical.path->used == 0) { char *qstr; + + /* we only come here when we have the parse the full request again + * + * a HANDLER_COMEBACK from mod_rewrite and mod_fastcgi might be a + * problem here as mod_setenv might get called multiple times + * + * fastcgi-auth might lead to a COMEBACK too + * fastcgi again dead server too + * + * mod_compress might add headers twice too + * + * */ if (con->conf.log_condition_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "run condition"); @@ -334,7 +346,7 @@ handler_t http_response_prepare(server *srv, connection *con) { * * convert to lower-case */ - if (con->conf.force_lower_case) { + if (con->conf.force_lowercase_filenames) { buffer_to_lower(con->physical.rel_path); } diff --git a/src/server.c b/src/server.c index 5c515e3..9eb9eb4 100644 --- a/src/server.c +++ b/src/server.c @@ -50,6 +50,10 @@ #include <sys/resource.h> #endif +#ifdef HAVE_SYS_PRCTL_H +#include <sys/prctl.h> +#endif + #ifndef __sgi /* IRIX doesn't like the alarm based time() optimization */ /* #define USE_ALARM */ @@ -67,7 +71,11 @@ static void sigaction_handler(int sig, siginfo_t *si, void *context) { switch (sig) { case SIGTERM: srv_shutdown = 1; break; - case SIGINT: graceful_shutdown = 1; break; + case SIGINT: + if (graceful_shutdown) srv_shutdown = 1; + else graceful_shutdown = 1; + + break; case SIGALRM: handle_sig_alarm = 1; break; case SIGHUP: handle_sig_hup = 1; break; case SIGCHLD: break; @@ -77,7 +85,11 @@ static void sigaction_handler(int sig, siginfo_t *si, void *context) { static void signal_handler(int sig) { switch (sig) { case SIGTERM: srv_shutdown = 1; break; - case SIGINT: graceful_shutdown = 1; break; + case SIGINT: + if (graceful_shutdown) srv_shutdown = 1; + else graceful_shutdown = 1; + + break; case SIGALRM: handle_sig_alarm = 1; break; case SIGHUP: handle_sig_hup = 1; break; case SIGCHLD: break; @@ -144,6 +156,7 @@ static server *server_init(void) { CLEAN(config_context); CLEAN(config_touched); + CLEAN(status); #undef CLEAN for (i = 0; i < FILE_CACHE_MAX; i++) { @@ -241,6 +254,7 @@ static void server_free(server *srv) { CLEAN(config_context); CLEAN(config_touched); + CLEAN(status); #undef CLEAN joblist_free(srv, srv->joblist); @@ -323,7 +337,7 @@ int main (int argc, char **argv) { setlocale(LC_TIME, "C"); if (NULL == (srv = server_init())) { - fprintf(stderr, "did this really happend ?\n"); + fprintf(stderr, "did this really happen?\n"); return -1; } @@ -521,6 +535,12 @@ int main (int argc, char **argv) { } else { srv->max_fds = rlim.rlim_cur; } + + /* set core file rlimit, if enable_cores is set */ + if (use_rlimit && srv->srvconf.enable_cores && getrlimit(RLIMIT_CORE, &rlim) == 0) { + rlim.rlim_cur = rlim.rlim_max; + setrlimit(RLIMIT_CORE, &rlim); + } #endif if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* don't raise the limit above FD_SET_SIZE */ @@ -593,6 +613,11 @@ int main (int argc, char **argv) { initgroups(srv->srvconf.username->ptr, grp->gr_gid); if (srv->srvconf.username->used) setuid(pwd->pw_uid); #endif +#ifdef HAVE_PRCTL + if (srv->srvconf.enable_cores) { + prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); + } +#endif } else { #ifdef HAVE_GETRLIMIT @@ -608,6 +633,13 @@ int main (int argc, char **argv) { } else { srv->max_fds = rlim.rlim_cur; } + + /* set core file rlimit, if enable_cores is set */ + if (srv->srvconf.enable_cores && getrlimit(RLIMIT_CORE, &rlim) == 0) { + rlim.rlim_cur = rlim.rlim_max; + setrlimit(RLIMIT_CORE, &rlim); + } + #endif if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* don't raise the limit above FD_SET_SIZE */ |