debian/0000775000000000000000000000000012310202747007166 5ustar debian/nginx-common.nginx.init0000664000000000000000000001102112305451330013577 0ustar #!/bin/sh ### BEGIN INIT INFO # Provides: nginx # Required-Start: $local_fs $remote_fs $network $syslog $named # Required-Stop: $local_fs $remote_fs $network $syslog $named # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: starts the nginx web server # Description: starts nginx using start-stop-daemon ### END INIT INFO PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin DAEMON=/usr/sbin/nginx NAME=nginx DESC=nginx # Include nginx defaults if available if [ -r /etc/default/nginx ]; then . /etc/default/nginx fi test -x $DAEMON || exit 0 . /lib/init/vars.sh . /lib/lsb/init-functions PID=$(awk -F'[ \t;]+' '/[^#]pid/ {print $2}' /etc/nginx/nginx.conf) if [ -z "$PID" ] then PID=/run/nginx.pid fi # Check if the ULIMIT is set in /etc/default/nginx if [ -n "$ULIMIT" ]; then # Set the ulimits ulimit $ULIMIT fi # # Function that starts the daemon/service # do_start() { # Return # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON --test > /dev/null \ || return 1 start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON -- \ $DAEMON_OPTS 2>/dev/null \ || return 2 } test_nginx_config() { $DAEMON -t $DAEMON_OPTS >/dev/null 2>&1 } # # Function that stops the daemon/service # do_stop() { # Return # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # other if a failure occurred start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PID --name $NAME RETVAL="$?" sleep 1 return "$RETVAL" } # # Function that sends a SIGHUP to the daemon/service # do_reload() { start-stop-daemon --stop --signal HUP --quiet --pidfile $PID --name $NAME return 0 } # # Rotate log files # do_rotate() { start-stop-daemon --stop --signal USR1 --quiet --pidfile $PID --name $NAME return 0 } # # Online upgrade nginx executable # # "Upgrading Executable on the Fly" # http://nginx.org/en/docs/control.html # do_upgrade() { # Return # 0 if nginx has been successfully upgraded # 1 if nginx is not running # 2 if the pid files were not created on time # 3 if the old master could not be killed if start-stop-daemon --stop --signal USR2 --quiet --pidfile $PID --name $NAME; then # Wait for both old and new master to write their pid file while [ ! -s "${PID}.oldbin" ] || [ ! -s "${PID}" ]; do cnt=`expr $cnt + 1` if [ $cnt -gt 10 ]; then return 2 fi sleep 1 done # Everything is ready, gracefully stop the old master if start-stop-daemon --stop --signal QUIT --quiet --pidfile "${PID}.oldbin" --name $NAME; then return 0 else return 3 fi else return 1 fi } case "$1" in start) [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" do_start case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; stop) [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" do_stop case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; restart) log_daemon_msg "Restarting $DESC" "$NAME" # Check configuration before stopping nginx if ! test_nginx_config; then log_end_msg 1 # Configuration error exit 0 fi do_stop case "$?" in 0|1) do_start case "$?" in 0) log_end_msg 0 ;; 1) log_end_msg 1 ;; # Old process is still running *) log_end_msg 1 ;; # Failed to start esac ;; *) # Failed to stop log_end_msg 1 ;; esac ;; reload|force-reload) log_daemon_msg "Reloading $DESC configuration" "$NAME" # Check configuration before reload nginx # # This is not entirely correct since the on-disk nginx binary # may differ from the in-memory one, but that's not common. # We prefer to check the configuration and return an error # to the administrator. if ! test_nginx_config; then log_end_msg 1 # Configuration error exit 0 fi do_reload log_end_msg $? ;; configtest|testconfig) log_daemon_msg "Testing $DESC configuration" test_nginx_config log_end_msg $? ;; status) status_of_proc -p $PID "$DAEMON" "$NAME" && exit 0 || exit $? ;; upgrade) log_daemon_msg "Upgrading binary" "$NAME" do_upgrade log_end_msg 0 ;; rotate) log_daemon_msg "Re-opening $DESC log files" "$NAME" do_rotate log_end_msg $? ;; *) echo "Usage: $NAME {start|stop|restart|reload|force-reload|status|configtest|rotate|upgrade}" >&2 exit 3 ;; esac : debian/nginx-extras.lintian-overrides0000664000000000000000000000007612305451330015176 0ustar nginx-extras: spelling-error-in-binary usr/sbin/nginx tEH the debian/nginx-common.nginx.service0000664000000000000000000000064212305451330014303 0ustar [Unit] Description=A high performance web server and a reverse proxy server After=network.target [Service] Type=forking PIDFile=/run/nginx.pid ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;' ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;' ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload ExecStop=/usr/sbin/nginx -s quit [Install] WantedBy=multi-user.target debian/nginx-full.lintian-overrides0000664000000000000000000000007412305451330014630 0ustar nginx-full: spelling-error-in-binary usr/sbin/nginx tEH the debian/nginx-naxsi-ui.postinst0000664000000000000000000000051712305451330013652 0ustar #!/bin/sh set -e case "$1" in configure) if dpkg --compare-versions "$2" lt 1.4.1; then cp /etc/nginx/naxsi-ui.conf.1.4.1 /etc/nginx/naxsi-ui.conf fi ;; abort-upgrade|abort-remove|abort-deconfigure) ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac #DEBHELPER# exit 0 debian/nginx-common.nginx.default0000664000000000000000000000036012305451330014264 0ustar # Note: You may want to look at the following page before setting the ULIMIT. # http://wiki.nginx.org/CoreModule#worker_rlimit_nofile # Set the ulimit variable if you need defaults to change. # Example: ULIMIT="-n 4096" #ULIMIT="-n 4096" debian/ufw/0000775000000000000000000000000012305451341007767 5ustar debian/ufw/nginx0000664000000000000000000000056612305451337011051 0ustar [Nginx HTTP] title=Web Server (Nginx, HTTP) description=Small, but very powerful and efficient web server ports=80/tcp [Nginx HTTPS] title=Web Server (Nginx, HTTPS) description=Small, but very powerful and efficient web server ports=443/tcp [Nginx Full] title=Web Server (Nginx, HTTP + HTTPS) description=Small, but very powerful and efficient web server ports=80,443/tcp debian/nginx-naxsi-ui.manpages0000664000000000000000000000006612305451330013561 0ustar debian/naxsi-ui-intercept.1 debian/naxsi-ui-extract.1 debian/nginx-common.install0000664000000000000000000000013512305451330013164 0ustar debian/conf/* etc/nginx debian/ufw/nginx etc/ufw/applications.d html/* usr/share/nginx/html/ debian/upstream/0000775000000000000000000000000012305451341011026 5ustar debian/upstream/signing-key.asc0000664000000000000000000000373112305375534013757 0ustar -----BEGIN PGP PUBLIC KEY BLOCK----- mQENBE7SKu8BCADQo6x4ZQfAcPlJMLmL8zBEBUS6GyKMMMDtrTh3Yaq481HB54oR 0cpKL05Ff9upjrIzLD5TJUCzYYM9GQOhguDUP8+ZU9JpSz3yO2TvH7WBbUZ8FADf hblmmUBLNgOWgLo3W+FYhl3mz1GFS2Fvid6Tfn02L8CBAj7jxbjL1Qj/OA/WmLLc m6BMTqI7IBlYW2vyIOIHasISGiAwZfp0ucMeXXvTtt14LGa8qXVcFnJTdwbf03AS ljhYrQnKnpl3VpDAoQt8C68YCwjaNJW59hKqWB+XeIJ9CW98+EOAxLAFszSyGanp rCqPd0numj9TIddjcRkTA/ZbmCWK+xjpVBGXABEBAAG0IU1heGltIERvdW5pbiA8 bWRvdW5pbkBtZG91bmluLnJ1PohGBBARAgAGBQJO01Y/AAoJEOzw6QssFyCDVyQA n3qwTZlcZgyyzWu9Cs8gJ0CXREaSAJ92QjGLT9DijTcbB+q9OS/nl16Z/IhGBBAR AgAGBQJO02JDAAoJEKk3YTmlJMU+P64AnjCKEXFelSVMtgefJk3+vpyt3QX1AKCH 9M3MbTWPeDUL+MpULlfdyfvjj4heBBARCAAGBQJRCTwgAAoJEFGFCWhsfl6CzF0B AJsQ3DJbtGcZ+0VIcM2a06RRQfBvIHqm1A/1WSYmObLGAP90lxWlNjSugvUUlqTk YEEgRTGozgixSyMWGJrNwqgMYokBOAQTAQIAIgUCTtIq7wIbAwYLCQgHAwIGFQgC CQoLBBYCAwECHgECF4AACgkQUgqZk6HAUvj+iwf/b4FS6zVzJ5T0v1vcQGD4ZzXe D5xMC4BJW414wVMU15rfX7aCdtoCYBNiApPxEd7SwiyxWRhRA9bikUq87JEgmnyV 0iYbHZvCvc1jOkx4WR7E45t1Mi29KBoPaFXA9X5adZkYcOQLDxa2Z8m6LGXnlF6N tJkxQ8APrjZsdrbDvo3HxU9muPcq49ydzhgwfLwpUs11LYkwB0An9WRPuv3jporZ /XgI6RfPMZ5NIx+FRRCjn6DnfHboY9rNF6NzrOReJRBhXCi6I+KkHHEnMoyg8XET 9lVkfHTOl81aIZqrAloX3/00TkYWyM2zO9oYpOg6eUFCX/Lw4MJZsTcT5EKVxLkB DQRO0irvAQgA0LjCc8S6oZzjiap2MjRNhRFA5BYjXZRZBdKF2VP74avt2/RELq8G W0n7JWmKn6vvrXabEGLyfkCngAhTq9tJ/K7LPx/bmlO5+jboO/1inH2BTtLiHjAX vicXZk3oaZt2Sotx5mMI3yzpFQRVqZXsi0LpUTPJEh3oS8IdYRjslQh1A7P5hfCZ wtzwb/hKm8upODe/ITUMuXeWfLuQj/uEU6wMzmfMHb+jlYMWtb+v98aJa2FODeKP mWCXLa7bliXp1SSeBOEfIgEAmjM6QGlDx5sZhr2Ss2xSPRdZ8DqD7oiRVzmstX1Y oxEzC0yXfaefC7SgM0nMnaTvYEOYJ9CH3wARAQABiQEfBBgBAgAJBQJO0irvAhsM AAoJEFIKmZOhwFL4844H/jo8icCcS6eOWvnen7lg0FcCo1fIm4wW3tEmkQdchSHE CJDq7pgTloN65pwB5tBoT47cyYNZA9eTfJVgRc74q5cexKOYrMC3KuAqWbwqXhkV s0nkWxnOIidTHSXvBZfDFA4Idwte94Thrzf8Pn8UESudTiqrWoCBXk2UyVsl03gJ blSJAeJGYPPeo+Yj6m63OWe2+/S2VTgmbPS/RObn0Aeg7yuff0n5+ytEt2KL51gO QE2uIxTCawHr12PsllPkbqPk/PagIttfEJqn9b0CrqPC3HREePb2aMJ/Ctw/76CO wn0mtXeIXLCTvBmznXfaMKllsqbsy2nCJ2P2uJjOntw= =ISka -----END PGP PUBLIC KEY BLOCK----- debian/nginx-core.dirs0000664000000000000000000000001112310175122012106 0ustar usr/sbin debian/nginx-common.dirs0000664000000000000000000000022012305451330012452 0ustar etc/nginx etc/nginx/sites-available etc/nginx/sites-enabled etc/nginx/conf.d etc/ufw/applications.d usr/share/nginx var/log/nginx var/lib/nginx debian/nginx-full.prerm0000664000000000000000000000050412305451330012315 0ustar #!/bin/sh set -e case "$1" in remove|remove-in-favour|deconfigure|deconfigure-in-favour) if [ -x /etc/init.d/nginx ]; then invoke-rc.d nginx stop || exit $? fi ;; upgrade|failed-upgrade) ;; *) echo "prerm called with unknown argument \`$1'" >&2 exit 1 ;; esac #DEBHELPER# exit 0 debian/copyright0000664000000000000000000001203412305451330011117 0ustar Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: Nginx Source: http://nginx.net/ Upstream-Contact: Igor Sysoev Files: * Copyright: 2002-2013 Igor Sysoev 2011-2013 Nginx, Inc. License: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: . 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. . THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Files: contrib/geo2nginx.pl Copyright: 2005, Andrei Nigmatulin License: Same as upstream license. Files: debian/modules/nginx-upstream-fair/ngx_http_upstream_fair_module.c Copyright: 2007, Igor Sysoev, Grzegorz Nosek License: Same as upstream license. Files: debian/modules/nginx-development-kit/* Copyright: Marcus Clyne License: Same as upstream license. Files: debian/modules/nginx-http-push/* Copyright: 2009, Leo Ponomarev License: Same as upstream license. Files: debian/modules/nginx-upload-progress/ngx_http_uploadprogress_module.c Copyright: 2007, Brice Figureau 2002-2007, Igor Sysoev License: Same as upstream license. Files: debian/modules/agentzh-chunkin-nginx-module-*/* debian/modules/agentzh-headers-more-nginx-module-*/* Copyright: 2009, Taobao Inc., Alibaba Group . 2009-2011, Zhang "agentzh" Yichun (章亦春) . License: Same as upstream license. Files: debian/modules/nginx-auth-pam/* Copyright: 2008-2010, Sergio Talens Oliag License: Same as upstream license. Files: debian/modules/nginx-cache-purge Copyright: 2009-2011, FRiCKLE , 2009-2011, Piotr Sikora License: Same as upstream license. Files: debian/modules/nginx-dav-ext-module Copyright: 2012, Arutyunyan Roman License: Same as upstream license. Files: debian/modules/naxsi Copyright: 2012, Thibault Koechlin License: GPL-2+ with OpenSSL exception Files: debian/modules/naxsi/contrib/fp-reporter/fp-reporter.php Copyright: 2011, Thibault 'bui' Koechlin, Didier Conchaudron License: GPL-2+ with OpenSSL exception License: GPL-2+ with OpenSSL exception This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. . You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. . This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . You should have received a copy of the GNU General Public License along with this program. If not, see . . On Debian systems, the complete text of the GNU General Public Licenses can be found in "/usr/share/common-licenses/GPL-2" and "/usr/share/common-licenses/GPL-3" Files: debian/* Copyright: 2006-2010, Jose Parrella , Fabio Tranchitella , 2009-2013, Kartik Mistry , 2012-2013, Michael Lustfield , Dmitry E. Oboukhov , Cyril Lavier . License: Same as upstream license. debian/nginx-naxsi-ui.nginx-naxsi-ui.init0000664000000000000000000000512412305451330015606 0ustar #!/bin/sh ### BEGIN INIT INFO # Provides: nginx-naxsi-ui # Required-Start: $local_fs $remote_fs $network $syslog nginx # Required-Stop: $local_fs $remote_fs $network $syslog nginx # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: starts the nginx web server # Description: starts nginx-naxsi-ui using start-stop-daemon ### END INIT INFO PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin DAEMON_intercept=/usr/sbin/naxsi-ui-intercept DAEMON_extract=/usr/sbin/naxsi-ui-extract NAME=nginx-naxsi-ui NAME_intercept=nginx-naxsi-ui_intercept NAME_extract=nginx-naxsi-ui_extract DESC=nginx-naxsi-ui # Include nginx defaults if available if [ -f /etc/default/nginx-naxsi-ui ]; then . /etc/default/nginx-naxsi-ui fi test -x $DAEMON_intercept || exit 0 test -x $DAEMON_extract || exit 0 set -e . /lib/lsb/init-functions case "$1" in start) echo -n "Starting $DESC: " # Check if the ULIMIT is set in /etc/default/nginx if [ -n "$ULIMIT" ]; then # Set the ulimits ulimit $ULIMIT fi start-stop-daemon --start --quiet --pidfile /run/$NAME_intercept.pid \ --exec /usr/bin/daemon -- $DAEMON_intercept || true echo "$NAME_intercept." start-stop-daemon --start --quiet --pidfile /run/$NAME_extract.pid \ --exec /usr/bin/daemon -- $DAEMON_extract || true echo "$NAME_extract." ;; stop) echo -n "Stopping $DESC: " start-stop-daemon --stop --quiet --pidfile /run/$NAME_extract.pid && rm /run/$NAME_extract.pid || true echo "$NAME_extract." start-stop-daemon --stop --quiet --pidfile /run/$NAME_intercept.pid && rm /run/$NAME_intercept.pid || true echo "$NAME_intercept." ;; restart|force-reload|reload) echo -n "Restarting $DESC: " start-stop-daemon --stop --quiet --pidfile /run/$NAME_extract.pid && rm /run/$NAME_extract.pid || true echo "$NAME_extract." start-stop-daemon --stop --quiet --pidfile /run/$NAME_intercept.pid && rm /run/$NAME_intercept.pid || true echo "$NAME_intercept." sleep 1 start-stop-daemon --start --quiet --pidfile /run/$NAME_intercept.pid \ --exec /usr/bin/daemon -- $DAEMON_intercept || true echo "$NAME_intercept." start-stop-daemon --start --quiet --pidfile /run/$NAME_extract.pid \ --exec /usr/bin/daemon -- $DAEMON_extract || true echo "$NAME_extract." ;; status) (status_of_proc -p /run/$NAME_extract.pid "$DAEMON_extract" nginx-naxsi-ui_extract && status_of_proc -p /run/$NAME_intercept.pid "$DAEMON_intercept" nginx-naxsi-ui_intercept) && exit 0 || exit $? ;; *) echo "Usage: $NAME {start|stop|restart|reload|force-reload|status}" >&2 exit 1 ;; esac exit 0 debian/po/0000775000000000000000000000000012305451341007604 5ustar debian/po/ja.po0000664000000000000000000000216512305451337010547 0ustar # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # victory , 2012. # msgid "" msgstr "" "Project-Id-Version: nginx\n" "Report-Msgid-Bugs-To: nginx@packages.debian.org\n" "POT-Creation-Date: 2012-11-06 14:30+0000\n" "PO-Revision-Date: 2012-11-06 23:30+0900\n" "Last-Translator: victory \n" "Language-Team: Japanese \n" "Language: ja\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "Database host for naxsi:" msgstr "naxsi 用のデータベースホスト:" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "" "Please specify the hostname of the server that will host the database for " "the naxsi web application firewall." msgstr "" "naxsi ウェブアプリケーションファイアウォール用のデータベースのホストとなるサー" "バのホスト名を指定してください。" debian/po/pt_BR.po0000664000000000000000000000221512305451337011157 0ustar # Debconf translations for nginx. # Copyright (C) 2012 THE nginx'S COPYRIGHT HOLDER # This file is distributed under the same license as the nginx package. # Adriano Rafael Gomes , 2012. # msgid "" msgstr "" "Project-Id-Version: nginx 1.2.4-2\n" "Report-Msgid-Bugs-To: nginx@packages.debian.org\n" "POT-Creation-Date: 2012-06-11 08:18+0200\n" "PO-Revision-Date: 2012-11-15 20:04-0200\n" "Last-Translator: Adriano Rafael Gomes \n" "Language-Team: Brazilian Portuguese \n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:2001 msgid "Database host for naxsi:" msgstr "Máquina do banco de dados para o naxsi:" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:2001 msgid "" "Please specify the hostname of the server that will host the database for " "the naxsi web application firewall." msgstr "" "Por favor, especifique o nome de máquina do servidor que hospedará o banco " "de dados para o firewall de aplicativos web naxsi." debian/po/de.po0000664000000000000000000000217112305451337010542 0ustar # Template PO for nginx-naxsi-ui. # Copyright (C) 2012 Cyril Lavier # This file is distributed under the same license as the nginx-naxsi-ui package. # Copyright (C) of this file 2012 Chris Leick. # msgid "" msgstr "" "Project-Id-Version: nginx 1.2.0-2\n" "Report-Msgid-Bugs-To: nginx@packages.debian.org\n" "POT-Creation-Date: 2012-11-04 09:03+0530\n" "PO-Revision-Date: 2012-06-13 23:11+0200\n" "Last-Translator: Chris Leick \n" "Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "Database host for naxsi:" msgstr "Datenbankrechner für Naxsi:" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "" "Please specify the hostname of the server that will host the database for " "the naxsi web application firewall." msgstr "" "Bitte geben Sie den Rechnernamen des Servers an, der die Datenbank der Web-" "Anwendungs-Firewall Naxsi beherbergen soll." debian/po/cs.po0000664000000000000000000000214712305451337010562 0ustar # Czech PO debconf template translation of nginx. # Copyright (C) 2012 Michal Simunek # This file is distributed under the same license as the nginx package. # Michal Simunek , 2012. # msgid "" msgstr "" "Project-Id-Version: nginx 1.2.0-2\n" "Report-Msgid-Bugs-To: nginx@packages.debian.org\n" "POT-Creation-Date: 2012-11-04 09:03+0530\n" "PO-Revision-Date: 2012-06-23 11:31+0200\n" "Last-Translator: Michal Simunek \n" "Language-Team: Czech \n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "Database host for naxsi:" msgstr "Hostitel databáze pro naxsi:" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "" "Please specify the hostname of the server that will host the database for " "the naxsi web application firewall." msgstr "" "Zadejte prosím název hostitele serveru, který bude hostovat databázi " "webového firewallu naxsi." debian/po/ru.po0000664000000000000000000000255412305451337010605 0ustar # Template PO for nginx-naxsi-ui. # Copyright (C) 2012 Cyril Lavier # This file is distributed under the same license as the nginx-naxsi-ui package. # # Cyril Lavier , 2012. # Yuri Kozlov , 2012. msgid "" msgstr "" "Project-Id-Version: nginx 1.2.0-2\n" "Report-Msgid-Bugs-To: nginx@packages.debian.org\n" "POT-Creation-Date: 2012-11-04 09:03+0530\n" "PO-Revision-Date: 2012-06-12 09:32+0400\n" "Last-Translator: Yuri Kozlov \n" "Language-Team: Russian \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Lokalize 1.2\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "Database host for naxsi:" msgstr "Узел для базы данных naxsi:" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "" "Please specify the hostname of the server that will host the database for " "the naxsi web application firewall." msgstr "" "Укажите имя узла сервера, на котором будет размещена база данных веб-" "приложения межсетевого экрана naxsi." debian/po/gl.po0000664000000000000000000000223012305451337010550 0ustar # Galician translations for nginx package. # Copyright (C) 2012 THE nginx'S COPYRIGHT HOLDER # This file is distributed under the same license as the nginx package. # # Jorge Barreiro Gonzalez , 2012. msgid "" msgstr "" "Project-Id-Version: nginx\n" "Report-Msgid-Bugs-To: nginx@packages.debian.org\n" "POT-Creation-Date: 2012-11-04 09:03+0530\n" "PO-Revision-Date: 2012-06-25 01:23+0200\n" "Last-Translator: Jorge Barreiro \n" "Language-Team: Galician \n" "Language: gl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Lokalize 1.0\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "Database host for naxsi:" msgstr "Servidor de base de datos para «naxsi»:" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "" "Please specify the hostname of the server that will host the database for " "the naxsi web application firewall." msgstr "" "Indique o nome do servidor que aloxará a base de datos para a devasa do " "aplicativo web «naxsi»," debian/po/pl.po0000664000000000000000000000232312305451337010564 0ustar # Translation of nginx debconf templates to Polish. # Copyright (C) 2012 # This file is distributed under the same license as the nginx package. # # Michał Kułach , 2012. msgid "" msgstr "" "Project-Id-Version: nginx\n" "Report-Msgid-Bugs-To: nginx@packages.debian.org\n" "POT-Creation-Date: 2012-11-04 09:03+0530\n" "PO-Revision-Date: 2012-06-14 19:46+0200\n" "Last-Translator: Michał Kułach \n" "Language-Team: Polish \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Lokalize 1.2\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2);\n" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "Database host for naxsi:" msgstr "Komputer z bazą danych do naxsi:" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "" "Please specify the hostname of the server that will host the database for " "the naxsi web application firewall." msgstr "" "Proszę podać nazwę serwera, który będzie przechowywał bazę danych do zapory " "sieciowej aplikacji WWW naxci." debian/po/sv.po0000664000000000000000000000221412305451337010600 0ustar # Translation of nginx debconf template to Swedish # Copyright (C) 2012 Martin Bagge # This file is distributed under the same license as the nginx package. # # Martin Bagge , 2012 msgid "" msgstr "" "Project-Id-Version: nginx\n" "Report-Msgid-Bugs-To: nginx@packages.debian.org\n" "POT-Creation-Date: 2012-11-04 09:03+0530\n" "PO-Revision-Date: 2012-06-11 11:08+0100\n" "Last-Translator: Martin Bagge / brother \n" "Language-Team: Swedish \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-Language: Swedish\n" "X-Poedit-Country: Sweden\n" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "Database host for naxsi:" msgstr "Databasvärd för naxsi:" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "" "Please specify the hostname of the server that will host the database for " "the naxsi web application firewall." msgstr "" "Ange värdnamnet för servern som kommer att tillhandahålla databasen för " "naxsi web application firewall." debian/po/pt.po0000664000000000000000000000206612305451337010600 0ustar # Copyright (C) 2012 Cyril Lavier # This file is distributed under the same license as the nginx-naxsi-ui package. # Pedro Ribeiro , 2012. # msgid "" msgstr "" "Project-Id-Version: nginx\n" "Report-Msgid-Bugs-To: nginx@packages.debian.org\n" "POT-Creation-Date: 2012-11-04 09:03+0530\n" "PO-Revision-Date: 2012-06-26 19:14+0100\n" "Last-Translator: Pedro Ribeiro \n" "Language-Team: Portuguese \n" "Language: Portuguese\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "Database host for naxsi:" msgstr "Servidor de base de dados para o naxsi:" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "" "Please specify the hostname of the server that will host the database for " "the naxsi web application firewall." msgstr "" "Indique, por favor, o nome da máquina do servidor que irá alojar a base de " "dados para a firewall da aplicação web naxsi." debian/po/da.po0000664000000000000000000000206612305451337010541 0ustar # Danish translation nginx. # Copyright (C) 2012 nginx & nedenstående oversættere. # This file is distributed under the same license as the nginx package. # Joe Hansen , 2012. # msgid "" msgstr "" "Project-Id-Version: nginx\n" "Report-Msgid-Bugs-To: nginx@packages.debian.org\n" "POT-Creation-Date: 2012-11-04 09:03+0530\n" "PO-Revision-Date: 2012-06-16 17:30+01:00\n" "Last-Translator: Joe Hansen \n" "Language-Team: Danish \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "Database host for naxsi:" msgstr "Databasevært for naxsi:" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "" "Please specify the hostname of the server that will host the database for " "the naxsi web application firewall." msgstr "" "Angiv venligst værtsnavnet for serveren som skal betjene brandmurens " "database for naxsi-internetprogrammet." debian/po/sk.po0000664000000000000000000000206612305451337010572 0ustar # Slovak translation for nginx # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the nginx package. # Ivan Masár , 2012. # msgid "" msgstr "" "Project-Id-Version: nginx\n" "Report-Msgid-Bugs-To: nginx@packages.debian.org\n" "POT-Creation-Date: 2012-11-04 09:03+0530\n" "PO-Revision-Date: 2012-06-12 23:15+0100\n" "Last-Translator: Ivan Masár \n" "Language-Team: Slovak \n" "Language: sk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "Database host for naxsi:" msgstr "Hostiteľ databázy naxsi:" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "" "Please specify the hostname of the server that will host the database for " "the naxsi web application firewall." msgstr "" "Prosím, zadajte názov servera, ktorý bude hostiteľom databázy webového " "aplikačného firewallu naxsi." debian/po/templates.pot0000664000000000000000000000161612305451337012337 0ustar # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: nginx\n" "Report-Msgid-Bugs-To: nginx@packages.debian.org\n" "POT-Creation-Date: 2012-11-04 09:03+0530\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "Database host for naxsi:" msgstr "" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "" "Please specify the hostname of the server that will host the database for " "the naxsi web application firewall." msgstr "" debian/po/es.po0000664000000000000000000000424512305451337010565 0ustar # nginx translation to Spanish # Copyright (C) 2012 Software in the Public Interest, SPI Inc. # This file is distributed under the same license as the nginx package. # # Changes: # - Initial translation # Fernando C. Estrada , 2012 # # Traductores, si no conoce el formato PO, merece la pena leer la # documentación de gettext, especialmente las secciones dedicadas a este # formato, por ejemplo ejecutando: # info -n '(gettext)PO Files' # info -n '(gettext)Header Entry' # # Equipo de traducción al español, por favor lean antes de traducir # los siguientes documentos: # # - El proyecto de traducción de Debian al español # http://www.debian.org/intl/spanish/ # especialmente las notas y normas de traducción en # http://www.debian.org/intl/spanish/notas # # - La guía de traducción de po's de debconf: # /usr/share/doc/po-debconf/README-trans # o http://www.debian.org/intl/l10n/po-debconf/README-trans # # Si tiene dudas o consultas sobre esta traducción consulte con el último # traductor (campo Last-Translator) y ponga en copia a la lista de # traducción de Debian al español () # msgid "" msgstr "" "Project-Id-Version: nginx\n" "Report-Msgid-Bugs-To: nginx@packages.debian.org\n" "POT-Creation-Date: 2012-11-04 09:03+0530\n" "PO-Revision-Date: 2012-06-25 00:17-0500\n" "Last-Translator: Fernando C. Estrada \n" "Language-Team: Debian L10N Spanish \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Virtaal 0.7.1\n" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "Database host for naxsi:" msgstr "Servidor de base de datos para naxsi:" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "" "Please specify the hostname of the server that will host the database for " "the naxsi web application firewall." msgstr "" "Especifique el nombre del servidor que alojará la base de datos para el " "cortafuegos («firewall») de aplicaciones web naxsi." debian/po/it.po0000664000000000000000000000213212305451337010563 0ustar # Italian translation of nginx PO-DEBCONF FILE. # Copyright (C) 2012 Cyril Lavier # This file is distributed under the same license as the nginx-naxsi-ui package. # Dario Santamaria , 2012. # msgid "" msgstr "" "Project-Id-Version: nginx 1.2.0-1\n" "Report-Msgid-Bugs-To: nginx@packages.debian.org\n" "POT-Creation-Date: 2012-11-04 09:03+0530\n" "PO-Revision-Date: 2012-06-26 23:27+0200\n" "Last-Translator: Dario Santamaria \n" "Language-Team: Italian \n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "Database host for naxsi:" msgstr "Host del database per naxsi:" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "" "Please specify the hostname of the server that will host the database for " "the naxsi web application firewall." msgstr "" "Specificare il nome host del server dove deve essere collocato il database " "del Firewall dell'Applicazione Web naxsi." debian/po/fr.po0000664000000000000000000000232212305451337010557 0ustar # French PO file for nginx-naxsi-ui. # Copyright (C) 2012 Cyril Lavier # This file is distributed under the same license as the nginx-naxsi-ui package. # # Cyril Lavier , 2012. # Christian Perrier , 2012. msgid "" msgstr "" "Project-Id-Version: nginx\n" "Report-Msgid-Bugs-To: nginx@packages.debian.org\n" "POT-Creation-Date: 2012-11-04 09:03+0530\n" "PO-Revision-Date: 2012-06-30 08:18+0200\n" "Last-Translator: Christian Perrier \n" "Language-Team: French \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Generator: Lokalize 1.4\n" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "Database host for naxsi:" msgstr "Serveur de bases de données pour Naxsi :" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "" "Please specify the hostname of the server that will host the database for " "the naxsi web application firewall." msgstr "" "Veuillez indiquer le serveur qui doit accueillir la base de données de la " "l'application de pare-feu web Naxsi." debian/po/POTFILES.in0000664000000000000000000000006312305451337011365 0ustar [type: gettext/rfc822deb] nginx-naxsi-ui.templates debian/po/nl.po0000664000000000000000000000213612305451337010564 0ustar # Dutch translation of ngnix debconf templates. # Copyright (C) 2012 THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the ngnix package. # Jeroen Schot , 2012. # msgid "" msgstr "" "Project-Id-Version: ngnix 1.2.0-2\n" "Report-Msgid-Bugs-To: nginx@packages.debian.org\n" "POT-Creation-Date: 2012-11-04 09:03+0530\n" "PO-Revision-Date: 2012-06-13 12:51+0200\n" "Last-Translator: Jeroen Schot \n" "Language-Team: Debian l10n Dutch \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "Database host for naxsi:" msgstr "Database-server voor naxsi:" #. Type: string #. Description #: ../nginx-naxsi-ui.templates:1001 msgid "" "Please specify the hostname of the server that will host the database for " "the naxsi web application firewall." msgstr "" "Wat is de computernaam van de server die de database van de naxsi " "webapplicatie-firewall zal aanbieden?" debian/nginx-naxsi.dirs0000664000000000000000000000001112305451330012302 0ustar usr/sbin debian/nginx-common.postrm0000664000000000000000000000043512305451330013045 0ustar #!/bin/sh set -e case "$1" in purge) rm -rf /var/lib/nginx /var/log/nginx /etc/nginx ;; upgrade|remove|failed-upgrade|abort-install|abort-upgrade|disappear) ;; *) echo "postrm called with unknown argument \`$1'" >&2 exit 1 ;; esac #DEBHELPER# exit 0 debian/nginx-extras.postinst0000664000000000000000000000142012305451330013415 0ustar #!/bin/sh set -e case "$1" in abort-upgrade|abort-remove|abort-deconfigure|configure) ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac if [ -x /etc/init.d/nginx ]; then if [ -f /run/nginx.pid ] && pidof /usr/sbin/nginx >/dev/null; then NGX_PID=`cat /run/nginx.pid` if kill -s USR2 $NGX_PID 2>/dev/null; then while [ ! -s /run/nginx.pid.oldbin ] || [ ! -s /run/nginx.pid ]; do cnt=`expr $cnt + 1` if [ $cnt -gt 10 ]; then kill -s KILL $NGX_PID invoke-rc.d nginx start exit 0 fi sleep 1 done NGX_OLD_PID=`cat /run/nginx.pid.oldbin` kill -s QUIT $NGX_OLD_PID fi else invoke-rc.d nginx start || exit $? fi fi #DEBHELPER# exit 0 debian/nginx-light.lintian-overrides0000664000000000000000000000007512305451330014776 0ustar nginx-light: spelling-error-in-binary usr/sbin/nginx tEH the debian/nginx-core.install0000664000000000000000000000004612310176053012627 0ustar debian/build-core/objs/nginx usr/sbin debian/source/0000775000000000000000000000000012305451341010466 5ustar debian/source/format0000664000000000000000000000001412305451337011701 0ustar 3.0 (quilt) debian/source/include-binaries0000664000000000000000000000024012305451337013627 0ustar debian/modules/naxsi/contrib/naxsi-ui/bootstrap/img/glyphicons-halflings.png debian/modules/naxsi/contrib/naxsi-ui/bootstrap/img/glyphicons-halflings-white.png debian/nginx-common.nginx.logrotate0000664000000000000000000000053212305451330014641 0ustar /var/log/nginx/*.log { weekly missingok rotate 52 compress delaycompress notifempty create 0640 www-data adm sharedscripts prerotate if [ -d /etc/logrotate.d/httpd-prerotate ]; then \ run-parts /etc/logrotate.d/httpd-prerotate; \ fi \ endscript postrotate [ -s /run/nginx.pid ] && kill -USR1 `cat /run/nginx.pid` endscript } debian/naxsi-ui-extract.10000664000000000000000000000124112305451330012451 0ustar .TH naxsi-ui-extract 1 "2012-05-09" .SH NAME naxsi-ui-extract \- Wrapper script to launch the extractor daemon for the naxsi-ui tool. .SH SYNOPSIS .B naxsi-ui-extract .SH DESCRIPTION .PP naxsi-ui-extract is the extractor part of naxsi-ui which reads the database and prints reports about blocked requests. .PP .SH USAGE naxsi-ui-extract .SH SEE ALSO Website: .SH AUTHORS naxsi was written by Thibault Koechlin . naxsi-ui-extract was written by Cyril Lavier . .PP This manual page was written by Cyril Lavier for the Debian project (but may be used by others). debian/nginx-full.install0000664000000000000000000000004612305451330012637 0ustar debian/build-full/objs/nginx usr/sbin debian/changelog0000664000000000000000000017054612310202747011055 0ustar nginx (1.4.6-1ubuntu3) trusty; urgency=medium * Add new binary package for main, nginx-core, which contains only source-tarball-included modules and no third-party modules. * Changes to debian/ directory: - control: + Add entry for nginx-core and nginx-core-dbg. - rules: + Add nginx-core flavor to the build rules. - nginx-core.*: Add new packaging files for nginx-core based on the packaging files for nginx-full. * The above changes satisfy the requirements for main (LP: #1262710) -- Thomas Ward Mon, 10 Mar 2014 18:23:36 -0400 nginx (1.4.6-1ubuntu2) trusty; urgency=medium * debian/rules: Drop from -O3 to -O2 to work around a build failure. -- Adam Conrad Sun, 09 Mar 2014 11:49:28 -0600 nginx (1.4.6-1ubuntu1) trusty; urgency=low * Merge from Debian unstable. Remaining changes: - debian/patches/ubuntu-branding.patch: Add Ubuntu branding. -- Adam Conrad Sun, 09 Mar 2014 11:13:26 -0600 nginx (1.4.6-1) unstable; urgency=medium [ Christos Trochalakis ] * New upstream release. * debian/rules: + Avoid double declaration of hardening flags. + Expand buildflags so the build log is easier to follow. + Switch to --with--cc-opt, --with-ld-opt configure flags. Nginx is now compiled with --Werror, making all warnings into errors. + Split common configure options. + Enable realip module for nginx-light. + Enable debug module for nginx-light and nginx-extras. * debian/patches/perl-use-dpkg-buildflags.patch: + Rewrite patch to just just override LDDLFLAGS and not CCFLAGS since this is handled by --with-cc-opt. * debian/watch, debian/upstream/signing-key.asc: + Fix upstream signature verification. -- Christos Trochalakis Tue, 04 Mar 2014 18:06:55 +0200 nginx (1.4.5-1ubuntu1) trusty; urgency=medium * Resynchronise with Debian (LP: #1280511). Remaining changes: - debian/patches/ubuntu-branding.patch: + Add Ubuntu branding to server_tokens. -- Colin Watson Sat, 15 Feb 2014 03:05:42 +0000 nginx (1.4.5-1) unstable; urgency=medium [ Christos Trochalakis ] * New upstream release. * debian/modules/nginx-lua: + Update nginx-lua to v0.9.4 * debian/nginx-naxsi-ui.preinst: + Fix exit status issue (Closes: #735152) * debian/control: + Fix arch:all to arch:any dependencies + Make nginx depend on specific flavor version * debian/nginx-*.postinst: + Make nginx start by default (Closes: #735551) * debian/nginx-*.prerm: + No need to check for invoke-rc.d, correctly set the exit code on error * debian/nginx-common.nginx.init: + Rewrite some parts of the initscript + Introduce rotate command + Introduce upgrade command -- Christos Trochalakis Thu, 13 Feb 2014 11:41:49 +0200 nginx (1.4.4-4) unstable; urgency=medium [ Christos Trochalakis ] * debian/nginx-common.postinst: + Fix log re-opening issue due to wrong /var/log/nginx permissions. (Closes: #734139) -- Christos Trochalakis Sat, 04 Jan 2014 09:41:40 +0200 nginx (1.4.4-3) unstable; urgency=medium [ Christos Trochalakis ] * debian/nginx-naxsi-ui.postinst: + Fix early exit issue (Closes: #715435) -- Christos Trochalakis Tue, 31 Dec 2013 11:47:07 +0200 nginx (1.4.4-2ubuntu1) trusty; urgency=medium * Resynchronise with Debian. Remaining changes: - debian/patches/ubuntu-branding.patch: + Add Ubuntu branding to server_tokens. -- Colin Watson Sat, 28 Dec 2013 10:21:44 +0000 nginx (1.4.4-2) unstable; urgency=low [ Michael Lustfield ] * debian/control: + Added Provides: httpd-cgi to packages. (Closes: #701508) + Added other options to nginx depends. (Closes: #729860) + Added Spdy to nginx-full package description. * debian/nginx-common.nginx.init: + Added missing line from patch. (Closes: #728103) * debian/conf/sites-available/default: + Changed ssl_protocols and ssl_ciphers. (Closes: 730142) * debian/nginx-common.preinst: + Modify permissions of /var/log/nginx. (Closes: #701112) * debian/rules: + Added spdy support to nginx-full. (Closes: #730432) [ Christos Trochalakis ] * debian/nginx-doc,docs, debian/nginx-common.NEWS: + Ship NEWS with nginx-common instead of nginx-doc. * debian/conf/proxy_params: + Host header should be passed unmodified to the proxied server. + Pass X-Forwarded-Proto header to the proxied server. * debian/control: + Fix nginx-naxsi-ui Depends and Conflicts lines. [ Neutron Soutmun ] * debian/patches/guard-use-of-deprecated-openssl-definition.patch: + Fix FTBFS against the recent libssl-dev. (Closes: #733107) [ Kartik Mistry ] * debian/control: + Updated to Standards-Version 3.9.5 * debian/watch, debian/upstream-signing-key.pgp: + Use upstream PGP signature to verify by watch file. -- Kartik Mistry Fri, 27 Dec 2013 21:16:01 +0530 nginx (1.4.4-1ubuntu1) trusty; urgency=low * Resynchronise with Debian (LP: #1253691). Remaining changes: - debian/patches/ubuntu-branding.patch: + Add Ubuntu branding to server_tokens. -- Colin Watson Fri, 22 Nov 2013 12:23:25 +0000 nginx (1.4.4-1) unstable; urgency=low [ Christos Trochalakis ] * New upstream release. (Closes: #730012) * debian/nginx-*.postinst: + Wait for the new master to write its pid file before sending QUIT to the old master. This solves an issue with systemd and the upgrade mechanism. Systemd receives the SIGCHLD from the old master but it can't see the new pid because the new master has not written it yet. As a result, it kills everything inside the cgroup, including the new master. * debian/modules/ngx-fancyindex: + Upgrade Fancy Index module to v0.3.3 (Closes: #728721) * debian/control: + Remove Upload module from nginx-extras description (Closes: #729003) [ Michael Lustfield ] * debian/control: + Added spdy to package description (Closes: #728038) * debian/nginx-common.nginx.init: + Showing better start/stop messages. Thanks Pim van den Berg. (Closes: #728103) -- Michael Lustfield Thu, 21 Nov 2013 19:25:50 +0530 nginx (1.4.3-2ubuntu1) trusty; urgency=low * Resynchronise with Debian. Remaining changes: - debian/patches/ubuntu-branding.patch: + Add Ubuntu branding to server_tokens. -- Colin Watson Mon, 21 Oct 2013 13:26:52 +0100 nginx (1.4.3-2) unstable; urgency=low [ Kartik Mistry ] * Renamed debian/nginx-common.service to debian/nginx-common.nginx.service so it is installed properly for systemd. Thanks to Christos Trochalakis. * Set debian/compat to 9 and updated debhelper dependency. [ Christos Trochalakis ] * debian/rules, debian/nginx-common.dirs, debian/debian-common.nginx.logrotate: + Switch to dh_installlogrotate. * debian/rules: + *-stamp files are deleted by dh_clean. + Remove unused mime-types target. + Remove not needed config.sub and config.guess targets. * debian/nginx-common.dirs: + Don't ship an empty /run dir. * debian/nginx-{light,full,extras}.lintian-overrides: + Override false lintian spelling error warning. -- Kartik Mistry Wed, 16 Oct 2013 14:58:11 +0530 nginx (1.4.3-1) unstable; urgency=low [ Cyril Lavier ] * debian/nginx-naxsi-ui.postinst, debian/nginx-naxsi-ui.preinst: + Added missing arguments to have clean postinst/preinst scripts. * debian/conf/fastcgi_params: + Defined fastcgi param HTTPS the same as upstream default. (Closes: #712989) [ Michael Lustfield ] * conf/sites-available/default: + Removed /doc/ section per bug #715804. [ Christos Trochalakis ] * New upstream release. (Closes: #722640) * debian/nginx-common.nginx.init: + Better pidfile extraction from nginx.conf. We now accept multiple spaces and tabs as a field separator. * debian/modules/nginx-auth-pam: + Fixed upstream bug. (Closes: #721702) * debian/watch: + Only check for stable releases. * debian/conf/sites-available/default: + Correctly fallback to 404 when the requested file is missing. (Closes: #724232) * debian/logrotate: + Gracefully handle empty pidfile in logrotate script. (Closes: #696797) [ Kartik Mistry ] * Switch to dh-systemd (Closes: #713853) -- Kartik Mistry Thu, 10 Oct 2013 17:28:56 +0530 nginx (1.4.1-3ubuntu1) saucy; urgency=low * Resynchronise with Debian. Remaining changes: - debian/patches/ubuntu-branding.patch: + Add Ubuntu branding to server_tokens. -- Colin Watson Thu, 20 Jun 2013 15:08:44 +0100 nginx (1.4.1-3) unstable; urgency=low [ Kartik Mistry ] * debian/control: + Changed libgd2-dev build-dep to libgd2-dev|libgd2-noxpm-dev allow backporting (Closes: #711505) [ Cyril Lavier ] * debian/nginx-naxsi-ui.preinst + Added argument "install" to the preinst script. (Closes: #711590) -- Kartik Mistry Sun, 09 Jun 2013 13:07:52 +0530 nginx (1.4.1-2ubuntu1) saucy; urgency=low * Resynchronise with Debian. Remaining changes: - debian/patches/ubuntu-branding.patch: + Add Ubuntu branding to server_tokens. -- Colin Watson Fri, 07 Jun 2013 15:41:48 +0100 nginx (1.4.1-2) unstable; urgency=medium [ Kartik Mistry] * Urgency set to medium due to Security and RC bugs. * debian/conf/sites-available/default: + Fixed typo in listen ipv6only=on (Closes: #707684) [ Cyril Lavier ] * debian/control: + Replaced the build-dep libgd2-noxpm-dev to libgd2-dev as part of the libgd2-{xpm,noxpm}-dev -> libgd2-dev transition. * debian/nginx-naxsi-ui.preinst, debian/nginx-naxsi-ui.postinst, debian/conf/naxsi-ui.conf.1.4.1: + Added preinst script and renamed the new config file to avoid prompting the user. (Closes: #707291) * debian/rules: + Made "debian/rules binary" enough to build binary package. (Closes: #708522) -- Kartik Mistry Thu, 06 Jun 2013 12:55:23 +0530 nginx (1.4.1-1ubuntu2) saucy; urgency=low * Rebuild for libgd3. -- Colin Watson Mon, 20 May 2013 00:45:10 +0100 nginx (1.4.1-1ubuntu1) saucy; urgency=low * Merge with Debian unstable (LP: #1177919). Remaining changes: - debian/conf/sites-available/default: + Modify default site configuration file to correct a typo that prevented out-of-the-box usability (LP: #1162177). - debian/patches/ubuntu-branding.patch: + Add ubuntu branding to server_tokens. * Refresh all patches. -- Dmitry Shachnev Sat, 11 May 2013 14:47:53 +0400 nginx (1.4.1-1) unstable; urgency=low * New upstream release: + Fixes arbitrary code execution (CVE-2013-2028). * Uploaded to unstable. * debian/control: + Updated Standards-Version to 3.9.4 -- Kartik Mistry Tue, 07 May 2013 19:53:46 +0530 nginx (1.4.0-2) experimental; urgency=low [ Ondřej Surý ] * debian/modules/: + Updated nginx-echo, nginx-cache-purge and naxsi modules. + Removed useless .gitignore, .gitmodules files. [ Kartik Mistry ] * debian/modules/nginx-upload: + This module no longer works with 1.3.x and above. Removed as of now. * debian/modules/ngx-fancyindex: + Added Fancy Indexes module (Closes: #704210) * debian/copyright: + Fixed path for modules in Files: field. + Updated copyright for debian/* * debian/rules: + Enabled spdy module (Closes: #706195). * debian/control: + Suggests: fcgiwrap (Closes: #701508). [ Cyril Lavier ] * debian/conf/naxsi-ui.conf: + Added configuration file for nginx-naxsi-ui using SQLite (Closes: #699678). * debian/nginx-naxsi-ui.config, debian/nginx-naxsi-ui.postinst, debian/nginx-naxsi-ui.postrm, debian/nginx-naxsi-ui.prerm, debian/nginx-naxsi-ui.templates: + Removed these files as they are not necessary anymore with the database engine switching from MySQL to SQLite. * debian/control: + Removed dependencies against MySQL. * debian/rules, debian/modules/ngx_http_substitutions_filter_module, debian/README.Modules-versions: + Added http_substitutions_filter module. (Closes: #706456) -- Kartik Mistry Wed, 01 May 2013 10:48:43 +0530 nginx (1.4.0-1) experimental; urgency=low [ Kartik Mistry ] * New upstream release (Closes: #706127). * debian/rules, debian/modules/: + Removed chunkin-nginx-module as it no longer supported for nginx 1.3.9+ + Updated nginx-lua module to 0.8.0 + Patched nginx-upload module as described in modules/README file. * Refreshed debian/patches/perl-use-dpkg-buildflags.patch * debian/logrotate: + Set default log to keep for 52 weeks instead of 52 days. Thanks to RjY for patch (Closes: #696440) * debian/rules: + Added cache purge module to nginx-extras. * debian/control: + Suggests: nginx-docs. Thanks to colliar for reporting bug (Closes: #702923) * debian/copyright: + Updated copyright year. + Fixed broken license text. [ Michael Lustfield ] * debian/conf/sites-available/default: + Added ipv6_only=on to default server block. (Closes: #700857) + Added default_server to ipv4 default server block. Now matches ipv6. * debian/rules: + Removed obsolete --with-md5 and --with-sha1 -- Kartik Mistry Thu, 25 Apr 2013 12:51:45 +0530 nginx (1.2.6-1ubuntu4) saucy; urgency=low * debian/patches/ubuntu-branding.patch: Move Ubuntu branding from the numerical version string to the long version (LP: #1174158) -- Adam Conrad Thu, 09 May 2013 11:50:52 -0600 nginx (1.2.6-1ubuntu3) raring; urgency=low * debian/patches/ubuntu-branding.patch: Add ubuntu branding to server_tokens. -- Chuck Short Tue, 09 Apr 2013 10:23:26 -0500 nginx (1.2.6-1ubuntu2) raring; urgency=low * debian/conf/sites-available/default: * Modify default site configuration file to correct a typo that prevented out-of-the-box usability (LP: #1162177) -- Thomas Ward Fri, 05 Apr 2013 20:22:38 +0100 nginx (1.2.6-1ubuntu1) raring; urgency=low * debian/conf/sites-available/default: * Modify default site default configuration file to bind to IPv6 only for IPv6 default listen statement (LP: #1132678) -- Thomas Ward Tue, 12 Mar 2013 00:23:02 +0000 nginx (1.2.6-1) unstable; urgency=low [ Kartik Mistry ] * New upstream release. * debian/nginx-common.nginx.init: + Used log_*_msg instead of echo for better init messages. + Added patch to check start-stop-daemon exit status, Thanks to Sergey B Kirpichev (Closes: #695374). * debian/po/ja.po: + Added new Japanese translation. Thanks to victory (Closes: #692481). * debian/po/pt_BR.po: + Added new Brazilian Portuguese translation. Thanks to Adriano Rafael Gomes (Closes: #692481). [ Cyril Lavier ] * debian/rules + Added RealIP module in nginx-naxsi (Closes: #693302). * debian/modules/nginx-cache-purge/ + Updated nginx-cache-purge module with the 2.0 version. * debian/modules/nginx-lua/ + Updated nginx-lua module with the 0.7.8 version. * debian/modules/nginx-echo/ + Updated the nginx-echo module with the 0.41 version. * debian/modules/headers-more-nginx-module/ + Updated the Headers-more module with the 0.19 version. * debian/modules/README.Modules-versions + Updated the current version of modules following the updates. [ Michael Lustfield ] * debian/conf/sites-available/default + Uncommented listen lines to make server block default. -- Kartik Mistry Tue, 18 Dec 2012 10:29:18 +0530 nginx (1.2.4-2) unstable; urgency=low * debian/conf/nginx.conf: + Fixed pid path to /run/nginx.pid in config. * debian/po/*.po, debian/nginx-naxsi-ui.templates, debian/po/templates.pot: + Fixed ^M characters in template file. Thanks to Christian Perrier for help (Closes: #692191, #692335) -- Kartik Mistry Mon, 05 Nov 2012 13:05:34 +0530 nginx (1.2.4-1) unstable; urgency=low [ Michael Lustfield ] * debian/nginx-common.nginx.init + Added --retry to start-stop-daemon. (Closes: #682360) + Added $named to Required-Start/Stop. (Closes: #679879) + Added some other modifications after testing. (LP: #1033856) * debian/rules: + Added -DFORTIFY_SOURCE=2 -fstack-protector to build flags. (Closes: #680712) * debian/nginx-common.install: + Moved default docs to /usr/share/nginx/html. (Closes: #1031515) * debian/rules: + Set prefix to /usr/share/nginx. * debian/po/fr.po + Updated French translation file. (Closes: #679601) * debian/logrotate: + Removed semicolon after an fi statement. (Closes: #683870) * debian/copyright: + Removed trailing slash from File: fields. * Changed /var/run to /run in multiple files. (LP: #1050516) * debian/nginx-common.dirs: + Added /run to .dirs to avoid conflicts with backports. (LP: #1072641) * debian/nginx-*.postinst: + Added sites-enabled/available directory checks. (Closes: #688410) - Thanks Andreas Marschke + Moved the symlink creation to nginx-common. * Updated debconf templates. * conf/sites-available/default: + Updated root to point at /usr/share/nginx/html instead of www. [ Kartik Mistry ] * Acknowledged NMUs (Closes: #681758, #681381) * New upstream release (Closes: #683704) * debian/po/*.po: + Removed ^M characters from headers. + Fixed all files with recent unneeded template change. -- Kartik Mistry Tue, 30 Oct 2012 10:10:34 +0530 nginx (1.2.1-2.2) unstable; urgency=low * Non-maintainer upload. * Fix "removes files that were installed by another package": don't remove directories that are owned by (and removed from) nginx-common from nginx-extras.postrm. This seems to have been the idea in commit e30a854 ("Moved configuration purging to nginx-common.") except that it was added in nginx-common.postrm without being removed in nginx-extras.postrm. Remove nginx-extras.postrm since it's empty after this change. (Closes: #681758) -- gregor herrmann Sat, 04 Aug 2012 18:13:10 +0200 nginx (1.2.1-2.1) unstable; urgency=low * Non-maintainer upload to deal with the goal of 100% translated debconf templates for 7 languages in wheezy. * Drop nasty ^M characters from the templates file. They break debconf-updatepo and therefore translations. Closes: #681381 * As a consequence, fix translations as some of them had been "updated" in the meantime wrt broken templates -- Christian Perrier Thu, 12 Jul 2012 21:23:57 +0200 nginx (1.2.1-2) unstable; urgency=medium [Cyril Lavier] * Urgency set to medium, security bug in naxsi module, fix via upstream. * debian/modules/naxsi: + Updated naxsi module to version 0.46-1 fixing the following security issue : potential file disclosure in nx_extract. -- Cyril Lavier Wed, 27 Jun 2012 13:52:03 +0200 nginx (1.2.1-1) unstable; urgency=medium [Kartik Mistry] * Urgency set to medium, RC bug fix. * New upstream release (Closes: #677396) * debian/nginx.1: + Updated with description taken from debian/control. * debian/po/cs.po: + Added Czech translation of PO debconf template. (Closes: #673120, #678659) * debian/po/da.po: + Added Danish translation of PO debconf template. (Closes: #674461, #677806) * debian/po/it.po: + Added Italian translation of PO debconf template. (Closes: #679185) * debian/po/nl.po: + Added Dutch translation of PO debconf template. (Closes: #675314) * debian/po/pl.po: + Added Polish translation of PO debconf template. (Closes: #678156) * debian/po/pt.po: + Added Portuguese translation of PO debconf template. (Closes: #674609, #679149) * debian/po/sk.po: + Added Slovak translation of PO debconf template. (Closes: #677282) * debian/po/sv.po: + Added Swedish translation of PO debconf template. (Closes: #675107, #677032) * debian/copyright: + Removed duplicate Copyright fields. * debian/modules/nginx-lua: + Updated to latest upstream git tag 0.5.0rc29-6446ee71b8, Fix for worker process crashes (Closes: #674928) * debian/nginx-naxsi-ui.templates, debian/po/templates.pot: + Updated debconf template after review from l10n-english team. (Closes: #675237) + Do not add empty line. * debian/po/*.po: + Refreshed after template update. [Cyril Lavier] * debian/rules: + Added status module to nginx-naxsi. (Closes: #672998) * debian/control: + Updated nginx-naxsi module list. + Updated nginx-full module list. (Closes: #677128) * debian/po/de.po: + Added German translation of PO debconf template. (Closes: #673861, #677790) * debian/po/ru.po: + Added Russian translation of PO debconf template. (Closes: #674188) * debian/po/es.po: + Added Spanish translation of PO debconf template. (Closes: #678915) * debian/po/gl.po: + Added Galician translation of PO debconf template. (Closes: #678910) * debian/nginx-naxsi-ui.postrm, debian/nginx-naxsi-ui.prerm: + Added postrm and prerm scripts for nginx-naxsi-ui. (Closes: #673787) [Michael Lustfield] * debian/nginx-common.preinst: + Copied install stuff to upgrade in case statement. (LP: #1014506) * debian/rules + Added --with-pcre-jit to build. (LP: #915344) * debian/conf/sites-available/default: + Changed default try_files example. (LP: #969207) * debian/nginx-*.postrm: + Changed a few things so files are only purged on purge. (Closes: #678060) - Thanks Jeroen Dekkers -- Kartik Mistry Wed, 27 Jun 2012 09:24:50 +0530 nginx (1.2.0-1) unstable; urgency=low [Cyril Lavier] * New upstream release. (Closes: #670306) + 1.2.x is stable release now. * debian/modules/chunkin-nginx-module: + Updated chunkin-nginx-module to v0.23rc2-3-g85eca98. * debian/modules/headers-more-module: + Updated headers-more-module to v0.17rc1-4-g33a82ed. * debian/modules/nginx-development-kit: + Updated nginx-development-kit to v0.2.17-7-g24202b4. * debian/modules/nginx-echo: + Updated nginx-echo to v0.38rc2-7-g080c0a1. * debian/modules/nginx-lua: + Updated nginx-lua to v0.5.0rc25-5-g8d28785. * debian/modules/nginx-upstream-fair: + Updated nginx-upstream-fair to a18b409. * debian/modules/nginx-upload-progress: + Updated nginx-upload-progress to v0.9.0-0-ga788dea. * debian/modules/naxsi: + Updated naxsi to 0.46 * debian/modules/README.Modules-versions: + Updated versions and URLs for modules. * debian/naxsi-ui-extract, debian/naxsi-ui-intercept, debian/nginx-naxsi-ui.*, debian/naxsi-ui-extract.1, debian/naxsi-ui-intercept.1, debian/rules: + Added nginx-naxsi-ui package containing the learning daemon and the WebUI. * debian/nginx-common.nginx.default, debian/nginx-common.nginx.init: + Renamed files to be compliant with the nginx-naxsi-ui package. * debian/po: + Added needed files for using po-debconf. + Added French translation. * debian/control: + Applied the modifications given after the review by Justin Rye. [Michael Lustfield] * debian/conf/uwsgi_params: + Added UWSGI_SCHEME to uwsgi_params. (Closes: #664878) * debian/conf/sites-available/default: + Added allow directive for ipv6 localhost. (Closes: #664271) [Kartik Mistry] * debian/control: + wrap-and-sort. * debian/copyright: + Added missing copyrights, minor formatting fixes. * debian/nginx-common.nginx.init: + Added ulimit for restarts, Thanks to Daniel Roschka for patch. (Closes: #673580) * debian/conf/sites-available/default: + Added patch to fix deprecated "listen" directive, Thanks to Guillaume Plessis for patch. (Closes: #672632) -- Kartik Mistry Mon, 14 May 2012 11:15:00 +0530 nginx (1.1.19-1) unstable; urgency=high [Cyril Lavier] * New upstream release. + Fixed a buffer overflow in the ngx_http_mp4_module. See: CVE-2012-2089 for more details. * debian/copyright: + Updated licenses. * debian/nginx-extras.postinst, debian/nginx-full.postinst, debian/nginx-light.postinst, debian/nginx-naxsi.postinst: + Removing the debug markers. (Closes: #667894) * debian/control, debian/rules, debian/copyright, debian/modules/nginx-dav-ext-module: + Added nginx-dav-ext-module in full and extras. * debian/modules/naxsi: + Updated naxsi to the SVN snapshot (r280) to fix the licence issue with OpenSSL. [Kartik Mistry] * Misc cleanups in debian/control, debian/copyright. -- Cyril Lavier Fri, 13 Apr 2012 16:58:59 +0530 nginx (1.1.18-1) unstable; urgency=low [Cyril Lavier] * New upstream release. * New binaries introduced: nginx-naxsi, nginx-naxsi-dbg. * debian/modules/nginx-cache-purge: + Added nginx cache purge 1.5 to nginx-naxsi. * debian/rules, debian/control, debian/nginx-naxsi.dirs, debian/nginx-naxsi.install, debian/nginx-naxsi.postinst, debian/nginx-naxsi.postrm, debian/nginx-naxsi.prerm, debian/modules/naxsi, debian/conf/naxsi_core.rules, debian/modules/README.Modules-versions, debian/conf/nginx.conf: + Added nginx-naxsi flavour. + Added naxsi 0.44 to nginx-naxsi. -- Cyril Lavier Thu, 29 Mar 2012 22:43:07 +0530 nginx (1.1.17-2) unstable; urgency=high [Cyril Lavier] * debian/control: + Added build dependency to dpkg-dev (>= 1.15.7). (Closes: #664212) * debian/patches/perl-use-dpkg-buildflags.patch: + Added patch to harden flags for perl module (Thanks to Simon Ruderich for the patch). (Closes: #664090) [Kartik Mistry] * Set urgency due to fix for security and RC bugs with 1.17.1-1 upload. -- Cyril Lavier Sun, 18 Mar 2012 09:31:19 +0530 nginx (1.1.17-1) unstable; urgency=medium [Kartik Mistry] * New upstream release. (Closes: #664137) + Fixed malformed HTTP responses. See: CVE-2012-1180 for more details. * Set urgency to medium due to security issue. [Cyril Lavier] * debian/rules: + Set NUMJOBS to 1 if no value is given + Added Auth PAM module to nginx-extras + Enable hardened flags for perl module (Thanks to Simon Ruderich for the patch) (Closes: #664090). * debian/conf/sites-available/default: + Added the fastcgi_pass for php5-fpm. (Closes: #662997) * debian/nginx-common.postrm, debian/rules, debian/nginx-common.postinst, debian/nginx-common.prerm, debian/nginx-common.service: + Added the systemd support. Thanks to Michael Stapelberg for the patch. (Closes: #662799) -- Kartik Mistry Fri, 16 Mar 2012 10:27:38 +0530 nginx (1.1.16-1) unstable; urgency=low [Cyril Lavier] * Modified the parallel build to avoid random FTBFS in Ubuntu. * New upstream release. * debian/modules: + Updated nginx-upload-progress to 0.8.3-2-g03cbf1f. * debian/control: + Added myself as uploader. + Added libpam0g-dev to Build-Depends. + Use wrap-and-sort to wrap control file fields. [Michael Lustfield] * debian/nginx-common.postinst: + Changed postinst to not change permissions on all log files. (Closes: #658492) * debian/rules: + Added Auth PAM module at debian/modules/nginx-auth-pam. (Closes: #660408) [Kartik Mistry] * debian/control: + Added conflict between -dbg packages as seen by Lintian. + Updated Standards-Version to 3.9.3 * debian/copyright: + Updated for copyright-format 1.0 + Updated upstream copyright year and details. * debian/rules: + Moved nginx-auth-pam module to nginx-full package. -- Kartik Mistry Sun, 04 Mar 2012 10:31:21 +0530 nginx (1.1.14-1) unstable; urgency=low [Cyril Lavier] * New upstream release. * debian/rules: + Resolved the lintian errors "unstripped-binary-or-object" with a cleaner correction (Thanks to Steven Chamberlain for the patch). + Added a check on the parallel building to force NUMJOBS to 1 if the value 0 is given. * debian/modules: + Updated nginx-lua module to version 0.4.1. [Kartik Mistry] * debian/rules, debian/control, debian/copyright, debian/modules/nginx-upload-module: + Added Upload module to nginx-extras, updated long description and copyright. (Closes: #654593) * debian/modules/README.modules: + Added Homepage information for some modules. * debian/rules: + Enable hardened build flags, Thanks to Moritz Muehlenhoff for patch. (Closes: #658186) -- Kartik Mistry Wed, 01 Feb 2012 17:50:05 +0530 nginx (1.1.12-1) unstable; urgency=low [Kartik Mistry] * debian/control: + Set myself as Maintainer, Jose Parrella as Uploaders with approval from team. * debian/copyright: + Fixed DEP5 URL. + Updated debian/* copyright. * debian/modules: + Updated nginx-lua module to version 0.3.1rc43 [Cyril Lavier] * New upstream release. * debian/conf/sites-available/default: + Added a / in the alias directive. (Closes: #653160) * debian/rules: + Added necessary lines for parallel building. -- Kartik Mistry Sun, 01 Jan 2012 17:21:02 +0530 nginx (1.1.11-1) unstable; urgency=low [Kartik Mistry] * New upstream release. * debian/control: + Set priority to extra for nginx-light and nginx-extras binaries (Policy: Section 2.5) * debian/patches/607418-ipv6-addresses.diff: + Removed. Merged upstream with 1.1.9 release. * debian/copyright: + Updated upstream copyright year, updated Michael's email address, misc changes for format. [Michael Lustfield] * debian/conf/fastcgi_params: + Changed $server_https to $https per new feature in 1.1.11. * debian/conf/nginx.conf: + Removed map for $server_https as it's no longer needed. -- Kartik Mistry Wed, 14 Dec 2011 09:45:40 +0530 nginx (1.1.8-2) unstable; urgency=low * debian/modules/chunkin-nginx-module: + Reinclude HttpChunkin Module with new upstream version (closes: #638814) * debian/control: + Add myself to uploaders list. -- Dmitry E. Oboukhov Thu, 24 Nov 2011 14:16:50 +0530 nginx (1.1.8-1) unstable; urgency=low [Kartik Mistry] * New upstream release. * debian/modules/chunkin-nginx-module: + Removed as of now, as it breaks with Perl 5.14 (Closes: #649061) [Michael Lustfield] * debian/control: + Added Map module to nginx-light modules list. * debian/rules: + Removed --without-http_map_module form nginx-light. * debian/nginx-common.install: + Changed ufw profile installation (LP: #825349). - debian/ufw.profile -> debian/ufw/nginx. * debian/nginx-common.preinst: + Cleanup of moved nginx profile. * debian/conf/nginx.conf: + Added a default map for $server_https (on|off). * debian/conf/fastcgi_params: + Pass HTTPS so $_SERVER['HTTPS'] is set (LP: #857831). * debian/conf/mime.types: + Added json type (LP: #883440). * debian/conf/sites-available/default: + Added notes about PHP (Closes: #642995). + Changed location /doc from root to alias. + Changed location /doc to /doc/ for people that don't bother reading or learning anything about Nginx configuration files (LP: #840358). -- Kartik Mistry Fri, 18 Nov 2011 23:44:00 +0530 nginx (1.1.4-2) unstable; urgency=low [Kartik Mistry] * debian/modules: + Updated nginx-upload-progress module, Thanks to upstream for fixing issue that FTBFS nginx on kFreeBSD-* archs. + Updated nginx-lua module to latest upstream. -- Kartik Mistry Mon, 26 Sep 2011 10:17:04 +0530 nginx (1.1.4-1) unstable; urgency=low [Kartik Mistry] * New upstream release. * debian/rules: + Added 'HTTPStubStatus' module in nginx-light also (Closes: #639661). + Added 'HTTP MP4' module in nginx-extras. (Closes: #642022). * Add new modules, Thanks to Dmitry E. Oboukhov : + NginxHttpHeadersMore Module (closes: #638820). + HttpChunkin Module (closes: #638814). * Renamed debian/modules/versions to debian/modules/README.modules-version, added missing homepage and version information. -- Kartik Mistry Sun, 25 Sep 2011 13:44:48 +0530 nginx (1.1.2-1) unstable; urgency=low [ Kartik Mistry ] * New upstream release * debian/control: + Added missing 'http-push' module in long description of nginx-extras. (Closes: #638924) + Removed 'File AIO' module from description, which we don't provide. (Closes: #639360) + Added 'HTTP Sub Module' in long description of nginx-extras, nginx-full. + Updated Breaks/Replaces fields. [ Michael Lustfield ] * debian/copyright: + Updated email address for Michael Lustfield * debian/control: + Updated email address for Michael Lustfield + Updated Breaks on nginx-common to address conflicting versions -- Kartik Mistry Wed, 07 Sep 2011 12:15:36 +0530 nginx (1.1.0-1) unstable; urgency=low [ Kartik Mistry ] * New upstream release * debian/control: + Added 'Upload Progress' module in long description of nginx-extras (Closes: #635313) * debian/rules, debian/modules: + Removed deprecated module: nginx-secure-download * debian/modules: + Updated nginx-lua module to version: v0.2.1rc2 + Updated nginx-echo module to version: v0.37rc1 -- Kartik Mistry Tue, 02 Aug 2011 20:06:43 +0530 nginx (1.0.5-1) unstable; urgency=low * New upstream release * debian/control: + nginx Depends on either nginx-full or nginx-light, Thanks to Hans van Kranenburg (Closes: #633879) * debian/copyright: + Updated to latest DEP-5 specification + Added some missing copyrights for modules + Updated copyright years -- Kartik Mistry Wed, 20 Jul 2011 10:16:45 +0530 nginx (1.0.4-1) unstable; urgency=low * New upstream release * debian/*.lintian-overrides, debian/rules: + Removed unused lintian for missing manpages -- Kartik Mistry Wed, 01 Jun 2011 19:12:25 +0530 nginx (1.0.1-1) unstable; urgency=low * New upstream release * debian/rules: + Removed if surrounding copy of man/ as it is required for builds. + Added nginx-upload-progress to nginx-extras. (Closes: #618306) + Added nginx-secure-downloads to nginx-extras. (Closes: #622268) + Added --prefix to configure command. (Closes: #619482) * debian/modules: + Added nginx-upload-progress/*. + Added nginx-secure-download/*. + Updated nginx-lua/*. + Updated versions. * debian/control: + Added libmhash-dev build dependency. + Updated Standards-Version to 3.9.1. -- Michael Lustfield Mon, 09 May 2011 00:36:54 +0530 nginx (1.0.0-2) unstable; urgency=low * debian/rules: + Removed --with-file-aio support. Fixed FTBFS on kFreeBSD-* arch (Closes: #621882) -- Kartik Mistry Sat, 16 Apr 2011 13:47:58 +0530 nginx (1.0.0-1) unstable; urgency=low [Kartik Mistry] * New upstream release: + Removed debian/patches/609343-log-time-iso8601.diff, merged upstream. * debian/rules: + Fixed: FTBFS on sparc64, detects sparc64 as sparc. Thanks to Aurelien Jarno (Closes: #617924) * debian/watch: + Updated for 1.x.x release [Michael Lustfield] * debian/rules: + Added Echo module back to nginx-full. (LP: #735622) + Moved nginx-push-0.96 to nginx-http-push. * debian/modules: + Updated nginx-development-kit. + Updated nginx-echo. + Updated nginx-http-push. + Updated nginx-lua. + Updated nginx-upstream-fair. + Added versions to track module versions. * debian/conf/fastcgi_params: + Changed $document_root$fastcgi_script_name to $request_filename. -- Kartik Mistry Thu, 14 Apr 2011 10:41:08 +0530 nginx (0.8.54-4) unstable; urgency=low [Micheal Lustfield] * debian/nginx-{full,light,extras}.default: + Added comment about alternative to ULIMIT. * debian/nginx-{full,light,extras}.init.d: + Added quotes around a test variable. (Closes: #610946, LP: #699736) * debian/patches/609343-log-time-iso8601.diff: + Added patch to add $time_iso8601 variable to logs. (Closes: #609343) * Clean up old logrotate files. (Closes: #608983, Closes: #610289) + Added Files: - debian/nginx-common.preinst + Modified Files: - debian/rules + Moved debian/nginx-common.logrotate to debian/logrotate. * Added common files to nginx-common package. (Closes: #610290) + Removed Files: - debian/nginx-full.dirs - debian/nginx-light.dirs - debian/nginx-full.install - debian/nginx-light.install - debian/nginx-extras.install - debian/nginx.* + Added Files: - debian/nginx-common.default - debian/nginx-common.dirs - debian/nginx-common.init.d - debian/nginx-common.install - debian/nginx-common.manpages - debian/logrotate + Modified Files: - debian/nginx-extras.dirs - debian/control - debian/rules * debian/nginx-*.install: (Closes: #609797) + Removed NEWS.Debian from nginx-{full,light,extras}.install. + Added NEWS.Debian to nginx-common.install. * nginx-common.postinst: + Enforce /var/log/nginx mode and user:group. (Closes: #610983) + Enforce /var/log/nginx/*.log mode and user:group. (Closes: #612832) * debian/rules: + Added --with-file-aio to nginx-extras. (Closes: #613175) + Removed split clients and user id modules from nginx-light. * debian/conf/sites-available/default: + Fixed a minor typo ( s/Quickstart/QuickStart/ ). (Closes: #613355) * debian/conf/mime.types: + Changed xml type to application/xhtml+xml. (Closes: #613851) * debian/help/docs/fcgiwrap: + Removed Ubuntu specific line in docs. (Closes: #614987) * debian/conf/sites-available/default: + Fixed a pointer to a file. (Closes: #614980) [Kartik Mistry] * debian/*.lintian-overrides: + Add Lintian overrides for nginx man page. We've manpage in nginx-common binary -- Michael Lustfield Thu, 03 Mar 2011 23:39:07 +0530 nginx (0.8.54-3) unstable; urgency=low * debian/control: + Added Breaks: and Replaces: to nginx packages. (Closes: #609254) * debian/rules: + Adding SSL support to nginx-light. (Closes: #609134) + Adding HTTP Addition module to nginx-full and nginx-extras. (Closes: #609411) + Added HTTP Push module to nginx-extras. (LP: #694456) -- Michael Lustfield Tue, 11 Jan 2011 05:27:39 -0600 nginx (0.8.54-2) unstable; urgency=low * Do not install nginx.8 man page in debug packages (Closes: #608633) -- Kartik Mistry Sun, 02 Jan 2011 18:46:03 +0530 nginx (0.8.54-1) unstable; urgency=low [ Michael Lustfield ] * Added ipv6 patch. (Closes: #607418) + Edited files: - src/http/ngx_http_request.c * Added ulmit patches. (Closes: #606094) + Added files: - debian/nginx-full.default - debian/nginx-light.default - debian/nginx-extras.default + Edited files: - debian/nginx-full.init.d - debian/nginx-light.init.d - debian/nginx-extras.init.d * Added perl module to nginx-extras. (Closes: #598461) + debian/rules: - Added --with-http_perl_module to nginx-extras. + debian/control: - Added libperl-dev build dependency. + debian/nginx-extras.install: - Added perl install files + debian/nginx-extras.dirs: - Added directory to install perl files. * debian/rules: + Added ifeq to handle nginx.8 (man page) if it exists in the source. * debian/control: + Added descriptions to make clear the differences between nginx packages. * Removed patches: + debian/patches/nginx-echo.diff - Put source into debian/patches/. + debian/patches/nginx-upstream-fair.diff - Put source into debian/patches/. + debian/patches/dlopen.diff - Patch no longer relevant. * Added modules: + debian/modules/nginx-development-kit + debian/modules/nginx-upstream-fair + debian/modules/nginx-echo + debian/modules/nginx-lua * Split Nginx into multiple variations of the package. + Added files for nginx-full: - debian/nginx-full.install - debian/nginx-full.dirs - debian/nginx-full.logrotate - debian/nginx-full.init.d - debian/nginx-full.postinst - debian/nginx-full.prerm - debian/nginx-full.postrm + Added files for nginx-light: - debian/nginx-light.install - debian/nginx-light.dirs - debian/nginx-light.logrotate - debian/nginx-light.init.d - debian/nginx-light.postinst - debian/nginx-light.prerm - debian/nginx-light.postrm + Added files for nginx-extras: - debian/nginx-extras.install - debian/nginx-extras.dirs - debian/nginx-extras.logrotate - debian/nginx-extras.init.d - debian/nginx-extras.postinst - debian/nginx-extras.prerm - debian/nginx-extras.postrm + Removed files for nginx: - debian/nginx.install - debian/nginx.dirs - debian/nginx.logrotate - debian/init.d - debian/postinst - debian/prerm - debian/postrm + Updated debian/rules: - Modified rules to create extra packages. + Updated debian/control: - Added packages for nginx-{full,light,extras}. - Added packages for nginx-{full,light,extras}-dbg. - Changed nginx to a dummy package selecting nginx-full. [Kartik Mistry] * debian/control: + Added doc section for nginx-doc + Wrapped some long descriptions, updated short descriptions + Added perl:Depends for nginx-extras * debian/rules: + Fixed dh_installchangelogs, dh_compress -- Michael Lustfield Thu, 30 Dec 2010 14:15:26 +0530 nginx (0.8.53-2) unstable; urgency=low [Kartik Mistry] * debian/conf/mime.types: + Added html5 codecs support (Closes: #605212) * debian/conf/fastcgi_params: + Fixed typo (Closes: #605582) [Michael Lustfield] * debian/conf/nginx.conf: + Reduced worker_connections to 768 (Closes: #605529) * Add an nginx site management script to nginx-doc. (Closes: #593580) + debian/help/examples/nginx_modsite: Added. * debian/nginx.logrotate: + Added prerotate chunk for awstats. (Closes: #590098) * debian/copyright: + Added Michael Lustfield * Created nginx-doc: + Added 'Package: nginx-doc' section to debian/control + Moved debian/nginx.examples -> debian/nginx-doc.examples + Moved debian/nginx.docs -> debian/nginx-doc.docs -- Kartik Mistry Mon, 06 Dec 2010 11:26:22 +0530 nginx (0.8.53-1) unstable; urgency=low [Kartik Mistry] * debian/control: + Added Michael Lustfield as co-maintainer * nginx.conf: + No need to use regex in gzip_disable for msie6, Thanks to António P. P. Almeida (Closes: #592147) * conf/sites-available/default: + Fixed typo for "include fastcgi", Thanks to Mostafa Ghadamyari (Closes: #593142, #593143) * debian/patches/fix_reloading_ipv6.diff: + Removed, merged upstream * debian/init.d: + Added fix to control nginx by user in a simple way by setting DAEMON variable to an invalid name in /etc/default/nginx. Patch by Toni Mueller (Closes: #594598) * debian/NEWS.Debian: + Updated news for 0.8.x as stable branch [Michael Lustfield] * New upstream release (Closes: #602970) + 0.8.x branch is declared stable by upstream now * Add a UFW profile set: + debian/nginx.ufw.profile: Added. + debian/control: nginx: Suggests ufw. + debian/dirs: Add 'etc/ufw/applications.d' + debian/rules: Add install rule for the nginx UFW profile. * Moved debian/dirs to debian/nginx.dirs * Added types_hash_max_size to nginx.conf * Install simple default index.html file (Closes: #581416) + debian/dirs: Add 'usr/share/nginx/www'. + debian/nginx.install: Add 'html/* usr/share/nginx/www'. * debian/patches/nginx-echo.diff: + Added Echo module * Added files for nginx.docs - /usr/share/doc/nginx/ + debian/help/docs/fcgiwrap + debian/help/docs/php + debian/help/docs/support-irc + debian/help/docs/upstream * Added files for nginx.examples - /usr/share/doc/nginx/examples/ + debian/help/docs/drupal + debian/help/docs/http + debian/help/docs/mail + debian/help/docs/mailman + debian/help/docs/nginx.conf + debian/help/docs/virtual_hosts + debian/help/docs/wordpress * debian/conf/: + Removed excess spaces + Added tabs where appropriate + Added SCRIPT_FILENAME to fastcgi_params -- Kartik Mistry Sat, 27 Nov 2010 21:04:02 +0530 nginx (0.7.67-3) unstable; urgency=low * debian/rules: + Readded the configure option --with-mail_ssl_module, Thanks to Roland Rosenfeld (Closes: #590815) * debian/control: + Updated Standards-Version to 3.9.1 (no changes needed) -- Kartik Mistry Thu, 29 Jul 2010 21:41:52 +0530 nginx (0.7.67-2) unstable; urgency=low * debian/conf/sites-available/default: + Removed reference to SSLv2 protocol (Closes: #589139) * debian/control: + Updated Standards-Version to 3.9.0 * debian/copyright: + Don't point to BSD license file, included exact upstream version of license text + Added missing copyright owner for contrib/ scripts + debian/* license is same as upstream now as discussed with co-maintainers -- Kartik Mistry Mon, 19 Jul 2010 10:36:32 +0530 nginx (0.7.67-1) unstable; urgency=low * New upstream release -- Kartik Mistry Wed, 16 Jun 2010 01:26:51 +0530 nginx (0.7.65-7) unstable; urgency=low [Kartik Mistry] * debian/rules: + Enabled HTTPSubModule module in configure (Closes: #584828) + Arranged configure options in better manner -- Kartik Mistry Mon, 07 Jun 2010 14:33:24 +0530 nginx (0.7.65-6) unstable; urgency=low [Kartik Mistry] * debian/README.Debian: + Fixed typo and somewhat better wordings * debian/conf/mime.types: + Added entry to support 7zip files (Closes: #580423) * debian/init.d: + Do not print config testing info until an error found, Thanks to Ubuntu bug 568293 * debian/copyright: + Updated as per DEP-5 specification -- Kartik Mistry Sat, 22 May 2010 01:41:33 +0530 nginx (0.7.65-5) unstable; urgency=low [Kartik Mistry] * debian/patches/fix_reloading_ipv6.diff: + Added patch to fix reloading with IPv6 addresses, Thanks to Matthias-Christian Ott for patch (Closes: #577456) -- Kartik Mistry Wed, 14 Apr 2010 11:36:48 +0530 nginx (0.7.65-4) unstable; urgency=low [Kartik Mistry] * debian/conf/sites-available/default: + Really listen for both IPv4 and IPv6 addresses. Thanks to Nikolaus Schulz for notice (Closes: #574983) * debian/control, debian/rules: + Added GeoIP support, Thanks to Caetano Carezzato (Closes: #575280) * debian/conf/mime.types: + Added svg entry to mime.types, Jeremy Lal (Closes: #575155) -- Kartik Mistry Thu, 25 Mar 2010 00:21:50 +0530 nginx (0.7.65-3) unstable; urgency=medium [Kartik Mistry] * Urgency set to medium due to Release Goal * debian/conf/sites-available/default: + Listen for both IPv4 and IPv6 addresses by default (Closes: #574983) -- Kartik Mistry Tue, 23 Mar 2010 10:30:18 +0530 nginx (0.7.65-2) unstable; urgency=low * debian/README.Debian: + Added explanation about not installing files in /var/www/ (Closes: #572513) * debian/rules: + Readded realip module support (Closes: #507419) -- Kartik Mistry Sat, 06 Mar 2010 13:14:48 +0530 nginx (0.7.65-1) unstable; urgency=low [Kartik Mistry] * New upstream release * debian/init.d: + $local_fs $remote_fs $network $syslog is what we need in Required-Start and Required-Stop (Closes: #568238) * debian/copyright: + Updated package copyright year + Updated license text to make same as upstream license -- Kartik Mistry Fri, 05 Feb 2010 11:20:28 +0530 nginx (0.7.64-3) unstable; urgency=low [Kartik Mistry] * debian/conf/sites-available/default: + Added patch to fix default virtual host, Thanks to Thomas Venieris (Closes: #564726) * debian/init.d: + Added dependency on $remote_fs in Required-Start and Required-Stop (Closes: #566862) * Converted package to use 3.0 (quilt) source format * Updated to Standards-Version 3.8.4 -- Kartik Mistry Mon, 01 Feb 2010 23:44:21 +0530 nginx (0.7.64-2) unstable; urgency=low [Kartik Mistry] * debian/rules: + Used dh_prep instead of dh_clean -k * debian/control: + Added ${misc:Depends} in Depends * Added patch for adding debug package, Thanks to Matthew Palmer (Closes: #563339) * debian/copyright: + Added missing copyright owners for Debian package and fixed year and licence doesn't point to versionless symlink [Fabio Tranchitella] * debian/init.d: + Added patch from Wouter de Bie to add $DAEMON_OPTS in test_nginx_config() -- Kartik Mistry Sat, 09 Jan 2010 11:15:59 +0530 nginx (0.7.64-1) unstable; urgency=medium [Kartik Mistry] * Urgency set to medium due to security issue * New upstream release (Closes: #557602) + fixes SSL renegotiation vuln CVE-2009-3555 (Closes: #557873) * debian/nginx.1: + Corrected homepage entry (Closes: #556617) + Minor whitespace and empty line cleanups, added SEE ALSO section. Used .TP instead of .br, Added missing options, and this should (Closes: #556616) * debian/copyright: + Used © instead of deprecated (C) symbol + Formatted some texts * debian/control: + Added myself as uploader + Wrapped and rearranged Build-Depends for better readability * debian/patches/dlopen.dpatch: + Fixed patch name in comment + Added missing DP comment from changelog * debian/rules: + Minor fixes related to formatting of file and whitespaces * debian/watch: + Removed comments out of it -- Kartik Mistry Fri, 27 Nov 2009 11:10:18 +0530 nginx (0.7.63-1) unstable; urgency=low * New upstream release. -- Fabio Tranchitella Sun, 08 Nov 2009 09:53:46 +0100 nginx (0.7.62-4) unstable; urgency=low * debian/conf/nginx.conf: commented out the mail proxy example. (Closes: #551682) * debian/init.d: do not redirect the output of nginx -t. (Closes: #551683) -- Fabio Tranchitella Thu, 22 Oct 2009 19:50:05 +0200 nginx (0.7.62-3) unstable; urgency=low * debian/rules: fix the FTBFS on sparc. -- Fabio Tranchitella Fri, 16 Oct 2009 06:39:22 +0000 nginx (0.7.62-2) unstable; urgency=low * debian/rules: added --with-mail and --with-mail_ssl_module. * debian/patches/nginx-upstream-fair.dpatch: added support for the nginx-upstream-fair module. (Closes: #521447) * debian/patches/dlopen.dpatch: applied patch to remove the unnecesssary link to libdl. (Closes: #540599) -- Fabio Tranchitella Thu, 15 Oct 2009 18:32:00 +0000 nginx (0.7.62-1) unstable; urgency=low * New upstream release. * debian/rules: added --with-ipv6. (Closes: #547249) -- Fabio Tranchitella Sun, 27 Sep 2009 22:25:16 +0200 nginx (0.7.61-2) unstable; urgency=low * debian/control: bumped Standards-Version to 3.8.3, no changes needed. * debian/init.d: added status action. (Closes: #541228) * debian/rules: - added -with-http_gzip_static_module. (Closes: #541229) - use the switch --with-cc-opt="-m32 -mcpu=ultrasparc" on sparc. (Closes: #543571) -- Fabio Tranchitella Sat, 29 Aug 2009 17:03:44 +0200 nginx (0.7.61-1) unstable; urgency=low * New upstream version: *) Bugfix: nginx could not be built --without-http-cache; the bug had appeared in 0.7.60. *) Bugfix: a segmentation fault occurred in worker process, if a backend 401 error was intercepted and the backend did not set the "WWW-Authenticate" response header line. Thanks to Eugene Mychlo. *) Feature: the "keepalive_requests" directive. *) Bugfix: in open_file_cache and proxy/fastcgi cache interaction on start up. *) Bugfix: open_file_cache might cache open file descriptors too long. *) Bugfix: XLST filter did not work in subrequests. * Fix "gzip_disable in default conf for old IE", adding ignore rules for certain MSIE versions in the default configuration (Closes: #540025) * Fix "init.d: Config test before allowing a restart" adding a function and calling it before reloading, restarting and starting (Closes: #539778) * Drops path for pidof call in postinst script. -- Jose Parrella Wed, 05 Aug 2009 14:00:11 -0500 nginx (0.7.59-1) unstable; urgency=low * New upstream release, first in Debian for the 0.7 branch. Among other issues, it also fixes the problem with wildcard dns names used with SSL. (Closes: #515904) * debian/watch: updated. * debian/postinst: fixed a bashism. (Closes: #507913) * debian/conf/nginx.conf: removed default_type. (Closes: #509390) * debian/control: updated Standards-Version to 3.8.1, no changes needed. * debian/NEWS.Debian: documented the issues with server_names_hash_bucket_size. (Closes: #524785) -- Fabio Tranchitella Sun, 31 May 2009 18:38:56 +0200 nginx (0.7.14-1) experimental; urgency=low * New upstream release. * Adding configtest to the init.d script (Closes: #496279) * postinst now calls the builtin kill, so tweaks on the specific signal calls were needed. * Default HTML files are no longer installed. * The default configuration file no longer requires a 50x file under /var/www. -- Jose Parrella Tue, 02 Sep 2008 20:48:58 -0430 nginx (0.6.34-2) unstable; urgency=low * Added support for realip module. (Closes: #507419) -- Fabio Tranchitella Sat, 06 Dec 2008 10:34:01 +0100 nginx (0.6.34-1) unstable; urgency=low * New upstream release. * debian/rules: removed the --with-debug configure switch. (Closes: #500891) -- Fabio Tranchitella Sun, 30 Nov 2008 11:37:13 +0100 nginx (0.6.32-3) unstable; urgency=low * debian/control: build again on all the architectures, but use the switch --with-cc-opt="-m32 -mcpu=ultrasparc" on sparch. (Closes: #479185) -- Fabio Tranchitella Thu, 25 Sep 2008 17:14:27 +0200 nginx (0.6.32-2) unstable; urgency=low * debian/control: disabled support for sparc. (Closes: #479185) -- Fabio Tranchitella Sat, 20 Sep 2008 11:48:48 +0200 nginx (0.6.32-1) unstable; urgency=low * New upstream release. -- Fabio Tranchitella Fri, 18 Jul 2008 09:15:58 +0200 nginx (0.6.31-2) unstable; urgency=low * debian/control: removed httpd-cgi, nginx doesn't support executing external programs. (Closes: #482332) -- Fabio Tranchitella Thu, 22 May 2008 08:18:52 +0200 nginx (0.6.31-1) unstable; urgency=low * New upstream release. -- Fabio Tranchitella Mon, 12 May 2008 22:34:55 +0200 nginx (0.6.30-2) unstable; urgency=low * Upload into unstable. -- Fabio Tranchitella Mon, 12 May 2008 14:24:53 +0200 nginx (0.6.30-1) experimental; urgency=low * New upstream release. * Sync with the unstable packages. * debian/conf/nginx.conf: add support for a conf.d directory. (Closes: #476952) -- Fabio Tranchitella Fri, 02 May 2008 09:32:46 +0200 nginx (0.5.35-3) unstable; urgency=low * debian/conf/sites-available/default: adding support for /doc and /images namespaces (Closes: #474519) -- Jose Parrella Mon, 07 Apr 2008 13:22:32 -0430 nginx (0.5.35-2) unstable; urgency=low * debian/init.d: do not break if start-stop-daemon exits with an error. (Closes: #464453) -- Fabio Tranchitella Sun, 10 Feb 2008 17:57:57 +0100 nginx (0.5.35-1) unstable; urgency=low * New upstream release. -- Fabio Tranchitella Wed, 09 Jan 2008 07:58:56 +0100 nginx (0.5.34-1) unstable; urgency=low * New upstream release. * debian/control: added Homepage, Vcs-Svn, Vcs-Browser fields; updated Standard-Versions to 3.7.3. -- Fabio Tranchitella Mon, 24 Dec 2007 12:26:27 +0100 nginx (0.5.33-1) unstable; urgency=low * New stable upstream release (Closes: #451173) * nginx now provides httpd, httpd-cgi virtual packages (Closes: #439468, #452025) * sites-enabled/default link is now provided only on fresh installations (Closes: #432961) * Updated code for online upgrading of nginx (Closes: #445246) * Reviewed maintainer scripts for correct behaviour on updates (Closes: #452787, #435965) * Removed debian/nginx.links and debian/preinst. * Changing Maintainer address to bureado@debian.org. * Welcoming Fabio Tranchitella as an nginx uploader for Debian. Thanks for your patches. -- Jose Parrella Sat, 08 Dec 2007 11:27:54 -0400 nginx (0.5.30-1) unstable; urgency=low * New stable upstream release. * Adding mime.types (and other upstream conffiles) to the package since it was lost on 0.5.26 (Closes: #435964) -- Jose Parrella Sat, 04 Aug 2007 10:39:40 -0400 nginx (0.5.26-1) unstable; urgency=low * New stable upstream release. * Updating init.d usage message. * Installing logrotate script (Closes: #397978) -- Jose Parrella Sun, 17 Jun 2007 20:43:35 +0100 nginx (0.5.18-1) unstable; urgency=low * New upstream version (Closes: #420063) * Adding DAV support (Closes: #415678) * Adding support for sites-[available|enabled]/ (Closes: #419912) -- Jose Parrella Thu, 19 Apr 2007 21:50:37 -0400 nginx (0.5.13-1) unstable; urgency=low * Adding FLV support. (Closes: #411105) * Adding SSL support. -- Jose Parrella Mon, 19 Feb 2007 10:30:07 -0400 nginx (0.5.12-2) unstable; urgency=low * Patching src/core/ngx_shmtx.h in order to solve FTBFS problems in non-x86 archs (Thanks to Igor Sysoev) (Closes: #410884) -- Jose Parrella Wed, 14 Feb 2007 13:32:01 -0400 nginx (0.5.12-1) unstable; urgency=low * New upstream version fixes FTBFS problems in non-x86 archs (Closes: #409878) -- Jose Parrella Mon, 12 Feb 2007 19:37:07 -0400 nginx (0.5.11-1) unstable; urgency=low * New upstream version. (Closes: #405983) -- Jose Parrella Sun, 5 Feb 2007 19:35:56 -0400 nginx (0.4.13-2) unstable; urgency=low * Fixed default installation paths for www/ files, avoiding the default installation of index.html in /var/www (Closes: #400103) * Alexey Besciokov provided new postinst and prerm scripts with the ability to reload the master binary (Closes: #398009) -- Jose Parrella Fri, 24 Nov 2006 07:12:46 -0400 nginx (0.4.13-1) unstable; urgency=low * New upstream version * Fixed default configuration file which made nginx to fail at installation and purge. -- Jose Parrella Thu, 16 Nov 2006 22:44:33 -0400 nginx (0.4.12-1) unstable; urgency=low * New upstream version (Closes: #397969) * nginx will now compile with debugging and status support (Closes: #397967) * Added configuration reload support in the init.d script (Closes: #397980) * Added logrotate script (Closes: #397978) * Added debian/upstream.pl as a brief helper for new upstream version packaging. * Thanks to Alexey Besciokov for suggestions, bug reports and patches. -- Jose Parrella Sun, 12 Nov 2006 18:31:24 -0400 nginx (0.4.2-1) unstable; urgency=low * Initial release (Closes: #386151) * Tweaked the configuration file and the path handling for Debian. -- Jose Parrella Thu, 14 Sep 2006 11:40:20 -0400 debian/nginx-core.postinst0000664000000000000000000000142012310175122013035 0ustar #!/bin/sh set -e case "$1" in abort-upgrade|abort-remove|abort-deconfigure|configure) ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac if [ -x /etc/init.d/nginx ]; then if [ -f /run/nginx.pid ] && pidof /usr/sbin/nginx >/dev/null; then NGX_PID=`cat /run/nginx.pid` if kill -s USR2 $NGX_PID 2>/dev/null; then while [ ! -s /run/nginx.pid.oldbin ] || [ ! -s /run/nginx.pid ]; do cnt=`expr $cnt + 1` if [ $cnt -gt 10 ]; then kill -s KILL $NGX_PID invoke-rc.d nginx start exit 0 fi sleep 1 done NGX_OLD_PID=`cat /run/nginx.pid.oldbin` kill -s QUIT $NGX_OLD_PID fi else invoke-rc.d nginx start || exit $? fi fi #DEBHELPER# exit 0 debian/nginx-naxsi.install0000664000000000000000000000004712305451330013020 0ustar debian/build-naxsi/objs/nginx usr/sbin debian/compat0000664000000000000000000000000212305451330010362 0ustar 9 debian/rules0000775000000000000000000001574012310175701010254 0ustar #!/usr/bin/make -f debian_cflags:=$(shell dpkg-buildflags --get CFLAGS | sed 's/-O3/-O2/') $(shell dpkg-buildflags --get CPPFLAGS) debian_ldflags:=$(shell dpkg-buildflags --get LDFLAGS) # export necessary for (hardening) flags for perl # (src/http/modules/perl/Makefile.PL). DEBIAN_NGINX_PERL_LDFLAGS:= $(debian_ldflags) export DEBIAN_NGINX_PERL_LDFLAGS FLAVOURS:=core full light extras naxsi BUILDDIR_core = $(CURDIR)/debian/build-core BUILDDIR_full = $(CURDIR)/debian/build-full BUILDDIR_light = $(CURDIR)/debian/build-light BUILDDIR_extras = $(CURDIR)/debian/build-extras BUILDDIR_naxsi = $(CURDIR)/debian/build-naxsi MODULESDIR = $(CURDIR)/debian/modules BASEDIR = $(CURDIR) DEB_BUILD_ARCH ?=$(shell dpkg-architecture -qDEB_BUILD_ARCH) ifeq ($(DEB_BUILD_ARCH),sparc) debian_cflags += -m32 -mcpu=ultrasparc endif ifneq (,$(filter parallel=%,$(DEB_BUILD_OPTIONS))) NUMJOBS = $(patsubst parallel=%,%,$(filter parallel=%,$(DEB_BUILD_OPTIONS))) ifeq (${NUMJOBS}, 0) NUMJOBS = 1 endif else NUMJOBS = 1 endif # configure flags common_configure_flags := \ --with-cc-opt="$(debian_cflags)" \ --with-ld-opt="$(debian_ldflags)" \ --prefix=/usr/share/nginx \ --conf-path=/etc/nginx/nginx.conf \ --http-log-path=/var/log/nginx/access.log \ --error-log-path=/var/log/nginx/error.log \ --lock-path=/var/lock/nginx.lock \ --pid-path=/run/nginx.pid \ --http-client-body-temp-path=/var/lib/nginx/body \ --http-fastcgi-temp-path=/var/lib/nginx/fastcgi \ --http-proxy-temp-path=/var/lib/nginx/proxy \ --http-scgi-temp-path=/var/lib/nginx/scgi \ --http-uwsgi-temp-path=/var/lib/nginx/uwsgi \ --with-debug \ --with-pcre-jit \ --with-ipv6 \ --with-http_ssl_module \ --with-http_stub_status_module \ --with-http_realip_module \ config.env.%: dh_testdir mkdir -p $(BUILDDIR_$*) cp -Pa $(CURDIR)/auto $(BUILDDIR_$*)/ cp -Pa $(CURDIR)/conf $(BUILDDIR_$*)/ cp -Pa $(CURDIR)/configure $(BUILDDIR_$*)/ cp -Pa $(CURDIR)/contrib $(BUILDDIR_$*)/ cp -Pa $(CURDIR)/src $(BUILDDIR_$*)/ cp -Pa $(CURDIR)/man $(BUILDDIR_$*)/ config.status.core: config.env.core cd $(BUILDDIR_core) && ./configure \ $(common_configure_flags) \ --with-http_addition_module \ --with-http_dav_module \ --with-http_geoip_module \ --with-http_gzip_static_module \ --with-http_image_filter_module \ --with-http_spdy_module \ --with-http_sub_module \ --with-http_xslt_module \ --with-mail \ --with-mail_ssl_module \ >$@ touch $@ config.status.full: config.env.full cd $(BUILDDIR_full) && ./configure \ $(common_configure_flags) \ --with-http_addition_module \ --with-http_dav_module \ --with-http_geoip_module \ --with-http_gzip_static_module \ --with-http_image_filter_module \ --with-http_spdy_module \ --with-http_sub_module \ --with-http_xslt_module \ --with-mail \ --with-mail_ssl_module \ --add-module=$(MODULESDIR)/nginx-auth-pam \ --add-module=$(MODULESDIR)/nginx-dav-ext-module \ --add-module=$(MODULESDIR)/nginx-echo \ --add-module=$(MODULESDIR)/nginx-upstream-fair \ --add-module=$(MODULESDIR)/ngx_http_substitutions_filter_module \ >$@ touch $@ config.status.light: config.env.light cd $(BUILDDIR_light) && ./configure \ $(common_configure_flags) \ --with-http_gzip_static_module \ --without-http_browser_module \ --without-http_geo_module \ --without-http_limit_req_module \ --without-http_limit_zone_module \ --without-http_memcached_module \ --without-http_referer_module \ --without-http_scgi_module \ --without-http_split_clients_module \ --without-http_ssi_module \ --without-http_userid_module \ --without-http_uwsgi_module \ --add-module=$(MODULESDIR)/nginx-echo \ >$@ touch $@ config.status.extras: config.env.extras cd $(BUILDDIR_extras) && ./configure \ $(common_configure_flags) \ --with-http_addition_module \ --with-http_dav_module \ --with-http_flv_module \ --with-http_geoip_module \ --with-http_gzip_static_module \ --with-http_image_filter_module \ --with-http_mp4_module \ --with-http_perl_module \ --with-http_random_index_module \ --with-http_secure_link_module \ --with-http_spdy_module \ --with-http_sub_module \ --with-http_xslt_module \ --with-mail \ --with-mail_ssl_module \ --add-module=$(MODULESDIR)/headers-more-nginx-module \ --add-module=$(MODULESDIR)/nginx-auth-pam \ --add-module=$(MODULESDIR)/nginx-cache-purge \ --add-module=$(MODULESDIR)/nginx-dav-ext-module \ --add-module=$(MODULESDIR)/nginx-development-kit \ --add-module=$(MODULESDIR)/nginx-echo \ --add-module=$(MODULESDIR)/ngx-fancyindex \ --add-module=$(MODULESDIR)/nginx-http-push \ --add-module=$(MODULESDIR)/nginx-lua \ --add-module=$(MODULESDIR)/nginx-upload-progress \ --add-module=$(MODULESDIR)/nginx-upstream-fair \ --add-module=$(MODULESDIR)/ngx_http_substitutions_filter_module \ >$@ touch $@ config.status.naxsi: config.env.naxsi cd $(BUILDDIR_naxsi) && ./configure \ $(common_configure_flags) \ --without-mail_pop3_module \ --without-mail_smtp_module \ --without-mail_imap_module \ --without-http_uwsgi_module \ --without-http_scgi_module \ --add-module=$(MODULESDIR)/naxsi/naxsi_src \ --add-module=$(MODULESDIR)/nginx-cache-purge \ --add-module=$(MODULESDIR)/nginx-upstream-fair \ >$@ touch $@ config.status.%: echo "configuration for flavour $* not yet defined." build-arch.%: config.status.% dh_testdir dh_prep $(MAKE) -j$(NUMJOBS) -C $(BUILDDIR_$*) build build-arch: $(foreach flavour,$(FLAVOURS),build-arch.$(flavour)) dh_testdir touch $@ build-dbg.%: install dh_testdir dh_strip --package=nginx-$(*) --dbg-package=nginx-$(*)-dbg build-dbg: $(foreach flavour,$(FLAVOURS),build-dbg.$(flavour)) dh_testdir touch $@ build-indep: build: build-indep build-arch dh_testdir touch $@ clean: dh_testdir dh_testroot dh_clean rm -rf $(CURDIR)/debian/build-* install: dh_testdir dh_testroot dh_prep dh_installdirs dh_install binary-indep: build install dh_testdir dh_testroot dh_installman -i dh_installchangelogs -i -k CHANGES dh_installdocs -i dh_installdebconf dh_installexamples -i dh_systemd_enable --name=nginx dh_installinit -i --no-restart-on-upgrade --no-start --name=nginx dh_installinit -i --no-restart-on-upgrade --no-start --name=nginx-naxsi-ui dh_systemd_start dh_installlogrotate -i -pnginx-common --name=nginx dh_link -i dh_compress -i dh_fixperms -i dh_installdeb -i dh_gencontrol -i dh_md5sums -i dh_builddeb -i binary-arch: install build-dbg dh_testdir dh_testroot dh_installchangelogs -a -k CHANGES dh_installdocs -a dh_lintian -a dh_link -aA dh_compress -a dh_perl -a dh_fixperms -a dh_installdeb -a dh_shlibdeps -a dh_gencontrol -a dh_md5sums -a dh_builddeb -a binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install debian/naxsi-ui-intercept0000664000000000000000000000026012305451330012635 0ustar #!/bin/bash cd /usr/share/nginx-naxsi/naxsi-ui/ python nx_intercept.py -c /etc/nginx/naxsi-ui.conf & somepid=$! echo $somepid > /run/nginx-naxsi-ui_intercept.pid wait $somepid debian/nginx-naxsi-ui.dirs0000664000000000000000000000005012305451330012720 0ustar usr/share/nginx-naxsi/naxsi-ui usr/sbin debian/nginx-full.postinst0000664000000000000000000000142012305451330013051 0ustar #!/bin/sh set -e case "$1" in abort-upgrade|abort-remove|abort-deconfigure|configure) ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac if [ -x /etc/init.d/nginx ]; then if [ -f /run/nginx.pid ] && pidof /usr/sbin/nginx >/dev/null; then NGX_PID=`cat /run/nginx.pid` if kill -s USR2 $NGX_PID 2>/dev/null; then while [ ! -s /run/nginx.pid.oldbin ] || [ ! -s /run/nginx.pid ]; do cnt=`expr $cnt + 1` if [ $cnt -gt 10 ]; then kill -s KILL $NGX_PID invoke-rc.d nginx start exit 0 fi sleep 1 done NGX_OLD_PID=`cat /run/nginx.pid.oldbin` kill -s QUIT $NGX_OLD_PID fi else invoke-rc.d nginx start || exit $? fi fi #DEBHELPER# exit 0 debian/nginx-naxsi-ui.preinst0000664000000000000000000000050712305451330013452 0ustar #!/bin/sh set -e case "$1" in upgrade) if dpkg --compare-versions "$2" lt 1.4.1; then mv /etc/nginx/naxsi-ui.conf /etc/nginx/naxsi-ui.conf.before_upgrade_to_1.4.1 fi ;; install|abort-upgrade) ;; *) echo "preinst called with unknown argument \`$1'" >&2 exit 1 ;; esac #DEBHELPER# exit 0 debian/nginx-common.postinst0000664000000000000000000000173512305451330013410 0ustar #!/bin/sh set -e case "$1" in configure) logdir="/var/log/nginx" # Ensure secure permissions (CVE-2013-0337) # http://bugs.debian.org/701112 # # nginx uses 0755 for log files making them world readable, # we fix that by using 0750 for the log directory. # # Allow local admin to override: # e.g. dpkg-statoverride --add root adm 0755 /var/log/nginx if ! dpkg-statoverride --list "$logdir" >/dev/null; then chown www-data:adm $logdir chmod 0750 $logdir fi # If a symlink doesn't exist and can be created, then create it. if [ -z $2 ] && [ ! -e /etc/nginx/sites-enabled/default ] && [ -d /etc/nginx/sites-enabled ] && [ -d /etc/nginx/sites-available ]; then ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default fi ;; abort-upgrade|abort-remove|abort-deconfigure) ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac #DEBHELPER# exit 0 debian/nginx-light.dirs0000664000000000000000000000001112305451330012267 0ustar usr/sbin debian/nginx-extras.prerm0000664000000000000000000000050412305451330012661 0ustar #!/bin/sh set -e case "$1" in remove|remove-in-favour|deconfigure|deconfigure-in-favour) if [ -x /etc/init.d/nginx ]; then invoke-rc.d nginx stop || exit $? fi ;; upgrade|failed-upgrade) ;; *) echo "prerm called with unknown argument \`$1'" >&2 exit 1 ;; esac #DEBHELPER# exit 0 debian/control0000664000000000000000000003235412310175325010601 0ustar Source: nginx Section: httpd Priority: optional Maintainer: Ubuntu Developers XSBC-Original-Maintainer: Kartik Mistry Uploaders: Jose Parrella , Fabio Tranchitella , Michael Lustfield , Dmitry E. Oboukhov , Cyril Lavier , Christos Trochalakis Build-Depends: autotools-dev, debhelper (>= 9), dh-systemd (>= 1.5), dpkg-dev (>= 1.15.5), libexpat-dev, libgd2-dev | libgd2-noxpm-dev, libgeoip-dev, liblua5.1-dev, libmhash-dev, libpam0g-dev, libpcre3-dev, libperl-dev, libssl-dev, libxslt1-dev, po-debconf, zlib1g-dev Standards-Version: 3.9.5 Homepage: http://nginx.net Vcs-Git: git://anonscm.debian.org/collab-maint/nginx.git Vcs-Browser: http://anonscm.debian.org/gitweb/?p=collab-maint/nginx.git;a=summary Package: nginx Architecture: all Depends: nginx-core (>= ${source:Version}) | nginx-full (>= ${source:Version}) | nginx-light (>= ${source:Version}) | nginx-extras (>= ${source:Version}) | nginx-naxsi (>= ${source:Version}) , nginx-core (<< ${source:Version}.1~) | nginx-full (<< ${source:Version}.1~) | nginx-light (<< ${source:Version}.1~) | nginx-extras (<< ${source:Version}.1~) | nginx-naxsi (<< ${source:Version}.1~) , ${misc:Depends} Description: small, powerful, scalable web/proxy server Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This is a dependency package to install either nginx-core (by default), nginx-full, nginx-light, nginx-extras, or nginx-naxsi. Package: nginx-doc Architecture: all Section: doc Depends: lsb-base (>= 3.2-14), ${misc:Depends} Description: small, powerful, scalable web/proxy server - documentation Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides extra documentation to help unleash the power of Nginx. Package: nginx-common Architecture: all Depends: lsb-base (>= 3.2-14), ${misc:Depends} Replaces: nginx (<< 0.8.54-4), nginx-extras (<< 0.8.54-4), nginx-full (<< 0.8.54-4), nginx-light (<< 0.8.54-4) Breaks: nginx (<< 0.8.54-4), nginx-extras (<< 0.8.54-4), nginx-full (<< 0.8.54-4), nginx-light (<< 0.8.54-4) Suggests: fcgiwrap, nginx-doc Description: small, powerful, scalable web/proxy server - common files Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package contains base configuration files used by all versions of nginx. Package: nginx-core Architecture: any Depends: nginx-common (= ${source:Version}), ${misc:Depends}, ${shlibs:Depends} Breaks: nginx (<< 1.4.5-1) Provides: httpd, httpd-cgi, nginx Conflicts: nginx-extras, nginx-light, nginx-naxsi, nginx-full Suggests: nginx-doc (= ${source:Version}) Description: nginx web/proxy server (core version) Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides a version of nginx with the complete set of standard modules included (but omitting some of those included in nginx-extras). . STANDARD HTTP MODULES: Core, Access, Auth Basic, Auto Index, Browser, Charset, Empty GIF, FastCGI, Geo, Gzip, Headers, Index, Limit Requests, Limit Zone, Log, Map, Memcached, Proxy, Referer, Rewrite, SCGI, Split Clients, SSI, Upstream, User ID, UWSGI. . OPTIONAL HTTP MODULES: Addition, Debug, GeoIP, Gzip Precompression, HTTP Sub, Image Filter, IPv6, Real IP, Spdy, SSL, Stub Status, Substitution, WebDAV, XSLT. . MAIL MODULES: Mail Core, IMAP, POP3, SMTP, SSL. . NOTE: This is identical to the -full build, but without any third party modules built in. Package: nginx-core-dbg Architecture: any Section: debug Priority: extra Depends: nginx-core (= ${binary:Version}), ${misc:Depends} Conflicts: nginx-extras-dbg, nginx-light-dbg, nginx-naxsi-dbg, nginx-full-dbg Description: nginx web/proxy server (core version) - debugging symbols Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides debugging symbols for nginx-core, to assist in debugging issues that you may find. It should not be required for normal operation. Package: nginx-full Architecture: any Depends: nginx-common (= ${source:Version}), ${misc:Depends}, ${shlibs:Depends} Breaks: nginx (<< 1.4.5-1) Provides: httpd, httpd-cgi, nginx Conflicts: nginx-extras, nginx-light, nginx-naxsi, nginx-core Suggests: nginx-doc (= ${source:Version}) Description: nginx web/proxy server (standard version) Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides a version of nginx with the complete set of standard modules included (but omitting some of those included in nginx-extra). . STANDARD HTTP MODULES: Core, Access, Auth Basic, Auto Index, Browser, Charset, Empty GIF, FastCGI, Geo, Gzip, Headers, Index, Limit Requests, Limit Zone, Log, Map, Memcached, Proxy, Referer, Rewrite, SCGI, Split Clients, SSI, Upstream, User ID, UWSGI. . OPTIONAL HTTP MODULES: Addition, Debug, GeoIP, Gzip Precompression, HTTP Sub, Image Filter, IPv6, Real IP, Spdy, SSL, Stub Status, Substitution, WebDAV, XSLT. . MAIL MODULES: Mail Core, IMAP, POP3, SMTP, SSL. . THIRD PARTY MODULES: Auth PAM, DAV Ext, Echo, HTTP Substitution Filter, Upstream Fair Queue. Package: nginx-full-dbg Architecture: any Section: debug Priority: extra Depends: nginx-full (= ${binary:Version}), ${misc:Depends} Conflicts: nginx-extras-dbg, nginx-light-dbg, nginx-naxsi-dbg, nginx-core-dbg Description: nginx web/proxy server (standard version) - debugging symbols Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides debugging symbols for nginx-full, to assist in debugging issues that you may find. It should not be required for normal operation. Package: nginx-light Architecture: any Priority: extra Depends: nginx-common (= ${source:Version}), ${misc:Depends}, ${shlibs:Depends} Breaks: nginx (<< 1.4.5-1) Provides: httpd, httpd-cgi, nginx Conflicts: nginx-extras, nginx-full, nginx-naxsi, nginx-core Suggests: nginx-doc (= ${source:Version}) Description: nginx web/proxy server (basic version) Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides a very light version of nginx with only the minimal set of features and modules. . STANDARD HTTP MODULES: Core, Access, Auth Basic, Auto Index, Charset, Empty GIF, FastCGI, Gzip, Headers, Index, Log, Map, Proxy, Rewrite, Upstream. . OPTIONAL HTTP MODULES: Debug, Gzip Precompression, IPv6, Real Ip, SSL, Stub Status. . THIRD PARTY MODULES: Echo. Package: nginx-light-dbg Architecture: any Section: debug Priority: extra Depends: nginx-light (= ${binary:Version}), ${misc:Depends} Conflicts: nginx-extras-dbg, nginx-full-dbg, nginx-naxsi-dbg, nginx-core-dbg Description: nginx web/proxy server (basic version) - debugging symbols Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides debugging symbols for nginx-light, to assist in debugging issues that you may find. It should not be required for normal operation. Package: nginx-extras Architecture: any Priority: extra Depends: nginx-common (= ${source:Version}), ${misc:Depends}, ${perl:Depends}, ${shlibs:Depends} Breaks: nginx (<< 1.4.5-1) Provides: httpd, httpd-cgi, nginx Conflicts: nginx-full, nginx-light, nginx-naxsi, nginx-core Suggests: nginx-doc (= ${source:Version}) Description: nginx web/proxy server (extended version) Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides a version of nginx with the standard modules, plus extra features and modules such as the Perl module, which allows the addition of Perl in configuration files. . STANDARD HTTP MODULES: Core, Access, Auth Basic, Auto Index, Browser, Charset, Empty GIF, FastCGI, Geo, Gzip, Headers, Index, Limit Requests, Limit Zone, Log, Map, Memcached, Proxy, Referer, Rewrite, SCGI, Split Clients, SSI, Upstream, User ID, UWSGI. . OPTIONAL HTTP MODULES: Addition, Debug, Embedded Perl, FLV, GeoIP, Gzip Precompression, Image Filter, IPv6, MP4, Random Index, Real IP, Secure Link, Spdy, SSL, Stub Status, Substitution, WebDAV, XSLT. . MAIL MODULES: Mail Core, IMAP, POP3, SMTP, SSL. . THIRD PARTY MODULES: Auth PAM, Chunkin, DAV Ext, Echo, Embedded Lua, Fancy Index, HttpHeadersMore, HTTP Substitution Filter, http push, Nginx Development Kit, Upload Progress, Upstream Fair Queue. Package: nginx-extras-dbg Architecture: any Section: debug Priority: extra Depends: nginx-extras (= ${binary:Version}), ${misc:Depends} Conflicts: nginx-full-dbg, nginx-light-dbg, nginx-naxsi-dbg, nginx-core-dbg Description: nginx web/proxy server (extended version) - debugging symbols Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides debugging symbols for nginx-extras, to assist in debugging issues that you may find. It should not be required for normal operation. Package: nginx-naxsi Architecture: any Priority: extra Depends: nginx-common (= ${source:Version}), ${misc:Depends}, ${shlibs:Depends} Breaks: nginx (<< 1.4.5-1), nginx-naxsi-ui (<< 1.4.5-1) Provides: httpd, httpd-cgi, nginx Conflicts: nginx-extras, nginx-full, nginx-light, nginx-core Description: nginx web/proxy server (version with naxsi) Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides a version of nginx with the basic modules, plus the naxsi Web Application Firewall module. . STANDARD HTTP MODULES: Core, Access, Auth Basic, Auto Index, Browser, Charset, Core, Empty GIF, FastCGI, Geo, Gzip, Headers, Index, Limit Requests, Limit Zone, Log, Map, Memcached, Proxy, Referer, Rewrite, Split Clients, SSI, Upstream, User ID. . OPTIONAL HTTP MODULES: Debug, IPv6, Real IP, SSL, Stub Status. . THIRD PARTY MODULES: Naxsi, Cache Purge, Upstream Fair. Package: nginx-naxsi-dbg Architecture: any Section: debug Priority: extra Depends: nginx-naxsi (= ${binary:Version}), ${misc:Depends} Conflicts: nginx-extras-dbg, nginx-full-dbg, nginx-light-dbg, nginx-core-dbg Description: nginx web/proxy server (version with naxsi) - debugging symbols Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides debugging symbols for nginx-naxsi, to assist in debugging issues that you may find. It should not be required for normal operation. Package: nginx-naxsi-ui Architecture: all Priority: extra Depends: daemon, dbconfig-common, nginx-naxsi (>= ${source:Version}), nginx-naxsi (<< ${source:Version}.1~), python-twisted-web, ${misc:Depends} Description: nginx web/proxy server - naxsi configuration front-end Nginx ("engine X") is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers. . This package provides the autolearning daemon and web user interface for nginx's naxsi module. . It includes an interceptor (listening on TCP port 8080), which monitors HTTP requests from naxsi, and an extractor (running on TCP port 8081), which reads the database and prints reports about blocked requests. debian/modules/0000775000000000000000000000000012305451341010636 5ustar debian/modules/ngx-fancyindex/0000775000000000000000000000000012305451341013560 5ustar debian/modules/ngx-fancyindex/ngx_http_fancyindex_module.c0000664000000000000000000011321512305451334021341 0ustar /* * ngx_http_fancyindex_module.c * Copyright © 2007-2013 Adrian Perez * * Module used for fancy indexing of directories. Features and differences * with the stock nginx autoindex module: * * - Output is a table instead of a
 element with embedded  links.
 *  - Header and footer may be added to every generated directory listing.
 *  - Default header and/or footer are generated if custom ones are not
 *    configured. Files used for header and footer can only be local path
 *    names (i.e. you cannot insert the result of a subrequest.)
 *  - Proper HTML is generated: it should validate both as XHTML 1.0 Strict
 *    and HTML 4.01.
 *
 * Base functionality heavy based upon the stock nginx autoindex module,
 * which in turn was made by Igor Sysoev, like the majority of nginx.
 *
 * Distributed under terms of the BSD license.
 */

#include 
#include 
#include 
#include 

#include "template.h"

#if defined(__GNUC__) && (__GNUC__ >= 3)
# define ngx_force_inline __attribute__((__always_inline__))
#else /* !__GNUC__ */
# define ngx_force_inline
#endif /* __GNUC__ */



/**
 * Configuration structure for the fancyindex module. The configuration
 * commands defined in the module do fill in the members of this structure.
 */
typedef struct {
    ngx_flag_t enable;       /**< Module is enabled. */
    ngx_flag_t localtime;    /**< File mtime dates are sent in local time. */
    ngx_flag_t exact_size;   /**< Sizes are sent always in bytes. */

    ngx_str_t  header;       /**< File name for header, or empty if none. */
    ngx_str_t  footer;       /**< File name for footer, or empty if none. */
    ngx_str_t  css_href;     /**< Link to a CSS stylesheet, or empty if none. */

    ngx_array_t *ignore;     /**< List of files to ignore in listings. */
} ngx_http_fancyindex_loc_conf_t;


#define NGX_HTTP_FANCYINDEX_PREALLOCATE  50
#define NGX_HTTP_FANCYINDEX_NAME_LEN     50


/**
 * Calculates the length of a NULL-terminated string. It is ugly having to
 * remember to substract 1 from the sizeof result.
 */
#define ngx_sizeof_ssz(_s)  (sizeof(_s) - 1)


/**
 * Copy a static zero-terminated string. Useful to output template
 * string pieces into a temporary buffer.
 */
#define ngx_cpymem_ssz(_p, _t) \
	(ngx_cpymem((_p), (_t), sizeof(_t) - 1))

/**
 * Copy a ngx_str_t.
 */
#define ngx_cpymem_str(_p, _s) \
	(ngx_cpymem((_p), (_s).data, (_s).len))

/**
 * Check whether a particular bit is set in a particular value.
 */
#define ngx_has_flag(_where, _what) \
	(((_where) & (_what)) == (_what))




typedef struct {
    ngx_str_t      name;
    size_t         utf_len;
    ngx_uint_t     escape;
    ngx_uint_t     dir;
    time_t         mtime;
    off_t          size;
} ngx_http_fancyindex_entry_t;



static int ngx_libc_cdecl
    ngx_http_fancyindex_cmp_entries_name_desc(const void *one, const void *two);
static int ngx_libc_cdecl
    ngx_http_fancyindex_cmp_entries_size_desc(const void *one, const void *two);
static int ngx_libc_cdecl
    ngx_http_fancyindex_cmp_entries_mtime_desc(const void *one, const void *two);
static int ngx_libc_cdecl
    ngx_http_fancyindex_cmp_entries_name_asc(const void *one, const void *two);
static int ngx_libc_cdecl
    ngx_http_fancyindex_cmp_entries_size_asc(const void *one, const void *two);
static int ngx_libc_cdecl
    ngx_http_fancyindex_cmp_entries_mtime_asc(const void *one, const void *two);

static ngx_int_t ngx_http_fancyindex_error(ngx_http_request_t *r,
    ngx_dir_t *dir, ngx_str_t *name);

static ngx_int_t ngx_http_fancyindex_init(ngx_conf_t *cf);

static void *ngx_http_fancyindex_create_loc_conf(ngx_conf_t *cf);

static char *ngx_http_fancyindex_merge_loc_conf(ngx_conf_t *cf,
    void *parent, void *child);

static char *ngx_http_fancyindex_ignore(ngx_conf_t    *cf,
                                        ngx_command_t *cmd,
                                        void          *conf);

static uintptr_t
    ngx_fancyindex_escape_uri(u_char *dst, u_char*src, size_t size);

/*
 * These are used only once per handler invocation. We can tell GCC to
 * inline them always, if possible (see how ngx_force_inline is defined
 * above).
 */
static ngx_inline ngx_buf_t*
    make_header_buf(ngx_http_request_t *r, const ngx_str_t css_href)
    ngx_force_inline;

static ngx_inline ngx_buf_t*
    make_footer_buf(ngx_http_request_t *r)
    ngx_force_inline;



static ngx_command_t  ngx_http_fancyindex_commands[] = {

    { ngx_string("fancyindex"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_fancyindex_loc_conf_t, enable),
      NULL },

    { ngx_string("fancyindex_localtime"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_fancyindex_loc_conf_t, localtime),
      NULL },

    { ngx_string("fancyindex_exact_size"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_fancyindex_loc_conf_t, exact_size),
      NULL },

    { ngx_string("fancyindex_header"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
      ngx_conf_set_str_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_fancyindex_loc_conf_t, header),
      NULL },

    { ngx_string("fancyindex_footer"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
      ngx_conf_set_str_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_fancyindex_loc_conf_t, footer),
      NULL },

    { ngx_string("fancyindex_css_href"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
      ngx_conf_set_str_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_fancyindex_loc_conf_t, css_href),
      NULL },

    { ngx_string("fancyindex_ignore"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
      ngx_http_fancyindex_ignore,
      NGX_HTTP_LOC_CONF_OFFSET,
      0,
      NULL },

    ngx_null_command
};


static ngx_http_module_t  ngx_http_fancyindex_module_ctx = {
    NULL,                                  /* preconfiguration */
    ngx_http_fancyindex_init,              /* postconfiguration */

    NULL,                                  /* create main configuration */
    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */
    NULL,                                  /* merge server configuration */

    ngx_http_fancyindex_create_loc_conf,   /* create location configration */
    ngx_http_fancyindex_merge_loc_conf     /* merge location configration */
};


ngx_module_t  ngx_http_fancyindex_module = {
    NGX_MODULE_V1,
    &ngx_http_fancyindex_module_ctx,       /* module context */
    ngx_http_fancyindex_commands,          /* module directives */
    NGX_HTTP_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};



static const ngx_str_t css_href_pre =
    ngx_string("\n");


static uintptr_t
ngx_fancyindex_escape_uri(u_char *dst, u_char *src, size_t size)
{
    /*
     * The ngx_escape_uri() function will not escape colons or the
     * ? character, which signals the beginning of the query string.
     * So we handle those characters ourselves.
     *
     * TODO: Get rid of this once ngx_escape_uri() works as expected!
     */

    u_int escapes = 0;
    u_char *psrc = src;
    size_t psize = size;

    while (psize--) {
        switch (*psrc++) {
            case ':':
            case '?':
                escapes++;
                break;
        }
    }

    if (dst == NULL) {
        return escapes + ngx_escape_uri(NULL, src, size, NGX_ESCAPE_HTML);
    }
    else if (escapes == 0) {
        /* No need to do extra escaping, avoid the temporary buffer */
        return ngx_escape_uri(dst, src, size, NGX_ESCAPE_HTML);
    }
    else {
        uintptr_t uescapes = ngx_escape_uri(NULL, src, size, NGX_ESCAPE_HTML);
        size_t bufsz = size + 2 * uescapes;

        /*
         * GCC and CLANG both support stack-allocated variable length
         * arrays. Take advantage of that to avoid a malloc-free cycle.
         */
#if defined(__GNUC__) || defined(__clang__)
        u_char cbuf[bufsz];
        u_char *buf = cbuf;
#else  /* __GNUC__ || __clang__ */
        u_char *buf = (u_char*) malloc(sizeof(u_char) * bufsz);
#endif /* __GNUC__ || __clang__ */

        ngx_escape_uri(buf, src, size, NGX_ESCAPE_HTML);

        while (bufsz--) {
            switch (*buf) {
                case ':':
                    *dst++ = '%';
                    *dst++ = '3';
                    *dst++ = 'A';
                    break;
                case '?':
                    *dst++ = '%';
                    *dst++ = '3';
                    *dst++ = 'F';
                    break;
                default:
                    *dst++ = *buf;
            }
            buf++;
        }

#if !defined(__GNUC__) && !defined(__clang__)
        free(buf);
#endif /* !__GNUC__ && !__clang__ */

        return escapes + uescapes;
    }
}


static ngx_inline ngx_buf_t*
make_header_buf(ngx_http_request_t *r, const ngx_str_t css_href)
{
    size_t blen = r->uri.len
        + ngx_sizeof_ssz(t01_head1)
        + ngx_sizeof_ssz(t02_head2)
        + ngx_sizeof_ssz(t03_head3)
        + ngx_sizeof_ssz(t04_body1)
        ;

    if (css_href.len) {
        blen += css_href_pre.len \
              + css_href.len \
              + css_href_post.len
              ;
    }

    ngx_buf_t *b = ngx_create_temp_buf(r->pool, blen);

    if (b == NULL) goto bailout;

    b->last = ngx_cpymem_ssz(b->last, t01_head1);

    if (css_href.len) {
        b->last = ngx_cpymem_str(b->last, css_href_pre);
        b->last = ngx_cpymem_str(b->last, css_href);
        b->last = ngx_cpymem_str(b->last, css_href_post);
    }

    b->last = ngx_cpymem_ssz(b->last, t02_head2);
    b->last = ngx_cpymem_str(b->last, r->uri);
    b->last = ngx_cpymem_ssz(b->last, t03_head3);
    b->last = ngx_cpymem_ssz(b->last, t04_body1);

bailout:
    return b;
}



static ngx_inline ngx_buf_t*
make_footer_buf(ngx_http_request_t *r)
{
    /*
     * TODO: Make this buffer static (i.e. readonly and reusable from
     * one request to another.
     */
    ngx_buf_t *b = ngx_create_temp_buf(r->pool, ngx_sizeof_ssz(t08_foot1));

    if (b == NULL) goto bailout;

    b->last = ngx_cpymem_ssz(b->last, t08_foot1);

bailout:
    return b;
}



static ngx_inline ngx_int_t
make_content_buf(
        ngx_http_request_t *r, ngx_buf_t **pb,
        ngx_http_fancyindex_loc_conf_t *alcf)
{
    ngx_http_fancyindex_entry_t *entry;

    int (*sort_cmp_func) (const void*, const void*);
    const char  *sort_url_args = "";

    off_t        length;
    size_t       len, root, copy, allocated;
    u_char      *filename, *last, scale;
    ngx_tm_t     tm;
    ngx_array_t  entries;
    ngx_time_t  *tp;
    ngx_uint_t   i;
    ngx_int_t    size;
    ngx_str_t    path;
    ngx_dir_t    dir;
    ngx_buf_t   *b;

    static char *months[] = {
        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
    };

    /*
     * NGX_DIR_MASK_LEN is lesser than NGX_HTTP_FANCYINDEX_PREALLOCATE
     */
    if ((last = ngx_http_map_uri_to_path(r, &path, &root,
                    NGX_HTTP_FANCYINDEX_PREALLOCATE)) == NULL)
        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    allocated = path.len;
    path.len  = last - path.data - 1;
    path.data[path.len] = '\0';

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http fancyindex: \"%s\"", path.data);

    if (ngx_open_dir(&path, &dir) == NGX_ERROR) {
        ngx_int_t rc, err = ngx_errno;
        ngx_uint_t level;

        if (err == NGX_ENOENT || err == NGX_ENOTDIR || err == NGX_ENAMETOOLONG) {
            level = NGX_LOG_ERR;
            rc = NGX_HTTP_NOT_FOUND;
        } else if (err == NGX_EACCES) {
            level = NGX_LOG_ERR;
            rc = NGX_HTTP_FORBIDDEN;
        } else {
            level = NGX_LOG_CRIT;
            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        ngx_log_error(level, r->connection->log, err,
                ngx_open_dir_n " \"%s\" failed", path.data);

        return rc;
    }

#if (NGX_SUPPRESS_WARN)
    /* MSVC thinks 'entries' may be used without having been initialized */
    ngx_memzero(&entries, sizeof(ngx_array_t));
#endif /* NGX_SUPPRESS_WARN */


    if (ngx_array_init(&entries, r->pool, 40,
                sizeof(ngx_http_fancyindex_entry_t)) != NGX_OK)
        return ngx_http_fancyindex_error(r, &dir, &path);

    filename = path.data;
    filename[path.len] = '/';

    /* Read directory entries and their associated information. */
    for (;;) {
        ngx_set_errno(0);

        if (ngx_read_dir(&dir) == NGX_ERROR) {
            ngx_int_t err = ngx_errno;

            if (err != NGX_ENOMOREFILES) {
                ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
                        ngx_read_dir_n " \"%V\" failed", &path);
                return ngx_http_fancyindex_error(r, &dir, &path);
            }
            break;
        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "http fancyindex file: \"%s\"", ngx_de_name(&dir));

        len = ngx_de_namelen(&dir);

        if (ngx_de_name(&dir)[0] == '.')
            continue;

#if NGX_PCRE
        {
            ngx_str_t str = { len, ngx_de_name(&dir) };

            if (alcf->ignore && ngx_regex_exec_array(alcf->ignore, &str,
                                                     r->connection->log)
                != NGX_DECLINED)
            {
                continue;
            }
        }
#else /* !NGX_PCRE */
        if (alcf->ignore) {
            u_int match_found = 0;
            ngx_str_t *s = alcf->ignore->elts;

            for (i = 0; i < alcf->ignore->nelts; i++, s++) {
                if (ngx_strcmp(ngx_de_name(&dir), s->data) == 0) {
                    match_found = 1;
                    break;
                }
            }

            if (match_found) {
                continue;
            }
        }
#endif /* NGX_PCRE */

        if (!dir.valid_info) {
            /* 1 byte for '/' and 1 byte for terminating '\0' */
            if (path.len + 1 + len + 1 > allocated) {
                allocated = path.len + 1 + len + 1
                          + NGX_HTTP_FANCYINDEX_PREALLOCATE;

                if ((filename = ngx_palloc(r->pool, allocated)) == NULL)
                    return ngx_http_fancyindex_error(r, &dir, &path);

                last = ngx_cpystrn(filename, path.data, path.len + 1);
                *last++ = '/';
            }

            ngx_cpystrn(last, ngx_de_name(&dir), len + 1);

            if (ngx_de_info(filename, &dir) == NGX_FILE_ERROR) {
                ngx_int_t err = ngx_errno;

                if (err != NGX_ENOENT) {
                    ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
                            ngx_de_info_n " \"%s\" failed", filename);
                    continue;
                }

                if (ngx_de_link_info(filename, &dir) == NGX_FILE_ERROR) {
                    ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
                            ngx_de_link_info_n " \"%s\" failed", filename);
                    return ngx_http_fancyindex_error(r, &dir, &path);
                }
            }
        }

        if ((entry = ngx_array_push(&entries)) == NULL)
            return ngx_http_fancyindex_error(r, &dir, &path);

        entry->name.len  = len;
        entry->name.data = ngx_palloc(r->pool, len + 1);
        if (entry->name.data == NULL)
            return ngx_http_fancyindex_error(r, &dir, &path);

        ngx_cpystrn(entry->name.data, ngx_de_name(&dir), len + 1);
        entry->escape = 2 * ngx_fancyindex_escape_uri(NULL,
                                                      ngx_de_name(&dir),
                                                      len);

        entry->dir     = ngx_de_is_dir(&dir);
        entry->mtime   = ngx_de_mtime(&dir);
        entry->size    = ngx_de_size(&dir);
        entry->utf_len = (r->headers_out.charset.len == 5 &&
                ngx_strncasecmp(r->headers_out.charset.data, (u_char*) "utf-8", 5) == 0)
            ?  ngx_utf8_length(entry->name.data, entry->name.len)
            : len;
    }

    if (ngx_close_dir(&dir) == NGX_ERROR) {
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
                ngx_close_dir_n " \"%s\" failed", &path);
    }

    /*
     * Calculate needed buffer length.
     */
    len = r->uri.len
        + ngx_sizeof_ssz(t05_body2)
        + ngx_sizeof_ssz(t06_list1)
        + ngx_sizeof_ssz(t_parentdir_entry)
        + ngx_sizeof_ssz(t07_list2)
        ;

    entry = entries.elts;
    for (i = 0; i < entries.nelts; i++) {
        /*
         * Genearated table rows are as follows, unneeded whitespace
         * is stripped out:
         *
         *   
         *     fname
         *     sizedate
         *   
         */
        len += ngx_sizeof_ssz("")
            + entry[i].name.len + entry[i].utf_len
            + NGX_HTTP_FANCYINDEX_NAME_LEN + ngx_sizeof_ssz(">")
            + ngx_sizeof_ssz("")
            + 20 /* File size */
            + ngx_sizeof_ssz("")
            + ngx_sizeof_ssz(" 28-Sep-1970 12:00 ")
            + ngx_sizeof_ssz("\n")
            + 2 /* CR LF */
            ;
    }

    if ((b = ngx_create_temp_buf(r->pool, len)) == NULL)
        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    /*
     * Determine the sorting criteria. URL arguments look like:
     *
     *    C=x[&O=y]
     *
     * Where x={M,S,N} and y={A,D}
     */
    if ((r->args.len == 3 || (r->args.len == 7 && r->args.data[3] == '&')) &&
        r->args.data[0] == 'C' && r->args.data[1] == '=')
    {
        /* Determine whether the direction of the sorting */
        ngx_int_t sort_descending = r->args.len == 7
                                 && r->args.data[4] == 'O'
                                 && r->args.data[5] == '='
                                 && r->args.data[6] == 'D';

        /* Pick the sorting criteria */
        switch (r->args.data[2]) {
            case 'M': /* Sort by mtime */
                if (sort_descending) {
                    sort_cmp_func = ngx_http_fancyindex_cmp_entries_mtime_desc;
                    sort_url_args = "?C=M&O=D";
                }
                else {
                    sort_cmp_func = ngx_http_fancyindex_cmp_entries_mtime_asc;
                    sort_url_args = "?C=M&O=A";
                }
                break;
            case 'S': /* Sort by size */
                if (sort_descending) {
                    sort_cmp_func = ngx_http_fancyindex_cmp_entries_size_desc;
                    sort_url_args = "?C=S&O=D";
                }
                else {
                    sort_cmp_func = ngx_http_fancyindex_cmp_entries_size_asc;
                    sort_url_args = "?C=S&O=A";
                }
                break;
            case 'N': /* Sort by name */
            default:
                if (sort_descending) {
                    sort_cmp_func = ngx_http_fancyindex_cmp_entries_name_desc;
                    sort_url_args = "?C=N&O=D";
                }
                else {
                    sort_cmp_func = ngx_http_fancyindex_cmp_entries_name_asc;
                }
                break;
        }
    }
    else {
        sort_cmp_func = ngx_http_fancyindex_cmp_entries_name_asc;
    }

    /* Sort entries, if needed */
    if (entries.nelts > 1) {
        ngx_qsort(entry, (size_t) entries.nelts,
                  sizeof(ngx_http_fancyindex_entry_t),
                  sort_cmp_func);
    }

    b->last = ngx_cpymem_str(b->last, r->uri);
    b->last = ngx_cpymem_ssz(b->last, t05_body2);
    b->last = ngx_cpymem_ssz(b->last, t06_list1);

    tp = ngx_timeofday();

    /* "Parent dir" entry, always first */
    b->last = ngx_cpymem_ssz(b->last,
                             ""
                             "last = ngx_cpymem(b->last,
                             sort_url_args,
                             ngx_sizeof_ssz("?C=N&O=A"));
    }
    b->last = ngx_cpymem_ssz(b->last,
                             "\">Parent directory/"
                             "-"
                             "-"
                             "");

    /* Entries for directories and files */
    for (i = 0; i < entries.nelts; i++) {
        static const char _evenodd[] = { 'e', 'o' };
        b->last = ngx_cpymem_ssz(b->last, "last++ = _evenodd[i & 0x01];
        /*
         * Alternative implementation:
         *   *b->last++ = (i & 0x01) ? 'e' : 'o';
         */
        b->last = ngx_cpymem_ssz(b->last, "\">last,
                                      entry[i].name.data,
                                      entry[i].name.len);

            b->last += entry[i].name.len + entry[i].escape;

        } else {
            b->last = ngx_cpymem_str(b->last, entry[i].name);
        }

        if (entry[i].dir) {
            *b->last++ = '/';
            if (*sort_url_args) {
                b->last = ngx_cpymem(b->last,
                                     sort_url_args,
                                     ngx_sizeof_ssz("?C=x&O=y"));
            }
        }

        *b->last++ = '"';
        *b->last++ = '>';

        len = entry[i].utf_len;

        if (entry[i].name.len - len) {
            if (len > NGX_HTTP_FANCYINDEX_NAME_LEN) {
                copy = NGX_HTTP_FANCYINDEX_NAME_LEN - 3 + 1;
            } else {
                copy = NGX_HTTP_FANCYINDEX_NAME_LEN + 1;
            }

            b->last = ngx_utf8_cpystrn(b->last, entry[i].name.data,
                                          copy, entry[i].name.len);
            last = b->last;

        } else {
            b->last = ngx_cpystrn(b->last, entry[i].name.data,
                                  NGX_HTTP_FANCYINDEX_NAME_LEN + 1);
            last = b->last - 3;
        }

        if (len > NGX_HTTP_FANCYINDEX_NAME_LEN) {
            b->last = ngx_cpymem_ssz(last, "..>");

        } else {
            if (entry[i].dir && NGX_HTTP_FANCYINDEX_NAME_LEN - len > 0) {
                *b->last++ = '/';
                len++;
            }

            b->last = ngx_cpymem_ssz(b->last, "");
        }

        if (alcf->exact_size) {
            if (entry[i].dir) {
                *b->last++ = '-';
            } else {
                b->last = ngx_sprintf(b->last, "%19O", entry[i].size);
            }

        } else {
            if (entry[i].dir) {
                *b->last++ = '-';
            } else {
                length = entry[i].size;

                if (length > 1024 * 1024 * 1024 - 1) {
                    size = (ngx_int_t) (length / (1024 * 1024 * 1024));
                    if ((length % (1024 * 1024 * 1024))
                                                > (1024 * 1024 * 1024 / 2 - 1))
                    {
                        size++;
                    }
                    scale = 'G';

                } else if (length > 1024 * 1024 - 1) {
                    size = (ngx_int_t) (length / (1024 * 1024));
                    if ((length % (1024 * 1024)) > (1024 * 1024 / 2 - 1)) {
                        size++;
                    }
                    scale = 'M';

                } else if (length > 9999) {
                    size = (ngx_int_t) (length / 1024);
                    if (length % 1024 > 511) {
                        size++;
                    }
                    scale = 'K';

                } else {
                    size = (ngx_int_t) length;
                    scale = '\0';
                }

                if (scale) {
                    b->last = ngx_sprintf(b->last, "%6i%c", size, scale);

                } else {
                    b->last = ngx_sprintf(b->last, " %6i", size);
                }
            }
        }

        ngx_gmtime(entry[i].mtime + tp->gmtoff * 60 * alcf->localtime, &tm);

        b->last = ngx_sprintf(b->last, "%02d-%s-%d %02d:%02d",
                              tm.ngx_tm_mday,
                              months[tm.ngx_tm_mon - 1],
                              tm.ngx_tm_year,
                              tm.ngx_tm_hour,
                              tm.ngx_tm_min);


        *b->last++ = CR;
        *b->last++ = LF;
    }

    /* Output table bottom */
    b->last = ngx_cpymem_ssz(b->last, t07_list2);

    *pb = b;
    return NGX_OK;
}



static ngx_int_t
ngx_http_fancyindex_handler(ngx_http_request_t *r)
{
    ngx_http_request_t             *sr;
    ngx_str_t                      *sr_uri;
    ngx_str_t                       rel_uri;
    ngx_int_t                       rc;
    ngx_http_fancyindex_loc_conf_t *alcf;
    ngx_chain_t                     out[3] = {
        { NULL, NULL }, { NULL, NULL}, { NULL, NULL }};


    if (r->uri.data[r->uri.len - 1] != '/') {
        return NGX_DECLINED;
    }

    /* TODO: Win32 */
#if defined(nginx_version) \
    && ((nginx_version < 7066) \
        || ((nginx_version > 8000) && (nginx_version < 8038)))
    if (r->zero_in_uri) {
        return NGX_DECLINED;
    }
#endif

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
        return NGX_DECLINED;
    }

    alcf = ngx_http_get_module_loc_conf(r, ngx_http_fancyindex_module);

    if (!alcf->enable) {
        return NGX_DECLINED;
    }

    if ((rc = make_content_buf(r, &out[0].buf, alcf) != NGX_OK))
        return rc;

    out[0].buf->last_in_chain = 1;

    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_type_len  = ngx_sizeof_ssz("text/html");
    r->headers_out.content_type.len  = ngx_sizeof_ssz("text/html");
    r->headers_out.content_type.data = (u_char *) "text/html";

    rc = ngx_http_send_header(r);

    if (rc != NGX_OK || r->header_only)
        return rc;

    if (alcf->header.len > 0) {
        /* URI is configured, make Nginx take care of with a subrequest. */
        sr_uri = &alcf->header;

        if (*sr_uri->data != '/') {
            /* Relative path */
            rel_uri.len  = r->uri.len + alcf->header.len;
            rel_uri.data = ngx_palloc(r->pool, rel_uri.len);
            if (rel_uri.data == NULL) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }
            ngx_memcpy(ngx_cpymem(rel_uri.data, r->uri.data, r->uri.len),
                    alcf->header.data, alcf->header.len);
            sr_uri = &rel_uri;
        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "http fancyindex: header subrequest \"%V\"", sr_uri);

        rc = ngx_http_subrequest(r, sr_uri, NULL, &sr, NULL, 0);
        if (rc == NGX_ERROR || rc == NGX_DONE) {
            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http fancyindex: header subrequest for \"%V\" failed", sr_uri);
            return rc;
        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "http fancyindex: header subrequest status = %i",
                sr->headers_out.status);
	/* ngx_http_subrequest returns NGX_OK(0), not NGX_HTTP_OK(200) */
        if (sr->headers_out.status != NGX_OK) {
            /*
             * XXX: Should we write a message to the error log just in case
             * we get something different from a 404?
             */
            goto add_builtin_header;
        }
    }
    else {
add_builtin_header:
        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "http fancyindex: adding built-in header");
        /* Make space before */
        out[1].next = out[0].next;
        out[1].buf  = out[0].buf;
        /* Chain header buffer */
        out[0].next = &out[1];
        out[0].buf  = make_header_buf(r, alcf->css_href);
    }

    /* If footer is disabled, chain up footer buffer. */
    if (alcf->footer.len == 0) {
        ngx_uint_t last  = (alcf->header.len == 0) ? 2 : 1;

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "http fancyindex: adding built-in footer at %i", last);

        out[last-1].next = &out[last];
        out[last].buf    = make_footer_buf(r);

        out[last-1].buf->last_in_chain = 0;
        out[last].buf->last_in_chain   = 1;
        out[last].buf->last_buf        = 1;
        /* Send everything with a single call :D */
        return ngx_http_output_filter(r, &out[0]);
    }

    /*
     * If we reach here, we were asked to send a custom footer. We need to:
     * partially send whatever is referenced from out[0] and then send the
     * footer as a subrequest. If the subrequest fails, we should send the
     * standard footer as well.
     */
    rc = ngx_http_output_filter(r, &out[0]);

    if (rc != NGX_OK && rc != NGX_AGAIN)
        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    /* URI is configured, make Nginx take care of with a subrequest. */
    sr_uri = &alcf->footer;

    if (*sr_uri->data != '/') {
        /* Relative path */
        rel_uri.len  = r->uri.len + alcf->footer.len;
        rel_uri.data = ngx_palloc(r->pool, rel_uri.len);
        if (rel_uri.data == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
        ngx_memcpy(ngx_cpymem(rel_uri.data, r->uri.data, r->uri.len),
                alcf->footer.data, alcf->footer.len);
        sr_uri = &rel_uri;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
            "http fancyindex: footer subrequest \"%V\"", sr_uri);

    rc = ngx_http_subrequest(r, sr_uri, NULL, &sr, NULL, 0);
    if (rc == NGX_ERROR || rc == NGX_DONE) {
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "http fancyindex: footer subrequest for \"%V\" failed", sr_uri);
        return rc;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
            "http fancyindex: header subrequest status = %i",
            sr->headers_out.status);

    /* see above: ngx_http_subrequest resturns NGX_OK (0) not NGX_HTTP_OK (200) */
    if (sr->headers_out.status != NGX_OK) {
        /*
         * XXX: Should we write a message to the error log just in case
         * we get something different from a 404?
         */
        out[0].next = NULL;
        out[0].buf  = make_footer_buf(r);
        out[0].buf->last_in_chain = 1;
        out[0].buf->last_buf = 1;
        /* Directly send out the builtin footer */
        return ngx_http_output_filter(r, &out[0]);
    }

    return (r != r->main) ? rc : ngx_http_send_special(r, NGX_HTTP_LAST);
}


static int ngx_libc_cdecl
ngx_http_fancyindex_cmp_entries_name_desc(const void *one, const void *two)
{
    ngx_http_fancyindex_entry_t *first = (ngx_http_fancyindex_entry_t *) one;
    ngx_http_fancyindex_entry_t *second = (ngx_http_fancyindex_entry_t *) two;

    /* move the directories to the start */
    if (first->dir && !second->dir) {
        return -1;
    }
    if (!first->dir && second->dir) {
        return 1;
    }

    return (int) ngx_strcmp(second->name.data, first->name.data);
}


static int ngx_libc_cdecl
ngx_http_fancyindex_cmp_entries_size_desc(const void *one, const void *two)
{
    ngx_http_fancyindex_entry_t *first = (ngx_http_fancyindex_entry_t *) one;
    ngx_http_fancyindex_entry_t *second = (ngx_http_fancyindex_entry_t *) two;

    /* move the directories to the start */
    if (first->dir && !second->dir) {
        return -1;
    }
    if (!first->dir && second->dir) {
        return 1;
    }

    return second->size - first->size;
}


static int ngx_libc_cdecl
ngx_http_fancyindex_cmp_entries_mtime_desc(const void *one, const void *two)
{
    ngx_http_fancyindex_entry_t *first = (ngx_http_fancyindex_entry_t *) one;
    ngx_http_fancyindex_entry_t *second = (ngx_http_fancyindex_entry_t *) two;

    /* move the directories to the start */
    if (first->dir && !second->dir) {
        return -1;
    }
    if (!first->dir && second->dir) {
        return 1;
    }

    return second->mtime - first->mtime;
}


static int ngx_libc_cdecl
ngx_http_fancyindex_cmp_entries_name_asc(const void *one, const void *two)
{
    ngx_http_fancyindex_entry_t *first = (ngx_http_fancyindex_entry_t *) one;
    ngx_http_fancyindex_entry_t *second = (ngx_http_fancyindex_entry_t *) two;

    /* move the directories to the start */
    if (first->dir && !second->dir) {
        return -1;
    }
    if (!first->dir && second->dir) {
        return 1;
    }

    return (int) ngx_strcmp(first->name.data, second->name.data);
}


static int ngx_libc_cdecl
ngx_http_fancyindex_cmp_entries_size_asc(const void *one, const void *two)
{
    ngx_http_fancyindex_entry_t *first = (ngx_http_fancyindex_entry_t *) one;
    ngx_http_fancyindex_entry_t *second = (ngx_http_fancyindex_entry_t *) two;

    /* move the directories to the start */
    if (first->dir && !second->dir) {
        return -1;
    }
    if (!first->dir && second->dir) {
        return 1;
    }

    return first->size - second->size;
}


static int ngx_libc_cdecl
ngx_http_fancyindex_cmp_entries_mtime_asc(const void *one, const void *two)
{
    ngx_http_fancyindex_entry_t *first = (ngx_http_fancyindex_entry_t *) one;
    ngx_http_fancyindex_entry_t *second = (ngx_http_fancyindex_entry_t *) two;

    /* move the directories to the start */
    if (first->dir && !second->dir) {
        return -1;
    }
    if (!first->dir && second->dir) {
        return 1;
    }

    return first->mtime - second->mtime;
}


static ngx_int_t
ngx_http_fancyindex_error(ngx_http_request_t *r, ngx_dir_t *dir, ngx_str_t *name)
{
    if (ngx_close_dir(dir) == NGX_ERROR) {
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
                      ngx_close_dir_n " \"%V\" failed", name);
    }

    return NGX_HTTP_INTERNAL_SERVER_ERROR;
}


static void *
ngx_http_fancyindex_create_loc_conf(ngx_conf_t *cf)
{
    ngx_http_fancyindex_loc_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fancyindex_loc_conf_t));
    if (conf == NULL) {
        return NGX_CONF_ERROR;
    }

    /*
     * Set by ngx_pcalloc:
     *    conf->header.len   = 0
     *    conf->header.data  = NULL
     *    conf->footer.len   = 0
     *    conf->footer.data  = NULL
     */
    conf->enable = NGX_CONF_UNSET;
    conf->localtime = NGX_CONF_UNSET;
    conf->exact_size = NGX_CONF_UNSET;
    conf->ignore = NGX_CONF_UNSET_PTR;

    return conf;
}


static char *
ngx_http_fancyindex_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
    ngx_http_fancyindex_loc_conf_t *prev = parent;
    ngx_http_fancyindex_loc_conf_t *conf = child;

    ngx_conf_merge_value(conf->enable, prev->enable, 0);
    ngx_conf_merge_value(conf->localtime, prev->localtime, 0);
    ngx_conf_merge_value(conf->exact_size, prev->exact_size, 1);

    ngx_conf_merge_str_value(conf->header, prev->header, "");
    ngx_conf_merge_str_value(conf->footer, prev->footer, "");

    ngx_conf_merge_ptr_value(conf->ignore, prev->ignore, NULL);

    return NGX_CONF_OK;
}


static char*
ngx_http_fancyindex_ignore(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_fancyindex_loc_conf_t *alcf = conf;
    ngx_str_t *value;

#if (NGX_PCRE)
    ngx_uint_t          i;
    ngx_regex_elt_t    *re;
    ngx_regex_compile_t rc;
    u_char              errstr[NGX_MAX_CONF_ERRSTR];

    if (alcf->ignore == NGX_CONF_UNSET_PTR) {
        alcf->ignore = ngx_array_create(cf->pool, 2, sizeof(ngx_regex_elt_t));
        if (alcf->ignore == NULL) {
            return NGX_CONF_ERROR;
        }
    }

    value = cf->args->elts;

    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));

    rc.err.data = errstr;
    rc.err.len  = NGX_MAX_CONF_ERRSTR;
    rc.pool     = cf->pool;

    for (i = 1; i < cf->args->nelts; i++) {
        re = ngx_array_push(alcf->ignore);
        if (re == NULL) {
            return NGX_CONF_ERROR;
        }

        rc.pattern = value[i];
        rc.options = NGX_REGEX_CASELESS;

        if (ngx_regex_compile(&rc) != NGX_OK) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
            return NGX_CONF_ERROR;
        }

        re->name  = value[i].data;
        re->regex = rc.regex;
    }

    return NGX_CONF_OK;
#else /* !NGX_PCRE */
    ngx_uint_t i;
    ngx_str_t *str;

    if (alcf->ignore == NGX_CONF_UNSET_PTR) {
        alcf->ignore = ngx_array_create(cf->pool, 2, sizeof(ngx_str_t));
        if (alcf->ignore == NULL) {
            return NGX_CONF_ERROR;
        }
    }

    value = cf->args->elts;

    for (i = 1; i < cf->args->nelts; i++) {
        str = ngx_array_push(alcf->ignore);
        if (str == NULL) {
            return NGX_CONF_ERROR;
        }

        str->data = value[i].data;
        str->len  = value[i].len;
    }

    return NGX_CONF_OK;
#endif /* NGX_PCRE */

}


static ngx_int_t
ngx_http_fancyindex_init(ngx_conf_t *cf)
{
    ngx_http_handler_pt        *h;
    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

    h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
    if (h == NULL) {
        return NGX_ERROR;
    }

    *h = ngx_http_fancyindex_handler;

    return NGX_OK;
}

/* vim:et:sw=4:ts=4:
 */
debian/modules/ngx-fancyindex/LICENSE0000664000000000000000000000230112305451334014563 0ustar  Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
debian/modules/ngx-fancyindex/config0000664000000000000000000000047312305451334014756 0ustar  # vim:ft=sh:
ngx_addon_name=ngx_http_fancyindex_module

# XXX: Insert fancyindex module *after* index module!
#
HTTP_MODULES=`echo "${HTTP_MODULES}" | sed -e \
	's/ngx_http_index_module/ngx_http_fancyindex_module ngx_http_index_module/'`
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_fancyindex_module.c"
debian/modules/ngx-fancyindex/HACKING.rst0000664000000000000000000000211612305451334015360 0ustar  ===================================
 Fancy Index module Hacking HOW-TO
===================================

.. contents::


How to modify the template
==========================

The template is in the ``template.html`` file. Note that comment markers are
used to control how the ``template.awk`` Awk script generates the C header
which gets ultimately included in the compiled object code. Comment markers
have the ```` format. Here ``identifier`` must be
a valid C identifier. All the text following the marker until the next
marker will be flattened into a C string.

If the identifier is ``NONE`` (capitalized) the text from that marker up to
the next marker will be discarded.


Regenerating the C header
~~~~~~~~~~~~~~~~~~~~~~~~~
You will need Awk. I hope any decent implementation will do, but the GNU one
is known to work flawlessly. Just do::

  $ awk -f template.awk template.html > template.h

If your copy of ``awk`` is not the GNU implementation, you will need to
install it and use ``gawk`` instead in the command line above.

.. vim: spell spelllang=en expandtab

debian/modules/ngx-fancyindex/template.html0000664000000000000000000000400312305451334016260 0ustar  




	
		
		



		Index of 
<!-- var NONE -->
			/path/to/somewhere
<!-- var t03_head3 -->
		

	

	
		

Index of /path/to/somewhere

File Name  ↓  File Size  ↓  Date  ↓ 
Parent directory/ - -
test file 1 123kB date
test file 2 321MB date
test file 3 666 date
debian/modules/ngx-fancyindex/template.awk0000775000000000000000000000165412305451334016112 0ustar #! /usr/bin/awk -f # # Copyright © Adrian Perez # # Converts an HTML template into a C header suitable for inclusion. # Take a look at the HACKING.rst file to know how to use it :-) # # This code is placed in the public domain. BEGIN { varname = 0; print "/* Automagically generated, do not edit! */" vars_count = 0; } /^$/ { if (varname) print ";"; if ($3 == "NONE") { varname = 0; next; } varname = $3; vars[vars_count++] = varname; print "static const u_char " varname "[] = \"\""; next; } /^$/ { if (!varname) next; print "\"\\n\""; next; } { if (!varname) next; # Order matters gsub(/[\t\v\n\r\f]+/, ""); gsub(/\\/, "\\\\"); gsub(/"/, "\\\""); print "\"" $0 "\"" } END { if (varname) print ";"; print "#define NFI_TEMPLATE_SIZE (0 \\"; for (var in vars) { print "\t+ nfi_sizeof_ssz(" vars[var] ") \\"; } print "\t)" } debian/modules/ngx-fancyindex/README.rst0000664000000000000000000001124012305451334015247 0ustar ======================== Nginx Fancy Index module ======================== .. contents:: The Fancy Index module makes possible the generation of file listings, like the built-in `autoindex `__ module does, but adding a touch of style. This is possible because the module module allows a certain degree of customization of the generated content: * Custom headers. Either local or stored remotely. * Custom footers. Either local or stored remotely. * Add you own CSS style rules. * Allow choosing to sort elements by name (default), modification time, or size; both ascending (default), or descending. This module is designed to work with Nginx_, a high performance open source web server written by `Igor Sysoev `__. Requirements ============ You will need the sources for Nginx_. Any version starting from the 0.7 series onwards will work. Note that the modules *might* compile with versions in the 0.6 series by applying ``nginx-0.6-support.patch``, but this is unsupported (YMMV). Building ======== 1. Unpack the Nginx_ sources:: $ gunzip -c nginx-?.?.?.tar.gz | tar -xvf - 2. Unpack the sources for the fancy indexing module:: $ gunzip -c nginx-fancyindex-?.?.?.tar.gz | tar -xvf - 3. Change to the directory which contains the Nginx_ sources, run the configuration script with the desired options and be sure to put an ``--add-module`` flag pointing to the directory which contains the source of the fancy indexing module:: $ cd nginx-?.?.? $ ./configure --add-module=../nginx-fancyindex-?.?.? [extra desired options] 4. Build and install the software:: $ make And then, as ``root``:: # make install 5. Configure Nginx_ by using the modules' configuration directives_. Example ======= You can test the default built-in style by adding the following lines into a ``server`` section in your Nginx_ configuration file:: location / { fancyindex on; # Enable fancy indexes. fancyindex_exact_size off; # Output human-readable file sizes. } Directives ========== fancyindex ~~~~~~~~~~ :Syntax: *fancyindex* [*on* | *off*] :Default: fancyindex off :Context: http, server, location :Description: Enables or disables fancy directory indexes. fancyindex_css_href ~~~~~~~~~~~~~~~~~~~ :Syntax: *fancyindex_css_href uri* :Default: fancyindex_css_href "" :Context: http, server, location :Description: Allows inserting a link to a CSS style sheet in generated listings. The provided *uri* parameter will be inserted as-is in a ```` HTML tag. The link is inserted after the built-in CSS rules, so you can override the default styles. fancyindex_exact_size ~~~~~~~~~~~~~~~~~~~~~ :Syntax: *fancyindex_exact_size* [*on* | *off*] :Default: fancyindex_exact_size on :Context: http, server, location :Description: Defines how to represent file sizes in the directory listing; either accurately, or rounding off to the kilobyte, the megabyte and the gigabyte. fancyindex_footer ~~~~~~~~~~~~~~~~~ :Syntax: *fancyindex_footer path* :Default: fancyindex_footer "" :Context: http, server, location :Description: Specifies which file should be inserted at the foot of directory listings. If set to an empty string, the default footer supplied by the module will be sent. .. warning:: When inserting custom header/footer a subrequest will be issued so potentially any URL can be used as source for them. Although it will work with external URLs, only using internal ones is supported. External URLs are totally untested and using them will make Nginx_ block while waiting for the subrequest to complete. If you feel like external header/footer is a must-have for you, please `let me know `__. fancyindex_header ~~~~~~~~~~~~~~~~~ :Syntax: *fancyindex_header path* :Default: fancyindex_header "" :Context: http, server, location :Description: Specifies which file should be inserted at the head of directory listings. If set to an empty string, the default header supplied by the module will be sent. fancyindex_ignore ~~~~~~~~~~~~~~~~~ :Syntax: *fancyindex_ignore string1 [string2 [... stringN]]* :Default: No default. :Context: http, server, location :Description: Specifies a list of file names which will be not be shown in generated listings. If Nginx was built with PCRE support strings are interpreted as regular expressions. fancyindex_localtime ~~~~~~~~~~~~~~~~~~~~ :Syntax: *fancyindex_localtime* [*on* | *off*] :Default: fancyindex_localtime off :Context: http, server, location :Description: Enables showing file times as local time. Default is “off” (GMT time). .. _nginx: http://nginx.net .. vim:ft=rst:spell:spelllang=en: debian/modules/ngx-fancyindex/nginx-0.6-support.patch0000664000000000000000000000150212305451334017737 0ustar === modified file 'ngx_http_fancyindex_module.c' --- ngx_http_fancyindex_module.c 2008-09-11 17:55:52 +0000 +++ ngx_http_fancyindex_module.c 2008-12-10 01:33:43 +0000 @@ -383,7 +383,7 @@ entry->mtime = ngx_de_mtime(&dir); entry->size = ngx_de_size(&dir); entry->utf_len = (r->utf8) - ? ngx_utf8_length(entry->name.data, entry->name.len) + ? ngx_utf_length(entry->name.data, entry->name.len) : len; } @@ -478,8 +478,7 @@ copy = NGX_HTTP_FANCYINDEX_NAME_LEN + 1; } - b->last = ngx_utf8_cpystrn(b->last, entry[i].name.data, - copy, entry[i].name.len); + b->last = ngx_utf_cpystrn(b->last, entry[i].name.data, copy); last = b->last; } else { debian/modules/ngx-fancyindex/NEWS.rst0000664000000000000000000000075712305451334015101 0ustar ============================== Major changes among versions ============================== v0.3.3 ====== - New feature: table headers in the default template are now clickable to set the sorting criteria and direction of the index entries. (https://github.com/aperezdc/ngx-fancyindex/issues/7) v0.3.2 ====== - Solved a bug that would leave certain clients stalled forever. - Improved handling of subrequests for non-builtin headers/footers. v0.3.1 ====== (First entry in the NEWS file.) debian/modules/ngx-fancyindex/template.h0000664000000000000000000000422312305451334015547 0ustar /* Automagically generated, do not edit! */ static const u_char t01_head1[] = "" "" "\n" "" "\n" "" "" "" "\n" ; static const u_char t02_head2[] = "" "\n" "Index of " ; static const u_char t03_head3[] = "" "" "\n" "" ; static const u_char t04_body1[] = "" "" "

Index of " ; static const u_char t05_body2[] = "" "

" "\n" ; static const u_char t06_list1[] = "" "" "\n" "" "" "" "" "" "\n" "" "" "" "" "" "" "" "\n" "" ; static const u_char t_parentdir_entry[] = "" "" "" "" "" "" ; static const u_char t07_list2[] = "" "" "
File Name  ↓ File Size  ↓ Date  ↓ 
Parent directory/--
" ; static const u_char t08_foot1[] = "" "" "" ; #define NFI_TEMPLATE_SIZE (0 \ + nfi_sizeof_ssz(t01_head1) \ + nfi_sizeof_ssz(t02_head2) \ + nfi_sizeof_ssz(t03_head3) \ + nfi_sizeof_ssz(t04_body1) \ + nfi_sizeof_ssz(t05_body2) \ + nfi_sizeof_ssz(t06_list1) \ + nfi_sizeof_ssz(t_parentdir_entry) \ + nfi_sizeof_ssz(t07_list2) \ + nfi_sizeof_ssz(t08_foot1) \ ) debian/modules/nginx-lua/0000775000000000000000000000000012305451341012540 5ustar debian/modules/nginx-lua/src/0000775000000000000000000000000012305451341013327 5ustar debian/modules/nginx-lua/src/ngx_http_lua_time.c0000664000000000000000000001272312305451335017215 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_time.h" #include "ngx_http_lua_util.h" static int ngx_http_lua_ngx_today(lua_State *L); static int ngx_http_lua_ngx_time(lua_State *L); static int ngx_http_lua_ngx_now(lua_State *L); static int ngx_http_lua_ngx_localtime(lua_State *L); static int ngx_http_lua_ngx_utctime(lua_State *L); static int ngx_http_lua_ngx_cookie_time(lua_State *L); static int ngx_http_lua_ngx_http_time(lua_State *L); static int ngx_http_lua_ngx_parse_http_time(lua_State *L); static int ngx_http_lua_ngx_update_time(lua_State *L); static int ngx_http_lua_ngx_req_start_time(lua_State *L); static int ngx_http_lua_ngx_today(lua_State *L) { time_t now; ngx_tm_t tm; u_char buf[sizeof("2010-11-19") - 1]; now = ngx_time(); ngx_gmtime(now + ngx_cached_time->gmtoff * 60, &tm); ngx_sprintf(buf, "%04d-%02d-%02d", tm.ngx_tm_year, tm.ngx_tm_mon, tm.ngx_tm_mday); lua_pushlstring(L, (char *) buf, sizeof(buf)); return 1; } static int ngx_http_lua_ngx_localtime(lua_State *L) { ngx_tm_t tm; u_char buf[sizeof("2010-11-19 20:56:31") - 1]; ngx_gmtime(ngx_time() + ngx_cached_time->gmtoff * 60, &tm); ngx_sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d", tm.ngx_tm_year, tm.ngx_tm_mon, tm.ngx_tm_mday, tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec); lua_pushlstring(L, (char *) buf, sizeof(buf)); return 1; } static int ngx_http_lua_ngx_time(lua_State *L) { lua_pushnumber(L, (lua_Number) ngx_time()); return 1; } static int ngx_http_lua_ngx_now(lua_State *L) { ngx_time_t *tp; tp = ngx_timeofday(); lua_pushnumber(L, (lua_Number) (tp->sec + tp->msec / 1000.0L)); return 1; } static int ngx_http_lua_ngx_update_time(lua_State *L) { ngx_time_update(); return 0; } static int ngx_http_lua_ngx_utctime(lua_State *L) { ngx_tm_t tm; u_char buf[sizeof("2010-11-19 20:56:31") - 1]; ngx_gmtime(ngx_time(), &tm); ngx_sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d", tm.ngx_tm_year, tm.ngx_tm_mon, tm.ngx_tm_mday, tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec); lua_pushlstring(L, (char *) buf, sizeof(buf)); return 1; } static int ngx_http_lua_ngx_cookie_time(lua_State *L) { time_t t; u_char *p; u_char buf[sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1]; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting one argument"); } t = (time_t) luaL_checknumber(L, 1); p = buf; p = ngx_http_cookie_time(p, t); lua_pushlstring(L, (char *) buf, p - buf); return 1; } static int ngx_http_lua_ngx_http_time(lua_State *L) { time_t t; u_char *p; u_char buf[sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1]; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting one argument"); } t = (time_t) luaL_checknumber(L, 1); p = buf; p = ngx_http_time(p, t); lua_pushlstring(L, (char *) buf, p - buf); return 1; } static int ngx_http_lua_ngx_parse_http_time(lua_State *L) { u_char *p; size_t len; time_t time; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting one argument"); } p = (u_char *) luaL_checklstring(L, 1, &len); time = ngx_http_parse_time(p, len); if (time == NGX_ERROR) { lua_pushnil(L); return 1; } lua_pushnumber(L, (lua_Number) time); return 1; } static int ngx_http_lua_ngx_req_start_time(lua_State *L) { ngx_http_request_t *r; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } lua_pushnumber(L, (lua_Number) (r->start_sec + r->start_msec / 1000.0L)); return 1; } void ngx_http_lua_inject_time_api(lua_State *L) { lua_pushcfunction(L, ngx_http_lua_ngx_utctime); lua_setfield(L, -2, "utctime"); lua_pushcfunction(L, ngx_http_lua_ngx_time); lua_setfield(L, -2, "get_now_ts"); /* deprecated */ lua_pushcfunction(L, ngx_http_lua_ngx_localtime); lua_setfield(L, -2, "get_now"); /* deprecated */ lua_pushcfunction(L, ngx_http_lua_ngx_localtime); lua_setfield(L, -2, "localtime"); lua_pushcfunction(L, ngx_http_lua_ngx_time); lua_setfield(L, -2, "time"); lua_pushcfunction(L, ngx_http_lua_ngx_now); lua_setfield(L, -2, "now"); lua_pushcfunction(L, ngx_http_lua_ngx_update_time); lua_setfield(L, -2, "update_time"); lua_pushcfunction(L, ngx_http_lua_ngx_today); lua_setfield(L, -2, "get_today"); /* deprecated */ lua_pushcfunction(L, ngx_http_lua_ngx_today); lua_setfield(L, -2, "today"); lua_pushcfunction(L, ngx_http_lua_ngx_cookie_time); lua_setfield(L, -2, "cookie_time"); lua_pushcfunction(L, ngx_http_lua_ngx_http_time); lua_setfield(L, -2, "http_time"); lua_pushcfunction(L, ngx_http_lua_ngx_parse_http_time); lua_setfield(L, -2, "parse_http_time"); } void ngx_http_lua_inject_req_time_api(lua_State *L) { lua_pushcfunction(L, ngx_http_lua_ngx_req_start_time); lua_setfield(L, -2, "start_time"); } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_capturefilter.c0000664000000000000000000001126312305451335021126 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include #include "ngx_http_lua_capturefilter.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_exception.h" #include "ngx_http_lua_subrequest.h" ngx_http_output_header_filter_pt ngx_http_lua_next_header_filter; ngx_http_output_body_filter_pt ngx_http_lua_next_body_filter; static ngx_int_t ngx_http_lua_capture_header_filter(ngx_http_request_t *r); static ngx_int_t ngx_http_lua_capture_body_filter(ngx_http_request_t *r, ngx_chain_t *in); ngx_int_t ngx_http_lua_capture_filter_init(ngx_conf_t *cf) { /* setting up output filters to intercept subrequest responses */ ngx_http_lua_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_lua_capture_header_filter; ngx_http_lua_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_lua_capture_body_filter; return NGX_OK; } static ngx_int_t ngx_http_lua_capture_header_filter(ngx_http_request_t *r) { ngx_http_post_subrequest_t *psr; ngx_http_lua_ctx_t *old_ctx; ngx_http_lua_ctx_t *ctx; ngx_http_lua_post_subrequest_data_t *psr_data; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua capture header filter, uri \"%V\"", &r->uri); ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); dd("old ctx: %p", ctx); if (ctx == NULL || ! ctx->capture) { psr = r->post_subrequest; if (psr != NULL && psr->handler == ngx_http_lua_post_subrequest && psr->data != NULL) { /* the lua ctx has been cleared by ngx_http_internal_redirect, * resume it from the post_subrequest data */ psr_data = psr->data; old_ctx = psr_data->ctx; if (ctx == NULL) { ctx = old_ctx; ngx_http_set_ctx(r, ctx, ngx_http_lua_module); } else { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua restoring ctx with capture %d, index %d", old_ctx->capture, old_ctx->index); ctx->capture = old_ctx->capture; ctx->index = old_ctx->index; ctx->body = NULL; ctx->last_body = &ctx->body; psr_data->ctx = ctx; } } } if (ctx && ctx->capture) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua capturing response body"); /* force subrequest response body buffer in memory */ r->filter_need_in_memory = 1; r->header_sent = 1; return NGX_OK; } return ngx_http_lua_next_header_filter(r); } static ngx_int_t ngx_http_lua_capture_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { int rc; ngx_int_t eof; ngx_http_lua_ctx_t *ctx; ngx_http_lua_ctx_t *pr_ctx; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua capture body filter, uri \"%V\"", &r->uri); if (in == NULL) { return ngx_http_lua_next_body_filter(r, NULL); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (!ctx || !ctx->capture) { dd("no ctx or no capture %.*s", (int) r->uri.len, r->uri.data); return ngx_http_lua_next_body_filter(r, in); } if (ctx->run_post_subrequest) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua body filter skipped because post subrequest " "already run"); return NGX_OK; } if (r->parent == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua body filter skipped because no parent request " "found"); return NGX_ERROR; } pr_ctx = ngx_http_get_module_ctx(r->parent, ngx_http_lua_module); if (pr_ctx == NULL) { return NGX_ERROR; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua capture body filter capturing response body, uri " "\"%V\"", &r->uri); rc = ngx_http_lua_add_copy_chain(r, pr_ctx, &ctx->last_body, in, &eof); if (rc != NGX_OK) { return NGX_ERROR; } dd("add copy chain eof: %d, sr: %d", (int) eof, r != r->main); if (eof) { ctx->seen_last_for_subreq = 1; } ngx_http_lua_discard_bufs(r->pool, in); return NGX_OK; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ddebug.h0000664000000000000000000000325312305451335014740 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _DDEBUG_H_INCLUDED_ #define _DDEBUG_H_INCLUDED_ #include #include #if defined(DDEBUG) && (DDEBUG) # if (NGX_HAVE_VARIADIC_MACROS) # define dd(...) fprintf(stderr, "lua *** %s: ", __func__); \ fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, " at %s line %d.\n", __FILE__, __LINE__) # else #include #include #include static void dd(const char *fmt, ...) { } # endif #else # if (NGX_HAVE_VARIADIC_MACROS) # define dd(...) # else #include static void dd(const char *fmt, ...) { } # endif #endif #if defined(DDEBUG) && (DDEBUG) #define dd_check_read_event_handler(r) \ dd("r->read_event_handler = %s", \ r->read_event_handler == ngx_http_block_reading ? \ "ngx_http_block_reading" : \ r->read_event_handler == ngx_http_test_reading ? \ "ngx_http_test_reading" : \ r->read_event_handler == ngx_http_request_empty_handler ? \ "ngx_http_request_empty_handler" : "UNKNOWN") #define dd_check_write_event_handler(r) \ dd("r->write_event_handler = %s", \ r->write_event_handler == ngx_http_handler ? \ "ngx_http_handler" : \ r->write_event_handler == ngx_http_core_run_phases ? \ "ngx_http_core_run_phases" : \ r->write_event_handler == ngx_http_request_empty_handler ? \ "ngx_http_request_empty_handler" : "UNKNOWN") #else #define dd_check_read_event_handler(r) #define dd_check_write_event_handler(r) #endif #endif /* _DDEBUG_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_pcrefix.h0000664000000000000000000000070112305451334017714 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_PCREFIX_H_INCLUDED_ #define _NGX_HTTP_LUA_PCREFIX_H_INCLUDED_ #include "ngx_http_lua_common.h" #if (NGX_PCRE) ngx_pool_t *ngx_http_lua_pcre_malloc_init(ngx_pool_t *pool); void ngx_http_lua_pcre_malloc_done(ngx_pool_t *old_pool); #endif #endif /* _NGX_HTTP_LUA_PCREFIX_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_coroutine.c0000664000000000000000000002354312305451334020267 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_coroutine.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_probe.h" /* * Design: * * In order to support using ngx.* API in Lua coroutines, we have to create * new coroutine in the main coroutine instead of the calling coroutine */ static int ngx_http_lua_coroutine_create(lua_State *L); static int ngx_http_lua_coroutine_resume(lua_State *L); static int ngx_http_lua_coroutine_yield(lua_State *L); static int ngx_http_lua_coroutine_status(lua_State *L); static const char * ngx_http_lua_co_status_names[] = {"running", "suspended", "normal", "dead", "zombie"}; static int ngx_http_lua_coroutine_create(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } return ngx_http_lua_coroutine_create_helper(L, r, ctx, NULL); } int ngx_http_lua_coroutine_create_helper(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t **pcoctx) { lua_State *vm; /* the Lua VM */ lua_State *co; /* new coroutine to be created */ ngx_http_lua_co_ctx_t *coctx; /* co ctx for the new coroutine */ luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, "Lua function expected"); ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER); vm = ngx_http_lua_get_lua_vm(r, ctx); /* create new coroutine on root Lua state, so it always yields * to main Lua thread */ co = lua_newthread(vm); ngx_http_lua_probe_user_coroutine_create(r, L, co); coctx = ngx_http_lua_get_co_ctx(co, ctx); if (coctx == NULL) { coctx = ngx_http_lua_create_co_ctx(r, ctx); if (coctx == NULL) { return luaL_error(L, "out of memory"); } } else { ngx_memzero(coctx, sizeof(ngx_http_lua_co_ctx_t)); coctx->co_ref = LUA_NOREF; } coctx->co = co; coctx->co_status = NGX_HTTP_LUA_CO_SUSPENDED; /* make new coroutine share globals of the parent coroutine. * NOTE: globals don't have to be separated! */ lua_pushvalue(L, LUA_GLOBALSINDEX); lua_xmove(L, co, 1); lua_replace(co, LUA_GLOBALSINDEX); lua_xmove(vm, L, 1); /* move coroutine from main thread to L */ lua_pushvalue(L, 1); /* copy entry function to top of L*/ lua_xmove(L, co, 1); /* move entry function from L to co */ if (pcoctx) { *pcoctx = coctx; } return 1; /* return new coroutine to Lua */ } static int ngx_http_lua_coroutine_resume(lua_State *L) { lua_State *co; ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; ngx_http_lua_co_ctx_t *p_coctx; /* parent co ctx */ co = lua_tothread(L, 1); luaL_argcheck(L, co, 1, "coroutine expected"); r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER); p_coctx = ctx->cur_co_ctx; if (p_coctx == NULL) { return luaL_error(L, "no parent co ctx found"); } coctx = ngx_http_lua_get_co_ctx(co, ctx); if (coctx == NULL) { return luaL_error(L, "no co ctx found"); } ngx_http_lua_probe_user_coroutine_resume(r, L, co); if (coctx->co_status != NGX_HTTP_LUA_CO_SUSPENDED) { dd("coroutine resume: %d", coctx->co_status); lua_pushboolean(L, 0); lua_pushfstring(L, "cannot resume %s coroutine", ngx_http_lua_co_status_names[coctx->co_status]); return 2; } p_coctx->co_status = NGX_HTTP_LUA_CO_NORMAL; coctx->parent_co_ctx = p_coctx; dd("set coroutine to running"); coctx->co_status = NGX_HTTP_LUA_CO_RUNNING; ctx->co_op = NGX_HTTP_LUA_USER_CORO_RESUME; ctx->cur_co_ctx = coctx; /* yield and pass args to main thread, and resume target coroutine from * there */ return lua_yield(L, lua_gettop(L) - 1); } static int ngx_http_lua_coroutine_yield(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER); coctx = ctx->cur_co_ctx; coctx->co_status = NGX_HTTP_LUA_CO_SUSPENDED; ctx->co_op = NGX_HTTP_LUA_USER_CORO_YIELD; if (!coctx->is_uthread && coctx->parent_co_ctx) { dd("set coroutine to running"); coctx->parent_co_ctx->co_status = NGX_HTTP_LUA_CO_RUNNING; ngx_http_lua_probe_user_coroutine_yield(r, coctx->parent_co_ctx->co, L); } else { ngx_http_lua_probe_user_coroutine_yield(r, NULL, L); } /* yield and pass retvals to main thread, * and resume parent coroutine there */ return lua_yield(L, lua_gettop(L)); } void ngx_http_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L) { int rc; /* new coroutine table */ lua_createtable(L, 0 /* narr */, 14 /* nrec */); /* get old coroutine table */ lua_getglobal(L, "coroutine"); /* set running to the old one */ lua_getfield(L, -1, "running"); lua_setfield(L, -3, "running"); lua_getfield(L, -1, "create"); lua_setfield(L, -3, "_create"); lua_getfield(L, -1, "resume"); lua_setfield(L, -3, "_resume"); lua_getfield(L, -1, "yield"); lua_setfield(L, -3, "_yield"); lua_getfield(L, -1, "status"); lua_setfield(L, -3, "_status"); /* pop the old coroutine */ lua_pop(L, 1); lua_pushcfunction(L, ngx_http_lua_coroutine_create); lua_setfield(L, -2, "__create"); lua_pushcfunction(L, ngx_http_lua_coroutine_resume); lua_setfield(L, -2, "__resume"); lua_pushcfunction(L, ngx_http_lua_coroutine_yield); lua_setfield(L, -2, "__yield"); lua_pushcfunction(L, ngx_http_lua_coroutine_status); lua_setfield(L, -2, "__status"); lua_setglobal(L, "coroutine"); /* inject coroutine APIs */ { const char buf[] = "local keys = {'create', 'yield', 'resume', 'status'}\n" "local getfenv = getfenv\n" "for _, key in ipairs(keys) do\n" "local std = coroutine['_' .. key]\n" "local ours = coroutine['__' .. key]\n" "coroutine[key] = function (...)\n" "if getfenv(0).__ngx_req then\n" "return ours(...)\n" "end\n" "return std(...)\n" "end\n" "end\n" "local create, resume = coroutine.create, coroutine.resume\n" "coroutine.wrap = function(f)\n" "local co = create(f)\n" "return function(...) return select(2, resume(co, ...)) end\n" "end"; #if 0 "debug.sethook(function () collectgarbage() end, 'rl', 1)" #endif ; rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, "coroutine.wrap"); } if (rc != 0) { ngx_log_error(NGX_LOG_ERR, log, 0, "failed to load Lua code for coroutine.wrap(): %i: %s", rc, lua_tostring(L, -1)); lua_pop(L, 1); return; } rc = lua_pcall(L, 0, 0, 0); if (rc != 0) { ngx_log_error(NGX_LOG_ERR, log, 0, "failed to run the Lua code for coroutine.wrap(): %i: %s", rc, lua_tostring(L, -1)); lua_pop(L, 1); } } static int ngx_http_lua_coroutine_status(lua_State *L) { lua_State *co; /* new coroutine to be created */ ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; /* co ctx for the new coroutine */ co = lua_tothread(L, 1); luaL_argcheck(L, co, 1, "coroutine expected"); r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER); coctx = ngx_http_lua_get_co_ctx(co, ctx); if (coctx == NULL) { lua_pushstring(L, ngx_http_lua_co_status_names[NGX_HTTP_LUA_CO_DEAD]); return 1; } dd("co status: %d", coctx->co_status); lua_pushstring(L, ngx_http_lua_co_status_names[coctx->co_status]); return 1; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_uri.c0000664000000000000000000000453212305451335017055 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_uri.h" #include "ngx_http_lua_util.h" static int ngx_http_lua_ngx_req_set_uri(lua_State *L); void ngx_http_lua_inject_req_uri_api(ngx_log_t *log, lua_State *L) { lua_pushcfunction(L, ngx_http_lua_ngx_req_set_uri); lua_setfield(L, -2, "set_uri"); } static int ngx_http_lua_ngx_req_set_uri(lua_State *L) { ngx_http_request_t *r; size_t len; u_char *p; int n; int jump = 0; ngx_http_lua_ctx_t *ctx; n = lua_gettop(L); if (n != 1 && n != 2) { return luaL_error(L, "expecting 1 argument but seen %d", n); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ngx_http_lua_check_fake_request(L, r); p = (u_char *) luaL_checklstring(L, 1, &len); if (len == 0) { return luaL_error(L, "attempt to use zero-length uri"); } if (n == 2) { luaL_checktype(L, 2, LUA_TBOOLEAN); jump = lua_toboolean(L, 2); if (jump) { ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no ctx found"); } dd("rewrite: %d, access: %d, content: %d", (int) ctx->entered_rewrite_phase, (int) ctx->entered_access_phase, (int) ctx->entered_content_phase); ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua set uri jump to \"%*s\"", len, p); ngx_http_lua_check_if_abortable(L, ctx); } } r->uri.data = ngx_palloc(r->pool, len); if (r->uri.data == NULL) { return luaL_error(L, "out of memory"); } ngx_memcpy(r->uri.data, p, len); r->uri.len = len; r->internal = 1; r->valid_unparsed_uri = 0; ngx_http_set_exten(r); if (jump) { r->uri_changed = 1; return lua_yield(L, 0); } r->valid_location = 0; r->uri_changed = 0; return 0; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_worker.c0000664000000000000000000000110212305451335017555 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_worker.h" static int ngx_http_lua_ngx_worker_exiting(lua_State *L); void ngx_http_lua_inject_worker_api(lua_State *L) { lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* ngx.timer. */ lua_pushcfunction(L, ngx_http_lua_ngx_worker_exiting); lua_setfield(L, -2, "exiting"); lua_setfield(L, -2, "worker"); } static int ngx_http_lua_ngx_worker_exiting(lua_State *L) { lua_pushboolean(L, ngx_exiting); return 1; } debian/modules/nginx-lua/src/ngx_http_lua_headerfilterby.c0000664000000000000000000002012112305451335021237 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_headerfilterby.h" #include "ngx_http_lua_exception.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_pcrefix.h" #include "ngx_http_lua_time.h" #include "ngx_http_lua_log.h" #include "ngx_http_lua_regex.h" #include "ngx_http_lua_cache.h" #include "ngx_http_lua_headers.h" #include "ngx_http_lua_variable.h" #include "ngx_http_lua_string.h" #include "ngx_http_lua_misc.h" #include "ngx_http_lua_consts.h" #include "ngx_http_lua_shdict.h" static ngx_http_output_header_filter_pt ngx_http_next_header_filter; /* light user data key for the "ngx" table in the Lua VM regsitry */ static char ngx_http_lua_headerfilterby_ngx_key; /** * Set environment table for the given code closure. * * Before: * | code closure | <- top * | ... | * * After: * | code closure | <- top * | ... | * */ static void ngx_http_lua_header_filter_by_lua_env(lua_State *L, ngx_http_request_t *r) { /* set nginx request pointer to current lua thread's globals table */ ngx_http_lua_set_req(L, r); /** * we want to create empty environment for current script * * newt = {} * newt["_G"] = newt * setmetatable(newt, {__index = _G}) * * if a function or symbol is not defined in our env, __index will lookup * in the global env. * * all variables created in the script-env will be thrown away at the end * of the script run. * */ ngx_http_lua_create_new_global_table(L, 0 /* narr */, 1 /* nrec */); /* {{{ initialize ngx.* namespace */ lua_pushlightuserdata(L, &ngx_http_lua_headerfilterby_ngx_key); lua_rawget(L, LUA_REGISTRYINDEX); lua_setfield(L, -2, "ngx"); /* }}} */ /* {{{ make new env inheriting main thread's globals table */ lua_createtable(L, 0, 1 /* nrec */); /* the metatable for the new env */ lua_pushvalue(L, LUA_GLOBALSINDEX); lua_setfield(L, -2, "__index"); lua_setmetatable(L, -2); /* setmetatable({}, {__index = _G}) */ /* }}} */ lua_setfenv(L, -2); /* set new running env for the code closure */ } ngx_int_t ngx_http_lua_header_filter_by_chunk(lua_State *L, ngx_http_request_t *r) { int old_exit_code = 0; ngx_int_t rc; u_char *err_msg; size_t len; #if (NGX_PCRE) ngx_pool_t *old_pool; #endif ngx_http_lua_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx->exited) { old_exit_code = ctx->exit_code; } /* initialize nginx context in Lua VM, code chunk at stack top sp = 1 */ ngx_http_lua_header_filter_by_lua_env(L, r); #if (NGX_PCRE) /* XXX: work-around to nginx regex subsystem */ old_pool = ngx_http_lua_pcre_malloc_init(r->pool); #endif lua_pushcfunction(L, ngx_http_lua_traceback); lua_insert(L, 1); /* put it under chunk and args */ /* protected call user code */ rc = lua_pcall(L, 0, 1, 1); lua_remove(L, 1); /* remove traceback function */ #if (NGX_PCRE) /* XXX: work-around to nginx regex subsystem */ ngx_http_lua_pcre_malloc_done(old_pool); #endif dd("rc == %d", (int) rc); if (rc != 0) { /* error occured when running loaded code */ err_msg = (u_char *) lua_tolstring(L, -1, &len); if (err_msg == NULL) { err_msg = (u_char *) "unknown reason"; len = sizeof("unknown reason") - 1; } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to run header_filter_by_lua*: %*s", len, err_msg); lua_settop(L, 0); /* clear remaining elems on stack */ return NGX_ERROR; } dd("exited: %d, exit code: %d, old exit code: %d", (int) ctx->exited, (int) ctx->exit_code, (int) old_exit_code); if (ctx->exited && ctx->exit_code != old_exit_code) { if (ctx->exit_code == NGX_ERROR) { return NGX_ERROR; } dd("finalize request with %d", (int) ctx->exit_code); rc = ngx_http_filter_finalize_request(r, &ngx_http_lua_module, ctx->exit_code); if (rc == NGX_ERROR || rc == NGX_AGAIN) { return rc; } return NGX_DECLINED; } /* clear Lua stack */ lua_settop(L, 0); return NGX_OK; } ngx_int_t ngx_http_lua_header_filter_inline(ngx_http_request_t *r) { lua_State *L; ngx_int_t rc; ngx_http_lua_loc_conf_t *llcf; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); L = ngx_http_lua_get_lua_vm(r, NULL); /* load Lua inline script (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadbuffer(L, llcf->header_filter_src.value.data, llcf->header_filter_src.value.len, llcf->header_filter_src_key, "header_filter_by_lua"); if (rc != NGX_OK) { return NGX_ERROR; } dd("calling header filter by chunk"); return ngx_http_lua_header_filter_by_chunk(L, r); } ngx_int_t ngx_http_lua_header_filter_file(ngx_http_request_t *r) { lua_State *L; ngx_int_t rc; u_char *script_path; ngx_http_lua_loc_conf_t *llcf; ngx_str_t eval_src; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); /* Eval nginx variables in code path string first */ if (ngx_http_complex_value(r, &llcf->header_filter_src, &eval_src) != NGX_OK) { return NGX_ERROR; } script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data, eval_src.len); if (script_path == NULL) { return NGX_ERROR; } L = ngx_http_lua_get_lua_vm(r, NULL); /* load Lua script file (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadfile(L, script_path, llcf->header_filter_src_key); if (rc != NGX_OK) { return NGX_ERROR; } /* make sure we have a valid code chunk */ assert(lua_isfunction(L, -1)); return ngx_http_lua_header_filter_by_chunk(L, r); } static ngx_int_t ngx_http_lua_header_filter(ngx_http_request_t *r) { ngx_http_lua_loc_conf_t *llcf; ngx_http_lua_ctx_t *ctx; ngx_int_t rc; ngx_http_cleanup_t *cln; uint16_t old_context; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua header filter for user lua code, uri \"%V\"", &r->uri); llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->body_filter_handler) { r->filter_need_in_memory = 1; } if (llcf->header_filter_handler == NULL) { dd("no header filter handler found"); return ngx_http_next_header_filter(r); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); dd("ctx = %p", ctx); if (ctx == NULL) { ctx = ngx_http_lua_create_ctx(r); if (ctx == NULL) { return NGX_ERROR; } } if (ctx->cleanup == NULL) { cln = ngx_http_cleanup_add(r, 0); if (cln == NULL) { return NGX_ERROR; } cln->handler = ngx_http_lua_request_cleanup_handler; cln->data = ctx; ctx->cleanup = &cln->handler; } old_context = ctx->context; ctx->context = NGX_HTTP_LUA_CONTEXT_HEADER_FILTER; dd("calling header filter handler"); rc = llcf->header_filter_handler(r); ctx->context = old_context; if (rc == NGX_DECLINED) { return NGX_OK; } if (rc == NGX_AGAIN || rc == NGX_ERROR) { return rc; } return ngx_http_next_header_filter(r); } ngx_int_t ngx_http_lua_header_filter_init(void) { dd("calling header filter init"); ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_lua_header_filter; return NGX_OK; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_shdict.h0000664000000000000000000000260612305451335017541 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_SHDICT_H_INCLUDED_ #define _NGX_HTTP_LUA_SHDICT_H_INCLUDED_ #include "ngx_http_lua_common.h" typedef struct { u_char color; u_char dummy; u_short key_len; ngx_queue_t queue; uint64_t expires; uint8_t value_type; uint32_t value_len; uint32_t user_flags; u_char data[1]; } ngx_http_lua_shdict_node_t; typedef struct { ngx_rbtree_t rbtree; ngx_rbtree_node_t sentinel; ngx_queue_t queue; } ngx_http_lua_shdict_shctx_t; typedef struct { ngx_http_lua_shdict_shctx_t *sh; ngx_slab_pool_t *shpool; ngx_str_t name; ngx_http_lua_main_conf_t *main_conf; ngx_log_t *log; } ngx_http_lua_shdict_ctx_t; ngx_int_t ngx_http_lua_shdict_init_zone(ngx_shm_zone_t *shm_zone, void *data); void ngx_http_lua_shdict_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); void ngx_http_lua_inject_shdict_api(ngx_http_lua_main_conf_t *lmcf, lua_State *L); #endif /* _NGX_HTTP_LUA_SHDICT_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_headers.c0000664000000000000000000006132312305451335017672 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_headers.h" #include "ngx_http_lua_headers_out.h" #include "ngx_http_lua_headers_in.h" #include "ngx_http_lua_util.h" static int ngx_http_lua_ngx_req_http_version(lua_State *L); static int ngx_http_lua_ngx_req_raw_header(lua_State *L); static int ngx_http_lua_ngx_req_header_set_helper(lua_State *L); static int ngx_http_lua_ngx_header_get(lua_State *L); static int ngx_http_lua_ngx_header_set(lua_State *L); static int ngx_http_lua_ngx_req_get_headers(lua_State *L); static int ngx_http_lua_ngx_req_header_clear(lua_State *L); static int ngx_http_lua_ngx_req_header_set(lua_State *L); static int ngx_http_lua_ngx_req_http_version(lua_State *L) { ngx_http_request_t *r; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } ngx_http_lua_check_fake_request(L, r); switch (r->http_version) { case NGX_HTTP_VERSION_9: lua_pushnumber(L, 0.9); break; case NGX_HTTP_VERSION_10: lua_pushnumber(L, 1.0); break; case NGX_HTTP_VERSION_11: lua_pushnumber(L, 1.1); break; default: lua_pushnil(L); break; } return 1; } static int ngx_http_lua_ngx_req_raw_header(lua_State *L) { int n; u_char *data, *p, *last, *pos; unsigned no_req_line = 0, found; size_t size; ngx_buf_t *b, *first = NULL; ngx_int_t i; ngx_connection_t *c; ngx_http_request_t *r, *mr; ngx_http_connection_t *hc; n = lua_gettop(L); if (n > 0) { no_req_line = lua_toboolean(L, 1); } dd("no req line: %d", (int) no_req_line); r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } ngx_http_lua_check_fake_request(L, r); mr = r->main; hc = mr->http_connection; c = mr->connection; #if 0 dd("hc->nbusy: %d", (int) hc->nbusy); dd("hc->busy: %p %p %p %p", hc->busy[0]->start, hc->busy[0]->pos, hc->busy[0]->last, hc->busy[0]->end); dd("request line: %p %p", mr->request_line.data, mr->request_line.data + mr->request_line.len); dd("header in: %p %p %p %p", mr->header_in->start, mr->header_in->pos, mr->header_in->last, mr->header_in->end); dd("c->buffer: %p %p %p %p", c->buffer->start, c->buffer->pos, c->buffer->last, c->buffer->end); #endif size = 0; b = c->buffer; if (mr->request_line.data >= b->start && mr->request_line.data + mr->request_line.len + 2 <= b->pos) { first = b; if (mr->header_in == b) { size += mr->header_end + 2 - mr->request_line.data; } else { /* the subsequent part of the header is in the large header * buffers */ #if 1 p = b->pos; size += p - mr->request_line.data; /* skip truncated header entries (if any) */ while (b->pos > b->start && b->pos[-1] != LF) { b->pos--; size--; } #endif } } if (hc->nbusy) { b = NULL; for (i = 0; i < hc->nbusy; i++) { b = hc->busy[i]; dd("busy buf: %d: [%.*s]", (int) i, (int) (b->pos - b->start), b->start); if (first == NULL) { if (mr->request_line.data >= b->pos || mr->request_line.data + mr->request_line.len + 2 <= b->start) { continue; } dd("found first at %d", (int) i); first = b; } if (b == mr->header_in) { size += mr->header_end + 2 - b->start; break; } size += b->pos - b->start; } } data = lua_newuserdata(L, size); last = data; b = c->buffer; if (first == b) { if (mr->header_in == b) { pos = mr->header_end + 2; } else { pos = b->pos; } if (no_req_line) { last = ngx_copy(data, mr->request_line.data + mr->request_line.len + 2, pos - mr->request_line.data - mr->request_line.len - 2); } else { last = ngx_copy(data, mr->request_line.data, pos - mr->request_line.data); } for (p = data; p != last; p++) { if (*p == '\0') { if (p + 1 != last && *(p + 1) == LF) { *p = CR; } else { *p = ':'; } } } } if (hc->nbusy) { found = (b == c->buffer); for (i = 0; i < hc->nbusy; i++) { b = hc->busy[i]; if (!found) { if (b != first) { continue; } dd("found first"); found = 1; } p = last; if (b == mr->header_in) { pos = mr->header_end + 2; } else { pos = b->pos; } if (b == first) { dd("request line: %.*s", (int) mr->request_line.len, mr->request_line.data); if (no_req_line) { last = ngx_copy(last, mr->request_line.data + mr->request_line.len + 2, pos - mr->request_line.data - mr->request_line.len - 2); } else { last = ngx_copy(last, mr->request_line.data, pos - mr->request_line.data); } } else { last = ngx_copy(last, b->start, pos - b->start); } #if 1 /* skip truncated header entries (if any) */ while (last > p && last[-1] != LF) { last--; } #endif for (; p != last; p++) { if (*p == '\0') { if (p + 1 == last) { /* XXX this should not happen */ dd("found string end!!"); } else if (*(p + 1) == LF) { *p = CR; } else { *p = ':'; } } } if (b == mr->header_in) { break; } } } if (last - data > (ssize_t) size) { return luaL_error(L, "buffer error: %d", (int) (last - data - size)); } lua_pushlstring(L, (char *) data, last - data); return 1; } static int ngx_http_lua_ngx_req_get_headers(lua_State *L) { ngx_list_part_t *part; ngx_table_elt_t *header; ngx_http_request_t *r; ngx_uint_t i; int n; int max; int raw = 0; int count = 0; n = lua_gettop(L); if (n >= 1) { if (lua_isnil(L, 1)) { max = NGX_HTTP_LUA_MAX_HEADERS; } else { max = luaL_checkinteger(L, 1); } if (n >= 2) { raw = lua_toboolean(L, 2); } } else { max = NGX_HTTP_LUA_MAX_HEADERS; } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } ngx_http_lua_check_fake_request(L, r); part = &r->headers_in.headers.part; count = part->nelts; while (part->next) { part = part->next; count += part->nelts; } if (max > 0 && count > max) { count = max; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua exceeding request header limit %d", max); } lua_createtable(L, 0, count); if (!raw) { lua_pushlightuserdata(L, &ngx_http_lua_req_get_headers_metatable_key); lua_rawget(L, LUA_REGISTRYINDEX); lua_setmetatable(L, -2); } part = &r->headers_in.headers.part; header = part->elts; for (i = 0; /* void */; i++) { dd("stack top: %d", lua_gettop(L)); if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; header = part->elts; i = 0; } if (raw) { lua_pushlstring(L, (char *) header[i].key.data, header[i].key.len); } else { lua_pushlstring(L, (char *) header[i].lowcase_key, header[i].key.len); } /* stack: table key */ lua_pushlstring(L, (char *) header[i].value.data, header[i].value.len); /* stack: table key value */ ngx_http_lua_set_multi_value_table(L, -3); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua request header: \"%V: %V\"", &header[i].key, &header[i].value); if (--count == 0) { return 1; } } return 1; } static int ngx_http_lua_ngx_header_get(lua_State *L) { ngx_http_request_t *r; u_char *p; ngx_str_t key; ngx_uint_t i; size_t len; ngx_http_lua_loc_conf_t *llcf; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } ngx_http_lua_check_fake_request(L, r); /* we skip the first argument that is the table */ p = (u_char *) luaL_checklstring(L, 2, &len); dd("key: %.*s, len %d", (int) len, p, (int) len); key.data = ngx_palloc(r->pool, len + 1); if (key.data == NULL) { return luaL_error(L, "out of memory"); } ngx_memcpy(key.data, p, len); llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->transform_underscores_in_resp_headers) { /* replace "_" with "-" */ for (i = 0; i < len; i++) { if (key.data[i] == '_') { key.data[i] = '-'; } } } key.data[len] = '\0'; key.len = len; return ngx_http_lua_get_output_header(L, r, &key); } static int ngx_http_lua_ngx_header_set(lua_State *L) { ngx_http_request_t *r; u_char *p; ngx_str_t key; ngx_str_t value; ngx_uint_t i; size_t len; ngx_http_lua_ctx_t *ctx; ngx_int_t rc; ngx_uint_t n; ngx_http_lua_loc_conf_t *llcf; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no ctx"); } ngx_http_lua_check_fake_request(L, r); if (r->header_sent) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "attempt to " "set ngx.header.HEADER after sending out " "response headers"); return 0; } /* we skip the first argument that is the table */ p = (u_char *) luaL_checklstring(L, 2, &len); dd("key: %.*s, len %d", (int) len, p, (int) len); key.data = ngx_palloc(r->pool, len + 1); if (key.data == NULL) { return luaL_error(L, "out of memory"); } ngx_memcpy(key.data, p, len); key.data[len] = '\0'; key.len = len; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->transform_underscores_in_resp_headers) { /* replace "_" with "-" */ p = key.data; for (i = 0; i < len; i++) { if (p[i] == '_') { p[i] = '-'; } } } if (!ctx->headers_set) { rc = ngx_http_lua_set_content_type(r); if (rc != NGX_OK) { return luaL_error(L, "failed to set default content type: %d", (int) rc); } ctx->headers_set = 1; } if (lua_type(L, 3) == LUA_TNIL) { value.data = NULL; value.len = 0; } else if (lua_type(L, 3) == LUA_TTABLE) { n = luaL_getn(L, 3); if (n == 0) { value.data = NULL; value.len = 0; } else { for (i = 1; i <= n; i++) { dd("header value table index %d", (int) i); lua_rawgeti(L, 3, i); p = (u_char *) luaL_checklstring(L, -1, &len); value.data = ngx_palloc(r->pool, len); if (value.data == NULL) { return luaL_error(L, "out of memory"); } ngx_memcpy(value.data, p, len); value.len = len; rc = ngx_http_lua_set_output_header(r, key, value, i == 1 /* override */); if (rc == NGX_ERROR) { return luaL_error(L, "failed to set header %s (error: %d)", key.data, (int) rc); } } return 0; } } else { p = (u_char *) luaL_checklstring(L, 3, &len); value.data = ngx_palloc(r->pool, len); if (value.data == NULL) { return luaL_error(L, "out of memory"); } ngx_memcpy(value.data, p, len); value.len = len; } dd("key: %.*s, value: %.*s", (int) key.len, key.data, (int) value.len, value.data); rc = ngx_http_lua_set_output_header(r, key, value, 1 /* override */); if (rc == NGX_ERROR) { return luaL_error(L, "failed to set header %s (error: %d)", key.data, (int) rc); } return 0; } static int ngx_http_lua_ngx_req_header_clear(lua_State *L) { if (lua_gettop(L) != 1) { return luaL_error(L, "expecting one arguments, but seen %d", lua_gettop(L)); } lua_pushnil(L); return ngx_http_lua_ngx_req_header_set_helper(L); } static int ngx_http_lua_ngx_req_header_set(lua_State *L) { if (lua_gettop(L) != 2) { return luaL_error(L, "expecting two arguments, but seen %d", lua_gettop(L)); } return ngx_http_lua_ngx_req_header_set_helper(L); } static int ngx_http_lua_ngx_req_header_set_helper(lua_State *L) { ngx_http_request_t *r; u_char *p; ngx_str_t key; ngx_str_t value; ngx_uint_t i; size_t len; ngx_int_t rc; ngx_uint_t n; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } ngx_http_lua_check_fake_request(L, r); if (r->http_version < NGX_HTTP_VERSION_10) { return 0; } p = (u_char *) luaL_checklstring(L, 1, &len); dd("key: %.*s, len %d", (int) len, p, (int) len); #if 0 /* replace "_" with "-" */ for (i = 0; i < len; i++) { if (p[i] == '_') { p[i] = '-'; } } #endif key.data = ngx_palloc(r->pool, len + 1); if (key.data == NULL) { return luaL_error(L, "out of memory"); } ngx_memcpy(key.data, p, len); key.data[len] = '\0'; key.len = len; if (lua_type(L, 2) == LUA_TNIL) { value.data = NULL; value.len = 0; } else if (lua_type(L, 2) == LUA_TTABLE) { n = luaL_getn(L, 2); if (n == 0) { value.data = NULL; value.len = 0; } else { for (i = 1; i <= n; i++) { dd("header value table index %d, top: %d", (int) i, lua_gettop(L)); lua_rawgeti(L, 2, i); p = (u_char *) luaL_checklstring(L, -1, &len); /* * we also copy the trailling '\0' char here because nginx * header values must be null-terminated * */ value.data = ngx_palloc(r->pool, len + 1); if (value.data == NULL) { return luaL_error(L, "out of memory"); } ngx_memcpy(value.data, p, len + 1); value.len = len; rc = ngx_http_lua_set_input_header(r, key, value, i == 1 /* override */); if (rc == NGX_ERROR) { return luaL_error(L, "failed to set header %s (error: %d)", key.data, (int) rc); } } return 0; } } else { /* * we also copy the trailling '\0' char here because nginx * header values must be null-terminated * */ p = (u_char *) luaL_checklstring(L, 2, &len); value.data = ngx_palloc(r->pool, len + 1); if (value.data == NULL) { return luaL_error(L, "out of memory"); } ngx_memcpy(value.data, p, len + 1); value.len = len; } dd("key: %.*s, value: %.*s", (int) key.len, key.data, (int) value.len, value.data); rc = ngx_http_lua_set_input_header(r, key, value, 1 /* override */); if (rc == NGX_ERROR) { return luaL_error(L, "failed to set header %s (error: %d)", key.data, (int) rc); } return 0; } void ngx_http_lua_inject_resp_header_api(lua_State *L) { lua_newtable(L); /* .header */ lua_createtable(L, 0, 2); /* metatable for .header */ lua_pushcfunction(L, ngx_http_lua_ngx_header_get); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, ngx_http_lua_ngx_header_set); lua_setfield(L, -2, "__newindex"); lua_setmetatable(L, -2); lua_setfield(L, -2, "header"); } void ngx_http_lua_inject_req_header_api(ngx_log_t *log, lua_State *L) { int rc; lua_pushcfunction(L, ngx_http_lua_ngx_req_http_version); lua_setfield(L, -2, "http_version"); lua_pushcfunction(L, ngx_http_lua_ngx_req_raw_header); lua_setfield(L, -2, "raw_header"); lua_pushcfunction(L, ngx_http_lua_ngx_req_header_clear); lua_setfield(L, -2, "clear_header"); lua_pushcfunction(L, ngx_http_lua_ngx_req_header_set); lua_setfield(L, -2, "set_header"); lua_pushcfunction(L, ngx_http_lua_ngx_req_get_headers); lua_setfield(L, -2, "get_headers"); lua_pushlightuserdata(L, &ngx_http_lua_req_get_headers_metatable_key); lua_createtable(L, 0, 1); /* metatable for ngx.req.get_headers(_, true) */ { const char buf[] = "local tb, key = ...\n" "local new_key = string.gsub(string.lower(key), '_', '-')\n" "if new_key ~= key then return tb[new_key] else return nil end"; rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, "ngx.req.get_headers __index"); } if (rc != 0) { ngx_log_error(NGX_LOG_ERR, log, 0, "failed to load Lua code of the metamethod for " "ngx.req.get_headers: %i: %s", rc, lua_tostring(L, -1)); lua_pop(L, 3); return; } lua_setfield(L, -2, "__index"); lua_rawset(L, LUA_REGISTRYINDEX); } #ifndef NGX_HTTP_LUA_NO_FFI_API int ngx_http_lua_ffi_req_get_headers_count(ngx_http_request_t *r, int max) { int count; ngx_list_part_t *part; if (r->connection->fd == -1) { return NGX_HTTP_LUA_FFI_BAD_CONTEXT; } if (max < 0) { max = NGX_HTTP_LUA_MAX_HEADERS; } part = &r->headers_in.headers.part; count = part->nelts; while (part->next) { part = part->next; count += part->nelts; } if (max > 0 && count > max) { count = max; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua exceeding request header limit %d", max); } return count; } int ngx_http_lua_ffi_req_get_headers(ngx_http_request_t *r, ngx_http_lua_ffi_table_elt_t *out, int count, int raw) { int n; ngx_uint_t i; ngx_list_part_t *part; ngx_table_elt_t *header; if (count <= 0) { return NGX_OK; } n = 0; part = &r->headers_in.headers.part; header = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; header = part->elts; i = 0; } if (raw) { out[n].key.data = header[i].key.data; out[n].key.len = (int) header[i].key.len; } else { out[n].key.data = header[i].lowcase_key; out[n].key.len = (int) header[i].key.len; } out[n].value.data = header[i].value.data; out[n].value.len = (int) header[i].value.len; if (++n == count) { return NGX_OK; } } return NGX_OK; } int ngx_http_lua_ffi_set_resp_header(ngx_http_request_t *r, const u_char *key_data, size_t key_len, int is_nil, const u_char *sval, size_t sval_len, ngx_str_t *mvals, size_t mvals_len, char **errmsg) { u_char *p; ngx_str_t value, key; ngx_uint_t i; size_t len; ngx_http_lua_ctx_t *ctx; ngx_int_t rc; ngx_http_lua_loc_conf_t *llcf; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return NGX_HTTP_LUA_FFI_NO_REQ_CTX; } if (r->connection->fd == -1) { return NGX_HTTP_LUA_FFI_BAD_CONTEXT; } if (r->header_sent) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "attempt to " "set ngx.header.HEADER after sending out " "response headers"); return NGX_DECLINED; } key.data = ngx_palloc(r->pool, key_len + 1); if (key.data == NULL) { goto nomem; } ngx_memcpy(key.data, key_data, key_len); key.data[key_len] = '\0'; key.len = key_len; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->transform_underscores_in_resp_headers) { /* replace "_" with "-" */ p = key.data; for (i = 0; i < key_len; i++) { if (p[i] == '_') { p[i] = '-'; } } } if (!ctx->headers_set) { rc = ngx_http_lua_set_content_type(r); if (rc != NGX_OK) { *errmsg = "failed to set default content type"; return NGX_ERROR; } ctx->headers_set = 1; } if (is_nil) { value.data = NULL; value.len = 0; } else if (mvals) { if (mvals_len == 0) { value.data = NULL; value.len = 0; } else { for (i = 0; i < mvals_len; i++) { dd("header value table index %d", (int) i); p = mvals[i].data; len = mvals[i].len; value.data = ngx_palloc(r->pool, len); if (value.data == NULL) { goto nomem; } ngx_memcpy(value.data, p, len); value.len = len; rc = ngx_http_lua_set_output_header(r, key, value, i == 0 /* override */); if (rc == NGX_ERROR) { *errmsg = "failed to set header"; return NGX_ERROR; } } return NGX_OK; } } else { p = (u_char *) sval; value.data = ngx_palloc(r->pool, sval_len); if (value.data == NULL) { goto nomem; } ngx_memcpy(value.data, p, sval_len); value.len = sval_len; } dd("key: %.*s, value: %.*s", (int) key.len, key.data, (int) value.len, value.data); rc = ngx_http_lua_set_output_header(r, key, value, 1 /* override */); if (rc == NGX_ERROR) { *errmsg = "failed to set header"; return NGX_ERROR; } return 0; nomem: *errmsg = "no memory"; return NGX_ERROR; } #endif /* NGX_HTTP_LUA_NO_FFI_API */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_req_method.c0000664000000000000000000000642312305451335020406 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_req_method.h" #include "ngx_http_lua_subrequest.h" #include "ngx_http_lua_util.h" static int ngx_http_lua_ngx_req_get_method(lua_State *L); static int ngx_http_lua_ngx_req_set_method(lua_State *L); void ngx_http_lua_inject_req_method_api(lua_State *L) { lua_pushcfunction(L, ngx_http_lua_ngx_req_get_method); lua_setfield(L, -2, "get_method"); lua_pushcfunction(L, ngx_http_lua_ngx_req_set_method); lua_setfield(L, -2, "set_method"); } static int ngx_http_lua_ngx_req_get_method(lua_State *L) { int n; ngx_http_request_t *r; n = lua_gettop(L); if (n != 0) { return luaL_error(L, "only one argument expected but got %d", n); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "request object not found"); } ngx_http_lua_check_fake_request(L, r); lua_pushlstring(L, (char *) r->method_name.data, r->method_name.len); return 1; } static int ngx_http_lua_ngx_req_set_method(lua_State *L) { int n; int method; ngx_http_request_t *r; n = lua_gettop(L); if (n != 1) { return luaL_error(L, "only one argument expected but got %d", n); } method = luaL_checkint(L, 1); r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "request object not found"); } ngx_http_lua_check_fake_request(L, r); r->method = method; switch (method) { case NGX_HTTP_GET: r->method_name = ngx_http_lua_get_method; break; case NGX_HTTP_POST: r->method_name = ngx_http_lua_post_method; break; case NGX_HTTP_PUT: r->method_name = ngx_http_lua_put_method; break; case NGX_HTTP_HEAD: r->method_name = ngx_http_lua_head_method; break; case NGX_HTTP_DELETE: r->method_name = ngx_http_lua_delete_method; break; case NGX_HTTP_OPTIONS: r->method_name = ngx_http_lua_options_method; break; case NGX_HTTP_MKCOL: r->method_name = ngx_http_lua_mkcol_method; break; case NGX_HTTP_COPY: r->method_name = ngx_http_lua_copy_method; break; case NGX_HTTP_MOVE: r->method_name = ngx_http_lua_move_method; break; case NGX_HTTP_PROPFIND: r->method_name = ngx_http_lua_propfind_method; break; case NGX_HTTP_PROPPATCH: r->method_name = ngx_http_lua_proppatch_method; break; case NGX_HTTP_LOCK: r->method_name = ngx_http_lua_lock_method; break; case NGX_HTTP_UNLOCK: r->method_name = ngx_http_lua_unlock_method; break; case NGX_HTTP_PATCH: r->method_name = ngx_http_lua_patch_method; break; case NGX_HTTP_TRACE: r->method_name = ngx_http_lua_trace_method; break; default: return luaL_error(L, "unsupported HTTP method: %d", method); } return 0; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_headers_out.h0000664000000000000000000000102512305451335020557 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_HEADERS_OUT_H_INCLUDED_ #define _NGX_HTTP_LUA_HEADERS_OUT_H_INCLUDED_ #include "ngx_http_lua_common.h" ngx_int_t ngx_http_lua_set_output_header(ngx_http_request_t *r, ngx_str_t key, ngx_str_t value, unsigned override); int ngx_http_lua_get_output_header(lua_State *L, ngx_http_request_t *r, ngx_str_t *key); #endif /* _NGX_HTTP_LUA_HEADERS_OUT_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_control.h0000664000000000000000000000057012305451335017741 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_CONTROL_H_INCLUDED_ #define _NGX_HTTP_LUA_CONTROL_H_INCLUDED_ #include "ngx_http_lua_common.h" void ngx_http_lua_inject_control_api(ngx_log_t *log, lua_State *L); #endif /* _NGX_HTTP_LUA_CONTROL_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_variable.c0000664000000000000000000003036512305451335020046 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_variable.h" #include "ngx_http_lua_util.h" static int ngx_http_lua_var_get(lua_State *L); static int ngx_http_lua_var_set(lua_State *L); void ngx_http_lua_inject_variable_api(lua_State *L) { /* {{{ register reference maps */ lua_newtable(L); /* ngx.var */ lua_createtable(L, 0, 2 /* nrec */); /* metatable for .var */ lua_pushcfunction(L, ngx_http_lua_var_get); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, ngx_http_lua_var_set); lua_setfield(L, -2, "__newindex"); lua_setmetatable(L, -2); lua_setfield(L, -2, "var"); } /** * Get nginx internal variables content * * @retval Always return a string or nil on Lua stack. Return nil when failed * to get content, and actual content string when found the specified variable. * @seealso ngx_http_lua_var_set * */ static int ngx_http_lua_var_get(lua_State *L) { ngx_http_request_t *r; u_char *p, *lowcase; size_t len; ngx_uint_t hash; ngx_str_t name; ngx_http_variable_value_t *vv; #if (NGX_PCRE) u_char *val; ngx_uint_t n; LUA_NUMBER index; int *cap; #endif r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } ngx_http_lua_check_fake_request(L, r); #if (NGX_PCRE) if (lua_type(L, -1) == LUA_TNUMBER) { /* it is a regex capturing variable */ index = lua_tonumber(L, -1); if (index <= 0) { lua_pushnil(L); return 1; } n = (ngx_uint_t) index * 2; dd("n = %d, ncaptures = %d", (int) n, (int) r->ncaptures); if (r->captures == NULL || r->captures_data == NULL || n >= r->ncaptures) { lua_pushnil(L); return 1; } /* n >= 0 && n < r->ncaptures */ cap = r->captures; p = r->captures_data; val = &p[cap[n]]; lua_pushlstring(L, (const char *) val, (size_t) (cap[n + 1] - cap[n])); return 1; } #endif if (lua_type(L, -1) != LUA_TSTRING) { return luaL_error(L, "bad variable name"); } p = (u_char *) lua_tolstring(L, -1, &len); lowcase = lua_newuserdata(L, len); hash = ngx_hash_strlow(lowcase, p, len); name.len = len; name.data = lowcase; vv = ngx_http_get_variable(r, &name, hash); if (vv == NULL || vv->not_found) { lua_pushnil(L); return 1; } lua_pushlstring(L, (const char *) vv->data, (size_t) vv->len); return 1; } /** * Set nginx internal variable content * * @retval Always return a boolean on Lua stack. Return true when variable * content was modified successfully, false otherwise. * @seealso ngx_http_lua_var_get * */ static int ngx_http_lua_var_set(lua_State *L) { ngx_http_variable_t *v; ngx_http_variable_value_t *vv; ngx_http_core_main_conf_t *cmcf; u_char *p, *lowcase, *val; size_t len; ngx_str_t name; ngx_uint_t hash; ngx_http_request_t *r; int value_type; const char *msg; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } ngx_http_lua_check_fake_request(L, r); /* we skip the first argument that is the table */ /* we read the variable name */ if (lua_type(L, 2) != LUA_TSTRING) { return luaL_error(L, "bad variable name"); } p = (u_char *) lua_tolstring(L, 2, &len); lowcase = lua_newuserdata(L, len + 1); hash = ngx_hash_strlow(lowcase, p, len); lowcase[len] = '\0'; name.len = len; name.data = lowcase; /* we read the variable new value */ value_type = lua_type(L, 3); switch (value_type) { case LUA_TNUMBER: case LUA_TSTRING: p = (u_char *) luaL_checklstring(L, 3, &len); val = ngx_palloc(r->pool, len); if (val == NULL) { return luaL_error(L, "memory allocation erorr"); } ngx_memcpy(val, p, len); break; case LUA_TNIL: /* undef the variable */ val = NULL; len = 0; break; default: msg = lua_pushfstring(L, "string, number, or nil expected, " "but got %s", lua_typename(L, value_type)); return luaL_argerror(L, 1, msg); } /* we fetch the variable itself */ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); v = ngx_hash_find(&cmcf->variables_hash, hash, name.data, name.len); if (v) { if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) { return luaL_error(L, "variable \"%s\" not changeable", lowcase); } if (v->set_handler) { dd("set variables with set_handler"); vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t)); if (vv == NULL) { return luaL_error(L, "out of memory"); } if (value_type == LUA_TNIL) { vv->valid = 0; vv->not_found = 1; vv->no_cacheable = 0; vv->data = NULL; vv->len = 0; } else { vv->valid = 1; vv->not_found = 0; vv->no_cacheable = 0; vv->data = val; vv->len = len; } v->set_handler(r, vv, v->data); return 0; } if (v->flags & NGX_HTTP_VAR_INDEXED) { vv = &r->variables[v->index]; dd("set indexed variable"); if (value_type == LUA_TNIL) { vv->valid = 0; vv->not_found = 1; vv->no_cacheable = 0; vv->data = NULL; vv->len = 0; } else { vv->valid = 1; vv->not_found = 0; vv->no_cacheable = 0; vv->data = val; vv->len = len; } return 0; } return luaL_error(L, "variable \"%s\" cannot be assigned a value", lowcase); } /* variable not found */ return luaL_error(L, "variable \"%s\" not found for writing; " "maybe it is a built-in variable that is not changeable " "or you forgot to use \"set $%s '';\" " "in the config file to define it first", lowcase, lowcase); } #ifndef NGX_HTTP_LUA_NO_FFI_API int ngx_http_lua_ffi_var_get(ngx_http_request_t *r, u_char *name_data, size_t name_len, u_char *lowcase_buf, int capture_id, u_char **value, size_t *value_len, char **err) { ngx_uint_t hash; ngx_str_t name; ngx_http_variable_value_t *vv; #if (NGX_PCRE) u_char *p; ngx_uint_t n; int *cap; #endif if (r == NULL) { *err = "no request object found"; return NGX_ERROR; } if ((r)->connection->fd == -1) { *err = "API disabled in the current context"; return NGX_ERROR; } #if (NGX_PCRE) if (name_data == 0) { if (capture_id <= 0) { return NGX_DECLINED; } /* it is a regex capturing variable */ n = (ngx_uint_t) capture_id * 2; dd("n = %d, ncaptures = %d", (int) n, (int) r->ncaptures); if (r->captures == NULL || r->captures_data == NULL || n >= r->ncaptures) { return NGX_DECLINED; } /* n >= 0 && n < r->ncaptures */ cap = r->captures; p = r->captures_data; *value = &p[cap[n]]; *value_len = (size_t) (cap[n + 1] - cap[n]); return NGX_OK; } #endif hash = ngx_hash_strlow(lowcase_buf, name_data, name_len); name.data = lowcase_buf; name.len = name_len; dd("variable name: %.*s", (int) name_len, lowcase_buf); vv = ngx_http_get_variable(r, &name, hash); if (vv == NULL || vv->not_found) { return NGX_DECLINED; } *value = vv->data; *value_len = vv->len; return NGX_OK; } int ngx_http_lua_ffi_var_set(ngx_http_request_t *r, u_char *name_data, size_t name_len, u_char *lowcase_buf, u_char *value, size_t value_len, u_char *errbuf, size_t errlen) { u_char *p; ngx_uint_t hash; ngx_http_variable_t *v; ngx_http_variable_value_t *vv; ngx_http_core_main_conf_t *cmcf; if (r == NULL) { ngx_snprintf(errbuf, errlen, "no request object found"); return NGX_ERROR; } if ((r)->connection->fd == -1) { ngx_snprintf(errbuf, errlen, "API disabled in the current context"); return NGX_ERROR; } hash = ngx_hash_strlow(lowcase_buf, name_data, name_len); dd("variable name: %.*s", (int) name_len, lowcase_buf); /* we fetch the variable itself */ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); v = ngx_hash_find(&cmcf->variables_hash, hash, lowcase_buf, name_len); if (v) { if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) { dd("variable not changeable"); ngx_snprintf(errbuf, errlen, "variable \"%*s\" not changeable", name_len, lowcase_buf); return NGX_ERROR; } if (v->set_handler) { dd("set variables with set_handler"); if (value != NULL && value_len) { vv = ngx_pnalloc(r->pool, sizeof(ngx_http_variable_value_t) + value_len); if (vv == NULL) { goto nomem; } p = (u_char *) vv + sizeof(ngx_http_variable_value_t); ngx_memcpy(p, value, value_len); value = p; } else { vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t)); if (vv == NULL) { goto nomem; } } if (value == NULL) { vv->valid = 0; vv->not_found = 1; vv->no_cacheable = 0; vv->data = NULL; vv->len = 0; } else { vv->valid = 1; vv->not_found = 0; vv->no_cacheable = 0; vv->data = value; vv->len = value_len; } v->set_handler(r, vv, v->data); return NGX_OK; } if (v->flags & NGX_HTTP_VAR_INDEXED) { vv = &r->variables[v->index]; dd("set indexed variable"); if (value == NULL) { vv->valid = 0; vv->not_found = 1; vv->no_cacheable = 0; vv->data = NULL; vv->len = 0; } else { p = ngx_palloc(r->pool, value_len); if (p == NULL) { goto nomem; } ngx_memcpy(p, value, value_len); value = p; vv->valid = 1; vv->not_found = 0; vv->no_cacheable = 0; vv->data = value; vv->len = value_len; } return NGX_OK; } ngx_snprintf(errbuf, errlen, "variable \"%*s\" cannot be assigned " "a value", name_len, lowcase_buf); return NGX_ERROR; } /* variable not found */ ngx_snprintf(errbuf, errlen, "variable \"%*s\" not found for writing; " "maybe it is a built-in variable that is not changeable " "or you forgot to use \"set $%*s '';\" " "in the config file to define it first", name_len, lowcase_buf, name_len, lowcase_buf); return NGX_ERROR; nomem: ngx_snprintf(errbuf, errlen, "no memory"); return NGX_ERROR; } #endif /* NGX_HTTP_LUA_NO_FFI_API */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_bodyfilterby.h0000664000000000000000000000150712305451335020760 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_BODYFILTERBY_H_INCLUDED_ #define _NGX_HTTP_LUA_BODYFILTERBY_H_INCLUDED_ #include "ngx_http_lua_common.h" extern ngx_http_output_body_filter_pt ngx_http_lua_next_filter_body_filter; ngx_int_t ngx_http_lua_body_filter_init(void); ngx_int_t ngx_http_lua_body_filter_by_chunk(lua_State *L, ngx_http_request_t *r, ngx_chain_t *in); ngx_int_t ngx_http_lua_body_filter_inline(ngx_http_request_t *r, ngx_chain_t *in); ngx_int_t ngx_http_lua_body_filter_file(ngx_http_request_t *r, ngx_chain_t *in); int ngx_http_lua_body_filter_param_get(lua_State *L); int ngx_http_lua_body_filter_param_set(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx); #endif /* _NGX_HTTP_LUA_BODYFILTERBY_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_consts.c0000664000000000000000000000715212305451335017570 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_consts.h" void ngx_http_lua_inject_core_consts(lua_State *L) { /* {{{ core constants */ lua_pushinteger(L, NGX_OK); lua_setfield(L, -2, "OK"); lua_pushinteger(L, NGX_AGAIN); lua_setfield(L, -2, "AGAIN"); lua_pushinteger(L, NGX_DONE); lua_setfield(L, -2, "DONE"); lua_pushinteger(L, NGX_DECLINED); lua_setfield(L, -2, "DECLINED"); lua_pushinteger(L, NGX_ERROR); lua_setfield(L, -2, "ERROR"); lua_pushlightuserdata(L, NULL); lua_setfield(L, -2, "null"); /* }}} */ } void ngx_http_lua_inject_http_consts(lua_State *L) { /* {{{ HTTP status constants */ lua_pushinteger(L, NGX_HTTP_GET); lua_setfield(L, -2, "HTTP_GET"); lua_pushinteger(L, NGX_HTTP_POST); lua_setfield(L, -2, "HTTP_POST"); lua_pushinteger(L, NGX_HTTP_PUT); lua_setfield(L, -2, "HTTP_PUT"); lua_pushinteger(L, NGX_HTTP_HEAD); lua_setfield(L, -2, "HTTP_HEAD"); lua_pushinteger(L, NGX_HTTP_DELETE); lua_setfield(L, -2, "HTTP_DELETE"); lua_pushinteger(L, NGX_HTTP_OPTIONS); lua_setfield(L, -2, "HTTP_OPTIONS"); lua_pushinteger(L, NGX_HTTP_MKCOL); lua_setfield(L, -2, "HTTP_MKCOL"); lua_pushinteger(L, NGX_HTTP_COPY); lua_setfield(L, -2, "HTTP_COPY"); lua_pushinteger(L, NGX_HTTP_MOVE); lua_setfield(L, -2, "HTTP_MOVE"); lua_pushinteger(L, NGX_HTTP_PROPFIND); lua_setfield(L, -2, "HTTP_PROPFIND"); lua_pushinteger(L, NGX_HTTP_PROPPATCH); lua_setfield(L, -2, "HTTP_PROPPATCH"); lua_pushinteger(L, NGX_HTTP_LOCK); lua_setfield(L, -2, "HTTP_LOCK"); lua_pushinteger(L, NGX_HTTP_UNLOCK); lua_setfield(L, -2, "HTTP_UNLOCK"); lua_pushinteger(L, NGX_HTTP_PATCH); lua_setfield(L, -2, "HTTP_PATCH"); lua_pushinteger(L, NGX_HTTP_TRACE); lua_setfield(L, -2, "HTTP_TRACE"); /* }}} */ lua_pushinteger(L, NGX_HTTP_OK); lua_setfield(L, -2, "HTTP_OK"); lua_pushinteger(L, NGX_HTTP_CREATED); lua_setfield(L, -2, "HTTP_CREATED"); lua_pushinteger(L, NGX_HTTP_SPECIAL_RESPONSE); lua_setfield(L, -2, "HTTP_SPECIAL_RESPONSE"); lua_pushinteger(L, NGX_HTTP_MOVED_PERMANENTLY); lua_setfield(L, -2, "HTTP_MOVED_PERMANENTLY"); lua_pushinteger(L, NGX_HTTP_MOVED_TEMPORARILY); lua_setfield(L, -2, "HTTP_MOVED_TEMPORARILY"); #if defined(nginx_version) && nginx_version >= 8042 lua_pushinteger(L, NGX_HTTP_SEE_OTHER); lua_setfield(L, -2, "HTTP_SEE_OTHER"); #endif lua_pushinteger(L, NGX_HTTP_NOT_MODIFIED); lua_setfield(L, -2, "HTTP_NOT_MODIFIED"); lua_pushinteger(L, NGX_HTTP_BAD_REQUEST); lua_setfield(L, -2, "HTTP_BAD_REQUEST"); lua_pushinteger(L, NGX_HTTP_UNAUTHORIZED); lua_setfield(L, -2, "HTTP_UNAUTHORIZED"); lua_pushinteger(L, NGX_HTTP_FORBIDDEN); lua_setfield(L, -2, "HTTP_FORBIDDEN"); lua_pushinteger(L, NGX_HTTP_NOT_FOUND); lua_setfield(L, -2, "HTTP_NOT_FOUND"); lua_pushinteger(L, NGX_HTTP_NOT_ALLOWED); lua_setfield(L, -2, "HTTP_NOT_ALLOWED"); lua_pushinteger(L, 410); lua_setfield(L, -2, "HTTP_GONE"); lua_pushinteger(L, NGX_HTTP_INTERNAL_SERVER_ERROR); lua_setfield(L, -2, "HTTP_INTERNAL_SERVER_ERROR"); lua_pushinteger(L, 501); lua_setfield(L, -2, "HTTP_METHOD_NOT_IMPLEMENTED"); lua_pushinteger(L, NGX_HTTP_SERVICE_UNAVAILABLE); lua_setfield(L, -2, "HTTP_SERVICE_UNAVAILABLE"); lua_pushinteger(L, 504); lua_setfield(L, -2, "HTTP_GATEWAY_TIMEOUT"); /* }}} */ } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_initby.c0000664000000000000000000000346712305451335017562 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_initby.h" #include "ngx_http_lua_util.h" static int ngx_http_lua_report(ngx_log_t *log, lua_State *L, int status); static int ngx_http_lua_do_call(ngx_log_t *log, lua_State *L); int ngx_http_lua_init_by_inline(ngx_log_t *log, ngx_http_lua_main_conf_t *lmcf, lua_State *L) { int status; status = luaL_loadbuffer(L, (char *) lmcf->init_src.data, lmcf->init_src.len, "init_by_lua") || ngx_http_lua_do_call(log, L); return ngx_http_lua_report(log, L, status); } int ngx_http_lua_init_by_file(ngx_log_t *log, ngx_http_lua_main_conf_t *lmcf, lua_State *L) { int status; status = luaL_loadfile(L, (char *) lmcf->init_src.data) || ngx_http_lua_do_call(log, L); return ngx_http_lua_report(log, L, status); } static int ngx_http_lua_report(ngx_log_t *log, lua_State *L, int status) { const char *msg; if (status && !lua_isnil(L, -1)) { msg = lua_tostring(L, -1); if (msg == NULL) { msg = "unknown error"; } ngx_log_error(NGX_LOG_ERR, log, 0, "failed to run init_by_lua*: %s", msg); lua_pop(L, 1); } /* force a full garbage-collection cycle */ lua_gc(L, LUA_GCCOLLECT, 0); return status; } static int ngx_http_lua_do_call(ngx_log_t *log, lua_State *L) { int status, base; base = lua_gettop(L); /* function index */ lua_pushcfunction(L, ngx_http_lua_traceback); /* push traceback function */ lua_insert(L, base); /* put it under chunk and args */ status = lua_pcall(L, 0, 0, base); lua_remove(L, base); return status; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_req_body.h0000664000000000000000000000055412305451334020066 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_REQ_BODY_H_INCLUDED_ #define _NGX_HTTP_LUA_REQ_BODY_H_INCLUDED_ #include "ngx_http_lua_common.h" void ngx_http_lua_inject_req_body_api(lua_State *L); #endif /* _NGX_HTTP_LUA_REQ_BODY_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_uri.h0000664000000000000000000000055412305451335017062 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_URI_H_INCLUDED_ #define _NGX_HTTP_LUA_URI_H_INCLUDED_ #include "ngx_http_lua_common.h" void ngx_http_lua_inject_req_uri_api(ngx_log_t *log, lua_State *L); #endif /* _NGX_HTTP_LUA_URI_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_output.h0000664000000000000000000000117112305451334017616 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_OUTPUT_H_INCLUDED_ #define _NGX_HTTP_LUA_OUTPUT_H_INCLUDED_ #include "ngx_http_lua_common.h" void ngx_http_lua_inject_output_api(lua_State *L); size_t ngx_http_lua_calc_strlen_in_table(lua_State *L, int index, int arg_i, unsigned strict); u_char * ngx_http_lua_copy_str_in_table(lua_State *L, int index, u_char *dst); ngx_int_t ngx_http_lua_flush_resume_helper(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx); #endif /* _NGX_HTTP_LUA_OUTPUT_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_accessby.c0000664000000000000000000001766512305451334020064 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include #include "ngx_http_lua_accessby.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_exception.h" #include "ngx_http_lua_cache.h" static ngx_int_t ngx_http_lua_access_by_chunk(lua_State *L, ngx_http_request_t *r); ngx_int_t ngx_http_lua_access_handler(ngx_http_request_t *r) { ngx_int_t rc; ngx_http_lua_ctx_t *ctx; ngx_http_lua_loc_conf_t *llcf; ngx_http_lua_main_conf_t *lmcf; ngx_http_phase_handler_t tmp, *ph, *cur_ph, *last_ph; ngx_http_core_main_conf_t *cmcf; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua access handler, uri:\"%V\" c:%ud", &r->uri, r->main->count); lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); if (!lmcf->postponed_to_access_phase_end) { lmcf->postponed_to_access_phase_end = 1; cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); ph = cmcf->phase_engine.handlers; cur_ph = &ph[r->phase_handler]; /* we should skip the post_access phase handler here too */ last_ph = &ph[cur_ph->next - 2]; dd("ph cur: %d, ph next: %d", (int) r->phase_handler, (int) (cur_ph->next - 2)); #if 0 if (cur_ph == last_ph) { dd("XXX our handler is already the last access phase handler"); } #endif if (cur_ph < last_ph) { dd("swaping the contents of cur_ph and last_ph..."); tmp = *cur_ph; memmove(cur_ph, cur_ph + 1, (last_ph - cur_ph) * sizeof (ngx_http_phase_handler_t)); *last_ph = tmp; r->phase_handler--; /* redo the current ph */ return NGX_DECLINED; } } llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->access_handler == NULL) { dd("no access handler found"); return NGX_DECLINED; } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); dd("ctx = %p", ctx); if (ctx == NULL) { ctx = ngx_http_lua_create_ctx(r); if (ctx == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } dd("entered? %d", (int) ctx->entered_access_phase); if (ctx->entered_access_phase) { dd("calling wev handler"); rc = ctx->resume_handler(r); dd("wev handler returns %d", (int) rc); if (rc == NGX_ERROR || rc == NGX_DONE || rc >= NGX_OK) { return rc; } return NGX_DECLINED; } if (ctx->waiting_more_body) { dd("WAITING MORE BODY"); return NGX_DONE; } if (llcf->force_read_body && !ctx->read_body_done) { r->request_body_in_single_buf = 1; r->request_body_in_persistent_file = 1; r->request_body_in_clean_file = 1; rc = ngx_http_read_client_request_body(r, ngx_http_lua_generic_phase_post_read); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { #if (nginx_version < 1002006) || \ (nginx_version >= 1003000 && nginx_version < 1003009) r->main->count--; #endif return rc; } if (rc == NGX_AGAIN) { ctx->waiting_more_body = 1; return NGX_DONE; } } dd("calling access handler"); return llcf->access_handler(r); } ngx_int_t ngx_http_lua_access_handler_inline(ngx_http_request_t *r) { ngx_int_t rc; lua_State *L; ngx_http_lua_loc_conf_t *llcf; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); L = ngx_http_lua_get_lua_vm(r, NULL); /* load Lua inline script (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadbuffer(L, llcf->access_src.value.data, llcf->access_src.value.len, llcf->access_src_key, "access_by_lua"); if (rc != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } return ngx_http_lua_access_by_chunk(L, r); } ngx_int_t ngx_http_lua_access_handler_file(ngx_http_request_t *r) { u_char *script_path; ngx_int_t rc; ngx_str_t eval_src; lua_State *L; ngx_http_lua_loc_conf_t *llcf; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); /* Eval nginx variables in code path string first */ if (ngx_http_complex_value(r, &llcf->access_src, &eval_src) != NGX_OK) { return NGX_ERROR; } script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data, eval_src.len); if (script_path == NULL) { return NGX_ERROR; } L = ngx_http_lua_get_lua_vm(r, NULL); /* load Lua script file (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadfile(L, script_path, llcf->access_src_key); if (rc != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* make sure we have a valid code chunk */ assert(lua_isfunction(L, -1)); return ngx_http_lua_access_by_chunk(L, r); } static ngx_int_t ngx_http_lua_access_by_chunk(lua_State *L, ngx_http_request_t *r) { int co_ref; ngx_int_t rc; lua_State *co; ngx_connection_t *c; ngx_http_lua_ctx_t *ctx; ngx_http_cleanup_t *cln; ngx_http_lua_loc_conf_t *llcf; /* {{{ new coroutine to handle request */ co = ngx_http_lua_new_thread(r, L, &co_ref); if (co == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "lua: failed to create new coroutine " "to handle request"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* move code closure to new coroutine */ lua_xmove(L, co, 1); /* set closure's env table to new coroutine's globals table */ lua_pushvalue(co, LUA_GLOBALSINDEX); lua_setfenv(co, -2); /* save nginx request in coroutine globals table */ ngx_http_lua_set_req(co, r); /* {{{ initialize request context */ ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); dd("ctx = %p", ctx); if (ctx == NULL) { return NGX_ERROR; } ngx_http_lua_reset_ctx(r, L, ctx); ctx->entered_access_phase = 1; ctx->cur_co_ctx = &ctx->entry_co_ctx; ctx->cur_co_ctx->co = co; ctx->cur_co_ctx->co_ref = co_ref; /* }}} */ /* {{{ register request cleanup hooks */ if (ctx->cleanup == NULL) { cln = ngx_http_cleanup_add(r, 0); if (cln == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } cln->handler = ngx_http_lua_request_cleanup_handler; cln->data = ctx; ctx->cleanup = &cln->handler; } /* }}} */ ctx->context = NGX_HTTP_LUA_CONTEXT_ACCESS; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->check_client_abort) { r->read_event_handler = ngx_http_lua_rd_check_broken_connection; } else { r->read_event_handler = ngx_http_block_reading; } rc = ngx_http_lua_run_thread(L, r, ctx, 0); dd("returned %d", (int) rc); if (rc == NGX_ERROR || rc >= NGX_OK) { return rc; } c = r->connection; if (rc == NGX_AGAIN) { rc = ngx_http_lua_run_posted_threads(c, L, r, ctx); if (rc == NGX_ERROR || rc == NGX_DONE || rc >= NGX_OK) { return rc; } return NGX_DECLINED; } if (rc == NGX_DONE) { ngx_http_lua_finalize_request(r, NGX_DONE); rc = ngx_http_lua_run_posted_threads(c, L, r, ctx); if (rc == NGX_ERROR || rc == NGX_DONE || rc >= NGX_OK) { return rc; } return NGX_DECLINED; } return NGX_DECLINED; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_capturefilter.h0000664000000000000000000000056712305451334021137 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_CAPTUREFILTER_H_INCLUDED_ #define _NGX_HTTP_LUA_CAPTUREFILTER_H_INCLUDED_ #include "ngx_http_lua_common.h" ngx_int_t ngx_http_lua_capture_filter_init(ngx_conf_t *cf); #endif /* NGX_HTTP_LUA_CAPTUREFILTER_H */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_exception.c0000664000000000000000000000234712305451335020256 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_exception.h" #include "ngx_http_lua_util.h" /* longjmp mark for restoring nginx execution after Lua VM crashing */ jmp_buf ngx_http_lua_exception; /** * Override default Lua panic handler, output VM crash reason to nginx error * log, and restore execution to the nearest jmp-mark. * * @param L Lua state pointer * @retval Long jump to the nearest jmp-mark, never returns. * @note nginx request pointer should be stored in Lua thread's globals table * in order to make logging working. * */ int ngx_http_lua_atpanic(lua_State *L) { u_char *s = NULL; size_t len = 0; if (lua_type(L, -1) == LUA_TSTRING) { s = (u_char *) lua_tolstring(L, -1, &len); } if (s == NULL) { s = (u_char *) "unknown reason"; len = sizeof("unknown reason") - 1; } ngx_log_stderr(0, "lua atpanic: Lua VM crashed, reason: %*s", len, s); ngx_quit = 1; /* restore nginx execution */ NGX_LUA_EXCEPTION_THROW(1); /* impossible to reach here */ } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_headers.h0000664000000000000000000000066312305451335017677 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_HEADERS_H_INCLUDED_ #define _NGX_HTTP_LUA_HEADERS_H_INCLUDED_ #include "ngx_http_lua_common.h" void ngx_http_lua_inject_resp_header_api(lua_State *L); void ngx_http_lua_inject_req_header_api(ngx_log_t *log, lua_State *L); #endif /* _NGX_HTTP_LUA_HEADERS_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_ndk.h0000664000000000000000000000051312305451335017032 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_NDK_H_INCLUDED_ #define _NGX_HTTP_LUA_NDK_H_INCLUDED_ #include "ngx_http_lua_common.h" #if defined(NDK) && NDK void ngx_http_lua_inject_ndk_api(lua_State *L); #endif #endif /* _NGX_HTTP_LUA_NDK_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_timer.h0000664000000000000000000000054012305451334017375 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_TIMER_H_INCLUDED_ #define _NGX_HTTP_LUA_TIMER_H_INCLUDED_ #include "ngx_http_lua_common.h" void ngx_http_lua_inject_timer_api(lua_State *L); #endif /* _NGX_HTTP_LUA_TIMER_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_rewriteby.c0000664000000000000000000001751712305451335020301 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include #include "ngx_http_lua_rewriteby.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_exception.h" #include "ngx_http_lua_cache.h" static ngx_int_t ngx_http_lua_rewrite_by_chunk(lua_State *L, ngx_http_request_t *r); ngx_int_t ngx_http_lua_rewrite_handler(ngx_http_request_t *r) { ngx_http_lua_loc_conf_t *llcf; ngx_http_lua_ctx_t *ctx; ngx_int_t rc; ngx_http_lua_main_conf_t *lmcf; /* XXX we need to take into account ngx_rewrite's location dump */ if (r->uri_changed) { return NGX_DECLINED; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua rewrite handler, uri:\"%V\" c:%ud", &r->uri, r->main->count); lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); if (!lmcf->postponed_to_rewrite_phase_end) { ngx_http_core_main_conf_t *cmcf; ngx_http_phase_handler_t tmp; ngx_http_phase_handler_t *ph; ngx_http_phase_handler_t *cur_ph; ngx_http_phase_handler_t *last_ph; lmcf->postponed_to_rewrite_phase_end = 1; cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); ph = cmcf->phase_engine.handlers; cur_ph = &ph[r->phase_handler]; last_ph = &ph[cur_ph->next - 1]; #if 0 if (cur_ph == last_ph) { dd("XXX our handler is already the last rewrite phase handler"); } #endif if (cur_ph < last_ph) { dd("swaping the contents of cur_ph and last_ph..."); tmp = *cur_ph; memmove(cur_ph, cur_ph + 1, (last_ph - cur_ph) * sizeof (ngx_http_phase_handler_t)); *last_ph = tmp; r->phase_handler--; /* redo the current ph */ return NGX_DECLINED; } } llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->rewrite_handler == NULL) { dd("no rewrite handler found"); return NGX_DECLINED; } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); dd("ctx = %p", ctx); if (ctx == NULL) { ctx = ngx_http_lua_create_ctx(r); if (ctx == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } dd("entered? %d", (int) ctx->entered_rewrite_phase); if (ctx->entered_rewrite_phase) { dd("rewriteby: calling wev handler"); rc = ctx->resume_handler(r); dd("rewriteby: wev handler returns %d", (int) rc); if (rc == NGX_OK) { return NGX_DECLINED; } return rc; } if (ctx->waiting_more_body) { return NGX_DONE; } if (llcf->force_read_body && !ctx->read_body_done) { r->request_body_in_single_buf = 1; r->request_body_in_persistent_file = 1; r->request_body_in_clean_file = 1; rc = ngx_http_read_client_request_body(r, ngx_http_lua_generic_phase_post_read); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { #if (nginx_version < 1002006) || \ (nginx_version >= 1003000 && nginx_version < 1003009) r->main->count--; #endif return rc; } if (rc == NGX_AGAIN) { ctx->waiting_more_body = 1; return NGX_DONE; } } dd("calling rewrite handler"); return llcf->rewrite_handler(r); } ngx_int_t ngx_http_lua_rewrite_handler_inline(ngx_http_request_t *r) { lua_State *L; ngx_int_t rc; ngx_http_lua_loc_conf_t *llcf; dd("rewrite by lua inline"); llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); L = ngx_http_lua_get_lua_vm(r, NULL); /* load Lua inline script (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadbuffer(L, llcf->rewrite_src.value.data, llcf->rewrite_src.value.len, llcf->rewrite_src_key, "rewrite_by_lua"); if (rc != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } return ngx_http_lua_rewrite_by_chunk(L, r); } ngx_int_t ngx_http_lua_rewrite_handler_file(ngx_http_request_t *r) { lua_State *L; ngx_int_t rc; u_char *script_path; ngx_http_lua_loc_conf_t *llcf; ngx_str_t eval_src; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (ngx_http_complex_value(r, &llcf->rewrite_src, &eval_src) != NGX_OK) { return NGX_ERROR; } script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data, eval_src.len); if (script_path == NULL) { return NGX_ERROR; } L = ngx_http_lua_get_lua_vm(r, NULL); /* load Lua script file (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadfile(L, script_path, llcf->rewrite_src_key); if (rc != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } return ngx_http_lua_rewrite_by_chunk(L, r); } static ngx_int_t ngx_http_lua_rewrite_by_chunk(lua_State *L, ngx_http_request_t *r) { int co_ref; lua_State *co; ngx_int_t rc; ngx_connection_t *c; ngx_http_lua_ctx_t *ctx; ngx_http_cleanup_t *cln; ngx_http_lua_loc_conf_t *llcf; /* {{{ new coroutine to handle request */ co = ngx_http_lua_new_thread(r, L, &co_ref); if (co == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "lua: failed to create new coroutine to handle request"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* move code closure to new coroutine */ lua_xmove(L, co, 1); /* set closure's env table to new coroutine's globals table */ lua_pushvalue(co, LUA_GLOBALSINDEX); lua_setfenv(co, -2); /* save nginx request in coroutine globals table */ ngx_http_lua_set_req(co, r); /* {{{ initialize request context */ ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); dd("ctx = %p", ctx); if (ctx == NULL) { return NGX_ERROR; } ngx_http_lua_reset_ctx(r, L, ctx); ctx->entered_rewrite_phase = 1; ctx->cur_co_ctx = &ctx->entry_co_ctx; ctx->cur_co_ctx->co = co; ctx->cur_co_ctx->co_ref = co_ref; /* }}} */ /* {{{ register request cleanup hooks */ if (ctx->cleanup == NULL) { cln = ngx_http_cleanup_add(r, 0); if (cln == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } cln->handler = ngx_http_lua_request_cleanup_handler; cln->data = ctx; ctx->cleanup = &cln->handler; } /* }}} */ ctx->context = NGX_HTTP_LUA_CONTEXT_REWRITE; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->check_client_abort) { r->read_event_handler = ngx_http_lua_rd_check_broken_connection; } else { r->read_event_handler = ngx_http_block_reading; } rc = ngx_http_lua_run_thread(L, r, ctx, 0); if (rc == NGX_ERROR || rc > NGX_OK) { return rc; } c = r->connection; if (rc == NGX_AGAIN) { rc = ngx_http_lua_run_posted_threads(c, L, r, ctx); if (rc == NGX_OK) { return NGX_DECLINED; } return rc; } if (rc == NGX_DONE) { ngx_http_lua_finalize_request(r, NGX_DONE); rc = ngx_http_lua_run_posted_threads(c, L, r, ctx); if (rc == NGX_OK) { return NGX_DECLINED; } return rc; } return NGX_DECLINED; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_sleep.c0000664000000000000000000001056512305451335017371 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_sleep.h" #include "ngx_http_lua_contentby.h" static int ngx_http_lua_ngx_sleep(lua_State *L); static void ngx_http_lua_sleep_handler(ngx_event_t *ev); static void ngx_http_lua_sleep_cleanup(void *data); static ngx_int_t ngx_http_lua_sleep_resume(ngx_http_request_t *r); static int ngx_http_lua_ngx_sleep(lua_State *L) { int n; ngx_int_t delay; /* in msec */ ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; n = lua_gettop(L); if (n != 1) { return luaL_error(L, "attempt to pass %d arguments, but accepted 1", n); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } delay = (ngx_int_t) (luaL_checknumber(L, 1) * 1000); if (delay < 0) { return luaL_error(L, "invalid sleep duration \"%d\"", delay); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER); coctx = ctx->cur_co_ctx; if (coctx == NULL) { return luaL_error(L, "no co ctx found"); } coctx->data = r; coctx->sleep.handler = ngx_http_lua_sleep_handler; coctx->sleep.data = coctx; coctx->sleep.log = r->connection->log; dd("adding timer with delay %lu ms, r:%.*s", (unsigned long) delay, (int) r->uri.len, r->uri.data); ngx_add_timer(&coctx->sleep, (ngx_msec_t) delay); coctx->cleanup = ngx_http_lua_sleep_cleanup; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua ready to sleep for %d ms", delay); return lua_yield(L, 0); } void ngx_http_lua_sleep_handler(ngx_event_t *ev) { ngx_connection_t *c; ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_log_ctx_t *log_ctx; ngx_http_lua_co_ctx_t *coctx; coctx = ev->data; r = coctx->data; c = r->connection; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return; } log_ctx = c->log->data; log_ctx->current_request = r; coctx->cleanup = NULL; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "lua sleep timer expired: \"%V?%V\"", &r->uri, &r->args); ctx->cur_co_ctx = coctx; if (ctx->entered_content_phase) { (void) ngx_http_lua_sleep_resume(r); } else { ctx->resume_handler = ngx_http_lua_sleep_resume; ngx_http_core_run_phases(r); } ngx_http_run_posted_requests(c); } void ngx_http_lua_inject_sleep_api(lua_State *L) { lua_pushcfunction(L, ngx_http_lua_ngx_sleep); lua_setfield(L, -2, "sleep"); } static void ngx_http_lua_sleep_cleanup(void *data) { ngx_http_lua_co_ctx_t *coctx = data; if (coctx->sleep.timer_set) { dd("cleanup: deleting timer for ngx.sleep"); ngx_del_timer(&coctx->sleep); } } static ngx_int_t ngx_http_lua_sleep_resume(ngx_http_request_t *r) { lua_State *vm; ngx_connection_t *c; ngx_int_t rc; ngx_http_lua_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return NGX_ERROR; } ctx->resume_handler = ngx_http_lua_wev_handler; c = r->connection; vm = ngx_http_lua_get_lua_vm(r, ctx); rc = ngx_http_lua_run_thread(vm, r, ctx, 0); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua run thread returned %d", rc); if (rc == NGX_AGAIN) { return ngx_http_lua_run_posted_threads(c, vm, r, ctx); } if (rc == NGX_DONE) { ngx_http_lua_finalize_request(r, NGX_DONE); return ngx_http_lua_run_posted_threads(c, vm, r, ctx); } if (ctx->entered_content_phase) { ngx_http_lua_finalize_request(r, rc); return NGX_DONE; } return rc; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_misc.c0000664000000000000000000001311412305451334017204 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_misc.h" #include "ngx_http_lua_ctx.h" #include "ngx_http_lua_util.h" static int ngx_http_lua_ngx_get(lua_State *L); static int ngx_http_lua_ngx_set(lua_State *L); void ngx_http_lua_inject_misc_api(lua_State *L) { /* ngx. getter and setter */ lua_createtable(L, 0, 2); /* metatable for .ngx */ lua_pushcfunction(L, ngx_http_lua_ngx_get); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, ngx_http_lua_ngx_set); lua_setfield(L, -2, "__newindex"); lua_setmetatable(L, -2); } static int ngx_http_lua_ngx_get(lua_State *L) { int status; ngx_http_request_t *r; u_char *p; size_t len; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } p = (u_char *) luaL_checklstring(L, -1, &len); dd("ngx get %s", p); if (len == sizeof("status") - 1 && ngx_strncmp(p, "status", sizeof("status") - 1) == 0) { ngx_http_lua_check_fake_request(L, r); if (r->err_status) { status = r->err_status; } else if (r->headers_out.status) { status = r->headers_out.status; } else if (r->http_version == NGX_HTTP_VERSION_9) { status = 9; } else { status = 0; } lua_pushinteger(L, status); return 1; } if (len == sizeof("ctx") - 1 && ngx_strncmp(p, "ctx", sizeof("ctx") - 1) == 0) { return ngx_http_lua_ngx_get_ctx(L); } if (len == sizeof("is_subrequest") - 1 && ngx_strncmp(p, "is_subrequest", sizeof("is_subrequest") - 1) == 0) { lua_pushboolean(L, r != r->main); return 1; } if (len == sizeof("headers_sent") - 1 && ngx_strncmp(p, "headers_sent", sizeof("headers_sent") - 1) == 0) { ngx_http_lua_check_fake_request(L, r); dd("headers sent: %d", r->header_sent); lua_pushboolean(L, r->header_sent ? 1 : 0); return 1; } dd("key %s not matched", p); lua_pushnil(L); return 1; } static int ngx_http_lua_ngx_set(lua_State *L) { ngx_http_request_t *r; u_char *p; size_t len; /* we skip the first argument that is the table */ p = (u_char *) luaL_checklstring(L, 2, &len); if (len == sizeof("status") - 1 && ngx_strncmp(p, "status", sizeof("status") - 1) == 0) { r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } if (r->header_sent) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "attempt to set ngx.status after sending out " "response headers"); return 0; } ngx_http_lua_check_fake_request(L, r); /* get the value */ r->headers_out.status = (ngx_uint_t) luaL_checknumber(L, 3); if (r->headers_out.status == 101) { /* * XXX work-around a bug in the Nginx core that 101 does * not have a default status line */ ngx_str_set(&r->headers_out.status_line, "101 Switching Protocols"); } else { r->headers_out.status_line.len = 0; } return 0; } if (len == sizeof("ctx") - 1 && ngx_strncmp(p, "ctx", sizeof("ctx") - 1) == 0) { r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } return ngx_http_lua_ngx_set_ctx(L); } lua_rawset(L, -3); return 0; } #ifndef NGX_HTTP_LUA_NO_FFI_API int ngx_http_lua_ffi_get_resp_status(ngx_http_request_t *r) { if (r->connection->fd == -1) { return NGX_HTTP_LUA_FFI_BAD_CONTEXT; } if (r->err_status) { return r->err_status; } else if (r->headers_out.status) { return r->headers_out.status; } else if (r->http_version == NGX_HTTP_VERSION_9) { return 9; } else { return 0; } } int ngx_http_lua_ffi_set_resp_status(ngx_http_request_t *r, int status) { if (r->connection->fd == -1) { return NGX_HTTP_LUA_FFI_BAD_CONTEXT; } if (r->header_sent) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "attempt to set ngx.status after sending out " "response headers"); return NGX_DECLINED; } r->headers_out.status = status; if (status == 101) { /* * XXX work-around a bug in the Nginx core older than 1.5.5 * that 101 does not have a default status line */ ngx_str_set(&r->headers_out.status_line, "101 Switching Protocols"); } else { r->headers_out.status_line.len = 0; } return NGX_OK; } int ngx_http_lua_ffi_is_subrequest(ngx_http_request_t *r) { if (r->connection->fd == -1) { return NGX_HTTP_LUA_FFI_BAD_CONTEXT; } return r != r->main; } int ngx_http_lua_ffi_headers_sent(ngx_http_request_t *r) { ngx_http_lua_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return NGX_HTTP_LUA_FFI_NO_REQ_CTX; } if (r->connection->fd == -1) { return NGX_HTTP_LUA_FFI_BAD_CONTEXT; } return r->header_sent ? 1 : 0; } #endif /* NGX_HTTP_LUA_NO_FFI_API */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_ndk.c0000664000000000000000000001056512305451335017035 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_ndk.h" #include "ngx_http_lua_util.h" #if defined(NDK) && NDK static ndk_set_var_value_pt ngx_http_lookup_ndk_set_var_directive(u_char *name, size_t name_len); static int ngx_http_lua_ndk_set_var_get(lua_State *L); static int ngx_http_lua_ndk_set_var_set(lua_State *L); static int ngx_http_lua_run_set_var_directive(lua_State *L); int ngx_http_lua_ndk_set_var_get(lua_State *L) { ndk_set_var_value_pt func; size_t len; u_char *p; p = (u_char *) luaL_checklstring(L, 2, &len); dd("ndk.set_var metatable __index: %s", p); func = ngx_http_lookup_ndk_set_var_directive(p, len); if (func == NULL) { return luaL_error(L, "ndk.set_var: directive \"%s\" not found " "or does not use ndk_set_var_value", p); } lua_pushvalue(L, -1); /* table key key */ lua_pushvalue(L, -1); /* table key key key */ lua_pushlightuserdata(L, (void *) func); /* table key key key func */ lua_pushcclosure(L, ngx_http_lua_run_set_var_directive, 2); /* table key key closure */ lua_rawset(L, 1); /* table key */ lua_rawget(L, 1); /* table closure */ return 1; } int ngx_http_lua_ndk_set_var_set(lua_State *L) { return luaL_error(L, "Not allowed"); } int ngx_http_lua_run_set_var_directive(lua_State *L) { ngx_int_t rc; ndk_set_var_value_pt func; ngx_str_t res; ngx_http_variable_value_t arg; u_char *p; size_t len; ngx_http_request_t *r; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting one argument"); } #if 1 ngx_memzero(&arg, sizeof(ngx_http_variable_value_t)); arg.valid = 1; #endif arg.data = (u_char *) luaL_checklstring(L, 1, &len); arg.len = len; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } p = (u_char *) luaL_checklstring(L, lua_upvalueindex(1), &len); dd("calling set_var func for %s", p); func = (ndk_set_var_value_pt) lua_touserdata(L, lua_upvalueindex(2)); rc = func(r, &res, &arg); if (rc != NGX_OK) { return luaL_error(L, "calling directive %s failed with code %d", p, (int) rc); } lua_pushlstring(L, (char *) res.data, res.len); return 1; } static ndk_set_var_value_pt ngx_http_lookup_ndk_set_var_directive(u_char *name, size_t name_len) { ndk_set_var_t *filter; ngx_uint_t i; ngx_module_t *module; ngx_command_t *cmd; for (i = 0; ngx_modules[i]; i++) { module = ngx_modules[i]; if (module->type != NGX_HTTP_MODULE) { continue; } cmd = ngx_modules[i]->commands; if (cmd == NULL) { continue; } for ( /* void */ ; cmd->name.len; cmd++) { if (cmd->set != ndk_set_var_value) { continue; } filter = cmd->post; if (filter == NULL) { continue; } if (cmd->name.len != name_len || ngx_strncmp(cmd->name.data, name, name_len) != 0) { continue; } return (ndk_set_var_value_pt)(filter->func); } } return NULL; } void ngx_http_lua_inject_ndk_api(lua_State *L) { lua_createtable(L, 0, 1 /* nrec */); /* ndk.* */ lua_newtable(L); /* .set_var */ lua_createtable(L, 0, 2 /* nrec */); /* metatable for .set_var */ lua_pushcfunction(L, ngx_http_lua_ndk_set_var_get); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, ngx_http_lua_ndk_set_var_set); lua_setfield(L, -2, "__newindex"); lua_setmetatable(L, -2); lua_setfield(L, -2, "set_var"); lua_getglobal(L, "package"); /* ndk package */ lua_getfield(L, -1, "loaded"); /* ndk package loaded */ lua_pushvalue(L, -3); /* ndk package loaded ndk */ lua_setfield(L, -2, "ndk"); /* ndk package loaded */ lua_pop(L, 2); lua_setglobal(L, "ndk"); } #endif /* defined(NDK) && NDK */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_exception.h0000664000000000000000000000133012305451334020251 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_EXCEPTION_H_INCLUDED_ #define _NGX_HTTP_LUA_EXCEPTION_H_INCLUDED_ #include "ngx_http_lua_common.h" #define NGX_LUA_EXCEPTION_TRY \ if (setjmp(ngx_http_lua_exception) == 0) #define NGX_LUA_EXCEPTION_CATCH \ else #define NGX_LUA_EXCEPTION_THROW(x) \ longjmp(ngx_http_lua_exception, (x)) extern jmp_buf ngx_http_lua_exception; int ngx_http_lua_atpanic(lua_State *L); #endif /* _NGX_HTTP_LUA_EXCEPTION_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_logby.h0000664000000000000000000000100512305451335017367 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_LOGBY_H_INCLUDED_ #define _NGX_HTTP_LUA_LOGBY_H_INCLUDED_ #include "ngx_http_lua_common.h" ngx_int_t ngx_http_lua_log_handler(ngx_http_request_t *r); ngx_int_t ngx_http_lua_log_handler_inline(ngx_http_request_t *r); ngx_int_t ngx_http_lua_log_handler_file(ngx_http_request_t *r); void ngx_http_lua_inject_logby_ngx_api(ngx_conf_t *cf, lua_State *L); #endif /* _NGX_HTTP_LUA_LOGBY_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_string.c0000664000000000000000000003570312305451335017570 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_string.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_args.h" #include "ngx_crc32.h" #if NGX_HAVE_SHA1 #include "ngx_sha1.h" #endif #include "ngx_md5.h" #if (NGX_OPENSSL) #include #include #endif #ifndef SHA_DIGEST_LENGTH #define SHA_DIGEST_LENGTH 20 #endif static uintptr_t ngx_http_lua_ngx_escape_sql_str(u_char *dst, u_char *src, size_t size); static int ngx_http_lua_ngx_escape_uri(lua_State *L); static int ngx_http_lua_ngx_unescape_uri(lua_State *L); static int ngx_http_lua_ngx_quote_sql_str(lua_State *L); static int ngx_http_lua_ngx_md5(lua_State *L); static int ngx_http_lua_ngx_md5_bin(lua_State *L); #if (NGX_HAVE_SHA1) static int ngx_http_lua_ngx_sha1_bin(lua_State *L); #endif static int ngx_http_lua_ngx_decode_base64(lua_State *L); static int ngx_http_lua_ngx_encode_base64(lua_State *L); static int ngx_http_lua_ngx_crc32_short(lua_State *L); static int ngx_http_lua_ngx_crc32_long(lua_State *L); static int ngx_http_lua_ngx_encode_args(lua_State *L); static int ngx_http_lua_ngx_decode_args(lua_State *L); #if (NGX_OPENSSL) static int ngx_http_lua_ngx_hmac_sha1(lua_State *L); #endif void ngx_http_lua_inject_string_api(lua_State *L) { lua_pushcfunction(L, ngx_http_lua_ngx_escape_uri); lua_setfield(L, -2, "escape_uri"); lua_pushcfunction(L, ngx_http_lua_ngx_unescape_uri); lua_setfield(L, -2, "unescape_uri"); lua_pushcfunction(L, ngx_http_lua_ngx_encode_args); lua_setfield(L, -2, "encode_args"); lua_pushcfunction(L, ngx_http_lua_ngx_decode_args); lua_setfield(L, -2, "decode_args"); lua_pushcfunction(L, ngx_http_lua_ngx_quote_sql_str); lua_setfield(L, -2, "quote_sql_str"); lua_pushcfunction(L, ngx_http_lua_ngx_decode_base64); lua_setfield(L, -2, "decode_base64"); lua_pushcfunction(L, ngx_http_lua_ngx_encode_base64); lua_setfield(L, -2, "encode_base64"); lua_pushcfunction(L, ngx_http_lua_ngx_md5_bin); lua_setfield(L, -2, "md5_bin"); lua_pushcfunction(L, ngx_http_lua_ngx_md5); lua_setfield(L, -2, "md5"); #if (NGX_HAVE_SHA1) lua_pushcfunction(L, ngx_http_lua_ngx_sha1_bin); lua_setfield(L, -2, "sha1_bin"); #endif lua_pushcfunction(L, ngx_http_lua_ngx_crc32_short); lua_setfield(L, -2, "crc32_short"); lua_pushcfunction(L, ngx_http_lua_ngx_crc32_long); lua_setfield(L, -2, "crc32_long"); #if (NGX_OPENSSL) lua_pushcfunction(L, ngx_http_lua_ngx_hmac_sha1); lua_setfield(L, -2, "hmac_sha1"); #endif } static int ngx_http_lua_ngx_escape_uri(lua_State *L) { size_t len, dlen; uintptr_t escape; u_char *src, *dst; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting one argument"); } if (lua_isnil(L, 1)) { lua_pushliteral(L, ""); return 1; } src = (u_char *) luaL_checklstring(L, 1, &len); if (len == 0) { return 1; } escape = 2 * ngx_http_lua_escape_uri(NULL, src, len, NGX_ESCAPE_URI); if (escape) { dlen = escape + len; dst = lua_newuserdata(L, dlen); ngx_http_lua_escape_uri(dst, src, len, NGX_ESCAPE_URI); lua_pushlstring(L, (char *) dst, dlen); } return 1; } static int ngx_http_lua_ngx_unescape_uri(lua_State *L) { size_t len, dlen; u_char *p; u_char *src, *dst; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting one argument"); } if (lua_isnil(L, 1)) { lua_pushliteral(L, ""); return 1; } src = (u_char *) luaL_checklstring(L, 1, &len); /* the unescaped string can only be smaller */ dlen = len; p = lua_newuserdata(L, dlen); dst = p; ngx_http_lua_unescape_uri(&dst, &src, len, NGX_UNESCAPE_URI_COMPONENT); lua_pushlstring(L, (char *) p, dst - p); return 1; } static int ngx_http_lua_ngx_quote_sql_str(lua_State *L) { size_t len, dlen, escape; u_char *p; u_char *src, *dst; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting one argument"); } src = (u_char *) luaL_checklstring(L, 1, &len); if (len == 0) { dst = (u_char *) "''"; dlen = sizeof("''") - 1; lua_pushlstring(L, (char *) dst, dlen); return 1; } escape = ngx_http_lua_ngx_escape_sql_str(NULL, src, len); dlen = sizeof("''") - 1 + len + escape; p = lua_newuserdata(L, dlen); dst = p; *p++ = '\''; if (escape == 0) { p = ngx_copy(p, src, len); } else { p = (u_char *) ngx_http_lua_ngx_escape_sql_str(p, src, len); } *p++ = '\''; if (p != dst + dlen) { return NGX_ERROR; } lua_pushlstring(L, (char *) dst, p - dst); return 1; } static uintptr_t ngx_http_lua_ngx_escape_sql_str(u_char *dst, u_char *src, size_t size) { ngx_uint_t n; if (dst == NULL) { /* find the number of chars to be escaped */ n = 0; while (size) { /* the highest bit of all the UTF-8 chars * is always 1 */ if ((*src & 0x80) == 0) { switch (*src) { case '\0': case '\b': case '\n': case '\r': case '\t': case 26: /* \z */ case '\\': case '\'': case '"': n++; break; default: break; } } src++; size--; } return (uintptr_t) n; } while (size) { if ((*src & 0x80) == 0) { switch (*src) { case '\0': *dst++ = '\\'; *dst++ = '0'; break; case '\b': *dst++ = '\\'; *dst++ = 'b'; break; case '\n': *dst++ = '\\'; *dst++ = 'n'; break; case '\r': *dst++ = '\\'; *dst++ = 'r'; break; case '\t': *dst++ = '\\'; *dst++ = 't'; break; case 26: *dst++ = '\\'; *dst++ = 'z'; break; case '\\': *dst++ = '\\'; *dst++ = '\\'; break; case '\'': *dst++ = '\\'; *dst++ = '\''; break; case '"': *dst++ = '\\'; *dst++ = '"'; break; default: *dst++ = *src; break; } } else { *dst++ = *src; } src++; size--; } /* while (size) */ return (uintptr_t) dst; } static int ngx_http_lua_ngx_md5(lua_State *L) { u_char *src; size_t slen; ngx_md5_t md5; u_char md5_buf[MD5_DIGEST_LENGTH]; u_char hex_buf[2 * sizeof(md5_buf)]; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting one argument"); } if (lua_isnil(L, 1)) { src = (u_char *) ""; slen = 0; } else { src = (u_char *) luaL_checklstring(L, 1, &slen); } ngx_md5_init(&md5); ngx_md5_update(&md5, src, slen); ngx_md5_final(md5_buf, &md5); ngx_hex_dump(hex_buf, md5_buf, sizeof(md5_buf)); lua_pushlstring(L, (char *) hex_buf, sizeof(hex_buf)); return 1; } static int ngx_http_lua_ngx_md5_bin(lua_State *L) { u_char *src; size_t slen; ngx_md5_t md5; u_char md5_buf[MD5_DIGEST_LENGTH]; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting one argument"); } if (lua_isnil(L, 1)) { src = (u_char *) ""; slen = 0; } else { src = (u_char *) luaL_checklstring(L, 1, &slen); } dd("slen: %d", (int) slen); ngx_md5_init(&md5); ngx_md5_update(&md5, src, slen); ngx_md5_final(md5_buf, &md5); lua_pushlstring(L, (char *) md5_buf, sizeof(md5_buf)); return 1; } #if (NGX_HAVE_SHA1) static int ngx_http_lua_ngx_sha1_bin(lua_State *L) { u_char *src; size_t slen; ngx_sha1_t sha; u_char sha_buf[SHA_DIGEST_LENGTH]; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting one argument"); } if (lua_isnil(L, 1)) { src = (u_char *) ""; slen = 0; } else { src = (u_char *) luaL_checklstring(L, 1, &slen); } dd("slen: %d", (int) slen); ngx_sha1_init(&sha); ngx_sha1_update(&sha, src, slen); ngx_sha1_final(sha_buf, &sha); lua_pushlstring(L, (char *) sha_buf, sizeof(sha_buf)); return 1; } #endif static int ngx_http_lua_ngx_decode_base64(lua_State *L) { ngx_str_t p, src; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting one argument"); } if (lua_type(L, 1) != LUA_TSTRING) { return luaL_error(L, "string argument only"); } src.data = (u_char *) luaL_checklstring(L, 1, &src.len); p.len = ngx_base64_decoded_length(src.len); p.data = lua_newuserdata(L, p.len); if (ngx_decode_base64(&p, &src) == NGX_OK) { lua_pushlstring(L, (char *) p.data, p.len); } else { lua_pushnil(L); } return 1; } static int ngx_http_lua_ngx_encode_base64(lua_State *L) { ngx_str_t p, src; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting one argument"); } if (lua_isnil(L, 1)) { src.data = (u_char *) ""; src.len = 0; } else { src.data = (u_char *) luaL_checklstring(L, 1, &src.len); } p.len = ngx_base64_encoded_length(src.len); p.data = lua_newuserdata(L, p.len); ngx_encode_base64(&p, &src); lua_pushlstring(L, (char *) p.data, p.len); return 1; } static int ngx_http_lua_ngx_crc32_short(lua_State *L) { u_char *p; size_t len; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting one argument, but got %d", lua_gettop(L)); } p = (u_char *) luaL_checklstring(L, 1, &len); lua_pushnumber(L, (lua_Number) ngx_crc32_short(p, len)); return 1; } static int ngx_http_lua_ngx_crc32_long(lua_State *L) { u_char *p; size_t len; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting one argument, but got %d", lua_gettop(L)); } p = (u_char *) luaL_checklstring(L, 1, &len); lua_pushnumber(L, (lua_Number) ngx_crc32_long(p, len)); return 1; } static int ngx_http_lua_ngx_encode_args(lua_State *L) { ngx_str_t args; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting 1 argument but seen %d", lua_gettop(L)); } luaL_checktype(L, 1, LUA_TTABLE); ngx_http_lua_process_args_option(NULL, L, 1, &args); lua_pushlstring(L, (char *) args.data, args.len); return 1; } static int ngx_http_lua_ngx_decode_args(lua_State *L) { u_char *buf; u_char *tmp; size_t len = 0; int n; int max; n = lua_gettop(L); if (n != 1 && n != 2) { return luaL_error(L, "expecting 1 or 2 arguments but seen %d", n); } buf = (u_char *) luaL_checklstring(L, 1, &len); if (n == 2) { max = luaL_checkint(L, 2); lua_pop(L, 1); } else { max = NGX_HTTP_LUA_MAX_ARGS; } tmp = lua_newuserdata(L, len); ngx_memcpy(tmp, buf, len); lua_createtable(L, 0, 4); return ngx_http_lua_parse_args(L, tmp, tmp + len, max); } #if (NGX_OPENSSL) static int ngx_http_lua_ngx_hmac_sha1(lua_State *L) { u_char *sec, *sts; size_t lsec, lsts; unsigned int md_len; unsigned char md[EVP_MAX_MD_SIZE]; const EVP_MD *evp_md; if (lua_gettop(L) != 2) { return luaL_error(L, "expecting one argument, but got %d", lua_gettop(L)); } sec = (u_char *) luaL_checklstring(L, 1, &lsec); sts = (u_char *) luaL_checklstring(L, 2, &lsts); evp_md = EVP_sha1(); HMAC(evp_md, sec, lsec, sts, lsts, md, &md_len); lua_pushlstring(L, (char *) md, md_len); return 1; } #endif #ifndef NGX_HTTP_LUA_NO_FFI_API void ngx_http_lua_ffi_md5_bin(const u_char *src, size_t len, u_char *dst) { ngx_md5_t md5; ngx_md5_init(&md5); ngx_md5_update(&md5, src, len); ngx_md5_final(dst, &md5); } void ngx_http_lua_ffi_md5(const u_char *src, size_t len, u_char *dst) { ngx_md5_t md5; u_char md5_buf[MD5_DIGEST_LENGTH]; ngx_md5_init(&md5); ngx_md5_update(&md5, src, len); ngx_md5_final(md5_buf, &md5); ngx_hex_dump(dst, md5_buf, sizeof(md5_buf)); } int ngx_http_lua_ffi_sha1_bin(const u_char *src, size_t len, u_char *dst) { #if NGX_HAVE_SHA1 ngx_sha1_t sha; ngx_sha1_init(&sha); ngx_sha1_update(&sha, src, len); ngx_sha1_final(dst, &sha); return 1; #else return 0; #endif } size_t ngx_http_lua_ffi_encode_base64(const u_char *src, size_t slen, u_char *dst) { ngx_str_t in, out; in.data = (u_char *) src; in.len = slen; out.data = dst; ngx_encode_base64(&out, &in); return out.len; } int ngx_http_lua_ffi_decode_base64(const u_char *src, size_t slen, u_char *dst, size_t *dlen) { ngx_int_t rc; ngx_str_t in, out; in.data = (u_char *) src; in.len = slen; out.data = dst; rc = ngx_decode_base64(&out, &in); *dlen = out.len; return rc == NGX_OK; } size_t ngx_http_lua_ffi_unescape_uri(const u_char *src, size_t len, u_char *dst) { u_char *p = dst; ngx_http_lua_unescape_uri(&p, (u_char **) &src, len, NGX_UNESCAPE_URI_COMPONENT); return p - dst; } size_t ngx_http_lua_ffi_uri_escaped_length(const u_char *src, size_t len) { return len + 2 * ngx_http_lua_escape_uri(NULL, (u_char *) src, len, NGX_ESCAPE_URI); } void ngx_http_lua_ffi_escape_uri(const u_char *src, size_t len, u_char *dst) { ngx_http_lua_escape_uri(dst, (u_char *) src, len, NGX_ESCAPE_URI); } #endif /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_misc.h0000664000000000000000000000053412305451334017213 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_MISC_H_INCLUDED_ #define _NGX_HTTP_LUA_MISC_H_INCLUDED_ #include "ngx_http_lua_common.h" void ngx_http_lua_inject_misc_api(lua_State *L); #endif /* _NGX_HTTP_LUA_MISC_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_util.h0000664000000000000000000002456012305451335017243 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_UTIL_H_INCLUDED_ #define _NGX_HTTP_LUA_UTIL_H_INCLUDED_ #include "ngx_http_lua_common.h" #ifndef NGX_UNESCAPE_URI_COMPONENT #define NGX_UNESCAPE_URI_COMPONENT 0 #endif #ifndef NGX_HTTP_LUA_NO_FFI_API typedef struct { int len; u_char *data; } ngx_http_lua_ffi_str_t; typedef struct { ngx_http_lua_ffi_str_t key; ngx_http_lua_ffi_str_t value; } ngx_http_lua_ffi_table_elt_t; #endif /* NGX_HTTP_LUA_NO_FFI_API */ /* char whose address we use as the key in Lua vm registry for * user code cache table */ extern char ngx_http_lua_code_cache_key; /* key in Lua vm registry for all the "ngx.ctx" tables */ #define ngx_http_lua_ctx_tables_key "ngx_lua_ctx_tables" /* char whose address we use as the key in Lua vm registry for * regex cache table */ extern char ngx_http_lua_regex_cache_key; /* char whose address we use as the key in Lua vm registry for * socket connection pool table */ extern char ngx_http_lua_socket_pool_key; /* char whose address we use as the key for the coroutine parent relationship */ extern char ngx_http_lua_coroutine_parents_key; /* coroutine anchoring table key in Lua VM registry */ extern char ngx_http_lua_coroutines_key; /* key to the metatable for ngx.req.get_headers() */ extern char ngx_http_lua_req_get_headers_metatable_key; #ifndef ngx_str_set #define ngx_str_set(str, text) \ (str)->len = sizeof(text) - 1; (str)->data = (u_char *) text #endif #if defined(nginx_version) && nginx_version < 1000000 #define ngx_memmove(dst, src, n) (void) memmove(dst, src, n) #endif #define ngx_http_lua_context_name(c) \ ((c) == NGX_HTTP_LUA_CONTEXT_SET ? "set_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_REWRITE ? "rewrite_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_ACCESS ? "access_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_CONTENT ? "content_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_LOG ? "log_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_HEADER_FILTER ? "header_filter_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_TIMER ? "ngx.timer" \ : "(unknown)") #define ngx_http_lua_check_context(L, ctx, flags) \ if (!((ctx)->context & (flags))) { \ return luaL_error(L, "API disabled in the context of %s", \ ngx_http_lua_context_name((ctx)->context)); \ } #ifndef NGX_HTTP_LUA_NO_FFI_API static ngx_inline ngx_int_t ngx_http_lua_ffi_check_context(ngx_http_lua_ctx_t *ctx, unsigned flags, u_char *err, size_t *errlen) { if (!(ctx->context & flags)) { *errlen = ngx_snprintf(err, *errlen, "API disabled in the context of %s", ngx_http_lua_context_name((ctx)->context)) - err; return NGX_DECLINED; } return NGX_OK; } #endif #define ngx_http_lua_check_fake_request(L, r) \ if ((r)->connection->fd == -1) { \ return luaL_error(L, "API disabled in the current context"); \ } #define ngx_http_lua_check_fake_request2(L, r, ctx) \ if ((r)->connection->fd == -1) { \ return luaL_error(L, "API disabled in the context of %s", \ ngx_http_lua_context_name((ctx)->context)); \ } lua_State * ngx_http_lua_init_vm(lua_State *parent_vm, ngx_cycle_t *cycle, ngx_pool_t *pool, ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log, ngx_pool_cleanup_t **pcln); lua_State * ngx_http_lua_new_thread(ngx_http_request_t *r, lua_State *l, int *ref); u_char * ngx_http_lua_rebase_path(ngx_pool_t *pool, u_char *src, size_t len); ngx_int_t ngx_http_lua_send_header_if_needed(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx); ngx_int_t ngx_http_lua_send_chain_link(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, ngx_chain_t *cl); void ngx_http_lua_discard_bufs(ngx_pool_t *pool, ngx_chain_t *in); ngx_int_t ngx_http_lua_add_copy_chain(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, ngx_chain_t ***plast, ngx_chain_t *in, ngx_int_t *eof); void ngx_http_lua_reset_ctx(ngx_http_request_t *r, lua_State *L, ngx_http_lua_ctx_t *ctx); void ngx_http_lua_generic_phase_post_read(ngx_http_request_t *r); void ngx_http_lua_request_cleanup(ngx_http_lua_ctx_t *ctx, int foricible); void ngx_http_lua_request_cleanup_handler(void *data); ngx_int_t ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, volatile int nret); ngx_int_t ngx_http_lua_wev_handler(ngx_http_request_t *r); u_char * ngx_http_lua_digest_hex(u_char *dest, const u_char *buf, int buf_len); void ngx_http_lua_set_multi_value_table(lua_State *L, int index); void ngx_http_lua_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type); uintptr_t ngx_http_lua_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type); void ngx_http_lua_inject_req_api(ngx_log_t *log, lua_State *L); void ngx_http_lua_process_args_option(ngx_http_request_t *r, lua_State *L, int table, ngx_str_t *args); ngx_int_t ngx_http_lua_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, ngx_log_t *log); ngx_chain_t * ngx_http_lua_chains_get_free_buf(ngx_log_t *log, ngx_pool_t *p, ngx_chain_t **free, size_t len, ngx_buf_tag_t tag); void ngx_http_lua_create_new_global_table(lua_State *L, int narr, int nrec); int ngx_http_lua_traceback(lua_State *L); ngx_http_lua_co_ctx_t * ngx_http_lua_get_co_ctx(lua_State *L, ngx_http_lua_ctx_t *ctx); ngx_http_lua_co_ctx_t * ngx_http_lua_create_co_ctx(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx); ngx_int_t ngx_http_lua_run_posted_threads(ngx_connection_t *c, lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx); ngx_int_t ngx_http_lua_post_thread(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t *coctx); void ngx_http_lua_del_thread(ngx_http_request_t *r, lua_State *L, ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t *coctx); void ngx_http_lua_rd_check_broken_connection(ngx_http_request_t *r); ngx_int_t ngx_http_lua_test_expect(ngx_http_request_t *r); ngx_int_t ngx_http_lua_check_broken_connection(ngx_http_request_t *r, ngx_event_t *ev); void ngx_http_lua_finalize_request(ngx_http_request_t *r, ngx_int_t rc); void ngx_http_lua_finalize_fake_request(ngx_http_request_t *r, ngx_int_t rc); void ngx_http_lua_close_fake_connection(ngx_connection_t *c); void ngx_http_lua_release_ngx_ctx_table(ngx_log_t *log, lua_State *L, ngx_http_lua_ctx_t *ctx); void ngx_http_lua_cleanup_vm(void *data); #define ngx_http_lua_check_if_abortable(L, ctx) \ if ((ctx)->no_abort) { \ return luaL_error(L, "attempt to abort with pending subrequests"); \ } static ngx_inline void ngx_http_lua_init_ctx(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) { ngx_memzero(ctx, sizeof(ngx_http_lua_ctx_t)); ctx->ctx_ref = LUA_NOREF; ctx->entry_co_ctx.co_ref = LUA_NOREF; ctx->resume_handler = ngx_http_lua_wev_handler; ctx->request = r; } static ngx_inline ngx_http_lua_ctx_t * ngx_http_lua_create_ctx(ngx_http_request_t *r) { lua_State *L; ngx_http_lua_ctx_t *ctx; ngx_pool_cleanup_t *cln; ngx_http_lua_loc_conf_t *llcf; ngx_http_lua_main_conf_t *lmcf; ctx = ngx_palloc(r->pool, sizeof(ngx_http_lua_ctx_t)); if (ctx == NULL) { return NULL; } ngx_http_lua_init_ctx(r, ctx); ngx_http_set_ctx(r, ctx, ngx_http_lua_module); llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (!llcf->enable_code_cache && r->connection->fd != -1) { lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); dd("lmcf: %p", lmcf); L = ngx_http_lua_init_vm(lmcf->lua, lmcf->cycle, r->pool, lmcf, r->connection->log, &cln); if (L == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to initialize Lua VM"); return NULL; } if (lmcf->init_handler) { if (lmcf->init_handler(r->connection->log, lmcf, L) != NGX_OK) { /* an error happened */ return NULL; } } ctx->vm_state = cln->data; } else { ctx->vm_state = NULL; } return ctx; } static ngx_inline lua_State * ngx_http_lua_get_lua_vm(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) { ngx_http_lua_main_conf_t *lmcf; if (ctx == NULL) { ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); } if (ctx && ctx->vm_state) { return ctx->vm_state->vm; } lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); dd("lmcf->lua: %p", lmcf->lua); return lmcf->lua; } static ngx_inline ngx_http_request_t * ngx_http_lua_get_req(lua_State *L) { ngx_http_request_t *r; lua_pushliteral(L, "__ngx_req"); lua_rawget(L, LUA_GLOBALSINDEX); r = lua_touserdata(L, -1); lua_pop(L, 1); return r; } static ngx_inline void ngx_http_lua_set_req(lua_State *L, ngx_http_request_t *r) { lua_pushliteral(L, "__ngx_req"); lua_pushlightuserdata(L, r); lua_rawset(L, LUA_GLOBALSINDEX); } #define ngx_http_lua_hash_literal(s) \ ngx_http_lua_hash_str((u_char *) s, sizeof(s) - 1) static ngx_inline ngx_uint_t ngx_http_lua_hash_str(u_char *src, size_t n) { ngx_uint_t key; key = 0; while (n--) { key = ngx_hash(key, *src); src++; } return key; } static ngx_inline ngx_int_t ngx_http_lua_set_content_type(ngx_http_request_t *r) { ngx_http_lua_loc_conf_t *llcf; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->use_default_type) { return ngx_http_set_content_type(r); } return NGX_OK; } extern ngx_uint_t ngx_http_lua_location_hash; extern ngx_uint_t ngx_http_lua_content_length_hash; #endif /* _NGX_HTTP_LUA_UTIL_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_subrequest.h0000664000000000000000000000245112305451335020463 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_SUBREQUEST_H_INCLUDED_ #define _NGX_HTTP_LUA_SUBREQUEST_H_INCLUDED_ #include "ngx_http_lua_common.h" void ngx_http_lua_inject_subrequest_api(lua_State *L); ngx_int_t ngx_http_lua_post_subrequest(ngx_http_request_t *r, void *data, ngx_int_t rc); extern ngx_str_t ngx_http_lua_get_method; extern ngx_str_t ngx_http_lua_put_method; extern ngx_str_t ngx_http_lua_post_method; extern ngx_str_t ngx_http_lua_head_method; extern ngx_str_t ngx_http_lua_delete_method; extern ngx_str_t ngx_http_lua_options_method; extern ngx_str_t ngx_http_lua_copy_method; extern ngx_str_t ngx_http_lua_move_method; extern ngx_str_t ngx_http_lua_lock_method; extern ngx_str_t ngx_http_lua_mkcol_method; extern ngx_str_t ngx_http_lua_propfind_method; extern ngx_str_t ngx_http_lua_proppatch_method; extern ngx_str_t ngx_http_lua_unlock_method; extern ngx_str_t ngx_http_lua_patch_method; extern ngx_str_t ngx_http_lua_trace_method; typedef struct ngx_http_lua_post_subrequest_data_s { ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *pr_co_ctx; } ngx_http_lua_post_subrequest_data_t; #endif /* _NGX_HTTP_LUA_SUBREQUEST_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_contentby.h0000664000000000000000000000141612305451335020266 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_CONTENT_BY_H_INCLUDED_ #define _NGX_HTTP_LUA_CONTENT_BY_H_INCLUDED_ #include "ngx_http_lua_common.h" ngx_int_t ngx_http_lua_content_by_chunk(lua_State *l, ngx_http_request_t *r); void ngx_http_lua_content_wev_handler(ngx_http_request_t *r); ngx_int_t ngx_http_lua_content_handler_file(ngx_http_request_t *r); ngx_int_t ngx_http_lua_content_handler_inline(ngx_http_request_t *r); ngx_int_t ngx_http_lua_content_handler(ngx_http_request_t *r); ngx_int_t ngx_http_lua_content_run_posted_threads(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, int n); #endif /* _NGX_HTTP_LUA_CONTENT_BY_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_timer.c0000664000000000000000000004000312305451335017367 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_timer.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_contentby.h" #include "ngx_http_lua_probe.h" typedef struct { unsigned premature; /* :1 */ int co_ref; lua_State *co; void **main_conf; void **srv_conf; void **loc_conf; ngx_http_lua_main_conf_t *lmcf; ngx_http_lua_vm_state_t *vm_state; } ngx_http_lua_timer_ctx_t; static int ngx_http_lua_ngx_timer_at(lua_State *L); static void ngx_http_lua_timer_handler(ngx_event_t *ev); static u_char * ngx_http_lua_log_timer_error(ngx_log_t *log, u_char *buf, size_t len); static void ngx_http_lua_abort_pending_timers(ngx_event_t *ev); void ngx_http_lua_inject_timer_api(lua_State *L) { lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* ngx.timer. */ lua_pushcfunction(L, ngx_http_lua_ngx_timer_at); lua_setfield(L, -2, "at"); lua_setfield(L, -2, "timer"); } static int ngx_http_lua_ngx_timer_at(lua_State *L) { int nargs, co_ref; u_char *p; lua_State *vm; /* the main thread */ lua_State *co; ngx_msec_t delay; ngx_event_t *ev; ngx_http_request_t *r; ngx_connection_t *saved_c = NULL; ngx_http_lua_ctx_t *ctx; #if 0 ngx_http_connection_t *hc; #endif ngx_http_lua_timer_ctx_t *tctx; ngx_http_lua_main_conf_t *lmcf; #if 0 ngx_http_core_main_conf_t *cmcf; #endif nargs = lua_gettop(L); if (nargs < 2) { return luaL_error(L, "expecting at least 2 arguments but got %d", nargs); } delay = (ngx_msec_t) (luaL_checknumber(L, 1) * 1000); luaL_argcheck(L, lua_isfunction(L, 2) && !lua_iscfunction(L, 2), 2, "Lua function expected"); r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ngx_exiting && delay > 0) { lua_pushnil(L); lua_pushliteral(L, "process exiting"); return 2; } lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); if (lmcf->pending_timers >= lmcf->max_pending_timers) { lua_pushnil(L); lua_pushliteral(L, "too many pending timers"); return 2; } if (lmcf->watcher == NULL) { /* create the watcher fake connection */ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "lua creating fake watcher connection"); if (ngx_cycle->files) { saved_c = ngx_cycle->files[0]; } lmcf->watcher = ngx_get_connection(0, ngx_cycle->log); if (ngx_cycle->files) { ngx_cycle->files[0] = saved_c; } if (lmcf->watcher == NULL) { return luaL_error(L, "no memory"); } /* to work around the -1 check in ngx_worker_process_cycle: */ lmcf->watcher->fd = (ngx_socket_t) -2; lmcf->watcher->idle = 1; lmcf->watcher->read->handler = ngx_http_lua_abort_pending_timers; lmcf->watcher->data = lmcf; } vm = ngx_http_lua_get_lua_vm(r, ctx); co = lua_newthread(vm); /* L stack: time func [args] thread */ ngx_http_lua_probe_user_coroutine_create(r, L, co); lua_createtable(co, 0, 0); /* the new global table */ /* co stack: global_tb */ lua_createtable(co, 0, 1); /* the metatable */ lua_pushvalue(co, LUA_GLOBALSINDEX); lua_setfield(co, -2, "__index"); lua_setmetatable(co, -2); /* co stack: global_tb */ lua_replace(co, LUA_GLOBALSINDEX); /* co stack: */ dd("stack top: %d", lua_gettop(L)); lua_xmove(vm, L, 1); /* move coroutine from main thread to L */ /* L stack: time func [args] thread */ /* vm stack: empty */ lua_pushvalue(L, 2); /* copy entry function to top of L*/ /* L stack: time func [args] thread func */ lua_xmove(L, co, 1); /* move entry function from L to co */ /* L stack: time func [args] thread */ /* co stack: func */ lua_pushvalue(co, LUA_GLOBALSINDEX); lua_setfenv(co, -2); /* co stack: func */ lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key); lua_rawget(L, LUA_REGISTRYINDEX); /* L stack: time func [args] thread corountines */ lua_pushvalue(L, -2); /* L stack: time func [args] thread coroutines thread */ co_ref = luaL_ref(L, -2); lua_pop(L, 1); /* L stack: time func [args] thread */ if (nargs > 2) { lua_pop(L, 1); /* L stack: time func [args] */ lua_xmove(L, co, nargs - 2); /* L stack: time func */ /* co stack: func [args] */ } p = ngx_alloc(sizeof(ngx_event_t) + sizeof(ngx_http_lua_timer_ctx_t), r->connection->log); if (p == NULL) { lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key); lua_rawget(L, LUA_REGISTRYINDEX); luaL_unref(L, -1, co_ref); return luaL_error(L, "no memory"); } ev = (ngx_event_t *) p; ngx_memzero(ev, sizeof(ngx_event_t)); p += sizeof(ngx_event_t); tctx = (ngx_http_lua_timer_ctx_t *) p; tctx->premature = 0; tctx->co_ref = co_ref; tctx->co = co; tctx->main_conf = r->main_conf; tctx->srv_conf = r->srv_conf; tctx->loc_conf = r->loc_conf; tctx->lmcf = lmcf; if (ctx && ctx->vm_state) { tctx->vm_state = ctx->vm_state; tctx->vm_state->count++; } else { tctx->vm_state = NULL; } ev->handler = ngx_http_lua_timer_handler; ev->data = tctx; ev->log = ngx_cycle->log; lmcf->pending_timers++; ngx_add_timer(ev, delay); lua_pushinteger(L, 1); return 1; } static void ngx_http_lua_timer_handler(ngx_event_t *ev) { int n; lua_State *L; ngx_int_t rc; ngx_log_t *log; ngx_connection_t *c = NULL, *saved_c = NULL; ngx_http_request_t *r = NULL; ngx_http_lua_ctx_t *ctx; ngx_http_cleanup_t *cln; ngx_pool_cleanup_t *pcln; ngx_http_log_ctx_t *logctx; ngx_http_lua_timer_ctx_t tctx; ngx_http_lua_main_conf_t *lmcf; ngx_http_core_loc_conf_t *clcf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "lua ngx.timer expired"); ngx_memcpy(&tctx, ev->data, sizeof(ngx_http_lua_timer_ctx_t)); ngx_free(ev); ev = NULL; lmcf = tctx.lmcf; lmcf->pending_timers--; if (lmcf->running_timers >= lmcf->max_running_timers) { ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "%i lua_max_running_timers are not enough", lmcf->max_running_timers); goto abort; } /* create the fake connection (we temporarily use a valid fd (0) to make ngx_get_connection happy) */ if (ngx_cycle->files) { saved_c = ngx_cycle->files[0]; } c = ngx_get_connection(0, ngx_cycle->log); if (ngx_cycle->files) { ngx_cycle->files[0] = saved_c; } if (c == NULL) { goto abort; } c->fd = (ngx_socket_t) -1; c->pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, c->log); if (c->pool == NULL) { goto abort; } log = ngx_pcalloc(c->pool, sizeof(ngx_log_t)); if (log == NULL) { goto abort; } logctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t)); if (logctx == NULL) { goto abort; } dd("c pool allocated: %d", (int) (sizeof(ngx_log_t) + sizeof(ngx_http_log_ctx_t) + sizeof(ngx_http_request_t))); logctx->connection = c; logctx->request = NULL; logctx->current_request = NULL; c->log = log; c->log->connection = c->number; c->log->handler = ngx_http_lua_log_timer_error; c->log->data = logctx; c->log->action = NULL; c->log_error = NGX_ERROR_INFO; #if 0 c->buffer = ngx_create_temp_buf(c->pool, 2); if (c->buffer == NULL) { goto abort; } c->buffer->start[0] = CR; c->buffer->start[1] = LF; #endif /* create the fake request */ r = ngx_pcalloc(c->pool, sizeof(ngx_http_request_t)); if (r == NULL) { goto abort; } c->requests++; logctx->request = r; logctx->current_request = r; r->pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, c->log); if (r->pool == NULL) { goto abort; } dd("r pool allocated: %d", (int) (sizeof(ngx_http_lua_ctx_t) + sizeof(void *) * ngx_http_max_module + sizeof(ngx_http_cleanup_t))); #if 0 hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t)); if (hc == NULL) { goto abort; } r->header_in = c->buffer; r->header_end = c->buffer->start; if (ngx_list_init(&r->headers_out.headers, r->pool, 0, sizeof(ngx_table_elt_t)) != NGX_OK) { goto abort; } if (ngx_list_init(&r->headers_in.headers, r->pool, 0, sizeof(ngx_table_elt_t)) != NGX_OK) { goto abort; } #endif r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module); if (r->ctx == NULL) { goto abort; } #if 0 cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts * sizeof(ngx_http_variable_value_t)); if (r->variables == NULL) { goto abort; } #endif r->connection = c; r->main_conf = tctx.main_conf; r->srv_conf = tctx.srv_conf; r->loc_conf = tctx.loc_conf; dd("lmcf: %p", lmcf); ctx = ngx_http_lua_create_ctx(r); if (ctx == NULL) { goto abort; } if (tctx.vm_state) { ctx->vm_state = tctx.vm_state; pcln = ngx_pool_cleanup_add(r->pool, 0); if (pcln == NULL) { goto abort; } pcln->handler = ngx_http_lua_cleanup_vm; pcln->data = tctx.vm_state; } r->headers_in.content_length_n = 0; c->data = r; #if 0 hc->request = r; r->http_connection = hc; #endif r->signature = NGX_HTTP_MODULE; r->main = r; r->count = 1; r->method = NGX_HTTP_UNKNOWN; r->headers_in.keep_alive_n = -1; r->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1; r->subrequests = NGX_HTTP_MAX_SUBREQUESTS + 1; r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE; r->discard_body = 1; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); c->log->file = clcf->error_log->file; if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { c->log->log_level = clcf->error_log->log_level; } c->error = 1; ctx->cur_co_ctx = &ctx->entry_co_ctx; L = ngx_http_lua_get_lua_vm(r, ctx); cln = ngx_http_cleanup_add(r, 0); if (cln == NULL) { goto abort; } cln->handler = ngx_http_lua_request_cleanup_handler; cln->data = ctx; ctx->cleanup = &cln->handler; ctx->entered_content_phase = 1; ctx->context = NGX_HTTP_LUA_CONTEXT_TIMER; r->read_event_handler = ngx_http_block_reading; ctx->cur_co_ctx->co_ref = tctx.co_ref; ctx->cur_co_ctx->co = tctx.co; ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_RUNNING; dd("r connection: %p, log %p", r->connection, r->connection->log); /* save the request in coroutine globals table */ ngx_http_lua_set_req(tctx.co, r); lmcf->running_timers++; lua_pushboolean(tctx.co, tctx.premature); n = lua_gettop(tctx.co); if (n > 2) { lua_insert(tctx.co, 2); } rc = ngx_http_lua_run_thread(L, r, ctx, n - 1); dd("timer lua run thread: %d", (int) rc); if (rc == NGX_ERROR || rc >= NGX_OK) { /* do nothing */ } else if (rc == NGX_AGAIN) { rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 0); } else if (rc == NGX_DONE) { rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 1); } else { rc = NGX_OK; } ngx_http_lua_finalize_request(r, rc); return; abort: if (tctx.co_ref && tctx.co) { lua_pushlightuserdata(tctx.co, &ngx_http_lua_coroutines_key); lua_rawget(tctx.co, LUA_REGISTRYINDEX); luaL_unref(tctx.co, -1, tctx.co_ref); lua_settop(tctx.co, 0); } if (tctx.vm_state) { ngx_http_lua_cleanup_vm(tctx.vm_state); } if (r && r->pool) { ngx_destroy_pool(r->pool); } if (c) { ngx_http_lua_close_fake_connection(c); } } static u_char * ngx_http_lua_log_timer_error(ngx_log_t *log, u_char *buf, size_t len) { u_char *p; if (log->action) { p = ngx_snprintf(buf, len, " while %s", log->action); len -= p - buf; buf = p; } return ngx_snprintf(buf, len, ", context: ngx.timer"); } static void ngx_http_lua_abort_pending_timers(ngx_event_t *ev) { ngx_int_t i, n; ngx_event_t **events; ngx_connection_t *c, *saved_c = NULL; ngx_rbtree_node_t *cur, *prev, *next, *sentinel; ngx_http_lua_timer_ctx_t *tctx; ngx_http_lua_main_conf_t *lmcf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "lua abort pending timers"); c = ev->data; lmcf = c->data; dd("lua connection fd: %d", (int) c->fd); if (!c->close) { return; } c->read->closed = 1; c->write->closed = 1; /* we temporarily use a valid fd (0) to make ngx_free_connection happy */ c->fd = 0; if (ngx_cycle->files) { saved_c = ngx_cycle->files[0]; } ngx_free_connection(c); c->fd = (ngx_socket_t) -1; if (ngx_cycle->files) { ngx_cycle->files[0] = saved_c; } if (lmcf->pending_timers == 0) { return; } /* expire pending timers immediately */ sentinel = ngx_event_timer_rbtree.sentinel; prev = NULL; cur = ngx_event_timer_rbtree.root; events = ngx_pcalloc(ngx_cycle->pool, lmcf->pending_timers * sizeof(ngx_event_t)); if (events == NULL) { return; } n = 0; dd("root: %p, root parent: %p, sentinel: %p", cur, cur->parent, sentinel); while (lmcf->pending_timers > n) { if (cur == sentinel || cur == NULL) { ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "lua pending timer counter got out of sync: %i", lmcf->pending_timers); break; } if (prev == cur->parent) { next = cur->left; if (next == sentinel) { ev = (ngx_event_t *) ((char *) cur - offsetof(ngx_event_t, timer)); if (ev->handler == ngx_http_lua_timer_handler) { dd("found node: %p", cur); events[n++] = ev; } next = (cur->right != sentinel) ? cur->right : cur->parent; } } else if (prev == cur->left) { ev = (ngx_event_t *) ((char *) cur - offsetof(ngx_event_t, timer)); if (ev->handler == ngx_http_lua_timer_handler) { dd("found node 2: %p", cur); events[n++] = ev; } next = (cur->right != sentinel) ? cur->right : cur->parent; } else if (prev == cur->right) { next = cur->parent; } else { next = NULL; } prev = cur; cur = next; } for (i = 0; i < n; i++) { ev = events[i]; ngx_rbtree_delete(&ngx_event_timer_rbtree, &ev->timer); #if (NGX_DEBUG) ev->timer.left = NULL; ev->timer.right = NULL; ev->timer.parent = NULL; #endif ev->timer_set = 0; ev->timedout = 1; tctx = ev->data; tctx->premature = 1; ev->handler(ev); } #if 0 if (pending_timers) { ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "lua pending timer counter got out of sync: %i", pending_timers); } #endif } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_phase.h0000664000000000000000000000040212305451335017353 0ustar #ifndef _NGX_HTTP_LUA_PHASE_H_INCLUDED_ #define _NGX_HTTP_LUA_PHASE_H_INCLUDED_ #include "ngx_http_lua_common.h" void ngx_http_lua_inject_phase_api(lua_State *L); #endif /* _NGX_HTTP_LUA_PHASE_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_sleep.h0000664000000000000000000000054012305451335017366 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_SLEEP_H_INCLUDED_ #define _NGX_HTTP_LUA_SLEEP_H_INCLUDED_ #include "ngx_http_lua_common.h" void ngx_http_lua_inject_sleep_api(lua_State *L); #endif /* _NGX_HTTP_LUA_SLEEP_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_probe.h0000664000000000000000000000665512305451334017401 0ustar /* * automatically generated from the file dtrace/ngx_lua_provider.d by the * gen-dtrace-probe-header tool in the nginx-devel-utils project: * https://github.com/agentzh/nginx-devel-utils */ #ifndef _NGX_HTTP_LUA_PROBE_H_INCLUDED_ #define _NGX_HTTP_LUA_PROBE_H_INCLUDED_ #include #include #include #if defined(NGX_DTRACE) && NGX_DTRACE #include #define ngx_http_lua_probe_info(s) \ NGINX_LUA_HTTP_LUA_INFO(s) #define ngx_http_lua_probe_register_preload_package(L, pkg) \ NGINX_LUA_HTTP_LUA_REGISTER_PRELOAD_PACKAGE(L, pkg) #define ngx_http_lua_probe_req_socket_consume_preread(r, data, len) \ NGINX_LUA_HTTP_LUA_REQ_SOCKET_CONSUME_PREREAD(r, data, len) #define ngx_http_lua_probe_user_coroutine_create(r, parent, child) \ NGINX_LUA_HTTP_LUA_USER_COROUTINE_CREATE(r, parent, child) #define ngx_http_lua_probe_user_coroutine_resume(r, parent, child) \ NGINX_LUA_HTTP_LUA_USER_COROUTINE_RESUME(r, parent, child) #define ngx_http_lua_probe_user_coroutine_yield(r, parent, child) \ NGINX_LUA_HTTP_LUA_USER_COROUTINE_YIELD(r, parent, child) #define ngx_http_lua_probe_thread_yield(r, L) \ NGINX_LUA_HTTP_LUA_THREAD_YIELD(r, L) #define ngx_http_lua_probe_socket_tcp_send_start(r, u, data, len) \ NGINX_LUA_HTTP_LUA_SOCKET_TCP_SEND_START(r, u, data, len) #define ngx_http_lua_probe_socket_tcp_receive_done(r, u, data, len) \ NGINX_LUA_HTTP_LUA_SOCKET_TCP_RECEIVE_DONE(r, u, data, len) #define ngx_http_lua_probe_socket_tcp_setkeepalive_buf_unread(r, u, data, len)\ NGINX_LUA_HTTP_LUA_SOCKET_TCP_SETKEEPALIVE_BUF_UNREAD(r, u, data, len) #define ngx_http_lua_probe_user_thread_spawn(r, creator, newthread) \ NGINX_LUA_HTTP_LUA_USER_THREAD_SPAWN(r, creator, newthread) #define ngx_http_lua_probe_thread_delete(r, thread, ctx) \ NGINX_LUA_HTTP_LUA_THREAD_DELETE(r, thread, ctx) #define ngx_http_lua_probe_run_posted_thread(r, thread, status) \ NGINX_LUA_HTTP_LUA_RUN_POSTED_THREAD(r, thread, status) #define ngx_http_lua_probe_coroutine_done(r, co, success) \ NGINX_LUA_HTTP_LUA_COROUTINE_DONE(r, co, success) #define ngx_http_lua_probe_user_thread_wait(parent, child) \ NGINX_LUA_HTTP_LUA_USER_THREAD_WAIT(parent, child) #else /* !(NGX_DTRACE) */ #define ngx_http_lua_probe_info(s) #define ngx_http_lua_probe_register_preload_package(L, pkg) #define ngx_http_lua_probe_req_socket_consume_preread(r, data, len) #define ngx_http_lua_probe_user_coroutine_create(r, parent, child) #define ngx_http_lua_probe_user_coroutine_resume(r, parent, child) #define ngx_http_lua_probe_user_coroutine_yield(r, parent, child) #define ngx_http_lua_probe_thread_yield(r, L) #define ngx_http_lua_probe_socket_tcp_send_start(r, u, data, len) #define ngx_http_lua_probe_socket_tcp_receive_done(r, u, data, len) #define ngx_http_lua_probe_socket_tcp_setkeepalive_buf_unread(r, u, data, len) #define ngx_http_lua_probe_user_thread_spawn(r, creator, newthread) #define ngx_http_lua_probe_thread_delete(r, thread, ctx) #define ngx_http_lua_probe_run_posted_thread(r, thread, status) #define ngx_http_lua_probe_coroutine_done(r, co, success) #define ngx_http_lua_probe_user_thread_wait(parent, child) #endif #endif /* _NGX_HTTP_LUA_PROBE_H_INCLUDED_ */ debian/modules/nginx-lua/src/ngx_http_lua_accessby.h0000664000000000000000000000077512305451335020064 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_ACCESSBY_H_INCLUDED_ #define _NGX_HTTP_LUA_ACCESSBY_H_INCLUDED_ #include "ngx_http_lua_common.h" ngx_int_t ngx_http_lua_access_handler(ngx_http_request_t *r); ngx_int_t ngx_http_lua_access_handler_inline(ngx_http_request_t *r); ngx_int_t ngx_http_lua_access_handler_file(ngx_http_request_t *r); #endif /* _NGX_HTTP_LUA_ACCESSBY_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_headers_out.c0000664000000000000000000003562412305451335020566 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include #include "ngx_http_lua_headers_out.h" #include "ngx_http_lua_util.h" #include static ngx_int_t ngx_http_set_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_header_helper(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value, ngx_table_elt_t **output_header, unsigned no_create); static ngx_int_t ngx_http_set_builtin_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_builtin_multi_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_last_modified_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_content_length_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_content_type_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_clear_builtin_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_clear_last_modified_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_clear_content_length_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value); static ngx_http_lua_set_header_t ngx_http_lua_set_handlers[] = { { ngx_string("Server"), offsetof(ngx_http_headers_out_t, server), ngx_http_set_builtin_header }, { ngx_string("Date"), offsetof(ngx_http_headers_out_t, date), ngx_http_set_builtin_header }, #if 1 { ngx_string("Content-Encoding"), offsetof(ngx_http_headers_out_t, content_encoding), ngx_http_set_builtin_header }, #endif { ngx_string("Location"), offsetof(ngx_http_headers_out_t, location), ngx_http_set_builtin_header }, { ngx_string("Refresh"), offsetof(ngx_http_headers_out_t, refresh), ngx_http_set_builtin_header }, { ngx_string("Last-Modified"), offsetof(ngx_http_headers_out_t, last_modified), ngx_http_set_last_modified_header }, { ngx_string("Content-Range"), offsetof(ngx_http_headers_out_t, content_range), ngx_http_set_builtin_header }, { ngx_string("Accept-Ranges"), offsetof(ngx_http_headers_out_t, accept_ranges), ngx_http_set_builtin_header }, { ngx_string("WWW-Authenticate"), offsetof(ngx_http_headers_out_t, www_authenticate), ngx_http_set_builtin_header }, { ngx_string("Expires"), offsetof(ngx_http_headers_out_t, expires), ngx_http_set_builtin_header }, { ngx_string("E-Tag"), offsetof(ngx_http_headers_out_t, etag), ngx_http_set_builtin_header }, { ngx_string("ETag"), offsetof(ngx_http_headers_out_t, etag), ngx_http_set_builtin_header }, { ngx_string("Content-Length"), offsetof(ngx_http_headers_out_t, content_length), ngx_http_set_content_length_header }, { ngx_string("Content-Type"), offsetof(ngx_http_headers_out_t, content_type), ngx_http_set_content_type_header }, { ngx_string("Cache-Control"), offsetof(ngx_http_headers_out_t, cache_control), ngx_http_set_builtin_multi_header }, { ngx_null_string, 0, ngx_http_set_header } }; /* request time implementation */ static ngx_int_t ngx_http_set_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value) { return ngx_http_set_header_helper(r, hv, value, NULL, 0); } static ngx_int_t ngx_http_set_header_helper(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value, ngx_table_elt_t **output_header, unsigned no_create) { ngx_table_elt_t *h; ngx_list_part_t *part; ngx_uint_t i; unsigned matched = 0; if (hv->no_override) { goto new_header; } #if 1 if (r->headers_out.location && r->headers_out.location->value.len && r->headers_out.location->value.data[0] == '/') { /* XXX ngx_http_core_find_config_phase, for example, * may not initialize the "key" and "hash" fields * for a nasty optimization purpose, and * we have to work-around it here */ r->headers_out.location->hash = ngx_http_lua_location_hash; ngx_str_set(&r->headers_out.location->key, "Location"); } #endif part = &r->headers_out.headers.part; h = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; h = part->elts; i = 0; } if (h[i].hash != 0 && h[i].key.len == hv->key.len && ngx_strncasecmp(hv->key.data, h[i].key.data, h[i].key.len) == 0) { dd("found out header %.*s", (int) h[i].key.len, h[i].key.data); if (value->len == 0 || matched) { dd("clearing normal header for %.*s", (int) hv->key.len, hv->key.data); h[i].value.len = 0; h[i].hash = 0; } else { dd("setting header to value %.*s", (int) value->len, value->data); h[i].value = *value; h[i].hash = hv->hash; } if (output_header) { *output_header = &h[i]; } /* return NGX_OK; */ matched = 1; } } if (matched){ return NGX_OK; } if (no_create && value->len == 0) { return NGX_OK; } new_header: /* XXX we still need to create header slot even if the value * is empty because some builtin headers like Last-Modified * relies on this to get cleared */ h = ngx_list_push(&r->headers_out.headers); if (h == NULL) { return NGX_ERROR; } if (value->len == 0) { h->hash = 0; } else { h->hash = hv->hash; } h->key = hv->key; h->value = *value; h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == NULL) { return NGX_ERROR; } ngx_strlow(h->lowcase_key, h->key.data, h->key.len); if (output_header) { *output_header = h; } return NGX_OK; } static ngx_int_t ngx_http_set_builtin_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value) { ngx_table_elt_t *h, **old; if (hv->offset) { old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset); } else { old = NULL; } if (old == NULL || *old == NULL) { return ngx_http_set_header_helper(r, hv, value, old, 0); } h = *old; if (value->len == 0) { dd("clearing the builtin header"); h->hash = 0; h->value = *value; return NGX_OK; } h->hash = hv->hash; h->key = hv->key; h->value = *value; return NGX_OK; } static ngx_int_t ngx_http_set_builtin_multi_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value) { ngx_array_t *pa; ngx_table_elt_t *ho, **ph; ngx_uint_t i; pa = (ngx_array_t *) ((char *) &r->headers_out + hv->offset); if (pa->elts == NULL) { if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK) { return NGX_ERROR; } } if (hv->no_override) { ph = pa->elts; for (i = 0; i < pa->nelts; i++) { if (!ph[i]->hash) { ph[i]->value = *value; ph[i]->hash = hv->hash; return NGX_OK; } } goto create; } /* override old values (if any) */ if (pa->nelts > 0) { ph = pa->elts; for (i = 1; i < pa->nelts; i++) { ph[i]->hash = 0; ph[i]->value.len = 0; } ph[0]->value = *value; if (value->len == 0) { ph[0]->hash = 0; } else { ph[0]->hash = hv->hash; } return NGX_OK; } create: ph = ngx_array_push(pa); if (ph == NULL) { return NGX_ERROR; } ho = ngx_list_push(&r->headers_out.headers); if (ho == NULL) { return NGX_ERROR; } ho->value = *value; if (value->len == 0) { ho->hash = 0; } else { ho->hash = hv->hash; } ho->key = hv->key; *ph = ho; return NGX_OK; } static ngx_int_t ngx_http_set_content_type_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value) { r->headers_out.content_type_len = value->len; r->headers_out.content_type = *value; r->headers_out.content_type_hash = hv->hash; r->headers_out.content_type_lowcase = NULL; value->len = 0; return ngx_http_set_header_helper(r, hv, value, NULL, 1); } static ngx_int_t ngx_http_set_last_modified_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value) { if (value->len == 0) { return ngx_http_clear_last_modified_header(r, hv, value); } r->headers_out.last_modified_time = ngx_http_parse_time(value->data, value->len); dd("last modified time: %d", (int) r->headers_out.last_modified_time); return ngx_http_set_builtin_header(r, hv, value); } static ngx_int_t ngx_http_clear_last_modified_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value) { r->headers_out.last_modified_time = -1; return ngx_http_clear_builtin_header(r, hv, value); } static ngx_int_t ngx_http_set_content_length_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value) { off_t len; if (value->len == 0) { return ngx_http_clear_content_length_header(r, hv, value); } len = ngx_atosz(value->data, value->len); if (len == NGX_ERROR) { return NGX_ERROR; } r->headers_out.content_length_n = len; return ngx_http_set_builtin_header(r, hv, value); } static ngx_int_t ngx_http_clear_content_length_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value) { r->headers_out.content_length_n = -1; return ngx_http_clear_builtin_header(r, hv, value); } static ngx_int_t ngx_http_clear_builtin_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value) { value->len = 0; return ngx_http_set_builtin_header(r, hv, value); } ngx_int_t ngx_http_lua_set_output_header(ngx_http_request_t *r, ngx_str_t key, ngx_str_t value, unsigned override) { ngx_http_lua_header_val_t hv; ngx_http_lua_set_header_t *handlers = ngx_http_lua_set_handlers; ngx_uint_t i; dd("set header value: %.*s", (int) value.len, value.data); hv.hash = ngx_hash_key_lc(key.data, key.len); hv.key = key; hv.offset = 0; hv.no_override = !override; hv.handler = NULL; for (i = 0; handlers[i].name.len; i++) { if (hv.key.len != handlers[i].name.len || ngx_strncasecmp(hv.key.data, handlers[i].name.data, handlers[i].name.len) != 0) { dd("hv key comparison: %s <> %s", handlers[i].name.data, hv.key.data); continue; } dd("Matched handler: %s %s", handlers[i].name.data, hv.key.data); hv.offset = handlers[i].offset; hv.handler = handlers[i].handler; break; } if (handlers[i].name.len == 0 && handlers[i].handler) { hv.offset = handlers[i].offset; hv.handler = handlers[i].handler; } #if 1 if (hv.handler == NULL) { return NGX_ERROR; } #endif return hv.handler(r, &hv, &value); } int ngx_http_lua_get_output_header(lua_State *L, ngx_http_request_t *r, ngx_str_t *key) { ngx_table_elt_t *h; ngx_list_part_t *part; ngx_uint_t i; unsigned found; dd("looking for response header \"%.*s\"", (int) key->len, key->data); switch (key->len) { case 14: if (r->headers_out.content_length == NULL && r->headers_out.content_length_n >= 0 && ngx_strncasecmp(key->data, (u_char *) "Content-Length", 14) == 0) { lua_pushinteger(L, (lua_Integer) r->headers_out.content_length_n); return 1; } break; case 12: if (r->headers_out.content_type.len && ngx_strncasecmp(key->data, (u_char *) "Content-Type", 12) == 0) { lua_pushlstring(L, (char *) r->headers_out.content_type.data, r->headers_out.content_type.len); return 1; } break; default: break; } dd("not a built-in output header"); found = 0; #if 1 if (r->headers_out.location && r->headers_out.location->value.len && r->headers_out.location->value.data[0] == '/') { /* XXX ngx_http_core_find_config_phase, for example, * may not initialize the "key" and "hash" fields * for a nasty optimization purpose, and * we have to work-around it here */ r->headers_out.location->hash = ngx_http_lua_location_hash; ngx_str_set(&r->headers_out.location->key, "Location"); } #endif part = &r->headers_out.headers.part; h = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; h = part->elts; i = 0; } if (h[i].hash == 0) { continue; } if (h[i].hash != 0 && h[i].key.len == key->len && ngx_strncasecmp(key->data, h[i].key.data, h[i].key.len) == 0) { if (!found) { found = 1; lua_pushlstring(L, (char *) h[i].value.data, h[i].value.len); continue; } if (found == 1) { lua_createtable(L, 4 /* narr */, 0); lua_insert(L, -2); lua_rawseti(L, -2, found); } found++; lua_pushlstring(L, (char *) h[i].value.data, h[i].value.len); lua_rawseti(L, -2, found); } } if (found) { return 1; } lua_pushnil(L); return 1; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_headerfilterby.h0000664000000000000000000000120512305451335021246 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_HEADERFILTERBY_H_INCLUDED_ #define _NGX_HTTP_LUA_HEADERFILTERBY_H_INCLUDED_ #include "ngx_http_lua_common.h" extern ngx_http_output_header_filter_pt ngx_http_lua_next_filter_header_filter; ngx_int_t ngx_http_lua_header_filter_init(void); ngx_int_t ngx_http_lua_header_filter_by_chunk(lua_State *L, ngx_http_request_t *r); ngx_int_t ngx_http_lua_header_filter_inline(ngx_http_request_t *r); ngx_int_t ngx_http_lua_header_filter_file(ngx_http_request_t *r); #endif /* _NGX_HTTP_LUA_HEADERFILTERBY_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_subrequest.c0000664000000000000000000013662012305451334020463 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_subrequest.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_ctx.h" #include "ngx_http_lua_contentby.h" #include "ngx_http_lua_headers_in.h" #if defined(NGX_DTRACE) && NGX_DTRACE #include "ngx_http_probe.h" #endif #define NGX_HTTP_LUA_SHARE_ALL_VARS 0x01 #define NGX_HTTP_LUA_COPY_ALL_VARS 0x02 #define ngx_http_lua_method_name(m) { sizeof(m) - 1, (u_char *) m " " } ngx_str_t ngx_http_lua_get_method = ngx_http_lua_method_name("GET"); ngx_str_t ngx_http_lua_put_method = ngx_http_lua_method_name("PUT"); ngx_str_t ngx_http_lua_post_method = ngx_http_lua_method_name("POST"); ngx_str_t ngx_http_lua_head_method = ngx_http_lua_method_name("HEAD"); ngx_str_t ngx_http_lua_delete_method = ngx_http_lua_method_name("DELETE"); ngx_str_t ngx_http_lua_options_method = ngx_http_lua_method_name("OPTIONS"); ngx_str_t ngx_http_lua_copy_method = ngx_http_lua_method_name("COPY"); ngx_str_t ngx_http_lua_move_method = ngx_http_lua_method_name("MOVE"); ngx_str_t ngx_http_lua_lock_method = ngx_http_lua_method_name("LOCK"); ngx_str_t ngx_http_lua_mkcol_method = ngx_http_lua_method_name("MKCOL"); ngx_str_t ngx_http_lua_propfind_method = ngx_http_lua_method_name("PROPFIND"); ngx_str_t ngx_http_lua_proppatch_method = ngx_http_lua_method_name("PROPPATCH"); ngx_str_t ngx_http_lua_unlock_method = ngx_http_lua_method_name("UNLOCK"); ngx_str_t ngx_http_lua_patch_method = ngx_http_lua_method_name("PATCH"); ngx_str_t ngx_http_lua_trace_method = ngx_http_lua_method_name("TRACE"); static ngx_str_t ngx_http_lua_content_length_header_key = ngx_string("Content-Length"); static ngx_int_t ngx_http_lua_set_content_length_header(ngx_http_request_t *r, off_t len); static ngx_int_t ngx_http_lua_adjust_subrequest(ngx_http_request_t *sr, ngx_uint_t method, int forward_body, ngx_http_request_body_t *body, unsigned vars_action, ngx_array_t *extra_vars); static int ngx_http_lua_ngx_location_capture(lua_State *L); static int ngx_http_lua_ngx_location_capture_multi(lua_State *L); static void ngx_http_lua_process_vars_option(ngx_http_request_t *r, lua_State *L, int table, ngx_array_t **varsp); static ngx_int_t ngx_http_lua_subrequest_add_extra_vars(ngx_http_request_t *r, ngx_array_t *extra_vars); static ngx_int_t ngx_http_lua_subrequest(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr, ngx_http_post_subrequest_t *ps, ngx_uint_t flags); static ngx_int_t ngx_http_lua_subrequest_resume(ngx_http_request_t *r); static void ngx_http_lua_handle_subreq_responses(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx); static void ngx_http_lua_cancel_subreq(ngx_http_request_t *r); static ngx_int_t ngx_http_post_request_to_head(ngx_http_request_t *r); static ngx_int_t ngx_http_lua_copy_in_file_request_body(ngx_http_request_t *r); static ngx_int_t ngx_http_lua_copy_request_headers(ngx_http_request_t *sr, ngx_http_request_t *r); /* ngx.location.capture is just a thin wrapper around * ngx.location.capture_multi */ static int ngx_http_lua_ngx_location_capture(lua_State *L) { int n; n = lua_gettop(L); if (n != 1 && n != 2) { return luaL_error(L, "expecting one or two arguments"); } lua_createtable(L, n, 0); /* uri opts? table */ lua_insert(L, 1); /* table uri opts? */ if (n == 1) { /* table uri */ lua_rawseti(L, 1, 1); /* table */ } else { /* table uri opts */ lua_rawseti(L, 1, 2); /* table uri */ lua_rawseti(L, 1, 1); /* table */ } lua_createtable(L, 1, 0); /* table table' */ lua_insert(L, 1); /* table' table */ lua_rawseti(L, 1, 1); /* table' */ return ngx_http_lua_ngx_location_capture_multi(L); } static int ngx_http_lua_ngx_location_capture_multi(lua_State *L) { ngx_http_request_t *r; ngx_http_request_t *sr; /* subrequest object */ ngx_http_post_subrequest_t *psr; ngx_http_lua_ctx_t *sr_ctx; ngx_http_lua_ctx_t *ctx; ngx_array_t *extra_vars; ngx_str_t uri; ngx_str_t args; ngx_str_t extra_args; ngx_uint_t flags; u_char *p; u_char *q; size_t len; size_t nargs; int rc; int n; int always_forward_body = 0; ngx_uint_t method; ngx_http_request_body_t *body; int type; ngx_buf_t *b; unsigned vars_action; ngx_uint_t nsubreqs; ngx_uint_t index; size_t sr_statuses_len; size_t sr_headers_len; size_t sr_bodies_len; size_t sr_flags_len; unsigned custom_ctx; ngx_http_lua_co_ctx_t *coctx; ngx_http_lua_post_subrequest_data_t *psr_data; n = lua_gettop(L); if (n != 1) { return luaL_error(L, "only one argument is expected, but got %d", n); } luaL_checktype(L, 1, LUA_TTABLE); nsubreqs = lua_objlen(L, 1); if (nsubreqs == 0) { return luaL_error(L, "at least one subrequest should be specified"); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } #if (NGX_HTTP_SPDY) if (r->spdy_stream) { return luaL_error(L, "spdy not supported yet"); } #endif ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); coctx = ctx->cur_co_ctx; if (coctx == NULL) { return luaL_error(L, "no co ctx found"); } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua location capture, uri:\"%V\" c:%ud", &r->uri, r->main->count); sr_statuses_len = nsubreqs * sizeof(ngx_int_t); sr_headers_len = nsubreqs * sizeof(ngx_http_headers_out_t *); sr_bodies_len = nsubreqs * sizeof(ngx_str_t); sr_flags_len = nsubreqs * sizeof(uint8_t); p = ngx_pcalloc(r->pool, sr_statuses_len + sr_headers_len + sr_bodies_len + sr_flags_len); if (p == NULL) { return luaL_error(L, "out of memory"); } coctx->sr_statuses = (void *) p; p += sr_statuses_len; coctx->sr_headers = (void *) p; p += sr_headers_len; coctx->sr_bodies = (void *) p; p += sr_bodies_len; coctx->sr_flags = (void *) p; coctx->nsubreqs = nsubreqs; coctx->pending_subreqs = 0; extra_vars = NULL; for (index = 0; index < nsubreqs; index++) { coctx->pending_subreqs++; lua_rawgeti(L, 1, index + 1); if (lua_isnil(L, -1)) { return luaL_error(L, "only array-like tables are allowed"); } dd("queries query: top %d", lua_gettop(L)); if (lua_type(L, -1) != LUA_TTABLE) { return luaL_error(L, "the query argument %d is not a table, " "but a %s", index, lua_typename(L, lua_type(L, -1))); } nargs = lua_objlen(L, -1); if (nargs != 1 && nargs != 2) { return luaL_error(L, "query argument %d expecting one or " "two arguments", index); } lua_rawgeti(L, 2, 1); /* queries query uri */ dd("queries query uri: %d", lua_gettop(L)); dd("first arg in first query: %s", lua_typename(L, lua_type(L, -1))); body = NULL; extra_args.data = NULL; extra_args.len = 0; if (extra_vars != NULL) { /* flush out existing elements in the array */ extra_vars->nelts = 0; } vars_action = 0; custom_ctx = 0; if (nargs == 2) { /* check out the options table */ lua_rawgeti(L, 2, 2); /* queries query uri opts */ dd("queries query uri opts: %d", lua_gettop(L)); if (lua_type(L, 4) != LUA_TTABLE) { return luaL_error(L, "expecting table as the 2nd argument for " "subrequest %d, but got %s", index, luaL_typename(L, 4)); } dd("queries query uri opts: %d", lua_gettop(L)); /* check the args option */ lua_getfield(L, 4, "args"); type = lua_type(L, -1); switch (type) { case LUA_TTABLE: ngx_http_lua_process_args_option(r, L, -1, &extra_args); break; case LUA_TNIL: /* do nothing */ break; case LUA_TNUMBER: case LUA_TSTRING: extra_args.data = (u_char *) lua_tolstring(L, -1, &len); extra_args.len = len; break; default: return luaL_error(L, "Bad args option value"); } lua_pop(L, 1); dd("queries query uri opts: %d", lua_gettop(L)); /* check the vars option */ lua_getfield(L, 4, "vars"); switch (lua_type(L, -1)) { case LUA_TTABLE: ngx_http_lua_process_vars_option(r, L, -1, &extra_vars); dd("post process vars top: %d", lua_gettop(L)); break; case LUA_TNIL: /* do nothing */ break; default: return luaL_error(L, "Bad vars option value"); } lua_pop(L, 1); dd("queries query uri opts: %d", lua_gettop(L)); /* check the share_all_vars option */ lua_getfield(L, 4, "share_all_vars"); switch (lua_type(L, -1)) { case LUA_TNIL: /* do nothing */ break; case LUA_TBOOLEAN: if (lua_toboolean(L, -1)) { vars_action |= NGX_HTTP_LUA_SHARE_ALL_VARS; } break; default: return luaL_error(L, "Bad share_all_vars option value"); } lua_pop(L, 1); dd("queries query uri opts: %d", lua_gettop(L)); /* check the copy_all_vars option */ lua_getfield(L, 4, "copy_all_vars"); switch (lua_type(L, -1)) { case LUA_TNIL: /* do nothing */ break; case LUA_TBOOLEAN: if (lua_toboolean(L, -1)) { vars_action |= NGX_HTTP_LUA_COPY_ALL_VARS; } break; default: return luaL_error(L, "Bad copy_all_vars option value"); } lua_pop(L, 1); dd("queries query uri opts: %d", lua_gettop(L)); /* check the "forward_body" option */ lua_getfield(L, 4, "always_forward_body"); always_forward_body = lua_toboolean(L, -1); lua_pop(L, 1); dd("always foward body: %d", always_forward_body); /* check the "method" option */ lua_getfield(L, 4, "method"); type = lua_type(L, -1); if (type == LUA_TNIL) { method = NGX_HTTP_GET; } else { if (type != LUA_TNUMBER) { return luaL_error(L, "Bad http request method"); } method = (ngx_uint_t) lua_tonumber(L, -1); } lua_pop(L, 1); dd("queries query uri opts: %d", lua_gettop(L)); /* check the "ctx" option */ lua_getfield(L, 4, "ctx"); type = lua_type(L, -1); if (type != LUA_TNIL) { if (type != LUA_TTABLE) { return luaL_error(L, "Bad ctx option value type %s, " "expected a Lua table", lua_typename(L, type)); } custom_ctx = 1; } else { lua_pop(L, 1); } dd("queries query uri opts ctx?: %d", lua_gettop(L)); /* check the "body" option */ lua_getfield(L, 4, "body"); type = lua_type(L, -1); if (type != LUA_TNIL) { if (type != LUA_TSTRING && type != LUA_TNUMBER) { return luaL_error(L, "Bad http request body"); } body = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); if (body == NULL) { return luaL_error(L, "out of memory"); } q = (u_char *) lua_tolstring(L, -1, &len); dd("request body: [%.*s]", (int) len, q); if (len) { b = ngx_create_temp_buf(r->pool, len); if (b == NULL) { return luaL_error(L, "out of memory"); } b->last = ngx_copy(b->last, q, len); body->bufs = ngx_alloc_chain_link(r->pool); if (body->bufs == NULL) { return luaL_error(L, "out of memory"); } body->bufs->buf = b; body->bufs->next = NULL; body->buf = b; } } lua_pop(L, 1); /* pop the body */ /* stack: queries query uri opts ctx? */ lua_remove(L, 4); /* stack: queries query uri ctx? */ dd("queries query uri ctx?: %d", lua_gettop(L)); } else { method = NGX_HTTP_GET; } /* stack: queries query uri ctx? */ p = (u_char *) luaL_checklstring(L, 3, &len); uri.data = ngx_palloc(r->pool, len); if (uri.data == NULL) { return luaL_error(L, "memory allocation error"); } ngx_memcpy(uri.data, p, len); uri.len = len; args.data = NULL; args.len = 0; flags = 0; rc = ngx_http_parse_unsafe_uri(r, &uri, &args, &flags); if (rc != NGX_OK) { dd("rc = %d", (int) rc); return luaL_error(L, "unsafe uri in argument #1: %s", p); } if (args.len == 0) { if (extra_args.len) { p = ngx_palloc(r->pool, extra_args.len); if (p == NULL) { return luaL_error(L, "out of memory"); } ngx_memcpy(p, extra_args.data, extra_args.len); args.data = p; args.len = extra_args.len; } } else if (extra_args.len) { /* concatenate the two parts of args together */ len = args.len + (sizeof("&") - 1) + extra_args.len; p = ngx_palloc(r->pool, len); if (p == NULL) { return luaL_error(L, "out of memory"); } q = ngx_copy(p, args.data, args.len); *q++ = '&'; ngx_memcpy(q, extra_args.data, extra_args.len); args.data = p; args.len = len; } p = ngx_pnalloc(r->pool, sizeof(ngx_http_post_subrequest_t) + sizeof(ngx_http_lua_ctx_t) + sizeof(ngx_http_lua_post_subrequest_data_t)); if (p == NULL) { return luaL_error(L, "out of memory"); } psr = (ngx_http_post_subrequest_t *) p; p += sizeof(ngx_http_post_subrequest_t); sr_ctx = (ngx_http_lua_ctx_t *) p; p += sizeof(ngx_http_lua_ctx_t); psr_data = (ngx_http_lua_post_subrequest_data_t *) p; ngx_memzero(sr_ctx, sizeof(ngx_http_lua_ctx_t)); /* set by ngx_memzero: * sr_ctx->run_post_subrequest = 0 * sr_ctx->free = NULL * sr_ctx->body = NULL */ psr_data->ctx = sr_ctx; psr_data->pr_co_ctx = coctx; psr->handler = ngx_http_lua_post_subrequest; psr->data = psr_data; rc = ngx_http_lua_subrequest(r, &uri, &args, &sr, psr, 0); if (rc != NGX_OK) { return luaL_error(L, "failed to issue subrequest: %d", (int) rc); } ngx_http_lua_init_ctx(sr, sr_ctx); sr_ctx->capture = 1; sr_ctx->index = index; sr_ctx->last_body = &sr_ctx->body; sr_ctx->vm_state = ctx->vm_state; ngx_http_set_ctx(sr, sr_ctx, ngx_http_lua_module); rc = ngx_http_lua_adjust_subrequest(sr, method, always_forward_body, body, vars_action, extra_vars); if (rc != NGX_OK) { ngx_http_lua_cancel_subreq(sr); return luaL_error(L, "failed to adjust the subrequest: %d", (int) rc); } dd("queries query uri opts ctx? %d", lua_gettop(L)); /* stack: queries query uri ctx? */ if (custom_ctx) { ngx_http_lua_ngx_set_ctx_helper(L, sr, sr_ctx, -1); lua_pop(L, 3); } else { lua_pop(L, 2); } /* stack: queries */ } if (extra_vars) { ngx_array_destroy(extra_vars); } ctx->no_abort = 1; return lua_yield(L, 0); } static ngx_int_t ngx_http_lua_adjust_subrequest(ngx_http_request_t *sr, ngx_uint_t method, int always_forward_body, ngx_http_request_body_t *body, unsigned vars_action, ngx_array_t *extra_vars) { ngx_http_request_t *r; ngx_int_t rc; ngx_http_core_main_conf_t *cmcf; size_t size; r = sr->parent; sr->header_in = r->header_in; if (body) { sr->request_body = body; rc = ngx_http_lua_set_content_length_header(sr, body->buf ? ngx_buf_size(body->buf) : 0); if (rc != NGX_OK) { return NGX_ERROR; } } else if (!always_forward_body && method != NGX_HTTP_PUT && method != NGX_HTTP_POST && r->headers_in.content_length_n > 0) { rc = ngx_http_lua_set_content_length_header(sr, 0); if (rc != NGX_OK) { return NGX_ERROR; } #if 1 sr->request_body = NULL; #endif } else { if (ngx_http_lua_copy_request_headers(sr, r) != NGX_OK) { return NGX_ERROR; } if (sr->request_body) { /* deep-copy the request body */ if (sr->request_body->temp_file) { if (ngx_http_lua_copy_in_file_request_body(sr) != NGX_OK) { return NGX_ERROR; } } } } sr->method = method; switch (method) { case NGX_HTTP_GET: sr->method_name = ngx_http_lua_get_method; break; case NGX_HTTP_POST: sr->method_name = ngx_http_lua_post_method; break; case NGX_HTTP_PUT: sr->method_name = ngx_http_lua_put_method; break; case NGX_HTTP_HEAD: sr->method_name = ngx_http_lua_head_method; break; case NGX_HTTP_DELETE: sr->method_name = ngx_http_lua_delete_method; break; case NGX_HTTP_OPTIONS: sr->method_name = ngx_http_lua_options_method; break; case NGX_HTTP_MKCOL: sr->method_name = ngx_http_lua_mkcol_method; break; case NGX_HTTP_COPY: sr->method_name = ngx_http_lua_copy_method; break; case NGX_HTTP_MOVE: sr->method_name = ngx_http_lua_move_method; break; case NGX_HTTP_PROPFIND: sr->method_name = ngx_http_lua_propfind_method; break; case NGX_HTTP_PROPPATCH: sr->method_name = ngx_http_lua_proppatch_method; break; case NGX_HTTP_LOCK: sr->method_name = ngx_http_lua_lock_method; break; case NGX_HTTP_UNLOCK: sr->method_name = ngx_http_lua_unlock_method; break; case NGX_HTTP_PATCH: sr->method_name = ngx_http_lua_patch_method; break; case NGX_HTTP_TRACE: sr->method_name = ngx_http_lua_trace_method; break; default: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "unsupported HTTP method: %u", (unsigned) method); return NGX_ERROR; } if (!(vars_action & NGX_HTTP_LUA_SHARE_ALL_VARS)) { /* we do not inherit the parent request's variables */ cmcf = ngx_http_get_module_main_conf(sr, ngx_http_core_module); size = cmcf->variables.nelts * sizeof(ngx_http_variable_value_t); if (vars_action & NGX_HTTP_LUA_COPY_ALL_VARS) { sr->variables = ngx_palloc(sr->pool, size); if (sr->variables == NULL) { return NGX_ERROR; } ngx_memcpy(sr->variables, r->variables, size); } else { /* we do not inherit the parent request's variables */ sr->variables = ngx_pcalloc(sr->pool, size); if (sr->variables == NULL) { return NGX_ERROR; } } } return ngx_http_lua_subrequest_add_extra_vars(sr, extra_vars); } static ngx_int_t ngx_http_lua_subrequest_add_extra_vars(ngx_http_request_t *sr, ngx_array_t *extra_vars) { ngx_http_core_main_conf_t *cmcf; ngx_http_variable_t *v; ngx_http_variable_value_t *vv; u_char *val; u_char *p; ngx_uint_t i, hash; ngx_str_t name; size_t len; ngx_hash_t *variables_hash; ngx_keyval_t *var; /* set any extra variables that were passed to the subrequest */ if (extra_vars == NULL || extra_vars->nelts == 0) { return NGX_OK; } cmcf = ngx_http_get_module_main_conf(sr, ngx_http_core_module); variables_hash = &cmcf->variables_hash; var = extra_vars->elts; for (i = 0; i < extra_vars->nelts; i++, var++) { /* copy the variable's name and value because they are allocated * by the lua VM */ len = var->key.len + var->value.len; p = ngx_pnalloc(sr->pool, len); if (p == NULL) { return NGX_ERROR; } name.data = p; name.len = var->key.len; p = ngx_copy(p, var->key.data, var->key.len); hash = ngx_hash_strlow(name.data, name.data, name.len); val = p; len = var->value.len; ngx_memcpy(p, var->value.data, len); v = ngx_hash_find(variables_hash, hash, name.data, name.len); if (v) { if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) { ngx_log_error(NGX_LOG_ERR, sr->connection->log, 0, "variable \"%V\" not changeable", &name); return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (v->set_handler) { vv = ngx_palloc(sr->pool, sizeof(ngx_http_variable_value_t)); if (vv == NULL) { return NGX_ERROR; } vv->valid = 1; vv->not_found = 0; vv->no_cacheable = 0; vv->data = val; vv->len = len; v->set_handler(sr, vv, v->data); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sr->connection->log, 0, "variable \"%V\" set to value \"%v\"", &name, vv); continue; } if (v->flags & NGX_HTTP_VAR_INDEXED) { vv = &sr->variables[v->index]; vv->valid = 1; vv->not_found = 0; vv->no_cacheable = 0; vv->data = val; vv->len = len; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sr->connection->log, 0, "variable \"%V\" set to value \"%v\"", &name, vv); continue; } } ngx_log_error(NGX_LOG_ERR, sr->connection->log, 0, "variable \"%V\" cannot be assigned a value (maybe you " "forgot to define it first?) ", &name); return NGX_ERROR; } return NGX_OK; } static void ngx_http_lua_process_vars_option(ngx_http_request_t *r, lua_State *L, int table, ngx_array_t **varsp) { ngx_array_t *vars; ngx_keyval_t *var; if (table < 0) { table = lua_gettop(L) + table + 1; } vars = *varsp; if (vars == NULL) { vars = ngx_array_create(r->pool, 4, sizeof(ngx_keyval_t)); if (vars == NULL) { dd("here"); luaL_error(L, "out of memory"); return; } *varsp = vars; } lua_pushnil(L); while (lua_next(L, table) != 0) { if (lua_type(L, -2) != LUA_TSTRING) { luaL_error(L, "attempt to use a non-string key in the " "\"vars\" option table"); return; } if (!lua_isstring(L, -1)) { luaL_error(L, "attempt to use bad variable value type %s", luaL_typename(L, -1)); } var = ngx_array_push(vars); if (var == NULL) { dd("here"); luaL_error(L, "out of memory"); return; } var->key.data = (u_char *) lua_tolstring(L, -2, &var->key.len); var->value.data = (u_char *) lua_tolstring(L, -1, &var->value.len); lua_pop(L, 1); } } ngx_int_t ngx_http_lua_post_subrequest(ngx_http_request_t *r, void *data, ngx_int_t rc) { ngx_http_request_t *pr; ngx_http_lua_ctx_t *pr_ctx; ngx_http_lua_ctx_t *ctx; /* subrequest ctx */ ngx_http_lua_co_ctx_t *pr_coctx; size_t len; ngx_str_t *body_str; u_char *p; ngx_chain_t *cl; ngx_http_lua_post_subrequest_data_t *psr_data = data; ctx = psr_data->ctx; if (ctx->run_post_subrequest) { if (r != r->connection->data) { r->connection->data = r; } return NGX_OK; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua run post subrequest handler, rc:%i c:%ud", rc, r->main->count); ctx->run_post_subrequest = 1; pr = r->parent; pr_ctx = ngx_http_get_module_ctx(pr, ngx_http_lua_module); if (pr_ctx == NULL) { return NGX_ERROR; } pr_coctx = psr_data->pr_co_ctx; pr_coctx->pending_subreqs--; if (pr_coctx->pending_subreqs == 0) { dd("all subrequests are done"); pr_ctx->no_abort = 0; pr_ctx->resume_handler = ngx_http_lua_subrequest_resume; pr_ctx->cur_co_ctx = pr_coctx; } if (pr_ctx->entered_content_phase) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua restoring write event handler"); pr->write_event_handler = ngx_http_lua_content_wev_handler; } else { pr->write_event_handler = ngx_http_core_run_phases; } dd("status rc = %d", (int) rc); dd("status headers_out.status = %d", (int) r->headers_out.status); dd("uri: %.*s", (int) r->uri.len, r->uri.data); /* capture subrequest response status */ pr_coctx->sr_statuses[ctx->index] = r->headers_out.status; if (pr_coctx->sr_statuses[ctx->index] == 0) { if (rc == NGX_OK) { rc = NGX_HTTP_OK; } if (rc == NGX_ERROR) { rc = NGX_HTTP_INTERNAL_SERVER_ERROR; } if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { pr_coctx->sr_statuses[ctx->index] = rc; } } if (!ctx->seen_last_for_subreq) { pr_coctx->sr_flags[ctx->index] |= NGX_HTTP_LUA_SUBREQ_TRUNCATED; } dd("pr_coctx status: %d", (int) pr_coctx->sr_statuses[ctx->index]); /* copy subrequest response headers */ pr_coctx->sr_headers[ctx->index] = &r->headers_out; /* copy subrequest response body */ body_str = &pr_coctx->sr_bodies[ctx->index]; len = 0; for (cl = ctx->body; cl; cl = cl->next) { /* ignore all non-memory buffers */ len += cl->buf->last - cl->buf->pos; } body_str->len = len; if (len == 0) { body_str->data = NULL; } else { p = ngx_palloc(r->pool, len); if (p == NULL) { return NGX_ERROR; } body_str->data = p; /* copy from and then free the data buffers */ for (cl = ctx->body; cl; cl = cl->next) { p = ngx_copy(p, cl->buf->pos, cl->buf->last - cl->buf->pos); cl->buf->last = cl->buf->pos; #if 0 dd("free body chain link buf ASAP"); ngx_pfree(r->pool, cl->buf->start); #endif } } if (ctx->body) { #if defined(nginx_version) && nginx_version >= 1001004 ngx_chain_update_chains(r->pool, #else ngx_chain_update_chains( #endif &pr_ctx->free_bufs, &pr_ctx->busy_bufs, &ctx->body, (ngx_buf_tag_t) &ngx_http_lua_module); dd("free bufs: %p", pr_ctx->free_bufs); } ngx_http_post_request_to_head(pr); if (r != r->connection->data) { r->connection->data = r; } if (rc == NGX_ERROR || rc == NGX_HTTP_CREATED || rc == NGX_HTTP_NO_CONTENT || (rc >= NGX_HTTP_SPECIAL_RESPONSE && rc != NGX_HTTP_CLOSE && rc != NGX_HTTP_REQUEST_TIME_OUT && rc != NGX_HTTP_CLIENT_CLOSED_REQUEST)) { /* emulate ngx_http_special_response_handler */ if (rc > NGX_OK) { r->err_status = rc; r->expect_tested = 1; r->headers_out.content_type.len = 0; r->headers_out.content_length_n = 0; ngx_http_clear_accept_ranges(r); ngx_http_clear_last_modified(r); rc = ngx_http_lua_send_header_if_needed(r, ctx); if (rc == NGX_ERROR) { return NGX_ERROR; } } return NGX_OK; } return rc; } static ngx_int_t ngx_http_lua_set_content_length_header(ngx_http_request_t *r, off_t len) { ngx_table_elt_t *h, *header; u_char *p; ngx_list_part_t *part; ngx_http_request_t *pr; ngx_uint_t i; r->headers_in.content_length_n = len; if (ngx_list_init(&r->headers_in.headers, r->pool, 20, sizeof(ngx_table_elt_t)) != NGX_OK) { return NGX_ERROR; } h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { return NGX_ERROR; } h->key = ngx_http_lua_content_length_header_key; h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == NULL) { return NGX_ERROR; } ngx_strlow(h->lowcase_key, h->key.data, h->key.len); r->headers_in.content_length = h; p = ngx_palloc(r->pool, NGX_OFF_T_LEN); if (p == NULL) { return NGX_ERROR; } h->value.data = p; h->value.len = ngx_sprintf(h->value.data, "%O", len) - h->value.data; h->hash = ngx_http_lua_content_length_hash; #if 0 dd("content length hash: %lu == %lu", (unsigned long) h->hash, ngx_hash_key_lc((u_char *) "Content-Length", sizeof("Content-Length") - 1)); #endif dd("r content length: %.*s", (int)r->headers_in.content_length->value.len, r->headers_in.content_length->value.data); pr = r->parent; if (pr == NULL) { return NGX_OK; } /* forward the parent request's all other request headers */ part = &pr->headers_in.headers.part; header = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; header = part->elts; i = 0; } if (header[i].key.len == sizeof("Content-Length") - 1 && ngx_strncasecmp(header[i].key.data, (u_char *) "Content-Length", sizeof("Content-Length") - 1) == 0) { continue; } if (ngx_http_lua_set_input_header(r, header[i].key, header[i].value, 0) == NGX_ERROR) { return NGX_ERROR; } } return NGX_OK; } static void ngx_http_lua_handle_subreq_responses(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) { ngx_uint_t i, count; ngx_uint_t index; lua_State *co; ngx_str_t *body_str; ngx_table_elt_t *header; ngx_list_part_t *part; ngx_http_headers_out_t *sr_headers; ngx_http_lua_co_ctx_t *coctx; u_char buf[sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1]; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua handle subrequest responses"); coctx = ctx->cur_co_ctx; co = coctx->co; for (index = 0; index < coctx->nsubreqs; index++) { dd("summary: reqs %d, subquery %d, pending %d, req %.*s", (int) coctx->nsubreqs, (int) index, (int) coctx->pending_subreqs, (int) r->uri.len, r->uri.data); /* {{{ construct ret value */ lua_createtable(co, 0 /* narr */, 4 /* nrec */); /* copy captured status */ lua_pushinteger(co, coctx->sr_statuses[index]); lua_setfield(co, -2, "status"); dd("captured subrequest flags: %d", (int) coctx->sr_flags[index]); /* set truncated flag if truncation happens */ if (coctx->sr_flags[index] & NGX_HTTP_LUA_SUBREQ_TRUNCATED) { lua_pushboolean(co, 1); lua_setfield(co, -2, "truncated"); } else { lua_pushboolean(co, 0); lua_setfield(co, -2, "truncated"); } /* copy captured body */ body_str = &coctx->sr_bodies[index]; lua_pushlstring(co, (char *) body_str->data, body_str->len); lua_setfield(co, -2, "body"); if (body_str->data) { dd("free body buffer ASAP"); ngx_pfree(r->pool, body_str->data); } /* copy captured headers */ sr_headers = coctx->sr_headers[index]; part = &sr_headers->headers.part; count = part->nelts; while (part->next) { part = part->next; count += part->nelts; } lua_createtable(co, 0, count + 5); /* res.header */ dd("saving subrequest response headers"); part = &sr_headers->headers.part; header = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; header = part->elts; i = 0; } dd("checking sr header %.*s", (int) header[i].key.len, header[i].key.data); #if 1 if (header[i].hash == 0) { continue; } #endif header[i].hash = 0; dd("pushing sr header %.*s", (int) header[i].key.len, header[i].key.data); lua_pushlstring(co, (char *) header[i].key.data, header[i].key.len); /* header key */ lua_pushvalue(co, -1); /* stack: table key key */ /* check if header already exists */ lua_rawget(co, -3); /* stack: table key value */ if (lua_isnil(co, -1)) { lua_pop(co, 1); /* stack: table key */ lua_pushlstring(co, (char *) header[i].value.data, header[i].value.len); /* stack: table key value */ lua_rawset(co, -3); /* stack: table */ } else { if (!lua_istable(co, -1)) { /* already inserted one value */ lua_createtable(co, 4, 0); /* stack: table key value table */ lua_insert(co, -2); /* stack: table key table value */ lua_rawseti(co, -2, 1); /* stack: table key table */ lua_pushlstring(co, (char *) header[i].value.data, header[i].value.len); /* stack: table key table value */ lua_rawseti(co, -2, lua_objlen(co, -2) + 1); /* stack: table key table */ lua_rawset(co, -3); /* stack: table */ } else { lua_pushlstring(co, (char *) header[i].value.data, header[i].value.len); /* stack: table key table value */ lua_rawseti(co, -2, lua_objlen(co, -2) + 1); /* stack: table key table */ lua_pop(co, 2); /* stack: table */ } } } if (sr_headers->content_type.len) { lua_pushliteral(co, "Content-Type"); /* header key */ lua_pushlstring(co, (char *) sr_headers->content_type.data, sr_headers->content_type.len); /* head key value */ lua_rawset(co, -3); /* head */ } if (sr_headers->content_length == NULL && sr_headers->content_length_n >= 0) { lua_pushliteral(co, "Content-Length"); /* header key */ lua_pushnumber(co, (lua_Number) sr_headers->content_length_n); /* head key value */ lua_rawset(co, -3); /* head */ } /* to work-around an issue in ngx_http_static_module * (github issue #41) */ if (sr_headers->location && sr_headers->location->value.len) { lua_pushliteral(co, "Location"); /* header key */ lua_pushlstring(co, (char *) sr_headers->location->value.data, sr_headers->location->value.len); /* head key value */ lua_rawset(co, -3); /* head */ } if (sr_headers->last_modified_time != -1) { if (sr_headers->status != NGX_HTTP_OK && sr_headers->status != NGX_HTTP_PARTIAL_CONTENT && sr_headers->status != NGX_HTTP_NOT_MODIFIED && sr_headers->status != NGX_HTTP_NO_CONTENT) { sr_headers->last_modified_time = -1; sr_headers->last_modified = NULL; } } if (sr_headers->last_modified == NULL && sr_headers->last_modified_time != -1) { (void) ngx_http_time(buf, sr_headers->last_modified_time); lua_pushliteral(co, "Last-Modified"); /* header key */ lua_pushlstring(co, (char *) buf, sizeof(buf)); /* head key value */ lua_rawset(co, -3); /* head */ } lua_setfield(co, -2, "header"); /* }}} */ } } void ngx_http_lua_inject_subrequest_api(lua_State *L) { lua_createtable(L, 0 /* narr */, 2 /* nrec */); /* .location */ lua_pushcfunction(L, ngx_http_lua_ngx_location_capture); lua_setfield(L, -2, "capture"); lua_pushcfunction(L, ngx_http_lua_ngx_location_capture_multi); lua_setfield(L, -2, "capture_multi"); lua_setfield(L, -2, "location"); } static ngx_int_t ngx_http_lua_subrequest(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr, ngx_http_post_subrequest_t *ps, ngx_uint_t flags) { ngx_time_t *tp; ngx_connection_t *c; ngx_http_request_t *sr; ngx_http_core_srv_conf_t *cscf; r->main->subrequests--; if (r->main->subrequests == 0) { #if defined(NGX_DTRACE) && NGX_DTRACE ngx_http_probe_subrequest_cycle(r, uri, args); #endif ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "subrequests cycle while processing \"%V\"", uri); r->main->subrequests = 1; return NGX_ERROR; } sr = ngx_pcalloc(r->pool, sizeof(ngx_http_request_t)); if (sr == NULL) { return NGX_ERROR; } sr->signature = NGX_HTTP_MODULE; c = r->connection; sr->connection = c; sr->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module); if (sr->ctx == NULL) { return NGX_ERROR; } if (ngx_list_init(&sr->headers_out.headers, r->pool, 20, sizeof(ngx_table_elt_t)) != NGX_OK) { return NGX_ERROR; } cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); sr->main_conf = cscf->ctx->main_conf; sr->srv_conf = cscf->ctx->srv_conf; sr->loc_conf = cscf->ctx->loc_conf; sr->pool = r->pool; sr->headers_in.content_length_n = -1; sr->headers_in.keep_alive_n = -1; ngx_http_clear_content_length(sr); ngx_http_clear_accept_ranges(sr); ngx_http_clear_last_modified(sr); sr->request_body = r->request_body; #ifdef HAVE_ALLOW_REQUEST_BODY_UPDATING_PATCH sr->content_length_n = -1; #endif sr->method = NGX_HTTP_GET; sr->http_version = r->http_version; sr->request_line = r->request_line; sr->uri = *uri; if (args) { sr->args = *args; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http subrequest \"%V?%V\"", uri, &sr->args); sr->subrequest_in_memory = (flags & NGX_HTTP_SUBREQUEST_IN_MEMORY) != 0; sr->waited = (flags & NGX_HTTP_SUBREQUEST_WAITED) != 0; sr->unparsed_uri = r->unparsed_uri; sr->method_name = ngx_http_core_get_method; sr->http_protocol = r->http_protocol; ngx_http_set_exten(sr); sr->main = r->main; sr->parent = r; sr->post_subrequest = ps; sr->read_event_handler = ngx_http_request_empty_handler; sr->write_event_handler = ngx_http_handler; sr->variables = r->variables; sr->log_handler = r->log_handler; sr->internal = 1; sr->discard_body = r->discard_body; sr->expect_tested = 1; sr->main_filter_need_in_memory = r->main_filter_need_in_memory; sr->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1; tp = ngx_timeofday(); sr->start_sec = tp->sec; sr->start_msec = tp->msec; r->main->count++; *psr = sr; #if defined(NGX_DTRACE) && NGX_DTRACE ngx_http_probe_subrequest_start(sr); #endif return ngx_http_post_request(sr, NULL); } static ngx_int_t ngx_http_lua_subrequest_resume(ngx_http_request_t *r) { lua_State *vm; ngx_int_t rc; ngx_connection_t *c; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return NGX_ERROR; } ctx->resume_handler = ngx_http_lua_wev_handler; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua run subrequests done, resuming lua thread"); coctx = ctx->cur_co_ctx; dd("nsubreqs: %d", (int) coctx->nsubreqs); ngx_http_lua_handle_subreq_responses(r, ctx); dd("free sr_statues/headers/bodies memory ASAP"); #if 1 ngx_pfree(r->pool, coctx->sr_statuses); coctx->sr_statuses = NULL; coctx->sr_headers = NULL; coctx->sr_bodies = NULL; coctx->sr_flags = NULL; #endif c = r->connection; vm = ngx_http_lua_get_lua_vm(r, ctx); rc = ngx_http_lua_run_thread(vm, r, ctx, coctx->nsubreqs); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua run thread returned %d", rc); if (rc == NGX_AGAIN) { return ngx_http_lua_run_posted_threads(c, vm, r, ctx); } if (rc == NGX_DONE) { ngx_http_lua_finalize_request(r, NGX_DONE); return ngx_http_lua_run_posted_threads(c, vm, r, ctx); } /* rc == NGX_ERROR || rc >= NGX_OK */ if (ctx->entered_content_phase) { ngx_http_lua_finalize_request(r, rc); return NGX_DONE; } return rc; } static void ngx_http_lua_cancel_subreq(ngx_http_request_t *r) { ngx_http_posted_request_t *pr; ngx_http_posted_request_t **p; #if 1 r->main->count--; r->main->subrequests++; #endif p = &r->main->posted_requests; for (pr = r->main->posted_requests; pr->next; pr = pr->next) { p = &pr->next; } *p = NULL; r->connection->data = r->parent; } static ngx_int_t ngx_http_post_request_to_head(ngx_http_request_t *r) { ngx_http_posted_request_t *pr; pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t)); if (pr == NULL) { return NGX_ERROR; } pr->request = r; pr->next = r->main->posted_requests; r->main->posted_requests = pr; return NGX_OK; } static ngx_int_t ngx_http_lua_copy_in_file_request_body(ngx_http_request_t *r) { ngx_temp_file_t *tf; ngx_http_request_body_t *body; tf = r->request_body->temp_file; if (!tf->persistent || !tf->clean) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the request body was not read by ngx_lua"); return NGX_ERROR; } body = ngx_palloc(r->pool, sizeof(ngx_http_request_body_t)); if (body == NULL) { return NGX_ERROR; } ngx_memcpy(body, r->request_body, sizeof(ngx_http_request_body_t)); body->temp_file = ngx_palloc(r->pool, sizeof(ngx_temp_file_t)); if (body->temp_file == NULL) { return NGX_ERROR; } ngx_memcpy(body->temp_file, tf, sizeof(ngx_temp_file_t)); dd("file fd: %d", body->temp_file->file.fd); r->request_body = body; return NGX_OK; } static ngx_int_t ngx_http_lua_copy_request_headers(ngx_http_request_t *sr, ngx_http_request_t *r) { ngx_table_elt_t *header; ngx_list_part_t *part; ngx_uint_t i; if (ngx_list_init(&sr->headers_in.headers, sr->pool, 20, sizeof(ngx_table_elt_t)) != NGX_OK) { return NGX_ERROR; } dd("before: parent req headers count: %d", (int) r->headers_in.headers.part.nelts); part = &r->headers_in.headers.part; header = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; header = part->elts; i = 0; } dd("setting request header %.*s: %.*s", (int) header[i].key.len, header[i].key.data, (int) header[i].value.len, header[i].value.data); if (ngx_http_lua_set_input_header(sr, header[i].key, header[i].value, 0) == NGX_ERROR) { return NGX_ERROR; } } dd("after: parent req headers count: %d", (int) r->headers_in.headers.part.nelts); return NGX_OK; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_clfactory.c0000664000000000000000000006436712305451335020260 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include #include "ngx_http_lua_clfactory.h" #define CLFACTORY_BEGIN_CODE "return function() " #define CLFACTORY_BEGIN_SIZE (sizeof(CLFACTORY_BEGIN_CODE) - 1) #define CLFACTORY_END_CODE "\nend" #define CLFACTORY_END_SIZE (sizeof(CLFACTORY_END_CODE) - 1) /* * taken from chaoslawful: * Lua bytecode header Luajit bytecode header * -------------- -------------- * | \033Lua | 0-3 | \033LJ | 0-2 * -------------- -------------- * | LuaC | 4 | bytecode | 3 * | Version | | version | * -------------- -------------- * | LuaC | 5 | misc flag | 4 [F|S|B] * | Format | -------------- * -------------- | chunkname | ULEB128 var-len * | Endian | 6 | len | encoded uint32 * -------------- -------------- * | size of | 7 | chunkname | * | int | | str no \0 | * -------------- -------------- * | size of | 8 * | size_t | * -------------- * | size of | 9 * | instruction| * -------------- * | size of | 10 * | number | * -------------- * | number | 11 * | is int? | * -------------- */ /* * CLOSURE 0 0 RETURN 0 2 RETURN 0 1 * length(Instruction) = 4 or 8 * little endian or big endian */ #define LUA_LITTLE_ENDIAN_4BYTES_CODE \ "\x24\x00\x00\x00\x1e\x00\x00\x01\x1e\x00\x80\x00" #define LUA_LITTLE_ENDIAN_8BYTES_CODE \ "\x24\x00\x00\x00\x00\x00\x00\x00\x1e\x00\x00\x01" \ "\x00\x00\x00\x00\x1e\x00\x80\x00\x00\x00\x00\x00" #define LUA_BIG_ENDIAN_4BYTES_CODE \ "\x00\x00\x00\x24\x01\x00\x00\x1e\x00\x08\x00\x1e" #define LUA_BIG_ENDIAN_8BYTES_CODE \ "\x00\x00\x00\x00\x00\x00\x00\x24\x00\x00\x00\x00" \ "\x01\x00\x00\x1e\x00\x00\x00\x00\x00\x08\x00\x1e" #define LUA_LITTLE_ENDIAN_4BYTES_CODE_LEN (4 + 4 + 4) #define LUA_LITTLE_ENDIAN_8BYTES_CODE_LEN (8 + 8 + 8) #define LUA_BIG_ENDIAN_4BYTES_CODE_LEN (4 + 4 + 4) #define LUA_BIG_ENDIAN_8BYTES_CODE_LEN (8 + 8 + 8) #define LUAC_HEADERSIZE 12 #define LUAC_VERSION 0x51 /* * taken from chaoslawful: * Lua Proto * --------------------- * | String | Can be empty string * | [source] | (stripped or internal function) * --------------------- * | Int | At which line this function is defined * | [linedefined] | * --------------------- * | Int | At while line this function definition ended * | [lastlinedefined] | * --------------------- * | Char | Number of upvalues referenced by this function * | [nups] | * --------------------- * | Char | Number of paramters of this function * | [numparams] | * --------------------- * | Char | Does this function has variable number of arguments? * | [is_var_arg] | main function always set to VARARG_ISVARARG (2) * --------------------- * | Char | Maximum stack size this function used * | [maxstacksize] | Intially set to 2 * --------------------- * | Vector(instr) | Code instructions of this function * | [code] | * --------------------- * | Int | Number of constants referenced by this function * | [sizek] | * --------------------- * | Char | ------------------------------------ * | type of [k[i]] | The type and content of constants | * --------------------- |-> repeat for i in * | Char if boolean | No content part if type is NIL | [1..sizek] * | Number if number | ------------------------------------ * | String if string | * --------------------- * | Int | Number of internal functions * | [sizep] | * --------------------- * | Function | -> repeat for i in [1..sizep] * | at [p[i]] | * --------------------- * | Vector | Debug lineinfo vector * | [lineinfo] | Empty vector here if dubug info is stripped * --------------------- * | Int | Number of local variable in this function * | [sizelocvars] | 0 if debug info is stripped * --------------------- * | String | ------------------------------------ * | [locvars[i]] | Name of local var i | * | .varname] | | * --------------------- | * | Int | instruction counter | * | [locvars[i]] | where lcoal var i start to be |-> repeat for i in * | .startpc] | referenced | [0..sizelocvars] * --------------------- | * | Int | instruction counter, where local | * | [locvars[i]] | var i ceased to be referenced | * | .endpc] | ------------------------------------ * --------------------- * | Int | Number of upvalues referenced by this function, * | [sizeupvalues] | 0 if stripped * --------------------- * | String | -> repeat for i in[0..sizeupvalues] * | [upvalues[i]] | * --------------------- */ #define POS_SOURCE_STR_LEN LUAC_HEADERSIZE #define POS_START_LINE (POS_SOURCE_STR_LEN + sizeof(size_t)) #define POS_LAST_LINE (POS_START_LINE + sizeof(int)) #define POS_NUM_OF_UPVS (POS_LAST_LINE + sizeof(int)) #define POS_NUM_OF_PARA (POS_NUM_OF_UPVS + sizeof(char)) #define POS_IS_VAR_ARG (POS_NUM_OF_PARA + sizeof(char)) #define POS_MAX_STACK_SIZE (POS_IS_VAR_ARG + sizeof(char)) #define POS_NUM_OF_INST (POS_MAX_STACK_SIZE +sizeof(char)) #define POS_BYTECODE (POS_NUM_OF_INST + sizeof(int)) #define MAX_BEGIN_CODE_SIZE \ (POS_BYTECODE + LUA_LITTLE_ENDIAN_8BYTES_CODE_LEN \ + sizeof(int) + sizeof(int)) #define MAX_END_CODE_SIZE (sizeof(int) + sizeof(int) + sizeof(int)) /* * taken from chaoslawful: * Luajit bytecode format * --------------------- * | HEAD | Luajit bytecode head * --------------------- * | Internal | All internal functions * | functions | * --------------------- * | ULEB128 | Rest data total length of this function * | [Date len of | (not include itself) * | this function] | * --------------------- * | Char | F(ffi) | V(vararg)| C(has internal funcs) * | [func flag] | * --------------------- * | Char | Number of paramters of this function * | [numparams] | * --------------------- * | Char | * | [framesize] | * --------------------- * | Char | Number of upvalues referenced by this function * | [sizeupvalues] | * --------------------- * | ULEB128 | Number of collectable constants referenced * | [sizekgc] | by this function * --------------------- * | ULEB128 | Number of lua number constants referenced * | [sizekn] | by this function * --------------------- * | ULEB128 | Number of bytecode instructions of this function * | [sizebc]m1 | minus 1 to omit the BC_FUNCV/BC_FUNCF header bytecode * --------------------- * | ULEB128 | * | [size of dbg | Size of debug lineinfo map, available when not stripped * | lineinfo] | * --------------------- * | ULEB128 | Available when not stripped * | [firstline] | The first line of this function's definition * --------------------- * | ULEB128 | Available when not stripped * | [numline] | The number of lines of this function's definition * --------------------- * | [bytecode] | Bytecode instructions of this function * --------------------- * |[upvalue ref slots]| [sizeupvalues] * 2 * --------------------- * | [collectable | [sizekgc] elems, variable length * | constants] | * --------------------- * | [lua number | [sizekn] elems, variable length * | constants] | * --------------------- * | [debug lineinfo | Length is the calculated size of debug lineinfo above * | | Only available if not stripped * --------------------- * | Char | * | [\x00] | Footer * --------------------- */ /* bytecode for luajit 2.0 */ #define LJ20_LITTLE_ENDIAN_CODE_STRIPPED \ "\x14\x03\x00\x01\x00\x01\x00\x03" \ "\x31\x00\x00\x00\x30\x00\x00\x80\x48\x00\x02\x00" \ "\x00\x00" #define LJ20_BIG_ENDIAN_CODE_STRIPPED \ "\x14\x03\x00\x01\x00\x01\x00\x03" \ "\x00\x00\x00\x31\x80\x00\x00\x30\x00\x02\x00\x48" \ "\x00\x00" #define LJ20_LITTLE_ENDIAN_CODE \ "\x15\x03\x00\x01\x00\x01\x00\x03\x00" \ "\x31\x00\x00\x00\x30\x00\x00\x80\x48\x00\x02\x00" \ "\x00\x00" #define LJ20_BIG_ENDIAN_CODE \ "\x15\x03\x00\x01\x00\x01\x00\x03\x00" \ "\x00\x00\x00\x31\x80\x00\x00\x30\x00\x02\x00\x48" \ "\x00\x00" /* bytecode for luajit 2.1 */ #define LJ21_LITTLE_ENDIAN_CODE_STRIPPED \ "\x14\x03\x00\x01\x00\x01\x00\x03" \ "\x33\x00\x00\x00\x32\x00\x00\x80\x4c\x00\x02\x00" \ "\x00\x00" #define LJ21_BIG_ENDIAN_CODE_STRIPPED \ "\x14\x03\x00\x01\x00\x01\x00\x03" \ "\x00\x00\x00\x33\x80\x00\x00\x32\x00\x02\x00\x4c" \ "\x00\x00" #define LJ21_LITTLE_ENDIAN_CODE \ "\x15\x03\x00\x01\x00\x01\x00\x03\x00" \ "\x33\x00\x00\x00\x32\x00\x00\x80\x4c\x00\x02\x00" \ "\x00\x00" #define LJ21_BIG_ENDIAN_CODE \ "\x15\x03\x00\x01\x00\x01\x00\x03\x00" \ "\x00\x00\x00\x33\x80\x00\x00\x32\x00\x02\x00\x4c" \ "\x00\x00" #define LJ_CODE_LEN 23 #define LJ_CODE_LEN_STRIPPED 22 #define LJ_HEADERSIZE 5 #define LJ_BCDUMP_F_BE 0x01 #define LJ_BCDUMP_F_STRIP 0x02 #define LJ21_BCDUMP_VERSION 2 #define LJ20_BCDUMP_VERSION 1 #define LJ_SIGNATURE "\x1b\x4c\x4a" typedef enum { NGX_LUA_TEXT_FILE, NGX_LUA_BT_LUA, NGX_LUA_BT_LJ } ngx_http_lua_clfactory_file_type_e; typedef struct { ngx_http_lua_clfactory_file_type_e file_type; int sent_begin; int sent_end; int extraline; FILE *f; size_t begin_code_len; size_t end_code_len; size_t rest_len; union { char *ptr; char str[MAX_BEGIN_CODE_SIZE]; } begin_code; union { char *ptr; char str[MAX_END_CODE_SIZE]; } end_code; char buff[LUAL_BUFFERSIZE]; } ngx_http_lua_clfactory_file_ctx_t; typedef struct { int sent_begin; int sent_end; const char *s; size_t size; } ngx_http_lua_clfactory_buffer_ctx_t; static const char *ngx_http_lua_clfactory_getF(lua_State *L, void *ud, size_t *size); static int ngx_http_lua_clfactory_errfile(lua_State *L, const char *what, int fname_index); static const char *ngx_http_lua_clfactory_getS(lua_State *L, void *ud, size_t *size); static long ngx_http_lua_clfactory_file_size(FILE *f); int ngx_http_lua_clfactory_bytecode_prepare(lua_State *L, ngx_http_lua_clfactory_file_ctx_t *lf, int fname_index) { int x = 1, size_of_int, size_of_size_t, little_endian, size_of_inst, version, stripped; static int num_of_inst = 3, num_of_inter_func = 1; const char *filename, *emsg, *serr, *bytecode; size_t size, bytecode_len; long fsize; serr = NULL; *lf->begin_code.str = LUA_SIGNATURE[0]; if (lf->file_type == NGX_LUA_BT_LJ) { size = fread(lf->begin_code.str + 1, 1, LJ_HEADERSIZE - 1, lf->f); if (size != LJ_HEADERSIZE - 1) { serr = strerror(errno); emsg = "cannot read header"; goto error; } version = *(lf->begin_code.str + 3); dd("version: %d", (int) version); if (ngx_memcmp(lf->begin_code.str, LJ_SIGNATURE, sizeof(LJ_SIGNATURE) - 1)) { emsg = "bad byte-code header"; goto error; } #if defined(DDEBUG) && (DDEBUG) { dd("==LJ_BT_HEADER=="); size_t i; for (i = 0; i < LJ_HEADERSIZE; i++) { dd("%ld: 0x%02X", i, (unsigned)(u_char)lf->begin_code.str[i]); } dd("==LJ_BT_HEADER_END=="); } #endif lf->begin_code_len = LJ_HEADERSIZE; little_endian = !((*(lf->begin_code.str + 4)) & LJ_BCDUMP_F_BE); stripped = (*(lf->begin_code.str + 4)) & LJ_BCDUMP_F_STRIP; dd("stripped: %d", (int) stripped); if (version == LJ21_BCDUMP_VERSION) { if (stripped) { if (little_endian) { lf->end_code.ptr = LJ21_LITTLE_ENDIAN_CODE_STRIPPED; } else { lf->end_code.ptr = LJ21_BIG_ENDIAN_CODE_STRIPPED; } lf->end_code_len = LJ_CODE_LEN_STRIPPED; } else { if (little_endian) { lf->end_code.ptr = LJ21_LITTLE_ENDIAN_CODE; } else { lf->end_code.ptr = LJ21_BIG_ENDIAN_CODE; } lf->end_code_len = LJ_CODE_LEN; } } else if (version == LJ20_BCDUMP_VERSION) { if (stripped) { if (little_endian) { lf->end_code.ptr = LJ20_LITTLE_ENDIAN_CODE_STRIPPED; } else { lf->end_code.ptr = LJ20_BIG_ENDIAN_CODE_STRIPPED; } lf->end_code_len = LJ_CODE_LEN_STRIPPED; } else { if (little_endian) { lf->end_code.ptr = LJ20_LITTLE_ENDIAN_CODE; } else { lf->end_code.ptr = LJ20_BIG_ENDIAN_CODE; } lf->end_code_len = LJ_CODE_LEN; } } else { emsg = "bytecode format version unsupported"; goto error; } fsize = ngx_http_lua_clfactory_file_size(lf->f); if (fsize < 0) { serr = strerror(errno); emsg = "cannot fseek/ftell"; goto error; } lf->rest_len = fsize - LJ_HEADERSIZE; #if defined(DDEBUG) && (DDEBUG) { size_t i = 0; dd("==LJ_END_CODE: %ld rest_len: %ld==", lf->end_code_len, lf->rest_len); for (i = 0; i < lf->end_code_len; i++) { dd("%ld: 0x%02X", i, (unsigned) ((u_char) lf->end_code.ptr[i])); } dd("==LJ_END_CODE_END=="); } #endif } else { size = fread(lf->begin_code.str + 1, 1, LUAC_HEADERSIZE - 1, lf->f); if (size != LUAC_HEADERSIZE - 1) { serr = strerror(errno); emsg = "cannot read header"; goto error; } version = *(lf->begin_code.str + 4); little_endian = *(lf->begin_code.str + 6); size_of_int = *(lf->begin_code.str + 7); size_of_size_t = *(lf->begin_code.str + 8); size_of_inst = *(lf->begin_code.str + 9); #if defined(DDEBUG) && (DDEBUG) { dd("==LUA_BT_HEADER=="); size_t i; for (i = 0; i < LUAC_HEADERSIZE; i++) { dd("%ld, 0x%02X", i, (unsigned)(u_char)lf->begin_code.str[i]); } dd("==LUA_BT_HEADER_END=="); } #endif if (ngx_memcmp(lf->begin_code.str, LUA_SIGNATURE, sizeof(LUA_SIGNATURE) -1) || version != LUAC_VERSION || little_endian != (int) (*(char *) &x) || size_of_int != sizeof(int) || size_of_size_t != sizeof(size_t) || (size_of_inst != 4 && size_of_inst != 8)) { emsg = "bad byte-code header"; goto error; } /* clear the following fields to zero: * - source string length * - start line * - last line */ ngx_memzero(lf->begin_code.str + POS_SOURCE_STR_LEN, sizeof(size_t) + sizeof(int) * 2); /* number of upvalues */ *(lf->begin_code.str + POS_NUM_OF_UPVS) = 0; /* number of paramters */ *(lf->begin_code.str + POS_NUM_OF_PARA) = 0; /* is var-argument function? */ *(lf->begin_code.str + POS_IS_VAR_ARG) = 2; /* max stack size */ *(lf->begin_code.str + POS_MAX_STACK_SIZE) = 2; /* number of bytecode instructions */ ngx_memcpy(lf->begin_code.str + POS_NUM_OF_INST, &num_of_inst, sizeof(int)); lf->begin_code_len = POS_BYTECODE; if (little_endian) { if (size_of_inst == 4) { bytecode = LUA_LITTLE_ENDIAN_4BYTES_CODE; bytecode_len = LUA_LITTLE_ENDIAN_4BYTES_CODE_LEN; } else { bytecode = LUA_LITTLE_ENDIAN_8BYTES_CODE; bytecode_len = LUA_LITTLE_ENDIAN_8BYTES_CODE_LEN; } } else { if (size_of_inst == 4) { bytecode = LUA_BIG_ENDIAN_4BYTES_CODE; bytecode_len = LUA_BIG_ENDIAN_4BYTES_CODE_LEN; } else { bytecode = LUA_BIG_ENDIAN_8BYTES_CODE; bytecode_len = LUA_BIG_ENDIAN_8BYTES_CODE_LEN; } } /* bytecode */ ngx_memcpy(lf->begin_code.str + POS_BYTECODE, bytecode, bytecode_len); /* number of consts */ ngx_memzero(lf->begin_code.str + POS_BYTECODE + bytecode_len, sizeof(int)); /* number of internal functions */ ngx_memcpy(lf->begin_code.str + POS_BYTECODE + bytecode_len + sizeof(int), &num_of_inter_func, sizeof(int)); lf->begin_code_len += bytecode_len + sizeof(int) + sizeof(int); #if defined(DDEBUG) && (DDEBUG) { size_t i = 0; dd("==LUA_BEGIN_CODE: %ld==", lf->begin_code_len); for (i = 0; i < lf->begin_code_len; i++) { dd("%ld: 0x%02X", i, (unsigned) ((u_char) lf->begin_code.str[i])); } dd("==LUA_BEGIN_CODE_END=="); } #endif /* clear the following fields to zero: * - lineinfo vector size * - number of local vars * - number of upvalues */ ngx_memzero(lf->end_code.str, sizeof(int) * 3); lf->end_code_len = sizeof(int) + sizeof(int) + sizeof(int); #if defined(DDEBUG) && (DDEBUG) { size_t i = 0; dd("==LUA_END_CODE: %ld==", lf->end_code_len); for (i = 0; i < lf->end_code_len; i++) { dd("%ld: 0x%02X", i, (unsigned) ((u_char) lf->end_code.str[i])); } dd("==LUA_END_CODE_END=="); } #endif } return 0; error: fclose(lf->f); /* close file (even in case of errors) */ filename = lua_tostring(L, fname_index) + 1; if (serr) { lua_pushfstring(L, "%s in %s: %s", emsg, filename, serr); } else { lua_pushfstring(L, "%s in %s", emsg, filename); } lua_remove(L, fname_index); return LUA_ERRFILE; } int ngx_http_lua_clfactory_loadfile(lua_State *L, const char *filename) { int c, status, readstatus; ngx_flag_t sharp; ngx_http_lua_clfactory_file_ctx_t lf; /* index of filename on the stack */ int fname_index; sharp = 0; fname_index = lua_gettop(L) + 1; lf.extraline = 0; lf.file_type = NGX_LUA_TEXT_FILE; lf.begin_code.ptr = CLFACTORY_BEGIN_CODE; lf.begin_code_len = CLFACTORY_BEGIN_SIZE; lf.end_code.ptr = CLFACTORY_END_CODE; lf.end_code_len = CLFACTORY_END_SIZE; lua_pushfstring(L, "@%s", filename); lf.f = fopen(filename, "r"); if (lf.f == NULL) { return ngx_http_lua_clfactory_errfile(L, "open", fname_index); } c = getc(lf.f); if (c == '#') { /* Unix exec. file? */ lf.extraline = 1; while ((c = getc(lf.f)) != EOF && c != '\n') { /* skip first line */ } if (c == '\n') { c = getc(lf.f); } sharp = 1; } if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ if (lf.f == NULL) { return ngx_http_lua_clfactory_errfile(L, "reopen", fname_index); } /* check whether lib jit exists */ luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1); lua_getfield(L, -1, "jit"); /* get _LOADED["jit"] */ if (lua_istable(L, -1)) { lf.file_type = NGX_LUA_BT_LJ; } else { lf.file_type = NGX_LUA_BT_LUA; } lua_pop(L, 2); /* * Loading bytecode with an extra header is disabled for security * reasons. This may circumvent the usual check for bytecode vs. * Lua code by looking at the first char. Since this is a potential * security violation no attempt is made to echo the chunkname either. */ if (lf.file_type == NGX_LUA_BT_LJ && sharp) { if (filename) { fclose(lf.f); /* close file (even in case of errors) */ } filename = lua_tostring(L, fname_index) + 1; lua_pushfstring(L, "bad byte-code header in %s", filename); lua_remove(L, fname_index); return LUA_ERRFILE; } while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) { /* skip eventual `#!...' */ } status = ngx_http_lua_clfactory_bytecode_prepare(L, &lf, fname_index); if (status != 0) { return status; } lf.extraline = 0; } if (lf.file_type == NGX_LUA_TEXT_FILE) { ungetc(c, lf.f); } lf.sent_begin = lf.sent_end = 0; status = lua_load(L, ngx_http_lua_clfactory_getF, &lf, lua_tostring(L, -1)); readstatus = ferror(lf.f); if (filename) { fclose(lf.f); /* close file (even in case of errors) */ } if (readstatus) { lua_settop(L, fname_index); /* ignore results from `lua_load' */ return ngx_http_lua_clfactory_errfile(L, "read", fname_index); } lua_remove(L, fname_index); return status; } int ngx_http_lua_clfactory_loadbuffer(lua_State *L, const char *buff, size_t size, const char *name) { ngx_http_lua_clfactory_buffer_ctx_t ls; ls.s = buff; ls.size = size; ls.sent_begin = 0; ls.sent_end = 0; return lua_load(L, ngx_http_lua_clfactory_getS, &ls, name); } static const char * ngx_http_lua_clfactory_getF(lua_State *L, void *ud, size_t *size) { char *buf; size_t num; ngx_http_lua_clfactory_file_ctx_t *lf; lf = (ngx_http_lua_clfactory_file_ctx_t *) ud; if (lf->extraline) { lf->extraline = 0; *size = 1; return "\n"; } if (lf->sent_begin == 0) { lf->sent_begin = 1; *size = lf->begin_code_len; if (lf->file_type == NGX_LUA_TEXT_FILE) { buf = lf->begin_code.ptr; } else { buf = lf->begin_code.str; } return buf; } if (feof(lf->f)) { if (lf->sent_end == 0) { lf->sent_end = 1; *size = lf->end_code_len; if (lf->file_type == NGX_LUA_BT_LUA) { buf = lf->end_code.str; } else { buf = lf->end_code.ptr; } return buf; } return NULL; } num = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* skip the footer(\x00) in luajit */ if (num > 0 && lf->file_type == NGX_LUA_BT_LJ) { lf->rest_len -= num; if (lf->rest_len == 0) { if (--num == 0 && lf->sent_end == 0) { lf->sent_end = 1; buf = lf->end_code.ptr; *size = lf->end_code_len; return buf; } } } *size = num; return (*size > 0) ? lf->buff : NULL; } static int ngx_http_lua_clfactory_errfile(lua_State *L, const char *what, int fname_index) { const char *serr; const char *filename; filename = lua_tostring(L, fname_index) + 1; if (errno) { serr = strerror(errno); lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); } else { lua_pushfstring(L, "cannot %s %s", what, filename); } lua_remove(L, fname_index); return LUA_ERRFILE; } static const char * ngx_http_lua_clfactory_getS(lua_State *L, void *ud, size_t *size) { ngx_http_lua_clfactory_buffer_ctx_t *ls = ud; if (ls->sent_begin == 0) { ls->sent_begin = 1; *size = CLFACTORY_BEGIN_SIZE; return CLFACTORY_BEGIN_CODE; } if (ls->size == 0) { if (ls->sent_end == 0) { ls->sent_end = 1; *size = CLFACTORY_END_SIZE; return CLFACTORY_END_CODE; } return NULL; } *size = ls->size; ls->size = 0; return ls->s; } static long ngx_http_lua_clfactory_file_size(FILE *f) { long cur_pos, len; cur_pos = ftell(f); if (cur_pos == -1) { return -1; } if (fseek(f, 0, SEEK_END) != 0) { return -1; } len = ftell(f); if (len == -1) { return -1; } if (fseek(f, cur_pos, SEEK_SET) != 0) { return -1; } return len; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_log.h0000664000000000000000000000053012305451335017036 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_LOG_H_INCLUDED_ #define _NGX_HTTP_LUA_LOG_H_INCLUDED_ #include "ngx_http_lua_common.h" void ngx_http_lua_inject_log_api(lua_State *L); #endif /* _NGX_HTTP_LUA_LOG_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_socket_udp.h0000664000000000000000000000275112305451335020424 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_SOCKET_UDP_H_INCLUDED_ #define _NGX_HTTP_LUA_SOCKET_UDP_H_INCLUDED_ #include "ngx_http_lua_common.h" typedef struct ngx_http_lua_socket_udp_upstream_s ngx_http_lua_socket_udp_upstream_t; typedef int (*ngx_http_lua_socket_udp_retval_handler)(ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u, lua_State *L); typedef void (*ngx_http_lua_socket_udp_upstream_handler_pt)( ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u); struct ngx_http_lua_socket_udp_upstream_s { ngx_http_lua_socket_udp_retval_handler prepare_retvals; ngx_http_lua_socket_udp_upstream_handler_pt read_event_handler; ngx_http_lua_loc_conf_t *conf; ngx_http_cleanup_pt *cleanup; ngx_http_request_t *request; ngx_udp_connection_t udp_connection; ngx_msec_t read_timeout; ngx_http_upstream_resolved_t *resolved; ngx_uint_t ft_type; ngx_err_t socket_errno; size_t received; /* for receive */ size_t recv_buf_size; ngx_http_lua_co_ctx_t *co_ctx; unsigned waiting; /* :1 */ }; void ngx_http_lua_inject_socket_udp_api(ngx_log_t *log, lua_State *L); #endif /* _NGX_HTTP_LUA_SOCKET_UDP_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_args.h0000664000000000000000000000037712305451335017222 0ustar #ifndef NGX_HTTP_LUA_ARGS #define NGX_HTTP_LUA_ARGS #include "ngx_http_lua_common.h" void ngx_http_lua_inject_req_args_api(lua_State *L); int ngx_http_lua_parse_args(lua_State *L, u_char *buf, u_char *last, int max); #endif /* NGX_HTTP_LUA_ARGS */ debian/modules/nginx-lua/src/ngx_http_lua_script.c0000664000000000000000000003252712305451335017567 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_script.h" static void * ngx_http_lua_script_add_code(ngx_array_t *codes, size_t size); static size_t ngx_http_lua_script_copy_len_code( ngx_http_lua_script_engine_t *e); static void ngx_http_lua_script_copy_code(ngx_http_lua_script_engine_t *e); static ngx_int_t ngx_http_lua_script_add_copy_code( ngx_http_lua_script_compile_t *sc, ngx_str_t *value, ngx_uint_t last); static ngx_int_t ngx_http_lua_script_compile(ngx_http_lua_script_compile_t *sc); static ngx_int_t ngx_http_lua_script_add_capture_code( ngx_http_lua_script_compile_t *sc, ngx_uint_t n); static size_t ngx_http_lua_script_copy_capture_len_code( ngx_http_lua_script_engine_t *e); static void ngx_http_lua_script_copy_capture_code( ngx_http_lua_script_engine_t *e); static ngx_int_t ngx_http_lua_script_done(ngx_http_lua_script_compile_t *sc); static ngx_int_t ngx_http_lua_script_init_arrays( ngx_http_lua_script_compile_t *sc); ngx_int_t ngx_http_lua_compile_complex_value(ngx_http_lua_compile_complex_value_t *ccv) { ngx_str_t *v; ngx_uint_t i, n, nv; ngx_array_t lengths, values, *pl, *pv; ngx_http_lua_script_compile_t sc; v = ccv->value; nv = 0; for (i = 0; i < v->len; i++) { if (v->data[i] == '$') { nv++; } } ccv->complex_value->value = *v; ccv->complex_value->lengths = NULL; ccv->complex_value->values = NULL; if (nv == 0) { return NGX_OK; } n = nv * (2 * sizeof(ngx_http_lua_script_copy_code_t) + sizeof(ngx_http_lua_script_capture_code_t)) + sizeof(uintptr_t); if (ngx_array_init(&lengths, ccv->pool, n, 1) != NGX_OK) { return NGX_ERROR; } n = (nv * (2 * sizeof(ngx_http_lua_script_copy_code_t) + sizeof(ngx_http_lua_script_capture_code_t)) + sizeof(uintptr_t) + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1); if (ngx_array_init(&values, ccv->pool, n, 1) != NGX_OK) { return NGX_ERROR; } pl = &lengths; pv = &values; ngx_memzero(&sc, sizeof(ngx_http_lua_script_compile_t)); sc.pool = ccv->pool; sc.log = ccv->log; sc.source = v; sc.lengths = &pl; sc.values = &pv; sc.complete_lengths = 1; sc.complete_values = 1; if (ngx_http_lua_script_compile(&sc) != NGX_OK) { ngx_array_destroy(&lengths); ngx_array_destroy(&values); return NGX_ERROR; } ccv->complex_value->lengths = lengths.elts; ccv->complex_value->values = values.elts; return NGX_OK; } ngx_int_t ngx_http_lua_complex_value(ngx_http_request_t *r, ngx_str_t *subj, size_t offset, ngx_int_t count, int *cap, ngx_http_lua_complex_value_t *val, luaL_Buffer *luabuf) { size_t len; u_char *p; ngx_http_lua_script_code_pt code; ngx_http_lua_script_len_code_pt lcode; ngx_http_lua_script_engine_t e; if (val->lengths == NULL) { luaL_addlstring(luabuf, (char *) &subj->data[offset], cap[0] - offset); luaL_addlstring(luabuf, (char *) val->value.data, val->value.len); return NGX_OK; } ngx_memzero(&e, sizeof(ngx_http_lua_script_engine_t)); e.log = r->connection->log; e.ncaptures = count * 2; e.captures = cap; e.captures_data = subj->data; e.ip = val->lengths; len = 0; while (*(uintptr_t *) e.ip) { lcode = *(ngx_http_lua_script_len_code_pt *) e.ip; len += lcode(&e); } p = ngx_pnalloc(r->pool, len); if (p == NULL) { return NGX_ERROR; } e.ip = val->values; e.pos = p; while (*(uintptr_t *) e.ip) { code = *(ngx_http_lua_script_code_pt *) e.ip; code((ngx_http_lua_script_engine_t *) &e); } luaL_addlstring(luabuf, (char *) &subj->data[offset], cap[0] - offset); luaL_addlstring(luabuf, (char *) p, len); ngx_pfree(r->pool, p); return NGX_OK; } static ngx_int_t ngx_http_lua_script_compile(ngx_http_lua_script_compile_t *sc) { u_char ch; ngx_str_t name; ngx_uint_t i, bracket; unsigned num_var; ngx_uint_t n = 0; if (ngx_http_lua_script_init_arrays(sc) != NGX_OK) { return NGX_ERROR; } for (i = 0; i < sc->source->len; /* void */ ) { name.len = 0; if (sc->source->data[i] == '$') { if (++i == sc->source->len) { goto invalid_variable; } if (sc->source->data[i] == '$') { name.data = &sc->source->data[i]; i++; name.len++; if (ngx_http_lua_script_add_copy_code(sc, &name, (i == sc->source->len)) != NGX_OK) { return NGX_ERROR; } continue; } if (sc->source->data[i] >= '0' && sc->source->data[i] <= '9') { num_var = 1; n = 0; } else { num_var = 0; } if (sc->source->data[i] == '{') { bracket = 1; if (++i == sc->source->len) { goto invalid_variable; } if (sc->source->data[i] >= '0' && sc->source->data[i] <= '9') { num_var = 1; n = 0; } name.data = &sc->source->data[i]; } else { bracket = 0; name.data = &sc->source->data[i]; } for ( /* void */ ; i < sc->source->len; i++, name.len++) { ch = sc->source->data[i]; if (ch == '}' && bracket) { i++; bracket = 0; break; } if (num_var) { if (ch >= '0' && ch <= '9') { n = n * 10 + (ch - '0'); continue; } break; } /* not a number variable like $1, $2, etc */ if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_') { continue; } break; } if (bracket) { ngx_log_error(NGX_LOG_ERR, sc->log, 0, "the closing bracket in \"%V\" " "variable is missing", &name); return NGX_ERROR; } if (name.len == 0) { goto invalid_variable; } if (!num_var) { ngx_log_error(NGX_LOG_ERR, sc->log, 0, "attempt to use named capturing variable " "\"%V\" (named captures not supported yet)", &name); return NGX_ERROR; } sc->variables++; if (ngx_http_lua_script_add_capture_code(sc, n) != NGX_OK) { return NGX_ERROR; } continue; } name.data = &sc->source->data[i]; while (i < sc->source->len) { if (sc->source->data[i] == '$') { break; } i++; name.len++; } if (ngx_http_lua_script_add_copy_code(sc, &name, (i == sc->source->len)) != NGX_OK) { return NGX_ERROR; } } return ngx_http_lua_script_done(sc); invalid_variable: ngx_log_error(NGX_LOG_ERR, sc->log, 0, "lua script: invalid capturing variable name found in \"%V\"", sc->source); return NGX_ERROR; } static ngx_int_t ngx_http_lua_script_add_copy_code(ngx_http_lua_script_compile_t *sc, ngx_str_t *value, ngx_uint_t last) { size_t size, len; ngx_http_lua_script_copy_code_t *code; len = value->len; code = ngx_http_lua_script_add_code(*sc->lengths, sizeof(ngx_http_lua_script_copy_code_t)); if (code == NULL) { return NGX_ERROR; } code->code = (ngx_http_lua_script_code_pt) ngx_http_lua_script_copy_len_code; code->len = len; size = (sizeof(ngx_http_lua_script_copy_code_t) + len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1); code = ngx_http_lua_script_add_code(*sc->values, size); if (code == NULL) { return NGX_ERROR; } code->code = ngx_http_lua_script_copy_code; code->len = len; ngx_memcpy((u_char *) code + sizeof(ngx_http_lua_script_copy_code_t), value->data, value->len); return NGX_OK; } static size_t ngx_http_lua_script_copy_len_code(ngx_http_lua_script_engine_t *e) { ngx_http_lua_script_copy_code_t *code; code = (ngx_http_lua_script_copy_code_t *) e->ip; e->ip += sizeof(ngx_http_lua_script_copy_code_t); return code->len; } static void ngx_http_lua_script_copy_code(ngx_http_lua_script_engine_t *e) { u_char *p; ngx_http_lua_script_copy_code_t *code; code = (ngx_http_lua_script_copy_code_t *) e->ip; p = e->pos; if (!e->skip) { e->pos = ngx_copy(p, e->ip + sizeof(ngx_http_lua_script_copy_code_t), code->len); } e->ip += sizeof(ngx_http_lua_script_copy_code_t) + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1)); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->log, 0, "lua script copy: \"%*s\"", e->pos - p, p); } static ngx_int_t ngx_http_lua_script_add_capture_code(ngx_http_lua_script_compile_t *sc, ngx_uint_t n) { ngx_http_lua_script_capture_code_t *code; code = ngx_http_lua_script_add_code(*sc->lengths, sizeof(ngx_http_lua_script_capture_code_t)); if (code == NULL) { return NGX_ERROR; } code->code = (ngx_http_lua_script_code_pt) ngx_http_lua_script_copy_capture_len_code; code->n = 2 * n; code = ngx_http_lua_script_add_code(*sc->values, sizeof(ngx_http_lua_script_capture_code_t)); if (code == NULL) { return NGX_ERROR; } code->code = ngx_http_lua_script_copy_capture_code; code->n = 2 * n; return NGX_OK; } static size_t ngx_http_lua_script_copy_capture_len_code(ngx_http_lua_script_engine_t *e) { int *cap; ngx_uint_t n; ngx_http_lua_script_capture_code_t *code; code = (ngx_http_lua_script_capture_code_t *) e->ip; e->ip += sizeof(ngx_http_lua_script_capture_code_t); n = code->n; if (n < e->ncaptures) { cap = e->captures; return cap[n + 1] - cap[n]; } return 0; } static void ngx_http_lua_script_copy_capture_code(ngx_http_lua_script_engine_t *e) { int *cap; u_char *p, *pos; ngx_uint_t n; ngx_http_lua_script_capture_code_t *code; code = (ngx_http_lua_script_capture_code_t *) e->ip; e->ip += sizeof(ngx_http_lua_script_capture_code_t); n = code->n; pos = e->pos; if (n < e->ncaptures) { cap = e->captures; p = e->captures_data; e->pos = ngx_copy(pos, &p[cap[n]], cap[n + 1] - cap[n]); } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->log, 0, "lua script capture: \"%*s\"", e->pos - pos, pos); } static ngx_int_t ngx_http_lua_script_init_arrays(ngx_http_lua_script_compile_t *sc) { ngx_uint_t n; if (*sc->lengths == NULL) { n = sc->variables * (2 * sizeof(ngx_http_lua_script_copy_code_t) + sizeof(ngx_http_lua_script_capture_code_t)) + sizeof(uintptr_t); *sc->lengths = ngx_array_create(sc->pool, n, 1); if (*sc->lengths == NULL) { return NGX_ERROR; } } if (*sc->values == NULL) { n = (sc->variables * (2 * sizeof(ngx_http_lua_script_copy_code_t) + sizeof(ngx_http_lua_script_capture_code_t)) + sizeof(uintptr_t) + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1); *sc->values = ngx_array_create(sc->pool, n, 1); if (*sc->values == NULL) { return NGX_ERROR; } } sc->variables = 0; return NGX_OK; } static ngx_int_t ngx_http_lua_script_done(ngx_http_lua_script_compile_t *sc) { uintptr_t *code; if (sc->complete_lengths) { code = ngx_http_lua_script_add_code(*sc->lengths, sizeof(uintptr_t)); if (code == NULL) { return NGX_ERROR; } *code = (uintptr_t) NULL; } if (sc->complete_values) { code = ngx_http_lua_script_add_code(*sc->values, sizeof(uintptr_t)); if (code == NULL) { return NGX_ERROR; } *code = (uintptr_t) NULL; } return NGX_OK; } static void * ngx_http_lua_script_add_code(ngx_array_t *codes, size_t size) { return ngx_array_push_n(codes, size); } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_bodyfilterby.c0000664000000000000000000003566212305451335020764 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_bodyfilterby.h" #include "ngx_http_lua_exception.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_pcrefix.h" #include "ngx_http_lua_time.h" #include "ngx_http_lua_log.h" #include "ngx_http_lua_regex.h" #include "ngx_http_lua_cache.h" #include "ngx_http_lua_headers.h" #include "ngx_http_lua_variable.h" #include "ngx_http_lua_string.h" #include "ngx_http_lua_misc.h" #include "ngx_http_lua_consts.h" #include "ngx_http_lua_output.h" static void ngx_http_lua_body_filter_by_lua_env(lua_State *L, ngx_http_request_t *r, ngx_chain_t *in); static ngx_http_output_body_filter_pt ngx_http_next_body_filter; /* light user data key for the ngx_chain_t *in pointer in the * Lua VM registory */ static char ngx_http_lua_body_filter_chain_key; /** * Set environment table for the given code closure. * * Before: * | code closure | <- top * | ... | * * After: * | code closure | <- top * | ... | * */ static void ngx_http_lua_body_filter_by_lua_env(lua_State *L, ngx_http_request_t *r, ngx_chain_t *in) { /* set nginx request pointer to current lua thread's globals table */ ngx_http_lua_set_req(L, r); lua_pushlightuserdata(L, &ngx_http_lua_body_filter_chain_key); lua_pushlightuserdata(L, in); lua_rawset(L, LUA_GLOBALSINDEX); /** * we want to create empty environment for current script * * setmetatable({}, {__index = _G}) * * if a function or symbol is not defined in our env, __index will lookup * in the global env. * * all variables created in the script-env will be thrown away at the end * of the script run. * */ ngx_http_lua_create_new_global_table(L, 0 /* narr */, 1 /* nrec */); /* {{{ make new env inheriting main thread's globals table */ lua_createtable(L, 0, 1 /* nrec */); /* the metatable for the new env */ lua_pushvalue(L, LUA_GLOBALSINDEX); lua_setfield(L, -2, "__index"); lua_setmetatable(L, -2); /* setmetatable({}, {__index = _G}) */ /* }}} */ lua_setfenv(L, -2); /* set new running env for the code closure */ } ngx_int_t ngx_http_lua_body_filter_by_chunk(lua_State *L, ngx_http_request_t *r, ngx_chain_t *in) { ngx_int_t rc; u_char *err_msg; size_t len; #if (NGX_PCRE) ngx_pool_t *old_pool; #endif dd("initialize nginx context in Lua VM, code chunk at stack top sp = 1"); ngx_http_lua_body_filter_by_lua_env(L, r, in); #if (NGX_PCRE) /* XXX: work-around to nginx regex subsystem */ old_pool = ngx_http_lua_pcre_malloc_init(r->pool); #endif lua_pushcfunction(L, ngx_http_lua_traceback); lua_insert(L, 1); /* put it under chunk and args */ dd("protected call user code"); rc = lua_pcall(L, 0, 1, 1); lua_remove(L, 1); /* remove traceback function */ #if (NGX_PCRE) /* XXX: work-around to nginx regex subsystem */ ngx_http_lua_pcre_malloc_done(old_pool); #endif if (rc != 0) { /* error occured */ err_msg = (u_char *) lua_tolstring(L, -1, &len); if (err_msg == NULL) { err_msg = (u_char *) "unknown reason"; len = sizeof("unknown reason") - 1; } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to run body_filter_by_lua*: %*s", len, err_msg); lua_settop(L, 0); /* clear remaining elems on stack */ return NGX_ERROR; } /* rc == 0 */ rc = (ngx_int_t) lua_tointeger(L, -1); dd("got return value: %d", (int) rc); lua_settop(L, 0); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return NGX_ERROR; } return NGX_OK; } ngx_int_t ngx_http_lua_body_filter_inline(ngx_http_request_t *r, ngx_chain_t *in) { lua_State *L; ngx_int_t rc; ngx_http_lua_loc_conf_t *llcf; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); L = ngx_http_lua_get_lua_vm(r, NULL); /* load Lua inline script (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadbuffer(L, llcf->body_filter_src.value.data, llcf->body_filter_src.value.len, llcf->body_filter_src_key, "body_filter_by_lua"); if (rc != NGX_OK) { return NGX_ERROR; } rc = ngx_http_lua_body_filter_by_chunk(L, r, in); dd("body filter by chunk returns %d", (int) rc); if (rc != NGX_OK) { return NGX_ERROR; } return NGX_OK; } ngx_int_t ngx_http_lua_body_filter_file(ngx_http_request_t *r, ngx_chain_t *in) { lua_State *L; ngx_int_t rc; u_char *script_path; ngx_http_lua_loc_conf_t *llcf; ngx_str_t eval_src; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); /* Eval nginx variables in code path string first */ if (ngx_http_complex_value(r, &llcf->body_filter_src, &eval_src) != NGX_OK) { return NGX_ERROR; } script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data, eval_src.len); if (script_path == NULL) { return NGX_ERROR; } L = ngx_http_lua_get_lua_vm(r, NULL); /* load Lua script file (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadfile(L, script_path, llcf->body_filter_src_key); if (rc != NGX_OK) { return NGX_ERROR; } /* make sure we have a valid code chunk */ assert(lua_isfunction(L, -1)); rc = ngx_http_lua_body_filter_by_chunk(L, r, in); if (rc != NGX_OK) { return NGX_ERROR; } return NGX_OK; } static ngx_int_t ngx_http_lua_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { ngx_http_lua_loc_conf_t *llcf; ngx_http_lua_ctx_t *ctx; ngx_int_t rc; uint16_t old_context; ngx_http_cleanup_t *cln; lua_State *L; ngx_chain_t *out; ngx_buf_tag_t tag; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua body filter for user lua code, uri \"%V\"", &r->uri); if (in == NULL) { return ngx_http_next_body_filter(r, in); } llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->body_filter_handler == NULL) { dd("no body filter handler found"); return ngx_http_next_body_filter(r, in); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); dd("ctx = %p", ctx); if (ctx == NULL) { ctx = ngx_http_lua_create_ctx(r); if (ctx == NULL) { return NGX_ERROR; } } if (ctx->seen_last_in_filter) { for (/* void */; in; in = in->next) { dd("mark the buf as consumed: %d", (int) ngx_buf_size(in->buf)); in->buf->pos = in->buf->last; in->buf->file_pos = in->buf->file_last; } return NGX_OK; } if (ctx->cleanup == NULL) { cln = ngx_http_cleanup_add(r, 0); if (cln == NULL) { return NGX_ERROR; } cln->handler = ngx_http_lua_request_cleanup_handler; cln->data = ctx; ctx->cleanup = &cln->handler; } old_context = ctx->context; ctx->context = NGX_HTTP_LUA_CONTEXT_BODY_FILTER; dd("calling body filter handler"); rc = llcf->body_filter_handler(r, in); dd("calling body filter handler returned %d", (int) rc); ctx->context = old_context; if (rc != NGX_OK) { return NGX_ERROR; } L = ngx_http_lua_get_lua_vm(r, ctx); lua_pushlightuserdata(L, &ngx_http_lua_body_filter_chain_key); lua_rawget(L, LUA_GLOBALSINDEX); out = lua_touserdata(L, -1); lua_pop(L, 1); if (in == out) { return ngx_http_next_body_filter(r, in); } /* in != out */ rc = ngx_http_next_body_filter(r, out); if (rc == NGX_ERROR) { return NGX_ERROR; } tag = (ngx_buf_tag_t) &ngx_http_lua_module; #if nginx_version >= 1001004 ngx_chain_update_chains(r->pool, #else ngx_chain_update_chains( #endif &ctx->free_bufs, &ctx->busy_bufs, &out, tag); return rc; } ngx_int_t ngx_http_lua_body_filter_init(void) { dd("calling body filter init"); ngx_http_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_lua_body_filter; return NGX_OK; } int ngx_http_lua_body_filter_param_get(lua_State *L) { u_char *data, *p; size_t size; ngx_chain_t *cl; ngx_buf_t *b; int idx; ngx_chain_t *in; idx = luaL_checkint(L, 2); dd("index: %d", idx); if (idx != 1 && idx != 2) { lua_pushnil(L); return 1; } lua_pushlightuserdata(L, &ngx_http_lua_body_filter_chain_key); lua_rawget(L, LUA_GLOBALSINDEX); in = lua_touserdata(L, -1); if (idx == 2) { /* asking for the eof argument */ for (cl = in; cl; cl = cl->next) { if (cl->buf->last_buf || cl->buf->last_in_chain) { lua_pushboolean(L, 1); return 1; } } lua_pushboolean(L, 0); return 1; } /* idx == 1 */ size = 0; if (in->next == NULL) { dd("seen only single buffer"); b = in->buf; lua_pushlstring(L, (char *) b->pos, b->last - b->pos); return 1; } dd("seen multiple buffers"); for (cl = in; cl; cl = cl->next) { b = cl->buf; size += b->last - b->pos; if (b->last_buf || b->last_in_chain) { break; } } data = (u_char *) lua_newuserdata(L, size); for (p = data, cl = in; cl; cl = cl->next) { b = cl->buf; p = ngx_copy(p, b->pos, b->last - b->pos); if (b->last_buf || b->last_in_chain) { break; } } lua_pushlstring(L, (char *) data, size); return 1; } int ngx_http_lua_body_filter_param_set(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) { int type; int idx; u_char *data; size_t size; unsigned last; ngx_chain_t *cl; ngx_chain_t *in; ngx_buf_tag_t tag; idx = luaL_checkint(L, 2); dd("index: %d", idx); if (idx != 1 && idx != 2) { return luaL_error(L, "bad index: %d", idx); } if (idx == 2) { /* overwriting the eof flag */ last = lua_toboolean(L, 3); lua_pushlightuserdata(L, &ngx_http_lua_body_filter_chain_key); lua_rawget(L, LUA_GLOBALSINDEX); in = lua_touserdata(L, -1); if (last) { ctx->seen_last_in_filter = 1; for (cl = in; cl; cl = cl->next) { if (cl->next == NULL) { if (r == r->main) { cl->buf->last_buf = 1; } else { cl->buf->last_in_chain = 1; } break; } } } else { /* last == 0 */ for (cl = in; cl; cl = cl->next) { if (cl->buf->last_buf) { cl->buf->last_buf = 0; } if (cl->buf->last_in_chain) { cl->buf->last_in_chain = 0; } if (cl->buf->last - cl->buf->pos == 0) { cl->buf->temporary = 0; cl->buf->memory = 0; cl->buf->mmap = 0; cl->buf->in_file = 0; cl->buf->sync = 1; } } } return 0; } /* idx == 1, overwriting the chunk data */ type = lua_type(L, 3); switch (type) { case LUA_TSTRING: case LUA_TNUMBER: data = (u_char *) lua_tolstring(L, 3, &size); break; case LUA_TNIL: /* discard the buffers */ lua_pushlightuserdata(L, &ngx_http_lua_body_filter_chain_key); /* key */ lua_pushvalue(L, -1); /* key key */ lua_rawget(L, LUA_GLOBALSINDEX); /* key val */ in = lua_touserdata(L, -1); lua_pop(L, 1); /* key */ for (cl = in; cl; cl = cl->next) { dd("mark the buf as consumed: %d", (int) ngx_buf_size(cl->buf)); cl->buf->pos = cl->buf->last; cl->buf->file_pos = cl->buf->file_last; cl->buf->temporary = 0; cl->buf->memory = 0; cl->buf->mmap = 0; cl->buf->in_file = 0; cl->buf->sync = 1; } return 0; case LUA_TTABLE: size = ngx_http_lua_calc_strlen_in_table(L, 3 /* index */, 3 /* arg */, 1 /* strict */); data = NULL; break; default: return luaL_error(L, "bad chunk data type: %s", lua_typename(L, type)); } lua_pushlightuserdata(L, &ngx_http_lua_body_filter_chain_key); lua_rawget(L, LUA_GLOBALSINDEX); in = lua_touserdata(L, -1); lua_pop(L, 1); last = 0; for (cl = in; cl; cl = cl->next) { if (cl->buf->last_buf || cl->buf->last_in_chain) { last = 1; } dd("mark the buf as consumed: %d", (int) ngx_buf_size(cl->buf)); cl->buf->pos = cl->buf->last; } if (size == 0) { if (last) { if (r == r->main) { in->buf->last_buf = 1; } else { in->buf->last_in_chain = 1; } } else { for (cl = in; cl; cl = cl->next) { cl->buf->sync = 1; cl->buf->temporary = 0; cl->buf->memory = 0; cl->buf->mmap = 0; cl->buf->in_file = 0; } } return 0; } tag = (ngx_buf_tag_t) &ngx_http_lua_module; cl = ngx_http_lua_chains_get_free_buf(r->connection->log, r->pool, &ctx->free_bufs, size, tag); if (cl == NULL) { return luaL_error(L, "out of memory"); } if (type == LUA_TTABLE) { cl->buf->last = ngx_http_lua_copy_str_in_table(L, 3, cl->buf->last); } else { cl->buf->last = ngx_copy(cl->buf->pos, data, size); } if (last) { ctx->seen_last_in_filter = 1; if (r == r->main) { cl->buf->last_buf = 1; } else { cl->buf->last_in_chain = 1; } } lua_pushlightuserdata(L, &ngx_http_lua_body_filter_chain_key); lua_pushlightuserdata(L, cl); lua_rawset(L, LUA_GLOBALSINDEX); return 0; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_api.c0000664000000000000000000000277512305451335017036 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #include "ddebug.h" #include "ngx_http_lua_common.h" #include "api/ngx_http_lua_api.h" #include "ngx_http_lua_util.h" lua_State * ngx_http_lua_get_global_state(ngx_conf_t *cf) { ngx_http_lua_main_conf_t *lmcf; lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module); return lmcf->lua; } ngx_http_request_t * ngx_http_lua_get_request(lua_State *L) { return ngx_http_lua_get_req(L); } ngx_int_t ngx_http_lua_add_package_preload(ngx_conf_t *cf, const char *package, lua_CFunction func) { lua_State *L; ngx_http_lua_main_conf_t *lmcf; ngx_http_lua_preload_hook_t *hook; lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module); L = lmcf->lua; if (L) { lua_getglobal(L, "package"); lua_getfield(L, -1, "preload"); lua_pushcfunction(L, func); lua_setfield(L, -2, package); lua_pop(L, 2); return NGX_OK; } /* L == NULL */ if (lmcf->preload_hooks == NULL) { lmcf->preload_hooks = ngx_array_create(cf->pool, 4, sizeof(ngx_http_lua_preload_hook_t)); if (lmcf->preload_hooks == NULL) { return NGX_ERROR; } } hook = ngx_array_push(lmcf->preload_hooks); if (hook == NULL) { return NGX_ERROR; } hook->package = (u_char *) package; hook->loader = func; return NGX_OK; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_ctx.c0000664000000000000000000000643412305451335017057 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_ctx.h" int ngx_http_lua_ngx_get_ctx(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } if (ctx->ctx_ref == LUA_NOREF) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua create ngx.ctx table for the current request"); lua_pushliteral(L, ngx_http_lua_ctx_tables_key); lua_rawget(L, LUA_REGISTRYINDEX); lua_createtable(L, 0 /* narr */, 4 /* nrec */); lua_pushvalue(L, -1); ctx->ctx_ref = luaL_ref(L, -3); return 1; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua fetching existing ngx.ctx table for the current " "request"); lua_pushliteral(L, ngx_http_lua_ctx_tables_key); lua_rawget(L, LUA_REGISTRYINDEX); lua_rawgeti(L, -1, ctx->ctx_ref); return 1; } int ngx_http_lua_ngx_set_ctx(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } return ngx_http_lua_ngx_set_ctx_helper(L, r, ctx, 3); } int ngx_http_lua_ngx_set_ctx_helper(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, int index) { if (index < 0) { index = lua_gettop(L) + index + 1; } if (ctx->ctx_ref == LUA_NOREF) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua create ngx.ctx table for the current request"); lua_pushliteral(L, ngx_http_lua_ctx_tables_key); lua_rawget(L, LUA_REGISTRYINDEX); lua_pushvalue(L, index); ctx->ctx_ref = luaL_ref(L, -2); lua_pop(L, 1); return 0; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua fetching existing ngx.ctx table for the current " "request"); lua_pushliteral(L, ngx_http_lua_ctx_tables_key); lua_rawget(L, LUA_REGISTRYINDEX); luaL_unref(L, -1, ctx->ctx_ref); lua_pushvalue(L, index); ctx->ctx_ref = luaL_ref(L, -2); lua_pop(L, 1); return 0; } #ifndef NGX_HTTP_LUA_NO_FFI_API int ngx_http_lua_ffi_get_ctx_ref(ngx_http_request_t *r) { ngx_http_lua_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return NGX_HTTP_LUA_FFI_NO_REQ_CTX; } return ctx->ctx_ref; } int ngx_http_lua_ffi_set_ctx_ref(ngx_http_request_t *r, int ref) { ngx_http_lua_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return NGX_HTTP_LUA_FFI_NO_REQ_CTX; } ctx->ctx_ref = ref; return NGX_OK; } #endif /* NGX_HTTP_LUA_NO_FFI_API */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_socket_tcp.h0000664000000000000000000001021012305451335020407 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_SOCKET_TCP_H_INCLUDED_ #define _NGX_HTTP_LUA_SOCKET_TCP_H_INCLUDED_ #include "ngx_http_lua_common.h" #define NGX_HTTP_LUA_SOCKET_FT_ERROR 0x0001 #define NGX_HTTP_LUA_SOCKET_FT_TIMEOUT 0x0002 #define NGX_HTTP_LUA_SOCKET_FT_CLOSED 0x0004 #define NGX_HTTP_LUA_SOCKET_FT_RESOLVER 0x0008 #define NGX_HTTP_LUA_SOCKET_FT_BUFTOOSMALL 0x0010 #define NGX_HTTP_LUA_SOCKET_FT_NOMEM 0x0020 #define NGX_HTTP_LUA_SOCKET_FT_PARTIALWRITE 0x0040 #define NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT 0x0080 typedef struct ngx_http_lua_socket_tcp_upstream_s ngx_http_lua_socket_tcp_upstream_t; typedef int (*ngx_http_lua_socket_tcp_retval_handler)(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); typedef void (*ngx_http_lua_socket_tcp_upstream_handler_pt)( ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u); typedef struct { lua_State *lua_vm; /* active connections == out-of-pool reused connections * + in-pool connections */ ngx_uint_t active_connections; /* queues of ngx_http_lua_socket_pool_item_t: */ ngx_queue_t cache; ngx_queue_t free; u_char key[1]; } ngx_http_lua_socket_pool_t; struct ngx_http_lua_socket_tcp_upstream_s { ngx_http_lua_socket_tcp_retval_handler prepare_retvals; ngx_http_lua_socket_tcp_upstream_handler_pt read_event_handler; ngx_http_lua_socket_tcp_upstream_handler_pt write_event_handler; ngx_http_lua_socket_pool_t *socket_pool; ngx_http_lua_loc_conf_t *conf; ngx_http_cleanup_pt *cleanup; ngx_http_request_t *request; ngx_peer_connection_t peer; ngx_msec_t read_timeout; ngx_msec_t send_timeout; ngx_msec_t connect_timeout; ngx_http_upstream_resolved_t *resolved; ngx_chain_t *bufs_in; /* input data buffers */ ngx_chain_t *buf_in; /* last input data buffer */ ngx_buf_t buffer; /* receive buffer */ size_t length; size_t rest; ngx_uint_t ft_type; ngx_err_t socket_errno; ngx_int_t (*input_filter)(void *data, ssize_t bytes); void *input_filter_ctx; size_t request_len; ngx_chain_t *request_bufs; ngx_http_lua_co_ctx_t *co_ctx; ngx_uint_t reused; unsigned no_close:1; unsigned waiting:1; unsigned eof:1; unsigned body_downstream:1; unsigned raw_downstream:1; }; typedef struct ngx_http_lua_dfa_edge_s ngx_http_lua_dfa_edge_t; struct ngx_http_lua_dfa_edge_s { u_char chr; int new_state; ngx_http_lua_dfa_edge_t *next; }; typedef struct { ngx_http_lua_socket_tcp_upstream_t *upstream; ngx_str_t pattern; int state; ngx_http_lua_dfa_edge_t **recovering; unsigned inclusive:1; } ngx_http_lua_socket_compiled_pattern_t; typedef struct { ngx_http_lua_socket_pool_t *socket_pool; ngx_queue_t queue; ngx_connection_t *connection; socklen_t socklen; struct sockaddr_storage sockaddr; ngx_uint_t reused; } ngx_http_lua_socket_pool_item_t; void ngx_http_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L); void ngx_http_lua_inject_req_socket_api(lua_State *L); #endif /* _NGX_HTTP_LUA_SOCKET_TCP_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_socket_tcp.c0000664000000000000000000034101012305451334020406 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_socket_tcp.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_output.h" #include "ngx_http_lua_contentby.h" #include "ngx_http_lua_probe.h" #if 1 #undef ngx_http_lua_probe_info #define ngx_http_lua_probe_info(msg) #endif static int ngx_http_lua_socket_tcp(lua_State *L); static int ngx_http_lua_socket_tcp_connect(lua_State *L); static int ngx_http_lua_socket_tcp_receive(lua_State *L); static int ngx_http_lua_socket_tcp_send(lua_State *L); static int ngx_http_lua_socket_tcp_close(lua_State *L); static int ngx_http_lua_socket_tcp_setoption(lua_State *L); static int ngx_http_lua_socket_tcp_settimeout(lua_State *L); static void ngx_http_lua_socket_tcp_handler(ngx_event_t *ev); static ngx_int_t ngx_http_lua_socket_tcp_get_peer(ngx_peer_connection_t *pc, void *data); static void ngx_http_lua_socket_read_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u); static void ngx_http_lua_socket_send_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u); static void ngx_http_lua_socket_connected_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u); static void ngx_http_lua_socket_tcp_cleanup(void *data); static void ngx_http_lua_socket_tcp_finalize(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u); static ngx_int_t ngx_http_lua_socket_send(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u); static ngx_int_t ngx_http_lua_socket_test_connect(ngx_http_request_t *r, ngx_connection_t *c); static void ngx_http_lua_socket_handle_error(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type); static void ngx_http_lua_socket_handle_success(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u); static int ngx_http_lua_socket_tcp_send_retval_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); static int ngx_http_lua_socket_tcp_connect_retval_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); static void ngx_http_lua_socket_dummy_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u); static ngx_int_t ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u); static void ngx_http_lua_socket_read_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u); static int ngx_http_lua_socket_tcp_receive_retval_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); static ngx_int_t ngx_http_lua_socket_read_line(void *data, ssize_t bytes); static void ngx_http_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx); static int ngx_http_lua_socket_resolve_retval_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); static int ngx_http_lua_socket_error_retval_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); static ngx_int_t ngx_http_lua_socket_read_all(void *data, ssize_t bytes); static ngx_int_t ngx_http_lua_socket_read_until(void *data, ssize_t bytes); static ngx_int_t ngx_http_lua_socket_read_chunk(void *data, ssize_t bytes); static int ngx_http_lua_socket_tcp_receiveuntil(lua_State *L); static int ngx_http_lua_socket_receiveuntil_iterator(lua_State *L); static ngx_int_t ngx_http_lua_socket_compile_pattern(u_char *data, size_t len, ngx_http_lua_socket_compiled_pattern_t *cp, ngx_log_t *log); static int ngx_http_lua_socket_cleanup_compiled_pattern(lua_State *L); static int ngx_http_lua_req_socket(lua_State *L); static void ngx_http_lua_req_socket_rev_handler(ngx_http_request_t *r); static int ngx_http_lua_socket_tcp_getreusedtimes(lua_State *L); static int ngx_http_lua_socket_tcp_setkeepalive(lua_State *L); static ngx_int_t ngx_http_lua_get_keepalive_peer(ngx_http_request_t *r, lua_State *L, int key_index, ngx_http_lua_socket_tcp_upstream_t *u); static void ngx_http_lua_socket_keepalive_dummy_handler(ngx_event_t *ev); static ngx_int_t ngx_http_lua_socket_keepalive_close_handler(ngx_event_t *ev); static void ngx_http_lua_socket_keepalive_rev_handler(ngx_event_t *ev); static void ngx_http_lua_socket_free_pool(ngx_log_t *log, ngx_http_lua_socket_pool_t *spool); static int ngx_http_lua_socket_tcp_upstream_destroy(lua_State *L); static int ngx_http_lua_socket_downstream_destroy(lua_State *L); static ngx_int_t ngx_http_lua_socket_push_input_data(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L); static ngx_int_t ngx_http_lua_socket_add_pending_data(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, u_char *pos, size_t len, u_char *pat, int prefix, int old_state); static ngx_int_t ngx_http_lua_socket_add_input_buffer(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u); static ngx_int_t ngx_http_lua_socket_insert_buffer(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, u_char *pat, size_t prefix); static ngx_int_t ngx_http_lua_socket_tcp_resume(ngx_http_request_t *r); static void ngx_http_lua_tcp_resolve_cleanup(void *data); static void ngx_http_lua_coctx_cleanup(void *data); enum { SOCKET_CTX_INDEX = 1, SOCKET_TIMEOUT_INDEX = 2, SOCKET_KEY_INDEX = 3 }; static char ngx_http_lua_req_socket_metatable_key; static char ngx_http_lua_raw_req_socket_metatable_key; static char ngx_http_lua_tcp_socket_metatable_key; void ngx_http_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) { ngx_int_t rc; lua_createtable(L, 0, 3 /* nrec */); /* ngx.socket */ lua_pushcfunction(L, ngx_http_lua_socket_tcp); lua_setfield(L, -2, "tcp"); { const char buf[] = "local sock = ngx.socket.tcp()" " local ok, err = sock:connect(...)" " if ok then return sock else return nil, err end"; rc = luaL_loadbuffer(L, buf, sizeof(buf) - 1, "ngx.socket.connect"); } if (rc != NGX_OK) { ngx_log_error(NGX_LOG_CRIT, log, 0, "failed to load Lua code for ngx.socket.connect(): %i", rc); } else { lua_setfield(L, -2, "connect"); } lua_setfield(L, -2, "socket"); /* {{{req socket object metatable */ lua_pushlightuserdata(L, &ngx_http_lua_req_socket_metatable_key); lua_createtable(L, 0 /* narr */, 3 /* nrec */); lua_pushcfunction(L, ngx_http_lua_socket_tcp_receive); lua_setfield(L, -2, "receive"); lua_pushcfunction(L, ngx_http_lua_socket_tcp_receiveuntil); lua_setfield(L, -2, "receiveuntil"); lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeout); lua_setfield(L, -2, "settimeout"); /* ngx socket mt */ lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); lua_rawset(L, LUA_REGISTRYINDEX); /* }}} */ /* {{{raw req socket object metatable */ lua_pushlightuserdata(L, &ngx_http_lua_raw_req_socket_metatable_key); lua_createtable(L, 0 /* narr */, 4 /* nrec */); lua_pushcfunction(L, ngx_http_lua_socket_tcp_receive); lua_setfield(L, -2, "receive"); lua_pushcfunction(L, ngx_http_lua_socket_tcp_receiveuntil); lua_setfield(L, -2, "receiveuntil"); lua_pushcfunction(L, ngx_http_lua_socket_tcp_send); lua_setfield(L, -2, "send"); lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeout); lua_setfield(L, -2, "settimeout"); /* ngx socket mt */ lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); lua_rawset(L, LUA_REGISTRYINDEX); /* }}} */ /* {{{tcp object metatable */ lua_pushlightuserdata(L, &ngx_http_lua_tcp_socket_metatable_key); lua_createtable(L, 0 /* narr */, 10 /* nrec */); lua_pushcfunction(L, ngx_http_lua_socket_tcp_connect); lua_setfield(L, -2, "connect"); lua_pushcfunction(L, ngx_http_lua_socket_tcp_receive); lua_setfield(L, -2, "receive"); lua_pushcfunction(L, ngx_http_lua_socket_tcp_receiveuntil); lua_setfield(L, -2, "receiveuntil"); lua_pushcfunction(L, ngx_http_lua_socket_tcp_send); lua_setfield(L, -2, "send"); lua_pushcfunction(L, ngx_http_lua_socket_tcp_close); lua_setfield(L, -2, "close"); lua_pushcfunction(L, ngx_http_lua_socket_tcp_setoption); lua_setfield(L, -2, "setoption"); lua_pushcfunction(L, ngx_http_lua_socket_tcp_settimeout); lua_setfield(L, -2, "settimeout"); /* ngx socket mt */ lua_pushcfunction(L, ngx_http_lua_socket_tcp_getreusedtimes); lua_setfield(L, -2, "getreusedtimes"); lua_pushcfunction(L, ngx_http_lua_socket_tcp_setkeepalive); lua_setfield(L, -2, "setkeepalive"); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); lua_rawset(L, LUA_REGISTRYINDEX); /* }}} */ } void ngx_http_lua_inject_req_socket_api(lua_State *L) { lua_pushcfunction(L, ngx_http_lua_req_socket); lua_setfield(L, -2, "socket"); } static int ngx_http_lua_socket_tcp(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; if (lua_gettop(L) != 0) { return luaL_error(L, "expecting zero arguments, but got %d", lua_gettop(L)); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER); lua_createtable(L, 3 /* narr */, 1 /* nrec */); lua_pushlightuserdata(L, &ngx_http_lua_tcp_socket_metatable_key); lua_rawget(L, LUA_REGISTRYINDEX); lua_setmetatable(L, -2); dd("top: %d", lua_gettop(L)); return 1; } static int ngx_http_lua_socket_tcp_connect(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_str_t host; int port; ngx_resolver_ctx_t *rctx, temp; ngx_http_core_loc_conf_t *clcf; int saved_top; int n; u_char *p; size_t len; ngx_url_t url; ngx_int_t rc; ngx_http_lua_loc_conf_t *llcf; ngx_peer_connection_t *pc; int timeout; unsigned custom_pool; int key_index; const char *msg; ngx_http_lua_co_ctx_t *coctx; ngx_http_lua_socket_tcp_upstream_t *u; n = lua_gettop(L); if (n != 2 && n != 3 && n != 4) { return luaL_error(L, "ngx.socket connect: expecting 2, 3, or 4 " "arguments (including the object), but seen %d", n); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER); luaL_checktype(L, 1, LUA_TTABLE); p = (u_char *) luaL_checklstring(L, 2, &len); host.data = ngx_palloc(r->pool, len + 1); if (host.data == NULL) { return luaL_error(L, "out of memory"); } host.len = len; ngx_memcpy(host.data, p, len); host.data[len] = '\0'; key_index = 2; custom_pool = 0; if (lua_type(L, n) == LUA_TTABLE) { /* found the last optional option table */ lua_getfield(L, n, "pool"); switch (lua_type(L, -1)) { case LUA_TNUMBER: lua_tostring(L, -1); case LUA_TSTRING: custom_pool = 1; lua_pushvalue(L, -1); lua_rawseti(L, 1, SOCKET_KEY_INDEX); key_index = n + 1; break; case LUA_TNIL: lua_pop(L, 2); break; default: msg = lua_pushfstring(L, "bad \"pool\" option type: %s", luaL_typename(L, -1)); luaL_argerror(L, n, msg); break; } n--; } if (n == 3) { port = luaL_checkinteger(L, 3); if (port < 0 || port > 65536) { lua_pushnil(L); lua_pushfstring(L, "bad port number: %d", port); return 2; } if (!custom_pool) { lua_pushliteral(L, ":"); lua_insert(L, 3); lua_concat(L, 3); } dd("socket key: %s", lua_tostring(L, -1)); } else { /* n == 2 */ port = 0; } if (!custom_pool) { /* the key's index is 2 */ lua_pushvalue(L, 2); lua_rawseti(L, 1, SOCKET_KEY_INDEX); } lua_rawgeti(L, 1, SOCKET_CTX_INDEX); u = lua_touserdata(L, -1); lua_pop(L, 1); if (u) { if (u->waiting) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; } if (u->body_downstream || u->raw_downstream) { return luaL_error(L, "attempt to re-connect a request socket"); } if (u->peer.connection) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket reconnect without shutting down"); ngx_http_lua_socket_tcp_finalize(r, u); } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua reuse socket upstream ctx"); } else { u = lua_newuserdata(L, sizeof(ngx_http_lua_socket_tcp_upstream_t)); if (u == NULL) { return luaL_error(L, "out of memory"); } #if 1 lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ lua_pushcfunction(L, ngx_http_lua_socket_tcp_upstream_destroy); lua_setfield(L, -2, "__gc"); lua_setmetatable(L, -2); #endif lua_rawseti(L, 1, SOCKET_CTX_INDEX); } ngx_memzero(u, sizeof(ngx_http_lua_socket_tcp_upstream_t)); coctx = ctx->cur_co_ctx; u->request = r; /* set the controlling request */ llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); u->conf = llcf; pc = &u->peer; pc->log = r->connection->log; pc->log_error = NGX_ERROR_ERR; dd("lua peer connection log: %p", pc->log); lua_rawgeti(L, 1, SOCKET_TIMEOUT_INDEX); timeout = (ngx_int_t) lua_tointeger(L, -1); lua_pop(L, 1); if (timeout > 0) { u->send_timeout = (ngx_msec_t) timeout; u->read_timeout = (ngx_msec_t) timeout; u->connect_timeout = (ngx_msec_t) timeout; } else { u->read_timeout = u->conf->read_timeout; u->send_timeout = u->conf->send_timeout; u->connect_timeout = u->conf->connect_timeout; } rc = ngx_http_lua_get_keepalive_peer(r, L, key_index, u); if (rc == NGX_OK) { lua_pushinteger(L, 1); return 1; } if (rc == NGX_ERROR) { lua_pushnil(L); lua_pushliteral(L, "error in get keepalive peer"); return 2; } /* rc == NGX_DECLINED */ ngx_memzero(&url, sizeof(ngx_url_t)); url.url.len = host.len; url.url.data = host.data; url.default_port = (in_port_t) port; url.no_resolve = 1; if (ngx_parse_url(r->pool, &url) != NGX_OK) { lua_pushnil(L); if (url.err) { lua_pushfstring(L, "failed to parse host name \"%s\": %s", host.data, url.err); } else { lua_pushfstring(L, "failed to parse host name \"%s\"", host.data); } return 2; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket connect timeout: %M", u->connect_timeout); u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t)); if (u->resolved == NULL) { return luaL_error(L, "out of memory"); } if (url.addrs && url.addrs[0].sockaddr) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket network address given directly"); u->resolved->sockaddr = url.addrs[0].sockaddr; u->resolved->socklen = url.addrs[0].socklen; u->resolved->naddrs = 1; u->resolved->host = url.addrs[0].name; } else { u->resolved->host = host; u->resolved->port = (in_port_t) port; } if (u->resolved->sockaddr) { rc = ngx_http_lua_socket_resolve_retval_handler(r, u, L); if (rc == NGX_AGAIN) { return lua_yield(L, 0); } return rc; } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); temp.name = host; rctx = ngx_resolve_start(clcf->resolver, &temp); if (rctx == NULL) { u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER; lua_pushnil(L); lua_pushliteral(L, "failed to start the resolver"); return 2; } if (rctx == NGX_NO_RESOLVER) { u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER; lua_pushnil(L); lua_pushfstring(L, "no resolver defined to resolve \"%s\"", host.data); return 2; } rctx->name = host; #if !defined(nginx_version) || nginx_version < 1005008 rctx->type = NGX_RESOLVE_A; #endif rctx->handler = ngx_http_lua_socket_resolve_handler; rctx->data = u; rctx->timeout = clcf->resolver_timeout; u->resolved->ctx = rctx; u->co_ctx = ctx->cur_co_ctx; coctx->data = u; saved_top = lua_gettop(L); coctx->cleanup = ngx_http_lua_tcp_resolve_cleanup; if (ngx_resolve_name(rctx) != NGX_OK) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket fail to run resolver immediately"); u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER; u->resolved->ctx = NULL; lua_pushnil(L); lua_pushfstring(L, "%s could not be resolved", host.data); return 2; } if (u->waiting == 1) { /* resolved and already connecting */ return lua_yield(L, 0); } n = lua_gettop(L) - saved_top; if (n) { /* errors occurred during resolving or connecting * or already connected */ return n; } /* still resolving */ u->waiting = 1; u->prepare_retvals = ngx_http_lua_socket_resolve_retval_handler; dd("setting data to %p", u); if (ctx->entered_content_phase) { r->write_event_handler = ngx_http_lua_content_wev_handler; } else { r->write_event_handler = ngx_http_core_run_phases; } return lua_yield(L, 0); } static void ngx_http_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx) { ngx_http_request_t *r; ngx_connection_t *c; ngx_http_upstream_resolved_t *ur; ngx_http_lua_ctx_t *lctx; lua_State *L; ngx_http_lua_socket_tcp_upstream_t *u; u_char *p; size_t len; #if defined(nginx_version) && nginx_version >= 1005008 socklen_t socklen; struct sockaddr *sockaddr; #else struct sockaddr_in *sin; #endif ngx_uint_t i; unsigned waiting; u = ctx->data; r = u->request; c = r->connection; ur = u->resolved; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "lua tcp socket resolve handler"); lctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (lctx == NULL) { return; } lctx->cur_co_ctx = u->co_ctx; u->co_ctx->cleanup = NULL; L = lctx->cur_co_ctx->co; waiting = u->waiting; if (ctx->state) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "lua tcp socket resolver error: %s (waiting: %d)", ngx_resolver_strerror(ctx->state), (int) u->waiting); lua_pushnil(L); lua_pushlstring(L, (char *) ctx->name.data, ctx->name.len); lua_pushfstring(L, " could not be resolved (%d: %s)", (int) ctx->state, ngx_resolver_strerror(ctx->state)); lua_concat(L, 2); u->prepare_retvals = ngx_http_lua_socket_error_retval_handler; ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_RESOLVER); if (waiting) { ngx_http_run_posted_requests(c); } return; } ur->naddrs = ctx->naddrs; ur->addrs = ctx->addrs; #if (NGX_DEBUG) { # if defined(nginx_version) && nginx_version >= 1005008 u_char text[NGX_SOCKADDR_STRLEN]; ngx_str_t addr; # else in_addr_t addr; # endif ngx_uint_t i; # if defined(nginx_version) && nginx_version >= 1005008 addr.data = text; for (i = 0; i < ctx->naddrs; i++) { addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen, text, NGX_SOCKADDR_STRLEN, 0); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "name was resolved to %V", &addr); } # else for (i = 0; i < ctx->naddrs; i++) { dd("addr i: %d %p", (int) i, &ctx->addrs[i]); addr = ntohl(ctx->addrs[i]); ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, "name was resolved to %ud.%ud.%ud.%ud", (addr >> 24) & 0xff, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff); } # endif } #endif if (ur->naddrs == 0) { ngx_resolve_name_done(ctx); u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER; lua_pushnil(L); lua_pushliteral(L, "name cannot be resolved to a address"); if (waiting) { ngx_http_run_posted_requests(c); } return; } if (ur->naddrs == 1) { i = 0; } else { i = ngx_random() % ur->naddrs; } dd("selected addr index: %d", (int) i); #if defined(nginx_version) && nginx_version >= 1005008 socklen = ur->addrs[i].socklen; sockaddr = ngx_palloc(r->pool, socklen); if (sockaddr == NULL) { goto nomem; } ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen); switch (sockaddr->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: ((struct sockaddr_in6 *) sockaddr)->sin6_port = htons(ur->port); break; #endif default: /* AF_INET */ ((struct sockaddr_in *) sockaddr)->sin_port = htons(ur->port); } p = ngx_pnalloc(r->pool, NGX_SOCKADDR_STRLEN); if (p == NULL) { goto nomem; } len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1); ur->sockaddr = sockaddr; ur->socklen = socklen; #else /* for nginx older than 1.5.8 */ len = NGX_INET_ADDRSTRLEN + sizeof(":65536") - 1; p = ngx_pnalloc(r->pool, len + sizeof(struct sockaddr_in)); if (p == NULL) { goto nomem; } sin = (struct sockaddr_in *) &p[len]; ngx_memzero(sin, sizeof(struct sockaddr_in)); len = ngx_inet_ntop(AF_INET, &ur->addrs[i], p, NGX_INET_ADDRSTRLEN); len = ngx_sprintf(&p[len], ":%d", ur->port) - p; sin->sin_family = AF_INET; sin->sin_port = htons(ur->port); sin->sin_addr.s_addr = ur->addrs[i]; ur->sockaddr = (struct sockaddr *) sin; ur->socklen = sizeof(struct sockaddr_in); #endif ur->host.data = p; ur->host.len = len; ur->naddrs = 1; ur->ctx = NULL; ngx_resolve_name_done(ctx); u->waiting = 0; if (waiting) { lctx->resume_handler = ngx_http_lua_socket_tcp_resume; r->write_event_handler(r); ngx_http_run_posted_requests(c); } else { (void) ngx_http_lua_socket_resolve_retval_handler(r, u, L); } return; nomem: ngx_resolve_name_done(ctx); u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_NOMEM; lua_pushnil(L); lua_pushliteral(L, "no memory"); if (waiting) { ngx_http_run_posted_requests(c); } } static int ngx_http_lua_socket_resolve_retval_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) { ngx_http_lua_ctx_t *ctx; ngx_peer_connection_t *pc; ngx_connection_t *c; ngx_http_cleanup_t *cln; ngx_http_upstream_resolved_t *ur; ngx_int_t rc; ngx_http_lua_co_ctx_t *coctx; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket resolve retval handler"); if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_RESOLVER) { return 2; } pc = &u->peer; ur = u->resolved; if (ur->sockaddr) { pc->sockaddr = ur->sockaddr; pc->socklen = ur->socklen; pc->name = &ur->host; } else { lua_pushnil(L); lua_pushliteral(L, "resolver not working"); return 2; } pc->get = ngx_http_lua_socket_tcp_get_peer; rc = ngx_event_connect_peer(pc); if (rc == NGX_ERROR) { u->socket_errno = ngx_socket_errno; } if (u->cleanup == NULL) { cln = ngx_http_cleanup_add(r, 0); if (cln == NULL) { u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; lua_pushnil(L); lua_pushliteral(L, "out of memory"); return 2; } cln->handler = ngx_http_lua_socket_tcp_cleanup; cln->data = u; u->cleanup = &cln->handler; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket connect: %i", rc); if (rc == NGX_ERROR) { return ngx_http_lua_socket_error_retval_handler(r, u, L); } if (rc == NGX_BUSY) { u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; lua_pushnil(L); lua_pushliteral(L, "no live connection"); return 2; } if (rc == NGX_DECLINED) { dd("socket errno: %d", (int) ngx_socket_errno); u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; u->socket_errno = ngx_socket_errno; return ngx_http_lua_socket_error_retval_handler(r, u, L); } /* rc == NGX_OK || rc == NGX_AGAIN */ c = pc->connection; c->data = u; c->write->handler = ngx_http_lua_socket_tcp_handler; c->read->handler = ngx_http_lua_socket_tcp_handler; u->write_event_handler = ngx_http_lua_socket_connected_handler; u->read_event_handler = ngx_http_lua_socket_connected_handler; c->sendfile &= r->connection->sendfile; c->pool = r->pool; c->log = r->connection->log; c->read->log = c->log; c->write->log = c->log; /* init or reinit the ngx_output_chain() and ngx_chain_writer() contexts */ #if 0 u->writer.out = NULL; u->writer.last = &u->writer.out; #endif ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); coctx = ctx->cur_co_ctx; dd("setting data to %p", u); coctx->data = u; if (rc == NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket connected: fd:%d", (int) c->fd); /* We should delete the current write/read event * here because the socket object may not be used immediately * on the Lua land, thus causing hot spin around level triggered * event poll and wasting CPU cycles. */ if (ngx_handle_write_event(c->write, 0) != NGX_OK) { ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); lua_pushnil(L); lua_pushliteral(L, "failed to handle write event"); return 2; } if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); lua_pushnil(L); lua_pushliteral(L, "failed to handle write event"); return 2; } u->read_event_handler = ngx_http_lua_socket_dummy_handler; u->write_event_handler = ngx_http_lua_socket_dummy_handler; lua_pushinteger(L, 1); return 1; } /* rc == NGX_AGAIN */ coctx->cleanup = ngx_http_lua_coctx_cleanup; ngx_add_timer(c->write, u->connect_timeout); if (ctx->entered_content_phase) { r->write_event_handler = ngx_http_lua_content_wev_handler; } else { r->write_event_handler = ngx_http_core_run_phases; } u->co_ctx = ctx->cur_co_ctx; u->waiting = 1; u->prepare_retvals = ngx_http_lua_socket_tcp_connect_retval_handler; dd("setting data to %p", u); if (ctx->entered_content_phase) { r->write_event_handler = ngx_http_lua_content_wev_handler; } else { r->write_event_handler = ngx_http_core_run_phases; } return NGX_AGAIN; } static int ngx_http_lua_socket_error_retval_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) { ngx_uint_t ft_type; u_char errstr[NGX_MAX_ERROR_STR]; u_char *p; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket error retval handler"); if (u->co_ctx) { u->co_ctx->cleanup = NULL; } ft_type = u->ft_type; if (u->no_close) { u->no_close = 0; u->ft_type = 0; } else { ngx_http_lua_socket_tcp_finalize(r, u); } if (ft_type & NGX_HTTP_LUA_SOCKET_FT_RESOLVER) { return 2; } lua_pushnil(L); if (ft_type & NGX_HTTP_LUA_SOCKET_FT_TIMEOUT) { lua_pushliteral(L, "timeout"); } else if (ft_type & NGX_HTTP_LUA_SOCKET_FT_CLOSED) { lua_pushliteral(L, "closed"); } else if (ft_type & NGX_HTTP_LUA_SOCKET_FT_BUFTOOSMALL) { lua_pushliteral(L, "buffer too small"); } else if (ft_type & NGX_HTTP_LUA_SOCKET_FT_NOMEM) { lua_pushliteral(L, "out of memory"); } else if (ft_type & NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT) { lua_pushliteral(L, "client aborted"); } else { if (u->socket_errno) { #if (nginx_version >= 1000000) p = ngx_strerror(u->socket_errno, errstr, sizeof(errstr)); #else p = ngx_strerror_r(u->socket_errno, errstr, sizeof(errstr)); #endif /* for compatibility with LuaSocket */ ngx_strlow(errstr, errstr, p - errstr); lua_pushlstring(L, (char *) errstr, p - errstr); } else { lua_pushliteral(L, "error"); } } return 2; } static int ngx_http_lua_socket_tcp_connect_retval_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) { if (u->ft_type) { return ngx_http_lua_socket_error_retval_handler(r, u, L); } lua_pushinteger(L, 1); return 1; } static int ngx_http_lua_socket_tcp_receive(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_socket_tcp_upstream_t *u; ngx_int_t rc; ngx_http_lua_ctx_t *ctx; int n; ngx_str_t pat; lua_Integer bytes; char *p; int typ; ngx_http_lua_loc_conf_t *llcf; ngx_http_lua_co_ctx_t *coctx; n = lua_gettop(L); if (n != 1 && n != 2) { return luaL_error(L, "expecting 1 or 2 arguments " "(including the object), but got %d", n); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket calling receive() method"); luaL_checktype(L, 1, LUA_TTABLE); lua_rawgeti(L, 1, SOCKET_CTX_INDEX); u = lua_touserdata(L, -1); if (u == NULL || u->peer.connection == NULL || u->ft_type || u->eof) { llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->log_socket_errors) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "attempt to receive data on a closed socket: u:%p, " "c:%p, ft:%ui eof:%ud", u, u ? u->peer.connection : NULL, u ? u->ft_type : 0, u ? u->eof : 0); } lua_pushnil(L); lua_pushliteral(L, "closed"); return 2; } if (u->waiting) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket read timeout: %M", u->read_timeout); if (n > 1) { if (lua_isnumber(L, 2)) { typ = LUA_TNUMBER; } else { typ = lua_type(L, 2); } switch (typ) { case LUA_TSTRING: pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len); if (pat.len != 2 || pat.data[0] != '*') { p = (char *) lua_pushfstring(L, "bad pattern argument: %s", (char *) pat.data); return luaL_argerror(L, 2, p); } switch (pat.data[1]) { case 'l': u->input_filter = ngx_http_lua_socket_read_line; break; case 'a': u->input_filter = ngx_http_lua_socket_read_all; break; default: return luaL_argerror(L, 2, "bad pattern argument"); break; } u->length = 0; u->rest = 0; break; case LUA_TNUMBER: bytes = lua_tointeger(L, 2); if (bytes < 0) { return luaL_argerror(L, 2, "bad pattern argument"); } #if 1 if (bytes == 0) { lua_pushliteral(L, ""); return 1; } #endif u->input_filter = ngx_http_lua_socket_read_chunk; u->length = (size_t) bytes; u->rest = u->length; break; default: return luaL_argerror(L, 2, "bad pattern argument"); break; } } else { u->input_filter = ngx_http_lua_socket_read_line; u->length = 0; u->rest = 0; } u->input_filter_ctx = u; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (u->bufs_in == NULL) { u->bufs_in = ngx_http_lua_chains_get_free_buf(r->connection->log, r->pool, &ctx->free_recv_bufs, u->conf->buffer_size, (ngx_buf_tag_t) &ngx_http_lua_module); if (u->bufs_in == NULL) { return luaL_error(L, "out of memory"); } u->buf_in = u->bufs_in; u->buffer = *u->buf_in->buf; } dd("tcp receive: buf_in: %p, bufs_in: %p", u->buf_in, u->bufs_in); if (u->raw_downstream || u->body_downstream) { r->read_event_handler = ngx_http_lua_req_socket_rev_handler; } u->waiting = 0; rc = ngx_http_lua_socket_tcp_read(r, u); if (rc == NGX_ERROR) { dd("read failed: %d", (int) u->ft_type); rc = ngx_http_lua_socket_tcp_receive_retval_handler(r, u, L); dd("tcp receive retval returned: %d", (int) rc); return rc; } if (rc == NGX_OK) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket receive done in a single run"); return ngx_http_lua_socket_tcp_receive_retval_handler(r, u, L); } /* rc == NGX_AGAIN */ u->read_event_handler = ngx_http_lua_socket_read_handler; u->write_event_handler = ngx_http_lua_socket_dummy_handler; ctx->cur_co_ctx->cleanup = ngx_http_lua_coctx_cleanup; if (ctx->entered_content_phase) { r->write_event_handler = ngx_http_lua_content_wev_handler; } else { r->write_event_handler = ngx_http_core_run_phases; } u->co_ctx = ctx->cur_co_ctx; u->waiting = 1; u->prepare_retvals = ngx_http_lua_socket_tcp_receive_retval_handler; coctx = ctx->cur_co_ctx; dd("setting data to %p, coctx:%p", u, coctx); coctx->data = u; if (u->raw_downstream || u->body_downstream) { ctx->downstream_co_ctx = coctx; } return lua_yield(L, 0); } static ngx_int_t ngx_http_lua_socket_read_chunk(void *data, ssize_t bytes) { ngx_http_lua_socket_tcp_upstream_t *u = data; ngx_buf_t *b; #if (NGX_DEBUG) ngx_http_request_t *r; r = u->request; #endif ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket read chunk %z", bytes); if (bytes == 0) { u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_CLOSED; return NGX_ERROR; } b = &u->buffer; if (bytes >= (ssize_t) u->rest) { u->buf_in->buf->last += u->rest; b->pos += u->rest; u->rest = 0; return NGX_OK; } /* bytes < u->rest */ u->buf_in->buf->last += bytes; b->pos += bytes; u->rest -= bytes; return NGX_AGAIN; } static ngx_int_t ngx_http_lua_socket_read_all(void *data, ssize_t bytes) { ngx_http_lua_socket_tcp_upstream_t *u = data; ngx_buf_t *b; #if (NGX_DEBUG) ngx_http_request_t *r; r = u->request; #endif ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket read all"); if (bytes == 0) { return NGX_OK; } b = &u->buffer; u->buf_in->buf->last += bytes; b->pos += bytes; return NGX_AGAIN; } static ngx_int_t ngx_http_lua_socket_read_line(void *data, ssize_t bytes) { ngx_http_lua_socket_tcp_upstream_t *u = data; ngx_buf_t *b; u_char *dst; u_char c; #if (NGX_DEBUG) u_char *begin; #endif ngx_log_debug0(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0, "lua tcp socket read line"); if (bytes == 0) { u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_CLOSED; return NGX_ERROR; } b = &u->buffer; #if (NGX_DEBUG) begin = b->pos; #endif dd("already read: %p: %.*s", u->buf_in, (int) (u->buf_in->buf->last - u->buf_in->buf->pos), u->buf_in->buf->pos); dd("data read: %.*s", (int) bytes, b->pos); dst = u->buf_in->buf->last; while (bytes--) { c = *b->pos++; switch (c) { case '\n': ngx_log_debug2(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0, "lua tcp socket read the final line part: \"%*s\"", b->pos - 1 - begin, begin); u->buf_in->buf->last = dst; dd("read a line: %p: %.*s", u->buf_in, (int) (u->buf_in->buf->last - u->buf_in->buf->pos), u->buf_in->buf->pos); return NGX_OK; case '\r': /* ignore it */ break; default: *dst++ = c; break; } } #if (NGX_DEBUG) ngx_log_debug2(NGX_LOG_DEBUG_HTTP, u->request->connection->log, 0, "lua tcp socket read partial line data: %*s", dst - begin, begin); #endif u->buf_in->buf->last = dst; return NGX_AGAIN; } static ngx_int_t ngx_http_lua_socket_tcp_read(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u) { ngx_int_t rc; ngx_connection_t *c; ngx_buf_t *b; ngx_event_t *rev; size_t size; ssize_t n; unsigned read; off_t preread = 0; ngx_http_lua_loc_conf_t *llcf; c = u->peer.connection; rev = c->read; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "lua tcp socket read data: waiting: %d", (int) u->waiting); b = &u->buffer; read = 0; for ( ;; ) { size = b->last - b->pos; if (size || u->eof) { rc = u->input_filter(u->input_filter_ctx, size); if (rc == NGX_OK) { ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket receive done: wait:%d, eof:%d, " "uri:\"%V?%V\"", (int) u->waiting, (int) u->eof, &r->uri, &r->args); if (u->body_downstream && b->last == b->pos && r->request_body->rest == 0) { llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->check_client_abort) { rc = ngx_http_lua_check_broken_connection(r, rev); if (rc == NGX_OK) { goto success; } if (rc == NGX_HTTP_CLIENT_CLOSED_REQUEST) { ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT); } else { ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); } return NGX_ERROR; } } #if 1 if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } #endif success: ngx_http_lua_socket_handle_success(r, u); return NGX_OK; } if (rc == NGX_ERROR) { dd("input filter error: ft_type:%d waiting:%d", (int) u->ft_type, (int) u->waiting); ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } /* rc == NGX_AGAIN */ if (u->body_downstream && r->request_body->rest == 0) { u->eof = 1; } continue; } if (read && !rev->ready) { rc = NGX_AGAIN; break; } size = b->end - b->last; if (size == 0) { rc = ngx_http_lua_socket_add_input_buffer(r, u); if (rc == NGX_ERROR) { ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_NOMEM); return NGX_ERROR; } b = &u->buffer; size = (size_t) (b->end - b->last); } if (u->raw_downstream) { preread = r->header_in->last - r->header_in->pos; if (preread) { if ((off_t) size > preread) { size = (size_t) preread; } ngx_http_lua_probe_req_socket_consume_preread(r, r->header_in->pos, size); b->last = ngx_copy(b->last, r->header_in->pos, size); r->header_in->pos += size; continue; } } else if (u->body_downstream) { if (r->request_body->rest == 0) { dd("request body rest is zero"); u->eof = 1; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua request body exhausted"); continue; } /* try to process the preread body */ preread = r->header_in->last - r->header_in->pos; if (preread) { /* there is the pre-read part of the request body */ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http client request body preread %O", preread); if (preread >= r->request_body->rest) { preread = r->request_body->rest; } if ((off_t) size > preread) { size = (size_t) preread; } ngx_http_lua_probe_req_socket_consume_preread(r, r->header_in->pos, size); b->last = ngx_copy(b->last, r->header_in->pos, size); r->header_in->pos += size; r->request_length += size; if (r->request_body->rest) { r->request_body->rest -= size; } continue; } if (size > (size_t) r->request_body->rest) { size = (size_t) r->request_body->rest; } } #if 1 if (rev->active && !rev->ready) { rc = NGX_AGAIN; break; } #endif ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket try to recv data %uz: \"%V?%V\"", size, &r->uri, &r->args); n = c->recv(c, b->last, size); dd("read event ready: %d", (int) c->read->ready); read = 1; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket recv returned %d: \"%V?%V\"", (int) n, &r->uri, &r->args); if (n == NGX_AGAIN) { rc = NGX_AGAIN; dd("socket recv busy"); break; } if (n == 0) { if (u->raw_downstream || u->body_downstream) { llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->check_client_abort) { ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT); return NGX_ERROR; } /* llcf->check_client_abort == 0 */ if (u->body_downstream && r->request_body->rest) { ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_CLIENTABORT); return NGX_ERROR; } } u->eof = 1; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket closed"); continue; } if (n == NGX_ERROR) { u->socket_errno = ngx_socket_errno; ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } b->last += n; if (u->body_downstream) { r->request_length += n; r->request_body->rest -= n; } } #if 1 if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } #endif if (rev->active) { ngx_add_timer(rev, u->read_timeout); } else if (rev->timer_set) { ngx_del_timer(rev); } return rc; } static int ngx_http_lua_socket_tcp_send(lua_State *L) { ngx_int_t rc; ngx_http_request_t *r; u_char *p; size_t len; ngx_chain_t *cl; ngx_http_lua_ctx_t *ctx; ngx_http_lua_socket_tcp_upstream_t *u; int type; const char *msg; ngx_buf_t *b; ngx_http_lua_loc_conf_t *llcf; ngx_http_lua_co_ctx_t *coctx; /* TODO: add support for the optional "i" and "j" arguments */ if (lua_gettop(L) != 2) { return luaL_error(L, "expecting 2 arguments (including the object), " "but got %d", lua_gettop(L)); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } luaL_checktype(L, 1, LUA_TTABLE); lua_rawgeti(L, 1, SOCKET_CTX_INDEX); u = lua_touserdata(L, -1); lua_pop(L, 1); if (u == NULL || u->peer.connection == NULL || u->ft_type || u->eof) { llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->log_socket_errors) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "attempt to send data on a closed socket: u:%p, " "c:%p, ft:%ui eof:%ud", u, u ? u->peer.connection : NULL, u ? u->ft_type : 0, u ? u->eof : 0); } lua_pushnil(L); lua_pushliteral(L, "closed"); return 2; } if (u->waiting) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; } if (u->raw_downstream && r->connection->buffered) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; } if (u->body_downstream) { return luaL_error(L, "attempt to write to request sockets"); } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket send timeout: %M", u->send_timeout); type = lua_type(L, 2); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: lua_tolstring(L, 2, &len); break; case LUA_TTABLE: len = ngx_http_lua_calc_strlen_in_table(L, 2, 2, 1 /* strict */); break; default: msg = lua_pushfstring(L, "string, number, boolean, nil, " "or array table expected, got %s", lua_typename(L, type)); return luaL_argerror(L, 2, msg); } if (len == 0) { lua_pushinteger(L, 0); return 1; } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); cl = ngx_http_lua_chains_get_free_buf(r->connection->log, r->pool, &ctx->free_bufs, len, (ngx_buf_tag_t) &ngx_http_lua_module); if (cl == NULL) { return luaL_error(L, "out of memory"); } b = cl->buf; switch (type) { case LUA_TNUMBER: case LUA_TSTRING: p = (u_char *) lua_tolstring(L, -1, &len); b->last = ngx_copy(b->last, (u_char *) p, len); break; case LUA_TTABLE: b->last = ngx_http_lua_copy_str_in_table(L, -1, b->last); break; default: return luaL_error(L, "impossible to reach here"); } u->request_bufs = cl; u->request_len = len; u->ft_type = 0; /* mimic ngx_http_upstream_init_request here */ #if 1 u->waiting = 0; #endif ngx_http_lua_probe_socket_tcp_send_start(r, u, b->pos, len); rc = ngx_http_lua_socket_send(r, u); dd("socket send returned %d", (int) rc); if (rc == NGX_ERROR) { return ngx_http_lua_socket_error_retval_handler(r, u, L); } if (rc == NGX_OK) { lua_pushinteger(L, len); return 1; } /* rc == NGX_AGAIN */ ctx->cur_co_ctx->cleanup = ngx_http_lua_coctx_cleanup; if (u->raw_downstream) { ctx->writing_raw_req_socket = 1; } if (ctx->entered_content_phase) { r->write_event_handler = ngx_http_lua_content_wev_handler; } else { r->write_event_handler = ngx_http_core_run_phases; } u->co_ctx = ctx->cur_co_ctx; u->waiting = 1; u->prepare_retvals = ngx_http_lua_socket_tcp_send_retval_handler; dd("setting data to %p", u); coctx = ctx->cur_co_ctx; coctx->data = u; return lua_yield(L, 0); } static int ngx_http_lua_socket_tcp_send_retval_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket send return value handler"); if (u->ft_type) { return ngx_http_lua_socket_error_retval_handler(r, u, L); } lua_pushinteger(L, u->request_len); return 1; } static int ngx_http_lua_socket_tcp_receive_retval_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) { int n; ngx_int_t rc; ngx_http_lua_ctx_t *ctx; ngx_event_t *ev; ngx_http_lua_loc_conf_t *llcf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket receive return value handler"); ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); #if 1 if (u->raw_downstream || u->body_downstream) { llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->check_client_abort) { r->read_event_handler = ngx_http_lua_rd_check_broken_connection; ev = r->connection->read; dd("rev active: %d", ev->active); if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && !ev->active) { if (ngx_add_event(ev, NGX_READ_EVENT, 0) != NGX_OK) { lua_pushnil(L); lua_pushliteral(L, "failed to add event"); return 2; } } } else { /* llcf->check_client_abort == 0 */ r->read_event_handler = ngx_http_block_reading; } } #endif if (u->ft_type) { if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_TIMEOUT) { u->no_close = 1; } dd("u->bufs_in: %p", u->bufs_in); if (u->bufs_in) { rc = ngx_http_lua_socket_push_input_data(r, ctx, u, L); if (rc == NGX_ERROR) { lua_pushnil(L); lua_pushliteral(L, "out of memory"); return 2; } (void) ngx_http_lua_socket_error_retval_handler(r, u, L); lua_pushvalue(L, -3); lua_remove(L, -4); return 3; } n = ngx_http_lua_socket_error_retval_handler(r, u, L); lua_pushliteral(L, ""); return n + 1; } rc = ngx_http_lua_socket_push_input_data(r, ctx, u, L); if (rc == NGX_ERROR) { lua_pushnil(L); lua_pushliteral(L, "out of memory"); return 2; } return 1; } static int ngx_http_lua_socket_tcp_close(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_socket_tcp_upstream_t *u; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting 1 argument " "(including the object) but seen %d", lua_gettop(L)); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } luaL_checktype(L, 1, LUA_TTABLE); lua_rawgeti(L, 1, SOCKET_CTX_INDEX); u = lua_touserdata(L, -1); lua_pop(L, 1); if (u == NULL || u->peer.connection == NULL || u->ft_type || u->eof) { lua_pushnil(L); lua_pushliteral(L, "closed"); return 2; } if (u->waiting) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; } if (u->raw_downstream || u->body_downstream) { lua_pushnil(L); lua_pushliteral(L, "attempt to close a request socket"); return 2; } ngx_http_lua_socket_tcp_finalize(r, u); lua_pushinteger(L, 1); return 1; } static int ngx_http_lua_socket_tcp_setoption(lua_State *L) { /* TODO */ return 0; } static int ngx_http_lua_socket_tcp_settimeout(lua_State *L) { int n; ngx_int_t timeout; ngx_http_lua_socket_tcp_upstream_t *u; n = lua_gettop(L); if (n != 2) { return luaL_error(L, "ngx.socket settimout: expecting at least 2 " "arguments (including the object) but seen %d", lua_gettop(L)); } timeout = (ngx_int_t) lua_tonumber(L, 2); lua_rawseti(L, 1, SOCKET_TIMEOUT_INDEX); lua_rawgeti(L, 1, SOCKET_CTX_INDEX); u = lua_touserdata(L, -1); if (u) { if (timeout > 0) { u->read_timeout = (ngx_msec_t) timeout; u->send_timeout = (ngx_msec_t) timeout; u->connect_timeout = (ngx_msec_t) timeout; } else { u->read_timeout = u->conf->read_timeout; u->send_timeout = u->conf->send_timeout; u->connect_timeout = u->conf->connect_timeout; } } return 0; } static void ngx_http_lua_socket_tcp_handler(ngx_event_t *ev) { ngx_connection_t *c; ngx_http_request_t *r; ngx_http_log_ctx_t *ctx; ngx_http_lua_socket_tcp_upstream_t *u; c = ev->data; u = c->data; r = u->request; c = r->connection; ctx = c->log->data; ctx->current_request = r; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket handler for \"%V?%V\", wev %d", &r->uri, &r->args, (int) ev->write); if (ev->write) { u->write_event_handler(r, u); } else { u->read_event_handler(r, u); } ngx_http_run_posted_requests(c); } static ngx_int_t ngx_http_lua_socket_tcp_get_peer(ngx_peer_connection_t *pc, void *data) { /* empty */ return NGX_OK; } static void ngx_http_lua_socket_read_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u) { ngx_connection_t *c; ngx_http_lua_loc_conf_t *llcf; c = u->peer.connection; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket read handler"); if (c->read->timedout) { c->read->timedout = 0; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->log_socket_errors) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "lua tcp socket read timed out"); } ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_TIMEOUT); return; } #if 1 if (c->read->timer_set) { ngx_del_timer(c->read); } #endif if (u->buffer.start != NULL) { (void) ngx_http_lua_socket_tcp_read(r, u); } } static void ngx_http_lua_socket_send_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u) { ngx_connection_t *c; ngx_http_lua_loc_conf_t *llcf; c = u->peer.connection; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket send handler"); if (c->write->timedout) { llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->log_socket_errors) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "lua tcp socket write timed out"); } ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_TIMEOUT); return; } if (u->request_bufs) { (void) ngx_http_lua_socket_send(r, u); } } static ngx_int_t ngx_http_lua_socket_send(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u) { ngx_int_t n; ngx_connection_t *c; ngx_http_lua_ctx_t *ctx; ngx_buf_t *b; c = u->peer.connection; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket send data"); dd("lua connection log: %p", c->log); ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } b = u->request_bufs->buf; for (;;) { n = c->send(c, b->pos, b->last - b->pos); if (n >= 0) { b->pos += n; if (b->pos == b->last) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "lua tcp socket sent all the data"); if (c->write->timer_set) { ngx_del_timer(c->write); } #if defined(nginx_version) && nginx_version >= 1001004 ngx_chain_update_chains(r->pool, #else ngx_chain_update_chains( #endif &ctx->free_bufs, &ctx->busy_bufs, &u->request_bufs, (ngx_buf_tag_t) &ngx_http_lua_module); u->write_event_handler = ngx_http_lua_socket_dummy_handler; if (ngx_handle_write_event(c->write, 0) != NGX_OK) { ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } ngx_http_lua_socket_handle_success(r, u); return NGX_OK; } /* keep sending more data */ continue; } /* NGX_ERROR || NGX_AGAIN */ break; } if (n == NGX_ERROR) { u->socket_errno = ngx_socket_errno; ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } /* n == NGX_AGAIN */ if (u->raw_downstream) { ctx->writing_raw_req_socket = 1; } u->write_event_handler = ngx_http_lua_socket_send_handler; u->read_event_handler = ngx_http_lua_socket_dummy_handler; ngx_add_timer(c->write, u->send_timeout); if (ngx_handle_write_event(c->write, u->conf->send_lowat) != NGX_OK) { ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } return NGX_AGAIN; } static void ngx_http_lua_socket_handle_success(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u) { ngx_http_lua_ctx_t *ctx; #if 1 u->read_event_handler = ngx_http_lua_socket_dummy_handler; u->write_event_handler = ngx_http_lua_socket_dummy_handler; #endif if (u->co_ctx) { u->co_ctx->cleanup = NULL; } #if 0 if (u->eof) { ngx_http_lua_socket_tcp_finalize(r, u); } #endif if (u->waiting) { u->waiting = 0; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return; } ctx->resume_handler = ngx_http_lua_socket_tcp_resume; ctx->cur_co_ctx = u->co_ctx; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket waking up the current request"); r->write_event_handler(r); } } static void ngx_http_lua_socket_handle_error(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, ngx_uint_t ft_type) { ngx_http_lua_ctx_t *ctx; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket handle error"); u->ft_type |= ft_type; #if 0 ngx_http_lua_socket_tcp_finalize(r, u); #endif if (u->co_ctx) { u->co_ctx->cleanup = NULL; } u->read_event_handler = ngx_http_lua_socket_dummy_handler; u->write_event_handler = ngx_http_lua_socket_dummy_handler; if (u->waiting) { u->waiting = 0; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return; } ctx->resume_handler = ngx_http_lua_socket_tcp_resume; ctx->cur_co_ctx = u->co_ctx; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket waking up the current request"); r->write_event_handler(r); } } static void ngx_http_lua_socket_connected_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u) { ngx_int_t rc; ngx_connection_t *c; ngx_http_lua_loc_conf_t *llcf; c = u->peer.connection; if (c->write->timedout) { llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->log_socket_errors) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "lua tcp socket connect timed out"); } ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_TIMEOUT); return; } if (c->write->timer_set) { ngx_del_timer(c->write); } rc = ngx_http_lua_socket_test_connect(r, c); if (rc != NGX_OK) { if (rc > 0) { u->socket_errno = (ngx_err_t) rc; } ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); return; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket connected"); /* We should delete the current write/read event * here because the socket object may not be used immediately * on the Lua land, thus causing hot spin around level triggered * event poll and wasting CPU cycles. */ if (ngx_handle_write_event(c->write, 0) != NGX_OK) { ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); return; } if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_http_lua_socket_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); return; } ngx_http_lua_socket_handle_success(r, u); } static void ngx_http_lua_socket_tcp_cleanup(void *data) { ngx_http_lua_socket_tcp_upstream_t *u = data; ngx_http_request_t *r; r = u->request; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "cleanup lua tcp socket request: \"%V\"", &r->uri); ngx_http_lua_socket_tcp_finalize(r, u); } static void ngx_http_lua_socket_tcp_finalize(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u) { ngx_http_lua_socket_pool_t *spool; ngx_chain_t *cl; ngx_chain_t **ll; ngx_http_lua_ctx_t *ctx; dd("request: %p, u: %p, u->cleanup: %p", r, u, u->cleanup); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua finalize socket"); if (u->cleanup) { *u->cleanup = NULL; u->cleanup = NULL; } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx && u->bufs_in) { ll = &u->bufs_in; for (cl = u->bufs_in; cl; cl = cl->next) { dd("bufs_in chain: %p, next %p", cl, cl->next); cl->buf->pos = cl->buf->last; ll = &cl->next; } dd("ctx: %p", ctx); dd("free recv bufs: %p", ctx->free_recv_bufs); *ll = ctx->free_recv_bufs; ctx->free_recv_bufs = u->bufs_in; u->bufs_in = NULL; u->buf_in = NULL; ngx_memzero(&u->buffer, sizeof(ngx_buf_t)); } if (u->raw_downstream || u->body_downstream) { if (ctx && ctx->writing_raw_req_socket) { ctx->writing_raw_req_socket = 0; if (r->connection->write->timer_set) { ngx_del_timer(r->connection->write); } r->connection->write->error = 1; } if (r->connection->read->timer_set) { ngx_del_timer(r->connection->read); } u->peer.connection = NULL; return; } if (u->resolved && u->resolved->ctx) { ngx_resolve_name_done(u->resolved->ctx); u->resolved->ctx = NULL; } if (u->peer.free) { u->peer.free(&u->peer, u->peer.data, 0); } if (u->peer.connection) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua close socket connection"); ngx_close_connection(u->peer.connection); u->peer.connection = NULL; if (!u->reused) { return; } spool = u->socket_pool; if (spool == NULL) { return; } spool->active_connections--; if (spool->active_connections == 0) { ngx_http_lua_socket_free_pool(r->connection->log, spool); } } } static ngx_int_t ngx_http_lua_socket_test_connect(ngx_http_request_t *r, ngx_connection_t *c) { int err; socklen_t len; ngx_http_lua_loc_conf_t *llcf; #if (NGX_HAVE_KQUEUE) ngx_event_t *ev; if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { dd("pending eof: (%p)%d (%p)%d", c->write, c->write->pending_eof, c->read, c->read->pending_eof); if (c->write->pending_eof) { ev = c->write; } else if (c->read->pending_eof) { ev = c->read; } else { ev = NULL; } if (ev) { llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->log_socket_errors) { (void) ngx_connection_error(c, ev->kq_errno, "kevent() reported that " "connect() failed"); } return ev->kq_errno; } } else #endif { err = 0; len = sizeof(int); /* * BSDs and Linux return 0 and set a pending error in err * Solaris returns -1 and sets errno */ if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len) == -1) { err = ngx_errno; } if (err) { llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->log_socket_errors) { (void) ngx_connection_error(c, err, "connect() failed"); } return err; } } return NGX_OK; } static void ngx_http_lua_socket_dummy_handler(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket dummy handler"); } static int ngx_http_lua_socket_tcp_receiveuntil(lua_State *L) { ngx_http_request_t *r; int n; ngx_str_t pat; ngx_int_t rc; size_t size; unsigned inclusive = 0; ngx_http_lua_socket_compiled_pattern_t *cp; n = lua_gettop(L); if (n != 2 && n != 3) { return luaL_error(L, "expecting 2 or 3 arguments " "(including the object), but got %d", n); } if (n == 3) { /* check out the options table */ luaL_checktype(L, 3, LUA_TTABLE); lua_getfield(L, 3, "inclusive"); switch (lua_type(L, -1)) { case LUA_TNIL: /* do nothing */ break; case LUA_TBOOLEAN: if (lua_toboolean(L, -1)) { inclusive = 1; } break; default: return luaL_error(L, "bad \"inclusive\" option value type: %s", luaL_typename(L, -1)); } lua_pop(L, 2); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket calling receiveuntil() method"); luaL_checktype(L, 1, LUA_TTABLE); pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len); if (pat.len == 0) { lua_pushnil(L); lua_pushliteral(L, "pattern is empty"); return 2; } size = sizeof(ngx_http_lua_socket_compiled_pattern_t); cp = lua_newuserdata(L, size); if (cp == NULL) { return luaL_error(L, "out of memory"); } lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ lua_pushcfunction(L, ngx_http_lua_socket_cleanup_compiled_pattern); lua_setfield(L, -2, "__gc"); lua_setmetatable(L, -2); ngx_memzero(cp, size); cp->inclusive = inclusive; rc = ngx_http_lua_socket_compile_pattern(pat.data, pat.len, cp, r->connection->log); if (rc != NGX_OK) { lua_pushnil(L); lua_pushliteral(L, "failed to compile pattern"); return 2; } lua_pushcclosure(L, ngx_http_lua_socket_receiveuntil_iterator, 3); return 1; } static int ngx_http_lua_socket_receiveuntil_iterator(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_socket_tcp_upstream_t *u; ngx_int_t rc; ngx_http_lua_ctx_t *ctx; lua_Integer bytes; int n; ngx_http_lua_co_ctx_t *coctx; ngx_http_lua_socket_compiled_pattern_t *cp; n = lua_gettop(L); if (n > 1) { return luaL_error(L, "expecting 0 or 1 arguments, " "but seen %d", n); } if (n >= 1) { bytes = luaL_checkinteger(L, 1); if (bytes < 0) { bytes = 0; } } else { bytes = 0; } lua_rawgeti(L, lua_upvalueindex(1), SOCKET_CTX_INDEX); u = lua_touserdata(L, -1); lua_pop(L, 1); if (u == NULL || u->peer.connection == NULL || u->ft_type || u->eof) { lua_pushnil(L); lua_pushliteral(L, "closed"); return 2; } if (u->waiting) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; } r = u->request; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket receiveuntil iterator"); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket read timeout: %M", u->read_timeout); u->input_filter = ngx_http_lua_socket_read_until; cp = lua_touserdata(L, lua_upvalueindex(3)); dd("checking existing state: %d", cp->state); if (cp->state == -1) { cp->state = 0; lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); return 3; } cp->upstream = u; cp->pattern.data = (u_char *) lua_tolstring(L, lua_upvalueindex(2), &cp->pattern.len); u->input_filter_ctx = cp; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (u->bufs_in == NULL) { u->bufs_in = ngx_http_lua_chains_get_free_buf(r->connection->log, r->pool, &ctx->free_recv_bufs, u->conf->buffer_size, (ngx_buf_tag_t) &ngx_http_lua_module); if (u->bufs_in == NULL) { return luaL_error(L, "out of memory"); } u->buf_in = u->bufs_in; u->buffer = *u->buf_in->buf; } u->length = (size_t) bytes; u->rest = u->length; if (u->raw_downstream || u->body_downstream) { r->read_event_handler = ngx_http_lua_req_socket_rev_handler; } u->waiting = 0; rc = ngx_http_lua_socket_tcp_read(r, u); if (rc == NGX_ERROR) { dd("read failed: %d", (int) u->ft_type); rc = ngx_http_lua_socket_tcp_receive_retval_handler(r, u, L); dd("tcp receive retval returned: %d", (int) rc); return rc; } if (rc == NGX_OK) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket receive done in a single run"); return ngx_http_lua_socket_tcp_receive_retval_handler(r, u, L); } /* rc == NGX_AGAIN */ u->read_event_handler = ngx_http_lua_socket_read_handler; u->write_event_handler = ngx_http_lua_socket_dummy_handler; ctx->cur_co_ctx->cleanup = ngx_http_lua_coctx_cleanup; if (ctx->entered_content_phase) { r->write_event_handler = ngx_http_lua_content_wev_handler; } else { r->write_event_handler = ngx_http_core_run_phases; } u->co_ctx = ctx->cur_co_ctx; u->waiting = 1; u->prepare_retvals = ngx_http_lua_socket_tcp_receive_retval_handler; coctx = ctx->cur_co_ctx; dd("setting data to %p", u); coctx->data = u; if (u->raw_downstream || u->body_downstream) { ctx->downstream_co_ctx = coctx; } return lua_yield(L, 0); } static ngx_int_t ngx_http_lua_socket_compile_pattern(u_char *data, size_t len, ngx_http_lua_socket_compiled_pattern_t *cp, ngx_log_t *log) { size_t i; size_t prefix_len; size_t size; unsigned found; int cur_state, new_state; ngx_http_lua_dfa_edge_t *edge; ngx_http_lua_dfa_edge_t **last = NULL; cp->pattern.len = len; if (len <= 2) { return NGX_OK; } for (i = 1; i < len; i++) { prefix_len = 1; while (prefix_len <= len - i - 1) { if (ngx_memcmp(data, &data[i], prefix_len) == 0) { if (data[prefix_len] == data[i + prefix_len]) { prefix_len++; continue; } cur_state = i + prefix_len; new_state = prefix_len + 1; if (cp->recovering == NULL) { size = sizeof(void *) * (len - 2); cp->recovering = ngx_alloc(size, log); if (cp->recovering == NULL) { return NGX_ERROR; } ngx_memzero(cp->recovering, size); } edge = cp->recovering[cur_state - 2]; found = 0; if (edge == NULL) { last = &cp->recovering[cur_state - 2]; } else { for (; edge; edge = edge->next) { last = &edge->next; if (edge->chr == data[prefix_len]) { found = 1; if (edge->new_state < new_state) { edge->new_state = new_state; } break; } } } if (!found) { ngx_log_debug7(NGX_LOG_DEBUG_HTTP, log, 0, "lua tcp socket read until recovering point:" " on state %d (%*s), if next is '%c', then " "recover to state %d (%*s)", cur_state, (size_t) cur_state, data, data[prefix_len], new_state, (size_t) new_state, data); edge = ngx_alloc(sizeof(ngx_http_lua_dfa_edge_t), log); edge->chr = data[prefix_len]; edge->new_state = new_state; edge->next = NULL; *last = edge; } break; } break; } } return NGX_OK; } static ngx_int_t ngx_http_lua_socket_read_until(void *data, ssize_t bytes) { ngx_http_lua_socket_compiled_pattern_t *cp = data; ngx_http_lua_socket_tcp_upstream_t *u; ngx_http_request_t *r; ngx_buf_t *b; u_char c; u_char *pat; size_t pat_len; int i; int state; int old_state = 0; /* just to make old gcc happy */ ngx_http_lua_dfa_edge_t *edge; unsigned matched; ngx_int_t rc; u = cp->upstream; r = u->request; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket read until"); if (bytes == 0) { u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_CLOSED; return NGX_ERROR; } b = &u->buffer; pat = cp->pattern.data; pat_len = cp->pattern.len; state = cp->state; i = 0; while (i < bytes) { c = b->pos[i]; dd("%d: read char %d, state: %d", i, c, state); if (c == pat[state]) { i++; state++; if (state == (int) pat_len) { /* already matched the whole pattern */ dd("pat len: %d", (int) pat_len); b->pos += i; if (u->length) { cp->state = -1; } else { cp->state = 0; } if (cp->inclusive) { rc = ngx_http_lua_socket_add_pending_data(r, u, b->pos, 0, pat, state, state); if (rc != NGX_OK) { u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; return NGX_ERROR; } } return NGX_OK; } continue; } if (state == 0) { u->buf_in->buf->last++; i++; if (u->length && --u->rest == 0) { cp->state = state; b->pos += i; return NGX_OK; } continue; } matched = 0; if (cp->recovering && state >= 2) { dd("accessing state: %d, index: %d", state, state - 2); for (edge = cp->recovering[state - 2]; edge; edge = edge->next) { if (edge->chr == c) { dd("matched '%c' and jumping to state %d", c, edge->new_state); old_state = state; state = edge->new_state; matched = 1; break; } } } if (!matched) { #if 1 dd("adding pending data: %.*s", state, pat); rc = ngx_http_lua_socket_add_pending_data(r, u, b->pos, i, pat, state, state); if (rc != NGX_OK) { u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; return NGX_ERROR; } #endif if (u->length) { if (u->rest <= (size_t) state) { u->rest = 0; cp->state = 0; b->pos += i; return NGX_OK; } else { u->rest -= state; } } state = 0; continue; } /* matched */ dd("adding pending data: %.*s", (int) (old_state + 1 - state), (char *) pat); rc = ngx_http_lua_socket_add_pending_data(r, u, b->pos, i, pat, old_state + 1 - state, old_state); if (rc != NGX_OK) { u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; return NGX_ERROR; } i++; if (u->length) { if (u->rest <= (size_t) state) { u->rest = 0; cp->state = state; b->pos += i; return NGX_OK; } else { u->rest -= state; } } continue; } b->pos += i; cp->state = state; return NGX_AGAIN; } static int ngx_http_lua_socket_cleanup_compiled_pattern(lua_State *L) { ngx_http_lua_socket_compiled_pattern_t *cp; ngx_http_lua_dfa_edge_t *edge, *p; unsigned i; dd("cleanup compiled pattern"); cp = lua_touserdata(L, 1); if (cp == NULL || cp->recovering == NULL) { return 0; } dd("pattern len: %d", (int) cp->pattern.len); for (i = 0; i < cp->pattern.len - 2; i++) { edge = cp->recovering[i]; while (edge) { p = edge; edge = edge->next; dd("freeing edge %p", p); ngx_free(p); dd("edge: %p", edge); } } #if 1 ngx_free(cp->recovering); cp->recovering = NULL; #endif return 0; } static int ngx_http_lua_req_socket(lua_State *L) { int n, raw; ngx_peer_connection_t *pc; ngx_http_lua_loc_conf_t *llcf; ngx_connection_t *c; ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_request_body_t *rb; ngx_http_cleanup_t *cln; ngx_http_lua_co_ctx_t *coctx; #if nginx_version >= 1003013 int tcp_nodelay; ngx_http_core_loc_conf_t *clcf; #endif ngx_http_lua_socket_tcp_upstream_t *u; n = lua_gettop(L); if (n == 0) { raw = 0; } else if (n == 1) { raw = lua_toboolean(L, 1); lua_pop(L, 1); } else { return luaL_error(L, "expecting zero arguments, but got %d", lua_gettop(L)); } r = ngx_http_lua_get_req(L); if (r != r->main) { return luaL_error(L, "attempt to read the request body in a " "subrequest"); } #if (NGX_HTTP_SPDY) if (r->spdy_stream) { return luaL_error(L, "spdy not supported yet"); } #endif #if nginx_version >= 1003009 if (r->headers_in.chunked) { lua_pushnil(L); lua_pushliteral(L, "chunked request bodies not supported yet"); return 2; } #endif ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); c = r->connection; if (raw) { #if !defined(nginx_version) || nginx_version < 1003013 lua_pushnil(L); lua_pushliteral(L, "nginx version too old"); return 2; #else if (r->request_body) { if (r->request_body->rest > 0) { lua_pushnil(L); lua_pushliteral(L, "pending request body reading in some " "other thread"); return 2; } } else { rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); if (rb == NULL) { return luaL_error(L, "out of memory"); } r->request_body = rb; } if (c->buffered) { lua_pushnil(L); lua_pushliteral(L, "pending data to write"); return 2; } if (ctx->buffering) { lua_pushnil(L); lua_pushliteral(L, "http 1.0 buffering"); return 2; } if (!r->header_sent) { /* prevent other parts of nginx from sending out * the response header */ r->header_sent = 1; } dd("ctx acquired raw req socket: %d", ctx->acquired_raw_req_socket); if (ctx->acquired_raw_req_socket) { lua_pushnil(L); lua_pushliteral(L, "duplicate call"); return 2; } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (clcf->tcp_nodelay) { tcp_nodelay = 1; if (c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "lua raw req socket tcp_nodelay"); if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, (const void *) &tcp_nodelay, sizeof(int)) == -1) { llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->log_socket_errors) { ngx_connection_error(c, ngx_socket_errno, "setsockopt(TCP_NODELAY) " "failed"); } lua_pushnil(L); lua_pushliteral(L, "setsocketopt tcp_nodelay failed"); return 2; } c->tcp_nodelay = NGX_TCP_NODELAY_SET; } } ctx->acquired_raw_req_socket = 1; r->keepalive = 0; r->lingering_close = 1; #endif } else { /* request body reader */ if (r->request_body) { lua_pushnil(L); lua_pushliteral(L, "request body already exists"); return 2; } if (r->discard_body) { lua_pushnil(L); lua_pushliteral(L, "request body discarded"); return 2; } dd("req content length: %d", (int) r->headers_in.content_length_n); if (r->headers_in.content_length_n <= 0) { lua_pushnil(L); lua_pushliteral(L, "no body"); return 2; } if (ngx_http_lua_test_expect(r) != NGX_OK) { lua_pushnil(L); lua_pushliteral(L, "test expect failed"); return 2; } /* prevent other request body reader from running */ rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); if (rb == NULL) { return luaL_error(L, "out of memory"); } rb->rest = r->headers_in.content_length_n; r->request_body = rb; } lua_createtable(L, 3 /* narr */, 1 /* nrec */); /* the object */ if (raw) { lua_pushlightuserdata(L, &ngx_http_lua_raw_req_socket_metatable_key); } else { lua_pushlightuserdata(L, &ngx_http_lua_req_socket_metatable_key); } lua_rawget(L, LUA_REGISTRYINDEX); lua_setmetatable(L, -2); u = lua_newuserdata(L, sizeof(ngx_http_lua_socket_tcp_upstream_t)); if (u == NULL) { return luaL_error(L, "out of memory"); } #if 1 lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ lua_pushcfunction(L, ngx_http_lua_socket_downstream_destroy); lua_setfield(L, -2, "__gc"); lua_setmetatable(L, -2); #endif lua_rawseti(L, 1, SOCKET_CTX_INDEX); ngx_memzero(u, sizeof(ngx_http_lua_socket_tcp_upstream_t)); if (raw) { u->raw_downstream = 1; } else { u->body_downstream = 1; } coctx = ctx->cur_co_ctx; u->request = r; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); u->conf = llcf; u->read_timeout = u->conf->read_timeout; u->connect_timeout = u->conf->connect_timeout; u->send_timeout = u->conf->send_timeout; cln = ngx_http_cleanup_add(r, 0); if (cln == NULL) { u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; lua_pushnil(L); lua_pushliteral(L, "out of memory"); return 2; } cln->handler = ngx_http_lua_socket_tcp_cleanup; cln->data = u; u->cleanup = &cln->handler; pc = &u->peer; pc->log = c->log; pc->log_error = NGX_ERROR_ERR; pc->connection = c; dd("setting data to %p", u); coctx->data = u; ctx->downstream_co_ctx = coctx; if (c->read->timer_set) { ngx_del_timer(c->read); } if (raw) { if (c->write->timer_set) { ngx_del_timer(c->write); } } lua_settop(L, 1); return 1; } static void ngx_http_lua_req_socket_rev_handler(ngx_http_request_t *r) { ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; ngx_http_lua_socket_tcp_upstream_t *u; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua request socket read event handler"); ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return; } coctx = ctx->downstream_co_ctx; u = coctx->data; if (u) { u->read_event_handler(r, u); } } static int ngx_http_lua_socket_tcp_getreusedtimes(lua_State *L) { ngx_http_lua_socket_tcp_upstream_t *u; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting 1 argument " "(including the object), but got %d", lua_gettop(L)); } luaL_checktype(L, 1, LUA_TTABLE); lua_rawgeti(L, 1, SOCKET_CTX_INDEX); u = lua_touserdata(L, -1); if (u == NULL || u->peer.connection == NULL || u->ft_type || u->eof) { lua_pushnil(L); lua_pushliteral(L, "closed"); return 2; } lua_pushinteger(L, u->reused); return 1; } static int ngx_http_lua_socket_tcp_setkeepalive(lua_State *L) { ngx_http_lua_loc_conf_t *llcf; ngx_http_lua_socket_tcp_upstream_t *u; ngx_connection_t *c; ngx_http_lua_socket_pool_t *spool; size_t size; ngx_str_t key; ngx_uint_t i; ngx_queue_t *q; ngx_peer_connection_t *pc; u_char *p; ngx_http_request_t *r; ngx_msec_t timeout; ngx_uint_t pool_size; int n; ngx_int_t rc; ngx_buf_t *b; ngx_http_lua_socket_pool_item_t *items, *item; n = lua_gettop(L); if (n < 1 || n > 3) { return luaL_error(L, "expecting 1 to 3 arguments " "(including the object), but got %d", n); } luaL_checktype(L, 1, LUA_TTABLE); lua_pushlightuserdata(L, &ngx_http_lua_socket_pool_key); lua_rawget(L, LUA_REGISTRYINDEX); lua_rawgeti(L, 1, SOCKET_KEY_INDEX); key.data = (u_char *) lua_tolstring(L, -1, &key.len); if (key.data == NULL) { lua_pushnil(L); lua_pushliteral(L, "key not found"); return 2; } lua_rawgeti(L, 1, SOCKET_CTX_INDEX); u = lua_touserdata(L, -1); lua_pop(L, 1); /* stack: obj cache key */ pc = &u->peer; c = pc->connection; if (u == NULL || c == NULL || u->ft_type || u->eof) { lua_pushnil(L); lua_pushliteral(L, "closed"); return 2; } if (u->waiting) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } b = &u->buffer; if (b->start && ngx_buf_size(b)) { ngx_http_lua_probe_socket_tcp_setkeepalive_buf_unread(r, u, b->pos, b->last - b->pos); lua_pushnil(L); lua_pushliteral(L, "unread data in buffer"); return 2; } if (c->read->eof || c->read->error || c->read->timedout || c->write->error || c->write->timedout) { lua_pushnil(L); lua_pushliteral(L, "invalid connection"); return 2; } if (ngx_handle_read_event(c->read, 0) != NGX_OK) { lua_pushnil(L); lua_pushliteral(L, "failed to handle read event"); return 2; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "lua tcp socket set keepalive: saving connection %p", c); dd("saving connection to key %s", lua_tostring(L, -1)); lua_pushvalue(L, -1); lua_rawget(L, -3); spool = lua_touserdata(L, -1); lua_pop(L, 1); /* stack: obj timeout? size? cache key */ llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (spool == NULL) { /* create a new socket pool for the current peer key */ if (n == 3) { pool_size = luaL_checkinteger(L, 3); } else { pool_size = llcf->pool_size; } if (pool_size == 0) { lua_pushnil(L); lua_pushliteral(L, "zero pool size"); return 2; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket connection pool size: %ui", pool_size); size = sizeof(ngx_http_lua_socket_pool_t) + key.len + sizeof(ngx_http_lua_socket_pool_item_t) * pool_size; spool = lua_newuserdata(L, size); if (spool == NULL) { return luaL_error(L, "out of memory"); } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "lua tcp socket keepalive create connection pool for key" " \"%s\"", lua_tostring(L, -2)); lua_rawset(L, -3); spool->active_connections = 0; spool->lua_vm = ngx_http_lua_get_lua_vm(r, NULL); ngx_queue_init(&spool->cache); ngx_queue_init(&spool->free); p = ngx_copy(spool->key, key.data, key.len); *p++ = '\0'; items = (ngx_http_lua_socket_pool_item_t *) p; for (i = 0; i < pool_size; i++) { ngx_queue_insert_head(&spool->free, &items[i].queue); items[i].socket_pool = spool; } } if (ngx_queue_empty(&spool->free)) { q = ngx_queue_last(&spool->cache); ngx_queue_remove(q); spool->active_connections--; item = ngx_queue_data(q, ngx_http_lua_socket_pool_item_t, queue); ngx_close_connection(item->connection); } else { q = ngx_queue_head(&spool->free); ngx_queue_remove(q); item = ngx_queue_data(q, ngx_http_lua_socket_pool_item_t, queue); } item->connection = c; ngx_queue_insert_head(&spool->cache, q); if (!u->reused) { spool->active_connections++; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, "lua tcp socket clear current socket connection"); pc->connection = NULL; #if 0 if (u->cleanup) { *u->cleanup = NULL; u->cleanup = NULL; } #endif if (c->read->timer_set) { ngx_del_timer(c->read); } if (c->write->timer_set) { ngx_del_timer(c->write); } if (n >= 2) { timeout = (ngx_msec_t) luaL_checkinteger(L, 2); } else { timeout = llcf->keepalive_timeout; } #if (NGX_DEBUG) if (timeout == 0) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket keepalive timeout: unlimited"); } #endif if (timeout) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket keepalive timeout: %M ms", timeout); ngx_add_timer(c->read, timeout); } c->write->handler = ngx_http_lua_socket_keepalive_dummy_handler; c->read->handler = ngx_http_lua_socket_keepalive_rev_handler; c->data = item; c->idle = 1; c->log = ngx_cycle->log; c->read->log = ngx_cycle->log; c->write->log = ngx_cycle->log; item->socklen = pc->socklen; ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen); item->reused = u->reused; if (c->read->ready) { rc = ngx_http_lua_socket_keepalive_close_handler(c->read); if (rc != NGX_OK) { lua_pushnil(L); lua_pushliteral(L, "connection in dubious state"); return 2; } } #if 1 ngx_http_lua_socket_tcp_finalize(r, u); #endif lua_pushinteger(L, 1); return 1; } static ngx_int_t ngx_http_lua_get_keepalive_peer(ngx_http_request_t *r, lua_State *L, int key_index, ngx_http_lua_socket_tcp_upstream_t *u) { ngx_http_lua_socket_pool_item_t *item; ngx_http_lua_socket_pool_t *spool; ngx_http_cleanup_t *cln; ngx_queue_t *q; int top; ngx_peer_connection_t *pc; ngx_connection_t *c; top = lua_gettop(L); if (key_index < 0) { key_index = top + key_index + 1; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket pool get keepalive peer"); pc = &u->peer; lua_pushlightuserdata(L, &ngx_http_lua_socket_pool_key); lua_rawget(L, LUA_REGISTRYINDEX); /* table */ lua_pushvalue(L, key_index); /* key */ lua_rawget(L, -2); spool = lua_touserdata(L, -1); if (spool == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, "lua tcp socket keepalive connection pool not found"); lua_settop(L, top); return NGX_DECLINED; } u->socket_pool = spool; if (!ngx_queue_empty(&spool->cache)) { q = ngx_queue_head(&spool->cache); item = ngx_queue_data(q, ngx_http_lua_socket_pool_item_t, queue); c = item->connection; ngx_queue_remove(q); ngx_queue_insert_head(&spool->free, q); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, "lua tcp socket get keepalive peer: using connection %p," " fd:%d", c, c->fd); c->idle = 0; c->log = pc->log; c->read->log = pc->log; c->write->log = pc->log; c->data = u; #if 1 c->write->handler = ngx_http_lua_socket_tcp_handler; c->read->handler = ngx_http_lua_socket_tcp_handler; #endif if (c->read->timer_set) { ngx_del_timer(c->read); } pc->connection = c; pc->cached = 1; u->reused = item->reused + 1; #if 1 u->write_event_handler = ngx_http_lua_socket_dummy_handler; u->read_event_handler = ngx_http_lua_socket_dummy_handler; #endif if (u->cleanup == NULL) { cln = ngx_http_cleanup_add(r, 0); if (cln == NULL) { u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; lua_settop(L, top); return NGX_ERROR; } cln->handler = ngx_http_lua_socket_tcp_cleanup; cln->data = u; u->cleanup = &cln->handler; } lua_settop(L, top); return NGX_OK; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, "lua tcp socket keepalive: connection pool empty"); lua_settop(L, top); return NGX_DECLINED; } static void ngx_http_lua_socket_keepalive_dummy_handler(ngx_event_t *ev) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, "keepalive dummy handler"); } static void ngx_http_lua_socket_keepalive_rev_handler(ngx_event_t *ev) { (void) ngx_http_lua_socket_keepalive_close_handler(ev); } static ngx_int_t ngx_http_lua_socket_keepalive_close_handler(ngx_event_t *ev) { ngx_http_lua_socket_pool_item_t *item; ngx_http_lua_socket_pool_t *spool; int n; char buf[1]; ngx_connection_t *c; c = ev->data; if (c->close) { goto close; } if (c->read->timedout) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, "lua tcp socket keepalive max idle timeout"); goto close; } dd("read event ready: %d", (int) c->read->ready); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, "lua tcp socket keepalive close handler check stale events"); n = recv(c->fd, buf, 1, MSG_PEEK); if (n == -1 && ngx_socket_errno == NGX_EAGAIN) { /* stale event */ if (ngx_handle_read_event(c->read, 0) != NGX_OK) { goto close; } return NGX_OK; } close: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, 0, "lua tcp socket keepalive close handler: fd:%d", c->fd); item = c->data; spool = item->socket_pool; ngx_close_connection(c); ngx_queue_remove(&item->queue); ngx_queue_insert_head(&spool->free, &item->queue); spool->active_connections--; dd("keepalive: active connections: %u", (unsigned) spool->active_connections); if (spool->active_connections == 0) { ngx_http_lua_socket_free_pool(ev->log, spool); } return NGX_DECLINED; } static void ngx_http_lua_socket_free_pool(ngx_log_t *log, ngx_http_lua_socket_pool_t *spool) { lua_State *L; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "lua tcp socket keepalive: free connection pool for \"%s\"", spool->key); L = spool->lua_vm; lua_pushlightuserdata(L, &ngx_http_lua_socket_pool_key); lua_rawget(L, LUA_REGISTRYINDEX); lua_pushstring(L, (char *) spool->key); lua_pushnil(L); lua_rawset(L, -3); lua_pop(L, 1); } static int ngx_http_lua_socket_tcp_upstream_destroy(lua_State *L) { ngx_http_lua_socket_tcp_upstream_t *u; dd("upstream destroy triggered by Lua GC"); u = lua_touserdata(L, 1); if (u == NULL) { return 0; } if (u->cleanup) { ngx_http_lua_socket_tcp_cleanup(u); /* it will clear u->cleanup */ } return 0; } static int ngx_http_lua_socket_downstream_destroy(lua_State *L) { ngx_http_lua_socket_tcp_upstream_t *u; dd("downstream destory"); u = lua_touserdata(L, 1); if (u == NULL) { dd("u is NULL"); return 0; } if (u->cleanup) { ngx_http_lua_socket_tcp_cleanup(u); /* it will clear u->cleanup */ } return 0; } static ngx_int_t ngx_http_lua_socket_push_input_data(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L) { ngx_chain_t *cl; ngx_chain_t **ll; size_t size; ngx_buf_t *b; size_t nbufs; u_char *p; u_char *last; if (!u->bufs_in) { lua_pushliteral(L, ""); ngx_http_lua_probe_socket_tcp_receive_done(r, u, (u_char *) "", 0); return NGX_OK; } dd("bufs_in: %p, buf_in: %p", u->bufs_in, u->buf_in); size = 0; nbufs = 0; ll = NULL; for (cl = u->bufs_in; cl; cl = cl->next) { b = cl->buf; size += b->last - b->pos; if (cl->next) { ll = &cl->next; } nbufs++; } dd("size: %d, nbufs: %d", (int) size, (int) nbufs); if (size == 0) { lua_pushliteral(L, ""); ngx_http_lua_probe_socket_tcp_receive_done(r, u, (u_char *) "", 0); goto done; } if (nbufs == 1) { b = u->buf_in->buf; lua_pushlstring(L, (char *) b->pos, size); dd("copying input data chunk from %p: \"%.*s\"", u->buf_in, (int) size, b->pos); ngx_http_lua_probe_socket_tcp_receive_done(r, u, b->pos, size); goto done; } /* nbufs > 1 */ dd("WARN: allocate a big memory: %d", (int) size); p = ngx_palloc(r->pool, size); if (p == NULL) { return NGX_ERROR; } last = p; for (cl = u->bufs_in; cl; cl = cl->next) { b = cl->buf; last = ngx_copy(last, b->pos, b->last - b->pos); dd("copying input data chunk from %p: \"%.*s\"", cl, (int) (b->last - b->pos), b->pos); } lua_pushlstring(L, (char *) p, size); ngx_http_lua_probe_socket_tcp_receive_done(r, u, p, size); ngx_pfree(r->pool, p); done: if (nbufs > 1 && ll) { dd("recycle buffers: %d", (int) (nbufs - 1)); *ll = ctx->free_recv_bufs; ctx->free_recv_bufs = u->bufs_in; u->bufs_in = u->buf_in; } if (u->buffer.pos == u->buffer.last) { dd("resetting u->buffer pos & last"); u->buffer.pos = u->buffer.start; u->buffer.last = u->buffer.start; } u->buf_in->buf->last = u->buffer.pos; u->buf_in->buf->pos = u->buffer.pos; return NGX_OK; } static ngx_int_t ngx_http_lua_socket_add_input_buffer(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u) { ngx_chain_t *cl; ngx_http_lua_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); cl = ngx_http_lua_chains_get_free_buf(r->connection->log, r->pool, &ctx->free_recv_bufs, u->conf->buffer_size, (ngx_buf_tag_t) &ngx_http_lua_module); if (cl == NULL) { return NGX_ERROR; } u->buf_in->next = cl; u->buf_in = cl; u->buffer = *cl->buf; return NGX_OK; } static ngx_int_t ngx_http_lua_socket_add_pending_data(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, u_char *pos, size_t len, u_char *pat, int prefix, int old_state) { u_char *last; ngx_buf_t *b; dd("resuming data: %d: [%.*s]", prefix, prefix, pat); last = &pos[len]; b = u->buf_in->buf; if (last - b->last == old_state) { b->last += prefix; return NGX_OK; } dd("need more buffers because %d != %d", (int) (last - b->last), (int) old_state); if (ngx_http_lua_socket_insert_buffer(r, u, pat, prefix) != NGX_OK) { return NGX_ERROR; } b->pos = last; b->last = last; return NGX_OK; } static ngx_int_t ngx_http_lua_socket_insert_buffer(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, u_char *pat, size_t prefix) { ngx_chain_t *cl, *new_cl, **ll; ngx_http_lua_ctx_t *ctx; size_t size; ngx_buf_t *b; if (prefix <= u->conf->buffer_size) { size = u->conf->buffer_size; } else { size = prefix; } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); new_cl = ngx_http_lua_chains_get_free_buf(r->connection->log, r->pool, &ctx->free_recv_bufs, size, (ngx_buf_tag_t) &ngx_http_lua_module); if (new_cl == NULL) { return NGX_ERROR; } b = new_cl->buf; b->last = ngx_copy(b->last, pat, prefix); dd("copy resumed data to %p: %d: \"%.*s\"", new_cl, (int) (b->last - b->pos), (int) (b->last - b->pos), b->pos); dd("before resuming data: bufs_in %p, buf_in %p, buf_in next %p", u->bufs_in, u->buf_in, u->buf_in->next); ll = &u->bufs_in; for (cl = u->bufs_in; cl->next; cl = cl->next) { ll = &cl->next; } *ll = new_cl; new_cl->next = u->buf_in; dd("after resuming data: bufs_in %p, buf_in %p, buf_in next %p", u->bufs_in, u->buf_in, u->buf_in->next); #if (DDEBUG) for (cl = u->bufs_in; cl; cl = cl->next) { b = cl->buf; dd("result buf after resuming data: %p: %.*s", cl, (int) ngx_buf_size(b), b->pos); } #endif return NGX_OK; } static ngx_int_t ngx_http_lua_socket_tcp_resume(ngx_http_request_t *r) { int nret; lua_State *vm; ngx_int_t rc; ngx_connection_t *c; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; ngx_http_lua_socket_tcp_upstream_t *u; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return NGX_ERROR; } ctx->resume_handler = ngx_http_lua_wev_handler; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp operation done, resuming lua thread"); coctx = ctx->cur_co_ctx; #if 0 ngx_http_lua_probe_info("tcp resume"); #endif dd("coctx: %p", coctx); u = coctx->data; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua tcp socket calling prepare retvals handler %p, " "u:%p", u->prepare_retvals, u); nret = u->prepare_retvals(r, u, ctx->cur_co_ctx->co); if (nret == NGX_AGAIN) { return NGX_DONE; } c = r->connection; vm = ngx_http_lua_get_lua_vm(r, ctx); rc = ngx_http_lua_run_thread(vm, r, ctx, nret); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua run thread returned %d", rc); if (rc == NGX_AGAIN) { return ngx_http_lua_run_posted_threads(c, vm, r, ctx); } if (rc == NGX_DONE) { ngx_http_lua_finalize_request(r, NGX_DONE); return ngx_http_lua_run_posted_threads(c, vm, r, ctx); } if (ctx->entered_content_phase) { ngx_http_lua_finalize_request(r, rc); return NGX_DONE; } return rc; } static void ngx_http_lua_tcp_resolve_cleanup(void *data) { ngx_resolver_ctx_t *rctx; ngx_http_lua_socket_tcp_upstream_t *u; ngx_http_lua_co_ctx_t *coctx = data; u = coctx->data; if (u == NULL) { return; } rctx = u->resolved->ctx; if (rctx == NULL) { return; } ngx_resolve_name_done(rctx); } static void ngx_http_lua_coctx_cleanup(void *data) { ngx_http_lua_socket_tcp_upstream_t *u; ngx_http_lua_co_ctx_t *coctx = data; dd("running coctx cleanup"); u = coctx->data; if (u == NULL) { return; } if (u->request == NULL) { return; } ngx_http_lua_socket_tcp_finalize(u->request, u); } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_script.h0000664000000000000000000000420212305451335017561 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_SCRIPT_H_INCLUDED_ #define _NGX_HTTP_LUA_SCRIPT_H_INCLUDED_ #include "ngx_http_lua_common.h" typedef struct { ngx_log_t *log; ngx_pool_t *pool; ngx_str_t *source; ngx_array_t **lengths; ngx_array_t **values; ngx_uint_t variables; unsigned complete_lengths:1; unsigned complete_values:1; } ngx_http_lua_script_compile_t; typedef struct { ngx_str_t value; void *lengths; void *values; } ngx_http_lua_complex_value_t; typedef struct { ngx_log_t *log; ngx_pool_t *pool; ngx_str_t *value; ngx_http_lua_complex_value_t *complex_value; } ngx_http_lua_compile_complex_value_t; typedef struct { u_char *ip; u_char *pos; ngx_str_t buf; int *captures; ngx_uint_t ncaptures; u_char *captures_data; unsigned skip:1; ngx_log_t *log; } ngx_http_lua_script_engine_t; typedef void (*ngx_http_lua_script_code_pt) (ngx_http_lua_script_engine_t *e); typedef size_t (*ngx_http_lua_script_len_code_pt) (ngx_http_lua_script_engine_t *e); typedef struct { ngx_http_lua_script_code_pt code; uintptr_t len; } ngx_http_lua_script_copy_code_t; typedef struct { ngx_http_lua_script_code_pt code; uintptr_t n; } ngx_http_lua_script_capture_code_t; ngx_int_t ngx_http_lua_compile_complex_value( ngx_http_lua_compile_complex_value_t *ccv); ngx_int_t ngx_http_lua_complex_value(ngx_http_request_t *r, ngx_str_t *subj, size_t offset, ngx_int_t count, int *cap, ngx_http_lua_complex_value_t *val, luaL_Buffer *luabuf); #endif /* _NGX_HTTP_LUA_SCRIPT_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_output.c0000664000000000000000000004702112305451335017616 0ustar #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_output.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_contentby.h" #include static int ngx_http_lua_ngx_say(lua_State *L); static int ngx_http_lua_ngx_print(lua_State *L); static int ngx_http_lua_ngx_flush(lua_State *L); static int ngx_http_lua_ngx_eof(lua_State *L); static int ngx_http_lua_ngx_send_headers(lua_State *L); static int ngx_http_lua_ngx_echo(lua_State *L, unsigned newline); static void ngx_http_lua_flush_cleanup(void *data); static int ngx_http_lua_ngx_print(lua_State *L) { dd("calling lua print"); return ngx_http_lua_ngx_echo(L, 0); } static int ngx_http_lua_ngx_say(lua_State *L) { dd("calling"); return ngx_http_lua_ngx_echo(L, 1); } static int ngx_http_lua_ngx_echo(lua_State *L, unsigned newline) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; const char *p; size_t len; size_t size; ngx_buf_t *b; ngx_chain_t *cl; ngx_int_t rc; int i; int nargs; int type; const char *msg; ngx_buf_tag_t tag; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); if (ctx->acquired_raw_req_socket) { lua_pushnil(L); lua_pushliteral(L, "raw request socket acquired"); return 2; } if (r->header_only) { lua_pushnil(L); lua_pushliteral(L, "header only"); return 2; } if (ctx->eof) { lua_pushnil(L); lua_pushliteral(L, "seen eof"); return 2; } nargs = lua_gettop(L); size = 0; for (i = 1; i <= nargs; i++) { type = lua_type(L, i); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: lua_tolstring(L, i, &len); size += len; break; case LUA_TNIL: size += sizeof("nil") - 1; break; case LUA_TBOOLEAN: if (lua_toboolean(L, i)) { size += sizeof("true") - 1; } else { size += sizeof("false") - 1; } break; case LUA_TTABLE: size += ngx_http_lua_calc_strlen_in_table(L, i, i, 0 /* strict */); break; case LUA_TLIGHTUSERDATA: dd("userdata: %p", lua_touserdata(L, i)); if (lua_touserdata(L, i) == NULL) { size += sizeof("null") - 1; break; } continue; default: msg = lua_pushfstring(L, "string, number, boolean, nil, " "ngx.null, or array table expected, " "but got %s", lua_typename(L, type)); return luaL_argerror(L, i, msg); } } if (newline) { size += sizeof("\n") - 1; } if (size == 0) { /* do nothing for empty strings */ lua_pushinteger(L, 1); return 1; } tag = (ngx_buf_tag_t) &ngx_http_lua_module; cl = ngx_http_lua_chains_get_free_buf(r->connection->log, r->pool, &ctx->free_bufs, size, tag); if (cl == NULL) { return luaL_error(L, "out of memory"); } b = cl->buf; for (i = 1; i <= nargs; i++) { type = lua_type(L, i); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: p = lua_tolstring(L, i, &len); b->last = ngx_copy(b->last, (u_char *) p, len); break; case LUA_TNIL: *b->last++ = 'n'; *b->last++ = 'i'; *b->last++ = 'l'; break; case LUA_TBOOLEAN: if (lua_toboolean(L, i)) { *b->last++ = 't'; *b->last++ = 'r'; *b->last++ = 'u'; *b->last++ = 'e'; } else { *b->last++ = 'f'; *b->last++ = 'a'; *b->last++ = 'l'; *b->last++ = 's'; *b->last++ = 'e'; } break; case LUA_TTABLE: b->last = ngx_http_lua_copy_str_in_table(L, i, b->last); break; case LUA_TLIGHTUSERDATA: *b->last++ = 'n'; *b->last++ = 'u'; *b->last++ = 'l'; *b->last++ = 'l'; break; default: return luaL_error(L, "impossible to reach here"); } } if (newline) { *b->last++ = '\n'; } #if 0 if (b->last != b->end) { return luaL_error(L, "buffer error: %p != %p", b->last, b->end); } #endif ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, newline ? "lua say response" : "lua print response"); rc = ngx_http_lua_send_chain_link(r, ctx, cl); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { lua_pushnil(L); lua_pushliteral(L, "nginx output filter error"); return 2; } dd("downstream write: %d, buf len: %d", (int) rc, (int) (b->last - b->pos)); if (!ctx->out) { #if nginx_version >= 1001004 ngx_chain_update_chains(r->pool, #else ngx_chain_update_chains( #endif &ctx->free_bufs, &ctx->busy_bufs, &cl, tag); dd("out lua buf tag: %p, buffered: %x, busy bufs: %p", &ngx_http_lua_module, (int) r->connection->buffered, ctx->busy_bufs); } lua_pushinteger(L, 1); return 1; } size_t ngx_http_lua_calc_strlen_in_table(lua_State *L, int index, int arg_i, unsigned strict) { double key; int max; int i; int type; size_t size; size_t len; const char *msg; if (index < 0) { index = lua_gettop(L) + index + 1; } dd("table index: %d", index); max = 0; lua_pushnil(L); /* stack: table key */ while (lua_next(L, index) != 0) { /* stack: table key value */ dd("key type: %s", luaL_typename(L, -2)); if (lua_type(L, -2) == LUA_TNUMBER) { key = lua_tonumber(L, -2); dd("key value: %d", (int) key); if (floor(key) == key && key >= 1) { if (key > max) { max = (int) key; } lua_pop(L, 1); /* stack: table key */ continue; } } /* not an array (non positive integer key) */ lua_pop(L, 2); /* stack: table */ msg = lua_pushfstring(L, "non-array table found"); luaL_argerror(L, arg_i, msg); return 0; } size = 0; for (i = 1; i <= max; i++) { lua_rawgeti(L, index, i); /* stack: table value */ type = lua_type(L, -1); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: lua_tolstring(L, -1, &len); size += len; break; case LUA_TNIL: if (strict) { goto bad_type; } size += sizeof("nil") - 1; break; case LUA_TBOOLEAN: if (strict) { goto bad_type; } if (lua_toboolean(L, -1)) { size += sizeof("true") - 1; } else { size += sizeof("false") - 1; } break; case LUA_TTABLE: size += ngx_http_lua_calc_strlen_in_table(L, -1, arg_i, strict); break; case LUA_TLIGHTUSERDATA: if (strict) { goto bad_type; } if (lua_touserdata(L, -1) == NULL) { size += sizeof("null") - 1; break; } continue; default: bad_type: msg = lua_pushfstring(L, "bad data type %s found", lua_typename(L, type)); return luaL_argerror(L, arg_i, msg); } lua_pop(L, 1); /* stack: table */ } return size; } u_char * ngx_http_lua_copy_str_in_table(lua_State *L, int index, u_char *dst) { double key; int max; int i; int type; size_t len; u_char *p; if (index < 0) { index = lua_gettop(L) + index + 1; } max = 0; lua_pushnil(L); /* stack: table key */ while (lua_next(L, index) != 0) { /* stack: table key value */ key = lua_tonumber(L, -2); if (key > max) { max = (int) key; } lua_pop(L, 1); /* stack: table key */ } for (i = 1; i <= max; i++) { lua_rawgeti(L, index, i); /* stack: table value */ type = lua_type(L, -1); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: p = (u_char *) lua_tolstring(L, -1, &len); dst = ngx_copy(dst, p, len); break; case LUA_TNIL: *dst++ = 'n'; *dst++ = 'i'; *dst++ = 'l'; break; case LUA_TBOOLEAN: if (lua_toboolean(L, -1)) { *dst++ = 't'; *dst++ = 'r'; *dst++ = 'u'; *dst++ = 'e'; } else { *dst++ = 'f'; *dst++ = 'a'; *dst++ = 'l'; *dst++ = 's'; *dst++ = 'e'; } break; case LUA_TTABLE: dst = ngx_http_lua_copy_str_in_table(L, -1, dst); break; case LUA_TLIGHTUSERDATA: *dst++ = 'n'; *dst++ = 'u'; *dst++ = 'l'; *dst++ = 'l'; break; default: luaL_error(L, "impossible to reach here"); return NULL; } lua_pop(L, 1); /* stack: table */ } return dst; } /** * Force flush out response content * */ static int ngx_http_lua_ngx_flush(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_buf_t *buf; ngx_chain_t *cl; ngx_int_t rc; int n; unsigned wait = 0; ngx_event_t *wev; ngx_http_core_loc_conf_t *clcf; ngx_http_lua_co_ctx_t *coctx; n = lua_gettop(L); if (n > 1) { return luaL_error(L, "attempt to pass %d arguments, but accepted 0 " "or 1", n); } r = ngx_http_lua_get_req(L); if (n == 1 && r == r->main) { luaL_checktype(L, 1, LUA_TBOOLEAN); wait = lua_toboolean(L, 1); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); if (ctx->acquired_raw_req_socket) { lua_pushnil(L); lua_pushliteral(L, "raw request socket acquired"); return 2; } coctx = ctx->cur_co_ctx; if (coctx == NULL) { return luaL_error(L, "no co ctx found"); } if (r->header_only) { lua_pushnil(L); lua_pushliteral(L, "header only"); return 2; } if (ctx->eof) { lua_pushnil(L); lua_pushliteral(L, "seen eof"); return 2; } if (ctx->buffering) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua http 1.0 buffering makes ngx.flush() a no-op"); lua_pushnil(L); lua_pushliteral(L, "buffering"); return 2; } #if 1 if (!r->header_sent) { lua_pushnil(L); lua_pushliteral(L, "nothing to flush"); return 2; } #endif if (ctx->flush_buf) { cl = ctx->flush_buf; } else { dd("allocating new flush buf"); buf = ngx_calloc_buf(r->pool); if (buf == NULL) { return luaL_error(L, "out of memory"); } buf->flush = 1; dd("allocating new flush chain"); cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return luaL_error(L, "out of memory"); } cl->next = NULL; cl->buf = buf; ctx->flush_buf = cl; } rc = ngx_http_lua_send_chain_link(r, ctx, cl); dd("send chain: %d", (int) rc); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { lua_pushnil(L); lua_pushliteral(L, "nginx output filter error"); return 2; } dd("wait:%d, rc:%d, buffered:%d", wait, (int) rc, r->connection->buffered); if (wait && r->connection->buffered) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua flush requires waiting: buffered 0x%uxd", (int) r->connection->buffered); coctx->flushing = 1; ctx->flushing_coros++; if (ctx->entered_content_phase) { /* mimic ngx_http_set_write_handler */ r->write_event_handler = ngx_http_lua_content_wev_handler; } else { r->write_event_handler = ngx_http_core_run_phases; } wev = r->connection->write; if (wev->ready && wev->delayed) { lua_pushnil(L); lua_pushliteral(L, "delayed"); return 2; } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (!wev->delayed) { ngx_add_timer(wev, clcf->send_timeout); } if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { if (wev->timer_set) { ngx_del_timer(wev); } lua_pushnil(L); lua_pushliteral(L, "connection broken"); return 2; } coctx->cleanup = ngx_http_lua_flush_cleanup; coctx->data = r; return lua_yield(L, 0); } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua flush asynchronously"); lua_pushinteger(L, 1); return 1; } /** * Send last_buf, terminate output stream * */ static int ngx_http_lua_ngx_eof(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_int_t rc; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } if (lua_gettop(L) != 0) { return luaL_error(L, "no argument is expected"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no ctx found"); } if (ctx->acquired_raw_req_socket) { lua_pushnil(L); lua_pushliteral(L, "raw request socket acquired"); return 2; } if (ctx->eof) { lua_pushnil(L); lua_pushliteral(L, "seen eof"); return 2; } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua send eof"); rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); dd("send chain: %d", (int) rc); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { lua_pushnil(L); lua_pushliteral(L, "nginx output filter error"); return 2; } lua_pushinteger(L, 1); return 1; } void ngx_http_lua_inject_output_api(lua_State *L) { lua_pushcfunction(L, ngx_http_lua_ngx_send_headers); lua_setfield(L, -2, "send_headers"); lua_pushcfunction(L, ngx_http_lua_ngx_print); lua_setfield(L, -2, "print"); lua_pushcfunction(L, ngx_http_lua_ngx_say); lua_setfield(L, -2, "say"); lua_pushcfunction(L, ngx_http_lua_ngx_flush); lua_setfield(L, -2, "flush"); lua_pushcfunction(L, ngx_http_lua_ngx_eof); lua_setfield(L, -2, "eof"); } /** * Send out headers * */ static int ngx_http_lua_ngx_send_headers(lua_State *L) { ngx_int_t rc; ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); if (!r->header_sent) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua send headers"); rc = ngx_http_lua_send_header_if_needed(r, ctx); if (rc == NGX_ERROR || rc > NGX_OK) { lua_pushnil(L); lua_pushliteral(L, "nginx output filter error"); return 2; } } lua_pushinteger(L, 1); return 1; } ngx_int_t ngx_http_lua_flush_resume_helper(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) { lua_State *vm; ngx_int_t rc; ngx_connection_t *c; c = r->connection; ctx->cur_co_ctx->cleanup = NULL; /* push the return value 1 */ lua_pushinteger(ctx->cur_co_ctx->co, 1); vm = ngx_http_lua_get_lua_vm(r, ctx); rc = ngx_http_lua_run_thread(vm, r, ctx, 1); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua run thread returned %d", rc); if (rc == NGX_AGAIN) { return ngx_http_lua_run_posted_threads(c, vm, r, ctx); } if (rc == NGX_DONE) { ngx_http_lua_finalize_request(r, NGX_DONE); return ngx_http_lua_run_posted_threads(c, vm, r, ctx); } /* rc == NGX_ERROR || rc >= NGX_OK */ if (ctx->entered_content_phase) { ngx_http_lua_finalize_request(r, rc); return NGX_DONE; } return rc; } static void ngx_http_lua_flush_cleanup(void *data) { ngx_http_request_t *r; ngx_event_t *wev; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx = data; coctx->flushing = 0; r = coctx->data; if (r == NULL) { return; } wev = r->connection->write; if (wev && wev->timer_set) { ngx_del_timer(wev); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return; } ctx->flushing_coros--; } debian/modules/nginx-lua/src/ngx_http_lua_clfactory.h0000664000000000000000000000075512305451335020254 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_CLFACTORY_H_INCLUDED_ #define _NGX_HTTP_LUA_CLFACTORY_H_INCLUDED_ #include "ngx_http_lua_common.h" int ngx_http_lua_clfactory_loadfile(lua_State *L, const char *filename); int ngx_http_lua_clfactory_loadbuffer(lua_State *L, const char *buff, size_t size, const char *name); #endif /* _NGX_HTTP_LUA_CLFACTORY_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_coroutine.h0000664000000000000000000000101512305451335020263 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_COROUTINE_H_INCLUDED_ #define _NGX_HTTP_LUA_COROUTINE_H_INCLUDED_ #include "ngx_http_lua_common.h" void ngx_http_lua_inject_coroutine_api(ngx_log_t *log, lua_State *L); int ngx_http_lua_coroutine_create_helper(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t **pcoctx); #endif /* _NGX_HTTP_LUA_COROUTINE_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_variable.h0000664000000000000000000000055412305451335020050 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_VARIABLE_H_INCLUDED_ #define _NGX_HTTP_LUA_VARIABLE_H_INCLUDED_ #include "ngx_http_lua_common.h" void ngx_http_lua_inject_variable_api(lua_State *L); #endif /* _NGX_HTTP_LUA_VARIABLE_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_rewriteby.h0000664000000000000000000000100312305451335020265 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_REWRITEBY_H_INCLUDED_ #define _NGX_HTTP_LUA_REWRITEBY_H_INCLUDED_ #include "ngx_http_lua_common.h" ngx_int_t ngx_http_lua_rewrite_handler(ngx_http_request_t *r); ngx_int_t ngx_http_lua_rewrite_handler_inline(ngx_http_request_t *r); ngx_int_t ngx_http_lua_rewrite_handler_file(ngx_http_request_t *r); #endif /* _NGX_HTTP_LUA_REWRITEBY_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_cache.h0000664000000000000000000000103712305451335017323 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_CACHE_H_INCLUDED_ #define _NGX_HTTP_LUA_CACHE_H_INCLUDED_ #include "ngx_http_lua_common.h" ngx_int_t ngx_http_lua_cache_loadbuffer(lua_State *L, const u_char *src, size_t src_len, const u_char *cache_key, const char *name); ngx_int_t ngx_http_lua_cache_loadfile(lua_State *L, const u_char *script, const u_char *cache_key); #endif /* _NGX_HTTP_LUA_CACHE_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/api/0000775000000000000000000000000012305451341014100 5ustar debian/modules/nginx-lua/src/api/ngx_http_lua_api.h0000664000000000000000000000177712305451335017615 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_API_H_INCLUDED_ #define _NGX_HTTP_LUA_API_H_INCLUDED_ #include #include #include #include /* Public API for other Nginx modules */ #define ngx_http_lua_version 9004 typedef struct { uint8_t type; union { int b; /* boolean */ lua_Number n; /* number */ ngx_str_t s; /* string */ } value; } ngx_http_lua_value_t; lua_State * ngx_http_lua_get_global_state(ngx_conf_t *cf); ngx_http_request_t * ngx_http_lua_get_request(lua_State *L); ngx_int_t ngx_http_lua_add_package_preload(ngx_conf_t *cf, const char *package, lua_CFunction func); ngx_int_t ngx_http_lua_shared_dict_get(ngx_shm_zone_t *shm_zone, u_char *key_data, size_t key_len, ngx_http_lua_value_t *value); ngx_shm_zone_t *ngx_http_lua_find_zone(u_char *name_data, size_t name_len); #endif /* _NGX_HTTP_LUA_API_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_socket_udp.c0000664000000000000000000012174112305451335020420 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_socket_udp.h" #include "ngx_http_lua_socket_tcp.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_contentby.h" #include "ngx_http_lua_output.h" #include "ngx_http_lua_probe.h" #if 1 #undef ngx_http_lua_probe_info #define ngx_http_lua_probe_info(msg) #endif #define UDP_MAX_DATAGRAM_SIZE 8192 static int ngx_http_lua_socket_udp(lua_State *L); static int ngx_http_lua_socket_udp_setpeername(lua_State *L); static int ngx_http_lua_socket_udp_send(lua_State *L); static int ngx_http_lua_socket_udp_receive(lua_State *L); static int ngx_http_lua_socket_udp_settimeout(lua_State *L); static void ngx_http_lua_socket_udp_finalize(ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u); static int ngx_http_lua_socket_udp_upstream_destroy(lua_State *L); static int ngx_http_lua_socket_resolve_retval_handler(ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u, lua_State *L); static void ngx_http_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx); static int ngx_http_lua_socket_error_retval_handler(ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u, lua_State *L); static void ngx_http_lua_socket_udp_handle_error(ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u, ngx_uint_t ft_type); static void ngx_http_lua_socket_udp_cleanup(void *data); static void ngx_http_lua_socket_udp_handler(ngx_event_t *ev); static void ngx_http_lua_socket_dummy_handler(ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u); static int ngx_http_lua_socket_udp_receive_retval_handler(ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u, lua_State *L); static ngx_int_t ngx_http_lua_socket_udp_read(ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u); static void ngx_http_lua_socket_udp_read_handler(ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u); static void ngx_http_lua_socket_udp_handle_success(ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u); static ngx_int_t ngx_http_lua_udp_connect(ngx_udp_connection_t *uc); static int ngx_http_lua_socket_udp_close(lua_State *L); static ngx_int_t ngx_http_lua_socket_udp_resume(ngx_http_request_t *r); static void ngx_http_lua_udp_resolve_cleanup(void *data); static void ngx_http_lua_udp_socket_cleanup(void *data); enum { SOCKET_CTX_INDEX = 1, SOCKET_TIMEOUT_INDEX = 2 }; static char ngx_http_lua_socket_udp_metatable_key; static u_char ngx_http_lua_socket_udp_buffer[UDP_MAX_DATAGRAM_SIZE]; void ngx_http_lua_inject_socket_udp_api(ngx_log_t *log, lua_State *L) { lua_getfield(L, -1, "socket"); /* ngx socket */ lua_pushcfunction(L, ngx_http_lua_socket_udp); lua_setfield(L, -2, "udp"); /* ngx socket */ /* udp socket object metatable */ lua_pushlightuserdata(L, &ngx_http_lua_socket_udp_metatable_key); lua_createtable(L, 0 /* narr */, 4 /* nrec */); lua_pushcfunction(L, ngx_http_lua_socket_udp_setpeername); lua_setfield(L, -2, "setpeername"); /* ngx socket mt */ lua_pushcfunction(L, ngx_http_lua_socket_udp_send); lua_setfield(L, -2, "send"); lua_pushcfunction(L, ngx_http_lua_socket_udp_receive); lua_setfield(L, -2, "receive"); lua_pushcfunction(L, ngx_http_lua_socket_udp_settimeout); lua_setfield(L, -2, "settimeout"); /* ngx socket mt */ lua_pushcfunction(L, ngx_http_lua_socket_udp_close); lua_setfield(L, -2, "close"); /* ngx socket mt */ lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); lua_rawset(L, LUA_REGISTRYINDEX); /* }}} */ lua_pop(L, 1); } static int ngx_http_lua_socket_udp(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; if (lua_gettop(L) != 0) { return luaL_error(L, "expecting zero arguments, but got %d", lua_gettop(L)); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER); lua_createtable(L, 3 /* narr */, 1 /* nrec */); lua_pushlightuserdata(L, &ngx_http_lua_socket_udp_metatable_key); lua_rawget(L, LUA_REGISTRYINDEX); lua_setmetatable(L, -2); dd("top: %d", lua_gettop(L)); return 1; } static int ngx_http_lua_socket_udp_setpeername(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_str_t host; int port; ngx_resolver_ctx_t *rctx, temp; ngx_http_core_loc_conf_t *clcf; int saved_top; int n; u_char *p; size_t len; ngx_url_t url; ngx_int_t rc; ngx_http_lua_loc_conf_t *llcf; ngx_udp_connection_t *uc; int timeout; ngx_http_lua_co_ctx_t *coctx; ngx_http_lua_socket_udp_upstream_t *u; /* * TODO: we should probably accept an extra argument to setpeername() * to allow the user bind the datagram unix domain socket himself, * which is necessary for systems without autobind support. */ n = lua_gettop(L); if (n != 2 && n != 3) { return luaL_error(L, "ngx.socket.udp setpeername: expecting 2 or 3 " "arguments (including the object), but seen %d", n); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER); luaL_checktype(L, 1, LUA_TTABLE); p = (u_char *) luaL_checklstring(L, 2, &len); host.data = ngx_palloc(r->pool, len + 1); if (host.data == NULL) { return luaL_error(L, "out of memory"); } host.len = len; ngx_memcpy(host.data, p, len); host.data[len] = '\0'; if (n == 3) { port = luaL_checkinteger(L, 3); if (port < 0 || port > 65536) { lua_pushnil(L); lua_pushfstring(L, "bad port number: %d", port); return 2; } } else { /* n == 2 */ port = 0; } lua_rawgeti(L, 1, SOCKET_CTX_INDEX); u = lua_touserdata(L, -1); lua_pop(L, 1); if (u) { if (u->waiting) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; } if (u->udp_connection.connection) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp socket reconnect without shutting down"); ngx_http_lua_socket_udp_finalize(r, u); } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua reuse socket upstream ctx"); } else { u = lua_newuserdata(L, sizeof(ngx_http_lua_socket_udp_upstream_t)); if (u == NULL) { return luaL_error(L, "out of memory"); } #if 1 lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ lua_pushcfunction(L, ngx_http_lua_socket_udp_upstream_destroy); lua_setfield(L, -2, "__gc"); lua_setmetatable(L, -2); #endif lua_rawseti(L, 1, SOCKET_CTX_INDEX); } ngx_memzero(u, sizeof(ngx_http_lua_socket_udp_upstream_t)); u->request = r; /* set the controlling request */ llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); u->conf = llcf; uc = &u->udp_connection; uc->log = *r->connection->log; dd("lua peer connection log: %p", &uc->log); lua_rawgeti(L, 1, SOCKET_TIMEOUT_INDEX); timeout = (ngx_int_t) lua_tointeger(L, -1); lua_pop(L, 1); if (timeout > 0) { u->read_timeout = (ngx_msec_t) timeout; } else { u->read_timeout = u->conf->read_timeout; } ngx_memzero(&url, sizeof(ngx_url_t)); url.url.len = host.len; url.url.data = host.data; url.default_port = (in_port_t) port; url.no_resolve = 1; if (ngx_parse_url(r->pool, &url) != NGX_OK) { lua_pushnil(L); if (url.err) { lua_pushfstring(L, "failed to parse host name \"%s\": %s", host.data, url.err); } else { lua_pushfstring(L, "failed to parse host name \"%s\"", host.data); } return 2; } u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t)); if (u->resolved == NULL) { return luaL_error(L, "out of memory"); } if (url.addrs && url.addrs[0].sockaddr) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp socket network address given directly"); u->resolved->sockaddr = url.addrs[0].sockaddr; u->resolved->socklen = url.addrs[0].socklen; u->resolved->naddrs = 1; u->resolved->host = url.addrs[0].name; } else { u->resolved->host = host; u->resolved->port = (in_port_t) port; } if (u->resolved->sockaddr) { rc = ngx_http_lua_socket_resolve_retval_handler(r, u, L); if (rc == NGX_AGAIN) { return lua_yield(L, 0); } return rc; } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); temp.name = host; rctx = ngx_resolve_start(clcf->resolver, &temp); if (rctx == NULL) { u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER; lua_pushnil(L); lua_pushliteral(L, "failed to start the resolver"); return 2; } if (rctx == NGX_NO_RESOLVER) { u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER; lua_pushnil(L); lua_pushfstring(L, "no resolver defined to resolve \"%s\"", host.data); return 2; } rctx->name = host; #if !defined(nginx_version) || nginx_version < 1005008 rctx->type = NGX_RESOLVE_A; #endif rctx->handler = ngx_http_lua_socket_resolve_handler; rctx->data = u; rctx->timeout = clcf->resolver_timeout; u->co_ctx = ctx->cur_co_ctx; u->resolved->ctx = rctx; saved_top = lua_gettop(L); coctx = ctx->cur_co_ctx; coctx->cleanup = ngx_http_lua_udp_resolve_cleanup; if (ngx_resolve_name(rctx) != NGX_OK) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp socket fail to run resolver immediately"); u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER; u->resolved->ctx = NULL; lua_pushnil(L); lua_pushfstring(L, "%s could not be resolved", host.data); return 2; } if (u->waiting == 1) { /* resolved and already connecting */ return lua_yield(L, 0); } n = lua_gettop(L) - saved_top; if (n) { /* errors occurred during resolving or connecting * or already connected */ return n; } /* still resolving */ u->waiting = 1; u->prepare_retvals = ngx_http_lua_socket_resolve_retval_handler; coctx->data = u; if (ctx->entered_content_phase) { r->write_event_handler = ngx_http_lua_content_wev_handler; } else { r->write_event_handler = ngx_http_core_run_phases; } return lua_yield(L, 0); } static void ngx_http_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx) { ngx_http_request_t *r; ngx_connection_t *c; ngx_http_upstream_resolved_t *ur; ngx_http_lua_ctx_t *lctx; lua_State *L; ngx_http_lua_socket_udp_upstream_t *u; u_char *p; size_t len; #if defined(nginx_version) && nginx_version >= 1005008 socklen_t socklen; struct sockaddr *sockaddr; #else struct sockaddr_in *sin; #endif ngx_uint_t i; unsigned waiting; u = ctx->data; r = u->request; c = r->connection; ur = u->resolved; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "lua udp socket resolve handler"); lctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (lctx == NULL) { return; } lctx->cur_co_ctx = u->co_ctx; u->co_ctx->cleanup = NULL; L = lctx->cur_co_ctx->co; dd("setting socket_ready to 1"); waiting = u->waiting; if (ctx->state) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "lua udp socket resolver error: %s (waiting: %d)", ngx_resolver_strerror(ctx->state), (int) u->waiting); lua_pushnil(L); lua_pushlstring(L, (char *) ctx->name.data, ctx->name.len); lua_pushfstring(L, " could not be resolved (%d: %s)", (int) ctx->state, ngx_resolver_strerror(ctx->state)); lua_concat(L, 2); #if 1 ur->ctx = NULL; ngx_resolve_name_done(ctx); #endif u->prepare_retvals = ngx_http_lua_socket_error_retval_handler; ngx_http_lua_socket_udp_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_RESOLVER); if (waiting) { ngx_http_run_posted_requests(c); } return; } ur->naddrs = ctx->naddrs; ur->addrs = ctx->addrs; #if (NGX_DEBUG) { # if defined(nginx_version) && nginx_version >= 1005008 u_char text[NGX_SOCKADDR_STRLEN]; ngx_str_t addr; # else in_addr_t addr; # endif ngx_uint_t i; # if defined(nginx_version) && nginx_version >= 1005008 addr.data = text; for (i = 0; i < ctx->naddrs; i++) { addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen, text, NGX_SOCKADDR_STRLEN, 0); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "name was resolved to %V", &addr); } # else for (i = 0; i < ctx->naddrs; i++) { dd("addr i: %d %p", (int) i, &ctx->addrs[i]); addr = ntohl(ctx->addrs[i]); ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, "name was resolved to %ud.%ud.%ud.%ud", (addr >> 24) & 0xff, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff); } # endif } #endif if (ur->naddrs == 0) { ngx_resolve_name_done(ctx); u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_RESOLVER; lua_pushnil(L); lua_pushliteral(L, "name cannot be resolved to a address"); if (waiting) { ngx_http_run_posted_requests(c); } return; } if (ur->naddrs == 1) { i = 0; } else { i = ngx_random() % ur->naddrs; } dd("selected addr index: %d", (int) i); #if defined(nginx_version) && nginx_version >= 1005008 socklen = ur->addrs[i].socklen; sockaddr = ngx_palloc(r->pool, socklen); if (sockaddr == NULL) { goto nomem; } ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen); switch (sockaddr->sa_family) { #if (NGX_HAVE_INET6) case AF_INET6: ((struct sockaddr_in6 *) sockaddr)->sin6_port = htons(ur->port); break; #endif default: /* AF_INET */ ((struct sockaddr_in *) sockaddr)->sin_port = htons(ur->port); } p = ngx_pnalloc(r->pool, NGX_SOCKADDR_STRLEN); if (p == NULL) { goto nomem; } len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1); ur->sockaddr = sockaddr; ur->socklen = socklen; #else /* for nginx older than 1.5.8 */ len = NGX_INET_ADDRSTRLEN + sizeof(":65536") - 1; p = ngx_pnalloc(r->pool, len + sizeof(struct sockaddr_in)); if (p == NULL) { goto nomem; } sin = (struct sockaddr_in *) &p[len]; ngx_memzero(sin, sizeof(struct sockaddr_in)); len = ngx_inet_ntop(AF_INET, &ur->addrs[i], p, NGX_INET_ADDRSTRLEN); len = ngx_sprintf(&p[len], ":%d", ur->port) - p; sin->sin_family = AF_INET; sin->sin_port = htons(ur->port); sin->sin_addr.s_addr = ur->addrs[i]; ur->sockaddr = (struct sockaddr *) sin; ur->socklen = sizeof(struct sockaddr_in); #endif ur->host.data = p; ur->host.len = len; ur->naddrs = 1; ur->ctx = NULL; ngx_resolve_name_done(ctx); u->waiting = 0; if (waiting) { lctx->resume_handler = ngx_http_lua_socket_udp_resume; r->write_event_handler(r); ngx_http_run_posted_requests(c); } else { (void) ngx_http_lua_socket_resolve_retval_handler(r, u, L); } return; nomem: ngx_resolve_name_done(ctx); u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_NOMEM; lua_pushnil(L); lua_pushliteral(L, "no memory"); if (waiting) { ngx_http_run_posted_requests(c); } } static int ngx_http_lua_socket_resolve_retval_handler(ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u, lua_State *L) { ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; ngx_udp_connection_t *uc; ngx_connection_t *c; ngx_http_cleanup_t *cln; ngx_http_upstream_resolved_t *ur; ngx_int_t rc; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp socket resolve retval handler"); if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_RESOLVER) { return 2; } uc = &u->udp_connection; ur = u->resolved; if (ur->sockaddr) { uc->sockaddr = ur->sockaddr; uc->socklen = ur->socklen; uc->server = ur->host; } else { lua_pushnil(L); lua_pushliteral(L, "resolver not working"); return 2; } rc = ngx_http_lua_udp_connect(uc); if (rc != NGX_OK) { u->socket_errno = ngx_socket_errno; } if (u->cleanup == NULL) { cln = ngx_http_cleanup_add(r, 0); if (cln == NULL) { u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_ERROR; lua_pushnil(L); lua_pushliteral(L, "out of memory"); return 2; } cln->handler = ngx_http_lua_socket_udp_cleanup; cln->data = u; u->cleanup = &cln->handler; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp socket connect: %i", rc); if (rc != NGX_OK) { return ngx_http_lua_socket_error_retval_handler(r, u, L); } /* rc == NGX_OK */ c = uc->connection; c->data = u; c->write->handler = NULL; c->read->handler = ngx_http_lua_socket_udp_handler; c->read->resolver = 0; c->pool = r->pool; c->log = r->connection->log; c->read->log = c->log; c->write->log = c->log; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); coctx = ctx->cur_co_ctx; coctx->data = u; u->read_event_handler = ngx_http_lua_socket_dummy_handler; lua_pushinteger(L, 1); return 1; } static int ngx_http_lua_socket_error_retval_handler(ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u, lua_State *L) { u_char errstr[NGX_MAX_ERROR_STR]; u_char *p; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp socket error retval handler"); if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_RESOLVER) { return 2; } lua_pushnil(L); if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_PARTIALWRITE) { lua_pushliteral(L, "partial write"); } else if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_TIMEOUT) { lua_pushliteral(L, "timeout"); } else if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_CLOSED) { lua_pushliteral(L, "closed"); } else if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_BUFTOOSMALL) { lua_pushliteral(L, "buffer too small"); } else if (u->ft_type & NGX_HTTP_LUA_SOCKET_FT_NOMEM) { lua_pushliteral(L, "out of memory"); } else { if (u->socket_errno) { #if (nginx_version >= 1000000) p = ngx_strerror(u->socket_errno, errstr, sizeof(errstr)); #else p = ngx_strerror_r(u->socket_errno, errstr, sizeof(errstr)); #endif /* for compatibility with LuaSocket */ ngx_strlow(errstr, errstr, p - errstr); lua_pushlstring(L, (char *) errstr, p - errstr); } else { lua_pushliteral(L, "error"); } } return 2; } static int ngx_http_lua_socket_udp_send(lua_State *L) { ssize_t n; ngx_http_request_t *r; u_char *p; size_t len; ngx_http_lua_socket_udp_upstream_t *u; int type; const char *msg; ngx_str_t query; ngx_http_lua_loc_conf_t *llcf; if (lua_gettop(L) != 2) { return luaL_error(L, "expecting 2 arguments (including the object), " "but got %d", lua_gettop(L)); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "request object not found"); } luaL_checktype(L, 1, LUA_TTABLE); lua_rawgeti(L, 1, SOCKET_CTX_INDEX); u = lua_touserdata(L, -1); lua_pop(L, 1); if (u == NULL || u->udp_connection.connection == NULL) { llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->log_socket_errors) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "attempt to send data on a closed socket: u:%p, c:%p", u, u ? u->udp_connection.connection : NULL); } lua_pushnil(L); lua_pushliteral(L, "closed"); return 2; } if (u->ft_type) { u->ft_type = 0; } if (u->waiting) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; } type = lua_type(L, 2); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: lua_tolstring(L, 2, &len); break; case LUA_TTABLE: len = ngx_http_lua_calc_strlen_in_table(L, 2, 2, 1 /* strict */); break; default: msg = lua_pushfstring(L, "string, number, boolean, nil, " "or array table expected, got %s", lua_typename(L, type)); return luaL_argerror(L, 2, msg); } query.data = lua_newuserdata(L, len); query.len = len; switch (type) { case LUA_TNUMBER: case LUA_TSTRING: p = (u_char *) lua_tolstring(L, 2, &len); ngx_memcpy(query.data, (u_char *) p, len); break; case LUA_TTABLE: (void) ngx_http_lua_copy_str_in_table(L, 2, query.data); break; default: return luaL_error(L, "impossible to reach here"); } u->ft_type = 0; /* mimic ngx_http_upstream_init_request here */ #if 1 u->waiting = 0; #endif dd("sending query %.*s", (int) query.len, query.data); n = ngx_send(u->udp_connection.connection, query.data, query.len); dd("ngx_send returns %d (query len %d)", (int) n, (int) query.len); if (n == NGX_ERROR || n == NGX_AGAIN) { u->socket_errno = ngx_socket_errno; return ngx_http_lua_socket_error_retval_handler(r, u, L); } if (n != (ssize_t) query.len) { dd("not the while query was sent"); u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_PARTIALWRITE; return ngx_http_lua_socket_error_retval_handler(r, u, L); } dd("n == len"); lua_pushinteger(L, 1); return 1; } static int ngx_http_lua_socket_udp_receive(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_socket_udp_upstream_t *u; ngx_int_t rc; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; size_t size; int nargs; ngx_http_lua_loc_conf_t *llcf; nargs = lua_gettop(L); if (nargs != 1 && nargs != 2) { return luaL_error(L, "expecting 1 or 2 arguments " "(including the object), but got %d", nargs); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp socket calling receive() method"); luaL_checktype(L, 1, LUA_TTABLE); lua_rawgeti(L, 1, SOCKET_CTX_INDEX); u = lua_touserdata(L, -1); lua_pop(L, 1); if (u == NULL || u->udp_connection.connection == NULL) { llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->log_socket_errors) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "attempt to receive data on a closed socket: u:%p, " "c:%p", u, u ? u->udp_connection.connection : NULL); } lua_pushnil(L); lua_pushliteral(L, "closed"); return 2; } if (u->ft_type) { u->ft_type = 0; } #if 1 if (u->waiting) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; } #endif ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp socket read timeout: %M", u->read_timeout); size = (size_t) luaL_optnumber(L, 2, UDP_MAX_DATAGRAM_SIZE); size = ngx_min(size, UDP_MAX_DATAGRAM_SIZE); u->recv_buf_size = size; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp socket receive buffer size: %uz", u->recv_buf_size); rc = ngx_http_lua_socket_udp_read(r, u); if (rc == NGX_ERROR) { dd("read failed: %d", (int) u->ft_type); rc = ngx_http_lua_socket_udp_receive_retval_handler(r, u, L); dd("udp receive retval returned: %d", (int) rc); return rc; } if (rc == NGX_OK) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp socket receive done in a single run"); return ngx_http_lua_socket_udp_receive_retval_handler(r, u, L); } /* n == NGX_AGAIN */ u->read_event_handler = ngx_http_lua_socket_udp_read_handler; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } ctx->cur_co_ctx->cleanup = ngx_http_lua_udp_socket_cleanup; if (ctx->entered_content_phase) { r->write_event_handler = ngx_http_lua_content_wev_handler; } else { r->write_event_handler = ngx_http_core_run_phases; } u->co_ctx = ctx->cur_co_ctx; u->waiting = 1; u->prepare_retvals = ngx_http_lua_socket_udp_receive_retval_handler; coctx = ctx->cur_co_ctx; coctx->data = u; return lua_yield(L, 0); } static int ngx_http_lua_socket_udp_receive_retval_handler(ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u, lua_State *L) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp socket receive return value handler"); if (u->ft_type) { return ngx_http_lua_socket_error_retval_handler(r, u, L); } lua_pushlstring(L, (char *) ngx_http_lua_socket_udp_buffer, u->received); return 1; } static int ngx_http_lua_socket_udp_settimeout(lua_State *L) { int n; ngx_int_t timeout; ngx_http_lua_socket_udp_upstream_t *u; n = lua_gettop(L); if (n != 2) { return luaL_error(L, "ngx.socket settimout: expecting at least 2 " "arguments (including the object) but seen %d", lua_gettop(L)); } timeout = (ngx_int_t) lua_tonumber(L, 2); lua_rawseti(L, 1, SOCKET_TIMEOUT_INDEX); lua_rawgeti(L, 1, SOCKET_CTX_INDEX); u = lua_touserdata(L, -1); if (u) { if (timeout > 0) { u->read_timeout = (ngx_msec_t) timeout; } else { u->read_timeout = u->conf->read_timeout; } } return 0; } static void ngx_http_lua_socket_udp_finalize(ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua finalize socket"); if (u->cleanup) { *u->cleanup = NULL; u->cleanup = NULL; } if (u->resolved && u->resolved->ctx) { ngx_resolve_name_done(u->resolved->ctx); u->resolved->ctx = NULL; } if (u->udp_connection.connection) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua close socket connection"); ngx_close_connection(u->udp_connection.connection); u->udp_connection.connection = NULL; } if (u->waiting) { u->waiting = 0; } } static int ngx_http_lua_socket_udp_upstream_destroy(lua_State *L) { ngx_http_lua_socket_udp_upstream_t *u; dd("upstream destroy triggered by Lua GC"); u = lua_touserdata(L, 1); if (u == NULL) { return 0; } if (u->cleanup) { ngx_http_lua_socket_udp_cleanup(u); /* it will clear u->cleanup */ } return 0; } static void ngx_http_lua_socket_dummy_handler(ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp socket dummy handler"); } static ngx_int_t ngx_http_lua_socket_udp_read(ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u) { ngx_connection_t *c; ngx_event_t *rev; ssize_t n; c = u->udp_connection.connection; rev = c->read; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "lua udp socket read data: waiting: %d", (int) u->waiting); n = ngx_udp_recv(u->udp_connection.connection, ngx_http_lua_socket_udp_buffer, u->recv_buf_size); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "lua udp recv returned %z", n); if (n >= 0) { u->received = n; ngx_http_lua_socket_udp_handle_success(r, u); return NGX_OK; } if (n == NGX_ERROR) { u->socket_errno = ngx_socket_errno; ngx_http_lua_socket_udp_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } /* n == NGX_AGAIN */ #if 1 if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_lua_socket_udp_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_ERROR); return NGX_ERROR; } #endif if (rev->active) { ngx_add_timer(rev, u->read_timeout); } else if (rev->timer_set) { ngx_del_timer(rev); } return NGX_AGAIN; } static void ngx_http_lua_socket_udp_read_handler(ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u) { ngx_connection_t *c; ngx_http_lua_loc_conf_t *llcf; c = u->udp_connection.connection; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp socket read handler"); if (c->read->timedout) { c->read->timedout = 0; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->log_socket_errors) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "lua udp socket read timed out"); } ngx_http_lua_socket_udp_handle_error(r, u, NGX_HTTP_LUA_SOCKET_FT_TIMEOUT); return; } #if 1 if (c->read->timer_set) { ngx_del_timer(c->read); } #endif (void) ngx_http_lua_socket_udp_read(r, u); } static void ngx_http_lua_socket_udp_handle_error(ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u, ngx_uint_t ft_type) { ngx_http_lua_ctx_t *ctx; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp socket handle error"); u->ft_type |= ft_type; #if 0 ngx_http_lua_socket_udp_finalize(r, u); #endif u->read_event_handler = ngx_http_lua_socket_dummy_handler; if (u->co_ctx) { u->co_ctx->cleanup = NULL; } if (u->waiting) { u->waiting = 0; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return; } ctx->resume_handler = ngx_http_lua_socket_udp_resume; ctx->cur_co_ctx = u->co_ctx; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp socket waking up the current request"); r->write_event_handler(r); } } static void ngx_http_lua_socket_udp_cleanup(void *data) { ngx_http_lua_socket_udp_upstream_t *u = data; ngx_http_request_t *r; r = u->request; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "cleanup lua udp socket upstream request: \"%V\"", &r->uri); ngx_http_lua_socket_udp_finalize(r, u); } static void ngx_http_lua_socket_udp_handler(ngx_event_t *ev) { ngx_connection_t *c; ngx_http_request_t *r; ngx_http_log_ctx_t *ctx; ngx_http_lua_socket_udp_upstream_t *u; c = ev->data; u = c->data; r = u->request; c = r->connection; ctx = c->log->data; ctx->current_request = r; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp socket handler for \"%V?%V\", wev %d", &r->uri, &r->args, (int) ev->write); u->read_event_handler(r, u); ngx_http_run_posted_requests(c); } static void ngx_http_lua_socket_udp_handle_success(ngx_http_request_t *r, ngx_http_lua_socket_udp_upstream_t *u) { ngx_http_lua_ctx_t *ctx; u->read_event_handler = ngx_http_lua_socket_dummy_handler; if (u->co_ctx) { u->co_ctx->cleanup = NULL; } if (u->waiting) { u->waiting = 0; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return; } ctx->resume_handler = ngx_http_lua_socket_udp_resume; ctx->cur_co_ctx = u->co_ctx; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp socket waking up the current request"); r->write_event_handler(r); } } static ngx_int_t ngx_http_lua_udp_connect(ngx_udp_connection_t *uc) { int rc; ngx_int_t event; ngx_event_t *rev, *wev; ngx_socket_t s; ngx_connection_t *c; s = ngx_socket(uc->sockaddr->sa_family, SOCK_DGRAM, 0); ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &uc->log, 0, "UDP socket %d", s); if (s == -1) { ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno, ngx_socket_n " failed"); return NGX_ERROR; } c = ngx_get_connection(s, &uc->log); if (c == NULL) { if (ngx_close_socket(s) == -1) { ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno, ngx_close_socket_n "failed"); } return NGX_ERROR; } if (ngx_nonblocking(s) == -1) { ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno, ngx_nonblocking_n " failed"); ngx_free_connection(c); if (ngx_close_socket(s) == -1) { ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno, ngx_close_socket_n " failed"); } return NGX_ERROR; } rev = c->read; wev = c->write; rev->log = &uc->log; wev->log = &uc->log; uc->connection = c; c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); #if (NGX_THREADS) /* TODO: lock event when call completion handler */ rev->lock = &c->lock; wev->lock = &c->lock; rev->own_lock = &c->lock; wev->own_lock = &c->lock; #endif #if (NGX_HTTP_LUA_HAVE_SO_PASSCRED) if (uc->sockaddr->sa_family == AF_UNIX) { struct sockaddr addr; addr.sa_family = AF_UNIX; /* just to make valgrind happy */ ngx_memzero(addr.sa_data, sizeof(addr.sa_data)); ngx_log_debug0(NGX_LOG_DEBUG_EVENT, &uc->log, 0, "datagram unix " "domain socket autobind"); if (bind(uc->connection->fd, &addr, sizeof(sa_family_t)) != 0) { ngx_log_error(NGX_LOG_CRIT, &uc->log, ngx_socket_errno, "bind() failed"); return NGX_ERROR; } } #endif ngx_log_debug3(NGX_LOG_DEBUG_EVENT, &uc->log, 0, "connect to %V, fd:%d #%d", &uc->server, s, c->number); rc = connect(s, uc->sockaddr, uc->socklen); /* TODO: aio, iocp */ if (rc == -1) { ngx_log_error(NGX_LOG_CRIT, &uc->log, ngx_socket_errno, "connect() failed"); return NGX_ERROR; } /* UDP sockets are always ready to write */ wev->ready = 1; if (ngx_add_event) { event = (ngx_event_flags & NGX_USE_CLEAR_EVENT) ? /* kqueue, epoll */ NGX_CLEAR_EVENT: /* select, poll, /dev/poll */ NGX_LEVEL_EVENT; /* eventport event type has no meaning: oneshot only */ if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) { return NGX_ERROR; } } else { /* rtsig */ if (ngx_add_conn(c) == NGX_ERROR) { return NGX_ERROR; } } return NGX_OK; } static int ngx_http_lua_socket_udp_close(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_socket_udp_upstream_t *u; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting 1 argument " "(including the object) but seen %d", lua_gettop(L)); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } luaL_checktype(L, 1, LUA_TTABLE); lua_rawgeti(L, 1, SOCKET_CTX_INDEX); u = lua_touserdata(L, -1); lua_pop(L, 1); if (u == NULL || u->udp_connection.connection == NULL) { lua_pushnil(L); lua_pushliteral(L, "closed"); return 2; } if (u->waiting) { lua_pushnil(L); lua_pushliteral(L, "socket busy"); return 2; } ngx_http_lua_socket_udp_finalize(r, u); lua_pushinteger(L, 1); return 1; } static ngx_int_t ngx_http_lua_socket_udp_resume(ngx_http_request_t *r) { int nret; lua_State *vm; ngx_int_t rc; ngx_connection_t *c; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; ngx_http_lua_socket_udp_upstream_t *u; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return NGX_ERROR; } ctx->resume_handler = ngx_http_lua_wev_handler; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp operation done, resuming lua thread"); coctx = ctx->cur_co_ctx; #if 0 ngx_http_lua_probe_info("udp resume"); #endif u = coctx->data; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua udp socket calling prepare retvals handler %p, " "u:%p", u->prepare_retvals, u); nret = u->prepare_retvals(r, u, ctx->cur_co_ctx->co); if (nret == NGX_AGAIN) { return NGX_DONE; } c = r->connection; vm = ngx_http_lua_get_lua_vm(r, ctx); rc = ngx_http_lua_run_thread(vm, r, ctx, nret); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua run thread returned %d", rc); if (rc == NGX_AGAIN) { return ngx_http_lua_run_posted_threads(c, vm, r, ctx); } if (rc == NGX_DONE) { ngx_http_lua_finalize_request(r, NGX_DONE); return ngx_http_lua_run_posted_threads(c, vm, r, ctx); } if (ctx->entered_content_phase) { ngx_http_lua_finalize_request(r, rc); return NGX_DONE; } return rc; } static void ngx_http_lua_udp_resolve_cleanup(void *data) { ngx_resolver_ctx_t *rctx; ngx_http_lua_socket_udp_upstream_t *u; ngx_http_lua_co_ctx_t *coctx = data; u = coctx->data; if (u == NULL) { return; } rctx = u->resolved->ctx; if (rctx == NULL) { return; } ngx_resolve_name_done(rctx); } static void ngx_http_lua_udp_socket_cleanup(void *data) { ngx_http_lua_socket_udp_upstream_t *u; ngx_http_lua_co_ctx_t *coctx = data; u = coctx->data; if (u == NULL) { return; } if (u->request == NULL) { return; } ngx_http_lua_socket_udp_finalize(u->request, u); } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_pcrefix.c0000664000000000000000000000433612305451334017717 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_pcrefix.h" #include "stdio.h" #if (NGX_PCRE) static ngx_pool_t *ngx_http_lua_pcre_pool = NULL; static void *(*old_pcre_malloc)(size_t); static void (*old_pcre_free)(void *ptr); /* XXX: work-around to nginx regex subsystem, must init a memory pool * to use PCRE functions. As PCRE still has memory-leaking problems, * and nginx overwrote pcre_malloc/free hooks with its own static * functions, so nobody else can reuse nginx regex subsystem... */ static void * ngx_http_lua_pcre_malloc(size_t size) { dd("lua pcre pool is %p", ngx_http_lua_pcre_pool); if (ngx_http_lua_pcre_pool) { return ngx_palloc(ngx_http_lua_pcre_pool, size); } fprintf(stderr, "error: lua pcre malloc failed due to empty pcre pool"); return NULL; } static void ngx_http_lua_pcre_free(void *ptr) { dd("lua pcre pool is %p", ngx_http_lua_pcre_pool); if (ngx_http_lua_pcre_pool) { ngx_pfree(ngx_http_lua_pcre_pool, ptr); return; } fprintf(stderr, "error: lua pcre free failed due to empty pcre pool"); } ngx_pool_t * ngx_http_lua_pcre_malloc_init(ngx_pool_t *pool) { ngx_pool_t *old_pool; if (pcre_malloc != ngx_http_lua_pcre_malloc) { dd("overriding nginx pcre malloc and free"); ngx_http_lua_pcre_pool = pool; old_pcre_malloc = pcre_malloc; old_pcre_free = pcre_free; pcre_malloc = ngx_http_lua_pcre_malloc; pcre_free = ngx_http_lua_pcre_free; return NULL; } dd("lua pcre pool was %p", ngx_http_lua_pcre_pool); old_pool = ngx_http_lua_pcre_pool; ngx_http_lua_pcre_pool = pool; dd("lua pcre pool is %p", ngx_http_lua_pcre_pool); return old_pool; } void ngx_http_lua_pcre_malloc_done(ngx_pool_t *old_pool) { dd("lua pcre pool was %p", ngx_http_lua_pcre_pool); ngx_http_lua_pcre_pool = old_pool; dd("lua pcre pool is %p", ngx_http_lua_pcre_pool); if (old_pool == NULL) { pcre_malloc = old_pcre_malloc; pcre_free = old_pcre_free; } } #endif /* NGX_PCRE */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_shdict.c0000664000000000000000000012712112305451334017533 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_shdict.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_api.h" static int ngx_http_lua_shdict_set(lua_State *L); static int ngx_http_lua_shdict_safe_set(lua_State *L); static int ngx_http_lua_shdict_get(lua_State *L); static int ngx_http_lua_shdict_get_stale(lua_State *L); static int ngx_http_lua_shdict_get_helper(lua_State *L, int get_stale); static int ngx_http_lua_shdict_expire(ngx_http_lua_shdict_ctx_t *ctx, ngx_uint_t n); static ngx_int_t ngx_http_lua_shdict_lookup(ngx_shm_zone_t *shm_zone, ngx_uint_t hash, u_char *kdata, size_t klen, ngx_http_lua_shdict_node_t **sdp); static int ngx_http_lua_shdict_set_helper(lua_State *L, int flags); static int ngx_http_lua_shdict_add(lua_State *L); static int ngx_http_lua_shdict_safe_add(lua_State *L); static int ngx_http_lua_shdict_replace(lua_State *L); static int ngx_http_lua_shdict_incr(lua_State *L); static int ngx_http_lua_shdict_delete(lua_State *L); static int ngx_http_lua_shdict_flush_all(lua_State *L); static int ngx_http_lua_shdict_flush_expired(lua_State *L); static int ngx_http_lua_shdict_get_keys(lua_State *L); #define NGX_HTTP_LUA_SHDICT_ADD 0x0001 #define NGX_HTTP_LUA_SHDICT_REPLACE 0x0002 #define NGX_HTTP_LUA_SHDICT_SAFE_STORE 0x0004 ngx_int_t ngx_http_lua_shdict_init_zone(ngx_shm_zone_t *shm_zone, void *data) { ngx_http_lua_shdict_ctx_t *octx = data; size_t len; ngx_http_lua_shdict_ctx_t *ctx; ngx_http_lua_main_conf_t *lmcf; dd("init zone"); ctx = shm_zone->data; if (octx) { ctx->sh = octx->sh; ctx->shpool = octx->shpool; goto done; } ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; if (shm_zone->shm.exists) { ctx->sh = ctx->shpool->data; goto done; } ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_lua_shdict_shctx_t)); if (ctx->sh == NULL) { return NGX_ERROR; } ctx->shpool->data = ctx->sh; ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel, ngx_http_lua_shdict_rbtree_insert_value); ngx_queue_init(&ctx->sh->queue); len = sizeof(" in lua_shared_dict zone \"\"") + shm_zone->shm.name.len; ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len); if (ctx->shpool->log_ctx == NULL) { return NGX_ERROR; } ngx_sprintf(ctx->shpool->log_ctx, " in lua_shared_dict zone \"%V\"%Z", &shm_zone->shm.name); done: dd("get lmcf"); lmcf = ctx->main_conf; dd("lmcf->lua: %p", lmcf->lua); lmcf->shm_zones_inited++; if (lmcf->shm_zones_inited == lmcf->shm_zones->nelts && lmcf->init_handler) { if (lmcf->init_handler(ctx->log, lmcf, lmcf->lua) != 0) { /* an error happened */ return NGX_ERROR; } } return NGX_OK; } void ngx_http_lua_shdict_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) { ngx_rbtree_node_t **p; ngx_http_lua_shdict_node_t *sdn, *sdnt; for ( ;; ) { if (node->key < temp->key) { p = &temp->left; } else if (node->key > temp->key) { p = &temp->right; } else { /* node->key == temp->key */ sdn = (ngx_http_lua_shdict_node_t *) &node->color; sdnt = (ngx_http_lua_shdict_node_t *) &temp->color; p = ngx_memn2cmp(sdn->data, sdnt->data, sdn->key_len, sdnt->key_len) < 0 ? &temp->left : &temp->right; } if (*p == sentinel) { break; } temp = *p; } *p = node; node->parent = temp; node->left = sentinel; node->right = sentinel; ngx_rbt_red(node); } static ngx_int_t ngx_http_lua_shdict_lookup(ngx_shm_zone_t *shm_zone, ngx_uint_t hash, u_char *kdata, size_t klen, ngx_http_lua_shdict_node_t **sdp) { ngx_int_t rc; ngx_time_t *tp; uint64_t now; int64_t ms; ngx_rbtree_node_t *node, *sentinel; ngx_http_lua_shdict_ctx_t *ctx; ngx_http_lua_shdict_node_t *sd; ctx = shm_zone->data; node = ctx->sh->rbtree.root; sentinel = ctx->sh->rbtree.sentinel; while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ sd = (ngx_http_lua_shdict_node_t *) &node->color; rc = ngx_memn2cmp(kdata, sd->data, klen, (size_t) sd->key_len); if (rc == 0) { ngx_queue_remove(&sd->queue); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); *sdp = sd; dd("node expires: %lld", (long long) sd->expires); if (sd->expires != 0) { tp = ngx_timeofday(); now = (uint64_t) tp->sec * 1000 + tp->msec; ms = sd->expires - now; dd("time to live: %lld", (long long) ms); if (ms < 0) { dd("node already expired"); return NGX_DONE; } } return NGX_OK; } node = (rc < 0) ? node->left : node->right; } *sdp = NULL; return NGX_DECLINED; } static int ngx_http_lua_shdict_expire(ngx_http_lua_shdict_ctx_t *ctx, ngx_uint_t n) { ngx_time_t *tp; uint64_t now; ngx_queue_t *q; int64_t ms; ngx_rbtree_node_t *node; ngx_http_lua_shdict_node_t *sd; int freed = 0; tp = ngx_timeofday(); now = (uint64_t) tp->sec * 1000 + tp->msec; /* * n == 1 deletes one or two expired entries * n == 0 deletes oldest entry by force * and one or two zero rate entries */ while (n < 3) { if (ngx_queue_empty(&ctx->sh->queue)) { return freed; } q = ngx_queue_last(&ctx->sh->queue); sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue); if (n++ != 0) { if (sd->expires == 0) { return freed; } ms = sd->expires - now; if (ms > 0) { return freed; } } ngx_queue_remove(q); node = (ngx_rbtree_node_t *) ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); ngx_rbtree_delete(&ctx->sh->rbtree, node); ngx_slab_free_locked(ctx->shpool, node); freed++; } return freed; } void ngx_http_lua_inject_shdict_api(ngx_http_lua_main_conf_t *lmcf, lua_State *L) { ngx_http_lua_shdict_ctx_t *ctx; ngx_uint_t i; ngx_shm_zone_t **zone; if (lmcf->shm_zones != NULL) { lua_createtable(L, 0, lmcf->shm_zones->nelts /* nrec */); /* ngx.shared */ lua_createtable(L, 0 /* narr */, 13 /* nrec */); /* shared mt */ lua_pushcfunction(L, ngx_http_lua_shdict_get); lua_setfield(L, -2, "get"); lua_pushcfunction(L, ngx_http_lua_shdict_get_stale); lua_setfield(L, -2, "get_stale"); lua_pushcfunction(L, ngx_http_lua_shdict_set); lua_setfield(L, -2, "set"); lua_pushcfunction(L, ngx_http_lua_shdict_safe_set); lua_setfield(L, -2, "safe_set"); lua_pushcfunction(L, ngx_http_lua_shdict_add); lua_setfield(L, -2, "add"); lua_pushcfunction(L, ngx_http_lua_shdict_safe_add); lua_setfield(L, -2, "safe_add"); lua_pushcfunction(L, ngx_http_lua_shdict_replace); lua_setfield(L, -2, "replace"); lua_pushcfunction(L, ngx_http_lua_shdict_incr); lua_setfield(L, -2, "incr"); lua_pushcfunction(L, ngx_http_lua_shdict_delete); lua_setfield(L, -2, "delete"); lua_pushcfunction(L, ngx_http_lua_shdict_flush_all); lua_setfield(L, -2, "flush_all"); lua_pushcfunction(L, ngx_http_lua_shdict_flush_expired); lua_setfield(L, -2, "flush_expired"); lua_pushcfunction(L, ngx_http_lua_shdict_get_keys); lua_setfield(L, -2, "get_keys"); lua_pushvalue(L, -1); /* shared mt mt */ lua_setfield(L, -2, "__index"); /* shared mt */ zone = lmcf->shm_zones->elts; for (i = 0; i < lmcf->shm_zones->nelts; i++) { ctx = zone[i]->data; lua_pushlstring(L, (char *) ctx->name.data, ctx->name.len); /* shared mt key */ lua_pushlightuserdata(L, zone[i]); /* shared mt key ud */ lua_pushvalue(L, -3); /* shared mt key ud mt */ lua_setmetatable(L, -2); /* shared mt key ud */ lua_rawset(L, -4); /* shared mt */ } lua_pop(L, 1); /* shared */ } else { lua_newtable(L); /* ngx.shared */ } lua_setfield(L, -2, "shared"); } static int ngx_http_lua_shdict_get(lua_State *L) { return ngx_http_lua_shdict_get_helper(L, 0 /* stale */); } static int ngx_http_lua_shdict_get_stale(lua_State *L) { return ngx_http_lua_shdict_get_helper(L, 1 /* stale */); } static int ngx_http_lua_shdict_get_helper(lua_State *L, int get_stale) { int n; ngx_str_t name; ngx_str_t key; uint32_t hash; ngx_int_t rc; ngx_http_lua_shdict_ctx_t *ctx; ngx_http_lua_shdict_node_t *sd; ngx_str_t value; int value_type; double num; u_char c; ngx_shm_zone_t *zone; uint32_t user_flags = 0; n = lua_gettop(L); if (n != 2) { return luaL_error(L, "expecting exactly two arguments, " "but only seen %d", n); } zone = lua_touserdata(L, 1); if (zone == NULL) { return luaL_error(L, "bad \"zone\" argument"); } ctx = zone->data; name = ctx->name; if (lua_isnil(L, 2)) { lua_pushnil(L); lua_pushliteral(L, "nil key"); return 2; } key.data = (u_char *) luaL_checklstring(L, 2, &key.len); if (key.len == 0) { lua_pushnil(L); lua_pushliteral(L, "empty key"); return 2; } if (key.len > 65535) { lua_pushnil(L); lua_pushliteral(L, "key too long"); return 2; } hash = ngx_crc32_short(key.data, key.len); #if (NGX_DEBUG) ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "fetching key \"%V\" in shared dict \"%V\"", &key, &name); #endif /* NGX_DEBUG */ ngx_shmtx_lock(&ctx->shpool->mutex); #if 1 if (!get_stale) { ngx_http_lua_shdict_expire(ctx, 1); } #endif rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); dd("shdict lookup returns %d", (int) rc); if (rc == NGX_DECLINED || (rc == NGX_DONE && !get_stale)) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushnil(L); return 1; } /* rc == NGX_OK || (rc == NGX_DONE && get_stale) */ value_type = sd->value_type; dd("data: %p", sd->data); dd("key len: %d", (int) sd->key_len); value.data = sd->data + sd->key_len; value.len = (size_t) sd->value_len; switch (value_type) { case LUA_TSTRING: lua_pushlstring(L, (char *) value.data, value.len); break; case LUA_TNUMBER: if (value.len != sizeof(double)) { ngx_shmtx_unlock(&ctx->shpool->mutex); return luaL_error(L, "bad lua number value size found for key %s " "in shared_dict %s: %lu", key.data, name.data, (unsigned long) value.len); } num = *(double *) value.data; lua_pushnumber(L, num); break; case LUA_TBOOLEAN: if (value.len != sizeof(u_char)) { ngx_shmtx_unlock(&ctx->shpool->mutex); return luaL_error(L, "bad lua boolean value size found for key %s " "in shared_dict %s: %lu", key.data, name.data, (unsigned long) value.len); } c = *value.data; lua_pushboolean(L, c ? 1 : 0); break; default: ngx_shmtx_unlock(&ctx->shpool->mutex); return luaL_error(L, "bad value type found for key %s in " "shared_dict %s: %d", key.data, name.data, value_type); } user_flags = sd->user_flags; ngx_shmtx_unlock(&ctx->shpool->mutex); if (get_stale) { /* always return value, flags, stale */ if (user_flags) { lua_pushinteger(L, (lua_Integer) user_flags); } else { lua_pushnil(L); } lua_pushboolean(L, rc == NGX_DONE); return 3; } if (user_flags) { lua_pushinteger(L, (lua_Integer) user_flags); return 2; } return 1; } static int ngx_http_lua_shdict_delete(lua_State *L) { int n; n = lua_gettop(L); if (n != 2) { return luaL_error(L, "expecting 2 arguments, " "but only seen %d", n); } lua_pushnil(L); return ngx_http_lua_shdict_set_helper(L, 0); } static int ngx_http_lua_shdict_flush_all(lua_State *L) { ngx_queue_t *q; ngx_http_lua_shdict_node_t *sd; int n; ngx_http_lua_shdict_ctx_t *ctx; ngx_shm_zone_t *zone; n = lua_gettop(L); if (n != 1) { return luaL_error(L, "expecting 1 argument, but seen %d", n); } luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); zone = lua_touserdata(L, 1); if (zone == NULL) { return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); } ctx = zone->data; ngx_shmtx_lock(&ctx->shpool->mutex); for (q = ngx_queue_head(&ctx->sh->queue); q != ngx_queue_sentinel(&ctx->sh->queue); q = ngx_queue_next(q)) { sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue); sd->expires = 1; } ngx_http_lua_shdict_expire(ctx, 0); ngx_shmtx_unlock(&ctx->shpool->mutex); return 0; } static int ngx_http_lua_shdict_flush_expired(lua_State *L) { ngx_queue_t *q, *prev; ngx_http_lua_shdict_node_t *sd; ngx_http_lua_shdict_ctx_t *ctx; ngx_shm_zone_t *zone; ngx_time_t *tp; int freed = 0; int attempts = 0; ngx_rbtree_node_t *node; uint64_t now; int n; n = lua_gettop(L); if (n != 1 && n != 2) { return luaL_error(L, "expecting 1 or 2 argument(s), but saw %d", n); } luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); zone = lua_touserdata(L, 1); if (zone == NULL) { return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); } if (n == 2) { attempts = luaL_checkint(L, 2); } ctx = zone->data; ngx_shmtx_lock(&ctx->shpool->mutex); if (ngx_queue_empty(&ctx->sh->queue)) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushnumber(L, 0); return 1; } tp = ngx_timeofday(); now = (uint64_t) tp->sec * 1000 + tp->msec; q = ngx_queue_last(&ctx->sh->queue); while (q != ngx_queue_sentinel(&ctx->sh->queue)) { prev = ngx_queue_prev(q); sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue); if (sd->expires != 0 && sd->expires <= now) { ngx_queue_remove(q); node = (ngx_rbtree_node_t *) ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); ngx_rbtree_delete(&ctx->sh->rbtree, node); ngx_slab_free_locked(ctx->shpool, node); freed++; if (attempts && freed == attempts) { break; } } q = prev; } ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushnumber(L, freed); return 1; } /* * This trades CPU for memory. This is potentially slow. O(2n) */ static int ngx_http_lua_shdict_get_keys(lua_State *L) { ngx_queue_t *q, *prev; ngx_http_lua_shdict_node_t *sd; ngx_http_lua_shdict_ctx_t *ctx; ngx_shm_zone_t *zone; ngx_time_t *tp; int total = 0; int attempts = 1024; uint64_t now; int n; n = lua_gettop(L); if (n != 1 && n != 2) { return luaL_error(L, "expecting 1 or 2 argument(s), " "but saw %d", n); } luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); zone = lua_touserdata(L, 1); if (zone == NULL) { return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); } if (n == 2) { attempts = luaL_checkint(L, 2); } ctx = zone->data; ngx_shmtx_lock(&ctx->shpool->mutex); if (ngx_queue_empty(&ctx->sh->queue)) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_createtable(L, 0, 0); return 1; } tp = ngx_timeofday(); now = (uint64_t) tp->sec * 1000 + tp->msec; /* first run through: get total number of elements we need to allocate */ q = ngx_queue_last(&ctx->sh->queue); while (q != ngx_queue_sentinel(&ctx->sh->queue)) { prev = ngx_queue_prev(q); sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue); if (sd->expires == 0 || sd->expires > now) { total++; if (attempts && total == attempts) { break; } } q = prev; } lua_createtable(L, total, 0); /* second run through: add keys to table */ total = 0; q = ngx_queue_last(&ctx->sh->queue); while (q != ngx_queue_sentinel(&ctx->sh->queue)) { prev = ngx_queue_prev(q); sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue); if (sd->expires == 0 || sd->expires > now) { lua_pushlstring(L, (char *) sd->data, sd->key_len); lua_rawseti(L, -2, ++total); if (attempts && total == attempts) { break; } } q = prev; } ngx_shmtx_unlock(&ctx->shpool->mutex); /* table is at top of stack */ return 1; } static int ngx_http_lua_shdict_add(lua_State *L) { return ngx_http_lua_shdict_set_helper(L, NGX_HTTP_LUA_SHDICT_ADD); } static int ngx_http_lua_shdict_safe_add(lua_State *L) { return ngx_http_lua_shdict_set_helper(L, NGX_HTTP_LUA_SHDICT_ADD |NGX_HTTP_LUA_SHDICT_SAFE_STORE); } static int ngx_http_lua_shdict_replace(lua_State *L) { return ngx_http_lua_shdict_set_helper(L, NGX_HTTP_LUA_SHDICT_REPLACE); } static int ngx_http_lua_shdict_set(lua_State *L) { return ngx_http_lua_shdict_set_helper(L, 0); } static int ngx_http_lua_shdict_safe_set(lua_State *L) { return ngx_http_lua_shdict_set_helper(L, NGX_HTTP_LUA_SHDICT_SAFE_STORE); } static int ngx_http_lua_shdict_set_helper(lua_State *L, int flags) { int i, n; ngx_str_t key; uint32_t hash; ngx_int_t rc; ngx_http_lua_shdict_ctx_t *ctx; ngx_http_lua_shdict_node_t *sd; ngx_str_t value; int value_type; double num; u_char c; lua_Number exptime = 0; u_char *p; ngx_rbtree_node_t *node; ngx_time_t *tp; ngx_shm_zone_t *zone; int forcible = 0; /* indicates whether to foricibly override other * valid entries */ int32_t user_flags = 0; n = lua_gettop(L); if (n != 3 && n != 4 && n != 5) { return luaL_error(L, "expecting 3, 4 or 5 arguments, " "but only seen %d", n); } zone = lua_touserdata(L, 1); if (zone == NULL) { return luaL_error(L, "bad \"zone\" argument"); } ctx = zone->data; if (lua_isnil(L, 2)) { lua_pushnil(L); lua_pushliteral(L, "nil key"); return 2; } key.data = (u_char *) luaL_checklstring(L, 2, &key.len); if (key.len == 0) { lua_pushnil(L); lua_pushliteral(L, "empty key"); return 2; } if (key.len > 65535) { lua_pushnil(L); lua_pushliteral(L, "key too long"); return 2; } hash = ngx_crc32_short(key.data, key.len); value_type = lua_type(L, 3); switch (value_type) { case LUA_TSTRING: value.data = (u_char *) lua_tolstring(L, 3, &value.len); break; case LUA_TNUMBER: value.len = sizeof(double); num = lua_tonumber(L, 3); value.data = (u_char *) # break; case LUA_TBOOLEAN: value.len = sizeof(u_char); c = lua_toboolean(L, 3) ? 1 : 0; value.data = &c; break; case LUA_TNIL: if (flags & (NGX_HTTP_LUA_SHDICT_ADD|NGX_HTTP_LUA_SHDICT_REPLACE)) { lua_pushnil(L); lua_pushliteral(L, "attempt to add or replace nil values"); return 2; } value.len = 0; value.data = NULL; break; default: lua_pushnil(L); lua_pushliteral(L, "bad value type"); return 2; } if (n >= 4) { exptime = luaL_checknumber(L, 4); if (exptime < 0) { exptime = 0; } } if (n == 5) { user_flags = (uint32_t) luaL_checkinteger(L, 5); } ngx_shmtx_lock(&ctx->shpool->mutex); #if 1 ngx_http_lua_shdict_expire(ctx, 1); #endif rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); dd("shdict lookup returned %d", (int) rc); if (flags & NGX_HTTP_LUA_SHDICT_REPLACE) { if (rc == NGX_DECLINED || rc == NGX_DONE) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 0); lua_pushliteral(L, "not found"); lua_pushboolean(L, forcible); return 3; } /* rc == NGX_OK */ goto replace; } if (flags & NGX_HTTP_LUA_SHDICT_ADD) { if (rc == NGX_OK) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 0); lua_pushliteral(L, "exists"); lua_pushboolean(L, forcible); return 3; } if (rc == NGX_DONE) { /* exists but expired */ dd("go to replace"); goto replace; } /* rc == NGX_DECLINED */ dd("go to insert"); goto insert; } if (rc == NGX_OK || rc == NGX_DONE) { if (value_type == LUA_TNIL) { goto remove; } replace: if (value.data && value.len == (size_t) sd->value_len) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "lua shared dict set: found old entry and value " "size matched, reusing it"); ngx_queue_remove(&sd->queue); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); sd->key_len = (u_short) key.len; if (exptime > 0) { tp = ngx_timeofday(); sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + (uint64_t) (exptime * 1000); } else { sd->expires = 0; } sd->user_flags = user_flags; sd->value_len = (uint32_t) value.len; dd("setting value type to %d", value_type); sd->value_type = (uint8_t) value_type; p = ngx_copy(sd->data, key.data, key.len); ngx_memcpy(p, value.data, value.len); ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 1); lua_pushnil(L); lua_pushboolean(L, forcible); return 3; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "lua shared dict set: found old entry bug value size " "NOT matched, removing it first"); remove: ngx_queue_remove(&sd->queue); node = (ngx_rbtree_node_t *) ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); ngx_rbtree_delete(&ctx->sh->rbtree, node); ngx_slab_free_locked(ctx->shpool, node); } insert: /* rc == NGX_DECLINED or value size unmatch */ if (value.data == NULL) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 1); lua_pushnil(L); lua_pushboolean(L, 0); return 3; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "lua shared dict set: creating a new entry"); n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_lua_shdict_node_t, data) + key.len + value.len; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { if (flags & NGX_HTTP_LUA_SHDICT_SAFE_STORE) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 0); lua_pushliteral(L, "no memory"); return 2; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "lua shared dict set: overriding non-expired items " "due to memory shortage for entry \"%V\"", &key); for (i = 0; i < 30; i++) { if (ngx_http_lua_shdict_expire(ctx, 0) == 0) { break; } forcible = 1; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node != NULL) { goto allocated; } } ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 0); lua_pushliteral(L, "no memory"); lua_pushboolean(L, forcible); return 3; } allocated: sd = (ngx_http_lua_shdict_node_t *) &node->color; node->key = hash; sd->key_len = (u_short) key.len; if (exptime > 0) { tp = ngx_timeofday(); sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + (uint64_t) (exptime * 1000); } else { sd->expires = 0; } sd->user_flags = user_flags; sd->value_len = (uint32_t) value.len; dd("setting value type to %d", value_type); sd->value_type = (uint8_t) value_type; p = ngx_copy(sd->data, key.data, key.len); ngx_memcpy(p, value.data, value.len); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 1); lua_pushnil(L); lua_pushboolean(L, forcible); return 3; } static int ngx_http_lua_shdict_incr(lua_State *L) { int n; ngx_str_t key; uint32_t hash; ngx_int_t rc; ngx_http_lua_shdict_ctx_t *ctx; ngx_http_lua_shdict_node_t *sd; double num; u_char *p; ngx_shm_zone_t *zone; double value; n = lua_gettop(L); if (n != 3) { return luaL_error(L, "expecting 3 arguments, but only seen %d", n); } if (lua_type(L, 1) != LUA_TLIGHTUSERDATA) { return luaL_error(L, "bad \"zone\" argument"); } zone = lua_touserdata(L, 1); if (zone == NULL) { return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); } ctx = zone->data; if (lua_isnil(L, 2)) { lua_pushnil(L); lua_pushliteral(L, "nil key"); return 2; } key.data = (u_char *) luaL_checklstring(L, 2, &key.len); if (key.len == 0) { lua_pushnil(L); lua_pushliteral(L, "empty key"); return 2; } if (key.len > 65535) { lua_pushnil(L); lua_pushliteral(L, "key too long"); return 2; } hash = ngx_crc32_short(key.data, key.len); value = luaL_checknumber(L, 3); dd("looking up key %.*s in shared dict %.*s", (int) key.len, key.data, (int) ctx->name.len, ctx->name.data); ngx_shmtx_lock(&ctx->shpool->mutex); #if 1 ngx_http_lua_shdict_expire(ctx, 1); #endif rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); dd("shdict lookup returned %d", (int) rc); if (rc == NGX_DECLINED || rc == NGX_DONE) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushnil(L); lua_pushliteral(L, "not found"); return 2; } /* rc == NGX_OK */ if (sd->value_type != LUA_TNUMBER || sd->value_len != sizeof(double)) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushnil(L); lua_pushliteral(L, "not a number"); return 2; } ngx_queue_remove(&sd->queue); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); dd("setting value type to %d", (int) sd->value_type); p = sd->data + key.len; num = *(double *) p; num += value; ngx_memcpy(p, (double *) &num, sizeof(double)); ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushnumber(L, num); lua_pushnil(L); return 2; } ngx_int_t ngx_http_lua_shared_dict_get(ngx_shm_zone_t *zone, u_char *key_data, size_t key_len, ngx_http_lua_value_t *value) { u_char *data; size_t len; uint32_t hash; ngx_int_t rc; ngx_http_lua_shdict_ctx_t *ctx; ngx_http_lua_shdict_node_t *sd; if (zone == NULL) { return NGX_ERROR; } hash = ngx_crc32_short(key_data, key_len); ctx = zone->data; ngx_shmtx_lock(&ctx->shpool->mutex); rc = ngx_http_lua_shdict_lookup(zone, hash, key_data, key_len, &sd); dd("shdict lookup returned %d", (int) rc); if (rc == NGX_DECLINED || rc == NGX_DONE) { ngx_shmtx_unlock(&ctx->shpool->mutex); return rc; } /* rc == NGX_OK */ value->type = sd->value_type; dd("type: %d", (int) value->type); data = sd->data + sd->key_len; len = (size_t) sd->value_len; switch (value->type) { case LUA_TSTRING: if (value->value.s.data == NULL || value->value.s.len == 0) { ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "no string buffer " "initialized"); return NGX_ERROR; } if (len > value->value.s.len) { len = value->value.s.len; } else { value->value.s.len = len; } ngx_memcpy(value->value.s.data, data, len); break; case LUA_TNUMBER: if (len != sizeof(double)) { ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua number " "value size found for key %*s: %lu", key_len, key_data, (unsigned long) len); ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_ERROR; } ngx_memcpy(&value->value.b, data, len); break; case LUA_TBOOLEAN: if (len != sizeof(u_char)) { ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua boolean " "value size found for key %*s: %lu", key_len, key_data, (unsigned long) len); ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_ERROR; } value->value.b = *data; break; default: ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua value type " "found for key %*s: %d", key_len, key_data, (int) value->type); ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_ERROR; } ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_OK; } ngx_shm_zone_t * ngx_http_lua_find_zone(u_char *name_data, size_t name_len) { ngx_str_t *name; ngx_uint_t i; ngx_shm_zone_t *zone; volatile ngx_list_part_t *part; part = &ngx_cycle->shared_memory.part; zone = part->elts; for (i = 0; /* void */ ; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; zone = part->elts; i = 0; } name = &zone[i].shm.name; dd("name: [%.*s] %d", (int) name->len, name->data, (int) name->len); dd("name2: [%.*s] %d", (int) name_len, name_data, (int) name_len); if (name->len == name_len && ngx_strncmp(name->data, name_data, name_len) == 0) { return zone; } } return NULL; } #ifndef NGX_HTTP_LUA_NO_FFI_API int ngx_http_lua_ffi_shdict_store(ngx_shm_zone_t *zone, int op, u_char *key, size_t key_len, int value_type, u_char *str_value_buf, size_t str_value_len, double num_value, int exptime, int user_flags, char **errmsg, int *forcible) { int i, n; u_char c, *p; uint32_t hash; ngx_int_t rc; ngx_time_t *tp; ngx_rbtree_node_t *node; ngx_http_lua_shdict_ctx_t *ctx; ngx_http_lua_shdict_node_t *sd; if (zone == NULL) { return NGX_ERROR; } dd("exptime: %d", exptime); ctx = zone->data; *forcible = 0; hash = ngx_crc32_short(key, key_len); switch (value_type) { case LUA_TSTRING: /* do nothing */ break; case LUA_TNUMBER: dd("num value: %lf", num_value); str_value_buf = (u_char *) &num_value; str_value_len = sizeof(double); break; case LUA_TBOOLEAN: c = num_value ? 1 : 0; str_value_buf = &c; str_value_len = sizeof(u_char); break; case LUA_TNIL: if (op & (NGX_HTTP_LUA_SHDICT_ADD|NGX_HTTP_LUA_SHDICT_REPLACE)) { *errmsg = "attempt to add or replace nil values"; return NGX_ERROR; } str_value_buf = NULL; str_value_len = 0; break; default: *errmsg = "unsupported value type"; return NGX_ERROR; } ngx_shmtx_lock(&ctx->shpool->mutex); #if 1 ngx_http_lua_shdict_expire(ctx, 1); #endif rc = ngx_http_lua_shdict_lookup(zone, hash, key, key_len, &sd); dd("lookup returns %d", (int) rc); if (op & NGX_HTTP_LUA_SHDICT_REPLACE) { if (rc == NGX_DECLINED || rc == NGX_DONE) { ngx_shmtx_unlock(&ctx->shpool->mutex); *errmsg = "not found"; return NGX_DECLINED; } /* rc == NGX_OK */ goto replace; } if (op & NGX_HTTP_LUA_SHDICT_ADD) { if (rc == NGX_OK) { ngx_shmtx_unlock(&ctx->shpool->mutex); *errmsg = "exists"; return NGX_DECLINED; } if (rc == NGX_DONE) { /* exists but expired */ dd("go to replace"); goto replace; } /* rc == NGX_DECLINED */ dd("go to insert"); goto insert; } if (rc == NGX_OK || rc == NGX_DONE) { if (value_type == LUA_TNIL) { goto remove; } replace: if (str_value_buf && str_value_len == (size_t) sd->value_len) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "lua shared dict set: found old entry and value " "size matched, reusing it"); ngx_queue_remove(&sd->queue); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); sd->key_len = (u_short) key_len; if (exptime > 0) { tp = ngx_timeofday(); sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + (uint64_t) exptime; } else { sd->expires = 0; } sd->user_flags = user_flags; sd->value_len = (uint32_t) str_value_len; dd("setting value type to %d", value_type); sd->value_type = (uint8_t) value_type; p = ngx_copy(sd->data, key, key_len); ngx_memcpy(p, str_value_buf, str_value_len); ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_OK; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "lua shared dict set: found old entry bug value size " "NOT matched, removing it first"); remove: ngx_queue_remove(&sd->queue); node = (ngx_rbtree_node_t *) ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); ngx_rbtree_delete(&ctx->sh->rbtree, node); ngx_slab_free_locked(ctx->shpool, node); } insert: /* rc == NGX_DECLINED or value size unmatch */ if (str_value_buf == NULL) { ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_OK; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "lua shared dict set: creating a new entry"); n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_lua_shdict_node_t, data) + key_len + str_value_len; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { if (op & NGX_HTTP_LUA_SHDICT_SAFE_STORE) { ngx_shmtx_unlock(&ctx->shpool->mutex); *errmsg = "no memory"; return NGX_ERROR; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "lua shared dict set: overriding non-expired items " "due to memory shortage for entry \"%*s\"", key_len, key); for (i = 0; i < 30; i++) { if (ngx_http_lua_shdict_expire(ctx, 0) == 0) { break; } *forcible = 1; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node != NULL) { goto allocated; } } ngx_shmtx_unlock(&ctx->shpool->mutex); *errmsg = "no memory"; return NGX_ERROR; } allocated: sd = (ngx_http_lua_shdict_node_t *) &node->color; node->key = hash; sd->key_len = (u_short) key_len; if (exptime > 0) { tp = ngx_timeofday(); sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + (uint64_t) exptime; } else { sd->expires = 0; } sd->user_flags = user_flags; sd->value_len = (uint32_t) str_value_len; dd("setting value type to %d", value_type); sd->value_type = (uint8_t) value_type; p = ngx_copy(sd->data, key, key_len); ngx_memcpy(p, str_value_buf, str_value_len); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_OK; } int ngx_http_lua_ffi_shdict_get(ngx_shm_zone_t *zone, u_char *key, size_t key_len, int *value_type, u_char **str_value_buf, size_t *str_value_len, double *num_value, int *user_flags, int get_stale, int *is_stale) { ngx_str_t name; uint32_t hash; ngx_int_t rc; ngx_http_lua_shdict_ctx_t *ctx; ngx_http_lua_shdict_node_t *sd; ngx_str_t value; if (zone == NULL) { return NGX_ERROR; } ctx = zone->data; name = ctx->name; hash = ngx_crc32_short(key, key_len); #if (NGX_DEBUG) ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "fetching key \"%*s\" in shared dict \"%V\"", key_len, key, &name); #endif /* NGX_DEBUG */ ngx_shmtx_lock(&ctx->shpool->mutex); #if 1 if (!get_stale) { ngx_http_lua_shdict_expire(ctx, 1); } #endif rc = ngx_http_lua_shdict_lookup(zone, hash, key, key_len, &sd); dd("shdict lookup returns %d", (int) rc); if (rc == NGX_DECLINED || (rc == NGX_DONE && !get_stale)) { ngx_shmtx_unlock(&ctx->shpool->mutex); *value_type = LUA_TNIL; return NGX_OK; } /* rc == NGX_OK || (rc == NGX_DONE && get_stale) */ *value_type = sd->value_type; dd("data: %p", sd->data); dd("key len: %d", (int) sd->key_len); value.data = sd->data + sd->key_len; value.len = (size_t) sd->value_len; if (*str_value_len < (size_t) value.len) { if (*value_type != LUA_TSTRING) { return NGX_ERROR; } *str_value_buf = malloc(value.len); if (*str_value_buf == NULL) { ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_ERROR; } } *str_value_len = value.len; switch (*value_type) { case LUA_TSTRING: ngx_memcpy(*str_value_buf, value.data, value.len); break; case LUA_TNUMBER: if (value.len != sizeof(double)) { ngx_shmtx_unlock(&ctx->shpool->mutex); ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua number value size found for key %*s " "in shared_dict %V: %z", key_len, key, &name, value.len); return NGX_ERROR; } *num_value = *(double *) value.data; break; case LUA_TBOOLEAN: if (value.len != sizeof(u_char)) { ngx_shmtx_unlock(&ctx->shpool->mutex); ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad lua boolean value size found for key %*s " "in shared_dict %V: %z", key_len, key, &name, value.len); return NGX_ERROR; } ngx_memcpy(*str_value_buf, value.data, value.len); break; default: ngx_shmtx_unlock(&ctx->shpool->mutex); ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "bad value type found for key %*s in " "shared_dict %V: %d", key_len, key, &name, *value_type); } *user_flags = sd->user_flags; dd("user flags: %d", *user_flags); ngx_shmtx_unlock(&ctx->shpool->mutex); if (get_stale) { /* always return value, flags, stale */ *is_stale = (rc == NGX_DONE); return NGX_OK; } return NGX_OK; } int ngx_http_lua_ffi_shdict_incr(ngx_shm_zone_t *zone, u_char *key, size_t key_len, double *value, char **err) { uint32_t hash; ngx_int_t rc; ngx_http_lua_shdict_ctx_t *ctx; ngx_http_lua_shdict_node_t *sd; double num; u_char *p; ctx = zone->data; hash = ngx_crc32_short(key, key_len); dd("looking up key %.*s in shared dict %.*s", (int) key_len, key, (int) ctx->name.len, ctx->name.data); ngx_shmtx_lock(&ctx->shpool->mutex); #if 1 ngx_http_lua_shdict_expire(ctx, 1); #endif rc = ngx_http_lua_shdict_lookup(zone, hash, key, key_len, &sd); dd("shdict lookup returned %d", (int) rc); if (rc == NGX_DECLINED || rc == NGX_DONE) { ngx_shmtx_unlock(&ctx->shpool->mutex); *err = "not found"; return NGX_ERROR; } /* rc == NGX_OK */ if (sd->value_type != LUA_TNUMBER || sd->value_len != sizeof(double)) { ngx_shmtx_unlock(&ctx->shpool->mutex); *err = "not a number"; return NGX_ERROR; } ngx_queue_remove(&sd->queue); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); dd("setting value type to %d", (int) sd->value_type); p = sd->data + key_len; num = *(double *) p; num += *value; ngx_memcpy(p, (double *) &num, sizeof(double)); ngx_shmtx_unlock(&ctx->shpool->mutex); *value = num; return NGX_OK; } #endif /* NGX_HTTP_LUA_NO_FFI_API */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_setby.c0000664000000000000000000001304712305451335017405 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_setby.h" #include "ngx_http_lua_exception.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_pcrefix.h" #include "ngx_http_lua_time.h" #include "ngx_http_lua_log.h" #include "ngx_http_lua_regex.h" #include "ngx_http_lua_variable.h" #include "ngx_http_lua_string.h" #include "ngx_http_lua_misc.h" #include "ngx_http_lua_consts.h" #include "ngx_http_lua_shdict.h" #include "ngx_http_lua_util.h" static void ngx_http_lua_set_by_lua_env(lua_State *L, ngx_http_request_t *r, size_t nargs, ngx_http_variable_value_t *args); /* chars whose addresses are used as keys in Lua VM regsitry */ static char ngx_http_lua_setby_nargs_key; static char ngx_http_lua_setby_args_key; ngx_int_t ngx_http_lua_set_by_chunk(lua_State *L, ngx_http_request_t *r, ngx_str_t *val, ngx_http_variable_value_t *args, size_t nargs, ngx_str_t *script) { size_t i; ngx_int_t rc; u_char *err_msg; size_t len; u_char *data; #if (NGX_PCRE) ngx_pool_t *old_pool; #endif dd("nargs: %d", (int) nargs); dd("set Lua VM panic handler"); lua_atpanic(L, ngx_http_lua_atpanic); NGX_LUA_EXCEPTION_TRY { dd("initialize nginx context in Lua VM, code chunk at " "stack top sp = 1"); ngx_http_lua_set_by_lua_env(L, r, nargs, args); /* passing directive arguments to the user code */ for (i = 0; i < nargs; i++) { lua_pushlstring(L, (const char *) args[i].data, args[i].len); } #if (NGX_PCRE) /* XXX: work-around to nginx regex subsystem */ old_pool = ngx_http_lua_pcre_malloc_init(r->pool); #endif lua_pushcfunction(L, ngx_http_lua_traceback); lua_insert(L, 1); /* put it under chunk and args */ dd("protected call user code"); rc = lua_pcall(L, nargs, 1, 1); dd("after protected call user code"); lua_remove(L, 1); /* remove traceback function */ #if (NGX_PCRE) /* XXX: work-around to nginx regex subsystem */ ngx_http_lua_pcre_malloc_done(old_pool); #endif if (rc != 0) { /* error occured when running loaded code */ err_msg = (u_char *) lua_tolstring(L, -1, &len); if (err_msg == NULL) { err_msg = (u_char *) "unknown reason"; len = sizeof("unknown reason") - 1; } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to run set_by_lua*: %*s", len, err_msg); lua_settop(L, 0); /* clear remaining elems on stack */ return NGX_ERROR; } data = (u_char *) lua_tolstring(L, -1, &len); if (data) { val->data = ngx_palloc(r->pool, len); if (val->data == NULL) { return NGX_ERROR; } ngx_memcpy(val->data, data, len); val->len = len; } else { val->data = NULL; val->len = 0; } } NGX_LUA_EXCEPTION_CATCH { dd("nginx execution restored"); return NGX_ERROR; } /* clear Lua stack */ lua_settop(L, 0); return NGX_OK; } int ngx_http_lua_setby_param_get(lua_State *L) { int idx; int n; ngx_http_variable_value_t *v; idx = luaL_checkint(L, 2); idx--; /* get number of args from globals */ lua_pushlightuserdata(L, &ngx_http_lua_setby_nargs_key); lua_rawget(L, LUA_GLOBALSINDEX); n = (int) lua_tointeger(L, -1); /* get args from globals */ lua_pushlightuserdata(L, &ngx_http_lua_setby_args_key); lua_rawget(L, LUA_GLOBALSINDEX); v = lua_touserdata(L, -1); if (idx < 0 || idx > n - 1) { lua_pushnil(L); } else { lua_pushlstring(L, (const char *) (v[idx].data), v[idx].len); } return 1; } /** * Set environment table for the given code closure. * * Before: * | code closure | <- top * | ... | * * After: * | code closure | <- top * | ... | * */ static void ngx_http_lua_set_by_lua_env(lua_State *L, ngx_http_request_t *r, size_t nargs, ngx_http_variable_value_t *args) { /* set nginx request pointer to current lua thread's globals table */ ngx_http_lua_set_req(L, r); lua_pushlightuserdata(L, &ngx_http_lua_setby_nargs_key); lua_pushinteger(L, nargs); lua_rawset(L, LUA_GLOBALSINDEX); lua_pushlightuserdata(L, &ngx_http_lua_setby_args_key); lua_pushlightuserdata(L, args); lua_rawset(L, LUA_GLOBALSINDEX); /** * we want to create empty environment for current script * * newt = {} * newt["_G"] = newt * setmetatable(newt, {__index = _G}) * * if a function or symbol is not defined in our env, __index will lookup * in the global env. * * all variables created in the script-env will be thrown away at the end * of the script run. * */ ngx_http_lua_create_new_global_table(L, 0 /* narr */, 1 /* nrec */); /* {{{ make new env inheriting main thread's globals table */ /* the metatable for the new env */ lua_createtable(L, 0 /* narr */, 1 /* nrec */); lua_pushvalue(L, LUA_GLOBALSINDEX); lua_setfield(L, -2, "__index"); lua_setmetatable(L, -2); /* setmetatable(newt, {__index = _G}) */ /* }}} */ lua_setfenv(L, -2); /* set new running env for the code closure */ } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_regex.h0000664000000000000000000000055312305451334017373 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_REGEX_H_INCLUDED_ #define _NGX_HTTP_LUA_REGEX_H_INCLUDED_ #include "ngx_http_lua_common.h" #include "ngx_http_lua_script.h" #if (NGX_PCRE) void ngx_http_lua_inject_regex_api(lua_State *L); #endif #endif /* _NGX_HTTP_LUA_REGEX_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_logby.c0000664000000000000000000001423712305451335017375 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_directive.h" #include "ngx_http_lua_logby.h" #include "ngx_http_lua_exception.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_pcrefix.h" #include "ngx_http_lua_time.h" #include "ngx_http_lua_log.h" #include "ngx_http_lua_regex.h" #include "ngx_http_lua_cache.h" #include "ngx_http_lua_headers.h" #include "ngx_http_lua_variable.h" #include "ngx_http_lua_string.h" #include "ngx_http_lua_misc.h" #include "ngx_http_lua_consts.h" #include "ngx_http_lua_shdict.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_exception.h" static ngx_int_t ngx_http_lua_log_by_chunk(lua_State *L, ngx_http_request_t *r); static void ngx_http_lua_log_by_lua_env(lua_State *L, ngx_http_request_t *r) { /* set nginx request pointer to current lua thread's globals table */ ngx_http_lua_set_req(L, r); /** * we want to create empty environment for current script * * newt = {} * newt["_G"] = newt * setmetatable(newt, {__index = _G}) * * if a function or symbol is not defined in our env, __index will lookup * in the global env. * * all variables created in the script-env will be thrown away at the end * of the script run. * */ ngx_http_lua_create_new_global_table(L, 0 /* narr */, 1 /* nrec */); /* {{{ make new env inheriting main thread's globals table */ lua_createtable(L, 0, 1); /* the metatable for the new env */ lua_pushvalue(L, LUA_GLOBALSINDEX); lua_setfield(L, -2, "__index"); lua_setmetatable(L, -2); /* setmetatable({}, {__index = _G}) */ /* }}} */ lua_setfenv(L, -2); /* set new running env for the code closure */ } ngx_int_t ngx_http_lua_log_handler(ngx_http_request_t *r) { ngx_http_lua_loc_conf_t *llcf; ngx_int_t rc; lua_State *L; ngx_http_lua_ctx_t *ctx; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua log handler, uri:\"%V\" c:%ud", &r->uri, r->main->count); llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->log_handler == NULL) { dd("no log handler found"); return NGX_DECLINED; } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); dd("ctx = %p", ctx); if (ctx == NULL) { ctx = ngx_http_lua_create_ctx(r); if (ctx == NULL) { return NGX_ERROR; } } ctx->context = NGX_HTTP_LUA_CONTEXT_LOG; dd("calling log handler"); rc = llcf->log_handler(r); /* we must release the ngx.ctx table here because request cleanup runs * before log phase handlers */ if (ctx->ctx_ref != LUA_NOREF) { L = ngx_http_lua_get_lua_vm(r, ctx); ngx_http_lua_release_ngx_ctx_table(r->connection->log, L, ctx); } return rc; } ngx_int_t ngx_http_lua_log_handler_inline(ngx_http_request_t *r) { lua_State *L; ngx_int_t rc; ngx_http_lua_loc_conf_t *llcf; dd("log by lua inline"); llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); L = ngx_http_lua_get_lua_vm(r, NULL); /* load Lua inline script (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadbuffer(L, llcf->log_src.value.data, llcf->log_src.value.len, llcf->log_src_key, "log_by_lua"); if (rc != NGX_OK) { return NGX_ERROR; } return ngx_http_lua_log_by_chunk(L, r); } ngx_int_t ngx_http_lua_log_handler_file(ngx_http_request_t *r) { lua_State *L; ngx_int_t rc; u_char *script_path; ngx_http_lua_loc_conf_t *llcf; ngx_str_t eval_src; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (ngx_http_complex_value(r, &llcf->log_src, &eval_src) != NGX_OK) { return NGX_ERROR; } script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data, eval_src.len); if (script_path == NULL) { return NGX_ERROR; } L = ngx_http_lua_get_lua_vm(r, NULL); /* load Lua script file (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadfile(L, script_path, llcf->log_src_key); if (rc != NGX_OK) { return NGX_ERROR; } return ngx_http_lua_log_by_chunk(L, r); } ngx_int_t ngx_http_lua_log_by_chunk(lua_State *L, ngx_http_request_t *r) { ngx_int_t rc; u_char *err_msg; size_t len; #if (NGX_PCRE) ngx_pool_t *old_pool; #endif /* set Lua VM panic handler */ lua_atpanic(L, ngx_http_lua_atpanic); NGX_LUA_EXCEPTION_TRY { /* initialize nginx context in Lua VM, code chunk at stack top sp = 1 */ ngx_http_lua_log_by_lua_env(L, r); #if (NGX_PCRE) /* XXX: work-around to nginx regex subsystem */ old_pool = ngx_http_lua_pcre_malloc_init(r->pool); #endif lua_pushcfunction(L, ngx_http_lua_traceback); lua_insert(L, 1); /* put it under chunk and args */ /* protected call user code */ rc = lua_pcall(L, 0, 1, 1); lua_remove(L, 1); /* remove traceback function */ #if (NGX_PCRE) /* XXX: work-around to nginx regex subsystem */ ngx_http_lua_pcre_malloc_done(old_pool); #endif if (rc != 0) { /* error occured when running loaded code */ err_msg = (u_char *) lua_tolstring(L, -1, &len); if (err_msg == NULL) { err_msg = (u_char *) "unknown reason"; len = sizeof("unknown reason") - 1; } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to run log_by_lua*: %*s", len, err_msg); lua_settop(L, 0); /* clear remaining elems on stack */ return NGX_ERROR; } } NGX_LUA_EXCEPTION_CATCH { dd("nginx execution restored"); return NGX_ERROR; } /* clear Lua stack */ lua_settop(L, 0); return NGX_OK; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_uthread.c0000664000000000000000000001156612305451335017717 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_uthread.h" #include "ngx_http_lua_coroutine.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_probe.h" #if 1 #undef ngx_http_lua_probe_info #define ngx_http_lua_probe_info(msg) #endif static int ngx_http_lua_uthread_spawn(lua_State *L); static int ngx_http_lua_uthread_wait(lua_State *L); void ngx_http_lua_inject_uthread_api(ngx_log_t *log, lua_State *L) { /* new thread table */ lua_createtable(L, 0 /* narr */, 2 /* nrec */); lua_pushcfunction(L, ngx_http_lua_uthread_spawn); lua_setfield(L, -2, "spawn"); lua_pushcfunction(L, ngx_http_lua_uthread_wait); lua_setfield(L, -2, "wait"); lua_setfield(L, -2, "thread"); } static int ngx_http_lua_uthread_spawn(lua_State *L) { int n; ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx = NULL; n = lua_gettop(L); r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } ngx_http_lua_coroutine_create_helper(L, r, ctx, &coctx); /* anchor the newly created coroutine into the Lua registry */ lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key); lua_rawget(L, LUA_REGISTRYINDEX); lua_pushvalue(L, -2); coctx->co_ref = luaL_ref(L, -2); lua_pop(L, 1); if (n > 1) { lua_replace(L, 1); lua_xmove(L, coctx->co, n - 1); } coctx->is_uthread = 1; ctx->uthreads++; coctx->co_status = NGX_HTTP_LUA_CO_RUNNING; ctx->co_op = NGX_HTTP_LUA_USER_THREAD_RESUME; ctx->cur_co_ctx->thread_spawn_yielded = 1; if (ngx_http_lua_post_thread(r, ctx, ctx->cur_co_ctx) != NGX_OK) { return luaL_error(L, "out of memory"); } coctx->parent_co_ctx = ctx->cur_co_ctx; ctx->cur_co_ctx = coctx; ngx_http_lua_probe_user_thread_spawn(r, L, coctx->co); return lua_yield(L, 1); } static int ngx_http_lua_uthread_wait(lua_State *L) { int i, nargs, nrets; lua_State *sub_co; ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx, *sub_coctx; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER); coctx = ctx->cur_co_ctx; nargs = lua_gettop(L); for (i = 1; i <= nargs; i++) { sub_co = lua_tothread(L, i); luaL_argcheck(L, sub_co, i, "lua thread expected"); sub_coctx = ngx_http_lua_get_co_ctx(sub_co, ctx); if (sub_coctx == NULL) { return luaL_error(L, "no co ctx found"); } if (!sub_coctx->is_uthread) { return luaL_error(L, "attempt to wait on a coroutine that is " "not a user thread"); } if (sub_coctx->parent_co_ctx != coctx) { return luaL_error(L, "only the parent coroutine can wait on the " "thread"); } switch (sub_coctx->co_status) { case NGX_HTTP_LUA_CO_ZOMBIE: ngx_http_lua_probe_info("found zombie child"); nrets = lua_gettop(sub_coctx->co); dd("child retval count: %d, %s: %s", (int) nrets, luaL_typename(sub_coctx->co, -1), lua_tostring(sub_coctx->co, -1)); if (nrets) { lua_xmove(sub_coctx->co, L, nrets); } #if 1 ngx_http_lua_del_thread(r, L, ctx, sub_coctx); ctx->uthreads--; #endif return nrets; case NGX_HTTP_LUA_CO_DEAD: dd("uthread already waited: %p (parent %p)", sub_coctx, coctx); if (i < nargs) { /* just ignore it if it is not the last one */ continue; } /* being the last one */ lua_pushnil(L); lua_pushliteral(L, "already waited"); return 2; default: dd("uthread %p still alive, status: %d, parent %p", sub_coctx, sub_coctx->co_status, coctx); break; } ngx_http_lua_probe_user_thread_wait(L, sub_coctx->co); sub_coctx->waited_by_parent = 1; } return lua_yield(L, 0); } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_ctx.h0000664000000000000000000000076212305451335017062 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_CTX_H_INCLUDED_ #define _NGX_HTTP_LUA_CTX_H_INCLUDED_ #include "ngx_http_lua_common.h" int ngx_http_lua_ngx_get_ctx(lua_State *L); int ngx_http_lua_ngx_set_ctx(lua_State *L); int ngx_http_lua_ngx_set_ctx_helper(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, int index); #endif /* _NGX_HTTP_LUA_CTX_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_worker.h0000664000000000000000000000041412305451335017567 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_WORKER_H_INCLUDED_ #define _NGX_HTTP_LUA_WORKER_H_INCLUDED_ #include "ngx_http_lua_common.h" void ngx_http_lua_inject_worker_api(lua_State *L); #endif /* _NGX_HTTP_LUA_WORKER_H_INCLUDED_ */ debian/modules/nginx-lua/src/ngx_http_lua_module.c0000664000000000000000000005601012305451335017541 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_directive.h" #include "ngx_http_lua_capturefilter.h" #include "ngx_http_lua_contentby.h" #include "ngx_http_lua_rewriteby.h" #include "ngx_http_lua_accessby.h" #include "ngx_http_lua_logby.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_headerfilterby.h" #include "ngx_http_lua_bodyfilterby.h" #include "ngx_http_lua_initby.h" #include "ngx_http_lua_probe.h" #if !defined(nginx_version) || nginx_version < 8054 #error "at least nginx 0.8.54 is required" #endif static void *ngx_http_lua_create_main_conf(ngx_conf_t *cf); static char *ngx_http_lua_init_main_conf(ngx_conf_t *cf, void *conf); static void *ngx_http_lua_create_loc_conf(ngx_conf_t *cf); static char *ngx_http_lua_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); static ngx_int_t ngx_http_lua_init(ngx_conf_t *cf); static char *ngx_http_lua_lowat_check(ngx_conf_t *cf, void *post, void *data); static ngx_conf_post_t ngx_http_lua_lowat_post = { ngx_http_lua_lowat_check }; static volatile ngx_cycle_t *ngx_http_lua_prev_cycle = NULL; static ngx_command_t ngx_http_lua_cmds[] = { { ngx_string("lua_max_running_timers"), NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_HTTP_MAIN_CONF_OFFSET, offsetof(ngx_http_lua_main_conf_t, max_running_timers), NULL }, { ngx_string("lua_max_pending_timers"), NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_HTTP_MAIN_CONF_OFFSET, offsetof(ngx_http_lua_main_conf_t, max_pending_timers), NULL }, { ngx_string("lua_shared_dict"), NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2, ngx_http_lua_shared_dict, 0, 0, NULL }, #if (NGX_PCRE) { ngx_string("lua_regex_cache_max_entries"), NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_HTTP_MAIN_CONF_OFFSET, offsetof(ngx_http_lua_main_conf_t, regex_cache_max_entries), NULL }, { ngx_string("lua_regex_match_limit"), NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_HTTP_MAIN_CONF_OFFSET, offsetof(ngx_http_lua_main_conf_t, regex_match_limit), NULL }, #endif { ngx_string("lua_package_cpath"), NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, ngx_http_lua_package_cpath, NGX_HTTP_MAIN_CONF_OFFSET, 0, NULL }, { ngx_string("lua_package_path"), NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, ngx_http_lua_package_path, NGX_HTTP_MAIN_CONF_OFFSET, 0, NULL }, { ngx_string("lua_code_cache"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_FLAG, ngx_http_lua_code_cache, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_lua_loc_conf_t, enable_code_cache), NULL }, { ngx_string("lua_need_request_body"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_FLAG, ngx_conf_set_flag_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_lua_loc_conf_t, force_read_body), NULL }, { ngx_string("lua_transform_underscores_in_response_headers"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_FLAG, ngx_conf_set_flag_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_lua_loc_conf_t, transform_underscores_in_resp_headers), NULL }, { ngx_string("lua_socket_log_errors"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_FLAG, ngx_conf_set_flag_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_lua_loc_conf_t, log_socket_errors), NULL }, { ngx_string("init_by_lua"), NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, ngx_http_lua_init_by_lua, NGX_HTTP_MAIN_CONF_OFFSET, 0, (void *) ngx_http_lua_init_by_inline }, { ngx_string("init_by_lua_file"), NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, ngx_http_lua_init_by_lua, NGX_HTTP_MAIN_CONF_OFFSET, 0, (void *) ngx_http_lua_init_by_file }, #if defined(NDK) && NDK /* set_by_lua $res [$arg1 [$arg2 [...]]] */ { ngx_string("set_by_lua"), NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_2MORE, ngx_http_lua_set_by_lua, NGX_HTTP_LOC_CONF_OFFSET, 0, (void *) ngx_http_lua_filter_set_by_lua_inline }, /* set_by_lua_file $res rel/or/abs/path/to/script [$arg1 [$arg2 [..]]] */ { ngx_string("set_by_lua_file"), NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_2MORE, ngx_http_lua_set_by_lua_file, NGX_HTTP_LOC_CONF_OFFSET, 0, (void *) ngx_http_lua_filter_set_by_lua_file }, #endif /* rewrite_by_lua */ { ngx_string("rewrite_by_lua"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE1, ngx_http_lua_rewrite_by_lua, NGX_HTTP_LOC_CONF_OFFSET, 0, (void *) ngx_http_lua_rewrite_handler_inline }, /* access_by_lua */ { ngx_string("access_by_lua"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE1, ngx_http_lua_access_by_lua, NGX_HTTP_LOC_CONF_OFFSET, 0, (void *) ngx_http_lua_access_handler_inline }, /* content_by_lua */ { ngx_string("content_by_lua"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, ngx_http_lua_content_by_lua, NGX_HTTP_LOC_CONF_OFFSET, 0, (void *) ngx_http_lua_content_handler_inline }, /* log_by_lua */ { ngx_string("log_by_lua"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE1, ngx_http_lua_log_by_lua, NGX_HTTP_LOC_CONF_OFFSET, 0, (void *) ngx_http_lua_log_handler_inline }, { ngx_string("rewrite_by_lua_file"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE1, ngx_http_lua_rewrite_by_lua, NGX_HTTP_LOC_CONF_OFFSET, 0, (void *) ngx_http_lua_rewrite_handler_file }, { ngx_string("rewrite_by_lua_no_postpone"), NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, NGX_HTTP_MAIN_CONF_OFFSET, offsetof(ngx_http_lua_main_conf_t, postponed_to_rewrite_phase_end), NULL }, { ngx_string("access_by_lua_file"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE1, ngx_http_lua_access_by_lua, NGX_HTTP_LOC_CONF_OFFSET, 0, (void *) ngx_http_lua_access_handler_file }, /* content_by_lua_file rel/or/abs/path/to/script */ { ngx_string("content_by_lua_file"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, ngx_http_lua_content_by_lua, NGX_HTTP_LOC_CONF_OFFSET, 0, (void *) ngx_http_lua_content_handler_file }, { ngx_string("log_by_lua_file"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE1, ngx_http_lua_log_by_lua, NGX_HTTP_LOC_CONF_OFFSET, 0, (void *) ngx_http_lua_log_handler_file }, /* header_filter_by_lua */ { ngx_string("header_filter_by_lua"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE1, ngx_http_lua_header_filter_by_lua, NGX_HTTP_LOC_CONF_OFFSET, 0, (void *) ngx_http_lua_header_filter_inline }, { ngx_string("header_filter_by_lua_file"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE1, ngx_http_lua_header_filter_by_lua, NGX_HTTP_LOC_CONF_OFFSET, 0, (void *) ngx_http_lua_header_filter_file }, { ngx_string("body_filter_by_lua"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE1, ngx_http_lua_body_filter_by_lua, NGX_HTTP_LOC_CONF_OFFSET, 0, (void *) ngx_http_lua_body_filter_inline }, { ngx_string("body_filter_by_lua_file"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE1, ngx_http_lua_body_filter_by_lua, NGX_HTTP_LOC_CONF_OFFSET, 0, (void *) ngx_http_lua_body_filter_file }, { ngx_string("lua_socket_keepalive_timeout"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, ngx_conf_set_msec_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_lua_loc_conf_t, keepalive_timeout), NULL }, { ngx_string("lua_socket_connect_timeout"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, ngx_conf_set_msec_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_lua_loc_conf_t, connect_timeout), NULL }, { ngx_string("lua_socket_send_timeout"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, ngx_conf_set_msec_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_lua_loc_conf_t, send_timeout), NULL }, { ngx_string("lua_socket_send_lowat"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_lua_loc_conf_t, send_lowat), &ngx_http_lua_lowat_post }, { ngx_string("lua_socket_buffer_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_lua_loc_conf_t, buffer_size), NULL }, { ngx_string("lua_socket_pool_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_lua_loc_conf_t, pool_size), NULL }, { ngx_string("lua_socket_read_timeout"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, ngx_conf_set_msec_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_lua_loc_conf_t, read_timeout), NULL }, { ngx_string("lua_http10_buffering"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_FLAG, ngx_conf_set_flag_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_lua_loc_conf_t, http10_buffering), NULL }, { ngx_string("lua_check_client_abort"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_FLAG, ngx_conf_set_flag_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_lua_loc_conf_t, check_client_abort), NULL }, { ngx_string("lua_use_default_type"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_FLAG, ngx_conf_set_flag_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_lua_loc_conf_t, use_default_type), NULL }, ngx_null_command }; ngx_http_module_t ngx_http_lua_module_ctx = { NULL, /* preconfiguration */ ngx_http_lua_init, /* postconfiguration */ ngx_http_lua_create_main_conf, /* create main configuration */ ngx_http_lua_init_main_conf, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ ngx_http_lua_create_loc_conf, /* create location configuration */ ngx_http_lua_merge_loc_conf /* merge location configuration */ }; ngx_module_t ngx_http_lua_module = { NGX_MODULE_V1, &ngx_http_lua_module_ctx, /* module context */ ngx_http_lua_cmds, /* module directives */ NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ NULL, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING }; static ngx_int_t ngx_http_lua_init(ngx_conf_t *cf) { int multi_http_blocks; ngx_int_t rc; ngx_array_t *arr; ngx_http_handler_pt *h; ngx_http_core_main_conf_t *cmcf; ngx_http_lua_main_conf_t *lmcf; lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module); if (ngx_http_lua_prev_cycle != ngx_cycle) { ngx_http_lua_prev_cycle = ngx_cycle; multi_http_blocks = 0; } else { multi_http_blocks = 1; } if (multi_http_blocks || lmcf->requires_capture_filter) { rc = ngx_http_lua_capture_filter_init(cf); if (rc != NGX_OK) { return rc; } } if (lmcf->postponed_to_rewrite_phase_end == NGX_CONF_UNSET) { lmcf->postponed_to_rewrite_phase_end = 0; } cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); if (lmcf->requires_rewrite) { h = ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers); if (h == NULL) { return NGX_ERROR; } *h = ngx_http_lua_rewrite_handler; } if (lmcf->requires_access) { h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); if (h == NULL) { return NGX_ERROR; } *h = ngx_http_lua_access_handler; } dd("requires log: %d", (int) lmcf->requires_log); if (lmcf->requires_log) { arr = &cmcf->phases[NGX_HTTP_LOG_PHASE].handlers; h = ngx_array_push(arr); if (h == NULL) { return NGX_ERROR; } if (arr->nelts > 1) { h = arr->elts; ngx_memmove(&h[1], h, (arr->nelts - 1) * sizeof(ngx_http_handler_pt)); } *h = ngx_http_lua_log_handler; } if (multi_http_blocks || lmcf->requires_header_filter) { rc = ngx_http_lua_header_filter_init(); if (rc != NGX_OK) { return rc; } } if (multi_http_blocks || lmcf->requires_body_filter) { rc = ngx_http_lua_body_filter_init(); if (rc != NGX_OK) { return rc; } } if (lmcf->lua == NULL) { dd("initializing lua vm"); ngx_http_lua_content_length_hash = ngx_http_lua_hash_literal("content-length"); ngx_http_lua_location_hash = ngx_http_lua_hash_literal("location"); lmcf->lua = ngx_http_lua_init_vm(NULL, cf->cycle, cf->pool, lmcf, cf->log, NULL); if (lmcf->lua == NULL) { ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "failed to initialize Lua VM"); return NGX_ERROR; } if (!lmcf->requires_shm && lmcf->init_handler) { if (lmcf->init_handler(cf->log, lmcf, lmcf->lua) != NGX_OK) { /* an error happened */ return NGX_ERROR; } } dd("Lua VM initialized!"); } return NGX_OK; } static char * ngx_http_lua_lowat_check(ngx_conf_t *cf, void *post, void *data) { #if (NGX_FREEBSD) ssize_t *np = data; if ((u_long) *np >= ngx_freebsd_net_inet_tcp_sendspace) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"fastcgi_send_lowat\" must be less than %d " "(sysctl net.inet.tcp.sendspace)", ngx_freebsd_net_inet_tcp_sendspace); return NGX_CONF_ERROR; } #elif !(NGX_HAVE_SO_SNDLOWAT) ssize_t *np = data; ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "\"fastcgi_send_lowat\" is not supported, ignored"); *np = 0; #endif return NGX_CONF_OK; } static void * ngx_http_lua_create_main_conf(ngx_conf_t *cf) { ngx_http_lua_main_conf_t *lmcf; lmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_lua_main_conf_t)); if (lmcf == NULL) { return NULL; } /* set by ngx_pcalloc: * lmcf->lua = NULL; * lmcf->lua_path = { 0, NULL }; * lmcf->lua_cpath = { 0, NULL }; * lmcf->pending_timers = 0; * lmcf->running_timers = 0; * lmcf->watcher = NULL; * lmcf->regex_cache_entries = 0; * lmcf->shm_zones = NULL; * lmcf->init_handler = NULL; * lmcf->init_src = { 0, NULL }; * lmcf->shm_zones_inited = 0; * lmcf->preload_hooks = NULL; * lmcf->requires_header_filter = 0; * lmcf->requires_body_filter = 0; * lmcf->requires_capture_filter = 0; * lmcf->requires_rewrite = 0; * lmcf->requires_access = 0; * lmcf->requires_log = 0; * lmcf->requires_shm = 0; */ lmcf->pool = cf->pool; lmcf->max_pending_timers = NGX_CONF_UNSET; lmcf->max_running_timers = NGX_CONF_UNSET; #if (NGX_PCRE) lmcf->regex_cache_max_entries = NGX_CONF_UNSET; lmcf->regex_match_limit = NGX_CONF_UNSET; #endif lmcf->postponed_to_rewrite_phase_end = NGX_CONF_UNSET; dd("nginx Lua module main config structure initialized!"); return lmcf; } static char * ngx_http_lua_init_main_conf(ngx_conf_t *cf, void *conf) { ngx_http_lua_main_conf_t *lmcf = conf; #if (NGX_PCRE) if (lmcf->regex_cache_max_entries == NGX_CONF_UNSET) { lmcf->regex_cache_max_entries = 1024; } if (lmcf->regex_match_limit == NGX_CONF_UNSET) { lmcf->regex_match_limit = 0; } #endif if (lmcf->max_pending_timers == NGX_CONF_UNSET) { lmcf->max_pending_timers = 1024; } if (lmcf->max_running_timers == NGX_CONF_UNSET) { lmcf->max_running_timers = 256; } lmcf->cycle = cf->cycle; return NGX_CONF_OK; } static void * ngx_http_lua_create_loc_conf(ngx_conf_t *cf) { ngx_http_lua_loc_conf_t *conf; conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_lua_loc_conf_t)); if (conf == NULL) { return NGX_CONF_ERROR; } /* set by ngx_pcalloc: * conf->access_src = {{ 0, NULL }, NULL, NULL, NULL}; * conf->access_src_key = NULL * conf->rewrite_src = {{ 0, NULL }, NULL, NULL, NULL}; * conf->rewrite_src_key = NULL * conf->rewrite_handler = NULL; * * conf->content_src = {{ 0, NULL }, NULL, NULL, NULL}; * conf->content_src_key = NULL * conf->content_handler = NULL; * * conf->log_src = {{ 0, NULL }, NULL, NULL, NULL}; * conf->log_src_key = NULL * conf->log_handler = NULL; * * conf->header_filter_src = {{ 0, NULL }, NULL, NULL, NULL}; * conf->header_filter_src_key = NULL * conf->header_filter_handler = NULL; * * conf->body_filter_src = {{ 0, NULL }, NULL, NULL, NULL}; * conf->body_filter_src_key = NULL * conf->body_filter_handler = NULL; */ conf->force_read_body = NGX_CONF_UNSET; conf->enable_code_cache = NGX_CONF_UNSET; conf->http10_buffering = NGX_CONF_UNSET; conf->check_client_abort = NGX_CONF_UNSET; conf->use_default_type = NGX_CONF_UNSET; conf->keepalive_timeout = NGX_CONF_UNSET_MSEC; conf->connect_timeout = NGX_CONF_UNSET_MSEC; conf->send_timeout = NGX_CONF_UNSET_MSEC; conf->read_timeout = NGX_CONF_UNSET_MSEC; conf->send_lowat = NGX_CONF_UNSET_SIZE; conf->buffer_size = NGX_CONF_UNSET_SIZE; conf->pool_size = NGX_CONF_UNSET_UINT; conf->transform_underscores_in_resp_headers = NGX_CONF_UNSET; conf->log_socket_errors = NGX_CONF_UNSET; return conf; } static char * ngx_http_lua_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_http_lua_loc_conf_t *prev = parent; ngx_http_lua_loc_conf_t *conf = child; if (conf->rewrite_src.value.len == 0) { conf->rewrite_src = prev->rewrite_src; conf->rewrite_handler = prev->rewrite_handler; conf->rewrite_src_key = prev->rewrite_src_key; } if (conf->access_src.value.len == 0) { conf->access_src = prev->access_src; conf->access_handler = prev->access_handler; conf->access_src_key = prev->access_src_key; } if (conf->content_src.value.len == 0) { conf->content_src = prev->content_src; conf->content_handler = prev->content_handler; conf->content_src_key = prev->content_src_key; } if (conf->log_src.value.len == 0) { conf->log_src = prev->log_src; conf->log_handler = prev->log_handler; conf->log_src_key = prev->log_src_key; } if (conf->header_filter_src.value.len == 0) { conf->header_filter_src = prev->header_filter_src; conf->header_filter_handler = prev->header_filter_handler; conf->header_filter_src_key = prev->header_filter_src_key; } if (conf->body_filter_src.value.len == 0) { conf->body_filter_src = prev->body_filter_src; conf->body_filter_handler = prev->body_filter_handler; conf->body_filter_src_key = prev->body_filter_src_key; } ngx_conf_merge_value(conf->force_read_body, prev->force_read_body, 0); ngx_conf_merge_value(conf->enable_code_cache, prev->enable_code_cache, 1); ngx_conf_merge_value(conf->http10_buffering, prev->http10_buffering, 1); ngx_conf_merge_value(conf->check_client_abort, prev->check_client_abort, 0); ngx_conf_merge_value(conf->use_default_type, prev->use_default_type, 1); ngx_conf_merge_msec_value(conf->keepalive_timeout, prev->keepalive_timeout, 60000); ngx_conf_merge_msec_value(conf->connect_timeout, prev->connect_timeout, 60000); ngx_conf_merge_msec_value(conf->send_timeout, prev->send_timeout, 60000); ngx_conf_merge_msec_value(conf->read_timeout, prev->read_timeout, 60000); ngx_conf_merge_size_value(conf->send_lowat, prev->send_lowat, 0); ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, (size_t) ngx_pagesize); ngx_conf_merge_uint_value(conf->pool_size, prev->pool_size, 30); ngx_conf_merge_value(conf->transform_underscores_in_resp_headers, prev->transform_underscores_in_resp_headers, 1); ngx_conf_merge_value(conf->log_socket_errors, prev->log_socket_errors, 1); return NGX_CONF_OK; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_control.c0000664000000000000000000003105412305451334017734 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_control.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_coroutine.h" static int ngx_http_lua_ngx_exec(lua_State *L); static int ngx_http_lua_ngx_redirect(lua_State *L); static int ngx_http_lua_ngx_exit(lua_State *L); static int ngx_http_lua_on_abort(lua_State *L); void ngx_http_lua_inject_control_api(ngx_log_t *log, lua_State *L) { /* ngx.redirect */ lua_pushcfunction(L, ngx_http_lua_ngx_redirect); lua_setfield(L, -2, "redirect"); /* ngx.exec */ lua_pushcfunction(L, ngx_http_lua_ngx_exec); lua_setfield(L, -2, "exec"); lua_pushcfunction(L, ngx_http_lua_ngx_exit); lua_setfield(L, -2, "throw_error"); /* deprecated */ /* ngx.exit */ lua_pushcfunction(L, ngx_http_lua_ngx_exit); lua_setfield(L, -2, "exit"); /* ngx.on_abort */ lua_pushcfunction(L, ngx_http_lua_on_abort); lua_setfield(L, -2, "on_abort"); } static int ngx_http_lua_ngx_exec(lua_State *L) { int n; ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_str_t uri; ngx_str_t args, user_args; ngx_uint_t flags; u_char *p; u_char *q; size_t len; const char *msg; n = lua_gettop(L); if (n != 1 && n != 2) { return luaL_error(L, "expecting one or two arguments, but got %d", n); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } args.data = NULL; args.len = 0; /* read the 1st argument (uri) */ p = (u_char *) luaL_checklstring(L, 1, &len); if (len == 0) { return luaL_error(L, "The uri argument is empty"); } uri.data = ngx_palloc(r->pool, len); if (uri.data == NULL) { return luaL_error(L, "out of memory"); } ngx_memcpy(uri.data, p, len); uri.len = len; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); ngx_http_lua_check_if_abortable(L, ctx); if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (n == 2) { /* read the 2nd argument (args) */ dd("args type: %s", luaL_typename(L, 2)); switch (lua_type(L, 2)) { case LUA_TNUMBER: case LUA_TSTRING: p = (u_char *) lua_tolstring(L, 2, &len); user_args.data = ngx_palloc(r->pool, len); if (user_args.data == NULL) { return luaL_error(L, "out of memory"); } ngx_memcpy(user_args.data, p, len); user_args.len = len; break; case LUA_TTABLE: ngx_http_lua_process_args_option(r, L, 2, &user_args); dd("user_args: %.*s", (int) user_args.len, user_args.data); break; case LUA_TNIL: user_args.data = NULL; user_args.len = 0; break; default: msg = lua_pushfstring(L, "string, number, or table expected, " "but got %s", luaL_typename(L, 2)); return luaL_argerror(L, 2, msg); } } else { user_args.data = NULL; user_args.len = 0; } if (user_args.len) { if (args.len == 0) { args = user_args; } else { p = ngx_palloc(r->pool, args.len + user_args.len + 1); if (p == NULL) { return luaL_error(L, "out of memory"); } q = ngx_copy(p, args.data, args.len); *q++ = '&'; ngx_memcpy(q, user_args.data, user_args.len); args.data = p; args.len += user_args.len + 1; } } if (r->header_sent) { return luaL_error(L, "attempt to call ngx.exec after " "sending out response headers"); } ctx->exec_uri = uri; ctx->exec_args = args; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua exec \"%V?%V\"", &ctx->exec_uri, &ctx->exec_args); return lua_yield(L, 0); } static int ngx_http_lua_ngx_redirect(lua_State *L) { ngx_http_lua_ctx_t *ctx; ngx_int_t rc; int n; u_char *p; u_char *uri; size_t len; ngx_http_request_t *r; n = lua_gettop(L); if (n != 1 && n != 2) { return luaL_error(L, "expecting one or two arguments"); } p = (u_char *) luaL_checklstring(L, 1, &len); if (n == 2) { rc = (ngx_int_t) luaL_checknumber(L, 2); if (rc != NGX_HTTP_MOVED_TEMPORARILY && rc != NGX_HTTP_MOVED_PERMANENTLY) { return luaL_error(L, "only ngx.HTTP_MOVED_TEMPORARILY and " "ngx.HTTP_MOVED_PERMANENTLY are allowed"); } } else { rc = NGX_HTTP_MOVED_TEMPORARILY; } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); ngx_http_lua_check_if_abortable(L, ctx); if (r->header_sent) { return luaL_error(L, "attempt to call ngx.redirect after sending out " "the headers"); } uri = ngx_palloc(r->pool, len); if (uri == NULL) { return luaL_error(L, "out of memory"); } ngx_memcpy(uri, p, len); r->headers_out.location = ngx_list_push(&r->headers_out.headers); if (r->headers_out.location == NULL) { return luaL_error(L, "out of memory"); } r->headers_out.location->hash = ngx_http_lua_location_hash; #if 0 dd("location hash: %lu == %lu", (unsigned long) r->headers_out.location->hash, (unsigned long) ngx_hash_key_lc((u_char *) "Location", sizeof("Location") - 1)); #endif r->headers_out.location->value.len = len; r->headers_out.location->value.data = uri; ngx_str_set(&r->headers_out.location->key, "Location"); r->headers_out.status = rc; ctx->exit_code = rc; ctx->exited = 1; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua redirect to \"%V\" with code %i", &r->headers_out.location->value, ctx->exit_code); return lua_yield(L, 0); } static int ngx_http_lua_ngx_exit(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_int_t rc; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting one argument"); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER | NGX_HTTP_LUA_CONTEXT_HEADER_FILTER); rc = (ngx_int_t) luaL_checkinteger(L, 1); if (ctx->no_abort && rc != NGX_ERROR && rc != NGX_HTTP_CLOSE && rc != NGX_HTTP_REQUEST_TIME_OUT && rc != NGX_HTTP_CLIENT_CLOSED_REQUEST) { return luaL_error(L, "attempt to abort with pending subrequests"); } if (r->header_sent && rc >= NGX_HTTP_SPECIAL_RESPONSE && rc != NGX_HTTP_REQUEST_TIME_OUT && rc != NGX_HTTP_CLIENT_CLOSED_REQUEST && rc != NGX_HTTP_CLOSE) { if (rc != (ngx_int_t) r->headers_out.status) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "attempt to " "set status %i via ngx.exit after sending out the " "response status %ui", rc, r->headers_out.status); } rc = NGX_HTTP_OK; } dd("setting exit code: %d", (int) rc); ctx->exit_code = rc; ctx->exited = 1; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua exit with code %i", ctx->exit_code); if (ctx->context & NGX_HTTP_LUA_CONTEXT_HEADER_FILTER) { return 0; } dd("calling yield"); return lua_yield(L, 0); } static int ngx_http_lua_on_abort(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx = NULL; ngx_http_lua_loc_conf_t *llcf; r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } ngx_http_lua_check_fake_request2(L, r, ctx); if (ctx->on_abort_co_ctx) { lua_pushnil(L); lua_pushliteral(L, "duplicate call"); return 2; } llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (!llcf->check_client_abort) { lua_pushnil(L); lua_pushliteral(L, "lua_check_client_abort is off"); return 2; } ngx_http_lua_coroutine_create_helper(L, r, ctx, &coctx); lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key); lua_rawget(L, LUA_REGISTRYINDEX); lua_pushvalue(L, -2); dd("on_wait thread 1: %p", lua_tothread(L, -1)); coctx->co_ref = luaL_ref(L, -2); lua_pop(L, 1); coctx->is_uthread = 1; ctx->on_abort_co_ctx = coctx; dd("on_wait thread 2: %p", coctx->co); coctx->co_status = NGX_HTTP_LUA_CO_SUSPENDED; coctx->parent_co_ctx = ctx->cur_co_ctx; lua_pushinteger(L, 1); return 1; } #ifndef NGX_HTTP_LUA_NO_FFI_API int ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status, u_char *err, size_t *errlen) { ngx_http_lua_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { *errlen = ngx_snprintf(err, *errlen, "no request ctx found") - err; return NGX_ERROR; } if (ngx_http_lua_ffi_check_context(ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT | NGX_HTTP_LUA_CONTEXT_TIMER | NGX_HTTP_LUA_CONTEXT_HEADER_FILTER, err, errlen) != NGX_OK) { return NGX_ERROR; } if (ctx->no_abort && status != NGX_ERROR && status != NGX_HTTP_CLOSE && status != NGX_HTTP_REQUEST_TIME_OUT && status != NGX_HTTP_CLIENT_CLOSED_REQUEST) { *errlen = ngx_snprintf(err, *errlen, "attempt to abort with pending subrequests") - err; return NGX_ERROR; } if (r->header_sent && status >= NGX_HTTP_SPECIAL_RESPONSE && status != NGX_HTTP_REQUEST_TIME_OUT && status != NGX_HTTP_CLIENT_CLOSED_REQUEST && status != NGX_HTTP_CLOSE) { if (status != (ngx_int_t) r->headers_out.status) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "attempt to " "set status %i via ngx.exit after sending out the " "response status %ui", status, r->headers_out.status); } status = NGX_HTTP_OK; } ctx->exit_code = status; ctx->exited = 1; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua exit with code %i", ctx->exit_code); if (ctx->context & NGX_HTTP_LUA_CONTEXT_HEADER_FILTER) { return NGX_DONE; } return NGX_OK; } #endif /* NGX_HTTP_LUA_NO_FFI_API */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_config.c0000664000000000000000000000167612305451335017531 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_config.h" #include "api/ngx_http_lua_api.h" static int ngx_http_lua_config_prefix(lua_State *L); void ngx_http_lua_inject_config_api(lua_State *L) { /* ngx.config */ lua_createtable(L, 0, 4 /* nrec */); /* .config */ #if (NGX_DEBUG) lua_pushboolean(L, 1); #else lua_pushboolean(L, 0); #endif lua_setfield(L, -2, "debug"); lua_pushcfunction(L, ngx_http_lua_config_prefix); lua_setfield(L, -2, "prefix"); lua_pushinteger(L, nginx_version); lua_setfield(L, -2, "nginx_version"); lua_pushinteger(L, ngx_http_lua_version); lua_setfield(L, -2, "ngx_lua_version"); lua_setfield(L, -2, "config"); } static int ngx_http_lua_config_prefix(lua_State *L) { lua_pushlstring(L, (char *) ngx_cycle->prefix.data, ngx_cycle->prefix.len); return 1; } debian/modules/nginx-lua/src/ngx_http_lua_uthread.h0000664000000000000000000000170412305451334017714 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_UTHREAD_H_INCLUDED_ #define _NGX_HTTP_LUA_UTHREAD_H_INCLUDED_ #include "ngx_http_lua_common.h" #define ngx_http_lua_is_thread(ctx) \ ((ctx)->cur_co_ctx->is_uthread || (ctx)->cur_co_ctx == &(ctx)->entry_co_ctx) #define ngx_http_lua_is_entry_thread(ctx) \ ((ctx)->cur_co_ctx == &(ctx)->entry_co_ctx) #define ngx_http_lua_entry_thread_alive(ctx) \ ((ctx)->entry_co_ctx.co_ref != LUA_NOREF) #define ngx_http_lua_coroutine_alive(coctx) \ ((coctx)->co_status != NGX_HTTP_LUA_CO_DEAD \ && (coctx)->co_status != NGX_HTTP_LUA_CO_ZOMBIE) void ngx_http_lua_inject_uthread_api(ngx_log_t *log, lua_State *L); #endif /* _NGX_HTTP_LUA_UTHREAD_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_util.c0000664000000000000000000030275612305451335017244 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "nginx.h" #include "ngx_http_lua_directive.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_exception.h" #include "ngx_http_lua_pcrefix.h" #include "ngx_http_lua_regex.h" #include "ngx_http_lua_args.h" #include "ngx_http_lua_uri.h" #include "ngx_http_lua_req_body.h" #include "ngx_http_lua_headers.h" #include "ngx_http_lua_output.h" #include "ngx_http_lua_time.h" #include "ngx_http_lua_control.h" #include "ngx_http_lua_ndk.h" #include "ngx_http_lua_subrequest.h" #include "ngx_http_lua_log.h" #include "ngx_http_lua_variable.h" #include "ngx_http_lua_string.h" #include "ngx_http_lua_misc.h" #include "ngx_http_lua_consts.h" #include "ngx_http_lua_req_method.h" #include "ngx_http_lua_shdict.h" #include "ngx_http_lua_coroutine.h" #include "ngx_http_lua_socket_tcp.h" #include "ngx_http_lua_socket_udp.h" #include "ngx_http_lua_sleep.h" #include "ngx_http_lua_setby.h" #include "ngx_http_lua_headerfilterby.h" #include "ngx_http_lua_bodyfilterby.h" #include "ngx_http_lua_logby.h" #include "ngx_http_lua_phase.h" #include "ngx_http_lua_probe.h" #include "ngx_http_lua_uthread.h" #include "ngx_http_lua_contentby.h" #include "ngx_http_lua_timer.h" #include "ngx_http_lua_config.h" #include "ngx_http_lua_worker.h" #if 1 #undef ngx_http_lua_probe_info #define ngx_http_lua_probe_info(msg) #endif #ifndef NGX_HTTP_LUA_BT_DEPTH #define NGX_HTTP_LUA_BT_DEPTH 22 #endif #ifndef NGX_HTTP_LUA_BT_MAX_COROS #define NGX_HTTP_LUA_BT_MAX_COROS 5 #endif char ngx_http_lua_code_cache_key; char ngx_http_lua_regex_cache_key; char ngx_http_lua_socket_pool_key; char ngx_http_lua_coroutines_key; char ngx_http_lua_req_get_headers_metatable_key; ngx_uint_t ngx_http_lua_location_hash = 0; ngx_uint_t ngx_http_lua_content_length_hash = 0; static ngx_int_t ngx_http_lua_send_http10_headers(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx); static void ngx_http_lua_init_registry(lua_State *L, ngx_log_t *log); static void ngx_http_lua_init_globals(lua_State *L, ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log); static void ngx_http_lua_set_path(ngx_cycle_t *cycle, lua_State *L, int tab_idx, const char *fieldname, const char *path, const char *default_path, ngx_log_t *log); static ngx_int_t ngx_http_lua_handle_exec(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx); static ngx_int_t ngx_http_lua_handle_exit(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx); static ngx_int_t ngx_http_lua_handle_rewrite_jump(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx); static int ngx_http_lua_thread_traceback(lua_State *L, lua_State *co, ngx_http_lua_co_ctx_t *coctx); static void ngx_http_lua_inject_ngx_api(lua_State *L, ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log); static void ngx_http_lua_inject_arg_api(lua_State *L); static int ngx_http_lua_param_get(lua_State *L); static int ngx_http_lua_param_set(lua_State *L); static void ngx_http_lua_del_all_threads(ngx_http_request_t *r, lua_State *L, ngx_http_lua_ctx_t *ctx); static ngx_int_t ngx_http_lua_output_filter(ngx_http_request_t *r, ngx_chain_t *in); static ngx_int_t ngx_http_lua_send_special(ngx_http_request_t *r, ngx_uint_t flags); static void ngx_http_lua_finalize_coroutines(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx); static ngx_int_t ngx_http_lua_post_zombie_thread(ngx_http_request_t *r, ngx_http_lua_co_ctx_t *parent, ngx_http_lua_co_ctx_t *thread); static void ngx_http_lua_cleanup_zombie_child_uthreads(ngx_http_request_t *r, lua_State *L, ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t *coctx); static ngx_int_t ngx_http_lua_on_abort_resume(ngx_http_request_t *r); static void ngx_http_lua_close_fake_request(ngx_http_request_t *r); static void ngx_http_lua_free_fake_request(ngx_http_request_t *r); static ngx_int_t ngx_http_lua_flush_pending_output(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx); static ngx_int_t ngx_http_lua_process_flushing_coroutines(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx); static lua_State * ngx_http_lua_new_state(lua_State *parent_vm, ngx_cycle_t *cycle, ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log); static void ngx_http_lua_cleanup_conn_pools(lua_State *L); #ifndef LUA_PATH_SEP #define LUA_PATH_SEP ";" #endif #define AUX_MARK "\1" static void ngx_http_lua_set_path(ngx_cycle_t *cycle, lua_State *L, int tab_idx, const char *fieldname, const char *path, const char *default_path, ngx_log_t *log) { const char *tmp_path; const char *prefix; /* XXX here we use some hack to simplify string manipulation */ tmp_path = luaL_gsub(L, path, LUA_PATH_SEP LUA_PATH_SEP, LUA_PATH_SEP AUX_MARK LUA_PATH_SEP); lua_pushlstring(L, (char *) cycle->prefix.data, cycle->prefix.len); prefix = lua_tostring(L, -1); tmp_path = luaL_gsub(L, tmp_path, "$prefix", prefix); tmp_path = luaL_gsub(L, tmp_path, "${prefix}", prefix); lua_pop(L, 3); dd("tmp_path path: %s", tmp_path); #if (NGX_DEBUG) tmp_path = #else (void) #endif luaL_gsub(L, tmp_path, AUX_MARK, default_path); #if (NGX_DEBUG) ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0, "lua setting lua package.%s to \"%s\"", fieldname, tmp_path); #endif lua_remove(L, -2); /* fix negative index as there's new data on stack */ tab_idx = (tab_idx < 0) ? (tab_idx - 1) : tab_idx; lua_setfield(L, tab_idx, fieldname); } /** * Create new table and set _G field to itself. * * After: * | new table | <- top * | ... | * */ void ngx_http_lua_create_new_global_table(lua_State *L, int narr, int nrec) { lua_createtable(L, narr, nrec + 1); lua_pushvalue(L, -1); lua_setfield(L, -2, "_G"); } static lua_State * ngx_http_lua_new_state(lua_State *parent_vm, ngx_cycle_t *cycle, ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log) { lua_State *L; const char *old_path; const char *new_path; size_t old_path_len; const char *old_cpath; const char *new_cpath; size_t old_cpath_len; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "lua creating new vm state"); L = luaL_newstate(); if (L == NULL) { return NULL; } luaL_openlibs(L); lua_getglobal(L, "package"); if (!lua_istable(L, -1)) { ngx_log_error(NGX_LOG_EMERG, log, 0, "the \"package\" table does not exist"); return NULL; } if (parent_vm) { lua_getglobal(parent_vm, "package"); lua_getfield(parent_vm, -1, "path"); old_path = lua_tolstring(parent_vm, -1, &old_path_len); lua_pop(parent_vm, 1); lua_pushlstring(L, old_path, old_path_len); lua_setfield(L, -2, "path"); lua_getfield(parent_vm, -1, "cpath"); old_path = lua_tolstring(parent_vm, -1, &old_path_len); lua_pop(parent_vm, 2); lua_pushlstring(L, old_path, old_path_len); lua_setfield(L, -2, "cpath"); } else { #ifdef LUA_DEFAULT_PATH # define LUA_DEFAULT_PATH_LEN (sizeof(LUA_DEFAULT_PATH) - 1) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "lua prepending default package.path with %s", LUA_DEFAULT_PATH); lua_pushliteral(L, LUA_DEFAULT_PATH ";"); /* package default */ lua_getfield(L, -2, "path"); /* package default old */ old_path = lua_tolstring(L, -1, &old_path_len); lua_concat(L, 2); /* package new */ lua_setfield(L, -2, "path"); /* package */ #endif #ifdef LUA_DEFAULT_CPATH # define LUA_DEFAULT_CPATH_LEN (sizeof(LUA_DEFAULT_CPATH) - 1) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "lua prepending default package.cpath with %s", LUA_DEFAULT_CPATH); lua_pushliteral(L, LUA_DEFAULT_CPATH ";"); /* package default */ lua_getfield(L, -2, "cpath"); /* package default old */ old_cpath = lua_tolstring(L, -1, &old_cpath_len); lua_concat(L, 2); /* package new */ lua_setfield(L, -2, "cpath"); /* package */ #endif if (lmcf->lua_path.len != 0) { lua_getfield(L, -1, "path"); /* get original package.path */ old_path = lua_tolstring(L, -1, &old_path_len); dd("old path: %s", old_path); lua_pushlstring(L, (char *) lmcf->lua_path.data, lmcf->lua_path.len); new_path = lua_tostring(L, -1); ngx_http_lua_set_path(cycle, L, -3, "path", new_path, old_path, log); lua_pop(L, 2); } if (lmcf->lua_cpath.len != 0) { lua_getfield(L, -1, "cpath"); /* get original package.cpath */ old_cpath = lua_tolstring(L, -1, &old_cpath_len); dd("old cpath: %s", old_cpath); lua_pushlstring(L, (char *) lmcf->lua_cpath.data, lmcf->lua_cpath.len); new_cpath = lua_tostring(L, -1); ngx_http_lua_set_path(cycle, L, -3, "cpath", new_cpath, old_cpath, log); lua_pop(L, 2); } } lua_pop(L, 1); /* remove the "package" table */ ngx_http_lua_init_registry(L, log); ngx_http_lua_init_globals(L, lmcf, log); return L; } lua_State * ngx_http_lua_new_thread(ngx_http_request_t *r, lua_State *L, int *ref) { int base; lua_State *co; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua creating new thread"); base = lua_gettop(L); lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key); lua_rawget(L, LUA_REGISTRYINDEX); co = lua_newthread(L); /* {{{ inherit coroutine's globals to main thread's globals table * for print() function will try to find tostring() in current * globals table. */ /* new globals table for coroutine */ ngx_http_lua_create_new_global_table(co, 0, 0); lua_createtable(co, 0, 1); lua_pushvalue(co, LUA_GLOBALSINDEX); lua_setfield(co, -2, "__index"); lua_setmetatable(co, -2); lua_replace(co, LUA_GLOBALSINDEX); /* }}} */ *ref = luaL_ref(L, -2); if (*ref == LUA_NOREF) { lua_settop(L, base); /* restore main thread stack */ return NULL; } lua_settop(L, base); return co; } void ngx_http_lua_del_thread(ngx_http_request_t *r, lua_State *L, ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t *coctx) { if (coctx->co_ref == LUA_NOREF) { return; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua deleting light thread"); lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key); lua_rawget(L, LUA_REGISTRYINDEX); ngx_http_lua_probe_thread_delete(r, coctx->co, ctx); luaL_unref(L, -1, coctx->co_ref); coctx->co_ref = LUA_NOREF; coctx->co_status = NGX_HTTP_LUA_CO_DEAD; lua_pop(L, 1); } static void ngx_http_lua_del_all_threads(ngx_http_request_t *r, lua_State *L, ngx_http_lua_ctx_t *ctx) { int inited = 0; int ref; ngx_uint_t i; ngx_list_part_t *part; ngx_http_lua_co_ctx_t *entry_coctx; ngx_http_lua_co_ctx_t *cc; cc = ctx->on_abort_co_ctx; if (cc) { lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key); lua_rawget(L, LUA_REGISTRYINDEX); inited = 1; if (cc->co_ref != LUA_NOREF) { ngx_http_lua_probe_thread_delete(r, cc->co, ctx); luaL_unref(L, -1, cc->co_ref); cc->co_ref = LUA_NOREF; if (cc->co_status != NGX_HTTP_LUA_CO_SUSPENDED) { ctx->uthreads--; } cc->co_status = NGX_HTTP_LUA_CO_DEAD; } ctx->on_abort_co_ctx = NULL; } if (ctx->uthreads && ctx->user_co_ctx) { /* release all pending user threads */ if (!inited) { lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key); lua_rawget(L, LUA_REGISTRYINDEX); inited = 1; } part = &ctx->user_co_ctx->part; cc = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; cc = part->elts; i = 0; } ref = cc[i].co_ref; if (ref != LUA_NOREF) { ngx_http_lua_probe_thread_delete(r, cc[i].co, ctx); luaL_unref(L, -1, ref); cc[i].co_ref = LUA_NOREF; cc[i].co_status = NGX_HTTP_LUA_CO_DEAD; ctx->uthreads--; if (ctx->uthreads == 0) { break; } } } } /* release the reference to the entry thread */ entry_coctx = &ctx->entry_co_ctx; if (entry_coctx->co_ref != LUA_NOREF) { if (!inited) { lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key); lua_rawget(L, LUA_REGISTRYINDEX); inited = 1; } ngx_http_lua_probe_thread_delete(r, entry_coctx->co, ctx); luaL_unref(L, -1, entry_coctx->co_ref); entry_coctx->co_ref = LUA_NOREF; entry_coctx->co_status = NGX_HTTP_LUA_CO_DEAD; } if (inited) { lua_pop(L, 1); } } u_char * ngx_http_lua_rebase_path(ngx_pool_t *pool, u_char *src, size_t len) { u_char *p, *dst; if (len == 0) { return NULL; } if (src[0] == '/') { /* being an absolute path already */ dst = ngx_palloc(pool, len + 1); if (dst == NULL) { return NULL; } p = ngx_copy(dst, src, len); *p = '\0'; return dst; } dst = ngx_palloc(pool, ngx_cycle->prefix.len + len + 1); if (dst == NULL) { return NULL; } p = ngx_copy(dst, ngx_cycle->prefix.data, ngx_cycle->prefix.len); p = ngx_copy(p, src, len); *p = '\0'; return dst; } ngx_int_t ngx_http_lua_send_header_if_needed(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) { ngx_int_t rc; if (!r->header_sent) { if (r->headers_out.status == 0) { r->headers_out.status = NGX_HTTP_OK; } if (!ctx->headers_set && ngx_http_lua_set_content_type(r) != NGX_OK) { return NGX_ERROR; } if (!ctx->headers_set) { ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); } if (!ctx->buffering) { dd("sending headers"); rc = ngx_http_send_header(r); return rc; } } return NGX_OK; } ngx_int_t ngx_http_lua_send_chain_link(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, ngx_chain_t *in) { ngx_int_t rc; ngx_chain_t *cl; ngx_chain_t **ll; ngx_http_lua_loc_conf_t *llcf; #if 1 if (ctx->acquired_raw_req_socket || ctx->eof) { dd("ctx->eof already set or raw req socket already acquired"); return NGX_OK; } #endif if ((r->method & NGX_HTTP_HEAD) && !r->header_only) { r->header_only = 1; } llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->http10_buffering && !ctx->buffering && !r->header_sent && r->http_version < NGX_HTTP_VERSION_11 && r->headers_out.content_length_n < 0) { ctx->buffering = 1; } rc = ngx_http_lua_send_header_if_needed(r, ctx); if (rc == NGX_ERROR || rc > NGX_OK) { return rc; } if (r->header_only) { ctx->eof = 1; if (ctx->buffering) { return ngx_http_lua_send_http10_headers(r, ctx); } return rc; } if (in == NULL) { if (ctx->buffering) { rc = ngx_http_lua_send_http10_headers(r, ctx); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } if (ctx->out) { rc = ngx_http_lua_output_filter(r, ctx->out); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } ctx->out = NULL; } } #if defined(nginx_version) && nginx_version <= 8004 /* earlier versions of nginx does not allow subrequests to send last_buf themselves */ if (r != r->main) { return NGX_OK; } #endif ctx->eof = 1; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua sending last buf of the response body"); rc = ngx_http_lua_send_special(r, NGX_HTTP_LAST); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } return NGX_OK; } /* in != NULL */ if (ctx->buffering) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua buffering output bufs for the HTTP 1.0 request"); for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) { ll = &cl->next; } *ll = in; return NGX_OK; } return ngx_http_lua_output_filter(r, in); } static ngx_int_t ngx_http_lua_send_special(ngx_http_request_t *r, ngx_uint_t flags) { ngx_int_t rc; ngx_http_request_t *ar; /* active request */ ar = r->connection->data; if (ar != r) { /* bypass ngx_http_postpone_filter_module */ r->connection->data = r; rc = ngx_http_send_special(r, flags); r->connection->data = ar; return rc; } return ngx_http_send_special(r, flags); } static ngx_int_t ngx_http_lua_output_filter(ngx_http_request_t *r, ngx_chain_t *in) { ngx_int_t rc; ngx_http_request_t *ar; /* active request */ ar = r->connection->data; if (ar != r) { /* bypass ngx_http_postpone_filter_module */ r->connection->data = r; rc = ngx_http_output_filter(r, in); r->connection->data = ar; return rc; } return ngx_http_output_filter(r, in); } static ngx_int_t ngx_http_lua_send_http10_headers(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) { off_t size; ngx_chain_t *cl; ngx_int_t rc; if (r->header_sent) { return NGX_OK; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua sending HTTP 1.0 response headers"); if (r->header_only) { goto send; } if (r->headers_out.content_length == NULL) { for (size = 0, cl = ctx->out; cl; cl = cl->next) { size += ngx_buf_size(cl->buf); } r->headers_out.content_length_n = size; if (r->headers_out.content_length) { r->headers_out.content_length->hash = 0; } } send: rc = ngx_http_send_header(r); return rc; } static void ngx_http_lua_init_registry(lua_State *L, ngx_log_t *log) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "lua initializing lua registry"); /* {{{ register a table to anchor lua coroutines reliably: * {([int]ref) = [cort]} */ lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key); lua_createtable(L, 0, 32 /* nrec */); lua_rawset(L, LUA_REGISTRYINDEX); /* }}} */ /* create the registry entry for the Lua request ctx data table */ lua_pushliteral(L, ngx_http_lua_ctx_tables_key); lua_createtable(L, 0, 32 /* nrec */); lua_rawset(L, LUA_REGISTRYINDEX); /* create the registry entry for the Lua socket connection pool table */ lua_pushlightuserdata(L, &ngx_http_lua_socket_pool_key); lua_createtable(L, 0, 8 /* nrec */); lua_rawset(L, LUA_REGISTRYINDEX); #if (NGX_PCRE) /* create the registry entry for the Lua precompiled regex object cache */ lua_pushlightuserdata(L, &ngx_http_lua_regex_cache_key); lua_createtable(L, 0, 16 /* nrec */); lua_rawset(L, LUA_REGISTRYINDEX); #endif /* {{{ register table to cache user code: * { [(string)cache_key] = } */ lua_pushlightuserdata(L, &ngx_http_lua_code_cache_key); lua_createtable(L, 0, 8 /* nrec */); lua_rawset(L, LUA_REGISTRYINDEX); /* }}} */ } static void ngx_http_lua_init_globals(lua_State *L, ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "lua initializing lua globals"); #if defined(NDK) && NDK ngx_http_lua_inject_ndk_api(L); #endif /* defined(NDK) && NDK */ ngx_http_lua_inject_ngx_api(L, lmcf, log); } static void ngx_http_lua_inject_ngx_api(lua_State *L, ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log) { lua_createtable(L, 0 /* narr */, 97 /* nrec */); /* ngx.* */ ngx_http_lua_inject_arg_api(L); ngx_http_lua_inject_http_consts(L); ngx_http_lua_inject_core_consts(L); ngx_http_lua_inject_log_api(L); ngx_http_lua_inject_output_api(L); ngx_http_lua_inject_time_api(L); ngx_http_lua_inject_string_api(L); ngx_http_lua_inject_control_api(log, L); ngx_http_lua_inject_subrequest_api(L); ngx_http_lua_inject_sleep_api(L); ngx_http_lua_inject_phase_api(L); #if (NGX_PCRE) ngx_http_lua_inject_regex_api(L); #endif ngx_http_lua_inject_req_api(log, L); ngx_http_lua_inject_resp_header_api(L); ngx_http_lua_inject_variable_api(L); ngx_http_lua_inject_shdict_api(lmcf, L); ngx_http_lua_inject_socket_tcp_api(log, L); ngx_http_lua_inject_socket_udp_api(log, L); ngx_http_lua_inject_uthread_api(log, L); ngx_http_lua_inject_timer_api(L); ngx_http_lua_inject_config_api(L); ngx_http_lua_inject_worker_api(L); ngx_http_lua_inject_misc_api(L); lua_getglobal(L, "package"); /* ngx package */ lua_getfield(L, -1, "loaded"); /* ngx package loaded */ lua_pushvalue(L, -3); /* ngx package loaded ngx */ lua_setfield(L, -2, "ngx"); /* ngx package loaded */ lua_pop(L, 2); lua_setglobal(L, "ngx"); ngx_http_lua_inject_coroutine_api(log, L); } void ngx_http_lua_discard_bufs(ngx_pool_t *pool, ngx_chain_t *in) { ngx_chain_t *cl; for (cl = in; cl; cl = cl->next) { cl->buf->pos = cl->buf->last; cl->buf->file_pos = cl->buf->file_last; } } ngx_int_t ngx_http_lua_add_copy_chain(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, ngx_chain_t ***plast, ngx_chain_t *in, ngx_int_t *eof) { ngx_chain_t *cl; size_t len; ngx_buf_t *b; len = 0; *eof = 0; for (cl = in; cl; cl = cl->next) { if (ngx_buf_in_memory(cl->buf)) { len += cl->buf->last - cl->buf->pos; } if (cl->buf->last_in_chain || cl->buf->last_buf) { *eof = 1; } } if (len == 0) { return NGX_OK; } cl = ngx_http_lua_chains_get_free_buf(r->connection->log, r->pool, &ctx->free_bufs, len, (ngx_buf_tag_t) &ngx_http_lua_module); if (cl == NULL) { return NGX_ERROR; } dd("chains get free buf: %d == %d", (int) (cl->buf->end - cl->buf->start), (int) len); b = cl->buf; while (in) { if (ngx_buf_in_memory(in->buf)) { b->last = ngx_copy(b->last, in->buf->pos, in->buf->last - in->buf->pos); } in = in->next; } **plast = cl; *plast = &cl->next; return NGX_OK; } void ngx_http_lua_reset_ctx(ngx_http_request_t *r, lua_State *L, ngx_http_lua_ctx_t *ctx) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua reset ctx"); ngx_http_lua_del_all_threads(r, L, ctx); if (ctx->user_co_ctx) { /* no way to destroy a list but clean up the whole pool */ ctx->user_co_ctx = NULL; } ngx_memzero(&ctx->entry_co_ctx, sizeof(ngx_http_lua_co_ctx_t)); ctx->entry_co_ctx.co_ref = LUA_NOREF; ctx->entered_rewrite_phase = 0; ctx->entered_access_phase = 0; ctx->entered_content_phase = 0; ctx->exit_code = 0; ctx->exited = 0; ctx->resume_handler = ngx_http_lua_wev_handler; ngx_str_null(&ctx->exec_uri); ngx_str_null(&ctx->exec_args); ctx->co_op = 0; } /* post read callback for rewrite and access phases */ void ngx_http_lua_generic_phase_post_read(ngx_http_request_t *r) { ngx_http_lua_ctx_t *ctx; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua post read for rewrite/access phases"); ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); ctx->read_body_done = 1; #if defined(nginx_version) && nginx_version >= 8011 r->main->count--; #endif if (ctx->waiting_more_body) { ctx->waiting_more_body = 0; ngx_http_core_run_phases(r); } } void ngx_http_lua_request_cleanup_handler(void *data) { ngx_http_lua_ctx_t *ctx = data; ngx_http_lua_request_cleanup(ctx, 0 /* forcible */); } void ngx_http_lua_request_cleanup(ngx_http_lua_ctx_t *ctx, int forcible) { lua_State *L; ngx_http_request_t *r; ngx_http_lua_main_conf_t *lmcf; ngx_http_lua_loc_conf_t *llcf; ngx_http_lua_ctx_t *cur_ctx; r = ctx->request; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua request cleanup: forcible=%d", forcible); /* force coroutine handling the request quit */ if (ctx == NULL) { dd("ctx is NULL"); return; } if (ctx->cleanup) { *ctx->cleanup = NULL; ctx->cleanup = NULL; } lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); #if 1 if (r->connection->fd == -1) { /* being a fake request */ lmcf->running_timers--; } #endif L = ngx_http_lua_get_lua_vm(r, ctx); /* we cannot release the ngx.ctx table if we have log_by_lua* hooks * because request cleanup runs before log phase handlers */ if (ctx->ctx_ref != LUA_NOREF) { if (forcible || r->connection->fd == -1 /* being a fake request */) { ngx_http_lua_release_ngx_ctx_table(r->connection->log, L, ctx); } else { cur_ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (cur_ctx != ctx) { /* internal redirects happened */ ngx_http_lua_release_ngx_ctx_table(r->connection->log, L, ctx); } else { llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->log_handler == NULL) { /* no log_by_lua* configured */ ngx_http_lua_release_ngx_ctx_table(r->connection->log, L, ctx); } } } } ngx_http_lua_finalize_coroutines(r, ctx); ngx_http_lua_del_all_threads(r, L, ctx); } void ngx_http_lua_release_ngx_ctx_table(ngx_log_t *log, lua_State *L, ngx_http_lua_ctx_t *ctx) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "lua release ngx.ctx at ref %d", ctx->ctx_ref); lua_pushliteral(L, ngx_http_lua_ctx_tables_key); lua_rawget(L, LUA_REGISTRYINDEX); luaL_unref(L, -1, ctx->ctx_ref); ctx->ctx_ref = LUA_NOREF; lua_pop(L, 1); } /* * description: * run a Lua coroutine specified by ctx->cur_co_ctx->co * return value: * NGX_AGAIN: I/O interruption: r->main->count intact * NGX_DONE: I/O interruption: r->main->count already incremented by 1 * NGX_ERROR: error * >= 200 HTTP status code */ ngx_int_t ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, volatile int nrets) { ngx_http_lua_co_ctx_t *next_coctx, *parent_coctx, *orig_coctx; int rv, success = 1; lua_State *next_co; lua_State *old_co; const char *err, *msg, *trace; ngx_int_t rc; #if (NGX_PCRE) ngx_pool_t *old_pool = NULL; #endif ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua run thread, top:%d c:%ud", lua_gettop(L), r->main->count); /* set Lua VM panic handler */ lua_atpanic(L, ngx_http_lua_atpanic); dd("ctx = %p", ctx); NGX_LUA_EXCEPTION_TRY { if (ctx->cur_co_ctx->thread_spawn_yielded) { ngx_http_lua_probe_info("thread spawn yielded"); ctx->cur_co_ctx->thread_spawn_yielded = 0; nrets = 1; } for ( ;; ) { dd("calling lua_resume: vm %p, nret %d", ctx->cur_co_ctx->co, (int) nrets); #if (NGX_PCRE) /* XXX: work-around to nginx regex subsystem */ old_pool = ngx_http_lua_pcre_malloc_init(r->pool); #endif /* run code */ dd("ctx: %p", ctx); dd("cur co: %p", ctx->cur_co_ctx->co); dd("cur co status: %d", ctx->cur_co_ctx->co_status); orig_coctx = ctx->cur_co_ctx; rv = lua_resume(orig_coctx->co, nrets); #if (NGX_PCRE) /* XXX: work-around to nginx regex subsystem */ ngx_http_lua_pcre_malloc_done(old_pool); #endif #if 0 /* test the longjmp thing */ if (rand() % 2 == 0) { NGX_LUA_EXCEPTION_THROW(1); } #endif ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua resume returned %d", rv); switch (rv) { case LUA_YIELD: /* yielded, let event handler do the rest job */ /* FIXME: add io cmd dispatcher here */ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua thread yielded"); if (r->uri_changed) { return ngx_http_lua_handle_rewrite_jump(L, r, ctx); } if (ctx->exited) { return ngx_http_lua_handle_exit(L, r, ctx); } if (ctx->exec_uri.len) { return ngx_http_lua_handle_exec(L, r, ctx); } /* * check if coroutine.resume or coroutine.yield called * lua_yield() */ switch(ctx->co_op) { case NGX_HTTP_LUA_USER_CORO_NOP: dd("hit! it is the API yield"); lua_settop(ctx->cur_co_ctx->co, 0); ctx->cur_co_ctx = NULL; return NGX_AGAIN; case NGX_HTTP_LUA_USER_THREAD_RESUME: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua user thread resume"); ctx->co_op = NGX_HTTP_LUA_USER_CORO_NOP; nrets = lua_gettop(ctx->cur_co_ctx->co) - 1; dd("nrets = %d", nrets); break; case NGX_HTTP_LUA_USER_CORO_RESUME: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua coroutine: resume"); /* * the target coroutine lies at the base of the * parent's stack */ ctx->co_op = NGX_HTTP_LUA_USER_CORO_NOP; old_co = ctx->cur_co_ctx->parent_co_ctx->co; nrets = lua_gettop(old_co); if (nrets) { lua_xmove(old_co, ctx->cur_co_ctx->co, nrets); } break; default: /* ctx->co_op == NGX_HTTP_LUA_USER_CORO_YIELD */ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua coroutine: yield"); ctx->co_op = NGX_HTTP_LUA_USER_CORO_NOP; if (ngx_http_lua_is_thread(ctx)) { ngx_http_lua_probe_thread_yield(r, ctx->cur_co_ctx->co); lua_settop(ctx->cur_co_ctx->co, 0); ngx_http_lua_probe_info("set co running"); ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_RUNNING; if (ctx->posted_threads) { ngx_http_lua_post_thread(r, ctx, ctx->cur_co_ctx); ctx->cur_co_ctx = NULL; return NGX_AGAIN; } /* no pending threads, so resume the thread * immediately */ nrets = 0; continue; } /* being a user coroutine that has a parent */ nrets = lua_gettop(ctx->cur_co_ctx->co); next_coctx = ctx->cur_co_ctx->parent_co_ctx; next_co = next_coctx->co; /* * prepare return values for coroutine.resume * (true plus any retvals) */ lua_pushboolean(next_co, 1); if (nrets) { lua_xmove(ctx->cur_co_ctx->co, next_co, nrets); } nrets++; ctx->cur_co_ctx = next_coctx; break; } /* try resuming on the new coroutine again */ continue; case 0: ngx_http_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 1); ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD; if (ctx->cur_co_ctx->zombie_child_threads) { ngx_http_lua_cleanup_zombie_child_uthreads(r, L, ctx, ctx->cur_co_ctx); } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua light thread ended normally"); if (ngx_http_lua_is_entry_thread(ctx)) { lua_settop(L, 0); ngx_http_lua_del_thread(r, L, ctx, ctx->cur_co_ctx); dd("uthreads: %d", (int) ctx->uthreads); if (ctx->uthreads) { ctx->cur_co_ctx = NULL; return NGX_AGAIN; } /* all user threads terminated already */ goto done; } if (ctx->cur_co_ctx->is_uthread) { /* being a user thread */ lua_settop(L, 0); parent_coctx = ctx->cur_co_ctx->parent_co_ctx; if (ngx_http_lua_coroutine_alive(parent_coctx)) { if (ctx->cur_co_ctx->waited_by_parent) { ngx_http_lua_probe_info("parent already waiting"); ctx->cur_co_ctx->waited_by_parent = 0; success = 1; goto user_co_done; } ngx_http_lua_probe_info("parent still alive"); if (ngx_http_lua_post_zombie_thread(r, parent_coctx, ctx->cur_co_ctx) != NGX_OK) { return NGX_ERROR; } lua_pushboolean(ctx->cur_co_ctx->co, 1); lua_insert(ctx->cur_co_ctx->co, 1); ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_ZOMBIE; ctx->cur_co_ctx = NULL; return NGX_AGAIN; } ngx_http_lua_del_thread(r, L, ctx, ctx->cur_co_ctx); ctx->uthreads--; if (ctx->uthreads == 0) { if (ngx_http_lua_entry_thread_alive(ctx)) { ctx->cur_co_ctx = NULL; return NGX_AGAIN; } /* all threads terminated already */ goto done; } /* some other user threads still running */ ctx->cur_co_ctx = NULL; return NGX_AGAIN; } /* being a user coroutine that has a parent */ success = 1; user_co_done: nrets = lua_gettop(ctx->cur_co_ctx->co); next_coctx = ctx->cur_co_ctx->parent_co_ctx; if (next_coctx == NULL) { /* being a light thread */ goto no_parent; } next_co = next_coctx->co; /* * ended successful, coroutine.resume returns true plus * any return values */ lua_pushboolean(next_co, success); if (nrets) { lua_xmove(ctx->cur_co_ctx->co, next_co, nrets); } if (ctx->cur_co_ctx->is_uthread) { ngx_http_lua_del_thread(r, L, ctx, ctx->cur_co_ctx); ctx->uthreads--; } nrets++; ctx->cur_co_ctx = next_coctx; ngx_http_lua_probe_info("set parent running"); next_coctx->co_status = NGX_HTTP_LUA_CO_RUNNING; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua coroutine: lua user thread ended normally"); continue; case LUA_ERRRUN: err = "runtime error"; break; case LUA_ERRSYNTAX: err = "syntax error"; break; case LUA_ERRMEM: err = "memory allocation error"; ngx_quit = 1; break; case LUA_ERRERR: err = "error handler error"; break; default: err = "unknown error"; break; } if (ctx->cur_co_ctx != orig_coctx) { ctx->cur_co_ctx = orig_coctx; } if (lua_isstring(ctx->cur_co_ctx->co, -1)) { dd("user custom error msg"); msg = lua_tostring(ctx->cur_co_ctx->co, -1); } else { msg = "unknown reason"; } ngx_http_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 0); ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD; ngx_http_lua_thread_traceback(L, ctx->cur_co_ctx->co, ctx->cur_co_ctx); trace = lua_tostring(L, -1); if (ctx->cur_co_ctx->is_uthread) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "lua user thread aborted: %s: %s\n%s", err, msg, trace); lua_settop(L, 0); parent_coctx = ctx->cur_co_ctx->parent_co_ctx; if (ngx_http_lua_coroutine_alive(parent_coctx)) { if (ctx->cur_co_ctx->waited_by_parent) { ctx->cur_co_ctx->waited_by_parent = 0; success = 0; goto user_co_done; } if (ngx_http_lua_post_zombie_thread(r, parent_coctx, ctx->cur_co_ctx) != NGX_OK) { return NGX_ERROR; } lua_pushboolean(ctx->cur_co_ctx->co, 0); lua_insert(ctx->cur_co_ctx->co, 1); ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_ZOMBIE; ctx->cur_co_ctx = NULL; return NGX_AGAIN; } ngx_http_lua_del_thread(r, L, ctx, ctx->cur_co_ctx); ctx->uthreads--; if (ctx->uthreads == 0) { if (ngx_http_lua_entry_thread_alive(ctx)) { ctx->cur_co_ctx = NULL; return NGX_AGAIN; } /* all threads terminated already */ goto done; } /* some other user threads still running */ ctx->cur_co_ctx = NULL; return NGX_AGAIN; } if (ngx_http_lua_is_entry_thread(ctx)) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "lua entry thread aborted: %s: %s\n%s", err, msg, trace); lua_settop(L, 0); /* being the entry thread aborted */ if (r->filter_finalize) { ngx_http_set_ctx(r, ctx, ngx_http_lua_module); } ngx_http_lua_request_cleanup(ctx, 0); dd("headers sent? %d", r->header_sent ? 1 : 0); if (ctx->no_abort) { ctx->no_abort = 0; return NGX_ERROR; } return r->header_sent ? NGX_ERROR : NGX_HTTP_INTERNAL_SERVER_ERROR; } /* being a user coroutine that has a parent */ next_coctx = ctx->cur_co_ctx->parent_co_ctx; if (next_coctx == NULL) { goto no_parent; } next_co = next_coctx->co; ngx_http_lua_probe_info("set parent running"); next_coctx->co_status = NGX_HTTP_LUA_CO_RUNNING; /* * ended with error, coroutine.resume returns false plus * err msg */ lua_pushboolean(next_co, 0); lua_xmove(ctx->cur_co_ctx->co, next_co, 1); nrets = 2; ctx->cur_co_ctx = next_coctx; ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "lua coroutine: %s: %s\n%s", err, msg, trace); /* try resuming on the new coroutine again */ continue; } } NGX_LUA_EXCEPTION_CATCH { dd("nginx execution restored"); } return NGX_ERROR; no_parent: lua_settop(L, 0); ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD; if (r->filter_finalize) { ngx_http_set_ctx(r, ctx, ngx_http_lua_module); } ngx_http_lua_request_cleanup(ctx, 0); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "lua handler aborted: " "user coroutine has no parent"); return r->header_sent ? NGX_ERROR : NGX_HTTP_INTERNAL_SERVER_ERROR; done: if (ctx->entered_content_phase && r->connection->fd != -1) { rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* last_buf */); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } } return NGX_OK; } ngx_int_t ngx_http_lua_wev_handler(ngx_http_request_t *r) { ngx_int_t rc; ngx_event_t *wev; ngx_connection_t *c; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; ngx_http_core_loc_conf_t *clcf; ngx_http_lua_socket_tcp_upstream_t *u; c = r->connection; wev = c->write; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return NGX_ERROR; } ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "lua run write event handler: timedout:%ud, ready:%ud, " "writing_raw_req_socket:%ud", wev->timedout, wev->ready, ctx->writing_raw_req_socket); clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module); if (wev->timedout && !ctx->writing_raw_req_socket) { if (!wev->delayed) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); c->timedout = 1; if (ctx->entered_content_phase) { ngx_http_lua_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT); } return NGX_HTTP_REQUEST_TIME_OUT; } wev->timedout = 0; wev->delayed = 0; if (!wev->ready) { ngx_add_timer(wev, clcf->send_timeout); if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { if (ctx->entered_content_phase) { ngx_http_lua_finalize_request(r, NGX_ERROR); } return NGX_ERROR; } } } if (!wev->ready && !wev->timedout) { goto useless; } if (ctx->writing_raw_req_socket) { ctx->writing_raw_req_socket = 0; coctx = ctx->downstream_co_ctx; if (coctx == NULL) { return NGX_ERROR; } u = coctx->data; if (u == NULL) { return NGX_ERROR; } u->write_event_handler(r, u); return NGX_DONE; } if (c->buffered) { rc = ngx_http_lua_flush_pending_output(r, ctx); if (rc != NGX_OK) { return rc; } } if (ctx->flushing_coros) { return ngx_http_lua_process_flushing_coroutines(r, ctx); } /* ctx->flushing_coros == 0 */ useless: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "useless lua write event handler"); if (ctx->entered_content_phase) { return NGX_OK; } return NGX_DONE; } static ngx_int_t ngx_http_lua_process_flushing_coroutines(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) { ngx_int_t rc, n; ngx_uint_t i; ngx_list_part_t *part; ngx_http_lua_co_ctx_t *coctx; dd("processing flushing coroutines"); coctx = &ctx->entry_co_ctx; n = ctx->flushing_coros; if (coctx->flushing) { coctx->flushing = 0; ctx->flushing_coros--; n--; ctx->cur_co_ctx = coctx; rc = ngx_http_lua_flush_resume_helper(r, ctx); if (rc == NGX_ERROR || rc >= NGX_OK) { return rc; } /* rc == NGX_DONE */ } if (n) { if (ctx->user_co_ctx == NULL) { return NGX_ERROR; } part = &ctx->user_co_ctx->part; coctx = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; coctx = part->elts; i = 0; } if (coctx[i].flushing) { coctx[i].flushing = 0; ctx->cur_co_ctx = &coctx[i]; rc = ngx_http_lua_flush_resume_helper(r, ctx); if (rc == NGX_ERROR || rc >= NGX_OK) { return rc; } /* rc == NGX_DONE */ ctx->flushing_coros--; n--; if (n == 0) { return NGX_DONE; } } } } if (n) { return NGX_ERROR; } return NGX_DONE; } static ngx_int_t ngx_http_lua_flush_pending_output(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) { ngx_int_t rc; ngx_chain_t *cl; ngx_event_t *wev; ngx_connection_t *c; ngx_http_core_loc_conf_t *clcf; c = r->connection; wev = c->write; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "lua flushing output: buffered 0x%uxd", c->buffered); rc = ngx_http_lua_output_filter(r, NULL); if (rc == NGX_ERROR || rc > NGX_OK) { if (ctx->entered_content_phase) { ngx_http_lua_finalize_request(r, rc); } return rc; } if (ctx->busy_bufs) { cl = NULL; dd("updating chains..."); #if nginx_version >= 1001004 ngx_chain_update_chains(r->pool, #else ngx_chain_update_chains( #endif &ctx->free_bufs, &ctx->busy_bufs, &cl, (ngx_buf_tag_t) &ngx_http_lua_module); dd("update lua buf tag: %p, buffered: %x, busy bufs: %p", &ngx_http_lua_module, (int) c->buffered, ctx->busy_bufs); } if (c->buffered) { clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module); if (!wev->delayed) { ngx_add_timer(wev, clcf->send_timeout); } if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { if (ctx->entered_content_phase) { ngx_http_lua_finalize_request(r, NGX_ERROR); } return NGX_ERROR; } if (ctx->flushing_coros) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "lua flush still waiting: buffered 0x%uxd", c->buffered); return NGX_DONE; } } else { #if 1 if (wev->timer_set) { ngx_del_timer(wev); } #endif } return NGX_OK; } u_char * ngx_http_lua_digest_hex(u_char *dest, const u_char *buf, int buf_len) { ngx_md5_t md5; u_char md5_buf[MD5_DIGEST_LENGTH]; ngx_md5_init(&md5); ngx_md5_update(&md5, buf, buf_len); ngx_md5_final(md5_buf, &md5); return ngx_hex_dump(dest, md5_buf, sizeof(md5_buf)); } void ngx_http_lua_set_multi_value_table(lua_State *L, int index) { if (index < 0) { index = lua_gettop(L) + index + 1; } lua_pushvalue(L, -2); /* stack: table key value key */ lua_rawget(L, index); if (lua_isnil(L, -1)) { lua_pop(L, 1); /* stack: table key value */ lua_rawset(L, index); /* stack: table */ } else { if (!lua_istable(L, -1)) { /* just inserted one value */ lua_createtable(L, 4, 0); /* stack: table key value value table */ lua_insert(L, -2); /* stack: table key value table value */ lua_rawseti(L, -2, 1); /* stack: table key value table */ lua_insert(L, -2); /* stack: table key table value */ lua_rawseti(L, -2, 2); /* stack: table key table */ lua_rawset(L, index); /* stack: table */ } else { /* stack: table key value table */ lua_insert(L, -2); /* stack: table key table value */ lua_rawseti(L, -2, lua_objlen(L, -2) + 1); /* stack: table key table */ lua_pop(L, 2); /* stack: table */ } } } uintptr_t ngx_http_lua_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type) { ngx_uint_t n; uint32_t *escape; static u_char hex[] = "0123456789abcdef"; /* " ", "#", "%", "?", %00-%1F, %7F-%FF */ static uint32_t uri[] = { 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ 0xfc00886d, /* 1111 1100 0000 0000 1000 1000 0110 1101 */ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ 0x78000000, /* 0111 1000 0000 0000 0000 0000 0000 0000 */ /* ~}| {zyx wvut srqp onml kjih gfed cba` */ 0xa8000000, /* 1010 1000 0000 0000 0000 0000 0000 0000 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ }; /* " ", "#", "%", "+", "?", %00-%1F, %7F-%FF */ static uint32_t args[] = { 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ 0x80000829, /* 1000 0000 0000 0000 0000 1000 0010 1001 */ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ /* ~}| {zyx wvut srqp onml kjih gfed cba` */ 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ }; /* " ", "#", """, "%", "'", %00-%1F, %7F-%FF */ static uint32_t html[] = { 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ 0x000000ad, /* 0000 0000 0000 0000 0000 0000 1010 1101 */ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ /* ~}| {zyx wvut srqp onml kjih gfed cba` */ 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ }; /* " ", """, "%", "'", %00-%1F, %7F-%FF */ static uint32_t refresh[] = { 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ 0x00000085, /* 0000 0000 0000 0000 0000 0000 1000 0101 */ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ /* ~}| {zyx wvut srqp onml kjih gfed cba` */ 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ }; /* " ", "%", %00-%1F */ static uint32_t memcached[] = { 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ 0x00000021, /* 0000 0000 0000 0000 0000 0000 0010 0001 */ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ /* ~}| {zyx wvut srqp onml kjih gfed cba` */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ }; /* mail_auth is the same as memcached */ static uint32_t *map[] = { uri, args, html, refresh, memcached, memcached }; escape = map[type]; if (dst == NULL) { /* find the number of the characters to be escaped */ n = 0; while (size) { if (escape[*src >> 5] & (1 << (*src & 0x1f))) { n++; } src++; size--; } return (uintptr_t) n; } while (size) { if (escape[*src >> 5] & (1 << (*src & 0x1f))) { *dst++ = '%'; *dst++ = hex[*src >> 4]; *dst++ = hex[*src & 0xf]; src++; } else { *dst++ = *src++; } size--; } return (uintptr_t) dst; } /* XXX we also decode '+' to ' ' */ void ngx_http_lua_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type) { u_char *d, *s, ch, c, decoded; enum { sw_usual = 0, sw_quoted, sw_quoted_second } state; d = *dst; s = *src; state = 0; decoded = 0; while (size--) { ch = *s++; switch (state) { case sw_usual: if (ch == '?' && (type & (NGX_UNESCAPE_URI|NGX_UNESCAPE_REDIRECT))) { *d++ = ch; goto done; } if (ch == '%') { state = sw_quoted; break; } if (ch == '+') { *d++ = ' '; break; } *d++ = ch; break; case sw_quoted: if (ch >= '0' && ch <= '9') { decoded = (u_char) (ch - '0'); state = sw_quoted_second; break; } c = (u_char) (ch | 0x20); if (c >= 'a' && c <= 'f') { decoded = (u_char) (c - 'a' + 10); state = sw_quoted_second; break; } /* the invalid quoted character */ state = sw_usual; *d++ = ch; break; case sw_quoted_second: state = sw_usual; if (ch >= '0' && ch <= '9') { ch = (u_char) ((decoded << 4) + ch - '0'); if (type & NGX_UNESCAPE_REDIRECT) { if (ch > '%' && ch < 0x7f) { *d++ = ch; break; } *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1); break; } *d++ = ch; break; } c = (u_char) (ch | 0x20); if (c >= 'a' && c <= 'f') { ch = (u_char) ((decoded << 4) + c - 'a' + 10); if (type & NGX_UNESCAPE_URI) { if (ch == '?') { *d++ = ch; goto done; } *d++ = ch; break; } if (type & NGX_UNESCAPE_REDIRECT) { if (ch == '?') { *d++ = ch; goto done; } if (ch > '%' && ch < 0x7f) { *d++ = ch; break; } *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1); break; } *d++ = ch; break; } /* the invalid quoted character */ break; } } done: *dst = d; *src = s; } void ngx_http_lua_inject_req_api(ngx_log_t *log, lua_State *L) { /* ngx.req table */ lua_createtable(L, 0 /* narr */, 23 /* nrec */); /* .req */ ngx_http_lua_inject_req_header_api(log, L); ngx_http_lua_inject_req_uri_api(log, L); ngx_http_lua_inject_req_args_api(L); ngx_http_lua_inject_req_body_api(L); ngx_http_lua_inject_req_socket_api(L); ngx_http_lua_inject_req_method_api(L); ngx_http_lua_inject_req_time_api(L); lua_setfield(L, -2, "req"); } static ngx_int_t ngx_http_lua_handle_exec(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) { ngx_int_t rc; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua thread initiated internal redirect to %V", &ctx->exec_uri); ngx_http_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 1); ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD; if (r->filter_finalize) { ngx_http_set_ctx(r, ctx, ngx_http_lua_module); } ngx_http_lua_request_cleanup(ctx, 1 /* forcible */); if (ctx->exec_uri.data[0] == '@') { if (ctx->exec_args.len > 0) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "query strings %V ignored when exec'ing " "named location %V", &ctx->exec_args, &ctx->exec_uri); } r->write_event_handler = ngx_http_request_empty_handler; #if 1 if (r->read_event_handler == ngx_http_lua_rd_check_broken_connection) { /* resume the read event handler */ r->read_event_handler = ngx_http_block_reading; } #endif #if 1 /* clear the modules contexts */ ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module); #endif rc = ngx_http_named_location(r, &ctx->exec_uri); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } #if 0 if (!ctx->entered_content_phase) { /* XXX ensure the main request ref count * is decreased because the current * request will be quit */ r->main->count--; dd("XXX decrement main count: c:%d", (int) r->main->count); } #endif return NGX_DONE; } dd("internal redirect to %.*s", (int) ctx->exec_uri.len, ctx->exec_uri.data); r->write_event_handler = ngx_http_request_empty_handler; if (r->read_event_handler == ngx_http_lua_rd_check_broken_connection) { /* resume the read event handler */ r->read_event_handler = ngx_http_block_reading; } rc = ngx_http_internal_redirect(r, &ctx->exec_uri, &ctx->exec_args); dd("internal redirect returned %d when in content phase? " "%d", (int) rc, ctx->entered_content_phase); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } #if 0 if (!ctx->entered_content_phase) { /* XXX ensure the main request ref count * is decreased because the current * request will be quit */ dd("XXX decrement main count"); r->main->count--; } #endif return NGX_DONE; } static ngx_int_t ngx_http_lua_handle_exit(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) { ngx_int_t rc; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua thread aborting request with status %d", ctx->exit_code); #if 1 if (!r->header_sent && r->headers_out.status == 0 && ctx->exit_code >= NGX_HTTP_OK) { r->headers_out.status = ctx->exit_code; } #endif ngx_http_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 1); ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD; if (r->filter_finalize) { ngx_http_set_ctx(r, ctx, ngx_http_lua_module); } ngx_http_lua_request_cleanup(ctx, 0); if (ctx->buffering && r->headers_out.status && ctx->exit_code != NGX_ERROR && ctx->exit_code != NGX_HTTP_REQUEST_TIME_OUT && ctx->exit_code != NGX_HTTP_CLIENT_CLOSED_REQUEST && ctx->exit_code != NGX_HTTP_CLOSE) { rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } if (ctx->exit_code >= NGX_HTTP_OK) { return NGX_HTTP_OK; } return ctx->exit_code; } if ((ctx->exit_code == NGX_OK && ctx->entered_content_phase) || (ctx->exit_code >= NGX_HTTP_OK && ctx->exit_code < NGX_HTTP_SPECIAL_RESPONSE && ctx->exit_code != NGX_HTTP_NO_CONTENT)) { rc = ngx_http_lua_send_chain_link(r, ctx, NULL /* indicate last_buf */); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } } #if 1 if (r->header_sent && ctx->exit_code > NGX_OK && ctx->exit_code != NGX_HTTP_REQUEST_TIME_OUT && ctx->exit_code != NGX_HTTP_CLIENT_CLOSED_REQUEST && ctx->exit_code != NGX_HTTP_CLOSE) { if (ctx->entered_content_phase) { return NGX_OK; } return NGX_HTTP_OK; } #endif return ctx->exit_code; } void ngx_http_lua_process_args_option(ngx_http_request_t *r, lua_State *L, int table, ngx_str_t *args) { u_char *key; size_t key_len; u_char *value; size_t value_len; size_t len = 0; size_t key_escape = 0; uintptr_t total_escape = 0; int n; int i; u_char *p; if (table < 0) { table = lua_gettop(L) + table + 1; } n = 0; lua_pushnil(L); while (lua_next(L, table) != 0) { if (lua_type(L, -2) != LUA_TSTRING) { luaL_error(L, "attempt to use a non-string key in the " "\"args\" option table"); return; } key = (u_char *) lua_tolstring(L, -2, &key_len); key_escape = 2 * ngx_http_lua_escape_uri(NULL, key, key_len, NGX_ESCAPE_URI); total_escape += key_escape; switch (lua_type(L, -1)) { case LUA_TNUMBER: case LUA_TSTRING: value = (u_char *) lua_tolstring(L, -1, &value_len); total_escape += 2 * ngx_http_lua_escape_uri(NULL, value, value_len, NGX_ESCAPE_URI); len += key_len + value_len + (sizeof("=") - 1); n++; break; case LUA_TBOOLEAN: if (lua_toboolean(L, -1)) { len += key_len; n++; } break; case LUA_TTABLE: i = 0; lua_pushnil(L); while (lua_next(L, -2) != 0) { if (lua_isboolean(L, -1)) { if (lua_toboolean(L, -1)) { len += key_len; } else { lua_pop(L, 1); continue; } } else { value = (u_char *) lua_tolstring(L, -1, &value_len); if (value == NULL) { luaL_error(L, "attempt to use %s as query arg value", luaL_typename(L, -1)); return; } total_escape += 2 * ngx_http_lua_escape_uri(NULL, value, value_len, NGX_ESCAPE_URI); len += key_len + value_len + (sizeof("=") - 1); } if (i++ > 0) { total_escape += key_escape; } n++; lua_pop(L, 1); } break; default: luaL_error(L, "attempt to use %s as query arg value", luaL_typename(L, -1)); return; } lua_pop(L, 1); } len += (size_t) total_escape; if (n > 1) { len += (n - 1) * (sizeof("&") - 1); } dd("len 1: %d", (int) len); if (r) { p = ngx_palloc(r->pool, len); if (p == NULL) { luaL_error(L, "out of memory"); return; } } else { p = lua_newuserdata(L, len); } args->data = p; args->len = len; i = 0; lua_pushnil(L); while (lua_next(L, table) != 0) { key = (u_char *) lua_tolstring(L, -2, &key_len); switch (lua_type(L, -1)) { case LUA_TNUMBER: case LUA_TSTRING: if (total_escape) { p = (u_char *) ngx_http_lua_escape_uri(p, key, key_len, NGX_ESCAPE_URI); } else { dd("shortcut: no escape required"); p = ngx_copy(p, key, key_len); } *p++ = '='; value = (u_char *) lua_tolstring(L, -1, &value_len); if (total_escape) { p = (u_char *) ngx_http_lua_escape_uri(p, value, value_len, NGX_ESCAPE_URI); } else { p = ngx_copy(p, value, value_len); } if (i != n - 1) { /* not the last pair */ *p++ = '&'; } i++; break; case LUA_TBOOLEAN: if (lua_toboolean(L, -1)) { if (total_escape) { p = (u_char *) ngx_http_lua_escape_uri(p, key, key_len, NGX_ESCAPE_URI); } else { dd("shortcut: no escape required"); p = ngx_copy(p, key, key_len); } if (i != n - 1) { /* not the last pair */ *p++ = '&'; } i++; } break; case LUA_TTABLE: lua_pushnil(L); while (lua_next(L, -2) != 0) { if (lua_isboolean(L, -1)) { if (lua_toboolean(L, -1)) { if (total_escape) { p = (u_char *) ngx_http_lua_escape_uri(p, key, key_len, NGX_ESCAPE_URI); } else { dd("shortcut: no escape required"); p = ngx_copy(p, key, key_len); } } else { lua_pop(L, 1); continue; } } else { if (total_escape) { p = (u_char *) ngx_http_lua_escape_uri(p, key, key_len, NGX_ESCAPE_URI); } else { dd("shortcut: no escape required"); p = ngx_copy(p, key, key_len); } *p++ = '='; value = (u_char *) lua_tolstring(L, -1, &value_len); if (total_escape) { p = (u_char *) ngx_http_lua_escape_uri(p, value, value_len, NGX_ESCAPE_URI); } else { p = ngx_copy(p, value, value_len); } } if (i != n - 1) { /* not the last pair */ *p++ = '&'; } i++; lua_pop(L, 1); } break; default: luaL_error(L, "should not reach here"); return; } lua_pop(L, 1); } if (p - args->data != (ssize_t) len) { luaL_error(L, "buffer error: %d != %d", (int) (p - args->data), (int) len); return; } } static ngx_int_t ngx_http_lua_handle_rewrite_jump(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua thread aborting request with URI rewrite jump: " "\"%V?%V\"", &r->uri, &r->args); ngx_http_lua_probe_coroutine_done(r, ctx->cur_co_ctx->co, 1); ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_DEAD; if (r->filter_finalize) { ngx_http_set_ctx(r, ctx, ngx_http_lua_module); } ngx_http_lua_request_cleanup(ctx, 1 /* forcible */); ngx_http_lua_init_ctx(r, ctx); return NGX_OK; } /* XXX ngx_open_and_stat_file is static in the core. sigh. */ ngx_int_t ngx_http_lua_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, ngx_log_t *log) { ngx_fd_t fd; ngx_file_info_t fi; if (of->fd != NGX_INVALID_FILE) { if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) { of->failed = ngx_file_info_n; goto failed; } if (of->uniq == ngx_file_uniq(&fi)) { goto done; } } else if (of->test_dir) { if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) { of->failed = ngx_file_info_n; goto failed; } if (ngx_is_dir(&fi)) { goto done; } } if (!of->log) { /* * Use non-blocking open() not to hang on FIFO files, etc. * This flag has no effect on a regular files. */ fd = ngx_open_file(name, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, NGX_FILE_OPEN, 0); } else { fd = ngx_open_file(name, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN, NGX_FILE_DEFAULT_ACCESS); } if (fd == NGX_INVALID_FILE) { of->failed = ngx_open_file_n; goto failed; } if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_CRIT, log, ngx_errno, ngx_fd_info_n " \"%s\" failed", name); if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, ngx_close_file_n " \"%s\" failed", name); } of->fd = NGX_INVALID_FILE; return NGX_ERROR; } if (ngx_is_dir(&fi)) { if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, ngx_close_file_n " \"%s\" failed", name); } of->fd = NGX_INVALID_FILE; } else { of->fd = fd; if (of->directio <= ngx_file_size(&fi)) { if (ngx_directio_on(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, ngx_directio_on_n " \"%s\" failed", name); } else { of->is_directio = 1; } } } done: of->uniq = ngx_file_uniq(&fi); of->mtime = ngx_file_mtime(&fi); of->size = ngx_file_size(&fi); #if defined(nginx_version) && nginx_version >= 1000001 of->fs_size = ngx_file_fs_size(&fi); #endif of->is_dir = ngx_is_dir(&fi); of->is_file = ngx_is_file(&fi); of->is_link = ngx_is_link(&fi); of->is_exec = ngx_is_exec(&fi); return NGX_OK; failed: of->fd = NGX_INVALID_FILE; of->err = ngx_errno; return NGX_ERROR; } ngx_chain_t * ngx_http_lua_chains_get_free_buf(ngx_log_t *log, ngx_pool_t *p, ngx_chain_t **free, size_t len, ngx_buf_tag_t tag) { ngx_chain_t *cl; ngx_buf_t *b; if (*free) { cl = *free; *free = cl->next; cl->next = NULL; b = cl->buf; if ((size_t) (b->end - b->start) >= len) { ngx_log_debug4(NGX_LOG_DEBUG_HTTP, log, 0, "lua reuse free buf memory %O >= %uz, cl:%p, p:%p", (off_t) (b->end - b->start), len, cl, b->start); b->pos = b->start; b->last = b->start; b->tag = tag; return cl; } ngx_log_debug4(NGX_LOG_DEBUG_HTTP, log, 0, "lua reuse free buf chain, but reallocate memory " "because %uz >= %O, cl:%p, p:%p", len, (off_t) (b->end - b->start), cl, b->start); if (ngx_buf_in_memory(b) && b->start) { ngx_pfree(p, b->start); } if (len) { b->start = ngx_palloc(p, len); if (b->start == NULL) { return NULL; } b->end = b->start + len; } else { b->last = NULL; b->end = NULL; } dd("buf start: %p", cl->buf->start); b->pos = b->start; b->last = b->start; b->tag = tag; return cl; } cl = ngx_alloc_chain_link(p); if (cl == NULL) { return NULL; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0, "lua allocate new chainlink and new buf of size %uz, cl:%p", len, cl); cl->buf = ngx_create_temp_buf(p, len); if (cl->buf == NULL) { return NULL; } dd("buf start: %p", cl->buf->start); cl->buf->tag = tag; cl->next = NULL; return cl; } static int ngx_http_lua_thread_traceback(lua_State *L, lua_State *co, ngx_http_lua_co_ctx_t *coctx) { int base; int level, coid; lua_Debug ar; base = lua_gettop(L); lua_pushliteral(L, "stack traceback:"); coid = 0; while (co) { if (coid >= NGX_HTTP_LUA_BT_MAX_COROS) { break; } lua_pushfstring(L, "\ncoroutine %d:", coid++); level = 0; while (lua_getstack(co, level++, &ar)) { if (level > NGX_HTTP_LUA_BT_DEPTH) { lua_pushliteral(L, "\n\t..."); break; } lua_pushliteral(L, "\n\t"); lua_getinfo(co, "Snl", &ar); lua_pushfstring(L, "%s:", ar.short_src); if (ar.currentline > 0) { lua_pushfstring(L, "%d:", ar.currentline); } if (*ar.namewhat != '\0') { /* is there a name? */ lua_pushfstring(L, " in function " LUA_QS, ar.name); } else { if (*ar.what == 'm') { /* main? */ lua_pushfstring(L, " in main chunk"); } else if (*ar.what == 'C' || *ar.what == 't') { lua_pushliteral(L, " ?"); /* C function or tail call */ } else { lua_pushfstring(L, " in function <%s:%d>", ar.short_src, ar.linedefined); } } } if (lua_gettop(L) - base >= 15) { lua_concat(L, lua_gettop(L) - base); } /* check if the coroutine has a parent coroutine*/ coctx = coctx->parent_co_ctx; if (!coctx || coctx->co_status == NGX_HTTP_LUA_CO_DEAD) { break; } co = coctx->co; } lua_concat(L, lua_gettop(L) - base); return 1; } int ngx_http_lua_traceback(lua_State *L) { if (!lua_isstring(L, 1)) { /* 'message' not a string? */ return 1; /* keep it intact */ } lua_getfield(L, LUA_GLOBALSINDEX, "debug"); if (!lua_istable(L, -1)) { lua_pop(L, 1); return 1; } lua_getfield(L, -1, "traceback"); if (!lua_isfunction(L, -1)) { lua_pop(L, 2); return 1; } lua_pushvalue(L, 1); /* pass error message */ lua_pushinteger(L, 2); /* skip this function and traceback */ lua_call(L, 2, 1); /* call debug.traceback */ return 1; } static void ngx_http_lua_inject_arg_api(lua_State *L) { lua_pushliteral(L, "arg"); lua_newtable(L); /* .arg table aka {} */ lua_createtable(L, 0 /* narr */, 2 /* nrec */); /* the metatable */ lua_pushcfunction(L, ngx_http_lua_param_get); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, ngx_http_lua_param_set); lua_setfield(L, -2, "__newindex"); lua_setmetatable(L, -2); /* tie the metatable to param table */ dd("top: %d, type -1: %s", lua_gettop(L), luaL_typename(L, -1)); lua_rawset(L, -3); /* set ngx.arg table */ } static int ngx_http_lua_param_get(lua_State *L) { ngx_http_lua_ctx_t *ctx; ngx_http_request_t *r; r = ngx_http_lua_get_req(L); if (r == NULL) { return 0; } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "ctx not found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_SET | NGX_HTTP_LUA_CONTEXT_BODY_FILTER); if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SET)) { return ngx_http_lua_setby_param_get(L); } /* ctx->context & (NGX_HTTP_LUA_CONTEXT_BODY_FILTER) */ return ngx_http_lua_body_filter_param_get(L); } static int ngx_http_lua_param_set(lua_State *L) { ngx_http_lua_ctx_t *ctx; ngx_http_request_t *r; r = ngx_http_lua_get_req(L); if (r == NULL) { return 0; } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "ctx not found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_BODY_FILTER); return ngx_http_lua_body_filter_param_set(L, r, ctx); } ngx_http_lua_co_ctx_t * ngx_http_lua_get_co_ctx(lua_State *L, ngx_http_lua_ctx_t *ctx) { ngx_uint_t i; ngx_list_part_t *part; ngx_http_lua_co_ctx_t *coctx; if (L == ctx->entry_co_ctx.co) { return &ctx->entry_co_ctx; } if (ctx->user_co_ctx == NULL) { return NULL; } part = &ctx->user_co_ctx->part; coctx = part->elts; /* FIXME: we should use rbtree here to prevent O(n) lookup overhead */ for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; coctx = part->elts; i = 0; } if (coctx[i].co == L) { return &coctx[i]; } } return NULL; } ngx_http_lua_co_ctx_t * ngx_http_lua_create_co_ctx(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) { ngx_http_lua_co_ctx_t *coctx; if (ctx->user_co_ctx == NULL) { ctx->user_co_ctx = ngx_list_create(r->pool, 4, sizeof(ngx_http_lua_co_ctx_t)); if (ctx->user_co_ctx == NULL) { return NULL; } } coctx = ngx_list_push(ctx->user_co_ctx); if (coctx == NULL) { return NULL; } ngx_memzero(coctx, sizeof(ngx_http_lua_co_ctx_t)); coctx->co_ref = LUA_NOREF; return coctx; } /* this is for callers other than the content handler */ ngx_int_t ngx_http_lua_run_posted_threads(ngx_connection_t *c, lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) { ngx_int_t rc; ngx_http_lua_posted_thread_t *pt; for ( ;; ) { if (c->destroyed) { return NGX_DONE; } pt = ctx->posted_threads; if (pt == NULL) { return NGX_DONE; } ctx->posted_threads = pt->next; ngx_http_lua_probe_run_posted_thread(r, pt->co_ctx->co, (int) pt->co_ctx->co_status); if (pt->co_ctx->co_status != NGX_HTTP_LUA_CO_RUNNING) { continue; } ctx->cur_co_ctx = pt->co_ctx; rc = ngx_http_lua_run_thread(L, r, ctx, 0); if (rc == NGX_AGAIN) { continue; } if (rc == NGX_DONE) { ngx_http_lua_finalize_request(r, NGX_DONE); continue; } /* rc == NGX_ERROR || rc >= NGX_OK */ if (ctx->entered_content_phase) { ngx_http_lua_finalize_request(r, rc); } return rc; } /* impossible to reach here */ } ngx_int_t ngx_http_lua_post_thread(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t *coctx) { ngx_http_lua_posted_thread_t **p; ngx_http_lua_posted_thread_t *pt; pt = ngx_palloc(r->pool, sizeof(ngx_http_lua_posted_thread_t)); if (pt == NULL) { return NGX_ERROR; } pt->co_ctx = coctx; pt->next = NULL; for (p = &ctx->posted_threads; *p; p = &(*p)->next) { /* void */ } *p = pt; return NGX_OK; } static void ngx_http_lua_finalize_coroutines(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) { ngx_http_lua_co_ctx_t *cc, *coctx; ngx_list_part_t *part; ngx_uint_t i; if (ctx->uthreads == 0) { if (ngx_http_lua_is_entry_thread(ctx)) { return; } /* the current thread is not the entry thread */ if (ctx->entry_co_ctx.co_status == NGX_HTTP_LUA_CO_DEAD) { return; } } if (ctx->user_co_ctx) { part = &ctx->user_co_ctx->part; cc = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; cc = part->elts; i = 0; } coctx = &cc[i]; if (coctx->cleanup) { coctx->cleanup(coctx); coctx->cleanup = NULL; coctx->co_status = NGX_HTTP_LUA_CO_DEAD; /* TODO we could also free the user thread here */ } } } coctx = &ctx->entry_co_ctx; if (coctx->cleanup) { coctx->cleanup(coctx); } } static ngx_int_t ngx_http_lua_post_zombie_thread(ngx_http_request_t *r, ngx_http_lua_co_ctx_t *parent, ngx_http_lua_co_ctx_t *thread) { ngx_http_lua_posted_thread_t **p; ngx_http_lua_posted_thread_t *pt; pt = ngx_palloc(r->pool, sizeof(ngx_http_lua_posted_thread_t)); if (pt == NULL) { return NGX_ERROR; } pt->co_ctx = thread; pt->next = NULL; for (p = &parent->zombie_child_threads; *p; p = &(*p)->next) { /* void */ } *p = pt; return NGX_OK; } static void ngx_http_lua_cleanup_zombie_child_uthreads(ngx_http_request_t *r, lua_State *L, ngx_http_lua_ctx_t *ctx, ngx_http_lua_co_ctx_t *coctx) { ngx_http_lua_posted_thread_t *pt; for (pt = coctx->zombie_child_threads; pt; pt = pt->next) { if (pt->co_ctx->co_ref != LUA_NOREF) { ngx_http_lua_del_thread(r, L, ctx, pt->co_ctx); ctx->uthreads--; } } coctx->zombie_child_threads = NULL; } ngx_int_t ngx_http_lua_check_broken_connection(ngx_http_request_t *r, ngx_event_t *ev) { int n; char buf[1]; ngx_err_t err; ngx_int_t event; ngx_connection_t *c; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0, "http lua check client, write event:%d, \"%V\"", ev->write, &r->uri); c = r->connection; if (c->error) { if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) { event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT; if (ngx_del_event(ev, event, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } return NGX_HTTP_CLIENT_CLOSED_REQUEST; } #if (NGX_HAVE_KQUEUE) if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { if (!ev->pending_eof) { return NGX_OK; } ev->eof = 1; if (ev->kq_errno) { ev->error = 1; } ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno, "kevent() reported that client prematurely closed " "connection"); return NGX_HTTP_CLIENT_CLOSED_REQUEST; } #endif n = recv(c->fd, buf, 1, MSG_PEEK); err = ngx_socket_errno; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, err, "http lua recv(): %d", n); if (ev->write && (n >= 0 || err == NGX_EAGAIN)) { return NGX_OK; } if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) { dd("event is active"); event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT; #if 1 if (ngx_del_event(ev, event, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } #endif } dd("HERE %d", (int) n); if (n > 0) { return NGX_OK; } if (n == -1) { if (err == NGX_EAGAIN) { dd("HERE"); return NGX_OK; } ev->error = 1; } else { /* n == 0 */ err = 0; } ev->eof = 1; ngx_log_error(NGX_LOG_INFO, ev->log, err, "client prematurely closed connection"); return NGX_HTTP_CLIENT_CLOSED_REQUEST; } void ngx_http_lua_rd_check_broken_connection(ngx_http_request_t *r) { ngx_int_t rc; ngx_event_t *rev; ngx_http_lua_ctx_t *ctx; if (r->done) { return; } rc = ngx_http_lua_check_broken_connection(r, r->connection->read); if (rc == NGX_OK) { return; } /* rc == NGX_ERROR || rc > NGX_OK */ ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return; } if (ctx->on_abort_co_ctx == NULL) { r->connection->error = 1; ngx_http_lua_request_cleanup(ctx, 0); ngx_http_lua_finalize_request(r, rc); return; } if (ctx->on_abort_co_ctx->co_status != NGX_HTTP_LUA_CO_SUSPENDED) { /* on_abort already run for the current request handler */ rev = r->connection->read; if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && rev->active) { if (ngx_del_event(rev, NGX_READ_EVENT, 0) != NGX_OK) { ngx_http_lua_request_cleanup(ctx, 0); ngx_http_lua_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } } return; } ctx->uthreads++; ctx->resume_handler = ngx_http_lua_on_abort_resume; ctx->on_abort_co_ctx->co_status = NGX_HTTP_LUA_CO_RUNNING; ctx->cur_co_ctx = ctx->on_abort_co_ctx; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua waking up the on_abort callback thread"); if (ctx->entered_content_phase) { r->write_event_handler = ngx_http_lua_content_wev_handler; } else { r->write_event_handler = ngx_http_core_run_phases; } r->write_event_handler(r); } static ngx_int_t ngx_http_lua_on_abort_resume(ngx_http_request_t *r) { lua_State *vm; ngx_int_t rc; ngx_connection_t *c; ngx_http_lua_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return NGX_ERROR; } ctx->resume_handler = ngx_http_lua_wev_handler; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua resuming the on_abort callback thread"); #if 0 ngx_http_lua_probe_info("tcp resume"); #endif c = r->connection; vm = ngx_http_lua_get_lua_vm(r, ctx); rc = ngx_http_lua_run_thread(vm, r, ctx, 0); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua run thread returned %d", rc); if (rc == NGX_AGAIN) { return ngx_http_lua_run_posted_threads(c, vm, r, ctx); } if (rc == NGX_DONE) { ngx_http_lua_finalize_request(r, NGX_DONE); return ngx_http_lua_run_posted_threads(c, vm, r, ctx); } if (ctx->entered_content_phase) { ngx_http_lua_finalize_request(r, rc); return NGX_DONE; } return rc; } ngx_int_t ngx_http_lua_test_expect(ngx_http_request_t *r) { ngx_int_t n; ngx_str_t *expect; if (r->expect_tested || r->headers_in.expect == NULL || r->http_version < NGX_HTTP_VERSION_11) { return NGX_OK; } r->expect_tested = 1; expect = &r->headers_in.expect->value; if (expect->len != sizeof("100-continue") - 1 || ngx_strncasecmp(expect->data, (u_char *) "100-continue", sizeof("100-continue") - 1) != 0) { return NGX_OK; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "send 100 Continue"); n = r->connection->send(r->connection, (u_char *) "HTTP/1.1 100 Continue" CRLF CRLF, sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1); if (n == sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1) { return NGX_OK; } /* we assume that such small packet should be send successfully */ return NGX_ERROR; } void ngx_http_lua_finalize_request(ngx_http_request_t *r, ngx_int_t rc) { if (r->connection->fd != -1) { ngx_http_finalize_request(r, rc); return; } ngx_http_lua_finalize_fake_request(r, rc); } void ngx_http_lua_finalize_fake_request(ngx_http_request_t *r, ngx_int_t rc) { ngx_connection_t *c; c = r->connection; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http lua finalize fake request: %d, a:%d, c:%d", rc, r == c->data, r->main->count); if (rc == NGX_DONE) { ngx_http_lua_close_fake_request(r); return; } if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { ngx_http_lua_close_fake_request(r); return; } if (c->read->timer_set) { ngx_del_timer(c->read); } if (c->write->timer_set) { c->write->delayed = 0; ngx_del_timer(c->write); } ngx_http_lua_close_fake_request(r); } static void ngx_http_lua_close_fake_request(ngx_http_request_t *r) { ngx_connection_t *c; r = r->main; c = r->connection; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http lua fake request count:%d", r->count); if (r->count == 0) { ngx_log_error(NGX_LOG_ALERT, c->log, 0, "http lua fake request " "count is zero"); } r->count--; if (r->count) { return; } ngx_http_lua_free_fake_request(r); ngx_http_lua_close_fake_connection(c); } static void ngx_http_lua_free_fake_request(ngx_http_request_t *r) { ngx_log_t *log; ngx_http_cleanup_t *cln; ngx_http_log_ctx_t *ctx; log = r->connection->log; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http lua close fake " "request"); if (r->pool == NULL) { ngx_log_error(NGX_LOG_ALERT, log, 0, "http lua fake request " "already closed"); return; } for (cln = r->cleanup; cln; cln = cln->next) { if (cln->handler) { cln->handler(cln->data); } } /* the various request strings were allocated from r->pool */ ctx = log->data; ctx->request = NULL; r->request_line.len = 0; r->connection->destroyed = 1; ngx_destroy_pool(r->pool); } void ngx_http_lua_close_fake_connection(ngx_connection_t *c) { ngx_pool_t *pool; ngx_connection_t *saved_c = NULL; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http lua close fake http connection"); c->destroyed = 1; pool = c->pool; if (c->read->timer_set) { ngx_del_timer(c->read); } if (c->write->timer_set) { ngx_del_timer(c->write); } c->read->closed = 1; c->write->closed = 1; /* we temporarily use a valid fd (0) to make ngx_free_connection happy */ c->fd = 0; if (ngx_cycle->files) { saved_c = ngx_cycle->files[0]; } ngx_free_connection(c); c->fd = (ngx_socket_t) -1; if (ngx_cycle->files) { ngx_cycle->files[0] = saved_c; } if (pool) { ngx_destroy_pool(pool); } } lua_State * ngx_http_lua_init_vm(lua_State *parent_vm, ngx_cycle_t *cycle, ngx_pool_t *pool, ngx_http_lua_main_conf_t *lmcf, ngx_log_t *log, ngx_pool_cleanup_t **pcln) { lua_State *L; ngx_uint_t i; ngx_pool_cleanup_t *cln; ngx_http_lua_preload_hook_t *hook; ngx_http_lua_vm_state_t *state; /* add new cleanup handler to config mem pool */ cln = ngx_pool_cleanup_add(pool, 0); if (cln == NULL) { return NULL; } /* create new Lua VM instance */ L = ngx_http_lua_new_state(parent_vm, cycle, lmcf, log); if (L == NULL) { return NULL; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "lua initialize the " "global Lua VM %p", L); /* register cleanup handler for Lua VM */ cln->handler = ngx_http_lua_cleanup_vm; state = ngx_alloc(sizeof(ngx_http_lua_vm_state_t), log); if (state == NULL) { return NULL; } state->vm = L; state->count = 1; cln->data = state; if (pcln) { *pcln = cln; } if (lmcf->preload_hooks) { /* register the 3rd-party module's preload hooks */ lua_getglobal(L, "package"); lua_getfield(L, -1, "preload"); hook = lmcf->preload_hooks->elts; for (i = 0; i < lmcf->preload_hooks->nelts; i++) { ngx_http_lua_probe_register_preload_package(L, hook[i].package); lua_pushcfunction(L, hook[i].loader); lua_setfield(L, -2, (char *) hook[i].package); } lua_pop(L, 2); } return L; } void ngx_http_lua_cleanup_vm(void *data) { lua_State *L; ngx_http_lua_vm_state_t *state = data; #if (DDEBUG) if (state) { dd("cleanup VM: c:%d, s:%p", (int) state->count, state->vm); } #endif if (state) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "decrementing " "the reference count for Lua VM: %i", state->count); if (--state->count == 0) { L = state->vm; ngx_http_lua_cleanup_conn_pools(L); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "lua close the global Lua VM %p", L); lua_close(L); ngx_free(state); } } } static void ngx_http_lua_cleanup_conn_pools(lua_State *L) { ngx_queue_t *q; ngx_connection_t *c; ngx_http_lua_socket_pool_t *spool; ngx_http_lua_socket_pool_item_t *item; lua_pushlightuserdata(L, &ngx_http_lua_socket_pool_key); lua_rawget(L, LUA_REGISTRYINDEX); /* table */ lua_pushnil(L); /* first key */ while (lua_next(L, -2) != 0) { /* tb key val */ spool = lua_touserdata(L, -1); if (!ngx_queue_empty(&spool->cache)) { q = ngx_queue_head(&spool->cache); item = ngx_queue_data(q, ngx_http_lua_socket_pool_item_t, queue); c = item->connection; ngx_close_connection(c); ngx_queue_remove(q); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "lua tcp socket keepalive: free connection pool " "for \"%s\"", spool->key); } lua_pop(L, 1); } lua_pop(L, 1); } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_phase.c0000664000000000000000000000346612305451335017363 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_phase.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_ctx.h" static int ngx_http_lua_ngx_get_phase(lua_State *L); static int ngx_http_lua_ngx_get_phase(lua_State *L) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; r = ngx_http_lua_get_req(L); /* If we have no request object, assume we are called from the "init" * phase. */ if (r == NULL) { lua_pushlstring(L, (char *) "init", sizeof("init") - 1); return 1; } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no request ctx found"); } switch (ctx->context) { case NGX_HTTP_LUA_CONTEXT_SET: lua_pushliteral(L, "set"); break; case NGX_HTTP_LUA_CONTEXT_REWRITE: lua_pushliteral(L, "rewrite"); break; case NGX_HTTP_LUA_CONTEXT_ACCESS: lua_pushliteral(L, "access"); break; case NGX_HTTP_LUA_CONTEXT_CONTENT: lua_pushliteral(L, "content"); break; case NGX_HTTP_LUA_CONTEXT_LOG: lua_pushliteral(L, "log"); break; case NGX_HTTP_LUA_CONTEXT_HEADER_FILTER: lua_pushliteral(L, "header_filter"); break; case NGX_HTTP_LUA_CONTEXT_BODY_FILTER: lua_pushliteral(L, "body_filter"); break; case NGX_HTTP_LUA_CONTEXT_TIMER: lua_pushliteral(L, "timer"); break; default: return luaL_error(L, "unknown phase: %d", (int) ctx->context); } return 1; } void ngx_http_lua_inject_phase_api(lua_State *L) { lua_pushcfunction(L, ngx_http_lua_ngx_get_phase); lua_setfield(L, -2, "get_phase"); } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_time.h0000664000000000000000000000062112305451334017213 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_TIME_H_INCLUDED_ #define _NGX_HTTP_LUA_TIME_H_INCLUDED_ #include "ngx_http_lua_common.h" void ngx_http_lua_inject_time_api(lua_State *L); void ngx_http_lua_inject_req_time_api(lua_State *L); #endif /* _NGX_HTTP_LUA_TIME_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_string.h0000664000000000000000000000054412305451335017570 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_STRING_H_INCLUDED_ #define _NGX_HTTP_LUA_STRING_H_INCLUDED_ #include "ngx_http_lua_common.h" void ngx_http_lua_inject_string_api(lua_State *L); #endif /* _NGX_HTTP_LUA_STRING_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_config.h0000664000000000000000000000047012305451335017525 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_CONFIG_H_INCLUDED_ #define _NGX_HTTP_LUA_CONFIG_H_INCLUDED_ #include "ngx_http_lua_common.h" void ngx_http_lua_inject_config_api(lua_State *L); #endif /* _NGX_HTTP_LUA_CONFIG_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_log.c0000664000000000000000000001552712305451335017045 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_log.h" #include "ngx_http_lua_util.h" static int ngx_http_lua_print(lua_State *L); static int ngx_http_lua_ngx_log(lua_State *L); static int log_wrapper(ngx_log_t *log, const char *ident, ngx_uint_t level, lua_State *L); static void ngx_http_lua_inject_log_consts(lua_State *L); /** * Wrapper of nginx log functionality. Take a log level param and varargs of * log message params. * * @param L Lua state pointer * @retval always 0 (don't return values to Lua) * */ int ngx_http_lua_ngx_log(lua_State *L) { ngx_log_t *log; ngx_http_request_t *r; const char *msg; int level; r = ngx_http_lua_get_req(L); if (r && r->connection && r->connection->log) { log = r->connection->log; } else { log = ngx_cycle->log; } level = luaL_checkint(L, 1); if (level < NGX_LOG_STDERR || level > NGX_LOG_DEBUG) { msg = lua_pushfstring(L, "bad log level: %d", level); return luaL_argerror(L, 1, msg); } /* remove log-level param from stack */ lua_remove(L, 1); return log_wrapper(log, "[lua] ", (ngx_uint_t) level, L); } /** * Override Lua print function, output message to nginx error logs. Equal to * ngx.log(ngx.NOTICE, ...). * * @param L Lua state pointer * @retval always 0 (don't return values to Lua) * */ int ngx_http_lua_print(lua_State *L) { ngx_log_t *log; ngx_http_request_t *r; r = ngx_http_lua_get_req(L); if (r && r->connection && r->connection->log) { log = r->connection->log; } else { log = ngx_cycle->log; } return log_wrapper(log, "[lua] ", NGX_LOG_NOTICE, L); } static int log_wrapper(ngx_log_t *log, const char *ident, ngx_uint_t level, lua_State *L) { u_char *buf; u_char *p, *q; ngx_str_t name; int nargs, i; size_t size, len; size_t src_len = 0; int type; const char *msg; lua_Debug ar; if (level > log->log_level) { return 0; } #if 1 /* add debug info */ lua_getstack(L, 1, &ar); lua_getinfo(L, "Snl", &ar); /* get the basename of the Lua source file path, stored in q */ name.data = (u_char *) ar.short_src; if (name.data == NULL) { name.len = 0; } else { p = name.data; while (*p != '\0') { if (*p == '/' || *p == '\\') { name.data = p + 1; } p++; } name.len = p - name.data; } #endif nargs = lua_gettop(L); size = name.len + NGX_INT_T_LEN + sizeof(":: ") - 1; if (*ar.namewhat != '\0' && *ar.what == 'L') { src_len = ngx_strlen(ar.name); size += src_len + sizeof("(): ") - 1; } for (i = 1; i <= nargs; i++) { type = lua_type(L, i); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: lua_tolstring(L, i, &len); size += len; break; case LUA_TNIL: size += sizeof("nil") - 1; break; case LUA_TBOOLEAN: if (lua_toboolean(L, i)) { size += sizeof("true") - 1; } else { size += sizeof("false") - 1; } break; case LUA_TLIGHTUSERDATA: if (lua_touserdata(L, i) == NULL) { size += sizeof("null") - 1; break; } continue; default: msg = lua_pushfstring(L, "string, number, boolean, or nil " "expected, got %s", lua_typename(L, type)); return luaL_argerror(L, i, msg); } } buf = lua_newuserdata(L, size); p = ngx_copy(buf, name.data, name.len); *p++ = ':'; p = ngx_snprintf(p, NGX_INT_T_LEN, "%d", ar.currentline ? ar.currentline : ar.linedefined); *p++ = ':'; *p++ = ' '; if (*ar.namewhat != '\0' && *ar.what == 'L') { p = ngx_copy(p, ar.name, src_len); *p++ = '('; *p++ = ')'; *p++ = ':'; *p++ = ' '; } for (i = 1; i <= nargs; i++) { type = lua_type(L, i); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: q = (u_char *) lua_tolstring(L, i, &len); p = ngx_copy(p, q, len); break; case LUA_TNIL: *p++ = 'n'; *p++ = 'i'; *p++ = 'l'; break; case LUA_TBOOLEAN: if (lua_toboolean(L, i)) { *p++ = 't'; *p++ = 'r'; *p++ = 'u'; *p++ = 'e'; } else { *p++ = 'f'; *p++ = 'a'; *p++ = 'l'; *p++ = 's'; *p++ = 'e'; } break; case LUA_TLIGHTUSERDATA: *p++ = 'n'; *p++ = 'u'; *p++ = 'l'; *p++ = 'l'; break; default: return luaL_error(L, "impossible to reach here"); } } if (p - buf > (off_t) size) { return luaL_error(L, "buffer error: %d > %d", (int) (p - buf), (int) size); } ngx_log_error(level, log, 0, "%s%*s", ident, (size_t) (p - buf), buf); return 0; } void ngx_http_lua_inject_log_api(lua_State *L) { ngx_http_lua_inject_log_consts(L); lua_pushcfunction(L, ngx_http_lua_ngx_log); lua_setfield(L, -2, "log"); lua_pushcfunction(L, ngx_http_lua_print); lua_setglobal(L, "print"); } static void ngx_http_lua_inject_log_consts(lua_State *L) { /* {{{ nginx log level constants */ lua_pushinteger(L, NGX_LOG_STDERR); lua_setfield(L, -2, "STDERR"); lua_pushinteger(L, NGX_LOG_EMERG); lua_setfield(L, -2, "EMERG"); lua_pushinteger(L, NGX_LOG_ALERT); lua_setfield(L, -2, "ALERT"); lua_pushinteger(L, NGX_LOG_CRIT); lua_setfield(L, -2, "CRIT"); lua_pushinteger(L, NGX_LOG_ERR); lua_setfield(L, -2, "ERR"); lua_pushinteger(L, NGX_LOG_WARN); lua_setfield(L, -2, "WARN"); lua_pushinteger(L, NGX_LOG_NOTICE); lua_setfield(L, -2, "NOTICE"); lua_pushinteger(L, NGX_LOG_INFO); lua_setfield(L, -2, "INFO"); lua_pushinteger(L, NGX_LOG_DEBUG); lua_setfield(L, -2, "DEBUG"); /* }}} */ } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_common.h0000664000000000000000000003503512305451335017555 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_COMMON_H_INCLUDED_ #define _NGX_HTTP_LUA_COMMON_H_INCLUDED_ #include #include #include #include #include #include #include #include #include #include #if defined(NDK) && NDK #include #endif #ifndef MD5_DIGEST_LENGTH #define MD5_DIGEST_LENGTH 16 #endif #define ngx_http_lua_assert(a) assert(a) /* Nginx HTTP Lua Inline tag prefix */ #define NGX_HTTP_LUA_INLINE_TAG "nhli_" #define NGX_HTTP_LUA_INLINE_TAG_LEN \ (sizeof(NGX_HTTP_LUA_INLINE_TAG) - 1) #define NGX_HTTP_LUA_INLINE_KEY_LEN \ (NGX_HTTP_LUA_INLINE_TAG_LEN + 2 * MD5_DIGEST_LENGTH) /* Nginx HTTP Lua File tag prefix */ #define NGX_HTTP_LUA_FILE_TAG "nhlf_" #define NGX_HTTP_LUA_FILE_TAG_LEN \ (sizeof(NGX_HTTP_LUA_FILE_TAG) - 1) #define NGX_HTTP_LUA_FILE_KEY_LEN \ (NGX_HTTP_LUA_FILE_TAG_LEN + 2 * MD5_DIGEST_LENGTH) #if defined(NDK) && NDK typedef struct { size_t size; u_char *key; ngx_str_t script; } ngx_http_lua_set_var_data_t; #endif #ifndef NGX_HTTP_LUA_MAX_ARGS #define NGX_HTTP_LUA_MAX_ARGS 100 #endif #ifndef NGX_HTTP_LUA_MAX_HEADERS #define NGX_HTTP_LUA_MAX_HEADERS 100 #endif #define NGX_HTTP_LUA_CONTEXT_SET 0x01 #define NGX_HTTP_LUA_CONTEXT_REWRITE 0x02 #define NGX_HTTP_LUA_CONTEXT_ACCESS 0x04 #define NGX_HTTP_LUA_CONTEXT_CONTENT 0x08 #define NGX_HTTP_LUA_CONTEXT_LOG 0x10 #define NGX_HTTP_LUA_CONTEXT_HEADER_FILTER 0x20 #define NGX_HTTP_LUA_CONTEXT_BODY_FILTER 0x40 #define NGX_HTTP_LUA_CONTEXT_TIMER 0x80 #ifndef NGX_HTTP_LUA_NO_FFI_API #define NGX_HTTP_LUA_FFI_NO_REQ_CTX -100 #define NGX_HTTP_LUA_FFI_BAD_CONTEXT -101 #endif typedef struct ngx_http_lua_main_conf_s ngx_http_lua_main_conf_t; typedef ngx_int_t (*ngx_http_lua_conf_handler_pt)(ngx_log_t *log, ngx_http_lua_main_conf_t *lmcf, lua_State *L); typedef struct { u_char *package; lua_CFunction loader; } ngx_http_lua_preload_hook_t; struct ngx_http_lua_main_conf_s { lua_State *lua; ngx_str_t lua_path; ngx_str_t lua_cpath; ngx_cycle_t *cycle; ngx_pool_t *pool; ngx_int_t max_pending_timers; ngx_int_t pending_timers; ngx_int_t max_running_timers; ngx_int_t running_timers; ngx_connection_t *watcher; /* for watching the process exit event */ #if (NGX_PCRE) ngx_int_t regex_cache_entries; ngx_int_t regex_cache_max_entries; ngx_int_t regex_match_limit; #endif ngx_array_t *shm_zones; /* of ngx_shm_zone_t* */ ngx_array_t *preload_hooks; /* of ngx_http_lua_preload_hook_t */ ngx_flag_t postponed_to_rewrite_phase_end; ngx_flag_t postponed_to_access_phase_end; ngx_http_lua_conf_handler_pt init_handler; ngx_str_t init_src; ngx_uint_t shm_zones_inited; unsigned requires_header_filter:1; unsigned requires_body_filter:1; unsigned requires_capture_filter:1; unsigned requires_rewrite:1; unsigned requires_access:1; unsigned requires_log:1; unsigned requires_shm:1; }; typedef struct { ngx_flag_t force_read_body; /* whether force request body to be read */ ngx_flag_t enable_code_cache; /* whether to enable code cache */ ngx_flag_t http10_buffering; ngx_http_handler_pt rewrite_handler; ngx_http_handler_pt access_handler; ngx_http_handler_pt content_handler; ngx_http_handler_pt log_handler; ngx_http_handler_pt header_filter_handler; ngx_http_output_body_filter_pt body_filter_handler; ngx_http_complex_value_t rewrite_src; /* rewrite_by_lua inline script/script file path */ u_char *rewrite_src_key; /* cached key for rewrite_src */ ngx_http_complex_value_t access_src; /* access_by_lua inline script/script file path */ u_char *access_src_key; /* cached key for access_src */ ngx_http_complex_value_t content_src; /* content_by_lua inline script/script file path */ u_char *content_src_key; /* cached key for content_src */ ngx_http_complex_value_t log_src; /* log_by_lua inline script/script file path */ u_char *log_src_key; /* cached key for log_src */ ngx_http_complex_value_t header_filter_src; /* header_filter_by_lua inline script/script file path */ u_char *header_filter_src_key; /* cached key for header_filter_src */ ngx_http_complex_value_t body_filter_src; u_char *body_filter_src_key; ngx_msec_t keepalive_timeout; ngx_msec_t connect_timeout; ngx_msec_t send_timeout; ngx_msec_t read_timeout; size_t send_lowat; size_t buffer_size; ngx_uint_t pool_size; ngx_flag_t transform_underscores_in_resp_headers; ngx_flag_t log_socket_errors; ngx_flag_t check_client_abort; ngx_flag_t use_default_type; } ngx_http_lua_loc_conf_t; typedef enum { NGX_HTTP_LUA_USER_CORO_NOP = 0, NGX_HTTP_LUA_USER_CORO_RESUME = 1, NGX_HTTP_LUA_USER_CORO_YIELD = 2, NGX_HTTP_LUA_USER_THREAD_RESUME = 3 } ngx_http_lua_user_coro_op_t; typedef enum { NGX_HTTP_LUA_CO_RUNNING = 0, /* coroutine running */ NGX_HTTP_LUA_CO_SUSPENDED = 1, /* coroutine suspended */ NGX_HTTP_LUA_CO_NORMAL = 2, /* coroutine normal */ NGX_HTTP_LUA_CO_DEAD = 3, /* coroutine dead */ NGX_HTTP_LUA_CO_ZOMBIE = 4, /* coroutine zombie */ } ngx_http_lua_co_status_t; typedef struct ngx_http_lua_co_ctx_s ngx_http_lua_co_ctx_t; typedef struct ngx_http_lua_posted_thread_s ngx_http_lua_posted_thread_t; struct ngx_http_lua_posted_thread_s { ngx_http_lua_co_ctx_t *co_ctx; ngx_http_lua_posted_thread_t *next; }; enum { NGX_HTTP_LUA_SUBREQ_TRUNCATED = 1 }; struct ngx_http_lua_co_ctx_s { void *data; /* user state for cosockets */ lua_State *co; ngx_http_lua_co_ctx_t *parent_co_ctx; ngx_http_lua_posted_thread_t *zombie_child_threads; ngx_http_cleanup_pt cleanup; unsigned nsubreqs; /* number of subrequests of the * current request */ ngx_int_t *sr_statuses; /* all capture subrequest statuses */ ngx_http_headers_out_t **sr_headers; ngx_str_t *sr_bodies; /* all captured subrequest bodies */ uint8_t *sr_flags; unsigned pending_subreqs; /* number of subrequests being waited */ ngx_event_t sleep; /* used for ngx.sleep */ int co_ref; /* reference to anchor the thread coroutines (entry coroutine and user threads) in the Lua registry, preventing the thread coroutine from beging collected by the Lua GC */ unsigned waited_by_parent:1; /* whether being waited by a parent coroutine */ unsigned co_status:3; /* the current coroutine's status */ unsigned flushing:1; /* indicates whether the current coroutine is waiting for ngx.flush(true) */ unsigned is_uthread:1; /* whether the current coroutine is a user thread */ unsigned thread_spawn_yielded:1; /* yielded from the ngx.thread.spawn() call */ }; typedef struct { lua_State *vm; ngx_int_t count; } ngx_http_lua_vm_state_t; typedef struct ngx_http_lua_ctx_s { /* for lua_coce_cache off: */ ngx_http_lua_vm_state_t *vm_state; ngx_http_request_t *request; ngx_http_handler_pt resume_handler; ngx_http_lua_co_ctx_t *cur_co_ctx; /* co ctx for the current coroutine */ /* FIXME: we should use rbtree here to prevent O(n) lookup overhead */ ngx_list_t *user_co_ctx; /* coroutine contexts for user coroutines */ ngx_http_lua_co_ctx_t entry_co_ctx; /* coroutine context for the entry coroutine */ ngx_http_lua_co_ctx_t *on_abort_co_ctx; /* coroutine context for the on_abort thread */ int ctx_ref; /* reference to anchor request ctx data in lua registry */ unsigned flushing_coros; /* number of coroutines waiting on ngx.flush(true) */ unsigned uthreads; /* number of active user threads */ ngx_chain_t *out; /* buffered output chain for HTTP 1.0 */ ngx_chain_t *free_bufs; ngx_chain_t *busy_bufs; ngx_chain_t *free_recv_bufs; ngx_chain_t *flush_buf; ngx_http_cleanup_pt *cleanup; ngx_chain_t *body; /* buffered subrequest response body chains */ ngx_chain_t **last_body; /* for the "body" field */ ngx_str_t exec_uri; ngx_str_t exec_args; ngx_int_t exit_code; ngx_http_lua_co_ctx_t *downstream_co_ctx; /* co ctx for the coroutine reading the request body */ ngx_uint_t index; /* index of the current subrequest in its parent request */ ngx_http_lua_posted_thread_t *posted_threads; uint16_t context; /* the current running directive context (or running phase) for the current Lua chunk */ unsigned run_post_subrequest:1; /* whether it has run post_subrequest (for subrequests only) */ unsigned waiting_more_body:1; /* 1: waiting for more request body data; 0: no need to wait */ unsigned co_op:2; /* coroutine API operation */ unsigned exited:1; unsigned eof:1; /* 1: last_buf has been sent; 0: last_buf not sent yet */ unsigned capture:1; /* 1: response body of current request is to be captured by the lua capture filter, 0: not to be captured */ unsigned read_body_done:1; /* 1: request body has been all read; 0: body has not been all read */ unsigned headers_set:1; /* whether the user has set custom response headers */ unsigned entered_rewrite_phase:1; unsigned entered_access_phase:1; unsigned entered_content_phase:1; unsigned buffering:1; /* HTTP 1.0 response body buffering flag */ unsigned no_abort:1; /* prohibit "world abortion" via ngx.exit() and etc */ unsigned seen_last_in_filter:1; /* used by body_filter_by_lua* */ unsigned seen_last_for_subreq:1; /* used by body capture filter */ unsigned writing_raw_req_socket:1; /* used by raw downstream socket */ unsigned acquired_raw_req_socket:1; /* whether a raw req socket is acquired */ } ngx_http_lua_ctx_t; typedef struct ngx_http_lua_header_val_s ngx_http_lua_header_val_t; typedef ngx_int_t (*ngx_http_lua_set_header_pt)(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value); struct ngx_http_lua_header_val_s { ngx_http_complex_value_t value; ngx_uint_t hash; ngx_str_t key; ngx_http_lua_set_header_pt handler; ngx_uint_t offset; unsigned no_override; }; typedef struct { ngx_str_t name; ngx_uint_t offset; ngx_http_lua_set_header_pt handler; } ngx_http_lua_set_header_t; extern ngx_module_t ngx_http_lua_module; extern ngx_http_output_header_filter_pt ngx_http_lua_next_header_filter; extern ngx_http_output_body_filter_pt ngx_http_lua_next_body_filter; #endif /* _NGX_HTTP_LUA_COMMON_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_regex.c0000664000000000000000000020107212305451334017365 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #if (NGX_PCRE) #include "ngx_http_lua_regex.h" #include "ngx_http_lua_pcrefix.h" #include "ngx_http_lua_script.h" #include "ngx_http_lua_pcrefix.h" #include "ngx_http_lua_util.h" #include #if (PCRE_MAJOR > 8) || (PCRE_MAJOR == 8 && PCRE_MINOR >= 21) # define LUA_HAVE_PCRE_JIT 1 #else # define LUA_HAVE_PCRE_JIT 0 #endif #if (PCRE_MAJOR >= 6) # define LUA_HAVE_PCRE_DFA 1 #else # define LUA_HAVE_PCRE_DFA 0 #endif #define NGX_LUA_RE_COMPILE_ONCE (1<<0) #define NGX_LUA_RE_MODE_DFA (1<<1) #define NGX_LUA_RE_MODE_JIT (1<<2) #define NGX_LUA_RE_MODE_DUPNAMES (1<<3) #define NGX_LUA_RE_NO_UTF8_CHECK (1<<4) #define NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT (100) typedef struct { #ifndef NGX_HTTP_LUA_NO_FFI_API ngx_pool_t *pool; u_char *name_table; int name_count; int name_entry_size; #endif int ncaptures; int *captures; pcre *regex; pcre_extra *regex_sd; ngx_http_lua_complex_value_t *replace; } ngx_http_lua_regex_t; typedef struct { ngx_str_t pattern; ngx_pool_t *pool; ngx_int_t options; pcre *regex; int captures; ngx_str_t err; } ngx_http_lua_regex_compile_t; typedef struct { ngx_http_cleanup_pt *cleanup; ngx_http_request_t *request; pcre *regex; pcre_extra *regex_sd; int ncaptures; int *captures; int captures_len; uint8_t flags; } ngx_http_lua_regex_ctx_t; static int ngx_http_lua_ngx_re_gmatch_iterator(lua_State *L); static ngx_uint_t ngx_http_lua_ngx_re_parse_opts(lua_State *L, ngx_http_lua_regex_compile_t *re, ngx_str_t *opts, int narg); static int ngx_http_lua_ngx_re_sub_helper(lua_State *L, unsigned global); static int ngx_http_lua_ngx_re_match_helper(lua_State *L, int wantcaps); static int ngx_http_lua_ngx_re_find(lua_State *L); static int ngx_http_lua_ngx_re_match(lua_State *L); static int ngx_http_lua_ngx_re_gmatch(lua_State *L); static int ngx_http_lua_ngx_re_sub(lua_State *L); static int ngx_http_lua_ngx_re_gsub(lua_State *L); static void ngx_http_lua_regex_free_study_data(ngx_pool_t *pool, pcre_extra *sd); static ngx_int_t ngx_http_lua_regex_compile(ngx_http_lua_regex_compile_t *rc); static void ngx_http_lua_ngx_re_gmatch_cleanup(void *data); static int ngx_http_lua_ngx_re_gmatch_gc(lua_State *L); static void ngx_http_lua_re_collect_named_captures(lua_State *L, int res_tb_idx, u_char *name_table, int name_count, int name_entry_size, unsigned flags, ngx_str_t *subj); #define ngx_http_lua_regex_exec(re, e, s, start, captures, size, opts) \ pcre_exec(re, e, (const char *) (s)->data, (s)->len, start, opts, \ captures, size) #define ngx_http_lua_regex_dfa_exec(re, e, s, start, captures, size, ws, \ wscount, opts) \ pcre_dfa_exec(re, e, (const char *) (s)->data, (s)->len, start, opts, \ captures, size, ws, wscount) static int ngx_http_lua_ngx_re_match(lua_State *L) { return ngx_http_lua_ngx_re_match_helper(L, 1 /* want captures */); } static int ngx_http_lua_ngx_re_find(lua_State *L) { return ngx_http_lua_ngx_re_match_helper(L, 0 /* want captures */); } static int ngx_http_lua_ngx_re_match_helper(lua_State *L, int wantcaps) { /* u_char *p; */ int res_tb_idx = 0; ngx_http_request_t *r; ngx_str_t subj; ngx_str_t pat; ngx_str_t opts; ngx_http_lua_regex_t *re; const char *msg; ngx_int_t rc; ngx_uint_t n; int i; ngx_int_t pos = 0; int nargs; int *cap = NULL; int ovecsize; ngx_uint_t flags; ngx_pool_t *pool, *old_pool; ngx_http_lua_main_conf_t *lmcf; u_char errstr[NGX_MAX_CONF_ERRSTR + 1]; pcre_extra *sd = NULL; int name_entry_size = 0, name_count; u_char *name_table = NULL; int exec_opts; int group_id = 0; ngx_http_lua_regex_compile_t re_comp; nargs = lua_gettop(L); if (nargs != 2 && nargs != 3 && nargs != 4 && nargs != 5) { return luaL_error(L, "expecting 2, 3, 4 or 5 arguments, " "but got %d", nargs); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } subj.data = (u_char *) luaL_checklstring(L, 1, &subj.len); pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len); ngx_memzero(&re_comp, sizeof(ngx_http_lua_regex_compile_t)); if (nargs >= 3) { opts.data = (u_char *) luaL_checklstring(L, 3, &opts.len); if (nargs == 4) { luaL_checktype(L, 4, LUA_TTABLE); lua_getfield(L, 4, "pos"); if (lua_isnumber(L, -1)) { pos = (ngx_int_t) lua_tointeger(L, -1); if (pos <= 0) { pos = 0; } else { pos--; /* 1-based on the Lua land */ } } else if (lua_isnil(L, -1)) { pos = 0; } else { msg = lua_pushfstring(L, "bad pos field type in the ctx table " "argument: %s", luaL_typename(L, -1)); return luaL_argerror(L, 4, msg); } lua_pop(L, 1); } } else { opts.data = (u_char *) ""; opts.len = 0; } if (nargs == 5) { if (wantcaps) { luaL_checktype(L, 5, LUA_TTABLE); res_tb_idx = 5; /* clear the Lua table */ lua_pushnil(L); while (lua_next(L, res_tb_idx) != 0) { lua_pop(L, 1); lua_pushvalue(L, -1); lua_pushnil(L); lua_rawset(L, res_tb_idx); } } else { group_id = luaL_checkint(L, 5); if (group_id < 0) { group_id = 0; } } } re_comp.options = 0; flags = ngx_http_lua_ngx_re_parse_opts(L, &re_comp, &opts, 3); lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); if (flags & NGX_LUA_RE_COMPILE_ONCE) { pool = lmcf->pool; dd("server pool %p", lmcf->pool); lua_pushlightuserdata(L, &ngx_http_lua_regex_cache_key); lua_rawget(L, LUA_REGISTRYINDEX); /* table */ lua_pushliteral(L, "m"); lua_pushvalue(L, 2); /* table regex */ dd("options size: %d", (int) sizeof(re_comp.options)); lua_pushlstring(L, (char *) &re_comp.options, sizeof(re_comp.options)); /* table regex opts */ lua_concat(L, 3); /* table key */ lua_pushvalue(L, -1); /* table key key */ dd("regex cache key: %.*s", (int) (pat.len + sizeof(re_comp.options)), lua_tostring(L, -1)); lua_rawget(L, -3); /* table key re */ re = lua_touserdata(L, -1); lua_pop(L, 1); /* table key */ if (re) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua regex cache hit for match regex \"%s\" with " "options \"%s\"", pat.data, opts.data); lua_pop(L, 2); dd("restoring regex %p, ncaptures %d, captures %p", re->regex, re->ncaptures, re->captures); re_comp.regex = re->regex; sd = re->regex_sd; re_comp.captures = re->ncaptures; cap = re->captures; if (flags & NGX_LUA_RE_MODE_DFA) { ovecsize = 2; } else { ovecsize = (re->ncaptures + 1) * 3; } goto exec; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua regex cache miss for match regex \"%s\" " "with options \"%s\"", pat.data, opts.data); if (lmcf->regex_cache_entries >= lmcf->regex_cache_max_entries) { if (lmcf->regex_cache_entries == lmcf->regex_cache_max_entries) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "lua exceeding regex cache max entries (%i)", lmcf->regex_cache_max_entries); lmcf->regex_cache_entries++; } pool = r->pool; flags &= ~NGX_LUA_RE_COMPILE_ONCE; } } else { pool = r->pool; } dd("pool %p, r pool %p", pool, r->pool); re_comp.pattern = pat; re_comp.err.len = NGX_MAX_CONF_ERRSTR; re_comp.err.data = errstr; re_comp.pool = pool; ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua compiling match regex \"%s\" with options \"%s\" " "(compile once: %d) (dfa mode: %d) (jit mode: %d)", pat.data, opts.data, (flags & NGX_LUA_RE_COMPILE_ONCE) != 0, (flags & NGX_LUA_RE_MODE_DFA) != 0, (flags & NGX_LUA_RE_MODE_JIT) != 0); old_pool = ngx_http_lua_pcre_malloc_init(pool); rc = ngx_http_lua_regex_compile(&re_comp); ngx_http_lua_pcre_malloc_done(old_pool); if (rc != NGX_OK) { dd("compile failed"); lua_pushnil(L); if (!wantcaps) { lua_pushnil(L); } lua_pushlstring(L, (char *) re_comp.err.data, re_comp.err.len); return wantcaps ? 2 : 3; } #if (LUA_HAVE_PCRE_JIT) if (flags & NGX_LUA_RE_MODE_JIT) { old_pool = ngx_http_lua_pcre_malloc_init(pool); sd = pcre_study(re_comp.regex, PCRE_STUDY_JIT_COMPILE, &msg); ngx_http_lua_pcre_malloc_done(old_pool); # if (NGX_DEBUG) dd("sd = %p", sd); if (msg != NULL) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "pcre study failed with PCRE_STUDY_JIT_COMPILE: " "%s (%p)", msg, sd); } if (sd != NULL) { int jitted; old_pool = ngx_http_lua_pcre_malloc_init(pool); pcre_fullinfo(re_comp.regex, sd, PCRE_INFO_JIT, &jitted); ngx_http_lua_pcre_malloc_done(old_pool); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "pcre JIT compiling result: %d", jitted); } # endif /* !(NGX_DEBUG) */ } else { old_pool = ngx_http_lua_pcre_malloc_init(pool); sd = pcre_study(re_comp.regex, 0, &msg); ngx_http_lua_pcre_malloc_done(old_pool); # if (NGX_DEBUG) dd("sd = %p", sd); if (msg != NULL) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "pcre_study failed with PCRE_STUDY_JIT_COMPILE: " "%s (%p)", msg, sd); } # endif /* NGX_DEBUG */ } #else /* !(LUA_HAVE_PCRE_JIT) */ if (flags & NGX_LUA_RE_MODE_JIT) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "your pcre build does not have JIT support and " "the \"j\" regex option is ignored"); } #endif /* LUA_HAVE_PCRE_JIT */ if (sd && lmcf->regex_match_limit > 0) { sd->flags |= PCRE_EXTRA_MATCH_LIMIT; sd->match_limit = lmcf->regex_match_limit; } dd("compile done, captures %d", (int) re_comp.captures); if (flags & NGX_LUA_RE_MODE_DFA) { ovecsize = 2; } else { ovecsize = (re_comp.captures + 1) * 3; } dd("allocating cap with size: %d", (int) ovecsize); cap = ngx_palloc(pool, ovecsize * sizeof(int)); if (cap == NULL) { flags &= ~NGX_LUA_RE_COMPILE_ONCE; msg = "no memory"; goto error; } if (flags & NGX_LUA_RE_COMPILE_ONCE) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua saving compiled regex (%d captures) into the cache " "(entries %i)", re_comp.captures, lmcf->regex_cache_entries); re = ngx_palloc(pool, sizeof(ngx_http_lua_regex_t)); if (re == NULL) { msg = "no memory"; goto error; } dd("saving regex %p, ncaptures %d, captures %p", re_comp.regex, re_comp.captures, cap); re->regex = re_comp.regex; re->regex_sd = sd; re->ncaptures = re_comp.captures; re->captures = cap; re->replace = NULL; lua_pushlightuserdata(L, re); /* table key value */ lua_rawset(L, -3); /* table */ lua_pop(L, 1); if (lmcf) { lmcf->regex_cache_entries++; } } exec: if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMECOUNT, &name_count) != 0) { msg = "cannot acquire named subpattern count"; goto error; } if (name_count > 0) { if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMEENTRYSIZE, &name_entry_size) != 0) { msg = "cannot acquire named subpattern entry size"; goto error; } if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMETABLE, &name_table) != 0) { msg = "cannot acquire named subpattern table"; goto error; } } if (flags & NGX_LUA_RE_NO_UTF8_CHECK) { exec_opts = PCRE_NO_UTF8_CHECK; } else { exec_opts = 0; } if (flags & NGX_LUA_RE_MODE_DFA) { #if LUA_HAVE_PCRE_DFA int ws[NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT]; rc = ngx_http_lua_regex_dfa_exec(re_comp.regex, sd, &subj, (int) pos, cap, ovecsize, ws, sizeof(ws)/sizeof(ws[0]), exec_opts); #else /* LUA_HAVE_PCRE_DFA */ msg = "at least pcre 6.0 is required for the DFA mode"; goto error; #endif /* LUA_HAVE_PCRE_DFA */ } else { rc = ngx_http_lua_regex_exec(re_comp.regex, sd, &subj, (int) pos, cap, ovecsize, exec_opts); } if (rc == NGX_REGEX_NO_MATCHED) { ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "regex \"%V\" not matched on string \"%V\" starting " "from %i", &pat, &subj, pos); if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) { if (sd) { ngx_http_lua_regex_free_study_data(pool, sd); } ngx_pfree(pool, re_comp.regex); ngx_pfree(pool, cap); } lua_pushnil(L); return 1; } if (rc < 0) { msg = lua_pushfstring(L, ngx_regex_exec_n " failed: %d", (int) rc); goto error; } if (rc == 0) { if (flags & NGX_LUA_RE_MODE_DFA) { rc = 1; } else { msg = "capture size too small"; goto error; } } dd("rc = %d", (int) rc); if (nargs == 4) { /* having ctx table */ pos = cap[1]; lua_pushinteger(L, (lua_Integer) (pos + 1)); lua_setfield(L, 4, "pos"); } if (!wantcaps) { if (group_id > re_comp.captures) { lua_pushnil(L); lua_pushnil(L); lua_pushliteral(L, "nth out of bound"); return 3; } if (group_id >= rc) { lua_pushnil(L); lua_pushnil(L); return 2; } { int from, to; from = cap[group_id * 2] + 1; to = cap[group_id * 2 + 1]; if (from < 0 || to < 0) { lua_pushnil(L); lua_pushnil(L); return 2; } lua_pushinteger(L, from); lua_pushinteger(L, to); return 2; } } if (res_tb_idx == 0) { lua_createtable(L, rc /* narr */, 0 /* nrec */); res_tb_idx = lua_gettop(L); } for (i = 0, n = 0; i < rc; i++, n += 2) { dd("capture %d: %d %d", i, cap[n], cap[n + 1]); if (cap[n] < 0) { lua_pushnil(L); } else { lua_pushlstring(L, (char *) &subj.data[cap[n]], cap[n + 1] - cap[n]); dd("pushing capture %s at %d", lua_tostring(L, -1), (int) i); } lua_rawseti(L, res_tb_idx, (int) i); } if (name_count > 0) { ngx_http_lua_re_collect_named_captures(L, res_tb_idx, name_table, name_count, name_entry_size, flags, &subj); } if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) { if (sd) { ngx_http_lua_regex_free_study_data(pool, sd); } ngx_pfree(pool, re_comp.regex); ngx_pfree(pool, cap); } return 1; error: if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) { if (sd) { ngx_http_lua_regex_free_study_data(pool, sd); } if (re_comp.regex) { ngx_pfree(pool, re_comp.regex); } if (cap) { ngx_pfree(pool, cap); } } lua_pushnil(L); if (!wantcaps) { lua_pushnil(L); } lua_pushstring(L, msg); return wantcaps ? 2 : 3; } static int ngx_http_lua_ngx_re_gmatch(lua_State *L) { ngx_http_lua_main_conf_t *lmcf; ngx_http_request_t *r; ngx_str_t subj; ngx_str_t pat; ngx_str_t opts; int ovecsize; ngx_http_lua_regex_t *re; ngx_http_lua_regex_ctx_t *ctx; const char *msg; int nargs; ngx_int_t flags; int *cap = NULL; ngx_int_t rc; ngx_pool_t *pool, *old_pool; u_char errstr[NGX_MAX_CONF_ERRSTR + 1]; pcre_extra *sd = NULL; ngx_http_cleanup_t *cln; ngx_http_lua_regex_compile_t re_comp; nargs = lua_gettop(L); if (nargs != 2 && nargs != 3) { return luaL_error(L, "expecting two or three arguments, but got %d", nargs); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } subj.data = (u_char *) luaL_checklstring(L, 1, &subj.len); pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len); if (nargs == 3) { opts.data = (u_char *) luaL_checklstring(L, 3, &opts.len); lua_pop(L, 1); } else { opts.data = (u_char *) ""; opts.len = 0; } /* stack: subj regex */ re_comp.options = 0; flags = ngx_http_lua_ngx_re_parse_opts(L, &re_comp, &opts, 3); lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); if (flags & NGX_LUA_RE_COMPILE_ONCE) { pool = lmcf->pool; dd("server pool %p", lmcf->pool); lua_pushlightuserdata(L, &ngx_http_lua_regex_cache_key); lua_rawget(L, LUA_REGISTRYINDEX); /* table */ lua_pushliteral(L, "m"); lua_pushvalue(L, 2); /* table regex */ dd("options size: %d", (int) sizeof(re_comp.options)); lua_pushlstring(L, (char *) &re_comp.options, sizeof(re_comp.options)); /* table regex opts */ lua_concat(L, 3); /* table key */ lua_pushvalue(L, -1); /* table key key */ dd("regex cache key: %.*s", (int) (pat.len + sizeof(re_comp.options)), lua_tostring(L, -1)); lua_rawget(L, -3); /* table key re */ re = lua_touserdata(L, -1); lua_pop(L, 1); /* table key */ if (re) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua regex cache hit for match regex \"%s\" " "with options \"%s\"", pat.data, opts.data); lua_pop(L, 2); dd("restoring regex %p, ncaptures %d, captures %p", re->regex, re->ncaptures, re->captures); re_comp.regex = re->regex; sd = re->regex_sd; re_comp.captures = re->ncaptures; cap = re->captures; if (flags & NGX_LUA_RE_MODE_DFA) { ovecsize = 2; } else { ovecsize = (re->ncaptures + 1) * 3; } goto compiled; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua regex cache miss for match regex \"%s\" " "with options \"%s\"", pat.data, opts.data); if (lmcf->regex_cache_entries >= lmcf->regex_cache_max_entries) { if (lmcf->regex_cache_entries == lmcf->regex_cache_max_entries) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "lua exceeding regex cache max entries (%i)", lmcf->regex_cache_max_entries); lmcf->regex_cache_entries++; } pool = r->pool; flags &= ~NGX_LUA_RE_COMPILE_ONCE; } } else { pool = r->pool; } re_comp.pattern = pat; re_comp.err.len = NGX_MAX_CONF_ERRSTR; re_comp.err.data = errstr; re_comp.pool = pool; ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua compiling gmatch regex \"%s\" with options \"%s\" " "(compile once: %d) (dfa mode: %d) (jit mode: %d)", pat.data, opts.data, (flags & NGX_LUA_RE_COMPILE_ONCE) != 0, (flags & NGX_LUA_RE_MODE_DFA) != 0, (flags & NGX_LUA_RE_MODE_JIT) != 0); old_pool = ngx_http_lua_pcre_malloc_init(pool); rc = ngx_http_lua_regex_compile(&re_comp); ngx_http_lua_pcre_malloc_done(old_pool); if (rc != NGX_OK) { dd("compile failed"); lua_pushnil(L); lua_pushlstring(L, (char *) re_comp.err.data, re_comp.err.len); return 2; } #if LUA_HAVE_PCRE_JIT if (flags & NGX_LUA_RE_MODE_JIT) { old_pool = ngx_http_lua_pcre_malloc_init(pool); sd = pcre_study(re_comp.regex, PCRE_STUDY_JIT_COMPILE, &msg); ngx_http_lua_pcre_malloc_done(old_pool); # if (NGX_DEBUG) dd("sd = %p", sd); if (msg != NULL) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "pcre_study failed with PCRE_STUDY_JIT_COMPILE: " "%s (%p)", msg, sd); } if (sd != NULL) { int jitted; old_pool = ngx_http_lua_pcre_malloc_init(pool); pcre_fullinfo(re_comp.regex, sd, PCRE_INFO_JIT, &jitted); ngx_http_lua_pcre_malloc_done(old_pool); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "pcre JIT compiling result: %d", jitted); } # endif /* NGX_DEBUG */ } else { old_pool = ngx_http_lua_pcre_malloc_init(pool); sd = pcre_study(re_comp.regex, 0, &msg); ngx_http_lua_pcre_malloc_done(old_pool); # if (NGX_DEBUG) dd("sd = %p", sd); if (msg != NULL) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "pcre study failed with PCRE_STUDY_JIT_COMPILE: " "%s (%p)", msg, sd); } # endif /* NGX_DEBUG */ } #else /* LUA_HAVE_PCRE_JIT */ if (flags & NGX_LUA_RE_MODE_JIT) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "your pcre build does not have JIT support and " "the \"j\" regex option is ignored"); } #endif /* LUA_HAVE_PCRE_JIT */ if (sd && lmcf->regex_match_limit > 0) { sd->flags |= PCRE_EXTRA_MATCH_LIMIT; sd->match_limit = lmcf->regex_match_limit; } dd("compile done, captures %d", re_comp.captures); if (flags & NGX_LUA_RE_MODE_DFA) { ovecsize = 2; } else { ovecsize = (re_comp.captures + 1) * 3; } cap = ngx_palloc(pool, ovecsize * sizeof(int)); if (cap == NULL) { flags &= ~NGX_LUA_RE_COMPILE_ONCE; msg = "no memory"; goto error; } if (flags & NGX_LUA_RE_COMPILE_ONCE) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua saving compiled regex (%d captures) into the cache " "(entries %i)", re_comp.captures, lmcf->regex_cache_entries); re = ngx_palloc(pool, sizeof(ngx_http_lua_regex_t)); if (re == NULL) { msg = "no memory"; goto error; } dd("saving regex %p, ncaptures %d, captures %p", re_comp.regex, re_comp.captures, cap); re->regex = re_comp.regex; re->regex_sd = sd; re->ncaptures = re_comp.captures; re->captures = cap; re->replace = NULL; lua_pushlightuserdata(L, re); /* table key value */ lua_rawset(L, -3); /* table */ lua_pop(L, 1); if (lmcf) { lmcf->regex_cache_entries++; } } compiled: lua_settop(L, 1); ctx = lua_newuserdata(L, sizeof(ngx_http_lua_regex_ctx_t)); ctx->request = r; ctx->regex = re_comp.regex; ctx->regex_sd = sd; ctx->ncaptures = re_comp.captures; ctx->captures = cap; ctx->captures_len = ovecsize; ctx->flags = (uint8_t) flags; if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) { lua_createtable(L, 0 /* narr */, 1 /* nrec */); /* metatable */ lua_pushcfunction(L, ngx_http_lua_ngx_re_gmatch_gc); lua_setfield(L, -2, "__gc"); lua_setmetatable(L, -2); cln = ngx_http_cleanup_add(r, 0); if (cln == NULL) { msg = "no memory"; goto error; } cln->handler = ngx_http_lua_ngx_re_gmatch_cleanup; cln->data = ctx; ctx->cleanup = &cln->handler; } else { ctx->cleanup = NULL; } lua_pushinteger(L, 0); /* upvalues in order: subj ctx offset */ lua_pushcclosure(L, ngx_http_lua_ngx_re_gmatch_iterator, 3); return 1; error: if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) { if (sd) { ngx_http_lua_regex_free_study_data(pool, sd); } if (re_comp.regex) { ngx_pfree(pool, re_comp.regex); } if (cap) { ngx_pfree(pool, cap); } } lua_pushnil(L); lua_pushstring(L, msg); return 2; } static int ngx_http_lua_ngx_re_gmatch_iterator(lua_State *L) { ngx_http_lua_regex_ctx_t *ctx; ngx_http_request_t *r; int *cap; ngx_int_t rc; ngx_uint_t n; int i; ngx_str_t subj; int offset; const char *msg = NULL; int name_entry_size = 0, name_count; u_char *name_table = NULL; int exec_opts; /* upvalues in order: subj ctx offset */ subj.data = (u_char *) lua_tolstring(L, lua_upvalueindex(1), &subj.len); ctx = (ngx_http_lua_regex_ctx_t *) lua_touserdata(L, lua_upvalueindex(2)); offset = (int) lua_tointeger(L, lua_upvalueindex(3)); if (offset < 0) { lua_pushnil(L); return 1; } cap = ctx->captures; dd("offset %d, r %p, subj %s", (int) offset, ctx->request, subj.data); r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } if (r != ctx->request || r->pool != ctx->request->pool) { return luaL_error(L, "attempt to use ngx.re.gmatch iterator in a " "request that did not create it"); } dd("regex exec..."); if (pcre_fullinfo(ctx->regex, NULL, PCRE_INFO_NAMECOUNT, &name_count) != 0) { msg = "cannot acquire named subpattern count"; goto error; } if (name_count > 0) { if (pcre_fullinfo(ctx->regex, NULL, PCRE_INFO_NAMEENTRYSIZE, &name_entry_size) != 0) { msg = "cannot acquire named subpattern entry size"; goto error; } if (pcre_fullinfo(ctx->regex, NULL, PCRE_INFO_NAMETABLE, &name_table) != 0) { msg = "cannot acquire named subpattern table"; goto error; } } if (ctx->flags & NGX_LUA_RE_NO_UTF8_CHECK) { exec_opts = PCRE_NO_UTF8_CHECK; } else { exec_opts = 0; } if (ctx->flags & NGX_LUA_RE_MODE_DFA) { #if LUA_HAVE_PCRE_DFA int ws[NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT]; rc = ngx_http_lua_regex_dfa_exec(ctx->regex, ctx->regex_sd, &subj, offset, cap, ctx->captures_len, ws, sizeof(ws)/sizeof(ws[0]), exec_opts); #else /* LUA_HAVE_PCRE_DFA */ msg = "at least pcre 6.0 is required for the DFA mode"; goto error; #endif /* LUA_HAVE_PCRE_DFA */ } else { rc = ngx_http_lua_regex_exec(ctx->regex, ctx->regex_sd, &subj, offset, cap, ctx->captures_len, exec_opts); } if (rc == NGX_REGEX_NO_MATCHED) { /* set upvalue "offset" to -1 */ lua_pushinteger(L, -1); lua_replace(L, lua_upvalueindex(3)); if (!(ctx->flags & NGX_LUA_RE_COMPILE_ONCE)) { if (ctx->regex_sd) { ngx_http_lua_regex_free_study_data(r->pool, ctx->regex_sd); ctx->regex_sd = NULL; } ngx_pfree(r->pool, cap); } lua_pushnil(L); return 1; } if (rc < 0) { msg = lua_pushfstring(L, ngx_regex_exec_n " failed: %d", (int) rc); goto error; } if (rc == 0) { if (ctx->flags & NGX_LUA_RE_MODE_DFA) { rc = 1; } else { goto error; } } dd("rc = %d", (int) rc); lua_createtable(L, rc /* narr */, 0 /* nrec */); for (i = 0, n = 0; i < rc; i++, n += 2) { dd("capture %d: %d %d", i, cap[n], cap[n + 1]); if (cap[n] < 0) { lua_pushnil(L); } else { lua_pushlstring(L, (char *) &subj.data[cap[n]], cap[n + 1] - cap[n]); dd("pushing capture %s at %d", lua_tostring(L, -1), (int) i); } lua_rawseti(L, -2, (int) i); } if (name_count > 0) { ngx_http_lua_re_collect_named_captures(L, lua_gettop(L), name_table, name_count, name_entry_size, ctx->flags, &subj); } offset = cap[1]; if (offset == cap[0]) { offset++; } if (offset > (ssize_t) subj.len) { offset = -1; if (!(ctx->flags & NGX_LUA_RE_COMPILE_ONCE)) { if (ctx->regex_sd) { ngx_http_lua_regex_free_study_data(r->pool, ctx->regex_sd); ctx->regex_sd = NULL; } ngx_pfree(r->pool, cap); } } lua_pushinteger(L, offset); lua_replace(L, lua_upvalueindex(3)); return 1; error: lua_pushinteger(L, -1); lua_replace(L, lua_upvalueindex(3)); if (!(ctx->flags & NGX_LUA_RE_COMPILE_ONCE)) { if (ctx->regex_sd) { ngx_http_lua_regex_free_study_data(r->pool, ctx->regex_sd); ctx->regex_sd = NULL; } ngx_pfree(r->pool, cap); } lua_pushnil(L); lua_pushstring(L, msg); return 2; } static ngx_uint_t ngx_http_lua_ngx_re_parse_opts(lua_State *L, ngx_http_lua_regex_compile_t *re, ngx_str_t *opts, int narg) { u_char *p; const char *msg; ngx_uint_t flags; flags = 0; p = opts->data; while (*p != '\0') { switch (*p) { case 'i': re->options |= NGX_REGEX_CASELESS; break; case 's': re->options |= PCRE_DOTALL; break; case 'm': re->options |= PCRE_MULTILINE; break; case 'u': re->options |= PCRE_UTF8; break; case 'U': re->options |= PCRE_UTF8; flags |= NGX_LUA_RE_NO_UTF8_CHECK; break; case 'x': re->options |= PCRE_EXTENDED; break; case 'o': flags |= NGX_LUA_RE_COMPILE_ONCE; break; case 'j': flags |= NGX_LUA_RE_MODE_JIT; break; case 'd': flags |= NGX_LUA_RE_MODE_DFA; break; case 'a': re->options |= PCRE_ANCHORED; break; #if (PCRE_MAJOR > 8) || (PCRE_MAJOR == 8 && PCRE_MINOR >= 12) case 'D': re->options |= PCRE_DUPNAMES; flags |= NGX_LUA_RE_MODE_DUPNAMES; break; case 'J': re->options |= PCRE_JAVASCRIPT_COMPAT; break; #endif default: msg = lua_pushfstring(L, "unknown flag \"%c\" (flags \"%s\")", *p, opts->data); return luaL_argerror(L, narg, msg); } p++; } /* pcre does not support JIT for DFA mode yet, * so if DFA mode is specified, we turn off JIT automatically * */ if ((flags & NGX_LUA_RE_MODE_JIT) && (flags & NGX_LUA_RE_MODE_DFA)) { flags &= ~NGX_LUA_RE_MODE_JIT; } return flags; } static int ngx_http_lua_ngx_re_sub(lua_State *L) { return ngx_http_lua_ngx_re_sub_helper(L, 0 /* global */); } static int ngx_http_lua_ngx_re_gsub(lua_State *L) { return ngx_http_lua_ngx_re_sub_helper(L, 1 /* global */); } static int ngx_http_lua_ngx_re_sub_helper(lua_State *L, unsigned global) { ngx_http_lua_regex_t *re; ngx_http_request_t *r; ngx_str_t subj; ngx_str_t pat; ngx_str_t opts; ngx_str_t tpl; ngx_http_lua_main_conf_t *lmcf; ngx_pool_t *pool, *old_pool; const char *msg; ngx_int_t rc; ngx_uint_t n; ngx_int_t i; int nargs; int *cap = NULL; int ovecsize; int type; unsigned func; int offset; int cp_offset; size_t count; luaL_Buffer luabuf; ngx_int_t flags; u_char *p; u_char errstr[NGX_MAX_CONF_ERRSTR + 1]; pcre_extra *sd = NULL; int name_entry_size = 0, name_count; u_char *name_table = NULL; int exec_opts; ngx_http_lua_regex_compile_t re_comp; ngx_http_lua_complex_value_t *ctpl = NULL; ngx_http_lua_compile_complex_value_t ccv; nargs = lua_gettop(L); if (nargs != 3 && nargs != 4) { return luaL_error(L, "expecting three or four arguments, but got %d", nargs); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } subj.data = (u_char *) luaL_checklstring(L, 1, &subj.len); pat.data = (u_char *) luaL_checklstring(L, 2, &pat.len); func = 0; type = lua_type(L, 3); switch (type) { case LUA_TFUNCTION: func = 1; tpl.len = 0; tpl.data = (u_char *) ""; break; case LUA_TNUMBER: case LUA_TSTRING: tpl.data = (u_char *) lua_tolstring(L, 3, &tpl.len); break; default: msg = lua_pushfstring(L, "string, number, or function expected, " "got %s", lua_typename(L, type)); return luaL_argerror(L, 3, msg); } ngx_memzero(&re_comp, sizeof(ngx_http_lua_regex_compile_t)); if (nargs == 4) { opts.data = (u_char *) luaL_checklstring(L, 4, &opts.len); lua_pop(L, 1); } else { /* nargs == 3 */ opts.data = (u_char *) ""; opts.len = 0; } /* stack: subj regex repl */ re_comp.options = 0; flags = ngx_http_lua_ngx_re_parse_opts(L, &re_comp, &opts, 4); lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module); if (flags & NGX_LUA_RE_COMPILE_ONCE) { pool = lmcf->pool; dd("server pool %p", lmcf->pool); lua_pushlightuserdata(L, &ngx_http_lua_regex_cache_key); lua_rawget(L, LUA_REGISTRYINDEX); /* table */ lua_pushliteral(L, "s"); lua_pushinteger(L, tpl.len); lua_pushliteral(L, ":"); lua_pushvalue(L, 2); if (tpl.len != 0) { lua_pushvalue(L, 3); } dd("options size: %d", (int) sizeof(re_comp.options)); lua_pushlstring(L, (char *) &re_comp.options, sizeof(re_comp.options)); /* table regex opts */ if (tpl.len == 0) { lua_concat(L, 5); /* table key */ } else { lua_concat(L, 6); /* table key */ } lua_pushvalue(L, -1); /* table key key */ dd("regex cache key: %.*s", (int) (pat.len + sizeof(re_comp.options)), lua_tostring(L, -1)); lua_rawget(L, -3); /* table key re */ re = lua_touserdata(L, -1); lua_pop(L, 1); /* table key */ if (re) { ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua regex cache hit for sub regex \"%s\" with " "options \"%s\" and replace \"%s\"", pat.data, opts.data, func ? (u_char *) "" : tpl.data); lua_pop(L, 2); dd("restoring regex %p, ncaptures %d, captures %p", re->regex, re->ncaptures, re->captures); re_comp.regex = re->regex; sd = re->regex_sd; re_comp.captures = re->ncaptures; cap = re->captures; ctpl = re->replace; if (flags & NGX_LUA_RE_MODE_DFA) { ovecsize = 2; } else { ovecsize = (re->ncaptures + 1) * 3; } goto exec; } ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua regex cache miss for %ssub regex \"%s\" with " "options \"%s\" and replace \"%s\"", global ? "g" : "", pat.data, opts.data, func ? (u_char *) "" : tpl.data); if (lmcf->regex_cache_entries >= lmcf->regex_cache_max_entries) { if (lmcf->regex_cache_entries == lmcf->regex_cache_max_entries) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "lua exceeding regex cache max entries (%i)", lmcf->regex_cache_max_entries); lmcf->regex_cache_entries++; } pool = r->pool; flags &= ~NGX_LUA_RE_COMPILE_ONCE; } } else { pool = r->pool; } re_comp.pattern = pat; re_comp.err.len = NGX_MAX_CONF_ERRSTR; re_comp.err.data = errstr; re_comp.pool = pool; dd("compiling regex"); ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua compiling %ssub regex \"%s\" with options \"%s\" " "(compile once: %d) (dfa mode: %d) (jit mode: %d)", global ? "g" : "", pat.data, opts.data, (flags & NGX_LUA_RE_COMPILE_ONCE) != 0, (flags & NGX_LUA_RE_MODE_DFA) != 0, (flags & NGX_LUA_RE_MODE_JIT) != 0); old_pool = ngx_http_lua_pcre_malloc_init(pool); rc = ngx_http_lua_regex_compile(&re_comp); ngx_http_lua_pcre_malloc_done(old_pool); if (rc != NGX_OK) { dd("compile failed"); lua_pushnil(L); lua_pushnil(L); lua_pushlstring(L, (char *) re_comp.err.data, re_comp.err.len); return 3; } #if LUA_HAVE_PCRE_JIT if (flags & NGX_LUA_RE_MODE_JIT) { old_pool = ngx_http_lua_pcre_malloc_init(pool); sd = pcre_study(re_comp.regex, PCRE_STUDY_JIT_COMPILE, &msg); ngx_http_lua_pcre_malloc_done(old_pool); # if (NGX_DEBUG) dd("sd = %p", sd); if (msg != NULL) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "pcre study failed with PCRE_STUDY_JIT_COMPILE: " "%s (%p)", msg, sd); } if (sd != NULL) { int jitted; old_pool = ngx_http_lua_pcre_malloc_init(pool); pcre_fullinfo(re_comp.regex, sd, PCRE_INFO_JIT, &jitted); ngx_http_lua_pcre_malloc_done(old_pool); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "pcre JIT compiling result: %d", jitted); } # endif /* NGX_DEBUG */ } else { old_pool = ngx_http_lua_pcre_malloc_init(pool); sd = pcre_study(re_comp.regex, 0, &msg); ngx_http_lua_pcre_malloc_done(old_pool); # if (NGX_DEBUG) dd("sd = %p", sd); if (msg != NULL) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "pcre_study failed with PCRE_STUDY_JIT_COMPILE: " "%s (%p)", msg, sd); } # endif /* NGX_DEBUG */ } #else /* LUA_HAVE_PCRE_JIT */ if (flags & NGX_LUA_RE_MODE_JIT) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "your pcre build does not have JIT support and " "the \"j\" regex option is ignored"); } #endif /* LUA_HAVE_PCRE_JIT */ if (sd && lmcf->regex_match_limit > 0) { sd->flags |= PCRE_EXTRA_MATCH_LIMIT; sd->match_limit = lmcf->regex_match_limit; } dd("compile done, captures %d", re_comp.captures); if (flags & NGX_LUA_RE_MODE_DFA) { ovecsize = 2; } else { ovecsize = (re_comp.captures + 1) * 3; } cap = ngx_palloc(pool, ovecsize * sizeof(int)); if (cap == NULL) { flags &= ~NGX_LUA_RE_COMPILE_ONCE; msg = "no memory"; goto error; } if (func) { ctpl = NULL; } else { ctpl = ngx_palloc(pool, sizeof(ngx_http_lua_complex_value_t)); if (ctpl == NULL) { flags &= ~NGX_LUA_RE_COMPILE_ONCE; msg = "no memory"; goto error; } if ((flags & NGX_LUA_RE_COMPILE_ONCE) && tpl.len != 0) { /* copy the string buffer pointed to by tpl.data from Lua VM */ p = ngx_palloc(pool, tpl.len + 1); if (p == NULL) { flags &= ~NGX_LUA_RE_COMPILE_ONCE; msg = "no memory"; goto error; } ngx_memcpy(p, tpl.data, tpl.len); p[tpl.len] = '\0'; tpl.data = p; } ngx_memzero(&ccv, sizeof(ngx_http_lua_compile_complex_value_t)); ccv.pool = pool; ccv.log = r->connection->log; ccv.value = &tpl; ccv.complex_value = ctpl; if (ngx_http_lua_compile_complex_value(&ccv) != NGX_OK) { ngx_pfree(pool, cap); ngx_pfree(pool, ctpl); if ((flags & NGX_LUA_RE_COMPILE_ONCE) && tpl.len != 0) { ngx_pfree(pool, tpl.data); } if (sd) { ngx_http_lua_regex_free_study_data(pool, sd); } ngx_pfree(pool, re_comp.regex); lua_pushnil(L); lua_pushnil(L); lua_pushliteral(L, "failed to compile the replacement template"); return 3; } } if (flags & NGX_LUA_RE_COMPILE_ONCE) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua saving compiled sub regex (%d captures) into " "the cache (entries %i)", re_comp.captures, lmcf->regex_cache_entries); re = ngx_palloc(pool, sizeof(ngx_http_lua_regex_t)); if (re == NULL) { msg = "no memory"; goto error; } dd("saving regex %p, ncaptures %d, captures %p", re_comp.regex, re_comp.captures, cap); re->regex = re_comp.regex; re->regex_sd = sd; re->ncaptures = re_comp.captures; re->captures = cap; re->replace = ctpl; lua_pushlightuserdata(L, re); /* table key value */ lua_rawset(L, -3); /* table */ lua_pop(L, 1); if (lmcf) { lmcf->regex_cache_entries++; } } exec: count = 0; offset = 0; cp_offset = 0; if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMECOUNT, &name_count) != 0) { msg = "cannot acquire named subpattern count"; goto error; } if (name_count > 0) { if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMEENTRYSIZE, &name_entry_size) != 0) { msg = "cannot acquire named subpattern entry size"; goto error; } if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMETABLE, &name_table) != 0) { msg = "cannot acquire named subpattern table"; goto error; } } if (flags & NGX_LUA_RE_NO_UTF8_CHECK) { exec_opts = PCRE_NO_UTF8_CHECK; } else { exec_opts = 0; } for (;;) { if (flags & NGX_LUA_RE_MODE_DFA) { #if LUA_HAVE_PCRE_DFA int ws[NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT]; rc = ngx_http_lua_regex_dfa_exec(re_comp.regex, sd, &subj, offset, cap, ovecsize, ws, sizeof(ws)/sizeof(ws[0]), exec_opts); #else /* LUA_HAVE_PCRE_DFA */ msg = "at least pcre 6.0 is required for the DFA mode"; goto error; #endif /* LUA_HAVE_PCRE_DFA */ } else { rc = ngx_http_lua_regex_exec(re_comp.regex, sd, &subj, offset, cap, ovecsize, exec_opts); } if (rc == NGX_REGEX_NO_MATCHED) { break; } if (rc < 0) { msg = lua_pushfstring(L, ngx_regex_exec_n " failed: %d", (int) rc); goto error; } if (rc == 0) { if (flags & NGX_LUA_RE_MODE_DFA) { rc = 1; } else { msg = "capture size too small"; goto error; } } dd("rc = %d", (int) rc); count++; if (count == 1) { luaL_buffinit(L, &luabuf); } if (func) { lua_pushvalue(L, 3); lua_createtable(L, rc - 1 /* narr */, 1 /* nrec */); for (i = 0, n = 0; i < rc; i++, n += 2) { dd("capture %d: %d %d", (int) i, cap[n], cap[n + 1]); if (cap[n] < 0) { lua_pushnil(L); } else { lua_pushlstring(L, (char *) &subj.data[cap[n]], cap[n + 1] - cap[n]); dd("pushing capture %s at %d", lua_tostring(L, -1), (int) i); } lua_rawseti(L, -2, (int) i); } if (name_count > 0) { ngx_http_lua_re_collect_named_captures(L, lua_gettop(L), name_table, name_count, name_entry_size, flags, &subj); } dd("stack size at call: %d", lua_gettop(L)); lua_call(L, 1 /* nargs */, 1 /* nresults */); type = lua_type(L, -1); switch (type) { case LUA_TNUMBER: case LUA_TSTRING: tpl.data = (u_char *) lua_tolstring(L, -1, &tpl.len); break; default: msg = lua_pushfstring(L, "string or number expected to be " "returned by the replace " "function, got %s", lua_typename(L, type)); return luaL_argerror(L, 3, msg); } lua_insert(L, 1); luaL_addlstring(&luabuf, (char *) &subj.data[cp_offset], cap[0] - cp_offset); luaL_addlstring(&luabuf, (char *) tpl.data, tpl.len); lua_remove(L, 1); cp_offset = cap[1]; offset = cp_offset; if (offset == cap[0]) { offset++; if (offset > (ssize_t) subj.len) { break; } } if (global) { continue; } break; } rc = ngx_http_lua_complex_value(r, &subj, cp_offset, rc, cap, ctpl, &luabuf); if (rc != NGX_OK) { msg = lua_pushfstring(L, "failed to eval the template for " "replacement: \"%s\"", tpl.data); goto error; } cp_offset = cap[1]; offset = cp_offset; if (offset == cap[0]) { offset++; if (offset > (ssize_t) subj.len) { break; } } if (global) { continue; } break; } if (count == 0) { dd("no match, just the original subject"); lua_settop(L, 1); } else { if (offset < (int) subj.len) { dd("adding trailer: %s (len %d)", &subj.data[offset], (int) (subj.len - offset)); luaL_addlstring(&luabuf, (char *) &subj.data[offset], subj.len - offset); } luaL_pushresult(&luabuf); dd("the dst string: %s", lua_tostring(L, -1)); } if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) { if (sd) { ngx_http_lua_regex_free_study_data(pool, sd); } if (re_comp.regex) { ngx_pfree(pool, re_comp.regex); } if (ctpl) { ngx_pfree(pool, ctpl); } if (cap) { ngx_pfree(pool, cap); } } lua_pushinteger(L, count); return 2; error: if (!(flags & NGX_LUA_RE_COMPILE_ONCE)) { if (sd) { ngx_http_lua_regex_free_study_data(pool, sd); } if (re_comp.regex) { ngx_pfree(pool, re_comp.regex); } if (ctpl) { ngx_pfree(pool, ctpl); } if (cap) { ngx_pfree(pool, cap); } } lua_pushnil(L); lua_pushnil(L); lua_pushstring(L, msg); return 3; } void ngx_http_lua_inject_regex_api(lua_State *L) { /* ngx.re */ lua_createtable(L, 0, 5 /* nrec */); /* .re */ lua_pushcfunction(L, ngx_http_lua_ngx_re_find); lua_setfield(L, -2, "find"); lua_pushcfunction(L, ngx_http_lua_ngx_re_match); lua_setfield(L, -2, "match"); lua_pushcfunction(L, ngx_http_lua_ngx_re_gmatch); lua_setfield(L, -2, "gmatch"); lua_pushcfunction(L, ngx_http_lua_ngx_re_sub); lua_setfield(L, -2, "sub"); lua_pushcfunction(L, ngx_http_lua_ngx_re_gsub); lua_setfield(L, -2, "gsub"); lua_setfield(L, -2, "re"); } static void ngx_http_lua_regex_free_study_data(ngx_pool_t *pool, pcre_extra *sd) { ngx_pool_t *old_pool; old_pool = ngx_http_lua_pcre_malloc_init(pool); #if LUA_HAVE_PCRE_JIT pcre_free_study(sd); #else pcre_free(sd); #endif ngx_http_lua_pcre_malloc_done(old_pool); } static ngx_int_t ngx_http_lua_regex_compile(ngx_http_lua_regex_compile_t *rc) { int n, erroff; char *p; const char *errstr; pcre *re; ngx_pool_t *old_pool; old_pool = ngx_http_lua_pcre_malloc_init(rc->pool); re = pcre_compile((const char *) rc->pattern.data, (int) rc->options, &errstr, &erroff, NULL); ngx_http_lua_pcre_malloc_done(old_pool); if (re == NULL) { if ((size_t) erroff == rc->pattern.len) { rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, "pcre_compile() failed: %s in \"%V\"", errstr, &rc->pattern) - rc->err.data; } else { rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, "pcre_compile() failed: %s in \"%V\" " "at \"%s\"", errstr, &rc->pattern, rc->pattern.data + erroff) - rc->err.data; } return NGX_ERROR; } rc->regex = re; #if 1 n = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &rc->captures); if (n < 0) { p = "pcre_fullinfo(\"%V\", PCRE_INFO_CAPTURECOUNT) failed: %d"; goto failed; } #endif return NGX_OK; failed: rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, p, &rc->pattern, n) - rc->err.data; return NGX_OK; } static void ngx_http_lua_ngx_re_gmatch_cleanup(void *data) { ngx_http_lua_regex_ctx_t *ctx = data; if (ctx) { if (ctx->regex_sd) { ngx_http_lua_regex_free_study_data(ctx->request->pool, ctx->regex_sd); ctx->regex_sd = NULL; } if (ctx->cleanup) { *ctx->cleanup = NULL; ctx->cleanup = NULL; } ctx->request = NULL; } return; } static int ngx_http_lua_ngx_re_gmatch_gc(lua_State *L) { ngx_http_lua_regex_ctx_t *ctx; ctx = lua_touserdata(L, 1); if (ctx && ctx->cleanup) { ngx_http_lua_ngx_re_gmatch_cleanup(ctx); } return 0; } static void ngx_http_lua_re_collect_named_captures(lua_State *L, int res_tb_idx, u_char *name_table, int name_count, int name_entry_size, unsigned flags, ngx_str_t *subj) { int i, n; size_t len; u_char *name_entry; char *name; for (i = 0; i < name_count; i++) { dd("top: %d", lua_gettop(L)); name_entry = &name_table[i * name_entry_size]; n = (name_entry[0] << 8) | name_entry[1]; name = (char *) &name_entry[2]; lua_rawgeti(L, -1, n); if (lua_isnil(L, -1)) { lua_pop(L, 1); continue; } if (flags & NGX_LUA_RE_MODE_DUPNAMES) { lua_getfield(L, -2, name); /* big_tb cap small_tb */ if (lua_isnil(L, -1)) { lua_pop(L, 1); /* assuming named submatches are usually unique */ lua_createtable(L, 1 /* narr */, 0 /* nrec */); lua_pushstring(L, name); lua_pushvalue(L, -2); /* big_tb cap small_tb key small_tb */ lua_rawset(L, res_tb_idx); /* big_tb cap small_tb */ len = 0; } else { len = lua_objlen(L, -1); } lua_pushvalue(L, -2); /* big_tb cap small_tb cap */ lua_rawseti(L, -2, (int) len + 1); /* big_tb cap small_tb */ lua_pop(L, 2); } else { lua_pushstring(L, name); /* big_tb cap key */ lua_pushvalue(L, -2); /* big_tb cap key cap */ lua_rawset(L, res_tb_idx); /* big_tb cap */ lua_pop(L, 1); } dd("top 2: %d", lua_gettop(L)); } } #ifndef NGX_HTTP_LUA_NO_FFI_API ngx_http_lua_regex_t * ngx_http_lua_ffi_compile_regex(const unsigned char *pat, size_t pat_len, int flags, int pcre_opts, u_char *errstr, size_t errstr_size) { int *cap = NULL, ovecsize; u_char *p; ngx_int_t rc; const char *msg; ngx_pool_t *pool, *old_pool; pcre_extra *sd = NULL; ngx_http_lua_regex_t *re; ngx_http_lua_main_conf_t *lmcf; ngx_http_lua_regex_compile_t re_comp; pool = ngx_create_pool(512, ngx_cycle->log); if (pool == NULL) { msg = "no memory"; goto error; } re = ngx_palloc(pool, sizeof(ngx_http_lua_regex_t)); if (re == NULL) { ngx_destroy_pool(pool); pool = NULL; msg = "no memory"; goto error; } re->pool = pool; re_comp.options = pcre_opts; re_comp.pattern.data = (u_char *) pat; re_comp.pattern.len = pat_len; re_comp.err.len = errstr_size; re_comp.err.data = errstr; re_comp.pool = pool; old_pool = ngx_http_lua_pcre_malloc_init(pool); rc = ngx_http_lua_regex_compile(&re_comp); ngx_http_lua_pcre_malloc_done(old_pool); if (rc != NGX_OK) { re_comp.err.data[re_comp.err.len] = '\0'; msg = (char *) re_comp.err.data; goto error; } #if (LUA_HAVE_PCRE_JIT) if (flags & NGX_LUA_RE_MODE_JIT) { old_pool = ngx_http_lua_pcre_malloc_init(pool); sd = pcre_study(re_comp.regex, PCRE_STUDY_JIT_COMPILE, &msg); ngx_http_lua_pcre_malloc_done(old_pool); # if (NGX_DEBUG) if (msg != NULL) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "pcre study failed with PCRE_STUDY_JIT_COMPILE: " "%s (%p)", msg, sd); } if (sd != NULL) { int jitted; old_pool = ngx_http_lua_pcre_malloc_init(pool); pcre_fullinfo(re_comp.regex, sd, PCRE_INFO_JIT, &jitted); ngx_http_lua_pcre_malloc_done(old_pool); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "pcre JIT compiling result: %d", jitted); } # endif /* !(NGX_DEBUG) */ } else { old_pool = ngx_http_lua_pcre_malloc_init(pool); sd = pcre_study(re_comp.regex, 0, &msg); ngx_http_lua_pcre_malloc_done(old_pool); } #endif /* LUA_HAVE_PCRE_JIT */ lmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, ngx_http_lua_module); if (sd && lmcf && lmcf->regex_match_limit > 0) { sd->flags |= PCRE_EXTRA_MATCH_LIMIT; sd->match_limit = lmcf->regex_match_limit; } if (flags & NGX_LUA_RE_MODE_DFA) { ovecsize = 2; } else { ovecsize = (re_comp.captures + 1) * 3; } dd("allocating cap with size: %d", (int) ovecsize); cap = ngx_palloc(pool, ovecsize * sizeof(int)); if (cap == NULL) { msg = "no memory"; goto error; } if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMECOUNT, &re->name_count) != 0) { msg = "cannot acquire named subpattern count"; goto error; } if (re->name_count > 0) { if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMEENTRYSIZE, &re->name_entry_size) != 0) { msg = "cannot acquire named subpattern entry size"; goto error; } if (pcre_fullinfo(re_comp.regex, NULL, PCRE_INFO_NAMETABLE, &re->name_table) != 0) { msg = "cannot acquire named subpattern table"; goto error; } } re->regex = re_comp.regex; re->regex_sd = sd; re->ncaptures = re_comp.captures; re->captures = cap; re->replace = NULL; return re; error: p = ngx_snprintf(errstr, errstr_size - 1, "%s", msg); *p = '\0'; if (sd) { ngx_http_lua_regex_free_study_data(pool, sd); } if (pool) { ngx_destroy_pool(pool); } return NULL; } int ngx_http_lua_ffi_exec_regex(ngx_http_lua_regex_t *re, int flags, const u_char *s, size_t len, int pos) { int rc, ovecsize, exec_opts, *cap; ngx_str_t subj; pcre_extra *sd; cap = re->captures; sd = re->regex_sd; if (flags & NGX_LUA_RE_MODE_DFA) { ovecsize = 2; } else { ovecsize = (re->ncaptures + 1) * 3; } if (flags & NGX_LUA_RE_NO_UTF8_CHECK) { exec_opts = PCRE_NO_UTF8_CHECK; } else { exec_opts = 0; } subj.data = (u_char *) s; subj.len = len; if (flags & NGX_LUA_RE_MODE_DFA) { #if LUA_HAVE_PCRE_DFA int ws[NGX_LUA_RE_DFA_MODE_WORKSPACE_COUNT]; rc = ngx_http_lua_regex_dfa_exec(re->regex, sd, &subj, (int) pos, cap, ovecsize, ws, sizeof(ws)/sizeof(ws[0]), exec_opts); #else /* LUA_HAVE_PCRE_DFA */ return PCRE_ERROR_INTERNAL; #endif /* LUA_HAVE_PCRE_DFA */ } else { rc = ngx_http_lua_regex_exec(re->regex, sd, &subj, (int) pos, cap, ovecsize, exec_opts); } return rc; } void ngx_http_lua_ffi_destroy_regex(ngx_http_lua_regex_t *re) { dd("destroy regex called"); if (re == NULL || re->pool == NULL) { return; } if (re->regex_sd) { #if LUA_HAVE_PCRE_JIT pcre_free_study(re->regex_sd); #else pcre_free(re->regex_sd); #endif } ngx_destroy_pool(re->pool); } int ngx_http_lua_ffi_compile_replace_template(ngx_http_lua_regex_t *re, const u_char *replace_data, size_t replace_len) { ngx_int_t rc; ngx_str_t tpl; ngx_http_lua_complex_value_t *ctpl; ngx_http_lua_compile_complex_value_t ccv; ctpl = ngx_palloc(re->pool, sizeof(ngx_http_lua_complex_value_t)); if (ctpl == NULL) { return NGX_ERROR; } if (replace_len != 0) { /* copy the string buffer pointed to by tpl.data from Lua VM */ tpl.data = ngx_palloc(re->pool, replace_len + 1); if (tpl.data == NULL) { return NGX_ERROR; } ngx_memcpy(tpl.data, replace_data, replace_len); tpl.data[replace_len] = '\0'; } else { tpl.data = (u_char *) replace_data; } tpl.len = replace_len; ngx_memzero(&ccv, sizeof(ngx_http_lua_compile_complex_value_t)); ccv.pool = re->pool; ccv.log = ngx_cycle->log; ccv.value = &tpl; ccv.complex_value = ctpl; rc = ngx_http_lua_compile_complex_value(&ccv); re->replace = ctpl; return rc; } ngx_http_lua_script_engine_t * ngx_http_lua_ffi_create_script_engine(void) { return ngx_calloc(sizeof(ngx_http_lua_script_engine_t), ngx_cycle->log); } void ngx_http_lua_ffi_init_script_engine(ngx_http_lua_script_engine_t *e, const unsigned char *subj, ngx_http_lua_regex_t *compiled, int count) { e->log = ngx_cycle->log; e->ncaptures = count * 2; e->captures = compiled->captures; e->captures_data = (u_char *) subj; } void ngx_http_lua_ffi_destroy_script_engine(ngx_http_lua_script_engine_t *e) { ngx_free(e); } size_t ngx_http_lua_ffi_script_eval_len(ngx_http_lua_script_engine_t *e, ngx_http_lua_complex_value_t *val) { size_t len; ngx_http_lua_script_len_code_pt lcode; e->ip = val->lengths; len = 0; while (*(uintptr_t *) e->ip) { lcode = *(ngx_http_lua_script_len_code_pt *) e->ip; len += lcode(e); } return len; } void ngx_http_lua_ffi_script_eval_data(ngx_http_lua_script_engine_t *e, ngx_http_lua_complex_value_t *val, u_char *dst, size_t len) { ngx_http_lua_script_code_pt code; e->ip = val->values; e->pos = dst; while (*(uintptr_t *) e->ip) { code = *(ngx_http_lua_script_code_pt *) e->ip; code(e); } } uint32_t ngx_http_lua_ffi_max_regex_cache_size(void) { ngx_http_lua_main_conf_t *lmcf; lmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, ngx_http_lua_module); if (lmcf == NULL) { return 0; } return (uint32_t) lmcf->regex_cache_max_entries; } #endif /* NGX_HTTP_LUA_NO_FFI_API */ #endif /* NGX_PCRE */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_setby.h0000664000000000000000000000064612305451334017412 0ustar #ifndef _NGX_HTTP_LUA_SET_BY_H_INCLUDED_ #define _NGX_HTTP_LUA_SET_BY_H_INCLUDED_ #include "ngx_http_lua_common.h" ngx_int_t ngx_http_lua_set_by_chunk(lua_State *L, ngx_http_request_t *r, ngx_str_t *val, ngx_http_variable_value_t *args, size_t nargs, ngx_str_t *script); int ngx_http_lua_setby_param_get(lua_State *L); #endif /* _NGX_HTTP_LUA_SET_BY_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_directive.c0000664000000000000000000006055612305451335020244 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_common.h" #include "ngx_http_lua_directive.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_cache.h" #include "ngx_http_lua_contentby.h" #include "ngx_http_lua_accessby.h" #include "ngx_http_lua_rewriteby.h" #include "ngx_http_lua_logby.h" #include "ngx_http_lua_headerfilterby.h" #include "ngx_http_lua_bodyfilterby.h" #include "ngx_http_lua_initby.h" #include "ngx_http_lua_shdict.h" #if defined(NDK) && NDK #include "ngx_http_lua_setby.h" static ngx_int_t ngx_http_lua_set_by_lua_init(ngx_http_request_t *r); #endif char * ngx_http_lua_shared_dict(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_lua_main_conf_t *lmcf = conf; ngx_str_t *value, name; ngx_shm_zone_t *zone; ngx_shm_zone_t **zp; ngx_http_lua_shdict_ctx_t *ctx; ssize_t size; if (lmcf->shm_zones == NULL) { lmcf->shm_zones = ngx_palloc(cf->pool, sizeof(ngx_array_t)); if (lmcf->shm_zones == NULL) { return NGX_CONF_ERROR; } ngx_array_init(lmcf->shm_zones, cf->pool, 2, sizeof(ngx_shm_zone_t *)); } value = cf->args->elts; ctx = NULL; if (value[1].len == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid lua shared dict name \"%V\"", &value[1]); return NGX_CONF_ERROR; } name = value[1]; size = ngx_parse_size(&value[2]); if (size <= 8191) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid lua shared dict size \"%V\"", &value[2]); return NGX_CONF_ERROR; } ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_lua_shdict_ctx_t)); if (ctx == NULL) { return NGX_CONF_ERROR; } ctx->name = name; ctx->main_conf = lmcf; ctx->log = &cf->cycle->new_log; zone = ngx_shared_memory_add(cf, &name, (size_t) size, &ngx_http_lua_module); if (zone == NULL) { return NGX_CONF_ERROR; } if (zone->data) { ctx = zone->data; ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "lua_shared_dict \"%V\" is already defined as " "\"%V\"", &name, &ctx->name); return NGX_CONF_ERROR; } zone->init = ngx_http_lua_shdict_init_zone; zone->data = ctx; zp = ngx_array_push(lmcf->shm_zones); if (zp == NULL) { return NGX_CONF_ERROR; } *zp = zone; lmcf->requires_shm = 1; return NGX_CONF_OK; } char * ngx_http_lua_code_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *p = conf; ngx_flag_t *fp; char *ret; ret = ngx_conf_set_flag_slot(cf, cmd, conf); if (ret != NGX_CONF_OK) { return ret; } fp = (ngx_flag_t *) (p + cmd->offset); if (!*fp) { ngx_conf_log_error(NGX_LOG_ALERT, cf, 0, "lua_code_cache is off; this will hurt " "performance"); } return NGX_CONF_OK; } char * ngx_http_lua_package_cpath(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_lua_main_conf_t *lmcf = conf; ngx_str_t *value; if (lmcf->lua_cpath.len != 0) { return "is duplicate"; } dd("enter"); value = cf->args->elts; lmcf->lua_cpath.len = value[1].len; lmcf->lua_cpath.data = value[1].data; return NGX_CONF_OK; } char * ngx_http_lua_package_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_lua_main_conf_t *lmcf = conf; ngx_str_t *value; if (lmcf->lua_path.len != 0) { return "is duplicate"; } dd("enter"); value = cf->args->elts; lmcf->lua_path.len = value[1].len; lmcf->lua_path.data = value[1].data; return NGX_CONF_OK; } #if defined(NDK) && NDK char * ngx_http_lua_set_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { u_char *p; ngx_str_t *value; ngx_str_t target; ndk_set_var_t filter; ngx_http_lua_set_var_data_t *filter_data; /* * value[0] = "set_by_lua" * value[1] = target variable name * value[2] = lua script source to be executed * value[3..] = real params * */ value = cf->args->elts; target = value[1]; filter.type = NDK_SET_VAR_MULTI_VALUE_DATA; filter.func = cmd->post; filter.size = cf->args->nelts - 3; /* get number of real params */ filter_data = ngx_palloc(cf->pool, sizeof(ngx_http_lua_set_var_data_t)); if (filter_data == NULL) { return NGX_CONF_ERROR; } filter_data->size = filter.size; p = ngx_palloc(cf->pool, NGX_HTTP_LUA_INLINE_KEY_LEN + 1); if (p == NULL) { return NGX_CONF_ERROR; } filter_data->key = p; p = ngx_copy(p, NGX_HTTP_LUA_INLINE_TAG, NGX_HTTP_LUA_INLINE_TAG_LEN); p = ngx_http_lua_digest_hex(p, value[2].data, value[2].len); *p = '\0'; filter_data->script = value[2]; filter.data = filter_data; return ndk_set_var_multi_value_core(cf, &target, &value[3], &filter); } char * ngx_http_lua_set_by_lua_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { u_char *p; ngx_str_t *value; ngx_str_t target; ndk_set_var_t filter; ngx_http_lua_set_var_data_t *filter_data; /* * value[0] = "set_by_lua_file" * value[1] = target variable name * value[2] = lua script file path to be executed * value[3..] = real params * */ value = cf->args->elts; target = value[1]; filter.type = NDK_SET_VAR_MULTI_VALUE_DATA; filter.func = cmd->post; filter.size = cf->args->nelts - 2; /* get number of real params and lua script */ filter_data = ngx_palloc(cf->pool, sizeof(ngx_http_lua_set_var_data_t)); if (filter_data == NULL) { return NGX_CONF_ERROR; } filter_data->size = filter.size; p = ngx_palloc(cf->pool, NGX_HTTP_LUA_FILE_KEY_LEN + 1); if (p == NULL) { return NGX_CONF_ERROR; } filter_data->key = p; p = ngx_copy(p, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN); p = ngx_http_lua_digest_hex(p, value[2].data, value[2].len); *p = '\0'; ngx_str_null(&filter_data->script); filter.data = filter_data; return ndk_set_var_multi_value_core(cf, &target, &value[2], &filter); } ngx_int_t ngx_http_lua_filter_set_by_lua_inline(ngx_http_request_t *r, ngx_str_t *val, ngx_http_variable_value_t *v, void *data) { lua_State *L; ngx_int_t rc; ngx_http_lua_set_var_data_t *filter_data = data; if (ngx_http_lua_set_by_lua_init(r) != NGX_OK) { return NGX_ERROR; } L = ngx_http_lua_get_lua_vm(r, NULL); /* load Lua inline script (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadbuffer(L, filter_data->script.data, filter_data->script.len, filter_data->key, "set_by_lua"); if (rc != NGX_OK) { return NGX_ERROR; } rc = ngx_http_lua_set_by_chunk(L, r, val, v, filter_data->size, &filter_data->script); if (rc != NGX_OK) { return NGX_ERROR; } return NGX_OK; } ngx_int_t ngx_http_lua_filter_set_by_lua_file(ngx_http_request_t *r, ngx_str_t *val, ngx_http_variable_value_t *v, void *data) { lua_State *L; ngx_int_t rc; u_char *script_path; size_t nargs; ngx_http_lua_set_var_data_t *filter_data = data; dd("set by lua file"); if (ngx_http_lua_set_by_lua_init(r) != NGX_OK) { return NGX_ERROR; } filter_data->script.data = v[0].data; filter_data->script.len = v[0].len; /* skip the lua file path argument */ v++; nargs = filter_data->size - 1; dd("script: %.*s", (int) filter_data->script.len, filter_data->script.data); dd("nargs: %d", (int) nargs); script_path = ngx_http_lua_rebase_path(r->pool, filter_data->script.data, filter_data->script.len); if (script_path == NULL) { return NGX_ERROR; } L = ngx_http_lua_get_lua_vm(r, NULL); /* load Lua script file (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadfile(L, script_path, filter_data->key); if (rc != NGX_OK) { return NGX_ERROR; } rc = ngx_http_lua_set_by_chunk(L, r, val, v, nargs, &filter_data->script); if (rc != NGX_OK) { return NGX_ERROR; } return NGX_OK; } #endif /* defined(NDK) && NDK */ char * ngx_http_lua_rewrite_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { u_char *p; ngx_str_t *value; ngx_http_lua_main_conf_t *lmcf; ngx_http_lua_loc_conf_t *llcf = conf; ngx_http_compile_complex_value_t ccv; dd("enter"); #if defined(nginx_version) && nginx_version >= 8042 && nginx_version <= 8053 return "does not work with " NGINX_VER; #endif /* must specifiy a content handler */ if (cmd->post == NULL) { return NGX_CONF_ERROR; } if (llcf->rewrite_handler) { return "is duplicate"; } value = cf->args->elts; if (value[1].len == 0) { /* Oops...Invalid location conf */ ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "invalid location config: no runnable Lua code"); return NGX_CONF_ERROR; } if (cmd->post == ngx_http_lua_rewrite_handler_inline) { /* Don't eval nginx variables for inline lua code */ llcf->rewrite_src.value = value[1]; p = ngx_palloc(cf->pool, NGX_HTTP_LUA_INLINE_KEY_LEN + 1); if (p == NULL) { return NGX_CONF_ERROR; } llcf->rewrite_src_key = p; p = ngx_copy(p, NGX_HTTP_LUA_INLINE_TAG, NGX_HTTP_LUA_INLINE_TAG_LEN); p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len); *p = '\0'; } else { ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = cf; ccv.value = &value[1]; ccv.complex_value = &llcf->rewrite_src; if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } if (llcf->rewrite_src.lengths == NULL) { /* no variable found */ p = ngx_palloc(cf->pool, NGX_HTTP_LUA_FILE_KEY_LEN + 1); if (p == NULL) { return NGX_CONF_ERROR; } llcf->rewrite_src_key = p; p = ngx_copy(p, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN); p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len); *p = '\0'; } } llcf->rewrite_handler = (ngx_http_handler_pt) cmd->post; lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module); lmcf->requires_rewrite = 1; lmcf->requires_capture_filter = 1; return NGX_CONF_OK; } char * ngx_http_lua_access_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { u_char *p; ngx_str_t *value; ngx_http_lua_main_conf_t *lmcf; ngx_http_lua_loc_conf_t *llcf = conf; ngx_http_compile_complex_value_t ccv; dd("enter"); /* must specifiy a content handler */ if (cmd->post == NULL) { return NGX_CONF_ERROR; } if (llcf->access_handler) { return "is duplicate"; } value = cf->args->elts; if (value[1].len == 0) { /* Oops...Invalid location conf */ ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "invalid location config: no runnable Lua code"); return NGX_CONF_ERROR; } if (cmd->post == ngx_http_lua_access_handler_inline) { /* Don't eval nginx variables for inline lua code */ llcf->access_src.value = value[1]; p = ngx_palloc(cf->pool, NGX_HTTP_LUA_INLINE_KEY_LEN + 1); if (p == NULL) { return NGX_CONF_ERROR; } llcf->access_src_key = p; p = ngx_copy(p, NGX_HTTP_LUA_INLINE_TAG, NGX_HTTP_LUA_INLINE_TAG_LEN); p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len); *p = '\0'; } else { ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = cf; ccv.value = &value[1]; ccv.complex_value = &llcf->access_src; if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } if (llcf->access_src.lengths == NULL) { /* no variable found */ p = ngx_palloc(cf->pool, NGX_HTTP_LUA_FILE_KEY_LEN + 1); if (p == NULL) { return NGX_CONF_ERROR; } llcf->access_src_key = p; p = ngx_copy(p, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN); p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len); *p = '\0'; } } llcf->access_handler = (ngx_http_handler_pt) cmd->post; lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module); lmcf->requires_access = 1; lmcf->requires_capture_filter = 1; return NGX_CONF_OK; } char * ngx_http_lua_content_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { u_char *p; ngx_str_t *value; ngx_http_core_loc_conf_t *clcf; ngx_http_lua_main_conf_t *lmcf; ngx_http_lua_loc_conf_t *llcf = conf; ngx_http_compile_complex_value_t ccv; dd("enter"); /* must specifiy a content handler */ if (cmd->post == NULL) { return NGX_CONF_ERROR; } if (llcf->content_handler) { return "is duplicate"; } value = cf->args->elts; if (value[1].len == 0) { /* Oops...Invalid location conf */ ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "invalid location config: no runnable Lua code"); return NGX_CONF_ERROR; } if (cmd->post == ngx_http_lua_content_handler_inline) { /* Don't eval nginx variables for inline lua code */ llcf->content_src.value = value[1]; p = ngx_palloc(cf->pool, NGX_HTTP_LUA_INLINE_KEY_LEN + 1); if (p == NULL) { return NGX_CONF_ERROR; } llcf->content_src_key = p; p = ngx_copy(p, NGX_HTTP_LUA_INLINE_TAG, NGX_HTTP_LUA_INLINE_TAG_LEN); p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len); *p = '\0'; } else { ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = cf; ccv.value = &value[1]; ccv.complex_value = &llcf->content_src; if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } if (llcf->content_src.lengths == NULL) { /* no variable found */ p = ngx_palloc(cf->pool, NGX_HTTP_LUA_FILE_KEY_LEN + 1); if (p == NULL) { return NGX_CONF_ERROR; } llcf->content_src_key = p; p = ngx_copy(p, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN); p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len); *p = '\0'; } } llcf->content_handler = (ngx_http_handler_pt) cmd->post; lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module); lmcf->requires_capture_filter = 1; /* register location content handler */ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); if (clcf == NULL) { return NGX_CONF_ERROR; } clcf->handler = ngx_http_lua_content_handler; return NGX_CONF_OK; } char * ngx_http_lua_log_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { u_char *p; ngx_str_t *value; ngx_http_lua_main_conf_t *lmcf; ngx_http_lua_loc_conf_t *llcf = conf; ngx_http_compile_complex_value_t ccv; dd("enter"); /* must specifiy a content handler */ if (cmd->post == NULL) { return NGX_CONF_ERROR; } if (llcf->log_handler) { return "is duplicate"; } value = cf->args->elts; if (value[1].len == 0) { /* Oops...Invalid location conf */ ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "invalid location config: no runnable Lua code"); return NGX_CONF_ERROR; } if (cmd->post == ngx_http_lua_log_handler_inline) { /* Don't eval nginx variables for inline lua code */ llcf->log_src.value = value[1]; p = ngx_palloc(cf->pool, NGX_HTTP_LUA_INLINE_KEY_LEN + 1); if (p == NULL) { return NGX_CONF_ERROR; } llcf->log_src_key = p; p = ngx_copy(p, NGX_HTTP_LUA_INLINE_TAG, NGX_HTTP_LUA_INLINE_TAG_LEN); p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len); *p = '\0'; } else { ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = cf; ccv.value = &value[1]; ccv.complex_value = &llcf->log_src; if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } if (llcf->log_src.lengths == NULL) { /* no variable found */ p = ngx_palloc(cf->pool, NGX_HTTP_LUA_FILE_KEY_LEN + 1); if (p == NULL) { return NGX_CONF_ERROR; } llcf->log_src_key = p; p = ngx_copy(p, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN); p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len); *p = '\0'; } } llcf->log_handler = (ngx_http_handler_pt) cmd->post; lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module); lmcf->requires_log = 1; return NGX_CONF_OK; } char * ngx_http_lua_header_filter_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { u_char *p; ngx_str_t *value; ngx_http_lua_main_conf_t *lmcf; ngx_http_lua_loc_conf_t *llcf = conf; ngx_http_compile_complex_value_t ccv; dd("enter"); /* must specifiy a content handler */ if (cmd->post == NULL) { return NGX_CONF_ERROR; } if (llcf->header_filter_handler) { return "is duplicate"; } value = cf->args->elts; if (value[1].len == 0) { /* Oops...Invalid location conf */ ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "invalid location config: no runnable Lua code"); return NGX_CONF_ERROR; } if (cmd->post == ngx_http_lua_header_filter_inline) { /* Don't eval nginx variables for inline lua code */ llcf->header_filter_src.value = value[1]; p = ngx_palloc(cf->pool, NGX_HTTP_LUA_INLINE_KEY_LEN + 1); if (p == NULL) { return NGX_CONF_ERROR; } llcf->header_filter_src_key = p; p = ngx_copy(p, NGX_HTTP_LUA_INLINE_TAG, NGX_HTTP_LUA_INLINE_TAG_LEN); p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len); *p = '\0'; } else { ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = cf; ccv.value = &value[1]; ccv.complex_value = &llcf->header_filter_src; if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } if (llcf->header_filter_src.lengths == NULL) { /* no variable found */ p = ngx_palloc(cf->pool, NGX_HTTP_LUA_FILE_KEY_LEN + 1); if (p == NULL) { return NGX_CONF_ERROR; } llcf->header_filter_src_key = p; p = ngx_copy(p, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN); p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len); *p = '\0'; } } llcf->header_filter_handler = (ngx_http_handler_pt) cmd->post; lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module); lmcf->requires_header_filter = 1; return NGX_CONF_OK; } char * ngx_http_lua_body_filter_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { u_char *p; ngx_str_t *value; ngx_http_lua_main_conf_t *lmcf; ngx_http_lua_loc_conf_t *llcf = conf; ngx_http_compile_complex_value_t ccv; dd("enter"); /* must specifiy a content handler */ if (cmd->post == NULL) { return NGX_CONF_ERROR; } if (llcf->body_filter_handler) { return "is duplicate"; } value = cf->args->elts; if (value[1].len == 0) { /* Oops...Invalid location conf */ ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "invalid location config: no runnable Lua code"); return NGX_CONF_ERROR; } if (cmd->post == ngx_http_lua_body_filter_inline) { /* Don't eval nginx variables for inline lua code */ llcf->body_filter_src.value = value[1]; p = ngx_palloc(cf->pool, NGX_HTTP_LUA_INLINE_KEY_LEN + 1); if (p == NULL) { return NGX_CONF_ERROR; } llcf->body_filter_src_key = p; p = ngx_copy(p, NGX_HTTP_LUA_INLINE_TAG, NGX_HTTP_LUA_INLINE_TAG_LEN); p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len); *p = '\0'; } else { ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = cf; ccv.value = &value[1]; ccv.complex_value = &llcf->body_filter_src; if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } if (llcf->body_filter_src.lengths == NULL) { /* no variable found */ p = ngx_palloc(cf->pool, NGX_HTTP_LUA_FILE_KEY_LEN + 1); if (p == NULL) { return NGX_CONF_ERROR; } llcf->body_filter_src_key = p; p = ngx_copy(p, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN); p = ngx_http_lua_digest_hex(p, value[1].data, value[1].len); *p = '\0'; } } llcf->body_filter_handler = (ngx_http_output_body_filter_pt) cmd->post; lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_lua_module); lmcf->requires_body_filter = 1; lmcf->requires_header_filter = 1; return NGX_CONF_OK; } char * ngx_http_lua_init_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { u_char *name; ngx_str_t *value; ngx_http_lua_main_conf_t *lmcf = conf; dd("enter"); /* must specifiy a content handler */ if (cmd->post == NULL) { return NGX_CONF_ERROR; } if (lmcf->init_handler) { return "is duplicate"; } value = cf->args->elts; if (value[1].len == 0) { /* Oops...Invalid location conf */ ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "invalid location config: no runnable Lua code"); return NGX_CONF_ERROR; } lmcf->init_handler = (ngx_http_lua_conf_handler_pt) cmd->post; if (cmd->post == ngx_http_lua_init_by_file) { name = ngx_http_lua_rebase_path(cf->pool, value[1].data, value[1].len); if (name == NULL) { return NGX_CONF_ERROR; } lmcf->init_src.data = name; lmcf->init_src.len = ngx_strlen(name); } else { lmcf->init_src = value[1]; } return NGX_CONF_OK; } #if defined(NDK) && NDK static ngx_int_t ngx_http_lua_set_by_lua_init(ngx_http_request_t *r) { lua_State *L; ngx_http_lua_ctx_t *ctx; ngx_http_cleanup_t *cln; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { ctx = ngx_http_lua_create_ctx(r); if (ctx == NULL) { return NGX_ERROR; } } else { L = ngx_http_lua_get_lua_vm(r, ctx); ngx_http_lua_reset_ctx(r, L, ctx); } if (ctx->cleanup == NULL) { cln = ngx_http_cleanup_add(r, 0); if (cln == NULL) { return NGX_ERROR; } cln->handler = ngx_http_lua_request_cleanup_handler; cln->data = ctx; ctx->cleanup = &cln->handler; } ctx->context = NGX_HTTP_LUA_CONTEXT_SET; return NGX_OK; } #endif /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_args.c0000664000000000000000000003077712305451335017224 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_args.h" #include "ngx_http_lua_util.h" static int ngx_http_lua_ngx_req_set_uri_args(lua_State *L); static int ngx_http_lua_ngx_req_get_uri_args(lua_State *L); static int ngx_http_lua_ngx_req_get_post_args(lua_State *L); static int ngx_http_lua_ngx_req_set_uri_args(lua_State *L) { ngx_http_request_t *r; ngx_str_t args; const char *msg; size_t len; u_char *p; if (lua_gettop(L) != 1) { return luaL_error(L, "expecting 1 argument but seen %d", lua_gettop(L)); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } ngx_http_lua_check_fake_request(L, r); switch (lua_type(L, 1)) { case LUA_TNUMBER: case LUA_TSTRING: p = (u_char *) lua_tolstring(L, 1, &len); args.data = ngx_palloc(r->pool, len); if (args.data == NULL) { return luaL_error(L, "out of memory"); } ngx_memcpy(args.data, p, len); args.len = len; break; case LUA_TTABLE: ngx_http_lua_process_args_option(r, L, 1, &args); dd("args: %.*s", (int) args.len, args.data); break; default: msg = lua_pushfstring(L, "string, number, or table expected, " "but got %s", luaL_typename(L, 2)); return luaL_argerror(L, 1, msg); } dd("args: %.*s", (int) args.len, args.data); r->args.data = args.data; r->args.len = args.len; r->valid_unparsed_uri = 0; return 0; } static int ngx_http_lua_ngx_req_get_uri_args(lua_State *L) { ngx_http_request_t *r; u_char *buf; u_char *last; int retval; int n; int max; n = lua_gettop(L); if (n != 0 && n != 1) { return luaL_error(L, "expecting 0 or 1 arguments but seen %d", n); } if (n == 1) { max = luaL_checkinteger(L, 1); lua_pop(L, 1); } else { max = NGX_HTTP_LUA_MAX_ARGS; } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } ngx_http_lua_check_fake_request(L, r); lua_createtable(L, 0, 4); /* we copy r->args over to buf to simplify * unescaping query arg keys and values */ buf = ngx_palloc(r->pool, r->args.len); if (buf == NULL) { return luaL_error(L, "out of memory"); } ngx_memcpy(buf, r->args.data, r->args.len); last = buf + r->args.len; retval = ngx_http_lua_parse_args(L, buf, last, max); ngx_pfree(r->pool, buf); return retval; } static int ngx_http_lua_ngx_req_get_post_args(lua_State *L) { ngx_http_request_t *r; u_char *buf; int retval; size_t len; ngx_chain_t *cl; u_char *p; u_char *last; int n; int max; n = lua_gettop(L); if (n != 0 && n != 1) { return luaL_error(L, "expecting 0 or 1 arguments but seen %d", n); } if (n == 1) { max = luaL_checkinteger(L, 1); lua_pop(L, 1); } else { max = NGX_HTTP_LUA_MAX_ARGS; } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request object found"); } ngx_http_lua_check_fake_request(L, r); if (r->discard_body) { lua_createtable(L, 0, 0); return 1; } if (r->request_body == NULL) { return luaL_error(L, "no request body found; " "maybe you should turn on lua_need_request_body?"); } if (r->request_body->temp_file) { return luaL_error(L, "requesty body in temp file not supported"); } lua_createtable(L, 0, 4); if (r->request_body->bufs == NULL) { return 1; } /* we copy r->request_body->bufs over to buf to simplify * unescaping query arg keys and values */ len = 0; for (cl = r->request_body->bufs; cl; cl = cl->next) { len += cl->buf->last - cl->buf->pos; } dd("post body length: %d", (int) len); buf = ngx_palloc(r->pool, len); if (buf == NULL) { return luaL_error(L, "out of memory"); } p = buf; for (cl = r->request_body->bufs; cl; cl = cl->next) { p = ngx_copy(p, cl->buf->pos, cl->buf->last - cl->buf->pos); } dd("post body: %.*s", (int) len, buf); last = buf + len; retval = ngx_http_lua_parse_args(L, buf, last, max); ngx_pfree(r->pool, buf); return retval; } int ngx_http_lua_parse_args(lua_State *L, u_char *buf, u_char *last, int max) { u_char *p, *q; u_char *src, *dst; unsigned parsing_value; size_t len; int count = 0; int top; top = lua_gettop(L); p = buf; parsing_value = 0; q = p; while (p != last) { if (*p == '=' && ! parsing_value) { /* key data is between p and q */ src = q; dst = q; ngx_http_lua_unescape_uri(&dst, &src, p - q, NGX_UNESCAPE_URI_COMPONENT); dd("pushing key %.*s", (int) (dst - q), q); /* push the key */ lua_pushlstring(L, (char *) q, dst - q); /* skip the current '=' char */ p++; q = p; parsing_value = 1; } else if (*p == '&') { /* reached the end of a key or a value, just save it */ src = q; dst = q; ngx_http_lua_unescape_uri(&dst, &src, p - q, NGX_UNESCAPE_URI_COMPONENT); dd("pushing key or value %.*s", (int) (dst - q), q); /* push the value or key */ lua_pushlstring(L, (char *) q, dst - q); /* skip the current '&' char */ p++; q = p; if (parsing_value) { /* end of the current pair's value */ parsing_value = 0; } else { /* the current parsing pair takes no value, * just push the value "true" */ dd("pushing boolean true"); lua_pushboolean(L, 1); } (void) lua_tolstring(L, -2, &len); if (len == 0) { /* ignore empty string key pairs */ dd("popping key and value..."); lua_pop(L, 2); } else { dd("setting table..."); ngx_http_lua_set_multi_value_table(L, top); } if (max > 0 && ++count == max) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "lua hit query args limit %d", max); return 1; } } else { p++; } } if (p != q || parsing_value) { src = q; dst = q; ngx_http_lua_unescape_uri(&dst, &src, p - q, NGX_UNESCAPE_URI_COMPONENT); dd("pushing key or value %.*s", (int) (dst - q), q); lua_pushlstring(L, (char *) q, dst - q); if (!parsing_value) { dd("pushing boolean true..."); lua_pushboolean(L, 1); } (void) lua_tolstring(L, -2, &len); if (len == 0) { /* ignore empty string key pairs */ dd("popping key and value..."); lua_pop(L, 2); } else { dd("setting table..."); ngx_http_lua_set_multi_value_table(L, top); } } dd("gettop: %d", lua_gettop(L)); dd("type: %s", lua_typename(L, lua_type(L, 1))); if (lua_gettop(L) != top) { return luaL_error(L, "internal error: stack in bad state"); } return 1; } void ngx_http_lua_inject_req_args_api(lua_State *L) { lua_pushcfunction(L, ngx_http_lua_ngx_req_set_uri_args); lua_setfield(L, -2, "set_uri_args"); lua_pushcfunction(L, ngx_http_lua_ngx_req_get_uri_args); lua_setfield(L, -2, "get_uri_args"); lua_pushcfunction(L, ngx_http_lua_ngx_req_get_uri_args); lua_setfield(L, -2, "get_query_args"); /* deprecated */ lua_pushcfunction(L, ngx_http_lua_ngx_req_get_post_args); lua_setfield(L, -2, "get_post_args"); } #ifndef NGX_HTTP_LUA_NO_FFI_API size_t ngx_http_lua_ffi_req_get_querystring_len(ngx_http_request_t *r) { return r->args.len; } int ngx_http_lua_ffi_req_get_uri_args_count(ngx_http_request_t *r, int max) { int count; u_char *p, *last; if (r->connection->fd == -1) { return NGX_HTTP_LUA_FFI_BAD_CONTEXT; } if (max < 0) { max = NGX_HTTP_LUA_MAX_ARGS; } last = r->args.data + r->args.len; count = 0; for (p = r->args.data; p != last; p++) { if (*p == '&') { if (count == 0) { count += 2; } else { count++; } } } if (count) { if (max > 0 && count > max) { count = max; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua hit query args limit %d", max); } return count; } if (r->args.len) { return 1; } return 0; } int ngx_http_lua_ffi_req_get_uri_args(ngx_http_request_t *r, u_char *buf, ngx_http_lua_ffi_table_elt_t *out, int count) { int i, parsing_value = 0; u_char *last, *p, *q; u_char *src, *dst; if (count <= 0) { return NGX_OK; } ngx_memcpy(buf, r->args.data, r->args.len); i = 0; last = buf + r->args.len; p = buf; q = p; while (p != last) { if (*p == '=' && !parsing_value) { /* key data is between p and q */ src = q; dst = q; ngx_http_lua_unescape_uri(&dst, &src, p - q, NGX_UNESCAPE_URI_COMPONENT); dd("saving key %.*s", (int) (dst - q), q); out[i].key.data = q; out[i].key.len = (int) (dst - q); /* skip the current '=' char */ p++; q = p; parsing_value = 1; } else if (*p == '&') { /* reached the end of a key or a value, just save it */ src = q; dst = q; ngx_http_lua_unescape_uri(&dst, &src, p - q, NGX_UNESCAPE_URI_COMPONENT); dd("pushing key or value %.*s", (int) (dst - q), q); if (parsing_value) { /* end of the current pair's value */ parsing_value = 0; if (out[i].key.len) { out[i].value.data = q; out[i].value.len = (int) (dst - q); i++; } } else { /* the current parsing pair takes no value, * just push the value "true" */ dd("pushing boolean true"); if (dst - q) { out[i].key.data = q; out[i].key.len = (int) (dst - q); out[i].value.len = -1; i++; } } if (i == count) { return i; } /* skip the current '&' char */ p++; q = p; } else { p++; } } if (p != q || parsing_value) { src = q; dst = q; ngx_http_lua_unescape_uri(&dst, &src, p - q, NGX_UNESCAPE_URI_COMPONENT); dd("pushing key or value %.*s", (int) (dst - q), q); if (parsing_value) { if (out[i].key.len) { out[i].value.data = q; out[i].value.len = (int) (dst - q); i++; } } else { if (dst - q) { out[i].key.data = q; out[i].key.len = (int) (dst - q); out[i].value.len = (int) -1; i++; } } } return i; } #endif /* NGX_HTTP_LUA_NO_FFI_API */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_initby.h0000664000000000000000000000072212305451335017556 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_INITBY_H_INCLUDED_ #define _NGX_HTTP_LUA_INITBY_H_INCLUDED_ #include "ngx_http_lua_common.h" int ngx_http_lua_init_by_inline(ngx_log_t *log, ngx_http_lua_main_conf_t *lmcf, lua_State *L); int ngx_http_lua_init_by_file(ngx_log_t *log, ngx_http_lua_main_conf_t *lmcf, lua_State *L); #endif /* _NGX_HTTP_LUA_INITBY_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_cache.c0000664000000000000000000001507012305451335017320 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include #include #include "ngx_http_lua_common.h" #include "ngx_http_lua_cache.h" #include "ngx_http_lua_clfactory.h" #include "ngx_http_lua_util.h" /** * Find code chunk associated with the given key in code cache, * and push it to the top of Lua stack if found. * * Stack layout before call: * | ... | <- top * * Stack layout after call: * | code chunk | <- top * | ... | * * */ static ngx_int_t ngx_http_lua_cache_load_code(lua_State *L, const char *key) { /* get code cache table */ lua_pushlightuserdata(L, &ngx_http_lua_code_cache_key); lua_rawget(L, LUA_REGISTRYINDEX); /* sp++ */ dd("Code cache table to load: %p", lua_topointer(L, -1)); if (!lua_istable(L, -1)) { dd("Error: code cache table to load did not exist!!"); return NGX_ERROR; } lua_getfield(L, -1, key); /* sp++ */ if (lua_isfunction(L, -1)) { /* call closure factory to gen new closure */ int rc = lua_pcall(L, 0, 1, 0); if (rc == 0) { /* remove cache table from stack, leave code chunk at * top of stack */ lua_remove(L, -2); /* sp-- */ return NGX_OK; } } dd("Value associated with given key in code cache table is not code " "chunk: stack top=%d, top value type=%s\n", lua_gettop(L), lua_typename(L, -1)); /* remove cache table and value from stack */ lua_pop(L, 2); /* sp-=2 */ return NGX_DECLINED; } /** * Store the closure factory at the top of Lua stack to code cache, and * associate it with the given key. Then generate new closure. * * Stack layout before call: * | code factory | <- top * | ... | * * Stack layout after call: * | code chunk | <- top * | ... | * * */ static ngx_int_t ngx_http_lua_cache_store_code(lua_State *L, const char *key) { int rc; /* get code cache table */ lua_pushlightuserdata(L, &ngx_http_lua_code_cache_key); lua_rawget(L, LUA_REGISTRYINDEX); dd("Code cache table to store: %p", lua_topointer(L, -1)); if (!lua_istable(L, -1)) { dd("Error: code cache table to load did not exist!!"); return NGX_ERROR; } lua_pushvalue(L, -2); /* closure cache closure */ lua_setfield(L, -2, key); /* closure cache */ /* remove cache table, leave closure factory at top of stack */ lua_pop(L, 1); /* closure */ /* call closure factory to generate new closure */ rc = lua_pcall(L, 0, 1, 0); if (rc != 0) { dd("Error: failed to call closure factory!!"); return NGX_ERROR; } return NGX_OK; } ngx_int_t ngx_http_lua_cache_loadbuffer(lua_State *L, const u_char *src, size_t src_len, const u_char *cache_key, const char *name) { int rc, n; const char *err = NULL; n = lua_gettop(L); dd("XXX cache key: [%s]", cache_key); if (ngx_http_lua_cache_load_code(L, (char *) cache_key) == NGX_OK) { /* code chunk loaded from cache, sp++ */ dd("Code cache hit! cache key='%s', stack top=%d, script='%.*s'", cache_key, lua_gettop(L), (int) src_len, src); return NGX_OK; } dd("Code cache missed! cache key='%s', stack top=%d, script='%.*s'", cache_key, lua_gettop(L), (int) src_len, src); /* load closure factory of inline script to the top of lua stack, sp++ */ rc = ngx_http_lua_clfactory_loadbuffer(L, (char *) src, src_len, name); if (rc != 0) { /* Oops! error occured when loading Lua script */ if (rc == LUA_ERRMEM) { err = "memory allocation error"; } else { if (lua_isstring(L, -1)) { err = lua_tostring(L, -1); } else { err = "unknown error"; } } goto error; } /* store closure factory and gen new closure at the top of lua stack to * code cache */ rc = ngx_http_lua_cache_store_code(L, (char *) cache_key); if (rc != NGX_OK) { err = "fail to generate new closure from the closure factory"; goto error; } return NGX_OK; error: ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "failed to load inlined Lua code: %s", err); lua_settop(L, n); return NGX_ERROR; } ngx_int_t ngx_http_lua_cache_loadfile(lua_State *L, const u_char *script, const u_char *cache_key) { int rc, n; u_char *p; u_char buf[NGX_HTTP_LUA_FILE_KEY_LEN + 1]; const char *err = NULL; n = lua_gettop(L); /* calculate digest of script file path */ if (cache_key == NULL) { dd("CACHE file key not pre-calculated...calculating"); p = ngx_copy(buf, NGX_HTTP_LUA_FILE_TAG, NGX_HTTP_LUA_FILE_TAG_LEN); p = ngx_http_lua_digest_hex(p, script, ngx_strlen(script)); *p = '\0'; cache_key = buf; } else { dd("CACHE file key already pre-calculated"); } dd("XXX cache key for file: [%s]", cache_key); if (ngx_http_lua_cache_load_code(L, (char *) cache_key) == NGX_OK) { /* code chunk loaded from cache, sp++ */ dd("Code cache hit! cache key='%s', stack top=%d, file path='%s'", cache_key, lua_gettop(L), script); return NGX_OK; } dd("Code cache missed! cache key='%s', stack top=%d, file path='%s'", cache_key, lua_gettop(L), script); /* load closure factory of script file to the top of lua stack, sp++ */ rc = ngx_http_lua_clfactory_loadfile(L, (char *) script); if (rc != 0) { /* Oops! error occured when loading Lua script */ if (rc == LUA_ERRMEM) { err = "memory allocation error"; } else { if (lua_isstring(L, -1)) { err = lua_tostring(L, -1); } else { err = "unknown error"; } } goto error; } /* store closure factory and gen new closure at the top of lua stack * to code cache */ rc = ngx_http_lua_cache_store_code(L, (char *) cache_key); if (rc != NGX_OK) { err = "fail to generate new closure from the closure factory"; goto error; } return NGX_OK; error: ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "failed to load external Lua file: %s", err); lua_settop(L, n); return NGX_ERROR; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_req_method.h0000664000000000000000000000047412305451335020413 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_METHOD_H_INCLUDED_ #define _NGX_HTTP_LUA_METHOD_H_INCLUDED_ #include "ngx_http_lua_common.h" void ngx_http_lua_inject_req_method_api(lua_State *L); #endif /* _NGX_HTTP_LUA_METHOD_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_directive.h0000664000000000000000000000344512305451335020243 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_DIRECTIVE_H_INCLUDED_ #define _NGX_HTTP_LUA_DIRECTIVE_H_INCLUDED_ #include "ngx_http_lua_common.h" char * ngx_http_lua_shared_dict(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char * ngx_http_lua_package_cpath(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char * ngx_http_lua_package_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char * ngx_http_lua_content_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char * ngx_http_lua_rewrite_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char * ngx_http_lua_access_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char * ngx_http_lua_log_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char * ngx_http_lua_header_filter_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char * ngx_http_lua_body_filter_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char * ngx_http_lua_init_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char * ngx_http_lua_code_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); #if defined(NDK) && NDK char * ngx_http_lua_set_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char * ngx_http_lua_set_by_lua_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); ngx_int_t ngx_http_lua_filter_set_by_lua_inline(ngx_http_request_t *r, ngx_str_t *val, ngx_http_variable_value_t *v, void *data); ngx_int_t ngx_http_lua_filter_set_by_lua_file(ngx_http_request_t *r, ngx_str_t *val, ngx_http_variable_value_t *v, void *data); #endif char * ngx_http_lua_rewrite_no_postpone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); #endif /* _NGX_HTTP_LUA_DIRECTIVE_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_consts.h0000664000000000000000000000035112305451335017567 0ustar #ifndef NGX_HTTP_LUA_CONSTS #define NGX_HTTP_LUA_CONSTS #include "ngx_http_lua_common.h" void ngx_http_lua_inject_http_consts(lua_State *L); void ngx_http_lua_inject_core_consts(lua_State *L); #endif /* NGX_HTTP_LUA_CONSTS */ debian/modules/nginx-lua/src/ngx_http_lua_headers_in.h0000664000000000000000000000070712305451335020364 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef _NGX_HTTP_LUA_HEADERS_IN_H_INCLUDED_ #define _NGX_HTTP_LUA_HEADERS_IN_H_INCLUDED_ #include #include "ngx_http_lua_common.h" ngx_int_t ngx_http_lua_set_input_header(ngx_http_request_t *r, ngx_str_t key, ngx_str_t value, unsigned override); #endif /* _NGX_HTTP_LUA_HEADERS_IN_H_INCLUDED_ */ /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_headers_in.c0000664000000000000000000004616012305451335020362 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include #include "ngx_http_lua_headers_in.h" #include "ngx_http_lua_util.h" #include static ngx_int_t ngx_http_set_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_header_helper(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value, ngx_table_elt_t **output_header); static ngx_int_t ngx_http_set_builtin_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_user_agent_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_connection_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_content_length_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_set_cookie_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_clear_builtin_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_clear_content_length_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_lua_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc); static ngx_int_t ngx_http_set_host_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value); static ngx_int_t ngx_http_lua_rm_header_helper(ngx_list_t *l, ngx_list_part_t *cur, ngx_uint_t i); static ngx_http_lua_set_header_t ngx_http_lua_set_handlers[] = { #if (NGX_HTTP_GZIP) { ngx_string("Accept-Encoding"), offsetof(ngx_http_headers_in_t, accept_encoding), ngx_http_set_builtin_header }, { ngx_string("Via"), offsetof(ngx_http_headers_in_t, via), ngx_http_set_builtin_header }, #endif { ngx_string("Host"), offsetof(ngx_http_headers_in_t, host), ngx_http_set_host_header }, { ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection), ngx_http_set_connection_header }, { ngx_string("If-Modified-Since"), offsetof(ngx_http_headers_in_t, if_modified_since), ngx_http_set_builtin_header }, { ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent), ngx_http_set_user_agent_header }, { ngx_string("Referer"), offsetof(ngx_http_headers_in_t, referer), ngx_http_set_builtin_header }, { ngx_string("Content-Type"), offsetof(ngx_http_headers_in_t, content_type), ngx_http_set_builtin_header }, { ngx_string("Range"), offsetof(ngx_http_headers_in_t, range), ngx_http_set_builtin_header }, { ngx_string("If-Range"), offsetof(ngx_http_headers_in_t, if_range), ngx_http_set_builtin_header }, { ngx_string("Transfer-Encoding"), offsetof(ngx_http_headers_in_t, transfer_encoding), ngx_http_set_builtin_header }, { ngx_string("Expect"), offsetof(ngx_http_headers_in_t, expect), ngx_http_set_builtin_header }, { ngx_string("Authorization"), offsetof(ngx_http_headers_in_t, authorization), ngx_http_set_builtin_header }, { ngx_string("Keep-Alive"), offsetof(ngx_http_headers_in_t, keep_alive), ngx_http_set_builtin_header }, { ngx_string("Content-Length"), offsetof(ngx_http_headers_in_t, content_length), ngx_http_set_content_length_header }, { ngx_string("Cookie"), 0, ngx_http_set_cookie_header }, #if (NGX_HTTP_REALIP) { ngx_string("X-Real-IP"), offsetof(ngx_http_headers_in_t, x_real_ip), ngx_http_set_builtin_header }, #endif { ngx_null_string, 0, ngx_http_set_header } }; /* request time implementation */ static ngx_int_t ngx_http_set_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value) { return ngx_http_set_header_helper(r, hv, value, NULL); } static ngx_int_t ngx_http_set_header_helper(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value, ngx_table_elt_t **output_header) { ngx_table_elt_t *h, *matched; ngx_list_part_t *part; ngx_uint_t i; ngx_uint_t rc; if (hv->no_override) { goto new_header; } matched = NULL; retry: part = &r->headers_in.headers.part; h = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; h = part->elts; i = 0; } dd("i: %d, part: %p", (int) i, part); if (h[i].key.len == hv->key.len && ngx_strncasecmp(h[i].key.data, hv->key.data, h[i].key.len) == 0) { if (value->len == 0 || (matched && matched != &h[i])) { h[i].hash = 0; dd("rm header %.*s: %.*s", (int) h[i].key.len, h[i].key.data, (int) h[i].value.len, h[i].value.data); rc = ngx_http_lua_rm_header_helper(&r->headers_in.headers, part, i); ngx_http_lua_assert(!(r->headers_in.headers.part.next == NULL && r->headers_in.headers.last != &r->headers_in.headers.part)); dd("rm header: rc=%d", (int) rc); if (rc == NGX_OK) { if (output_header) { *output_header = NULL; } goto retry; } return NGX_ERROR; } h[i].value = *value; if (output_header) { *output_header = &h[i]; dd("setting existing builtin input header"); } if (matched == NULL) { matched = &h[i]; } } } if (matched){ return NGX_OK; } if (value->len == 0) { return NGX_OK; } new_header: h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { return NGX_ERROR; } dd("created new header for %.*s", (int) hv->key.len, hv->key.data); if (value->len == 0) { h->hash = 0; } else { h->hash = hv->hash; } h->key = hv->key; h->value = *value; h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == NULL) { return NGX_ERROR; } ngx_strlow(h->lowcase_key, h->key.data, h->key.len); if (output_header) { *output_header = h; } return NGX_OK; } static ngx_int_t ngx_http_set_builtin_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value) { ngx_table_elt_t *h, **old; dd("entered set_builtin_header (input)"); if (hv->offset) { old = (ngx_table_elt_t **) ((char *) &r->headers_in + hv->offset); } else { old = NULL; } dd("old builtin ptr ptr: %p", old); if (old) { dd("old builtin ptr: %p", *old); } if (old == NULL || *old == NULL) { dd("set normal header"); return ngx_http_set_header_helper(r, hv, value, old); } h = *old; if (value->len == 0) { h->hash = 0; h->value = *value; return ngx_http_set_header_helper(r, hv, value, old); } h->hash = hv->hash; h->value = *value; return NGX_OK; } static ngx_int_t ngx_http_lua_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc) { u_char *h, ch; size_t i, dot_pos, host_len; enum { sw_usual = 0, sw_literal, sw_rest } state; dot_pos = host->len; host_len = host->len; h = host->data; state = sw_usual; for (i = 0; i < host->len; i++) { ch = h[i]; switch (ch) { case '.': if (dot_pos == i - 1) { return NGX_DECLINED; } dot_pos = i; break; case ':': if (state == sw_usual) { host_len = i; state = sw_rest; } break; case '[': if (i == 0) { state = sw_literal; } break; case ']': if (state == sw_literal) { host_len = i + 1; state = sw_rest; } break; case '\0': return NGX_DECLINED; default: if (ngx_path_separator(ch)) { return NGX_DECLINED; } if (ch >= 'A' && ch <= 'Z') { alloc = 1; } break; } } if (dot_pos == host_len - 1) { host_len--; } if (host_len == 0) { return NGX_DECLINED; } if (alloc) { host->data = ngx_pnalloc(pool, host_len); if (host->data == NULL) { return NGX_ERROR; } ngx_strlow(host->data, h, host_len); } host->len = host_len; return NGX_OK; } static ngx_int_t ngx_http_set_host_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value) { ngx_str_t host; dd("server new value len: %d", (int) value->len); if (value->len) { host= *value; if (ngx_http_lua_validate_host(&host, r->pool, 0) != NGX_OK) { return NGX_ERROR; } r->headers_in.server = host; } else { r->headers_in.server = *value; } return ngx_http_set_builtin_header(r, hv, value); } static ngx_int_t ngx_http_set_connection_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value) { r->headers_in.connection_type = 0; if (value->len == 0) { return ngx_http_set_builtin_header(r, hv, value); } if (ngx_strcasestrn(value->data, "close", 5 - 1)) { r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; r->headers_in.keep_alive_n = -1; } else if (ngx_strcasestrn(value->data, "keep-alive", 10 - 1)) { r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE; } return ngx_http_set_builtin_header(r, hv, value); } /* borrowed the code from ngx_http_request.c:ngx_http_process_user_agent */ static ngx_int_t ngx_http_set_user_agent_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value) { u_char *user_agent, *msie; /* clear existing settings */ r->headers_in.msie = 0; r->headers_in.msie6 = 0; r->headers_in.opera = 0; r->headers_in.gecko = 0; r->headers_in.chrome = 0; r->headers_in.safari = 0; r->headers_in.konqueror = 0; if (value->len == 0) { return ngx_http_set_builtin_header(r, hv, value); } /* check some widespread browsers */ user_agent = value->data; msie = ngx_strstrn(user_agent, "MSIE ", 5 - 1); if (msie && msie + 7 < user_agent + value->len) { r->headers_in.msie = 1; if (msie[6] == '.') { switch (msie[5]) { case '4': case '5': r->headers_in.msie6 = 1; break; case '6': if (ngx_strstrn(msie + 8, "SV1", 3 - 1) == NULL) { r->headers_in.msie6 = 1; } break; } } } if (ngx_strstrn(user_agent, "Opera", 5 - 1)) { r->headers_in.opera = 1; r->headers_in.msie = 0; r->headers_in.msie6 = 0; } if (!r->headers_in.msie && !r->headers_in.opera) { if (ngx_strstrn(user_agent, "Gecko/", 6 - 1)) { r->headers_in.gecko = 1; } else if (ngx_strstrn(user_agent, "Chrome/", 7 - 1)) { r->headers_in.chrome = 1; } else if (ngx_strstrn(user_agent, "Safari/", 7 - 1) && ngx_strstrn(user_agent, "Mac OS X", 8 - 1)) { r->headers_in.safari = 1; } else if (ngx_strstrn(user_agent, "Konqueror", 9 - 1)) { r->headers_in.konqueror = 1; } } return ngx_http_set_builtin_header(r, hv, value); } static ngx_int_t ngx_http_set_content_length_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value) { off_t len; if (value->len == 0) { return ngx_http_clear_content_length_header(r, hv, value); } len = ngx_atosz(value->data, value->len); if (len == NGX_ERROR) { return NGX_ERROR; } dd("reset headers_in.content_length_n to %d", (int)len); r->headers_in.content_length_n = len; return ngx_http_set_builtin_header(r, hv, value); } static ngx_int_t ngx_http_set_cookie_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value) { ngx_table_elt_t **cookie, *h; if (!hv->no_override && r->headers_in.cookies.nelts > 0) { ngx_array_destroy(&r->headers_in.cookies); if (ngx_array_init(&r->headers_in.cookies, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK) { return NGX_ERROR; } dd("clear headers in cookies: %d", (int) r->headers_in.cookies.nelts); } #if 1 if (r->headers_in.cookies.nalloc == 0) { if (ngx_array_init(&r->headers_in.cookies, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK) { return NGX_ERROR; } } #endif if (ngx_http_set_header_helper(r, hv, value, &h) == NGX_ERROR) { return NGX_ERROR; } if (value->len == 0) { return NGX_OK; } dd("new cookie header: %p", h); cookie = ngx_array_push(&r->headers_in.cookies); if (cookie == NULL) { return NGX_ERROR; } *cookie = h; return NGX_OK; } static ngx_int_t ngx_http_clear_content_length_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value) { r->headers_in.content_length_n = -1; return ngx_http_clear_builtin_header(r, hv, value); } static ngx_int_t ngx_http_clear_builtin_header(ngx_http_request_t *r, ngx_http_lua_header_val_t *hv, ngx_str_t *value) { value->len = 0; return ngx_http_set_builtin_header(r, hv, value); } ngx_int_t ngx_http_lua_set_input_header(ngx_http_request_t *r, ngx_str_t key, ngx_str_t value, unsigned override) { ngx_http_lua_header_val_t hv; ngx_http_lua_set_header_t *handlers = ngx_http_lua_set_handlers; ngx_uint_t i; dd("set header value: %.*s", (int) value.len, value.data); hv.hash = ngx_hash_key_lc(key.data, key.len); hv.key = key; hv.offset = 0; hv.no_override = !override; hv.handler = NULL; for (i = 0; handlers[i].name.len; i++) { if (hv.key.len != handlers[i].name.len || ngx_strncasecmp(hv.key.data, handlers[i].name.data, handlers[i].name.len) != 0) { dd("hv key comparison: %s <> %s", handlers[i].name.data, hv.key.data); continue; } dd("Matched handler: %s %s", handlers[i].name.data, hv.key.data); hv.offset = handlers[i].offset; hv.handler = handlers[i].handler; break; } if (handlers[i].name.len == 0 && handlers[i].handler) { hv.offset = handlers[i].offset; hv.handler = handlers[i].handler; } #if 1 if (hv.handler == NULL) { return NGX_ERROR; } #endif return hv.handler(r, &hv, &value); } static ngx_int_t ngx_http_lua_rm_header_helper(ngx_list_t *l, ngx_list_part_t *cur, ngx_uint_t i) { ngx_table_elt_t *data; ngx_list_part_t *new, *part; dd("list rm item: part %p, i %d, nalloc %d", cur, (int) i, (int) l->nalloc); data = cur->elts; dd("cur: nelts %d, nalloc %d", (int) cur->nelts, (int) l->nalloc); dd("removing: \"%.*s:%.*s\"", (int) data[i].key.len, data[i].key.data, (int) data[i].value.len, data[i].value.data); if (i == 0) { dd("first entry in the part"); cur->elts = (char *) cur->elts + l->size; cur->nelts--; if (cur == l->last) { dd("being the last part"); if (cur->nelts == 0) { #if 1 part = &l->part; dd("cur=%p, part=%p, part next=%p, last=%p", cur, part, part->next, l->last); if (part == cur) { cur->elts = (char *) cur->elts - l->size; /* do nothing */ } else { while (part->next != cur) { if (part->next == NULL) { return NGX_ERROR; } part = part->next; } l->last = part; part->next = NULL; dd("part nelts: %d", (int) part->nelts); l->nalloc = part->nelts; } #endif } else { l->nalloc--; dd("nalloc decreased: %d", (int) l->nalloc); } return NGX_OK; } if (cur->nelts == 0) { dd("current part is empty"); part = &l->part; if (part == cur) { ngx_http_lua_assert(cur->next != NULL); dd("remove 'cur' from the list by rewriting 'cur': " "l->last: %p, cur: %p, cur->next: %p, part: %p", l->last, cur, cur->next, part); if (l->last == cur->next) { dd("last is cur->next"); l->part = *(cur->next); l->last = part; l->nalloc = part->nelts; } else { l->part = *(cur->next); } } else { dd("remove 'cur' from the list"); while (part->next != cur) { if (part->next == NULL) { return NGX_ERROR; } part = part->next; } part->next = cur->next; } return NGX_OK; } return NGX_OK; } if (i == cur->nelts - 1) { dd("last entry in the part"); cur->nelts--; if (cur == l->last) { l->nalloc--; } return NGX_OK; } dd("the middle entry in the part"); new = ngx_palloc(l->pool, sizeof(ngx_list_part_t)); if (new == NULL) { return NGX_ERROR; } new->elts = &data[i + 1]; new->nelts = cur->nelts - i - 1; new->next = cur->next; cur->nelts = i; cur->next = new; if (cur == l->last) { l->last = new; l->nalloc = new->nelts; } return NGX_OK; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_contentby.c0000664000000000000000000002074612305451335020270 0ustar /* * Copyright (C) Xiaozhe Wang (chaoslawful) * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_contentby.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_exception.h" #include "ngx_http_lua_cache.h" #include "ngx_http_lua_probe.h" static void ngx_http_lua_content_phase_post_read(ngx_http_request_t *r); ngx_int_t ngx_http_lua_content_by_chunk(lua_State *L, ngx_http_request_t *r) { int co_ref; ngx_int_t rc; lua_State *co; ngx_http_lua_ctx_t *ctx; ngx_http_cleanup_t *cln; ngx_http_lua_loc_conf_t *llcf; dd("content by chunk"); ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { ctx = ngx_http_lua_create_ctx(r); if (ctx == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } else { dd("reset ctx"); ngx_http_lua_reset_ctx(r, L, ctx); } ctx->entered_content_phase = 1; /* {{{ new coroutine to handle request */ co = ngx_http_lua_new_thread(r, L, &co_ref); if (co == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "lua: failed to create new coroutine to handle request"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* move code closure to new coroutine */ lua_xmove(L, co, 1); /* set closure's env table to new coroutine's globals table */ lua_pushvalue(co, LUA_GLOBALSINDEX); lua_setfenv(co, -2); /* save nginx request in coroutine globals table */ ngx_http_lua_set_req(co, r); ctx->cur_co_ctx = &ctx->entry_co_ctx; ctx->cur_co_ctx->co = co; ctx->cur_co_ctx->co_ref = co_ref; /* {{{ register request cleanup hooks */ if (ctx->cleanup == NULL) { cln = ngx_http_cleanup_add(r, 0); if (cln == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } cln->handler = ngx_http_lua_request_cleanup_handler; cln->data = ctx; ctx->cleanup = &cln->handler; } /* }}} */ ctx->context = NGX_HTTP_LUA_CONTEXT_CONTENT; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->check_client_abort) { r->read_event_handler = ngx_http_lua_rd_check_broken_connection; } else { r->read_event_handler = ngx_http_block_reading; } rc = ngx_http_lua_run_thread(L, r, ctx, 0); if (rc == NGX_ERROR || rc >= NGX_OK) { return rc; } if (rc == NGX_AGAIN) { return ngx_http_lua_content_run_posted_threads(L, r, ctx, 0); } if (rc == NGX_DONE) { return ngx_http_lua_content_run_posted_threads(L, r, ctx, 1); } return NGX_OK; } void ngx_http_lua_content_wev_handler(ngx_http_request_t *r) { ngx_http_lua_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return; } (void) ctx->resume_handler(r); } ngx_int_t ngx_http_lua_content_handler(ngx_http_request_t *r) { ngx_http_lua_loc_conf_t *llcf; ngx_http_lua_ctx_t *ctx; ngx_int_t rc; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua content handler, uri:\"%V\" c:%ud", &r->uri, r->main->count); llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->content_handler == NULL) { dd("no content handler found"); return NGX_DECLINED; } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); dd("ctx = %p", ctx); if (ctx == NULL) { ctx = ngx_http_lua_create_ctx(r); if (ctx == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } } dd("entered? %d", (int) ctx->entered_content_phase); if (ctx->waiting_more_body) { return NGX_DONE; } if (ctx->entered_content_phase) { dd("calling wev handler"); rc = ctx->resume_handler(r); dd("wev handler returns %d", (int) rc); return rc; } if (llcf->force_read_body && !ctx->read_body_done) { r->request_body_in_single_buf = 1; r->request_body_in_persistent_file = 1; r->request_body_in_clean_file = 1; rc = ngx_http_read_client_request_body(r, ngx_http_lua_content_phase_post_read); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { #if (nginx_version < 1002006) || \ (nginx_version >= 1003000 && nginx_version < 1003009) r->main->count--; #endif return rc; } if (rc == NGX_AGAIN) { ctx->waiting_more_body = 1; return NGX_DONE; } } dd("setting entered"); ctx->entered_content_phase = 1; dd("calling content handler"); return llcf->content_handler(r); } /* post read callback for the content phase */ static void ngx_http_lua_content_phase_post_read(ngx_http_request_t *r) { ngx_http_lua_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); ctx->read_body_done = 1; if (ctx->waiting_more_body) { ctx->waiting_more_body = 0; ngx_http_lua_finalize_request(r, ngx_http_lua_content_handler(r)); } else { r->main->count--; } } ngx_int_t ngx_http_lua_content_handler_file(ngx_http_request_t *r) { lua_State *L; ngx_int_t rc; u_char *script_path; ngx_http_lua_loc_conf_t *llcf; ngx_str_t eval_src; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (ngx_http_complex_value(r, &llcf->content_src, &eval_src) != NGX_OK) { return NGX_ERROR; } script_path = ngx_http_lua_rebase_path(r->pool, eval_src.data, eval_src.len); if (script_path == NULL) { return NGX_ERROR; } L = ngx_http_lua_get_lua_vm(r, NULL); /* load Lua script file (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadfile(L, script_path, llcf->content_src_key); if (rc != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* make sure we have a valid code chunk */ assert(lua_isfunction(L, -1)); return ngx_http_lua_content_by_chunk(L, r); } ngx_int_t ngx_http_lua_content_handler_inline(ngx_http_request_t *r) { lua_State *L; ngx_int_t rc; ngx_http_lua_loc_conf_t *llcf; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); L = ngx_http_lua_get_lua_vm(r, NULL); /* load Lua inline script (w/ cache) sp = 1 */ rc = ngx_http_lua_cache_loadbuffer(L, llcf->content_src.value.data, llcf->content_src.value.len, llcf->content_src_key, "content_by_lua"); if (rc != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } return ngx_http_lua_content_by_chunk(L, r); } ngx_int_t ngx_http_lua_content_run_posted_threads(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx, int n) { ngx_int_t rc; ngx_http_lua_posted_thread_t *pt; dd("run posted threads: %p", ctx->posted_threads); for ( ;; ) { pt = ctx->posted_threads; if (pt == NULL) { goto done; } ctx->posted_threads = pt->next; ngx_http_lua_probe_run_posted_thread(r, pt->co_ctx->co, (int) pt->co_ctx->co_status); dd("posted thread status: %d", pt->co_ctx->co_status); if (pt->co_ctx->co_status != NGX_HTTP_LUA_CO_RUNNING) { continue; } ctx->cur_co_ctx = pt->co_ctx; rc = ngx_http_lua_run_thread(L, r, ctx, 0); if (rc == NGX_AGAIN) { continue; } if (rc == NGX_DONE) { n++; continue; } if (rc == NGX_OK) { while (n > 0) { ngx_http_lua_finalize_request(r, NGX_DONE); n--; } return NGX_OK; } /* rc == NGX_ERROR || rc > NGX_OK */ return rc; } done: if (n == 1) { return NGX_DONE; } if (n == 0) { r->main->count++; return NGX_DONE; } /* n > 1 */ do { ngx_http_lua_finalize_request(r, NGX_DONE); } while (--n > 1); return NGX_DONE; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/src/ngx_http_lua_req_body.c0000664000000000000000000007146512305451335020073 0ustar /* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_lua_req_body.h" #include "ngx_http_lua_util.h" #include "ngx_http_lua_headers_in.h" static int ngx_http_lua_ngx_req_read_body(lua_State *L); static void ngx_http_lua_req_body_post_read(ngx_http_request_t *r); static int ngx_http_lua_ngx_req_discard_body(lua_State *L); static int ngx_http_lua_ngx_req_get_body_data(lua_State *L); static int ngx_http_lua_ngx_req_get_body_file(lua_State *L); static int ngx_http_lua_ngx_req_set_body_data(lua_State *L); static void ngx_http_lua_pool_cleanup_file(ngx_pool_t *p, ngx_fd_t fd); static int ngx_http_lua_ngx_req_set_body_file(lua_State *L); static int ngx_http_lua_ngx_req_init_body(lua_State *L); static int ngx_http_lua_ngx_req_append_body(lua_State *L); static int ngx_http_lua_ngx_req_body_finish(lua_State *L); static ngx_int_t ngx_http_lua_write_request_body(ngx_http_request_t *r, ngx_chain_t *body); static ngx_int_t ngx_http_lua_read_body_resume(ngx_http_request_t *r); static void ngx_http_lua_req_body_cleanup(void *data); void ngx_http_lua_inject_req_body_api(lua_State *L) { lua_pushcfunction(L, ngx_http_lua_ngx_req_read_body); lua_setfield(L, -2, "read_body"); lua_pushcfunction(L, ngx_http_lua_ngx_req_discard_body); lua_setfield(L, -2, "discard_body"); lua_pushcfunction(L, ngx_http_lua_ngx_req_get_body_data); lua_setfield(L, -2, "get_body_data"); lua_pushcfunction(L, ngx_http_lua_ngx_req_get_body_file); lua_setfield(L, -2, "get_body_file"); lua_pushcfunction(L, ngx_http_lua_ngx_req_set_body_data); lua_setfield(L, -2, "set_body_data"); lua_pushcfunction(L, ngx_http_lua_ngx_req_set_body_file); lua_setfield(L, -2, "set_body_file"); lua_pushcfunction(L, ngx_http_lua_ngx_req_init_body); lua_setfield(L, -2, "init_body"); lua_pushcfunction(L, ngx_http_lua_ngx_req_append_body); lua_setfield(L, -2, "append_body"); lua_pushcfunction(L, ngx_http_lua_ngx_req_body_finish); lua_setfield(L, -2, "finish_body"); } static int ngx_http_lua_ngx_req_read_body(lua_State *L) { ngx_http_request_t *r; int n; ngx_int_t rc; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; n = lua_gettop(L); if (n != 0) { return luaL_error(L, "expecting 0 arguments but seen %d", n); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "request object not found"); } r->request_body_in_single_buf = 1; r->request_body_in_persistent_file = 1; r->request_body_in_clean_file = 1; #if 1 if (r->request_body_in_file_only) { r->request_body_file_log_level = 0; } #endif ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return luaL_error(L, "no ctx found"); } ngx_http_lua_check_context(L, ctx, NGX_HTTP_LUA_CONTEXT_REWRITE | NGX_HTTP_LUA_CONTEXT_ACCESS | NGX_HTTP_LUA_CONTEXT_CONTENT); coctx = ctx->cur_co_ctx; if (coctx == NULL) { return luaL_error(L, "no co ctx found"); } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua start to read buffered request body"); rc = ngx_http_read_client_request_body(r, ngx_http_lua_req_body_post_read); #if (nginx_version < 1002006) || \ (nginx_version >= 1003000 && nginx_version < 1003009) r->main->count--; #endif if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { ctx->exit_code = rc; ctx->exited = 1; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http read client request body returned error code %i, " "exitting now", rc); return lua_yield(L, 0); } #if (nginx_version >= 1002006 && nginx_version < 1003000) || \ nginx_version >= 1003009 r->main->count--; dd("decrement r->main->count: %d", (int) r->main->count); #endif if (rc == NGX_AGAIN) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua read buffered request body requires I/O " "interruptions"); ctx->waiting_more_body = 1; ctx->downstream_co_ctx = coctx; coctx->cleanup = ngx_http_lua_req_body_cleanup; coctx->data = r; return lua_yield(L, 0); } /* rc == NGX_OK */ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua has read buffered request body in a single run"); return 0; } static void ngx_http_lua_req_body_post_read(ngx_http_request_t *r) { ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx; ngx_http_lua_loc_conf_t *llcf; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua req body post read, c:%ud", r->main->count); ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx->waiting_more_body) { ctx->waiting_more_body = 0; coctx = ctx->downstream_co_ctx; ctx->cur_co_ctx = coctx; coctx->cleanup = NULL; llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); if (llcf->check_client_abort) { r->read_event_handler = ngx_http_lua_rd_check_broken_connection; } else { r->read_event_handler = ngx_http_block_reading; } if (ctx->entered_content_phase) { (void) ngx_http_lua_read_body_resume(r); } else { ctx->resume_handler = ngx_http_lua_read_body_resume; ngx_http_core_run_phases(r); } } } static int ngx_http_lua_ngx_req_discard_body(lua_State *L) { ngx_http_request_t *r; ngx_int_t rc; int n; n = lua_gettop(L); if (n != 0) { return luaL_error(L, "expecting 0 arguments but seen %d", n); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "request object not found"); } ngx_http_lua_check_fake_request(L, r); rc = ngx_http_discard_request_body(r); if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return luaL_error(L, "failed to discard request body"); } return 0; } static int ngx_http_lua_ngx_req_get_body_data(lua_State *L) { ngx_http_request_t *r; int n; size_t len; ngx_chain_t *cl; u_char *p; u_char *buf; n = lua_gettop(L); if (n != 0) { return luaL_error(L, "expecting 0 arguments but seen %d", n); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "request object not found"); } ngx_http_lua_check_fake_request(L, r); if (r->request_body == NULL || r->request_body->temp_file || r->request_body->bufs == NULL) { lua_pushnil(L); return 1; } cl = r->request_body->bufs; if (cl->next == NULL) { len = cl->buf->last - cl->buf->pos; if (len == 0) { lua_pushnil(L); return 1; } lua_pushlstring(L, (char *) cl->buf->pos, len); return 1; } /* found multi-buffer body */ len = 0; for (; cl; cl = cl->next) { dd("body chunk len: %d", (int) ngx_buf_size(cl->buf)); len += cl->buf->last - cl->buf->pos; } if (len == 0) { lua_pushnil(L); return 1; } buf = (u_char *) lua_newuserdata(L, len); p = buf; for (cl = r->request_body->bufs; cl; cl = cl->next) { p = ngx_copy(p, cl->buf->pos, cl->buf->last - cl->buf->pos); } lua_pushlstring(L, (char *) buf, len); return 1; } static int ngx_http_lua_ngx_req_get_body_file(lua_State *L) { ngx_http_request_t *r; int n; n = lua_gettop(L); if (n != 0) { return luaL_error(L, "expecting 0 arguments but seen %d", n); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "request object not found"); } ngx_http_lua_check_fake_request(L, r); if (r->request_body == NULL || r->request_body->temp_file == NULL) { lua_pushnil(L); return 1; } dd("XXX file directio: %u, f:%u, m:%u, t:%u, end - pos %d, size %d", r->request_body->temp_file->file.directio, r->request_body->bufs->buf->in_file, r->request_body->bufs->buf->memory, r->request_body->bufs->buf->temporary, (int) (r->request_body->bufs->buf->end - r->request_body->bufs->buf->pos), (int) ngx_buf_size(r->request_body->bufs->buf)); lua_pushlstring(L, (char *) r->request_body->temp_file->file.name.data, r->request_body->temp_file->file.name.len); return 1; } static int ngx_http_lua_ngx_req_set_body_data(lua_State *L) { ngx_http_request_t *r; int n; ngx_http_request_body_t *rb; ngx_temp_file_t *tf; ngx_buf_t *b; ngx_str_t body, key, value; #if 1 ngx_int_t rc; #endif ngx_chain_t *cl; ngx_buf_tag_t tag; n = lua_gettop(L); if (n != 1) { return luaL_error(L, "expecting 1 arguments but seen %d", n); } body.data = (u_char *) luaL_checklstring(L, 1, &body.len); r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "request object not found"); } ngx_http_lua_check_fake_request(L, r); if (r->discard_body) { return luaL_error(L, "request body already discarded asynchronously"); } if (r->request_body == NULL) { return luaL_error(L, "request body not read yet"); } rb = r->request_body; tag = (ngx_buf_tag_t) &ngx_http_lua_module; tf = rb->temp_file; if (tf) { if (tf->file.fd != NGX_INVALID_FILE) { dd("cleaning temp file %.*s", (int) tf->file.name.len, tf->file.name.data); ngx_http_lua_pool_cleanup_file(r->pool, tf->file.fd); tf->file.fd = NGX_INVALID_FILE; dd("temp file cleaned: %.*s", (int) tf->file.name.len, tf->file.name.data); } rb->temp_file = NULL; } if (body.len == 0) { if (rb->bufs) { for (cl = rb->bufs; cl; cl = cl->next) { if (cl->buf->tag == tag && cl->buf->temporary) { dd("free old request body buffer: size:%d", (int) ngx_buf_size(cl->buf)); ngx_pfree(r->pool, cl->buf->start); cl->buf->tag = (ngx_buf_tag_t) NULL; cl->buf->temporary = 0; } } } rb->bufs = NULL; rb->buf = NULL; dd("request body is set to empty string"); goto set_header; } if (rb->bufs) { for (cl = rb->bufs; cl; cl = cl->next) { if (cl->buf->tag == tag && cl->buf->temporary) { dd("free old request body buffer: size:%d", (int) ngx_buf_size(cl->buf)); ngx_pfree(r->pool, cl->buf->start); cl->buf->tag = (ngx_buf_tag_t) NULL; cl->buf->temporary = 0; } } rb->bufs->next = NULL; b = rb->bufs->buf; ngx_memzero(b, sizeof(ngx_buf_t)); b->temporary = 1; b->tag = tag; b->start = ngx_palloc(r->pool, body.len); if (b->start == NULL) { return luaL_error(L, "out of memory"); } b->end = b->start + body.len; b->pos = b->start; b->last = ngx_copy(b->pos, body.data, body.len); } else { rb->bufs = ngx_alloc_chain_link(r->pool); if (rb->bufs == NULL) { return luaL_error(L, "out of memory"); } rb->bufs->next = NULL; b = ngx_create_temp_buf(r->pool, body.len); b->tag = tag; b->last = ngx_copy(b->pos, body.data, body.len); rb->bufs->buf = b; rb->buf = b; } set_header: /* override input header Content-Length (value must be null terminated) */ value.data = ngx_palloc(r->pool, NGX_SIZE_T_LEN + 1); if (value.data == NULL) { return luaL_error(L, "out of memory"); } value.len = ngx_sprintf(value.data, "%uz", body.len) - value.data; value.data[value.len] = '\0'; dd("setting request Content-Length to %.*s (%d)", (int) value.len, value.data, (int) body.len); r->headers_in.content_length_n = body.len; if (r->headers_in.content_length) { r->headers_in.content_length->value.data = value.data; r->headers_in.content_length->value.len = value.len; } else { ngx_str_set(&key, "Content-Length"); rc = ngx_http_lua_set_input_header(r, key, value, 1 /* override */); if (rc != NGX_OK) { return luaL_error(L, "failed to reset the Content-Length " "input header"); } } return 0; } static int ngx_http_lua_ngx_req_init_body(lua_State *L) { ngx_http_request_t *r; int n; ngx_http_request_body_t *rb; size_t size; lua_Integer num; #if 1 ngx_temp_file_t *tf; #endif ngx_http_core_loc_conf_t *clcf; n = lua_gettop(L); if (n != 1 && n != 0) { return luaL_error(L, "expecting 0 or 1 argument but seen %d", n); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ngx_http_lua_check_fake_request(L, r); if (r->discard_body) { return luaL_error(L, "request body already discarded asynchronously"); } if (r->request_body == NULL) { return luaL_error(L, "request body not read yet"); } if (n == 1) { num = luaL_checkinteger(L, 1); if (num <= 0) { return luaL_error(L, "bad size argument: %d", (int) num); } size = (size_t) num; } else { clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); size = clcf->client_body_buffer_size; size += size >> 2; /* avoid allocating an unnecessary large buffer */ if (size > (size_t) r->headers_in.content_length_n) { size = (size_t) r->headers_in.content_length_n; } } rb = r->request_body; #if 1 tf = rb->temp_file; if (tf) { if (tf->file.fd != NGX_INVALID_FILE) { dd("cleaning temp file %.*s", (int) tf->file.name.len, tf->file.name.data); ngx_http_lua_pool_cleanup_file(r->pool, tf->file.fd); ngx_memzero(tf, sizeof(ngx_temp_file_t)); tf->file.fd = NGX_INVALID_FILE; dd("temp file cleaned: %.*s", (int) tf->file.name.len, tf->file.name.data); } rb->temp_file = NULL; } #endif r->request_body_in_clean_file = 1; r->headers_in.content_length_n = 0; rb->buf = ngx_create_temp_buf(r->pool, size); if (rb->buf == NULL) { return luaL_error(L, "out of memory"); } rb->bufs = ngx_alloc_chain_link(r->pool); if (rb->bufs == NULL) { return luaL_error(L, "out of memory"); } rb->bufs->buf = rb->buf; rb->bufs->next = NULL; return 0; } static int ngx_http_lua_ngx_req_append_body(lua_State *L) { ngx_http_request_t *r; int n; ngx_http_request_body_t *rb; ngx_str_t body; size_t size, rest; size_t offset = 0; n = lua_gettop(L); if (n != 1) { return luaL_error(L, "expecting 1 arguments but seen %d", n); } body.data = (u_char *) luaL_checklstring(L, 1, &body.len); r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ngx_http_lua_check_fake_request(L, r); if (r->request_body == NULL || r->request_body->buf == NULL || r->request_body->bufs == NULL) { return luaL_error(L, "request_body not initalized"); } rb = r->request_body; rest = body.len; while (rest > 0) { if (rb->buf->last == rb->buf->end) { if (ngx_http_lua_write_request_body(r, rb->bufs) != NGX_OK) { return luaL_error(L, "fail to write file"); } rb->buf->last = rb->buf->start; } size = rb->buf->end - rb->buf->last; if (size > rest) { size = rest; } ngx_memcpy(rb->buf->last, body.data + offset, size); rb->buf->last += size; rest -= size; offset += size; r->headers_in.content_length_n += size; } return 0; } static int ngx_http_lua_ngx_req_body_finish(lua_State *L) { ngx_http_request_t *r; int n; ngx_http_request_body_t *rb; ngx_buf_t *b; size_t size; ngx_str_t value; ngx_str_t key; ngx_int_t rc; n = lua_gettop(L); if (n != 0) { return luaL_error(L, "expecting 0 argument but seen %d", n); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ngx_http_lua_check_fake_request(L, r); if (r->request_body == NULL || r->request_body->buf == NULL || r->request_body->bufs == NULL) { return luaL_error(L, "request_body not initalized"); } rb = r->request_body; if (rb->temp_file) { /* save the last part */ if (ngx_http_lua_write_request_body(r, rb->bufs) != NGX_OK) { return luaL_error(L, "fail to write file"); } b = ngx_calloc_buf(r->pool); if (b == NULL) { return luaL_error(L, "out of memory"); } b->in_file = 1; b->file_pos = 0; b->file_last = rb->temp_file->file.offset; b->file = &rb->temp_file->file; if (rb->bufs->next) { rb->bufs->next->buf = b; } else { rb->bufs->buf = b; } } /* override input header Content-Length (value must be null terminated) */ value.data = ngx_palloc(r->pool, NGX_SIZE_T_LEN + 1); if (value.data == NULL) { return luaL_error(L, "out of memory"); } size = (size_t) r->headers_in.content_length_n; value.len = ngx_sprintf(value.data, "%uz", size) - value.data; value.data[value.len] = '\0'; dd("setting request Content-Length to %.*s (%d)", (int) value.len, value.data, (int) size); if (r->headers_in.content_length) { r->headers_in.content_length->value.data = value.data; r->headers_in.content_length->value.len = value.len; } else { ngx_str_set(&key, "Content-Length"); rc = ngx_http_lua_set_input_header(r, key, value, 1 /* override */); if (rc != NGX_OK) { return luaL_error(L, "failed to reset the Content-Length " "input header"); } } return 0; } static void ngx_http_lua_pool_cleanup_file(ngx_pool_t *p, ngx_fd_t fd) { ngx_pool_cleanup_t *c; ngx_pool_cleanup_file_t *cf; for (c = p->cleanup; c; c = c->next) { if (c->handler == ngx_pool_cleanup_file || c->handler == ngx_pool_delete_file) { cf = c->data; if (cf->fd == fd) { c->handler(cf); c->handler = NULL; return; } } } } static int ngx_http_lua_ngx_req_set_body_file(lua_State *L) { u_char *p; ngx_http_request_t *r; int n; ngx_http_request_body_t *rb; ngx_temp_file_t *tf; ngx_buf_t *b; ngx_str_t name; ngx_int_t rc; int clean; ngx_open_file_info_t of; ngx_str_t key, value; ngx_pool_cleanup_t *cln; ngx_pool_cleanup_file_t *clnf; ngx_err_t err; ngx_chain_t *cl; ngx_buf_tag_t tag; n = lua_gettop(L); if (n != 1 && n != 2) { return luaL_error(L, "expecting 1 or 2 arguments but seen %d", n); } p = (u_char *) luaL_checklstring(L, 1, &name.len); r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ngx_http_lua_check_fake_request(L, r); if (r->discard_body) { return luaL_error(L, "request body already discarded asynchronously"); } if (r->request_body == NULL) { return luaL_error(L, "request body not read yet"); } name.data = ngx_palloc(r->pool, name.len + 1); if (name.data == NULL) { return luaL_error(L, "out of memory"); } ngx_memcpy(name.data, p, name.len); name.data[name.len] = '\0'; if (n == 2) { luaL_checktype(L, 2, LUA_TBOOLEAN); clean = lua_toboolean(L, 2); } else { clean = 0; } dd("clean: %d", (int) clean); rb = r->request_body; /* clean up existing r->request_body->bufs (if any) */ tag = (ngx_buf_tag_t) &ngx_http_lua_module; if (rb->bufs) { dd("XXX reusing buf"); for (cl = rb->bufs; cl; cl = cl->next) { if (cl->buf->tag == tag && cl->buf->temporary) { dd("free old request body buffer: size:%d", (int) ngx_buf_size(cl->buf)); ngx_pfree(r->pool, cl->buf->start); cl->buf->tag = (ngx_buf_tag_t) NULL; cl->buf->temporary = 0; } } rb->bufs->next = NULL; b = rb->bufs->buf; ngx_memzero(b, sizeof(ngx_buf_t)); b->tag = tag; rb->buf = NULL; } else { dd("XXX creating new buf"); rb->bufs = ngx_alloc_chain_link(r->pool); if (rb->bufs == NULL) { return luaL_error(L, "out of memory"); } rb->bufs->next = NULL; b = ngx_calloc_buf(r->pool); if (b == NULL) { return luaL_error(L, "out of memory"); } b->tag = tag; rb->bufs->buf = b; rb->buf = NULL; } b->last_in_chain = 1; /* just make r->request_body->temp_file a bare stub */ tf = rb->temp_file; if (tf) { if (tf->file.fd != NGX_INVALID_FILE) { dd("cleaning temp file %.*s", (int) tf->file.name.len, tf->file.name.data); ngx_http_lua_pool_cleanup_file(r->pool, tf->file.fd); ngx_memzero(tf, sizeof(ngx_temp_file_t)); tf->file.fd = NGX_INVALID_FILE; dd("temp file cleaned: %.*s", (int) tf->file.name.len, tf->file.name.data); } } else { tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); if (tf == NULL) { return luaL_error(L, "out of memory"); } tf->file.fd = NGX_INVALID_FILE; rb->temp_file = tf; } /* read the file info and construct an in-file buf */ ngx_memzero(&of, sizeof(ngx_open_file_info_t)); of.directio = NGX_OPEN_FILE_DIRECTIO_OFF; if (ngx_http_lua_open_and_stat_file(name.data, &of, r->connection->log) != NGX_OK) { return luaL_error(L, "%s \"%s\" failed", of.failed, name.data); } dd("XXX new body file fd: %d", of.fd); tf->file.fd = of.fd; tf->file.name = name; tf->file.log = r->connection->log; tf->file.directio = 0; if (of.size == 0) { if (clean) { if (ngx_delete_file(name.data) == NGX_FILE_ERROR) { err = ngx_errno; if (err != NGX_ENOENT) { ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, ngx_delete_file_n " \"%s\" failed", name.data); } } } if (ngx_close_file(of.fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, ngx_close_file_n " \"%s\" failed", name.data); } r->request_body->bufs = NULL; r->request_body->buf = NULL; goto set_header; } /* register file cleanup hook */ cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t)); if (cln == NULL) { return luaL_error(L, "out of memory"); } cln->handler = clean ? ngx_pool_delete_file : ngx_pool_cleanup_file; clnf = cln->data; clnf->fd = of.fd; clnf->name = name.data; clnf->log = r->pool->log; b->file = &tf->file; if (b->file == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } dd("XXX file size: %d", (int) of.size); b->file_pos = 0; b->file_last = of.size; b->in_file = 1; dd("buf file: %p, f:%u", b->file, b->in_file); set_header: /* override input header Content-Length (value must be null terminated) */ value.data = ngx_palloc(r->pool, NGX_OFF_T_LEN + 1); if (value.data == NULL) { return luaL_error(L, "out of memory"); } value.len = ngx_sprintf(value.data, "%O", of.size) - value.data; value.data[value.len] = '\0'; r->headers_in.content_length_n = of.size; if (r->headers_in.content_length) { r->headers_in.content_length->value.data = value.data; r->headers_in.content_length->value.len = value.len; } else { ngx_str_set(&key, "Content-Length"); rc = ngx_http_lua_set_input_header(r, key, value, 1 /* override */); if (rc != NGX_OK) { return luaL_error(L, "failed to reset the Content-Length " "input header"); } } return 0; } static ngx_int_t ngx_http_lua_write_request_body(ngx_http_request_t *r, ngx_chain_t *body) { ssize_t n; ngx_temp_file_t *tf; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; rb = r->request_body; if (rb->temp_file == NULL) { tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); if (tf == NULL) { return NGX_ERROR; } clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); tf->file.fd = NGX_INVALID_FILE; tf->file.log = r->connection->log; tf->path = clcf->client_body_temp_path; tf->pool = r->pool; tf->warn = "a client request body is buffered to a temporary file"; tf->log_level = r->request_body_file_log_level; tf->persistent = 1; tf->clean = 1; if (r->request_body_file_group_access) { tf->access = 0660; } rb->temp_file = tf; if (body == NULL) { /* empty body with r->request_body_in_file_only */ if (ngx_create_temp_file(&tf->file, tf->path, tf->pool, tf->persistent, tf->clean, tf->access) != NGX_OK) { return NGX_ERROR; } return NGX_OK; } } n = ngx_write_chain_to_temp_file(rb->temp_file, body); /* TODO: n == 0 or not complete and level event */ if (n == NGX_ERROR) { return NGX_ERROR; } rb->temp_file->offset += n; return NGX_OK; } static ngx_int_t ngx_http_lua_read_body_resume(ngx_http_request_t *r) { lua_State *vm; ngx_int_t rc; ngx_connection_t *c; ngx_http_lua_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); ctx->resume_handler = ngx_http_lua_wev_handler; c = r->connection; vm = ngx_http_lua_get_lua_vm(r, ctx); rc = ngx_http_lua_run_thread(vm, r, ctx, 0); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua run thread returned %d", rc); if (rc == NGX_AGAIN) { return ngx_http_lua_run_posted_threads(c, vm, r, ctx); } if (rc == NGX_DONE) { ngx_http_lua_finalize_request(r, NGX_DONE); return ngx_http_lua_run_posted_threads(c, vm, r, ctx); } if (ctx->entered_content_phase) { ngx_http_lua_finalize_request(r, rc); return NGX_DONE; } return rc; } static void ngx_http_lua_req_body_cleanup(void *data) { ngx_http_request_t *r; ngx_http_lua_ctx_t *ctx; ngx_http_lua_co_ctx_t *coctx = data; r = coctx->data; if (r == NULL) { return; } if (r->connection->read->timer_set) { ngx_del_timer(r->connection->read); } ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); if (ctx == NULL) { return; } ctx->waiting_more_body = 0; r->keepalive = 0; } /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ debian/modules/nginx-lua/misc/0000775000000000000000000000000012305451341013473 5ustar debian/modules/nginx-lua/misc/recv-until-pm/0000775000000000000000000000000012305451341016175 5ustar debian/modules/nginx-lua/misc/recv-until-pm/Makefile0000664000000000000000000000003112305451335017632 0ustar test: prove -Ilib -r t debian/modules/nginx-lua/misc/recv-until-pm/t/0000775000000000000000000000000012305451341016440 5ustar debian/modules/nginx-lua/misc/recv-until-pm/t/sanity.t0000664000000000000000000000310512305451335020136 0ustar # vi:ft= use 5.10.1; use Test::Base; use RecvUntil; plan tests => 1 * blocks(); run { my $block = shift; my $name = $block->name; my $pat = $block->pat // die "$name: No --- pat found"; my $txt = $block->txt // die "$name: No --- txt found"; my $expected = $block->out // die "$name: No --- out found"; my $it = RecvUntil::recv_until($pat); is $it->($txt), $expected, "$name: output ok"; }; __DATA__ === TEST 1: --- pat: abcabd --- txt: abcabcabd --- out: abc === TEST 2: --- pat: aa --- txt: abcabcaad --- out: abcabc === TEST 3: --- pat: ab --- txt: bbcabcaad --- out: bbc === TEST 4: --- pat: aaa --- txt: abaabcaaaef --- out: abaabc === TEST 5: --- pat: aaaaad --- txt: baaaaaaaaeaaaaaaadf --- out: baaaaaaaaeaa === TEST 6: --- pat: abacadae --- txt: a --- out: === TEST 7: --- pat: abacadae --- txt: ababacadae --- out: ab === TEST 8: --- pat: abacadae --- txt: abacabacadae --- out: abac === TEST 9: --- pat: abacadae --- txt: abaabacadae --- out: aba === TEST 10: --- pat: abacadae --- txt: abacadabacadae --- out: abacad === TEST 11: --- pat: abcabdabcabe --- txt: abcabdabcabdabcabe --- out: abcabd === TEST 12: --- pat: abcabdabcabe --- txt: abcabdabcabcabdabcabe --- out: abcabdabc === TEST 13: --- pat: abcabdabcabe --- txt: abcabcabdabcabe --- out: abc === TEST 14: --- pat: abcabdabcabe --- txt: ababcabdabcabe --- out: ab === TEST 15: --- pat: abcdef --- txt: abcabcdef --- out: abc === TEST 16: --- pat: -- abc --- txt: ---- abc --- out: -- === TEST 17: --- pat: yz--ababyz --- txt: --- out: -- --- SKIP debian/modules/nginx-lua/misc/recv-until-pm/lib/0000775000000000000000000000000012305451341016743 5ustar debian/modules/nginx-lua/misc/recv-until-pm/lib/RecvUntil.pm0000775000000000000000000000746212305451335021233 0ustar package RecvUntil; use strict; use warnings; sub recv_until { my ($pat) = @_; my $len = length $pat; my @backtracks; for (my $i = 1; $i <= $len - 1; $i++) { my $matched_prefix_len = 1; while ($matched_prefix_len <= $len - $i - 1) { #while (1) { #my $left = $len - $i; #warn "left: $i: $len: ", $len - 1 - $i, "\n"; #warn "matched_prefix_len: $matched_prefix_len\n"; #while (1) { my $prefix = substr($pat, 0, $matched_prefix_len); my $next = substr($pat, $matched_prefix_len, 1); my $prefix2 = substr($pat, $i, $matched_prefix_len); my $next2 = substr($pat, $i + $matched_prefix_len, 1); #warn "$i: global prefix $prefix $next\n"; #warn "$i: local prefix $prefix2 $next2\n"; if ($prefix2 eq $prefix) { if ($next2 eq $next) { $matched_prefix_len++; next; } #warn "$matched_prefix_len: $prefix: found match at $i (next $next, next2 $next2)\n"; my $cur_state = $i + $matched_prefix_len; my $new_state = $matched_prefix_len + 1; my $matched = substr($pat, 0, $cur_state); my $chain = $backtracks[$cur_state - 2]; if (!$chain) { $chain = []; $backtracks[$cur_state - 2] = $chain; } my $found = 0; for my $rec (@$chain) { if ($rec->{char} eq $next) { $found = 1; if ($rec->{new_state} < $new_state) { warn "overriding...\n"; $rec->{new_state} = $new_state; } } } if (!$found) { warn "on state $cur_state ($matched), if next is '$next', ", "then backtrack to state $new_state ($prefix$next)\n"; push @$chain, { char => $next, new_state => $new_state }; } #if ($matched_prefix_len > 1) { #$i += $matched_prefix_len - 1; #} last; } last; } } return sub { my ($txt) = @_; my $max_state = length $pat; my $len = length $txt; my $state = 0; my $ret = ''; for (my $i = 0; $i < $len; $i++) { # read the char my $c = substr($txt, $i, 1); #warn "$state: read char at $i: $c\n"; #warn "matched: $ret\n"; my $expected = substr($pat, $state, 1); if ($expected eq $c) { #warn "matched the char in pattern.\n"; $state++; if ($state == $max_state) { last; } next; } if ($state == 0) { #warn "did not match the first char in pattern\n"; $ret .= $c; next; } my $old_state; my $matched; my $chain = $backtracks[$state - 2]; for my $rec (@$chain) { if ($rec->{char} eq $c) { $old_state = $state; $state = $rec->{new_state}; #warn "matched the char for backtracking to state $state\n"; $matched = 1; last; } } if (!$matched) { $ret .= substr($pat, 0, $state); $state = 0; redo; } $ret .= substr($pat, 0, $old_state + 1 - $state); next; } return $ret; }; } 1; debian/modules/nginx-lua/Changes0000664000000000000000000001010712305451334014034 0ustar 0.2.0 - 5 July 2011 * now we support ngx.var[1], ngx.var[2], and etc to refer to the nginx regex capturing variables \$1, \$2, and etc in Lua. this resolved github issue #43. thanks Tobia Conforto for reporting it. * now we use the same value overriding mechanism as ngx_rewrite's set command for ngx.var.VAR = new_value. Assigning values to special variables like $limit_rate and $args should now work; also writing to built-in variables that are not changeable (like $arg_PARAMETER) will result in a 500 error page, as expected, now. thanks Richard Kearsley for reporting it. * fixed the lua_code_cache off warning when the lua_code_cache is explicitly on. thanks Feng Xingguo. * applied the patch from cyberty to add ngx.http_time() function to expose the nginx core function ngx_http_time to the Lua land. * fixed an issue on i386: we now use off_t consistently. mixing it with size_t on 32-bit systems can cause Bad Things. this fixed github issue #42. thanks moodydeath. * fixed an issue on i386: fixed a formatter mismatch issue in ngx_http_echo_adjust_subrequest. thanks Wang Bin. This caused incorrect subrequest Content-Length header when a body is specified. * now in the subrequest capturing processor, we worked around an issue in ngx_http_static_module that when it issues 301 redirect for directory access w/o a trailing slash, it does not inject r->headers_out.location into the r->headers_out.headers list. thanks moodydeath for reporting it in the discussion of github issue #41. * fixed a bug in ngx.location.capture() and ngx.location.capture_multi() that we could not capture locations with internal redirections in them. thanks moodydeath for reporting it in github issue #41. * fixed redundant last chunk issue for ngx.exec() invocation at rewrite and access phases: we should quit the current core_run_phases cycle; this also fixed github issue #40: 2 Subrequest calls when using access_by_lua, ngx.exec and echo_location. * fixed ngx.exit(status) where status >= 200 and status < 300 for access_by_lua* and rewrite_by_lua*: it should quit the whole request altegother and skip all those subsequent phase handlers (if any). thanks moodydeath for reporting it. * fixed github issue #39: setting differnt response headers in Lua with common prefix might interfere with each other. thanks moodydeath. * fixed GitHub issue #38: request headers did not forward to subrequests when the "method" or "body" option is explicitly specified by a non-nil value for ngx.location.capture(). thanks Richard Kearsley. * fixed a bug in output header set; we should always set the header->hash to 1. thanks moodydeath for reporting it. * fixed spots that trigger the "variable set but not used" warning issued by gcc 4.6.0. * now we turn the ngx.req.header table into an ngx.req.get_headers() function; we also added ngx.req.set_header(name, value) and ngx.req.clear_header(name). thanks moodydeath. * now we make ngx_devel_kit (NDK) optional. thanks Kirill A. Korinskiy. * removed a duplicate definition of the ngx_str_set macro caught by ctags; also fixed a warning thrown by gcc -O3 on Mac OS X 10.6. * added patch to use PCRE related Lua extensions in ngx_lua (chaoslawful) * now we change the way we process HTTP 1.0 requests by automatically buffering all the user outputs generated by ngx.print()/ngx.say() calls, which is much more natural than the old broken way. * fixed the "ngx.exec() after ngx.location.capture() hanging" bug for rewrite_by_lua* and access_by_lua* as well. thanks Wendal Chen. * applied a patch from moodydeath to introduce the "ngx.is_subrequest" attribute. * now we encourage use of the client_body_in_single_buffer directive instead of big client_body_buffer_size when lua_need_request_body is turned on. * fixed the config script and added extra linking options needed by LuaJIT in 64-bit Mac OS X. * fixed the zero size alert caused by ngx.print("") in Lua. * now we always allocate r->request_body for subrequests when the method option is specified for ngx.location.capture*. this prevents accidental inheritance of parent request's request body when client_body_buffer_size < client_max_body_size. debian/modules/nginx-lua/tapset/0000775000000000000000000000000012305451341014040 5ustar debian/modules/nginx-lua/tapset/ngx_lua.stp0000664000000000000000000000005312305451337016230 0ustar function ngx_http_lua_ctx_context(r) { } debian/modules/nginx-lua/config0000664000000000000000000003145612305451334013743 0ustar ngx_feature="Lua library" ngx_feature_libs="-llua -lm" ngx_feature_name= ngx_feature_run=no ngx_feature_incs="#include " ngx_feature_path= ngx_feature_test="(void) luaL_newstate();" if [ -n "$LUAJIT_INC" -o -n "$LUAJIT_LIB" ]; then # explicitly set Lua lib path ngx_feature="LuaJIT library in $LUAJIT_LIB and $LUAJIT_INC (specified by the LUAJIT_LIB and LUAJIT_INC env)" ngx_feature_path="$LUAJIT_INC" if [ $NGX_RPATH = YES ]; then ngx_feature_libs="-R$LUAJIT_LIB -L$LUAJIT_LIB -lluajit-5.1 -lm" else ngx_feature_libs="-L$LUAJIT_LIB -lluajit-5.1 -lm" fi . auto/feature if [ $ngx_found = no ]; then cat << END $0: error: ngx_http_lua_module requires the Lua or LuaJIT library and LUAJIT_LIB is defined as $LUAJIT_LIB and LUAJIT_INC (path for lua.h) $LUAJIT_INC, but we cannot find LuaJIT there. END exit 1 fi case "$NGX_PLATFORM" in Darwin:*) case "$NGX_MACHINE" in amd64 | x86_64 | i386) echo "adding extra linking options needed by LuaJIT" ngx_feature_libs="$ngx_feature_libs -pagezero_size 10000 -image_base 100000000" ;; *) ;; esac ;; *) ;; esac else if [ -n "$LUA_INC" -o -n "$LUA_LIB" ]; then # explicitly set Lua lib path ngx_feature="Lua library in $LUA_LIB and $LUA_INC (specified by the LUA_LIB and LUA_INC env)" ngx_feature_path="$LUA_INC" if [ $NGX_RPATH = YES ]; then ngx_feature_libs="-R$LUA_LIB -L$LUA_LIB -llua -lm" else ngx_feature_libs="-L$LUA_LIB -llua -lm" fi . auto/feature if [ $ngx_found = no ]; then cat << END $0: error: ngx_http_lua_module requires the Lua or LuaJIT library and LUA_LIB is defined as $LUA_LIB and LUA_INC (path for lua.h) is $LUA_INC, but we cannot find standard Lua there. END exit 1 fi else # auto-discovery ngx_feature="Lua library" ngx_feature_libs="-llua -lm" . auto/feature if [ $ngx_found = no ]; then # OpenBSD-5.2 ngx_feature="Lua library in /usr/local/" ngx_feature_path="/usr/local/include/lua-5.1" if [ $NGX_RPATH = YES ]; then ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -llua -lm" else ngx_feature_libs="-L/usr/local/lib -llua5.1 -lm" fi . auto/feature fi if [ $ngx_found = no ]; then # OpenBSD < 5.2 ngx_feature="Lua library in /usr/local/" ngx_feature_path="/usr/local/include" if [ $NGX_RPATH = YES ]; then ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -llua -lm" else ngx_feature_libs="-L/usr/local/lib -llua -lm" fi . auto/feature fi if [ $ngx_found = no ]; then # NetBSD ngx_feature="Lua library in /usr/pkg/" ngx_feature_path="/usr/pkg/include/" if [ $NGX_RPATH = YES ]; then ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lm -llua" else ngx_feature_libs="-L/usr/pkg/lib -lm -llua" fi . auto/feature fi if [ $ngx_found = no ]; then # MacPorts ngx_feature="Lua library in /opt/local/" ngx_feature_path="/opt/local/include" if [ $NGX_RPATH = YES ]; then ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lm -llua" else ngx_feature_libs="-L/opt/local/lib -lm -llua" fi . auto/feature fi if [ $ngx_found = no ]; then # FreeBSD ngx_feature="Lua library in /usr/local/*/lua51/" ngx_feature_path="/usr/local/include/lua51" if [ $NGX_RPATH = YES ]; then ngx_feature_libs="-R/usr/local/lib/lua51 -L/usr/local/lib/lua51 -llua -lm" else ngx_feature_libs="-L/usr/local/lib/lua51 -llua -lm" fi . auto/feature fi if [ $ngx_found = no ]; then # Debian ngx_feature="Lua library in /usr/" ngx_feature_path="/usr/include/lua5.1" if [ $NGX_RPATH = YES ]; then ngx_feature_libs="-R/usr/lib -L/usr/lib -lm -llua5.1" else ngx_feature_libs="-L/usr/lib -lm -llua5.1" fi . auto/feature fi if [ $ngx_found = no ]; then # FreeBSD with luajit-2.0 from ports collection ngx_feature="LuaJIT library in /usr/local/" ngx_feature_path="/usr/local/include/luajit-2.0" if [ $NGX_RPATH = YES ]; then ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lluajit-5.1 -lm" else ngx_feature_libs="-L/usr/local/lib -lluajit-5.1 -lm" fi . auto/feature fi if [ $ngx_found = no ]; then # Gentoo with LuaJIT-2.0 ngx_feature="LuaJIT library in /usr/" ngx_feature_path="/usr/include/luajit-2.0" if [ $NGX_RPATH = YES ]; then ngx_feature_libs="-R/usr/lib -L/usr/lib -lm -lluajit-5.1" else ngx_feature_libs="-L/usr/lib -lm -lluajit-5.1" fi . auto/feature fi fi fi if [ $ngx_found = yes ]; then CORE_INCS="$CORE_INCS $ngx_feature_path" CORE_LIBS="$CORE_LIBS $ngx_feature_libs" else cat << END $0: error: ngx_http_lua_module requires the Lua library. END exit 1 fi ngx_addon_name=ngx_http_lua_module HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_lua_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS \ $ngx_addon_dir/src/ngx_http_lua_script.c \ $ngx_addon_dir/src/ngx_http_lua_log.c \ $ngx_addon_dir/src/ngx_http_lua_subrequest.c \ $ngx_addon_dir/src/ngx_http_lua_ndk.c \ $ngx_addon_dir/src/ngx_http_lua_control.c \ $ngx_addon_dir/src/ngx_http_lua_time.c \ $ngx_addon_dir/src/ngx_http_lua_misc.c \ $ngx_addon_dir/src/ngx_http_lua_variable.c \ $ngx_addon_dir/src/ngx_http_lua_string.c \ $ngx_addon_dir/src/ngx_http_lua_output.c \ $ngx_addon_dir/src/ngx_http_lua_headers.c \ $ngx_addon_dir/src/ngx_http_lua_req_body.c \ $ngx_addon_dir/src/ngx_http_lua_uri.c \ $ngx_addon_dir/src/ngx_http_lua_args.c \ $ngx_addon_dir/src/ngx_http_lua_ctx.c \ $ngx_addon_dir/src/ngx_http_lua_regex.c \ $ngx_addon_dir/src/ngx_http_lua_module.c \ $ngx_addon_dir/src/ngx_http_lua_headers_out.c \ $ngx_addon_dir/src/ngx_http_lua_headers_in.c \ $ngx_addon_dir/src/ngx_http_lua_directive.c \ $ngx_addon_dir/src/ngx_http_lua_consts.c \ $ngx_addon_dir/src/ngx_http_lua_exception.c \ $ngx_addon_dir/src/ngx_http_lua_util.c \ $ngx_addon_dir/src/ngx_http_lua_cache.c \ $ngx_addon_dir/src/ngx_http_lua_contentby.c \ $ngx_addon_dir/src/ngx_http_lua_rewriteby.c \ $ngx_addon_dir/src/ngx_http_lua_accessby.c \ $ngx_addon_dir/src/ngx_http_lua_setby.c \ $ngx_addon_dir/src/ngx_http_lua_capturefilter.c \ $ngx_addon_dir/src/ngx_http_lua_clfactory.c \ $ngx_addon_dir/src/ngx_http_lua_pcrefix.c \ $ngx_addon_dir/src/ngx_http_lua_headerfilterby.c \ $ngx_addon_dir/src/ngx_http_lua_shdict.c \ $ngx_addon_dir/src/ngx_http_lua_socket_tcp.c \ $ngx_addon_dir/src/ngx_http_lua_api.c \ $ngx_addon_dir/src/ngx_http_lua_logby.c \ $ngx_addon_dir/src/ngx_http_lua_sleep.c \ $ngx_addon_dir/src/ngx_http_lua_coroutine.c \ $ngx_addon_dir/src/ngx_http_lua_bodyfilterby.c \ $ngx_addon_dir/src/ngx_http_lua_initby.c \ $ngx_addon_dir/src/ngx_http_lua_socket_udp.c \ $ngx_addon_dir/src/ngx_http_lua_req_method.c \ $ngx_addon_dir/src/ngx_http_lua_phase.c \ $ngx_addon_dir/src/ngx_http_lua_uthread.c \ $ngx_addon_dir/src/ngx_http_lua_timer.c \ $ngx_addon_dir/src/ngx_http_lua_config.c \ $ngx_addon_dir/src/ngx_http_lua_worker.c \ " NGX_ADDON_DEPS="$NGX_ADDON_DEPS \ $ngx_addon_dir/src/ddebug.h \ $ngx_addon_dir/src/ngx_http_lua_script.h \ $ngx_addon_dir/src/ngx_http_lua_log.h \ $ngx_addon_dir/src/ngx_http_lua_subrequest.h \ $ngx_addon_dir/src/ngx_http_lua_ndk.h \ $ngx_addon_dir/src/ngx_http_lua_control.h \ $ngx_addon_dir/src/ngx_http_lua_time.h \ $ngx_addon_dir/src/ngx_http_lua_string.h \ $ngx_addon_dir/src/ngx_http_lua_misc.h \ $ngx_addon_dir/src/ngx_http_lua_variable.h \ $ngx_addon_dir/src/ngx_http_lua_output.h \ $ngx_addon_dir/src/ngx_http_lua_headers.h \ $ngx_addon_dir/src/ngx_http_lua_uri.h \ $ngx_addon_dir/src/ngx_http_lua_req_body.h \ $ngx_addon_dir/src/ngx_http_lua_args.h \ $ngx_addon_dir/src/ngx_http_lua_ctx.h \ $ngx_addon_dir/src/ngx_http_lua_regex.h \ $ngx_addon_dir/src/ngx_http_lua_common.h \ $ngx_addon_dir/src/ngx_http_lua_directive.h \ $ngx_addon_dir/src/ngx_http_lua_headers_out.h \ $ngx_addon_dir/src/ngx_http_lua_headers_in.h \ $ngx_addon_dir/src/ngx_http_lua_consts.h \ $ngx_addon_dir/src/ngx_http_lua_exception.h \ $ngx_addon_dir/src/ngx_http_lua_util.h \ $ngx_addon_dir/src/ngx_http_lua_cache.h \ $ngx_addon_dir/src/ngx_http_lua_contentby.h \ $ngx_addon_dir/src/ngx_http_lua_rewriteby.h \ $ngx_addon_dir/src/ngx_http_lua_accessby.h \ $ngx_addon_dir/src/ngx_http_lua_setby.h \ $ngx_addon_dir/src/ngx_http_lua_capturefilter.h \ $ngx_addon_dir/src/ngx_http_lua_clfactory.h \ $ngx_addon_dir/src/ngx_http_lua_pcrefix.h \ $ngx_addon_dir/src/ngx_http_lua_headerfilterby.h \ $ngx_addon_dir/src/ngx_http_lua_shdict.h \ $ngx_addon_dir/src/ngx_http_lua_socket_tcp.h \ $ngx_addon_dir/src/api/ngx_http_lua_api.h \ $ngx_addon_dir/src/ngx_http_lua_logby.h \ $ngx_addon_dir/src/ngx_http_lua_sleep.h \ $ngx_addon_dir/src/ngx_http_lua_coroutine.h \ $ngx_addon_dir/src/ngx_http_lua_bodyfilterby.h \ $ngx_addon_dir/src/ngx_http_lua_initby.h \ $ngx_addon_dir/src/ngx_http_lua_socket_udp.h \ $ngx_addon_dir/src/ngx_http_lua_req_method.h \ $ngx_addon_dir/src/ngx_http_lua_phase.h \ $ngx_addon_dir/src/ngx_http_lua_probe.h \ $ngx_addon_dir/src/ngx_http_lua_uthread.h \ $ngx_addon_dir/src/ngx_http_lua_timer.h \ $ngx_addon_dir/src/ngx_http_lua_config.h \ $ngx_addon_dir/src/ngx_http_lua_worker.h \ " CFLAGS="$CFLAGS -DNDK_SET_VAR" ngx_feature="export symbols by default (-E)" ngx_feature_libs="-Wl,-E" ngx_feature_name= ngx_feature_run=no ngx_feature_incs="#include " ngx_feature_path= ngx_feature_test='printf("hello");' . auto/feature if [ $ngx_found = yes ]; then CORE_LIBS="-Wl,-E $CORE_LIBS" fi # for Cygwin ngx_feature="export symbols by default (--export-all-symbols)" ngx_feature_libs="-Wl,--export-all-symbols" ngx_feature_name= ngx_feature_run=no ngx_feature_incs="#include " ngx_feature_path= ngx_feature_test='printf("hello");' . auto/feature if [ $ngx_found = yes ]; then CORE_LIBS="-Wl,--export-all-symbols $CORE_LIBS" fi NGX_DTRACE_PROVIDERS="$NGX_DTRACE_PROVIDERS $ngx_addon_dir/dtrace/ngx_lua_provider.d" NGX_TAPSET_SRCS="$NGX_TAPSET_SRCS $ngx_addon_dir/tapset/ngx_lua.stp" USE_MD5=YES USE_SHA1=YES CORE_INCS="$CORE_INCS $ngx_addon_dir/src/api" ngx_feature="SO_PASSCRED" ngx_feature_libs= ngx_feature_name="NGX_HTTP_LUA_HAVE_SO_PASSCRED" ngx_feature_run=no ngx_feature_incs="#include #include " ngx_feature_path= ngx_feature_test='setsockopt(1, SOL_SOCKET, SO_PASSCRED, NULL, 0);' . auto/feature #CFLAGS=$"$CFLAGS -DLUA_DEFAULT_PATH='\"/usr/local/openresty/lualib/?.lua\"'" #CFLAGS=$"$CFLAGS -DLUA_DEFAULT_CPATH='\"/usr/local/openresty/lualib/?.so\"'" debian/modules/nginx-lua/dtrace/0000775000000000000000000000000012305451341014002 5ustar debian/modules/nginx-lua/dtrace/ngx_lua_provider.d0000664000000000000000000000460512305451335017526 0ustar provider nginx_lua { probe http__lua__info(char *s); /* lua_State *L */ probe http__lua__register__preload__package(void *L, u_char *pkg); probe http__lua__req__socket__consume__preread(ngx_http_request_t *r, u_char *data, size_t len); /* lua_State *parent, lua_State *child */ probe http__lua__user__coroutine__create(ngx_http_request_t *r, void *parent, void *child); /* lua_State *parent, lua_State *child */ probe http__lua__user__coroutine__resume(ngx_http_request_t *r, void *parent, void *child); /* lua_State *parent, lua_State *child */ probe http__lua__user__coroutine__yield(ngx_http_request_t *r, void *parent, void *child); /* lua_State *L */ probe http__lua__thread__yield(ngx_http_request_t *r, void *L); /* ngx_http_lua_socket_tcp_upstream_t *u */ probe http__lua__socket__tcp__send__start(ngx_http_request_t *r, void *u, u_char *data, size_t len); /* ngx_http_lua_socket_tcp_upstream_t *u */ probe http__lua__socket__tcp__receive__done(ngx_http_request_t *r, void *u, u_char *data, size_t len); /* ngx_http_lua_socket_tcp_upstream_t *u */ probe http__lua__socket__tcp__setkeepalive__buf__unread( ngx_http_request_t *r, void *u, u_char *data, size_t len); /* lua_State *creator, lua_State *newthread */ probe http__lua__user__thread__spawn(ngx_http_request_t *r, void *creator, void *newthread); /* lua_State *thread, ngx_http_lua_ctx_t *ctx */ probe http__lua__thread__delete(ngx_http_request_t *r, void *thread, void *ctx); /* lua_State *thread */ probe http__lua__run__posted__thread(ngx_http_request_t *r, void *thread, int status); probe http__lua__coroutine__done(ngx_http_request_t *r, void *co, int success); /* lua_State *parent, lua_State *child */ probe http__lua__user__thread__wait(void *parent, void *child); }; #pragma D attributes Evolving/Evolving/Common provider nginx_lua provider #pragma D attributes Private/Private/Unknown provider nginx_lua module #pragma D attributes Private/Private/Unknown provider nginx_lua function #pragma D attributes Private/Private/Common provider nginx_lua name #pragma D attributes Evolving/Evolving/Common provider nginx_lua args debian/modules/nginx-lua/doc/0000775000000000000000000000000012305451341013305 5ustar debian/modules/nginx-lua/doc/HttpLuaModule.wiki0000664000000000000000000076520212305451334016737 0ustar = Name = ngx_lua - Embed the power of Lua into Nginx ''This module is not distributed with the Nginx source.'' See [[#Installation|the installation instructions]]. = Status = This module is under active development and is production ready. = Version = This document describes ngx_lua [https://github.com/chaoslawful/lua-nginx-module/tags v0.9.4] released on 10 January 2014. = Synopsis = # set search paths for pure Lua external libraries (';;' is the default path): lua_package_path '/foo/bar/?.lua;/blah/?.lua;;'; # set search paths for Lua external libraries written in C (can also use ';;'): lua_package_cpath '/bar/baz/?.so;/blah/blah/?.so;;'; server { location /inline_concat { # MIME type determined by default_type: default_type 'text/plain'; set $a "hello"; set $b "world"; # inline Lua script set_by_lua $res "return ngx.arg[1]..ngx.arg[2]" $a $b; echo $res; } location /rel_file_concat { set $a "foo"; set $b "bar"; # script path relative to nginx prefix # $ngx_prefix/conf/concat.lua contents: # # return ngx.arg[1]..ngx.arg[2] # set_by_lua_file $res conf/concat.lua $a $b; echo $res; } location /abs_file_concat { set $a "fee"; set $b "baz"; # absolute script path not modified set_by_lua_file $res /usr/nginx/conf/concat.lua $a $b; echo $res; } location /lua_content { # MIME type determined by default_type: default_type 'text/plain'; content_by_lua "ngx.say('Hello,world!')"; } location /nginx_var { # MIME type determined by default_type: default_type 'text/plain'; # try access /nginx_var?a=hello,world content_by_lua "ngx.print(ngx.var['arg_a'], '\\n')"; } location /request_body { # force reading request body (default off) lua_need_request_body on; client_max_body_size 50k; client_body_buffer_size 50k; content_by_lua 'ngx.print(ngx.var.request_body)'; } # transparent non-blocking I/O in Lua via subrequests location /lua { # MIME type determined by default_type: default_type 'text/plain'; content_by_lua ' local res = ngx.location.capture("/some_other_location") if res.status == 200 then ngx.print(res.body) end'; } # GET /recur?num=5 location /recur { # MIME type determined by default_type: default_type 'text/plain'; content_by_lua ' local num = tonumber(ngx.var.arg_num) or 0 if num > 50 then ngx.say("num too big") return end ngx.say("num is: ", num) if num > 0 then res = ngx.location.capture("/recur?num=" .. tostring(num - 1)) ngx.print("status=", res.status, " ") ngx.print("body=", res.body) else ngx.say("end") end '; } location /foo { rewrite_by_lua ' res = ngx.location.capture("/memc", { args = { cmd = "incr", key = ngx.var.uri } } ) '; proxy_pass http://blah.blah.com; } location /blah { access_by_lua ' local res = ngx.location.capture("/auth") if res.status == ngx.HTTP_OK then return end if res.status == ngx.HTTP_FORBIDDEN then ngx.exit(res.status) end ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) '; # proxy_pass/fastcgi_pass/postgres_pass/... } location /mixed { rewrite_by_lua_file /path/to/rewrite.lua; access_by_lua_file /path/to/access.lua; content_by_lua_file /path/to/content.lua; } # use nginx var in code path # WARN: contents in nginx var must be carefully filtered, # otherwise there'll be great security risk! location ~ ^/app/(.+) { content_by_lua_file /path/to/lua/app/root/$1.lua; } location / { lua_need_request_body on; client_max_body_size 100k; client_body_buffer_size 100k; access_by_lua ' -- check the client IP address is in our black list if ngx.var.remote_addr == "132.5.72.3" then ngx.exit(ngx.HTTP_FORBIDDEN) end -- check if the request body contains bad words if ngx.var.request_body and string.match(ngx.var.request_body, "fsck") then return ngx.redirect("/terms_of_use.html") end -- tests passed '; # proxy_pass/fastcgi_pass/etc settings } } = Description = This module embeds Lua, via the standard Lua 5.1 interpreter or [http://luajit.org/luajit.html LuaJIT 2.0/2.1], into Nginx and by leveraging Nginx's subrequests, allows the integration of the powerful Lua threads (Lua coroutines) into the Nginx event model. Unlike [http://httpd.apache.org/docs/2.3/mod/mod_lua.html Apache's mod_lua] and [http://redmine.lighttpd.net/wiki/1/Docs:ModMagnet Lighttpd's mod_magnet], Lua code executed using this module can be ''100% non-blocking'' on network traffic as long as the [[#Nginx API for Lua|Nginx API for Lua]] provided by this module is used to handle requests to upstream services such as MySQL, PostgreSQL, Memcached, Redis, or upstream HTTP web services. At least the following Lua libraries and Nginx modules can be used with this ngx_lua module: * [https://github.com/agentzh/lua-resty-memcached lua-resty-memcached] * [https://github.com/agentzh/lua-resty-mysql lua-resty-mysql] * [https://github.com/agentzh/lua-resty-redis lua-resty-redis] * [https://github.com/agentzh/lua-resty-dns lua-resty-dns] * [https://github.com/agentzh/lua-resty-upload lua-resty-upload] * [https://github.com/agentzh/lua-resty-websocket lua-resty-websocket] * [https://github.com/agentzh/lua-resty-lock lua-resty-lock] * [https://github.com/agentzh/lua-resty-string lua-resty-string] * [[HttpMemcModule|ngx_memc]] * [https://github.com/FRiCKLE/ngx_postgres ngx_postgres] * [[HttpRedis2Module|ngx_redis2]] * [[HttpRedisModule|ngx_redis]] * [[HttpProxyModule|ngx_proxy]] * [[HttpFastcgiModule|ngx_fastcgi]] Almost all the Nginx modules can be used with this ngx_lua module by means of [[#ngx.location.capture|ngx.location.capture]] or [[#ngx.location.capture_multi|ngx.location.capture_multi]] but it is recommended to use those lua-resty-* libraries instead of creating subrequests to access the Nginx upstream modules because the former is usually much more flexible and memory-efficient. The Lua interpreter or LuaJIT instance is shared across all the requests in a single nginx worker process but request contexts are segregated using lightweight Lua coroutines. Loaded Lua modules persist in the nginx worker process level resulting in a small memory footprint in Lua even when under heavy loads. = Directives = == lua_use_default_type == '''syntax:''' ''lua_use_default_type on | off'' '''default:''' ''lua_use_default_type on'' '''context:''' ''http, server, location, location if'' Specifies whether to use the MIME type specified by the [http://nginx.org/en/docs/http/ngx_http_core_module.html#default_type default_type] directive for the default value of the Content-Type response header. If you do not want a default Content-Type response header for your Lua request handlers, then turn this directive off. This directive is turned on by default. This directive was first introduced in the v0.9.1 release. == lua_code_cache == '''syntax:''' ''lua_code_cache on | off'' '''default:''' ''lua_code_cache on'' '''context:''' ''http, server, location, location if'' Enables or disables the Lua code cache for Lua code in *_by_lua_file directives (like [[#set_by_lua_file|set_by_lua_file]] and [[#content_by_lua_file|content_by_lua_file]]) and Lua modules. When turning off, every request served by ngx_lua will run in a separate Lua VM instance, starting from the 0.9.3 release. So the Lua files referenced in [[#set_by_lua_file|set_by_lua_file]], [[#content_by_lua_file|content_by_lua_file]], [[#access_by_lua_file|access_by_lua_file]], and etc will not be cached and all Lua modules used will be loaded from scratch. With this in place, developers can adopt an edit-and-refresh approach. Please note however, that Lua code written inlined within nginx.conf such as those specified by [[#set_by_lua|set_by_lua]], [[#content_by_lua|content_by_lua]], [[#access_by_lua|access_by_lua]], and [[#rewrite_by_lua|rewrite_by_lua]] will not be updated when you edit the inlined Lua code in your nginx.conf file because only the Nginx config file parser can correctly parse the nginx.conf file and the only way is to reload the config file by sending a HUP signal or just to restart Nginx. Even when the code cache is enabled, Lua files which are loaded by dofile or loadfile in *_by_lua_file cannot be cached (unless you cache the results yourself). Usually you can either use the [[#init_by_lua|init_by_lua]] or [[#init-by_lua_file|init_by_lua_file]] directives to load all such files or just make these Lua files true Lua modules and load them via require. The ngx_lua module does not support the stat mode available with the Apache mod_lua module (yet). Disabling the Lua code cache is strongly discouraged for production use and should only be used during development as it has a significant negative impact on overall performance. For example, the performance a "hello world" Lua example can drop by an order of magnitude after disabling the Lua code cache. == lua_regex_cache_max_entries == '''syntax:''' ''lua_regex_cache_max_entries '' '''default:''' ''lua_regex_cache_max_entries 1024'' '''context:''' ''http'' Specifies the maximum number of entries allowed in the worker process level compiled regex cache. The regular expressions used in [[#ngx.re.match|ngx.re.match]], [[#ngx.re.gmatch|ngx.re.gmatch]], [[#ngx.re.sub|ngx.re.sub]], and [[#ngx.re.gsub|ngx.re.gsub]] will be cached within this cache if the regex option o (i.e., compile-once flag) is specified. The default number of entries allowed is 1024 and when this limit is reached, new regular expressions will not be cached (as if the o option was not specified) and there will be one, and only one, warning in the error.log file: 2011/08/27 23:18:26 [warn] 31997#0: *1 lua exceeding regex cache max entries (1024), ... Do not activate the o option for regular expressions (and/or replace string arguments for [[#ngx.re.sub|ngx.re.sub]] and [[#ngx.re.gsub|ngx.re.gsub]]) that are generated ''on the fly'' and give rise to infinite variations to avoid hitting the specified limit. == lua_regex_match_limit == '''syntax:''' ''lua_regex_match_limit '' '''default:''' ''lua_regex_match_limit 0'' '''context:''' ''http'' Specifies the "match limit" used by the PCRE library when executing the [[#ngx.re.match|ngx.re API]]. To quote the PCRE manpage, "the limit ... has the effect of limiting the amount of backtracking that can take place." When the limit is hit, the error string "pcre_exec() failed: -8" will be returned by the [[#ngx.re.match|ngx.re API]] functions on the Lua land. When setting the limit to 0, the default "match limit" when compiling the PCRE library is used. And this is the default value of this directive. This directive was first introduced in the v0.8.5 release. == lua_package_path == '''syntax:''' ''lua_package_path '' '''default:''' ''The content of LUA_PATH environ variable or Lua's compiled-in defaults.'' '''context:''' ''http'' Sets the Lua module search path used by scripts specified by [[#set_by_lua|set_by_lua]], [[#content_by_lua|content_by_lua]] and others. The path string is in standard Lua path form, and ;; can be used to stand for the original search paths. As from the v0.5.0rc29 release, the special notation $prefix or ${prefix} can be used in the search path string to indicate the path of the server prefix usually determined by the -p PATH command-line option while starting the Nginx server. == lua_package_cpath == '''syntax:''' ''lua_package_cpath '' '''default:''' ''The content of LUA_CPATH environment variable or Lua's compiled-in defaults.'' '''context:''' ''http'' Sets the Lua C-module search path used by scripts specified by [[#set_by_lua|set_by_lua]], [[#content_by_lua|content_by_lua]] and others. The cpath string is in standard Lua cpath form, and ;; can be used to stand for the original cpath. As from the v0.5.0rc29 release, the special notation $prefix or ${prefix} can be used in the search path string to indicate the path of the server prefix usually determined by the -p PATH command-line option while starting the Nginx server. == init_by_lua == '''syntax:''' ''init_by_lua '' '''context:''' ''http'' '''phase:''' ''loading-config'' Runs the Lua code specified by the argument on the global Lua VM level when the Nginx master process (if any) is loading the Nginx config file. When Nginx receives the HUP signal and starts reloading the config file, the Lua VM will also be re-created and init_by_lua will run again on the new Lua VM. Usually you can register (true) Lua global variables or pre-load Lua modules at server start-up by means of this hook. Here is an example for pre-loading Lua modules: init_by_lua 'cjson = require "cjson"'; server { location = /api { content_by_lua ' ngx.say(cjson.encode({dog = 5, cat = 6})) '; } } You can also initialize the [[#lua_shared_dict|lua_shared_dict]] shm storage at this phase. Here is an example for this: lua_shared_dict dogs 1m; init_by_lua ' local dogs = ngx.shared.dogs; dogs:set("Tom", 56) '; server { location = /api { content_by_lua ' local dogs = ngx.shared.dogs; ngx.say(dogs:get("Tom")) '; } } But note that, the [[#lua_shared_dict|lua_shared_dict]]'s shm storage will not be cleared through a config reload (via the HUP signal, for example). So if you do ''not'' want to re-initialize the shm storage in your init_by_lua code in this case, then you just need to set a custom flag in the shm storage and always check the flag in your init_by_lua code. Because the Lua code in this context runs before Nginx forks its worker processes (if any), data or code loaded here will enjoy the [http://en.wikipedia.org/wiki/Copy-on-write Copy-on-write (COW)] feature provided by many operating systems among all the worker processes, thus saving a lot of memory. Only a small set of the [[#Nginx API for Lua|Nginx API for Lua]] is supported in this context: * Logging APIs: [[#ngx.log|ngx.log]] and [[#print|print]], * Shared Dictionary API: [[#ngx.shared.DICT|ngx.shared.DICT]]. More Nginx APIs for Lua may be supported in this context upon future user requests. Basically you can safely use Lua libraries that do blocking I/O in this very context because blocking the master process during server start-up is completely okay. Even the Nginx core does blocking I/O (at least on resolving upstream's host names) at the configure-loading phase. You should be very careful about potential security vulnerabilities in your Lua code registered in this context because the Nginx master process is often run under the root account. This directive was first introduced in the v0.5.5 release. == init_by_lua_file == '''syntax:''' ''init_by_lua_file '' '''context:''' ''http'' '''phase:''' ''loading-config'' Equivalent to [[#init_by_lua|init_by_lua]], except that the file specified by contains the Lua code or [[#Lua/LuaJIT bytecode support|Lua/LuaJIT bytecode]] to be executed. When a relative path like foo/bar.lua is given, they will be turned into the absolute path relative to the server prefix path determined by the -p PATH command-line option while starting the Nginx server. This directive was first introduced in the v0.5.5 release. == set_by_lua == '''syntax:''' ''set_by_lua $res [$arg1 $arg2 ...]'' '''context:''' ''server, server if, location, location if'' '''phase:''' ''server-rewrite, rewrite'' Executes code specified in with optional input arguments $arg1 $arg2 ..., and returns string output to $res. The code in can make [[#Nginx API for Lua|API calls]] and can retrieve input arguments from the ngx.arg table (index starts from 1 and increases sequentially). This directive is designed to execute short, fast running code blocks as the Nginx event loop is blocked during code execution. Time consuming code sequences should therefore be avoided. Note that the following API functions are currently disabled within this context: * Output API functions (e.g., [[#ngx.say|ngx.say]] and [[#ngx.send_headers|ngx.send_headers]]) * Control API functions (e.g., [[#ngx.exit|ngx.exit]]) * Subrequest API functions (e.g., [[#ngx.location.capture|ngx.location.capture]] and [[#ngx.location.capture_multi|ngx.location.capture_multi]]) * Cosocket API functions (e.g., [[#ngx.socket.tcp|ngx.socket.tcp]] and [[#ngx.req.socket|ngx.req.socket]]). In addition, note that this directive can only write out a value to a single Nginx variable at a time. However, a workaround is possible using the [[#ngx.var.VARIABLE|ngx.var.VARIABLE]] interface. location /foo { set $diff ''; # we have to predefine the $diff variable here set_by_lua $sum ' local a = 32 local b = 56 ngx.var.diff = a - b; -- write to $diff directly return a + b; -- return the $sum value normally '; echo "sum = $sum, diff = $diff"; } This directive can be freely mixed with all directives of the [[HttpRewriteModule]], [[HttpSetMiscModule]], and [[HttpArrayVarModule]] modules. All of these directives will run in the same order as they appear in the config file. set $foo 32; set_by_lua $bar 'tonumber(ngx.var.foo) + 1'; set $baz "bar: $bar"; # $baz == "bar: 33" As from the v0.5.0rc29 release, Nginx variable interpolation is disabled in the argument of this directive and therefore, the dollar sign character ($) can be used directly. This directive requires the [https://github.com/simpl/ngx_devel_kit ngx_devel_kit] module. == set_by_lua_file == '''syntax:''' ''set_by_lua_file $res [$arg1 $arg2 ...]'' '''context:''' ''server, server if, location, location if'' '''phase:''' ''server-rewrite, rewrite'' Equivalent to [[#set_by_lua|set_by_lua]], except that the file specified by contains the Lua code, or, as from the v0.5.0rc32 release, the [[#Lua/LuaJIT bytecode support|Lua/LuaJIT bytecode]] to be executed. Nginx variable interpolation is supported in the argument string of this directive. But special care must be taken for injection attacks. When a relative path like foo/bar.lua is given, they will be turned into the absolute path relative to the server prefix path determined by the -p PATH command-line option while starting the Nginx server. When the Lua code cache is turned on (by default), the user code is loaded once at the first request and cached and the Nginx config must be reloaded each time the Lua source file is modified. The Lua code cache can be temporarily disabled during development by switching [[#lua_code_cache|lua_code_cache]] off in nginx.conf to avoid reloading Nginx. This directive requires the [https://github.com/simpl/ngx_devel_kit ngx_devel_kit] module. == content_by_lua == '''syntax:''' ''content_by_lua '' '''context:''' ''location, location if'' '''phase:''' ''content'' Acts as a "content handler" and executes Lua code string specified in for every request. The Lua code may make [[#Nginx API for Lua|API calls]] and is executed as a new spawned coroutine in an independent global environment (i.e. a sandbox). Do not use this directive and other content handler directives in the same location. For example, this directive and the [[HttpProxyModule#proxy_pass|proxy_pass]] directive should not be used in the same location. == content_by_lua_file == '''syntax:''' ''content_by_lua_file '' '''context:''' ''location, location if'' '''phase:''' ''content'' Equivalent to [[#content_by_lua|content_by_lua]], except that the file specified by contains the Lua code, or, as from the v0.5.0rc32 release, the [[#Lua/LuaJIT bytecode support|Lua/LuaJIT bytecode]] to be executed. Nginx variables can be used in the string to provide flexibility. This however carries some risks and is not ordinarily recommended. When a relative path like foo/bar.lua is given, they will be turned into the absolute path relative to the server prefix path determined by the -p PATH command-line option while starting the Nginx server. When the Lua code cache is turned on (by default), the user code is loaded once at the first request and cached and the Nginx config must be reloaded each time the Lua source file is modified. The Lua code cache can be temporarily disabled during development by switching [[#lua_code_cache|lua_code_cache]] off in nginx.conf to avoid reloading Nginx. == rewrite_by_lua == '''syntax:''' ''rewrite_by_lua '' '''context:''' ''http, server, location, location if'' '''phase:''' ''rewrite tail'' Acts as a rewrite phase handler and executes Lua code string specified in for every request. The Lua code may make [[#Nginx API for Lua|API calls]] and is executed as a new spawned coroutine in an independent global environment (i.e. a sandbox). Note that this handler always runs ''after'' the standard [[HttpRewriteModule]]. So the following will work as expected: location /foo { set $a 12; # create and initialize $a set $b ""; # create and initialize $b rewrite_by_lua 'ngx.var.b = tonumber(ngx.var.a) + 1'; echo "res = $b"; } because set $a 12 and set $b "" run ''before'' [[#rewrite_by_lua|rewrite_by_lua]]. On the other hand, the following will not work as expected: ? location /foo { ? set $a 12; # create and initialize $a ? set $b ''; # create and initialize $b ? rewrite_by_lua 'ngx.var.b = tonumber(ngx.var.a) + 1'; ? if ($b = '13') { ? rewrite ^ /bar redirect; ? break; ? } ? ? echo "res = $b"; ? } because if runs ''before'' [[#rewrite_by_lua|rewrite_by_lua]] even if it is placed after [[#rewrite_by_lua|rewrite_by_lua]] in the config. The right way of doing this is as follows: location /foo { set $a 12; # create and initialize $a set $b ''; # create and initialize $b rewrite_by_lua ' ngx.var.b = tonumber(ngx.var.a) + 1 if tonumber(ngx.var.b) == 13 then return ngx.redirect("/bar"); end '; echo "res = $b"; } Note that the [http://www.grid.net.ru/nginx/eval.en.html ngx_eval] module can be approximated by using [[#rewrite_by_lua|rewrite_by_lua]]. For example, location / { eval $res { proxy_pass http://foo.com/check-spam; } if ($res = 'spam') { rewrite ^ /terms-of-use.html redirect; } fastcgi_pass ...; } can be implemented in ngx_lua as: location = /check-spam { internal; proxy_pass http://foo.com/check-spam; } location / { rewrite_by_lua ' local res = ngx.location.capture("/check-spam") if res.body == "spam" then return ngx.redirect("/terms-of-use.html") end '; fastcgi_pass ...; } Just as any other rewrite phase handlers, [[#rewrite_by_lua|rewrite_by_lua]] also runs in subrequests. Note that when calling ngx.exit(ngx.OK) within a [[#rewrite_by_lua|rewrite_by_lua]] handler, the nginx request processing control flow will still continue to the content handler. To terminate the current request from within a [[#rewrite_by_lua|rewrite_by_lua]] handler, calling [[#ngx.exit|ngx.exit]] with status >= 200 (ngx.HTTP_OK) and status < 300 (ngx.HTTP_SPECIAL_RESPONSE) for successful quits and ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) (or its friends) for failures. If the [[HttpRewriteModule]]'s [[HttpRewriteModule#rewrite|rewrite]] directive is used to change the URI and initiate location re-lookups (internal redirections), then any [[#rewrite_by_lua|rewrite_by_lua]] or [[#rewrite_by_lua_file|rewrite_by_lua_file]] code sequences within the current location will not be executed. For example, location /foo { rewrite ^ /bar; rewrite_by_lua 'ngx.exit(503)'; } location /bar { ... } Here the Lua code ngx.exit(503) will never run. This will be the case if rewrite ^ /bar last is used as this will similarly initiate an internal redirection. If the break modifier is used instead, there will be no internal redirection and the rewrite_by_lua code will be executed. The rewrite_by_lua code will always run at the end of the rewrite request-processing phase unless [[#rewrite_by_lua_no_postpone|rewrite_by_lua_no_postpone]] is turned on. == rewrite_by_lua_file == '''syntax:''' ''rewrite_by_lua_file '' '''context:''' ''http, server, location, location if'' '''phase:''' ''rewrite tail'' Equivalent to [[#rewrite_by_lua|rewrite_by_lua]], except that the file specified by contains the Lua code, or, as from the v0.5.0rc32 release, the [[#Lua/LuaJIT bytecode support|Lua/LuaJIT bytecode]] to be executed. Nginx variables can be used in the string to provide flexibility. This however carries some risks and is not ordinarily recommended. When a relative path like foo/bar.lua is given, they will be turned into the absolute path relative to the server prefix path determined by the -p PATH command-line option while starting the Nginx server. When the Lua code cache is turned on (by default), the user code is loaded once at the first request and cached and the Nginx config must be reloaded each time the Lua source file is modified. The Lua code cache can be temporarily disabled during development by switching [[#lua_code_cache|lua_code_cache]] off in nginx.conf to avoid reloading Nginx. The rewrite_by_lua_file code will always run at the end of the rewrite request-processing phase unless [[#rewrite_by_lua_no_postpone|rewrite_by_lua_no_postpone]] is turned on. == access_by_lua == '''syntax:''' ''access_by_lua '' '''context:''' ''http, server, location, location if'' '''phase:''' ''access tail'' Acts as an access phase handler and executes Lua code string specified in for every request. The Lua code may make [[#Nginx API for Lua|API calls]] and is executed as a new spawned coroutine in an independent global environment (i.e. a sandbox). Note that this handler always runs ''after'' the standard [[HttpAccessModule]]. So the following will work as expected: location / { deny 192.168.1.1; allow 192.168.1.0/24; allow 10.1.1.0/16; deny all; access_by_lua ' local res = ngx.location.capture("/mysql", { ... }) ... '; # proxy_pass/fastcgi_pass/... } That is, if a client IP address is in the blacklist, it will be denied before the MySQL query for more complex authentication is executed by [[#access_by_lua|access_by_lua]]. Note that the [http://mdounin.ru/hg/ngx_http_auth_request_module/ ngx_auth_request] module can be approximated by using [[#access_by_lua|access_by_lua]]: location / { auth_request /auth; # proxy_pass/fastcgi_pass/postgres_pass/... } can be implemented in ngx_lua as: location / { access_by_lua ' local res = ngx.location.capture("/auth") if res.status == ngx.HTTP_OK then return end if res.status == ngx.HTTP_FORBIDDEN then ngx.exit(res.status) end ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) '; # proxy_pass/fastcgi_pass/postgres_pass/... } As with other access phase handlers, [[#access_by_lua|access_by_lua]] will ''not'' run in subrequests. Note that when calling ngx.exit(ngx.OK) within a [[#access_by_lua|access_by_lua]] handler, the nginx request processing control flow will still continue to the content handler. To terminate the current request from within a [[#access_by_lua|access_by_lua]] handler, calling [[#ngx.exit|ngx.exit]] with status >= 200 (ngx.HTTP_OK) and status < 300 (ngx.HTTP_SPECIAL_RESPONSE) for successful quits and ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) (or its friends) for failures. == access_by_lua_file == '''syntax:''' ''access_by_lua_file '' '''context:''' ''http, server, location, location if'' '''phase:''' ''access tail'' Equivalent to [[#access_by_lua|access_by_lua]], except that the file specified by contains the Lua code, or, as from the v0.5.0rc32 release, the [[#Lua/LuaJIT bytecode support|Lua/LuaJIT bytecode]] to be executed. Nginx variables can be used in the string to provide flexibility. This however carries some risks and is not ordinarily recommended. When a relative path like foo/bar.lua is given, they will be turned into the absolute path relative to the server prefix path determined by the -p PATH command-line option while starting the Nginx server. When the Lua code cache is turned on (by default), the user code is loaded once at the first request and cached and the Nginx config must be reloaded each time the Lua source file is modified. The Lua code cache can be temporarily disabled during development by switching [[#lua_code_cache|lua_code_cache]] off in nginx.conf to avoid repeatedly reloading Nginx. == header_filter_by_lua == '''syntax:''' ''header_filter_by_lua '' '''context:''' ''http, server, location, location if'' '''phase:''' ''output-header-filter'' Uses Lua code specified in to define an output header filter. Note that the following API functions are currently disabled within this context: * Output API functions (e.g., [[#ngx.say|ngx.say]] and [[#ngx.send_headers|ngx.send_headers]]) * Control API functions (e.g., [[#ngx.exit|ngx.exit]] and [[#ngx.exec|ngx.exec]]) * Subrequest API functions (e.g., [[#ngx.location.capture|ngx.location.capture]] and [[#ngx.location.capture_multi|ngx.location.capture_multi]]) * Cosocket API functions (e.g., [[#ngx.socket.tcp|ngx.socket.tcp]] and [[#ngx.req.socket|ngx.req.socket]]). Here is an example of overriding a response header (or adding one if absent) in our Lua header filter: location / { proxy_pass http://mybackend; header_filter_by_lua 'ngx.header.Foo = "blah"'; } This directive was first introduced in the v0.2.1rc20 release. == header_filter_by_lua_file == '''syntax:''' ''header_filter_by_lua_file '' '''context:''' ''http, server, location, location if'' '''phase:''' ''output-header-filter'' Equivalent to [[#header_filter_by_lua|header_filter_by_lua]], except that the file specified by contains the Lua code, or as from the v0.5.0rc32 release, the [[#Lua/LuaJIT bytecode support|Lua/LuaJIT bytecode]] to be executed. When a relative path like foo/bar.lua is given, they will be turned into the absolute path relative to the server prefix path determined by the -p PATH command-line option while starting the Nginx server. This directive was first introduced in the v0.2.1rc20 release. == body_filter_by_lua == '''syntax:''' ''body_filter_by_lua '' '''context:''' ''http, server, location, location if'' '''phase:''' ''output-body-filter'' Uses Lua code specified in to define an output body filter. The input data chunk is passed via [[#ngx.arg|ngx.arg]][1] (as a Lua string value) and the "eof" flag indicating the end of the response body data stream is passed via [[#ngx.arg|ngx.arg]][2] (as a Lua boolean value). Behind the scene, the "eof" flag is just the last_buf (for main requests) or last_in_chain (for subrequests) flag of the Nginx chain link buffers. (Before the v0.7.14 release, the "eof" flag does not work at all in subrequests.) The output data stream can be aborted immediately by running the following Lua statement: return ngx.ERROR This will truncate the response body and usually result in incomplete and also invalid responses. The Lua code can pass its own modified version of the input data chunk to the downstream Nginx output body filters by overriding [[#ngx.arg|ngx.arg]][1] with a Lua string or a Lua table of strings. For example, to transform all the lowercase letters in the response body, we can just write: location / { proxy_pass http://mybackend; body_filter_by_lua 'ngx.arg[1] = string.upper(ngx.arg[1])'; } When setting nil or an empty Lua string value to ngx.arg[1], no data chunk will be passed to the downstream Nginx output filters at all. Likewise, new "eof" flag can also be specified by setting a boolean value to [[#ngx.arg|ngx.arg]][2]. For example, location /t { echo hello world; echo hiya globe; body_filter_by_lua ' local chunk = ngx.arg[1] if string.match(chunk, "hello") then ngx.arg[2] = true -- new eof return end -- just throw away any remaining chunk data ngx.arg[1] = nil '; } Then GET /t will just return the output hello world That is, when the body filter sees a chunk containing the word "hello", then it will set the "eof" flag to true immediately, resulting in truncated but still valid responses. When the Lua code may change the length of the response body, then it is required to always clear out the Content-Length response header (if any) in a header filter to enforce streaming output, as in location /foo { # fastcgi_pass/proxy_pass/... header_filter_by_lua 'ngx.header.content_length = nil'; body_filter_by_lua 'ngx.arg[1] = string.len(ngx.arg[1]) .. "\\n"'; } Note that the following API functions are currently disabled within this context: * Output API functions (e.g., [[#ngx.say|ngx.say]] and [[#ngx.send_headers|ngx.send_headers]]) * Control API functions (e.g., [[#ngx.exit|ngx.exit]] and [[#ngx.exec|ngx.exec]]) * Subrequest API functions (e.g., [[#ngx.location.capture|ngx.location.capture]] and [[#ngx.location.capture_multi|ngx.location.capture_multi]]) * Cosocket API functions (e.g., [[#ngx.socket.tcp|ngx.socket.tcp]] and [[#ngx.req.socket|ngx.req.socket]]). Nginx output filters may be called multiple times for a single request because response body may be delivered in chunks. Thus, the Lua code specified by in this directive may also run multiple times in the lifetime of a single HTTP request. This directive was first introduced in the v0.5.0rc32 release. == body_filter_by_lua_file == '''syntax:''' ''body_filter_by_lua_file '' '''context:''' ''http, server, location, location if'' '''phase:''' ''output-body-filter'' Equivalent to [[#body_filter_by_lua|body_filter_by_lua]], except that the file specified by contains the Lua code, or, as from the v0.5.0rc32 release, the [[#Lua/LuaJIT bytecode support|Lua/LuaJIT bytecode]] to be executed. When a relative path like foo/bar.lua is given, they will be turned into the absolute path relative to the server prefix path determined by the -p PATH command-line option while starting the Nginx server. This directive was first introduced in the v0.5.0rc32 release. == log_by_lua == '''syntax:''' ''log_by_lua '' '''context:''' ''http, server, location, location if'' '''phase:''' ''log'' Run the Lua source code inlined as the at the log request processing phase. This does not replace the current access logs, but runs after. Note that the following API functions are currently disabled within this context: * Output API functions (e.g., [[#ngx.say|ngx.say]] and [[#ngx.send_headers|ngx.send_headers]]) * Control API functions (e.g., [[#ngx.exit|ngx.exit]]) * Subrequest API functions (e.g., [[#ngx.location.capture|ngx.location.capture]] and [[#ngx.location.capture_multi|ngx.location.capture_multi]]) * Cosocket API functions (e.g., [[#ngx.socket.tcp|ngx.socket.tcp]] and [[#ngx.req.socket|ngx.req.socket]]). Here is an example of gathering average data for [[HttpUpstreamModule#$upstream_response_time|$upstream_response_time]]: lua_shared_dict log_dict 5M; server { location / { proxy_pass http://mybackend; log_by_lua ' local log_dict = ngx.shared.log_dict local upstream_time = tonumber(ngx.var.upstream_response_time) local sum = log_dict:get("upstream_time-sum") or 0 sum = sum + upstream_time log_dict:set("upstream_time-sum", sum) local newval, err = log_dict:incr("upstream_time-nb", 1) if not newval and err == "not found" then log_dict:add("upstream_time-nb", 0) log_dict:incr("upstream_time-nb", 1) end '; } location = /status { content_by_lua ' local log_dict = ngx.shared.log_dict local sum = log_dict:get("upstream_time-sum") local nb = log_dict:get("upstream_time-nb") if nb and sum then ngx.say("average upstream response time: ", sum / nb, " (", nb, " reqs)") else ngx.say("no data yet") end '; } } This directive was first introduced in the v0.5.0rc31 release. == log_by_lua_file == '''syntax:''' ''log_by_lua_file '' '''context:''' ''http, server, location, location if'' '''phase:''' ''log'' Equivalent to [[#log_by_lua|log_by_lua]], except that the file specified by contains the Lua code, or, as from the v0.5.0rc32 release, the [[#Lua/LuaJIT bytecode support|Lua/LuaJIT bytecode]] to be executed. When a relative path like foo/bar.lua is given, they will be turned into the absolute path relative to the server prefix path determined by the -p PATH command-line option while starting the Nginx server. This directive was first introduced in the v0.5.0rc31 release. == lua_need_request_body == '''syntax:''' ''lua_need_request_body '' '''default:''' ''off'' '''context:''' ''main | server | location'' '''phase:''' ''depends on usage'' Determines whether to force the request body data to be read before running rewrite/access/access_by_lua* or not. The Nginx core does not read the client request body by default and if request body data is required, then this directive should be turned on or the [[#ngx.req.read_body|ngx.req.read_body]] function should be called within the Lua code. To read the request body data within the [[HttpCoreModule#$request_body|$request_body]] variable, [[HttpCoreModule#client_body_buffer_size|client_body_buffer_size]] must have the same value as [[HttpCoreModule#client_max_body_size|client_max_body_size]]. Because when the content length exceeds [[HttpCoreModule#client_body_buffer_size|client_body_buffer_size]] but less than [[HttpCoreModule#client_max_body_size|client_max_body_size]], Nginx will buffer the data into a temporary file on the disk, which will lead to empty value in the [[HttpCoreModule#$request_body|$request_body]] variable. If the current location includes [[#rewrite_by_lua|rewrite_by_lua]] or [[#rewrite_by_lua_file|rewrite_by_lua_file]] directives, then the request body will be read just before the [[#rewrite_by_lua|rewrite_by_lua]] or [[#rewrite_by_lua_file|rewrite_by_lua_file]] code is run (and also at the rewrite phase). Similarly, if only [[#content_by_lua|content_by_lua]] is specified, the request body will not be read until the content handler's Lua code is about to run (i.e., the request body will be read during the content phase). It is recommended however, to use the [[#ngx.req.read_body|ngx.req.read_body]] and [[#ngx.req.discard_body|ngx.req.discard_body]] functions for finer control over the request body reading process instead. This also applies to [[#access_by_lua|access_by_lua]] and [[#access_by_lua_file|access_by_lua_file]]. == lua_shared_dict == '''syntax:''' ''lua_shared_dict '' '''default:''' ''no'' '''context:''' ''http'' '''phase:''' ''depends on usage'' Declares a shared memory zone, , to serve as storage for the shm based Lua dictionary ngx.shared.. The argument accepts size units such as k and m: http { lua_shared_dict dogs 10m; ... } See [[#ngx.shared.DICT|ngx.shared.DICT]] for details. This directive was first introduced in the v0.3.1rc22 release. == lua_socket_connect_timeout == '''syntax:''' ''lua_socket_connect_timeout