From 592eaf98f851b9fd03206a4cd12ecaad462efa2c Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Mon, 22 Sep 2008 22:47:14 +0200 Subject: make limits in scgi.asynchronous configurable --- wsgitools/scgi/asynchronous.py | 58 ++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/wsgitools/scgi/asynchronous.py b/wsgitools/scgi/asynchronous.py index 5d0e43d..338fac9 100644 --- a/wsgitools/scgi/asynchronous.py +++ b/wsgitools/scgi/asynchronous.py @@ -10,20 +10,20 @@ except ImportError: class SCGIConnection(asyncore.dispatcher): """SCGI connection class used by WSGISCGIServer.""" - # maximum request size - MAX_REQUEST_SIZE = 65536 - # maximum post size - MAX_POST_SIZE = 8 << 20 - # read and write size - BLOCK_SIZE = 4096 # connection states NEW = 0*4 | 1 # connection established, waiting for request HEADER = 1*4 | 1 # the request length was received, waiting for the rest BODY = 2*4 | 1 # the request header was received, waiting for the body REQ = 3*4 | 2 # request received, sending response - def __init__(self, server, connection, addr): + def __init__(self, server, connection, addr, maxrequestsize=65536, + maxpostsize=8<<20, blocksize=4096): + asyncore.dispatcher.__init__(self, connection) + self.server = server # WSGISCGIServer instance self.addr = addr # scgi client address + self.maxrequestsize = maxrequestsize + self.maxpostsize = maxpostsize + self.blocksize = blocksize self.state = SCGIConnection.NEW # internal state self.environ = {} # environment passed to wsgi app self.reqlen = -1 # request length used in two different meanings @@ -33,7 +33,6 @@ class SCGIConnection(asyncore.dispatcher): self.outheaders = () # headers to be sent # () -> unset, (..,..) -> set, True -> sent self.body = StringIO.StringIO() # request body - asyncore.dispatcher.__init__(self, connection) def _wsgi_headers(self): return {"wsgi.version": (1, 0), @@ -69,7 +68,7 @@ class SCGIConnection(asyncore.dispatcher): def handle_read(self): """asyncore interface""" - data = self.recv(self.BLOCK_SIZE) + data = self.recv(self.blocksize) self.inbuff += data if self.state == SCGIConnection.NEW: if ':' in self.inbuff: @@ -78,12 +77,12 @@ class SCGIConnection(asyncore.dispatcher): self.close() return # invalid request format reqlen = long(reqlen) - if reqlen > self.MAX_REQUEST_SIZE: + if reqlen > self.maxrequestsize: self.close() return # request too long self.reqlen = reqlen self.state = SCGIConnection.HEADER - elif len(self.inbuff) > self.MAX_REQUEST_SIZE: + elif len(self.inbuff) > self.maxrequestsize: self.close() return # request too long @@ -105,7 +104,7 @@ class SCGIConnection(asyncore.dispatcher): self.close() return self.reqlen = long(self.environ["CONTENT_LENGTH"]) - if self.reqlen > self.MAX_POST_SIZE: + if self.reqlen > self.maxpostsize: self.close() return self.state = SCGIConnection.BODY @@ -151,13 +150,13 @@ class SCGIConnection(asyncore.dispatcher): def handle_write(self): """asyncore interface""" assert self.state >= SCGIConnection.REQ - if len(self.outbuff) < self.BLOCK_SIZE: + if len(self.outbuff) < self.blocksize: self._try_send_headers() for data in self.wsgihandler: assert isinstance(data, str) if data: self.outbuff += data - if len(self.outbuff) >= self.BLOCK_SIZE: + if len(self.outbuff) >= self.blocksize: break if len(self.outbuff) == 0: if hasattr(self.wsgihandler, "close"): @@ -165,7 +164,7 @@ class SCGIConnection(asyncore.dispatcher): self.close() return try: - sentbytes = self.send(self.outbuff[:self.BLOCK_SIZE]) + sentbytes = self.send(self.outbuff[:self.blocksize]) except socket.error: if hasattr(self.wsgihandler, "close"): self.wsgihandler.close() @@ -181,20 +180,41 @@ __all__.append("SCGIServer") class SCGIServer(asyncore.dispatcher): """SCGI Server for WSGI applications. It does not use multiple processes or multiple threads.""" - def __init__(self, wsgiapp, port, interface="localhost", error=sys.stderr): + def __init__(self, wsgiapp, port, interface="localhost", error=sys.stderr, + maxrequestsize=None, maxpostsize=None, blocksize=None): """ @param wsgiapp: is the wsgi application to be run. - @type port: number + @type port: int @param port: is an int representing the TCP port number to be used. @type interface: str @param interface: is a string specifying the network interface to bind which defaults to "localhost" making the server inaccessible over network. @param error: is a file-like object being passed as wsgi.error in the - environ parameter defaulting to stderr.""" + environ parameter defaulting to stderr. + @type maxrequestsize: int + @param maxrequestsize: limit the size of request blocks in scgi + connections. Connections are dropped when this limit is hit. + @type maxpostsize: int + @param maxpostsize: limit the size of post bodies that may be processed + by this instance. Connections are dropped when this limit is + hit. + @type blocksize: int + @param blocksize: is amount of data to read or write from or to the + network at once + """ asyncore.dispatcher.__init__(self) + self.wsgiapp = wsgiapp self.error = error + self.conf = {} + if maxrequestsize is not None: + self.conf["maxrequestsize"] = maxrequestsize + if maxpostsize is not None: + self.conf["maxpostsize"] = maxpostsize + if blocksize is not None: + self.conf["blocksize"] = blocksize + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() self.bind((interface, port)) @@ -205,7 +225,7 @@ class SCGIServer(asyncore.dispatcher): ret = self.accept() if ret is not None: conn, addr = ret - SCGIConnection(self, conn, addr) + SCGIConnection(self, conn, addr, **self.conf) def run(self): """Runs the server. It will not return and you can invoke -- cgit v1.2.3