mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	
		
			
	
	
		
			244 lines
		
	
	
	
		
			5.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			244 lines
		
	
	
	
		
			5.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
								 | 
							
								"""PythonCGISlave.py
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								This program can be used in two ways:
							 | 
						||
| 
								 | 
							
								- As a Python CGI script server for web servers supporting "Actions", like WebStar.
							 | 
						||
| 
								 | 
							
								- As a wrapper for a single Python CGI script, for any "compliant" Mac web server.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								See CGI_README.txt for more details.
							 | 
						||
| 
								 | 
							
								"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Written by Just van Rossum, but partly stolen from example code by Jack.
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								LONG_RUNNING = 1  # If true, don't quit after each request.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import MacOS
							 | 
						||
| 
								 | 
							
								MacOS.SchedParams(0, 0)
							 | 
						||
| 
								 | 
							
								from MiniAEFrame import AEServer, MiniApplication
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import os
							 | 
						||
| 
								 | 
							
								import string
							 | 
						||
| 
								 | 
							
								import cStringIO
							 | 
						||
| 
								 | 
							
								import sys
							 | 
						||
| 
								 | 
							
								import traceback
							 | 
						||
| 
								 | 
							
								import mimetools
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								__version__ = '3.2'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								slave_dir = os.getcwd()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# log file for errors
							 | 
						||
| 
								 | 
							
								sys.stderr = open(sys.argv[0] + ".errors", "a+")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def convertFSSpec(fss):
							 | 
						||
| 
								 | 
							
									return fss.as_pathname()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# AE -> os.environ mappings
							 | 
						||
| 
								 | 
							
								ae2environ = {
							 | 
						||
| 
								 | 
							
									'kfor': 'QUERY_STRING',
							 | 
						||
| 
								 | 
							
									'Kcip': 'REMOTE_ADDR',
							 | 
						||
| 
								 | 
							
									'svnm': 'SERVER_NAME',
							 | 
						||
| 
								 | 
							
									'svpt': 'SERVER_PORT',
							 | 
						||
| 
								 | 
							
									'addr': 'REMOTE_HOST',
							 | 
						||
| 
								 | 
							
									'scnm': 'SCRIPT_NAME',
							 | 
						||
| 
								 | 
							
									'meth': 'REQUEST_METHOD',
							 | 
						||
| 
								 | 
							
									'ctyp': 'CONTENT_TYPE',
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								ERROR_MESSAGE = """\
							 | 
						||
| 
								 | 
							
								Content-type: text/html
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<html>
							 | 
						||
| 
								 | 
							
								<head>
							 | 
						||
| 
								 | 
							
								<title>Error response</title>
							 | 
						||
| 
								 | 
							
								</head>
							 | 
						||
| 
								 | 
							
								<body>
							 | 
						||
| 
								 | 
							
								<h1>Error response</h1>
							 | 
						||
| 
								 | 
							
								<p>Error code %d.
							 | 
						||
| 
								 | 
							
								<p>Message: %s.
							 | 
						||
| 
								 | 
							
								</body>
							 | 
						||
| 
								 | 
							
								</html>
							 | 
						||
| 
								 | 
							
								"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def get_cgi_code():
							 | 
						||
| 
								 | 
							
									# If we're a CGI wrapper, the CGI code resides in a PYC resource.
							 | 
						||
| 
								 | 
							
									import Res, marshal
							 | 
						||
| 
								 | 
							
									try:
							 | 
						||
| 
								 | 
							
										code = Res.GetNamedResource('PYC ', "CGI_MAIN")
							 | 
						||
| 
								 | 
							
									except Res.Error:
							 | 
						||
| 
								 | 
							
										return None
							 | 
						||
| 
								 | 
							
									else:
							 | 
						||
| 
								 | 
							
										return marshal.loads(code.data[8:])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class PythonCGISlave(AEServer, MiniApplication):
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									def __init__(self):
							 | 
						||
| 
								 | 
							
										self.crumblezone = 100000 * "\0"
							 | 
						||
| 
								 | 
							
										MiniApplication.__init__(self)
							 | 
						||
| 
								 | 
							
										AEServer.__init__(self)
							 | 
						||
| 
								 | 
							
										self.installaehandler('aevt', 'oapp', self.open_app)
							 | 
						||
| 
								 | 
							
										self.installaehandler('aevt', 'quit', self.quit)
							 | 
						||
| 
								 | 
							
										self.installaehandler('WWW\275', 'sdoc', self.cgihandler)
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										self.code = get_cgi_code()
							 | 
						||
| 
								 | 
							
										self.long_running = LONG_RUNNING
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										if self.code is None:
							 | 
						||
| 
								 | 
							
											print "%s version %s, ready to serve." % (self.__class__.__name__, __version__)
							 | 
						||
| 
								 | 
							
										else:
							 | 
						||
| 
								 | 
							
											print "%s, ready to serve." % os.path.basename(sys.argv[0])
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										try:
							 | 
						||
| 
								 | 
							
											self.mainloop()
							 | 
						||
| 
								 | 
							
										except:
							 | 
						||
| 
								 | 
							
											self.crumblezone = None
							 | 
						||
| 
								 | 
							
											sys.stderr.write("- " * 30 + '\n')
							 | 
						||
| 
								 | 
							
											self.message("Unexpected exception")
							 | 
						||
| 
								 | 
							
											self.dump_environ()
							 | 
						||
| 
								 | 
							
											sys.stderr.write("%s: %s\n" % sys.exc_info()[:2])
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									def getabouttext(self):
							 | 
						||
| 
								 | 
							
										if self.code is None:
							 | 
						||
| 
								 | 
							
											return "PythonCGISlave %s, written by Just van Rossum." % __version__
							 | 
						||
| 
								 | 
							
										else:
							 | 
						||
| 
								 | 
							
											return "Python CGI script, wrapped by BuildCGIApplet and " \
							 | 
						||
| 
								 | 
							
													"PythonCGISlave, version %s." % __version__
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									def getaboutmenutext(self):
							 | 
						||
| 
								 | 
							
										return "About %s\311" % os.path.basename(sys.argv[0])
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									def message(self, msg):
							 | 
						||
| 
								 | 
							
										import time
							 | 
						||
| 
								 | 
							
										sys.stderr.write("%s (%s)\n" % (msg, time.asctime(time.localtime(time.time()))))
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									def dump_environ(self):
							 | 
						||
| 
								 | 
							
										sys.stderr.write("os.environ = {\n")
							 | 
						||
| 
								 | 
							
										keys = os.environ.keys()
							 | 
						||
| 
								 | 
							
										keys.sort()
							 | 
						||
| 
								 | 
							
										for key in keys:
							 | 
						||
| 
								 | 
							
											sys.stderr.write("  %s: %s,\n" % (repr(key), repr(os.environ[key])))
							 | 
						||
| 
								 | 
							
										sys.stderr.write("}\n")
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									def quit(self, **args):
							 | 
						||
| 
								 | 
							
										self.quitting = 1
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									def open_app(self, **args):
							 | 
						||
| 
								 | 
							
										pass
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									def cgihandler(self, pathargs, **args):
							 | 
						||
| 
								 | 
							
										# We emulate the unix way of doing CGI: fill os.environ with stuff.
							 | 
						||
| 
								 | 
							
										environ = os.environ
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										# First, find the document root. If we don't get a DIRE parameter,
							 | 
						||
| 
								 | 
							
										# we take the directory of this program, which may be wrong if
							 | 
						||
| 
								 | 
							
										# it doesn't live the actual http document root folder.
							 | 
						||
| 
								 | 
							
										if args.has_key('DIRE'):
							 | 
						||
| 
								 | 
							
											http_root = args['DIRE'].as_pathname()
							 | 
						||
| 
								 | 
							
											del args['DIRE']
							 | 
						||
| 
								 | 
							
										else:
							 | 
						||
| 
								 | 
							
											http_root = slave_dir
							 | 
						||
| 
								 | 
							
										environ['DOCUMENT_ROOT'] = http_root
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										if self.code is None:
							 | 
						||
| 
								 | 
							
											# create a Mac pathname to the Python CGI script or applet
							 | 
						||
| 
								 | 
							
											script = string.replace(args['scnm'], '/', ':')
							 | 
						||
| 
								 | 
							
											script_path = os.path.join(http_root, script)
							 | 
						||
| 
								 | 
							
										else:
							 | 
						||
| 
								 | 
							
											script_path = sys.argv[0]
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										if not os.path.exists(script_path):
							 | 
						||
| 
								 | 
							
											rv = "HTTP/1.0 404 Not found\n"
							 | 
						||
| 
								 | 
							
											rv = rv + ERROR_MESSAGE % (404, "Not found")
							 | 
						||
| 
								 | 
							
											return rv
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										# Kfrq is the complete http request.
							 | 
						||
| 
								 | 
							
										infile = cStringIO.StringIO(args['Kfrq'])
							 | 
						||
| 
								 | 
							
										firstline = infile.readline()
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										msg = mimetools.Message(infile, 0)
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										uri, protocol = string.split(firstline)[1:3]
							 | 
						||
| 
								 | 
							
										environ['REQUEST_URI'] = uri
							 | 
						||
| 
								 | 
							
										environ['SERVER_PROTOCOL'] = protocol
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										# Make all http headers available as HTTP_* fields.
							 | 
						||
| 
								 | 
							
										for key in msg.keys():
							 | 
						||
| 
								 | 
							
											environ['HTTP_' + string.upper(string.replace(key, "-", "_"))] = msg[key]
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										# Translate the AE parameters we know of to the appropriate os.environ
							 | 
						||
| 
								 | 
							
										# entries. Make the ones we don't know available as AE_* fields.
							 | 
						||
| 
								 | 
							
										items = args.items()
							 | 
						||
| 
								 | 
							
										items.sort()
							 | 
						||
| 
								 | 
							
										for key, value in items:
							 | 
						||
| 
								 | 
							
											if key[0] == "_":
							 | 
						||
| 
								 | 
							
												continue
							 | 
						||
| 
								 | 
							
											if ae2environ.has_key(key):
							 | 
						||
| 
								 | 
							
												envkey = ae2environ[key]
							 | 
						||
| 
								 | 
							
												environ[envkey] = value
							 | 
						||
| 
								 | 
							
											else:
							 | 
						||
| 
								 | 
							
												environ['AE_' + string.upper(key)] = str(value)
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										# Redirect stdout and stdin.
							 | 
						||
| 
								 | 
							
										saveout = sys.stdout
							 | 
						||
| 
								 | 
							
										savein = sys.stdin
							 | 
						||
| 
								 | 
							
										out = sys.stdout = cStringIO.StringIO()
							 | 
						||
| 
								 | 
							
										postdata = args.get('post', "")
							 | 
						||
| 
								 | 
							
										if postdata:
							 | 
						||
| 
								 | 
							
											environ['CONTENT_LENGTH'] = str(len(postdata))
							 | 
						||
| 
								 | 
							
											sys.stdin = cStringIO.StringIO(postdata)
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										# Set up the Python environment
							 | 
						||
| 
								 | 
							
										script_dir = os.path.dirname(script_path)
							 | 
						||
| 
								 | 
							
										os.chdir(script_dir)
							 | 
						||
| 
								 | 
							
										sys.path.insert(0, script_dir)
							 | 
						||
| 
								 | 
							
										sys.argv[:] = [script_path]
							 | 
						||
| 
								 | 
							
										namespace = {"__name__": "__main__"}
							 | 
						||
| 
								 | 
							
										rv = "HTTP/1.0 200 OK\n"
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										try:
							 | 
						||
| 
								 | 
							
											if self.code is None:
							 | 
						||
| 
								 | 
							
												# we're a Python script server
							 | 
						||
| 
								 | 
							
												execfile(script_path, namespace)
							 | 
						||
| 
								 | 
							
											else:
							 | 
						||
| 
								 | 
							
												# we're a CGI wrapper, self.code is the CGI code
							 | 
						||
| 
								 | 
							
												exec self.code in namespace
							 | 
						||
| 
								 | 
							
										except SystemExit:
							 | 
						||
| 
								 | 
							
											# We're not exiting dammit! ;-)
							 | 
						||
| 
								 | 
							
											pass
							 | 
						||
| 
								 | 
							
										except:
							 | 
						||
| 
								 | 
							
											self.crumblezone = None
							 | 
						||
| 
								 | 
							
											sys.stderr.write("- " * 30 + '\n')
							 | 
						||
| 
								 | 
							
											self.message("CGI exception")
							 | 
						||
| 
								 | 
							
											self.dump_environ()
							 | 
						||
| 
								 | 
							
											traceback.print_exc()
							 | 
						||
| 
								 | 
							
											sys.stderr.flush()
							 | 
						||
| 
								 | 
							
											self.quitting = 1
							 | 
						||
| 
								 | 
							
											# XXX we should return an error AE, but I don't know how to :-(
							 | 
						||
| 
								 | 
							
											rv = "HTTP/1.0 500 Internal error\n"
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										# clean up
							 | 
						||
| 
								 | 
							
										namespace.clear()
							 | 
						||
| 
								 | 
							
										environ.clear()
							 | 
						||
| 
								 | 
							
										sys.path.remove(script_dir)
							 | 
						||
| 
								 | 
							
										sys.stdout = saveout
							 | 
						||
| 
								 | 
							
										sys.stdin = savein
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										if not self.long_running:
							 | 
						||
| 
								 | 
							
											# quit after each request
							 | 
						||
| 
								 | 
							
											self.quitting = 1
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
										return rv + out.getvalue()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								PythonCGISlave()
							 |