summaryrefslogtreecommitdiff
path: root/paste/httpserver.py
diff options
context:
space:
mode:
Diffstat (limited to 'paste/httpserver.py')
-rwxr-xr-xpaste/httpserver.py37
1 files changed, 25 insertions, 12 deletions
diff --git a/paste/httpserver.py b/paste/httpserver.py
index 4ac032a..29c2065 100755
--- a/paste/httpserver.py
+++ b/paste/httpserver.py
@@ -196,12 +196,11 @@ class WSGIHandlerMixin:
content_length = int(self.headers.get('Content-Length', '0'))
except ValueError:
content_length = 0
- #
- # @@: LimitedLengthFile is currently broken in connection
- # with SSL (sporatic errors that are diffcult to trace, but
- # ones that go away when you don't use LimitedLengthFile)
- #
- #rfile = LimitedLengthFile(rfile, content_length)
+ if not hasattr(self.connection, 'get_context'):
+ # @@: LimitedLengthFile is currently broken in connection
+ # with SSL (sporatic errors that are diffcult to trace, but
+ # ones that go away when you don't use LimitedLengthFile)
+ rfile = LimitedLengthFile(rfile, content_length)
remote_address = self.client_address[0]
self.wsgi_environ = {
@@ -526,7 +525,9 @@ class ThreadPool(object):
necessarily reliable. This is turned off by default, since it is
only a good idea if you've deployed the server with some process
watching from above (something similar to daemontools or zdaemon).
-
+
+ Each worker thread only processes ``max_requests`` tasks before it
+ dies and replaces itself with a new worker thread.
"""
@@ -534,6 +535,7 @@ class ThreadPool(object):
def __init__(
self, nworkers, name="ThreadPool", daemon=False,
+ max_requests=100, # threads are killed after this many requests
hung_thread_limit=30, # when a thread is marked "hung"
kill_thread_limit=1800, # when you kill that hung thread
dying_limit=300, # seconds that a kill should take to go into effect (longer than this and the thread is a "zombie")
@@ -547,6 +549,7 @@ class ThreadPool(object):
Create thread pool with `nworkers` worker threads.
"""
self.nworkers = nworkers
+ self.max_requests = max_requests
self.name = name
self.queue = Queue.Queue()
self.workers = []
@@ -798,7 +801,12 @@ class ThreadPool(object):
thread_obj = threading.currentThread()
thread_id = thread_obj.thread_id = thread.get_ident()
self.idle_workers.append(thread_id)
+ requests_processed = 0
while True:
+ if self.max_requests and self.max_requests < requests_processed:
+ # Replace this thread then die
+ self.add_worker_thread()
+ break
runnable = self.queue.get()
if runnable is ThreadPool.SHUTDOWN:
self.logger.debug('Worker %s asked to SHUTDOWN', thread_id)
@@ -813,6 +821,7 @@ class ThreadPool(object):
except ValueError:
pass
self.worker_tracker[thread_id] = [time.time(), None]
+ requests_processed += 1
try:
try:
runnable()
@@ -1059,13 +1068,10 @@ class ServerExit(SystemExit):
caught)
"""
-# @@: ThreadPool is currently broken, it shouldn't be the default till
-# it is thoroughly tested.
-#
def serve(application, host=None, port=None, handler=None, ssl_pem=None,
ssl_context=None, server_version=None, protocol_version=None,
start_loop=True, daemon_threads=None, socket_timeout=None,
- use_threadpool=False, threadpool_workers=10,
+ use_threadpool=True, threadpool_workers=10,
threadpool_options=None):
"""
Serves your ``application`` over HTTP(S) via WSGI interface
@@ -1232,7 +1238,8 @@ def server_runner(wsgi_app, global_conf, **kwargs):
'threadpool_kill_thread_limit',
'threadpool_dying_limit', 'threadpool_spawn_if_under',
'threadpool_max_zombie_threads_before_die',
- 'threadpool_hung_check_period']:
+ 'threadpool_hung_check_period',
+ 'threadpool_max_requests']:
if name in kwargs:
kwargs[name] = int(kwargs[name])
for name in ['use_threadpool', 'daemon_threads']:
@@ -1253,6 +1260,12 @@ server_runner.__doc__ = serve.__doc__ + """
You can also set these threadpool options:
+ ``threadpool_max_requests``:
+
+ The maximum number of requests a worker thread will process
+ before dying (and replacing itself with a new worker thread).
+ Default 100.
+
``threadpool_hung_thread_limit``:
The number of seconds a thread can work on a task before it is