|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2001-03-19 22:41 UTC] ron dot baldwin at sourceprose dot com
Under the category of "You Can Never Have Too Much Information On A Bug", here is my experience with the "CGI App Misbehaved" bug (related bug reports: #8571 and #8744).
Bug report summary:
A PHP script that sends a 'Location' header directive and makes a database connection causes IIS to intermittently return a 'Gateway Error 502' when the client browser asks for the page specified in the redirect. The address bar will correctly show the page that should have loaded when the error is displayed, and pressing F5 (refresh) will correctly load the page. Note that the error is (seemingly) completely random, sometimes you get it, other times the script works correctly. The error message text is:
-- Begin --
CGI Error
The specified CGI application misbehaved by not returning a complete set of HTTP headers. The headers it did return are:
-- End --
This error depends on:
1) Using PHP in CGI mode (i.e. using php.exe, the ISAPI dll does not seem to produce this error, although it has it's own problems).
2) A PHP script that connects to a database (tested both mssql_connect and odbc_connect) *and* sends a 'Location:' header. If the script redirects to something other than a .php file (i.e. .html or .pdf) or does not connect to the database, the error will not occur.
3) A successful db connection. If the db connect call fails (due to bad password, etc.), the redirect always works.
This error does *not* depend on:
1) A fully qualified 'Location' header. The following also fails:
header('Location: http://10.0.0.30/test/done.php');
2) The relative order of the header() and xxx_connect() calls in 'doit.php'. I wouldn't expect the order to affect the result, but I checked it anyway.
Interesting note: While upgrading the server to SP1 to see if it had any affect on this bug, I continued to test while it was in the process of performing the upgrade. It was very difficult (I gave up) trying to get the error. The CPU was at 100% due to the upgrade which (obviously) caused the web server performance to suffer. Combined with the fact that it occurs randomly anyway, this suggests that it may have something to do with the timings between the request for doit.php, execution of doit.php, and the request for done.php.
Hardware/Software:
Web server:
Win2000 Server (with and without SP1)
IIS 5.0
PHP 4.0.4pl1 (CGI mode)
Database server:
WinNT 4.0 SP6
MS-SQL 7.0
Client:
Win2000 Professional SP1
IE 5.5 SP1
========================================
Test scripts
========================================
start.php
----------------------------------------
<html><head><title>Test</title></head>
<body>
<a href="doit.php">Do test</a>
</body></html>
----------------------------------------
doit.php
----------------------------------------
<?php
// Either connect line will cause the error
$conn = mssql_connect('host', 'uid, 'pwd') or die("couldn't connect");
//$conn = odbc_connect('dsn', 'uid', 'pwd') or die("couldn't connect");
header('Location: done.php');
?>
----------------------------------------
done.php
----------------------------------------
<html><head><title>Test</title></head>
<body>
Test complete
<p><a href="start.php">Reset test</a>
</body></html>
========================================
Here are actual HTTP headers recorded using a packet sniffer:
========================================
Scenario #1 - CGI Error Occurs
========================================
Client request
----------------------------------------
GET /test/doit.php HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/msword, */*
Referer: http://10.0.0.30/test/start.php
Accept-Language: en-us
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)
Host: 10.0.0.30
Connection: Keep-Alive
----------------------------------------
Server response
----------------------------------------
HTTP/1.1 302 Object Moved
Location: done.php
Server: Microsoft-IIS/5.0
Content-Type: text/html
Connection: close
Content-Length: 131
----------------------------------------
Client request (automatic IE request)
----------------------------------------
GET /test/done.php HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/msword, */*
Referer: http://10.0.0.30/test/start.php
Accept-Language: en-us
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)
Host: 10.0.0.30
Connection: Keep-Alive
----------------------------------------
Server response (Incorrect response)
----------------------------------------
HTTP/1.1 502 Gateway Error
Server: Microsoft-IIS/5.0
Date: Tue, 20 Mar 2001 00:30:10 GMT
Connection: close
Content-Length: 215
Content-Type: text/html
----------------------------------------
Client request (F5 - refresh)
----------------------------------------
GET /test/done.php HTTP/1.1
Accept: */*
Referer: http://10.0.0.30/test/start.php
Accept-Language: en-us
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)
Host: 10.0.0.30
Connection: Keep-Alive
----------------------------------------
Server response
----------------------------------------
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Tue, 20 Mar 2001 00:30:11 GMT
Connection: close
X-Powered-By: PHP/4.0.4pl1
Content-type: text/html
========================================
========================================
Scenario #2 - Correct operation
========================================
Client request
----------------------------------------
GET /test/doit.php HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/msword, */*
Referer: http://10.0.0.30/test/start.php
Accept-Language: en-us
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)
Host: 10.0.0.30
Connection: Keep-Alive
----------------------------------------
Server response
----------------------------------------
HTTP/1.1 302 Object Moved
Location: done.php
Server: Microsoft-IIS/5.0
Content-Type: text/html
Connection: close
Content-Length: 131
----------------------------------------
Client request (automatic IE request)
----------------------------------------
GET /test/done.php HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/msword, */*
Referer: http://10.0.0.30/test/start.php
Accept-Language: en-us
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)
Host: 10.0.0.30
Connection: Keep-Alive
----------------------------------------
Server response (Correct response)
----------------------------------------
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Tue, 20 Mar 2001 00:33:02 GMT
Connection: close
X-Powered-By: PHP/4.0.4pl1
Content-type: text/html
========================================
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Tue Nov 04 18:00:01 2025 UTC |
I can reproduce the problem with a simple "bounce" page (used for measuring roundtrip times). With two active browser windows requesting the same page, I see this error repeat (apparently at random) on one out of 10-40 thousand requests. This is with PHP 4.1.2 on Windows 2000 Professional w/ IIS 5 and all the current updates from Windows Update. bounce.php ---------- <html> <head> <title>BOUNCE</title> <?php list($usec, $sec) = explode(" ",microtime()); $now = intval(1000*((float)$usec + (float)$sec)); $i = (isset($i) ? $i : 0); $n = (isset($n) ? $n : 10); $s = (isset($s) ? $s : 0); $r = (isset($r) ? $r : 0); ?> <?php if (0 == $i) { ?> <meta http-equiv="Refresh" content="0; URL=bounce.php?s=<?= $s ?>&r=<?= $r ?>&i=1&t=<?= $now ?>"> <?php } else if ($i < $n) { ?> <meta http-equiv="Refresh" content="0; URL=bounce.php?s=<?= $s ?>&r=<?= $r ?>&i=<?= 1 + $i ?>&t=<?= $t ?>"> <?php } else { ?> <?php $e = $now - $t; $r = $r + $n; $s = $s + $e; ?> <meta http-equiv="Refresh" content="3; URL=bounce.php?s=<?= $s ?>&r=<?= $r ?>"> <?php } ?> </head> <body> <?php if ($i < $n) { ?> <h1>BOUNCING</h1> <?php } else { ?> <h1>BOUNCE</h1> <table border=1> <tr><th>milliseconds</th><th>requests</th></tr> <tr><td><?= intval($e/$n) ?></td><td><?= $n ?></td><td>latest</td></tr> <tr><td><?= intval($s/$r) ?></td><td><?= $r ?></td><td>cumulative</td></tr> </table> <?php } ?> </body> </html>Kai, when you mentioned 'under 2MBit' were you meaning 2 Mbit/s or 2 MB/s? IIS prompts for KB/s, so I assumed for your solution you limited it to 2,048 KB/s? For our machine, we can limit it to < 350 KB/s and it appears to solve the problem, but I haven't stress-tested the server yet. This is good news for us - we can get paid for our project. But this bug will likely re-appear as server equipment gets faster. In other news we've pulled down the PHP source and did some further debugging... we found: 1. The error occurs on the first 'dbexec' command run in the mssql driver. For example, in PHP it will not happen on a mssql_connect(), but on the *next* mssql command issued in that script (say a mssql_select_db() or a mssql_query()). Our test script will run fine if we connect to mssql but don't use it in any way after that. 2. We laced the PHP source with some fwrite()'s to stout to watch the flow of the code - when PHP failed, we didn't get any of these, not even the ones placed *before* the mssql_connect() function definition. This may indicate that PHP is not crashing, but IIS may not even be loading it in the first place! (strangely seems to conflict with point 1). 3. We can reproduce it quite freely under varying bandwidth and on fast and slow computers (happens more frequently on faster machines). Limiting the bandwidth on IIS does seem to fix the problem (under single user situations) 4. We have ported the same script to ASP without reproducing this bug. ASP seems to run fine. 5. We can reproduce it under ISAPI and CGI using our test script (below) - the ISAPI script fails less often than the CGI version, but it still fails. 6. It breaks under ODBC, too. IMHO, I believe that it is critical to the ongoing success of PHP to have it working with Windows 2000 and/or MSSQL. Simply blaming it on Microsoft and closing the bug is unacceptable, especially as this bug seems to occur more often with faster servers. This bug will hurt more and more people into the near future, and damage the reputation of this fine product (PHP, that is). Our investigations do suggest that the bug is likely in IIS, but nevertheless it will hurt PHP more than Microsoft as the bug affects more people. With regards to your suggestion on PERL/ASP exhibiting the same problem, I haven't been able to reproduce it with them (if you supply your ASP test script, I'll test it with my config here). I'd appreciate you marking this bug 'open' rather than 'bogus', as it needs to be solved regardless of who is to blame. Best regards, Scott NB: My company is working on the problem to try to solve it - I will post our findings or workarounds to this bug page. If anyone knows of other related bugs I would appreciate it if you can post them here. NB2: Has anyone had success with a Cygwin-compliled PHP? ---------- Test Harness Follows -------------- index.php: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>MSSQL Test</title> <script> var count = 0; </script> </head> <!-- frames --> <frameset cols="20%,20%,20%,20%,20%"> <frame name="frame1" src="mssql_test.php?j=<?php echo ++$j; ?>"> <frame name="frame2" src="mssql_test.php?j=<?php echo ++$j; ?>"> <frame name="frame3" src="mssql_test.php?j=<?php echo ++$j; ?>"> <frame name="frame4" src="mssql_test.php?j=<?php echo ++$j; ?>"> <frame name="frame5" src="mssql_test.php?j=<?php echo ++$j; ?>"> </frameset> </html> mssql_test.php: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>MSSQL Test</title> <script> top.count++; </script> </head> <body> <?php print "<h1>$j</h1>\n"; $dbConn = mssql_connect("localhost", "sa", "<password>"); mssql_select_db("cogs", $dbConn); $result = mssql_query("SELECT * FROM site", $dbConn); while ($array = mssql_fetch_row($result)) { print "<pre>"; print_r($array); print "</pre>"; } ?> <script> if (top.count == 5) { top.location = 'frameset.php?j=<?php echo $j ?>'; } </script> </body> </html> ---------- End test harness ----------We have the same problem and the same configuration Web server: Win2000 Server IIS 5.0 PHP 4.2.2 (CGI mode) Database server: WinNT 4.0 SP6 MS-SQL 7.0 Client: Win2000 Professional SP1 IE 5.5 SP1 The CGI Error is apperaing now and then. Tried all patches (MDAC 2.7 ...) and "Check File exists.." but nothing really worked.. I would love to try to change these perfomance options as Ottawa posted, but unfortunately I don't know where in IIS to set them! Could somebody give me a helping hand on that ?? I will then test and post the results! Thanks EwaldJust an idea: does it help to close the database connection right before calling the header('Location: URL') function? I understand that putting in a timer helps (most of the time), but I think that's a bit of an ugly solution. I mean: how long should you wait? The ideal number of microseconds to sleep depends on the server, the load on the server, and so on. As the problem occurs when there is a database connection, I wonder if it goes away if this connection is closed, before doing the real redirecting. Sadly enough I can't test this myself, because I don't have access to a server that's fast enough. Anyone?I can reproduce the error with the php script below(a simple script that do nothing but keeps refreshing itself with a different value of parameter passed into it as GET, REMEMBER to open serveral instances to run it.). IMHO, this is not a database problem(neither mysql nor mssql), and this might be a problem that IIS cannot handle sucha high frequent call to `php.exe', and it does assume that php.exe returns nothing!! (Am I right? :-D) doit.php 8<----------------------------------------------- <? echo "<html><head><title>testing</title>"; if (isset($_GET["times"])) $times = ($_GET["times"]) + 1; else $times = 0; //SEE??? even i comment the statement out, infamous cgi error occurs, STILL! //$conn = mssql_connect('127.0.0.1', 'sa', '') or die("couldn't connect"); echo "<meta http-equiv=\"Refresh\" content=\"0; URL=doit.php?times=" . $times . "\"></head></html>"; echo "Trying CGI"; echo "<BR>$times"; //header("doit.php"); ?> 8<----------------------------------------------- My config.: Windows 2000 Advanced Server with SP3 PHP 4.3.1 / 4.2.3 (both tested to have CGI problem) MSSQL Server 2000A temporary fix to this issue would be to create a custom 502 error page that automatically refreshes the page (typically under c:\WINNT\Help\issHelp\common - but don't forget that you also have to set the options in Internet Services Manager... Go to Start, Programs, Administrative Tools, Internet Services Manager. Click on your host, then right click your website and select Properties. Then click the "Custom Errors" tab. Find 502 in the list and ensure that it is set to "File" and that it is pointing to your custom page). The only problem with this approach is if the user was submitting a form, they will be presented with a dialog box asking if they want to "Retry" or "Cancel", so you should mention somewhere on your custom page that they should click "Retry". Here's the custom page I hacked together out of the default 502.htm for my university: ------------ BEGIN ------------ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> <html dir=ltr> <head> <style> a:link {font:8pt/11pt verdana; color:FF0000} a:visited {font:8pt/11pt verdana; color:#4e4e4e} </style> <META NAME="ROBOTS" CONTENT="NOINDEX"> <title>The page cannot be displayed</title> <META HTTP-EQUIV="Content-Type" Content="text-html; charset=Windows-1252"> </head> <body bgcolor="FFFFFF" onload="setTimeout( "location.reload();", 50 );"> <h1>502: CGI Error</h1> If it asks whether to Retry or Cancel, click <b>Retry</b>. </body> </html> ------------ END ------------ The reason I used setTimeout was because if I just used location.reload(), MSIE doesn't display any text from the page, it just reloads. So I figured a really small timeout value should make it display the page first (just in case the user gets presented with that "Retry/Cancel" box).