fcgi.py :  » Web-Frameworks » Webware » Webware-1.0.2 » WebKit » Adapters » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Web Frameworks » Webware 
Webware » Webware 1.0.2 » WebKit » Adapters » fcgi.py
#!/bin/env python
#------------------------------------------------------------------------
#               Copyright (c) 1998 by Total Control Software
#                         All Rights Reserved
#------------------------------------------------------------------------
#
# Module Name:  fcgi.py
#
# Description:  Handles communication with the FastCGI module of the
#               web server without using the FastCGI developers kit, but
#               will also work in a non-FastCGI environment, (straight CGI.)
#               This module was originally fetched from someplace on the
#               Net (I don't remember where and I can't find it now...) and
#               has been significantly modified to fix several bugs, be more
#               readable, more robust at handling large CGI data and return
#               document sizes, and also to fit the model that we had
#               previously used for FastCGI.
#
#     WARNING:  If you don't know what you are doing, don't tinker with this
#               module!
#
# Creation Date:    1/30/98 2:59:04PM
#
# License:      This is free software.  You may use this software for any
#               purpose including modification/redistribution, so long as
#               this header remains intact and that you do not claim any
#               rights of ownership or authorship of this software.  This
#               software has been tested, but no warranty is expressed or
#               implied.
#
#------------------------------------------------------------------------


import os, sys, socket, errno
try:
  from cStringIO import StringIO
except ImportError:
  from StringIO import StringIO
import cgi

#---------------------------------------------------------------------------

# Set various FastCGI constants
# Maximum number of requests that can be handled
FCGI_MAX_REQS = 1
FCGI_MAX_CONNS = 1

# Supported version of the FastCGI protocol
FCGI_VERSION_1 = 1

# Boolean: can this application multiplex connections?
FCGI_MPXS_CONNS = 0

# 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

# Types of management records
ManagementTypes = [FCGI_GET_VALUES]

FCGI_NULL_REQUEST_ID = 0

# Masks for flags component of FCGI_BEGIN_REQUEST
FCGI_KEEP_CONN = 1

# Values for role component of FCGI_BEGIN_REQUEST
FCGI_RESPONDER = 1 ; FCGI_AUTHORIZER = 2 ; FCGI_FILTER = 3

# Values for protocolStatus component of FCGI_END_REQUEST
FCGI_REQUEST_COMPLETE = 0               # Request completed nicely
FCGI_CANT_MPX_CONN    = 1               # This app can't multiplex
FCGI_OVERLOADED       = 2               # New request rejected; too busy
FCGI_UNKNOWN_ROLE     = 3               # Role value not known


error = 'fcgi.error'


#---------------------------------------------------------------------------

# The following function is used during debugging; it isn't called
# anywhere at the moment

def error(msg):
  "Append a string to /tmp/err"
  errf = open('/tmp/err', 'a+')
  errf.write(msg+'\n')
  errf.close()

#---------------------------------------------------------------------------

class record:
  "Class representing FastCGI records"
  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):
    s = map(ord, sock.recv(8))
    self.version, self.recType, paddingLength = s[0], s[1], s[6]
    self.reqId, contentLength = (s[2]<<8)+s[3], (s[4]<<8)+s[5]
    self.content = ""
    while len(self.content) < contentLength:
      data = sock.recv(contentLength - len(self.content))
      self.content += data
    if paddingLength != 0:
      padding = sock.recv(paddingLength)

    # Parse the content information
    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 = readPair(c, pos)
        self.values[name] = value
    elif self.recType == FCGI_END_REQUEST:
      b = map(ord, c[0:4])
      self.appStatus = (b[0]<<24) + (b[1]<<16) + (b[2]<<8) + b[3]
      self.protocolStatus = ord(c[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 += writePair(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 += 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 = ''.join(map(chr, hdr))

    sock.send(hdr + content + padLen*'\000')

#---------------------------------------------------------------------------

def readPair(s, pos):
  nameLen = ord(s[pos])
  pos += 1
  if nameLen & 128:
    b = map(ord, s[pos:pos+3])
    pos += 3
    nameLen = ((nameLen&127)<<24) + (b[0]<<16) + (b[1]<<8) + b[2]
  valueLen = ord(s[pos])
  pos += 1
  if valueLen & 128:
    b = map(ord, s[pos:pos+3])
    pos += 3
    valueLen = ((valueLen&127)<<24) + (b[0]<<16) + (b[1]<<8) + b[2]
  return (s[pos:pos+nameLen], s[pos+nameLen:pos+nameLen+valueLen],
    pos + nameLen + valueLen)

#---------------------------------------------------------------------------

def writePair(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 += chr(l)
  else:
    s += chr(128|(l>>24)&255) + chr((l>>16)&255) \
      + chr((l>>8)&255) + chr(l&255)
  return s + name + value

#---------------------------------------------------------------------------

def HandleManTypes(r, conn):
  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(conn)

#---------------------------------------------------------------------------
#---------------------------------------------------------------------------

_isFCGI = 1 # assume it is until we find out for sure

def isFCGI():
  global _isFCGI
  return _isFCGI

#---------------------------------------------------------------------------


_init = None
_sock = None

class FCGI:
  def __init__(self):
    self.haveFinished = 0
    if _init is None:
      _startup()
    if not isFCGI():
      self.haveFinished = 1
      self.inp, self.out, self.err, self.env = \
        sys.stdin, sys.stdout, sys.stderr, os.environ
      return

    if os.environ.has_key('FCGI_WEB_SERVER_ADDRS'):
      good_addrs = os.environ['FCGI_WEB_SERVER_ADDRS'].split(',')
      good_addrs = map(good_addrs.strip()) # remove whitespace
    else:
      good_addrs = None

    self.conn, addr = _sock.accept()
    stdin, data = "", ""
    self.env = {}
    self.requestId = 0
    remaining = 1

    # Check if the connection is from a legal address
    if good_addrs is not None and addr not in good_addrs:
      raise error, 'Connection from invalid server!'

    while remaining:
      r = record()
      r.readRecord(self.conn)

      if r.recType in ManagementTypes:
        HandleManTypes(r, self.conn)

      elif r.reqId == 0:
        # Oh, poopy. It's a management record of an unknown type.
        # Signal the error.
        r2 = record()
        r2.recType = FCGI_UNKNOWN_TYPE
        r2.unknownType = r.recType
        r2.writeRecord(self.conn)
        continue # charge onwards

      # Ignore requests that aren't active
      elif r.reqId != self.requestId \
          and r.recType != FCGI_BEGIN_REQUEST:
        continue

      # If we're already doing a request, ignore further BEGIN_REQUESTs
      elif r.recType == FCGI_BEGIN_REQUEST and self.requestId != 0:
        continue

      # Begin a new request
      if r.recType == FCGI_BEGIN_REQUEST:
        self.requestId = 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:
        if r.content == "":
          remaining -= 1
        else:
          for i in r.values.keys():
            self.env[i] = r.values[i]

      elif r.recType == FCGI_STDIN:
        if r.content == "":
          remaining -= 1
        else:
          stdin += r.content

      elif r.recType == FCGI_DATA:
        if r.content == "":
          remaining -= 1
        else:
          data += r.content
    # end of while remaining:

    self.inp = sys.stdin  = StringIO(stdin)
    self.err = sys.stderr = StringIO()
    self.out = sys.stdout = StringIO()
    self.data = StringIO(data)

  def __del__(self):
    self.Finish()

  def Finish(self, status=0):
    if not self.haveFinished:
      self.haveFinished = 1

      self.err.seek(0, 0)
      self.out.seek(0, 0)

      r = record()
      r.recType = FCGI_STDERR
      r.reqId = self.requestId
      data = self.err.read()
      while data:
        chunk, data = self.getNextChunk(data)
        r.content = chunk
        r.writeRecord(self.conn)
      r.content = ""
      r.writeRecord(self.conn) # Terminate stream

      r.recType = FCGI_STDOUT
      data = self.out.read()
      while data:
        chunk, data = self.getNextChunk(data)
        r.content = chunk
        r.writeRecord(self.conn)
      r.content = ""
      r.writeRecord(self.conn) # Terminate stream

      r = record()
      r.recType = FCGI_END_REQUEST
      r.reqId = self.requestId
      r.appStatus = status
      r.protocolStatus = FCGI_REQUEST_COMPLETE
      r.writeRecord(self.conn)
      self.conn.close()

  def getFieldStorage(self):
    method = 'GET'
    if self.env.has_key('REQUEST_METHOD'):
      method = self.env['REQUEST_METHOD'].upper()
    return cgi.FieldStorage(fp=method != 'GET' and self.inp or None,
      environ=self.env, keep_blank_values=1)

  def getNextChunk(self, data):
    chunk = data[:8192]
    data = data[8192:]
    return chunk, data


Accept = FCGI       # alias for backwards compatibility
#---------------------------------------------------------------------------

def _startup():
  global _init
  _init = 1
  try:
    s = socket.fromfd(sys.stdin.fileno(), socket.AF_INET,
              socket.SOCK_STREAM)
    s.getpeername()
  except socket.error, (err, errmsg):
    if err != errno.ENOTCONN:       # must be a non-fastCGI environment
      global _isFCGI
      _isFCGI = 0
      return

  global _sock
  _sock = s

#---------------------------------------------------------------------------

def _test():
  counter = 0
  try:
    while isFCGI():
      req = Accept()
      counter += 1

      try:
        fs = req.getFieldStorage()
        size = int(fs['size'].value)
        doc = ['*' * size]
      except:
        doc = ['<HTML><HEAD><TITLE>FCGI TestApp</TITLE></HEAD>\n<BODY>\n']
        doc.append('<H2>FCGI TestApp</H2><P>')
        doc.append('<b>request count</b> = %d<br>' % counter)
        doc.append('<b>pid</b> = %s<br>' % os.getpid())
        if req.env.has_key('CONTENT_LENGTH'):
          cl = int(req.env['CONTENT_LENGTH'])
          doc.append('<br><b>POST data (%d):</b><br><pre>' % cl)
          keys = fs.keys()
          keys.sort()
          for k in keys:
            val = fs[k]
            if type(val) == type([]):
              doc.append('    <b>%-15s :</b>  %s\n' % (k, val))
            else:
              doc.append('    <b>%-15s :</b>  %s\n' % (k, val.value))
          doc.append('</pre>')

        doc.append('<P><HR><P><pre>')
        keys = req.env.keys()
        keys.sort()
        for k in keys:
          doc.append('<b>%-20s :</b>  %s\n' % (k, req.env[k]))
        doc.append('\n</pre><P><HR>\n')
        doc.append('</BODY></HTML>\n')

      doc = ''.join(doc)
      req.out.write('Content-length: %s\r\n'
            'Content-type: text/html\r\n'
            'Cache-Control: no-cache\r\n'
            '\r\n'
              % len(doc))
      req.out.write(doc)

      req.Finish()
  except:
    import traceback
    f = open('traceback', 'w')
    traceback.print_exc(file = f)
    # f.write('%s' % doc)

if __name__ == '__main__':
  # import pdb
  # pdb.run('_test()')
  _test()
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.