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);//accomodate 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