-
-
Notifications
You must be signed in to change notification settings - Fork 90
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move code to create connection objects from sockets into separate mod…
…ule.
- Loading branch information
1 parent
c7ca7ff
commit 6f9a8f9
Showing
2 changed files
with
148 additions
and
130 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
from __future__ import absolute_import, division, print_function | ||
__metaclass__ = type | ||
|
||
import io | ||
import six | ||
import socket | ||
|
||
from . import errors | ||
from .makefile import MakeFile | ||
|
||
|
||
try: | ||
import fcntl | ||
except ImportError: | ||
try: | ||
from ctypes import windll, WinError | ||
import ctypes.wintypes | ||
_SetHandleInformation = windll.kernel32.SetHandleInformation | ||
_SetHandleInformation.argtypes = [ | ||
ctypes.wintypes.HANDLE, | ||
ctypes.wintypes.DWORD, | ||
ctypes.wintypes.DWORD, | ||
] | ||
_SetHandleInformation.restype = ctypes.wintypes.BOOL | ||
except ImportError: | ||
def prevent_socket_inheritance(sock): | ||
"""Stub inheritance prevention. | ||
Dummy function, since neither fcntl nor ctypes are available. | ||
""" | ||
pass | ||
else: | ||
def prevent_socket_inheritance(sock): | ||
"""Mark the given socket fd as non-inheritable (Windows).""" | ||
if not _SetHandleInformation(sock.fileno(), 1, 0): | ||
raise WinError() | ||
else: | ||
def prevent_socket_inheritance(sock): | ||
"""Mark the given socket fd as non-inheritable (POSIX).""" | ||
fd = sock.fileno() | ||
old_flags = fcntl.fcntl(fd, fcntl.F_GETFD) | ||
fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC) | ||
|
||
|
||
class ConnectionManager: | ||
|
||
def __init__(self, server): | ||
self.server = server | ||
|
||
def from_server_socket(self, server_socket): | ||
try: | ||
s, addr = server_socket.accept() | ||
if self.server.stats['Enabled']: | ||
self.server.stats['Accepts'] += 1 | ||
prevent_socket_inheritance(s) | ||
if hasattr(s, 'settimeout'): | ||
s.settimeout(self.server.timeout) | ||
|
||
mf = MakeFile | ||
ssl_env = {} | ||
# if ssl cert and key are set, we try to be a secure HTTP server | ||
if self.server.ssl_adapter is not None: | ||
try: | ||
s, ssl_env = self.server.ssl_adapter.wrap(s) | ||
except errors.NoSSLError: | ||
msg = ( | ||
'The client sent a plain HTTP request, but ' | ||
'this server only speaks HTTPS on this port.' | ||
) | ||
buf = [ | ||
'%s 400 Bad Request\r\n' % self.server.protocol, | ||
'Content-Length: %s\r\n' % len(msg), | ||
'Content-Type: text/plain\r\n\r\n', | ||
msg, | ||
] | ||
|
||
sock_to_make = s if not six.PY2 else s._sock | ||
wfile = mf(sock_to_make, 'wb', io.DEFAULT_BUFFER_SIZE) | ||
try: | ||
wfile.write(''.join(buf).encode('ISO-8859-1')) | ||
except socket.error as ex: | ||
if ex.args[0] not in errors.socket_errors_to_ignore: | ||
raise | ||
return | ||
if not s: | ||
return | ||
mf = self.server.ssl_adapter.makefile | ||
# Re-apply our timeout since we may have a new socket object | ||
if hasattr(s, 'settimeout'): | ||
s.settimeout(self.server.timeout) | ||
|
||
conn = self.server.ConnectionClass(self.server, s, mf) | ||
|
||
if not isinstance(self.server.bind_addr, six.string_types): | ||
# optional values | ||
# Until we do DNS lookups, omit REMOTE_HOST | ||
if addr is None: # sometimes this can happen | ||
# figure out if AF_INET or AF_INET6. | ||
if len(s.getsockname()) == 2: | ||
# AF_INET | ||
addr = ('0.0.0.0', 0) | ||
else: | ||
# AF_INET6 | ||
addr = ('::', 0) | ||
conn.remote_addr = addr[0] | ||
conn.remote_port = addr[1] | ||
|
||
conn.ssl_env = ssl_env | ||
return conn | ||
|
||
except socket.timeout: | ||
# The only reason for the timeout in start() is so we can | ||
# notice keyboard interrupts on Win32, which don't interrupt | ||
# accept() by default | ||
return | ||
except socket.error as ex: | ||
if self.server.stats['Enabled']: | ||
self.server.stats['Socket Errors'] += 1 | ||
if ex.args[0] in errors.socket_error_eintr: | ||
# I *think* this is right. EINTR should occur when a signal | ||
# is received during the accept() call; all docs say retry | ||
# the call, and I *think* I'm reading it right that Python | ||
# will then go ahead and poll for and handle the signal | ||
# elsewhere. See | ||
# https://github.com/cherrypy/cherrypy/issues/707. | ||
return | ||
if ex.args[0] in errors.socket_errors_nonblocking: | ||
# Just try again. See | ||
# https://github.com/cherrypy/cherrypy/issues/479. | ||
return | ||
if ex.args[0] in errors.socket_errors_to_ignore: | ||
# Our socket was closed. | ||
# See https://github.com/cherrypy/cherrypy/issues/686. | ||
return | ||
raise |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters