Skip to content

Commit d74674f

Browse files
committed
Deprecated Makefile in favor of StreamReader and StreamWriter
We replace the `MakeFile` factory function with direct class instantiation. The `MakeFile` function now emits a `DeprecationWarning` and will be removed in a future release. `makefile()` methods are also deprecated on the adapters and its base class.
1 parent c5d5175 commit d74674f

11 files changed

Lines changed: 333 additions & 74 deletions

File tree

cheroot/connections.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
from . import errors
1313
from ._compat import IS_WINDOWS
14-
from .makefile import MakeFile
1514

1615

1716
try:
@@ -304,7 +303,6 @@ def _from_server_socket(self, server_socket): # noqa: C901 # FIXME
304303
if hasattr(s, 'settimeout'):
305304
s.settimeout(self.server.timeout)
306305

307-
mf = MakeFile
308306
ssl_env = {}
309307
# if ssl cert and key are set, we try to be a secure HTTP server
310308
if self.server.ssl_adapter is not None:
@@ -327,12 +325,12 @@ def _from_server_socket(self, server_socket): # noqa: C901 # FIXME
327325
)
328326
self._send_bad_request_plain_http_error(s)
329327
return None
330-
mf = self.server.ssl_adapter.makefile
328+
331329
# Re-apply our timeout since we may have a new socket object
332330
if hasattr(s, 'settimeout'):
333331
s.settimeout(self.server.timeout)
334332

335-
conn = self.server.ConnectionClass(self.server, s, mf)
333+
conn = self.server.ConnectionClass(self.server, s)
336334

337335
if not isinstance(self.server.bind_addr, (str, bytes)):
338336
# optional values

cheroot/makefile.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# prefer slower Python-based io module
44
import _pyio as io
55
import socket
6+
from warnings import warn as _warn
67

78

89
# Write only 16K at a time to sockets
@@ -70,6 +71,16 @@ def write(self, val, *args, **kwargs):
7071

7172

7273
def MakeFile(sock, mode='r', bufsize=io.DEFAULT_BUFFER_SIZE):
73-
"""File object attached to a socket object."""
74+
"""
75+
File object attached to a socket object.
76+
77+
This function is now deprecated: Use
78+
StreamReader or StreamWriter directly.
79+
"""
80+
_warn(
81+
'MakeFile is deprecated. Use StreamReader or StreamWriter directly.',
82+
DeprecationWarning,
83+
stacklevel=2,
84+
)
7485
cls = StreamReader if 'r' in mode else StreamWriter
7586
return cls(sock, mode, bufsize)

cheroot/server.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484

8585
from . import __version__, connections, errors
8686
from ._compat import IS_PPC, bton
87-
from .makefile import MakeFile, StreamWriter
87+
from .makefile import StreamReader, StreamWriter
8888
from .workers import threadpool
8989

9090

@@ -1276,19 +1276,31 @@ class HTTPConnection:
12761276
# Fields set by ConnectionManager.
12771277
last_used = None
12781278

1279-
def __init__(self, server, sock, makefile=MakeFile):
1279+
def __init__(self, server, sock, makefile=None):
12801280
"""Initialize HTTPConnection instance.
12811281
12821282
Args:
12831283
server (HTTPServer): web server object receiving this request
12841284
sock (socket._socketobject): the raw socket object (usually
12851285
TCP) for this connection
1286-
makefile (file): a fileobject class for reading from the socket
1286+
makefile (file): Now deprecated.
1287+
Used to be a fileobject class for reading from the socket.
12871288
"""
12881289
self.server = server
12891290
self.socket = sock
1290-
self.rfile = makefile(sock, 'rb', self.rbufsize)
1291-
self.wfile = makefile(sock, 'wb', self.wbufsize)
1291+
1292+
if makefile is not None:
1293+
_warn(
1294+
'The `makefile` parameter in creating an `HTTPConnection` '
1295+
'is deprecated and will be removed in a future version. '
1296+
'The connection socket should now be fully wrapped by the '
1297+
'adapter before being passed to this constructor.',
1298+
DeprecationWarning,
1299+
stacklevel=2,
1300+
)
1301+
1302+
self.rfile = StreamReader(sock, 'rb', self.rbufsize)
1303+
self.wfile = StreamWriter(sock, 'wb', self.wbufsize)
12921304
self.requests_seen = 0
12931305

12941306
self.peercreds_enabled = self.server.peercreds_enabled
@@ -1358,12 +1370,8 @@ def communicate(self): # noqa: C901 # FIXME
13581370
def _handle_no_ssl(self, req):
13591371
if not req or req.sent_headers:
13601372
return
1361-
# Unwrap wfile
1362-
try:
1363-
resp_sock = self.socket._sock
1364-
except AttributeError:
1365-
# self.socket is of OpenSSL.SSL.Connection type
1366-
resp_sock = self.socket._socket
1373+
# Unwrap to get raw TCP socket
1374+
resp_sock = self.socket._sock
13671375
self.wfile = StreamWriter(resp_sock, 'wb', self.wbufsize)
13681376
msg = (
13691377
'The client sent a plain HTTP request, but '

cheroot/ssl/__init__.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,27 @@ def _ensure_peer_speaks_https(raw_socket, /) -> None:
5656

5757

5858
class Adapter(ABC):
59-
"""Base class for SSL driver library adapters.
59+
"""
60+
Base class for SSL driver library adapters.
6061
6162
Required methods:
6263
6364
* ``wrap(sock) -> (wrapped socket, ssl environ dict)``
64-
* ``makefile(sock, mode='r', bufsize=DEFAULT_BUFFER_SIZE) ->
65-
socket file object``
65+
* ``get_environ() -> (ssl environ dict)``
66+
67+
Optional methods:
68+
69+
* ``makefile(sock, mode='r', bufsize=-1) -> socket file object``
70+
71+
This method is deprecated and will be removed in a future release.
72+
73+
Historically, the ``PyOpenSSL`` adapter used ``makefile()`` to
74+
wrap the underlying socket in an ``OpenSSL``-aware file object
75+
so that Cheroot's HTTP request parser (which expects file-like I/O
76+
such as ``readline()``) could read from TLS connections. The
77+
adapter now fully wraps the socket in a TLSSocket object that
78+
implements the necessary socket and file-like
79+
methods directly, so ``makefile()`` is no longer needed.
6680
"""
6781

6882
@abstractmethod
@@ -110,9 +124,14 @@ def get_environ(self):
110124
"""Return WSGI environ entries to be merged into each request."""
111125
raise NotImplementedError # pragma: no cover
112126

113-
@abstractmethod
114127
def makefile(self, sock, mode='r', bufsize=-1):
115-
"""Return socket file object."""
128+
"""
129+
Return socket file object.
130+
131+
This method is now deprecated. It will be removed in a future version.
132+
133+
:raises NotImplementedError: Must be overridden by subclasses.
134+
"""
116135
raise NotImplementedError # pragma: no cover
117136

118137
def _prompt_for_tls_password(self) -> str:

cheroot/ssl/builtin.py

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,39 @@
1111
import sys
1212
import threading
1313
from contextlib import suppress
14+
from warnings import warn as _warn
1415

1516

1617
try:
1718
import ssl
1819
except ImportError:
1920
ssl = None
2021

21-
try:
22-
from _pyio import DEFAULT_BUFFER_SIZE
23-
except ImportError:
24-
try:
25-
from io import DEFAULT_BUFFER_SIZE
26-
except ImportError:
27-
DEFAULT_BUFFER_SIZE = -1
28-
2922
from .. import errors
30-
from ..makefile import StreamReader, StreamWriter
3123
from ..server import HTTPServer
3224
from . import Adapter
3325

3426

27+
# WPS413 is to suppress linter error:
28+
# bad magic module function: __getattr__
29+
# DEFAULT_BUFFER_SIZE is deprecated so this module method will be removed
30+
# in a future release
31+
def __getattr__(name): # noqa: WPS413
32+
if name == 'DEFAULT_BUFFER_SIZE':
33+
_warn(
34+
(
35+
'`DEFAULT_BUFFER_SIZE` is deprecated and '
36+
'will be removed in a future release.'
37+
),
38+
DeprecationWarning,
39+
stacklevel=2,
40+
)
41+
from io import DEFAULT_BUFFER_SIZE
42+
43+
return DEFAULT_BUFFER_SIZE
44+
raise AttributeError(f'module {__name__!r} has no attribute {name!r}')
45+
46+
3547
def _assert_ssl_exc_contains(exc, *msgs):
3648
"""Check whether SSL exception contains either of messages provided."""
3749
if len(msgs) < 1:
@@ -247,9 +259,6 @@ def __init__(
247259
private_key_password=private_key_password,
248260
)
249261

250-
if private_key_password is None:
251-
private_key_password = self._prompt_for_tls_password
252-
253262
self.context = ssl.create_default_context(
254263
purpose=ssl.Purpose.CLIENT_AUTH,
255264
cafile=certificate_chain,
@@ -496,7 +505,19 @@ def _make_env_dn_dict(self, env_prefix, cert_value):
496505
env['%s_%s_%i' % (env_prefix, attr_code, i)] = val
497506
return env
498507

499-
def makefile(self, sock, mode='r', bufsize=DEFAULT_BUFFER_SIZE):
500-
"""Return socket file object."""
501-
cls = StreamReader if 'r' in mode else StreamWriter
502-
return cls(sock, mode, bufsize)
508+
def makefile(self, sock, mode='r', bufsize=-1):
509+
"""
510+
Return socket file object.
511+
512+
``makefile`` is now deprecated and will be removed in a future
513+
version.
514+
"""
515+
_warn(
516+
'The `makefile` method is deprecated and will be removed in a future version. '
517+
'The connection socket should be fully wrapped by the adapter '
518+
'before being passed to the HTTPConnection constructor.',
519+
DeprecationWarning,
520+
stacklevel=2,
521+
)
522+
523+
return sock.makefile(mode, bufsize)

cheroot/ssl/builtin.pyi

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import typing as _t
33

44
from . import Adapter
55

6-
DEFAULT_BUFFER_SIZE: int
7-
86
class BuiltinSSLAdapter(Adapter):
97
CERT_KEY_TO_ENV: _t.Any
108
CERT_KEY_TO_LDAP_CODE: _t.Any

0 commit comments

Comments
 (0)