CGI


Click here to change the theme.

There are a couple of things that are necessary to know about to get a CGI program to work in a Windows environment. First I will say that I am assuming you know the basics about CGI; that is, that CGI programs read from standard input and write to standard output. I can write a little introduction but the basics are available elsewhere so for now I will write a bit about what I have discovered that is not documented as well. To learn about CGI, one good place to go is CGI - Common Gateway Interface in The World Wide Web Consortium web site. In particular you are likely to want to look at the C language sample programs in the cgi-src directory of the NCSA FTP site.

The most important thing to know is that for reading data from standard input it is probably not possible to detect the end of file using functions for that purpose. They might work in some environments but seem to not work for a Windows environment. It seems necessary to use the CONTENT_LENGTH Environment Variable to determine how much data to read and then read exactly that much data. If an attempt to read more data is made then the program will try to wait (block) for more data which will cause the program to actually hang. For advanced CGI programs it might be correct to expect more data but unless you know what you are doing it probably will not work. Notice that the sample programs in the NCSA FTP site use the CONTENT_LENGTH Environment Variable to determine how much data to read.

The next thing to know is that the functions we usually use for standard input seem to not work. What I mean are the C Runtime Library functions (such as gets()) and the C++ STL classes (such as cin). The problem with most of them is that they require a CrLf delimiter at the end of the data and/or more data to be available than is requested.

The following code will probably help. I hope it saves you time; you still might need to play with it but hopefully it will require less time than it took for me to figure things out. The GetData function will use the CONTENT_LENGTH Environment Variable (if it exists) to determine how much data to read. It will also support reading from other types of files that would probably not exist in most CGI environments. This is so that you can test your CGI program as a console program without a server. You can use redirected input instead of posted form data. You will probably need a way to capture sample data for testing purposes and that is something else I might add later.

Sample use of the GetData function is:

	DWORD dwSize;
	char *Data;
dwSize = GetData(&Data);
if (dwSize)
	cout << Data << endl;
else
	cout << "No Data\n";
delete [] Data;

The function is:

DWORD GetData(char **pBuffer) {
	DWORD FileType, FileSizeHigh, dwSize;
	HANDLE StdIn;
	char *Temp;
StdIn = GetStdHandle(STD_INPUT_HANDLE);
FileType = GetFileType(StdIn);
dwSize = ((Temp=getenv("CONTENT_LENGTH")) ? (atoi(Temp)) : 0);
if (!dwSize)
	if (FileType != FILE_TYPE_DISK && FileType != FILE_TYPE_CHAR) {
		*pBuffer = NULL;
		return 0;
		}
else {
	dwSize = GetFileSize(StdIn, &FileSizeHigh);
	if (!dwSize || FileSizeHigh || FileSizeHigh == 0xFFFFFFFF) {
		// Either no data, too big or there's an error
		*pBuffer = NULL;
		return 0;
		}
}
*pBuffer = new char[dwSize+1];
ReadFile(StdIn, *pBuffer, dwSize, &dwSize, NULL);
(*pBuffer)[dwSize] = 0;
return dwSize;
}

Revised Information

I have learned more since I wrote the above. I have now figured out how to use the STL to get the posted form data. I now know that cin::read can be used to read the data. The following is a revised GetData function.

DWORD GetData(char **pBuffer) {
	char *ContentLength;
	DWORD dwSize;
ContentLength = getenv("CONTENT_LENGTH");
if (!ContentLength) {
	cout << "No CONTENT_LENGTH environment variable\n";
	*pBuffer = NULL;
	return 0;
	}
dwSize = atoi(ContentLength);
if (!dwSize) {
	*pBuffer = NULL;
	return 0;
	}
*pBuffer = new char[dwSize+1];
cin.read(*pBuffer, dwSize);
(*pBuffer)[dwSize] = 0;
return dwSize;
}

Sample use of the GetData function:

char *Data;
DWORD dwSize;
dwSize = GetData(&Data);
if (!dwSize || !Data) {
	cout << "No Data\n";
	return 16;
	}
cout << '\n' << dwSize << " bytes of form data\n";
delete [] Data;