mirror of
				https://github.com/python/cpython.git
				synced 2025-10-30 21:21:22 +00:00 
			
		
		
		
	 4011723d0d
			
		
	
	
		4011723d0d
		
	
	
	
	
		
			
			- new script/applet BuildCGIApplet This largely supercedes :Mac:Demos:cgi, except for the html doc file. Should it move here? Merged with CGI_README.txt? Todo: fullbuild support. (jvr)
		
			
				
	
	
		
			243 lines
		
	
	
	
		
			5.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			243 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()
 |