home *** CD-ROM | disk | FTP | other *** search
- ##################################################
- # SPYCE - Python-based HTML Scripting
- # Copyright (c) 2002 Rimon Barr.
- #
- # Refer to spyce.py
- # CVS: $Id: fcgi.py 20864 2009-06-02 06:16:47Z ceros7 $
- ##################################################
-
- # Taken originally from: http://alldunn.com/python/fcgi.py
- # Edited a fair bit. -- RB
-
- __doc__ = 'Python Fast CGI implementation'
-
- import os, sys, string, socket, errno, cgi
- from cStringIO import StringIO
- import spyceUtil
-
- ##################################################
- # Constants
- #
-
- # Protol constants: record types
- FCGI_BEGIN_REQUEST = 1
- FCGI_ABORT_REQUEST = 2
- FCGI_END_REQUEST = 3
- FCGI_PARAMS = 4
- FCGI_STDIN = 5
- FCGI_STDOUT = 6
- FCGI_STDERR = 7
- FCGI_DATA = 8
- FCGI_GET_VALUES = 9
- FCGI_GET_VALUES_RESULT = 10
- FCGI_UNKNOWN_TYPE = 11
- FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
- # Protocol constants: FCGI_BEGIN_REQUEST flag mask
- FCGI_KEEP_CONN = 1
- # Protocol constants: FCGI_BEGIN_REQUEST role
- FCGI_RESPONDER = 1
- FCGI_AUTHORIZER = 2
- FCGI_FILTER = 3
- # Protocol constants: FCGI_END_REQUEST protocolStatus
- FCGI_REQUEST_COMPLETE = 0 # ok
- FCGI_CANT_MPX_CONN = 1 # can not multiplex
- FCGI_OVERLOADED = 2 # too busy
- FCGI_UNKNOWN_ROLE = 3 # role unknown
- # Protocol constants: management record types
- FCGI_NULL_REQUEST_ID = 0
-
- # Protocol setting: maximum number of requests
- FCGI_MAX_REQS = 1
- FCGI_MAX_CONNS = 1
- # Protocol setting: can multiplex?
- FCGI_MPXS_CONNS = 0
- # Protocol setting: FastCGI protocol version
- FCGI_VERSION_1 = 1
-
- ##################################################
- # Protocol
- #
-
- class record:
- def __init__(self):
- self.version = FCGI_VERSION_1
- self.recType = FCGI_UNKNOWN_TYPE
- self.reqId = FCGI_NULL_REQUEST_ID
- self.content = ""
- def readRecord(self, sock):
- # read content
- hdr = map(ord, self.readExact(sock, 8))
- self.version = hdr[0]
- self.recType = hdr[1]
- self.reqId = (hdr[2]<<8)+hdr[3]
- contentLength = (hdr[4]<<8)+hdr[5]
- paddingLength = hdr[6]
- self.content = self.readExact(sock, contentLength)
- self.readExact(sock, paddingLength)
- # parse
- c = self.content
- if self.recType == FCGI_BEGIN_REQUEST:
- self.role = (ord(c[0])<<8) + ord(c[1])
- self.flags = ord(c[2])
- elif self.recType == FCGI_UNKNOWN_TYPE:
- self.unknownType = ord(c[0])
- elif self.recType == FCGI_GET_VALUES or self.recType == FCGI_PARAMS:
- self.values={}
- pos=0
- while pos < len(c):
- name, value, pos = self.decodePair(c, pos)
- self.values[name] = value
- elif self.recType == FCGI_END_REQUEST:
- b = map(ord, c[0:5])
- self.appStatus = (b[0]<<24) + (b[1]<<16) + (b[2]<<8) + b[3]
- self.protocolStatus = b[4]
- def writeRecord(self, sock):
- content = self.content
- if self.recType == FCGI_BEGIN_REQUEST:
- content = chr(self.role>>8) + chr(self.role & 255) + chr(self.flags) + 5*'\000'
- elif self.recType == FCGI_UNKNOWN_TYPE:
- content = chr(self.unknownType) + 7*'\000'
- elif self.recType==FCGI_GET_VALUES or self.recType==FCGI_PARAMS:
- content = ""
- for i in self.values.keys():
- content = content + self.encodePair(i, self.values[i])
- elif self.recType==FCGI_END_REQUEST:
- v = self.appStatus
- content = chr((v>>24)&255) + chr((v>>16)&255) + chr((v>>8)&255) + chr(v&255)
- content = content + chr(self.protocolStatus) + 3*'\000'
- cLen = len(content)
- eLen = (cLen + 7) & (0xFFFF - 7) # align to an 8-byte boundary
- padLen = eLen - cLen
- hdr = [ self.version, self.recType, self.reqId >> 8,
- self.reqId & 255, cLen >> 8, cLen & 255, padLen, 0]
- hdr = string.joinfields(map(chr, hdr), '')
- sock.send(hdr + content + padLen*'\000')
- def readExact(self, sock, amount):
- data = ''
- while amount and len(data) < amount:
- data = data + sock.recv(amount-len(data))
- return data
- def decodePair(self, s, pos):
- nameLen=ord(s[pos]) ; pos=pos+1
- if nameLen & 128:
- b=map(ord, s[pos:pos+3]) ; pos=pos+3
- nameLen=((nameLen&127)<<24) + (b[0]<<16) + (b[1]<<8) + b[2]
- valueLen=ord(s[pos]) ; pos=pos+1
- if valueLen & 128:
- b=map(ord, s[pos:pos+3]) ; pos=pos+3
- valueLen=((valueLen&127)<<24) + (b[0]<<16) + (b[1]<<8) + b[2]
- name = s[pos:pos+nameLen] ; pos = pos + nameLen
- value = s[pos:pos+valueLen] ; pos = pos + valueLen
- return name, value, pos
- def encodePair(self, name, value):
- l=len(name)
- if l<128: s=chr(l)
- else: s=chr(128|(l>>24)&255)+chr((l>>16)&255)+chr((l>>8)&255)+chr(l&255)
- l=len(value)
- if l<128: s=s+chr(l)
- else: s=s+chr(128|(l>>24)&255)+chr((l>>16)&255)+chr((l>>8)&255)+chr(l&255)
- return s + name + value
-
- class FCGI:
- def __init__(self, port=None):
- # environment variables
- try:
- self.FCGI_PORT = int(os.environ['FCGI_PORT'])
- except:
- self.FCGI_PORT = None
- if port: self.FCGI_PORT = port
- self.FCGI_PORT = None
- try:
- self.FCGI_ALLOWED_ADDR = os.environ['FCGI_WEB_SERVER_ADDRS']
- self.FCGI_ALLOWED_ADDR = map(string.strip, string.split(self.FCGI_ALLOWED_ADDR, ','))
- except:
- self.FCGI_ALLOWED_ADDR = None
- self.firstCall = 1
- self.clearState()
- self.socket = None
- self.createServerSocket()
- def createServerSocket(self):
- if self.FCGI_PORT:
- s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.set_reuse_addr()
- s.bind(('127.0.0.1', self.FCGI_PORT))
- else:
- try:
- s=socket.fromfd(sys.stdin.fileno(), socket.AF_INET, socket.SOCK_STREAM)
- s.getpeername()
- except socket.error, (err, errmsg):
- if err!=errno.ENOTCONN:
- return
- except:
- return
- self.socket = s
- def accept(self):
- if not self.socket: # plain CGI
- if self.firstCall:
- result = sys.stdin, spyceUtil.NoCloseOut(sys.stdout), sys.stderr, os.environ
- else:
- return 0
- else: # FCGI
- if not self.firstCall:
- self.send()
- result = self.recv()
- self.firstCall = 0
- return result
- def clearState(self):
- self.reqID = 0
- self.connection = None
- self.environ = {}
- self.stdin = StringIO()
- self.stderr = StringIO()
- self.stdout = StringIO()
- self.data = StringIO()
- def send(self):
- self.stderr.seek(0,0)
- self.stdout.seek(0,0)
- self.sendStream(FCGI_STDERR, self.stderr.read())
- self.sendStream(FCGI_STDOUT, self.stdout.read())
- r=record()
- r.recType=FCGI_END_REQUEST
- r.reqId=self.reqID
- r.appStatus=0
- r.protocolStatus=FCGI_REQUEST_COMPLETE
- r.writeRecord(self.connection)
- self.connection.close()
- self.clearState()
- def sendStream(self, streamType, streamData):
- r=record()
- r.recType = streamType
- r.reqId = self.reqID
- data = streamData
- while data:
- r.content, data = data[:8192], data[8192:]
- r.writeRecord(self.connection)
- r.content='' ; r.writeRecord(self.connection)
- def recv(self):
- self.connection, address = self.socket.accept()
- # rimtodo: filter to serve only allowed addresses
- # if good_addrs!=None and addr not in good_addrs:
- # raise 'Connection from invalid server!'
- remaining=1
- while remaining:
- r=record(); r.readRecord(self.connection)
- if r.recType in [FCGI_GET_VALUES]: # management records
- if r.recType == FCGI_GET_VALUES:
- r.recType = FCGI_GET_VALUES_RESULT
- v={}
- vars={'FCGI_MAX_CONNS' : FCGI_MAX_CONNS,
- 'FCGI_MAX_REQS' : FCGI_MAX_REQS,
- 'FCGI_MPXS_CONNS': FCGI_MPXS_CONNS}
- for i in r.values.keys():
- if vars.has_key(i): v[i]=vars[i]
- r.values=vars
- r.writeRecord(self.connection)
- elif r.reqId == 0: # management record of unknown type
- r2 = record()
- r2.recType = FCGI_UNKNOWN_TYPE ; r2.unknownType = r.recType
- r2.writeRecord(self.connection)
- continue
- elif r.reqId != self.reqID and r.recType != FCGI_BEGIN_REQUEST:
- continue # ignore inactive requests
- elif r.recType == FCGI_BEGIN_REQUEST and self.reqID != 0:
- continue # ignore BEGIN_REQUESTs in the middle of request
- if r.recType == FCGI_BEGIN_REQUEST: # begin request
- self.reqID = r.reqId
- if r.role == FCGI_AUTHORIZER: remaining=1
- elif r.role == FCGI_RESPONDER: remaining=2
- elif r.role == FCGI_FILTER: remaining=3
- elif r.recType == FCGI_PARAMS: # environment
- if r.content == '': remaining=remaining-1
- else:
- for i in r.values.keys():
- self.environ[i] = r.values[i]
- elif r.recType == FCGI_STDIN: # stdin
- if r.content == '': remaining=remaining-1
- else: self.stdin.write(r.content)
- elif r.recType == FCGI_DATA: # data
- if r.content == '': remaining=remaining-1
- else: self.data.write(r.content)
- # end while
- self.stdin.seek(0,0)
- self.data.seek(0,0)
- # return CGI environment
- return self.stdin, spyceUtil.NoCloseOut(self.stdout), self.stderr, self.environ
-
-