summaryrefslogtreecommitdiff
path: root/sapi/thttpd
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2013-03-14 05:42:27 +0000
committer <>2013-04-03 16:25:08 +0000
commitc4dd7a1a684490673e25aaf4fabec5df138854c4 (patch)
tree4d57c44caae4480efff02b90b9be86f44bf25409 /sapi/thttpd
downloadphp2-master.tar.gz
Imported from /home/lorry/working-area/delta_php2/php-5.4.13.tar.bz2.HEADphp-5.4.13master
Diffstat (limited to 'sapi/thttpd')
-rw-r--r--sapi/thttpd/CREDITS2
-rw-r--r--sapi/thttpd/README85
-rw-r--r--sapi/thttpd/config.m439
-rw-r--r--sapi/thttpd/php.sym3
-rw-r--r--sapi/thttpd/php_thttpd.h35
-rw-r--r--sapi/thttpd/stub.c0
-rw-r--r--sapi/thttpd/thttpd.c772
-rw-r--r--sapi/thttpd/thttpd_patch2377
8 files changed, 3313 insertions, 0 deletions
diff --git a/sapi/thttpd/CREDITS b/sapi/thttpd/CREDITS
new file mode 100644
index 0000000..8f02f36
--- /dev/null
+++ b/sapi/thttpd/CREDITS
@@ -0,0 +1,2 @@
+thttpd
+Sascha Schumann
diff --git a/sapi/thttpd/README b/sapi/thttpd/README
new file mode 100644
index 0000000..1e80a01
--- /dev/null
+++ b/sapi/thttpd/README
@@ -0,0 +1,85 @@
+README FOR THTTPD MODULE (by Sascha Schumann)
+($Date$)
+
+ This is a SAPI module for PHP 4.x supporting thttpd, the tiny,
+ turbo, throttling HTTP server by Jef Poskanzer.
+
+ NOTE: All HTTP requests will be serialized. That means, one long running
+ script will block all other requests. Choose another web server,
+ if you want to execute arbitrarily long running scripts.
+
+ The module contains a patch against version 2.21b of thttpd. The patch
+ fixes a number of bugs and adds some functionality:
+
+ - HTTP/1.1 Persistent Connection/Pipeline Support
+ - PHP Scripting (**.php by default)
+ - Highlighting PHP Scripts (**.phps by default)
+ - Fast Accept Loop (unique to PHP)
+ - Periodic Connection Expiring (unique to PHP)
+ - Log to stdout (logfile=-)
+ - Fixes the Host: header vulnerability (affects vhosts only)
+ - Asynchronous request body handling (e.g. for POSTs)
+ - Accept filter for Linux
+ - Fix for non-blocking sending of thttpd-generated responses
+
+ You can configure the filename extensions by creating a config file for
+ thttpd and setting these entries:
+
+ phppat=PATTERN
+ phpspat=PATTERN
+
+ The PATTERN has the same format as defined here:
+
+ http://acme.com/software/thttpd/options.html#CGI_PATTERN
+
+ "**.php" means: match any file ending in .php in any directory.
+ Setting the pattern from the command line is not supported.
+
+ NOTE: This version supports *only* thttpd 2.21b, no prior or later
+ version.
+
+ This is a functional and stable module (it runs a large application
+ like IMP 2.2.0 without any problems). Its original intention was to
+ demonstrate the ability of PHP to work in every web server environment.
+
+REQUIRED DOWNLOADS
+
+ 1. thttpd 2.21b (2.20 or +2.22beta will _not_ work)
+
+ Full Distribution:
+ http://www.acme.com/software/thttpd/thttpd-2.21b.tar.gz
+
+ 2. PHP 4.x
+
+ Download:
+ http://www.php.net/
+
+ Snapshots from CVS:
+ http://snaps.php.net/
+
+
+BUILD INSTRUCTIONS
+
+ 1. Extract software packages
+
+ $ gunzip -c thttpd-2.xx.tar.gz | tar xf -
+ $ gunzip -c php-*.tar.gz | tar xf -
+
+ 2. Prepare PHP
+
+ $ cd php-*
+ $ ./configure \
+ --with-thttpd=../thttpd-2.xx \
+ <further PHP options>
+ $ make install
+ $ cd ..
+
+ You can see the list of valid PHP options by executing
+
+ $ ./configure --help
+
+ 3. Configure, compile, install thttpd
+
+ Now follow the thttpd instructions. The Makefile template of
+ thttpd was changed to automatically use the components
+ required by PHP.
diff --git a/sapi/thttpd/config.m4 b/sapi/thttpd/config.m4
new file mode 100644
index 0000000..371edae
--- /dev/null
+++ b/sapi/thttpd/config.m4
@@ -0,0 +1,39 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_WITH(thttpd,,
+[ --with-thttpd=SRCDIR Build PHP as thttpd module], no, no)
+
+AC_MSG_CHECKING([for thttpd])
+
+if test "$PHP_THTTPD" != "no"; then
+ if test ! -d $PHP_THTTPD; then
+ AC_MSG_RESULT(thttpd directory does not exist ($PHP_THTTPD))
+ fi
+
+ PHP_EXPAND_PATH($PHP_THTTPD, THTTPD)
+
+ if grep thttpd.2.21b $PHP_THTTPD/version.h >/dev/null; then
+ patch="test -f $THTTPD/php_patched || \
+ (cd $THTTPD && patch -p1 < $abs_srcdir/sapi/thttpd/thttpd_patch && touch php_patched)"
+
+ elif grep Premium $PHP_THTTPD/version.h >/dev/null; then
+ patch=
+ else
+ AC_MSG_ERROR([This version only supports thttpd-2.21b and Premium thttpd])
+ fi
+ PHP_TARGET_RDYNAMIC
+ INSTALL_IT="\
+ echo 'PHP_LIBS = -L. -lphp5 \$(PHP_LIBS) \$(EXTRA_LIBS)' > $THTTPD/php_makefile; \
+ echo 'PHP_LDFLAGS = \$(NATIVE_RPATHS) \$(PHP_LDFLAGS)' >> $THTTPD/php_makefile; \
+ echo 'PHP_CFLAGS = \$(COMMON_FLAGS) \$(CFLAGS_CLEAN) \$(CPPFLAGS) \$(EXTRA_CFLAGS)' >> $THTTPD/php_makefile; \
+ rm -f $THTTPD/php_thttpd.c $THTTPD/php_thttpd.h $THTTPD/libphp5.a; \
+ \$(LN_S) $abs_srcdir/sapi/thttpd/thttpd.c $THTTPD/php_thttpd.c; \
+ \$(LN_S) $abs_srcdir/sapi/thttpd/php_thttpd.h $abs_builddir/$SAPI_STATIC $THTTPD/;\
+ $patch"
+ PHP_THTTPD="yes, using $THTTPD"
+ PHP_ADD_INCLUDE($THTTPD)
+ PHP_SELECT_SAPI(thttpd, static)
+fi
+AC_MSG_RESULT($PHP_THTTPD)
diff --git a/sapi/thttpd/php.sym b/sapi/thttpd/php.sym
new file mode 100644
index 0000000..2214d39
--- /dev/null
+++ b/sapi/thttpd/php.sym
@@ -0,0 +1,3 @@
+thttpd_php_request
+thttpd_php_init
+thttpd_php_shutdown
diff --git a/sapi/thttpd/php_thttpd.h b/sapi/thttpd/php_thttpd.h
new file mode 100644
index 0000000..40d63d2
--- /dev/null
+++ b/sapi/thttpd/php_thttpd.h
@@ -0,0 +1,35 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Sascha Schumann <sascha@schumann.cx> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHP_THTTPD_H
+#define PHP_THTTPD_H
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <libhttpd.h>
+
+void thttpd_php_shutdown(void);
+void thttpd_php_init(void);
+off_t thttpd_php_request(httpd_conn *hc, int show_source);
+
+void thttpd_register_on_close(void (*)(int));
+void thttpd_closed_conn(int fd);
+int thttpd_get_fd(void);
+void thttpd_set_dont_close(void);
+
+#endif
diff --git a/sapi/thttpd/stub.c b/sapi/thttpd/stub.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sapi/thttpd/stub.c
diff --git a/sapi/thttpd/thttpd.c b/sapi/thttpd/thttpd.c
new file mode 100644
index 0000000..1a1baa7
--- /dev/null
+++ b/sapi/thttpd/thttpd.c
@@ -0,0 +1,772 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Sascha Schumann <sascha@schumann.cx> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "php.h"
+#include "SAPI.h"
+#include "php_main.h"
+#include "php_thttpd.h"
+#include "php_variables.h"
+#include "version.h"
+#include "php_ini.h"
+#include "zend_highlight.h"
+
+#include "ext/standard/php_smart_str.h"
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef HAVE_GETNAMEINFO
+#include <sys/socket.h>
+#include <netdb.h>
+#endif
+
+typedef struct {
+ httpd_conn *hc;
+ void (*on_close)(int);
+
+ size_t unconsumed_length;
+ smart_str sbuf;
+ int seen_cl;
+ int seen_cn;
+} php_thttpd_globals;
+
+#define PHP_SYS_CALL(x) do { x } while (n == -1 && errno == EINTR)
+
+#ifdef PREMIUM_THTTPD
+# define do_keep_alive persistent
+#endif
+
+#ifdef ZTS
+static int thttpd_globals_id;
+#define TG(v) TSRMG(thttpd_globals_id, php_thttpd_globals *, v)
+#else
+static php_thttpd_globals thttpd_globals;
+#define TG(v) (thttpd_globals.v)
+#endif
+
+static int sapi_thttpd_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+ int n;
+ uint sent = 0;
+
+ if (TG(sbuf).c != 0) {
+ smart_str_appendl_ex(&TG(sbuf), str, str_length, 1);
+ return str_length;
+ }
+
+ while (str_length > 0) {
+ PHP_SYS_CALL(n = send(TG(hc)->conn_fd, str, str_length, 0););
+
+ if (n == -1) {
+ if (errno == EAGAIN) {
+ smart_str_appendl_ex(&TG(sbuf), str, str_length, 1);
+
+ return sent + str_length;
+ } else
+ php_handle_aborted_connection();
+ }
+
+ TG(hc)->bytes_sent += n;
+ str += n;
+ sent += n;
+ str_length -= n;
+ }
+
+ return sent;
+}
+
+#define COMBINE_HEADERS 64
+
+#if defined(IOV_MAX)
+# if IOV_MAX - 64 <= 0
+# define SERIALIZE_HEADERS
+# endif
+#endif
+
+static int do_writev(struct iovec *vec, int nvec, int len TSRMLS_DC)
+{
+ int n;
+
+ assert(nvec <= IOV_MAX);
+
+ if (TG(sbuf).c == 0) {
+ PHP_SYS_CALL(n = writev(TG(hc)->conn_fd, vec, nvec););
+
+ if (n == -1) {
+ if (errno == EAGAIN) {
+ n = 0;
+ } else {
+ php_handle_aborted_connection();
+ }
+ }
+
+
+ TG(hc)->bytes_sent += n;
+ } else {
+ n = 0;
+ }
+
+ if (n < len) {
+ int i;
+
+ /* merge all unwritten data into sbuf */
+ for (i = 0; i < nvec; vec++, i++) {
+ /* has this vector been written completely? */
+ if (n >= vec->iov_len) {
+ /* yes, proceed */
+ n -= vec->iov_len;
+ continue;
+ }
+
+ if (n > 0) {
+ /* adjust vector */
+ vec->iov_base = (char *) vec->iov_base + n;
+ vec->iov_len -= n;
+ n = 0;
+ }
+
+ smart_str_appendl_ex(&TG(sbuf), vec->iov_base, vec->iov_len, 1);
+ }
+ }
+
+ return 0;
+}
+
+#ifdef SERIALIZE_HEADERS
+# define ADD_VEC(str,l) smart_str_appendl(&vec_str, (str), (l))
+# define VEC_BASE() smart_str vec_str = {0}
+# define VEC_FREE() smart_str_free(&vec_str)
+#else
+# define ADD_VEC(str,l) vec[n].iov_base=str;len += (vec[n].iov_len=l); n++
+# define VEC_BASE() struct iovec vec[COMBINE_HEADERS]
+# define VEC_FREE() do {} while (0)
+#endif
+
+#define ADD_VEC_S(str) ADD_VEC((str), sizeof(str)-1)
+
+#define CL_TOKEN "Content-length: "
+#define CN_TOKEN "Connection: "
+#define KA_DO "Connection: keep-alive\r\n"
+#define KA_NO "Connection: close\r\n"
+#define DEF_CT "Content-Type: text/html\r\n"
+
+static int sapi_thttpd_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ char buf[1024], *p;
+ VEC_BASE();
+ int n = 0;
+ zend_llist_position pos;
+ sapi_header_struct *h;
+ size_t len = 0;
+
+ if (!SG(sapi_headers).http_status_line) {
+ ADD_VEC_S("HTTP/1.1 ");
+ p = smart_str_print_long(buf+sizeof(buf)-1,
+ SG(sapi_headers).http_response_code);
+ ADD_VEC(p, strlen(p));
+ ADD_VEC_S(" HTTP\r\n");
+ } else {
+ ADD_VEC(SG(sapi_headers).http_status_line,
+ strlen(SG(sapi_headers).http_status_line));
+ ADD_VEC("\r\n", 2);
+ }
+ TG(hc)->status = SG(sapi_headers).http_response_code;
+
+ if (SG(sapi_headers).send_default_content_type) {
+ ADD_VEC(DEF_CT, strlen(DEF_CT));
+ }
+
+ h = zend_llist_get_first_ex(&sapi_headers->headers, &pos);
+ while (h) {
+
+ switch (h->header[0]) {
+ case 'c': case 'C':
+ if (!TG(seen_cl) && strncasecmp(h->header, CL_TOKEN, sizeof(CL_TOKEN)-1) == 0) {
+ TG(seen_cl) = 1;
+ } else if (!TG(seen_cn) && strncasecmp(h->header, CN_TOKEN, sizeof(CN_TOKEN)-1) == 0) {
+ TG(seen_cn) = 1;
+ }
+ }
+
+ ADD_VEC(h->header, h->header_len);
+#ifndef SERIALIZE_HEADERS
+ if (n >= COMBINE_HEADERS - 1) {
+ len = do_writev(vec, n, len TSRMLS_CC);
+ n = 0;
+ }
+#endif
+ ADD_VEC("\r\n", 2);
+
+ h = zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ }
+
+ if (TG(seen_cl) && !TG(seen_cn) && TG(hc)->do_keep_alive) {
+ ADD_VEC(KA_DO, sizeof(KA_DO)-1);
+ } else {
+ TG(hc)->do_keep_alive = 0;
+ ADD_VEC(KA_NO, sizeof(KA_NO)-1);
+ }
+
+ ADD_VEC("\r\n", 2);
+
+#ifdef SERIALIZE_HEADERS
+ sapi_thttpd_ub_write(vec_str.c, vec_str.len TSRMLS_CC);
+#else
+ do_writev(vec, n, len TSRMLS_CC);
+#endif
+
+ VEC_FREE();
+
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+
+/* to understand this, read cgi_interpose_input() in libhttpd.c */
+#define SIZEOF_UNCONSUMED_BYTES() (TG(hc)->read_idx - TG(hc)->checked_idx)
+#define CONSUME_BYTES(n) do { TG(hc)->checked_idx += (n); } while (0)
+
+
+static int sapi_thttpd_read_post(char *buffer, uint count_bytes TSRMLS_DC)
+{
+ size_t read_bytes = 0;
+
+ if (TG(unconsumed_length) > 0) {
+ read_bytes = MIN(TG(unconsumed_length), count_bytes);
+ memcpy(buffer, TG(hc)->read_buf + TG(hc)->checked_idx, read_bytes);
+ TG(unconsumed_length) -= read_bytes;
+ CONSUME_BYTES(read_bytes);
+ }
+
+ return read_bytes;
+}
+
+static char *sapi_thttpd_read_cookies(TSRMLS_D)
+{
+ return TG(hc)->cookie;
+}
+
+#define BUF_SIZE 512
+#define ADD_STRING_EX(name,buf) \
+ php_register_variable(name, buf, track_vars_array TSRMLS_CC)
+#define ADD_STRING(name) ADD_STRING_EX((name), buf)
+
+static void sapi_thttpd_register_variables(zval *track_vars_array TSRMLS_DC)
+{
+ char buf[BUF_SIZE + 1];
+ char *p;
+
+ php_register_variable("PHP_SELF", SG(request_info).request_uri, track_vars_array TSRMLS_CC);
+ php_register_variable("SERVER_SOFTWARE", SERVER_SOFTWARE, track_vars_array TSRMLS_CC);
+ php_register_variable("GATEWAY_INTERFACE", "CGI/1.1", track_vars_array TSRMLS_CC);
+ php_register_variable("REQUEST_METHOD", (char *) SG(request_info).request_method, track_vars_array TSRMLS_CC);
+ php_register_variable("REQUEST_URI", SG(request_info).request_uri, track_vars_array TSRMLS_CC);
+ php_register_variable("PATH_TRANSLATED", SG(request_info).path_translated, track_vars_array TSRMLS_CC);
+
+ if (TG(hc)->one_one) {
+ php_register_variable("SERVER_PROTOCOL", "HTTP/1.1", track_vars_array TSRMLS_CC);
+ } else {
+ php_register_variable("SERVER_PROTOCOL", "HTTP/1.0", track_vars_array TSRMLS_CC);
+ }
+
+ p = httpd_ntoa(&TG(hc)->client_addr);
+
+ ADD_STRING_EX("REMOTE_ADDR", p);
+ ADD_STRING_EX("REMOTE_HOST", p);
+
+ ADD_STRING_EX("SERVER_PORT",
+ smart_str_print_long(buf + sizeof(buf) - 1,
+ TG(hc)->hs->port));
+
+ buf[0] = '/';
+ memcpy(buf + 1, TG(hc)->pathinfo, strlen(TG(hc)->pathinfo) + 1);
+ ADD_STRING("PATH_INFO");
+
+ buf[0] = '/';
+ memcpy(buf + 1, TG(hc)->origfilename, strlen(TG(hc)->origfilename) + 1);
+ ADD_STRING("SCRIPT_NAME");
+
+#define CONDADD(name, field) \
+ if (TG(hc)->field[0]) { \
+ php_register_variable(#name, TG(hc)->field, track_vars_array TSRMLS_CC); \
+ }
+
+ CONDADD(QUERY_STRING, query);
+ CONDADD(HTTP_HOST, hdrhost);
+ CONDADD(HTTP_REFERER, referer);
+ CONDADD(HTTP_USER_AGENT, useragent);
+ CONDADD(HTTP_ACCEPT, accept);
+ CONDADD(HTTP_ACCEPT_LANGUAGE, acceptl);
+ CONDADD(HTTP_ACCEPT_ENCODING, accepte);
+ CONDADD(HTTP_COOKIE, cookie);
+ CONDADD(CONTENT_TYPE, contenttype);
+ CONDADD(REMOTE_USER, remoteuser);
+ CONDADD(SERVER_PROTOCOL, protocol);
+
+ if (TG(hc)->contentlength != -1) {
+ ADD_STRING_EX("CONTENT_LENGTH",
+ smart_str_print_long(buf + sizeof(buf) - 1,
+ TG(hc)->contentlength));
+ }
+
+ if (TG(hc)->authorization[0])
+ php_register_variable("AUTH_TYPE", "Basic", track_vars_array TSRMLS_CC);
+}
+
+static PHP_MINIT_FUNCTION(thttpd)
+{
+ return SUCCESS;
+}
+
+static zend_module_entry php_thttpd_module = {
+ STANDARD_MODULE_HEADER,
+ "thttpd",
+ NULL,
+ PHP_MINIT(thttpd),
+ NULL,
+ NULL,
+ NULL,
+ NULL, /* info */
+ NULL,
+ STANDARD_MODULE_PROPERTIES
+};
+
+static int php_thttpd_startup(sapi_module_struct *sapi_module)
+{
+#if PHP_API_VERSION >= 20020918
+ if (php_module_startup(sapi_module, &php_thttpd_module, 1) == FAILURE) {
+#else
+ if (php_module_startup(sapi_module) == FAILURE
+ || zend_startup_module(&php_thttpd_module) == FAILURE) {
+#endif
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+static int sapi_thttpd_get_fd(int *nfd TSRMLS_DC)
+{
+ if (nfd) *nfd = TG(hc)->conn_fd;
+ return SUCCESS;
+}
+
+static sapi_module_struct thttpd_sapi_module = {
+ "thttpd",
+ "thttpd",
+
+ php_thttpd_startup,
+ php_module_shutdown_wrapper,
+
+ NULL, /* activate */
+ NULL, /* deactivate */
+
+ sapi_thttpd_ub_write,
+ NULL,
+ NULL, /* get uid */
+ NULL, /* getenv */
+
+ php_error,
+
+ NULL,
+ sapi_thttpd_send_headers,
+ NULL,
+ sapi_thttpd_read_post,
+ sapi_thttpd_read_cookies,
+
+ sapi_thttpd_register_variables,
+ NULL, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* Child terminate */
+
+ NULL, /* php.ini path override */
+ NULL, /* Block interruptions */
+ NULL, /* Unblock interruptions */
+
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ sapi_thttpd_get_fd
+};
+
+static void thttpd_module_main(int show_source TSRMLS_DC)
+{
+ zend_file_handle file_handle;
+
+ if (php_request_startup(TSRMLS_C) == FAILURE) {
+ return;
+ }
+
+ if (show_source) {
+ zend_syntax_highlighter_ini syntax_highlighter_ini;
+
+ php_get_highlight_struct(&syntax_highlighter_ini);
+ highlight_file(SG(request_info).path_translated, &syntax_highlighter_ini TSRMLS_CC);
+ } else {
+ file_handle.type = ZEND_HANDLE_FILENAME;
+ file_handle.filename = SG(request_info).path_translated;
+ file_handle.free_filename = 0;
+ file_handle.opened_path = NULL;
+
+ php_execute_script(&file_handle TSRMLS_CC);
+ }
+
+ php_request_shutdown(NULL);
+}
+
+static void thttpd_request_ctor(TSRMLS_D)
+{
+ smart_str s = {0};
+
+ TG(seen_cl) = 0;
+ TG(seen_cn) = 0;
+ TG(sbuf).c = 0;
+ SG(request_info).query_string = TG(hc)->query?strdup(TG(hc)->query):NULL;
+
+ smart_str_appends_ex(&s, TG(hc)->hs->cwd, 1);
+ smart_str_appends_ex(&s, TG(hc)->expnfilename, 1);
+ smart_str_0(&s);
+ SG(request_info).path_translated = s.c;
+
+ s.c = NULL;
+ smart_str_appendc_ex(&s, '/', 1);
+ smart_str_appends_ex(&s, TG(hc)->origfilename, 1);
+ smart_str_0(&s);
+ SG(request_info).request_uri = s.c;
+ SG(request_info).request_method = httpd_method_str(TG(hc)->method);
+ if (TG(hc)->one_one) SG(request_info).proto_num = 1001;
+ else SG(request_info).proto_num = 1000;
+ SG(sapi_headers).http_response_code = 200;
+ if (TG(hc)->contenttype)
+ SG(request_info).content_type = strdup(TG(hc)->contenttype);
+ SG(request_info).content_length = TG(hc)->contentlength == -1 ? 0
+ : TG(hc)->contentlength;
+
+ TG(unconsumed_length) = SG(request_info).content_length;
+
+ php_handle_auth_data(TG(hc)->authorization TSRMLS_CC);
+}
+
+static void thttpd_request_dtor(TSRMLS_D)
+{
+ smart_str_free_ex(&TG(sbuf), 1);
+ if (SG(request_info).query_string)
+ free(SG(request_info).query_string);
+ free(SG(request_info).request_uri);
+ free(SG(request_info).path_translated);
+ if (SG(request_info).content_type)
+ free(SG(request_info).content_type);
+}
+
+#ifdef ZTS
+
+#ifdef TSRM_ST
+#define thread_create_simple_detached(n) st_thread_create(n, NULL, 0, 0)
+#define thread_usleep(n) st_usleep(n)
+#define thread_exit() st_thread_exit(NULL)
+/* No preemption, simple operations are safe */
+#define thread_atomic_inc(n) (++n)
+#define thread_atomic_dec(n) (--n)
+#else
+#error No thread primitives available
+#endif
+
+/* We might want to replace this with a STAILQ */
+typedef struct qreq {
+ httpd_conn *hc;
+ struct qreq *next;
+} qreq_t;
+
+static MUTEX_T qr_lock;
+static qreq_t *queued_requests;
+static qreq_t *last_qr;
+static int nr_free_threads;
+static int nr_threads;
+static int max_threads = 50;
+
+#define HANDLE_STRINGS() { \
+ HANDLE_STR(encodedurl); \
+ HANDLE_STR(decodedurl); \
+ HANDLE_STR(origfilename); \
+ HANDLE_STR(expnfilename); \
+ HANDLE_STR(pathinfo); \
+ HANDLE_STR(query); \
+ HANDLE_STR(referer); \
+ HANDLE_STR(useragent); \
+ HANDLE_STR(accept); \
+ HANDLE_STR(accepte); \
+ HANDLE_STR(acceptl); \
+ HANDLE_STR(cookie); \
+ HANDLE_STR(contenttype); \
+ HANDLE_STR(authorization); \
+ HANDLE_STR(remoteuser); \
+ }
+
+static httpd_conn *duplicate_conn(httpd_conn *hc, httpd_conn *nhc)
+{
+ memcpy(nhc, hc, sizeof(*nhc));
+
+#define HANDLE_STR(m) nhc->m = nhc->m ? strdup(nhc->m) : NULL
+ HANDLE_STRINGS();
+#undef HANDLE_STR
+
+ return nhc;
+}
+
+static void destroy_conn(httpd_conn *hc)
+{
+#define HANDLE_STR(m) if (hc->m) free(hc->m)
+ HANDLE_STRINGS();
+#undef HANDLE_STR
+}
+
+static httpd_conn *dequeue_request(void)
+{
+ httpd_conn *ret = NULL;
+ qreq_t *m;
+
+ tsrm_mutex_lock(qr_lock);
+ if (queued_requests) {
+ m = queued_requests;
+ ret = m->hc;
+ if (!(queued_requests = m->next))
+ last_qr = NULL;
+ free(m);
+ }
+ tsrm_mutex_unlock(qr_lock);
+
+ return ret;
+}
+
+static void *worker_thread(void *);
+
+static void queue_request(httpd_conn *hc)
+{
+ qreq_t *m;
+ httpd_conn *nhc;
+
+ /* Mark as long-running request */
+ hc->file_address = (char *) 1;
+
+ /*
+ * We cannot synchronously revoke accesses to hc in the worker
+ * thread, so we need to pass a copy of hc to the worker thread.
+ */
+ nhc = malloc(sizeof *nhc);
+ duplicate_conn(hc, nhc);
+
+ /* Allocate request queue container */
+ m = malloc(sizeof *m);
+ m->hc = nhc;
+ m->next = NULL;
+
+ tsrm_mutex_lock(qr_lock);
+ /* Create new threads when reaching a certain threshhold */
+ if (nr_threads < max_threads && nr_free_threads < 2) {
+ nr_threads++; /* protected by qr_lock */
+
+ thread_atomic_inc(nr_free_threads);
+ thread_create_simple_detached(worker_thread);
+ }
+ /* Insert container into request queue */
+ if (queued_requests)
+ last_qr->next = m;
+ else
+ queued_requests = m;
+ last_qr = m;
+ tsrm_mutex_unlock(qr_lock);
+}
+
+static off_t thttpd_real_php_request(httpd_conn *hc, int TSRMLS_DC);
+
+static void *worker_thread(void *dummy)
+{
+ int do_work = 50;
+ httpd_conn *hc;
+
+ while (do_work) {
+ hc = dequeue_request();
+
+ if (!hc) {
+/* do_work--; */
+ thread_usleep(500000);
+ continue;
+ }
+/* do_work = 50; */
+
+ thread_atomic_dec(nr_free_threads);
+
+ thttpd_real_php_request(hc, 0 TSRMLS_CC);
+ shutdown(hc->conn_fd, 0);
+ destroy_conn(hc);
+ free(hc);
+
+ thread_atomic_inc(nr_free_threads);
+ }
+ thread_atomic_dec(nr_free_threads);
+ thread_atomic_dec(nr_threads);
+ thread_exit();
+}
+
+static void remove_dead_conn(int fd)
+{
+ qreq_t *m, *prev = NULL;
+
+ tsrm_mutex_lock(qr_lock);
+ m = queued_requests;
+ while (m) {
+ if (m->hc->conn_fd == fd) {
+ if (prev)
+ if (!(prev->next = m->next))
+ last_qr = prev;
+ else
+ if (!(queued_requests = m->next))
+ last_qr = NULL;
+ destroy_conn(m->hc);
+ free(m->hc);
+ free(m);
+ break;
+ }
+ prev = m;
+ m = m->next;
+ }
+ tsrm_mutex_unlock(qr_lock);
+}
+
+#endif
+
+static off_t thttpd_real_php_request(httpd_conn *hc, int show_source TSRMLS_DC)
+{
+ TG(hc) = hc;
+ hc->bytes_sent = 0;
+
+ if (hc->contentlength != -1) {
+ hc->should_linger = 1;
+ hc->do_keep_alive = 0;
+ }
+
+ if (hc->contentlength != -1
+ && SIZEOF_UNCONSUMED_BYTES() < hc->contentlength) {
+ hc->read_body_into_mem = 1;
+ return 0;
+ }
+
+ thttpd_request_ctor(TSRMLS_C);
+
+ thttpd_module_main(show_source TSRMLS_CC);
+
+ /* disable kl, if no content-length was seen or Connection: was set */
+ if (TG(seen_cl) == 0 || TG(seen_cn) == 1) {
+ TG(hc)->do_keep_alive = 0;
+ }
+
+ if (TG(sbuf).c != 0) {
+ if (TG(hc)->response)
+ free(TG(hc)->response);
+
+ TG(hc)->response = TG(sbuf).c;
+ TG(hc)->responselen = TG(sbuf).len;
+ TG(hc)->maxresponse = TG(sbuf).a;
+
+ TG(sbuf).c = 0;
+ TG(sbuf).len = 0;
+ TG(sbuf).a = 0;
+ }
+
+ thttpd_request_dtor(TSRMLS_C);
+
+ return 0;
+}
+
+off_t thttpd_php_request(httpd_conn *hc, int show_source)
+{
+#ifdef ZTS
+ queue_request(hc);
+#else
+ TSRMLS_FETCH();
+ return thttpd_real_php_request(hc, show_source TSRMLS_CC);
+#endif
+}
+
+void thttpd_register_on_close(void (*arg)(int))
+{
+ TSRMLS_FETCH();
+ TG(on_close) = arg;
+}
+
+void thttpd_closed_conn(int fd)
+{
+ TSRMLS_FETCH();
+ if (TG(on_close)) TG(on_close)(fd);
+}
+
+int thttpd_get_fd(void)
+{
+ TSRMLS_FETCH();
+ return TG(hc)->conn_fd;
+}
+
+void thttpd_set_dont_close(void)
+{
+ TSRMLS_FETCH();
+#ifndef PREMIUM_THTTPD
+ TG(hc)->file_address = (char *) 1;
+#endif
+}
+
+
+void thttpd_php_init(void)
+{
+ char *ini;
+
+#ifdef ZTS
+ tsrm_startup(1, 1, 0, NULL);
+ ts_allocate_id(&thttpd_globals_id, sizeof(php_thttpd_globals), NULL, NULL);
+ qr_lock = tsrm_mutex_alloc();
+ thttpd_register_on_close(remove_dead_conn);
+#endif
+
+ if ((ini = getenv("PHP_INI_PATH"))) {
+ thttpd_sapi_module.php_ini_path_override = ini;
+ }
+
+ sapi_startup(&thttpd_sapi_module);
+ thttpd_sapi_module.startup(&thttpd_sapi_module);
+
+ {
+ TSRMLS_FETCH();
+
+ SG(server_context) = (void *) 1;
+ }
+}
+
+void thttpd_php_shutdown(void)
+{
+ TSRMLS_FETCH();
+
+ if (SG(server_context) != NULL) {
+ thttpd_sapi_module.shutdown(&thttpd_sapi_module);
+ sapi_shutdown();
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+ }
+}
diff --git a/sapi/thttpd/thttpd_patch b/sapi/thttpd/thttpd_patch
new file mode 100644
index 0000000..33de921
--- /dev/null
+++ b/sapi/thttpd/thttpd_patch
@@ -0,0 +1,2377 @@
+diff -ur thttpd-2.21b/Makefile.in thttpd-2.21b-cool/Makefile.in
+--- thttpd-2.21b/Makefile.in Thu Mar 29 20:36:21 2001
++++ thttpd-2.21b-cool/Makefile.in Sat Sep 20 14:43:20 2003
+@@ -46,13 +46,15 @@
+
+ # You shouldn't need to edit anything below here.
+
++include php_makefile
++
+ CC = @CC@
+ CCOPT = @V_CCOPT@
+ DEFS = @DEFS@
+-INCLS = -I.
++INCLS = -I. $(PHP_CFLAGS)
+ CFLAGS = $(CCOPT) $(DEFS) $(INCLS)
+-LDFLAGS = @LDFLAGS@
+-LIBS = @LIBS@
++LDFLAGS = @LDFLAGS@ $(PHP_LDFLAGS)
++LIBS = @LIBS@ $(PHP_LIBS)
+ NETLIBS = @V_NETLIBS@
+ INSTALL = @INSTALL@
+
+@@ -62,7 +64,7 @@
+ @rm -f $@
+ $(CC) $(CFLAGS) -c $*.c
+
+-SRC = thttpd.c libhttpd.c fdwatch.c mmc.c timers.c match.c tdate_parse.c syslog.c
++SRC = thttpd.c libhttpd.c fdwatch.c mmc.c timers.c match.c tdate_parse.c syslog.c php_thttpd.c
+
+ OBJ = $(SRC:.c=.o) @LIBOBJS@
+
+@@ -77,7 +79,7 @@
+ all: this subdirs
+ this: $(ALL)
+
+-thttpd: $(OBJ)
++thttpd: $(OBJ) libphp5.a
+ @rm -f $@
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) $(NETLIBS)
+
+diff -ur thttpd-2.21b/config.h thttpd-2.21b-cool/config.h
+--- thttpd-2.21b/config.h Mon Apr 9 23:57:36 2001
++++ thttpd-2.21b-cool/config.h Sat Sep 20 14:43:20 2003
+@@ -82,6 +82,11 @@
+ */
+ #define IDLE_READ_TIMELIMIT 60
+
++/* CONFIGURE: How many seconds to allow for reading the subsequent requests
++** on a keep-alive connection. Should be simiar to LINGER_TIME
++*/
++#define IDLE_KEEPALIVE_TIMELIMIT 2
++
+ /* CONFIGURE: How many seconds before an idle connection gets closed.
+ */
+ #define IDLE_SEND_TIMELIMIT 300
+@@ -316,7 +321,7 @@
+ /* CONFIGURE: A list of index filenames to check. The files are searched
+ ** for in this order.
+ */
+-#define INDEX_NAMES "index.html", "index.htm", "Default.htm", "index.cgi"
++#define INDEX_NAMES "index.php", "index.html", "index.htm", "Default.htm", "index.cgi"
+
+ /* CONFIGURE: If this is defined then thttpd will automatically generate
+ ** index pages for directories that don't have an explicit index file.
+diff -ur thttpd-2.21b/configure thttpd-2.21b-cool/configure
+--- thttpd-2.21b/configure Sat Apr 21 02:07:14 2001
++++ thttpd-2.21b-cool/configure Sat Sep 20 14:43:20 2003
+@@ -1021,7 +1021,7 @@
+ fi
+ echo "$ac_t""$CPP" 1>&6
+
+-for ac_hdr in fcntl.h grp.h memory.h paths.h poll.h sys/poll.h sys/event.h osreldate.h
++for ac_hdr in fcntl.h grp.h memory.h paths.h poll.h sys/poll.h sys/event.h osreldate.h netinet/tcp.h
+ do
+ ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+ echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+diff -ur thttpd-2.21b/configure.in thttpd-2.21b-cool/configure.in
+--- thttpd-2.21b/configure.in Sat Apr 21 02:06:23 2001
++++ thttpd-2.21b-cool/configure.in Sat Sep 20 14:43:20 2003
+@@ -64,7 +64,7 @@
+ AC_MSG_RESULT(no)
+ fi
+
+-AC_CHECK_HEADERS(fcntl.h grp.h memory.h paths.h poll.h sys/poll.h sys/event.h osreldate.h)
++AC_CHECK_HEADERS(fcntl.h grp.h memory.h paths.h poll.h sys/poll.h sys/event.h osreldate.h netinet/tcp.h)
+ AC_HEADER_TIME
+ AC_HEADER_DIRENT
+
+diff -ur thttpd-2.21b/fdwatch.c thttpd-2.21b-cool/fdwatch.c
+--- thttpd-2.21b/fdwatch.c Fri Apr 13 07:36:08 2001
++++ thttpd-2.21b-cool/fdwatch.c Sat Sep 20 14:43:20 2003
+@@ -419,6 +419,7 @@
+ if ( pollfds == (struct pollfd*) 0 || poll_fdidx == (int*) 0 ||
+ poll_rfdidx == (int*) 0 )
+ return -1;
++ memset(pollfds, 0, sizeof(struct pollfd) * nfiles);
+ return 0;
+ }
+
+@@ -460,7 +461,7 @@
+
+ ridx = 0;
+ for ( i = 0; i < npollfds; ++i )
+- if ( pollfds[i].revents & ( POLLIN | POLLOUT ) )
++ if ( pollfds[i].revents & ( POLLIN | POLLOUT | POLLERR | POLLHUP | POLLNVAL ) )
+ poll_rfdidx[ridx++] = pollfds[i].fd;
+
+ return r;
+@@ -472,8 +473,8 @@
+ {
+ switch ( fd_rw[fd] )
+ {
+- case FDW_READ: return pollfds[poll_fdidx[fd]].revents & POLLIN;
+- case FDW_WRITE: return pollfds[poll_fdidx[fd]].revents & POLLOUT;
++ case FDW_READ: return pollfds[poll_fdidx[fd]].revents & ( POLLIN | POLLERR | POLLHUP | POLLNVAL );
++ case FDW_WRITE: return pollfds[poll_fdidx[fd]].revents & ( POLLOUT | POLLERR | POLLHUP | POLLNVAL );
+ default: return 0;
+ }
+ }
+diff -ur thttpd-2.21b/libhttpd.c thttpd-2.21b-cool/libhttpd.c
+--- thttpd-2.21b/libhttpd.c Tue Apr 24 00:42:40 2001
++++ thttpd-2.21b-cool/libhttpd.c Sat Sep 20 14:43:29 2003
+@@ -56,6 +56,10 @@
+ #include <unistd.h>
+ #include <stdarg.h>
+
++#ifdef HAVE_NETINET_TCP_H
++#include <netinet/tcp.h>
++#endif
++
+ #ifdef HAVE_OSRELDATE_H
+ #include <osreldate.h>
+ #endif /* HAVE_OSRELDATE_H */
+@@ -85,6 +89,12 @@
+ #include "match.h"
+ #include "tdate_parse.h"
+
++#include "php_thttpd.h"
++
++#ifdef __CYGWIN__
++# define timezone _timezone
++#endif
++
+ #ifndef STDIN_FILENO
+ #define STDIN_FILENO 0
+ #endif
+@@ -111,7 +121,7 @@
+ static int initialize_listen_socket( httpd_sockaddr* saP );
+ static void unlisten( httpd_server* hs );
+ static void add_response( httpd_conn* hc, char* str );
+-static void send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, int length, time_t mod );
++static void send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, int length, time_t mod, const char *, size_t );
+ static void send_response( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg );
+ static void send_response_tail( httpd_conn* hc );
+ static void defang( char* str, char* dfstr, int dfsize );
+@@ -242,6 +252,10 @@
+ free( (void*) hs->cwd );
+ if ( hs->cgi_pattern != (char*) 0 )
+ free( (void*) hs->cgi_pattern );
++ if ( hs->php_pattern != (char*) 0 )
++ free( (void*) hs->php_pattern );
++ if ( hs->phps_pattern != (char*) 0 )
++ free( (void*) hs->phps_pattern );
+ if ( hs->charset != (char*) 0 )
+ free( (void*) hs->charset );
+ if ( hs->url_pattern != (char*) 0 )
+@@ -249,6 +263,7 @@
+ if ( hs->local_pattern != (char*) 0 )
+ free( (void*) hs->local_pattern );
+ free( (void*) hs );
++ thttpd_php_shutdown();
+ }
+
+
+@@ -257,7 +272,8 @@
+ char* hostname, httpd_sockaddr* sa4P, httpd_sockaddr* sa6P, int port,
+ char* cgi_pattern, char* charset, char* cwd, int no_log, FILE* logfp,
+ int no_symlink, int vhost, int global_passwd, char* url_pattern,
+- char* local_pattern, int no_empty_referers )
++ char* local_pattern, int no_empty_referers, char* php_pattern,
++ char* phps_pattern )
+ {
+ httpd_server* hs;
+ static char ghnbuf[256];
+@@ -312,6 +328,8 @@
+ }
+
+ hs->port = port;
++ hs->php_pattern = strdup(php_pattern);
++ hs->phps_pattern = strdup(phps_pattern);
+ if ( cgi_pattern == (char*) 0 )
+ hs->cgi_pattern = (char*) 0;
+ else
+@@ -329,7 +347,7 @@
+ while ( ( cp = strstr( hs->cgi_pattern, "|/" ) ) != (char*) 0 )
+ (void) strcpy( cp + 1, cp + 2 );
+ }
+- hs->charset = strdup( charset );
++ hs->charset = strdup( charset );
+ hs->cwd = strdup( cwd );
+ if ( hs->cwd == (char*) 0 )
+ {
+@@ -385,6 +403,8 @@
+ return (httpd_server*) 0;
+ }
+
++ thttpd_php_init();
++
+ /* Done initializing. */
+ if ( hs->binding_hostname == (char*) 0 )
+ syslog( LOG_INFO, "%.80s starting on port %d", SERVER_SOFTWARE, hs->port );
+@@ -418,6 +438,11 @@
+ }
+ (void) fcntl( listen_fd, F_SETFD, 1 );
+
++#if defined(TCP_DEFER_ACCEPT) && defined(SOL_TCP)
++ on = 30; /* give clients 30s to send first data packet */
++ setsockopt(listen_fd, SOL_TCP, TCP_DEFER_ACCEPT, &on, sizeof(on));
++#endif
++
+ /* Allow reuse of local addresses. */
+ on = 1;
+ if ( setsockopt(
+@@ -582,6 +607,9 @@
+ /* And send it, if necessary. */
+ if ( hc->responselen > 0 )
+ {
++/*
++printf("**RESPONSE [%d]** len = %d\n%*.*s\n", hc->conn_fd, hc->responselen, hc->responselen, hc->responselen, hc->response);
++*/
+ (void) write( hc->conn_fd, hc->response, hc->responselen );
+ hc->responselen = 0;
+ }
+@@ -619,18 +647,22 @@
+ }
+ }
+
++extern time_t httpd_time_now;
++extern char httpd_now_buf[];
++
++#define SMART_STR_USE_REALLOC
++
++#include "ext/standard/php_smart_str.h"
+
+ static void
+-send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, int length, time_t mod )
++send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, int length, time_t mod, const char *last_modified, size_t last_modified_len)
+ {
+- time_t now;
+ const char* rfc1123fmt = "%a, %d %b %Y %H:%M:%S GMT";
+- char nowbuf[100];
+ char modbuf[100];
+- char fixed_type[500];
+- char buf[1000];
+ int partial_content;
+-
++ smart_str s = {0};
++ int type_len;
++
+ hc->status = status;
+ hc->bytes_to_send = length;
+ if ( hc->mime_flag )
+@@ -649,41 +681,89 @@
+ else
+ partial_content = 0;
+
+- now = time( (time_t*) 0 );
+ if ( mod == (time_t) 0 )
+- mod = now;
+- (void) strftime( nowbuf, sizeof(nowbuf), rfc1123fmt, gmtime( &now ) );
+- (void) strftime( modbuf, sizeof(modbuf), rfc1123fmt, gmtime( &mod ) );
+- (void) my_snprintf(
+- fixed_type, sizeof(fixed_type), type, hc->hs->charset );
+- (void) my_snprintf( buf, sizeof(buf),
+- "%.20s %d %s\r\nServer: %s\r\nContent-Type: %s\r\nDate: %s\r\nLast-Modified: %s\r\nAccept-Ranges: bytes\r\nConnection: close\r\n",
+- hc->protocol, status, title, EXPOSED_SERVER_SOFTWARE, fixed_type,
+- nowbuf, modbuf );
+- add_response( hc, buf );
++ mod = httpd_time_now;
++
++ if (last_modified == 0) {
++ (void) strftime( modbuf, sizeof(modbuf), rfc1123fmt, gmtime( &mod ) );
++ last_modified = modbuf;
++ last_modified_len = strlen(modbuf);
++ }
++
++ type_len = strlen(type);
++
++ if (hc->response) {
++ s.c = hc->response;
++ s.len = 0;
++ s.a = hc->maxresponse;
++ hc->response = 0;
++ hc->maxresponse = 0;
++ }
++
++ smart_str_appends(&s, "HTTP/1.1 ");
++ smart_str_append_long(&s, status);
++ smart_str_appends(&s, " HTTP\r\nServer: " EXPOSED_SERVER_SOFTWARE "\r\n"
++ "Content-Type: ");
++
++ if (type[type_len-2] == '%' && type[type_len-1] == 's') {
++ smart_str_appendl(&s, type, type_len - 2);
++ smart_str_appends(&s, hc->hs->charset);
++ } else {
++ smart_str_appendl(&s, type, type_len);
++ }
++
++
++ smart_str_appends(&s, "\r\nDate: ");
++ smart_str_appends(&s, httpd_now_buf);
++ smart_str_appends(&s, "\r\nLast-Modified: ");
++ smart_str_appendl(&s, last_modified, last_modified_len);
++ smart_str_appends(&s, "\r\nAccept-Ranges: bytes\r\n");
++
+ if ( encodings[0] != '\0' )
+ {
+- (void) my_snprintf( buf, sizeof(buf),
+- "Content-Encoding: %s\r\n", encodings );
+- add_response( hc, buf );
++ smart_str_appends(&s, "Content-Encoding: ");
++ smart_str_appends(&s, encodings);
++ smart_str_appends(&s, "\r\n");
+ }
+ if ( partial_content )
+ {
+- (void) my_snprintf( buf, sizeof(buf),
+- "Content-Range: bytes %ld-%ld/%d\r\nContent-Length: %ld\r\n",
+- (long) hc->init_byte_loc, (long) hc->end_byte_loc, length,
+- (long) ( hc->end_byte_loc - hc->init_byte_loc + 1 ) );
+- add_response( hc, buf );
++
++ smart_str_appends(&s, "Content-Range: bytes ");
++ smart_str_append_long(&s, hc->init_byte_loc);
++ smart_str_appendc(&s, '-');
++ smart_str_append_long(&s, hc->end_byte_loc);
++ smart_str_appendc(&s, '/');
++ smart_str_append_long(&s, length);
++ smart_str_appends(&s, "\r\nContent-Length: ");
++ smart_str_append_long(&s, hc->end_byte_loc - hc->init_byte_loc + 1);
++ smart_str_appends(&s, "\r\n");
++
+ }
+ else if ( length >= 0 )
+ {
+- (void) my_snprintf( buf, sizeof(buf),
+- "Content-Length: %d\r\n", length );
+- add_response( hc, buf );
++ smart_str_appends(&s, "Content-Length: ");
++ smart_str_append_long(&s, length);
++ smart_str_appends(&s, "\r\n");
+ }
++ else {
++ hc->do_keep_alive = 0;
++ }
+ if ( extraheads[0] != '\0' )
+- add_response( hc, extraheads );
+- add_response( hc, "\r\n" );
++ smart_str_appends(&s, extraheads);
++ if (hc->do_keep_alive) {
++ smart_str_appends(&s, "Connection: keep-alive\r\n\r\n" );
++ } else {
++ smart_str_appends(&s, "Connection: close\r\n\r\n" );
++ }
++ smart_str_0(&s);
++
++ if (hc->response) {
++ free(hc->response);
++ }
++ hc->response = s.c;
++ hc->maxresponse = s.a;
++ hc->responselen = s.len;
++
+ }
+ }
+
+@@ -725,7 +805,7 @@
+ {
+ char defanged_arg[1000], buf[2000];
+
+- send_mime( hc, status, title, "", extraheads, "text/html", -1, 0 );
++ send_mime( hc, status, title, "", extraheads, "text/html", -1, 0, 0, 0 );
+ (void) my_snprintf( buf, sizeof(buf),
+ "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>\n<BODY BGCOLOR=\"#cc9999\"><H2>%d %s</H2>\n",
+ status, title, status, title );
+@@ -764,7 +844,7 @@
+ char* cp2;
+
+ for ( cp1 = str, cp2 = dfstr;
+- *cp1 != '\0' && cp2 - dfstr < dfsize - 1;
++ *cp1 != '\0' && cp2 - dfstr < dfsize - 5;
+ ++cp1, ++cp2 )
+ {
+ switch ( *cp1 )
+@@ -834,7 +914,7 @@
+ fp = fopen( filename, "r" );
+ if ( fp == (FILE*) 0 )
+ return 0;
+- send_mime( hc, status, title, "", extraheads, "text/html", -1, 0 );
++ send_mime( hc, status, title, "", extraheads, "text/html", -1, 0, 0, 0 );
+ for (;;)
+ {
+ r = fread( buf, 1, sizeof(buf) - 1, fp );
+@@ -1336,6 +1416,9 @@
+ if ( hc->tildemapped )
+ return 1;
+
++ if ( hc->hostname[0] == '.' || strchr( hc->hostname, '/' ) != (char*) 0 )
++ return 0;
++
+ /* Figure out the host directory. */
+ #ifdef VHOST_DIRLEVELS
+ httpd_realloc_str(
+@@ -1436,7 +1519,7 @@
+ restlen = strlen( path );
+ httpd_realloc_str( &rest, &maxrest, restlen );
+ (void) strcpy( rest, path );
+- if ( rest[restlen - 1] == '/' )
++ if ( restlen > 0 && rest[restlen - 1] == '/' )
+ rest[--restlen] = '\0'; /* trim trailing slash */
+ if ( ! tildemapped )
+ /* Remove any leading slashes. */
+@@ -1603,6 +1686,70 @@
+
+
+ int
++httpd_request_reset(httpd_conn* hc, int preserve_read_buf )
++{
++ if (!preserve_read_buf) {
++ hc->read_idx = 0;
++ hc->checked_idx = 0;
++ }
++
++ if (hc->read_buf_is_mmap) {
++ hc->read_buf_is_mmap = 0;
++ munmap(hc->read_buf, hc->read_size);
++ hc->read_buf = NULL;
++ hc->read_size = 0;
++ httpd_realloc_str( &hc->read_buf, &hc->read_size, 500 );
++ }
++ hc->checked_state = CHST_FIRSTWORD;
++ hc->method = METHOD_UNKNOWN;
++ hc->status = 0;
++ hc->bytes_to_send = 0;
++ hc->bytes_sent = 0;
++ hc->encodedurl = "";
++ hc->decodedurl[0] = '\0';
++ hc->protocol = "UNKNOWN";
++ hc->origfilename[0] = '\0';
++ hc->expnfilename[0] = '\0';
++ hc->encodings[0] = '\0';
++ hc->pathinfo[0] = '\0';
++ hc->query[0] = '\0';
++ hc->referer = "";
++ hc->useragent = "";
++ hc->accept[0] = '\0';
++ hc->accepte[0] = '\0';
++ hc->acceptl = "";
++ hc->cookie = "";
++ hc->contenttype = "";
++ hc->reqhost[0] = '\0';
++ hc->hdrhost = "";
++ hc->hostdir[0] = '\0';
++ hc->authorization = "";
++ hc->remoteuser[0] = '\0';
++ hc->response[0] = '\0';
++#ifdef TILDE_MAP_2
++ hc->altdir[0] = '\0';
++#endif /* TILDE_MAP_2 */
++ hc->responselen = 0;
++ hc->if_modified_since = (time_t) -1;
++ hc->range_if = (time_t) -1;
++ hc->contentlength = -1;
++ hc->type = "";
++ hc->hostname = (char*) 0;
++ hc->mime_flag = 1;
++ hc->one_one = 0;
++ hc->got_range = 0;
++ hc->tildemapped = 0;
++ hc->init_byte_loc = 0;
++ hc->end_byte_loc = -1;
++ hc->keep_alive = 0;
++ hc->do_keep_alive = 0;
++ hc->should_linger = 0;
++ hc->file_address = (char*) 0;
++ hc->read_body_into_mem = 0;
++ return GC_OK;
++}
++
++int
+ httpd_get_conn( httpd_server* hs, int listen_fd, httpd_conn* hc )
+ {
+ httpd_sockaddr sa;
+@@ -1612,6 +1759,7 @@
+ {
+ hc->read_size = 0;
+ httpd_realloc_str( &hc->read_buf, &hc->read_size, 500 );
++ hc->read_buf_is_mmap = 0;
+ hc->maxdecodedurl =
+ hc->maxorigfilename = hc->maxexpnfilename = hc->maxencodings =
+ hc->maxpathinfo = hc->maxquery = hc->maxaccept =
+@@ -1631,12 +1779,19 @@
+ httpd_realloc_str( &hc->reqhost, &hc->maxreqhost, 0 );
+ httpd_realloc_str( &hc->hostdir, &hc->maxhostdir, 0 );
+ httpd_realloc_str( &hc->remoteuser, &hc->maxremoteuser, 0 );
+- httpd_realloc_str( &hc->response, &hc->maxresponse, 0 );
++ httpd_realloc_str( &hc->response, &hc->maxresponse, 350 );
+ #ifdef TILDE_MAP_2
+ httpd_realloc_str( &hc->altdir, &hc->maxaltdir, 0 );
+ #endif /* TILDE_MAP_2 */
+ hc->initialized = 1;
+ }
++ if (hc->read_buf_is_mmap) {
++ hc->read_buf_is_mmap = 0;
++ munmap(hc->read_buf, hc->read_size);
++ hc->read_buf = NULL;
++ hc->read_size = 0;
++ httpd_realloc_str( &hc->read_buf, &hc->read_size, 500 );
++ }
+
+ /* Accept the new connection. */
+ sz = sizeof(sa);
+@@ -1657,53 +1812,12 @@
+ hc->hs = hs;
+ memset( &hc->client_addr, 0, sizeof(hc->client_addr) );
+ memcpy( &hc->client_addr, &sa, sockaddr_len( &sa ) );
+- hc->read_idx = 0;
+- hc->checked_idx = 0;
+- hc->checked_state = CHST_FIRSTWORD;
+- hc->method = METHOD_UNKNOWN;
+- hc->status = 0;
+- hc->bytes_to_send = 0;
+- hc->bytes_sent = 0;
+- hc->encodedurl = "";
+- hc->decodedurl[0] = '\0';
+- hc->protocol = "UNKNOWN";
+- hc->origfilename[0] = '\0';
+- hc->expnfilename[0] = '\0';
+- hc->encodings[0] = '\0';
+- hc->pathinfo[0] = '\0';
+- hc->query[0] = '\0';
+- hc->referer = "";
+- hc->useragent = "";
+- hc->accept[0] = '\0';
+- hc->accepte[0] = '\0';
+- hc->acceptl = "";
+- hc->cookie = "";
+- hc->contenttype = "";
+- hc->reqhost[0] = '\0';
+- hc->hdrhost = "";
+- hc->hostdir[0] = '\0';
+- hc->authorization = "";
+- hc->remoteuser[0] = '\0';
+- hc->response[0] = '\0';
+-#ifdef TILDE_MAP_2
+- hc->altdir[0] = '\0';
+-#endif /* TILDE_MAP_2 */
+- hc->responselen = 0;
+- hc->if_modified_since = (time_t) -1;
+- hc->range_if = (time_t) -1;
+- hc->contentlength = -1;
+- hc->type = "";
+- hc->hostname = (char*) 0;
+- hc->mime_flag = 1;
+- hc->one_one = 0;
+- hc->got_range = 0;
+- hc->tildemapped = 0;
+- hc->init_byte_loc = 0;
+- hc->end_byte_loc = -1;
+- hc->keep_alive = 0;
+- hc->should_linger = 0;
+- hc->file_address = (char*) 0;
+- return GC_OK;
++
++/*
++printf("doing httpd_get_con(%d)\n", hc->conn_fd);
++*/
++
++ return httpd_request_reset(hc, 0);
+ }
+
+
+@@ -1720,6 +1834,9 @@
+ {
+ char c;
+
++/*
++printf("**REQUEST [%d]**\n%*.*s\n", hc->conn_fd, hc->read_idx, hc->read_idx, hc->read_buf);
++*/
+ for ( ; hc->checked_idx < hc->read_idx; ++hc->checked_idx )
+ {
+ c = hc->read_buf[hc->checked_idx];
+@@ -1912,8 +2029,11 @@
+ eol = strpbrk( protocol, " \t\n\r" );
+ if ( eol != (char*) 0 )
+ *eol = '\0';
+- if ( strcasecmp( protocol, "HTTP/1.0" ) != 0 )
++ if ( strcasecmp( protocol, "HTTP/1.0" ) != 0 ) {
+ hc->one_one = 1;
++ hc->keep_alive = 1;
++ hc->do_keep_alive = 1;
++ }
+ }
+ }
+ /* Check for HTTP/1.1 absolute URL. */
+@@ -2129,6 +2249,7 @@
+ cp = &buf[11];
+ cp += strspn( cp, " \t" );
+ if ( strcasecmp( cp, "keep-alive" ) == 0 )
++ hc->do_keep_alive = 1;
+ hc->keep_alive = 1;
+ }
+ #ifdef LOG_UNKNOWN_HEADERS
+@@ -2168,6 +2289,9 @@
+ }
+ }
+
++/*
++printf("one_one = %d keep_alive = %d\n", hc->one_one, hc->keep_alive);
++*/
+ if ( hc->one_one )
+ {
+ /* Check that HTTP/1.1 requests specify a host, as required. */
+@@ -2177,14 +2301,14 @@
+ return -1;
+ }
+
+- /* If the client wants to do keep-alives, it might also be doing
+- ** pipelining. There's no way for us to tell. Since we don't
+- ** implement keep-alives yet, if we close such a connection there
+- ** might be unread pipelined requests waiting. So, we have to
+- ** do a lingering close.
++ /*
++ ** Disable keep alive support for bad browsers,
++ ** list taken from Apache 1.3.19
+ */
+- if ( hc->keep_alive )
+- hc->should_linger = 1;
++ if ( hc->do_keep_alive &&
++ ( strstr(hc->useragent, "Mozilla/2") != NULL ||
++ strstr(hc->useragent, "MSIE 4.0b2;") != NULL))
++ hc->do_keep_alive = 0;
+ }
+
+ /* Ok, the request has been parsed. Now we resolve stuff that
+@@ -2349,15 +2473,24 @@
+
+
+ void
+-httpd_close_conn( httpd_conn* hc, struct timeval* nowP )
+- {
+- make_log_entry( hc, nowP );
++httpd_complete_request( httpd_conn* hc, struct timeval* nowP)
++{
++ if (hc->method != METHOD_UNKNOWN)
++ make_log_entry( hc, nowP );
+
+- if ( hc->file_address != (char*) 0 )
++ if ( hc->file_address == (char*) 1 )
++ {
++ thttpd_closed_conn(hc->conn_fd);
++ } else if ( hc->file_address != (char*) 0 )
+ {
+ mmc_unmap( hc->file_address, &(hc->sb), nowP );
+ hc->file_address = (char*) 0;
+ }
++ }
++
++void
++httpd_close_conn( httpd_conn* hc, struct timeval* nowP )
++{
+ if ( hc->conn_fd >= 0 )
+ {
+ (void) close( hc->conn_fd );
+@@ -2370,7 +2503,12 @@
+ {
+ if ( hc->initialized )
+ {
+- free( (void*) hc->read_buf );
++
++ if ( hc->read_buf_is_mmap ) {
++ munmap( hc->read_buf, hc->read_size );
++ } else {
++ free( (void*) hc->read_buf );
++ }
+ free( (void*) hc->decodedurl );
+ free( (void*) hc->origfilename );
+ free( (void*) hc->expnfilename );
+@@ -2556,7 +2694,7 @@
+ return -1;
+ }
+
+- send_mime( hc, 200, ok200title, "", "", "text/html", -1, hc->sb.st_mtime );
++ send_mime( hc, 200, ok200title, "", "", "text/html", -1, hc->sb.st_mtime, 0, 0 );
+ if ( hc->method == METHOD_HEAD )
+ closedir( dirp );
+ else if ( hc->method == METHOD_GET )
+@@ -3026,11 +3164,9 @@
+ post_post_garbage_hack( httpd_conn* hc )
+ {
+ char buf[2];
+- int r;
+
+- r = recv( hc->conn_fd, buf, sizeof(buf), MSG_PEEK );
+- if ( r > 0 )
+- (void) read( hc->conn_fd, buf, r );
++ fcntl(hc->conn_fd, F_SETFL, O_NONBLOCK);
++ (void) read( hc->conn_fd, buf, 2 );
+ }
+
+
+@@ -3313,6 +3449,11 @@
+ int r;
+ ClientData client_data;
+
++ /*
++ ** We are not going to leave the socket open after a CGI... too hard
++ */
++ hc->do_keep_alive = 0;
++
+ if ( hc->method == METHOD_GET || hc->method == METHOD_POST )
+ {
+ httpd_clear_ndelay( hc->conn_fd );
+@@ -3369,6 +3510,7 @@
+ int expnlen, indxlen;
+ char* cp;
+ char* pi;
++ int nocache = 0;
+
+ expnlen = strlen( hc->expnfilename );
+
+@@ -3561,6 +3703,16 @@
+ match( hc->hs->cgi_pattern, hc->expnfilename ) )
+ return cgi( hc );
+
++ if ( hc->hs->php_pattern != (char*) 0 &&
++ match( hc->hs->php_pattern, hc->expnfilename)) {
++ return thttpd_php_request( hc, 0 );
++ }
++
++ if ( hc->hs->phps_pattern != (char*) 0 &&
++ match( hc->hs->phps_pattern, hc->expnfilename)) {
++ return thttpd_php_request( hc, 1 );
++ }
++
+ /* It's not CGI. If it's executable or there's pathinfo, someone's
+ ** trying to either serve or run a non-CGI file as CGI. Either case
+ ** is prohibited.
+@@ -3594,32 +3746,46 @@
+ hc->end_byte_loc = hc->sb.st_size - 1;
+
+ figure_mime( hc );
++ if ( strncmp(hc->decodedurl, "/nocache/", sizeof("/nocache/") - 1 ) == 0 )
++ nocache = 1;
+
+ if ( hc->method == METHOD_HEAD )
+ {
+ send_mime(
+ hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size,
+- hc->sb.st_mtime );
++ hc->sb.st_mtime, 0, 0 );
+ }
+- else if ( hc->if_modified_since != (time_t) -1 &&
++ else if ( !nocache && hc->if_modified_since != (time_t) -1 &&
+ hc->if_modified_since >= hc->sb.st_mtime )
+ {
+- hc->method = METHOD_HEAD;
+ send_mime(
+- hc, 304, err304title, hc->encodings, "", hc->type, hc->sb.st_size,
+- hc->sb.st_mtime );
++ hc, 304, err304title, hc->encodings, "", hc->type, -1,
++ hc->sb.st_mtime, 0, 0 );
+ }
+ else
+ {
+- hc->file_address = mmc_map( hc->expnfilename, &(hc->sb), nowP );
++ char *extraheads = "";
++ char *lm;
++ size_t lml;
++
++ if ( nocache )
++ {
++ extraheads = "Expires: Thu, 19 Nov 1981 08:52:00 GMT\r\n"
++ "Cache-Control: no-store, no-cache, must-revalidate, "
++ "post-check=0, pre-check=0\r\n"
++ "Pragma: no-cache\r\n";
++ }
++
++ hc->file_address = mmc_map( hc->expnfilename, &(hc->sb), nowP, nocache, &lm, &lml );
+ if ( hc->file_address == (char*) 0 )
+ {
+ httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
+ return -1;
+ }
++
+ send_mime(
+- hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size,
+- hc->sb.st_mtime );
++ hc, 200, ok200title, hc->encodings, extraheads, hc->type, hc->sb.st_size,
++ hc->sb.st_mtime, lm, lml );
+ }
+
+ return 0;
+@@ -3638,6 +3804,9 @@
+ return r;
+ }
+
++#define smart_str_append_const(a,b) smart_str_appendl(a,b,sizeof(b)-1)
++
++static smart_str bentries;
+
+ static void
+ make_log_entry( httpd_conn* hc, struct timeval* nowP )
+@@ -3648,88 +3817,62 @@
+
+ if ( hc->hs->no_log )
+ return;
+-
+- /* This is straight CERN Combined Log Format - the only tweak
+- ** being that if we're using syslog() we leave out the date, because
+- ** syslogd puts it in. The included syslogtocern script turns the
+- ** results into true CERN format.
+- */
+-
+ /* Format remote user. */
+ if ( hc->remoteuser[0] != '\0' )
+- ru = hc->remoteuser;
++ ru = hc->remoteuser;
+ else
+- ru = "-";
++ ru = "-";
+ /* If we're vhosting, prepend the hostname to the url. This is
+ ** a little weird, perhaps writing separate log files for
+ ** each vhost would make more sense.
+ */
+- if ( hc->hs->vhost && ! hc->tildemapped )
+- (void) my_snprintf( url, sizeof(url),
+- "/%.100s%.200s",
+- hc->hostname == (char*) 0 ? hc->hs->server_hostname : hc->hostname,
+- hc->encodedurl );
+- else
+- (void) my_snprintf( url, sizeof(url),
+- "%.200s", hc->encodedurl );
+- /* Format the bytes. */
+- if ( (long) hc->bytes_sent >= 0 )
+- (void) my_snprintf( bytes, sizeof(bytes),
+- "%ld", (long) hc->bytes_sent );
+- else
+- (void) strcpy( bytes, "-" );
+
+ /* Logfile or syslog? */
+ if ( hc->hs->logfp != (FILE*) 0 )
+- {
+- time_t now;
+- struct tm* t;
+- const char* cernfmt_nozone = "%d/%b/%Y:%H:%M:%S";
+- char date_nozone[100];
+- int zone;
+- char sign;
+- char date[100];
+-
+- /* Get the current time, if necessary. */
+- if ( nowP != (struct timeval*) 0 )
+- now = nowP->tv_sec;
+- else
+- now = time( (time_t*) 0 );
+- /* Format the time, forcing a numeric timezone (some log analyzers
+- ** are stoooopid about this).
+- */
+- t = localtime( &now );
+- (void) strftime( date_nozone, sizeof(date_nozone), cernfmt_nozone, t );
+-#ifdef HAVE_TM_GMTOFF
+- zone = t->tm_gmtoff / 60L;
+-#else
+- zone = -timezone / 60L;
+- /* Probably have to add something about daylight time here. */
+-#endif
+- if ( zone >= 0 )
+- sign = '+';
+- else
+- {
+- sign = '-';
+- zone = -zone;
+- }
+- zone = ( zone / 60 ) * 100 + zone % 60;
+- (void) my_snprintf( date, sizeof(date),
+- "%s %c%04d", date_nozone, sign, zone );
+- /* And write the log entry. */
+- (void) fprintf( hc->hs->logfp,
+- "%.80s - %.80s [%s] \"%.80s %.300s %.80s\" %d %s \"%.200s\" \"%.80s\"\n",
+- httpd_ntoa( &hc->client_addr ), ru, date,
+- httpd_method_str( hc->method ), url, hc->protocol,
+- hc->status, bytes, hc->referer, hc->useragent );
+- (void) fflush( hc->hs->logfp ); /* don't need to flush every time */
+- }
+- else
+- syslog( LOG_INFO,
+- "%.80s - %.80s \"%.80s %.200s %.80s\" %d %s \"%.200s\" \"%.80s\"",
+- httpd_ntoa( &hc->client_addr ), ru,
+- httpd_method_str( hc->method ), url, hc->protocol,
+- hc->status, bytes, hc->referer, hc->useragent );
++ {
++ /* XXXXXXX */
++
++ smart_str_appends(&bentries, httpd_ntoa(&hc->client_addr));
++ smart_str_append_const(&bentries, " - ");
++ smart_str_appends(&bentries, ru);
++ smart_str_append_const(&bentries, " [");
++ smart_str_appendl(&bentries, hc->hs->log_date, hc->hs->log_date_len);
++ smart_str_append_const(&bentries, "] \"");
++ smart_str_appends(&bentries, httpd_method_str(hc->method));
++ smart_str_appendc(&bentries, ' ');
++
++ if (hc->hs->vhost && ! hc->tildemapped) {
++ smart_str_appendc(&bentries, '/');
++ if (hc->hostname)
++ smart_str_appends(&bentries, hc->hostname);
++ else
++ smart_str_appends(&bentries, hc->hs->server_hostname);
++ }
++ smart_str_appends(&bentries, hc->encodedurl);
++
++ smart_str_appendc(&bentries, ' ');
++ smart_str_appends(&bentries, hc->protocol);
++ smart_str_append_const(&bentries, "\" ");
++ smart_str_append_long(&bentries, hc->status);
++ if (hc->bytes_sent >= 0) {
++ smart_str_appendc(&bentries, ' ');
++ smart_str_append_long(&bentries, hc->bytes_sent);
++ smart_str_append_const(&bentries, " \"");
++ } else {
++ smart_str_append_const(&bentries, " - \"");
++ }
++ smart_str_appends(&bentries, hc->referer);
++ smart_str_append_const(&bentries, "\" \"");
++ smart_str_appends(&bentries, hc->useragent);
++ smart_str_append_const(&bentries, "\"\n");
++
++ if (bentries.len > 16384) {
++ int fd = fileno(hc->hs->logfp);
++ write(fd, bentries.c, bentries.len);
++ bentries.len = 0;
++ }
++ }
++
+ }
+
+
+@@ -3840,7 +3983,24 @@
+ {
+ #ifdef HAVE_GETNAMEINFO
+ static char str[200];
++ static smart_str httpd_ntoa_buf;
++
++ if (saP->sa_in.sin_family == AF_INET) {
++ unsigned long n = ntohl(saP->sa_in.sin_addr.s_addr);
+
++ httpd_ntoa_buf.len = 0;
++ smart_str_append_long(&httpd_ntoa_buf, (n >> 24));
++ smart_str_appendc(&httpd_ntoa_buf, '.');
++ smart_str_append_long(&httpd_ntoa_buf, (n >> 16) & 255);
++ smart_str_appendc(&httpd_ntoa_buf, '.');
++ smart_str_append_long(&httpd_ntoa_buf, (n >> 8) & 255);
++ smart_str_appendc(&httpd_ntoa_buf, '.');
++ smart_str_append_long(&httpd_ntoa_buf, (n >> 0) & 255);
++ smart_str_0(&httpd_ntoa_buf);
++
++ return httpd_ntoa_buf.c;
++ }
++
+ if ( getnameinfo( &saP->sa, sockaddr_len( saP ), str, sizeof(str), 0, 0, NI_NUMERICHOST ) != 0 )
+ {
+ str[0] = '?';
+diff -ur thttpd-2.21b/libhttpd.h thttpd-2.21b-cool/libhttpd.h
+--- thttpd-2.21b/libhttpd.h Tue Apr 24 00:36:50 2001
++++ thttpd-2.21b-cool/libhttpd.h Sat Sep 20 14:43:20 2003
+@@ -69,6 +69,8 @@
+ char* server_hostname;
+ int port;
+ char* cgi_pattern;
++ char* php_pattern;
++ char* phps_pattern;
+ char* charset;
+ char* cwd;
+ int listen4_fd, listen6_fd;
+@@ -80,6 +82,8 @@
+ char* url_pattern;
+ char* local_pattern;
+ int no_empty_referers;
++ size_t log_date_len;
++ char log_date[100];
+ } httpd_server;
+
+ /* A connection. */
+@@ -88,6 +92,7 @@
+ httpd_server* hs;
+ httpd_sockaddr client_addr;
+ char* read_buf;
++ char read_buf_is_mmap;
+ int read_size, read_idx, checked_idx;
+ int checked_state;
+ int method;
+@@ -132,11 +137,12 @@
+ int got_range;
+ int tildemapped; /* this connection got tilde-mapped */
+ off_t init_byte_loc, end_byte_loc;
+- int keep_alive;
++ int keep_alive, do_keep_alive;
+ int should_linger;
+ struct stat sb;
+ int conn_fd;
+ char* file_address;
++ char read_body_into_mem;
+ } httpd_conn;
+
+ /* Methods. */
+@@ -168,7 +174,8 @@
+ char* hostname, httpd_sockaddr* sa4P, httpd_sockaddr* sa6P, int port,
+ char* cgi_pattern, char* charset, char* cwd, int no_log, FILE* logfp,
+ int no_symlink, int vhost, int global_passwd, char* url_pattern,
+- char* local_pattern, int no_empty_referers );
++ char* local_pattern, int no_empty_referers, char* php_pattern,
++ char* phps_pattern );
+
+ /* Change the log file. */
+ extern void httpd_set_logfp( httpd_server* hs, FILE* logfp );
+@@ -229,6 +236,8 @@
+ ** If you don't have a current timeval handy just pass in 0.
+ */
+ extern void httpd_close_conn( httpd_conn* hc, struct timeval* nowP );
++void httpd_complete_request( httpd_conn* hc, struct timeval* nowP);
++int httpd_request_reset(httpd_conn* hc,int );
+
+ /* Call this to de-initialize a connection struct and *really* free the
+ ** mallocced strings.
+diff -ur thttpd-2.21b/mime_encodings.txt thttpd-2.21b-cool/mime_encodings.txt
+--- thttpd-2.21b/mime_encodings.txt Wed May 10 03:22:28 2000
++++ thttpd-2.21b-cool/mime_encodings.txt Sat Sep 20 14:43:20 2003
+@@ -3,6 +3,6 @@
+ # A list of file extensions followed by the corresponding MIME encoding.
+ # Extensions not found in the table proceed to the mime_types table.
+
+-Z x-compress
+-gz x-gzip
++Z compress
++gz gzip
+ uu x-uuencode
+diff -ur thttpd-2.21b/mime_types.txt thttpd-2.21b-cool/mime_types.txt
+--- thttpd-2.21b/mime_types.txt Sat Apr 14 04:53:30 2001
++++ thttpd-2.21b-cool/mime_types.txt Sat Sep 20 14:43:20 2003
+@@ -1,135 +1,138 @@
+-# mime_types.txt
+-#
+-# A list of file extensions followed by the corresponding MIME type.
+-# Extensions not found in the table are returned as text/plain.
+-
+-html text/html; charset=%s
+-htm text/html; charset=%s
+-txt text/plain; charset=%s
+-rtx text/richtext
+-etx text/x-setext
+-tsv text/tab-separated-values
+-css text/css
+-xml text/xml
+-dtd text/xml
+-
+-gif image/gif
+-jpg image/jpeg
+-jpeg image/jpeg
+-jpe image/jpeg
+-jfif image/jpeg
+-tif image/tiff
+-tiff image/tiff
+-pbm image/x-portable-bitmap
+-pgm image/x-portable-graymap
+-ppm image/x-portable-pixmap
+-pnm image/x-portable-anymap
+-xbm image/x-xbitmap
+-xpm image/x-xpixmap
+-xwd image/x-xwindowdump
+-ief image/ief
+-png image/png
+-
+-au audio/basic
+-snd audio/basic
+-aif audio/x-aiff
+-aiff audio/x-aiff
+-aifc audio/x-aiff
+-ra audio/x-pn-realaudio
+-ram audio/x-pn-realaudio
+-rm audio/x-pn-realaudio
+-rpm audio/x-pn-realaudio-plugin
+-wav audio/wav
+-mid audio/midi
+-midi audio/midi
+-kar audio/midi
+-mpga audio/mpeg
+-mp2 audio/mpeg
+-mp3 audio/mpeg
+-
+-mpeg video/mpeg
+-mpg video/mpeg
+-mpe video/mpeg
+-qt video/quicktime
+-mov video/quicktime
+-avi video/x-msvideo
+-movie video/x-sgi-movie
+-mv video/x-sgi-movie
+-vx video/x-rad-screenplay
+-
+-a application/octet-stream
++ez application/andrew-inset
++hqx application/mac-binhex40
++cpt application/mac-compactpro
++doc application/msword
+ bin application/octet-stream
++dms application/octet-stream
++lha application/octet-stream
++lzh application/octet-stream
+ exe application/octet-stream
+-dump application/octet-stream
+-o application/octet-stream
+-class application/java
+-js application/x-javascript
++class application/octet-stream
++so application/octet-stream
++dll application/octet-stream
++oda application/oda
++pdf application/pdf
+ ai application/postscript
+ eps application/postscript
+ ps application/postscript
+-dir application/x-director
++smi application/smil
++smil application/smil
++mif application/vnd.mif
++xls application/vnd.ms-excel
++ppt application/vnd.ms-powerpoint
++wbxml application/vnd.wap.wbxml
++wmlc application/vnd.wap.wmlc
++wmlsc application/vnd.wap.wmlscriptc
++bcpio application/x-bcpio
++vcd application/x-cdlink
++pgn application/x-chess-pgn
++cpio application/x-cpio
++csh application/x-csh
+ dcr application/x-director
++dir application/x-director
+ dxr application/x-director
+-fgd application/x-director
+-aam application/x-authorware-map
+-aas application/x-authorware-seg
+-aab application/x-authorware-bin
+-fh4 image/x-freehand
+-fh7 image/x-freehand
+-fh5 image/x-freehand
+-fhc image/x-freehand
+-fh image/x-freehand
+-spl application/futuresplash
+-swf application/x-shockwave-flash
+ dvi application/x-dvi
++spl application/x-futuresplash
+ gtar application/x-gtar
+ hdf application/x-hdf
+-hqx application/mac-binhex40
+-iv application/x-inventor
++js application/x-javascript
++skp application/x-koan
++skd application/x-koan
++skt application/x-koan
++skm application/x-koan
+ latex application/x-latex
+-man application/x-troff-man
+-me application/x-troff-me
+-mif application/x-mif
+-ms application/x-troff-ms
+-oda application/oda
+-pdf application/pdf
+-rtf application/rtf
+-bcpio application/x-bcpio
+-cpio application/x-cpio
+-sv4cpio application/x-sv4cpio
+-sv4crc application/x-sv4crc
+-sh application/x-shar
++nc application/x-netcdf
++cdf application/x-netcdf
++sh application/x-sh
+ shar application/x-shar
++swf application/x-shockwave-flash
+ sit application/x-stuffit
++sv4cpio application/x-sv4cpio
++sv4crc application/x-sv4crc
+ tar application/x-tar
++tcl application/x-tcl
+ tex application/x-tex
+-texi application/x-texinfo
+ texinfo application/x-texinfo
++texi application/x-texinfo
++t application/x-troff
+ tr application/x-troff
+ roff application/x-troff
+ man application/x-troff-man
+ me application/x-troff-me
+ ms application/x-troff-ms
+-zip application/x-zip-compressed
+-tsp application/dsptype
+-wsrc application/x-wais-source
+ ustar application/x-ustar
+-cdf application/x-netcdf
+-nc application/x-netcdf
+-doc application/msword
+-ppt application/powerpoint
+-
+-crt application/x-x509-ca-cert
+-crl application/x-pkcs7-crl
+-
++src application/x-wais-source
++xhtml application/xhtml+xml
++xht application/xhtml+xml
++zip application/zip
++au audio/basic
++snd audio/basic
++mid audio/midi
++midi audio/midi
++kar audio/midi
++mpga audio/mpeg
++mp2 audio/mpeg
++mp3 audio/mpeg
++aif audio/x-aiff
++aiff audio/x-aiff
++aifc audio/x-aiff
++m3u audio/x-mpegurl
++ram audio/x-pn-realaudio
++rm audio/x-pn-realaudio
++rpm audio/x-pn-realaudio-plugin
++ra audio/x-realaudio
++wav audio/x-wav
++pdb chemical/x-pdb
++xyz chemical/x-xyz
++bmp image/bmp
++gif image/gif
++ief image/ief
++jpeg image/jpeg
++jpg image/jpeg
++jpe image/jpeg
++png image/png
++tiff image/tiff
++tif image/tiff
++djvu image/vnd.djvu
++djv image/vnd.djvu
++wbmp image/vnd.wap.wbmp
++ras image/x-cmu-raster
++pnm image/x-portable-anymap
++pbm image/x-portable-bitmap
++pgm image/x-portable-graymap
++ppm image/x-portable-pixmap
++rgb image/x-rgb
++xbm image/x-xbitmap
++xpm image/x-xpixmap
++xwd image/x-xwindowdump
++igs model/iges
++iges model/iges
++msh model/mesh
++mesh model/mesh
++silo model/mesh
+ wrl model/vrml
+ vrml model/vrml
+-mime message/rfc822
+-
+-pac application/x-ns-proxy-autoconfig
+-
++css text/css
++html text/html; charset=%s
++htm text/html; charset=%s
++asc text/plain; charset=%s
++txt text/plain; charset=%s
++rtx text/richtext
++rtf text/rtf
++sgml text/sgml
++sgm text/sgml
++tsv text/tab-separated-values
+ wml text/vnd.wap.wml
+-wmlc application/vnd.wap.wmlc
+ wmls text/vnd.wap.wmlscript
+-wmlsc application/vnd.wap.wmlscriptc
+-wbmp image/vnd.wap.wbmp
++etx text/x-setext
++xml text/xml
++xsl text/xml
++mpeg video/mpeg
++mpg video/mpeg
++mpe video/mpeg
++qt video/quicktime
++mov video/quicktime
++mxu video/vnd.mpegurl
++avi video/x-msvideo
++movie video/x-sgi-movie
++ice x-conference/x-cooltalk
+diff -ur thttpd-2.21b/mmc.c thttpd-2.21b-cool/mmc.c
+--- thttpd-2.21b/mmc.c Fri Apr 13 23:02:15 2001
++++ thttpd-2.21b-cool/mmc.c Sat Sep 20 14:43:20 2003
+@@ -70,6 +70,9 @@
+ unsigned int hash;
+ int hash_idx;
+ struct MapStruct* next;
++ char nocache;
++ size_t last_modified_len;
++ char last_modified[100];
+ } Map;
+
+
+@@ -93,12 +96,13 @@
+
+
+ void*
+-mmc_map( char* filename, struct stat* sbP, struct timeval* nowP )
++mmc_map( char* filename, struct stat* sbP, struct timeval* nowP, int nocache, char **last_modified, size_t *last_modified_len )
+ {
+ time_t now;
+ struct stat sb;
+ Map* m;
+ int fd;
++ const char* rfc1123fmt = "%a, %d %b %Y %H:%M:%S GMT";
+
+ /* Stat the file, if necessary. */
+ if ( sbP != (struct stat*) 0 )
+@@ -130,7 +134,7 @@
+ /* Yep. Just return the existing map */
+ ++m->refcount;
+ m->reftime = now;
+- return m->addr;
++ goto done;
+ }
+
+ /* Open the file. */
+@@ -167,12 +171,13 @@
+ m->ctime = sb.st_ctime;
+ m->refcount = 1;
+ m->reftime = now;
++ m->nocache = (char) nocache;
+
+ /* Avoid doing anything for zero-length files; some systems don't like
+ ** to mmap them, other systems dislike mallocing zero bytes.
+ */
+ if ( m->size == 0 )
+- m->addr = (void*) 1; /* arbitrary non-NULL address */
++ m->addr = (void*) 5; /* arbitrary non-NULL address */
+ else
+ {
+ #ifdef HAVE_MMAP
+@@ -223,6 +228,13 @@
+ maps = m;
+ ++map_count;
+
++ strftime( m->last_modified, sizeof(m->last_modified), rfc1123fmt, gmtime( &sb.st_mtime ) );
++ m->last_modified_len = strlen(m->last_modified);
++
++done:
++ *last_modified = m->last_modified;
++ *last_modified_len = m->last_modified_len;
++
+ /* And return the address. */
+ return m->addr;
+ }
+@@ -231,27 +243,32 @@
+ void
+ mmc_unmap( void* addr, struct stat* sbP, struct timeval* nowP )
+ {
+- Map* m = (Map*) 0;
++ Map* m = (Map*) 0, **mm = &maps;
+
+ /* Find the Map entry for this address. First try a hash. */
+ if ( sbP != (struct stat*) 0 )
+ {
+ m = find_hash( sbP->st_ino, sbP->st_dev, sbP->st_size, sbP->st_ctime );
+- if ( m != (Map*) 0 && m->addr != addr )
++ if ( m != (Map*) 0 && ( m->addr != addr || m->nocache == 1 ) )
+ m = (Map*) 0;
+ }
+ /* If that didn't work, try a full search. */
+ if ( m == (Map*) 0 )
+- for ( m = maps; m != (Map*) 0; m = m->next )
++ for ( m = maps; m != (Map*) 0; m = m->next ) {
+ if ( m->addr == addr )
+ break;
++ mm = &m->next;
++ }
+ if ( m == (Map*) 0 )
+ syslog( LOG_ERR, "mmc_unmap failed to find entry!" );
+ else if ( m->refcount <= 0 )
+ syslog( LOG_ERR, "mmc_unmap found zero or negative refcount!" );
+ else
+ {
+- --m->refcount;
++ if ( --m->refcount == 0 && m->nocache == 1 ) {
++ really_unmap( mm );
++ return;
++ }
+ if ( nowP != (struct timeval*) 0 )
+ m->reftime = nowP->tv_sec;
+ else
+diff -ur thttpd-2.21b/mmc.h thttpd-2.21b-cool/mmc.h
+--- thttpd-2.21b/mmc.h Fri Apr 13 07:36:54 2001
++++ thttpd-2.21b-cool/mmc.h Sat Sep 20 14:43:20 2003
+@@ -31,8 +31,9 @@
+ /* Returns an mmap()ed area for the given file, or (void*) 0 on errors.
+ ** If you have a stat buffer on the file, pass it in, otherwise pass 0.
+ ** Same for the current time.
++** Set nocache to 1, if this entry is supposed to be removed quickly.
+ */
+-extern void* mmc_map( char* filename, struct stat* sbP, struct timeval* nowP );
++extern void* mmc_map( char* filename, struct stat* sbP, struct timeval* nowP, int nocache, char **last_modified, size_t *last_modified_len );
+
+ /* Done with an mmap()ed area that was returned by mmc_map().
+ ** If you have a stat buffer on the file, pass it in, otherwise pass 0.
+diff -ur thttpd-2.21b/thttpd.c thttpd-2.21b-cool/thttpd.c
+--- thttpd-2.21b/thttpd.c Tue Apr 24 00:41:57 2001
++++ thttpd-2.21b-cool/thttpd.c Sat Sep 20 14:43:20 2003
+@@ -53,6 +53,10 @@
+ #endif
+ #include <unistd.h>
+
++#include <sys/mman.h>
++
++#include <limits.h>
++
+ #include "fdwatch.h"
+ #include "libhttpd.h"
+ #include "mmc.h"
+@@ -66,6 +70,8 @@
+ static char* dir;
+ static int do_chroot, no_log, no_symlink, do_vhost, do_global_passwd;
+ static char* cgi_pattern;
++static char* php_pattern;
++static char* phps_pattern;
+ static char* url_pattern;
+ static int no_empty_referers;
+ static char* local_pattern;
+@@ -95,10 +101,10 @@
+ httpd_conn* hc;
+ int tnums[MAXTHROTTLENUMS]; /* throttle indexes */
+ int numtnums;
++ int keep_alive;
+ long limit;
+ time_t started_at;
+- Timer* idle_read_timer;
+- Timer* idle_send_timer;
++ time_t last_io;
+ Timer* wakeup_timer;
+ Timer* linger_timer;
+ long wouldblock_delay;
+@@ -106,17 +112,22 @@
+ off_t bytes_sent;
+ off_t bytes_to_send;
+ } connecttab;
+-static connecttab* connects;
++static connecttab* connects, **free_connects;
++static int next_free_connect;
+ static int numconnects, maxconnects;
+ static int httpd_conn_count;
+
+ /* The connection states. */
+-#define CNST_FREE 0
+-#define CNST_READING 1
+-#define CNST_SENDING 2
+-#define CNST_PAUSING 3
+-#define CNST_LINGERING 4
+-
++enum {
++ CNST_FREE = 0,
++ CNST_READING,
++ CNST_SENDING,
++ CNST_PAUSING,
++ CNST_LINGERING,
++ CNST_SENDING_RESP,
++ CNST_READING_BODY,
++ CNST_TOTAL_NR
++};
+
+ static httpd_server* hs = (httpd_server*) 0;
+ int terminate = 0;
+@@ -140,23 +151,32 @@
+ static int handle_newconnect( struct timeval* tvP, int listen_fd );
+ static void handle_read( connecttab* c, struct timeval* tvP );
+ static void handle_send( connecttab* c, struct timeval* tvP );
++static void handle_send_resp( connecttab* c, struct timeval* tvP );
++static void handle_read_body( connecttab* c, struct timeval* tvP );
+ static void handle_linger( connecttab* c, struct timeval* tvP );
+ static int check_throttles( connecttab* c );
++static void timeout_conns( ClientData client_data, struct timeval* nowP );
+ static void clear_throttles( connecttab* c, struct timeval* tvP );
+ static void update_throttles( ClientData client_data, struct timeval* nowP );
+-static void clear_connection( connecttab* c, struct timeval* tvP );
++static void clear_connection( connecttab* c, struct timeval* tvP, int );
+ static void really_clear_connection( connecttab* c, struct timeval* tvP );
+-static void idle_read_connection( ClientData client_data, struct timeval* nowP );
+-static void idle_send_connection( ClientData client_data, struct timeval* nowP );
+ static void wakeup_connection( ClientData client_data, struct timeval* nowP );
+ static void linger_clear_connection( ClientData client_data, struct timeval* nowP );
+ static void occasional( ClientData client_data, struct timeval* nowP );
++static void periodic_jobs( ClientData client_data, struct timeval* nowP );
+ #ifdef STATS_TIME
+ static void show_stats( ClientData client_data, struct timeval* nowP );
+ #endif /* STATS_TIME */
+ static void logstats( struct timeval* nowP );
+ static void thttpd_logstats( long secs );
++static void boot_request(connecttab *c, struct timeval *tvP);
++
++typedef void (*handler_func)(connecttab*, struct timeval *);
++
++handler_func handler_array[CNST_TOTAL_NR] =
++{NULL, handle_read, handle_send, NULL, handle_linger, handle_send_resp, handle_read_body};
+
++#define RUN_HANDLER(type, c) if (handler_array[type]) handler_array[type](c, &tv)
+
+ static void
+ handle_term( int sig )
+@@ -177,7 +197,7 @@
+ return;
+
+ /* Re-open the log file. */
+- if ( logfile != (char*) 0 )
++ if ( logfile != (char*) 0 && strcmp(logfile, "-") != 0)
+ {
+ logfp = fopen( logfile, "a" );
+ if ( logfp == (FILE*) 0 )
+@@ -198,6 +218,8 @@
+ }
+
+
++time_t httpd_time_now;
++
+ static void
+ handle_usr2( int sig )
+ {
+@@ -217,7 +239,6 @@
+ int num_ready;
+ int cnum, ridx;
+ connecttab* c;
+- httpd_conn* hc;
+ httpd_sockaddr sa4;
+ httpd_sockaddr sa6;
+ int gotv4, gotv6;
+@@ -270,7 +291,9 @@
+ no_log = 1;
+ logfp = (FILE*) 0;
+ }
+- else
++ else if (strcmp(logfile, "-") == 0) {
++ logfp = stdout;
++ } else
+ {
+ logfp = fopen( logfile, "a" );
+ if ( logfp == (FILE*) 0 )
+@@ -420,7 +443,8 @@
+ hostname,
+ gotv4 ? &sa4 : (httpd_sockaddr*) 0, gotv6 ? &sa6 : (httpd_sockaddr*) 0,
+ port, cgi_pattern, charset, cwd, no_log, logfp, no_symlink, do_vhost,
+- do_global_passwd, url_pattern, local_pattern, no_empty_referers );
++ do_global_passwd, url_pattern, local_pattern, no_empty_referers,
++ php_pattern, phps_pattern);
+ if ( hs == (httpd_server*) 0 )
+ exit( 1 );
+
+@@ -430,6 +454,12 @@
+ syslog( LOG_CRIT, "tmr_create(occasional) failed" );
+ exit( 1 );
+ }
++
++ if (tmr_create(0, timeout_conns, JunkClientData, 30 * 1000, 1) == 0) {
++ syslog(LOG_CRIT, "tmr_create(timeout_conns) failed");
++ exit(1);
++ }
++
+ if ( numthrottles > 0 )
+ {
+ /* Set up the throttles timer. */
+@@ -439,6 +469,12 @@
+ exit( 1 );
+ }
+ }
++
++ if (tmr_create(0, periodic_jobs, JunkClientData, 2000, 1) == 0) {
++ syslog(LOG_CRIT, "tmr_create failed");
++ exit(1);
++ }
++
+ #ifdef STATS_TIME
+ /* Set up the stats timer. */
+ if ( tmr_create( (struct timeval*) 0, show_stats, JunkClientData, STATS_TIME * 1000L, 1 ) == (Timer*) 0 )
+@@ -454,12 +490,14 @@
+ /* If we're root, try to become someone else. */
+ if ( getuid() == 0 )
+ {
++#ifndef __CYGWIN__
+ /* Set aux groups to null. */
+ if ( setgroups( 0, (const gid_t*) 0 ) < 0 )
+ {
+ syslog( LOG_CRIT, "setgroups - %m" );
+ exit( 1 );
+ }
++#endif
+ /* Set primary group. */
+ if ( setgid( gid ) < 0 )
+ {
+@@ -495,13 +533,17 @@
+ }
+ maxconnects -= SPARE_FDS;
+ connects = NEW( connecttab, maxconnects );
++ free_connects = malloc(sizeof(connecttab *) * maxconnects);
++ next_free_connect = maxconnects;
+ if ( connects == (connecttab*) 0 )
+ {
+ syslog( LOG_CRIT, "out of memory allocating a connecttab" );
+ exit( 1 );
+ }
++
+ for ( cnum = 0; cnum < maxconnects; ++cnum )
+ {
++ free_connects[cnum] = &connects[maxconnects - cnum - 1];
+ connects[cnum].conn_state = CNST_FREE;
+ connects[cnum].hc = (httpd_conn*) 0;
+ }
+@@ -518,6 +560,9 @@
+
+ /* Main loop. */
+ (void) gettimeofday( &tv, (struct timezone*) 0 );
++ httpd_time_now = tv.tv_sec;
++ periodic_jobs(JunkClientData, &tv);
++
+ while ( ( ! terminate ) || numconnects > 0 )
+ {
+ /* Do the fd watch. */
+@@ -530,6 +575,7 @@
+ exit( 1 );
+ }
+ (void) gettimeofday( &tv, (struct timezone*) 0 );
++ httpd_time_now = tv.tv_sec;
+ if ( num_ready == 0 )
+ {
+ /* No fd's are ready - run the timers. */
+@@ -565,16 +611,10 @@
+ c = (connecttab*) fdwatch_get_client_data( ridx );
+ if ( c == (connecttab*) 0 )
+ continue;
+- hc = c->hc;
+- if ( c->conn_state == CNST_READING &&
+- fdwatch_check_fd( hc->conn_fd ) )
+- handle_read( c, &tv );
+- else if ( c->conn_state == CNST_SENDING &&
+- fdwatch_check_fd( hc->conn_fd ) )
+- handle_send( c, &tv );
+- else if ( c->conn_state == CNST_LINGERING &&
+- fdwatch_check_fd( hc->conn_fd ) )
+- handle_linger( c, &tv );
++#if DO_UNNECESSARY_CHECK_FD
++ fdwatch_check_fd(c->hc->conn_fd);
++#endif
++ RUN_HANDLER(c->conn_state, c);
+ }
+ tmr_run( &tv );
+
+@@ -627,6 +667,8 @@
+ #else /* CGI_PATTERN */
+ cgi_pattern = (char*) 0;
+ #endif /* CGI_PATTERN */
++ php_pattern = "**.php";
++ phps_pattern = "**.phps";
+ url_pattern = (char*) 0;
+ no_empty_referers = 0;
+ local_pattern = (char*) 0;
+@@ -833,6 +875,16 @@
+ value_required( name, value );
+ cgi_pattern = e_strdup( value );
+ }
++ else if ( strcasecmp( name, "phppat" ) == 0 )
++ {
++ value_required( name, value );
++ php_pattern = e_strdup( value );
++ }
++ else if ( strcasecmp( name, "phpspat" ) == 0 )
++ {
++ value_required( name, value );
++ phps_pattern = e_strdup( value );
++ }
+ else if ( strcasecmp( name, "urlpat" ) == 0 )
+ {
+ value_required( name, value );
+@@ -1196,8 +1248,10 @@
+ logstats( &tv );
+ for ( cnum = 0; cnum < maxconnects; ++cnum )
+ {
+- if ( connects[cnum].conn_state != CNST_FREE )
++ if ( connects[cnum].conn_state != CNST_FREE ) {
++ httpd_complete_request( connects[cnum].hc, &tv );
+ httpd_close_conn( connects[cnum].hc, &tv );
++ }
+ if ( connects[cnum].hc != (httpd_conn*) 0 )
+ {
+ httpd_destroy_conn( connects[cnum].hc );
+@@ -1214,6 +1268,7 @@
+ }
+ mmc_destroy();
+ tmr_destroy();
++ free( (void*) free_connects );
+ free( (void*) connects );
+ if ( throttles != (throttletab*) 0 )
+ free( (void*) throttles );
+@@ -1234,7 +1289,7 @@
+ for (;;)
+ {
+ /* Is there room in the connection table? */
+- if ( numconnects >= maxconnects )
++ if ( numconnects >= maxconnects || next_free_connect == 0 )
+ {
+ /* Out of connection slots. Run the timers, then the
+ ** existing connections, and maybe we'll free up a slot
+@@ -1245,10 +1300,10 @@
+ return 0;
+ }
+ /* Find a free connection entry. */
+- for ( cnum = 0; cnum < maxconnects; ++cnum )
+- if ( connects[cnum].conn_state == CNST_FREE )
+- break;
+- c = &connects[cnum];
++
++ c = free_connects[--next_free_connect];
++ free_connects[next_free_connect] = NULL;
++
+ /* Make the httpd_conn if necessary. */
+ if ( c->hc == (httpd_conn*) 0 )
+ {
+@@ -1267,24 +1322,18 @@
+ {
+ case GC_FAIL:
+ case GC_NO_MORE:
++ free_connects[next_free_connect++] = c;
+ return 1;
+ }
+ c->conn_state = CNST_READING;
+ ++numconnects;
+ client_data.p = c;
+- c->idle_read_timer = tmr_create(
+- tvP, idle_read_connection, client_data, IDLE_READ_TIMELIMIT * 1000L,
+- 0 );
+- if ( c->idle_read_timer == (Timer*) 0 )
+- {
+- syslog( LOG_CRIT, "tmr_create(idle_read_connection) failed" );
+- exit( 1 );
+- }
+- c->idle_send_timer = (Timer*) 0;
+ c->wakeup_timer = (Timer*) 0;
+ c->linger_timer = (Timer*) 0;
+ c->bytes_sent = 0;
+ c->numtnums = 0;
++ c->keep_alive = 0;
++ c->last_io = httpd_time_now;
+
+ /* Set the connection file descriptor to no-delay mode. */
+ httpd_set_ndelay( c->hc->conn_fd );
+@@ -1298,11 +1347,100 @@
+ }
+
+
++#define FIXUP(x) if (hc->x >= oldptr && hc->x < pe) hc->x += d
++
++static void
++realign_hc(httpd_conn *hc, char *oldptr)
++{
++ int d = hc->read_buf - oldptr;
++ char *pe = oldptr + hc->checked_idx;
++
++ FIXUP(encodedurl);
++ FIXUP(protocol);
++ FIXUP(referer);
++ FIXUP(useragent);
++ FIXUP(acceptl);
++ FIXUP(cookie);
++ FIXUP(contenttype);
++ FIXUP(hdrhost);
++ FIXUP(authorization);
++}
++
++#undef FIXUP
++
++static void
++setup_read_body(connecttab *c, struct timeval *tvP)
++{
++ httpd_conn *hc = c->hc;
++ int already, missing;
++ char *oldptr = hc->read_buf;
++
++ c->conn_state = CNST_READING_BODY;
++
++ hc->read_body_into_mem = 0;
++
++ already = hc->read_idx - hc->checked_idx;
++ missing = hc->contentlength - already;
++
++ if (missing > 16384) {
++ char filename[] = "/tmp/thttpd.upload.XXXXXX";
++ int tmp = mkstemp(filename);
++
++ if (tmp >= 0) {
++ void *p;
++ size_t sz = hc->contentlength + hc->checked_idx + 10;
++
++ unlink(filename);
++
++ ftruncate(tmp, sz);
++ p = mmap(NULL, sz,
++ PROT_READ|PROT_WRITE, MAP_PRIVATE, tmp, 0);
++
++ if (p != MAP_FAILED) {
++ memcpy(p, hc->read_buf, hc->read_idx);
++ free(hc->read_buf);
++ hc->read_size = sz;
++ hc->read_buf = p;
++ hc->read_buf_is_mmap = 1;
++ }
++ close(tmp);
++ }
++
++ if (!hc->read_buf_is_mmap) {
++ clear_connection( c, tvP, 0 );
++ return;
++ }
++ } else if (missing > 0) {
++ httpd_realloc_str(&hc->read_buf, &hc->read_size, hc->checked_idx + hc->contentlength + 10);
++ }
++ if (oldptr != hc->read_buf) realign_hc(hc, oldptr);
++
++ fdwatch_del_fd( hc->conn_fd );
++ fdwatch_add_fd( hc->conn_fd, c, FDW_READ );
++}
++
++static void
++setup_sending(connecttab *c, int state, struct timeval *tvP)
++{
++ httpd_conn *hc = c->hc;
++ ClientData client_data;
++
++ c->conn_state = state;
++ c->started_at = tvP->tv_sec;
++ c->wouldblock_delay = 0;
++ client_data.p = c;
++
++ fdwatch_del_fd( hc->conn_fd );
++ fdwatch_add_fd( hc->conn_fd, c, FDW_WRITE );
++}
++
++static void handle_request( connecttab *c, struct timeval *tvP);
++
++
+ static void
+ handle_read( connecttab* c, struct timeval* tvP )
+ {
+ int sz;
+- ClientData client_data;
+ httpd_conn* hc = c->hc;
+
+ /* Is there room in our buffer to read more bytes? */
+@@ -1311,7 +1449,7 @@
+ if ( hc->read_size > 5000 )
+ {
+ httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
+- clear_connection( c, tvP );
++ clear_connection( c, tvP, 0 );
+ return;
+ }
+ httpd_realloc_str(
+@@ -1327,14 +1465,53 @@
+ ** EWOULDBLOCK; however, this apparently can happen if a packet gets
+ ** garbled.
+ */
+- if ( sz == 0 || ( sz < 0 && ( errno != EWOULDBLOCK ) ) )
+- {
+- httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
+- clear_connection( c, tvP );
++ if ( sz == 0 ) {
++ if (! c->keep_alive) {
++ httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
++ }
++ clear_connection( c, tvP, 0 );
++ return;
++ } else if ( sz < 0 ) {
++ if (errno != EWOULDBLOCK) {
++ clear_connection( c, tvP, 0 );
++ }
+ return;
++ }
++
++ /* If this is a persistent PHP connection, we must not receive
++ ** any further requests on this connection. Some broken HTTP/1.1
++ ** implementations (e.g. Mozilla 1.0.1) are known to do
++ ** pipelining on a connection, although a prior response included
++ ** Connection: close
++ */
++ if (c->hc->file_address == (char *) 1) {
++ return;
++ }
++
++ c->last_io = httpd_time_now;
++ if (sz > 0) hc->read_idx += sz;
++
++ /*
++ ** if we start getting new data on this socket, "promote" it
++ ** to read timeout
++ */
++ if ( hc->keep_alive ) {
++ ClientData client_data;
++
++
++ client_data.p = c;
++
++ hc->keep_alive = 0;
++ }
++ handle_request(c, tvP);
+ }
+- hc->read_idx += sz;
+
++
++static void
++handle_request( connecttab *c, struct timeval *tvP)
++{
++ httpd_conn* hc = c->hc;
++
+ /* Do we have a complete request yet? */
+ switch ( httpd_got_request( hc ) )
+ {
+@@ -1342,14 +1519,14 @@
+ return;
+ case GR_BAD_REQUEST:
+ httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
+- clear_connection( c, tvP );
++ clear_connection( c, tvP, 0 );
+ return;
+ }
+
+ /* Yes. Try parsing and resolving it. */
+ if ( httpd_parse_request( hc ) < 0 )
+ {
+- clear_connection( c, tvP );
++ clear_connection( c, tvP, 0 );
+ return;
+ }
+
+@@ -1358,18 +1535,28 @@
+ {
+ httpd_send_err(
+ hc, 503, httpd_err503title, "", httpd_err503form, hc->encodedurl );
+- clear_connection( c, tvP );
++ clear_connection( c, tvP, 0 );
+ return;
+ }
++ boot_request(c, tvP);
++}
+
++static void boot_request(connecttab *c, struct timeval *tvP)
++{
++ httpd_conn *hc = c->hc;
+ /* Start the connection going. */
+ if ( httpd_start_request( hc, tvP ) < 0 )
+ {
+ /* Something went wrong. Close down the connection. */
+- clear_connection( c, tvP );
++ clear_connection( c, tvP, 0 );
+ return;
+ }
+
++ if ( hc->read_body_into_mem ) {
++ setup_read_body( c, tvP );
++ return;
++ }
++
+ /* Fill in bytes_to_send. */
+ if ( hc->got_range )
+ {
+@@ -1384,37 +1571,25 @@
+ {
+ /* No file address means someone else is handling it. */
+ c->bytes_sent = hc->bytes_sent;
+- clear_connection( c, tvP );
++ clear_connection( c, tvP, 1 );
+ return;
+ }
++ if (hc->file_address == (char *) 1) {
++ c->last_io = (time_t) LONG_MAX;
++ c->wouldblock_delay = 0;
++ return;
++ }
+ if ( c->bytes_sent >= c->bytes_to_send )
+ {
+ /* There's nothing to send. */
+- clear_connection( c, tvP );
++ clear_connection( c, tvP, 1 );
+ return;
+ }
+
+ /* Cool, we have a valid connection and a file to send to it. */
+- c->conn_state = CNST_SENDING;
+- c->started_at = tvP->tv_sec;
+- c->wouldblock_delay = 0;
+- client_data.p = c;
+- tmr_cancel( c->idle_read_timer );
+- c->idle_read_timer = (Timer*) 0;
+- c->idle_send_timer = tmr_create(
+- tvP, idle_send_connection, client_data, IDLE_SEND_TIMELIMIT * 1000L,
+- 0 );
+- if ( c->idle_send_timer == (Timer*) 0 )
+- {
+- syslog( LOG_CRIT, "tmr_create(idle_send_connection) failed" );
+- exit( 1 );
+- }
+-
+- fdwatch_del_fd( hc->conn_fd );
+- fdwatch_add_fd( hc->conn_fd, c, FDW_WRITE );
++ setup_sending(c, CNST_SENDING, tvP);
+ }
+
+-
+ static void
+ handle_send( connecttab* c, struct timeval* tvP )
+ {
+@@ -1443,6 +1618,9 @@
+ iv[1].iov_base = &(hc->file_address[c->bytes_sent]);
+ iv[1].iov_len = MIN( c->bytes_to_send - c->bytes_sent, c->limit );
+ sz = writev( hc->conn_fd, iv, 2 );
++/*
++printf("**RESPONSE2 [%d]** len = %d\n%*.*s\n", hc->conn_fd, hc->responselen, hc->responselen, hc->responselen, hc->response);
++*/
+ }
+
+ if ( sz == 0 ||
+@@ -1486,12 +1664,12 @@
+ */
+ if ( errno != EPIPE && errno != EINVAL && errno != ECONNRESET )
+ syslog( LOG_ERR, "write - %m sending %.80s", hc->encodedurl );
+- clear_connection( c, tvP );
++ clear_connection( c, tvP, 0 );
+ return;
+ }
+
+ /* Ok, we wrote something. */
+- tmr_reset( tvP, c->idle_send_timer );
++ c->last_io = httpd_time_now;
+ /* Was this a headers + file writev()? */
+ if ( hc->responselen > 0 )
+ {
+@@ -1500,7 +1678,7 @@
+ {
+ /* Yes; move the unwritten part to the front of the buffer. */
+ int newlen = hc->responselen - sz;
+- (void) memcpy( hc->response, &(hc->response[sz]), newlen );
++ (void) memmove( hc->response, &(hc->response[sz]), newlen );
+ hc->responselen = newlen;
+ sz = 0;
+ }
+@@ -1519,7 +1697,7 @@
+ if ( c->bytes_sent >= c->bytes_to_send )
+ {
+ /* This conection is finished! */
+- clear_connection( c, tvP );
++ clear_connection( c, tvP, 1 );
+ return;
+ }
+
+@@ -1560,6 +1738,9 @@
+ char buf[1024];
+ int r;
+
++/*
++printf("*LINGER read\n");
++*/
+ /* In lingering-close mode we just read and ignore bytes. An error
+ ** or EOF ends things, otherwise we go until a timeout.
+ */
+@@ -1569,6 +1750,63 @@
+ }
+
+
++static void
++handle_read_body(connecttab *c, struct timeval *tvP)
++{
++ httpd_conn *hc = c->hc;
++ int n;
++
++ n = read(hc->conn_fd, hc->read_buf + hc->read_idx,
++ hc->contentlength - (hc->read_idx - hc->checked_idx));
++
++ if (n <= 0) {
++ if (errno == EAGAIN)
++ return;
++ clear_connection(c, tvP, 0);
++ return;
++ }
++
++ c->last_io = httpd_time_now;
++
++ hc->read_idx += n;
++
++ if (hc->contentlength == hc->read_idx - hc->checked_idx) {
++ boot_request(c, tvP);
++ return;
++ }
++}
++
++static void
++handle_send_resp(connecttab *c, struct timeval *tvP)
++{
++ httpd_conn* hc = c->hc;
++ int n = send(hc->conn_fd, hc->response, hc->responselen, 0);
++ int dokeep = 1;
++
++ if (n < 0) {
++ if (errno == EAGAIN)
++ return;
++
++ dokeep = 0;
++ goto clear;
++ }
++
++ c->last_io = httpd_time_now;
++
++ if (n == hc->responselen) {
++clear:
++ hc->response = realloc(hc->response, hc->maxresponse + 1);
++ hc->responselen = 0;
++
++ clear_connection(c, tvP, dokeep);
++ return;
++ }
++
++ hc->responselen -= n;
++
++ memmove(hc->response, hc->response + n, hc->responselen);
++}
++
+ static int
+ check_throttles( connecttab* c )
+ {
+@@ -1635,23 +1873,18 @@
+
+
+ static void
+-clear_connection( connecttab* c, struct timeval* tvP )
++clear_connection( connecttab* c, struct timeval* tvP, int doKeep )
+ {
+ ClientData client_data;
++ int linger;
+
+ /* If we haven't actually sent the buffered response yet, do so now. */
+- httpd_write_response( c->hc );
++ if (c->hc->responselen && c->conn_state != CNST_SENDING_RESP) {
++ setup_sending(c, CNST_SENDING_RESP, tvP);
+
+- if ( c->idle_read_timer != (Timer*) 0 )
+- {
+- tmr_cancel( c->idle_read_timer );
+- c->idle_read_timer = 0;
+- }
+- if ( c->idle_send_timer != (Timer*) 0 )
+- {
+- tmr_cancel( c->idle_send_timer );
+- c->idle_send_timer = 0;
++ return;
+ }
++
+ if ( c->wakeup_timer != (Timer*) 0 )
+ {
+ tmr_cancel( c->wakeup_timer );
+@@ -1669,13 +1902,36 @@
+ ** circumstances that make a lingering close necessary. If the flag
+ ** isn't set we do the real close now.
+ */
+- if ( c->hc->should_linger )
++
++ if ( c->hc->do_keep_alive && doKeep)
+ {
+- c->conn_state = CNST_LINGERING;
++ httpd_conn *hc = c->hc;
++ c->conn_state = CNST_READING;
++
++ client_data.p = c;
++ c->bytes_sent = 0;
++ c->numtnums = 0;
++ c->keep_alive = 1;
++
++ httpd_complete_request( c->hc, tvP );
++
+ fdwatch_del_fd( c->hc->conn_fd );
+ fdwatch_add_fd( c->hc->conn_fd, c, FDW_READ );
++
++ httpd_request_reset( c->hc, 1 );
++
++ hc->read_idx -= hc->checked_idx;
++ memmove(hc->read_buf, hc->read_buf + hc->checked_idx, hc->read_idx);
++ hc->checked_idx = 0;
++
+ /* Make sure we are still in no-delay mode. */
+ httpd_set_ndelay( c->hc->conn_fd );
++ handle_request(c, tvP);
++ }
++ else if ( c->hc->should_linger )
++ {
++ c->conn_state = CNST_LINGERING;
++
+ client_data.p = c;
+ c->linger_timer = tmr_create(
+ tvP, linger_clear_connection, client_data, LINGER_TIME * 1000L, 0 );
+@@ -1684,9 +1940,19 @@
+ syslog( LOG_CRIT, "tmr_create(linger_clear_connection) failed" );
+ exit( 1 );
+ }
++
++ httpd_complete_request( c->hc, tvP );
++
++ fdwatch_del_fd( c->hc->conn_fd );
++ fdwatch_add_fd( c->hc->conn_fd, c, FDW_READ );
++ /* Make sure we are still in no-delay mode. */
++ httpd_set_ndelay( c->hc->conn_fd );
+ }
+- else
++ else
++ {
++ httpd_complete_request( c->hc, tvP );
+ really_clear_connection( c, tvP );
++ }
+ }
+
+
+@@ -1702,45 +1968,12 @@
+ tmr_cancel( c->linger_timer );
+ c->linger_timer = 0;
+ }
++ free_connects[next_free_connect++] = c;
+ c->conn_state = CNST_FREE;
+ --numconnects;
+ }
+
+
+-static void
+-idle_read_connection( ClientData client_data, struct timeval* nowP )
+- {
+- connecttab* c;
+-
+- c = (connecttab*) client_data.p;
+- c->idle_read_timer = (Timer*) 0;
+- if ( c->conn_state != CNST_FREE )
+- {
+- syslog( LOG_INFO,
+- "%.80s connection timed out reading",
+- httpd_ntoa( &c->hc->client_addr ) );
+- httpd_send_err( c->hc, 408, httpd_err408title, "", httpd_err408form, "" );
+- clear_connection( c, nowP );
+- }
+- }
+-
+-
+-static void
+-idle_send_connection( ClientData client_data, struct timeval* nowP )
+- {
+- connecttab* c;
+-
+- c = (connecttab*) client_data.p;
+- c->idle_send_timer = (Timer*) 0;
+- if ( c->conn_state != CNST_FREE )
+- {
+- syslog( LOG_INFO,
+- "%.80s connection timed out sending",
+- httpd_ntoa( &c->hc->client_addr ) );
+- clear_connection( c, nowP );
+- }
+- }
+-
+
+ static void
+ wakeup_connection( ClientData client_data, struct timeval* nowP )
+@@ -1783,6 +2016,43 @@
+ }
+ #endif /* STATS_TIME */
+
++char httpd_now_buf[100];
++
++
++
++static void
++periodic_jobs( ClientData client_data, struct timeval* nowP )
++{
++ const char* rfc1123fmt = "%a, %d %b %Y %H:%M:%S GMT";
++ struct tm *t;
++ char date_nozone[100];
++ const char* cernfmt_nozone = "%d/%b/%Y:%H:%M:%S";
++ char data[100];
++ int zone;
++ char sign;
++
++ strftime( httpd_now_buf, sizeof(httpd_now_buf), rfc1123fmt, gmtime( &nowP->tv_sec ) );
++
++ t = localtime(&nowP->tv_sec);
++ strftime( date_nozone, sizeof(date_nozone), cernfmt_nozone, t );
++#ifdef HAVE_TM_GMTOFF
++ zone = t->tm_gmtoff / 60L;
++#else
++ zone = -timezone / 60L;
++ /* Probably have to add something about daylight time here. */
++#endif
++ if ( zone >= 0 )
++ sign = '+';
++ else
++ {
++ sign = '-';
++ zone = -zone;
++ }
++ zone = ( zone / 60 ) * 100 + zone % 60;
++ hs->log_date_len = sprintf( hs->log_date, "%s %c%04d", date_nozone, sign,
++ zone );
++}
++
+
+ /* Generate debugging statistics syslog messages for all packages. */
+ static void
+@@ -1826,3 +2096,42 @@
+ stats_connections = stats_bytes = 0L;
+ stats_simultaneous = 0;
+ }
++
++static void
++timeout_conns(ClientData client_data, struct timeval *nowP)
++{
++ connecttab *c = connects, *ce = c + maxconnects;
++ time_t now = nowP->tv_sec;
++ int r = 0, w = 0;
++ int checked = 0;
++
++ while (c < ce) {
++ switch (c->conn_state) {
++ case CNST_SENDING:
++ case CNST_SENDING_RESP:
++ checked++;
++ if ((now - c->last_io) > IDLE_SEND_TIMELIMIT) {
++ clear_connection( c, nowP, 0 );
++ w++;
++ }
++ break;
++ case CNST_READING:
++ case CNST_READING_BODY:
++ checked++;
++ if ((now - c->last_io) > IDLE_READ_TIMELIMIT) {
++ clear_connection( c, nowP, 0 );
++ r++;
++ }
++ break;
++ case CNST_FREE: break;
++ default: checked++; break;
++ }
++ c++;
++ if (checked >= numconnects) break;
++ }
++
++ if (r > 0 || w > 0) {
++ syslog(LOG_INFO, "Expired %d/%d connections in read/write state", r, w);
++ }
++}
++
+diff -ur thttpd-2.21b/version.h thttpd-2.21b-cool/version.h
+--- thttpd-2.21b/version.h Tue Apr 24 04:05:23 2001
++++ thttpd-2.21b-cool/version.h Sat Sep 20 14:43:20 2003
+@@ -3,7 +3,7 @@
+ #ifndef _VERSION_H_
+ #define _VERSION_H_
+
+-#define SERVER_SOFTWARE "thttpd/2.21b 23apr2001"
++#define SERVER_SOFTWARE "thttpd/2.21b PHP/20030920"
+ #define SERVER_ADDRESS "http://www.acme.com/software/thttpd/"
+
+ #endif /* _VERSION_H_ */