3 This program can be used in two ways:
4 - As a Python CGI script server for web servers supporting "Actions", like WebStar.
5 - As a wrapper for a single Python CGI script, for any "compliant" Mac web server.
7 See CGI_README.txt for more details.
11 # Written by Just van Rossum, but partly stolen from example code by Jack.
15 LONG_RUNNING
= 1 # If true, don't quit after each request.
19 MacOS
.SchedParams(0, 0)
20 from MiniAEFrame
import AEServer
, MiniApplication
32 slave_dir
= os
.getcwd()
36 sys
.stderr
= open(sys
.argv
[0] + ".errors", "a+")
38 def convertFSSpec(fss
):
39 return fss
.as_pathname()
42 # AE -> os.environ mappings
44 'kfor': 'QUERY_STRING',
45 'Kcip': 'REMOTE_ADDR',
46 'svnm': 'SERVER_NAME',
47 'svpt': 'SERVER_PORT',
48 'addr': 'REMOTE_HOST',
49 'scnm': 'SCRIPT_NAME',
50 'meth': 'REQUEST_METHOD',
51 'ctyp': 'CONTENT_TYPE',
56 Content-type: text/html
60 <title>Error response</title>
63 <h1>Error response</h1>
72 # If we're a CGI wrapper, the CGI code resides in a PYC resource.
73 from Carbon
import Res
76 code
= Res
.GetNamedResource('PYC ', "CGI_MAIN")
80 return marshal
.loads(code
.data
[8:])
84 class PythonCGISlave(AEServer
, MiniApplication
):
87 self
.crumblezone
= 100000 * "\0"
88 MiniApplication
.__init
__(self
)
89 AEServer
.__init
__(self
)
90 self
.installaehandler('aevt', 'oapp', self
.open_app
)
91 self
.installaehandler('aevt', 'quit', self
.quit
)
92 self
.installaehandler('WWW\275', 'sdoc', self
.cgihandler
)
94 self
.code
= get_cgi_code()
95 self
.long_running
= LONG_RUNNING
98 print "%s version %s, ready to serve." % (self
.__class
__.__name
__, __version__
)
100 print "%s, ready to serve." % os
.path
.basename(sys
.argv
[0])
105 self
.crumblezone
= None
106 sys
.stderr
.write("- " * 30 + '\n')
107 self
.message("Unexpected exception")
109 sys
.stderr
.write("%s: %s\n" % sys
.exc_info()[:2])
111 def getabouttext(self
):
112 if self
.code
is None:
113 return "PythonCGISlave %s, written by Just van Rossum." % __version__
115 return "Python CGI script, wrapped by BuildCGIApplet and " \
116 "PythonCGISlave, version %s." % __version__
118 def getaboutmenutext(self
):
119 return "About %s\311" % os
.path
.basename(sys
.argv
[0])
121 def message(self
, msg
):
123 sys
.stderr
.write("%s (%s)\n" % (msg
, time
.asctime(time
.localtime(time
.time()))))
125 def dump_environ(self
):
126 sys
.stderr
.write("os.environ = {\n")
127 keys
= os
.environ
.keys()
130 sys
.stderr
.write(" %s: %s,\n" % (repr(key
), repr(os
.environ
[key
])))
131 sys
.stderr
.write("}\n")
133 def quit(self
, **args
):
136 def open_app(self
, **args
):
139 def cgihandler(self
, pathargs
, **args
):
140 # We emulate the unix way of doing CGI: fill os.environ with stuff.
143 # First, find the document root. If we don't get a DIRE parameter,
144 # we take the directory of this program, which may be wrong if
145 # it doesn't live the actual http document root folder.
146 if args
.has_key('DIRE'):
147 http_root
= args
['DIRE'].as_pathname()
150 http_root
= slave_dir
151 environ
['DOCUMENT_ROOT'] = http_root
153 if self
.code
is None:
154 # create a Mac pathname to the Python CGI script or applet
155 script
= string
.replace(args
['scnm'], '/', ':')
156 script_path
= os
.path
.join(http_root
, script
)
158 script_path
= sys
.argv
[0]
160 if not os
.path
.exists(script_path
):
161 rv
= "HTTP/1.0 404 Not found\n"
162 rv
= rv
+ ERROR_MESSAGE
% (404, "Not found")
165 # Kfrq is the complete http request.
166 infile
= cStringIO
.StringIO(args
['Kfrq'])
167 firstline
= infile
.readline()
169 msg
= mimetools
.Message(infile
, 0)
171 uri
, protocol
= string
.split(firstline
)[1:3]
172 environ
['REQUEST_URI'] = uri
173 environ
['SERVER_PROTOCOL'] = protocol
175 # Make all http headers available as HTTP_* fields.
176 for key
in msg
.keys():
177 environ
['HTTP_' + string
.upper(string
.replace(key
, "-", "_"))] = msg
[key
]
179 # Translate the AE parameters we know of to the appropriate os.environ
180 # entries. Make the ones we don't know available as AE_* fields.
183 for key
, value
in items
:
186 if ae2environ
.has_key(key
):
187 envkey
= ae2environ
[key
]
188 environ
[envkey
] = value
190 environ
['AE_' + string
.upper(key
)] = str(value
)
192 # Redirect stdout and stdin.
195 out
= sys
.stdout
= cStringIO
.StringIO()
196 postdata
= args
.get('post', "")
198 environ
['CONTENT_LENGTH'] = str(len(postdata
))
199 sys
.stdin
= cStringIO
.StringIO(postdata
)
201 # Set up the Python environment
202 script_dir
= os
.path
.dirname(script_path
)
204 sys
.path
.insert(0, script_dir
)
205 sys
.argv
[:] = [script_path
]
206 namespace
= {"__name__": "__main__"}
207 rv
= "HTTP/1.0 200 OK\n"
210 if self
.code
is None:
211 # we're a Python script server
212 execfile(script_path
, namespace
)
214 # we're a CGI wrapper, self.code is the CGI code
215 exec self
.code
in namespace
217 # We're not exiting dammit! ;-)
220 self
.crumblezone
= None
221 sys
.stderr
.write("- " * 30 + '\n')
222 self
.message("CGI exception")
224 traceback
.print_exc()
227 # XXX we should return an error AE, but I don't know how to :-(
228 rv
= "HTTP/1.0 500 Internal error\n"
233 sys
.path
.remove(script_dir
)
237 if not self
.long_running
:
238 # quit after each request
241 return rv
+ out
.getvalue()