summaryrefslogtreecommitdiff
path: root/sapi/isapi
diff options
context:
space:
mode:
Diffstat (limited to 'sapi/isapi')
-rw-r--r--sapi/isapi/CREDITS2
-rw-r--r--sapi/isapi/config.m424
-rw-r--r--sapi/isapi/config.w3213
-rw-r--r--sapi/isapi/php.sym5
-rw-r--r--sapi/isapi/php5isapi.c973
-rw-r--r--sapi/isapi/php5isapi.def5
-rw-r--r--sapi/isapi/php5isapi.dsp165
-rw-r--r--sapi/isapi/stresstest/getopt.c175
-rw-r--r--sapi/isapi/stresstest/getopt.h12
-rw-r--r--sapi/isapi/stresstest/notes.txt56
-rw-r--r--sapi/isapi/stresstest/stresstest.cpp936
-rw-r--r--sapi/isapi/stresstest/stresstest.dsp108
12 files changed, 2474 insertions, 0 deletions
diff --git a/sapi/isapi/CREDITS b/sapi/isapi/CREDITS
new file mode 100644
index 0000000..11c6fdc
--- /dev/null
+++ b/sapi/isapi/CREDITS
@@ -0,0 +1,2 @@
+ISAPI
+Andi Gutmans, Zeev Suraski
diff --git a/sapi/isapi/config.m4 b/sapi/isapi/config.m4
new file mode 100644
index 0000000..7c7dcf0
--- /dev/null
+++ b/sapi/isapi/config.m4
@@ -0,0 +1,24 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_WITH(isapi, for Zeus ISAPI support,
+[ --with-isapi[=DIR] Build PHP as an ISAPI module for use with Zeus], no, no)
+
+if test "$PHP_ISAPI" != "no"; then
+ if test "$PHP_ISAPI" = "yes"; then
+ ZEUSPATH=/usr/local/zeus # the default
+ else
+ ZEUSPATH=$PHP_ISAPI
+ fi
+ test -f "$ZEUSPATH/web/include/httpext.h" || AC_MSG_ERROR(Unable to find httpext.h in $ZEUSPATH/web/include)
+ PHP_BUILD_THREAD_SAFE
+ AC_DEFINE(WITH_ZEUS, 1, [ ])
+ PHP_ADD_INCLUDE($ZEUSPATH/web/include)
+ PHP_SELECT_SAPI(isapi, shared, php5isapi.c)
+ INSTALL_IT="\$(SHELL) \$(srcdir)/install-sh -m 0755 $SAPI_SHARED \$(INSTALL_ROOT)$ZEUSPATH/web/bin/"
+fi
+
+dnl ## Local Variables:
+dnl ## tab-width: 4
+dnl ## End:
diff --git a/sapi/isapi/config.w32 b/sapi/isapi/config.w32
new file mode 100644
index 0000000..8012352
--- /dev/null
+++ b/sapi/isapi/config.w32
@@ -0,0 +1,13 @@
+// vim:ft=javascript
+// $Id$
+
+ARG_ENABLE('isapi', 'Build ISAPI version of PHP', 'no');
+
+if (PHP_ISAPI == "yes") {
+ if (PHP_ZTS == "no") {
+ WARNING("ISAPI module requires an --enable-zts build of PHP");
+ } else {
+ SAPI('isapi', 'php5isapi.c', 'php' + PHP_VERSION + 'isapi.dll', '/D PHP5ISAPI_EXPORTS');
+ ADD_FLAG('LDFLAGS_ISAPI', '/DEF:sapi\\isapi\\php5isapi.def');
+ }
+}
diff --git a/sapi/isapi/php.sym b/sapi/isapi/php.sym
new file mode 100644
index 0000000..34b50b8
--- /dev/null
+++ b/sapi/isapi/php.sym
@@ -0,0 +1,5 @@
+GetFilterVersion
+HttpFilterProc
+GetExtensionVersion
+HttpExtensionProc
+ZSLMain
diff --git a/sapi/isapi/php5isapi.c b/sapi/isapi/php5isapi.c
new file mode 100644
index 0000000..002ad2a
--- /dev/null
+++ b/sapi/isapi/php5isapi.c
@@ -0,0 +1,973 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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. |
+ +----------------------------------------------------------------------+
+ | Authors: Zeev Suraski <zeev@zend.com> |
+ | Ben Mansell <ben@zeus.com> (Zeus Support) |
+ +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+#include "php.h"
+#include <httpext.h>
+#include <httpfilt.h>
+#include <httpext.h>
+#include "php_main.h"
+#include "SAPI.h"
+#include "php_globals.h"
+#include "ext/standard/info.h"
+#include "php_variables.h"
+#include "php_ini.h"
+
+#ifdef PHP_WIN32
+# include <process.h>
+#else
+# define __try
+# define __except(val)
+# define __declspec(foo)
+#endif
+
+
+#ifdef WITH_ZEUS
+# include "httpext.h"
+# include <errno.h>
+# define GetLastError() errno
+#endif
+
+#ifdef PHP_WIN32
+#define PHP_ENABLE_SEH
+#endif
+
+/*
+uncomment the following lines to turn off
+exception trapping when running under a debugger
+
+#ifdef _DEBUG
+#undef PHP_ENABLE_SEH
+#endif
+*/
+
+#define MAX_STATUS_LENGTH sizeof("xxxx LONGEST POSSIBLE STATUS DESCRIPTION")
+#define ISAPI_SERVER_VAR_BUF_SIZE 1024
+#define ISAPI_POST_DATA_BUF 1024
+
+static zend_bool bFilterLoaded=0;
+static zend_bool bTerminateThreadsOnError=0;
+
+static char *isapi_special_server_variable_names[] = {
+ "ALL_HTTP",
+ "HTTPS",
+#ifndef WITH_ZEUS
+ "SCRIPT_NAME",
+#endif
+ NULL
+};
+
+#define NUM_SPECIAL_VARS (sizeof(isapi_special_server_variable_names)/sizeof(char *))
+#define SPECIAL_VAR_ALL_HTTP 0
+#define SPECIAL_VAR_HTTPS 1
+#define SPECIAL_VAR_PHP_SELF 2
+
+static char *isapi_server_variable_names[] = {
+ "AUTH_PASSWORD",
+ "AUTH_TYPE",
+ "AUTH_USER",
+ "CONTENT_LENGTH",
+ "CONTENT_TYPE",
+ "PATH_TRANSLATED",
+ "QUERY_STRING",
+ "REMOTE_ADDR",
+ "REMOTE_HOST",
+ "REMOTE_USER",
+ "REQUEST_METHOD",
+ "SERVER_NAME",
+ "SERVER_PORT",
+ "SERVER_PROTOCOL",
+ "SERVER_SOFTWARE",
+#ifndef WITH_ZEUS
+ "APPL_MD_PATH",
+ "APPL_PHYSICAL_PATH",
+ "INSTANCE_ID",
+ "INSTANCE_META_PATH",
+ "LOGON_USER",
+ "REQUEST_URI",
+ "URL",
+#else
+ "DOCUMENT_ROOT",
+#endif
+ NULL
+};
+
+
+static char *isapi_secure_server_variable_names[] = {
+ "CERT_COOKIE",
+ "CERT_FLAGS",
+ "CERT_ISSUER",
+ "CERT_KEYSIZE",
+ "CERT_SECRETKEYSIZE",
+ "CERT_SERIALNUMBER",
+ "CERT_SERVER_ISSUER",
+ "CERT_SERVER_SUBJECT",
+ "CERT_SUBJECT",
+ "HTTPS_KEYSIZE",
+ "HTTPS_SECRETKEYSIZE",
+ "HTTPS_SERVER_ISSUER",
+ "HTTPS_SERVER_SUBJECT",
+ "SERVER_PORT_SECURE",
+#ifdef WITH_ZEUS
+ "SSL_CLIENT_CN",
+ "SSL_CLIENT_EMAIL",
+ "SSL_CLIENT_OU",
+ "SSL_CLIENT_O",
+ "SSL_CLIENT_L",
+ "SSL_CLIENT_ST",
+ "SSL_CLIENT_C",
+ "SSL_CLIENT_I_CN",
+ "SSL_CLIENT_I_EMAIL",
+ "SSL_CLIENT_I_OU",
+ "SSL_CLIENT_I_O",
+ "SSL_CLIENT_I_L",
+ "SSL_CLIENT_I_ST",
+ "SSL_CLIENT_I_C",
+#endif
+ NULL
+};
+
+
+static void php_info_isapi(ZEND_MODULE_INFO_FUNC_ARGS)
+{
+ char **p;
+ char variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
+ DWORD variable_len;
+ char **all_variables[] = {
+ isapi_server_variable_names,
+ isapi_special_server_variable_names,
+ isapi_secure_server_variable_names,
+ NULL
+ };
+ char ***server_variable_names;
+ LPEXTENSION_CONTROL_BLOCK lpECB;
+
+ lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
+
+ php_info_print_table_start();
+ php_info_print_table_header(2, "Server Variable", "Value");
+ server_variable_names = all_variables;
+ while (*server_variable_names) {
+ p = *server_variable_names;
+ while (*p) {
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if (lpECB->GetServerVariable(lpECB->ConnID, *p, variable_buf, &variable_len)
+ && variable_buf[0]) {
+ php_info_print_table_row(2, *p, variable_buf);
+ } else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ char *tmp_variable_buf;
+
+ tmp_variable_buf = (char *) emalloc(variable_len);
+ if (lpECB->GetServerVariable(lpECB->ConnID, *p, tmp_variable_buf, &variable_len)
+ && variable_buf[0]) {
+ php_info_print_table_row(2, *p, tmp_variable_buf);
+ }
+ efree(tmp_variable_buf);
+ }
+ p++;
+ }
+ server_variable_names++;
+ }
+ php_info_print_table_end();
+}
+
+
+static zend_module_entry php_isapi_module = {
+ STANDARD_MODULE_HEADER,
+ "ISAPI",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ php_info_isapi,
+ NULL,
+ STANDARD_MODULE_PROPERTIES
+};
+
+
+static int sapi_isapi_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+ DWORD num_bytes = str_length;
+ LPEXTENSION_CONTROL_BLOCK ecb;
+
+ ecb = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
+ if (ecb->WriteClient(ecb->ConnID, (char *) str, &num_bytes, HSE_IO_SYNC) == FALSE) {
+ php_handle_aborted_connection();
+ }
+ return num_bytes;
+}
+
+
+static int sapi_isapi_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ return SAPI_HEADER_ADD;
+}
+
+
+
+static void accumulate_header_length(sapi_header_struct *sapi_header, uint *total_length TSRMLS_DC)
+{
+ *total_length += sapi_header->header_len+2;
+}
+
+
+static void concat_header(sapi_header_struct *sapi_header, char **combined_headers_ptr TSRMLS_DC)
+{
+ memcpy(*combined_headers_ptr, sapi_header->header, sapi_header->header_len);
+ *combined_headers_ptr += sapi_header->header_len;
+ **combined_headers_ptr = '\r';
+ (*combined_headers_ptr)++;
+ **combined_headers_ptr = '\n';
+ (*combined_headers_ptr)++;
+}
+
+
+static int sapi_isapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ uint total_length = 2; /* account for the trailing \r\n */
+ char *combined_headers, *combined_headers_ptr;
+ LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
+ HSE_SEND_HEADER_EX_INFO header_info;
+ sapi_header_struct default_content_type;
+ char *status_buf = NULL;
+
+ /* Obtain headers length */
+ if (SG(sapi_headers).send_default_content_type) {
+ sapi_get_default_content_type_header(&default_content_type TSRMLS_CC);
+ accumulate_header_length(&default_content_type, (void *) &total_length TSRMLS_CC);
+ }
+ zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t) accumulate_header_length, (void *) &total_length TSRMLS_CC);
+
+ /* Generate headers */
+ combined_headers = (char *) emalloc(total_length+1);
+ combined_headers_ptr = combined_headers;
+ if (SG(sapi_headers).send_default_content_type) {
+ concat_header(&default_content_type, (void *) &combined_headers_ptr TSRMLS_CC);
+ sapi_free_header(&default_content_type); /* we no longer need it */
+ }
+ zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t) concat_header, (void *) &combined_headers_ptr TSRMLS_CC);
+ *combined_headers_ptr++ = '\r';
+ *combined_headers_ptr++ = '\n';
+ *combined_headers_ptr = 0;
+
+ switch (SG(sapi_headers).http_response_code) {
+ case 200:
+ header_info.pszStatus = "200 OK";
+ break;
+ case 302:
+ header_info.pszStatus = "302 Moved Temporarily";
+ break;
+ case 401:
+ header_info.pszStatus = "401 Authorization Required";
+ break;
+ default: {
+ const char *sline = SG(sapi_headers).http_status_line;
+ int sline_len;
+
+ /* httpd requires that r->status_line is set to the first digit of
+ * the status-code: */
+ if (sline && ((sline_len = strlen(sline)) > 12) && strncmp(sline, "HTTP/1.", 7) == 0 && sline[8] == ' ') {
+ if ((sline_len - 9) > MAX_STATUS_LENGTH) {
+ status_buf = estrndup(sline + 9, MAX_STATUS_LENGTH);
+ } else {
+ status_buf = estrndup(sline + 9, sline_len - 9);
+ }
+ } else {
+ status_buf = emalloc(MAX_STATUS_LENGTH + 1);
+ snprintf(status_buf, MAX_STATUS_LENGTH, "%d Undescribed", SG(sapi_headers).http_response_code);
+ }
+ header_info.pszStatus = status_buf;
+ break;
+ }
+ }
+ header_info.cchStatus = strlen(header_info.pszStatus);
+ header_info.pszHeader = combined_headers;
+ header_info.cchHeader = total_length;
+ header_info.fKeepConn = FALSE;
+ lpECB->dwHttpStatusCode = SG(sapi_headers).http_response_code;
+
+ lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX, &header_info, NULL, NULL);
+
+ efree(combined_headers);
+ if (status_buf) {
+ efree(status_buf);
+ }
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+
+
+static int php_isapi_startup(sapi_module_struct *sapi_module)
+{
+ if (php_module_startup(sapi_module, &php_isapi_module, 1)==FAILURE) {
+ return FAILURE;
+ } else {
+ bTerminateThreadsOnError = (zend_bool) INI_INT("isapi.terminate_threads_on_error");
+ return SUCCESS;
+ }
+}
+
+
+static int sapi_isapi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
+{
+ LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
+ DWORD read_from_buf=0;
+ DWORD read_from_input=0;
+ DWORD total_read=0;
+
+ if ((DWORD) SG(read_post_bytes) < lpECB->cbAvailable) {
+ read_from_buf = MIN(lpECB->cbAvailable-SG(read_post_bytes), count_bytes);
+ memcpy(buffer, lpECB->lpbData+SG(read_post_bytes), read_from_buf);
+ total_read += read_from_buf;
+ }
+ if (read_from_buf<count_bytes
+ && (SG(read_post_bytes)+read_from_buf) < lpECB->cbTotalBytes) {
+ DWORD cbRead=0, cbSize;
+
+ read_from_input = MIN(count_bytes-read_from_buf, lpECB->cbTotalBytes-SG(read_post_bytes)-read_from_buf);
+ while (cbRead < read_from_input) {
+ cbSize = read_from_input - cbRead;
+ if (!lpECB->ReadClient(lpECB->ConnID, buffer+read_from_buf+cbRead, &cbSize) || cbSize==0) {
+ break;
+ }
+ cbRead += cbSize;
+ }
+ total_read += cbRead;
+ }
+ return total_read;
+}
+
+
+static char *sapi_isapi_read_cookies(TSRMLS_D)
+{
+ LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
+ char variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
+ DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+
+ if (lpECB->GetServerVariable(lpECB->ConnID, "HTTP_COOKIE", variable_buf, &variable_len)) {
+ return estrndup(variable_buf, variable_len);
+ } else if (GetLastError()==ERROR_INSUFFICIENT_BUFFER) {
+ char *tmp_variable_buf = (char *) emalloc(variable_len+1);
+
+ if (lpECB->GetServerVariable(lpECB->ConnID, "HTTP_COOKIE", tmp_variable_buf, &variable_len)) {
+ tmp_variable_buf[variable_len] = 0;
+ return tmp_variable_buf;
+ } else {
+ efree(tmp_variable_buf);
+ }
+ }
+ return STR_EMPTY_ALLOC();
+}
+
+
+#ifdef WITH_ZEUS
+
+static void sapi_isapi_register_zeus_ssl_variables(LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array TSRMLS_DC)
+{
+ char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
+ DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ char static_cons_buf[ISAPI_SERVER_VAR_BUF_SIZE];
+ /*
+ * We need to construct the /C=.../ST=...
+ * DN's for SSL_CLIENT_DN and SSL_CLIENT_I_DN
+ */
+ strcpy( static_cons_buf, "/C=" );
+ if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_C", static_variable_buf, &variable_len ) && static_variable_buf[0] ) {
+ strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE);
+ }
+ strlcat( static_cons_buf, "/ST=", ISAPI_SERVER_VAR_BUF_SIZE);
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_ST", static_variable_buf, &variable_len ) && static_variable_buf[0] ) {
+ strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE );
+ }
+ php_register_variable( "SSL_CLIENT_DN", static_cons_buf, track_vars_array TSRMLS_CC );
+
+ strcpy( static_cons_buf, "/C=" );
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_I_C", static_variable_buf, &variable_len ) && static_variable_buf[0] ) {
+ strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE );
+ }
+ strlcat( static_cons_buf, "/ST=", ISAPI_SERVER_VAR_BUF_SIZE);
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_I_ST", static_variable_buf, &variable_len ) && static_variable_buf[0] ) {
+ strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE );
+ }
+ php_register_variable( "SSL_CLIENT_I_DN", static_cons_buf, track_vars_array TSRMLS_CC );
+}
+
+static void sapi_isapi_register_zeus_variables(LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array TSRMLS_DC)
+{
+ char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
+ DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ DWORD scriptname_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ DWORD pathinfo_len = 0;
+ char *strtok_buf = NULL;
+
+ /* Get SCRIPT_NAME, we use this to work out which bit of the URL
+ * belongs in PHP's version of PATH_INFO
+ */
+ lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_NAME", static_variable_buf, &scriptname_len);
+
+ /* Adjust Zeus' version of PATH_INFO, set PHP_SELF,
+ * and generate REQUEST_URI
+ */
+ if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_INFO", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
+
+ /* PHP_SELF is just PATH_INFO */
+ php_register_variable( "PHP_SELF", static_variable_buf, track_vars_array TSRMLS_CC );
+
+ /* Chop off filename to get just the 'real' PATH_INFO' */
+ pathinfo_len = variable_len - scriptname_len;
+ php_register_variable( "PATH_INFO", static_variable_buf + scriptname_len - 1, track_vars_array TSRMLS_CC );
+ /* append query string to give url... extra byte for '?' */
+ if ( strlen(lpECB->lpszQueryString) + variable_len + 1 < ISAPI_SERVER_VAR_BUF_SIZE ) {
+ /* append query string only if it is present... */
+ if ( strlen(lpECB->lpszQueryString) ) {
+ static_variable_buf[ variable_len - 1 ] = '?';
+ strcpy( static_variable_buf + variable_len, lpECB->lpszQueryString );
+ }
+ php_register_variable( "URL", static_variable_buf, track_vars_array TSRMLS_CC );
+ php_register_variable( "REQUEST_URI", static_variable_buf, track_vars_array TSRMLS_CC );
+ }
+ }
+
+ /* Get and adjust PATH_TRANSLATED to what PHP wants */
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_TRANSLATED", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
+ static_variable_buf[ variable_len - pathinfo_len - 1 ] = '\0';
+ php_register_variable( "PATH_TRANSLATED", static_variable_buf, track_vars_array TSRMLS_CC );
+ }
+
+ /* Bring in the AUTHENTICATION stuff as needed */
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if ( lpECB->GetServerVariable(lpECB->ConnID, "AUTH_USER", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
+ php_register_variable( "PHP_AUTH_USER", static_variable_buf, track_vars_array TSRMLS_CC );
+ }
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if ( lpECB->GetServerVariable(lpECB->ConnID, "AUTH_PASSWORD", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
+ php_register_variable( "PHP_AUTH_PW", static_variable_buf, track_vars_array TSRMLS_CC );
+ }
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if ( lpECB->GetServerVariable(lpECB->ConnID, "AUTH_TYPE", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
+ php_register_variable( "AUTH_TYPE", static_variable_buf, track_vars_array TSRMLS_CC );
+ }
+
+ /* And now, for the SSL variables (if applicable) */
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if ( lpECB->GetServerVariable(lpECB->ConnID, "CERT_COOKIE", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
+ sapi_isapi_register_zeus_ssl_variables( lpECB, track_vars_array TSRMLS_CC );
+ }
+ /* Copy some of the variables we need to meet Apache specs */
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if ( lpECB->GetServerVariable(lpECB->ConnID, "SERVER_SOFTWARE", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
+ php_register_variable( "SERVER_SIGNATURE", static_variable_buf, track_vars_array TSRMLS_CC );
+ }
+}
+#else
+
+static void sapi_isapi_register_iis_variables(LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array TSRMLS_DC)
+{
+ char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
+ char path_info_buf[ISAPI_SERVER_VAR_BUF_SIZE];
+ DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ DWORD scriptname_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ DWORD pathinfo_len = 0;
+ HSE_URL_MAPEX_INFO humi;
+
+ /* Get SCRIPT_NAME, we use this to work out which bit of the URL
+ * belongs in PHP's version of PATH_INFO. SCRIPT_NAME also becomes PHP_SELF.
+ */
+ lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_NAME", static_variable_buf, &scriptname_len);
+ php_register_variable("SCRIPT_FILENAME", SG(request_info).path_translated, track_vars_array TSRMLS_CC);
+
+ /* Adjust IIS' version of PATH_INFO, set PHP_SELF,
+ * and generate REQUEST_URI
+ * Get and adjust PATH_TRANSLATED to what PHP wants
+ */
+ if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_INFO", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
+
+ /* Chop off filename to get just the 'real' PATH_INFO' */
+ php_register_variable( "ORIG_PATH_INFO", static_variable_buf, track_vars_array TSRMLS_CC );
+ pathinfo_len = variable_len - scriptname_len;
+ strncpy(path_info_buf, static_variable_buf + scriptname_len - 1, sizeof(path_info_buf)-1);
+ php_register_variable( "PATH_INFO", path_info_buf, track_vars_array TSRMLS_CC );
+ /* append query string to give url... extra byte for '?' */
+ if ( strlen(lpECB->lpszQueryString) + variable_len + 1 < ISAPI_SERVER_VAR_BUF_SIZE ) {
+ /* append query string only if it is present... */
+ if ( strlen(lpECB->lpszQueryString) ) {
+ static_variable_buf[ variable_len - 1 ] = '?';
+ strcpy( static_variable_buf + variable_len, lpECB->lpszQueryString );
+ }
+ php_register_variable( "URL", static_variable_buf, track_vars_array TSRMLS_CC );
+ php_register_variable( "REQUEST_URI", static_variable_buf, track_vars_array TSRMLS_CC );
+ }
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_TRANSLATED", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
+ php_register_variable( "ORIG_PATH_TRANSLATED", static_variable_buf, track_vars_array TSRMLS_CC );
+ }
+ if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_MAP_URL_TO_PATH_EX, path_info_buf, &pathinfo_len, (LPDWORD) &humi)) {
+ /* Remove trailing \ */
+ if (humi.lpszPath[variable_len-2] == '\\') {
+ humi.lpszPath[variable_len-2] = 0;
+ }
+ php_register_variable("PATH_TRANSLATED", humi.lpszPath, track_vars_array TSRMLS_CC);
+ }
+ }
+
+ static_variable_buf[0] = '/';
+ static_variable_buf[1] = 0;
+ variable_len = 2;
+ if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_MAP_URL_TO_PATH_EX, static_variable_buf, &variable_len, (LPDWORD) &humi)) {
+ /* Remove trailing \ */
+ if (humi.lpszPath[variable_len-2] == '\\') {
+ humi.lpszPath[variable_len-2] = 0;
+ }
+ php_register_variable("DOCUMENT_ROOT", humi.lpszPath, track_vars_array TSRMLS_CC);
+ }
+
+ if (!SG(request_info).auth_user || !SG(request_info).auth_password ||
+ !SG(request_info).auth_user[0] || !SG(request_info).auth_password[0]) {
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if (lpECB->GetServerVariable(lpECB->ConnID, "HTTP_AUTHORIZATION", static_variable_buf, &variable_len)
+ && static_variable_buf[0]) {
+ php_handle_auth_data(static_variable_buf TSRMLS_CC);
+ }
+ }
+
+ if (SG(request_info).auth_user) {
+ php_register_variable("PHP_AUTH_USER", SG(request_info).auth_user, track_vars_array TSRMLS_CC );
+ }
+ if (SG(request_info).auth_password) {
+ php_register_variable("PHP_AUTH_PW", SG(request_info).auth_password, track_vars_array TSRMLS_CC );
+ }
+}
+#endif
+
+static void sapi_isapi_register_server_variables2(char **server_variables, LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array, char **recorded_values TSRMLS_DC)
+{
+ char **p=server_variables;
+ DWORD variable_len;
+ char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
+ char *variable_buf;
+
+ while (*p) {
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if (lpECB->GetServerVariable(lpECB->ConnID, *p, static_variable_buf, &variable_len)
+ && static_variable_buf[0]) {
+ php_register_variable(*p, static_variable_buf, track_vars_array TSRMLS_CC);
+ if (recorded_values) {
+ recorded_values[p-server_variables] = estrndup(static_variable_buf, variable_len);
+ }
+ } else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ variable_buf = (char *) emalloc(variable_len+1);
+ if (lpECB->GetServerVariable(lpECB->ConnID, *p, variable_buf, &variable_len)
+ && variable_buf[0]) {
+ php_register_variable(*p, variable_buf, track_vars_array TSRMLS_CC);
+ }
+ if (recorded_values) {
+ recorded_values[p-server_variables] = variable_buf;
+ } else {
+ efree(variable_buf);
+ }
+ } else { /* for compatibility with Apache SAPIs */
+ php_register_variable(*p, "", track_vars_array TSRMLS_CC);
+ }
+ p++;
+ }
+}
+
+
+static void sapi_isapi_register_server_variables(zval *track_vars_array TSRMLS_DC)
+{
+ DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ char *variable;
+ char *strtok_buf = NULL;
+ char *isapi_special_server_variables[NUM_SPECIAL_VARS];
+ LPEXTENSION_CONTROL_BLOCK lpECB;
+
+ lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
+
+ /* Register the special ISAPI variables */
+ memset(isapi_special_server_variables, 0, sizeof(isapi_special_server_variables));
+ sapi_isapi_register_server_variables2(isapi_special_server_variable_names, lpECB, track_vars_array, isapi_special_server_variables TSRMLS_CC);
+ if (SG(request_info).cookie_data) {
+ php_register_variable("HTTP_COOKIE", SG(request_info).cookie_data, track_vars_array TSRMLS_CC);
+ }
+
+ /* Register the standard ISAPI variables */
+ sapi_isapi_register_server_variables2(isapi_server_variable_names, lpECB, track_vars_array, NULL TSRMLS_CC);
+
+ if (isapi_special_server_variables[SPECIAL_VAR_HTTPS]
+ && (atoi(isapi_special_server_variables[SPECIAL_VAR_HTTPS])
+ || !strcasecmp(isapi_special_server_variables[SPECIAL_VAR_HTTPS], "on"))
+ ) {
+ /* Register SSL ISAPI variables */
+ sapi_isapi_register_server_variables2(isapi_secure_server_variable_names, lpECB, track_vars_array, NULL TSRMLS_CC);
+ }
+
+ if (isapi_special_server_variables[SPECIAL_VAR_HTTPS]) {
+ efree(isapi_special_server_variables[SPECIAL_VAR_HTTPS]);
+ }
+
+
+#ifdef WITH_ZEUS
+ sapi_isapi_register_zeus_variables(lpECB, track_vars_array TSRMLS_CC);
+#else
+ sapi_isapi_register_iis_variables(lpECB, track_vars_array TSRMLS_CC);
+#endif
+
+ /* PHP_SELF support */
+ if (isapi_special_server_variables[SPECIAL_VAR_PHP_SELF]) {
+ php_register_variable("PHP_SELF", isapi_special_server_variables[SPECIAL_VAR_PHP_SELF], track_vars_array TSRMLS_CC);
+ efree(isapi_special_server_variables[SPECIAL_VAR_PHP_SELF]);
+ }
+
+ if (isapi_special_server_variables[SPECIAL_VAR_ALL_HTTP]) {
+ /* Register the internal bits of ALL_HTTP */
+ variable = php_strtok_r(isapi_special_server_variables[SPECIAL_VAR_ALL_HTTP], "\r\n", &strtok_buf);
+ while (variable) {
+ char *colon = strchr(variable, ':');
+
+ if (colon) {
+ char *value = colon+1;
+
+ while (*value==' ') {
+ value++;
+ }
+ *colon = 0;
+ php_register_variable(variable, value, track_vars_array TSRMLS_CC);
+ *colon = ':';
+ }
+ variable = php_strtok_r(NULL, "\r\n", &strtok_buf);
+ }
+ efree(isapi_special_server_variables[SPECIAL_VAR_ALL_HTTP]);
+ }
+}
+
+
+static sapi_module_struct isapi_sapi_module = {
+ "isapi", /* name */
+ "ISAPI", /* pretty name */
+
+ php_isapi_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ NULL, /* activate */
+ NULL, /* deactivate */
+
+ sapi_isapi_ub_write, /* unbuffered write */
+ NULL, /* flush */
+ NULL, /* get uid */
+ NULL, /* getenv */
+
+ php_error, /* error handler */
+
+ sapi_isapi_header_handler, /* header handler */
+ sapi_isapi_send_headers, /* send headers handler */
+ NULL, /* send header handler */
+
+ sapi_isapi_read_post, /* read POST data */
+ sapi_isapi_read_cookies, /* read Cookies */
+
+ sapi_isapi_register_server_variables, /* register server variables */
+ NULL, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* Child terminate */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+
+
+BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pFilterVersion)
+{
+ bFilterLoaded = 1;
+ pFilterVersion->dwFilterVersion = HTTP_FILTER_REVISION;
+ strcpy(pFilterVersion->lpszFilterDesc, isapi_sapi_module.pretty_name);
+ pFilterVersion->dwFlags= (SF_NOTIFY_AUTHENTICATION | SF_NOTIFY_PREPROC_HEADERS);
+ return TRUE;
+}
+
+
+DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificationType, LPVOID pvNotification)
+{
+ TSRMLS_FETCH();
+
+ switch (notificationType) {
+ case SF_NOTIFY_PREPROC_HEADERS:
+ SG(request_info).auth_user = NULL;
+ SG(request_info).auth_password = NULL;
+ SG(request_info).auth_digest = NULL;
+ break;
+ case SF_NOTIFY_AUTHENTICATION: {
+ char *auth_user = ((HTTP_FILTER_AUTHENT *) pvNotification)->pszUser;
+ char *auth_password = ((HTTP_FILTER_AUTHENT *) pvNotification)->pszPassword;
+
+ if (auth_user && auth_user[0]) {
+ SG(request_info).auth_user = estrdup(auth_user);
+ }
+ if (auth_password && auth_password[0]) {
+ SG(request_info).auth_password = estrdup(auth_password);
+ }
+ return SF_STATUS_REQ_HANDLED_NOTIFICATION;
+ }
+ break;
+ }
+ return SF_STATUS_REQ_NEXT_NOTIFICATION;
+}
+
+
+static void init_request_info(LPEXTENSION_CONTROL_BLOCK lpECB TSRMLS_DC)
+{
+ DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
+#ifndef WITH_ZEUS
+ HSE_URL_MAPEX_INFO humi;
+#endif
+
+ SG(request_info).request_method = lpECB->lpszMethod;
+ SG(request_info).query_string = lpECB->lpszQueryString;
+ SG(request_info).request_uri = lpECB->lpszPathInfo;
+ SG(request_info).content_type = lpECB->lpszContentType;
+ SG(request_info).content_length = lpECB->cbTotalBytes;
+ SG(sapi_headers).http_response_code = 200; /* I think dwHttpStatusCode is invalid at this stage -RL */
+ if (!bFilterLoaded) { /* we don't have valid ISAPI Filter information */
+ SG(request_info).auth_user = SG(request_info).auth_password = SG(request_info).auth_digest = NULL;
+ }
+
+#ifdef WITH_ZEUS
+ /* PATH_TRANSLATED can contain extra PATH_INFO stuff after the
+ * file being loaded, so we must use SCRIPT_FILENAME instead
+ */
+ if(lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_FILENAME", static_variable_buf, &variable_len)) {
+ SG(request_info).path_translated = estrdup(static_variable_buf);
+ } else
+#else
+ /* happily, IIS gives us SCRIPT_NAME which is correct (without PATH_INFO stuff)
+ so we can just map that to the physical path and we have our filename */
+
+ lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_NAME", static_variable_buf, &variable_len);
+ if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_MAP_URL_TO_PATH_EX, static_variable_buf, &variable_len, (LPDWORD) &humi)) {
+ SG(request_info).path_translated = estrdup(humi.lpszPath);
+ } else
+#endif
+ /* if mapping fails, default to what the server tells us */
+ SG(request_info).path_translated = estrdup(lpECB->lpszPathTranslated);
+
+ /* some server configurations allow '..' to slip through in the
+ translated path. We'll just refuse to handle such a path. */
+ if (strstr(SG(request_info).path_translated,"..")) {
+ SG(sapi_headers).http_response_code = 404;
+ efree(SG(request_info).path_translated);
+ SG(request_info).path_translated = NULL;
+ }
+}
+
+
+static void php_isapi_report_exception(char *message, int message_len TSRMLS_DC)
+{
+ if (!SG(headers_sent)) {
+ HSE_SEND_HEADER_EX_INFO header_info;
+ LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
+
+ header_info.pszStatus = "500 Internal Server Error";
+ header_info.cchStatus = strlen(header_info.pszStatus);
+ header_info.pszHeader = "Content-Type: text/html\r\n\r\n";
+ header_info.cchHeader = strlen(header_info.pszHeader);
+
+ lpECB->dwHttpStatusCode = 500;
+ lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX, &header_info, NULL, NULL);
+ SG(headers_sent)=1;
+ }
+ sapi_isapi_ub_write(message, message_len TSRMLS_CC);
+}
+
+
+BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)
+{
+ pVer->dwExtensionVersion = HSE_VERSION;
+#ifdef WITH_ZEUS
+ strncpy( pVer->lpszExtensionDesc, isapi_sapi_module.name, HSE_MAX_EXT_DLL_NAME_LEN);
+#else
+ lstrcpyn(pVer->lpszExtensionDesc, isapi_sapi_module.name, HSE_MAX_EXT_DLL_NAME_LEN);
+#endif
+ return TRUE;
+}
+
+
+static void my_endthread()
+{
+#ifdef PHP_WIN32
+ if (bTerminateThreadsOnError) {
+ _endthread();
+ }
+#endif
+}
+
+#ifdef PHP_WIN32
+/* ep is accessible only in the context of the __except expression,
+ * so we have to call this function to obtain it.
+ */
+BOOL exceptionhandler(LPEXCEPTION_POINTERS *e, LPEXCEPTION_POINTERS ep)
+{
+ *e=ep;
+ return TRUE;
+}
+#endif
+
+DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB)
+{
+ zend_file_handle file_handle;
+ zend_bool stack_overflown=0;
+ int retval = FAILURE;
+#ifdef PHP_ENABLE_SEH
+ LPEXCEPTION_POINTERS e;
+#endif
+ TSRMLS_FETCH();
+
+ zend_first_try {
+#ifdef PHP_ENABLE_SEH
+ __try {
+#endif
+ init_request_info(lpECB TSRMLS_CC);
+ SG(server_context) = lpECB;
+
+ php_request_startup(TSRMLS_C);
+
+ file_handle.filename = SG(request_info).path_translated;
+ file_handle.free_filename = 0;
+ file_handle.type = ZEND_HANDLE_FILENAME;
+ file_handle.opened_path = NULL;
+
+ /* open the script here so we can 404 if it fails */
+ if (file_handle.filename)
+ retval = php_fopen_primary_script(&file_handle TSRMLS_CC);
+
+ if (!file_handle.filename || retval == FAILURE) {
+ SG(sapi_headers).http_response_code = 404;
+ PUTS("No input file specified.\n");
+ } else {
+ php_execute_script(&file_handle TSRMLS_CC);
+ }
+
+ if (SG(request_info).cookie_data) {
+ efree(SG(request_info).cookie_data);
+ }
+ if (SG(request_info).path_translated)
+ efree(SG(request_info).path_translated);
+#ifdef PHP_ENABLE_SEH
+ } __except(exceptionhandler(&e, GetExceptionInformation())) {
+ char buf[1024];
+ if (_exception_code()==EXCEPTION_STACK_OVERFLOW) {
+ LPBYTE lpPage;
+ static SYSTEM_INFO si;
+ static MEMORY_BASIC_INFORMATION mi;
+ static DWORD dwOldProtect;
+
+ GetSystemInfo(&si);
+
+ /* Get page ESP is pointing to */
+ _asm mov lpPage, esp;
+
+ /* Get stack allocation base */
+ VirtualQuery(lpPage, &mi, sizeof(mi));
+
+ /* Go to the page below the current page */
+ lpPage = (LPBYTE) (mi.BaseAddress) - si.dwPageSize;
+
+ /* Free pages below current page */
+ if (!VirtualFree(mi.AllocationBase, (LPBYTE)lpPage - (LPBYTE) mi.AllocationBase, MEM_DECOMMIT)) {
+ _endthread();
+ }
+
+ /* Restore the guard page */
+ if (!VirtualProtect(lpPage, si.dwPageSize, PAGE_GUARD | PAGE_READWRITE, &dwOldProtect)) {
+ _endthread();
+ }
+
+ CG(unclean_shutdown)=1;
+ _snprintf(buf, sizeof(buf)-1,"PHP has encountered a Stack overflow");
+ php_isapi_report_exception(buf, strlen(buf) TSRMLS_CC);
+ } else if (_exception_code()==EXCEPTION_ACCESS_VIOLATION) {
+ _snprintf(buf, sizeof(buf)-1,"PHP has encountered an Access Violation at %p", e->ExceptionRecord->ExceptionAddress);
+ php_isapi_report_exception(buf, strlen(buf) TSRMLS_CC);
+ my_endthread();
+ } else {
+ _snprintf(buf, sizeof(buf)-1,"PHP has encountered an Unhandled Exception Code %d at %p", e->ExceptionRecord->ExceptionCode , e->ExceptionRecord->ExceptionAddress);
+ php_isapi_report_exception(buf, strlen(buf) TSRMLS_CC);
+ my_endthread();
+ }
+ }
+#endif
+#ifdef PHP_ENABLE_SEH
+ __try {
+ php_request_shutdown(NULL);
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ my_endthread();
+ }
+#else
+ php_request_shutdown(NULL);
+#endif
+ } zend_catch {
+ zend_try {
+ php_request_shutdown(NULL);
+ } zend_end_try();
+ return HSE_STATUS_ERROR;
+ } zend_end_try();
+
+ return HSE_STATUS_SUCCESS;
+}
+
+
+
+__declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ switch (fdwReason) {
+ case DLL_PROCESS_ATTACH:
+#ifdef WITH_ZEUS
+ tsrm_startup(128, 1, TSRM_ERROR_LEVEL_CORE, "TSRM.log");
+#else
+ tsrm_startup(128, 1, TSRM_ERROR_LEVEL_CORE, "C:\\TSRM.log");
+#endif
+ sapi_startup(&isapi_sapi_module);
+ if (isapi_sapi_module.startup) {
+ isapi_sapi_module.startup(&sapi_module);
+ }
+ break;
+ case DLL_THREAD_ATTACH:
+ break;
+ case DLL_THREAD_DETACH:
+ ts_free_thread();
+ break;
+ case DLL_PROCESS_DETACH:
+ if (isapi_sapi_module.shutdown) {
+ isapi_sapi_module.shutdown(&sapi_module);
+ }
+ sapi_shutdown();
+ tsrm_shutdown();
+ break;
+ }
+ return TRUE;
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/sapi/isapi/php5isapi.def b/sapi/isapi/php5isapi.def
new file mode 100644
index 0000000..596023e
--- /dev/null
+++ b/sapi/isapi/php5isapi.def
@@ -0,0 +1,5 @@
+EXPORTS
+HttpFilterProc
+GetFilterVersion
+HttpExtensionProc
+GetExtensionVersion
diff --git a/sapi/isapi/php5isapi.dsp b/sapi/isapi/php5isapi.dsp
new file mode 100644
index 0000000..3dbab11
--- /dev/null
+++ b/sapi/isapi/php5isapi.dsp
@@ -0,0 +1,165 @@
+# Microsoft Developer Studio Project File - Name="php5isapi" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=php5isapi - Win32 Debug_TS
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "php5isapi.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "php5isapi.mak" CFG="php5isapi - Win32 Debug_TS"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "php5isapi - Win32 Debug_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "php5isapi - Win32 Release_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "php5isapi - Win32 Release_TS_inline" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "php5isapi - Win32 Release_TSDbg" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "php5isapi - Win32 Debug_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug_TS"
+# PROP BASE Intermediate_Dir "Debug_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\..\Debug_TS"
+# PROP Intermediate_Dir "Debug_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PHP5ISAPI_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /D "_DEBUG" /D "COMPILE_LIBZEND" /D ZEND_DEBUG=1 /D "_WINDOWS" /D "_USRDLL" /D "PHP5ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x40d /d "_DEBUG"
+# ADD RSC /l 0x40d /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 wsock32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php5ts_debug.lib /nologo /version:4.0 /dll /debug /machine:I386 /nodefaultlib:"libcmt" /pdbtype:sept /libpath:"..\..\Debug_TS"
+
+!ELSEIF "$(CFG)" == "php5isapi - Win32 Release_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release_TS"
+# PROP BASE Intermediate_Dir "Release_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Release_TS"
+# PROP Intermediate_Dir "Release_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PHP5ISAPI_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /D "NDEBUG" /D ZEND_DEBUG=0 /D "_WINDOWS" /D "_USRDLL" /D "PHP5ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x40d /d "NDEBUG"
+# ADD RSC /l 0x40d /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php5ts.lib /nologo /version:4.0 /dll /machine:I386 /libpath:"..\..\Release_TS"
+
+!ELSEIF "$(CFG)" == "php5isapi - Win32 Release_TS_inline"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "php5isapi___Win32_Release_TS_inline"
+# PROP BASE Intermediate_Dir "php5isapi___Win32_Release_TS_inline"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Release_TS_inline"
+# PROP Intermediate_Dir "Release_TS_inline"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /D "NDEBUG" /D "_WINDOWS" /D "_USRDLL" /D "PHP5ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "WIN32" /D "_MBCS" /D ZEND_DEBUG=0 /FR /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /D "NDEBUG" /D ZEND_DEBUG=0 /D "ZEND_WIN32_FORCE_INLINE" /D "_WINDOWS" /D "_USRDLL" /D "PHP5ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x40d /d "NDEBUG"
+# ADD RSC /l 0x40d /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php5ts.lib /nologo /dll /machine:I386 /libpath:"..\..\Release_TS"
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php5ts.lib /nologo /version:4.0 /dll /machine:I386 /libpath:"..\..\Release_TS_inline"
+
+!ELSEIF "$(CFG)" == "php5isapi - Win32 Release_TSDbg"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "php5isapi___Win32_Release_TSDbg"
+# PROP BASE Intermediate_Dir "php5isapi___Win32_Release_TSDbg"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Release_TSDbg"
+# PROP Intermediate_Dir "Release_TSDbg"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /D "NDEBUG" /D ZEND_DEBUG=0 /D "_WINDOWS" /D "_USRDLL" /D "PHP5ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /FR /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /Zi /Od /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /D "NDEBUG" /D ZEND_DEBUG=0 /D "_WINDOWS" /D "_USRDLL" /D "PHP5ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x40d /d "NDEBUG"
+# ADD RSC /l 0x40d /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php5ts.lib /nologo /version:4.0 /dll /machine:I386 /libpath:"..\..\Release_TS"
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php5ts.lib /nologo /version:4.0 /dll /debug /machine:I386 /libpath:"..\..\Release_TSDbg"
+
+!ENDIF
+
+# Begin Target
+
+# Name "php5isapi - Win32 Debug_TS"
+# Name "php5isapi - Win32 Release_TS"
+# Name "php5isapi - Win32 Release_TS_inline"
+# Name "php5isapi - Win32 Release_TSDbg"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\php5isapi.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\php5isapi.def
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# End Target
+# End Project
diff --git a/sapi/isapi/stresstest/getopt.c b/sapi/isapi/stresstest/getopt.c
new file mode 100644
index 0000000..57faa0f
--- /dev/null
+++ b/sapi/isapi/stresstest/getopt.c
@@ -0,0 +1,175 @@
+/* Borrowed from Apache NT Port */
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include "getopt.h"
+#define OPTERRCOLON (1)
+#define OPTERRNF (2)
+#define OPTERRARG (3)
+
+
+char *ap_optarg;
+int ap_optind = 1;
+static int ap_opterr = 1;
+static int ap_optopt;
+
+static int
+ap_optiserr(int argc, char * const *argv, int oint, const char *optstr,
+ int optchr, int err)
+{
+ if (ap_opterr)
+ {
+ fprintf(stderr, "Error in argument %d, char %d: ", oint, optchr+1);
+ switch(err)
+ {
+ case OPTERRCOLON:
+ fprintf(stderr, ": in flags\n");
+ break;
+ case OPTERRNF:
+ fprintf(stderr, "option not found %c\n", argv[oint][optchr]);
+ break;
+ case OPTERRARG:
+ fprintf(stderr, "no argument for option %c\n", argv[oint][optchr]);
+ break;
+ default:
+ fprintf(stderr, "unknown\n");
+ break;
+ }
+ }
+ ap_optopt = argv[oint][optchr];
+ return('?');
+}
+
+int ap_getopt(int argc, char* const *argv, const char *optstr)
+{
+ static int optchr = 0;
+ static int dash = 0; /* have already seen the - */
+
+ char *cp;
+
+ if (ap_optind >= argc)
+ return(EOF);
+ if (!dash && (argv[ap_optind][0] != '-'))
+ return(EOF);
+ if (!dash && (argv[ap_optind][0] == '-') && !argv[ap_optind][1])
+ {
+ /*
+ * use to specify stdin. Need to let pgm process this and
+ * the following args
+ */
+ return(EOF);
+ }
+ if ((argv[ap_optind][0] == '-') && (argv[ap_optind][1] == '-'))
+ {
+ /* -- indicates end of args */
+ ap_optind++;
+ return(EOF);
+ }
+ if (!dash)
+ {
+ assert((argv[ap_optind][0] == '-') && argv[ap_optind][1]);
+ dash = 1;
+ optchr = 1;
+ }
+
+ /* Check if the guy tries to do a -: kind of flag */
+ assert(dash);
+ if (argv[ap_optind][optchr] == ':')
+ {
+ dash = 0;
+ ap_optind++;
+ return(ap_optiserr(argc, argv, ap_optind-1, optstr, optchr, OPTERRCOLON));
+ }
+ if (!(cp = strchr(optstr, argv[ap_optind][optchr])))
+ {
+ int errind = ap_optind;
+ int errchr = optchr;
+
+ if (!argv[ap_optind][optchr+1])
+ {
+ dash = 0;
+ ap_optind++;
+ }
+ else
+ optchr++;
+ return(ap_optiserr(argc, argv, errind, optstr, errchr, OPTERRNF));
+ }
+ if (cp[1] == ':')
+ {
+ /* Check for cases where the value of the argument
+ is in the form -<arg> <val> or in the form -<arg><val> */
+ dash = 0;
+ if(!argv[ap_optind][2]) {
+ ap_optind++;
+ if (ap_optind == argc)
+ return(ap_optiserr(argc, argv, ap_optind-1, optstr, optchr, OPTERRARG));
+ ap_optarg = argv[ap_optind++];
+ }
+ else
+ {
+ ap_optarg = &argv[ap_optind][2];
+ ap_optind++;
+ }
+ return(*cp);
+ }
+ else
+ {
+ if (!argv[ap_optind][optchr+1])
+ {
+ dash = 0;
+ ap_optind++;
+ }
+ else
+ optchr++;
+ return(*cp);
+ }
+ assert(0);
+ return(0);
+}
+
+#ifdef TESTGETOPT
+int
+ main (int argc, char **argv)
+ {
+ int c;
+ extern char *ap_optarg;
+ extern int ap_optind;
+ int aflg = 0;
+ int bflg = 0;
+ int errflg = 0;
+ char *ofile = NULL;
+
+ while ((c = ap_getopt(argc, argv, "abo:")) != EOF)
+ switch (c) {
+ case 'a':
+ if (bflg)
+ errflg++;
+ else
+ aflg++;
+ break;
+ case 'b':
+ if (aflg)
+ errflg++;
+ else
+ bflg++;
+ break;
+ case 'o':
+ ofile = ap_optarg;
+ (void)printf("ofile = %s\n", ofile);
+ break;
+ case '?':
+ errflg++;
+ }
+ if (errflg) {
+ (void)fprintf(stderr,
+ "usage: cmd [-a|-b] [-o <filename>] files...\n");
+ exit (2);
+ }
+ for ( ; ap_optind < argc; ap_optind++)
+ (void)printf("%s\n", argv[ap_optind]);
+ return 0;
+ }
+
+#endif /* TESTGETOPT */
diff --git a/sapi/isapi/stresstest/getopt.h b/sapi/isapi/stresstest/getopt.h
new file mode 100644
index 0000000..a3e278e
--- /dev/null
+++ b/sapi/isapi/stresstest/getopt.h
@@ -0,0 +1,12 @@
+/* Borrowed from Apache NT Port */
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern char *ap_optarg;
+extern int ap_optind;
+
+int ap_getopt(int argc, char* const *argv, const char *optstr);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/sapi/isapi/stresstest/notes.txt b/sapi/isapi/stresstest/notes.txt
new file mode 100644
index 0000000..f58ab3c
--- /dev/null
+++ b/sapi/isapi/stresstest/notes.txt
@@ -0,0 +1,56 @@
+This stress test program is for debugging threading issues with the ISAPI
+module.
+
+2 ways to use it:
+
+1: test any php script file on multiple threads
+2: run the php test scripts bundled with the source code
+
+
+
+GLOBAL SETTINGS
+===============
+
+If you need to set special environement variables, in addition to your
+regular environment, create a file that contains them, one setting per line:
+
+MY_ENV_VAR=XXXXXXXX
+
+This can be used to simulate ISAPI environment variables if need be.
+
+By default, stress test uses 10 threads. To change this, change the define
+NUM_THREADS in stresstest.cpp.
+
+
+
+1: Test any php script file on multiple threads
+===============================================
+
+Create a file that contains a list of php script files, one per line. If
+you need to provide input, place the GET data, or Query String, after the
+filename. File contents would look like:
+
+e:\inetpub\pages\index.php
+e:\inetpub\pages\info.php
+e:\inetpub\pages\test.php a=1&b=2
+
+Run: stresstest L files.txt
+
+
+
+2: Run the php test scripts bundled with the source code
+========================================================
+
+supply the path to the parent of the "tests" directory (expect a couple
+long pauses for a couple of the larger tests)
+
+Run: stresstest T c:\php5-source
+
+
+
+TODO:
+
+* Make more options configurable: number of threads, iterations, etc.
+* Improve stdout output to make it more useful
+* Implement support for SKIPIF
+* Improve speed of CompareFile function (too slow on big files).
diff --git a/sapi/isapi/stresstest/stresstest.cpp b/sapi/isapi/stresstest/stresstest.cpp
new file mode 100644
index 0000000..97824e6
--- /dev/null
+++ b/sapi/isapi/stresstest/stresstest.cpp
@@ -0,0 +1,936 @@
+/*
+ * ======================================================================= *
+ * File: stress .c *
+ * stress tester for isapi dll's *
+ * based on cgiwrap *
+ * ======================================================================= *
+ *
+*/
+#define WIN32_LEAN_AND_MEAN
+#include <afx.h>
+#include <afxtempl.h>
+#include <winbase.h>
+#include <winerror.h>
+#include <httpext.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "getopt.h"
+
+// These are things that go out in the Response Header
+//
+#define HTTP_VER "HTTP/1.0"
+#define SERVER_VERSION "Http-Srv-Beta2/1.0"
+
+//
+// Simple wrappers for the heap APIS
+//
+#define xmalloc(s) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (s))
+#define xfree(s) HeapFree(GetProcessHeap(), 0, (s))
+
+//
+// The mandatory exports from the ISAPI DLL
+//
+DWORD numThreads = 1;
+DWORD iterations = 1;
+
+HANDLE StartNow;
+// quick and dirty environment
+typedef CMapStringToString TEnvironment;
+TEnvironment IsapiEnvironment;
+
+typedef struct _TResults {
+ LONG ok;
+ LONG bad;
+} TResults;
+
+CStringArray IsapiFileList; // list of filenames
+CStringArray TestNames; // --TEST--
+CStringArray IsapiGetData; // --GET--
+CStringArray IsapiPostData; // --POST--
+CStringArray IsapiMatchData; // --EXPECT--
+CArray<TResults, TResults> Results;
+
+typedef struct _TIsapiContext {
+ HANDLE in;
+ HANDLE out;
+ DWORD tid;
+ TEnvironment env;
+ HANDLE waitEvent;
+} TIsapiContext;
+
+//
+// Prototypes of the functions this sample implements
+//
+extern "C" {
+HINSTANCE hDll;
+typedef BOOL (WINAPI *VersionProc)(HSE_VERSION_INFO *) ;
+typedef DWORD (WINAPI *HttpExtProc)(EXTENSION_CONTROL_BLOCK *);
+typedef BOOL (WINAPI *TerminateProc) (DWORD);
+BOOL WINAPI FillExtensionControlBlock(EXTENSION_CONTROL_BLOCK *, TIsapiContext *) ;
+BOOL WINAPI GetServerVariable(HCONN, LPSTR, LPVOID, LPDWORD );
+BOOL WINAPI ReadClient(HCONN, LPVOID, LPDWORD);
+BOOL WINAPI WriteClient(HCONN, LPVOID, LPDWORD, DWORD);
+BOOL WINAPI ServerSupportFunction(HCONN, DWORD, LPVOID, LPDWORD, LPDWORD);
+VersionProc IsapiGetExtensionVersion;
+HttpExtProc IsapiHttpExtensionProc;
+TerminateProc TerminateExtensionProc;
+HSE_VERSION_INFO version_info;
+}
+
+char * MakeDateStr(VOID);
+char * GetEnv(char *);
+
+
+
+
+DWORD CALLBACK IsapiThread(void *);
+int stress_main(const char *filename,
+ const char *arg,
+ const char *postfile,
+ const char *matchdata);
+
+
+
+BOOL bUseTestFiles = FALSE;
+char temppath[MAX_PATH];
+
+void stripcrlf(char *line)
+{
+ DWORD l = strlen(line)-1;
+ if (line[l]==10 || line[l]==13) line[l]=0;
+ l = strlen(line)-1;
+ if (line[l]==10 || line[l]==13) line[l]=0;
+}
+
+#define COMPARE_BUF_SIZE 1024
+
+BOOL CompareFiles(const char*f1, const char*f2)
+{
+ FILE *fp1, *fp2;
+ bool retval;
+ char buf1[COMPARE_BUF_SIZE], buf2[COMPARE_BUF_SIZE];
+ int length1, length2;
+
+ if ((fp1=fopen(f1, "r"))==NULL) {
+ return FALSE;
+ }
+
+ if ((fp2=fopen(f2, "r"))==NULL) {
+ fclose(fp1);
+ return FALSE;
+ }
+
+ retval = TRUE; // success oriented
+ while (true) {
+ length1 = fread(buf1, 1, sizeof(buf1), fp1);
+ length2 = fread(buf2, 1, sizeof(buf2), fp2);
+
+ // check for end of file
+ if (feof(fp1)) {
+ if (!feof(fp2)) {
+ retval = FALSE;
+ }
+ break;
+ } else if (feof(fp2)) {
+ if (!feof(fp1)) {
+ retval = FALSE;
+ }
+ break;
+ }
+
+ // compare data
+ if (length1!=length2
+ || memcmp(buf1, buf2, length1)!=0) {
+ retval = FALSE;
+ break;
+ }
+ }
+ fclose(fp1);
+ fclose(fp2);
+
+ return retval;
+}
+
+
+BOOL CompareStringWithFile(const char *filename, const char *str, unsigned int str_length)
+{
+ FILE *fp;
+ bool retval;
+ char buf[COMPARE_BUF_SIZE];
+ unsigned int offset=0, readbytes;
+ fprintf(stderr, "test %s\n",filename);
+ if ((fp=fopen(filename, "rb"))==NULL) {
+ fprintf(stderr, "Error opening %s\n",filename);
+ return FALSE;
+ }
+
+ retval = TRUE; // success oriented
+ while (true) {
+ readbytes = fread(buf, 1, sizeof(buf), fp);
+
+ // check for end of file
+
+ if (offset+readbytes > str_length
+ || memcmp(buf, str+offset, readbytes)!=NULL) {
+ fprintf(stderr, "File missmatch %s\n",filename);
+ retval = FALSE;
+ break;
+ }
+ if (feof(fp)) {
+ if (!retval) fprintf(stderr, "File zero length %s\n",filename);
+ break;
+ }
+ }
+ fclose(fp);
+
+ return retval;
+}
+
+
+BOOL ReadGlobalEnvironment(const char *environment)
+{
+ if (environment) {
+ FILE *fp = fopen(environment, "r");
+ DWORD i=0;
+ if (fp) {
+ char line[2048];
+ while (fgets(line, sizeof(line)-1, fp)) {
+ // file.php arg1 arg2 etc.
+ char *p = strchr(line, '=');
+ if (p) {
+ *p=0;
+ IsapiEnvironment[line]=p+1;
+ }
+ }
+ fclose(fp);
+ return IsapiEnvironment.GetCount() > 0;
+ }
+ }
+ return FALSE;
+}
+
+BOOL ReadFileList(const char *filelist)
+{
+ FILE *fp = fopen(filelist, "r");
+ if (!fp) {
+ printf("Unable to open %s\r\n", filelist);
+ }
+ char line[2048];
+ int i=0;
+ while (fgets(line, sizeof(line)-1, fp)) {
+ // file.php arg1 arg2 etc.
+ stripcrlf(line);
+ if (strlen(line)>3) {
+ char *p = strchr(line, ' ');
+ if (p) {
+ *p = 0;
+ // get file
+
+ IsapiFileList.Add(line);
+ IsapiGetData.Add(p+1);
+ } else {
+ // just a filename is all
+ IsapiFileList.Add(line);
+ IsapiGetData.Add("");
+ }
+ }
+
+ // future use
+ IsapiPostData.Add("");
+ IsapiMatchData.Add("");
+ TestNames.Add("");
+
+ i++;
+ }
+ Results.SetSize(TestNames.GetSize());
+
+ fclose(fp);
+ return IsapiFileList.GetSize() > 0;
+}
+
+void DoThreads() {
+
+ if (IsapiFileList.GetSize() == 0) {
+ printf("No Files to test\n");
+ return;
+ }
+
+ printf("Starting Threads...\n");
+ // loop creating threads
+ DWORD tid;
+ HANDLE *threads = new HANDLE[numThreads];
+ DWORD i;
+ for (i=0; i< numThreads; i++) {
+ threads[i]=CreateThread(NULL, 0, IsapiThread, NULL, CREATE_SUSPENDED, &tid);
+ }
+ for (i=0; i< numThreads; i++) {
+ if (threads[i]) ResumeThread(threads[i]);
+ }
+ // wait for threads to finish
+ WaitForMultipleObjects(numThreads, threads, TRUE, INFINITE);
+ for (i=0; i< numThreads; i++) {
+ CloseHandle(threads[i]);
+ }
+ delete [] threads;
+}
+
+void DoFileList(const char *filelist, const char *environment)
+{
+ // read config files
+
+ if (!ReadFileList(filelist)) {
+ printf("No Files to test!\r\n");
+ return;
+ }
+
+ ReadGlobalEnvironment(environment);
+
+ DoThreads();
+}
+
+
+/**
+ * ParseTestFile
+ * parse a single phpt file and add it to the arrays
+ */
+BOOL ParseTestFile(const char *path, const char *fn)
+{
+ // parse the test file
+ char filename[MAX_PATH];
+ _snprintf(filename, sizeof(filename)-1, "%s\\%s", path, fn);
+ char line[1024];
+ memset(line, 0, sizeof(line));
+ CString cTest, cSkipIf, cPost, cGet, cFile, cExpect;
+ printf("Reading %s\r\n", filename);
+
+ enum state {none, test, skipif, post, get, file, expect} parsestate = none;
+
+ FILE *fp = fopen(filename, "rb");
+ char *tn = _tempnam(temppath,"pht.");
+ char *en = _tempnam(temppath,"exp.");
+ FILE *ft = fopen(tn, "wb+");
+ FILE *fe = fopen(en, "wb+");
+ if (fp && ft && fe) {
+ while (fgets(line, sizeof(line)-1, fp)) {
+ if (line[0]=='-') {
+ if (_strnicmp(line, "--TEST--", 8)==0) {
+ parsestate = test;
+ continue;
+ } else if (_strnicmp(line, "--SKIPIF--", 10)==0) {
+ parsestate = skipif;
+ continue;
+ } else if (_strnicmp(line, "--POST--", 8)==0) {
+ parsestate = post;
+ continue;
+ } else if (_strnicmp(line, "--GET--", 7)==0) {
+ parsestate = get;
+ continue;
+ } else if (_strnicmp(line, "--FILE--", 8)==0) {
+ parsestate = file;
+ continue;
+ } else if (_strnicmp(line, "--EXPECT--", 10)==0) {
+ parsestate = expect;
+ continue;
+ }
+ }
+ switch (parsestate) {
+ case test:
+ stripcrlf(line);
+ cTest = line;
+ break;
+ case skipif:
+ cSkipIf += line;
+ break;
+ case post:
+ cPost += line;
+ break;
+ case get:
+ cGet += line;
+ break;
+ case file:
+ fputs(line, ft);
+ break;
+ case expect:
+ fputs(line, fe);
+ break;
+ }
+ }
+
+ fclose(fp);
+ fclose(ft);
+ fclose(fe);
+
+ if (!cTest.IsEmpty()) {
+ IsapiFileList.Add(tn);
+ TestNames.Add(cTest);
+ IsapiGetData.Add(cGet);
+ IsapiPostData.Add(cPost);
+ IsapiMatchData.Add(en);
+ free(tn);
+ free(en);
+ return TRUE;
+ }
+ }
+ free(tn);
+ free(en);
+ return FALSE;
+}
+
+
+/**
+ * GetTestFiles
+ * Recurse through the path and subdirectories, parse each phpt file
+ */
+BOOL GetTestFiles(const char *path)
+{
+ // find all files .phpt under testpath\tests
+ char FindPath[MAX_PATH];
+ WIN32_FIND_DATA fd;
+ memset(&fd, 0, sizeof(WIN32_FIND_DATA));
+
+ _snprintf(FindPath, sizeof(FindPath)-1, "%s\\*.*", path);
+ HANDLE fh = FindFirstFile(FindPath, &fd);
+ if (fh != INVALID_HANDLE_VALUE) {
+ do {
+ if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
+ !strchr(fd.cFileName, '.')) {
+ // subdirectory, recurse into it
+ char NewFindPath[MAX_PATH];
+ _snprintf(NewFindPath, sizeof(NewFindPath)-1, "%s\\%s", path, fd.cFileName);
+ GetTestFiles(NewFindPath);
+ } else if (strstr(fd.cFileName, ".phpt")) {
+ // got test file, parse it now
+ if (ParseTestFile(path, fd.cFileName)) {
+ printf("Test File Added: %s\\%s\r\n", path, fd.cFileName);
+ }
+ }
+ memset(&fd, 0, sizeof(WIN32_FIND_DATA));
+ } while (FindNextFile(fh, &fd) != 0);
+ FindClose(fh);
+ }
+ return IsapiFileList.GetSize() > 0;
+}
+
+void DeleteTempFiles(const char *mask)
+{
+ char FindPath[MAX_PATH];
+ WIN32_FIND_DATA fd;
+ memset(&fd, 0, sizeof(WIN32_FIND_DATA));
+
+ _snprintf(FindPath, sizeof(FindPath)-1, "%s\\%s", temppath, mask);
+ HANDLE fh = FindFirstFile(FindPath, &fd);
+ if (fh != INVALID_HANDLE_VALUE) {
+ do {
+ char NewFindPath[MAX_PATH];
+ _snprintf(NewFindPath, sizeof(NewFindPath)-1, "%s\\%s", temppath, fd.cFileName);
+ DeleteFile(NewFindPath);
+ memset(&fd, 0, sizeof(WIN32_FIND_DATA));
+ } while (FindNextFile(fh, &fd) != 0);
+ FindClose(fh);
+ }
+}
+
+void DoTestFiles(const char *filelist, const char *environment)
+{
+ if (!GetTestFiles(filelist)) {
+ printf("No Files to test!\r\n");
+ return;
+ }
+
+ Results.SetSize(IsapiFileList.GetSize());
+
+ ReadGlobalEnvironment(environment);
+
+ DoThreads();
+
+ printf("\r\nRESULTS:\r\n");
+ // show results:
+ DWORD r = Results.GetSize();
+ for (DWORD i=0; i< r; i++) {
+ TResults result = Results.GetAt(i);
+ printf("%s\r\nOK: %d FAILED: %d\r\n", TestNames.GetAt(i), result.ok, result.bad);
+ }
+
+ // delete temp files
+ printf("Deleting Temp Files\r\n");
+ DeleteTempFiles("exp.*");
+ DeleteTempFiles("pht.*");
+ printf("Done\r\n");
+}
+
+#define OPTSTRING "m:f:d:h:t:i:"
+static void _usage(char *argv0)
+{
+ char *prog;
+
+ prog = strrchr(argv0, '/');
+ if (prog) {
+ prog++;
+ } else {
+ prog = "stresstest";
+ }
+
+ printf("Usage: %s -m <isapi.dll> -d|-l <file> [-t <numthreads>] [-i <numiterations>]\n"
+ " -m path to isapi dll\n"
+ " -d <directory> php directory (to run php test files).\n"
+ " -f <file> file containing list of files to run\n"
+ " -t number of threads to use (default=1)\n"
+ " -i number of iterations per thread (default=1)\n"
+ " -h This help\n", prog);
+}
+int main(int argc, char* argv[])
+{
+ LPVOID lpMsgBuf;
+ char *filelist=NULL, *environment=NULL, *module=NULL;
+ int c = NULL;
+ while ((c=ap_getopt(argc, argv, OPTSTRING))!=-1) {
+ switch (c) {
+ case 'd':
+ bUseTestFiles = TRUE;
+ filelist = strdup(ap_optarg);
+ break;
+ case 'f':
+ bUseTestFiles = FALSE;
+ filelist = strdup(ap_optarg);
+ break;
+ case 'e':
+ environment = strdup(ap_optarg);
+ break;
+ case 't':
+ numThreads = atoi(ap_optarg);
+ break;
+ case 'i':
+ iterations = atoi(ap_optarg);
+ break;
+ case 'm':
+ module = strdup(ap_optarg);
+ break;
+ case 'h':
+ _usage(argv[0]);
+ exit(0);
+ break;
+ }
+ }
+ if (!module || !filelist) {
+ _usage(argv[0]);
+ exit(0);
+ }
+
+ GetTempPath(sizeof(temppath), temppath);
+ hDll = LoadLibrary(module); // Load our DLL
+
+ if (!hDll) {
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL
+ );
+ fprintf(stderr,"Error: Dll 'php5isapi.dll' not found -%d\n%s\n", GetLastError(), lpMsgBuf);
+ free (module);
+ free(filelist);
+ LocalFree( lpMsgBuf );
+ return -1;
+ }
+
+ //
+ // Find the exported functions
+
+ IsapiGetExtensionVersion = (VersionProc)GetProcAddress(hDll,"GetExtensionVersion");
+ if (!IsapiGetExtensionVersion) {
+ fprintf(stderr,"Can't Get Extension Version %d\n", GetLastError());
+ free (module);
+ free(filelist);
+ return -1;
+ }
+ IsapiHttpExtensionProc = (HttpExtProc)GetProcAddress(hDll,"HttpExtensionProc");
+ if (!IsapiHttpExtensionProc) {
+ fprintf(stderr,"Can't Get Extension proc %d\n", GetLastError());
+ free (module);
+ free(filelist);
+ return -1;
+ }
+ TerminateExtensionProc = (TerminateProc) GetProcAddress(hDll,
+ "TerminateExtension");
+
+ // This should really check if the version information matches what we
+ // expect.
+ //
+ if (!IsapiGetExtensionVersion(&version_info) ) {
+ fprintf(stderr,"Fatal: GetExtensionVersion failed\n");
+ free (module);
+ free(filelist);
+ return -1;
+ }
+
+ if (bUseTestFiles) {
+ char TestPath[MAX_PATH];
+ if (filelist != NULL)
+ _snprintf(TestPath, sizeof(TestPath)-1, "%s\\tests", filelist);
+ else strcpy(TestPath, "tests");
+ DoTestFiles(TestPath, environment);
+ } else {
+ DoFileList(filelist, environment);
+ }
+
+ // cleanup
+ if (TerminateExtensionProc) TerminateExtensionProc(0);
+
+ // We should really free memory (e.g., from GetEnv), but we'll be dead
+ // soon enough
+
+ FreeLibrary(hDll);
+ free (module);
+ free(filelist);
+ return 0;
+}
+
+
+DWORD CALLBACK IsapiThread(void *p)
+{
+ DWORD filecount = IsapiFileList.GetSize();
+
+ for (DWORD j=0; j<iterations; j++) {
+ for (DWORD i=0; i<filecount; i++) {
+ // execute each file
+ CString testname = TestNames.GetAt(i);
+ BOOL ok = FALSE;
+ if (stress_main(IsapiFileList.GetAt(i),
+ IsapiGetData.GetAt(i),
+ IsapiPostData.GetAt(i),
+ IsapiMatchData.GetAt(i))) {
+ InterlockedIncrement(&Results[i].ok);
+ ok = TRUE;
+ } else {
+ InterlockedIncrement(&Results[i].bad);
+ ok = FALSE;
+ }
+
+ if (testname.IsEmpty()) {
+ printf("Thread %d File %s\n", GetCurrentThreadId(), IsapiFileList.GetAt(i));
+ } else {
+ printf("tid %d: %s %s\n", GetCurrentThreadId(), testname, ok?"OK":"FAIL");
+ }
+ Sleep(10);
+ }
+ }
+ printf("Thread ending...\n");
+ return 0;
+}
+
+/*
+ * ======================================================================= *
+ * In the startup of this program, we look at our executable name and *
+ * replace the ".EXE" with ".DLL" to find the ISAPI DLL we need to load. *
+ * This means that the executable need only be given the same "name" as *
+ * the DLL to load. There is no recompilation required. *
+ * ======================================================================= *
+*/
+BOOL stress_main(const char *filename,
+ const char *arg,
+ const char *postdata,
+ const char *matchdata)
+{
+
+ EXTENSION_CONTROL_BLOCK ECB;
+ DWORD rc;
+ TIsapiContext context;
+
+ // open output and input files
+ context.tid = GetCurrentThreadId();
+ CString fname;
+ fname.Format("%08X.out", context.tid);
+
+ context.out = CreateFile(fname, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH, NULL);
+ if (context.out==INVALID_HANDLE_VALUE) {
+ printf("failed to open output file %s\n", fname);
+ return 0;
+ }
+
+ // not using post files
+ context.in = INVALID_HANDLE_VALUE;
+
+ //
+ // Fill the ECB with the necessary information
+ //
+ if (!FillExtensionControlBlock(&ECB, &context) ) {
+ fprintf(stderr,"Fill Ext Block Failed\n");
+ return -1;
+ }
+
+ // check for command line argument,
+ // first arg = filename
+ // this is added for testing php from command line
+
+ context.env.RemoveAll();
+ context.env["PATH_TRANSLATED"]= filename;
+ context.env["SCRIPT_MAP"]= filename;
+ context.env["CONTENT_TYPE"]= "";
+ context.env["CONTENT_LENGTH"]= "";
+ context.env["QUERY_STRING"]= arg;
+ context.env["METHOD"]="GET";
+ context.env["PATH_INFO"] = "";
+ context.waitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ char buf[MAX_PATH];
+ if (postdata && *postdata !=0) {
+ ECB.cbAvailable = strlen(postdata);
+ ECB.cbTotalBytes = ECB.cbAvailable;
+ ECB.lpbData = (unsigned char *)postdata;
+ context.env["METHOD"]="POST";
+
+ _snprintf(buf, sizeof(buf)-1, "%d", ECB.cbTotalBytes);
+ context.env["CONTENT_LENGTH"]=buf;
+
+ context.env["CONTENT_TYPE"]="application/x-www-form-urlencoded";
+ }
+ ECB.lpszMethod = strdup(context.env["METHOD"]);
+ ECB.lpszPathTranslated = strdup(filename);
+ ECB.lpszQueryString = strdup(arg);
+ ECB.lpszPathInfo = strdup(context.env["PATH_INFO"]);
+
+
+ // Call the DLL
+ //
+ rc = IsapiHttpExtensionProc(&ECB);
+ if (rc == HSE_STATUS_PENDING) {
+ // We will exit in ServerSupportFunction
+ WaitForSingleObject(context.waitEvent, INFINITE);
+ }
+ CloseHandle(context.waitEvent);
+ //Sleep(75);
+ free(ECB.lpszPathTranslated);
+ free(ECB.lpszQueryString);
+ free(ECB.lpszMethod);
+ free(ECB.lpszPathInfo);
+
+ BOOL ok = TRUE;
+
+ if (context.out != INVALID_HANDLE_VALUE) CloseHandle(context.out);
+
+ // compare the output with the EXPECT section
+ if (matchdata && *matchdata != 0) {
+ ok = CompareFiles(fname, matchdata);
+ }
+
+ DeleteFile(fname);
+
+ return ok;
+
+}
+//
+// GetServerVariable() is how the DLL calls the main program to figure out
+// the environment variables it needs. This is a required function.
+//
+BOOL WINAPI GetServerVariable(HCONN hConn, LPSTR lpszVariableName,
+ LPVOID lpBuffer, LPDWORD lpdwSize){
+
+ DWORD rc;
+ CString value;
+ TIsapiContext *c = (TIsapiContext *)hConn;
+ if (!c) return FALSE;
+
+ if (IsapiEnvironment.Lookup(lpszVariableName, value)) {
+ rc = value.GetLength();
+ strncpy((char *)lpBuffer, value, *lpdwSize-1);
+ } else if (c->env.Lookup(lpszVariableName, value)) {
+ rc = value.GetLength();
+ strncpy((char *)lpBuffer, value, *lpdwSize-1);
+ } else
+ rc = GetEnvironmentVariable(lpszVariableName, (char *)lpBuffer, *lpdwSize) ;
+
+ if (!rc) { // return of 0 indicates the variable was not found
+ SetLastError(ERROR_NO_DATA);
+ return FALSE;
+ }
+
+ if (rc > *lpdwSize) {
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return FALSE;
+ }
+
+ *lpdwSize =rc + 1 ; // GetEnvironmentVariable does not count the NULL
+
+ return TRUE;
+
+}
+//
+// Again, we don't have an HCONN, so we simply wrap ReadClient() to
+// ReadFile on stdin. The semantics of the two functions are the same
+//
+BOOL WINAPI ReadClient(HCONN hConn, LPVOID lpBuffer, LPDWORD lpdwSize) {
+ TIsapiContext *c = (TIsapiContext *)hConn;
+ if (!c) return FALSE;
+
+ if (c->in != INVALID_HANDLE_VALUE)
+ return ReadFile(c->in, lpBuffer, (*lpdwSize), lpdwSize, NULL);
+
+ return FALSE;
+}
+//
+// ditto for WriteClient()
+//
+BOOL WINAPI WriteClient(HCONN hConn, LPVOID lpBuffer, LPDWORD lpdwSize,
+ DWORD dwReserved) {
+ TIsapiContext *c = (TIsapiContext *)hConn;
+ if (!c) return FALSE;
+
+ if (c->out != INVALID_HANDLE_VALUE)
+ return WriteFile(c->out, lpBuffer, *lpdwSize, lpdwSize, NULL);
+ return FALSE;
+}
+//
+// This is a special callback function used by the DLL for certain extra
+// functionality. Look at the API help for details.
+//
+BOOL WINAPI ServerSupportFunction(HCONN hConn, DWORD dwHSERequest,
+ LPVOID lpvBuffer, LPDWORD lpdwSize, LPDWORD lpdwDataType){
+
+ TIsapiContext *c = (TIsapiContext *)hConn;
+ char *lpszRespBuf;
+ char * temp = NULL;
+ DWORD dwBytes;
+ BOOL bRet = TRUE;
+
+ switch(dwHSERequest) {
+ case (HSE_REQ_SEND_RESPONSE_HEADER) :
+ lpszRespBuf = (char *)xmalloc(*lpdwSize);//+ 80);//accomodate our header
+ if (!lpszRespBuf)
+ return FALSE;
+ wsprintf(lpszRespBuf,"%s",
+ //HTTP_VER,
+
+ /* Default response is 200 Ok */
+
+ //lpvBuffer?lpvBuffer:"200 Ok",
+
+ /* Create a string for the time. */
+ //temp=MakeDateStr(),
+
+ //SERVER_VERSION,
+
+ /* If this exists, it is a pointer to a data buffer to
+ be sent. */
+ lpdwDataType?(char *)lpdwDataType:NULL);
+
+ if (temp) xfree(temp);
+
+ dwBytes = strlen(lpszRespBuf);
+ bRet = WriteClient(0, lpszRespBuf, &dwBytes, 0);
+ xfree(lpszRespBuf);
+
+ break;
+ //
+ // A real server would do cleanup here
+ case (HSE_REQ_DONE_WITH_SESSION):
+ SetEvent(c->waitEvent);
+ //ExitThread(0);
+ break;
+
+ //
+ // This sends a redirect (temporary) to the client.
+ // The header construction is similar to RESPONSE_HEADER above.
+ //
+ case (HSE_REQ_SEND_URL_REDIRECT_RESP):
+ lpszRespBuf = (char *)xmalloc(*lpdwSize +80) ;
+ if (!lpszRespBuf)
+ return FALSE;
+ wsprintf(lpszRespBuf,"%s %s %s\r\n",
+ HTTP_VER,
+ "302 Moved Temporarily",
+ (lpdwSize > 0)?lpvBuffer:0);
+ xfree(temp);
+ dwBytes = strlen(lpszRespBuf);
+ bRet = WriteClient(0, lpszRespBuf, &dwBytes, 0);
+ xfree(lpszRespBuf);
+ break;
+ default:
+ return FALSE;
+ break;
+ }
+ return bRet;
+
+}
+//
+// Makes a string of the date and time from GetSystemTime().
+// This is in UTC, as required by the HTTP spec.`
+//
+char * MakeDateStr(void){
+ SYSTEMTIME systime;
+ char *szDate= (char *)xmalloc(64);
+
+ char * DaysofWeek[] = {"Sun","Mon","Tue","Wed","Thurs","Fri","Sat"};
+ char * Months[] = {"NULL","Jan","Feb","Mar","Apr","May","Jun","Jul","Aug",
+ "Sep","Oct","Nov","Dec"};
+
+ GetSystemTime(&systime);
+
+ wsprintf(szDate,"%s, %d %s %d %d:%d.%d", DaysofWeek[systime.wDayOfWeek],
+ systime.wDay,
+ Months[systime.wMonth],
+ systime.wYear,
+ systime.wHour, systime.wMinute,
+ systime.wSecond );
+
+ return szDate;
+}
+//
+// Fill the ECB up
+//
+BOOL WINAPI FillExtensionControlBlock(EXTENSION_CONTROL_BLOCK *ECB, TIsapiContext *context) {
+
+ char * temp;
+ ECB->cbSize = sizeof(EXTENSION_CONTROL_BLOCK);
+ ECB->dwVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
+ ECB->ConnID = (void *)context;
+ //
+ // Pointers to the functions the DLL will call.
+ //
+ ECB->GetServerVariable = GetServerVariable;
+ ECB->ReadClient = ReadClient;
+ ECB->WriteClient = WriteClient;
+ ECB->ServerSupportFunction = ServerSupportFunction;
+
+ //
+ // Fill in the standard CGI environment variables
+ //
+ ECB->lpszMethod = GetEnv("REQUEST_METHOD");
+ if (!ECB->lpszMethod) ECB->lpszMethod = "GET";
+
+ ECB->lpszQueryString = GetEnv("QUERY_STRING");
+ ECB->lpszPathInfo = GetEnv("PATH_INFO");
+ ECB->lpszPathTranslated = GetEnv("PATH_TRANSLATED");
+ ECB->cbTotalBytes=( (temp=GetEnv("CONTENT_LENGTH")) ? (atoi(temp)): 0);
+ ECB->cbAvailable = 0;
+ ECB->lpbData = (unsigned char *)"";
+ ECB->lpszContentType = GetEnv("CONTENT_TYPE");
+ return TRUE;
+
+}
+
+//
+// Works like _getenv(), but uses win32 functions instead.
+//
+char *GetEnv(LPSTR lpszEnvVar)
+{
+
+ char *var, dummy;
+ DWORD dwLen;
+
+ if (!lpszEnvVar)
+ return "";
+
+ dwLen =GetEnvironmentVariable(lpszEnvVar, &dummy, 1);
+
+ if (dwLen == 0)
+ return "";
+
+ var = (char *)xmalloc(dwLen);
+ if (!var)
+ return "";
+ (void)GetEnvironmentVariable(lpszEnvVar, var, dwLen);
+
+ return var;
+}
diff --git a/sapi/isapi/stresstest/stresstest.dsp b/sapi/isapi/stresstest/stresstest.dsp
new file mode 100644
index 0000000..fb82303
--- /dev/null
+++ b/sapi/isapi/stresstest/stresstest.dsp
@@ -0,0 +1,108 @@
+# Microsoft Developer Studio Project File - Name="stresstest" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=stresstest - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "stresstest.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "stresstest.mak" CFG="stresstest - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "stresstest - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "stresstest - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "stresstest - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 2
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "_AFXDLL" /FD /c
+# SUBTRACT CPP /YX /Yc /Yu
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 /nologo /subsystem:console /machine:I386
+
+!ELSEIF "$(CFG)" == "stresstest - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 2
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "c:\php-fcgi"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "_AFXDLL" /FD /GZ /c
+# SUBTRACT CPP /YX /Yc /Yu
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "stresstest - Win32 Release"
+# Name "stresstest - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\getopt.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\getopt.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\stresstest.cpp
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\notes.txt
+# End Source File
+# End Target
+# End Project