mirror of
				https://github.com/python/cpython.git
				synced 2025-10-29 12:44:56 +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() |