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:
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:
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:
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:
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:
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:
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:
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: */
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: */
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:
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:
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:"
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: }
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:
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: */
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: //
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: //
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: //
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: //
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: //
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: //
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: //
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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>