1
# Copyright (c) 2002, 2003, 2005, 2006 Allan Saddi <allan@saddi.com>
4
# Redistribution and use in source and binary forms, with or without
5
# modification, are permitted provided that the following conditions
7
# 1. Redistributions of source code must retain the above copyright
8
# notice, this list of conditions and the following disclaimer.
9
# 2. Redistributions in binary form must reproduce the above copyright
10
# notice, this list of conditions and the following disclaimer in the
11
# documentation and/or other materials provided with the distribution.
13
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
__author__ = 'Allan Saddi <allan@saddi.com>'
28
__version__ = '$Revision$'
34
import cStringIO as StringIO
43
thread_available = True
45
import dummy_thread as thread
46
import dummy_threading as threading
47
thread_available = False
49
# Apparently 2.3 doesn't define SHUT_WR? Assume it is 1 in this case.
50
if not hasattr(socket, 'SHUT_WR'):
53
__all__ = ['BaseFCGIServer']
55
# Constants from the spec.
56
FCGI_LISTENSOCK_FILENO = 0
62
FCGI_BEGIN_REQUEST = 1
63
FCGI_ABORT_REQUEST = 2
71
FCGI_GET_VALUES_RESULT = 10
72
FCGI_UNKNOWN_TYPE = 11
73
FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
75
FCGI_NULL_REQUEST_ID = 0
83
FCGI_REQUEST_COMPLETE = 0
84
FCGI_CANT_MPX_CONN = 1
88
FCGI_MAX_CONNS = 'FCGI_MAX_CONNS'
89
FCGI_MAX_REQS = 'FCGI_MAX_REQS'
90
FCGI_MPXS_CONNS = 'FCGI_MPXS_CONNS'
92
FCGI_Header = '!BBHHBx'
93
FCGI_BeginRequestBody = '!HB5x'
94
FCGI_EndRequestBody = '!LB3x'
95
FCGI_UnknownTypeBody = '!B7x'
97
FCGI_EndRequestBody_LEN = struct.calcsize(FCGI_EndRequestBody)
98
FCGI_UnknownTypeBody_LEN = struct.calcsize(FCGI_UnknownTypeBody)
103
# Set non-zero to write debug output to a file.
105
DEBUGLOG = '/tmp/fcgi.log'
107
def _debug(level, msg):
112
f = open(DEBUGLOG, 'a')
113
f.write('%sfcgi: %s\n' % (time.ctime()[4:-4], msg))
118
class InputStream(object):
120
File-like object representing FastCGI input streams (FCGI_STDIN and
121
FCGI_DATA). Supports the minimum methods required by WSGI spec.
123
def __init__(self, conn):
127
self._shrinkThreshold = conn.server.inputStreamShrinkThreshold
131
self._pos = 0 # Current read position.
132
self._avail = 0 # Number of bytes currently available.
134
self._eof = False # True when server has sent EOF notification.
136
def _shrinkBuffer(self):
137
"""Gets rid of already read data (since we can't rewind)."""
138
if self._pos >= self._shrinkThreshold:
139
self._buf = self._buf[self._pos:]
140
self._avail -= self._pos
143
assert self._avail >= 0
145
def _waitForData(self):
146
"""Waits for more data to become available."""
147
self._conn.process_input()
149
def read(self, n=-1):
150
if self._pos == self._avail and self._eof:
153
if n < 0 or (self._avail - self._pos) < n:
154
# Not enough data available.
156
# And there's no more coming.
160
# Wait for more data.
164
newPos = self._pos + n
166
# Merge buffer list, if necessary.
168
self._buf += ''.join(self._bufList)
170
r = self._buf[self._pos:newPos]
175
def readline(self, length=None):
176
if self._pos == self._avail and self._eof:
179
# Unfortunately, we need to merge the buffer list early.
181
self._buf += ''.join(self._bufList)
184
i = self._buf.find('\n', self._pos)
188
# No more data coming.
192
if length is not None and len(self._buf) >= length + self._pos:
193
newPos = self._pos + length
195
# Wait for more to come.
201
r = self._buf[self._pos:newPos]
206
def readlines(self, sizehint=0):
209
line = self.readline()
213
if 0 < sizehint <= total:
215
line = self.readline()
227
def add_data(self, data):
231
self._bufList.append(data)
232
self._avail += len(data)
234
class MultiplexedInputStream(InputStream):
236
A version of InputStream meant to be used with MultiplexedConnections.
237
Assumes the MultiplexedConnection (the producer) and the Request
238
(the consumer) are running in different threads.
240
def __init__(self, conn):
241
super(MultiplexedInputStream, self).__init__(conn)
243
# Arbitrates access to this InputStream (it's used simultaneously
244
# by a Request and its owning Connection object).
245
lock = threading.RLock()
247
# Notifies Request thread that there is new data available.
248
self._lock = threading.Condition(lock)
250
def _waitForData(self):
251
# Wait for notification from add_data().
254
def read(self, n=-1):
257
return super(MultiplexedInputStream, self).read(n)
261
def readline(self, length=None):
264
return super(MultiplexedInputStream, self).readline(length)
268
def add_data(self, data):
271
super(MultiplexedInputStream, self).add_data(data)
276
class OutputStream(object):
278
FastCGI output stream (FCGI_STDOUT/FCGI_STDERR). By default, calls to
279
write() or writelines() immediately result in Records being sent back
280
to the server. Buffering should be done in a higher level!
282
def __init__(self, conn, req, type, buffered=False):
286
self._buffered = buffered
287
self._bufList = [] # Used if buffered is True
288
self.dataWritten = False
291
def _write(self, data):
294
toWrite = min(length, self._req.server.maxwrite - FCGI_HEADER_LEN)
296
rec = Record(self._type, self._req.requestId)
297
rec.contentLength = toWrite
298
rec.contentData = data[:toWrite]
299
self._conn.writeRecord(rec)
301
data = data[toWrite:]
304
def write(self, data):
305
assert not self.closed
310
self.dataWritten = True
313
self._bufList.append(data)
317
def writelines(self, lines):
318
assert not self.closed
324
# Only need to flush if this OutputStream is actually buffered.
326
data = ''.join(self._bufList)
330
# Though available, the following should NOT be called by WSGI apps.
332
"""Sends end-of-stream notification, if necessary."""
333
if not self.closed and self.dataWritten:
335
rec = Record(self._type, self._req.requestId)
336
self._conn.writeRecord(rec)
339
class TeeOutputStream(object):
341
Simple wrapper around two or more output file-like objects that copies
342
written data to all streams.
344
def __init__(self, streamList):
345
self._streamList = streamList
347
def write(self, data):
348
for f in self._streamList:
351
def writelines(self, lines):
356
for f in self._streamList:
359
class StdoutWrapper(object):
361
Wrapper for sys.stdout so we know if data has actually been written.
363
def __init__(self, stdout):
365
self.dataWritten = False
367
def write(self, data):
369
self.dataWritten = True
370
self._file.write(data)
372
def writelines(self, lines):
376
def __getattr__(self, name):
377
return getattr(self._file, name)
379
def decode_pair(s, pos=0):
381
Decodes a name/value pair.
383
The number of bytes decoded as well as the name/value pair
386
nameLength = ord(s[pos])
388
nameLength = struct.unpack('!L', s[pos:pos+4])[0] & 0x7fffffff
393
valueLength = ord(s[pos])
394
if valueLength & 128:
395
valueLength = struct.unpack('!L', s[pos:pos+4])[0] & 0x7fffffff
400
name = s[pos:pos+nameLength]
402
value = s[pos:pos+valueLength]
405
return (pos, (name, value))
407
def encode_pair(name, value):
409
Encodes a name/value pair.
411
The encoded string is returned.
413
nameLength = len(name)
417
s = struct.pack('!L', nameLength | 0x80000000L)
419
valueLength = len(value)
420
if valueLength < 128:
421
s += chr(valueLength)
423
s += struct.pack('!L', valueLength | 0x80000000L)
425
return s + name + value
427
class Record(object):
431
Used for encoding/decoding records.
433
def __init__(self, type=FCGI_UNKNOWN_TYPE, requestId=FCGI_NULL_REQUEST_ID):
434
self.version = FCGI_VERSION_1
436
self.requestId = requestId
437
self.contentLength = 0
438
self.paddingLength = 0
439
self.contentData = ''
441
def _recvall(sock, length):
443
Attempts to receive length bytes from a socket, blocking if necessary.
444
(Socket may be blocking or non-blocking.)
450
data = sock.recv(length)
451
except socket.error, e:
452
if e[0] == errno.EAGAIN:
453
select.select([sock], [], [])
459
dataList.append(data)
463
return ''.join(dataList), recvLen
464
_recvall = staticmethod(_recvall)
466
def read(self, sock):
467
"""Read and decode a Record from a socket."""
469
header, length = self._recvall(sock, FCGI_HEADER_LEN)
473
if length < FCGI_HEADER_LEN:
476
self.version, self.type, self.requestId, self.contentLength, \
477
self.paddingLength = struct.unpack(FCGI_Header, header)
479
if __debug__: _debug(9, 'read: fd = %d, type = %d, requestId = %d, '
480
'contentLength = %d' %
481
(sock.fileno(), self.type, self.requestId,
484
if self.contentLength:
486
self.contentData, length = self._recvall(sock,
491
if length < self.contentLength:
494
if self.paddingLength:
496
self._recvall(sock, self.paddingLength)
500
def _sendall(sock, data):
502
Writes data to a socket and does not return until all the data is sent.
507
sent = sock.send(data)
508
except socket.error, e:
509
if e[0] == errno.EAGAIN:
510
select.select([], [sock], [])
516
_sendall = staticmethod(_sendall)
518
def write(self, sock):
519
"""Encode and write a Record to a socket."""
520
self.paddingLength = -self.contentLength & 7
522
if __debug__: _debug(9, 'write: fd = %d, type = %d, requestId = %d, '
523
'contentLength = %d' %
524
(sock.fileno(), self.type, self.requestId,
527
header = struct.pack(FCGI_Header, self.version, self.type,
528
self.requestId, self.contentLength,
530
self._sendall(sock, header)
531
if self.contentLength:
532
self._sendall(sock, self.contentData)
533
if self.paddingLength:
534
self._sendall(sock, '\x00'*self.paddingLength)
536
class TimeoutException(Exception):
539
class Request(object):
541
Represents a single FastCGI request.
543
These objects are passed to your handler and is the main interface
544
between your handler and the fcgi module. The methods should not
545
be called by your handler. However, server, params, stdin, stdout,
546
stderr, and data are free for your handler's use.
548
def __init__(self, conn, inputStreamClass, timeout):
550
self._timeout = timeout
552
self.server = conn.server
554
self.stdin = inputStreamClass(conn)
555
self.stdout = OutputStream(conn, self, FCGI_STDOUT)
556
self.stderr = OutputStream(conn, self, FCGI_STDERR, buffered=True)
557
self.data = inputStreamClass(conn)
559
def timeout_handler(self, signum, frame):
560
self.stderr.write('Timeout Exceeded\n')
561
self.stderr.write("\n".join(traceback.format_stack(frame)))
564
raise TimeoutException
567
"""Runs the handler, flushes the streams, and ends the request."""
568
# If there is a timeout
570
old_alarm = signal.signal(signal.SIGALRM, self.timeout_handler)
571
signal.alarm(self._timeout)
574
protocolStatus, appStatus = self.server.handler(self)
576
traceback.print_exc(file=self.stderr)
578
if not self.stdout.dataWritten:
579
self.server.error(self)
581
protocolStatus, appStatus = FCGI_REQUEST_COMPLETE, 0
583
if __debug__: _debug(1, 'protocolStatus = %d, appStatus = %d' %
584
(protocolStatus, appStatus))
586
# Restore old handler if timeout was given
589
signal.signal(signal.SIGALRM, old_alarm)
593
self._end(appStatus, protocolStatus)
594
except socket.error, e:
595
if e[0] != errno.EPIPE:
598
def _end(self, appStatus=0L, protocolStatus=FCGI_REQUEST_COMPLETE):
599
self._conn.end_request(self, appStatus, protocolStatus)
605
class CGIRequest(Request):
606
"""A normal CGI request disguised as a FastCGI request."""
607
def __init__(self, server):
608
# These are normally filled in by Connection.
610
self.role = FCGI_RESPONDER
615
self.params = dict(os.environ)
616
self.stdin = sys.stdin
617
self.stdout = StdoutWrapper(sys.stdout) # Oh, the humanity!
618
self.stderr = sys.stderr
619
self.data = StringIO.StringIO()
622
def _end(self, appStatus=0L, protocolStatus=FCGI_REQUEST_COMPLETE):
626
# Not buffered, do nothing.
629
class Connection(object):
631
A Connection with the web server.
633
Each Connection is associated with a single socket (which is
634
connected to the web server) and is responsible for handling all
635
the FastCGI message processing for that socket.
638
_inputStreamClass = InputStream
640
def __init__(self, sock, addr, server, timeout):
644
self._timeout = timeout
646
# Active Requests for this Connection, mapped by request ID.
649
def _cleanupSocket(self):
650
"""Close the Connection's socket."""
652
self._sock.shutdown(socket.SHUT_WR)
657
r, w, e = select.select([self._sock], [], [])
658
if not r or not self._sock.recv(1024):
665
"""Begin processing data from the socket."""
666
self._keepGoing = True
667
while self._keepGoing:
670
except (EOFError, KeyboardInterrupt):
672
except (select.error, socket.error), e:
673
if e[0] == errno.EBADF: # Socket was closed by Request.
677
self._cleanupSocket()
679
def process_input(self):
680
"""Attempt to read a single Record from the socket and process it."""
681
# Currently, any children Request threads notify this Connection
682
# that it is no longer needed by closing the Connection's socket.
683
# We need to put a timeout on select, otherwise we might get
684
# stuck in it indefinitely... (I don't like this solution.)
685
while self._keepGoing:
687
r, w, e = select.select([self._sock], [], [], 1.0)
689
# Sigh. ValueError gets thrown sometimes when passing select
693
if not self._keepGoing:
698
if rec.type == FCGI_GET_VALUES:
699
self._do_get_values(rec)
700
elif rec.type == FCGI_BEGIN_REQUEST:
701
self._do_begin_request(rec)
702
elif rec.type == FCGI_ABORT_REQUEST:
703
self._do_abort_request(rec)
704
elif rec.type == FCGI_PARAMS:
706
elif rec.type == FCGI_STDIN:
708
elif rec.type == FCGI_DATA:
710
elif rec.requestId == FCGI_NULL_REQUEST_ID:
711
self._do_unknown_type(rec)
713
# Need to complain about this.
716
def writeRecord(self, rec):
718
Write a Record to the socket.
720
rec.write(self._sock)
722
def end_request(self, req, appStatus=0L,
723
protocolStatus=FCGI_REQUEST_COMPLETE, remove=True):
727
Called by Request objects. An FCGI_END_REQUEST Record is
728
sent to the web server. If the web server no longer requires
729
the connection, the socket is closed, thereby ending this
730
Connection (run() returns).
732
rec = Record(FCGI_END_REQUEST, req.requestId)
733
rec.contentData = struct.pack(FCGI_EndRequestBody, appStatus,
735
rec.contentLength = FCGI_EndRequestBody_LEN
736
self.writeRecord(rec)
739
del self._requests[req.requestId]
741
if __debug__: _debug(2, 'end_request: flags = %d' % req.flags)
743
if not (req.flags & FCGI_KEEP_CONN) and not self._requests:
744
self._cleanupSocket()
745
self._keepGoing = False
747
def _do_get_values(self, inrec):
748
"""Handle an FCGI_GET_VALUES request from the web server."""
749
outrec = Record(FCGI_GET_VALUES_RESULT)
752
while pos < inrec.contentLength:
753
pos, (name, value) = decode_pair(inrec.contentData, pos)
754
cap = self.server.capability.get(name)
756
outrec.contentData += encode_pair(name, str(cap))
758
outrec.contentLength = len(outrec.contentData)
759
self.writeRecord(outrec)
761
def _do_begin_request(self, inrec):
762
"""Handle an FCGI_BEGIN_REQUEST from the web server."""
763
role, flags = struct.unpack(FCGI_BeginRequestBody, inrec.contentData)
765
req = self.server.request_class(self, self._inputStreamClass,
767
req.requestId, req.role, req.flags = inrec.requestId, role, flags
770
if not self._multiplexed and self._requests:
771
# Can't multiplex requests.
772
self.end_request(req, 0L, FCGI_CANT_MPX_CONN, remove=False)
774
self._requests[inrec.requestId] = req
776
def _do_abort_request(self, inrec):
778
Handle an FCGI_ABORT_REQUEST from the web server.
780
We just mark a flag in the associated Request.
782
req = self._requests.get(inrec.requestId)
786
def _start_request(self, req):
787
"""Run the request."""
788
# Not multiplexed, so run it inline.
791
def _do_params(self, inrec):
793
Handle an FCGI_PARAMS Record.
795
If the last FCGI_PARAMS Record is received, start the request.
797
req = self._requests.get(inrec.requestId)
799
if inrec.contentLength:
801
while pos < inrec.contentLength:
802
pos, (name, value) = decode_pair(inrec.contentData, pos)
803
req.params[name] = value
805
self._start_request(req)
807
def _do_stdin(self, inrec):
808
"""Handle the FCGI_STDIN stream."""
809
req = self._requests.get(inrec.requestId)
811
req.stdin.add_data(inrec.contentData)
813
def _do_data(self, inrec):
814
"""Handle the FCGI_DATA stream."""
815
req = self._requests.get(inrec.requestId)
817
req.data.add_data(inrec.contentData)
819
def _do_unknown_type(self, inrec):
820
"""Handle an unknown request type. Respond accordingly."""
821
outrec = Record(FCGI_UNKNOWN_TYPE)
822
outrec.contentData = struct.pack(FCGI_UnknownTypeBody, inrec.type)
823
outrec.contentLength = FCGI_UnknownTypeBody_LEN
824
self.writeRecord(rec)
826
class MultiplexedConnection(Connection):
828
A version of Connection capable of handling multiple requests
832
_inputStreamClass = MultiplexedInputStream
834
def __init__(self, sock, addr, server, timeout):
835
super(MultiplexedConnection, self).__init__(sock, addr, server,
838
# Used to arbitrate access to self._requests.
839
lock = threading.RLock()
841
# Notification is posted everytime a request completes, allowing us
843
self._lock = threading.Condition(lock)
845
def _cleanupSocket(self):
846
# Wait for any outstanding requests before closing the socket.
848
while self._requests:
852
super(MultiplexedConnection, self)._cleanupSocket()
854
def writeRecord(self, rec):
855
# Must use locking to prevent intermingling of Records from different
859
# Probably faster than calling super. ;)
860
rec.write(self._sock)
864
def end_request(self, req, appStatus=0L,
865
protocolStatus=FCGI_REQUEST_COMPLETE, remove=True):
868
super(MultiplexedConnection, self).end_request(req, appStatus,
875
def _do_begin_request(self, inrec):
878
super(MultiplexedConnection, self)._do_begin_request(inrec)
882
def _do_abort_request(self, inrec):
885
super(MultiplexedConnection, self)._do_abort_request(inrec)
889
def _start_request(self, req):
891
thread.start_new_thread(req.run, ())
892
except thread.error, e:
893
self.end_request(req, 0L, FCGI_OVERLOADED, remove=True)
895
def _do_params(self, inrec):
898
super(MultiplexedConnection, self)._do_params(inrec)
902
def _do_stdin(self, inrec):
905
super(MultiplexedConnection, self)._do_stdin(inrec)
909
def _do_data(self, inrec):
912
super(MultiplexedConnection, self)._do_data(inrec)
916
class BaseFCGIServer(object):
917
request_class = Request
918
cgirequest_class = CGIRequest
920
# The maximum number of bytes (per Record) to write to the server.
921
# I've noticed mod_fastcgi has a relatively small receive buffer (8K or
925
# Limits the size of the InputStream's string buffer to this size + the
926
# server's maximum Record size. Since the InputStream is not seekable,
927
# we throw away already-read data once this certain amount has been read.
928
inputStreamShrinkThreshold = 102400 - 8192
930
def __init__(self, application, environ=None,
931
multithreaded=True, multiprocess=False,
932
bindAddress=None, umask=None, multiplexed=False,
933
debug=False, roles=(FCGI_RESPONDER,),
936
bindAddress, if present, must either be a string or a 2-tuple. If
937
present, run() will open its own listening socket. You would use
938
this if you wanted to run your application as an 'external' FastCGI
939
app. (i.e. the webserver would no longer be responsible for starting
940
your app) If a string, it will be interpreted as a filename and a UNIX
941
socket will be opened. If a tuple, the first element, a string,
942
is the interface name/IP to bind to, and the second element (an int)
945
If binding to a UNIX socket, umask may be set to specify what
946
the umask is to be changed to before the socket is created in the
947
filesystem. After the socket is created, the previous umask is
950
Set multiplexed to True if you want to handle multiple requests
951
per connection. Some FastCGI backends (namely mod_fastcgi) don't
952
multiplex requests at all, so by default this is off (which saves
953
on thread creation/locking overhead). If threads aren't available,
954
this keyword is ignored; it's not possible to multiplex requests
960
self.application = application
961
self.environ = environ
962
self.multithreaded = multithreaded
963
self.multiprocess = multiprocess
966
self.forceCGI = forceCGI
968
self._bindAddress = bindAddress
971
# Used to force single-threadedness
972
self._appLock = thread.allocate_lock()
977
# Attempt to glean the maximum number of connections
979
maxConns = resource.getrlimit(resource.RLIMIT_NOFILE)[0]
981
maxConns = 100 # Just some made up number.
984
self._connectionClass = MultiplexedConnection
985
maxReqs *= 5 # Another made up number.
987
self._connectionClass = Connection
989
FCGI_MAX_CONNS: maxConns,
990
FCGI_MAX_REQS: maxReqs,
991
FCGI_MPXS_CONNS: multiplexed and 1 or 0
994
self._connectionClass = Connection
996
# If threads aren't available, these are pretty much correct.
1002
def _setupSocket(self):
1003
if self._bindAddress is None:
1004
# Run as a normal FastCGI?
1006
# FastCGI/CGI discrimination is broken on Mac OS X.
1007
# Set the environment variable FCGI_FORCE_CGI to "Y" or "y"
1008
# if you want to run your app as a simple CGI. (You can do
1009
# this with Apache's mod_env [not loaded by default in OS X
1010
# client, ha ha] and the SetEnv directive.)
1011
forceCGI = self.forceCGI or \
1012
os.environ.get('FCGI_FORCE_CGI', 'N').upper().startswith('Y')
1017
if not hasattr(socket, 'fromfd'):
1018
# can happen on win32, no socket.fromfd there!
1020
'If you want FCGI, please create an external FCGI server '
1021
'by providing a valid bindAddress. '
1022
'If you want CGI, please force CGI operation. Use '
1023
'FCGI_FORCE_CGI=Y environment or forceCGI parameter.')
1024
sock = socket.fromfd(FCGI_LISTENSOCK_FILENO, socket.AF_INET,
1029
except socket.error, e:
1030
if e[0] == errno.ENOTSOCK:
1031
# Not a socket, assume CGI context.
1033
elif e[0] != errno.ENOTCONN:
1037
req = self.cgirequest_class(self)
1043
if type(self._bindAddress) is str:
1045
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
1047
os.unlink(self._bindAddress)
1050
if self._umask is not None:
1051
oldUmask = os.umask(self._umask)
1054
assert type(self._bindAddress) is tuple
1055
assert len(self._bindAddress) == 2
1056
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1057
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
1059
sock.bind(self._bindAddress)
1060
sock.listen(socket.SOMAXCONN)
1062
if oldUmask is not None:
1067
def _cleanupSocket(self, sock):
1068
"""Closes the main socket."""
1071
def handler(self, req):
1072
"""Special handler for WSGI."""
1073
if req.role not in self.roles:
1074
return FCGI_UNKNOWN_ROLE, 0
1076
# Mostly taken from example CGI gateway.
1077
environ = req.params
1078
environ.update(self.environ)
1080
environ['wsgi.version'] = (1,0)
1081
environ['wsgi.input'] = req.stdin
1082
if self._bindAddress is None:
1085
stderr = TeeOutputStream((sys.stderr, req.stderr))
1086
environ['wsgi.errors'] = stderr
1087
environ['wsgi.multithread'] = not isinstance(req, CGIRequest) and \
1088
thread_available and self.multithreaded
1089
environ['wsgi.multiprocess'] = isinstance(req, CGIRequest) or \
1091
environ['wsgi.run_once'] = isinstance(req, CGIRequest)
1093
if environ.get('HTTPS', 'off') in ('on', '1'):
1094
environ['wsgi.url_scheme'] = 'https'
1096
environ['wsgi.url_scheme'] = 'http'
1098
self._sanitizeEnv(environ)
1105
assert type(data) is str, 'write() argument must be string'
1106
assert headers_set, 'write() before start_response()'
1108
if not headers_sent:
1109
status, responseHeaders = headers_sent[:] = headers_set
1111
for header,value in responseHeaders:
1112
if header.lower() == 'content-length':
1115
if not found and result is not None:
1117
if len(result) == 1:
1118
responseHeaders.append(('Content-Length',
1122
s = 'Status: %s\r\n' % status
1123
for header in responseHeaders:
1124
s += '%s: %s\r\n' % header
1128
req.stdout.write(data)
1131
def start_response(status, response_headers, exc_info=None):
1135
# Re-raise if too late
1136
raise exc_info[0], exc_info[1], exc_info[2]
1138
exc_info = None # avoid dangling circular ref
1140
assert not headers_set, 'Headers already set!'
1142
assert type(status) is str, 'Status must be a string'
1143
assert len(status) >= 4, 'Status must be at least 4 characters'
1144
assert int(status[:3]), 'Status must begin with 3-digit code'
1145
assert status[3] == ' ', 'Status must have a space after code'
1146
assert type(response_headers) is list, 'Headers must be a list'
1148
for name,val in response_headers:
1149
assert type(name) is str, 'Header name "%s" must be a string' % name
1150
assert type(val) is str, 'Value of header "%s" must be a string' % name
1152
headers_set[:] = [status, response_headers]
1155
if not self.multithreaded:
1156
self._appLock.acquire()
1159
result = self.application(environ, start_response)
1164
if not headers_sent:
1165
write('') # in case body was empty
1167
if hasattr(result, 'close'):
1169
except socket.error, e:
1170
if e[0] != errno.EPIPE:
1171
raise # Don't let EPIPE propagate beyond server
1173
if not self.multithreaded:
1174
self._appLock.release()
1176
return FCGI_REQUEST_COMPLETE, 0
1178
def _sanitizeEnv(self, environ):
1179
"""Ensure certain values are present, if required by WSGI."""
1180
if not environ.has_key('SCRIPT_NAME'):
1181
environ['SCRIPT_NAME'] = ''
1184
if environ.has_key('REQUEST_URI'):
1185
reqUri = environ['REQUEST_URI'].split('?', 1)
1187
if not environ.has_key('PATH_INFO') or not environ['PATH_INFO']:
1188
if reqUri is not None:
1189
environ['PATH_INFO'] = reqUri[0]
1191
environ['PATH_INFO'] = ''
1192
if not environ.has_key('QUERY_STRING') or not environ['QUERY_STRING']:
1193
if reqUri is not None and len(reqUri) > 1:
1194
environ['QUERY_STRING'] = reqUri[1]
1196
environ['QUERY_STRING'] = ''
1198
# If any of these are missing, it probably signifies a broken
1200
for name,default in [('REQUEST_METHOD', 'GET'),
1201
('SERVER_NAME', 'localhost'),
1202
('SERVER_PORT', '80'),
1203
('SERVER_PROTOCOL', 'HTTP/1.0')]:
1204
if not environ.has_key(name):
1205
environ['wsgi.errors'].write('%s: missing FastCGI param %s '
1206
'required by WSGI!\n' %
1207
(self.__class__.__name__, name))
1208
environ[name] = default
1210
def error(self, req):
1212
Called by Request if an exception occurs within the handler. May and
1213
should be overridden.
1217
req.stdout.write('Content-Type: text/html\r\n\r\n' +
1218
cgitb.html(sys.exc_info()))
1220
errorpage = """<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
1222
<title>Unhandled Exception</title>
1224
<h1>Unhandled Exception</h1>
1225
<p>An unhandled exception was thrown by the application.</p>
1228
req.stdout.write('Content-Type: text/html\r\n\r\n' +