[Patches] [ python-Patches-511380 ] add CGIHTTPServer error supt for Win32

noreply@sourceforge.net noreply@sourceforge.net
Thu, 31 Jan 2002 17:04:21 -0800


Patches item #511380, was opened at 2002-01-31 11:53
You can respond by visiting: 
http://sourceforge.net/tracker/?func=detail&atid=305470&aid=511380&group_id=5470

Category: Library (Lib)
Group: Python 2.3
Status: Open
Resolution: None
>Priority: 3
Submitted By: Wesley J. Chun (wesc)
Assigned to: Nobody/Anonymous (nobody)
Summary: add CGIHTTPServer error supt for Win32

Initial Comment:
Patch suggestion for CGIHTTPServer.py in the Python 
Standard Library:

"add CGIHTTPServer error supt for Win32"
by Wesley J. Chun (wesc@rocketmail.com)

Python 2.2: Win32 and Unix (Solaris 2.8)

Description
===========
The CGIHTTPServer module uses the fork() mechanism 
provided by Unix
to run the CGI script, however, since the Win32 
platform does not
support fork(), CGIHTTPServer relies on os.popen2() to 
execute the
process and to retrieve the results.

This all works however, when you are debugging CGI 
applications, the
information provided by os.popen2() does not suffice 
because execution
errors (i.e., Python tracebacks) are sent to standard 
error, which are
ignored by os.popen2().

The suggested patch is to check first if os.popen3() 
is available to
us (otherwise do the normal check for os.popen2()).  
If we have access
to os.popen3(), use it instead and log any errors 
which appear via the
child's stderr.

Without the patch, it makes it much more difficult to 
debug the same
CGI application when run on Win32 because no errors 
are made apparent
in the error log.  Examples below -- first a 
successful call is made
(200), followed by a failed call due to a Python 
error, resulting in a
500 (ISE) appearing back on the web client.

The patch would bring the Win32 execution of 
CGIHTTPServer much closer
to its Unix counterpart.  A 3-line diff of the changes 
is available just
below the examples.  (This patch is valid for all 2.x 
releases and may
be backported to the 2.[012].x releases if necessary.)


Unix
====
** NO PATCH NEEDED **

# cgihttpd.py			# from CGIHTTPServer 
import test; test()
Serving HTTP on port 8000 ...
localhost - - [30/Jan/2002 10:13:20] "GET /cgi-
bin/rwrl.py?admin=1 HTTP/1.0" 200 -
Traceback (most recent call last):
  File "/home/wesc/public_html/cgi-bin/rwrl.py", line 
177, in ?
    main()
  File "/home/wesc/public_html/cgi-bin/rwrl.py", line 
153, in main
    cd = tag % ('date', title, 'date')
UnboundLocalError: local variable 'title' referenced 
before assignment
localhost - - [30/Jan/2002 10:13:20] CGI script exit 
status 0x100


Windoze
=======
** BEFORE PATCH **

C:\dev>python cgihttpd.py
Serving HTTP on 0.0.0.0 port 8000 ...
solo - - [30/Jan/2002 21:37:27] "GET /cgi-bin/rwrl.py?
admin=-1 HTTP/1.1" 200 -
solo - - [30/Jan/2002 21:37:27] command: c:\python22
\python.exe -u C:\dev\cgi-bin\rwrl.py
solo - - [30/Jan/2002 21:37:28] CGI script exit status 
0x1


** AFTER PATCH **

C:\dev>python cgihttpd.py
Serving HTTP on 0.0.0.0 port 8000 ...
solo - - [30/Jan/2002 21:46:22] "GET /cgi-bin/rwrl.py?
admin=-1 HTTP/1.1" 200 -
solo - - [30/Jan/2002 21:46:22] command: c:\python22
\python.exe -u C:\dev\cgi-bin\rwrl.py
solo - - [30/Jan/2002 21:46:23] Traceback (most recent 
call last):
  File "C:\dev\cgi-bin\rwrl.py", line 178, in ?
    main()
  File "C:\dev\cgi-bin\rwrl.py", line 160, in main
    cd = tag % ('date', title, 'date')
UnboundLocalError: local variable 'title' referenced 
before assignment

solo - - [30/Jan/2002 21:46:23] CGI script exit status 
0x1

DIFF -C3
========

% diff -C3 CGIHTTPServer.py.CVS CGIHTTPServer.py.new
*** CGIHTTPServer.py.CVS	Fri Oct 26 03:38:14 
2001 UTC
--- CGIHTTPServer.py.new	Thu Jan 31 10:58:16 
2002 PST
***************
*** 41,46 ****
--- 41,47 ----
      # Determine platform specifics
      have_fork = hasattr(os, 'fork')
      have_popen2 = hasattr(os, 'popen2')
+     have_popen3 = hasattr(os, 'popen3')
  
      # Make rfile unbuffered -- we need to read one 
line and then pass
      # the rest to a subprocess, so we can't use 
buffered input.
***************
*** 123,129 ****
              return
          ispy = self.is_python(scriptname)
          if not ispy:
!             if not (self.have_fork or 
self.have_popen2):
                  self.send_error(403, "CGI script is 
not a Python script (%s)" %
                                  `scriptname`)
                  return
--- 124,130 ----
              return
          ispy = self.is_python(scriptname)
          if not ispy:
!             if not (self.have_fork or 
self.have_popen2 or self.have_popen3):
                  self.send_error(403, "CGI script is 
not a Python script (%s)" %
                                  `scriptname`)
                  return
***************
*** 214,222 ****
                  self.server.handle_error
(self.request, self.client_address)
                  os._exit(127)
  
!         elif self.have_popen2:
!             # Windows -- use popen2 to create a 
subprocess
              import shutil
              os.environ.update(env)
              cmdline = scriptfile
              if self.is_python(scriptfile):
--- 215,227 ----
                  self.server.handle_error
(self.request, self.client_address)
                  os._exit(127)
  
!         elif self.have_popen3 or self.have_popen2:
!             # Windows -- use popen2 or popen3 to 
create a subprocess
              import shutil
+             if self.have_popen3:
+ 		cmd = os.popen3
+             else:
+ 		cmd = os.popen2
              os.environ.update(env)
              cmdline = scriptfile
              if self.is_python(scriptfile):
***************
*** 232,238 ****
                  nbytes = int(length)
              except:
                  nbytes = 0
!             fi, fo = os.popen2(cmdline, 'b')
              if self.command.lower() == "post" and 
nbytes > 0:
                  data = self.rfile.read(nbytes)
                  fi.write(data)
--- 237,247 ----
                  nbytes = int(length)
              except:
                  nbytes = 0
!             files = cmd(cmdline, 'b')
!             fi = files[0]
!             fo = files[1]
!             if self.have_popen3:
! 		fe = files[2]
              if self.command.lower() == "post" and 
nbytes > 0:
                  data = self.rfile.read(nbytes)
                  fi.write(data)
***************
*** 239,244 ****
--- 248,258 ----
              fi.close()
              shutil.copyfileobj(fo, self.wfile)
+             if self.have_popen3:
+ 		anyerr = fe.read()
+ 		fe.close()
+ 		if anyerr:
+ 		    self.log_error('%s', anyerr)
              sts = fo.close()
              if sts:
                  self.log_error("CGI script exit 
status %#x", sts)
              else:


----------------------------------------------------------------------

You can respond by visiting: 
http://sourceforge.net/tracker/?func=detail&atid=305470&aid=511380&group_id=5470