This is my first CGI app and I'm writing it in C. The intent of the C app is basically to echo all the environment variables passed the C CGI app, and any file attached as well.
It works on the Mac as expected, for the most part. I do have a second question about the post method boundary marker that is posed below.
First question:
This CGI setup does not "always" work when I access it from my Windows XP machine on my home network. I have lots of info to illustrate the issue, so please bare with me as I present it.
I'm running Tiger (OS X 1.4.11) on a Mac Pro. I've enabled web sharing and that seems to work fine from my Mac and from my Windows machine. I'm running the default Apache server that comes with Tiger. On Windows, I'm running IE 6 under XP.
I've created a simple html form with various controls to see how data and values are passed to the CGI program. Here is the form.
My CGI program is called "getmethod" (I know, I'm using "post" - but it's evolved and I didn't change the name...) It's written in C. Here's is that code:
Running getmethod from the command line in Terminal and under XCode produces the expected output (although the environment variables are different). The piece I focus on is the content-type header. Works great on the command line, works great on the Mac (Safari) and doesn't work the first time for Internet Explorer. Here is what happens. On Windows, I direct the url to the server on my Mac with the formtest.html to bring up the form. Works great. I click "test me" (aka "submit") and I get the very vague "Internal Server Error" screen. See attached screen grab.
Now, interestingly enough, while on this "Internal Server Error" screen, if I click the IE's Refresh button, I get the prompt for "do I want to resend this form", I click RETRY, and VIOLA! The output of the CGI program appears. This is eating my lunch. Why does it fail the first request, but work on the refresh?
The initial request error status code is 500, which seems to be a catch-all error code. If I tail the error_log and access_log, I can see the requests and errors, but I don't know what to do to fix them. Here's the access log:
The line in RED above is matched with the line in RED below in the error log. The line above in BLUE, showing success, is what I get when I click REFRESH in IE.
The GREEN line is interesting too. When I click REFRESH in IE when positioned on the FORM itself, this is what gets logged. In my IE session, I see no changes.
Here's the error log:
(As you can see, I was up late last night working on this!)
Why do you think I am getting the premature end of script headers? It obviously works under Safari, the command line output proves this, and is working under IE after the refresh, just not the first time. I've chmod'ed 755 so many times I'm blue in the face. I've tried renaming the getmethod execute to getmethod.cgi and there is no change in behavior. I've tried dozens of combinations of "\n\n" to add a blank line under the content-type header line. I googled and saw one tip for making sure I added fflush(stdout), and I didn't have that, so I added it to my C program.
So, what's the deal? Is this an Apache thing?
---------------------------------------------------------------------------------------------------
Now, for my second question....
When the output page is served up, for method="post", one of the environment variables is the boundary separator for the content-type data in stdin. See the attached image with yellow. Notice the 4 hyphens that lead off the start of the boundary separator.
Now, look down at data (white back ground). The boundary marker starts off the 6 hyphens, and the last marker even has two trailing hyphens. Is how this is supposed to work is to simply look at the "line" and if the boundary marker is on the line, then treat that line as a boundary marker? It seems reasonable that this would be the approach to take.
Sorry for the long post. Now that I've spilled my guts, any other advice for best practices would be appreciated too.
Thanks, Todd
It works on the Mac as expected, for the most part. I do have a second question about the post method boundary marker that is posed below.
First question:
This CGI setup does not "always" work when I access it from my Windows XP machine on my home network. I have lots of info to illustrate the issue, so please bare with me as I present it.
I'm running Tiger (OS X 1.4.11) on a Mac Pro. I've enabled web sharing and that seems to work fine from my Mac and from my Windows machine. I'm running the default Apache server that comes with Tiger. On Windows, I'm running IE 6 under XP.
I've created a simple html form with various controls to see how data and values are passed to the CGI program. Here is the form.
Code:
<html>
<head>
<title>Form Test</title>
</head>
<body>
<form id='f1' enctype="multipart/form-data" method="post"
action="http://192.168.1.100/cgi-bin/getmethod">
<input name="lowercase" type="text" maxlength="50" size="50" id="t1"><br />
<input name="password" type="password" maxlength="50" size="50" id="p1"><br />
<input name="uppercase" type="text" maxlength="50" size="50" id="t2" readonly><br />
<input type="hidden" name="email" value="me@example.com">
<input type="textarea" size="100,5" maxlength="500" value="enter your story here..." id="t3"><br />
<input type="file" name="myfile"><br />
<input type="submit" value="test me"><br />
<textarea rows="5" cols="100">Wow. Data.
</textarea><br />
</body>
<html>
My CGI program is called "getmethod" (I know, I'm using "post" - but it's evolved and I didn't change the name...) It's written in C. Here's is that code:
Code:
#include <stdio.h>
#include <string.h>
//int main (int argc, const char * argv[]) {
int main (int argc, char * const argv[], char * envp[] ) {
int i = 0 , j ; //, rc = 0 ;
char * position ;
char buffer[256] ;
char * ctype ;
int clength = 0 ;
printf("Content-Type: text/html\n\n") ;
printf("<html>") ;
printf("<head><title>From A C Program</title>") ;
printf("<style type='text/css'>\n") ;
printf(".yellow { background-color: yellow ; border: 1px solid black ;}\n") ;
printf("</style>\n") ;
printf("</head>\n") ;
printf("<body><table class='yellow'>\n") ;
while (envp[i]) {
strcpy(buffer, envp[i]) ;
position = strchr(buffer, '=') ;
if (!position) {
printf("\tcould not find equal sign in %s<br />\n", envp[i]) ;
continue ;
}
buffer[position-buffer] = 0 ; // null term the environment variable name
printf("<tr><td>%-30s</td><td>%s</td></tr>\n", buffer, position+1) ;
for ( j = 0 ; j < strlen(buffer) ; ++j) toupper(buffer[j]) ;
if (strcmp("CONTENT_TYPE",buffer)==0) {
ctype = position+1 ;
}
if (strcmp("CONTENT_LENGTH",buffer)==0) {
clength = atoi(position+1) ;
}
i++ ;
}
printf("</table>\n");
printf("<table frame='box' rules='all' cellpadding='5' >\n<tr><td>data... bytes = %d</td>\n<td>", clength );
for ( i = 0 ; i < clength ; ++i ) {
char c = fgetc(stdin) ;
if (c=='\n') printf("<br />") ;
else printf( "%c", c ) ;
}
printf("</td>\n</tr>\n") ;
printf("</table>\n</body>\n");
printf("</html>\n");
fflush(stdout) ;
return 0;
}
Running getmethod from the command line in Terminal and under XCode produces the expected output (although the environment variables are different). The piece I focus on is the content-type header. Works great on the command line, works great on the Mac (Safari) and doesn't work the first time for Internet Explorer. Here is what happens. On Windows, I direct the url to the server on my Mac with the formtest.html to bring up the form. Works great. I click "test me" (aka "submit") and I get the very vague "Internal Server Error" screen. See attached screen grab.
Now, interestingly enough, while on this "Internal Server Error" screen, if I click the IE's Refresh button, I get the prompt for "do I want to resend this form", I click RETRY, and VIOLA! The output of the CGI program appears. This is eating my lunch. Why does it fail the first request, but work on the refresh?
The initial request error status code is 500, which seems to be a catch-all error code. If I tail the error_log and access_log, I can see the requests and errors, but I don't know what to do to fix them. Here's the access log:
Code:
todd-burchs-computer:/var/log/httpd toddburch$ tail access_log
192.168.1.102 - - [15/Mar/2008:02:23:20 -0500] "POST /cgi-bin/getmethod HTTP/1.1" 500 608
192.168.1.102 - - [15/Mar/2008:02:30:42 -0500] "POST /cgi-bin/getmethod HTTP/1.1" 200 3431
192.168.1.102 - - [15/Mar/2008:02:41:46 -0500] "POST /cgi-bin/getmethod HTTP/1.1" 500 608
192.168.1.102 - - [15/Mar/2008:02:41:49 -0500] "POST /cgi-bin/getmethod HTTP/1.1" 200 3423
192.168.1.102 - - [15/Mar/2008:02:45:50 -0500] "GET /~toddburch/formtest.html HTTP/1.1" 200 755
192.168.1.102 - - [15/Mar/2008:02:45:52 -0500] "POST /cgi-bin/getmethod HTTP/1.1" 500 608
192.168.1.102 - - [15/Mar/2008:11:26:03 -0500] "POST /cgi-bin/getmethod HTTP/1.1" 200 3416
[color=green]192.168.1.102 - - [15/Mar/2008:11:31:55 -0500] "GET /~toddburch/formtest.html HTTP/1.1" 304 -[/color]
[color=red]192.168.1.102 - - [15/Mar/2008:11:32:11 -0500] "POST /cgi-bin/getmethod HTTP/1.1" 500 608[/color]
[color=blue]192.168.1.102 - - [15/Mar/2008:11:36:02 -0500] "POST /cgi-bin/getmethod HTTP/1.1" 200 3416[/color]
The line in RED above is matched with the line in RED below in the error log. The line above in BLUE, showing success, is what I get when I click REFRESH in IE.
The GREEN line is interesting too. When I click REFRESH in IE when positioned on the FORM itself, this is what gets logged. In my IE session, I see no changes.
Here's the error log:
Code:
todd-burchs-computer:/var/log/httpd toddburch$ tail error_log
[Sat Mar 15 02:14:40 2008] [error] [client 192.168.1.102] Premature end of script headers: /Library/WebServer/CGI-Executables/getmethod
[Sat Mar 15 02:15:36 2008] [error] [client 192.168.1.102] Premature end of script headers: /Library/WebServer/CGI-Executables/getmethod
[Sat Mar 15 02:16:02 2008] [error] [client 192.168.1.102] Premature end of script headers: /Library/WebServer/CGI-Executables/getmethod
[Sat Mar 15 02:20:39 2008] [error] [client 192.168.1.102] Premature end of script headers: /Library/WebServer/CGI-Executables/getmethod
[Sat Mar 15 02:21:11 2008] [error] [client 192.168.1.100] File does not exist: /Library/WebServer/Documents/favicon.ico
[Sat Mar 15 02:21:15 2008] [error] [client 192.168.1.102] Premature end of script headers: /Library/WebServer/CGI-Executables/getmethod
[Sat Mar 15 02:23:20 2008] [error] [client 192.168.1.102] Premature end of script headers: /Library/WebServer/CGI-Executables/getmethod
[Sat Mar 15 02:41:46 2008] [error] [client 192.168.1.102] Premature end of script headers: /Library/WebServer/CGI-Executables/getmethod
[Sat Mar 15 02:45:52 2008] [error] [client 192.168.1.102] Premature end of script headers: /Library/WebServer/CGI-Executables/getmethod
[color=red][Sat Mar 15 11:32:11 2008] [error] [client 192.168.1.102] Premature end of script headers: /Library/WebServer/CGI-Executables/getmethod[/color]
(As you can see, I was up late last night working on this!)
Why do you think I am getting the premature end of script headers? It obviously works under Safari, the command line output proves this, and is working under IE after the refresh, just not the first time. I've chmod'ed 755 so many times I'm blue in the face. I've tried renaming the getmethod execute to getmethod.cgi and there is no change in behavior. I've tried dozens of combinations of "\n\n" to add a blank line under the content-type header line. I googled and saw one tip for making sure I added fflush(stdout), and I didn't have that, so I added it to my C program.
So, what's the deal? Is this an Apache thing?
---------------------------------------------------------------------------------------------------
Now, for my second question....
When the output page is served up, for method="post", one of the environment variables is the boundary separator for the content-type data in stdin. See the attached image with yellow. Notice the 4 hyphens that lead off the start of the boundary separator.
Now, look down at data (white back ground). The boundary marker starts off the 6 hyphens, and the last marker even has two trailing hyphens. Is how this is supposed to work is to simply look at the "line" and if the boundary marker is on the line, then treat that line as a boundary marker? It seems reasonable that this would be the approach to take.
Sorry for the long post. Now that I've spilled my guts, any other advice for best practices would be appreciated too.
Thanks, Todd