River's Edge Rest.& Party House Website is a community education project
[tues-crep.git] / sites / rochesterbanquets.com / httpdocs / rer-jan16 / test / fcgi / fcgi.py
blob5a8789e6b8376c5e8bfaae3eebac0a4cabd264a6
1 #!/usr/bin/python
2 #------------------------------------------------------------------------
3 # Copyright (c) 1998 by Total Control Software
4 # All Rights Reserved
5 #------------------------------------------------------------------------
7 # Module Name: fcgi.py
9 # Description: Handles communication with the FastCGI module of the
10 # web server without using the FastCGI developers kit, but
11 # will also work in a non-FastCGI environment, (straight CGI.)
12 # This module was originally fetched from someplace on the
13 # Net (I don't remember where and I can't find it now...) and
14 # has been significantly modified to fix several bugs, be more
15 # readable, more robust at handling large CGI data and return
16 # document sizes, and also to fit the model that we had previously
17 # used for FastCGI.
19 # WARNING: If you don't know what you are doing, don't tinker with this
20 # module!
22 # Creation Date: 1/30/98 2:59:04PM
24 # License: This is free software. You may use this software for any
25 # purpose including modification/redistribution, so long as
26 # this header remains intact and that you do not claim any
27 # rights of ownership or authorship of this software. This
28 # software has been tested, but no warranty is expressed or
29 # implied.
31 #------------------------------------------------------------------------
34 import os, sys, string, socket, errno
35 from cStringIO import StringIO
36 import cgi
38 #---------------------------------------------------------------------------
40 # Set various FastCGI constants
41 # Maximum number of requests that can be handled
42 FCGI_MAX_REQS=1
43 FCGI_MAX_CONNS = 1
45 # Supported version of the FastCGI protocol
46 FCGI_VERSION_1 = 1
48 # Boolean: can this application multiplex connections?
49 FCGI_MPXS_CONNS=0
51 # Record types
52 FCGI_BEGIN_REQUEST = 1 ; FCGI_ABORT_REQUEST = 2 ; FCGI_END_REQUEST = 3
53 FCGI_PARAMS = 4 ; FCGI_STDIN = 5 ; FCGI_STDOUT = 6
54 FCGI_STDERR = 7 ; FCGI_DATA = 8 ; FCGI_GET_VALUES = 9
55 FCGI_GET_VALUES_RESULT = 10
56 FCGI_UNKNOWN_TYPE = 11
57 FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
59 # Types of management records
60 ManagementTypes = [FCGI_GET_VALUES]
62 FCGI_NULL_REQUEST_ID=0
64 # Masks for flags component of FCGI_BEGIN_REQUEST
65 FCGI_KEEP_CONN = 1
67 # Values for role component of FCGI_BEGIN_REQUEST
68 FCGI_RESPONDER = 1 ; FCGI_AUTHORIZER = 2 ; FCGI_FILTER = 3
70 # Values for protocolStatus component of FCGI_END_REQUEST
71 FCGI_REQUEST_COMPLETE = 0 # Request completed nicely
72 FCGI_CANT_MPX_CONN = 1 # This app can't multiplex
73 FCGI_OVERLOADED = 2 # New request rejected; too busy
74 FCGI_UNKNOWN_ROLE = 3 # Role value not known
77 error = 'fcgi.error'
80 #---------------------------------------------------------------------------
82 # The following function is used during debugging; it isn't called
83 # anywhere at the moment
85 def error(msg):
86 "Append a string to /tmp/err"
87 errf=open('/tmp/err', 'a+')
88 errf.write(msg+'\n')
89 errf.close()
91 #---------------------------------------------------------------------------
93 class record:
94 "Class representing FastCGI records"
95 def __init__(self):
96 self.version = FCGI_VERSION_1
97 self.recType = FCGI_UNKNOWN_TYPE
98 self.reqId = FCGI_NULL_REQUEST_ID
99 self.content = ""
101 #----------------------------------------
102 def readRecord(self, sock):
103 s = map(ord, sock.recv(8))
104 self.version, self.recType, paddingLength = s[0], s[1], s[6]
105 self.reqId, contentLength = (s[2]<<8)+s[3], (s[4]<<8)+s[5]
106 self.content = ""
107 while len(self.content) < contentLength:
108 data = sock.recv(contentLength - len(self.content))
109 self.content = self.content + data
110 if paddingLength != 0:
111 padding = sock.recv(paddingLength)
113 # Parse the content information
114 c = self.content
115 if self.recType == FCGI_BEGIN_REQUEST:
116 self.role = (ord(c[0])<<8) + ord(c[1])
117 self.flags = ord(c[2])
119 elif self.recType == FCGI_UNKNOWN_TYPE:
120 self.unknownType = ord(c[0])
122 elif self.recType == FCGI_GET_VALUES or self.recType == FCGI_PARAMS:
123 self.values={}
124 pos=0
125 while pos < len(c):
126 name, value, pos = readPair(c, pos)
127 self.values[name] = value
128 elif self.recType == FCGI_END_REQUEST:
129 b = map(ord, c[0:4])
130 self.appStatus = (b[0]<<24) + (b[1]<<16) + (b[2]<<8) + b[3]
131 self.protocolStatus = ord(c[4])
133 #----------------------------------------
134 def writeRecord(self, sock):
135 content = self.content
136 if self.recType == FCGI_BEGIN_REQUEST:
137 content = chr(self.role>>8) + chr(self.role & 255) + chr(self.flags) + 5*'\000'
139 elif self.recType == FCGI_UNKNOWN_TYPE:
140 content = chr(self.unknownType) + 7*'\000'
142 elif self.recType==FCGI_GET_VALUES or self.recType==FCGI_PARAMS:
143 content = ""
144 for i in self.values.keys():
145 content = content + writePair(i, self.values[i])
147 elif self.recType==FCGI_END_REQUEST:
148 v = self.appStatus
149 content = chr((v>>24)&255) + chr((v>>16)&255) + chr((v>>8)&255) + chr(v&255)
150 content = content + chr(self.protocolStatus) + 3*'\000'
152 cLen = len(content)
153 eLen = (cLen + 7) & (0xFFFF - 7) # align to an 8-byte boundary
154 padLen = eLen - cLen
156 hdr = [ self.version,
157 self.recType,
158 self.reqId >> 8,
159 self.reqId & 255,
160 cLen >> 8,
161 cLen & 255,
162 padLen,
164 hdr = string.joinfields(map(chr, hdr), '')
166 sock.send(hdr + content + padLen*'\000')
168 #---------------------------------------------------------------------------
170 def readPair(s, pos):
171 nameLen=ord(s[pos]) ; pos=pos+1
172 if nameLen & 128:
173 b=map(ord, s[pos:pos+3]) ; pos=pos+3
174 nameLen=((nameLen&127)<<24) + (b[0]<<16) + (b[1]<<8) + b[2]
175 valueLen=ord(s[pos]) ; pos=pos+1
176 if valueLen & 128:
177 b=map(ord, s[pos:pos+3]) ; pos=pos+3
178 valueLen=((valueLen&127)<<24) + (b[0]<<16) + (b[1]<<8) + b[2]
179 return ( s[pos:pos+nameLen], s[pos+nameLen:pos+nameLen+valueLen],
180 pos+nameLen+valueLen )
182 #---------------------------------------------------------------------------
184 def writePair(name, value):
185 l=len(name)
186 if l<128: s=chr(l)
187 else:
188 s=chr(128|(l>>24)&255) + chr((l>>16)&255) + chr((l>>8)&255) + chr(l&255)
189 l=len(value)
190 if l<128: s=s+chr(l)
191 else:
192 s=s+chr(128|(l>>24)&255) + chr((l>>16)&255) + chr((l>>8)&255) + chr(l&255)
193 return s + name + value
195 #---------------------------------------------------------------------------
197 def HandleManTypes(r, conn):
198 if r.recType == FCGI_GET_VALUES:
199 r.recType = FCGI_GET_VALUES_RESULT
200 v={}
201 vars={'FCGI_MAX_CONNS' : FCGI_MAX_CONNS,
202 'FCGI_MAX_REQS' : FCGI_MAX_REQS,
203 'FCGI_MPXS_CONNS': FCGI_MPXS_CONNS}
204 for i in r.values.keys():
205 if vars.has_key(i): v[i]=vars[i]
206 r.values=vars
207 r.writeRecord(conn)
209 #---------------------------------------------------------------------------
210 #---------------------------------------------------------------------------
213 _isFCGI = 1 # assume it is until we find out for sure
215 def isFCGI():
216 global _isFCGI
217 return _isFCGI
221 #---------------------------------------------------------------------------
224 _init = None
225 _sock = None
227 class FCGI:
228 def __init__(self):
229 self.haveFinished = 0
230 if _init == None:
231 _startup()
232 if not isFCGI():
233 self.haveFinished = 1
234 self.inp, self.out, self.err, self.env = \
235 sys.stdin, sys.stdout, sys.stderr, os.environ
236 return
238 if os.environ.has_key('FCGI_WEB_SERVER_ADDRS'):
239 good_addrs=string.split(os.environ['FCGI_WEB_SERVER_ADDRS'], ',')
240 good_addrs=map(string.strip(good_addrs)) # Remove whitespace
241 else:
242 good_addrs=None
244 self.conn, addr=_sock.accept()
245 stdin, data="", ""
246 self.env = {}
247 self.requestId=0
248 remaining=1
250 # Check if the connection is from a legal address
251 if good_addrs!=None and addr not in good_addrs:
252 raise error, 'Connection from invalid server!'
254 while remaining:
255 r=record(); r.readRecord(self.conn)
257 if r.recType in ManagementTypes:
258 HandleManTypes(r, self.conn)
260 elif r.reqId==0:
261 # Oh, poopy. It's a management record of an unknown
262 # type. Signal the error.
263 r2=record()
264 r2.recType=FCGI_UNKNOWN_TYPE ; r2.unknownType=r.recType
265 r2.writeRecord(self.conn)
266 continue # Charge onwards
268 # Ignore requests that aren't active
269 elif r.reqId != self.requestId and r.recType != FCGI_BEGIN_REQUEST:
270 continue
272 # If we're already doing a request, ignore further BEGIN_REQUESTs
273 elif r.recType == FCGI_BEGIN_REQUEST and self.requestId != 0:
274 continue
276 # Begin a new request
277 if r.recType == FCGI_BEGIN_REQUEST:
278 self.requestId = r.reqId
279 if r.role == FCGI_AUTHORIZER: remaining=1
280 elif r.role == FCGI_RESPONDER: remaining=2
281 elif r.role == FCGI_FILTER: remaining=3
283 elif r.recType == FCGI_PARAMS:
284 if r.content == "":
285 remaining=remaining-1
286 else:
287 for i in r.values.keys():
288 self.env[i] = r.values[i]
290 elif r.recType == FCGI_STDIN:
291 if r.content == "":
292 remaining=remaining-1
293 else:
294 stdin=stdin+r.content
296 elif r.recType==FCGI_DATA:
297 if r.content == "":
298 remaining=remaining-1
299 else:
300 data=data+r.content
301 # end of while remaining:
303 self.inp = sys.stdin = StringIO(stdin)
304 self.err = sys.stderr = StringIO()
305 self.out = sys.stdout = StringIO()
306 self.data = StringIO(data)
308 def __del__(self):
309 self.Finish()
311 def Finish(self, status=0):
312 if not self.haveFinished:
313 self.haveFinished = 1
315 self.err.seek(0,0)
316 self.out.seek(0,0)
318 r=record()
319 r.recType = FCGI_STDERR
320 r.reqId = self.requestId
321 data = self.err.read()
322 if data:
323 while data:
324 chunk, data = self.getNextChunk(data)
325 r.content = chunk
326 r.writeRecord(self.conn)
327 r.content="" ; r.writeRecord(self.conn) # Terminate stream
329 r.recType = FCGI_STDOUT
330 data = self.out.read()
331 while data:
332 chunk, data = self.getNextChunk(data)
333 r.content = chunk
334 r.writeRecord(self.conn)
335 r.content="" ; r.writeRecord(self.conn) # Terminate stream
337 r=record()
338 r.recType=FCGI_END_REQUEST
339 r.reqId=self.requestId
340 r.appStatus=status
341 r.protocolStatus=FCGI_REQUEST_COMPLETE
342 r.writeRecord(self.conn)
343 self.conn.close()
346 def getFieldStorage(self):
347 method = 'GET'
348 if self.env.has_key('REQUEST_METHOD'):
349 method = string.upper(self.env['REQUEST_METHOD'])
350 if method == 'GET':
351 return cgi.FieldStorage(environ=self.env, keep_blank_values=1)
352 else:
353 return cgi.FieldStorage(fp=self.inp, environ=self.env, keep_blank_values=1)
355 def getNextChunk(self, data):
356 chunk = data[:8192]
357 data = data[8192:]
358 return chunk, data
361 Accept = FCGI # alias for backwards compatibility
362 #---------------------------------------------------------------------------
364 def _startup():
365 global _init
366 _init = 1
367 try:
368 s=socket.fromfd(sys.stdin.fileno(), socket.AF_INET,
369 socket.SOCK_STREAM)
370 s.getpeername()
371 except socket.error, (err, errmsg):
372 if err!=errno.ENOTCONN: # must be a non-fastCGI environment
373 global _isFCGI
374 _isFCGI = 0
375 return
377 global _sock
378 _sock = s
381 #---------------------------------------------------------------------------
383 def _test():
384 counter=0
385 try:
386 while isFCGI():
387 req = Accept()
388 counter=counter+1
390 try:
391 fs = req.getFieldStorage()
392 size = string.atoi(fs['size'].value)
393 doc = ['*' * size]
394 except:
395 doc = ['<HTML><HEAD><TITLE>FCGI TestApp</TITLE></HEAD>\n<BODY>\n']
396 doc.append('<H2>FCGI TestApp</H2><P>')
397 doc.append('<b>request count</b> = %d<br>' % counter)
398 # doc.append('<b>pid</b> = %s<br>' % os.getpid())
399 # if req.env.has_key('CONTENT_LENGTH'):
400 # cl = string.atoi(req.env['CONTENT_LENGTH'])
401 # doc.append('<br><b>POST data (%s):</b><br><pre>' % cl)
402 # keys = fs.keys()
403 # keys.sort()
404 # for k in keys:
405 # val = fs[k]
406 # if type(val) == type([]):
407 # doc.append(' <b>%-15s :</b> %s\n' % (k, val))
408 # else:
409 # doc.append(' <b>%-15s :</b> %s\n' % (k, val.value))
410 # doc.append('</pre>')
413 # doc.append('<P><HR><P><pre>')
414 # keys = req.env.keys()
415 # keys.sort()
416 # for k in keys:
417 # doc.append('<b>%-20s :</b> %s\n' % (k, req.env[k]))
418 # doc.append('\n</pre><P><HR>\n')
419 doc.append('</BODY></HTML>\n')
422 doc = string.join(doc, '')
423 req.out.write('Content-length: %s\r\n'
424 'Content-type: text/html\r\n'
425 'Cache-Control: no-cache\r\n'
426 '\r\n'
427 % len(doc))
428 req.out.write(doc)
430 req.Finish()
431 except:
432 import traceback
433 f = open('traceback', 'w')
434 traceback.print_exc( file = f )
435 # f.write('%s' % doc)
437 if __name__=='__main__':
438 #import pdb
439 #pdb.run('_test()')
440 _test()