1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 """
18 SCGI Implementation
19 ===================
20
21 Here's the SCGI handling implemented.
22 """
23 __author__ = u"Andr\xe9 Malo"
24 __docformat__ = "restructuredtext en"
25
26 import errno as _errno
27 import itertools as _it
28 import socket as _socket
29 import sys as _sys
30 import traceback as _traceback
31
32 from wtf import Error
33 from wtf import impl as _impl
34 from wtf import stream as _stream
35 from wtf.config import ConfigurationError
36 from wtf.impl import _connection
37 from wtf.impl import _gateway
38 from wtf.impl import _util as _impl_util
39
40
42 """ Netstring error """
43
44
46 """
47 SCGI server
48
49 :IVariables:
50 - `config`: Configuration
51 - `opts`: Command line options
52 - `args`: Positioned command line arguments
53
54 :Types:
55 - `config`: `wtf.config.Config`
56 - `opts`: ``optparse.OptionContainer``
57 - `args`: ``list``
58 """
59 __implements__ = [_impl.ServerInterface]
60
62 """
63 Initialization
64
65 :See: `wtf.impl.ServerInterface`
66 """
67 self.config, self.opts, self.args = config, opts, args
68 self._gateway = Gateway(config, opts, args)
69
70 - def handle(self, (sock, peername), application, flags):
71 """
72 Handle an accepted socket
73
74 :See: `wtf.impl.ServerInterface`
75 """
76 conn = _connection.Connection(sock, peername)
77 try:
78 conn.settimeout(None)
79 request = SCGIRequest(self, conn, flags)
80 try:
81 try:
82 self._gateway.handle(conn, request, application)
83 except _socket.error, e:
84 if e[0] not in (_errno.EPIPE, _errno.ECONNRESET):
85 raise
86 print >> _sys.stderr, "Connection to webserver died."
87 except (SystemExit, KeyboardInterrupt):
88 raise
89 except:
90 try:
91 request.error(
92 "500 Internal Server Error",
93 "Something went wrong while processing the "
94 "request. You might want to try again later. "
95 "Sorry for the inconvenience."
96 )
97 except _socket.error:
98 pass
99 print >> _sys.stderr, \
100 "Request aborted due to exception:\n" + ''.join(
101 _traceback.format_exception(*_sys.exc_info())
102 )
103 finally:
104 request.close()
105 finally:
106 try:
107 conn.close()
108 except (SystemExit, KeyboardInterrupt):
109 raise
110 except:
111 pass
112
113
115 """ SCGI implementation specific gateway """
116
117 - def __init__(self, config, opts, args):
118 """
119 Initialization
120
121 :Parameters:
122 - `config`: Configuration
123 - `opts`: Command line options
124 - `args`: Positioned command line arguments
125
126 :Types:
127 - `config`: `wtf.config.Config`
128 - `opts`: ``optparse.OptionContainer``
129 - `args`: ``list``
130 """
131 super(Gateway, self).__init__(config, opts, args)
132
133 detector = self._detect_scheme_default
134 if 'scgi' in config.wtf:
135 option = config.wtf.scgi('ssl_detection', 'default')
136 if option:
137 try:
138 detector = getattr(self,
139 "_detect_scheme_%s" % option.encode('ascii')
140 )
141 except (AttributeError, UnicodeError):
142 raise ConfigurationError(
143 "Unrecognized SSL detection method %r" % (option,)
144 )
145 self._detect_scheme = detector
146
167
169 """ HTTPS environment scheme detector (SSL directly in gateway) """
170 if environ.get('HTTPS', '').lower() == 'on':
171 return 'https'
172 return 'http'
173
175 """
176 Scheme detector when lighttpd offloads SSL in the front of the GW
177
178 The lighttpd acts as a HTTP proxy.
179 """
180 scheme = environ.get('HTTP_X_FORWARDED_PROTO', '').lower()
181 if scheme in ('http', 'https'):
182
183 port = dict(http=80, https=443)[scheme]
184 environ['SERVER_PORT'] = str(port)
185 if ':' in environ['SERVER_NAME']:
186 shost, sport = environ['SERVER_NAME'].rsplit(':', 1)
187 try:
188 int(sport)
189 except ValueError:
190 pass
191 else:
192 environ['SERVER_NAME'] = '%s:%s' % (shost, port)
193 return scheme
194 return None
195
196
198 """ SCGI Request abstraction """
199 _response_body_stream = None
200 request_body_stream = None
201 _env = None
202
203 - def __init__(self, server, connection, flags):
204 """
205 Initialization
206
207 :Parameters:
208 - `server`: Server instance
209 - `connection`: Connection, this request is served on
210 - `flags`: Worker flags
211
212 :Types:
213 - `server`: `SCGIServer`
214 - `connection`: `Connection`
215 - `flags`: `FlagsInterface`
216 """
217 self._server = server
218 self.connection = SCGIConnection(connection)
219 self.flags = flags
220
228
251
276
277 - def error(self, status, message):
278 """
279 Emit a simple error
280
281 :Parameters:
282 - `status`: Status line
283 - `message`: Message
284
285 :Types:
286 - `status`: ``str``
287 - `message`: ``str``
288 """
289 out = "%s\n%s\n" % (status, message)
290 write = self.connection.writer.write
291 write("Status: %s\n" % status)
292 write("Content-Type: text/plain\n")
293 write("Content-Length: %s\n" % len(out))
294 write("\n")
295 write(out)
296
297
299 """
300 SCGI connection
301
302 :IVariables:
303 - `reader`: Connection read stream
304 - `writer`: Connection write stream
305 - `settimeout`: Timeout setter
306
307 :Types:
308 - `reader`: `stream.GenericStream`
309 - `writer`: `stream.GenericStream`
310 - `settimeout`: ``callable``
311 """
312
326
329
340
342 """
343 Read "netstring" from connection
344
345 :return: The netstring value
346 :rtype: ``str``
347
348 :Exceptions:
349 - `NetStringError`: Error reading or interpreting the netstring
350 """
351 data = _stream.read_exact(self.reader, self._netstring_size())
352 if self.reader.read(1) != ',':
353 raise NetStringError("EOS before netstring delimiter")
354 return data
355
357 """
358 Read netstring size from connection
359
360 :return: The netstring size
361 :rtype: ``int``
362
363 :Exceptions:
364 - `NetStringError`: Error reading or interpreting the number
365 """
366 chars, read = [], self.reader.read
367 push = chars.append
368 while True:
369 char = read(1)
370 if not char:
371 raise NetStringError("EOS before netstring size delimiter")
372 if char == ':':
373 break
374 push(char)
375 chars = ''.join(chars)
376 try:
377 return int(chars)
378 except ValueError:
379 raise NetStringError("Invalid netstring size: %r" % chars)
380