A new project: counterCGI.
I set out to write some useful CGI executables after my first success with the testCGI example. This program
tells you a lot about the 'I' part of CGI: the Common Gateway Interface.
So I decided it was time to write a counter for webpages, based on a CGI executable. Perhaps not a counter
that is visible on screen, but one that works in the background. But perhaps not. We'll see what comes of it.
Below you see the source of what counterCGI looked like, yesterday evening. As was to be expected with new projects on new territory, it dind't work. It only produced errors. More about that later. First things first, so it's time for the....
Source of counterCGI
MODULE counterCGI;
FROM Arguments IMPORT GetEnv, ArgTable;
FROM InOut IMPORT WriteBf, WriteLn, WriteString;
FROM Strings IMPORT Assign, CAPS, pos, String, StrEq;
TYPE CGItype = (QueryString, RequestMethod, none);
ServerDataType = (Text, Html, Gif, Jpeg, PS, Mpeg);
VAR content : String;
EnvTable : ArgTable;
PROCEDURE InformServer (dataType : ServerDataType);
BEGIN
WriteString ('Content-Type : ');
CASE dataType OF
Text : WriteString ('text/plain'); |
Html : WriteString ('text/html'); |
Gif : WriteString ('image/gif'); |
Jpeg : WriteString ('image/jpeg'); |
PS : WriteString ('application/postscript'); |
Mpeg : WriteString ('video/mpeg');
END;
WriteLn;
WriteLn
END InformServer;
PROCEDURE CheckType (str : String) : CGItype;
BEGIN
CAPS (str); (* Convert entire string to capitals. *)
WriteString ("In CheckType now....");
WriteString (str);
WriteLn;
IF pos ('QUERY_STRING', str) = 0 THEN
RETURN QueryString
ELSIF pos ('REQUEST_METHOD', str) = 0 THEN
RETURN RequestMethod
ELSE
RETURN none
END
END CheckType;
PROCEDURE GetEnvVar (kind : CGItype; VAR res : String) : BOOLEAN;
VAR found : BOOLEAN;
i, j : CARDINAL;
TexBuf : String;
type : CGItype;
BEGIN
found := FALSE;
i := 0;
LOOP
IF EnvTable^ [i] = NIL THEN EXIT END;
Assign (TexBuf, EnvTable^ [i]^);
WriteString (TexBuf);
IF CheckType (TexBuf) = kind THEN
found := TRUE;
EXIT
END;
INC (i);
END;
IF NOT found THEN RETURN FALSE END;
i := 0;
LOOP
IF TexBuf [i] = '=' THEN EXIT END;
INC (i);
END;
INC (i);
j := 0;
LOOP
res [j] := TexBuf [i];
INC (i);
INC (j);
IF j > HIGH (res) THEN EXIT END;
IF i > HIGH (TexBuf) THEN EXIT END;
IF TexBuf [i] = 0C THEN
res [j] := 0C;
EXIT
END
END;
RETURN TRUE
END GetEnvVar;
BEGIN
GetEnv (EnvTable);
IF GetEnvVar (QueryString, content) = FALSE THEN
WriteString ('Environment string not found.');
WriteLn;
HALT
END;
InformServer (Text);
WriteString ('The QUERY_STRING variable is : ');
WriteString (content);
WriteLn;
WriteBf;
END counterCGI.
In itself, this sourcefile is correct. There is just one error triggering multiple other errors, causing
Apache to go berserk.
New features..
As you can see, this program already looks like a real Modula-2 program. It has new datatypes like:
TYPE CGItype = (QueryString, RequestMethod, none);
ServerDataType = (Text, Html, Gif, Jpeg, PS, Mpeg);
These TYPEs are still preliminary. It's just to show what I'm up to and by looking at later versions you can
see how good my current thoughts are. Or will have been... :o)
PROCEDURE InformServer (dataType : ServerDataType);
BEGIN
WriteString ('Content-Type : ');
CASE dataType OF
Text : WriteString ('text/plain'); |
Html : WriteString ('text/html'); |
Gif : WriteString ('image/gif'); |
Jpeg : WriteString ('image/jpeg'); |
PS : WriteString ('application/postscript'); |
Mpeg : WriteString ('video/mpeg');
END;
WriteLn;
WriteLn
END InformServer;
But you have no guarantee it will still be so in September... But the topic of this page is debugging, not
gobbledygook.
Debugging countCGI
So I made this new program. I compiled it and it produced an executable. Which ran, but did not produce the webpage I was counting on:
Internal Server Error
The server encountered an internal error or misconfiguration and was unable to complete your request.
Please contact the server administrator, root@kevlar.example.net and inform them of the time the error
occurred, and anything you might have done that may have caused the error.
More information about this error may be available in the server error log.
Apache/1.3.20 Server at hydrogen.fruttenboel Port 80
Remember this page: you will have to get used to it while debugging....
On my Slackware Linux system, that file happens to be /var/log/apache/error_log. And you can look at the last 10 lines by issuing the command 'tail /var/log/apache/error_log'. You can only do so as user 'root'. But give it a try and see what you get:
[Wed Aug 25 01:03:11 2004] [error] [client 192.168.72.64] malformed header from script. Bad header=DOCUMENT_ROOT=/var/www/htdocs: /var/www/cgi-bin/counterCGINow, if you get this, your CGI isn't outputting data in the right format. The server is very picky about etiquette. If you don't fully behave as prescribed, it will kick you in the butt and do plain old nothing. Not what we were after in the first place.
Look carefully at the sources. When I saw the program go berserk in the first place, I added some WriteString
lines in the functions GetEnvVar and CheckType. Just to see if the program worked as expected.
You can run a CGI program from the commandline as well. It will not find the environment strings you need to
find. Unless you make one especially for this purpose, like so:
bash$ QUERY_STRING=HiThere
bash$ export QUERY_STRING
This will put a CGI style environment string in your Unix environment for debugging. Which showed the program
worked well. But what the heck was causing the error when the data went to the Apache server?
To cut a long story short: I fooled Apache and Apache kicked me in the by now well known place. The webserver wants:
The problem was solved by putting the 'InformServer' line just before the 'IF..' statement.
Wrapping up
As can be seen in the source, I wanted to send plain text data to the server. This proved to be a good idea for this webpage topic. If you want to debug a CGI executable do as follows:
Page created on August 25, 2004
Page equipped with FroogleBuster technology