Pyjoyment 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- Pyjo/App/ASGI.py +735 -0
- Pyjo/App/CGI.py +773 -0
- Pyjo/App/FastCGI.py +631 -0
- Pyjo/App/Lambda.py +987 -0
- Pyjo/App/WSGI.py +427 -0
- Pyjo/App/__init__.py +409 -0
- Pyjo/Asset/File.py +377 -0
- Pyjo/Asset/Memory.py +235 -0
- Pyjo/Asset/__init__.py +200 -0
- Pyjo/Base.py +215 -0
- Pyjo/Cache.py +119 -0
- Pyjo/Collection.py +570 -0
- Pyjo/Content/MultiPart.py +325 -0
- Pyjo/Content/Single.py +193 -0
- Pyjo/Content/__init__.py +806 -0
- Pyjo/Cookie/Request.py +90 -0
- Pyjo/Cookie/Response.py +294 -0
- Pyjo/Cookie/__init__.py +143 -0
- Pyjo/DOM/CSS.py +1019 -0
- Pyjo/DOM/HTML.py +550 -0
- Pyjo/DOM/__init__.py +1329 -0
- Pyjo/Date.py +294 -0
- Pyjo/EventEmitter.py +522 -0
- Pyjo/Exception.py +494 -0
- Pyjo/File.py +959 -0
- Pyjo/GetOpt.py +605 -0
- Pyjo/Headers.py +760 -0
- Pyjo/HelloWorld.py +77 -0
- Pyjo/Home.py +109 -0
- Pyjo/IOLoop/ChildProcess.py +591 -0
- Pyjo/IOLoop/Client.py +736 -0
- Pyjo/IOLoop/Datagram.py +982 -0
- Pyjo/IOLoop/Messages.py +664 -0
- Pyjo/IOLoop/Server.py +458 -0
- Pyjo/IOLoop/Stdio.py +446 -0
- Pyjo/IOLoop/Stream.py +644 -0
- Pyjo/IOLoop/Subprocess.py +419 -0
- Pyjo/IOLoop/TLS.py +360 -0
- Pyjo/IOLoop/__init__.py +1036 -0
- Pyjo/IOLoop/certs/README.md +41 -0
- Pyjo/IOLoop/certs/ca.crt +20 -0
- Pyjo/IOLoop/certs/server.crt +21 -0
- Pyjo/IOLoop/certs/server.key +28 -0
- Pyjo/JSON/Pointer.py +135 -0
- Pyjo/JSON/RPC.py +772 -0
- Pyjo/JSON/Schema.py +2506 -0
- Pyjo/JSON/__init__.py +573 -0
- Pyjo/Loader.py +342 -0
- Pyjo/Log.py +723 -0
- Pyjo/Message/Request.py +725 -0
- Pyjo/Message/Response.py +476 -0
- Pyjo/Message/__init__.py +830 -0
- Pyjo/Parameters.py +472 -0
- Pyjo/Path.py +419 -0
- Pyjo/Promise.py +894 -0
- Pyjo/Prove.py +1305 -0
- Pyjo/Reactor/Asyncio.py +854 -0
- Pyjo/Reactor/Poll.py +439 -0
- Pyjo/Reactor/__init__.py +525 -0
- Pyjo/SSE.py +119 -0
- Pyjo/Server/ASGI.py +486 -0
- Pyjo/Server/CGI.py +259 -0
- Pyjo/Server/Daemon.py +1007 -0
- Pyjo/Server/FastCGI.py +517 -0
- Pyjo/Server/Hot/__init__.py +687 -0
- Pyjo/Server/Hot/__main__.py +5 -0
- Pyjo/Server/Lambda.py +641 -0
- Pyjo/Server/Prefork.py +917 -0
- Pyjo/Server/Prethread.py +394 -0
- Pyjo/Server/Reload/Backend/Poll.py +114 -0
- Pyjo/Server/Reload/Backend/__init__.py +126 -0
- Pyjo/Server/Reload/__init__.py +482 -0
- Pyjo/Server/Reload/__main__.py +5 -0
- Pyjo/Server/Stdio.py +228 -0
- Pyjo/Server/WSGI.py +349 -0
- Pyjo/Server/__init__.py +396 -0
- Pyjo/String/Bytes.py +532 -0
- Pyjo/String/HTML.py +210 -0
- Pyjo/String/Mixin.py +333 -0
- Pyjo/String/Template.py +470 -0
- Pyjo/String/Unicode.py +553 -0
- Pyjo/String/__init__.py +0 -0
- Pyjo/TOML.py +232 -0
- Pyjo/Template.py +1290 -0
- Pyjo/Test/Pyjo.py +1454 -0
- Pyjo/Test/__init__.py +1233 -0
- Pyjo/Test/pytest_plugin.py +305 -0
- Pyjo/Transaction/HTTP.py +374 -0
- Pyjo/Transaction/WebSocket.py +709 -0
- Pyjo/Transaction/__init__.py +429 -0
- Pyjo/URL/Template.py +459 -0
- Pyjo/URL/__init__.py +651 -0
- Pyjo/Upload.py +139 -0
- Pyjo/UserAgent/CookieJar.py +391 -0
- Pyjo/UserAgent/Proxy.py +138 -0
- Pyjo/UserAgent/Server.py +239 -0
- Pyjo/UserAgent/Transactor.py +766 -0
- Pyjo/UserAgent/__init__.py +1243 -0
- Pyjo/Util.py +3567 -0
- Pyjo/WebSocket.py +332 -0
- Pyjo/YAML.py +1167 -0
- Pyjo/__init__.py +441 -0
- Pyjo/__main__.py +35 -0
- Pyjo/py.typed +0 -0
- Pyjoyment/Command/Author/__init__.py +20 -0
- Pyjoyment/Command/Author/generate/__init__.py +106 -0
- Pyjoyment/Command/Author/generate/app.py +299 -0
- Pyjoyment/Command/Author/generate/dockerfile.py +281 -0
- Pyjoyment/Command/Author/generate/kubernetes.py +514 -0
- Pyjoyment/Command/Author/generate/lambda_zip.py +372 -0
- Pyjoyment/Command/Author/generate/lite_app.py +113 -0
- Pyjoyment/Command/Author/generate/plugin.py +267 -0
- Pyjoyment/Command/Author/generate/pyproject.py +123 -0
- Pyjoyment/Command/Author/inflate.py +101 -0
- Pyjoyment/Command/__init__.py +332 -0
- Pyjoyment/Command/asgi.py +92 -0
- Pyjoyment/Command/cgi.py +90 -0
- Pyjoyment/Command/daemon.py +160 -0
- Pyjoyment/Command/eval.py +128 -0
- Pyjoyment/Command/fastcgi.py +99 -0
- Pyjoyment/Command/get.py +305 -0
- Pyjoyment/Command/lambda.py +117 -0
- Pyjoyment/Command/prefork.py +181 -0
- Pyjoyment/Command/prethread.py +126 -0
- Pyjoyment/Command/routes.py +133 -0
- Pyjoyment/Command/stdio.py +92 -0
- Pyjoyment/Command/version.py +141 -0
- Pyjoyment/Command/wsgi.py +93 -0
- Pyjoyment/Commands.py +546 -0
- Pyjoyment/Controller.py +1307 -0
- Pyjoyment/HelloWorld.py +56 -0
- Pyjoyment/Lite.py +494 -0
- Pyjoyment/Plugin/AllowedHosts.py +205 -0
- Pyjoyment/Plugin/Config.py +248 -0
- Pyjoyment/Plugin/DefaultHelpers.py +1202 -0
- Pyjoyment/Plugin/EPLRenderer.py +134 -0
- Pyjoyment/Plugin/EPRenderer.py +212 -0
- Pyjoyment/Plugin/HeaderCondition.py +98 -0
- Pyjoyment/Plugin/Health.py +325 -0
- Pyjoyment/Plugin/JSONConfig.py +172 -0
- Pyjoyment/Plugin/Mount.py +223 -0
- Pyjoyment/Plugin/PyRenderer.py +209 -0
- Pyjoyment/Plugin/TOMLConfig.py +141 -0
- Pyjoyment/Plugin/TagHelpers.py +959 -0
- Pyjoyment/Plugin/YAMLConfig.py +178 -0
- Pyjoyment/Plugin/__init__.py +80 -0
- Pyjoyment/Plugins.py +215 -0
- Pyjoyment/Renderer.py +673 -0
- Pyjoyment/Routes/Match.py +251 -0
- Pyjoyment/Routes/Pattern.py +443 -0
- Pyjoyment/Routes/Route.py +827 -0
- Pyjoyment/Routes/__init__.py +419 -0
- Pyjoyment/Sessions.py +255 -0
- Pyjoyment/Static.py +421 -0
- Pyjoyment/Types.py +238 -0
- Pyjoyment/Validator/Validation.py +395 -0
- Pyjoyment/Validator/__init__.py +304 -0
- Pyjoyment/__init__.py +1083 -0
- Pyjoyment/__main__.py +5 -0
- Pyjoyment/py.typed +0 -0
- Pyjoyment/resources/public/favicon.ico +0 -0
- Pyjoyment/resources/public/favicon.svg +345 -0
- Pyjoyment/resources/public/pyjo/bootstrap/LICENSE +21 -0
- Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.bundle.min.js.map +1 -0
- Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.css +6 -0
- Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.js +7 -0
- Pyjoyment/resources/public/pyjo/bootstrap/bootstrap.min.css.map +1 -0
- Pyjoyment/resources/public/pyjo/failunicat.png +0 -0
- Pyjoyment/resources/public/pyjo/failunicat.svg +50 -0
- Pyjoyment/resources/public/pyjo/highlight.js/LICENSE +29 -0
- Pyjoyment/resources/public/pyjo/highlight.js/highlight-mojo-dark.css +56 -0
- Pyjoyment/resources/public/pyjo/highlight.js/highlight.min.js +1262 -0
- Pyjoyment/resources/public/pyjo/highlight.js/pyjoyment.min.js +1 -0
- Pyjoyment/resources/public/pyjo/logo-black-4x.png +0 -0
- Pyjoyment/resources/public/pyjo/logo-black-4x.svg +346 -0
- Pyjoyment/resources/public/pyjo/logo-white-2x.png +0 -0
- Pyjoyment/resources/public/pyjo/logo-white-2x.svg +346 -0
- Pyjoyment/resources/public/pyjo/logo-white-4x.png +0 -0
- Pyjoyment/resources/public/pyjo/logo-white-4x.svg +346 -0
- Pyjoyment/resources/public/pyjo/logo-white.png +0 -0
- Pyjoyment/resources/public/pyjo/logo-white.svg +346 -0
- Pyjoyment/resources/public/pyjo/logo.png +0 -0
- Pyjoyment/resources/public/pyjo/logo.svg +346 -0
- Pyjoyment/resources/public/pyjo/notfound.png +0 -0
- Pyjoyment/resources/public/pyjo/notfound.svg +3 -0
- Pyjoyment/resources/public/pyjo/nounicat.png +0 -0
- Pyjoyment/resources/public/pyjo/nounicat.svg +5 -0
- Pyjoyment/resources/public/pyjo/pinstripe-dark.png +0 -0
- Pyjoyment/resources/public/pyjo/pinstripe-dark.svg +4 -0
- Pyjoyment/resources/public/pyjo/pinstripe-light.png +0 -0
- Pyjoyment/resources/public/pyjo/pinstripe-light.svg +4 -0
- Pyjoyment/resources/public/pyjo/pyjo.css +204 -0
- Pyjoyment/resources/templates/pyjo/debug.html.ep +346 -0
- Pyjoyment/resources/templates/pyjo/exception.html.ep +32 -0
- Pyjoyment/resources/templates/pyjo/not_found.html.ep +45 -0
- pyjoyment-0.1.0.dist-info/METADATA +376 -0
- pyjoyment-0.1.0.dist-info/RECORD +200 -0
- pyjoyment-0.1.0.dist-info/WHEEL +4 -0
- pyjoyment-0.1.0.dist-info/entry_points.txt +8 -0
- pyjoyment-0.1.0.dist-info/licenses/LICENSE +201 -0
Pyjo/IOLoop/TLS.py
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pyjo.IOLoop.TLS - Non-blocking TLS handshake
|
|
3
|
+
============================================
|
|
4
|
+
::
|
|
5
|
+
|
|
6
|
+
import Pyjo.IOLoop.TLS
|
|
7
|
+
|
|
8
|
+
# Negotiate TLS
|
|
9
|
+
tls = Pyjo.IOLoop.TLS.new(old_handle)
|
|
10
|
+
tls.on('upgrade', lambda tls, new_handle: ...)
|
|
11
|
+
tls.on('error', lambda tls, err: ...)
|
|
12
|
+
tls.negotiate(server=True)
|
|
13
|
+
|
|
14
|
+
# Start reactor if necessary
|
|
15
|
+
if not tls.reactor.is_running:
|
|
16
|
+
tls.reactor.start()
|
|
17
|
+
|
|
18
|
+
:mod:`Pyjo.IOLoop.TLS` negotiates TLS for :mod:`Pyjo.IOLoop`, with the
|
|
19
|
+
:mod:`ssl` module of the standard library.
|
|
20
|
+
|
|
21
|
+
The end of a ``with`` block closes the connection of the handshake.
|
|
22
|
+
|
|
23
|
+
Events
|
|
24
|
+
------
|
|
25
|
+
|
|
26
|
+
:mod:`Pyjo.IOLoop.TLS` inherits all events from :mod:`Pyjo.EventEmitter` and
|
|
27
|
+
can emit the following new ones.
|
|
28
|
+
|
|
29
|
+
upgrade
|
|
30
|
+
~~~~~~~
|
|
31
|
+
::
|
|
32
|
+
|
|
33
|
+
tls.on('upgrade', lambda tls, handle: ...)
|
|
34
|
+
|
|
35
|
+
Emitted once TLS has been negotiated, with the :class:`ssl.SSLSocket`.
|
|
36
|
+
|
|
37
|
+
error
|
|
38
|
+
~~~~~
|
|
39
|
+
::
|
|
40
|
+
|
|
41
|
+
tls.on('error', lambda tls, err: ...)
|
|
42
|
+
|
|
43
|
+
Emitted if an error occurs during negotiation, fatal if unhandled.
|
|
44
|
+
|
|
45
|
+
See also
|
|
46
|
+
--------
|
|
47
|
+
|
|
48
|
+
:mod:`Pyjoyment`, https://mojolicious.org.
|
|
49
|
+
|
|
50
|
+
Ported from `Mojo::IOLoop::TLS <https://github.com/mojolicious/mojo/blob/v9.49/lib/Mojo/IOLoop/TLS.pm>`_
|
|
51
|
+
of Mojolicious 9.49.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
import Pyjo.EventEmitter
|
|
55
|
+
import Pyjo.IOLoop
|
|
56
|
+
import Pyjo.Reactor
|
|
57
|
+
|
|
58
|
+
import os
|
|
59
|
+
import ssl
|
|
60
|
+
import sys
|
|
61
|
+
import weakref
|
|
62
|
+
|
|
63
|
+
from types import TracebackType
|
|
64
|
+
from typing import Any, Self, cast
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
TLS = not os.environ.get('PYJO_NO_TLS')
|
|
68
|
+
|
|
69
|
+
# The built-in certificate for localhost and 127.0.0.1, which certs/ca.crt signs
|
|
70
|
+
CERT = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'certs', 'server.crt')
|
|
71
|
+
KEY = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'certs', 'server.key')
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class Pyjo_IOLoop_TLS(Pyjo.EventEmitter.object):
|
|
75
|
+
"""
|
|
76
|
+
:mod:`Pyjo.IOLoop.TLS` inherits all methods from :mod:`Pyjo.EventEmitter`
|
|
77
|
+
and implements the following new ones.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def __init__(self, handle: Any = None) -> None:
|
|
81
|
+
"""::
|
|
82
|
+
|
|
83
|
+
tls = Pyjo.IOLoop.TLS.new(handle)
|
|
84
|
+
|
|
85
|
+
Construct a new :mod:`Pyjo.IOLoop.TLS` object.
|
|
86
|
+
"""
|
|
87
|
+
super().__init__()
|
|
88
|
+
self._handle: Any = handle
|
|
89
|
+
self._reactor: weakref.ref[Pyjo.Reactor.object] | None = None
|
|
90
|
+
|
|
91
|
+
def __del__(self) -> None:
|
|
92
|
+
# Module globals may be gone at the end of the interpreter
|
|
93
|
+
try:
|
|
94
|
+
if not sys.is_finalizing() and getattr(self, '_handle', None) is not None:
|
|
95
|
+
reactor = self._reactor() if self._reactor is not None else None
|
|
96
|
+
Pyjo.Reactor._finalize(self._release, reactor) # pyright: ignore[reportPrivateUsage]
|
|
97
|
+
except Exception:
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
# Not in Mojolicious: a context manager, which closes the connection of the handshake where the block ends, as PyPy
|
|
101
|
+
# frees the object only when its garbage collector runs
|
|
102
|
+
def __enter__(self) -> Self:
|
|
103
|
+
return self
|
|
104
|
+
|
|
105
|
+
def __exit__(
|
|
106
|
+
self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None
|
|
107
|
+
) -> None:
|
|
108
|
+
self.close()
|
|
109
|
+
|
|
110
|
+
def _release(self) -> None:
|
|
111
|
+
self._cleanup()
|
|
112
|
+
self._close()
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def reactor(self) -> Pyjo.Reactor.object:
|
|
116
|
+
"""::
|
|
117
|
+
|
|
118
|
+
reactor = tls.reactor
|
|
119
|
+
tls.reactor = Pyjo.Reactor.Poll.new()
|
|
120
|
+
|
|
121
|
+
Low-level event reactor, defaults to the ``reactor`` attribute value of
|
|
122
|
+
the global :mod:`Pyjo.IOLoop` singleton. Note that this attribute is
|
|
123
|
+
weakened.
|
|
124
|
+
"""
|
|
125
|
+
if self._reactor is None:
|
|
126
|
+
self._reactor = weakref.ref(Pyjo.IOLoop.singleton.reactor)
|
|
127
|
+
if (reactor := self._reactor()) is None:
|
|
128
|
+
raise ReferenceError('weakly-referenced object no longer exists')
|
|
129
|
+
return reactor
|
|
130
|
+
|
|
131
|
+
@reactor.setter
|
|
132
|
+
def reactor(self, value: Pyjo.Reactor.object) -> None:
|
|
133
|
+
self._reactor = weakref.ref(value)
|
|
134
|
+
|
|
135
|
+
@classmethod
|
|
136
|
+
def can_tls(cls) -> bool:
|
|
137
|
+
"""::
|
|
138
|
+
|
|
139
|
+
boolean = Pyjo.IOLoop.TLS.object.can_tls()
|
|
140
|
+
|
|
141
|
+
True if TLS support is enabled, as it is unless the ``PYJO_NO_TLS``
|
|
142
|
+
environment variable is set.
|
|
143
|
+
"""
|
|
144
|
+
return TLS
|
|
145
|
+
|
|
146
|
+
# Not in Mojolicious: the release of the connection of the handshake, which Perl does in DESTROY, also at the end of
|
|
147
|
+
# a with block
|
|
148
|
+
def close(self) -> None:
|
|
149
|
+
"""::
|
|
150
|
+
|
|
151
|
+
tls.close()
|
|
152
|
+
|
|
153
|
+
Close the connection of the handshake, as the end of a ``with`` block
|
|
154
|
+
does.
|
|
155
|
+
"""
|
|
156
|
+
self._release()
|
|
157
|
+
|
|
158
|
+
def negotiate(self, **options: Any) -> None:
|
|
159
|
+
"""::
|
|
160
|
+
|
|
161
|
+
tls.negotiate(server=True)
|
|
162
|
+
tls.negotiate(tls_ca='/etc/tls/ca.crt')
|
|
163
|
+
|
|
164
|
+
Negotiate TLS.
|
|
165
|
+
|
|
166
|
+
These options are currently available:
|
|
167
|
+
|
|
168
|
+
address
|
|
169
|
+
::
|
|
170
|
+
|
|
171
|
+
address='mojolicious.org'
|
|
172
|
+
|
|
173
|
+
Name of the server, for SNI and the verification of its
|
|
174
|
+
certificate on the client-side.
|
|
175
|
+
|
|
176
|
+
server
|
|
177
|
+
::
|
|
178
|
+
|
|
179
|
+
server=True
|
|
180
|
+
|
|
181
|
+
Negotiate TLS from the server-side, defaults to the client-side.
|
|
182
|
+
|
|
183
|
+
tls_ca
|
|
184
|
+
::
|
|
185
|
+
|
|
186
|
+
tls_ca='/etc/tls/ca.crt'
|
|
187
|
+
|
|
188
|
+
Path to TLS certificate authority file. On the server-side, a
|
|
189
|
+
client certificate is then required.
|
|
190
|
+
|
|
191
|
+
tls_cert
|
|
192
|
+
::
|
|
193
|
+
|
|
194
|
+
tls_cert='/etc/tls/server.crt'
|
|
195
|
+
tls_cert={'mojolicious.org': '/etc/tls/mojo.crt'}
|
|
196
|
+
|
|
197
|
+
Path to the TLS cert file, defaults to a built-in test certificate
|
|
198
|
+
on the server-side.
|
|
199
|
+
|
|
200
|
+
tls_context
|
|
201
|
+
::
|
|
202
|
+
|
|
203
|
+
tls_context=ssl.create_default_context()
|
|
204
|
+
|
|
205
|
+
The :class:`ssl.SSLContext` to use instead of the one that the
|
|
206
|
+
other options make.
|
|
207
|
+
|
|
208
|
+
tls_hostname
|
|
209
|
+
::
|
|
210
|
+
|
|
211
|
+
tls_hostname='mojolicious.org'
|
|
212
|
+
tls_hostname=''
|
|
213
|
+
|
|
214
|
+
Name of the server for SNI and the verification of its
|
|
215
|
+
certificate on the client-side instead of ``address``, an empty
|
|
216
|
+
one for no SNI.
|
|
217
|
+
|
|
218
|
+
tls_key
|
|
219
|
+
::
|
|
220
|
+
|
|
221
|
+
tls_key='/etc/tls/server.key'
|
|
222
|
+
tls_key={'mojolicious.org': '/etc/tls/mojo.key'}
|
|
223
|
+
|
|
224
|
+
Path to the TLS key file, defaults to a built-in test key on the
|
|
225
|
+
server-side.
|
|
226
|
+
"""
|
|
227
|
+
if not TLS:
|
|
228
|
+
return self._error('TLS support is disabled')
|
|
229
|
+
|
|
230
|
+
handle = self._handle
|
|
231
|
+
server = bool(options.get('server'))
|
|
232
|
+
try:
|
|
233
|
+
context = _expand(options)
|
|
234
|
+
# Not in Mojolicious: tls_hostname for the SSL_hostname and SSL_verifycn_name of IO::Socket::SSL
|
|
235
|
+
hostname = None if server else options.get('tls_hostname', options.get('address')) or None
|
|
236
|
+
handle = context.wrap_socket(
|
|
237
|
+
handle, server_side=server, do_handshake_on_connect=False, server_hostname=hostname
|
|
238
|
+
)
|
|
239
|
+
except (OSError, ValueError) as e:
|
|
240
|
+
return self._error(e)
|
|
241
|
+
self._handle = handle
|
|
242
|
+
|
|
243
|
+
# The reactor keeps the negotiation alive until it is done, as Mojo's callback refers to it
|
|
244
|
+
def cb(reactor: Any, writable: bool) -> None:
|
|
245
|
+
self._tls(handle)
|
|
246
|
+
|
|
247
|
+
self.reactor.io(handle, cb)
|
|
248
|
+
|
|
249
|
+
def _cleanup(self) -> None:
|
|
250
|
+
reactor = self._reactor() if self._reactor is not None else None
|
|
251
|
+
if reactor is not None and self._handle is not None:
|
|
252
|
+
try:
|
|
253
|
+
reactor.remove(self._handle)
|
|
254
|
+
except Exception:
|
|
255
|
+
pass
|
|
256
|
+
|
|
257
|
+
# The handle that is not upgraded is closed where it is dropped, as Perl closes it with the last reference
|
|
258
|
+
def _close(self) -> None:
|
|
259
|
+
if (handle := self._handle) is not None:
|
|
260
|
+
self._handle = None
|
|
261
|
+
handle.close()
|
|
262
|
+
|
|
263
|
+
def _error(self, err: Any) -> None:
|
|
264
|
+
self._cleanup()
|
|
265
|
+
self._close()
|
|
266
|
+
self.emit('error', err)
|
|
267
|
+
|
|
268
|
+
def _tls(self, handle: ssl.SSLSocket) -> None:
|
|
269
|
+
# Switch between reading and writing
|
|
270
|
+
try:
|
|
271
|
+
handle.do_handshake()
|
|
272
|
+
except ssl.SSLWantReadError:
|
|
273
|
+
self.reactor.watch(handle, True, False)
|
|
274
|
+
return
|
|
275
|
+
except ssl.SSLWantWriteError:
|
|
276
|
+
self.reactor.watch(handle, True, True)
|
|
277
|
+
return
|
|
278
|
+
except OSError as e:
|
|
279
|
+
return self._error(e)
|
|
280
|
+
|
|
281
|
+
self._cleanup()
|
|
282
|
+
self._handle = None
|
|
283
|
+
self.emit('upgrade', handle)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
# The certificates or keys of names, with '' for the one of any name
|
|
287
|
+
def _named(value: Any) -> dict[str, str]:
|
|
288
|
+
if isinstance(value, dict):
|
|
289
|
+
return {str(k): str(v) for k, v in cast(dict[Any, Any], value).items()}
|
|
290
|
+
return {'': value} if value else {}
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
# Left out from Mojolicious: _expand makes the options of IO::Socket::SSL, here an ssl.SSLContext
|
|
294
|
+
def _expand(options: dict[str, Any]) -> ssl.SSLContext:
|
|
295
|
+
# Unlike Mojolicious: tls_options of IO::Socket::SSL is tls_context, an ssl.SSLContext
|
|
296
|
+
if (context := options.get('tls_context')) is not None:
|
|
297
|
+
return context
|
|
298
|
+
|
|
299
|
+
ca = options.get('tls_ca')
|
|
300
|
+
if ca is not None and not os.path.isfile(ca):
|
|
301
|
+
ca = None
|
|
302
|
+
cert, key = options.get('tls_cert'), options.get('tls_key')
|
|
303
|
+
|
|
304
|
+
if options.get('server'):
|
|
305
|
+
|
|
306
|
+
def server_context(cert: str, key: str) -> ssl.SSLContext:
|
|
307
|
+
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
|
308
|
+
# Not in Mojolicious: no session tickets of TLS 1.3 on Windows, which the server sends after the handshake
|
|
309
|
+
# of the client, where a write to a client that has closed already aborts the handshake of the server
|
|
310
|
+
if sys.platform == 'win32':
|
|
311
|
+
context.num_tickets = 0
|
|
312
|
+
context.load_cert_chain(cert, key)
|
|
313
|
+
if ca is not None:
|
|
314
|
+
context.load_verify_locations(ca)
|
|
315
|
+
context.verify_mode = ssl.CERT_REQUIRED
|
|
316
|
+
return context
|
|
317
|
+
|
|
318
|
+
certs, keys = _named(cert), _named(key)
|
|
319
|
+
context = server_context(certs.pop('', None) or CERT, keys.pop('', None) or KEY)
|
|
320
|
+
|
|
321
|
+
# Not in Mojolicious: a certificate for each name, which IO::Socket::SSL chooses by SNI
|
|
322
|
+
if len(certs):
|
|
323
|
+
contexts = {name: server_context(c, keys.get(name) or KEY) for name, c in certs.items()}
|
|
324
|
+
|
|
325
|
+
def sni(socket: ssl.SSLObject | ssl.SSLSocket, name: str | None, context: ssl.SSLContext) -> None:
|
|
326
|
+
if name is not None and (named := contexts.get(name)) is not None:
|
|
327
|
+
socket.context = named
|
|
328
|
+
|
|
329
|
+
context.sni_callback = sni
|
|
330
|
+
return context
|
|
331
|
+
|
|
332
|
+
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
|
333
|
+
if ca is not None:
|
|
334
|
+
context.load_verify_locations(ca)
|
|
335
|
+
else:
|
|
336
|
+
context.load_default_certs()
|
|
337
|
+
if cert:
|
|
338
|
+
context.load_cert_chain(cert, key)
|
|
339
|
+
if not options.get('tls_hostname', options.get('address')):
|
|
340
|
+
context.check_hostname = False
|
|
341
|
+
return context
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
# Not in Mojolicious: the context that negotiate makes of the options, for a server that changes it, as
|
|
345
|
+
# Mojo::Server::Daemon adds tls_options of IO::Socket::SSL to the other options
|
|
346
|
+
def context(**options: Any) -> ssl.SSLContext:
|
|
347
|
+
"""::
|
|
348
|
+
|
|
349
|
+
context = context(server=True, tls_cert='/etc/tls/server.crt', tls_key='/etc/tls/server.key')
|
|
350
|
+
|
|
351
|
+
The :class:`ssl.SSLContext` that :meth:`Pyjo_IOLoop_TLS.negotiate` makes
|
|
352
|
+
of its options. A context of a server sends no session tickets of TLS 1.3
|
|
353
|
+
on Windows, where a client that closes the connection right after its
|
|
354
|
+
handshake would make the handshake of the server fail.
|
|
355
|
+
"""
|
|
356
|
+
return _expand(options)
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
new = Pyjo_IOLoop_TLS.new
|
|
360
|
+
object = Pyjo_IOLoop_TLS
|