sensu-plugins-mongodb-mrtrotl 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (181) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1 -0
  3. data/LICENSE +22 -0
  4. data/README.md +27 -0
  5. data/bin/check-mongodb-metric.rb +144 -0
  6. data/bin/check-mongodb-query-count.rb +267 -0
  7. data/bin/check-mongodb.py +1644 -0
  8. data/bin/check-mongodb.rb +5 -0
  9. data/bin/metrics-mongodb-replication.rb +254 -0
  10. data/bin/metrics-mongodb.rb +133 -0
  11. data/lib/bson/__init__.py +1347 -0
  12. data/lib/bson/__pycache__/__init__.cpython-310.pyc +0 -0
  13. data/lib/bson/__pycache__/_helpers.cpython-310.pyc +0 -0
  14. data/lib/bson/__pycache__/binary.cpython-310.pyc +0 -0
  15. data/lib/bson/__pycache__/code.cpython-310.pyc +0 -0
  16. data/lib/bson/__pycache__/codec_options.cpython-310.pyc +0 -0
  17. data/lib/bson/__pycache__/dbref.cpython-310.pyc +0 -0
  18. data/lib/bson/__pycache__/decimal128.cpython-310.pyc +0 -0
  19. data/lib/bson/__pycache__/errors.cpython-310.pyc +0 -0
  20. data/lib/bson/__pycache__/int64.cpython-310.pyc +0 -0
  21. data/lib/bson/__pycache__/json_util.cpython-310.pyc +0 -0
  22. data/lib/bson/__pycache__/max_key.cpython-310.pyc +0 -0
  23. data/lib/bson/__pycache__/min_key.cpython-310.pyc +0 -0
  24. data/lib/bson/__pycache__/objectid.cpython-310.pyc +0 -0
  25. data/lib/bson/__pycache__/raw_bson.cpython-310.pyc +0 -0
  26. data/lib/bson/__pycache__/regex.cpython-310.pyc +0 -0
  27. data/lib/bson/__pycache__/son.cpython-310.pyc +0 -0
  28. data/lib/bson/__pycache__/timestamp.cpython-310.pyc +0 -0
  29. data/lib/bson/__pycache__/tz_util.cpython-310.pyc +0 -0
  30. data/lib/bson/_cbson.cpython-310-x86_64-linux-gnu.so +0 -0
  31. data/lib/bson/_helpers.py +41 -0
  32. data/lib/bson/binary.py +364 -0
  33. data/lib/bson/code.py +101 -0
  34. data/lib/bson/codec_options.py +414 -0
  35. data/lib/bson/codec_options.pyi +100 -0
  36. data/lib/bson/dbref.py +133 -0
  37. data/lib/bson/decimal128.py +314 -0
  38. data/lib/bson/errors.py +35 -0
  39. data/lib/bson/int64.py +39 -0
  40. data/lib/bson/json_util.py +874 -0
  41. data/lib/bson/max_key.py +55 -0
  42. data/lib/bson/min_key.py +55 -0
  43. data/lib/bson/objectid.py +286 -0
  44. data/lib/bson/py.typed +2 -0
  45. data/lib/bson/raw_bson.py +175 -0
  46. data/lib/bson/regex.py +135 -0
  47. data/lib/bson/son.py +208 -0
  48. data/lib/bson/timestamp.py +124 -0
  49. data/lib/bson/tz_util.py +52 -0
  50. data/lib/gridfs/__init__.py +1015 -0
  51. data/lib/gridfs/__pycache__/__init__.cpython-310.pyc +0 -0
  52. data/lib/gridfs/__pycache__/errors.cpython-310.pyc +0 -0
  53. data/lib/gridfs/__pycache__/grid_file.cpython-310.pyc +0 -0
  54. data/lib/gridfs/errors.py +33 -0
  55. data/lib/gridfs/grid_file.py +907 -0
  56. data/lib/gridfs/py.typed +2 -0
  57. data/lib/pymongo/__init__.py +185 -0
  58. data/lib/pymongo/__pycache__/__init__.cpython-310.pyc +0 -0
  59. data/lib/pymongo/__pycache__/_csot.cpython-310.pyc +0 -0
  60. data/lib/pymongo/__pycache__/aggregation.cpython-310.pyc +0 -0
  61. data/lib/pymongo/__pycache__/auth.cpython-310.pyc +0 -0
  62. data/lib/pymongo/__pycache__/auth_aws.cpython-310.pyc +0 -0
  63. data/lib/pymongo/__pycache__/bulk.cpython-310.pyc +0 -0
  64. data/lib/pymongo/__pycache__/change_stream.cpython-310.pyc +0 -0
  65. data/lib/pymongo/__pycache__/client_options.cpython-310.pyc +0 -0
  66. data/lib/pymongo/__pycache__/client_session.cpython-310.pyc +0 -0
  67. data/lib/pymongo/__pycache__/collation.cpython-310.pyc +0 -0
  68. data/lib/pymongo/__pycache__/collection.cpython-310.pyc +0 -0
  69. data/lib/pymongo/__pycache__/command_cursor.cpython-310.pyc +0 -0
  70. data/lib/pymongo/__pycache__/common.cpython-310.pyc +0 -0
  71. data/lib/pymongo/__pycache__/compression_support.cpython-310.pyc +0 -0
  72. data/lib/pymongo/__pycache__/cursor.cpython-310.pyc +0 -0
  73. data/lib/pymongo/__pycache__/daemon.cpython-310.pyc +0 -0
  74. data/lib/pymongo/__pycache__/database.cpython-310.pyc +0 -0
  75. data/lib/pymongo/__pycache__/driver_info.cpython-310.pyc +0 -0
  76. data/lib/pymongo/__pycache__/encryption.cpython-310.pyc +0 -0
  77. data/lib/pymongo/__pycache__/encryption_options.cpython-310.pyc +0 -0
  78. data/lib/pymongo/__pycache__/errors.cpython-310.pyc +0 -0
  79. data/lib/pymongo/__pycache__/event_loggers.cpython-310.pyc +0 -0
  80. data/lib/pymongo/__pycache__/hello.cpython-310.pyc +0 -0
  81. data/lib/pymongo/__pycache__/helpers.cpython-310.pyc +0 -0
  82. data/lib/pymongo/__pycache__/max_staleness_selectors.cpython-310.pyc +0 -0
  83. data/lib/pymongo/__pycache__/message.cpython-310.pyc +0 -0
  84. data/lib/pymongo/__pycache__/mongo_client.cpython-310.pyc +0 -0
  85. data/lib/pymongo/__pycache__/monitor.cpython-310.pyc +0 -0
  86. data/lib/pymongo/__pycache__/monitoring.cpython-310.pyc +0 -0
  87. data/lib/pymongo/__pycache__/network.cpython-310.pyc +0 -0
  88. data/lib/pymongo/__pycache__/ocsp_cache.cpython-310.pyc +0 -0
  89. data/lib/pymongo/__pycache__/ocsp_support.cpython-310.pyc +0 -0
  90. data/lib/pymongo/__pycache__/operations.cpython-310.pyc +0 -0
  91. data/lib/pymongo/__pycache__/periodic_executor.cpython-310.pyc +0 -0
  92. data/lib/pymongo/__pycache__/pool.cpython-310.pyc +0 -0
  93. data/lib/pymongo/__pycache__/pyopenssl_context.cpython-310.pyc +0 -0
  94. data/lib/pymongo/__pycache__/read_concern.cpython-310.pyc +0 -0
  95. data/lib/pymongo/__pycache__/read_preferences.cpython-310.pyc +0 -0
  96. data/lib/pymongo/__pycache__/response.cpython-310.pyc +0 -0
  97. data/lib/pymongo/__pycache__/results.cpython-310.pyc +0 -0
  98. data/lib/pymongo/__pycache__/saslprep.cpython-310.pyc +0 -0
  99. data/lib/pymongo/__pycache__/server.cpython-310.pyc +0 -0
  100. data/lib/pymongo/__pycache__/server_api.cpython-310.pyc +0 -0
  101. data/lib/pymongo/__pycache__/server_description.cpython-310.pyc +0 -0
  102. data/lib/pymongo/__pycache__/server_selectors.cpython-310.pyc +0 -0
  103. data/lib/pymongo/__pycache__/server_type.cpython-310.pyc +0 -0
  104. data/lib/pymongo/__pycache__/settings.cpython-310.pyc +0 -0
  105. data/lib/pymongo/__pycache__/socket_checker.cpython-310.pyc +0 -0
  106. data/lib/pymongo/__pycache__/srv_resolver.cpython-310.pyc +0 -0
  107. data/lib/pymongo/__pycache__/ssl_context.cpython-310.pyc +0 -0
  108. data/lib/pymongo/__pycache__/ssl_support.cpython-310.pyc +0 -0
  109. data/lib/pymongo/__pycache__/topology.cpython-310.pyc +0 -0
  110. data/lib/pymongo/__pycache__/topology_description.cpython-310.pyc +0 -0
  111. data/lib/pymongo/__pycache__/typings.cpython-310.pyc +0 -0
  112. data/lib/pymongo/__pycache__/uri_parser.cpython-310.pyc +0 -0
  113. data/lib/pymongo/__pycache__/write_concern.cpython-310.pyc +0 -0
  114. data/lib/pymongo/_cmessage.cpython-310-x86_64-linux-gnu.so +0 -0
  115. data/lib/pymongo/_csot.py +118 -0
  116. data/lib/pymongo/aggregation.py +229 -0
  117. data/lib/pymongo/auth.py +549 -0
  118. data/lib/pymongo/auth_aws.py +94 -0
  119. data/lib/pymongo/bulk.py +513 -0
  120. data/lib/pymongo/change_stream.py +457 -0
  121. data/lib/pymongo/client_options.py +302 -0
  122. data/lib/pymongo/client_session.py +1112 -0
  123. data/lib/pymongo/collation.py +224 -0
  124. data/lib/pymongo/collection.py +3204 -0
  125. data/lib/pymongo/command_cursor.py +353 -0
  126. data/lib/pymongo/common.py +984 -0
  127. data/lib/pymongo/compression_support.py +149 -0
  128. data/lib/pymongo/cursor.py +1345 -0
  129. data/lib/pymongo/daemon.py +141 -0
  130. data/lib/pymongo/database.py +1202 -0
  131. data/lib/pymongo/driver_info.py +42 -0
  132. data/lib/pymongo/encryption.py +884 -0
  133. data/lib/pymongo/encryption_options.py +221 -0
  134. data/lib/pymongo/errors.py +365 -0
  135. data/lib/pymongo/event_loggers.py +221 -0
  136. data/lib/pymongo/hello.py +219 -0
  137. data/lib/pymongo/helpers.py +259 -0
  138. data/lib/pymongo/max_staleness_selectors.py +114 -0
  139. data/lib/pymongo/message.py +1440 -0
  140. data/lib/pymongo/mongo_client.py +2144 -0
  141. data/lib/pymongo/monitor.py +440 -0
  142. data/lib/pymongo/monitoring.py +1801 -0
  143. data/lib/pymongo/network.py +311 -0
  144. data/lib/pymongo/ocsp_cache.py +87 -0
  145. data/lib/pymongo/ocsp_support.py +372 -0
  146. data/lib/pymongo/operations.py +507 -0
  147. data/lib/pymongo/periodic_executor.py +183 -0
  148. data/lib/pymongo/pool.py +1660 -0
  149. data/lib/pymongo/py.typed +2 -0
  150. data/lib/pymongo/pyopenssl_context.py +383 -0
  151. data/lib/pymongo/read_concern.py +75 -0
  152. data/lib/pymongo/read_preferences.py +609 -0
  153. data/lib/pymongo/response.py +109 -0
  154. data/lib/pymongo/results.py +217 -0
  155. data/lib/pymongo/saslprep.py +113 -0
  156. data/lib/pymongo/server.py +247 -0
  157. data/lib/pymongo/server_api.py +170 -0
  158. data/lib/pymongo/server_description.py +285 -0
  159. data/lib/pymongo/server_selectors.py +153 -0
  160. data/lib/pymongo/server_type.py +32 -0
  161. data/lib/pymongo/settings.py +159 -0
  162. data/lib/pymongo/socket_checker.py +104 -0
  163. data/lib/pymongo/srv_resolver.py +126 -0
  164. data/lib/pymongo/ssl_context.py +39 -0
  165. data/lib/pymongo/ssl_support.py +99 -0
  166. data/lib/pymongo/topology.py +890 -0
  167. data/lib/pymongo/topology_description.py +639 -0
  168. data/lib/pymongo/typings.py +39 -0
  169. data/lib/pymongo/uri_parser.py +624 -0
  170. data/lib/pymongo/write_concern.py +129 -0
  171. data/lib/pymongo-4.2.0.dist-info/INSTALLER +1 -0
  172. data/lib/pymongo-4.2.0.dist-info/LICENSE +201 -0
  173. data/lib/pymongo-4.2.0.dist-info/METADATA +250 -0
  174. data/lib/pymongo-4.2.0.dist-info/RECORD +167 -0
  175. data/lib/pymongo-4.2.0.dist-info/REQUESTED +0 -0
  176. data/lib/pymongo-4.2.0.dist-info/WHEEL +6 -0
  177. data/lib/pymongo-4.2.0.dist-info/top_level.txt +3 -0
  178. data/lib/sensu-plugins-mongodb/metrics.rb +391 -0
  179. data/lib/sensu-plugins-mongodb/version.rb +9 -0
  180. data/lib/sensu-plugins-mongodb.rb +1 -0
  181. metadata +407 -0
@@ -0,0 +1,311 @@
1
+ # Copyright 2015-present MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Internal network layer helper methods."""
16
+
17
+ import datetime
18
+ import errno
19
+ import socket
20
+ import struct
21
+ import time
22
+
23
+ from bson import _decode_all_selective
24
+ from pymongo import _csot, helpers, message, ssl_support
25
+ from pymongo.common import MAX_MESSAGE_SIZE
26
+ from pymongo.compression_support import _NO_COMPRESSION, decompress
27
+ from pymongo.errors import (
28
+ NotPrimaryError,
29
+ OperationFailure,
30
+ ProtocolError,
31
+ _OperationCancelled,
32
+ )
33
+ from pymongo.message import _UNPACK_REPLY, _OpMsg
34
+ from pymongo.monitoring import _is_speculative_authenticate
35
+ from pymongo.socket_checker import _errno_from_exception
36
+
37
+ _UNPACK_HEADER = struct.Struct("<iiii").unpack
38
+
39
+
40
+ def command(
41
+ sock_info,
42
+ dbname,
43
+ spec,
44
+ is_mongos,
45
+ read_preference,
46
+ codec_options,
47
+ session,
48
+ client,
49
+ check=True,
50
+ allowable_errors=None,
51
+ address=None,
52
+ listeners=None,
53
+ max_bson_size=None,
54
+ read_concern=None,
55
+ parse_write_concern_error=False,
56
+ collation=None,
57
+ compression_ctx=None,
58
+ use_op_msg=False,
59
+ unacknowledged=False,
60
+ user_fields=None,
61
+ exhaust_allowed=False,
62
+ write_concern=None,
63
+ ):
64
+ """Execute a command over the socket, or raise socket.error.
65
+
66
+ :Parameters:
67
+ - `sock`: a raw socket instance
68
+ - `dbname`: name of the database on which to run the command
69
+ - `spec`: a command document as an ordered dict type, eg SON.
70
+ - `is_mongos`: are we connected to a mongos?
71
+ - `read_preference`: a read preference
72
+ - `codec_options`: a CodecOptions instance
73
+ - `session`: optional ClientSession instance.
74
+ - `client`: optional MongoClient instance for updating $clusterTime.
75
+ - `check`: raise OperationFailure if there are errors
76
+ - `allowable_errors`: errors to ignore if `check` is True
77
+ - `address`: the (host, port) of `sock`
78
+ - `listeners`: An instance of :class:`~pymongo.monitoring.EventListeners`
79
+ - `max_bson_size`: The maximum encoded bson size for this server
80
+ - `read_concern`: The read concern for this command.
81
+ - `parse_write_concern_error`: Whether to parse the ``writeConcernError``
82
+ field in the command response.
83
+ - `collation`: The collation for this command.
84
+ - `compression_ctx`: optional compression Context.
85
+ - `use_op_msg`: True if we should use OP_MSG.
86
+ - `unacknowledged`: True if this is an unacknowledged command.
87
+ - `user_fields` (optional): Response fields that should be decoded
88
+ using the TypeDecoders from codec_options, passed to
89
+ bson._decode_all_selective.
90
+ - `exhaust_allowed`: True if we should enable OP_MSG exhaustAllowed.
91
+ """
92
+ name = next(iter(spec))
93
+ ns = dbname + ".$cmd"
94
+ speculative_hello = False
95
+
96
+ # Publish the original command document, perhaps with lsid and $clusterTime.
97
+ orig = spec
98
+ if is_mongos and not use_op_msg:
99
+ spec = message._maybe_add_read_preference(spec, read_preference)
100
+ if read_concern and not (session and session.in_transaction):
101
+ if read_concern.level:
102
+ spec["readConcern"] = read_concern.document
103
+ if session:
104
+ session._update_read_concern(spec, sock_info)
105
+ if collation is not None:
106
+ spec["collation"] = collation
107
+
108
+ publish = listeners is not None and listeners.enabled_for_commands
109
+ if publish:
110
+ start = datetime.datetime.now()
111
+ speculative_hello = _is_speculative_authenticate(name, spec)
112
+
113
+ if compression_ctx and name.lower() in _NO_COMPRESSION:
114
+ compression_ctx = None
115
+
116
+ if client and client._encrypter and not client._encrypter._bypass_auto_encryption:
117
+ spec = orig = client._encrypter.encrypt(dbname, spec, codec_options)
118
+
119
+ # Support CSOT
120
+ if client:
121
+ sock_info.apply_timeout(client, spec)
122
+ _csot.apply_write_concern(spec, write_concern)
123
+
124
+ if use_op_msg:
125
+ flags = _OpMsg.MORE_TO_COME if unacknowledged else 0
126
+ flags |= _OpMsg.EXHAUST_ALLOWED if exhaust_allowed else 0
127
+ request_id, msg, size, max_doc_size = message._op_msg(
128
+ flags, spec, dbname, read_preference, codec_options, ctx=compression_ctx
129
+ )
130
+ # If this is an unacknowledged write then make sure the encoded doc(s)
131
+ # are small enough, otherwise rely on the server to return an error.
132
+ if unacknowledged and max_bson_size is not None and max_doc_size > max_bson_size:
133
+ message._raise_document_too_large(name, size, max_bson_size)
134
+ else:
135
+ request_id, msg, size = message._query(
136
+ 0, ns, 0, -1, spec, None, codec_options, compression_ctx
137
+ )
138
+
139
+ if max_bson_size is not None and size > max_bson_size + message._COMMAND_OVERHEAD:
140
+ message._raise_document_too_large(name, size, max_bson_size + message._COMMAND_OVERHEAD)
141
+
142
+ if publish:
143
+ encoding_duration = datetime.datetime.now() - start
144
+ listeners.publish_command_start(
145
+ orig, dbname, request_id, address, service_id=sock_info.service_id
146
+ )
147
+ start = datetime.datetime.now()
148
+
149
+ try:
150
+ sock_info.sock.sendall(msg)
151
+ if use_op_msg and unacknowledged:
152
+ # Unacknowledged, fake a successful command response.
153
+ reply = None
154
+ response_doc = {"ok": 1}
155
+ else:
156
+ reply = receive_message(sock_info, request_id)
157
+ sock_info.more_to_come = reply.more_to_come
158
+ unpacked_docs = reply.unpack_response(
159
+ codec_options=codec_options, user_fields=user_fields
160
+ )
161
+
162
+ response_doc = unpacked_docs[0]
163
+ if client:
164
+ client._process_response(response_doc, session)
165
+ if check:
166
+ helpers._check_command_response(
167
+ response_doc,
168
+ sock_info.max_wire_version,
169
+ allowable_errors,
170
+ parse_write_concern_error=parse_write_concern_error,
171
+ )
172
+ except Exception as exc:
173
+ if publish:
174
+ duration = (datetime.datetime.now() - start) + encoding_duration
175
+ if isinstance(exc, (NotPrimaryError, OperationFailure)):
176
+ failure = exc.details
177
+ else:
178
+ failure = message._convert_exception(exc)
179
+ listeners.publish_command_failure(
180
+ duration, failure, name, request_id, address, service_id=sock_info.service_id
181
+ )
182
+ raise
183
+ if publish:
184
+ duration = (datetime.datetime.now() - start) + encoding_duration
185
+ listeners.publish_command_success(
186
+ duration,
187
+ response_doc,
188
+ name,
189
+ request_id,
190
+ address,
191
+ service_id=sock_info.service_id,
192
+ speculative_hello=speculative_hello,
193
+ )
194
+
195
+ if client and client._encrypter and reply:
196
+ decrypted = client._encrypter.decrypt(reply.raw_command_response())
197
+ response_doc = _decode_all_selective(decrypted, codec_options, user_fields)[0]
198
+
199
+ return response_doc
200
+
201
+
202
+ _UNPACK_COMPRESSION_HEADER = struct.Struct("<iiB").unpack
203
+
204
+
205
+ def receive_message(sock_info, request_id, max_message_size=MAX_MESSAGE_SIZE):
206
+ """Receive a raw BSON message or raise socket.error."""
207
+ if _csot.get_timeout():
208
+ deadline = _csot.get_deadline()
209
+ else:
210
+ timeout = sock_info.sock.gettimeout()
211
+ if timeout:
212
+ deadline = time.monotonic() + timeout
213
+ else:
214
+ deadline = None
215
+ # Ignore the response's request id.
216
+ length, _, response_to, op_code = _UNPACK_HEADER(
217
+ _receive_data_on_socket(sock_info, 16, deadline)
218
+ )
219
+ # No request_id for exhaust cursor "getMore".
220
+ if request_id is not None:
221
+ if request_id != response_to:
222
+ raise ProtocolError("Got response id %r but expected %r" % (response_to, request_id))
223
+ if length <= 16:
224
+ raise ProtocolError(
225
+ "Message length (%r) not longer than standard message header size (16)" % (length,)
226
+ )
227
+ if length > max_message_size:
228
+ raise ProtocolError(
229
+ "Message length (%r) is larger than server max "
230
+ "message size (%r)" % (length, max_message_size)
231
+ )
232
+ if op_code == 2012:
233
+ op_code, _, compressor_id = _UNPACK_COMPRESSION_HEADER(
234
+ _receive_data_on_socket(sock_info, 9, deadline)
235
+ )
236
+ data = decompress(_receive_data_on_socket(sock_info, length - 25, deadline), compressor_id)
237
+ else:
238
+ data = _receive_data_on_socket(sock_info, length - 16, deadline)
239
+
240
+ try:
241
+ unpack_reply = _UNPACK_REPLY[op_code]
242
+ except KeyError:
243
+ raise ProtocolError("Got opcode %r but expected %r" % (op_code, _UNPACK_REPLY.keys()))
244
+ return unpack_reply(data)
245
+
246
+
247
+ _POLL_TIMEOUT = 0.5
248
+
249
+
250
+ def wait_for_read(sock_info, deadline):
251
+ """Block until at least one byte is read, or a timeout, or a cancel."""
252
+ context = sock_info.cancel_context
253
+ # Only Monitor connections can be cancelled.
254
+ if context:
255
+ sock = sock_info.sock
256
+ timed_out = False
257
+ while True:
258
+ # SSLSocket can have buffered data which won't be caught by select.
259
+ if hasattr(sock, "pending") and sock.pending() > 0:
260
+ readable = True
261
+ else:
262
+ # Wait up to 500ms for the socket to become readable and then
263
+ # check for cancellation.
264
+ if deadline:
265
+ remaining = deadline - time.monotonic()
266
+ # When the timeout has expired perform one final check to
267
+ # see if the socket is readable. This helps avoid spurious
268
+ # timeouts on AWS Lambda and other FaaS environments.
269
+ if remaining <= 0:
270
+ timed_out = True
271
+ timeout = max(min(remaining, _POLL_TIMEOUT), 0)
272
+ else:
273
+ timeout = _POLL_TIMEOUT
274
+ readable = sock_info.socket_checker.select(sock, read=True, timeout=timeout)
275
+ if context.cancelled:
276
+ raise _OperationCancelled("hello cancelled")
277
+ if readable:
278
+ return
279
+ if timed_out:
280
+ raise socket.timeout("timed out")
281
+
282
+
283
+ # Errors raised by sockets (and TLS sockets) when in non-blocking mode.
284
+ BLOCKING_IO_ERRORS = (BlockingIOError,) + ssl_support.BLOCKING_IO_ERRORS
285
+
286
+
287
+ def _receive_data_on_socket(sock_info, length, deadline):
288
+ buf = bytearray(length)
289
+ mv = memoryview(buf)
290
+ bytes_read = 0
291
+ while bytes_read < length:
292
+ try:
293
+ wait_for_read(sock_info, deadline)
294
+ # CSOT: Update timeout. When the timeout has expired perform one
295
+ # final non-blocking recv. This helps avoid spurious timeouts when
296
+ # the response is actually already buffered on the client.
297
+ if _csot.get_timeout():
298
+ sock_info.set_socket_timeout(max(deadline - time.monotonic(), 0))
299
+ chunk_length = sock_info.sock.recv_into(mv[bytes_read:])
300
+ except BLOCKING_IO_ERRORS:
301
+ raise socket.timeout("timed out")
302
+ except (IOError, OSError) as exc: # noqa: B014
303
+ if _errno_from_exception(exc) == errno.EINTR:
304
+ continue
305
+ raise
306
+ if chunk_length == 0:
307
+ raise OSError("connection closed")
308
+
309
+ bytes_read += chunk_length
310
+
311
+ return mv
@@ -0,0 +1,87 @@
1
+ # Copyright 2020-present MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Utilities for caching OCSP responses."""
16
+
17
+ from collections import namedtuple
18
+ from datetime import datetime as _datetime
19
+ from threading import Lock
20
+
21
+
22
+ class _OCSPCache(object):
23
+ """A cache for OCSP responses."""
24
+
25
+ CACHE_KEY_TYPE = namedtuple( # type: ignore
26
+ "OcspResponseCacheKey",
27
+ ["hash_algorithm", "issuer_name_hash", "issuer_key_hash", "serial_number"],
28
+ )
29
+
30
+ def __init__(self):
31
+ self._data = {}
32
+ # Hold this lock when accessing _data.
33
+ self._lock = Lock()
34
+
35
+ def _get_cache_key(self, ocsp_request):
36
+ return self.CACHE_KEY_TYPE(
37
+ hash_algorithm=ocsp_request.hash_algorithm.name.lower(),
38
+ issuer_name_hash=ocsp_request.issuer_name_hash,
39
+ issuer_key_hash=ocsp_request.issuer_key_hash,
40
+ serial_number=ocsp_request.serial_number,
41
+ )
42
+
43
+ def __setitem__(self, key, value):
44
+ """Add/update a cache entry.
45
+
46
+ 'key' is of type cryptography.x509.ocsp.OCSPRequest
47
+ 'value' is of type cryptography.x509.ocsp.OCSPResponse
48
+
49
+ Validity of the OCSP response must be checked by caller.
50
+ """
51
+ with self._lock:
52
+ cache_key = self._get_cache_key(key)
53
+
54
+ # As per the OCSP protocol, if the response's nextUpdate field is
55
+ # not set, the responder is indicating that newer revocation
56
+ # information is available all the time.
57
+ if value.next_update is None:
58
+ self._data.pop(cache_key, None)
59
+ return
60
+
61
+ # Do nothing if the response is invalid.
62
+ if not (value.this_update <= _datetime.utcnow() < value.next_update):
63
+ return
64
+
65
+ # Cache new response OR update cached response if new response
66
+ # has longer validity.
67
+ cached_value = self._data.get(cache_key, None)
68
+ if cached_value is None or cached_value.next_update < value.next_update:
69
+ self._data[cache_key] = value
70
+
71
+ def __getitem__(self, item):
72
+ """Get a cache entry if it exists.
73
+
74
+ 'item' is of type cryptography.x509.ocsp.OCSPRequest
75
+
76
+ Raises KeyError if the item is not in the cache.
77
+ """
78
+ with self._lock:
79
+ cache_key = self._get_cache_key(item)
80
+ value = self._data[cache_key]
81
+
82
+ # Return cached response if it is still valid.
83
+ if value.this_update <= _datetime.utcnow() < value.next_update:
84
+ return value
85
+
86
+ self._data.pop(cache_key, None)
87
+ raise KeyError(cache_key)