Back to index

php5  5.3.10
stresstest.cpp
Go to the documentation of this file.
00001 /*
00002  * ======================================================================= *
00003  * File: stress .c                                                         *
00004  * stress tester for isapi dll's                                           *
00005  * based on cgiwrap                                                        *
00006  * ======================================================================= *
00007  *
00008 */
00009 #define WIN32_LEAN_AND_MEAN
00010 #include <afx.h>
00011 #include <afxtempl.h>
00012 #include <winbase.h>
00013 #include <winerror.h>
00014 #include <httpext.h>
00015 #include <stdio.h>
00016 #include <stdlib.h>
00017 #include "getopt.h"
00018 
00019 // These are things that go out in the Response Header
00020 //
00021 #define HTTP_VER     "HTTP/1.0"
00022 #define SERVER_VERSION "Http-Srv-Beta2/1.0"
00023 
00024 //
00025 // Simple wrappers for the heap APIS
00026 //
00027 #define xmalloc(s) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (s))
00028 #define xfree(s)   HeapFree(GetProcessHeap(), 0, (s))
00029 
00030 //
00031 // The mandatory exports from the ISAPI DLL
00032 //
00033 DWORD numThreads = 1;
00034 DWORD iterations = 1;
00035 
00036 HANDLE StartNow;
00037 // quick and dirty environment
00038 typedef CMapStringToString TEnvironment;
00039 TEnvironment IsapiEnvironment;
00040 
00041 typedef struct _TResults {
00042        LONG ok;
00043        LONG bad;
00044 } TResults;
00045 
00046 CStringArray IsapiFileList;  // list of filenames
00047 CStringArray TestNames;      // --TEST--
00048 CStringArray IsapiGetData;   // --GET--
00049 CStringArray IsapiPostData;  // --POST--
00050 CStringArray IsapiMatchData; // --EXPECT--
00051 CArray<TResults, TResults> Results;
00052 
00053 typedef struct _TIsapiContext {
00054        HANDLE in;
00055        HANDLE out;
00056        DWORD tid;
00057        TEnvironment env;
00058        HANDLE waitEvent;
00059 } TIsapiContext;
00060 
00061 //
00062 // Prototypes of the functions this sample implements
00063 //
00064 extern "C" {
00065 HINSTANCE hDll;
00066 typedef BOOL (WINAPI *VersionProc)(HSE_VERSION_INFO *) ;
00067 typedef DWORD (WINAPI *HttpExtProc)(EXTENSION_CONTROL_BLOCK *);
00068 typedef BOOL (WINAPI *TerminateProc) (DWORD);
00069 BOOL WINAPI FillExtensionControlBlock(EXTENSION_CONTROL_BLOCK *, TIsapiContext *) ;
00070 BOOL WINAPI GetServerVariable(HCONN, LPSTR, LPVOID, LPDWORD );
00071 BOOL WINAPI ReadClient(HCONN, LPVOID, LPDWORD);
00072 BOOL WINAPI WriteClient(HCONN, LPVOID, LPDWORD, DWORD);
00073 BOOL WINAPI ServerSupportFunction(HCONN, DWORD, LPVOID, LPDWORD, LPDWORD);
00074 VersionProc IsapiGetExtensionVersion;
00075 HttpExtProc IsapiHttpExtensionProc;
00076 TerminateProc TerminateExtensionProc;
00077 HSE_VERSION_INFO version_info;
00078 }
00079 
00080 char * MakeDateStr(VOID);
00081 char * GetEnv(char *);
00082 
00083 
00084 
00085 
00086 DWORD CALLBACK IsapiThread(void *);
00087 int stress_main(const char *filename, 
00088                             const char *arg, 
00089                             const char *postfile, 
00090                             const char *matchdata);
00091 
00092 
00093 
00094 BOOL bUseTestFiles = FALSE;
00095 char temppath[MAX_PATH];
00096 
00097 void stripcrlf(char *line)
00098 {
00099        DWORD l = strlen(line)-1;
00100        if (line[l]==10 || line[l]==13) line[l]=0;
00101        l = strlen(line)-1;
00102        if (line[l]==10 || line[l]==13) line[l]=0;
00103 }
00104 
00105 #define COMPARE_BUF_SIZE    1024
00106 
00107 BOOL CompareFiles(const char*f1, const char*f2)
00108 {
00109        FILE *fp1, *fp2;
00110        bool retval;
00111        char buf1[COMPARE_BUF_SIZE], buf2[COMPARE_BUF_SIZE];
00112        int length1, length2;
00113 
00114        if ((fp1=fopen(f1, "r"))==NULL) {
00115               return FALSE;
00116        }
00117 
00118        if ((fp2=fopen(f2, "r"))==NULL) {
00119               fclose(fp1);
00120               return FALSE;
00121        }
00122 
00123        retval = TRUE; // success oriented
00124        while (true) {
00125               length1 = fread(buf1, 1, sizeof(buf1), fp1);
00126               length2 = fread(buf2, 1, sizeof(buf2), fp2);
00127 
00128               // check for end of file
00129               if (feof(fp1)) {
00130                      if (!feof(fp2)) {
00131                             retval = FALSE;
00132                      }
00133                      break;
00134               } else if (feof(fp2)) {
00135                      if (!feof(fp1)) {
00136                             retval = FALSE;
00137                      }
00138                      break;
00139               }
00140 
00141               // compare data
00142               if (length1!=length2
00143                      || memcmp(buf1, buf2, length1)!=0) {
00144                      retval = FALSE;
00145                      break;
00146               }
00147        }
00148        fclose(fp1);
00149        fclose(fp2);
00150 
00151        return retval;
00152 }
00153 
00154 
00155 BOOL CompareStringWithFile(const char *filename, const char *str, unsigned int str_length)
00156 {
00157        FILE *fp;
00158        bool retval;
00159        char buf[COMPARE_BUF_SIZE];
00160        unsigned int offset=0, readbytes;
00161        fprintf(stderr, "test %s\n",filename);
00162        if ((fp=fopen(filename, "rb"))==NULL) {
00163               fprintf(stderr, "Error opening %s\n",filename);
00164               return FALSE;
00165        }
00166 
00167        retval = TRUE; // success oriented
00168        while (true) {
00169               readbytes = fread(buf, 1, sizeof(buf), fp);
00170 
00171               // check for end of file
00172 
00173               if (offset+readbytes > str_length
00174                      || memcmp(buf, str+offset, readbytes)!=NULL) {
00175                      fprintf(stderr, "File missmatch %s\n",filename);
00176                      retval = FALSE;
00177                      break;
00178               }
00179               if (feof(fp)) {
00180                      if (!retval) fprintf(stderr, "File zero length %s\n",filename);
00181                      break;
00182               }
00183        }
00184        fclose(fp);
00185 
00186        return retval;
00187 }
00188 
00189 
00190 BOOL ReadGlobalEnvironment(const char *environment)
00191 {
00192        if (environment) {
00193        FILE *fp = fopen(environment, "r");
00194        DWORD i=0;
00195        if (fp) {
00196               char line[2048];
00197               while (fgets(line, sizeof(line)-1, fp)) {
00198                      // file.php arg1 arg2 etc.
00199                      char *p = strchr(line, '=');
00200                      if (p) {
00201                             *p=0;
00202                             IsapiEnvironment[line]=p+1;
00203                      }
00204               }
00205               fclose(fp);
00206               return IsapiEnvironment.GetCount() > 0;
00207        }
00208        }
00209        return FALSE;
00210 }
00211 
00212 BOOL ReadFileList(const char *filelist)
00213 {
00214        FILE *fp = fopen(filelist, "r");
00215        if (!fp) {
00216               printf("Unable to open %s\r\n", filelist);
00217        }
00218        char line[2048];
00219        int i=0;
00220        while (fgets(line, sizeof(line)-1, fp)) {
00221               // file.php arg1 arg2 etc.
00222               stripcrlf(line);
00223               if (strlen(line)>3) {
00224                      char *p = strchr(line, ' ');
00225                      if (p) {
00226                             *p = 0;
00227                             // get file
00228 
00229                             IsapiFileList.Add(line);
00230                             IsapiGetData.Add(p+1);
00231                      } else {
00232                             // just a filename is all
00233                             IsapiFileList.Add(line);
00234                             IsapiGetData.Add("");
00235                      }
00236               }
00237 
00238               // future use
00239               IsapiPostData.Add("");
00240               IsapiMatchData.Add("");
00241               TestNames.Add("");
00242 
00243               i++;
00244        }
00245        Results.SetSize(TestNames.GetSize());
00246 
00247        fclose(fp);
00248        return IsapiFileList.GetSize() > 0;
00249 }
00250 
00251 void DoThreads() {
00252 
00253        if (IsapiFileList.GetSize() == 0) {
00254               printf("No Files to test\n");
00255               return;
00256        }
00257 
00258        printf("Starting Threads...\n");
00259        // loop creating threads
00260        DWORD tid;
00261        HANDLE *threads = new HANDLE[numThreads];
00262        DWORD i;
00263        for (i=0; i< numThreads; i++) {
00264               threads[i]=CreateThread(NULL, 0, IsapiThread, NULL, CREATE_SUSPENDED, &tid);
00265        }
00266        for (i=0; i< numThreads; i++) {
00267               if (threads[i]) ResumeThread(threads[i]);
00268        }
00269        // wait for threads to finish
00270        WaitForMultipleObjects(numThreads, threads, TRUE, INFINITE);
00271        for (i=0; i< numThreads; i++) {
00272               CloseHandle(threads[i]);
00273        }
00274        delete [] threads;
00275 }
00276 
00277 void DoFileList(const char *filelist, const char *environment)
00278 {
00279        // read config files
00280 
00281        if (!ReadFileList(filelist)) {
00282               printf("No Files to test!\r\n");
00283               return;
00284        }
00285 
00286        ReadGlobalEnvironment(environment);
00287 
00288        DoThreads();
00289 }
00290 
00291 
00296 BOOL ParseTestFile(const char *path, const char *fn)
00297 {
00298        // parse the test file
00299        char filename[MAX_PATH];
00300        _snprintf(filename, sizeof(filename)-1, "%s\\%s", path, fn);
00301        char line[1024];
00302        memset(line, 0, sizeof(line));
00303        CString cTest, cSkipIf, cPost, cGet, cFile, cExpect;
00304        printf("Reading %s\r\n", filename);
00305 
00306        enum state {none, test, skipif, post, get, file, expect} parsestate = none;
00307 
00308        FILE *fp = fopen(filename, "rb");
00309        char *tn = _tempnam(temppath,"pht.");
00310        char *en = _tempnam(temppath,"exp.");
00311        FILE *ft = fopen(tn, "wb+");
00312        FILE *fe = fopen(en, "wb+");
00313        if (fp && ft && fe) {
00314               while (fgets(line, sizeof(line)-1, fp)) {
00315                      if (line[0]=='-') {
00316                             if (_strnicmp(line, "--TEST--", 8)==0) {
00317                                    parsestate = test;
00318                                    continue;
00319                             } else if (_strnicmp(line, "--SKIPIF--", 10)==0) {
00320                                    parsestate = skipif;
00321                                    continue;
00322                             } else if (_strnicmp(line, "--POST--", 8)==0) {
00323                                    parsestate = post;
00324                                    continue;
00325                             } else if (_strnicmp(line, "--GET--", 7)==0) {
00326                                    parsestate = get;
00327                                    continue;
00328                             } else if (_strnicmp(line, "--FILE--", 8)==0) {
00329                                    parsestate = file;
00330                                    continue;
00331                             } else if (_strnicmp(line, "--EXPECT--", 10)==0) {
00332                                    parsestate = expect;
00333                                    continue;
00334                             }
00335                      }
00336                      switch (parsestate) {
00337                      case test:
00338                             stripcrlf(line);
00339                             cTest = line;
00340                             break;
00341                      case skipif:
00342                             cSkipIf += line;
00343                             break;
00344                      case post:
00345                             cPost += line;
00346                             break;
00347                      case get:
00348                             cGet += line;
00349                             break;
00350                      case file:
00351                             fputs(line, ft);
00352                             break;
00353                      case expect:
00354                             fputs(line, fe);
00355                             break;
00356                      }
00357               }             
00358 
00359               fclose(fp);
00360               fclose(ft);
00361               fclose(fe);
00362 
00363               if (!cTest.IsEmpty()) {
00364                      IsapiFileList.Add(tn);
00365                      TestNames.Add(cTest);
00366                      IsapiGetData.Add(cGet);
00367                      IsapiPostData.Add(cPost);
00368                      IsapiMatchData.Add(en);
00369                      free(tn);
00370                      free(en);
00371                      return TRUE;
00372               }
00373        }
00374        free(tn);
00375        free(en);
00376        return FALSE;
00377 }
00378 
00379 
00384 BOOL GetTestFiles(const char *path)
00385 {
00386        // find all files .phpt under testpath\tests
00387        char FindPath[MAX_PATH];
00388        WIN32_FIND_DATA fd;
00389        memset(&fd, 0, sizeof(WIN32_FIND_DATA));
00390 
00391        _snprintf(FindPath, sizeof(FindPath)-1, "%s\\*.*", path);
00392        HANDLE fh = FindFirstFile(FindPath, &fd);
00393        if (fh != INVALID_HANDLE_VALUE) {
00394               do {
00395                      if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
00396                             !strchr(fd.cFileName, '.')) {
00397                             // subdirectory, recurse into it
00398                             char NewFindPath[MAX_PATH];
00399                             _snprintf(NewFindPath, sizeof(NewFindPath)-1, "%s\\%s", path, fd.cFileName);
00400                             GetTestFiles(NewFindPath);
00401                      } else if (strstr(fd.cFileName, ".phpt")) {
00402                             // got test file, parse it now
00403                             if (ParseTestFile(path, fd.cFileName)) {
00404                                    printf("Test File Added: %s\\%s\r\n", path, fd.cFileName);
00405                             }
00406                      }
00407                      memset(&fd, 0, sizeof(WIN32_FIND_DATA));
00408               } while (FindNextFile(fh, &fd) != 0);
00409               FindClose(fh);
00410        }
00411        return IsapiFileList.GetSize() > 0;
00412 }
00413 
00414 void DeleteTempFiles(const char *mask)
00415 {
00416        char FindPath[MAX_PATH];
00417        WIN32_FIND_DATA fd;
00418        memset(&fd, 0, sizeof(WIN32_FIND_DATA));
00419 
00420        _snprintf(FindPath, sizeof(FindPath)-1, "%s\\%s", temppath, mask);
00421        HANDLE fh = FindFirstFile(FindPath, &fd);
00422        if (fh != INVALID_HANDLE_VALUE) {
00423               do {
00424                      char NewFindPath[MAX_PATH];
00425                      _snprintf(NewFindPath, sizeof(NewFindPath)-1, "%s\\%s", temppath, fd.cFileName);
00426                      DeleteFile(NewFindPath);
00427                      memset(&fd, 0, sizeof(WIN32_FIND_DATA));
00428               } while (FindNextFile(fh, &fd) != 0);
00429               FindClose(fh);
00430        }
00431 }
00432 
00433 void DoTestFiles(const char *filelist, const char *environment)
00434 {
00435        if (!GetTestFiles(filelist)) {
00436               printf("No Files to test!\r\n");
00437               return;
00438        }
00439 
00440        Results.SetSize(IsapiFileList.GetSize());
00441 
00442        ReadGlobalEnvironment(environment);
00443 
00444        DoThreads();
00445 
00446        printf("\r\nRESULTS:\r\n");
00447        // show results:
00448        DWORD r = Results.GetSize();
00449        for (DWORD i=0; i< r; i++) {
00450               TResults result = Results.GetAt(i);
00451               printf("%s\r\nOK: %d FAILED: %d\r\n", TestNames.GetAt(i), result.ok, result.bad);
00452        }
00453 
00454        // delete temp files
00455        printf("Deleting Temp Files\r\n");
00456        DeleteTempFiles("exp.*");
00457        DeleteTempFiles("pht.*");
00458        printf("Done\r\n");
00459 }
00460 
00461 #define OPTSTRING "m:f:d:h:t:i:"
00462 static void _usage(char *argv0)
00463 {
00464        char *prog;
00465 
00466        prog = strrchr(argv0, '/');
00467        if (prog) {
00468               prog++;
00469        } else {
00470               prog = "stresstest";
00471        }
00472 
00473        printf("Usage: %s -m <isapi.dll> -d|-l <file> [-t <numthreads>] [-i <numiterations>]\n"
00474                             "  -m             path to isapi dll\n"
00475                             "  -d <directory> php directory (to run php test files).\n"
00476                             "  -f <file>      file containing list of files to run\n"
00477                             "  -t             number of threads to use (default=1)\n"
00478                 "  -i             number of iterations per thread (default=1)\n"
00479                             "  -h             This help\n", prog);
00480 }
00481 int main(int argc, char* argv[])
00482 {
00483        LPVOID lpMsgBuf;
00484        char *filelist=NULL, *environment=NULL, *module=NULL;
00485        int c = NULL;
00486        while ((c=ap_getopt(argc, argv, OPTSTRING))!=-1) {
00487               switch (c) {
00488                      case 'd':
00489                             bUseTestFiles = TRUE;
00490                             filelist = strdup(ap_optarg);
00491                             break;
00492                      case 'f':
00493                             bUseTestFiles = FALSE;
00494                             filelist = strdup(ap_optarg);
00495                             break;
00496                      case 'e':
00497                             environment = strdup(ap_optarg);
00498                             break;
00499                      case 't':
00500                             numThreads = atoi(ap_optarg);
00501                             break;
00502                      case 'i':
00503                             iterations = atoi(ap_optarg);
00504                             break;
00505                      case 'm':
00506                             module = strdup(ap_optarg);
00507                             break;
00508                      case 'h':
00509                             _usage(argv[0]);
00510                             exit(0);
00511                             break;
00512               }
00513        }
00514        if (!module || !filelist) {
00515               _usage(argv[0]);
00516               exit(0);
00517        }
00518 
00519        GetTempPath(sizeof(temppath), temppath);
00520        hDll = LoadLibrary(module); // Load our DLL
00521 
00522        if (!hDll) {
00523               FormatMessage( 
00524                  FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
00525                   NULL,
00526                   GetLastError(),
00527                   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
00528                   (LPTSTR) &lpMsgBuf,
00529                   0,
00530                   NULL 
00531               );
00532               fprintf(stderr,"Error: Dll 'php5isapi.dll' not found -%d\n%s\n", GetLastError(), lpMsgBuf);
00533               free (module);
00534               free(filelist);
00535               LocalFree( lpMsgBuf );
00536               return -1;
00537        }
00538 
00539        //
00540        // Find the exported functions
00541 
00542        IsapiGetExtensionVersion = (VersionProc)GetProcAddress(hDll,"GetExtensionVersion");
00543        if (!IsapiGetExtensionVersion) {
00544               fprintf(stderr,"Can't Get Extension Version %d\n", GetLastError());
00545               free (module);
00546               free(filelist);
00547               return -1;
00548        }
00549        IsapiHttpExtensionProc = (HttpExtProc)GetProcAddress(hDll,"HttpExtensionProc");
00550        if (!IsapiHttpExtensionProc) {
00551               fprintf(stderr,"Can't Get Extension proc %d\n", GetLastError());
00552               free (module);
00553               free(filelist);
00554               return -1;
00555        }
00556        TerminateExtensionProc = (TerminateProc) GetProcAddress(hDll, 
00557                                           "TerminateExtension");
00558 
00559        // This should really check if the version information matches what we
00560        // expect.
00561        //
00562        if (!IsapiGetExtensionVersion(&version_info) ) {
00563               fprintf(stderr,"Fatal: GetExtensionVersion failed\n");
00564               free (module);
00565               free(filelist);
00566               return -1;
00567        }
00568 
00569        if (bUseTestFiles) {
00570               char TestPath[MAX_PATH];
00571               if (filelist != NULL) 
00572                      _snprintf(TestPath, sizeof(TestPath)-1, "%s\\tests", filelist);
00573               else strcpy(TestPath, "tests");
00574               DoTestFiles(TestPath, environment);
00575        } else {
00576               DoFileList(filelist, environment);
00577        }
00578 
00579        // cleanup
00580        if (TerminateExtensionProc) TerminateExtensionProc(0);
00581 
00582        // We should really free memory (e.g., from GetEnv), but we'll be dead
00583        // soon enough
00584 
00585        FreeLibrary(hDll);
00586        free (module);
00587        free(filelist);
00588        return 0;
00589 }
00590 
00591 
00592 DWORD CALLBACK IsapiThread(void *p)
00593 {
00594        DWORD filecount = IsapiFileList.GetSize();
00595 
00596        for (DWORD j=0; j<iterations; j++) {
00597               for (DWORD i=0; i<filecount; i++) {
00598                      // execute each file
00599                      CString testname = TestNames.GetAt(i);
00600                      BOOL ok = FALSE;
00601                      if (stress_main(IsapiFileList.GetAt(i), 
00602                                           IsapiGetData.GetAt(i), 
00603                                           IsapiPostData.GetAt(i),
00604                                           IsapiMatchData.GetAt(i))) {
00605                             InterlockedIncrement(&Results[i].ok);
00606                             ok = TRUE;
00607                      } else {
00608                             InterlockedIncrement(&Results[i].bad);
00609                             ok = FALSE;
00610                      }
00611 
00612                      if (testname.IsEmpty()) {
00613                             printf("Thread %d File %s\n", GetCurrentThreadId(), IsapiFileList.GetAt(i));
00614                      } else {
00615                             printf("tid %d: %s %s\n", GetCurrentThreadId(), testname, ok?"OK":"FAIL");
00616                      }
00617                      Sleep(10);
00618               }
00619        }
00620        printf("Thread ending...\n");
00621        return 0;
00622 }
00623 
00624 /*
00625  * ======================================================================= *
00626  * In the startup of this program, we look at our executable name and      *
00627  * replace the ".EXE" with ".DLL" to find the ISAPI DLL we need to load.   *
00628  * This means that the executable need only be given the same "name" as    *
00629  * the DLL to load. There is no recompilation required.                    *
00630  * ======================================================================= *
00631 */
00632 BOOL stress_main(const char *filename, 
00633                             const char *arg, 
00634                             const char *postdata,
00635                             const char *matchdata) 
00636 {
00637 
00638        EXTENSION_CONTROL_BLOCK ECB;
00639        DWORD rc;
00640        TIsapiContext context;
00641 
00642        // open output and input files
00643        context.tid = GetCurrentThreadId();
00644        CString fname;
00645        fname.Format("%08X.out", context.tid);
00646 
00647        context.out = CreateFile(fname, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH, NULL);
00648        if (context.out==INVALID_HANDLE_VALUE) {
00649               printf("failed to open output file %s\n", fname);
00650               return 0;
00651        }
00652 
00653        // not using post files
00654        context.in = INVALID_HANDLE_VALUE;
00655 
00656        //
00657        // Fill the ECB with the necessary information
00658        //
00659        if (!FillExtensionControlBlock(&ECB, &context) ) {
00660               fprintf(stderr,"Fill Ext Block Failed\n");
00661               return -1;
00662        }
00663        
00664        // check for command line argument, 
00665        // first arg = filename
00666        // this is added for testing php from command line
00667 
00668        context.env.RemoveAll();
00669        context.env["PATH_TRANSLATED"]= filename;
00670        context.env["SCRIPT_MAP"]= filename;
00671        context.env["CONTENT_TYPE"]= "";
00672        context.env["CONTENT_LENGTH"]= "";
00673        context.env["QUERY_STRING"]= arg;
00674        context.env["METHOD"]="GET";
00675        context.env["PATH_INFO"] = "";
00676        context.waitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
00677        char buf[MAX_PATH];
00678        if (postdata && *postdata !=0) {
00679               ECB.cbAvailable = strlen(postdata);
00680               ECB.cbTotalBytes = ECB.cbAvailable;
00681               ECB.lpbData = (unsigned char *)postdata;
00682               context.env["METHOD"]="POST";
00683 
00684               _snprintf(buf, sizeof(buf)-1, "%d", ECB.cbTotalBytes);
00685               context.env["CONTENT_LENGTH"]=buf;
00686 
00687               context.env["CONTENT_TYPE"]="application/x-www-form-urlencoded";
00688        }
00689        ECB.lpszMethod = strdup(context.env["METHOD"]);
00690     ECB.lpszPathTranslated = strdup(filename);
00691        ECB.lpszQueryString = strdup(arg);
00692        ECB.lpszPathInfo = strdup(context.env["PATH_INFO"]);
00693 
00694 
00695        // Call the DLL
00696        //
00697        rc = IsapiHttpExtensionProc(&ECB);
00698        if (rc == HSE_STATUS_PENDING) {
00699               // We will exit in ServerSupportFunction
00700               WaitForSingleObject(context.waitEvent, INFINITE);
00701        }
00702        CloseHandle(context.waitEvent);
00703        //Sleep(75);
00704        free(ECB.lpszPathTranslated);
00705        free(ECB.lpszQueryString);
00706        free(ECB.lpszMethod);
00707        free(ECB.lpszPathInfo);
00708 
00709        BOOL ok = TRUE;
00710 
00711        if (context.out != INVALID_HANDLE_VALUE) CloseHandle(context.out);
00712 
00713        // compare the output with the EXPECT section
00714        if (matchdata && *matchdata != 0) {
00715               ok = CompareFiles(fname, matchdata);
00716        }
00717 
00718        DeleteFile(fname);
00719 
00720        return ok;
00721               
00722 }
00723 //
00724 // GetServerVariable() is how the DLL calls the main program to figure out
00725 // the environment variables it needs. This is a required function.
00726 //
00727 BOOL WINAPI GetServerVariable(HCONN hConn, LPSTR lpszVariableName,
00728                                                         LPVOID lpBuffer, LPDWORD lpdwSize){
00729 
00730        DWORD rc;
00731        CString value;
00732        TIsapiContext *c = (TIsapiContext *)hConn;
00733        if (!c) return FALSE;
00734 
00735        if (IsapiEnvironment.Lookup(lpszVariableName, value)) {
00736               rc = value.GetLength();
00737               strncpy((char *)lpBuffer, value, *lpdwSize-1);
00738        } else if (c->env.Lookup(lpszVariableName, value)) {
00739               rc = value.GetLength();
00740               strncpy((char *)lpBuffer, value, *lpdwSize-1);
00741        } else
00742               rc = GetEnvironmentVariable(lpszVariableName, (char *)lpBuffer, *lpdwSize) ;
00743 
00744        if (!rc) { // return of 0 indicates the variable was not found
00745               SetLastError(ERROR_NO_DATA);
00746               return FALSE;
00747        }
00748 
00749        if (rc > *lpdwSize) {
00750               SetLastError(ERROR_INSUFFICIENT_BUFFER);
00751               return FALSE;
00752        }
00753 
00754        *lpdwSize =rc + 1 ; // GetEnvironmentVariable does not count the NULL
00755 
00756        return TRUE;
00757 
00758 }
00759 //
00760 // Again, we don't have an HCONN, so we simply wrap ReadClient() to
00761 // ReadFile on stdin. The semantics of the two functions are the same
00762 //
00763 BOOL WINAPI ReadClient(HCONN hConn, LPVOID lpBuffer, LPDWORD lpdwSize) {
00764        TIsapiContext *c = (TIsapiContext *)hConn;
00765        if (!c) return FALSE;
00766 
00767        if (c->in != INVALID_HANDLE_VALUE) 
00768               return ReadFile(c->in, lpBuffer, (*lpdwSize), lpdwSize, NULL);
00769 
00770        return FALSE;
00771 }
00772 //
00773 // ditto for WriteClient()
00774 //
00775 BOOL WINAPI WriteClient(HCONN hConn, LPVOID lpBuffer, LPDWORD lpdwSize,
00776                      DWORD dwReserved) {
00777        TIsapiContext *c = (TIsapiContext *)hConn;
00778        if (!c) return FALSE;
00779 
00780        if (c->out != INVALID_HANDLE_VALUE)
00781               return WriteFile(c->out, lpBuffer, *lpdwSize, lpdwSize, NULL);
00782        return FALSE;
00783 }
00784 //
00785 // This is a special callback function used by the DLL for certain extra 
00786 // functionality. Look at the API help for details.
00787 //
00788 BOOL WINAPI ServerSupportFunction(HCONN hConn, DWORD dwHSERequest,
00789                             LPVOID lpvBuffer, LPDWORD lpdwSize, LPDWORD lpdwDataType){
00790 
00791        TIsapiContext *c = (TIsapiContext *)hConn;
00792        char *lpszRespBuf;
00793        char * temp = NULL;
00794        DWORD dwBytes;
00795        BOOL bRet = TRUE;
00796 
00797        switch(dwHSERequest) {
00798               case (HSE_REQ_SEND_RESPONSE_HEADER) :
00799                      lpszRespBuf = (char *)xmalloc(*lpdwSize);//+ 80);//accomodate our header
00800                      if (!lpszRespBuf)
00801                             return FALSE;
00802                      wsprintf(lpszRespBuf,"%s",
00803                             //HTTP_VER,
00804                             
00805                             /* Default response is 200 Ok */
00806 
00807                             //lpvBuffer?lpvBuffer:"200 Ok",
00808                             
00809                             /* Create a string for the time. */
00810                             //temp=MakeDateStr(),
00811 
00812                             //SERVER_VERSION,
00813                             
00814                             /* If this exists, it is a pointer to a data buffer to
00815                                be sent. */
00816                             lpdwDataType?(char *)lpdwDataType:NULL);
00817 
00818                      if (temp) xfree(temp);
00819 
00820                      dwBytes = strlen(lpszRespBuf);
00821                      bRet = WriteClient(0, lpszRespBuf, &dwBytes, 0);
00822                      xfree(lpszRespBuf);
00823 
00824                      break;
00825                      //
00826                      // A real server would do cleanup here
00827               case (HSE_REQ_DONE_WITH_SESSION):
00828                      SetEvent(c->waitEvent);
00829                      //ExitThread(0);
00830                      break;
00831               
00832               //
00833               // This sends a redirect (temporary) to the client.
00834               // The header construction is similar to RESPONSE_HEADER above.
00835               //
00836               case (HSE_REQ_SEND_URL_REDIRECT_RESP):
00837                      lpszRespBuf = (char *)xmalloc(*lpdwSize +80) ;
00838                      if (!lpszRespBuf)
00839                             return FALSE;
00840                      wsprintf(lpszRespBuf,"%s %s %s\r\n",
00841                                    HTTP_VER,
00842                                    "302 Moved Temporarily",
00843                                    (lpdwSize > 0)?lpvBuffer:0);
00844                      xfree(temp);
00845                      dwBytes = strlen(lpszRespBuf);
00846                      bRet = WriteClient(0, lpszRespBuf, &dwBytes, 0);
00847                      xfree(lpszRespBuf);
00848                      break;
00849               default:
00850                      return FALSE;
00851               break;
00852        }
00853        return bRet;
00854        
00855 }
00856 //
00857 // Makes a string of the date and time from GetSystemTime().
00858 // This is in UTC, as required by the HTTP spec.`
00859 //
00860 char * MakeDateStr(void){
00861        SYSTEMTIME systime;
00862        char *szDate= (char *)xmalloc(64);
00863 
00864        char * DaysofWeek[] = {"Sun","Mon","Tue","Wed","Thurs","Fri","Sat"};
00865        char * Months[] = {"NULL","Jan","Feb","Mar","Apr","May","Jun","Jul","Aug",
00866                                           "Sep","Oct","Nov","Dec"};
00867 
00868        GetSystemTime(&systime);
00869 
00870        wsprintf(szDate,"%s, %d %s %d %d:%d.%d", DaysofWeek[systime.wDayOfWeek],
00871                                                                  systime.wDay,
00872                                                                  Months[systime.wMonth],
00873                                                                  systime.wYear,
00874                                                                  systime.wHour, systime.wMinute,
00875                                                                  systime.wSecond );
00876 
00877        return szDate;
00878 }
00879 //
00880 // Fill the ECB up 
00881 //
00882 BOOL WINAPI FillExtensionControlBlock(EXTENSION_CONTROL_BLOCK *ECB, TIsapiContext *context) {
00883 
00884        char * temp;
00885        ECB->cbSize = sizeof(EXTENSION_CONTROL_BLOCK);
00886        ECB->dwVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
00887        ECB->ConnID = (void *)context;
00888        //
00889        // Pointers to the functions the DLL will call.
00890        //
00891        ECB->GetServerVariable = GetServerVariable;
00892        ECB->ReadClient  = ReadClient;
00893        ECB->WriteClient = WriteClient;
00894        ECB->ServerSupportFunction = ServerSupportFunction;
00895 
00896        //
00897        // Fill in the standard CGI environment variables
00898        //
00899        ECB->lpszMethod = GetEnv("REQUEST_METHOD");
00900        if (!ECB->lpszMethod) ECB->lpszMethod = "GET";
00901 
00902        ECB->lpszQueryString = GetEnv("QUERY_STRING");
00903        ECB->lpszPathInfo = GetEnv("PATH_INFO");
00904        ECB->lpszPathTranslated = GetEnv("PATH_TRANSLATED");
00905        ECB->cbTotalBytes=( (temp=GetEnv("CONTENT_LENGTH")) ? (atoi(temp)): 0);
00906        ECB->cbAvailable = 0;
00907        ECB->lpbData = (unsigned char *)"";
00908        ECB->lpszContentType = GetEnv("CONTENT_TYPE");
00909        return TRUE;
00910 
00911 }
00912 
00913 //
00914 // Works like _getenv(), but uses win32 functions instead.
00915 //
00916 char *GetEnv(LPSTR lpszEnvVar)
00917 {
00918        
00919        char *var, dummy;
00920        DWORD dwLen;
00921 
00922        if (!lpszEnvVar)
00923               return "";
00924        
00925        dwLen =GetEnvironmentVariable(lpszEnvVar, &dummy, 1);
00926 
00927        if (dwLen == 0)
00928               return "";
00929        
00930        var = (char *)xmalloc(dwLen);
00931        if (!var)
00932               return "";
00933        (void)GetEnvironmentVariable(lpszEnvVar, var, dwLen);
00934 
00935        return var;
00936 }