aboutsummaryrefslogtreecommitdiffstats
path: root/python/gevent/server.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/gevent/server.py')
-rw-r--r--python/gevent/server.py65
1 files changed, 46 insertions, 19 deletions
diff --git a/python/gevent/server.py b/python/gevent/server.py
index f2afab7..4c48bd3 100644
--- a/python/gevent/server.py
+++ b/python/gevent/server.py
@@ -1,9 +1,19 @@
# Copyright (c) 2009-2012 Denis Bilenko. See LICENSE for details.
"""TCP/SSL server"""
+
+from contextlib import closing
+
import sys
-import _socket
+
+from _socket import error as SocketError
+from _socket import SOL_SOCKET
+from _socket import SO_REUSEADDR
+from _socket import AF_INET
+from _socket import SOCK_DGRAM
+
from gevent.baseserver import BaseServer
-from gevent.socket import EWOULDBLOCK, socket
+from gevent.socket import EWOULDBLOCK
+from gevent.socket import socket as GeventSocket
from gevent._compat import PYPY, PY3
__all__ = ['StreamServer', 'DatagramServer']
@@ -16,6 +26,15 @@ else:
DEFAULT_REUSE_ADDR = 1
+if PY3:
+ # sockets and SSL sockets are context managers on Python 3
+ def _closing_socket(sock):
+ return sock
+else:
+ # but they are not guaranteed to be so on Python 2
+ _closing_socket = closing
+
+
class StreamServer(BaseServer):
"""
A generic TCP server.
@@ -66,7 +85,7 @@ class StreamServer(BaseServer):
SSLContext, the resulting client sockets will not cooperate with gevent.
Otherwise, keyword arguments are assumed to apply to :func:`ssl.wrap_socket`.
- These keyword arguments bay include:
+ These keyword arguments may include:
- keyfile
- certfile
@@ -147,7 +166,8 @@ class StreamServer(BaseServer):
if not sock.timeout:
return
raise
- sock = socket(sock.family, sock.type, sock.proto, fileno=fd)
+
+ sock = GeventSocket(sock.family, sock.type, sock.proto, fileno=fd)
# XXX Python issue #7995?
return sock, address
@@ -156,13 +176,20 @@ class StreamServer(BaseServer):
def do_read(self):
try:
client_socket, address = self.socket.accept()
- except _socket.error as err:
+ except SocketError as err:
if err.args[0] == EWOULDBLOCK:
return
raise
- sockobj = socket(_sock=client_socket)
- if PYPY:
- client_socket._drop()
+ # XXX: When would this not be the case? In Python 3 it makes sense
+ # because we're using the low-level _accept method,
+ # but not in Python 2.
+ if not isinstance(client_socket, GeventSocket):
+ # This leads to a leak of the watchers in client_socket
+ sockobj = GeventSocket(_sock=client_socket)
+ if PYPY:
+ client_socket._drop()
+ else:
+ sockobj = client_socket
return sockobj, address
def do_close(self, sock, *args):
@@ -171,8 +198,8 @@ class StreamServer(BaseServer):
def wrap_socket_and_handle(self, client_socket, address):
# used in case of ssl sockets
- ssl_socket = self.wrap_socket(client_socket, **self.ssl_args)
- return self.handle(ssl_socket, address)
+ with _closing_socket(self.wrap_socket(client_socket, **self.ssl_args)) as ssl_socket:
+ return self.handle(ssl_socket, address)
class DatagramServer(BaseServer):
@@ -206,7 +233,7 @@ class DatagramServer(BaseServer):
def do_read(self):
try:
data, address = self._socket.recvfrom(8192)
- except _socket.error as err:
+ except SocketError as err:
if err.args[0] == EWOULDBLOCK:
return
raise
@@ -220,14 +247,14 @@ class DatagramServer(BaseServer):
self._writelock.release()
-def _tcp_listener(address, backlog=50, reuse_addr=None, family=_socket.AF_INET):
+def _tcp_listener(address, backlog=50, reuse_addr=None, family=AF_INET):
"""A shortcut to create a TCP socket, bind it and put it into listening state."""
- sock = socket(family=family)
+ sock = GeventSocket(family=family)
if reuse_addr is not None:
- sock.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR, reuse_addr)
+ sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, reuse_addr)
try:
sock.bind(address)
- except _socket.error as ex:
+ except SocketError as ex:
strerror = getattr(ex, 'strerror', None)
if strerror is not None:
ex.strerror = strerror + ': ' + repr(address)
@@ -237,17 +264,17 @@ def _tcp_listener(address, backlog=50, reuse_addr=None, family=_socket.AF_INET):
return sock
-def _udp_socket(address, backlog=50, reuse_addr=None, family=_socket.AF_INET):
+def _udp_socket(address, backlog=50, reuse_addr=None, family=AF_INET):
# backlog argument for compat with tcp_listener
# pylint:disable=unused-argument
# we want gevent.socket.socket here
- sock = socket(family=family, type=_socket.SOCK_DGRAM)
+ sock = GeventSocket(family=family, type=SOCK_DGRAM)
if reuse_addr is not None:
- sock.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR, reuse_addr)
+ sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, reuse_addr)
try:
sock.bind(address)
- except _socket.error as ex:
+ except SocketError as ex:
strerror = getattr(ex, 'strerror', None)
if strerror is not None:
ex.strerror = strerror + ': ' + repr(address)