1 __all__ = []
2
3 import asyncore
4 import socket
5 import sys
6 try:
7 import io
8 except ImportError:
9 try:
10 import cStringIO as io
11 except ImportError:
12 import StringIO as io
13
15 """SCGI connection class used by L{SCGIServer}."""
16
17 NEW = 0*4 | 1
18 HEADER = 1*4 | 1
19 BODY = 2*4 | 1
20 REQ = 3*4 | 2
21 - def __init__(self, server, connection, addr, maxrequestsize=65536,
22 maxpostsize=8<<20, blocksize=4096, config={}):
23 asyncore.dispatcher.__init__(self, connection)
24
25 self.server = server
26 self.addr = addr
27 self.maxrequestsize = maxrequestsize
28 self.maxpostsize = maxpostsize
29 self.blocksize = blocksize
30 self.state = SCGIConnection.NEW
31 self.environ = config.copy()
32 self.reqlen = -1
33 self.inbuff = ""
34 self.outbuff = ""
35 self.wsgihandler = None
36 self.outheaders = ()
37
38 self.body = io.StringIO()
39
41 return {"wsgi.version": (1, 0),
42 "wsgi.input": self.body,
43 "wsgi.errors": self.server.error,
44 "wsgi.url_scheme": "http",
45 "wsgi.multithread": False,
46 "wsgi.multiprocess": False,
47 "wsgi.run_once": False}
48
50 if self.outheaders != True:
51 assert not self.outbuff
52 status, headers = self.outheaders
53 headdata = "".join(map("%s: %s\r\n".__mod__, headers))
54 self.outbuff = "Status: %s\r\n%s\r\n" % (status, headdata)
55 self.outheaders = True
56
63
65 """C{asyncore} interface"""
66 return self.state & 1 == 1
67
69 """C{asyncore} interface"""
70 return self.state & 2 == 2
71
73 """C{asyncore} interface"""
74 data = self.recv(self.blocksize)
75 self.inbuff += data
76 if self.state == SCGIConnection.NEW:
77 if ':' in self.inbuff:
78 reqlen, self.inbuff = self.inbuff.split(':', 1)
79 if not reqlen.isdigit():
80 self.close()
81 return
82 reqlen = int(reqlen)
83 if reqlen > self.maxrequestsize:
84 self.close()
85 return
86 self.reqlen = reqlen
87 self.state = SCGIConnection.HEADER
88 elif len(self.inbuff) > self.maxrequestsize:
89 self.close()
90 return
91
92 if self.state == SCGIConnection.HEADER:
93 buff = self.inbuff[:self.reqlen]
94 remainder = self.inbuff[self.reqlen:]
95
96 while buff.count('\0') >= 2:
97 key, value, buff = buff.split('\0', 2)
98 self.environ[key] = value
99 self.reqlen -= len(key) + len(value) + 2
100
101 self.inbuff = buff + remainder
102
103 if self.reqlen == 0:
104 if self.inbuff.startswith(','):
105 self.inbuff = self.inbuff[1:]
106 if not self.environ.get("CONTENT_LENGTH", "bad").isdigit():
107 self.close()
108 return
109 self.reqlen = int(self.environ["CONTENT_LENGTH"])
110 if self.reqlen > self.maxpostsize:
111 self.close()
112 return
113 self.state = SCGIConnection.BODY
114 else:
115 self.close()
116 return
117
118 if self.state == SCGIConnection.BODY:
119 if len(self.inbuff) >= self.reqlen:
120 self.body.write(self.inbuff[:self.reqlen])
121 self.body.seek(0)
122 self.inbuff = ""
123 self.reqlen = 0
124 self.environ.update(self._wsgi_headers())
125 if self.environ.get("HTTPS", "no").lower() in ('yes', 'y', '1'):
126 self.environ["wsgi.url_scheme"] = "https"
127 if "HTTP_CONTENT_TYPE" in self.environ:
128 self.environ["CONTENT_TYPE"] = \
129 self.environ.pop("HTTP_CONTENT_TYPE")
130 if "HTTP_CONTENT_LENGTH" in self.environ:
131 del self.environ["HTTP_CONTENT_LENGTH"]
132 self.wsgihandler = iter(self.server.wsgiapp(
133 self.environ, self.start_response))
134 self.state = SCGIConnection.REQ
135 else:
136 self.body.write(self.inbuff)
137 self.reqlen -= len(self.inbuff)
138 self.inbuff = ""
139
141 assert isinstance(status, str)
142 assert isinstance(headers, list)
143 if exc_info:
144 if self.outheaders == True:
145 try:
146 raise exc_info[0], exc_info[1], exc_info[2]
147 finally:
148 exc_info = None
149 assert self.outheaders != True
150 self.outheaders = (status, headers)
151 return self._wsgi_write
152
154 """C{asyncore} interface"""
155 assert self.state >= SCGIConnection.REQ
156 if len(self.outbuff) < self.blocksize:
157 self._try_send_headers()
158 for data in self.wsgihandler:
159 assert isinstance(data, str)
160 if data:
161 self.outbuff += data
162 break
163 if len(self.outbuff) == 0:
164 if hasattr(self.wsgihandler, "close"):
165 self.wsgihandler.close()
166 self.close()
167 return
168 try:
169 sentbytes = self.send(self.outbuff[:self.blocksize])
170 except socket.error:
171 if hasattr(self.wsgihandler, "close"):
172 self.wsgihandler.close()
173 self.close()
174 return
175 self.outbuff = self.outbuff[sentbytes:]
176
178 """C{asyncore} interface"""
179 self.close()
180
181 __all__.append("SCGIServer")
183 """SCGI Server for WSGI applications. It does not use multiple processes or
184 multiple threads."""
185 - def __init__(self, wsgiapp, port, interface="localhost", error=sys.stderr,
186 maxrequestsize=None, maxpostsize=None, blocksize=None,
187 config={}):
188 """
189 @param wsgiapp: is the wsgi application to be run.
190 @type port: int
191 @param port: is an int representing the TCP port number to be used.
192 @type interface: str
193 @param interface: is a string specifying the network interface to bind
194 which defaults to C{"localhost"} making the server inaccessible
195 over network.
196 @param error: is a file-like object being passed as C{wsgi.error} in the
197 environ parameter defaulting to stderr.
198 @type maxrequestsize: int
199 @param maxrequestsize: limit the size of request blocks in scgi
200 connections. Connections are dropped when this limit is hit.
201 @type maxpostsize: int
202 @param maxpostsize: limit the size of post bodies that may be processed
203 by this instance. Connections are dropped when this limit is
204 hit.
205 @type blocksize: int
206 @param blocksize: is amount of data to read or write from or to the
207 network at once
208 @type config: {}
209 @param config: the environ dictionary is updated using these values for
210 each request.
211 """
212 asyncore.dispatcher.__init__(self)
213
214 self.wsgiapp = wsgiapp
215 self.error = error
216 self.conf = {}
217 if maxrequestsize is not None:
218 self.conf["maxrequestsize"] = maxrequestsize
219 if maxpostsize is not None:
220 self.conf["maxpostsize"] = maxpostsize
221 if blocksize is not None:
222 self.conf["blocksize"] = blocksize
223 self.conf["config"] = config
224
225 self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
226 self.set_reuse_addr()
227 self.bind((interface, port))
228 self.listen(5)
229
231 """asyncore interface"""
232 ret = self.accept()
233 if ret is not None:
234 conn, addr = ret
235 SCGIConnection(self, conn, addr, **self.conf)
236
238 """Runs the server. It will not return and you can invoke
239 C{asyncore.loop()} instead achieving the same effect."""
240 asyncore.loop()
241