stresstest.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936
  1. /*
  2. * ======================================================================= *
  3. * File: stress .c *
  4. * stress tester for isapi dll's *
  5. * based on cgiwrap *
  6. * ======================================================================= *
  7. *
  8. */
  9. #define WIN32_LEAN_AND_MEAN
  10. #include <afx.h>
  11. #include <afxtempl.h>
  12. #include <winbase.h>
  13. #include <winerror.h>
  14. #include <httpext.h>
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include "getopt.h"
  18. // These are things that go out in the Response Header
  19. //
  20. #define HTTP_VER "HTTP/1.0"
  21. #define SERVER_VERSION "Http-Srv-Beta2/1.0"
  22. //
  23. // Simple wrappers for the heap APIS
  24. //
  25. #define xmalloc(s) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (s))
  26. #define xfree(s) HeapFree(GetProcessHeap(), 0, (s))
  27. //
  28. // The mandatory exports from the ISAPI DLL
  29. //
  30. DWORD numThreads = 1;
  31. DWORD iterations = 1;
  32. HANDLE StartNow;
  33. // quick and dirty environment
  34. typedef CMapStringToString TEnvironment;
  35. TEnvironment IsapiEnvironment;
  36. typedef struct _TResults {
  37. LONG ok;
  38. LONG bad;
  39. } TResults;
  40. CStringArray IsapiFileList; // list of filenames
  41. CStringArray TestNames; // --TEST--
  42. CStringArray IsapiGetData; // --GET--
  43. CStringArray IsapiPostData; // --POST--
  44. CStringArray IsapiMatchData; // --EXPECT--
  45. CArray<TResults, TResults> Results;
  46. typedef struct _TIsapiContext {
  47. HANDLE in;
  48. HANDLE out;
  49. DWORD tid;
  50. TEnvironment env;
  51. HANDLE waitEvent;
  52. } TIsapiContext;
  53. //
  54. // Prototypes of the functions this sample implements
  55. //
  56. extern "C" {
  57. HINSTANCE hDll;
  58. typedef BOOL (WINAPI *VersionProc)(HSE_VERSION_INFO *) ;
  59. typedef DWORD (WINAPI *HttpExtProc)(EXTENSION_CONTROL_BLOCK *);
  60. typedef BOOL (WINAPI *TerminateProc) (DWORD);
  61. BOOL WINAPI FillExtensionControlBlock(EXTENSION_CONTROL_BLOCK *, TIsapiContext *) ;
  62. BOOL WINAPI GetServerVariable(HCONN, LPSTR, LPVOID, LPDWORD );
  63. BOOL WINAPI ReadClient(HCONN, LPVOID, LPDWORD);
  64. BOOL WINAPI WriteClient(HCONN, LPVOID, LPDWORD, DWORD);
  65. BOOL WINAPI ServerSupportFunction(HCONN, DWORD, LPVOID, LPDWORD, LPDWORD);
  66. VersionProc IsapiGetExtensionVersion;
  67. HttpExtProc IsapiHttpExtensionProc;
  68. TerminateProc TerminateExtensionProc;
  69. HSE_VERSION_INFO version_info;
  70. }
  71. char * MakeDateStr(VOID);
  72. char * GetEnv(char *);
  73. DWORD CALLBACK IsapiThread(void *);
  74. int stress_main(const char *filename,
  75. const char *arg,
  76. const char *postfile,
  77. const char *matchdata);
  78. BOOL bUseTestFiles = FALSE;
  79. char temppath[MAX_PATH];
  80. void stripcrlf(char *line)
  81. {
  82. DWORD l = strlen(line)-1;
  83. if (line[l]==10 || line[l]==13) line[l]=0;
  84. l = strlen(line)-1;
  85. if (line[l]==10 || line[l]==13) line[l]=0;
  86. }
  87. #define COMPARE_BUF_SIZE 1024
  88. BOOL CompareFiles(const char*f1, const char*f2)
  89. {
  90. FILE *fp1, *fp2;
  91. bool retval;
  92. char buf1[COMPARE_BUF_SIZE], buf2[COMPARE_BUF_SIZE];
  93. int length1, length2;
  94. if ((fp1=fopen(f1, "r"))==NULL) {
  95. return FALSE;
  96. }
  97. if ((fp2=fopen(f2, "r"))==NULL) {
  98. fclose(fp1);
  99. return FALSE;
  100. }
  101. retval = TRUE; // success oriented
  102. while (true) {
  103. length1 = fread(buf1, 1, sizeof(buf1), fp1);
  104. length2 = fread(buf2, 1, sizeof(buf2), fp2);
  105. // check for end of file
  106. if (feof(fp1)) {
  107. if (!feof(fp2)) {
  108. retval = FALSE;
  109. }
  110. break;
  111. } else if (feof(fp2)) {
  112. if (!feof(fp1)) {
  113. retval = FALSE;
  114. }
  115. break;
  116. }
  117. // compare data
  118. if (length1!=length2
  119. || memcmp(buf1, buf2, length1)!=0) {
  120. retval = FALSE;
  121. break;
  122. }
  123. }
  124. fclose(fp1);
  125. fclose(fp2);
  126. return retval;
  127. }
  128. BOOL CompareStringWithFile(const char *filename, const char *str, unsigned int str_length)
  129. {
  130. FILE *fp;
  131. bool retval;
  132. char buf[COMPARE_BUF_SIZE];
  133. unsigned int offset=0, readbytes;
  134. fprintf(stderr, "test %s\n",filename);
  135. if ((fp=fopen(filename, "rb"))==NULL) {
  136. fprintf(stderr, "Error opening %s\n",filename);
  137. return FALSE;
  138. }
  139. retval = TRUE; // success oriented
  140. while (true) {
  141. readbytes = fread(buf, 1, sizeof(buf), fp);
  142. // check for end of file
  143. if (offset+readbytes > str_length
  144. || memcmp(buf, str+offset, readbytes)!=NULL) {
  145. fprintf(stderr, "File missmatch %s\n",filename);
  146. retval = FALSE;
  147. break;
  148. }
  149. if (feof(fp)) {
  150. if (!retval) fprintf(stderr, "File zero length %s\n",filename);
  151. break;
  152. }
  153. }
  154. fclose(fp);
  155. return retval;
  156. }
  157. BOOL ReadGlobalEnvironment(const char *environment)
  158. {
  159. if (environment) {
  160. FILE *fp = fopen(environment, "r");
  161. DWORD i=0;
  162. if (fp) {
  163. char line[2048];
  164. while (fgets(line, sizeof(line)-1, fp)) {
  165. // file.php arg1 arg2 etc.
  166. char *p = strchr(line, '=');
  167. if (p) {
  168. *p=0;
  169. IsapiEnvironment[line]=p+1;
  170. }
  171. }
  172. fclose(fp);
  173. return IsapiEnvironment.GetCount() > 0;
  174. }
  175. }
  176. return FALSE;
  177. }
  178. BOOL ReadFileList(const char *filelist)
  179. {
  180. FILE *fp = fopen(filelist, "r");
  181. if (!fp) {
  182. printf("Unable to open %s\r\n", filelist);
  183. }
  184. char line[2048];
  185. int i=0;
  186. while (fgets(line, sizeof(line)-1, fp)) {
  187. // file.php arg1 arg2 etc.
  188. stripcrlf(line);
  189. if (strlen(line)>3) {
  190. char *p = strchr(line, ' ');
  191. if (p) {
  192. *p = 0;
  193. // get file
  194. IsapiFileList.Add(line);
  195. IsapiGetData.Add(p+1);
  196. } else {
  197. // just a filename is all
  198. IsapiFileList.Add(line);
  199. IsapiGetData.Add("");
  200. }
  201. }
  202. // future use
  203. IsapiPostData.Add("");
  204. IsapiMatchData.Add("");
  205. TestNames.Add("");
  206. i++;
  207. }
  208. Results.SetSize(TestNames.GetSize());
  209. fclose(fp);
  210. return IsapiFileList.GetSize() > 0;
  211. }
  212. void DoThreads() {
  213. if (IsapiFileList.GetSize() == 0) {
  214. printf("No Files to test\n");
  215. return;
  216. }
  217. printf("Starting Threads...\n");
  218. // loop creating threads
  219. DWORD tid;
  220. HANDLE *threads = new HANDLE[numThreads];
  221. DWORD i;
  222. for (i=0; i< numThreads; i++) {
  223. threads[i]=CreateThread(NULL, 0, IsapiThread, NULL, CREATE_SUSPENDED, &tid);
  224. }
  225. for (i=0; i< numThreads; i++) {
  226. if (threads[i]) ResumeThread(threads[i]);
  227. }
  228. // wait for threads to finish
  229. WaitForMultipleObjects(numThreads, threads, TRUE, INFINITE);
  230. for (i=0; i< numThreads; i++) {
  231. CloseHandle(threads[i]);
  232. }
  233. delete [] threads;
  234. }
  235. void DoFileList(const char *filelist, const char *environment)
  236. {
  237. // read config files
  238. if (!ReadFileList(filelist)) {
  239. printf("No Files to test!\r\n");
  240. return;
  241. }
  242. ReadGlobalEnvironment(environment);
  243. DoThreads();
  244. }
  245. /**
  246. * ParseTestFile
  247. * parse a single phpt file and add it to the arrays
  248. */
  249. BOOL ParseTestFile(const char *path, const char *fn)
  250. {
  251. // parse the test file
  252. char filename[MAX_PATH];
  253. _snprintf(filename, sizeof(filename)-1, "%s\\%s", path, fn);
  254. char line[1024];
  255. memset(line, 0, sizeof(line));
  256. CString cTest, cSkipIf, cPost, cGet, cFile, cExpect;
  257. printf("Reading %s\r\n", filename);
  258. enum state {none, test, skipif, post, get, file, expect} parsestate = none;
  259. FILE *fp = fopen(filename, "rb");
  260. char *tn = _tempnam(temppath,"pht.");
  261. char *en = _tempnam(temppath,"exp.");
  262. FILE *ft = fopen(tn, "wb+");
  263. FILE *fe = fopen(en, "wb+");
  264. if (fp && ft && fe) {
  265. while (fgets(line, sizeof(line)-1, fp)) {
  266. if (line[0]=='-') {
  267. if (_strnicmp(line, "--TEST--", 8)==0) {
  268. parsestate = test;
  269. continue;
  270. } else if (_strnicmp(line, "--SKIPIF--", 10)==0) {
  271. parsestate = skipif;
  272. continue;
  273. } else if (_strnicmp(line, "--POST--", 8)==0) {
  274. parsestate = post;
  275. continue;
  276. } else if (_strnicmp(line, "--GET--", 7)==0) {
  277. parsestate = get;
  278. continue;
  279. } else if (_strnicmp(line, "--FILE--", 8)==0) {
  280. parsestate = file;
  281. continue;
  282. } else if (_strnicmp(line, "--EXPECT--", 10)==0) {
  283. parsestate = expect;
  284. continue;
  285. }
  286. }
  287. switch (parsestate) {
  288. case test:
  289. stripcrlf(line);
  290. cTest = line;
  291. break;
  292. case skipif:
  293. cSkipIf += line;
  294. break;
  295. case post:
  296. cPost += line;
  297. break;
  298. case get:
  299. cGet += line;
  300. break;
  301. case file:
  302. fputs(line, ft);
  303. break;
  304. case expect:
  305. fputs(line, fe);
  306. break;
  307. }
  308. }
  309. fclose(fp);
  310. fclose(ft);
  311. fclose(fe);
  312. if (!cTest.IsEmpty()) {
  313. IsapiFileList.Add(tn);
  314. TestNames.Add(cTest);
  315. IsapiGetData.Add(cGet);
  316. IsapiPostData.Add(cPost);
  317. IsapiMatchData.Add(en);
  318. free(tn);
  319. free(en);
  320. return TRUE;
  321. }
  322. }
  323. free(tn);
  324. free(en);
  325. return FALSE;
  326. }
  327. /**
  328. * GetTestFiles
  329. * Recurse through the path and subdirectories, parse each phpt file
  330. */
  331. BOOL GetTestFiles(const char *path)
  332. {
  333. // find all files .phpt under testpath\tests
  334. char FindPath[MAX_PATH];
  335. WIN32_FIND_DATA fd;
  336. memset(&fd, 0, sizeof(WIN32_FIND_DATA));
  337. _snprintf(FindPath, sizeof(FindPath)-1, "%s\\*.*", path);
  338. HANDLE fh = FindFirstFile(FindPath, &fd);
  339. if (fh != INVALID_HANDLE_VALUE) {
  340. do {
  341. if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  342. !strchr(fd.cFileName, '.')) {
  343. // subdirectory, recurse into it
  344. char NewFindPath[MAX_PATH];
  345. _snprintf(NewFindPath, sizeof(NewFindPath)-1, "%s\\%s", path, fd.cFileName);
  346. GetTestFiles(NewFindPath);
  347. } else if (strstr(fd.cFileName, ".phpt")) {
  348. // got test file, parse it now
  349. if (ParseTestFile(path, fd.cFileName)) {
  350. printf("Test File Added: %s\\%s\r\n", path, fd.cFileName);
  351. }
  352. }
  353. memset(&fd, 0, sizeof(WIN32_FIND_DATA));
  354. } while (FindNextFile(fh, &fd) != 0);
  355. FindClose(fh);
  356. }
  357. return IsapiFileList.GetSize() > 0;
  358. }
  359. void DeleteTempFiles(const char *mask)
  360. {
  361. char FindPath[MAX_PATH];
  362. WIN32_FIND_DATA fd;
  363. memset(&fd, 0, sizeof(WIN32_FIND_DATA));
  364. _snprintf(FindPath, sizeof(FindPath)-1, "%s\\%s", temppath, mask);
  365. HANDLE fh = FindFirstFile(FindPath, &fd);
  366. if (fh != INVALID_HANDLE_VALUE) {
  367. do {
  368. char NewFindPath[MAX_PATH];
  369. _snprintf(NewFindPath, sizeof(NewFindPath)-1, "%s\\%s", temppath, fd.cFileName);
  370. DeleteFile(NewFindPath);
  371. memset(&fd, 0, sizeof(WIN32_FIND_DATA));
  372. } while (FindNextFile(fh, &fd) != 0);
  373. FindClose(fh);
  374. }
  375. }
  376. void DoTestFiles(const char *filelist, const char *environment)
  377. {
  378. if (!GetTestFiles(filelist)) {
  379. printf("No Files to test!\r\n");
  380. return;
  381. }
  382. Results.SetSize(IsapiFileList.GetSize());
  383. ReadGlobalEnvironment(environment);
  384. DoThreads();
  385. printf("\r\nRESULTS:\r\n");
  386. // show results:
  387. DWORD r = Results.GetSize();
  388. for (DWORD i=0; i< r; i++) {
  389. TResults result = Results.GetAt(i);
  390. printf("%s\r\nOK: %d FAILED: %d\r\n", TestNames.GetAt(i), result.ok, result.bad);
  391. }
  392. // delete temp files
  393. printf("Deleting Temp Files\r\n");
  394. DeleteTempFiles("exp.*");
  395. DeleteTempFiles("pht.*");
  396. printf("Done\r\n");
  397. }
  398. #define OPTSTRING "m:f:d:h:t:i:"
  399. static void _usage(char *argv0)
  400. {
  401. char *prog;
  402. prog = strrchr(argv0, '/');
  403. if (prog) {
  404. prog++;
  405. } else {
  406. prog = "stresstest";
  407. }
  408. printf("Usage: %s -m <isapi.dll> -d|-l <file> [-t <numthreads>] [-i <numiterations>]\n"
  409. " -m path to isapi dll\n"
  410. " -d <directory> php directory (to run php test files).\n"
  411. " -f <file> file containing list of files to run\n"
  412. " -t number of threads to use (default=1)\n"
  413. " -i number of iterations per thread (default=1)\n"
  414. " -h This help\n", prog);
  415. }
  416. int main(int argc, char* argv[])
  417. {
  418. LPVOID lpMsgBuf;
  419. char *filelist=NULL, *environment=NULL, *module=NULL;
  420. int c = NULL;
  421. while ((c=ap_getopt(argc, argv, OPTSTRING))!=-1) {
  422. switch (c) {
  423. case 'd':
  424. bUseTestFiles = TRUE;
  425. filelist = strdup(ap_optarg);
  426. break;
  427. case 'f':
  428. bUseTestFiles = FALSE;
  429. filelist = strdup(ap_optarg);
  430. break;
  431. case 'e':
  432. environment = strdup(ap_optarg);
  433. break;
  434. case 't':
  435. numThreads = atoi(ap_optarg);
  436. break;
  437. case 'i':
  438. iterations = atoi(ap_optarg);
  439. break;
  440. case 'm':
  441. module = strdup(ap_optarg);
  442. break;
  443. case 'h':
  444. _usage(argv[0]);
  445. exit(0);
  446. break;
  447. }
  448. }
  449. if (!module || !filelist) {
  450. _usage(argv[0]);
  451. exit(0);
  452. }
  453. GetTempPath(sizeof(temppath), temppath);
  454. hDll = LoadLibrary(module); // Load our DLL
  455. if (!hDll) {
  456. FormatMessage(
  457. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  458. NULL,
  459. GetLastError(),
  460. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  461. (LPTSTR) &lpMsgBuf,
  462. 0,
  463. NULL
  464. );
  465. fprintf(stderr,"Error: Dll 'php5isapi.dll' not found -%d\n%s\n", GetLastError(), lpMsgBuf);
  466. free (module);
  467. free(filelist);
  468. LocalFree( lpMsgBuf );
  469. return -1;
  470. }
  471. //
  472. // Find the exported functions
  473. IsapiGetExtensionVersion = (VersionProc)GetProcAddress(hDll,"GetExtensionVersion");
  474. if (!IsapiGetExtensionVersion) {
  475. fprintf(stderr,"Can't Get Extension Version %d\n", GetLastError());
  476. free (module);
  477. free(filelist);
  478. return -1;
  479. }
  480. IsapiHttpExtensionProc = (HttpExtProc)GetProcAddress(hDll,"HttpExtensionProc");
  481. if (!IsapiHttpExtensionProc) {
  482. fprintf(stderr,"Can't Get Extension proc %d\n", GetLastError());
  483. free (module);
  484. free(filelist);
  485. return -1;
  486. }
  487. TerminateExtensionProc = (TerminateProc) GetProcAddress(hDll,
  488. "TerminateExtension");
  489. // This should really check if the version information matches what we
  490. // expect.
  491. //
  492. if (!IsapiGetExtensionVersion(&version_info) ) {
  493. fprintf(stderr,"Fatal: GetExtensionVersion failed\n");
  494. free (module);
  495. free(filelist);
  496. return -1;
  497. }
  498. if (bUseTestFiles) {
  499. char TestPath[MAX_PATH];
  500. if (filelist != NULL)
  501. _snprintf(TestPath, sizeof(TestPath)-1, "%s\\tests", filelist);
  502. else strcpy(TestPath, "tests");
  503. DoTestFiles(TestPath, environment);
  504. } else {
  505. DoFileList(filelist, environment);
  506. }
  507. // cleanup
  508. if (TerminateExtensionProc) TerminateExtensionProc(0);
  509. // We should really free memory (e.g., from GetEnv), but we'll be dead
  510. // soon enough
  511. FreeLibrary(hDll);
  512. free (module);
  513. free(filelist);
  514. return 0;
  515. }
  516. DWORD CALLBACK IsapiThread(void *p)
  517. {
  518. DWORD filecount = IsapiFileList.GetSize();
  519. for (DWORD j=0; j<iterations; j++) {
  520. for (DWORD i=0; i<filecount; i++) {
  521. // execute each file
  522. CString testname = TestNames.GetAt(i);
  523. BOOL ok = FALSE;
  524. if (stress_main(IsapiFileList.GetAt(i),
  525. IsapiGetData.GetAt(i),
  526. IsapiPostData.GetAt(i),
  527. IsapiMatchData.GetAt(i))) {
  528. InterlockedIncrement(&Results[i].ok);
  529. ok = TRUE;
  530. } else {
  531. InterlockedIncrement(&Results[i].bad);
  532. ok = FALSE;
  533. }
  534. if (testname.IsEmpty()) {
  535. printf("Thread %d File %s\n", GetCurrentThreadId(), IsapiFileList.GetAt(i));
  536. } else {
  537. printf("tid %d: %s %s\n", GetCurrentThreadId(), testname, ok?"OK":"FAIL");
  538. }
  539. Sleep(10);
  540. }
  541. }
  542. printf("Thread ending...\n");
  543. return 0;
  544. }
  545. /*
  546. * ======================================================================= *
  547. * In the startup of this program, we look at our executable name and *
  548. * replace the ".EXE" with ".DLL" to find the ISAPI DLL we need to load. *
  549. * This means that the executable need only be given the same "name" as *
  550. * the DLL to load. There is no recompilation required. *
  551. * ======================================================================= *
  552. */
  553. BOOL stress_main(const char *filename,
  554. const char *arg,
  555. const char *postdata,
  556. const char *matchdata)
  557. {
  558. EXTENSION_CONTROL_BLOCK ECB;
  559. DWORD rc;
  560. TIsapiContext context;
  561. // open output and input files
  562. context.tid = GetCurrentThreadId();
  563. CString fname;
  564. fname.Format("%08X.out", context.tid);
  565. context.out = CreateFile(fname, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH, NULL);
  566. if (context.out==INVALID_HANDLE_VALUE) {
  567. printf("failed to open output file %s\n", fname);
  568. return 0;
  569. }
  570. // not using post files
  571. context.in = INVALID_HANDLE_VALUE;
  572. //
  573. // Fill the ECB with the necessary information
  574. //
  575. if (!FillExtensionControlBlock(&ECB, &context) ) {
  576. fprintf(stderr,"Fill Ext Block Failed\n");
  577. return -1;
  578. }
  579. // check for command line argument,
  580. // first arg = filename
  581. // this is added for testing php from command line
  582. context.env.RemoveAll();
  583. context.env["PATH_TRANSLATED"]= filename;
  584. context.env["SCRIPT_MAP"]= filename;
  585. context.env["CONTENT_TYPE"]= "";
  586. context.env["CONTENT_LENGTH"]= "";
  587. context.env["QUERY_STRING"]= arg;
  588. context.env["METHOD"]="GET";
  589. context.env["PATH_INFO"] = "";
  590. context.waitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  591. char buf[MAX_PATH];
  592. if (postdata && *postdata !=0) {
  593. ECB.cbAvailable = strlen(postdata);
  594. ECB.cbTotalBytes = ECB.cbAvailable;
  595. ECB.lpbData = (unsigned char *)postdata;
  596. context.env["METHOD"]="POST";
  597. _snprintf(buf, sizeof(buf)-1, "%d", ECB.cbTotalBytes);
  598. context.env["CONTENT_LENGTH"]=buf;
  599. context.env["CONTENT_TYPE"]="application/x-www-form-urlencoded";
  600. }
  601. ECB.lpszMethod = strdup(context.env["METHOD"]);
  602. ECB.lpszPathTranslated = strdup(filename);
  603. ECB.lpszQueryString = strdup(arg);
  604. ECB.lpszPathInfo = strdup(context.env["PATH_INFO"]);
  605. // Call the DLL
  606. //
  607. rc = IsapiHttpExtensionProc(&ECB);
  608. if (rc == HSE_STATUS_PENDING) {
  609. // We will exit in ServerSupportFunction
  610. WaitForSingleObject(context.waitEvent, INFINITE);
  611. }
  612. CloseHandle(context.waitEvent);
  613. //Sleep(75);
  614. free(ECB.lpszPathTranslated);
  615. free(ECB.lpszQueryString);
  616. free(ECB.lpszMethod);
  617. free(ECB.lpszPathInfo);
  618. BOOL ok = TRUE;
  619. if (context.out != INVALID_HANDLE_VALUE) CloseHandle(context.out);
  620. // compare the output with the EXPECT section
  621. if (matchdata && *matchdata != 0) {
  622. ok = CompareFiles(fname, matchdata);
  623. }
  624. DeleteFile(fname);
  625. return ok;
  626. }
  627. //
  628. // GetServerVariable() is how the DLL calls the main program to figure out
  629. // the environment variables it needs. This is a required function.
  630. //
  631. BOOL WINAPI GetServerVariable(HCONN hConn, LPSTR lpszVariableName,
  632. LPVOID lpBuffer, LPDWORD lpdwSize){
  633. DWORD rc;
  634. CString value;
  635. TIsapiContext *c = (TIsapiContext *)hConn;
  636. if (!c) return FALSE;
  637. if (IsapiEnvironment.Lookup(lpszVariableName, value)) {
  638. rc = value.GetLength();
  639. strncpy((char *)lpBuffer, value, *lpdwSize-1);
  640. } else if (c->env.Lookup(lpszVariableName, value)) {
  641. rc = value.GetLength();
  642. strncpy((char *)lpBuffer, value, *lpdwSize-1);
  643. } else
  644. rc = GetEnvironmentVariable(lpszVariableName, (char *)lpBuffer, *lpdwSize) ;
  645. if (!rc) { // return of 0 indicates the variable was not found
  646. SetLastError(ERROR_NO_DATA);
  647. return FALSE;
  648. }
  649. if (rc > *lpdwSize) {
  650. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  651. return FALSE;
  652. }
  653. *lpdwSize =rc + 1 ; // GetEnvironmentVariable does not count the NULL
  654. return TRUE;
  655. }
  656. //
  657. // Again, we don't have an HCONN, so we simply wrap ReadClient() to
  658. // ReadFile on stdin. The semantics of the two functions are the same
  659. //
  660. BOOL WINAPI ReadClient(HCONN hConn, LPVOID lpBuffer, LPDWORD lpdwSize) {
  661. TIsapiContext *c = (TIsapiContext *)hConn;
  662. if (!c) return FALSE;
  663. if (c->in != INVALID_HANDLE_VALUE)
  664. return ReadFile(c->in, lpBuffer, (*lpdwSize), lpdwSize, NULL);
  665. return FALSE;
  666. }
  667. //
  668. // ditto for WriteClient()
  669. //
  670. BOOL WINAPI WriteClient(HCONN hConn, LPVOID lpBuffer, LPDWORD lpdwSize,
  671. DWORD dwReserved) {
  672. TIsapiContext *c = (TIsapiContext *)hConn;
  673. if (!c) return FALSE;
  674. if (c->out != INVALID_HANDLE_VALUE)
  675. return WriteFile(c->out, lpBuffer, *lpdwSize, lpdwSize, NULL);
  676. return FALSE;
  677. }
  678. //
  679. // This is a special callback function used by the DLL for certain extra
  680. // functionality. Look at the API help for details.
  681. //
  682. BOOL WINAPI ServerSupportFunction(HCONN hConn, DWORD dwHSERequest,
  683. LPVOID lpvBuffer, LPDWORD lpdwSize, LPDWORD lpdwDataType){
  684. TIsapiContext *c = (TIsapiContext *)hConn;
  685. char *lpszRespBuf;
  686. char * temp = NULL;
  687. DWORD dwBytes;
  688. BOOL bRet = TRUE;
  689. switch(dwHSERequest) {
  690. case (HSE_REQ_SEND_RESPONSE_HEADER) :
  691. lpszRespBuf = (char *)xmalloc(*lpdwSize);//+ 80);//accommodate our header
  692. if (!lpszRespBuf)
  693. return FALSE;
  694. wsprintf(lpszRespBuf,"%s",
  695. //HTTP_VER,
  696. /* Default response is 200 Ok */
  697. //lpvBuffer?lpvBuffer:"200 Ok",
  698. /* Create a string for the time. */
  699. //temp=MakeDateStr(),
  700. //SERVER_VERSION,
  701. /* If this exists, it is a pointer to a data buffer to
  702. be sent. */
  703. lpdwDataType?(char *)lpdwDataType:NULL);
  704. if (temp) xfree(temp);
  705. dwBytes = strlen(lpszRespBuf);
  706. bRet = WriteClient(0, lpszRespBuf, &dwBytes, 0);
  707. xfree(lpszRespBuf);
  708. break;
  709. //
  710. // A real server would do cleanup here
  711. case (HSE_REQ_DONE_WITH_SESSION):
  712. SetEvent(c->waitEvent);
  713. //ExitThread(0);
  714. break;
  715. //
  716. // This sends a redirect (temporary) to the client.
  717. // The header construction is similar to RESPONSE_HEADER above.
  718. //
  719. case (HSE_REQ_SEND_URL_REDIRECT_RESP):
  720. lpszRespBuf = (char *)xmalloc(*lpdwSize +80) ;
  721. if (!lpszRespBuf)
  722. return FALSE;
  723. wsprintf(lpszRespBuf,"%s %s %s\r\n",
  724. HTTP_VER,
  725. "302 Moved Temporarily",
  726. (lpdwSize > 0)?lpvBuffer:0);
  727. xfree(temp);
  728. dwBytes = strlen(lpszRespBuf);
  729. bRet = WriteClient(0, lpszRespBuf, &dwBytes, 0);
  730. xfree(lpszRespBuf);
  731. break;
  732. default:
  733. return FALSE;
  734. break;
  735. }
  736. return bRet;
  737. }
  738. //
  739. // Makes a string of the date and time from GetSystemTime().
  740. // This is in UTC, as required by the HTTP spec.`
  741. //
  742. char * MakeDateStr(void){
  743. SYSTEMTIME systime;
  744. char *szDate= (char *)xmalloc(64);
  745. char * DaysofWeek[] = {"Sun","Mon","Tue","Wed","Thurs","Fri","Sat"};
  746. char * Months[] = {"NULL","Jan","Feb","Mar","Apr","May","Jun","Jul","Aug",
  747. "Sep","Oct","Nov","Dec"};
  748. GetSystemTime(&systime);
  749. wsprintf(szDate,"%s, %d %s %d %d:%d.%d", DaysofWeek[systime.wDayOfWeek],
  750. systime.wDay,
  751. Months[systime.wMonth],
  752. systime.wYear,
  753. systime.wHour, systime.wMinute,
  754. systime.wSecond );
  755. return szDate;
  756. }
  757. //
  758. // Fill the ECB up
  759. //
  760. BOOL WINAPI FillExtensionControlBlock(EXTENSION_CONTROL_BLOCK *ECB, TIsapiContext *context) {
  761. char * temp;
  762. ECB->cbSize = sizeof(EXTENSION_CONTROL_BLOCK);
  763. ECB->dwVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
  764. ECB->ConnID = (void *)context;
  765. //
  766. // Pointers to the functions the DLL will call.
  767. //
  768. ECB->GetServerVariable = GetServerVariable;
  769. ECB->ReadClient = ReadClient;
  770. ECB->WriteClient = WriteClient;
  771. ECB->ServerSupportFunction = ServerSupportFunction;
  772. //
  773. // Fill in the standard CGI environment variables
  774. //
  775. ECB->lpszMethod = GetEnv("REQUEST_METHOD");
  776. if (!ECB->lpszMethod) ECB->lpszMethod = "GET";
  777. ECB->lpszQueryString = GetEnv("QUERY_STRING");
  778. ECB->lpszPathInfo = GetEnv("PATH_INFO");
  779. ECB->lpszPathTranslated = GetEnv("PATH_TRANSLATED");
  780. ECB->cbTotalBytes=( (temp=GetEnv("CONTENT_LENGTH")) ? (atoi(temp)): 0);
  781. ECB->cbAvailable = 0;
  782. ECB->lpbData = (unsigned char *)"";
  783. ECB->lpszContentType = GetEnv("CONTENT_TYPE");
  784. return TRUE;
  785. }
  786. //
  787. // Works like _getenv(), but uses win32 functions instead.
  788. //
  789. char *GetEnv(LPSTR lpszEnvVar)
  790. {
  791. char *var, dummy;
  792. DWORD dwLen;
  793. if (!lpszEnvVar)
  794. return "";
  795. dwLen =GetEnvironmentVariable(lpszEnvVar, &dummy, 1);
  796. if (dwLen == 0)
  797. return "";
  798. var = (char *)xmalloc(dwLen);
  799. if (!var)
  800. return "";
  801. (void)GetEnvironmentVariable(lpszEnvVar, var, dwLen);
  802. return var;
  803. }