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.
75 code
= Res
.GetNamedResource('PYC ', "CGI_MAIN")
79 return marshal
.loads(code
.data
[8:])
83 class PythonCGISlave(AEServer
, MiniApplication
):
86 self
.crumblezone
= 100000 * "\0"
87 MiniApplication
.__init
__(self
)
88 AEServer
.__init
__(self
)
89 self
.installaehandler('aevt', 'oapp', self
.open_app
)
90 self
.installaehandler('aevt', 'quit', self
.quit
)
91 self
.installaehandler('WWW\275', 'sdoc', self
.cgihandler
)
93 self
.code
= get_cgi_code()
94 self
.long_running
= LONG_RUNNING
97 print "%s version %s, ready to serve." % (self
.__class
__.__name
__, __version__
)
99 print "%s, ready to serve." % os
.path
.basename(sys
.argv
[0])
104 self
.crumblezone
= None
105 sys
.stderr
.write("- " * 30 + '\n')
106 self
.message("Unexpected exception")
108 sys
.stderr
.write("%s: %s\n" % sys
.exc_info()[:2])
110 def getabouttext(self
):
111 if self
.code
is None:
112 return "PythonCGISlave %s, written by Just van Rossum." % __version__
114 return "Python CGI script, wrapped by BuildCGIApplet and " \
115 "PythonCGISlave, version %s." % __version__
117 def getaboutmenutext(self
):
118 return "About %s\311" % os
.path
.basename(sys
.argv
[0])
120 def message(self
, msg
):
122 sys
.stderr
.write("%s (%s)\n" % (msg
, time
.asctime(time
.localtime(time
.time()))))
124 def dump_environ(self
):
125 sys
.stderr
.write("os.environ = {\n")
126 keys
= os
.environ
.keys()
129 sys
.stderr
.write(" %s: %s,\n" % (repr(key
), repr(os
.environ
[key
])))
130 sys
.stderr
.write("}\n")
132 def quit(self
, **args
):
135 def open_app(self
, **args
):
138 def cgihandler(self
, pathargs
, **args
):
139 # We emulate the unix way of doing CGI: fill os.environ with stuff.
142 # First, find the document root. If we don't get a DIRE parameter,
143 # we take the directory of this program, which may be wrong if
144 # it doesn't live the actual http document root folder.
145 if args
.has_key('DIRE'):
146 http_root
= args
['DIRE'].as_pathname()
149 http_root
= slave_dir
150 environ
['DOCUMENT_ROOT'] = http_root
152 if self
.code
is None:
153 # create a Mac pathname to the Python CGI script or applet
154 script
= string
.replace(args
['scnm'], '/', ':')
155 script_path
= os
.path
.join(http_root
, script
)
157 script_path
= sys
.argv
[0]
159 if not os
.path
.exists(script_path
):
160 rv
= "HTTP/1.0 404 Not found\n"
161 rv
= rv
+ ERROR_MESSAGE
% (404, "Not found")
164 # Kfrq is the complete http request.
165 infile
= cStringIO
.StringIO(args
['Kfrq'])
166 firstline
= infile
.readline()
168 msg
= mimetools
.Message(infile
, 0)
170 uri
, protocol
= string
.split(firstline
)[1:3]
171 environ
['REQUEST_URI'] = uri
172 environ
['SERVER_PROTOCOL'] = protocol
174 # Make all http headers available as HTTP_* fields.
175 for key
in msg
.keys():
176 environ
['HTTP_' + string
.upper(string
.replace(key
, "-", "_"))] = msg
[key
]
178 # Translate the AE parameters we know of to the appropriate os.environ
179 # entries. Make the ones we don't know available as AE_* fields.
182 for key
, value
in items
:
185 if ae2environ
.has_key(key
):
186 envkey
= ae2environ
[key
]
187 environ
[envkey
] = value
189 environ
['AE_' + string
.upper(key
)] = str(value
)
191 # Redirect stdout and stdin.
194 out
= sys
.stdout
= cStringIO
.StringIO()
195 postdata
= args
.get('post', "")
197 environ
['CONTENT_LENGTH'] = str(len(postdata
))
198 sys
.stdin
= cStringIO
.StringIO(postdata
)
200 # Set up the Python environment
201 script_dir
= os
.path
.dirname(script_path
)
203 sys
.path
.insert(0, script_dir
)
204 sys
.argv
[:] = [script_path
]
205 namespace
= {"__name__": "__main__"}
206 rv
= "HTTP/1.0 200 OK\n"
209 if self
.code
is None:
210 # we're a Python script server
211 execfile(script_path
, namespace
)
213 # we're a CGI wrapper, self.code is the CGI code
214 exec self
.code
in namespace
216 # We're not exiting dammit! ;-)
219 self
.crumblezone
= None
220 sys
.stderr
.write("- " * 30 + '\n')
221 self
.message("CGI exception")
223 traceback
.print_exc()
226 # XXX we should return an error AE, but I don't know how to :-(
227 rv
= "HTTP/1.0 500 Internal error\n"
232 sys
.path
.remove(script_dir
)
236 if not self
.long_running
:
237 # quit after each request
240 return rv
+ out
.getvalue()