summaryrefslogtreecommitdiff
path: root/sapi/isapi/stresstest
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/isapi/stresstest
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/isapi/stresstest')
-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
5 files changed, 1287 insertions, 0 deletions
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