--- /dev/null
+/*
+ * ======================================================================= *
+ * 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>
+
+// 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
+//
+
+typedef struct _TIsapiContext {
+ HANDLE in;
+ HANDLE out;
+ DWORD tid;
+} 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 *);
+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;
+HSE_VERSION_INFO version_info;
+}
+
+char * MakeDateStr(VOID);
+char * GetEnv(char *);
+
+
+#define NUM_THREADS 1
+#define ITERATIONS 1
+HANDLE terminate[NUM_THREADS];
+HANDLE StartNow;
+// quick and dirty environment
+CMapStringToString IsapiEnvironment;
+CStringArray IsapiFileList;
+CStringArray IsapiArgList;
+
+
+DWORD CALLBACK IsapiThread(void *);
+int stress_main(const char *filename, const char *arg);
+
+
+
+int main(int argc, char* argv[]) {
+ LPVOID lpMsgBuf;
+ char *filelist, *environment;
+
+ if (argc < 2) {
+ printf("Usage: stress filelist.txt env.txt\r\n");
+ return 0;
+ }
+ filelist = argv[1];
+ environment = argv[2];
+
+ hDll = LoadLibrary("php4isapi.dll"); // 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 'php4isapi.dll' not found -%d\n%s\n", GetLastError(),lpMsgBuf);
+ 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());
+ return -1;
+ }
+ IsapiHttpExtensionProc = (HttpExtProc)GetProcAddress(hDll,"HttpExtensionProc");
+ if (!IsapiHttpExtensionProc) {
+ fprintf(stderr,"Can't Get Extension proc %d\n",GetLastError());
+ return -1;
+ }
+
+ // This should really check if the version information matches what we
+ // expect.
+ //
+ __try {
+ if (!IsapiGetExtensionVersion(&version_info) ) {
+ fprintf(stderr,"Fatal: GetExtensionVersion failed\n");
+ return -1;
+ }
+ }
+ __except(1) {
+ return -1;
+ }
+
+ // read config files
+ 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.
+ char *p = strchr(line, ' ');
+ if (p) {
+ *p = 0;
+ // get file
+ IsapiFileList.Add(line);
+ IsapiArgList.Add(p+1);
+ } else {
+ // just a filename is all
+ IsapiFileList.Add(line);
+ IsapiArgList.Add("");
+ }
+ i++;
+ }
+ fclose(fp);
+
+ if (environment) {
+ fp = fopen(environment, "r");
+ 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);
+ }
+ }
+
+ printf("Starting Threads...\r\n");
+ // loop creating threads
+ for (i=0; i< NUM_THREADS; i++) {
+ terminate[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ DWORD tid;
+ if (CreateThread(NULL, 0, IsapiThread, &terminate[i], 0, &tid)==NULL){
+ SetEvent(terminate[i]);
+ }
+ }
+ // wait for threads to finish
+ WaitForMultipleObjects(NUM_THREADS, terminate, TRUE, INFINITE);
+
+ // cleanup
+
+ // We should really free memory (e.g., from GetEnv), but we'll be dead
+ // soon enough
+
+ FreeLibrary(hDll);
+ return 0;
+}
+
+
+DWORD CALLBACK IsapiThread(void *p)
+{
+ HANDLE *terminate = (HANDLE *)p;
+ DWORD filecount = IsapiFileList.GetSize();
+ for (DWORD j=0; j<ITERATIONS; j++) {
+ for (DWORD i=0; i<filecount; i++) {
+ // execute each file
+ printf("Thread %d File %s\r\n", GetCurrentThreadId(), IsapiFileList.GetAt(i));
+ stress_main(IsapiFileList.GetAt(i), IsapiArgList.GetAt(i));
+ Sleep(1);
+ }
+ }
+ SetEvent(*terminate);
+ 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. *
+ * ======================================================================= *
+*/
+int stress_main(const char *filename, const char *arg) {
+
+ 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, 0, NULL, CREATE_ALWAYS, 0, NULL);
+ if (context.out==INVALID_HANDLE_VALUE) {
+ printf("failed to open output file %s\n", fname);
+ return 0;
+ }
+ if (arg) context.in = CreateFile(arg, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
+ else 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
+
+ ECB.lpszPathTranslated = strdup(filename);
+
+ // Call the DLL
+ //
+ rc = IsapiHttpExtensionProc(&ECB);
+
+ free (ECB.lpszPathTranslated);
+
+ if (context.out) CloseHandle(context.out);
+ if (context.in) CloseHandle(context.in);
+
+ //if (rc == HSE_STATUS_PENDING) // We will exit in ServerSupportFunction
+ // Sleep(INFINITE);
+
+ return 0;
+
+}
+//
+// 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;
+
+ if (IsapiEnvironment.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) 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) 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){
+
+ char *lpszRespBuf;
+ char * temp = NULL;
+ DWORD dwBytes;
+ BOOL bRet;
+
+ 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):
+ //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");
+ 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;
+}
--- /dev/null
+# Microsoft Developer Studio Project File - Name="stresstest" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** DO NOT EDIT **\r
+\r
+# TARGTYPE "Win32 (x86) Console Application" 0x0103\r
+\r
+CFG=stresstest - Win32 Debug\r
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r
+!MESSAGE use the Export Makefile command and run\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "stresstest.mak".\r
+!MESSAGE \r
+!MESSAGE You can specify a configuration when running NMAKE\r
+!MESSAGE by defining the macro CFG on the command line. For example:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "stresstest.mak" CFG="stresstest - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Possible choices for configuration are:\r
+!MESSAGE \r
+!MESSAGE "stresstest - Win32 Release" (based on "Win32 (x86) Console Application")\r
+!MESSAGE "stresstest - Win32 Debug" (based on "Win32 (x86) Console Application")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+RSC=rc.exe\r
+\r
+!IF "$(CFG)" == "stresstest - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "Release"\r
+# PROP BASE Intermediate_Dir "Release"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 2\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "Release"\r
+# PROP Intermediate_Dir "Release"\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c\r
+# ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "_AFXDLL" /FD /c\r
+# SUBTRACT CPP /YX /Yc /Yu\r
+# ADD BASE RSC /l 0x409 /d "NDEBUG"\r
+# ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# 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\r
+# ADD LINK32 /nologo /subsystem:console /machine:I386\r
+\r
+!ELSEIF "$(CFG)" == "stresstest - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "Debug"\r
+# PROP BASE Intermediate_Dir "Debug"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 2\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "Debug"\r
+# PROP Intermediate_Dir "Debug"\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c\r
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "_AFXDLL" /FD /GZ /c\r
+# SUBTRACT CPP /YX /Yc /Yu\r
+# ADD BASE RSC /l 0x409 /d "_DEBUG"\r
+# ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# 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\r
+# ADD LINK32 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "stresstest - Win32 Release"\r
+# Name "stresstest - Win32 Debug"\r
+# Begin Group "Source Files"\r
+\r
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"\r
+# Begin Source File\r
+\r
+SOURCE=.\stresstest.cpp\r
+# End Source File\r
+# End Group\r
+# Begin Group "Header Files"\r
+\r
+# PROP Default_Filter "h;hpp;hxx;hm;inl"\r
+# End Group\r
+# Begin Group "Resource Files"\r
+\r
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"\r
+# End Group\r
+# Begin Source File\r
+\r
+SOURCE=.\ReadMe.txt\r
+# End Source File\r
+# End Target\r
+# End Project\r