diff options
76 files changed, 1970 insertions, 1055 deletions
@@ -3,6 +3,29 @@ NEWS ==== +- 1.4.9 - 2006-01-14 + + * added server.core-files option (sandy <sandy@meebo.com>) + * added docs for mod_status + * added mod_evasive to limit the number of connections by IP (<w1zzard@techpowerup.com>) + * added the power-magnet to mod_cml + * added internal statistics to mod_fastcgi + * added server.statistics-url to get internal statistics from mod_status + * added support for conditional range-requests through If-Range + * added static building via scons + * fixed 100% cpu loops in mod_cgi ("sandy" <sjen@cs.stanford.edu>) + * fixed handling for secure-download.timeout (jamis@37signals.com) + * fixed IE bug in content-charset in the output of mod_dirlisting (sniper@php.net) + * fixed typos and language in the docs (ryan-2005@ryandesign.com) + * fixed assertion in mod_cgi on HEAD request is Content-Length (<sandy@meebo.com>) + * fixed handling if equal but duplicate If-Modified-Since request headers + * fixed endless loops in mod_fastcgi if backend is dead + * fixed Depth: 1 handling in PROPFIND requests on empty dirs + * fixed encoding of UTF8 encoded dirlistings (Jani Taskinen <sniper@iki.fi>) + * fixed initial bind to a unix-domain socket through server.bind + * fixed handling of lowercase filesystems + * fixed duplicate request headers cause by mod_setenv + - 1.4.8 - 2005-11-23 * added auto-reconnect to ldap-server in mod_auth diff --git a/config.h.in b/config.h.in index 4c4f7d8..956639c 100644 --- a/config.h.in +++ b/config.h.in @@ -283,6 +283,9 @@ /* Define to 1 if you have the <sys/port.h> header file. */ #undef HAVE_SYS_PORT_H +/* Define to 1 if you have the <sys/prctl.h> header file. */ +#undef HAVE_SYS_PRCTL_H + /* Define to 1 if you have the <sys/resource.h> header file. */ #undef HAVE_SYS_RESOURCE_H @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.59 for lighttpd 1.4.8. +# Generated by GNU Autoconf 2.59 for lighttpd 1.4.9. # # Report bugs to <jan@kneschke.de>. # @@ -423,8 +423,8 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='lighttpd' PACKAGE_TARNAME='lighttpd' -PACKAGE_VERSION='1.4.8' -PACKAGE_STRING='lighttpd 1.4.8' +PACKAGE_VERSION='1.4.9' +PACKAGE_STRING='lighttpd 1.4.9' PACKAGE_BUGREPORT='jan@kneschke.de' ac_unique_file="src/server.c" @@ -954,7 +954,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures lighttpd 1.4.8 to adapt to many kinds of systems. +\`configure' configures lighttpd 1.4.9 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1021,7 +1021,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of lighttpd 1.4.8:";; + short | recursive ) echo "Configuration of lighttpd 1.4.9:";; esac cat <<\_ACEOF @@ -1183,7 +1183,7 @@ fi test -n "$ac_init_help" && exit 0 if $ac_init_version; then cat <<\_ACEOF -lighttpd configure 1.4.8 +lighttpd configure 1.4.9 generated by GNU Autoconf 2.59 Copyright (C) 2003 Free Software Foundation, Inc. @@ -1197,7 +1197,7 @@ cat >&5 <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by lighttpd $as_me 1.4.8, which was +It was created by lighttpd $as_me 1.4.9, which was generated by GNU Autoconf 2.59. Invocation command line was $ $0 $@ @@ -1925,7 +1925,7 @@ fi # Define the identity of the package. PACKAGE='lighttpd' - VERSION='1.4.8' + VERSION='1.4.9' cat >>confdefs.h <<_ACEOF @@ -20636,11 +20636,12 @@ fi + for ac_header in arpa/inet.h fcntl.h netinet/in.h stdlib.h string.h \ sys/socket.h sys/time.h unistd.h sys/sendfile.h sys/uio.h \ getopt.h sys/epoll.h sys/select.h poll.h sys/poll.h sys/devpoll.h sys/filio.h \ sys/mman.h sys/event.h sys/port.h pwd.h sys/syslimits.h \ -sys/resource.h sys/un.h syslog.h +sys/resource.h sys/un.h syslog.h sys/prctl.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then @@ -29197,7 +29198,7 @@ _ASBOX } >&5 cat >&5 <<_CSEOF -This file was extended by lighttpd $as_me 1.4.8, which was +This file was extended by lighttpd $as_me 1.4.9, which was generated by GNU Autoconf 2.59. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -29260,7 +29261,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ -lighttpd config.status 1.4.8 +lighttpd config.status 1.4.9 configured by $0, generated by GNU Autoconf 2.59, with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" diff --git a/configure.in b/configure.in index 0f94f5c..93cbb0f 100644 --- a/configure.in +++ b/configure.in @@ -1,7 +1,7 @@ # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ(2.57) -AC_INIT(lighttpd, 1.4.8, jan@kneschke.de) +AC_INIT(lighttpd, 1.4.9, jan@kneschke.de) AC_CONFIG_SRCDIR([src/server.c]) AC_CANONICAL_TARGET @@ -56,7 +56,7 @@ AC_CHECK_HEADERS([arpa/inet.h fcntl.h netinet/in.h stdlib.h string.h \ sys/socket.h sys/time.h unistd.h sys/sendfile.h sys/uio.h \ getopt.h sys/epoll.h sys/select.h poll.h sys/poll.h sys/devpoll.h sys/filio.h \ sys/mman.h sys/event.h sys/port.h pwd.h sys/syslimits.h \ -sys/resource.h sys/un.h syslog.h]) +sys/resource.h sys/un.h syslog.h sys/prctl.h]) # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST diff --git a/cygwin/lighttpd.README b/cygwin/lighttpd.README index 18bf85f..8acecba 100644 --- a/cygwin/lighttpd.README +++ b/cygwin/lighttpd.README @@ -31,17 +31,17 @@ Canonical download: ------------------------------------
Build instructions:
- unpack lighttpd-1.4.8-<REL>-src.tar.bz2
+ unpack lighttpd-1.4.9-<REL>-src.tar.bz2
if you use setup to install this src package, it will be
unpacked under /usr/src automatically
cd /usr/src
- ./lighttpd-1.4.8-<REL>.sh all
+ ./lighttpd-1.4.9-<REL>.sh all
This will create:
- /usr/src/lighttpd-1.4.8-<REL>.tar.bz2
- /usr/src/lighttpd-1.4.8-<REL>-src.tar.bz2
+ /usr/src/lighttpd-1.4.9-<REL>.tar.bz2
+ /usr/src/lighttpd-1.4.9-<REL>-src.tar.bz2
-Or use './lighttpd-1.4.8-<REL>.sh prep' to get a patched source directory
+Or use './lighttpd-1.4.9-<REL>.sh prep' to get a patched source directory
-------------------------------------------
diff --git a/doc/authentication.txt b/doc/authentication.txt index 20c06bd..2a11f64 100644 --- a/doc/authentication.txt +++ b/doc/authentication.txt @@ -7,8 +7,8 @@ Module: mod_auth ---------------- :Author: Jan Kneschke -:Date: $Date: 2005-09-16 14:45:15 +0200 (Fri, 16 Sep 2005) $ -:Revision: $Revision: 712 $ +:Date: $Date: 2006-01-12 19:34:26 +0100 (Thu, 12 Jan 2006) $ +:Revision: $Revision: 940 $ :abstract: The auth module provides ... @@ -85,7 +85,7 @@ newline. :: You can use htpasswd from the apache distribution to manage those files. :: - $ htpasswd lighttpd.user.digest agent007 + $ htpasswd lighttpd.user.htpasswd agent007 htdigest @@ -101,12 +101,24 @@ by a single newline. :: You can use htdigest from the apache distribution to manage those files. :: - $ htdigest src/lighttpd.user.digest 'download area' agent007 + $ htdigest lighttpd.user.htdigest 'download area' agent007 Using md5sum can also generate the password-hash: :: + + #!/bin/sh + user=$1 + realm=$2 + pass=$3 + + hash=`echo -n "$user:$realm:$pass" | md5sum | cut -b -32` + + echo "$user:$realm:$hash" + +To use it: + + $ htdigest.sh 'agent007' 'download area' 'secret' + agent007:download area:8364d0044ef57b3defcfa141e8f77b65 - $ echo -n "agent007:download area:secret" | md5sum - - 8364d0044ef57b3defcfa141e8f77b65 - ldap diff --git a/doc/cml.txt b/doc/cml.txt index f7752d4..ddae340 100644 --- a/doc/cml.txt +++ b/doc/cml.txt @@ -156,6 +156,53 @@ The index.cml for this looks like: :: Now we get about 10000 req/s instead of 600 req/s. +Power Magnet +------------ + +Next to all the features about Cache Decisions CML can do more. Starting +with lighttpd 1.4.9 a power-magnet was added which attracts each request +and allows you to manipulate the request for your needs. + +We want to display a maintainance page by putting a file in a specified +place: + +We enable the power magnet: :: + + cml.power-magnet = "/home/www/power-magnet.cml" + +and create /home/www/power-magnet.cml with: :: + + dr = request["DOCUMENT_ROOT"] + + if file_isreg(dr .. 'maintainance.html') then + output_include = { 'maintainance.html' } + return CACHE_HIT + end + + return CACHE_MISS + +For each requested file the /home/www/power-magnet.cml is executed which +checks if maintainance.html exists in the docroot and displays it +instead of handling the usual request. + +Another example, create thumbnail for requested image and serve it instead +of sending the big image: :: + + ## image-url is /album/baltic_winter_2005.jpg + ## no params -> 640x480 is served + ## /album/baltic_winter_2005.jpg/orig for full size + ## /album/baltic_winter_2005.jpg/thumb for thumbnail + + dr = request["DOCUMENT_ROOT"] + sn = request["SCRIPT_NAME"] + + ## to be continued :) ... + + trigger_handler = '/gen_image.php' + + return CACHE_MISS + + Installation ============ @@ -176,6 +223,8 @@ Options hosts for the memcache.* functions :cml.memcache-namespace: (not used yet) +:cml.power-magnet: + a cml file that is executed for each request Language ======== @@ -206,7 +255,7 @@ Additionally to the functions provided by lua mod_cml provides: :: boolean memcache_exists(string) -What ever your script does, it has to return either 0 or 1 for ``cache-hit`` or ``cache-miss``. +What ever your script does, it has to return either CACHE_HIT or CACHE_MISS. It case a error occures check the error-log, the user will get a error 500. If you don't like the standard error-page use ``server.errorfile-prefix``. diff --git a/doc/configuration.txt b/doc/configuration.txt index 106b008..29db662 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -7,8 +7,8 @@ Module: core ------------ :Author: Jan Kneschke -:Date: $Date: 2005-11-11 13:43:16 +0100 (Fri, 11 Nov 2005) $ -:Revision: $Revision: 835 $ +:Date: $Date: 2006-01-14 18:07:25 +0100 (Sat, 14 Jan 2006) $ +:Revision: $Revision: 947 $ :abstract: the layout of the configuration file @@ -96,9 +96,10 @@ $HTTP["url"] $HTTP["remoteip"] match on the remote IP or a remote Network $SERVER["socket"] - match on socket. Value must be on the format "$ip:$port" where $ip is an IP - address and $port a port number. Only equal match (==) is supported. - It also binds to this socket. + match on socket. Value must be on the format "ip:port" where ip is an IP + address and port a port number. Only equal match (==) is supported. + It also binds the daemon to this socket. Use this if you want to do IP/port- + based virtual hosts. <operator> is one of: @@ -126,8 +127,8 @@ Example } # handish virtual hosting - # map all subdomains to a single document-root - $HTTP["host"] =~ "\.example\.org$" { + # map all domains of a top-level-domain to a single document-root + $HTTP["host"] =~ "(^|\.)example\.org$" { server.document-root = "/var/www/htdocs/example.org/pages/" } @@ -180,9 +181,16 @@ server.document-root Default: no default, required server.bind - hostname of the server + IP address, hostname or absolute path to the unix-domain socket the server + listen on. Default: bind to all interfaces + + Example: :: + + server.bind = "127.0.0.1" + server.bind = "www.example.org" + server.bind = "/tmp/lighttpd.socket" server.port tcp-port to bind the server to @@ -221,6 +229,8 @@ dir-listing.activate enables virtual directory listings if a directory is requested no index-file was found + Default: disabled + dir-listing.hide-dotfiles if enabled, does not list hidden files in directory listings generated by the dir-listing option. @@ -291,17 +301,20 @@ server.event-handler server.pid-file set the name of the .pid-file where the PID of the server should be placed. - This option is used in combination with a start-script and the deamon mode + This option is used in combination with a start-script and the daemon mode Default: not set server.max-request-size - maximum size in kbytes of the request (header + body) + maximum size in kbytes of the request (header + body). Only applies to POST + requests. - Default: 2Gb + Default: 2097152 (2GB) server.max-worker - number of worker processes to spawn (works but has no benefit) + number of worker processes to spawn. This is usually only needed on servers + which are fairly loaded and the network handler calls delay often (e.g. new + requests are not handled instantaneously). Default: 0 diff --git a/doc/fastcgi.txt b/doc/fastcgi.txt index a9ab948..2407273 100644 --- a/doc/fastcgi.txt +++ b/doc/fastcgi.txt @@ -162,41 +162,32 @@ Examples Multiple extensions for the same host :: fastcgi.server = ( ".php" => - ( "grisu" => - ( - "host" => "127.0.0.1", - "port" => 1026, - "bin-path" => "/usr/local/bin/php" - ) - ), - ".php4" => - ( "grisu" => - ( - "host" => "127.0.0.1", - "port" => 1026 - ) - ) - ) + (( "host" => "127.0.0.1", + "port" => 1026, + "bin-path" => "/usr/local/bin/php" + )), + ".php4" => + (( "host" => "127.0.0.1", + "port" => 1026 + )) + ) Example with prefix: :: fastcgi.server = ( "/remote_scripts/" => - ( "fcg" => - ( - "host" => "192.168.0.3", - "port" => 9000, - "check-local" => "disable", - "docroot" => "/" # remote server may use - # it's own docroot - ) - ) - ) + (( "host" => "192.168.0.3", + "port" => 9000, + "check-local" => "disable", + "docroot" => "/" # remote server may use + # it's own docroot + )) + ) The request `http://my.host.com/remote_scripts/test.cgi` will be forwarded to fastcgi server at 192.168.0.3 and the value "/remote_scripts/test.cgi" will be used for the SCRIPT_NAME variable. Remote server may prepend it with its own - document root. The handling of index files si also the + document root. The handling of index files is also the resposibility of remote server for this case. In the case that the prefix is not terminated with a slash @@ -207,15 +198,12 @@ Examples Example for "authorizer" mode: :: fastcgi.server = ( "/remote_scripts/" => - ( "auth" => - ( - "host" => "10.0.0.2", - "port" => 9000, - "docroot" => "/path_to_private_docs", - "mode" => "authorizer" - ) - ) - ) + (( "host" => "10.0.0.2", + "port" => 9000, + "docroot" => "/path_to_private_docs", + "mode" => "authorizer" + )) + ) Note that if "docroot" is specified then its value will be used in DOCUMENT_ROOT and SCRIPT_FILENAME variables passed @@ -228,16 +216,9 @@ The FastCGI plugin provides automaticly a load-balancing between multiple FastCGI servers. :: fastcgi.server = ( ".php" => - ( "server1" => - ( "host" => "10.0.0.3", - "port" => 1030 ), - "server2" => - ( "host" => "10.0.0.3", - "port" => 1030 ) - ) - ) - - + (( "host" => "10.0.0.2", "port" => 1030 ), + ( "host" => "10.0.0.3", "port" => 1030 )) + ) To understand how the load-balancing works you can enable the @@ -268,11 +249,11 @@ The output shows: As you can see the list is always sorted by the load field. -When ever a new connection is requested, the first entry (the one -with the lowest load) is selected, the load is increase (got proc: ...) +Whenever a new connection is requested, the first entry (the one +with the lowest load) is selected, the load is increased (got proc: ...) and the list is sorted again. -If a FastCGI request is done or the connection is drop, the load on the +If a FastCGI request is done or the connection is dropped, the load on the FastCGI proc decreases and the list is sorted again (release proc: ...) This behaviour is very light-weight in code and still very efficient @@ -303,14 +284,15 @@ Example ------- :: - fastcgi.server = ( ".php" => ( "localhost" => - ( "socket" => "/tmp/php.socket", - "bin-path" => "/usr/local/bin/php", - "min-procs" => 1, - "max-procs" => 32, - "max-load-per-proc" => 4, - "idle-timeout" => 20 ) - ) ) + fastcgi.server = ( ".php" => + (( "socket" => "/tmp/php.socket", + "bin-path" => "/usr/local/bin/php", + "min-procs" => 1, + "max-procs" => 32, + "max-load-per-proc" => 4, + "idle-timeout" => 20 + )) + ) Disabling Adaptive Spawning --------------------------- @@ -328,14 +310,15 @@ is done: $ PHP_FCGI_CHILDREN=384 ./lighttpd -f ./lighttpd.conf - fastcgi.server = ( ".php" => ( "localhost" => - ( "socket" => "/tmp/php.socket", - "bin-path" => "/usr/local/bin/php", - "min-procs" => 1, - "max-procs" => 1, - "max-load-per-proc" => 4, - "idle-timeout" => 20 ) - ) ) + fastcgi.server = ( ".php" => + (( "socket" => "/tmp/php.socket", + "bin-path" => "/usr/local/bin/php", + "min-procs" => 1, + "max-procs" => 1, + "max-load-per-proc" => 4, + "idle-timeout" => 20 + )) + ) It will create one socket and let's PHP create the 384 processes itself. @@ -388,12 +371,10 @@ Starting with version 1.3.6 lighttpd can spawn the FastCGI processes locally itself if necessary: :: fastcgi.server = ( ".php" => - ( "localhost" => - ( "socket" => "/tmp/php-fastcgi.socket", - "bin-path" => "/usr/local/bin/php" - ) - ) - ) + (( "socket" => "/tmp/php-fastcgi.socket", + "bin-path" => "/usr/local/bin/php" + )) + ) PHP provides 2 special environment variables which control the number of spawned workes under the control of a single watching process @@ -401,34 +382,28 @@ spawned workes under the control of a single watching process handles before it kills itself. :: fastcgi.server = ( ".php" => - ( "localhost" => - ( "socket" => "/tmp/php-fastcgi.socket", - "bin-path" => "/usr/local/bin/php", - "bin-environment" => ( - "PHP_FCGI_CHILDREN" => "16", - "PHP_FCGI_MAX_REQUESTS" => "10000" - ) - ) - ) - ) + (( "socket" => "/tmp/php-fastcgi.socket", + "bin-path" => "/usr/local/bin/php", + "bin-environment" => ( + "PHP_FCGI_CHILDREN" => "16", + "PHP_FCGI_MAX_REQUESTS" => "10000" + ) + )) + ) To increase the security of the started process you should only pass the necessary environment variables to the FastCGI process. :: fastcgi.server = ( ".php" => - ( "localhost" => - ( "socket" => "/tmp/php-fastcgi.socket", - "bin-path" => "/usr/local/bin/php", - "bin-environment" => ( - "PHP_FCGI_CHILDREN" => "16", - "PHP_FCGI_MAX_REQUESTS" => "10000" - ), - "bin-copy-environment" => ( - "PATH", "SHELL", "USER" - ) - ) - ) - ) + (( "socket" => "/tmp/php-fastcgi.socket", + "bin-path" => "/usr/local/bin/php", + "bin-environment" => ( + "PHP_FCGI_CHILDREN" => "16", + "PHP_FCGI_MAX_REQUESTS" => "10000" ), + "bin-copy-environment" => ( + "PATH", "SHELL", "USER" ) + )) + ) Configuring PHP --------------- @@ -441,20 +416,16 @@ configure php and lighttpd. The php.ini needs the option: :: and the option ``broken-scriptfilename`` in your fastcgi.server config: :: fastcgi.server = ( ".php" => - ( "localhost" => - ( "socket" => "/tmp/php-fastcgi.socket", - "bin-path" => "/usr/local/bin/php", - "bin-environment" => ( - "PHP_FCGI_CHILDREN" => "16", - "PHP_FCGI_MAX_REQUESTS" => "10000" - ), - "bin-copy-environment" => ( - "PATH", "SHELL", "USER" - ), - "broken-scriptfilename" => "enable" - ) - ) - ) + (( "socket" => "/tmp/php-fastcgi.socket", + "bin-path" => "/usr/local/bin/php", + "bin-environment" => ( + "PHP_FCGI_CHILDREN" => "16", + "PHP_FCGI_MAX_REQUESTS" => "10000" ), + "bin-copy-environment" => ( + "PATH", "SHELL", "USER" ), + "broken-scriptfilename" => "enable" + )) + ) Why this ? the ``cgi.fix_pathinfo = 0`` would give you a working ``PATH_INFO`` but no ``PHP_SELF``. If you enable it, it turns around. To fix the diff --git a/doc/lighttpd.conf b/doc/lighttpd.conf index 0601841..28402ea 100644 --- a/doc/lighttpd.conf +++ b/doc/lighttpd.conf @@ -1,6 +1,6 @@ # lighttpd configuration file # -# use a it as base for lighttpd 1.0.0 and above +# use it as a base for lighttpd 1.0.0 and above # # $Id: lighttpd.conf,v 1.7 2004/11/03 22:26:05 weigon Exp $ @@ -243,7 +243,7 @@ static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" ) # "realm" => "download archiv", # "require" => "user=jan" # ), -# "/server-info" => +# "/server-config" => # ( # "method" => "digest", # "realm" => "download archiv", diff --git a/doc/mysqlvhost.txt b/doc/mysqlvhost.txt index 93a08dc..9a869a1 100644 --- a/doc/mysqlvhost.txt +++ b/doc/mysqlvhost.txt @@ -1,6 +1,5 @@ - ==================== -MySQL based vhosting +MySQL-based vhosting ==================== ----------------------- @@ -12,20 +11,20 @@ Module: mod_mysql_vhost :Revision: $Revision: 1.1 $ :abstract: - This module provides virtual hosts (vhosts) based on a MySQL table - + This module provides virtual hosts (vhosts) based on a MySQL table + .. meta:: :keywords: lighttpd, mysql, vhost - + .. contents:: Table of Contents Description =========== -With MySQL based vhosting you can put the information where to look for a -document-root of a given host into a MySQL database. +With MySQL-based vhosting you can store the path to a given host's +document root in a MySQL database. -.. note:: Keep in mind that only one vhost-module should be active at a time. +.. note:: Keep in mind that only one vhost module should be active at a time. Don't mix mod_simple_vhost with mod_mysql_vhost. Options @@ -38,8 +37,8 @@ Example: :: mysql-vhost.pass = "secret" mysql-vhost.sock = "/var/mysql.lighttpd.sock" mysql-vhost.sql = "SELECT docroot FROM domains WHERE domain='?'" - - + + MySQL setup: :: GRANT SELECT ON lighttpd.* TO lighttpd@localhost IDENTIFIED BY 'secret'; diff --git a/doc/performance.txt b/doc/performance.txt index 06a767b..3a5b964 100644 --- a/doc/performance.txt +++ b/doc/performance.txt @@ -12,10 +12,10 @@ Module: core :abstract: handling performance issues in lighttpd - + .. meta:: :keywords: lighttpd, performance - + .. contents:: Table of Contents Description @@ -24,17 +24,17 @@ Description Performance Issues ------------------ -lighttpd is optimized into various directions. The most important is -performance. The operation system has two major facalities to help lighttpd -a deliver it best performance. +lighttpd is optimized into varying directions. The most important direction is +performance. The operation system has two major facilities to help lighttpd +a deliver its best performance. HTTP Keep-Alive --------------- Disabling keep-alive might help your server if you suffer from a large -number of open file-descriptors. +number of open file descriptors. -The defaults fo the server is: :: +The defaults for the server are: :: server.max-keep-alive-requests = 128 server.max-keep-alive-idle = 30 @@ -42,7 +42,7 @@ The defaults fo the server is: :: server.max-write-idle = 360 handling 128 keep-alive requests in a row on a single connection, waiting 30 seconds -before a unused keep-alive connection get dropped by lighttpd. +before an unused keep-alive connection gets dropped by lighttpd. If you handle several connections at once under a high load (let's assume 500 connections in parallel for 24h) you might run into the out-of-fd problem described below. :: @@ -50,22 +50,22 @@ in parallel for 24h) you might run into the out-of-fd problem described below. : server.max-keep-alive-requests = 4 server.max-keep-alive-idle = 4 -would release the connections earlier and would free file-descriptors without a to large -performance loss. +would release the connections earlier and would free file descriptors without a +detrimental performance loss. -Disabling keep-alive completly is the last choice if you are still short in filedescriptors: :: +Disabling keep-alive completely is the last resort if you are still short on file descriptors: :: server.max-keep-alive-requests = 0 Event Handlers -------------- -The first one is the Event Handler which cares about notifying the server -that one of the connections is ready to send or to recieve. As you can see -every OS has at least the select() call which has some limitations. +The first one is the Event Handler which takes care of notifying the server +that one of the connections is ready to send or receive. As you can see, +every OS has at least the select() call which has some limitations. ============ ========== =============== -OS Method Config-Value +OS Method Config Value ============ ========== =============== all select select Unix poll poll @@ -76,151 +76,157 @@ FreeBSD, ... kqueue freebsd-kqueue ============ ========== =============== -For more infomation in this topic take a look at http://www.kegel.com/c10k.html +For more information on this topic take a look at http://www.kegel.com/c10k.html Configuration ````````````` -The event-handler can be set by specifying the 'Config-Value' from above +The event handler can be set by specifying the 'Config Value' from above in the ``server.event-handler`` variable e.g.: :: server.event-handler = "linux-sysepoll" - + Network Handlers ---------------- The basic network interface for all platforms at the syscalls read() and -write(). Each modern OS provides its own syscall to help network servers -to transfer files as fast as possible. +write(). Every modern OS provides its own syscall to help network servers +transfer files as fast as possible. -If you want to send out a file from the webserver it does make any sense +If you want to send out a file from the webserver, it doesn't make any sense to copy the file into the webserver just to write() it back into a socket in the next step. sendfile() minimizes the work in the application and pushes a file directly -into the network card (idealy spoken). +into the network card (ideally). -lighttpd supports all major platform specific calls: +lighttpd supports all major platform-specific calls: -========== ========== -OS Method ========== ========== -all write -Unix writev -Linux 2.4+ sendfile -Linux 2.6+ sendfile64 +OS Method +========== ========== +all write +Unix writev +Linux 2.4+ sendfile +Linux 2.6+ sendfile64 Solaris sendfilev FreeBSD sendfile -========== ========== +========== ========== -They are selected automaticly on compile-time. If you have problems check +They are selected automatically at compile-time. If you have problems, check ./src/network_backend.h and disable the corresponding USE\_... define. Max Connections --------------- -As lighttpd is a single-threaded server its main resource limit is the -number of file-descriptors which is (on most systems) set to 1024 by default. +As lighttpd is a single-threaded server, its main resource limit is the +number of file descriptors, which is set to 1024 by default (on most systems). If you are running a high-traffic site you might want to increase this limit by setting :: server.max-fds = 2048 - + This only works if lighttpd is started as root. Out-of-fd condition ------------------- -As fds are used for tcp/ip sockets, files, directories, ... a simple request -for a PHP page might result in using 3 fds: +Since file descriptors are used for TCP/IP sockets, files and directories, +a simple request for a PHP page might result in using 3 file descriptors: 1. the TCP/IP socket to the client 2. the TCP/IP and Unix domain socket to the FastCGI process -3. the filehandle to the file in the document-root to check if it is really existing +3. the filehandle to the file in the document root to check if it exists -If lighttpd runs out of file-descriptors it will stop accepting new -connections for while to use the currently available fds (file-descriptors) -to handle the currently running requests. +If lighttpd runs out of file descriptors, it will stop accepting new +connections for awhile to use the existing file descriptors to handle the +currently-running requests. -If more than 90% of the fds are used the the handling of new connections is -disabled, if it dropes below 80% again new connection will accepted again. +If more than 90% of the file descriptors are used then the handling of new +connections is disabled. If it drops below 80% again new connections will +be accepted again. Under some circumstances you will see :: ... accept() failed: Too many open files -in the error-log. This tells you the you had to many new requests at once -and lighttpd could not disable the incomming connections soon enough. The -connection is drop and the client will get a error-message like 'connection -failed'. This is very rare and might only occur in test-setups. +in the error log. This tells you there were too many new requests at once +and lighttpd could not disable the incoming connections soon enough. The +connection was dropped and the client received an error message like 'connection +failed'. This is very rare and might only occur in test setups. -Increasing the ``server.max-fds`` limit will reduce the propability of this +Increasing the ``server.max-fds`` limit will reduce the probability of this problem. stat() cache ============ -A stat(2) can be expensive, caching it saves time adn context-switches.. +A stat(2) can be expensive; caching it saves time and context switches. -Instead of stat() for the existence of the file you can stat() it once and -monitor the directory the file is in for modifications. As long as the -directiry doesn't change, the files in it are all the same. +Instead of using stat() every time to check for the existence of a file +you can stat() it once and monitor the directory the file is in for +modifications. As long as the directory doesn't change, the files in it +must all still be the same. With the help of FAM or gamin you can use kernel events to assure that -your stat-cache is up to date. :: +your stat cache is up to date. :: server.stat-cache-engine = "fam" # either fam, simple or disabled -Plattform Specific Notes -======================== +Platform-Specific Notes +======================= Linux ----- -For Linux 2.4.x should should think about compiling lighttpd with the option -``--disable-lfs`` to disable the support for files larger than 2Gb. lighttpd will +For Linux 2.4.x you should think about compiling lighttpd with the option +``--disable-lfs`` to disable the support for files larger than 2GB. lighttpd will fall back to the ``writev() + mmap()`` network calls which is ok, but not as -fast as possible but support files larger than 2Gb. +fast as possible but support files larger than 2GB. Disabling the TCP options reduces the overhead of each TCP packet and might -help to get the last few percent of performance out of the server. +help to get the last few percent of performance out of the server. Be aware that +disabling these options most likely decreases performance for high-latency and lossy +links. -- net.ipv4.tcp_sack = 0 +- net.ipv4.tcp_sack = 0 - net.ipv4.tcp_timestamps = 0 Increasing the TCP send and receive buffers will increase the performance a -lot if (and only if) you have a lot large files to send. +lot if (and only if) you have a lot of large files to send. - net.ipv4.tcp_wmem = 4096 65536 524288 - net.core.wmem_max = 1048576 -If you have a lot large file uploads increasing the receive buffers will help. +If you have a lot of large file uploads, increasing the receive buffers will help. - net.ipv4.tcp_rmem = 4096 87380 524288 - net.core.rmem_max = 1048576 -Keep in mind that the buffers have to multiplied by server.max-fds and be -allocated in the Kernel area. Be carefull with that. +Keep in mind that every TCP connection uses the configured amount of memory for socket +buffers. If you've got many connections this can quickly drain the available memory. + +See http://www.acc.umu.se/~maswan/linux-netperf.txt for more information on these parameters. FreeBSD ------- -On FreeBSD you might gain some performance by enabling accept-filters. Just +On FreeBSD you might gain some performance by enabling accept filters. Just compile your kernel with: :: options ACCEPT_FILTER_HTTP -For more ideas in tuning FreeBSD read: tuning(7) +For more ideas about tuning FreeBSD read: tuning(7) Reducing the recvspace should always be ok if the server only handles HTTP requests without large uploads. Increasing the sendspace would reduce the -system-load if you have a lot large files to be sent, but keep in mind that -you to provide the memory in kernel for each connection. 1024 * 64k would mean -64M of kernel-ram. Keep this in mind. +system load if you have a lot of large files to be sent, but keep in mind that +you have to provide the memory in the kernel for each connection. 1024 * 64KB +would mean 64MB of kernel RAM. Keep this in mind. - net.inet.tcp.recvspace = 4096 diff --git a/doc/plugins.txt b/doc/plugins.txt index 8a755fe..91c81fd 100644 --- a/doc/plugins.txt +++ b/doc/plugins.txt @@ -11,8 +11,8 @@ Module: core :Revision: $Revision: 1.1 $ :abstract: - The plugin interface is the integral part of lighttpd provide - a flexible way to add specific functionality to lighttpd. + The plugin interface is an integral part of lighttpd which + provides a flexible way to add specific functionality to lighttpd. .. meta:: :keywords: lighttpd, plugins @@ -22,9 +22,9 @@ Module: core Description =========== -Plugins allow you to enhance to functionality of lighttpd without -changing the core of the webserver. They can be loaded at startup-time -and can change hardly any aspect of the behaviour of the webserver. +Plugins allow you to enhance the functionality of lighttpd without +changing the core of the webserver. They can be loaded at startup time +and can change virtually any aspect of the behaviour of the webserver. Plugin Entry Points ------------------- @@ -49,17 +49,17 @@ Serverwide hooks Connectionwide hooks ```````````````````` -Most of these hooks are call in ``http_response_prepare()`` after some -field in the connection structure are set. +Most of these hooks are called in ``http_response_prepare()`` after some +fields in the connection structure are set. :handle_uri_raw_: called after uri.path_raw, uri.authority and uri.scheme are set :handle_uri_clean_: - called after uri.path (a clean uri without .. and %20) is set + called after uri.path (a clean URI without .. and %20) is set :handle_docroot_: called at the end of the logical path handle to get a docroot :handle_subrequest_start_: - called if the physical path is setup and checked + called if the physical path is set up and checked :handle_subrequest_: called at the end of ``http_response_prepare()`` :handle_physical_path_: @@ -82,9 +82,9 @@ Plugin Interface \*_plugin_init `````````````` -Every plugin has a uniquely named function which is called after the -plugin is loaded. It is used to setup the ``plugin`` structure with -some usefull data: +Every plugin has a uniquely-named function which is called after the +plugin is loaded. It is used to set up the ``plugin`` structure with +some useful data: - name of the plugin ``name`` - all hooks @@ -99,7 +99,7 @@ of the internal plugin data. init ```` -The first real call of a plugin function is the init-hook which is used +The first real call of a plugin function is the init hook which is used to set up the internal plugin data. The internal plugin is assigned the ``data`` field mentioned in the \*_plugin_init description. @@ -123,12 +123,12 @@ set_defaults set_defaults is your entry point into the configfile parsing. It should pass a list of options to ``config_insert_values`` and check if the plugin configuration is valid. If it is not valid yet, it should -set usefull defaults or return with HANDLER_ERROR and an error message. +set useful defaults or return with HANDLER_ERROR and an error message. :returns: HANDLER_GO_ON if ok - HANDLER_ERROR will terminated lighttpd + HANDLER_ERROR will terminate lighttpd connection_reset ```````````````` diff --git a/doc/rrdtool.txt b/doc/rrdtool.txt index ce0145c..0e05cd3 100644 --- a/doc/rrdtool.txt +++ b/doc/rrdtool.txt @@ -12,17 +12,17 @@ Module: mod_rrdtool :abstract: mod_rrdtool is used to monitor the traffic and load on the webserver - + .. meta:: :keywords: lighttpd, skeleton - + .. contents:: Table of Contents Description =========== RRD_ is a system to store and display time-series data (i.e. network -bandwidth, machine-room temperature, server load average). +bandwidth, machine-room temperature, server load average). .. _RRD: http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/ @@ -33,17 +33,17 @@ rrdtool.binary path to the rrdtool binary e.g.: :: - + rrdtool.binary = "/usr/bin/rrdtool" rrdtool.db-name - filename of the rrd-database. Make sure that <rrdtool.db-name> doesn't exists - before the first run as lighttpd has to create the DB for you. + filename of the rrd-database. Make sure that <rrdtool.db-name> doesn't exist + before the first run, as lighttpd has to create the DB for you. e.g.: :: - + rrdtool.db-name = "/var/www/lighttpd.rrd" - + Generating Graphs ================= diff --git a/doc/scgi.txt b/doc/scgi.txt index ff849fe..eeb694c 100644 --- a/doc/scgi.txt +++ b/doc/scgi.txt @@ -25,7 +25,7 @@ Description The SCGI module is heavily based on the FastCGI when it comes to configuration. Only the internal protocol between server and client has been replaced. Please check the documentation -of the fastcgi module for more information. +of the FastCGI module for more information. History ======= diff --git a/doc/secdownload.txt b/doc/secdownload.txt index fb946ed..570b911 100644 --- a/doc/secdownload.txt +++ b/doc/secdownload.txt @@ -11,12 +11,12 @@ Module: mod_secdownload :Revision: $Revision: 1.1 $ :abstract: - authenticated file requests and a counter measurement against + authenticated file requests and a countermeasure against deep-linking can be achieved easily by using mod_secdownload - + .. meta:: :keywords: lighttpd, secure, fast, downloads - + .. contents:: Table of Contents Options @@ -32,77 +32,77 @@ Options Description =========== -there are multiple way to handle secured download mechanisms: +there are multiple ways to handle secured download mechanisms: -1. use the webserver and the internal HTTP-authentication -2. use the application to authenticate and send the file +1. use the webserver and the internal HTTP authentication +2. use the application to authenticate and send the file through the application - -Both way have limitations: + +Both ways have limitations: webserver: -- ``+`` fast download -- ``+`` no additional system load -- ``-`` unflexible authentication handling - +- ``+`` fast download +- ``+`` no additional system load +- ``-`` inflexible authentication handling + application: - ``+`` integrated into the overall layout - ``+`` very flexible permission management -- ``-`` the download occupies a application thread/process - -A simple way to combine the two way could be: +- ``-`` the download occupies an application thread/process + +A simple way to combine the two ways could be: 1. app authenticates user and checks permissions to download the file. -2. app redirectes user the file accessable by the webserver - for further downloading -3. the webserver transfers the file to the user +2. app redirects user to the file accessable by the webserver + for further downloading. +3. the webserver transfers the file to the user. As the webserver doesn't know anything about the permissions -used in the app the resulting URL would be available to every -user who knows the URL. - -mod_secdownload removes this problem by introducing a way to +used in the app, the resulting URL would be available to every +user who knows the URL. + +mod_secdownload removes this problem by introducing a way to authenticate a URL for a specified time. The application has to generate a token and a timestamp which are checked by the -webserver before it allows the file to be downloaded by the +webserver before it allows the file to be downloaded by the webserver. The generated URL has to have the format: <uri-prefix><token>/<timestamp-in-hex><rel-path> -<token> is a MD5 of +<token> is an MD5 of 1. a secret string (user supplied) -2. <rel-path> (startes with /) +2. <rel-path> (starts with /) 3. <timestamp-in-hex> -As you can see the token is not bound to the user at all. The -only limiting factor is the timestamp which is used to +As you can see, the token is not bound to the user at all. The +only limiting factor is the timestamp which is used to invalidate the URL after a given timeout (secdownload.timeout). .. Note:: - Be sure to choose a another secret then used in the examples - as this is the only part of the token that is not known to - the user. - - - -If the user tries to fake the URL by choosing a random token + Be sure to choose a another secret than the one used in the + examples, as this is the only part of the token that is not + known to the user. + + + +If the user tries to fake the URL by choosing a random token, status 403 'Forbidden' will be sent out. -If the timeout is reached status 408 'Request Timeout' will be -sent (this not really standard conforming but should do the -trick). +If the timeout is reached, status 408 'Request Timeout' will be +sent. (This does not really conform to the standard, but should +do the trick). -If token and timeout are valid the <rel-path> is taken and -appended at the configured (secdownload.document-root) and -passed to the normal internal file transfer functionality. -This might lead to status 200 or 404. +If token and timeout are valid, the <rel-path> is appended to +the configured (secdownload.document-root) and passed to the +normal internal file transfer functionality. This might lead to +status 200 or 404. Example ======= @@ -135,8 +135,8 @@ code for PHP should be easily adaptable to any other language: :: Webserver --------- -The server has to configured in the same way. The uri-prefix and secret have -to match: :: +The server has to be configured in the same way. The URI prefix and +secret have to match: :: server.modules = ( ..., "mod_secdownload", ... ) diff --git a/doc/security.txt b/doc/security.txt index d4e9147..ebbbf68 100644 --- a/doc/security.txt +++ b/doc/security.txt @@ -33,7 +33,7 @@ Limiting POST requests System Security --------------- -Running daemons as root will full privileges is a bad idea in general. +Running daemons as root with full privileges is a bad idea in general. lighttpd runs best without any extra privileges and runs perfectly in chroot. Change Root @@ -41,7 +41,7 @@ Change Root server.chroot = "..." -Drop root-privileges +Drop root privileges ```````````````````` server.username = "..." diff --git a/doc/setenv.txt b/doc/setenv.txt index b04b083..0238c10 100644 --- a/doc/setenv.txt +++ b/doc/setenv.txt @@ -31,7 +31,7 @@ setenv.add-environment setenv.add-response-header - add a header to the HTTP-response sent to the client + adds a header to the HTTP response sent to the client setenv.add-request-header - add a header to the HTTP-request that was received by the client + adds a header to the HTTP request that was received from the client diff --git a/doc/simple-vhost.txt b/doc/simple-vhost.txt index b0776f0..d4b4db2 100644 --- a/doc/simple-vhost.txt +++ b/doc/simple-vhost.txt @@ -23,17 +23,17 @@ Description Simple assumption: -Every virtual host is in a direction below a base directory in a path that -is the same as the name of the vhost. Below this vhost-path might be a -extra directory which is the document-root of the vhost. +Every virtual host is in a directory below a base directory in a path that +is the same as the name of the vhost. Below this vhost path might be an +extra directory which is the document root of the vhost. -The document-root for each vhost is build from three values: +The document root for each vhost is built from three values: - server-root - hostname - document-root -Either the absolute documentroot is build by :: +The complete document root is constructed either by :: server-root + hostname + document-root @@ -41,7 +41,7 @@ or if this path does not exist by :: server-root + default-host + document-root -A small example should make this thinking clean: :: +A small example should make this idea clear: :: /var/www/ /var/www/logs/ @@ -62,7 +62,8 @@ You can use symbolic links to map several hostnames to the same directory. Conditionals vs. simple-vhost ----------------------------- -You have to keep in mind that conditionals and simple-vhost interfere. :: +You have to keep in mind that conditionals and simple-vhost interfere +with one another. :: simple-vhost.server-root = "/var/www/servers/" simple-vhost.default-host = "www.example.org" @@ -72,14 +73,15 @@ You have to keep in mind that conditionals and simple-vhost interfere. :: server.document-root = "/var/www/servers/news2.example.org/pages/" } -Even if the ``server.document-root`` will be set to ``/var/www/servers/news2.example.org/pages/`` -if ``news.example.org`` is requested simple-vhost will overwrite ``server.document-root`` shortly -afterwards. +When ``news.example.org`` is requested, the ``server.document-root`` +will be set to ``/var/www/servers/news2.example.org/pages/``, but +simple-vhost will overwrite it shortly afterwards. -If ``/var/www/servers/news.example.org/pages/`` exists it will be taken, if not -``/var/www/servers/www.example.org/pages/`` will be taken as it is the default. +If ``/var/www/servers/news.example.org/pages/`` exists, that will be +used. If not, ``/var/www/servers/www.example.org/pages/`` will be taken +because it is the default. -To get them working in parallel you should use: :: +To use conditionals together with simple-vhost, you should do this: :: $HTTP["host"] !~ "^(news\.example\.org)$" { simple-vhost.server-root = "/var/www/servers/" @@ -91,17 +93,17 @@ To get them working in parallel you should use: :: server.document-root = "/var/www/servers/news2.example.org/pages/" } -It will enable simple-vhosting for all host with are not named ``news.example.org``. +It will enable simple vhosting for all hosts other than ``news.example.org``. Options ======= simple-vhost.server-root - root of the virtual hosting + root of the virtual host simple-vhost.default-host - use this hostname if the + use this hostname if the requested hostname does not have its own directory simple-vhost.document-root - path below the vhost-directory - + path below the vhost directory + diff --git a/doc/spawn-php.sh b/doc/spawn-php.sh index 73abf67..83b7b16 100755 --- a/doc/spawn-php.sh +++ b/doc/spawn-php.sh @@ -6,22 +6,22 @@ SPAWNFCGI="/home/weigon/projects/spawn-fcgi/src/spawn-fcgi" ## ABSOLUTE path to the PHP binary FCGIPROGRAM="/usr/local/bin/php" -## bind to tcp-port on localhost +## TCP port to which to bind on localhost FCGIPORT="1026" -## number of PHP childs to spawn +## number of PHP children to spawn PHP_FCGI_CHILDREN=10 -## number of request server by a single php-process until is will be restarted +## maximum number of requests a single PHP process can serve before it is restarted PHP_FCGI_MAX_REQUESTS=1000 -## IP adresses where PHP should access server connections from +## IP addresses from which PHP should access server connections FCGI_WEB_SERVER_ADDRS="127.0.0.1,192.168.2.10" -# allowed environment variables sperated by spaces +# allowed environment variables, separated by spaces ALLOWED_ENV="ORACLE_HOME PATH USER" -## if this script is run as root switch to the following user +## if this script is run as root, switch to the following user USERID=wwwrun GROUPID=wwwrun @@ -50,5 +50,5 @@ for i in $ALLOWED_ENV; do E="$E $i=${!i}" done -# clean environment and set up a new one +# clean the environment and set up a new one env - $E $EX diff --git a/doc/ssi.txt b/doc/ssi.txt index c5791a2..8761416 100644 --- a/doc/ssi.txt +++ b/doc/ssi.txt @@ -11,8 +11,8 @@ Module: mod_ssi :Revision: $Revision: 1.2 $ :abstract: - The module for server-side includes provides a compat layer for - NSCA/Apache SSI. + The module for server-side includes provides a compatability + layer for NSCA/Apache SSI. .. meta:: :keywords: lighttpd, ssi, Server-Side Includes @@ -60,7 +60,7 @@ Every ''expr'' is interpreted: Flow Control ------------ -if, elif, else and endif can be used the insert content only under special +if, elif, else and endif can only be used to insert content under special conditions. Unsupported Features diff --git a/doc/ssl.txt b/doc/ssl.txt index 81b9215..447da4e 100644 --- a/doc/ssl.txt +++ b/doc/ssl.txt @@ -11,30 +11,30 @@ Module: core :Revision: $Revision: 1.2 $ :abstract: - How to setup SSL in lighttpd - + How to set up SSL in lighttpd + .. meta:: :keywords: lighttpd, ssl - + .. contents:: Table of Contents Description =========== -lighttpd support SSLv2 and SSLv3 if it compiled against openssl. +lighttpd supports SSLv2 and SSLv3 if it is compiled against openssl. Configuration ------------- -To enable SSL for the whole server you have to provide a valid +To enable SSL for the whole server you have to provide a valid certificate and have to enable the SSL engine.:: ssl.engine = "enable" ssl.pemfile = "/path/to/server.pem" - -As SSL and named-based virtual hosting can not work together you -have to use IP-based virtual hosting if you want to run multiple -SSL-servers with one lighttpd: :: + +The HTTPS protocol does not allow you to use name-based virtual +hosting with SSL. If you want to run multiple SSL servers with +one lighttpd instance you must use IP-based virtual hosting: :: $SERVER["socket"] == "10.0.0.1:443" { ssl.engine = "enable" @@ -44,7 +44,8 @@ SSL-servers with one lighttpd: :: server.document-root = "/www/servers/www.example.org/pages/" } -If you have a .crt and a .key file cat them together into a single PEM file: +If you have a .crt and a .key file, cat them together into a +single PEM file: :: $ cat host.key host.crt > host.pem @@ -53,9 +54,9 @@ If you have a .crt and a .key file cat them together into a single PEM file: Self-Signed Certificates ------------------------ -A self-signed SSL cerifitcate can be generated with: :: - +A self-signed SSL certificate can be generated like this: :: + $ openssl req -new -x509 \ -keyout server.pem -out server.pem \ -days 365 -nodes - + diff --git a/doc/status.txt b/doc/status.txt index 3e0acab..c64e993 100644 --- a/doc/status.txt +++ b/doc/status.txt @@ -11,7 +11,7 @@ Module: mod_status :Revision: $Revision: 1.2 $ :abstract: - mod_status displays server-status and server-config + mod_status displays the server's status and configuration .. meta:: :keywords: lighttpd, server status @@ -21,15 +21,81 @@ Module: mod_status Description =========== -The server status module generates the ... +The server status module generates the status overview of the webserver. The +information covers: + +- uptime +- average throughput +- current throughput +- active connections and their state + +By default the status page is disabled to hide internal information from +unauthorized users. :: + + status.status-url = "/server-status" + +If you want to open the status page just for users from the local network +cover it in a conditional. :: + + $HTTP["remoteip"] == "10.0.0.0/8" { + status.status-url = "/server-status" + } + +Or require authorization: :: + + auth.require = ( "/server-status" => + ( "realm" ... ) ) + + +Output Format +------------- + +By default a nice looking HTML page is generated. If you append ?auto to the +status-url you can get a text version which is simpler to parse. :: + + Total Accesses: 1234 + Total kBytes: 1043 + Uptime: 1234 + BusyServers: 123 + +Total Accesses is the number of handled requests, kBytes the overall outgoing +traffic, Uptime the uptime in seconds and BusyServers the number of currently +active connections. + +The naming is kept compatible to Apache even if we have another concept and +don't start new servers for each connection. + Options ======= status.status-url + relative URL which is used to retrieve the status-page + Default: unset + Example: status.status-url = "/server-status" + +status.enable-sort + + add JavaScript which allows client-side sorting for the connection overview + + Default: enable + status.config-url + relative URL for the config page which displays the loaded modules + Default: unset + + Example: status.status-url = "/server-config" + +status.statistics-url + + relative URL for a plain-text page containing the internal statistics + + Default: unset + + Example: status.status-url = "/server-stats" + diff --git a/doc/traffic-shaping.txt b/doc/traffic-shaping.txt index 7d16638..1076686 100644 --- a/doc/traffic-shaping.txt +++ b/doc/traffic-shaping.txt @@ -11,45 +11,45 @@ Module: core :Revision: $Revision: 1.2 $ :abstract: - limiting the bandwith usage - + limiting bandwidth usage + .. meta:: :keywords: lighttpd, bandwidth limit, traffic shaping - + .. contents:: Table of Contents Description =========== -Starting with 1.3.8 lighttpd supports limiting the bandwith for a single connection -or config-context like virtual-host or URL. +Starting with 1.3.8, lighttpd supports limiting the bandwidth for +a single connection or config context like a virtual host or a URL. Options ======= :connection.kbytes-per-second: - limit the through-put for each single connection to the given + limit the throughput for each single connection to the given limit in kbyte/s - + default: 0 (no limit) :server.kbytes-per-second: - limit the through-put for all connections to the given limit + limit the throughput for all connections to the given limit in kbyte/s - - if you want to specify a limit for a special virtual server + + if you want to specify a limit for a special virtual server use: :: - + $HTTP["host"] == "www.example.org" { server.kbytes-per-second = 128 } - - which will overwrite the default for this host. - + + which will override the default for this host. + default: 0 (no limit) Additional Notes ================ -Keep in mind that a limit below 32kb/s might actually limit the traffic to 32kb/s. This -is caused by by the size of the TCP-sendbuffer. +Keep in mind that a limit below 32kb/s might actually limit the traffic to 32kb/s. This +is caused by the size of the TCP send buffer. diff --git a/doc/trigger_b4_dl.txt b/doc/trigger_b4_dl.txt index ffdfb53..f5c9d29 100644 --- a/doc/trigger_b4_dl.txt +++ b/doc/trigger_b4_dl.txt @@ -11,11 +11,11 @@ Module: mod_trigger_b4_dl :Revision: $Revision: 1.2 $ :abstract: - another anti hot-linking module - + another anti-hot-linking module + .. meta:: :keywords: lighttpd, hot-linking, deep-linking - + .. contents:: Table of Contents Description @@ -23,11 +23,11 @@ Description Anti Hotlinking: - * if user requests ''download-url'' directly the request is denied and he is redirected to ''deny-url' - * if user visits ''trigger-url'' before requesting ''download-url'' access is granted - * if user visits ''download-url'' again after ''trigger-timeout'' has run down to the request is denied and he is redirected to ''deny-url'' + * if user requests ''download-url'' directly, the request is denied and he is redirected to ''deny-url' + * if user visits ''trigger-url'' before requesting ''download-url'', access is granted + * if user visits ''download-url'' again after ''trigger-timeout'' has elapsed, the request is denied and he is redirected to ''deny-url'' -The storage for the trigger information is either stored locally in a gdbm file or remotly in memcached. +The trigger information is either stored locally in a gdbm file or remotely in memcached. Requirements ------------ @@ -47,8 +47,8 @@ Options trigger-before-download.deny-url = "http://192.168.1.5:1025/index.html" trigger-before-download.trigger-timeout = 10 -If both trigger-before-download.gdbm-filename and -trigger-before-download.memcache-hosts is set gdbm will be prefered. +If both trigger-before-download.gdbm-filename and +trigger-before-download.memcache-hosts is set gdbm will take precedence. Installation ============ diff --git a/doc/userdir.txt b/doc/userdir.txt index 65cc9d0..10ba065 100644 --- a/doc/userdir.txt +++ b/doc/userdir.txt @@ -23,8 +23,8 @@ Description The userdir module provides a simple way to link user-based directories into the global namespace of the webserver. -Requests in the form ``/~user/page.html`` are rewritten to take the file ``page.html`` from the home-directory of the user. -If ``userdir.path`` is set, the path will be appended at the home-directory +Requests in the form ``/~user/page.html`` are rewritten to take the file ``page.html`` from the home directory of the user. +If ``userdir.path`` is set, the path will be appended to the home directory building the classic mapping of: :: userdir.path = "public_html" @@ -32,9 +32,9 @@ building the classic mapping of: :: URL: http://www.example.org/~jan/index.html Path: /home/jan/public_html/ -To control which users should be able to use this feature you can set a include- or a exclude list for username. +To control which users should be able to use this feature you can set a list of usernames to include or exclude. -In case you mapping is independent of /etc/passwd you can use +In case your mapping is independent of /etc/passwd you can use ``userdir.basepath``: :: userdir.path = "htdocs" @@ -47,15 +47,15 @@ Options ======= userdir.path - usually it should set the "public_html" to take ~/public_html/ as the document-root + usually it should be set to "public_html" to take ~/public_html/ as the document root - Default: empty (document-root is the home-directory) + Default: empty (document root is the home directory) Example: :: userdir.path = "public_html" userdir.exclude-user - list of usernames which should not be able to use this feature + list of usernames which may not use this feature Default: empty (all users may use it) Example: :: @@ -66,7 +66,7 @@ userdir.exclude-user userdir.include-user if set, only users from this list may use the feature - Default: empty (all user may use it) + Default: empty (all users may use it) userdir.basepath if set, don't check /etc/passwd for homedir diff --git a/doc/webdav.txt b/doc/webdav.txt index 175f33f..b10012f 100644 --- a/doc/webdav.txt +++ b/doc/webdav.txt @@ -22,7 +22,7 @@ Description =========== The WebDAV module is a very minimalistic implementation of RFC 2518. -Minimalistic means that not all operations are implementated yet. +Minimalistic means that not all operations are implemented yet. So far we have @@ -34,15 +34,15 @@ So far we have and the usual GET, POST, HEAD from HTTP/1.1. -So far mounting a webdav resource into Windows XP works and the basic litmus +So far, mounting a WebDAV resource into Windows XP works and the basic litmus tests are passed. Options ======= webdav.activate - If you load the webdav module the WebDAV functionality has to be - enabled for the directories you want to the provide to the user. + If you load the webdav module, the WebDAV functionality has to be + enabled for the directories you want to provide to the user. Default: disable @@ -54,9 +54,9 @@ webdav.is-readonly Examples ======== -To enable WebDAV for the /dav directory you take a conditional and wrap around -your webdav options. You have to use the regex like below as you want to match -the directory /dav and everything below it, but not /davos. :: +To enable WebDAV for the /dav directory, you wrap your webdav options in +a conditional. You have to use the regex like below as you want to match +the directory /dav and everything below it, but not e.g. /davos. :: $HTTP["url"] =~ "^/dav($|/)" { webdav.activate = "enable" diff --git a/lighttpd.spec b/lighttpd.spec index 3dbc637..70c8278 100644 --- a/lighttpd.spec +++ b/lighttpd.spec @@ -1,6 +1,6 @@ Summary: A fast webserver with minimal memory-footprint (lighttpd) Name: lighttpd -Version: 1.4.8 +Version: 1.4.9 Release: 1 Source: http://jan.kneschke.de/projects/lighttpd/download/lighttpd-%version.tar.gz Packager: Jan Kneschke <jan@kneschke.de> @@ -48,8 +48,8 @@ install -m 644 doc/sysconfig.lighttpd %{buildroot}%{_sysconfdir}/sysconfig/light rm -rf %{buildroot} %post - -if test "$1" = "0"; then +## read http://www.fedora.us/docs/spec.html next time :) +if test "$1" = "1"; then # real install, not upgrade /sbin/chkconfig --add lighttpd fi diff --git a/lighttpd.spec.in b/lighttpd.spec.in index 2eab259..182297d 100644 --- a/lighttpd.spec.in +++ b/lighttpd.spec.in @@ -48,8 +48,8 @@ install -m 644 doc/sysconfig.lighttpd %{buildroot}%{_sysconfdir}/sysconfig/light rm -rf %{buildroot} %post - -if test "$1" = "0"; then +## read http://www.fedora.us/docs/spec.html next time :) +if test "$1" = "1"; then # real install, not upgrade /sbin/chkconfig --add lighttpd fi diff --git a/openwrt/control b/openwrt/control index d7a7de3..f3acf51 100644 --- a/openwrt/control +++ b/openwrt/control @@ -1,8 +1,8 @@ Package: lighttpd -Version: 1.4.8 +Version: 1.4.9 Architecture: mipsel Maintainer: Jan Kneschke <jan@kneschke.de> -Source: http://jan.kneschke.de/projects/lighttpd/download/lighttpd-1.4.8.tar.gz +Source: http://jan.kneschke.de/projects/lighttpd/download/lighttpd-1.4.9.tar.gz Section: net Priority: optional Depends: diff --git a/openwrt/lighttpd.mk b/openwrt/lighttpd.mk index 08debee..0abc37f 100644 --- a/openwrt/lighttpd.mk +++ b/openwrt/lighttpd.mk @@ -10,7 +10,7 @@ # For this example we'll use a fairly simple package that compiles easily # and has sources available for download at sourceforge -LIGHTTPD=lighttpd-1.4.8 +LIGHTTPD=lighttpd-1.4.9 LIGHTTPD_TARGET=.built LIGHTTPD_DIR=$(BUILD_DIR)/$(LIGHTTPD) LIGHTTPD_IPK=$(BUILD_DIR)/$(LIGHTTPD)_mipsel.ipk 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 */ diff --git a/tests/LightyTest.pm b/tests/LightyTest.pm index 37023d3..85a08ef 100755 --- a/tests/LightyTest.pm +++ b/tests/LightyTest.pm @@ -158,6 +158,8 @@ sub handle_http { close $remote; + my $full_response = $lines; + my $href; foreach $href ( @{ $t->{RESPONSE} }) { # first line is always response header @@ -181,13 +183,12 @@ sub handle_http { if ($line =~ /^([^:]+):\s*(.+)$/) { (my $h = $1) =~ tr/[A-Z]/[a-z]/; -# if (defined $resp_hdr{$h}) { -# diag(sprintf("header %s is duplicated: %s and %s\n", -# $h, $resp_hdr{$h}, $2)); -# return -1; -# } - - $resp_hdr{$h} = $2; + if (defined $resp_hdr{$h}) { + diag(sprintf("header %s is duplicated: %s and %s\n", + $h, $resp_hdr{$h}, $2)); + } else { + $resp_hdr{$h} = $2; + } } else { diag(sprintf("unexpected line '$line'\n")); return -1; diff --git a/tests/docroot/www/dummydir/.svn/README.txt b/tests/docroot/www/dummydir/.svn/README.txt deleted file mode 100644 index 271a8ce..0000000 --- a/tests/docroot/www/dummydir/.svn/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -This is a Subversion working copy administrative directory. -Visit http://subversion.tigris.org/ for more information. diff --git a/tests/docroot/www/dummydir/.svn/empty-file b/tests/docroot/www/dummydir/.svn/empty-file deleted file mode 100644 index e69de29..0000000 --- a/tests/docroot/www/dummydir/.svn/empty-file +++ /dev/null diff --git a/tests/docroot/www/dummydir/.svn/entries b/tests/docroot/www/dummydir/.svn/entries deleted file mode 100644 index d0722cd..0000000 --- a/tests/docroot/www/dummydir/.svn/entries +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<wc-entries - xmlns="svn:"> -<entry - committed-rev="1" - name="" - committed-date="2005-02-12T17:08:04.106079Z" - url="svn://svn.lighttpd.net/lighttpd/branches/lighttpd-merge-1.4.x/tests/docroot/www/dummydir" - last-author="jan" - kind="dir" - uuid="152afb58-edef-0310-8abb-c4023f1b3aa9" - revision="803"/> -</wc-entries> diff --git a/tests/docroot/www/dummydir/.svn/format b/tests/docroot/www/dummydir/.svn/format deleted file mode 100644 index b8626c4..0000000 --- a/tests/docroot/www/dummydir/.svn/format +++ /dev/null @@ -1 +0,0 @@ -4 diff --git a/tests/fastcgi-13.conf b/tests/fastcgi-13.conf index 2d6a2bd..f351208 100644 --- a/tests/fastcgi-13.conf +++ b/tests/fastcgi-13.conf @@ -8,8 +8,6 @@ debug.log-request-handling = "enable" ## bind to port (default: 80) server.port = 2048 -# server.license = "00000001000000013feccb804014587f000000010000000105911c976a3d462c8eaa2d7ca850432c" - ## bind to localhost (default: all interfaces) server.bind = "localhost" server.errorlog = "@SRCDIR@/tmp/lighttpd/logs/lighttpd.error.log" @@ -87,7 +85,7 @@ fastcgi.server = ( ".php" => ( "grisu" => ( "host" => "127.0.0.1", "port" => 1048, - "bin-path" => "/home/jan/Documents/php-5.1.0b3/sapi/cgi/php -c /usr/local/lib/php.ini", + "bin-path" => "/home/jan/Documents/php-5.1.0/sapi/cgi/php -c /usr/local/lib/php.ini", "bin-copy-environment" => ( "PATH", "SHELL", "USER" ), ) ) diff --git a/tests/mod-fastcgi.t b/tests/mod-fastcgi.t index c6acec7..d3ea4a8 100755 --- a/tests/mod-fastcgi.t +++ b/tests/mod-fastcgi.t @@ -216,7 +216,7 @@ EOF } SKIP: { - skip "no php found", 4 unless -x "/home/jan/Documents/php-5.1.0b3/sapi/cgi/php"; + skip "no php found", 4 unless -x "/home/jan/Documents/php-5.1.0/sapi/cgi/php"; $tf->{CONFIGFILE} = 'fastcgi-13.conf'; ok($tf->start_proc == 0, "Starting lighttpd with $tf->{CONFIGFILE}") or die(); $t->{REQUEST} = ( <<EOF diff --git a/tests/request.t b/tests/request.t index 4049013..be1f5d8 100755 --- a/tests/request.t +++ b/tests/request.t @@ -8,7 +8,7 @@ BEGIN { use strict; use IO::Socket; -use Test::More tests => 29; +use Test::More tests => 33; use LightyTest; my $tf = LightyTest->new(); @@ -300,7 +300,45 @@ EOF $t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ]; ok($tf->handle_http($t) == 0, 'GET, Range with range-requests-disabled'); +$t->{REQUEST} = ( <<EOF +GET / HTTP/1.0 +Content-Length: 4 + +1234 +EOF + ); +$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 400 } ]; +ok($tf->handle_http($t) == 0, 'GET with Content-Length'); + +$t->{REQUEST} = ( <<EOF +OPTIONS / HTTP/1.0 +Content-Length: 4 + +1234 +EOF + ); +$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 400 } ]; +ok($tf->handle_http($t) == 0, 'OPTIONS with Content-Length'); +$t->{REQUEST} = ( <<EOF +HEAD / HTTP/1.0 +Content-Length: 4 + +1234 +EOF + ); +$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 400 } ]; +ok($tf->handle_http($t) == 0, 'HEAD with Content-Length'); + + +$t->{REQUEST} = ( <<EOF +GET / HTTP/1.0 +If-Modified-Since: Sun, 1970 Jan 01 00:00:01 GMT +If-Modified-Since: Sun, 1970 Jan 01 00:00:01 GMT +EOF + ); +$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ]; +ok($tf->handle_http($t) == 0, 'Duplicate If-Mod-Since, with equal timestamps'); ok($tf->stop_proc == 0, "Stopping lighttpd"); |