kafka-python 3.0.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.
Files changed (373) hide show
  1. kafka/__init__.py +34 -0
  2. kafka/__main__.py +5 -0
  3. kafka/admin/__init__.py +29 -0
  4. kafka/admin/__main__.py +5 -0
  5. kafka/admin/_acls.py +355 -0
  6. kafka/admin/_cluster.py +359 -0
  7. kafka/admin/_configs.py +479 -0
  8. kafka/admin/_groups.py +754 -0
  9. kafka/admin/_partitions.py +595 -0
  10. kafka/admin/_topics.py +281 -0
  11. kafka/admin/_transactions.py +450 -0
  12. kafka/admin/_users.py +194 -0
  13. kafka/admin/client.py +373 -0
  14. kafka/benchmarks/__init__.py +0 -0
  15. kafka/benchmarks/consumer_performance.py +138 -0
  16. kafka/benchmarks/load_example.py +109 -0
  17. kafka/benchmarks/producer_encode_path.py +201 -0
  18. kafka/benchmarks/producer_performance.py +161 -0
  19. kafka/benchmarks/profile_protocol.py +138 -0
  20. kafka/benchmarks/protocol_old_vs_new.py +447 -0
  21. kafka/benchmarks/record_batch_compose.py +77 -0
  22. kafka/benchmarks/record_batch_read.py +82 -0
  23. kafka/benchmarks/varint_speed.py +426 -0
  24. kafka/cli/__init__.py +36 -0
  25. kafka/cli/admin/__init__.py +117 -0
  26. kafka/cli/admin/acls/__init__.py +9 -0
  27. kafka/cli/admin/acls/common.py +76 -0
  28. kafka/cli/admin/acls/create.py +19 -0
  29. kafka/cli/admin/acls/delete.py +23 -0
  30. kafka/cli/admin/acls/describe.py +16 -0
  31. kafka/cli/admin/cluster/__init__.py +14 -0
  32. kafka/cli/admin/cluster/describe.py +11 -0
  33. kafka/cli/admin/cluster/describe_quorum.py +11 -0
  34. kafka/cli/admin/cluster/features.py +52 -0
  35. kafka/cli/admin/cluster/log_dirs.py +43 -0
  36. kafka/cli/admin/cluster/versions.py +33 -0
  37. kafka/cli/admin/configs/__init__.py +10 -0
  38. kafka/cli/admin/configs/alter.py +43 -0
  39. kafka/cli/admin/configs/common.py +17 -0
  40. kafka/cli/admin/configs/describe.py +30 -0
  41. kafka/cli/admin/configs/list.py +16 -0
  42. kafka/cli/admin/configs/reset.py +20 -0
  43. kafka/cli/admin/groups/__init__.py +16 -0
  44. kafka/cli/admin/groups/alter_offsets.py +30 -0
  45. kafka/cli/admin/groups/delete.py +11 -0
  46. kafka/cli/admin/groups/delete_offsets.py +29 -0
  47. kafka/cli/admin/groups/describe.py +11 -0
  48. kafka/cli/admin/groups/list.py +28 -0
  49. kafka/cli/admin/groups/list_offsets.py +29 -0
  50. kafka/cli/admin/groups/remove_members.py +40 -0
  51. kafka/cli/admin/groups/reset_offsets.py +139 -0
  52. kafka/cli/admin/partitions/__init__.py +21 -0
  53. kafka/cli/admin/partitions/alter_reassignments.py +37 -0
  54. kafka/cli/admin/partitions/create.py +27 -0
  55. kafka/cli/admin/partitions/delete_records.py +31 -0
  56. kafka/cli/admin/partitions/describe.py +36 -0
  57. kafka/cli/admin/partitions/elect_leaders.py +53 -0
  58. kafka/cli/admin/partitions/list_offsets.py +88 -0
  59. kafka/cli/admin/partitions/list_reassignments.py +35 -0
  60. kafka/cli/admin/topics/__init__.py +10 -0
  61. kafka/cli/admin/topics/create.py +13 -0
  62. kafka/cli/admin/topics/delete.py +19 -0
  63. kafka/cli/admin/topics/describe.py +18 -0
  64. kafka/cli/admin/topics/list.py +11 -0
  65. kafka/cli/admin/transactions/__init__.py +17 -0
  66. kafka/cli/admin/transactions/abort.py +38 -0
  67. kafka/cli/admin/transactions/describe.py +24 -0
  68. kafka/cli/admin/transactions/describe_producers.py +29 -0
  69. kafka/cli/admin/transactions/find_hanging.py +26 -0
  70. kafka/cli/admin/transactions/list.py +37 -0
  71. kafka/cli/admin/users/__init__.py +8 -0
  72. kafka/cli/admin/users/alter_user_scram_credentials.py +34 -0
  73. kafka/cli/admin/users/describe_user_scram_credentials.py +15 -0
  74. kafka/cli/common.py +95 -0
  75. kafka/cli/consumer/__init__.py +63 -0
  76. kafka/cli/producer/__init__.py +57 -0
  77. kafka/cluster.py +824 -0
  78. kafka/codec.py +325 -0
  79. kafka/consumer/__init__.py +5 -0
  80. kafka/consumer/__main__.py +5 -0
  81. kafka/consumer/fetcher.py +2012 -0
  82. kafka/consumer/group.py +1347 -0
  83. kafka/consumer/subscription_state.py +897 -0
  84. kafka/coordinator/__init__.py +0 -0
  85. kafka/coordinator/assignors/__init__.py +0 -0
  86. kafka/coordinator/assignors/abstract.py +90 -0
  87. kafka/coordinator/assignors/cooperative_sticky.py +167 -0
  88. kafka/coordinator/assignors/range.py +81 -0
  89. kafka/coordinator/assignors/roundrobin.py +101 -0
  90. kafka/coordinator/assignors/sticky/StickyAssignorUserData.json +37 -0
  91. kafka/coordinator/assignors/sticky/__init__.py +0 -0
  92. kafka/coordinator/assignors/sticky/partition_movements.py +149 -0
  93. kafka/coordinator/assignors/sticky/sorted_set.py +63 -0
  94. kafka/coordinator/assignors/sticky/sticky_assignor.py +665 -0
  95. kafka/coordinator/assignors/sticky/user_data.py +8 -0
  96. kafka/coordinator/base.py +1215 -0
  97. kafka/coordinator/consumer.py +1224 -0
  98. kafka/coordinator/heartbeat.py +82 -0
  99. kafka/coordinator/subscription.py +34 -0
  100. kafka/errors.py +1004 -0
  101. kafka/future.py +166 -0
  102. kafka/metrics/__init__.py +13 -0
  103. kafka/metrics/compound_stat.py +33 -0
  104. kafka/metrics/dict_reporter.py +81 -0
  105. kafka/metrics/kafka_metric.py +36 -0
  106. kafka/metrics/measurable.py +27 -0
  107. kafka/metrics/measurable_stat.py +13 -0
  108. kafka/metrics/metric_config.py +33 -0
  109. kafka/metrics/metric_name.py +105 -0
  110. kafka/metrics/metrics.py +261 -0
  111. kafka/metrics/metrics_reporter.py +53 -0
  112. kafka/metrics/quota.py +41 -0
  113. kafka/metrics/stat.py +19 -0
  114. kafka/metrics/stats/__init__.py +15 -0
  115. kafka/metrics/stats/avg.py +24 -0
  116. kafka/metrics/stats/count.py +17 -0
  117. kafka/metrics/stats/histogram.py +99 -0
  118. kafka/metrics/stats/max_stat.py +17 -0
  119. kafka/metrics/stats/min_stat.py +19 -0
  120. kafka/metrics/stats/percentile.py +14 -0
  121. kafka/metrics/stats/percentiles.py +75 -0
  122. kafka/metrics/stats/rate.py +118 -0
  123. kafka/metrics/stats/sampled_stat.py +99 -0
  124. kafka/metrics/stats/sensor.py +136 -0
  125. kafka/metrics/stats/total.py +15 -0
  126. kafka/net/__init__.py +19 -0
  127. kafka/net/compat.py +165 -0
  128. kafka/net/connection.py +593 -0
  129. kafka/net/http_connect.py +144 -0
  130. kafka/net/inet.py +122 -0
  131. kafka/net/manager.py +451 -0
  132. kafka/net/metrics.py +149 -0
  133. kafka/net/sasl/__init__.py +32 -0
  134. kafka/net/sasl/abc.py +28 -0
  135. kafka/net/sasl/gssapi.py +95 -0
  136. kafka/net/sasl/msk.py +245 -0
  137. kafka/net/sasl/oauth.py +98 -0
  138. kafka/net/sasl/plain.py +42 -0
  139. kafka/net/sasl/scram.py +135 -0
  140. kafka/net/sasl/sspi.py +111 -0
  141. kafka/net/selector.py +644 -0
  142. kafka/net/socks5.py +262 -0
  143. kafka/net/transport.py +415 -0
  144. kafka/net/wakeup_notifier.py +72 -0
  145. kafka/partitioner/__init__.py +8 -0
  146. kafka/partitioner/abc.py +8 -0
  147. kafka/partitioner/default.py +89 -0
  148. kafka/partitioner/sticky.py +109 -0
  149. kafka/producer/__init__.py +5 -0
  150. kafka/producer/__main__.py +5 -0
  151. kafka/producer/future.py +101 -0
  152. kafka/producer/kafka.py +1123 -0
  153. kafka/producer/producer_batch.py +192 -0
  154. kafka/producer/record_accumulator.py +647 -0
  155. kafka/producer/sender.py +884 -0
  156. kafka/producer/transaction_manager.py +1326 -0
  157. kafka/protocol/__init__.py +0 -0
  158. kafka/protocol/admin/__init__.py +29 -0
  159. kafka/protocol/admin/acl.py +83 -0
  160. kafka/protocol/admin/acl.pyi +375 -0
  161. kafka/protocol/admin/client_quotas.py +14 -0
  162. kafka/protocol/admin/client_quotas.pyi +265 -0
  163. kafka/protocol/admin/cluster.py +31 -0
  164. kafka/protocol/admin/cluster.pyi +620 -0
  165. kafka/protocol/admin/configs.py +22 -0
  166. kafka/protocol/admin/configs.pyi +437 -0
  167. kafka/protocol/admin/groups.py +24 -0
  168. kafka/protocol/admin/groups.pyi +261 -0
  169. kafka/protocol/admin/topics.py +53 -0
  170. kafka/protocol/admin/topics.pyi +982 -0
  171. kafka/protocol/admin/transactions.py +18 -0
  172. kafka/protocol/admin/transactions.pyi +311 -0
  173. kafka/protocol/admin/users.py +14 -0
  174. kafka/protocol/admin/users.pyi +223 -0
  175. kafka/protocol/api_data.py +125 -0
  176. kafka/protocol/api_header.py +55 -0
  177. kafka/protocol/api_key.py +97 -0
  178. kafka/protocol/api_message.py +277 -0
  179. kafka/protocol/broker_version_data.py +246 -0
  180. kafka/protocol/consumer/__init__.py +13 -0
  181. kafka/protocol/consumer/fetch.py +16 -0
  182. kafka/protocol/consumer/fetch.pyi +298 -0
  183. kafka/protocol/consumer/group.py +38 -0
  184. kafka/protocol/consumer/group.pyi +824 -0
  185. kafka/protocol/consumer/metadata.py +30 -0
  186. kafka/protocol/consumer/metadata.pyi +89 -0
  187. kafka/protocol/consumer/offsets.py +75 -0
  188. kafka/protocol/consumer/offsets.pyi +288 -0
  189. kafka/protocol/data_container.py +166 -0
  190. kafka/protocol/frame.py +30 -0
  191. kafka/protocol/generate_stubs.py +468 -0
  192. kafka/protocol/metadata/__init__.py +10 -0
  193. kafka/protocol/metadata/api_versions.py +41 -0
  194. kafka/protocol/metadata/api_versions.pyi +128 -0
  195. kafka/protocol/metadata/find_coordinator.py +19 -0
  196. kafka/protocol/metadata/find_coordinator.pyi +105 -0
  197. kafka/protocol/metadata/metadata.py +34 -0
  198. kafka/protocol/metadata/metadata.pyi +160 -0
  199. kafka/protocol/old/__init__.py +0 -0
  200. kafka/protocol/old/abstract.py +17 -0
  201. kafka/protocol/old/add_offsets_to_txn.py +54 -0
  202. kafka/protocol/old/add_partitions_to_txn.py +71 -0
  203. kafka/protocol/old/admin.py +1086 -0
  204. kafka/protocol/old/api.py +205 -0
  205. kafka/protocol/old/api_versions.py +133 -0
  206. kafka/protocol/old/commit.py +355 -0
  207. kafka/protocol/old/consumer_protocol.py +36 -0
  208. kafka/protocol/old/end_txn.py +53 -0
  209. kafka/protocol/old/fetch.py +408 -0
  210. kafka/protocol/old/find_coordinator.py +72 -0
  211. kafka/protocol/old/group.py +451 -0
  212. kafka/protocol/old/init_producer_id.py +42 -0
  213. kafka/protocol/old/list_offsets.py +186 -0
  214. kafka/protocol/old/metadata.py +290 -0
  215. kafka/protocol/old/offset_for_leader_epoch.py +133 -0
  216. kafka/protocol/old/produce.py +247 -0
  217. kafka/protocol/old/sasl_authenticate.py +38 -0
  218. kafka/protocol/old/sasl_handshake.py +39 -0
  219. kafka/protocol/old/struct.py +87 -0
  220. kafka/protocol/old/txn_offset_commit.py +73 -0
  221. kafka/protocol/old/types.py +440 -0
  222. kafka/protocol/parser.py +191 -0
  223. kafka/protocol/producer/__init__.py +7 -0
  224. kafka/protocol/producer/produce.py +17 -0
  225. kafka/protocol/producer/produce.pyi +197 -0
  226. kafka/protocol/producer/transaction.py +30 -0
  227. kafka/protocol/producer/transaction.pyi +663 -0
  228. kafka/protocol/sasl.py +52 -0
  229. kafka/protocol/sasl.pyi +126 -0
  230. kafka/protocol/schemas/__init__.py +7 -0
  231. kafka/protocol/schemas/fields/__init__.py +7 -0
  232. kafka/protocol/schemas/fields/array.py +127 -0
  233. kafka/protocol/schemas/fields/base.py +156 -0
  234. kafka/protocol/schemas/fields/codecs/__init__.py +12 -0
  235. kafka/protocol/schemas/fields/codecs/encode_buffer.py +82 -0
  236. kafka/protocol/schemas/fields/codecs/tagged_fields.py +109 -0
  237. kafka/protocol/schemas/fields/codecs/types.py +505 -0
  238. kafka/protocol/schemas/fields/codegen.py +40 -0
  239. kafka/protocol/schemas/fields/simple.py +127 -0
  240. kafka/protocol/schemas/fields/struct.py +357 -0
  241. kafka/protocol/schemas/fields/struct_array.py +142 -0
  242. kafka/protocol/schemas/load_json.py +42 -0
  243. kafka/protocol/schemas/resources/AddOffsetsToTxnRequest.json +40 -0
  244. kafka/protocol/schemas/resources/AddOffsetsToTxnResponse.json +35 -0
  245. kafka/protocol/schemas/resources/AddPartitionsToTxnRequest.json +65 -0
  246. kafka/protocol/schemas/resources/AddPartitionsToTxnResponse.json +60 -0
  247. kafka/protocol/schemas/resources/AlterClientQuotasRequest.json +47 -0
  248. kafka/protocol/schemas/resources/AlterClientQuotasResponse.json +41 -0
  249. kafka/protocol/schemas/resources/AlterConfigsRequest.json +43 -0
  250. kafka/protocol/schemas/resources/AlterConfigsResponse.json +39 -0
  251. kafka/protocol/schemas/resources/AlterPartitionReassignmentsRequest.json +42 -0
  252. kafka/protocol/schemas/resources/AlterPartitionReassignmentsResponse.json +47 -0
  253. kafka/protocol/schemas/resources/AlterReplicaLogDirsRequest.json +41 -0
  254. kafka/protocol/schemas/resources/AlterReplicaLogDirsResponse.json +41 -0
  255. kafka/protocol/schemas/resources/AlterUserScramCredentialsRequest.json +45 -0
  256. kafka/protocol/schemas/resources/AlterUserScramCredentialsResponse.json +35 -0
  257. kafka/protocol/schemas/resources/ApiVersionsRequest.json +34 -0
  258. kafka/protocol/schemas/resources/ApiVersionsResponse.json +79 -0
  259. kafka/protocol/schemas/resources/ConsumerProtocolAssignment.json +42 -0
  260. kafka/protocol/schemas/resources/ConsumerProtocolSubscription.json +49 -0
  261. kafka/protocol/schemas/resources/CreateAclsRequest.json +46 -0
  262. kafka/protocol/schemas/resources/CreateAclsResponse.json +37 -0
  263. kafka/protocol/schemas/resources/CreatePartitionsRequest.json +47 -0
  264. kafka/protocol/schemas/resources/CreatePartitionsResponse.json +41 -0
  265. kafka/protocol/schemas/resources/CreateTopicsRequest.json +65 -0
  266. kafka/protocol/schemas/resources/CreateTopicsResponse.json +72 -0
  267. kafka/protocol/schemas/resources/DeleteAclsRequest.json +46 -0
  268. kafka/protocol/schemas/resources/DeleteAclsResponse.json +59 -0
  269. kafka/protocol/schemas/resources/DeleteGroupsRequest.json +30 -0
  270. kafka/protocol/schemas/resources/DeleteGroupsResponse.json +36 -0
  271. kafka/protocol/schemas/resources/DeleteRecordsRequest.json +42 -0
  272. kafka/protocol/schemas/resources/DeleteRecordsResponse.json +43 -0
  273. kafka/protocol/schemas/resources/DeleteTopicsRequest.json +43 -0
  274. kafka/protocol/schemas/resources/DeleteTopicsResponse.json +52 -0
  275. kafka/protocol/schemas/resources/DescribeAclsRequest.json +43 -0
  276. kafka/protocol/schemas/resources/DescribeAclsResponse.json +55 -0
  277. kafka/protocol/schemas/resources/DescribeClientQuotasRequest.json +37 -0
  278. kafka/protocol/schemas/resources/DescribeClientQuotasResponse.json +47 -0
  279. kafka/protocol/schemas/resources/DescribeClusterRequest.json +35 -0
  280. kafka/protocol/schemas/resources/DescribeClusterResponse.json +56 -0
  281. kafka/protocol/schemas/resources/DescribeConfigsRequest.json +42 -0
  282. kafka/protocol/schemas/resources/DescribeConfigsResponse.json +69 -0
  283. kafka/protocol/schemas/resources/DescribeGroupsRequest.json +38 -0
  284. kafka/protocol/schemas/resources/DescribeGroupsResponse.json +74 -0
  285. kafka/protocol/schemas/resources/DescribeLogDirsRequest.json +38 -0
  286. kafka/protocol/schemas/resources/DescribeLogDirsResponse.json +65 -0
  287. kafka/protocol/schemas/resources/DescribeProducersRequest.json +32 -0
  288. kafka/protocol/schemas/resources/DescribeProducersResponse.json +55 -0
  289. kafka/protocol/schemas/resources/DescribeQuorumRequest.json +39 -0
  290. kafka/protocol/schemas/resources/DescribeQuorumResponse.json +82 -0
  291. kafka/protocol/schemas/resources/DescribeTopicPartitionsRequest.json +40 -0
  292. kafka/protocol/schemas/resources/DescribeTopicPartitionsResponse.json +66 -0
  293. kafka/protocol/schemas/resources/DescribeTransactionsRequest.json +27 -0
  294. kafka/protocol/schemas/resources/DescribeTransactionsResponse.json +52 -0
  295. kafka/protocol/schemas/resources/DescribeUserScramCredentialsRequest.json +30 -0
  296. kafka/protocol/schemas/resources/DescribeUserScramCredentialsResponse.json +45 -0
  297. kafka/protocol/schemas/resources/ElectLeadersRequest.json +41 -0
  298. kafka/protocol/schemas/resources/ElectLeadersResponse.json +45 -0
  299. kafka/protocol/schemas/resources/EndTxnRequest.json +43 -0
  300. kafka/protocol/schemas/resources/EndTxnResponse.json +41 -0
  301. kafka/protocol/schemas/resources/FetchRequest.json +125 -0
  302. kafka/protocol/schemas/resources/FetchResponse.json +124 -0
  303. kafka/protocol/schemas/resources/FindCoordinatorRequest.json +43 -0
  304. kafka/protocol/schemas/resources/FindCoordinatorResponse.json +58 -0
  305. kafka/protocol/schemas/resources/HeartbeatRequest.json +39 -0
  306. kafka/protocol/schemas/resources/HeartbeatResponse.json +35 -0
  307. kafka/protocol/schemas/resources/IncrementalAlterConfigsRequest.json +44 -0
  308. kafka/protocol/schemas/resources/IncrementalAlterConfigsResponse.json +38 -0
  309. kafka/protocol/schemas/resources/InitProducerIdRequest.json +50 -0
  310. kafka/protocol/schemas/resources/InitProducerIdResponse.json +47 -0
  311. kafka/protocol/schemas/resources/JoinGroupRequest.json +63 -0
  312. kafka/protocol/schemas/resources/JoinGroupResponse.json +69 -0
  313. kafka/protocol/schemas/resources/LeaveGroupRequest.json +47 -0
  314. kafka/protocol/schemas/resources/LeaveGroupResponse.json +47 -0
  315. kafka/protocol/schemas/resources/ListConfigResourcesRequest.json +31 -0
  316. kafka/protocol/schemas/resources/ListConfigResourcesResponse.json +37 -0
  317. kafka/protocol/schemas/resources/ListGroupsRequest.json +36 -0
  318. kafka/protocol/schemas/resources/ListGroupsResponse.json +49 -0
  319. kafka/protocol/schemas/resources/ListOffsetsRequest.json +72 -0
  320. kafka/protocol/schemas/resources/ListOffsetsResponse.json +71 -0
  321. kafka/protocol/schemas/resources/ListPartitionReassignmentsRequest.json +34 -0
  322. kafka/protocol/schemas/resources/ListPartitionReassignmentsResponse.json +46 -0
  323. kafka/protocol/schemas/resources/ListTransactionsRequest.json +40 -0
  324. kafka/protocol/schemas/resources/ListTransactionsResponse.json +42 -0
  325. kafka/protocol/schemas/resources/MetadataRequest.json +56 -0
  326. kafka/protocol/schemas/resources/MetadataResponse.json +101 -0
  327. kafka/protocol/schemas/resources/OffsetCommitRequest.json +76 -0
  328. kafka/protocol/schemas/resources/OffsetCommitResponse.json +71 -0
  329. kafka/protocol/schemas/resources/OffsetDeleteRequest.json +39 -0
  330. kafka/protocol/schemas/resources/OffsetDeleteResponse.json +42 -0
  331. kafka/protocol/schemas/resources/OffsetFetchRequest.json +76 -0
  332. kafka/protocol/schemas/resources/OffsetFetchResponse.json +107 -0
  333. kafka/protocol/schemas/resources/OffsetForLeaderEpochRequest.json +52 -0
  334. kafka/protocol/schemas/resources/OffsetForLeaderEpochResponse.json +51 -0
  335. kafka/protocol/schemas/resources/ProduceRequest.json +73 -0
  336. kafka/protocol/schemas/resources/ProduceResponse.json +96 -0
  337. kafka/protocol/schemas/resources/RequestHeader.json +44 -0
  338. kafka/protocol/schemas/resources/ResponseHeader.json +26 -0
  339. kafka/protocol/schemas/resources/SaslAuthenticateRequest.json +29 -0
  340. kafka/protocol/schemas/resources/SaslAuthenticateResponse.json +34 -0
  341. kafka/protocol/schemas/resources/SaslHandshakeRequest.json +31 -0
  342. kafka/protocol/schemas/resources/SaslHandshakeResponse.json +32 -0
  343. kafka/protocol/schemas/resources/SyncGroupRequest.json +56 -0
  344. kafka/protocol/schemas/resources/SyncGroupResponse.json +46 -0
  345. kafka/protocol/schemas/resources/TxnOffsetCommitRequest.json +68 -0
  346. kafka/protocol/schemas/resources/TxnOffsetCommitResponse.json +47 -0
  347. kafka/protocol/schemas/resources/UpdateFeaturesRequest.json +43 -0
  348. kafka/protocol/schemas/resources/UpdateFeaturesResponse.json +39 -0
  349. kafka/protocol/schemas/resources/WriteTxnMarkersRequest.json +49 -0
  350. kafka/protocol/schemas/resources/WriteTxnMarkersResponse.json +45 -0
  351. kafka/protocol/schemas/resources/__init__.py +0 -0
  352. kafka/record/__init__.py +3 -0
  353. kafka/record/_crc32c.py +161 -0
  354. kafka/record/abc.py +144 -0
  355. kafka/record/default_records.py +782 -0
  356. kafka/record/legacy_records.py +587 -0
  357. kafka/record/memory_records.py +255 -0
  358. kafka/record/util.py +135 -0
  359. kafka/serializer/__init__.py +4 -0
  360. kafka/serializer/abstract.py +20 -0
  361. kafka/serializer/default.py +16 -0
  362. kafka/serializer/json.py +17 -0
  363. kafka/serializer/wrapper.py +21 -0
  364. kafka/structs.py +69 -0
  365. kafka/util.py +159 -0
  366. kafka/vendor/__init__.py +0 -0
  367. kafka/version.py +1 -0
  368. kafka_python-3.0.0.dist-info/METADATA +319 -0
  369. kafka_python-3.0.0.dist-info/RECORD +373 -0
  370. kafka_python-3.0.0.dist-info/WHEEL +5 -0
  371. kafka_python-3.0.0.dist-info/entry_points.txt +2 -0
  372. kafka_python-3.0.0.dist-info/licenses/LICENSE +202 -0
  373. kafka_python-3.0.0.dist-info/top_level.txt +1 -0
kafka/net/socks5.py ADDED
@@ -0,0 +1,262 @@
1
+ import errno
2
+ import logging
3
+ import random
4
+ import socket
5
+ import struct
6
+ from urllib.parse import urlparse
7
+
8
+ from kafka.errors import KafkaConnectionError
9
+ from kafka.net.inet import KafkaNetSocket
10
+
11
+
12
+ log = logging.getLogger(__name__)
13
+
14
+
15
+ class ProxyConnectionStates:
16
+ DISCONNECTED = '<disconnected>'
17
+ CONNECTING = '<connecting>'
18
+ NEGOTIATE_PROPOSE = '<negotiate_propose>'
19
+ NEGOTIATING = '<negotiating>'
20
+ AUTHENTICATING = '<authenticating>'
21
+ REQUEST_SUBMIT = '<request_submit>'
22
+ REQUESTING = '<requesting>'
23
+ READ_ADDRESS = '<read_address>'
24
+ COMPLETE = '<complete>'
25
+
26
+
27
+ class Socks5Proxy(KafkaNetSocket):
28
+ """Socks5 proxy
29
+
30
+ Manages connection through socks5 proxy with support for username/password
31
+ authentication.
32
+ """
33
+ # socks5h for remote dns
34
+ SCHEMES = ('socks5', 'socks5h')
35
+
36
+ def __init__(self, proxy_url):
37
+ self._buffer_in = b''
38
+ self._buffer_out = b''
39
+ self._proxy_url = urlparse(proxy_url)
40
+ if self._proxy_url.scheme not in self.SCHEMES:
41
+ raise ValueError('Unsupported proxy scheme: %s' % (self._proxy_url.scheme,))
42
+ self._sock = None
43
+ self._state = ProxyConnectionStates.DISCONNECTED
44
+ self._target_afi = socket.AF_UNSPEC
45
+ self._proxy_addr = self._get_proxy_addr()
46
+
47
+ def _get_proxy_addr(self):
48
+ proxy_addrs = self.dns_lookup(self._proxy_url.hostname, self._proxy_url.port, proxy=True)
49
+ if not proxy_addrs:
50
+ raise KafkaConnectionError('Unable to resolve proxy_url via dns')
51
+ return random.choice(proxy_addrs)
52
+
53
+ def _use_remote_lookup(self):
54
+ return self._proxy_url.scheme == 'socks5h'
55
+
56
+ def dns_lookup(self, host, port, proxy=False):
57
+ if proxy:
58
+ return super().dns_lookup(host, port, raise_error=True)
59
+ elif self._use_remote_lookup():
60
+ return [(socket.AF_UNSPEC, socket.SOCK_STREAM, socket.IPPROTO_TCP, '', (host, port))]
61
+ else:
62
+ return super().dns_lookup(host, port)
63
+
64
+ def socket(self, family=socket.AF_UNSPEC, sock_type=socket.SOCK_STREAM, proto=socket.IPPROTO_TCP):
65
+ """Open and record a socket.
66
+
67
+ Returns the actual underlying socket
68
+ object to ensure e.g. selects and ssl wrapping works as expected.
69
+ """
70
+ self._target_afi = family # Store the address family of the target
71
+ proxy_family, _, _, _, _ = self._proxy_addr
72
+ self._sock = socket.socket(proxy_family, sock_type, proto)
73
+ return self._sock
74
+
75
+ def _flush_buf(self):
76
+ """Send out all data that is stored in the outgoing buffer.
77
+
78
+ It is expected that the caller handles error handling, including non-blocking
79
+ as well as connection failure exceptions.
80
+ """
81
+ while self._buffer_out:
82
+ sent_bytes = self._sock.send(self._buffer_out)
83
+ self._buffer_out = self._buffer_out[sent_bytes:]
84
+
85
+ def _peek_buf(self, datalen):
86
+ """Ensure local inbound buffer has enough data, and return that data without
87
+ consuming the local buffer
88
+
89
+ It's expected that the caller handles e.g. blocking exceptions"""
90
+ while True:
91
+ bytes_remaining = datalen - len(self._buffer_in)
92
+ if bytes_remaining <= 0:
93
+ break
94
+ data = self._sock.recv(bytes_remaining)
95
+ if not data:
96
+ break
97
+ self._buffer_in = self._buffer_in + data
98
+
99
+ return self._buffer_in[:datalen]
100
+
101
+ def _read_buf(self, datalen):
102
+ """Read and consume bytes from socket connection
103
+
104
+ It's expected that the caller handles e.g. blocking exceptions"""
105
+ buf = self._peek_buf(datalen)
106
+ if buf:
107
+ self._buffer_in = self._buffer_in[len(buf):]
108
+ return buf
109
+
110
+ def connect_ex(self, sock, addr):
111
+ """Runs a state machine through connection to authentication to
112
+ proxy connection request.
113
+
114
+ The somewhat strange setup is to facilitate non-intrusive use from
115
+ BrokerConnection state machine.
116
+
117
+ This function is called with a socket in non-blocking mode. Both
118
+ send and receive calls can return in EWOULDBLOCK/EAGAIN which we
119
+ specifically avoid handling here. These are handled in main
120
+ BrokerConnection connection loop, which then would retry calls
121
+ to this function."""
122
+ assert sock is self._sock
123
+ if self._state == ProxyConnectionStates.DISCONNECTED:
124
+ self._state = ProxyConnectionStates.CONNECTING
125
+
126
+ if self._state == ProxyConnectionStates.CONNECTING:
127
+ _, _, _, _, sockaddr = self._proxy_addr
128
+ ret = self._sock.connect_ex(sockaddr)
129
+ if not ret or ret == errno.EISCONN:
130
+ self._state = ProxyConnectionStates.NEGOTIATE_PROPOSE
131
+ else:
132
+ return ret
133
+
134
+ if self._state == ProxyConnectionStates.NEGOTIATE_PROPOSE:
135
+ if self._proxy_url.username and self._proxy_url.password:
136
+ # Propose username/password
137
+ self._buffer_out = b"\x05\x01\x02"
138
+ else:
139
+ # Propose no auth
140
+ self._buffer_out = b"\x05\x01\x00"
141
+ self._state = ProxyConnectionStates.NEGOTIATING
142
+
143
+ if self._state == ProxyConnectionStates.NEGOTIATING:
144
+ self._flush_buf()
145
+ buf = self._read_buf(2)
146
+ if buf[0:1] != b"\x05":
147
+ log.error("Unrecognized SOCKS version")
148
+ self._state = ProxyConnectionStates.DISCONNECTED
149
+ self._sock.close()
150
+ return errno.ECONNREFUSED
151
+
152
+ if buf[1:2] == b"\x00":
153
+ # No authentication required
154
+ self._state = ProxyConnectionStates.REQUEST_SUBMIT
155
+ elif buf[1:2] == b"\x02":
156
+ # Username/password authentication selected
157
+ userlen = len(self._proxy_url.username)
158
+ passlen = len(self._proxy_url.password)
159
+ self._buffer_out = struct.pack(
160
+ "!bb{}sb{}s".format(userlen, passlen),
161
+ 1, # version
162
+ userlen,
163
+ self._proxy_url.username.encode(),
164
+ passlen,
165
+ self._proxy_url.password.encode(),
166
+ )
167
+ self._state = ProxyConnectionStates.AUTHENTICATING
168
+ else:
169
+ log.error("Unrecognized SOCKS authentication method")
170
+ self._state = ProxyConnectionStates.DISCONNECTED
171
+ self._sock.close()
172
+ return errno.ECONNREFUSED
173
+
174
+ if self._state == ProxyConnectionStates.AUTHENTICATING:
175
+ self._flush_buf()
176
+ buf = self._read_buf(2)
177
+ if buf == b"\x01\x00":
178
+ # Authentication succesful
179
+ self._state = ProxyConnectionStates.REQUEST_SUBMIT
180
+ else:
181
+ log.error("Socks5 proxy authentication failure")
182
+ self._state = ProxyConnectionStates.DISCONNECTED
183
+ self._sock.close()
184
+ return errno.ECONNREFUSED
185
+
186
+ if self._state == ProxyConnectionStates.REQUEST_SUBMIT:
187
+ if self._use_remote_lookup():
188
+ addr_type = 3
189
+ addr_len = len(addr[0])
190
+ elif self._target_afi == socket.AF_INET:
191
+ addr_type = 1
192
+ addr_len = 4
193
+ elif self._target_afi == socket.AF_INET6:
194
+ addr_type = 4
195
+ addr_len = 16
196
+ else:
197
+ log.error("Unknown address family, %r", self._target_afi)
198
+ self._state = ProxyConnectionStates.DISCONNECTED
199
+ self._sock.close()
200
+ return errno.ECONNREFUSED
201
+
202
+ self._buffer_out = struct.pack(
203
+ "!bbbb",
204
+ 5, # version
205
+ 1, # command: connect
206
+ 0, # reserved
207
+ addr_type, # 1 for ipv4, 4 for ipv6 address, 3 for domain name
208
+ )
209
+ # Addr format depends on type
210
+ if addr_type == 3:
211
+ # len + domain name (no null terminator)
212
+ self._buffer_out += struct.pack(
213
+ "!b{}s".format(addr_len),
214
+ addr_len,
215
+ addr[0].encode('ascii'),
216
+ )
217
+ else:
218
+ # either 4 (type 1) or 16 (type 4) bytes of actual address
219
+ self._buffer_out += struct.pack(
220
+ "!{}s".format(addr_len),
221
+ socket.inet_pton(self._target_afi, addr[0]),
222
+ )
223
+ self._buffer_out += struct.pack("!H", addr[1]) # port
224
+
225
+ self._state = ProxyConnectionStates.REQUESTING
226
+
227
+ if self._state == ProxyConnectionStates.REQUESTING:
228
+ self._flush_buf()
229
+ buf = self._read_buf(2)
230
+ if buf[0:2] == b"\x05\x00":
231
+ self._state = ProxyConnectionStates.READ_ADDRESS
232
+ else:
233
+ log.error("Proxy request failed: %r", buf[1:2])
234
+ self._state = ProxyConnectionStates.DISCONNECTED
235
+ self._sock.close()
236
+ return errno.ECONNREFUSED
237
+
238
+ if self._state == ProxyConnectionStates.READ_ADDRESS:
239
+ # we don't really care about the remote endpoint address, but need to clear the stream
240
+ buf = self._peek_buf(2)
241
+ if buf[0:2] == b"\x00\x01":
242
+ _ = self._read_buf(2 + 4 + 2) # ipv4 address + port
243
+ elif buf[0:2] == b"\x00\x05":
244
+ _ = self._read_buf(2 + 16 + 2) # ipv6 address + port
245
+ else:
246
+ log.error("Unrecognized remote address type %r", buf[1:2])
247
+ self._state = ProxyConnectionStates.DISCONNECTED
248
+ self._sock.close()
249
+ return errno.ECONNREFUSED
250
+ self._state = ProxyConnectionStates.COMPLETE
251
+
252
+ if self._state == ProxyConnectionStates.COMPLETE:
253
+ return 0
254
+
255
+ # not reached;
256
+ # Send and recv will raise socket error on EWOULDBLOCK/EAGAIN that is assumed to be handled by
257
+ # the caller. The caller re-enters this state machine from retry logic with timer or via select & family
258
+ log.error("Internal error, state %r not handled correctly", self._state)
259
+ self._state = ProxyConnectionStates.DISCONNECTED
260
+ if self._sock:
261
+ self._sock.close()
262
+ return errno.ECONNREFUSED
kafka/net/transport.py ADDED
@@ -0,0 +1,415 @@
1
+ from collections import deque
2
+ import logging
3
+ import selectors
4
+ import socket
5
+ import ssl
6
+ import time
7
+
8
+ import kafka.errors as Errors
9
+
10
+
11
+ log = logging.getLogger(__name__)
12
+
13
+
14
+ class KafkaTCPTransport:
15
+ def __init__(self, net, sock, host=None):
16
+ self._net = net
17
+ self._sock = sock
18
+ self.host = host
19
+ self._closed = False
20
+ self._write_buffer = deque()
21
+ self._writing = False
22
+ self._protocol = None
23
+ self._read = False
24
+ self._write = True
25
+ self.last_write = time.monotonic()
26
+ self.last_read = time.monotonic()
27
+
28
+ @property
29
+ def last_activity(self):
30
+ return max(self.last_write, self.last_read)
31
+
32
+ # AsyncIO
33
+ def is_closing(self):
34
+ """Return True if the transport is closing or closed."""
35
+ return self._closed
36
+
37
+ def close(self):
38
+ """Close the transport.
39
+
40
+ Buffered data will be flushed asynchronously. No more data
41
+ will be received. After all buffered data is flushed, the
42
+ protocol's connection_lost() method will (eventually) be
43
+ called with None as its argument.
44
+ """
45
+ if not self._closed:
46
+ log.info('%s: Closing transport', self)
47
+ self._closed = True
48
+ self._read = False
49
+ if not self._write_buffer:
50
+ self._close()
51
+
52
+ def set_protocol(self, protocol):
53
+ """Set a new protocol."""
54
+ self._protocol = protocol
55
+ log.debug('%s: Set protocol %s', self, protocol)
56
+
57
+ def get_protocol(self):
58
+ """Return the current protocol."""
59
+ return self._protocol
60
+
61
+ """Interface for read-only transports."""
62
+
63
+ def is_reading(self):
64
+ """Return True if the transport is receiving."""
65
+ return self._read
66
+
67
+ def pause_reading(self):
68
+ """Pause the receiving end.
69
+
70
+ No data will be passed to the protocol's data_received()
71
+ method until resume_reading() is called.
72
+ """
73
+ self._read = False
74
+ log.debug('%s: Paused reading', self)
75
+
76
+ def resume_reading(self):
77
+ """Resume the receiving end.
78
+
79
+ Data received will once again be passed to the protocol's
80
+ data_received() method.
81
+ """
82
+ if not self._read:
83
+ self._net.call_soon(self._read_from_sock)
84
+ self._read = True
85
+ log.debug('%s: Resumed reading', self)
86
+
87
+ async def _read_from_sock(self):
88
+ while self._read and not self._closed:
89
+ await self._net.wait_read(self._sock)
90
+ recvd_data, err = self._sock_recv()
91
+ if err:
92
+ return self.abort(error=err)
93
+ log.debug('%s: received %d bytes', self, len(recvd_data))
94
+ self.last_read = time.monotonic()
95
+ if self._protocol and self._protocol._sensors:
96
+ self._protocol._sensors.bytes_received.record(len(recvd_data))
97
+ try:
98
+ self._protocol.data_received(recvd_data)
99
+ except Errors.KafkaProtocolError as e:
100
+ return self.abort(error=e)
101
+
102
+ def _sock_recv(self):
103
+ recvd = []
104
+ err = None
105
+ while True:
106
+ try:
107
+ data = self._sock.recv(4096)
108
+ # We expect socket.recv to raise an exception if there are no
109
+ # bytes available to read from the socket in non-blocking mode.
110
+ # but if the socket is disconnected, we will get empty data
111
+ # without an exception raised
112
+ if not data:
113
+ log.error('%s: socket disconnected', self)
114
+ err = Errors.KafkaConnectionError('socket disconnected')
115
+ break
116
+ else:
117
+ recvd.append(data)
118
+
119
+ except (BlockingIOError, InterruptedError):
120
+ break
121
+ except BaseException as e:
122
+ log.exception('%s: Error receiving network data'
123
+ ' closing socket', self)
124
+ err = Errors.KafkaConnectionError(e)
125
+ break
126
+
127
+ recvd_data = b''.join(recvd)
128
+ return recvd_data, err
129
+
130
+ """Interface for write-only transports."""
131
+
132
+ def set_write_buffer_limits(self, high=None, low=None):
133
+ """Set the high- and low-water limits for write flow control.
134
+
135
+ These two values control when to call the protocol's
136
+ pause_writing() and resume_writing() methods. If specified,
137
+ the low-water limit must be less than or equal to the
138
+ high-water limit. Neither value can be negative.
139
+
140
+ The defaults are implementation-specific. If only the
141
+ high-water limit is given, the low-water limit defaults to an
142
+ implementation-specific value less than or equal to the
143
+ high-water limit. Setting high to zero forces low to zero as
144
+ well, and causes pause_writing() to be called whenever the
145
+ buffer becomes non-empty. Setting low to zero causes
146
+ resume_writing() to be called only once the buffer is empty.
147
+ Use of zero for either limit is generally sub-optimal as it
148
+ reduces opportunities for doing I/O and computation
149
+ concurrently.
150
+ """
151
+ raise NotImplementedError
152
+
153
+ def get_write_buffer_size(self):
154
+ """Return the current size of the write buffer."""
155
+ raise NotImplementedError
156
+
157
+ def get_write_buffer_limits(self):
158
+ """Get the high and low watermarks for write flow control.
159
+ Return a tuple (low, high) where low and high are
160
+ positive number of bytes."""
161
+ raise NotImplementedError
162
+
163
+ def write(self, data):
164
+ """Write some data bytes to the transport.
165
+
166
+ This does not block; it buffers the data and arranges for it
167
+ to be sent out asynchronously.
168
+ """
169
+ if not self._write or self._closed:
170
+ raise RuntimeError('Transport closed for writes')
171
+ if not data:
172
+ raise ValueError('Cant write empty data')
173
+ self._write_buffer.append(data)
174
+ if not self._writing:
175
+ self._writing = True
176
+ self._net.call_soon(self._write_to_sock)
177
+
178
+ def writelines(self, list_of_data):
179
+ """Write a list (or any iterable) of data bytes to the transport."""
180
+ if not self._write or self._closed:
181
+ raise RuntimeError('Transport closed for writes')
182
+ self._write_buffer.extend(list_of_data)
183
+ if not self._writing:
184
+ self._writing = True
185
+ self._net.call_soon(self._write_to_sock)
186
+
187
+ async def _write_to_sock(self):
188
+ try:
189
+ while self._write and not self._closed and self._write_buffer:
190
+ await self._net.wait_write(self._sock)
191
+ total_bytes, err = self._sock_send()
192
+ if err:
193
+ return self.abort(error=err)
194
+ log.debug('%s: sent %d bytes', self, total_bytes)
195
+ self.last_write = time.monotonic()
196
+ if self._protocol and self._protocol._sensors:
197
+ self._protocol._sensors.bytes_sent.record(total_bytes)
198
+ finally:
199
+ self._writing = False
200
+ if not self._write:
201
+ self._sock.shutdown(socket.SHUT_WR)
202
+
203
+ def _sock_send(self):
204
+ total_bytes = 0
205
+ while self._write_buffer:
206
+ next_chunk = self._write_buffer.popleft()
207
+ # Wrap in memoryview so partial-send slicing is O(1) instead of
208
+ # copying the unsent tail on every BlockingIOError / short write.
209
+ if not isinstance(next_chunk, memoryview):
210
+ next_chunk = memoryview(next_chunk)
211
+ while next_chunk:
212
+ try:
213
+ sent_bytes = self._sock.send(next_chunk)
214
+ total_bytes += sent_bytes
215
+ next_chunk = next_chunk[sent_bytes:]
216
+ except (BlockingIOError, InterruptedError):
217
+ self._write_buffer.appendleft(next_chunk)
218
+ return total_bytes, None
219
+ except BaseException as e:
220
+ log.exception("%s: Error sending request data: %s", self, e)
221
+ return total_bytes, Errors.KafkaConnectionError(e)
222
+ return total_bytes, None
223
+
224
+ def write_eof(self):
225
+ """Close the write end after flushing buffered data.
226
+
227
+ (This is like typing ^D into a UNIX program reading from stdin.)
228
+
229
+ Data may still be received.
230
+ """
231
+ log.debug('%s: write_eof', self)
232
+ self._write = False
233
+ if not self._write_buffer:
234
+ self._sock.shutdown(socket.SHUT_WR)
235
+
236
+ def can_write_eof(self):
237
+ """Return True if this transport supports write_eof(), False if not."""
238
+ return True
239
+
240
+ def abort(self, error=None):
241
+ """Close the transport immediately.
242
+
243
+ Buffered data will be lost. No more data will be received.
244
+ The protocol's connection_lost() method will (eventually) be
245
+ called with None as its argument.
246
+ """
247
+ if not self._closed:
248
+ log.error('%s: Abort (%s)', self, error)
249
+ self._closed = True
250
+ self._write_buffer.clear()
251
+ self._read = self._write = False
252
+ self._close(error)
253
+
254
+ def _close(self, error=None):
255
+ # idempotent; no lock
256
+ sock = self._sock
257
+ self._sock = None
258
+ if sock is not None:
259
+ try:
260
+ self._net.unregister_event(sock, selectors.EVENT_READ | selectors.EVENT_WRITE)
261
+ except (KeyError, ValueError):
262
+ pass
263
+ sock.close()
264
+ proto = self._protocol
265
+ self._protocol = None
266
+ if proto is not None:
267
+ proto.connection_lost(error)
268
+
269
+ # Twisted
270
+ def abortConnection(self):
271
+ """Close the connection abruptly."""
272
+ return self.abort()
273
+
274
+ def getHost(self):
275
+ """Similar to getPeer, but returns an address describing this side of the connection.
276
+
277
+ Returns IPv4Address or IPv6Address.
278
+ """
279
+ return self._sock.getsockname()
280
+
281
+ def getPeer(self):
282
+ """Get the remote address of this connection.
283
+
284
+ Treat this method with caution. It is the unfortunate result of the CGI and Jabber standards,
285
+ but should not be considered reliable for the usual host of reasons;
286
+ port forwarding, proxying, firewalls, IP masquerading, etc.
287
+
288
+ Returns IPv4Address or IPv6Address.
289
+ """
290
+ return self._sock.getpeername()
291
+
292
+ def getTcpKeepAlive(self):
293
+ """Return if SO_KEEPALIVE is enabled."""
294
+ return self._sock.getsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE)
295
+
296
+ def getTcpNoDelay(self):
297
+ """Return if TCP_NODELAY is enabled."""
298
+ return self._sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY)
299
+
300
+ def loseWriteConnection(self):
301
+ """Half-close the write side of a TCP connection."""
302
+ return self.write_eof()
303
+
304
+ def setTcpKeepAlive(self, enabled):
305
+ """Enable/disable SO_KEEPALIVE."""
306
+ return self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, enabled)
307
+
308
+ def setTcpNoDelay(self, enabled):
309
+ """Enable/disable TCP_NODELAY."""
310
+ return self._sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, enabled)
311
+
312
+ def loseConnection(self):
313
+ """Close my connection, after writing all pending data.
314
+
315
+ Note that if there is a registered producer on a transport it will not be closed until the producer has been unregistered.
316
+ """
317
+ return self.close()
318
+
319
+ #def write(self, data):
320
+ # """Write some data to the physical connection, in sequence, in a non-blocking fashion.
321
+ #
322
+ # If possible, make sure that it is all written. No data will ever be lost,
323
+ # although (obviously) the connection may be closed before it all gets through.
324
+ # """
325
+ # pass
326
+
327
+ def writeSequence(self, data):
328
+ """Write an iterable of byte strings to the physical connection.
329
+
330
+ If possible, make sure that all of the data is written to the socket at once,
331
+ without first copying it all into a single byte string.
332
+ """
333
+ return self.writelines(data)
334
+
335
+ async def handshake(self):
336
+ pass
337
+
338
+ def host_port(self):
339
+ if self._sock is None:
340
+ return 'none'
341
+ try:
342
+ host, port = self._sock.getpeername()[0:2]
343
+ except (OSError, ValueError):
344
+ return 'none'
345
+ try:
346
+ local_port = self._sock.getsockname()[1]
347
+ except (OSError, ValueError):
348
+ return f'{host}:{port}'
349
+ return f'{host}:{port}<-{local_port}'
350
+
351
+ def __str__(self):
352
+ state = ' (closed)' if self._closed else ''
353
+ return f"<{self.__class__.__name__} [{self.host_port()}]{state}>"
354
+
355
+
356
+ class KafkaSSLTransport(KafkaTCPTransport):
357
+ def __init__(self, net, sock, ssl_context, host=None, ssl_check_hostname=False):
358
+ self._ssl_context = ssl_context
359
+ server_hostname = host if ssl_check_hostname else None
360
+ sock = ssl_context.wrap_socket(
361
+ sock, server_hostname=server_hostname, do_handshake_on_connect=False)
362
+ super().__init__(net, sock, host=host)
363
+
364
+ async def handshake(self):
365
+ while True:
366
+ try:
367
+ self._sock.do_handshake()
368
+ return
369
+ except ssl.SSLWantReadError:
370
+ await self._net.wait_read(self._sock)
371
+ except ssl.SSLWantWriteError:
372
+ await self._net.wait_write(self._sock)
373
+
374
+ def _sock_recv(self):
375
+ recvd = []
376
+ err = None
377
+ while True:
378
+ try:
379
+ data = self._sock.recv(4096)
380
+ if not data:
381
+ log.error('%s: socket disconnected', self)
382
+ err = Errors.KafkaConnectionError('socket disconnected')
383
+ break
384
+ else:
385
+ recvd.append(data)
386
+ except (BlockingIOError, InterruptedError,
387
+ ssl.SSLWantReadError, ssl.SSLWantWriteError):
388
+ break
389
+ except BaseException as e:
390
+ log.exception('%s: Error receiving network data'
391
+ ' closing socket', self)
392
+ err = Errors.KafkaConnectionError(e)
393
+ break
394
+ recvd_data = b''.join(recvd)
395
+ return recvd_data, err
396
+
397
+ def _sock_send(self):
398
+ total_bytes = 0
399
+ err = None
400
+ while self._write_buffer:
401
+ next_chunk = self._write_buffer.popleft()
402
+ while next_chunk:
403
+ try:
404
+ sent_bytes = self._sock.send(next_chunk)
405
+ total_bytes += sent_bytes
406
+ next_chunk = next_chunk[sent_bytes:]
407
+ except (BlockingIOError, InterruptedError,
408
+ ssl.SSLWantReadError, ssl.SSLWantWriteError):
409
+ self._write_buffer.appendleft(next_chunk)
410
+ return total_bytes, err
411
+ except BaseException as e:
412
+ log.exception("%s: Error sending request data: %s", self, e)
413
+ err = Errors.KafkaConnectionError(e)
414
+ return total_bytes, err
415
+ return total_bytes, err