summaryrefslogtreecommitdiff
path: root/sapi/isapi/stresstest/stresstest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sapi/isapi/stresstest/stresstest.cpp')
-rw-r--r--sapi/isapi/stresstest/stresstest.cpp936
1 files changed, 936 insertions, 0 deletions
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;
+}