Skip to content

Commit 7e162f6

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. The `pyOpenSSL` adapter still needs it own `makefile()` method to provide wrapped streaming classes capable of handling `OpenSSL errors.
1 parent 4b4bb7f commit 7e162f6

10 files changed

Lines changed: 67 additions & 40 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: 11 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,15 @@ 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 method is now deprecated: Use StreamReader or StreamWriter directly.
78+
"""
79+
_warn(
80+
'MakeFile is deprecated. Use StreamReader or StreamWriter directly.',
81+
DeprecationWarning,
82+
stacklevel=2,
83+
)
7484
cls = StreamReader if 'r' in mode else StreamWriter
7585
return cls(sock, mode, bufsize)

cheroot/server.py

Lines changed: 14 additions & 4 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,7 +1276,7 @@ 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):
12801280
"""Initialize HTTPConnection instance.
12811281
12821282
Args:
@@ -1287,8 +1287,18 @@ def __init__(self, server, sock, makefile=MakeFile):
12871287
"""
12881288
self.server = server
12891289
self.socket = sock
1290-
self.rfile = makefile(sock, 'rb', self.rbufsize)
1291-
self.wfile = makefile(sock, 'wb', self.wbufsize)
1290+
1291+
# Check if SSL adapter needs custom wrapped streaming
1292+
# objects (pyOpenSSL does, builtin doesn't)
1293+
if server.ssl_adapter is not None and hasattr(
1294+
server.ssl_adapter,
1295+
'makefile',
1296+
):
1297+
self.rfile = server.ssl_adapter.makefile(sock, 'rb', self.rbufsize)
1298+
self.wfile = server.ssl_adapter.makefile(sock, 'wb', self.wbufsize)
1299+
else:
1300+
self.rfile = StreamReader(sock, 'rb', self.rbufsize)
1301+
self.wfile = StreamWriter(sock, 'wb', self.wbufsize)
12921302
self.requests_seen = 0
12931303

12941304
self.peercreds_enabled = self.server.peercreds_enabled

cheroot/server.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ class HTTPConnection:
112112
rfile: _t.Any
113113
wfile: _t.Any
114114
requests_seen: int
115-
def __init__(self, server, sock, makefile=...) -> None: ...
115+
def __init__(self, server, sock) -> None: ...
116116
def communicate(self): ...
117117
linger: bool
118118
def close(self) -> None: ...

cheroot/ssl/__init__.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,16 @@ def _ensure_peer_speaks_https(raw_socket, /) -> None:
5555

5656

5757
class Adapter(ABC):
58-
"""Base class for SSL driver library adapters.
58+
"""
59+
Base class for SSL driver library adapters.
5960
6061
Required methods:
62+
``wrap(sock)``: Wrap a socket with SSL. Also returns ssl environ dict.
63+
``get_environ()``: Get SSL environment variables as a dict.
6164
62-
* ``wrap(sock) -> (wrapped socket, ssl environ dict)``
63-
* ``makefile(sock, mode='r', bufsize=DEFAULT_BUFFER_SIZE) ->
64-
socket file object``
65+
Optional methods:
66+
``makefile(sock, mode, bufsize)``: Create custom file object.
67+
The ``pyOpenSSL`` adapter uses this to handle SSL-specific errors.
6568
"""
6669

6770
@abstractmethod
@@ -108,8 +111,3 @@ def wrap(self, sock):
108111
def get_environ(self):
109112
"""Return WSGI environ entries to be merged into each request."""
110113
raise NotImplementedError # pragma: no cover
111-
112-
@abstractmethod
113-
def makefile(self, sock, mode='r', bufsize=-1):
114-
"""Return socket file object."""
115-
raise NotImplementedError # pragma: no cover

cheroot/ssl/__init__.pyi

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,3 @@ class Adapter(ABC):
2323
def wrap(self, sock): ...
2424
@abstractmethod
2525
def get_environ(self): ...
26-
@abstractmethod
27-
def makefile(self, sock, mode: str = ..., bufsize: int = ...): ...

cheroot/ssl/builtin.py

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,7 @@
1818
except ImportError:
1919
ssl = None
2020

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-
2921
from .. import errors
30-
from ..makefile import StreamReader, StreamWriter
3122
from ..server import HTTPServer
3223
from . import Adapter
3324

@@ -492,8 +483,3 @@ def _make_env_dn_dict(self, env_prefix, cert_value):
492483
for i, val in enumerate(values):
493484
env['%s_%s_%i' % (env_prefix, attr_code, i)] = val
494485
return env
495-
496-
def makefile(self, sock, mode='r', bufsize=DEFAULT_BUFFER_SIZE):
497-
"""Return socket file object."""
498-
cls = StreamReader if 'r' in mode else StreamWriter
499-
return cls(sock, mode, bufsize)

cheroot/ssl/builtin.pyi

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

33
from . import Adapter
44

5-
DEFAULT_BUFFER_SIZE: int
6-
75
class BuiltinSSLAdapter(Adapter):
86
CERT_KEY_TO_ENV: _t.Any
97
CERT_KEY_TO_LDAP_CODE: _t.Any
@@ -22,4 +20,3 @@ class BuiltinSSLAdapter(Adapter):
2220
def context(self, context) -> None: ...
2321
def wrap(self, sock): ...
2422
def get_environ(self, sock): ...
25-
def makefile(self, sock, mode: str = ..., bufsize: int = ...): ...

cheroot/test/test_makefile.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Tests for :py:mod:`cheroot.makefile`."""
22

3+
import pytest
4+
35
from cheroot import makefile
46

57

@@ -39,7 +41,7 @@ def test_bytes_read():
3941
"""Reader should capture bytes read."""
4042
sock = MockSocket()
4143
sock.messages.append(b'foo')
42-
rfile = makefile.MakeFile(sock, 'r')
44+
rfile = makefile.StreamReader(sock, 'r')
4345
rfile.read()
4446
assert rfile.bytes_read == 3
4547

@@ -48,6 +50,21 @@ def test_bytes_written():
4850
"""Writer should capture bytes written."""
4951
sock = MockSocket()
5052
sock.messages.append(b'foo')
51-
wfile = makefile.MakeFile(sock, 'w')
53+
wfile = makefile.StreamWriter(sock, 'w')
5254
wfile.write(b'bar')
5355
assert wfile.bytes_written == 3
56+
57+
58+
def test_makefile_deprecated():
59+
"""MakeFile function should emit a deprecation warning."""
60+
sock = MockSocket()
61+
sock.messages.append(b'foo')
62+
63+
with pytest.warns(
64+
DeprecationWarning,
65+
match='Use StreamReader or StreamWriter directly',
66+
) as warning_info:
67+
makefile.MakeFile(sock, 'r')
68+
69+
assert len(warning_info) == 1
70+
assert 'MakeFile is deprecated' in str(warning_info[0].message)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Deprecated :py:meth:`~cheroot.makefile.MakeFile` in favor of :py:class:`~cheroot.makefile.StreamReader`
2+
and :py:class:`~cheroot.makefile.StreamWriter`.
3+
4+
The ``MakeFile`` factory function is replaced with direct class instantiation.
5+
The ``MakeFile`` function now emits a ``DeprecationWarning`` and will be
6+
removed in a future release.
7+
8+
Adapter specific ``makefile()`` methods are removed from both :py:mod:`~cheroot.ssl.builtin.BuiltinSSLAdapter`
9+
and the :py:mod:`~cheroot.ssl.Adapter` interface but retained in :py:mod:`~cheroot.ssl.pyopenssl.pyOpenSSLAdapter,`
10+
which continues to need its own :py:meth:`~cheroot.ssl.pyopenssl.pyOpenSSLAdapter.makefile()`
11+
method to provide wrapped streaming classes equipped for dealing with ``OpenSSL`` errors.
12+
13+
-- by :user:`julianz-`

0 commit comments

Comments
 (0)