root/cherokee-pyscgi/pyscgi/pyscgi.py

Revision 1908, 5.2 kB (checked in by alo, 3 months ago)

--

Line 
1 """
2 pyscgi.py - Portable SCGI implementation
3
4 This module has been written as part of the Cherokee project:
5                http://www.cherokee-project.com/
6 """
7
8 # Copyright (c) 2006, Alvaro Lopez Ortega <alvaro@alobbs.com>
9 # All rights reserved.
10 #
11 # Redistribution and use in source and binary forms, with or without
12 # modification, are permitted provided that the following conditions
13 # are met:
14 #
15 # * Redistributions of source code must retain the above copyright
16 #   notice, this list of conditions and the following disclaimer.
17 # * Redistributions in binary form must reproduce the above copyright
18 #   notice, this list of conditions and the following disclaimer in
19 #   the documentation and/or other materials provided with the
20 #   distribution.
21 # * The name "Alvaro Lopez Ortega" may not be used to endorse or
22 #   promote products derived from this software without specific prior
23 #   written permission.
24 #
25 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29 # REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 # POSSIBILITY OF SUCH DAMAGE.
37
38 import SocketServer
39 import traceback
40 import socket
41 import errno
42 import sys
43
44 __version__ = '1.8'
45 __author__  = 'Alvaro Lopez Ortega'
46
47
48 class SCGIHandler (SocketServer.StreamRequestHandler):
49     def __init__ (self, request, client_address, server):
50         self.env    = {}
51         self.post   = None
52         SocketServer.StreamRequestHandler.__init__ (self, request, client_address, server)
53
54     def __safe_read (self, lenght):
55         while True:
56             chunk = None
57             try:
58                 chunk = self.rfile.read(lenght)
59                 return chunk
60             except socket.error, (err, strerr):
61                 if err == errno.EAGAIN or \
62                    err == errno.EWOULDBLOCK or \
63                    err == errno.EINPROGRESS:
64                    if chunk:
65                        return chunk
66                    continue
67             raise
68
69     def send(self, buf):
70         pending = len(buf)
71         offset = 0
72         while pending:
73             try:
74                 sent = self.connection.send(buf[offset:])
75                 pending -= sent
76                 offset += sent
77             except socket.error, e:
78                 if e[0]!=errno.EAGAIN:
79                     raise
80
81     def __read_netstring_size (self):
82         size = ""
83         while 1:
84             c = self.__safe_read(1)
85             if c == ':':
86                 break
87             elif not c:
88                 raise IOError, 'Malformed netstring'
89             size += c
90         return long(size)
91
92     def __read_netstring (self):
93         data = ""
94         size = self.__read_netstring_size()
95         while size > 0:
96             s = self.__safe_read(size)
97             if not s:
98                 raise IOError, 'Malformed netstring'
99             data += s
100             size -= len(s)
101             if self.__safe_read(1) != ',':
102                 raise IOError, 'Missing netstring terminator'
103         return data
104
105     def __read_env (self):
106         headers = self.__read_netstring()
107         items   = headers.split('\0')[:-1]
108         itemsn  = len(items)
109         if itemsn % 2 != 0:
110             raise Exception, 'Malformed headers'
111         for i in range(0, itemsn, 2):
112             self.env[items[i]] = items[i+1]
113
114     def handle_post (self):
115         if not self.env.has_key('CONTENT_LENGTH'):
116             return
117         if self.post:
118             return
119         length = int(self.env['CONTENT_LENGTH'])
120         self.post = self.__safe_read(length)
121
122     def handle (self):
123         self.__read_env()
124
125         try:
126             self.handle_request()
127         except:
128             if sys.exc_type != SystemExit:
129                 traceback.print_exc()  # Print the error
130
131         try:
132             self.finish()          # Closes wfile and rfile
133             self.request.close()   # ..
134         except: pass
135
136     def handle_request (self):
137         self.send('Status: 200 OK\r\n')
138         self.send("Content-Type: text/plain\r\n\r\n")
139         self.send("handle_request() should be overridden")
140
141
142 class SCGIServer(SocketServer.ThreadingTCPServer):
143     def __init__(self, handler_class=SCGIHandler, host="", port=4000):
144         self.allow_reuse_address = True
145         SocketServer.ThreadingTCPServer.__init__ (self, (host, port), handler_class)
146
147 class SCGIServerFork (SocketServer.ForkingTCPServer):
148     def __init__(self, handler_class=SCGIHandler, host="", port=4000):
149         self.allow_reuse_address = True
150         SocketServer.ForkingTCPServer.__init__ (self, (host, port), handler_class)
151
152
153 def ServerFactory (threading=False, *args, **kargs):
154     if threading:
155         return SCGIServer(*args, **kargs)
156     else:
157         return SCGIServerFork(*args, **kargs)
Note: See TracBrowser for help on using the browser.