summaryrefslogtreecommitdiff
path: root/Lib/socketserver.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/socketserver.py')
-rw-r--r--Lib/socketserver.py75
1 files changed, 63 insertions, 12 deletions
diff --git a/Lib/socketserver.py b/Lib/socketserver.py
index e7faf884ef..8f80a7dc31 100644
--- a/Lib/socketserver.py
+++ b/Lib/socketserver.py
@@ -82,7 +82,7 @@ On the other hand, if you are building e.g. an HTTP server, where all
data is stored externally (e.g. in the file system), a synchronous
class will essentially render the service "deaf" while one request is
being handled -- which may be for a very long time if a client is slow
-to reqd all the data it has requested. Here a threading or forking
+to read all the data it has requested. Here a threading or forking
server is appropriate.
In some cases, it may be appropriate to process part of a request
@@ -133,6 +133,7 @@ import socket
import select
import sys
import os
+import errno
try:
import threading
except ImportError:
@@ -147,6 +148,15 @@ if hasattr(socket, "AF_UNIX"):
"ThreadingUnixStreamServer",
"ThreadingUnixDatagramServer"])
+def _eintr_retry(func, *args):
+ """restart a system call interrupted by EINTR"""
+ while True:
+ try:
+ return func(*args)
+ except (OSError, select.error) as e:
+ if e.args[0] != errno.EINTR:
+ raise
+
class BaseServer:
"""Base class for server classes.
@@ -168,6 +178,7 @@ class BaseServer:
- verify_request(request, client_address)
- server_close()
- process_request(request, client_address)
+ - shutdown_request(request)
- close_request(request)
- handle_error()
@@ -221,7 +232,8 @@ class BaseServer:
# connecting to the socket to wake this up instead of
# polling. Polling reduces our responsiveness to a
# shutdown request and wastes cpu at all other times.
- r, w, e = select.select([self], [], [], poll_interval)
+ r, w, e = _eintr_retry(select.select, [self], [], [],
+ poll_interval)
if self in r:
self._handle_request_noblock()
finally:
@@ -261,7 +273,7 @@ class BaseServer:
timeout = self.timeout
elif self.timeout is not None:
timeout = min(timeout, self.timeout)
- fd_sets = select.select([self], [], [], timeout)
+ fd_sets = _eintr_retry(select.select, [self], [], [], timeout)
if not fd_sets[0]:
self.handle_timeout()
return
@@ -283,7 +295,7 @@ class BaseServer:
self.process_request(request, client_address)
except:
self.handle_error(request, client_address)
- self.close_request(request)
+ self.shutdown_request(request)
def handle_timeout(self):
"""Called if no new request arrives within self.timeout.
@@ -307,7 +319,7 @@ class BaseServer:
"""
self.finish_request(request, client_address)
- self.close_request(request)
+ self.shutdown_request(request)
def server_close(self):
"""Called to clean-up the server.
@@ -321,6 +333,10 @@ class BaseServer:
"""Finish one request by instantiating RequestHandlerClass."""
self.RequestHandlerClass(request, client_address, self)
+ def shutdown_request(self, request):
+ """Called to shutdown and close an individual request."""
+ self.close_request(request)
+
def close_request(self, request):
"""Called to clean up an individual request."""
pass
@@ -361,6 +377,7 @@ class TCPServer(BaseServer):
- handle_timeout()
- verify_request(request, client_address)
- process_request(request, client_address)
+ - shutdown_request(request)
- close_request(request)
- handle_error()
@@ -445,6 +462,16 @@ class TCPServer(BaseServer):
"""
return self.socket.accept()
+ def shutdown_request(self, request):
+ """Called to shutdown and close an individual request."""
+ try:
+ #explicitly shutdown. socket.close() merely releases
+ #the socket and waits for GC to perform the actual close.
+ request.shutdown(socket.SHUT_WR)
+ except socket.error:
+ pass #some platforms may raise ENOTCONN here
+ self.close_request(request)
+
def close_request(self, request):
"""Called to clean up an individual request."""
request.close()
@@ -468,6 +495,10 @@ class UDPServer(TCPServer):
# No need to call listen() for UDP.
pass
+ def shutdown_request(self, request):
+ # No need to shutdown anything.
+ self.close_request(request)
+
def close_request(self, request):
# No need to close anything.
pass
@@ -534,10 +565,12 @@ class ForkingMixIn:
# This must never return, hence os._exit()!
try:
self.finish_request(request, client_address)
+ self.shutdown_request(request)
os._exit(0)
except:
try:
self.handle_error(request, client_address)
+ self.shutdown_request(request)
finally:
os._exit(1)
@@ -557,17 +590,16 @@ class ThreadingMixIn:
"""
try:
self.finish_request(request, client_address)
- self.close_request(request)
+ self.shutdown_request(request)
except:
self.handle_error(request, client_address)
- self.close_request(request)
+ self.shutdown_request(request)
def process_request(self, request, client_address):
"""Start a new thread to process the request."""
t = threading.Thread(target = self.process_request_thread,
args = (request, client_address))
- if self.daemon_threads:
- t.daemon = True
+ t.daemon = self.daemon_threads
t.start()
@@ -612,8 +644,10 @@ class BaseRequestHandler:
self.client_address = client_address
self.server = server
self.setup()
- self.handle()
- self.finish()
+ try:
+ self.handle()
+ finally:
+ self.finish()
def setup(self):
pass
@@ -647,14 +681,31 @@ class StreamRequestHandler(BaseRequestHandler):
rbufsize = -1
wbufsize = 0
+ # A timeout to apply to the request socket, if not None.
+ timeout = None
+
+ # Disable nagle algorithm for this socket, if True.
+ # Use only when wbufsize != 0, to avoid small packets.
+ disable_nagle_algorithm = False
+
def setup(self):
self.connection = self.request
+ if self.timeout is not None:
+ self.connection.settimeout(self.timeout)
+ if self.disable_nagle_algorithm:
+ self.connection.setsockopt(socket.IPPROTO_TCP,
+ socket.TCP_NODELAY, True)
self.rfile = self.connection.makefile('rb', self.rbufsize)
self.wfile = self.connection.makefile('wb', self.wbufsize)
def finish(self):
if not self.wfile.closed:
- self.wfile.flush()
+ try:
+ self.wfile.flush()
+ except socket.error:
+ # An final socket error may have occurred here, such as
+ # the local error ECONNABORTED.
+ pass
self.wfile.close()
self.rfile.close()