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