@ajksunkang-aios/kgraph-linux-x64 0.1.2 → 0.1.3

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.
Files changed (196) hide show
  1. package/bin/kgraph-launcher +15 -3
  2. package/lib/kgraph/scripts/build-bundle.sh +17 -4
  3. package/lib/site-packages/outcome/__init__.py +20 -0
  4. package/lib/site-packages/outcome/_impl.py +239 -0
  5. package/lib/site-packages/outcome/_util.py +33 -0
  6. package/lib/site-packages/outcome/_version.py +7 -0
  7. package/lib/site-packages/outcome/py.typed +0 -0
  8. package/lib/site-packages/outcome-1.3.0.post0.dist-info/INSTALLER +1 -0
  9. package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE +3 -0
  10. package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE.APACHE2 +202 -0
  11. package/lib/site-packages/outcome-1.3.0.post0.dist-info/LICENSE.MIT +20 -0
  12. package/lib/site-packages/outcome-1.3.0.post0.dist-info/METADATA +63 -0
  13. package/lib/site-packages/outcome-1.3.0.post0.dist-info/RECORD +13 -0
  14. package/lib/site-packages/outcome-1.3.0.post0.dist-info/WHEEL +6 -0
  15. package/lib/site-packages/outcome-1.3.0.post0.dist-info/top_level.txt +1 -0
  16. package/lib/site-packages/sniffio/__init__.py +17 -0
  17. package/lib/site-packages/sniffio/_impl.py +95 -0
  18. package/lib/site-packages/sniffio/_tests/__init__.py +0 -0
  19. package/lib/site-packages/sniffio/_tests/test_sniffio.py +84 -0
  20. package/lib/site-packages/sniffio/_version.py +3 -0
  21. package/lib/site-packages/sniffio/py.typed +0 -0
  22. package/lib/site-packages/sniffio-1.3.1.dist-info/INSTALLER +1 -0
  23. package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE +3 -0
  24. package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE.APACHE2 +202 -0
  25. package/lib/site-packages/sniffio-1.3.1.dist-info/LICENSE.MIT +20 -0
  26. package/lib/site-packages/sniffio-1.3.1.dist-info/METADATA +104 -0
  27. package/lib/site-packages/sniffio-1.3.1.dist-info/RECORD +14 -0
  28. package/lib/site-packages/sniffio-1.3.1.dist-info/WHEEL +5 -0
  29. package/lib/site-packages/sniffio-1.3.1.dist-info/top_level.txt +1 -0
  30. package/lib/site-packages/sortedcontainers/__init__.py +74 -0
  31. package/lib/site-packages/sortedcontainers/sorteddict.py +812 -0
  32. package/lib/site-packages/sortedcontainers/sortedlist.py +2646 -0
  33. package/lib/site-packages/sortedcontainers/sortedset.py +733 -0
  34. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/INSTALLER +1 -0
  35. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/LICENSE +13 -0
  36. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/METADATA +264 -0
  37. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/RECORD +10 -0
  38. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/WHEEL +6 -0
  39. package/lib/site-packages/sortedcontainers-2.4.0.dist-info/top_level.txt +1 -0
  40. package/lib/site-packages/trio/__init__.py +133 -0
  41. package/lib/site-packages/trio/__main__.py +3 -0
  42. package/lib/site-packages/trio/_abc.py +714 -0
  43. package/lib/site-packages/trio/_channel.py +610 -0
  44. package/lib/site-packages/trio/_core/__init__.py +94 -0
  45. package/lib/site-packages/trio/_core/_asyncgens.py +243 -0
  46. package/lib/site-packages/trio/_core/_concat_tb.py +26 -0
  47. package/lib/site-packages/trio/_core/_entry_queue.py +223 -0
  48. package/lib/site-packages/trio/_core/_exceptions.py +169 -0
  49. package/lib/site-packages/trio/_core/_generated_instrumentation.py +50 -0
  50. package/lib/site-packages/trio/_core/_generated_io_epoll.py +98 -0
  51. package/lib/site-packages/trio/_core/_generated_io_kqueue.py +153 -0
  52. package/lib/site-packages/trio/_core/_generated_io_windows.py +204 -0
  53. package/lib/site-packages/trio/_core/_generated_run.py +269 -0
  54. package/lib/site-packages/trio/_core/_generated_windows_ffi.py +10 -0
  55. package/lib/site-packages/trio/_core/_instrumentation.py +117 -0
  56. package/lib/site-packages/trio/_core/_io_common.py +31 -0
  57. package/lib/site-packages/trio/_core/_io_epoll.py +385 -0
  58. package/lib/site-packages/trio/_core/_io_kqueue.py +292 -0
  59. package/lib/site-packages/trio/_core/_io_windows.py +1036 -0
  60. package/lib/site-packages/trio/_core/_ki.py +271 -0
  61. package/lib/site-packages/trio/_core/_local.py +104 -0
  62. package/lib/site-packages/trio/_core/_mock_clock.py +165 -0
  63. package/lib/site-packages/trio/_core/_parking_lot.py +317 -0
  64. package/lib/site-packages/trio/_core/_run.py +3148 -0
  65. package/lib/site-packages/trio/_core/_run_context.py +15 -0
  66. package/lib/site-packages/trio/_core/_tests/__init__.py +0 -0
  67. package/lib/site-packages/trio/_core/_tests/test_asyncgen.py +339 -0
  68. package/lib/site-packages/trio/_core/_tests/test_cancelled.py +222 -0
  69. package/lib/site-packages/trio/_core/_tests/test_exceptiongroup_gc.py +103 -0
  70. package/lib/site-packages/trio/_core/_tests/test_guest_mode.py +755 -0
  71. package/lib/site-packages/trio/_core/_tests/test_instrumentation.py +315 -0
  72. package/lib/site-packages/trio/_core/_tests/test_io.py +522 -0
  73. package/lib/site-packages/trio/_core/_tests/test_ki.py +703 -0
  74. package/lib/site-packages/trio/_core/_tests/test_local.py +118 -0
  75. package/lib/site-packages/trio/_core/_tests/test_mock_clock.py +193 -0
  76. package/lib/site-packages/trio/_core/_tests/test_parking_lot.py +389 -0
  77. package/lib/site-packages/trio/_core/_tests/test_run.py +3024 -0
  78. package/lib/site-packages/trio/_core/_tests/test_thread_cache.py +227 -0
  79. package/lib/site-packages/trio/_core/_tests/test_tutil.py +13 -0
  80. package/lib/site-packages/trio/_core/_tests/test_unbounded_queue.py +154 -0
  81. package/lib/site-packages/trio/_core/_tests/test_windows.py +305 -0
  82. package/lib/site-packages/trio/_core/_tests/tutil.py +117 -0
  83. package/lib/site-packages/trio/_core/_tests/type_tests/nursery_start.py +79 -0
  84. package/lib/site-packages/trio/_core/_tests/type_tests/run.py +51 -0
  85. package/lib/site-packages/trio/_core/_thread_cache.py +317 -0
  86. package/lib/site-packages/trio/_core/_traps.py +318 -0
  87. package/lib/site-packages/trio/_core/_unbounded_queue.py +163 -0
  88. package/lib/site-packages/trio/_core/_wakeup_socketpair.py +75 -0
  89. package/lib/site-packages/trio/_core/_windows_cffi.py +313 -0
  90. package/lib/site-packages/trio/_deprecate.py +171 -0
  91. package/lib/site-packages/trio/_dtls.py +1380 -0
  92. package/lib/site-packages/trio/_file_io.py +513 -0
  93. package/lib/site-packages/trio/_highlevel_generic.py +125 -0
  94. package/lib/site-packages/trio/_highlevel_open_tcp_listeners.py +251 -0
  95. package/lib/site-packages/trio/_highlevel_open_tcp_stream.py +397 -0
  96. package/lib/site-packages/trio/_highlevel_open_unix_stream.py +65 -0
  97. package/lib/site-packages/trio/_highlevel_serve_listeners.py +148 -0
  98. package/lib/site-packages/trio/_highlevel_socket.py +423 -0
  99. package/lib/site-packages/trio/_highlevel_ssl_helpers.py +180 -0
  100. package/lib/site-packages/trio/_path.py +289 -0
  101. package/lib/site-packages/trio/_repl.py +159 -0
  102. package/lib/site-packages/trio/_signals.py +185 -0
  103. package/lib/site-packages/trio/_socket.py +1326 -0
  104. package/lib/site-packages/trio/_ssl.py +964 -0
  105. package/lib/site-packages/trio/_subprocess.py +1178 -0
  106. package/lib/site-packages/trio/_subprocess_platform/__init__.py +123 -0
  107. package/lib/site-packages/trio/_subprocess_platform/kqueue.py +48 -0
  108. package/lib/site-packages/trio/_subprocess_platform/waitid.py +113 -0
  109. package/lib/site-packages/trio/_subprocess_platform/windows.py +11 -0
  110. package/lib/site-packages/trio/_sync.py +908 -0
  111. package/lib/site-packages/trio/_tests/__init__.py +0 -0
  112. package/lib/site-packages/trio/_tests/astrill-codesigning-cert.cer +0 -0
  113. package/lib/site-packages/trio/_tests/check_type_completeness.py +247 -0
  114. package/lib/site-packages/trio/_tests/module_with_deprecations.py +22 -0
  115. package/lib/site-packages/trio/_tests/pytest_plugin.py +54 -0
  116. package/lib/site-packages/trio/_tests/test_abc.py +72 -0
  117. package/lib/site-packages/trio/_tests/test_channel.py +750 -0
  118. package/lib/site-packages/trio/_tests/test_contextvars.py +56 -0
  119. package/lib/site-packages/trio/_tests/test_deprecate.py +277 -0
  120. package/lib/site-packages/trio/_tests/test_deprecate_strict_exception_groups_false.py +64 -0
  121. package/lib/site-packages/trio/_tests/test_dtls.py +950 -0
  122. package/lib/site-packages/trio/_tests/test_exports.py +626 -0
  123. package/lib/site-packages/trio/_tests/test_fakenet.py +317 -0
  124. package/lib/site-packages/trio/_tests/test_file_io.py +269 -0
  125. package/lib/site-packages/trio/_tests/test_highlevel_generic.py +98 -0
  126. package/lib/site-packages/trio/_tests/test_highlevel_open_tcp_listeners.py +419 -0
  127. package/lib/site-packages/trio/_tests/test_highlevel_open_tcp_stream.py +693 -0
  128. package/lib/site-packages/trio/_tests/test_highlevel_open_unix_stream.py +86 -0
  129. package/lib/site-packages/trio/_tests/test_highlevel_serve_listeners.py +186 -0
  130. package/lib/site-packages/trio/_tests/test_highlevel_socket.py +336 -0
  131. package/lib/site-packages/trio/_tests/test_highlevel_ssl_helpers.py +169 -0
  132. package/lib/site-packages/trio/_tests/test_path.py +279 -0
  133. package/lib/site-packages/trio/_tests/test_repl.py +428 -0
  134. package/lib/site-packages/trio/_tests/test_scheduler_determinism.py +47 -0
  135. package/lib/site-packages/trio/_tests/test_signals.py +186 -0
  136. package/lib/site-packages/trio/_tests/test_socket.py +1253 -0
  137. package/lib/site-packages/trio/_tests/test_ssl.py +1371 -0
  138. package/lib/site-packages/trio/_tests/test_subprocess.py +767 -0
  139. package/lib/site-packages/trio/_tests/test_sync.py +735 -0
  140. package/lib/site-packages/trio/_tests/test_testing.py +682 -0
  141. package/lib/site-packages/trio/_tests/test_testing_raisesgroup.py +1128 -0
  142. package/lib/site-packages/trio/_tests/test_threads.py +1173 -0
  143. package/lib/site-packages/trio/_tests/test_timeouts.py +281 -0
  144. package/lib/site-packages/trio/_tests/test_tracing.py +88 -0
  145. package/lib/site-packages/trio/_tests/test_trio.py +8 -0
  146. package/lib/site-packages/trio/_tests/test_unix_pipes.py +288 -0
  147. package/lib/site-packages/trio/_tests/test_util.py +349 -0
  148. package/lib/site-packages/trio/_tests/test_wait_for_object.py +225 -0
  149. package/lib/site-packages/trio/_tests/test_windows_pipes.py +112 -0
  150. package/lib/site-packages/trio/_tests/tools/__init__.py +0 -0
  151. package/lib/site-packages/trio/_tests/tools/test_gen_exports.py +179 -0
  152. package/lib/site-packages/trio/_tests/tools/test_mypy_annotate.py +140 -0
  153. package/lib/site-packages/trio/_tests/tools/test_sync_requirements.py +80 -0
  154. package/lib/site-packages/trio/_tests/type_tests/check_wraps.py +9 -0
  155. package/lib/site-packages/trio/_tests/type_tests/open_memory_channel.py +4 -0
  156. package/lib/site-packages/trio/_tests/type_tests/path.py +140 -0
  157. package/lib/site-packages/trio/_tests/type_tests/subprocesses.py +23 -0
  158. package/lib/site-packages/trio/_tests/type_tests/task_status.py +29 -0
  159. package/lib/site-packages/trio/_threads.py +610 -0
  160. package/lib/site-packages/trio/_timeouts.py +197 -0
  161. package/lib/site-packages/trio/_tools/__init__.py +0 -0
  162. package/lib/site-packages/trio/_tools/gen_exports.py +401 -0
  163. package/lib/site-packages/trio/_tools/mypy_annotate.py +126 -0
  164. package/lib/site-packages/trio/_tools/sync_requirements.py +98 -0
  165. package/lib/site-packages/trio/_tools/windows_ffi_build.py +220 -0
  166. package/lib/site-packages/trio/_unix_pipes.py +197 -0
  167. package/lib/site-packages/trio/_util.py +385 -0
  168. package/lib/site-packages/trio/_version.py +3 -0
  169. package/lib/site-packages/trio/_wait_for_object.py +67 -0
  170. package/lib/site-packages/trio/_windows_pipes.py +144 -0
  171. package/lib/site-packages/trio/abc.py +23 -0
  172. package/lib/site-packages/trio/from_thread.py +13 -0
  173. package/lib/site-packages/trio/lowlevel.py +95 -0
  174. package/lib/site-packages/trio/py.typed +0 -0
  175. package/lib/site-packages/trio/socket.py +602 -0
  176. package/lib/site-packages/trio/testing/__init__.py +58 -0
  177. package/lib/site-packages/trio/testing/_check_streams.py +570 -0
  178. package/lib/site-packages/trio/testing/_checkpoints.py +69 -0
  179. package/lib/site-packages/trio/testing/_fake_net.py +584 -0
  180. package/lib/site-packages/trio/testing/_memory_streams.py +633 -0
  181. package/lib/site-packages/trio/testing/_network.py +36 -0
  182. package/lib/site-packages/trio/testing/_raises_group.py +1015 -0
  183. package/lib/site-packages/trio/testing/_sequencer.py +87 -0
  184. package/lib/site-packages/trio/testing/_trio_test.py +50 -0
  185. package/lib/site-packages/trio/to_thread.py +4 -0
  186. package/lib/site-packages/trio-0.33.0.dist-info/INSTALLER +1 -0
  187. package/lib/site-packages/trio-0.33.0.dist-info/METADATA +186 -0
  188. package/lib/site-packages/trio-0.33.0.dist-info/RECORD +156 -0
  189. package/lib/site-packages/trio-0.33.0.dist-info/REQUESTED +0 -0
  190. package/lib/site-packages/trio-0.33.0.dist-info/WHEEL +5 -0
  191. package/lib/site-packages/trio-0.33.0.dist-info/entry_points.txt +2 -0
  192. package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE +3 -0
  193. package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE.APACHE2 +202 -0
  194. package/lib/site-packages/trio-0.33.0.dist-info/licenses/LICENSE.MIT +22 -0
  195. package/lib/site-packages/trio-0.33.0.dist-info/top_level.txt +1 -0
  196. package/package.json +1 -1
@@ -0,0 +1,964 @@
1
+ from __future__ import annotations
2
+
3
+ import contextlib
4
+ import operator as _operator
5
+ import ssl as _stdlib_ssl
6
+ from enum import Enum as _Enum
7
+ from typing import TYPE_CHECKING, Any, ClassVar, Final as TFinal, Generic, TypeVar
8
+
9
+ import trio
10
+
11
+ from . import _sync
12
+ from ._highlevel_generic import aclose_forcefully
13
+ from ._util import ConflictDetector, final
14
+ from .abc import Listener, Stream
15
+
16
+ if TYPE_CHECKING:
17
+ from collections.abc import Awaitable, Callable
18
+
19
+ from typing_extensions import TypeVarTuple, Unpack
20
+
21
+ Ts = TypeVarTuple("Ts")
22
+
23
+ # General theory of operation:
24
+ #
25
+ # We implement an API that closely mirrors the stdlib ssl module's blocking
26
+ # API, and we do it using the stdlib ssl module's non-blocking in-memory API.
27
+ # The stdlib non-blocking in-memory API is barely documented, and acts as a
28
+ # thin wrapper around openssl, whose documentation also leaves something to be
29
+ # desired. So here's the main things you need to know to understand the code
30
+ # in this file:
31
+ #
32
+ # We use an ssl.SSLObject, which exposes the four main I/O operations:
33
+ #
34
+ # - do_handshake: performs the initial handshake. Must be called once at the
35
+ # beginning of each connection; is a no-op once it's completed once.
36
+ #
37
+ # - write: takes some unencrypted data and attempts to send it to the remote
38
+ # peer.
39
+
40
+ # - read: attempts to decrypt and return some data from the remote peer.
41
+ #
42
+ # - unwrap: this is weirdly named; maybe it helps to realize that the thing it
43
+ # wraps is called SSL_shutdown. It sends a cryptographically signed message
44
+ # saying "I'm closing this connection now", and then waits to receive the
45
+ # same from the remote peer (unless we already received one, in which case
46
+ # it returns immediately).
47
+ #
48
+ # All of these operations read and write from some in-memory buffers called
49
+ # "BIOs", which are an opaque OpenSSL-specific object that's basically
50
+ # semantically equivalent to a Python bytearray. When they want to send some
51
+ # bytes to the remote peer, they append them to the outgoing BIO, and when
52
+ # they want to receive some bytes from the remote peer, they try to pull them
53
+ # out of the incoming BIO. "Sending" always succeeds, because the outgoing BIO
54
+ # can always be extended to hold more data. "Receiving" acts sort of like a
55
+ # non-blocking socket: it might manage to get some data immediately, or it
56
+ # might fail and need to be tried again later. We can also directly add or
57
+ # remove data from the BIOs whenever we want.
58
+ #
59
+ # Now the problem is that while these I/O operations are opaque atomic
60
+ # operations from the point of view of us calling them, under the hood they
61
+ # might require some arbitrary sequence of sends and receives from the remote
62
+ # peer. This is particularly true for do_handshake, which generally requires a
63
+ # few round trips, but it's also true for write and read, due to an evil thing
64
+ # called "renegotiation".
65
+ #
66
+ # Renegotiation is the process by which one of the peers might arbitrarily
67
+ # decide to redo the handshake at any time. Did I mention it's evil? It's
68
+ # pretty evil, and almost universally hated. The HTTP/2 spec forbids the use
69
+ # of TLS renegotiation for HTTP/2 connections. TLS 1.3 removes it from the
70
+ # protocol entirely. It's impossible to trigger a renegotiation if using
71
+ # Python's ssl module. OpenSSL's renegotiation support is pretty buggy [1].
72
+ # Nonetheless, it does get used in real life, mostly in two cases:
73
+ #
74
+ # 1) Normally in TLS 1.2 and below, when the client side of a connection wants
75
+ # to present a certificate to prove their identity, that certificate gets sent
76
+ # in plaintext. This is bad, because it means that anyone eavesdropping can
77
+ # see who's connecting – it's like sending your username in plain text. Not as
78
+ # bad as sending your password in plain text, but still, pretty bad. However,
79
+ # renegotiations *are* encrypted. So as a workaround, it's not uncommon for
80
+ # systems that want to use client certificates to first do an anonymous
81
+ # handshake, and then to turn around and do a second handshake (=
82
+ # renegotiation) and this time ask for a client cert. Or sometimes this is
83
+ # done on a case-by-case basis, e.g. a web server might accept a connection,
84
+ # read the request, and then once it sees the page you're asking for it might
85
+ # stop and ask you for a certificate.
86
+ #
87
+ # 2) In principle the same TLS connection can be used for an arbitrarily long
88
+ # time, and might transmit arbitrarily large amounts of data. But this creates
89
+ # a cryptographic problem: an attacker who has access to arbitrarily large
90
+ # amounts of data that's all encrypted using the same key may eventually be
91
+ # able to use this to figure out the key. Is this a real practical problem? I
92
+ # have no idea, I'm not a cryptographer. In any case, some people worry that
93
+ # it's a problem, so their TLS libraries are designed to automatically trigger
94
+ # a renegotiation every once in a while on some sort of timer.
95
+ #
96
+ # The end result is that you might be going along, minding your own business,
97
+ # and then *bam*! a wild renegotiation appears! And you just have to cope.
98
+ #
99
+ # The reason that coping with renegotiations is difficult is that some
100
+ # unassuming "read" or "write" call might find itself unable to progress until
101
+ # it does a handshake, which remember is a process with multiple round
102
+ # trips. So read might have to send data, and write might have to receive
103
+ # data, and this might happen multiple times. And some of those attempts might
104
+ # fail because there isn't any data yet, and need to be retried. Managing all
105
+ # this is pretty complicated.
106
+ #
107
+ # Here's how openssl (and thus the stdlib ssl module) handle this. All of the
108
+ # I/O operations above follow the same rules. When you call one of them:
109
+ #
110
+ # - it might write some data to the outgoing BIO
111
+ # - it might read some data from the incoming BIO
112
+ # - it might raise SSLWantReadError if it can't complete without reading more
113
+ # data from the incoming BIO. This is important: the "read" in ReadError
114
+ # refers to reading from the *underlying* stream.
115
+ # - (and in principle it might raise SSLWantWriteError too, but that never
116
+ # happens when using memory BIOs, so never mind)
117
+ #
118
+ # If it doesn't raise an error, then the operation completed successfully
119
+ # (though we still need to take any outgoing data out of the memory buffer and
120
+ # put it onto the wire). If it *does* raise an error, then we need to retry
121
+ # *exactly that method call* later – in particular, if a 'write' failed, we
122
+ # need to try again later *with the same data*, because openssl might have
123
+ # already committed some of the initial parts of our data to its output even
124
+ # though it didn't tell us that, and has remembered that the next time we call
125
+ # write it needs to skip the first 1024 bytes or whatever it is. (Well,
126
+ # technically, we're actually allowed to call 'write' again with a data buffer
127
+ # which is the same as our old one PLUS some extra stuff added onto the end,
128
+ # but in Trio that never comes up so never mind.)
129
+ #
130
+ # There are some people online who claim that once you've gotten a Want*Error
131
+ # then the *very next call* you make to openssl *must* be the same as the
132
+ # previous one. I'm pretty sure those people are wrong. In particular, it's
133
+ # okay to call write, get a WantReadError, and then call read a few times;
134
+ # it's just that *the next time you call write*, it has to be with the same
135
+ # data.
136
+ #
137
+ # One final wrinkle: we want our SSLStream to support full-duplex operation,
138
+ # i.e. it should be possible for one task to be calling send_all while another
139
+ # task is calling receive_some. But renegotiation makes this a big hassle, because
140
+ # even if SSLStream's restricts themselves to one task calling send_all and one
141
+ # task calling receive_some, those two tasks might end up both wanting to call
142
+ # send_all, or both to call receive_some at the same time *on the underlying
143
+ # stream*. So we have to do some careful locking to hide this problem from our
144
+ # users.
145
+ #
146
+ # (Renegotiation is evil.)
147
+ #
148
+ # So our basic strategy is to define a single helper method called "_retry",
149
+ # which has generic logic for dealing with SSLWantReadError, pushing data from
150
+ # the outgoing BIO to the wire, reading data from the wire to the incoming
151
+ # BIO, retrying an I/O call until it works, and synchronizing with other tasks
152
+ # that might be calling _retry concurrently. Basically it takes an SSLObject
153
+ # non-blocking in-memory method and converts it into a Trio async blocking
154
+ # method. _retry is only about 30 lines of code, but all these cases
155
+ # multiplied by concurrent calls make it extremely tricky, so there are lots
156
+ # of comments down below on the details, and a really extensive test suite in
157
+ # test_ssl.py. And now you know *why* it's so tricky, and can probably
158
+ # understand how it works.
159
+ #
160
+ # [1] https://rt.openssl.org/Ticket/Display.html?id=3712
161
+
162
+ # XX how closely should we match the stdlib API?
163
+ # - maybe suppress_ragged_eofs=False is a better default?
164
+ # - maybe check crypto folks for advice?
165
+ # - this is also interesting: https://bugs.python.org/issue8108#msg102867
166
+
167
+ # Definitely keep an eye on Cory's TLS API ideas on security-sig etc.
168
+
169
+ # XX document behavior on cancellation/error (i.e.: all is lost abandon
170
+ # stream)
171
+ # docs will need to make very clear that this is different from all the other
172
+ # cancellations in core Trio
173
+
174
+
175
+ T = TypeVar("T")
176
+
177
+ ################################################################
178
+ # SSLStream
179
+ ################################################################
180
+
181
+ # Ideally, when the user calls SSLStream.receive_some() with no argument, then
182
+ # we should do exactly one call to self.transport_stream.receive_some(),
183
+ # decrypt everything we got, and return it. Unfortunately, the way openssl's
184
+ # API works, we have to pick how much data we want to allow when we call
185
+ # read(), and then it (potentially) triggers a call to
186
+ # transport_stream.receive_some(). So at the time we pick the amount of data
187
+ # to decrypt, we don't know how much data we've read. As a simple heuristic,
188
+ # we record the max amount of data returned by previous calls to
189
+ # transport_stream.receive_some(), and we use that for future calls to read().
190
+ # But what do we use for the very first call? That's what this constant sets.
191
+ #
192
+ # Note that the value passed to read() is a limit on the amount of
193
+ # *decrypted* data, but we can only see the size of the *encrypted* data
194
+ # returned by transport_stream.receive_some(). TLS adds a small amount of
195
+ # framing overhead, and TLS compression is rarely used these days because it's
196
+ # insecure. So the size of the encrypted data should be a slight over-estimate
197
+ # of the size of the decrypted data, which is exactly what we want.
198
+ #
199
+ # The specific value is not really based on anything; it might be worth tuning
200
+ # at some point. But, if you have an TCP connection with the typical 1500 byte
201
+ # MTU and an initial window of 10 (see RFC 6928), then the initial burst of
202
+ # data will be limited to ~15000 bytes (or a bit less due to IP-level framing
203
+ # overhead), so this is chosen to be larger than that.
204
+ STARTING_RECEIVE_SIZE: TFinal = 16384
205
+
206
+
207
+ def _is_eof(exc: BaseException | None) -> bool:
208
+ # There appears to be a bug on Python 3.10, where SSLErrors
209
+ # aren't properly translated into SSLEOFErrors.
210
+ # This stringly-typed error check is borrowed from the AnyIO
211
+ # project.
212
+ return isinstance(exc, _stdlib_ssl.SSLEOFError) or (
213
+ "UNEXPECTED_EOF_WHILE_READING" in getattr(exc, "strerror", ())
214
+ )
215
+
216
+
217
+ class NeedHandshakeError(Exception):
218
+ """Some :class:`SSLStream` methods can't return any meaningful data until
219
+ after the handshake. If you call them before the handshake, they raise
220
+ this error.
221
+
222
+ """
223
+
224
+
225
+ class _Once:
226
+ __slots__ = ("_afn", "_args", "_done", "started")
227
+
228
+ def __init__(
229
+ self,
230
+ afn: Callable[[*Ts], Awaitable[object]],
231
+ *args: Unpack[Ts],
232
+ ) -> None:
233
+ self._afn = afn
234
+ self._args = args
235
+ self.started = False
236
+ self._done = _sync.Event()
237
+
238
+ async def ensure(self, *, checkpoint: bool) -> None:
239
+ if not self.started:
240
+ self.started = True
241
+ await self._afn(*self._args)
242
+ self._done.set()
243
+ elif not checkpoint and self._done.is_set():
244
+ return
245
+ else:
246
+ await self._done.wait()
247
+
248
+ @property
249
+ def done(self) -> bool:
250
+ return bool(self._done.is_set())
251
+
252
+
253
+ _State = _Enum("_State", ["OK", "BROKEN", "CLOSED"])
254
+
255
+ # invariant
256
+ T_Stream = TypeVar("T_Stream", bound=Stream)
257
+
258
+
259
+ @final
260
+ class SSLStream(Stream, Generic[T_Stream]):
261
+ r"""Encrypted communication using SSL/TLS.
262
+
263
+ :class:`SSLStream` wraps an arbitrary :class:`~trio.abc.Stream`, and
264
+ allows you to perform encrypted communication over it using the usual
265
+ :class:`~trio.abc.Stream` interface. You pass regular data to
266
+ :meth:`send_all`, then it encrypts it and sends the encrypted data on the
267
+ underlying :class:`~trio.abc.Stream`; :meth:`receive_some` takes encrypted
268
+ data out of the underlying :class:`~trio.abc.Stream` and decrypts it
269
+ before returning it.
270
+
271
+ You should read the standard library's :mod:`ssl` documentation carefully
272
+ before attempting to use this class, and probably other general
273
+ documentation on SSL/TLS as well. SSL/TLS is subtle and quick to
274
+ anger. Really. I'm not kidding.
275
+
276
+ Args:
277
+ transport_stream (~trio.abc.Stream): The stream used to transport
278
+ encrypted data. Required.
279
+
280
+ ssl_context (~ssl.SSLContext): The :class:`~ssl.SSLContext` used for
281
+ this connection. Required. Usually created by calling
282
+ :func:`ssl.create_default_context`.
283
+
284
+ server_hostname (str, bytes, or None): The name of the server being
285
+ connected to. Used for `SNI
286
+ <https://en.wikipedia.org/wiki/Server_Name_Indication>`__ and for
287
+ validating the server's certificate (if hostname checking is
288
+ enabled). This is effectively mandatory for clients, and actually
289
+ mandatory if ``ssl_context.check_hostname`` is ``True``.
290
+
291
+ server_side (bool): Whether this stream is acting as a client or
292
+ server. Defaults to False, i.e. client mode.
293
+
294
+ https_compatible (bool): There are two versions of SSL/TLS commonly
295
+ encountered in the wild: the standard version, and the version used
296
+ for HTTPS (HTTP-over-SSL/TLS).
297
+
298
+ Standard-compliant SSL/TLS implementations always send a
299
+ cryptographically signed ``close_notify`` message before closing the
300
+ connection. This is important because if the underlying transport
301
+ were simply closed, then there wouldn't be any way for the other
302
+ side to know whether the connection was intentionally closed by the
303
+ peer that they negotiated a cryptographic connection to, or by some
304
+ `man-in-the-middle
305
+ <https://en.wikipedia.org/wiki/Man-in-the-middle_attack>`__ attacker
306
+ who can't manipulate the cryptographic stream, but can manipulate
307
+ the transport layer (a so-called "truncation attack").
308
+
309
+ However, this part of the standard is widely ignored by real-world
310
+ HTTPS implementations, which means that if you want to interoperate
311
+ with them, then you NEED to ignore it too.
312
+
313
+ Fortunately this isn't as bad as it sounds, because the HTTP
314
+ protocol already includes its own equivalent of ``close_notify``, so
315
+ doing this again at the SSL/TLS level is redundant. But not all
316
+ protocols do! Therefore, by default Trio implements the safer
317
+ standard-compliant version (``https_compatible=False``). But if
318
+ you're speaking HTTPS or some other protocol where
319
+ ``close_notify``\s are commonly skipped, then you should set
320
+ ``https_compatible=True``; with this setting, Trio will neither
321
+ expect nor send ``close_notify`` messages.
322
+
323
+ If you have code that was written to use :class:`ssl.SSLSocket` and
324
+ now you're porting it to Trio, then it may be useful to know that a
325
+ difference between :class:`SSLStream` and :class:`ssl.SSLSocket` is
326
+ that :class:`~ssl.SSLSocket` implements the
327
+ ``https_compatible=True`` behavior by default.
328
+
329
+ Attributes:
330
+ transport_stream (trio.abc.Stream): The underlying transport stream
331
+ that was passed to ``__init__``. An example of when this would be
332
+ useful is if you're using :class:`SSLStream` over a
333
+ :class:`~trio.SocketStream` and want to call the
334
+ :class:`~trio.SocketStream`'s :meth:`~trio.SocketStream.setsockopt`
335
+ method.
336
+
337
+ Internally, this class is implemented using an instance of
338
+ :class:`ssl.SSLObject`, and all of :class:`~ssl.SSLObject`'s methods and
339
+ attributes are re-exported as methods and attributes on this class.
340
+ However, there is one difference: :class:`~ssl.SSLObject` has several
341
+ methods that return information about the encrypted connection, like
342
+ :meth:`~ssl.SSLSocket.cipher` or
343
+ :meth:`~ssl.SSLSocket.selected_alpn_protocol`. If you call them before the
344
+ handshake, when they can't possibly return useful data, then
345
+ :class:`ssl.SSLObject` returns None, but :class:`trio.SSLStream`
346
+ raises :exc:`NeedHandshakeError`.
347
+
348
+ This also means that if you register a SNI callback using
349
+ `~ssl.SSLContext.sni_callback`, then the first argument your callback
350
+ receives will be a :class:`ssl.SSLObject`.
351
+
352
+ """
353
+
354
+ # Note: any new arguments here should likely also be added to
355
+ # SSLListener.__init__, and maybe the open_ssl_over_tcp_* helpers.
356
+ def __init__(
357
+ self,
358
+ transport_stream: T_Stream,
359
+ ssl_context: _stdlib_ssl.SSLContext,
360
+ *,
361
+ server_hostname: str | bytes | None = None,
362
+ server_side: bool = False,
363
+ https_compatible: bool = False,
364
+ ) -> None:
365
+ self.transport_stream: T_Stream = transport_stream
366
+ self._state = _State.OK
367
+ self._https_compatible = https_compatible
368
+ self._outgoing = _stdlib_ssl.MemoryBIO()
369
+ self._delayed_outgoing: bytes | None = None
370
+ self._incoming = _stdlib_ssl.MemoryBIO()
371
+ self._ssl_object = ssl_context.wrap_bio(
372
+ self._incoming,
373
+ self._outgoing,
374
+ server_side=server_side,
375
+ server_hostname=server_hostname,
376
+ )
377
+ # Tracks whether we've already done the initial handshake
378
+ self._handshook = _Once(self._do_handshake)
379
+
380
+ # These are used to synchronize access to self.transport_stream
381
+ self._inner_send_lock = _sync.StrictFIFOLock()
382
+ self._inner_recv_count = 0
383
+ self._inner_recv_lock = _sync.Lock()
384
+
385
+ # These are used to make sure that our caller doesn't attempt to make
386
+ # multiple concurrent calls to send_all/wait_send_all_might_not_block
387
+ # or to receive_some.
388
+ self._outer_send_conflict_detector = ConflictDetector(
389
+ "another task is currently sending data on this SSLStream",
390
+ )
391
+ self._outer_recv_conflict_detector = ConflictDetector(
392
+ "another task is currently receiving data on this SSLStream",
393
+ )
394
+
395
+ self._estimated_receive_size = STARTING_RECEIVE_SIZE
396
+
397
+ _forwarded: ClassVar = {
398
+ "context",
399
+ "server_side",
400
+ "server_hostname",
401
+ "session",
402
+ "session_reused",
403
+ "getpeercert",
404
+ "selected_npn_protocol",
405
+ "cipher",
406
+ "shared_ciphers",
407
+ "compression",
408
+ "pending",
409
+ "get_channel_binding",
410
+ "selected_alpn_protocol",
411
+ "version",
412
+ }
413
+
414
+ _after_handshake: ClassVar = {
415
+ "session_reused",
416
+ "getpeercert",
417
+ "selected_npn_protocol",
418
+ "cipher",
419
+ "shared_ciphers",
420
+ "compression",
421
+ "get_channel_binding",
422
+ "selected_alpn_protocol",
423
+ "version",
424
+ }
425
+
426
+ def __getattr__( # type: ignore[explicit-any]
427
+ self,
428
+ name: str,
429
+ ) -> Any:
430
+ if name in self._forwarded:
431
+ if name in self._after_handshake and not self._handshook.done:
432
+ raise NeedHandshakeError(f"call do_handshake() before calling {name!r}")
433
+
434
+ return getattr(self._ssl_object, name)
435
+ else:
436
+ raise AttributeError(name)
437
+
438
+ def __setattr__(self, name: str, value: object) -> None:
439
+ if name in self._forwarded:
440
+ setattr(self._ssl_object, name, value)
441
+ else:
442
+ super().__setattr__(name, value)
443
+
444
+ def __dir__(self) -> list[str]:
445
+ return list(super().__dir__()) + list(self._forwarded)
446
+
447
+ def _check_status(self) -> None:
448
+ if self._state is _State.OK:
449
+ return
450
+ elif self._state is _State.BROKEN:
451
+ raise trio.BrokenResourceError
452
+ elif self._state is _State.CLOSED:
453
+ raise trio.ClosedResourceError
454
+ else: # pragma: no cover
455
+ raise AssertionError()
456
+
457
+ # This is probably the single trickiest function in Trio. It has lots of
458
+ # comments, though, just make sure to think carefully if you ever have to
459
+ # touch it. The big comment at the top of this file will help explain
460
+ # too.
461
+ async def _retry(
462
+ self,
463
+ fn: Callable[[*Ts], T],
464
+ *args: Unpack[Ts],
465
+ ignore_want_read: bool = False,
466
+ is_handshake: bool = False,
467
+ ) -> T | None:
468
+ await trio.lowlevel.checkpoint_if_cancelled()
469
+ yielded = False
470
+ finished = False
471
+ while not finished:
472
+ # WARNING: this code needs to be very careful with when it
473
+ # calls 'await'! There might be multiple tasks calling this
474
+ # function at the same time trying to do different operations,
475
+ # so we need to be careful to:
476
+ #
477
+ # 1) interact with the SSLObject, then
478
+ # 2) await on exactly one thing that lets us make forward
479
+ # progress, then
480
+ # 3) loop or exit
481
+ #
482
+ # In particular we don't want to yield while interacting with
483
+ # the SSLObject (because it's shared state, so someone else
484
+ # might come in and mess with it while we're suspended), and
485
+ # we don't want to yield *before* starting the operation that
486
+ # will help us make progress, because then someone else might
487
+ # come in and leapfrog us.
488
+
489
+ # Call the SSLObject method, and get its result.
490
+ #
491
+ # NB: despite what the docs say, SSLWantWriteError can't
492
+ # happen – "Writes to memory BIOs will always succeed if
493
+ # memory is available: that is their size can grow
494
+ # indefinitely."
495
+ # https://wiki.openssl.org/index.php/Manual:BIO_s_mem(3)
496
+ want_read = False
497
+ ret = None
498
+ try:
499
+ ret = fn(*args)
500
+ except _stdlib_ssl.SSLWantReadError:
501
+ want_read = True
502
+ except (_stdlib_ssl.SSLError, _stdlib_ssl.CertificateError) as exc:
503
+ self._state = _State.BROKEN
504
+ raise trio.BrokenResourceError from exc
505
+ else:
506
+ finished = True
507
+ if ignore_want_read:
508
+ want_read = False
509
+ finished = True
510
+ to_send = self._outgoing.read()
511
+
512
+ # Some versions of SSL_do_handshake have a bug in how they handle
513
+ # the TLS 1.3 handshake on the server side: after the handshake
514
+ # finishes, they automatically send session tickets, even though
515
+ # the client may not be expecting data to arrive at this point and
516
+ # sending it could cause a deadlock or lost data. This applies at
517
+ # least to OpenSSL 1.1.1c and earlier, and the OpenSSL devs
518
+ # currently have no plans to fix it:
519
+ #
520
+ # https://github.com/openssl/openssl/issues/7948
521
+ # https://github.com/openssl/openssl/issues/7967
522
+ #
523
+ # The correct behavior is to wait to send session tickets on the
524
+ # first call to SSL_write. (This is what BoringSSL does.) So, we
525
+ # use a heuristic to detect when OpenSSL has tried to send session
526
+ # tickets, and we manually delay sending them until the
527
+ # appropriate moment. For more discussion see:
528
+ #
529
+ # https://github.com/python-trio/trio/issues/819#issuecomment-517529763
530
+ if (
531
+ is_handshake
532
+ and not want_read
533
+ and self._ssl_object.server_side
534
+ and self._ssl_object.version() == "TLSv1.3"
535
+ ):
536
+ assert self._delayed_outgoing is None
537
+ self._delayed_outgoing = to_send
538
+ to_send = b""
539
+
540
+ # Outputs from the above code block are:
541
+ #
542
+ # - to_send: bytestring; if non-empty then we need to send
543
+ # this data to make forward progress
544
+ #
545
+ # - want_read: True if we need to receive_some some data to make
546
+ # forward progress
547
+ #
548
+ # - finished: False means that we need to retry the call to
549
+ # fn(*args) again, after having pushed things forward. True
550
+ # means we still need to do whatever was said (in particular
551
+ # send any data in to_send), but once we do then we're
552
+ # done.
553
+ #
554
+ # - ret: the operation's return value. (Meaningless unless
555
+ # finished is True.)
556
+ #
557
+ # Invariant: want_read and finished can't both be True at the
558
+ # same time.
559
+ #
560
+ # Now we need to move things forward. There are two things we
561
+ # might have to do, and any given operation might require
562
+ # either, both, or neither to proceed:
563
+ #
564
+ # - send the data in to_send
565
+ #
566
+ # - receive_some some data and put it into the incoming BIO
567
+ #
568
+ # Our strategy is: if there's data to send, send it;
569
+ # *otherwise* if there's data to receive_some, receive_some it.
570
+ #
571
+ # If both need to happen, then we only send. Why? Well, we
572
+ # know that *right now* we have to both send and receive_some
573
+ # before the operation can complete. But as soon as we yield,
574
+ # that information becomes potentially stale – e.g. while
575
+ # we're sending, some other task might go and receive_some the
576
+ # data we need and put it into the incoming BIO. And if it
577
+ # does, then we *definitely don't* want to do a receive_some –
578
+ # there might not be any more data coming, and we'd deadlock!
579
+ # We could do something tricky to keep track of whether a
580
+ # receive_some happens while we're sending, but the case where
581
+ # we have to do both is very unusual (only during a
582
+ # renegotiation), so it's better to keep things simple. So we
583
+ # do just one potentially-blocking operation, then check again
584
+ # for fresh information.
585
+ #
586
+ # And we prioritize sending over receiving because, if there
587
+ # are multiple tasks that want to receive_some, then it
588
+ # doesn't matter what order they go in. But if there are
589
+ # multiple tasks that want to send, then they each have
590
+ # different data, and the data needs to get put onto the wire
591
+ # in the same order that it was retrieved from the outgoing
592
+ # BIO. So if we have data to send, that *needs* to be the
593
+ # *very* *next* *thing* we do, to make sure no-one else sneaks
594
+ # in before us. Or if we can't send immediately because
595
+ # someone else is, then we at least need to get in line
596
+ # immediately.
597
+ if to_send:
598
+ # NOTE: This relies on the lock being strict FIFO fair!
599
+ async with self._inner_send_lock:
600
+ yielded = True
601
+ try:
602
+ if self._delayed_outgoing is not None:
603
+ to_send = self._delayed_outgoing + to_send
604
+ self._delayed_outgoing = None
605
+ await self.transport_stream.send_all(to_send)
606
+ except:
607
+ # Some unknown amount of our data got sent, and we
608
+ # don't know how much. This stream is doomed.
609
+ self._state = _State.BROKEN
610
+ raise
611
+ elif want_read:
612
+ # It's possible that someone else is already blocked in
613
+ # transport_stream.receive_some. If so then we want to
614
+ # wait for them to finish, but we don't want to call
615
+ # transport_stream.receive_some again ourselves; we just
616
+ # want to loop around and check if their contribution
617
+ # helped anything. So we make a note of how many times
618
+ # some task has been through here before taking the lock,
619
+ # and if it's changed by the time we get the lock, then we
620
+ # skip calling transport_stream.receive_some and loop
621
+ # around immediately.
622
+ recv_count = self._inner_recv_count
623
+ async with self._inner_recv_lock:
624
+ yielded = True
625
+ if recv_count == self._inner_recv_count:
626
+ data = await self.transport_stream.receive_some()
627
+ if not data:
628
+ self._incoming.write_eof()
629
+ else:
630
+ self._estimated_receive_size = max(
631
+ self._estimated_receive_size,
632
+ len(data),
633
+ )
634
+ self._incoming.write(data)
635
+ self._inner_recv_count += 1
636
+ if not yielded:
637
+ await trio.lowlevel.cancel_shielded_checkpoint()
638
+ return ret
639
+
640
+ async def _do_handshake(self) -> None:
641
+ try:
642
+ await self._retry(self._ssl_object.do_handshake, is_handshake=True)
643
+ except:
644
+ self._state = _State.BROKEN
645
+ raise
646
+
647
+ async def do_handshake(self) -> None:
648
+ """Ensure that the initial handshake has completed.
649
+
650
+ The SSL protocol requires an initial handshake to exchange
651
+ certificates, select cryptographic keys, and so forth, before any
652
+ actual data can be sent or received. You don't have to call this
653
+ method; if you don't, then :class:`SSLStream` will automatically
654
+ perform the handshake as needed, the first time you try to send or
655
+ receive data. But if you want to trigger it manually – for example,
656
+ because you want to look at the peer's certificate before you start
657
+ talking to them – then you can call this method.
658
+
659
+ If the initial handshake is already in progress in another task, this
660
+ waits for it to complete and then returns.
661
+
662
+ If the initial handshake has already completed, this returns
663
+ immediately without doing anything (except executing a checkpoint).
664
+
665
+ .. warning:: If this method is cancelled, then it may leave the
666
+ :class:`SSLStream` in an unusable state. If this happens then any
667
+ future attempt to use the object will raise
668
+ :exc:`trio.BrokenResourceError`.
669
+
670
+ """
671
+ self._check_status()
672
+ await self._handshook.ensure(checkpoint=True)
673
+
674
+ # Most things work if we don't explicitly force do_handshake to be called
675
+ # before calling receive_some or send_all, because openssl will
676
+ # automatically perform the handshake on the first SSL_{read,write}
677
+ # call. BUT, allowing openssl to do this will disable Python's hostname
678
+ # checking!!! See:
679
+ # https://bugs.python.org/issue30141
680
+ # So we *definitely* have to make sure that do_handshake is called
681
+ # before doing anything else.
682
+ async def receive_some(self, max_bytes: int | None = None) -> bytes | bytearray:
683
+ """Read some data from the underlying transport, decrypt it, and
684
+ return it.
685
+
686
+ See :meth:`trio.abc.ReceiveStream.receive_some` for details.
687
+
688
+ .. warning:: If this method is cancelled while the initial handshake
689
+ or a renegotiation are in progress, then it may leave the
690
+ :class:`SSLStream` in an unusable state. If this happens then any
691
+ future attempt to use the object will raise
692
+ :exc:`trio.BrokenResourceError`.
693
+
694
+ """
695
+ with self._outer_recv_conflict_detector:
696
+ self._check_status()
697
+ try:
698
+ await self._handshook.ensure(checkpoint=False)
699
+ except trio.BrokenResourceError as exc:
700
+ # For some reason, EOF before handshake sometimes raises
701
+ # SSLSyscallError instead of SSLEOFError (e.g. on my linux
702
+ # laptop, but not on appveyor). Thanks openssl.
703
+ if self._https_compatible and (
704
+ isinstance(exc.__cause__, _stdlib_ssl.SSLSyscallError)
705
+ or _is_eof(exc.__cause__)
706
+ ):
707
+ await trio.lowlevel.checkpoint()
708
+ return b""
709
+ else:
710
+ raise
711
+ if max_bytes is None:
712
+ # If we somehow have more data already in our pending buffer
713
+ # than the estimate receive size, bump up our size a bit for
714
+ # this read only.
715
+ max_bytes = max(self._estimated_receive_size, self._incoming.pending)
716
+ else:
717
+ max_bytes = _operator.index(max_bytes)
718
+ if max_bytes < 1:
719
+ raise ValueError("max_bytes must be >= 1")
720
+ try:
721
+ received = await self._retry(self._ssl_object.read, max_bytes)
722
+ assert received is not None
723
+ return received
724
+ except trio.BrokenResourceError as exc:
725
+ # This isn't quite equivalent to just returning b"" in the
726
+ # first place, because we still end up with self._state set to
727
+ # BROKEN. But that's actually fine, because after getting an
728
+ # EOF on TLS then the only thing you can do is close the
729
+ # stream, and closing doesn't care about the state.
730
+
731
+ if self._https_compatible and _is_eof(exc.__cause__):
732
+ await trio.lowlevel.checkpoint()
733
+ return b""
734
+ else:
735
+ raise
736
+
737
+ async def send_all(self, data: bytes | bytearray | memoryview) -> None:
738
+ """Encrypt some data and then send it on the underlying transport.
739
+
740
+ See :meth:`trio.abc.SendStream.send_all` for details.
741
+
742
+ .. warning:: If this method is cancelled, then it may leave the
743
+ :class:`SSLStream` in an unusable state. If this happens then any
744
+ attempt to use the object will raise
745
+ :exc:`trio.BrokenResourceError`.
746
+
747
+ """
748
+ with self._outer_send_conflict_detector:
749
+ self._check_status()
750
+ await self._handshook.ensure(checkpoint=False)
751
+ # SSLObject interprets write(b"") as an EOF for some reason, which
752
+ # is not what we want.
753
+ if not data:
754
+ await trio.lowlevel.checkpoint()
755
+ return
756
+ await self._retry(self._ssl_object.write, data)
757
+
758
+ async def unwrap(self) -> tuple[Stream, bytes | bytearray]:
759
+ """Cleanly close down the SSL/TLS encryption layer, allowing the
760
+ underlying stream to be used for unencrypted communication.
761
+
762
+ You almost certainly don't need this.
763
+
764
+ Returns:
765
+ A pair ``(transport_stream, trailing_bytes)``, where
766
+ ``transport_stream`` is the underlying transport stream, and
767
+ ``trailing_bytes`` is a byte string. Since :class:`SSLStream`
768
+ doesn't necessarily know where the end of the encrypted data will
769
+ be, it can happen that it accidentally reads too much from the
770
+ underlying stream. ``trailing_bytes`` contains this extra data; you
771
+ should process it as if it was returned from a call to
772
+ ``transport_stream.receive_some(...)``.
773
+
774
+ """
775
+ with self._outer_recv_conflict_detector, self._outer_send_conflict_detector:
776
+ self._check_status()
777
+ await self._handshook.ensure(checkpoint=False)
778
+ await self._retry(self._ssl_object.unwrap)
779
+ transport_stream = self.transport_stream
780
+ self._state = _State.CLOSED
781
+ self.transport_stream = None # type: ignore[assignment] # State is CLOSED now, nothing should use
782
+ return (transport_stream, self._incoming.read())
783
+
784
+ async def aclose(self) -> None:
785
+ """Gracefully shut down this connection, and close the underlying
786
+ transport.
787
+
788
+ If ``https_compatible`` is False (the default), then this attempts to
789
+ first send a ``close_notify`` and then close the underlying stream by
790
+ calling its :meth:`~trio.abc.AsyncResource.aclose` method.
791
+
792
+ If ``https_compatible`` is set to True, then this simply closes the
793
+ underlying stream and marks this stream as closed.
794
+
795
+ """
796
+ if self._state is _State.CLOSED:
797
+ await trio.lowlevel.checkpoint()
798
+ return
799
+ if self._state is _State.BROKEN or self._https_compatible:
800
+ self._state = _State.CLOSED
801
+ await self.transport_stream.aclose()
802
+ return
803
+ try:
804
+ # https_compatible=False, so we're in spec-compliant mode and have
805
+ # to send close_notify so that the other side gets a cryptographic
806
+ # assurance that we've called aclose. Of course, we can't do
807
+ # anything cryptographic until after we've completed the
808
+ # handshake:
809
+ await self._handshook.ensure(checkpoint=False)
810
+ # Then, we call SSL_shutdown *once*, because we want to send a
811
+ # close_notify but *not* wait for the other side to send back a
812
+ # response. In principle it would be more polite to wait for the
813
+ # other side to reply with their own close_notify. However, if
814
+ # they aren't paying attention (e.g., if they're just sending
815
+ # data and not receiving) then we will never notice our
816
+ # close_notify and we'll be waiting forever. Eventually we'll time
817
+ # out (hopefully), but it's still kind of nasty. And we can't
818
+ # require the other side to always be receiving, because (a)
819
+ # backpressure is kind of important, and (b) I bet there are
820
+ # broken TLS implementations out there that don't receive all the
821
+ # time. (Like e.g. anyone using Python ssl in synchronous mode.)
822
+ #
823
+ # The send-then-immediately-close behavior is explicitly allowed
824
+ # by the TLS specs, so we're ok on that.
825
+ #
826
+ # Subtlety: SSLObject.unwrap will immediately call it a second
827
+ # time, and the second time will raise SSLWantReadError because
828
+ # there hasn't been time for the other side to respond
829
+ # yet. (Unless they spontaneously sent a close_notify before we
830
+ # called this, and it's either already been processed or gets
831
+ # pulled out of the buffer by Python's second call.) So the way to
832
+ # do what we want is to ignore SSLWantReadError on this call.
833
+ #
834
+ # Also, because the other side might have already sent
835
+ # close_notify and closed their connection then it's possible that
836
+ # our attempt to send close_notify will raise
837
+ # BrokenResourceError. This is totally legal, and in fact can happen
838
+ # with two well-behaved Trio programs talking to each other, so we
839
+ # don't want to raise an error. So we suppress BrokenResourceError
840
+ # here. (This is safe, because literally the only thing this call
841
+ # to _retry will do is send the close_notify alert, so that's
842
+ # surely where the error comes from.)
843
+ #
844
+ # FYI in some cases this could also raise SSLSyscallError which I
845
+ # think is because SSL_shutdown is terrible. (Check out that note
846
+ # at the bottom of the man page saying that it sometimes gets
847
+ # raised spuriously.) I haven't seen this since we switched to
848
+ # immediately closing the socket, and I don't know exactly what
849
+ # conditions cause it and how to respond, so for now we're just
850
+ # letting that happen. But if you start seeing it, then hopefully
851
+ # this will give you a little head start on tracking it down,
852
+ # because whoa did this puzzle us at the 2017 PyCon sprints.
853
+ #
854
+ # Also, if someone else is blocked in send/receive, then we aren't
855
+ # going to be able to do a clean shutdown. If that happens, we'll
856
+ # just do an unclean shutdown.
857
+ with contextlib.suppress(trio.BrokenResourceError, trio.BusyResourceError):
858
+ await self._retry(self._ssl_object.unwrap, ignore_want_read=True)
859
+ except:
860
+ # Failure! Kill the stream and move on.
861
+ await aclose_forcefully(self.transport_stream)
862
+ raise
863
+ else:
864
+ # Success! Gracefully close the underlying stream.
865
+ await self.transport_stream.aclose()
866
+ finally:
867
+ self._state = _State.CLOSED
868
+
869
+ async def wait_send_all_might_not_block(self) -> None:
870
+ """See :meth:`trio.abc.SendStream.wait_send_all_might_not_block`."""
871
+ # This method's implementation is deceptively simple.
872
+ #
873
+ # First, we take the outer send lock, because of Trio's standard
874
+ # semantics that wait_send_all_might_not_block and send_all
875
+ # conflict.
876
+ with self._outer_send_conflict_detector:
877
+ self._check_status()
878
+ # Then we take the inner send lock. We know that no other tasks
879
+ # are calling self.send_all or self.wait_send_all_might_not_block,
880
+ # because we have the outer_send_lock. But! There might be another
881
+ # task calling self.receive_some -> transport_stream.send_all, in
882
+ # which case if we were to call
883
+ # transport_stream.wait_send_all_might_not_block directly we'd
884
+ # have two tasks doing write-related operations on
885
+ # transport_stream simultaneously, which is not allowed. We
886
+ # *don't* want to raise this conflict to our caller, because it's
887
+ # purely an internal affair – all they did was call
888
+ # wait_send_all_might_not_block and receive_some at the same time,
889
+ # which is totally valid. And waiting for the lock is OK, because
890
+ # a call to send_all certainly wouldn't complete while the other
891
+ # task holds the lock.
892
+ async with self._inner_send_lock:
893
+ # Now we have the lock, which creates another potential
894
+ # problem: what if a call to self.receive_some attempts to do
895
+ # transport_stream.send_all now? It'll have to wait for us to
896
+ # finish! But that's OK, because we release the lock as soon
897
+ # as the underlying stream becomes writable, and the
898
+ # self.receive_some call wasn't going to make any progress
899
+ # until then anyway.
900
+ #
901
+ # Of course, this does mean we might return *before* the
902
+ # stream is logically writable, because immediately after we
903
+ # return self.receive_some might write some data and make it
904
+ # non-writable again. But that's OK too,
905
+ # wait_send_all_might_not_block only guarantees that it
906
+ # doesn't return late.
907
+ await self.transport_stream.wait_send_all_might_not_block()
908
+
909
+
910
+ # this is necessary for Sphinx, see also `_abc.py`
911
+ SSLStream.__module__ = SSLStream.__module__.replace("._ssl", "")
912
+
913
+
914
+ @final
915
+ class SSLListener(Listener[SSLStream[T_Stream]]):
916
+ """A :class:`~trio.abc.Listener` for SSL/TLS-encrypted servers.
917
+
918
+ :class:`SSLListener` wraps around another Listener, and converts
919
+ all incoming connections to encrypted connections by wrapping them
920
+ in a :class:`SSLStream`.
921
+
922
+ Args:
923
+ transport_listener (~trio.abc.Listener): The listener whose incoming
924
+ connections will be wrapped in :class:`SSLStream`.
925
+
926
+ ssl_context (~ssl.SSLContext): The :class:`~ssl.SSLContext` that will be
927
+ used for incoming connections.
928
+
929
+ https_compatible (bool): Passed on to :class:`SSLStream`.
930
+
931
+ Attributes:
932
+ transport_listener (trio.abc.Listener): The underlying listener that was
933
+ passed to ``__init__``.
934
+
935
+ """
936
+
937
+ def __init__(
938
+ self,
939
+ transport_listener: Listener[T_Stream],
940
+ ssl_context: _stdlib_ssl.SSLContext,
941
+ *,
942
+ https_compatible: bool = False,
943
+ ) -> None:
944
+ self.transport_listener = transport_listener
945
+ self._ssl_context = ssl_context
946
+ self._https_compatible = https_compatible
947
+
948
+ async def accept(self) -> SSLStream[T_Stream]:
949
+ """Accept the next connection and wrap it in an :class:`SSLStream`.
950
+
951
+ See :meth:`trio.abc.Listener.accept` for details.
952
+
953
+ """
954
+ transport_stream = await self.transport_listener.accept()
955
+ return SSLStream(
956
+ transport_stream,
957
+ self._ssl_context,
958
+ server_side=True,
959
+ https_compatible=self._https_compatible,
960
+ )
961
+
962
+ async def aclose(self) -> None:
963
+ """Close the transport listener."""
964
+ await self.transport_listener.aclose()