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.
- kafka/__init__.py +34 -0
- kafka/__main__.py +5 -0
- kafka/admin/__init__.py +29 -0
- kafka/admin/__main__.py +5 -0
- kafka/admin/_acls.py +355 -0
- kafka/admin/_cluster.py +359 -0
- kafka/admin/_configs.py +479 -0
- kafka/admin/_groups.py +754 -0
- kafka/admin/_partitions.py +595 -0
- kafka/admin/_topics.py +281 -0
- kafka/admin/_transactions.py +450 -0
- kafka/admin/_users.py +194 -0
- kafka/admin/client.py +373 -0
- kafka/benchmarks/__init__.py +0 -0
- kafka/benchmarks/consumer_performance.py +138 -0
- kafka/benchmarks/load_example.py +109 -0
- kafka/benchmarks/producer_encode_path.py +201 -0
- kafka/benchmarks/producer_performance.py +161 -0
- kafka/benchmarks/profile_protocol.py +138 -0
- kafka/benchmarks/protocol_old_vs_new.py +447 -0
- kafka/benchmarks/record_batch_compose.py +77 -0
- kafka/benchmarks/record_batch_read.py +82 -0
- kafka/benchmarks/varint_speed.py +426 -0
- kafka/cli/__init__.py +36 -0
- kafka/cli/admin/__init__.py +117 -0
- kafka/cli/admin/acls/__init__.py +9 -0
- kafka/cli/admin/acls/common.py +76 -0
- kafka/cli/admin/acls/create.py +19 -0
- kafka/cli/admin/acls/delete.py +23 -0
- kafka/cli/admin/acls/describe.py +16 -0
- kafka/cli/admin/cluster/__init__.py +14 -0
- kafka/cli/admin/cluster/describe.py +11 -0
- kafka/cli/admin/cluster/describe_quorum.py +11 -0
- kafka/cli/admin/cluster/features.py +52 -0
- kafka/cli/admin/cluster/log_dirs.py +43 -0
- kafka/cli/admin/cluster/versions.py +33 -0
- kafka/cli/admin/configs/__init__.py +10 -0
- kafka/cli/admin/configs/alter.py +43 -0
- kafka/cli/admin/configs/common.py +17 -0
- kafka/cli/admin/configs/describe.py +30 -0
- kafka/cli/admin/configs/list.py +16 -0
- kafka/cli/admin/configs/reset.py +20 -0
- kafka/cli/admin/groups/__init__.py +16 -0
- kafka/cli/admin/groups/alter_offsets.py +30 -0
- kafka/cli/admin/groups/delete.py +11 -0
- kafka/cli/admin/groups/delete_offsets.py +29 -0
- kafka/cli/admin/groups/describe.py +11 -0
- kafka/cli/admin/groups/list.py +28 -0
- kafka/cli/admin/groups/list_offsets.py +29 -0
- kafka/cli/admin/groups/remove_members.py +40 -0
- kafka/cli/admin/groups/reset_offsets.py +139 -0
- kafka/cli/admin/partitions/__init__.py +21 -0
- kafka/cli/admin/partitions/alter_reassignments.py +37 -0
- kafka/cli/admin/partitions/create.py +27 -0
- kafka/cli/admin/partitions/delete_records.py +31 -0
- kafka/cli/admin/partitions/describe.py +36 -0
- kafka/cli/admin/partitions/elect_leaders.py +53 -0
- kafka/cli/admin/partitions/list_offsets.py +88 -0
- kafka/cli/admin/partitions/list_reassignments.py +35 -0
- kafka/cli/admin/topics/__init__.py +10 -0
- kafka/cli/admin/topics/create.py +13 -0
- kafka/cli/admin/topics/delete.py +19 -0
- kafka/cli/admin/topics/describe.py +18 -0
- kafka/cli/admin/topics/list.py +11 -0
- kafka/cli/admin/transactions/__init__.py +17 -0
- kafka/cli/admin/transactions/abort.py +38 -0
- kafka/cli/admin/transactions/describe.py +24 -0
- kafka/cli/admin/transactions/describe_producers.py +29 -0
- kafka/cli/admin/transactions/find_hanging.py +26 -0
- kafka/cli/admin/transactions/list.py +37 -0
- kafka/cli/admin/users/__init__.py +8 -0
- kafka/cli/admin/users/alter_user_scram_credentials.py +34 -0
- kafka/cli/admin/users/describe_user_scram_credentials.py +15 -0
- kafka/cli/common.py +95 -0
- kafka/cli/consumer/__init__.py +63 -0
- kafka/cli/producer/__init__.py +57 -0
- kafka/cluster.py +824 -0
- kafka/codec.py +325 -0
- kafka/consumer/__init__.py +5 -0
- kafka/consumer/__main__.py +5 -0
- kafka/consumer/fetcher.py +2012 -0
- kafka/consumer/group.py +1347 -0
- kafka/consumer/subscription_state.py +897 -0
- kafka/coordinator/__init__.py +0 -0
- kafka/coordinator/assignors/__init__.py +0 -0
- kafka/coordinator/assignors/abstract.py +90 -0
- kafka/coordinator/assignors/cooperative_sticky.py +167 -0
- kafka/coordinator/assignors/range.py +81 -0
- kafka/coordinator/assignors/roundrobin.py +101 -0
- kafka/coordinator/assignors/sticky/StickyAssignorUserData.json +37 -0
- kafka/coordinator/assignors/sticky/__init__.py +0 -0
- kafka/coordinator/assignors/sticky/partition_movements.py +149 -0
- kafka/coordinator/assignors/sticky/sorted_set.py +63 -0
- kafka/coordinator/assignors/sticky/sticky_assignor.py +665 -0
- kafka/coordinator/assignors/sticky/user_data.py +8 -0
- kafka/coordinator/base.py +1215 -0
- kafka/coordinator/consumer.py +1224 -0
- kafka/coordinator/heartbeat.py +82 -0
- kafka/coordinator/subscription.py +34 -0
- kafka/errors.py +1004 -0
- kafka/future.py +166 -0
- kafka/metrics/__init__.py +13 -0
- kafka/metrics/compound_stat.py +33 -0
- kafka/metrics/dict_reporter.py +81 -0
- kafka/metrics/kafka_metric.py +36 -0
- kafka/metrics/measurable.py +27 -0
- kafka/metrics/measurable_stat.py +13 -0
- kafka/metrics/metric_config.py +33 -0
- kafka/metrics/metric_name.py +105 -0
- kafka/metrics/metrics.py +261 -0
- kafka/metrics/metrics_reporter.py +53 -0
- kafka/metrics/quota.py +41 -0
- kafka/metrics/stat.py +19 -0
- kafka/metrics/stats/__init__.py +15 -0
- kafka/metrics/stats/avg.py +24 -0
- kafka/metrics/stats/count.py +17 -0
- kafka/metrics/stats/histogram.py +99 -0
- kafka/metrics/stats/max_stat.py +17 -0
- kafka/metrics/stats/min_stat.py +19 -0
- kafka/metrics/stats/percentile.py +14 -0
- kafka/metrics/stats/percentiles.py +75 -0
- kafka/metrics/stats/rate.py +118 -0
- kafka/metrics/stats/sampled_stat.py +99 -0
- kafka/metrics/stats/sensor.py +136 -0
- kafka/metrics/stats/total.py +15 -0
- kafka/net/__init__.py +19 -0
- kafka/net/compat.py +165 -0
- kafka/net/connection.py +593 -0
- kafka/net/http_connect.py +144 -0
- kafka/net/inet.py +122 -0
- kafka/net/manager.py +451 -0
- kafka/net/metrics.py +149 -0
- kafka/net/sasl/__init__.py +32 -0
- kafka/net/sasl/abc.py +28 -0
- kafka/net/sasl/gssapi.py +95 -0
- kafka/net/sasl/msk.py +245 -0
- kafka/net/sasl/oauth.py +98 -0
- kafka/net/sasl/plain.py +42 -0
- kafka/net/sasl/scram.py +135 -0
- kafka/net/sasl/sspi.py +111 -0
- kafka/net/selector.py +644 -0
- kafka/net/socks5.py +262 -0
- kafka/net/transport.py +415 -0
- kafka/net/wakeup_notifier.py +72 -0
- kafka/partitioner/__init__.py +8 -0
- kafka/partitioner/abc.py +8 -0
- kafka/partitioner/default.py +89 -0
- kafka/partitioner/sticky.py +109 -0
- kafka/producer/__init__.py +5 -0
- kafka/producer/__main__.py +5 -0
- kafka/producer/future.py +101 -0
- kafka/producer/kafka.py +1123 -0
- kafka/producer/producer_batch.py +192 -0
- kafka/producer/record_accumulator.py +647 -0
- kafka/producer/sender.py +884 -0
- kafka/producer/transaction_manager.py +1326 -0
- kafka/protocol/__init__.py +0 -0
- kafka/protocol/admin/__init__.py +29 -0
- kafka/protocol/admin/acl.py +83 -0
- kafka/protocol/admin/acl.pyi +375 -0
- kafka/protocol/admin/client_quotas.py +14 -0
- kafka/protocol/admin/client_quotas.pyi +265 -0
- kafka/protocol/admin/cluster.py +31 -0
- kafka/protocol/admin/cluster.pyi +620 -0
- kafka/protocol/admin/configs.py +22 -0
- kafka/protocol/admin/configs.pyi +437 -0
- kafka/protocol/admin/groups.py +24 -0
- kafka/protocol/admin/groups.pyi +261 -0
- kafka/protocol/admin/topics.py +53 -0
- kafka/protocol/admin/topics.pyi +982 -0
- kafka/protocol/admin/transactions.py +18 -0
- kafka/protocol/admin/transactions.pyi +311 -0
- kafka/protocol/admin/users.py +14 -0
- kafka/protocol/admin/users.pyi +223 -0
- kafka/protocol/api_data.py +125 -0
- kafka/protocol/api_header.py +55 -0
- kafka/protocol/api_key.py +97 -0
- kafka/protocol/api_message.py +277 -0
- kafka/protocol/broker_version_data.py +246 -0
- kafka/protocol/consumer/__init__.py +13 -0
- kafka/protocol/consumer/fetch.py +16 -0
- kafka/protocol/consumer/fetch.pyi +298 -0
- kafka/protocol/consumer/group.py +38 -0
- kafka/protocol/consumer/group.pyi +824 -0
- kafka/protocol/consumer/metadata.py +30 -0
- kafka/protocol/consumer/metadata.pyi +89 -0
- kafka/protocol/consumer/offsets.py +75 -0
- kafka/protocol/consumer/offsets.pyi +288 -0
- kafka/protocol/data_container.py +166 -0
- kafka/protocol/frame.py +30 -0
- kafka/protocol/generate_stubs.py +468 -0
- kafka/protocol/metadata/__init__.py +10 -0
- kafka/protocol/metadata/api_versions.py +41 -0
- kafka/protocol/metadata/api_versions.pyi +128 -0
- kafka/protocol/metadata/find_coordinator.py +19 -0
- kafka/protocol/metadata/find_coordinator.pyi +105 -0
- kafka/protocol/metadata/metadata.py +34 -0
- kafka/protocol/metadata/metadata.pyi +160 -0
- kafka/protocol/old/__init__.py +0 -0
- kafka/protocol/old/abstract.py +17 -0
- kafka/protocol/old/add_offsets_to_txn.py +54 -0
- kafka/protocol/old/add_partitions_to_txn.py +71 -0
- kafka/protocol/old/admin.py +1086 -0
- kafka/protocol/old/api.py +205 -0
- kafka/protocol/old/api_versions.py +133 -0
- kafka/protocol/old/commit.py +355 -0
- kafka/protocol/old/consumer_protocol.py +36 -0
- kafka/protocol/old/end_txn.py +53 -0
- kafka/protocol/old/fetch.py +408 -0
- kafka/protocol/old/find_coordinator.py +72 -0
- kafka/protocol/old/group.py +451 -0
- kafka/protocol/old/init_producer_id.py +42 -0
- kafka/protocol/old/list_offsets.py +186 -0
- kafka/protocol/old/metadata.py +290 -0
- kafka/protocol/old/offset_for_leader_epoch.py +133 -0
- kafka/protocol/old/produce.py +247 -0
- kafka/protocol/old/sasl_authenticate.py +38 -0
- kafka/protocol/old/sasl_handshake.py +39 -0
- kafka/protocol/old/struct.py +87 -0
- kafka/protocol/old/txn_offset_commit.py +73 -0
- kafka/protocol/old/types.py +440 -0
- kafka/protocol/parser.py +191 -0
- kafka/protocol/producer/__init__.py +7 -0
- kafka/protocol/producer/produce.py +17 -0
- kafka/protocol/producer/produce.pyi +197 -0
- kafka/protocol/producer/transaction.py +30 -0
- kafka/protocol/producer/transaction.pyi +663 -0
- kafka/protocol/sasl.py +52 -0
- kafka/protocol/sasl.pyi +126 -0
- kafka/protocol/schemas/__init__.py +7 -0
- kafka/protocol/schemas/fields/__init__.py +7 -0
- kafka/protocol/schemas/fields/array.py +127 -0
- kafka/protocol/schemas/fields/base.py +156 -0
- kafka/protocol/schemas/fields/codecs/__init__.py +12 -0
- kafka/protocol/schemas/fields/codecs/encode_buffer.py +82 -0
- kafka/protocol/schemas/fields/codecs/tagged_fields.py +109 -0
- kafka/protocol/schemas/fields/codecs/types.py +505 -0
- kafka/protocol/schemas/fields/codegen.py +40 -0
- kafka/protocol/schemas/fields/simple.py +127 -0
- kafka/protocol/schemas/fields/struct.py +357 -0
- kafka/protocol/schemas/fields/struct_array.py +142 -0
- kafka/protocol/schemas/load_json.py +42 -0
- kafka/protocol/schemas/resources/AddOffsetsToTxnRequest.json +40 -0
- kafka/protocol/schemas/resources/AddOffsetsToTxnResponse.json +35 -0
- kafka/protocol/schemas/resources/AddPartitionsToTxnRequest.json +65 -0
- kafka/protocol/schemas/resources/AddPartitionsToTxnResponse.json +60 -0
- kafka/protocol/schemas/resources/AlterClientQuotasRequest.json +47 -0
- kafka/protocol/schemas/resources/AlterClientQuotasResponse.json +41 -0
- kafka/protocol/schemas/resources/AlterConfigsRequest.json +43 -0
- kafka/protocol/schemas/resources/AlterConfigsResponse.json +39 -0
- kafka/protocol/schemas/resources/AlterPartitionReassignmentsRequest.json +42 -0
- kafka/protocol/schemas/resources/AlterPartitionReassignmentsResponse.json +47 -0
- kafka/protocol/schemas/resources/AlterReplicaLogDirsRequest.json +41 -0
- kafka/protocol/schemas/resources/AlterReplicaLogDirsResponse.json +41 -0
- kafka/protocol/schemas/resources/AlterUserScramCredentialsRequest.json +45 -0
- kafka/protocol/schemas/resources/AlterUserScramCredentialsResponse.json +35 -0
- kafka/protocol/schemas/resources/ApiVersionsRequest.json +34 -0
- kafka/protocol/schemas/resources/ApiVersionsResponse.json +79 -0
- kafka/protocol/schemas/resources/ConsumerProtocolAssignment.json +42 -0
- kafka/protocol/schemas/resources/ConsumerProtocolSubscription.json +49 -0
- kafka/protocol/schemas/resources/CreateAclsRequest.json +46 -0
- kafka/protocol/schemas/resources/CreateAclsResponse.json +37 -0
- kafka/protocol/schemas/resources/CreatePartitionsRequest.json +47 -0
- kafka/protocol/schemas/resources/CreatePartitionsResponse.json +41 -0
- kafka/protocol/schemas/resources/CreateTopicsRequest.json +65 -0
- kafka/protocol/schemas/resources/CreateTopicsResponse.json +72 -0
- kafka/protocol/schemas/resources/DeleteAclsRequest.json +46 -0
- kafka/protocol/schemas/resources/DeleteAclsResponse.json +59 -0
- kafka/protocol/schemas/resources/DeleteGroupsRequest.json +30 -0
- kafka/protocol/schemas/resources/DeleteGroupsResponse.json +36 -0
- kafka/protocol/schemas/resources/DeleteRecordsRequest.json +42 -0
- kafka/protocol/schemas/resources/DeleteRecordsResponse.json +43 -0
- kafka/protocol/schemas/resources/DeleteTopicsRequest.json +43 -0
- kafka/protocol/schemas/resources/DeleteTopicsResponse.json +52 -0
- kafka/protocol/schemas/resources/DescribeAclsRequest.json +43 -0
- kafka/protocol/schemas/resources/DescribeAclsResponse.json +55 -0
- kafka/protocol/schemas/resources/DescribeClientQuotasRequest.json +37 -0
- kafka/protocol/schemas/resources/DescribeClientQuotasResponse.json +47 -0
- kafka/protocol/schemas/resources/DescribeClusterRequest.json +35 -0
- kafka/protocol/schemas/resources/DescribeClusterResponse.json +56 -0
- kafka/protocol/schemas/resources/DescribeConfigsRequest.json +42 -0
- kafka/protocol/schemas/resources/DescribeConfigsResponse.json +69 -0
- kafka/protocol/schemas/resources/DescribeGroupsRequest.json +38 -0
- kafka/protocol/schemas/resources/DescribeGroupsResponse.json +74 -0
- kafka/protocol/schemas/resources/DescribeLogDirsRequest.json +38 -0
- kafka/protocol/schemas/resources/DescribeLogDirsResponse.json +65 -0
- kafka/protocol/schemas/resources/DescribeProducersRequest.json +32 -0
- kafka/protocol/schemas/resources/DescribeProducersResponse.json +55 -0
- kafka/protocol/schemas/resources/DescribeQuorumRequest.json +39 -0
- kafka/protocol/schemas/resources/DescribeQuorumResponse.json +82 -0
- kafka/protocol/schemas/resources/DescribeTopicPartitionsRequest.json +40 -0
- kafka/protocol/schemas/resources/DescribeTopicPartitionsResponse.json +66 -0
- kafka/protocol/schemas/resources/DescribeTransactionsRequest.json +27 -0
- kafka/protocol/schemas/resources/DescribeTransactionsResponse.json +52 -0
- kafka/protocol/schemas/resources/DescribeUserScramCredentialsRequest.json +30 -0
- kafka/protocol/schemas/resources/DescribeUserScramCredentialsResponse.json +45 -0
- kafka/protocol/schemas/resources/ElectLeadersRequest.json +41 -0
- kafka/protocol/schemas/resources/ElectLeadersResponse.json +45 -0
- kafka/protocol/schemas/resources/EndTxnRequest.json +43 -0
- kafka/protocol/schemas/resources/EndTxnResponse.json +41 -0
- kafka/protocol/schemas/resources/FetchRequest.json +125 -0
- kafka/protocol/schemas/resources/FetchResponse.json +124 -0
- kafka/protocol/schemas/resources/FindCoordinatorRequest.json +43 -0
- kafka/protocol/schemas/resources/FindCoordinatorResponse.json +58 -0
- kafka/protocol/schemas/resources/HeartbeatRequest.json +39 -0
- kafka/protocol/schemas/resources/HeartbeatResponse.json +35 -0
- kafka/protocol/schemas/resources/IncrementalAlterConfigsRequest.json +44 -0
- kafka/protocol/schemas/resources/IncrementalAlterConfigsResponse.json +38 -0
- kafka/protocol/schemas/resources/InitProducerIdRequest.json +50 -0
- kafka/protocol/schemas/resources/InitProducerIdResponse.json +47 -0
- kafka/protocol/schemas/resources/JoinGroupRequest.json +63 -0
- kafka/protocol/schemas/resources/JoinGroupResponse.json +69 -0
- kafka/protocol/schemas/resources/LeaveGroupRequest.json +47 -0
- kafka/protocol/schemas/resources/LeaveGroupResponse.json +47 -0
- kafka/protocol/schemas/resources/ListConfigResourcesRequest.json +31 -0
- kafka/protocol/schemas/resources/ListConfigResourcesResponse.json +37 -0
- kafka/protocol/schemas/resources/ListGroupsRequest.json +36 -0
- kafka/protocol/schemas/resources/ListGroupsResponse.json +49 -0
- kafka/protocol/schemas/resources/ListOffsetsRequest.json +72 -0
- kafka/protocol/schemas/resources/ListOffsetsResponse.json +71 -0
- kafka/protocol/schemas/resources/ListPartitionReassignmentsRequest.json +34 -0
- kafka/protocol/schemas/resources/ListPartitionReassignmentsResponse.json +46 -0
- kafka/protocol/schemas/resources/ListTransactionsRequest.json +40 -0
- kafka/protocol/schemas/resources/ListTransactionsResponse.json +42 -0
- kafka/protocol/schemas/resources/MetadataRequest.json +56 -0
- kafka/protocol/schemas/resources/MetadataResponse.json +101 -0
- kafka/protocol/schemas/resources/OffsetCommitRequest.json +76 -0
- kafka/protocol/schemas/resources/OffsetCommitResponse.json +71 -0
- kafka/protocol/schemas/resources/OffsetDeleteRequest.json +39 -0
- kafka/protocol/schemas/resources/OffsetDeleteResponse.json +42 -0
- kafka/protocol/schemas/resources/OffsetFetchRequest.json +76 -0
- kafka/protocol/schemas/resources/OffsetFetchResponse.json +107 -0
- kafka/protocol/schemas/resources/OffsetForLeaderEpochRequest.json +52 -0
- kafka/protocol/schemas/resources/OffsetForLeaderEpochResponse.json +51 -0
- kafka/protocol/schemas/resources/ProduceRequest.json +73 -0
- kafka/protocol/schemas/resources/ProduceResponse.json +96 -0
- kafka/protocol/schemas/resources/RequestHeader.json +44 -0
- kafka/protocol/schemas/resources/ResponseHeader.json +26 -0
- kafka/protocol/schemas/resources/SaslAuthenticateRequest.json +29 -0
- kafka/protocol/schemas/resources/SaslAuthenticateResponse.json +34 -0
- kafka/protocol/schemas/resources/SaslHandshakeRequest.json +31 -0
- kafka/protocol/schemas/resources/SaslHandshakeResponse.json +32 -0
- kafka/protocol/schemas/resources/SyncGroupRequest.json +56 -0
- kafka/protocol/schemas/resources/SyncGroupResponse.json +46 -0
- kafka/protocol/schemas/resources/TxnOffsetCommitRequest.json +68 -0
- kafka/protocol/schemas/resources/TxnOffsetCommitResponse.json +47 -0
- kafka/protocol/schemas/resources/UpdateFeaturesRequest.json +43 -0
- kafka/protocol/schemas/resources/UpdateFeaturesResponse.json +39 -0
- kafka/protocol/schemas/resources/WriteTxnMarkersRequest.json +49 -0
- kafka/protocol/schemas/resources/WriteTxnMarkersResponse.json +45 -0
- kafka/protocol/schemas/resources/__init__.py +0 -0
- kafka/record/__init__.py +3 -0
- kafka/record/_crc32c.py +161 -0
- kafka/record/abc.py +144 -0
- kafka/record/default_records.py +782 -0
- kafka/record/legacy_records.py +587 -0
- kafka/record/memory_records.py +255 -0
- kafka/record/util.py +135 -0
- kafka/serializer/__init__.py +4 -0
- kafka/serializer/abstract.py +20 -0
- kafka/serializer/default.py +16 -0
- kafka/serializer/json.py +17 -0
- kafka/serializer/wrapper.py +21 -0
- kafka/structs.py +69 -0
- kafka/util.py +159 -0
- kafka/vendor/__init__.py +0 -0
- kafka/version.py +1 -0
- kafka_python-3.0.0.dist-info/METADATA +319 -0
- kafka_python-3.0.0.dist-info/RECORD +373 -0
- kafka_python-3.0.0.dist-info/WHEEL +5 -0
- kafka_python-3.0.0.dist-info/entry_points.txt +2 -0
- kafka_python-3.0.0.dist-info/licenses/LICENSE +202 -0
- kafka_python-3.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import errno
|
|
3
|
+
import logging
|
|
4
|
+
import random
|
|
5
|
+
import socket
|
|
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
|
+
_WOULD_BLOCK = {errno.EWOULDBLOCK, errno.EAGAIN}
|
|
15
|
+
_MAX_RESPONSE_SIZE = 65536
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class _States:
|
|
19
|
+
DISCONNECTED = '<disconnected>'
|
|
20
|
+
CONNECTING = '<connecting>'
|
|
21
|
+
SENDING = '<sending>'
|
|
22
|
+
READING = '<reading>'
|
|
23
|
+
COMPLETE = '<complete>'
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class HttpConnectProxy(KafkaNetSocket):
|
|
27
|
+
"""Tunnels broker connections through an HTTP CONNECT proxy (RFC 7231 s4.3.6).
|
|
28
|
+
|
|
29
|
+
Registered for the ``http`` scheme -- pass ``proxy_url='http://host:port'``
|
|
30
|
+
to KafkaConsumer/KafkaProducer/KafkaAdminClient.
|
|
31
|
+
|
|
32
|
+
Basic proxy auth is supported via URL credentials: ``http://user:pass@host:8080``.
|
|
33
|
+
Broker hostnames are always forwarded unresolved so the proxy handles DNS.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
SCHEMES = ('http',)
|
|
37
|
+
|
|
38
|
+
def __init__(self, proxy_url):
|
|
39
|
+
self._proxy_url = urlparse(proxy_url)
|
|
40
|
+
self._sock = None
|
|
41
|
+
self._state = _States.DISCONNECTED
|
|
42
|
+
self._send_buf = b''
|
|
43
|
+
self._recv_buf = b''
|
|
44
|
+
self._proxy_addr = self._get_proxy_addr()
|
|
45
|
+
|
|
46
|
+
def _get_proxy_addr(self):
|
|
47
|
+
addrs = self.dns_lookup(self._proxy_url.hostname, self._proxy_url.port, proxy=True)
|
|
48
|
+
if not addrs:
|
|
49
|
+
raise KafkaConnectionError('Unable to resolve proxy_url via dns')
|
|
50
|
+
return random.choice(addrs)
|
|
51
|
+
|
|
52
|
+
def dns_lookup(self, host, port, proxy=False):
|
|
53
|
+
if proxy:
|
|
54
|
+
return super().dns_lookup(host, port, raise_error=True)
|
|
55
|
+
# Always forward broker hostname unresolved; the proxy handles DNS
|
|
56
|
+
return [(socket.AF_UNSPEC, socket.SOCK_STREAM, socket.IPPROTO_TCP, '', (host, port))]
|
|
57
|
+
|
|
58
|
+
def socket(self, family=socket.AF_UNSPEC, sock_type=socket.SOCK_STREAM, proto=socket.IPPROTO_TCP):
|
|
59
|
+
self._target_afi = family
|
|
60
|
+
proxy_family, _, _, _, _ = self._proxy_addr
|
|
61
|
+
self._sock = socket.socket(proxy_family, sock_type, proto)
|
|
62
|
+
return self._sock
|
|
63
|
+
|
|
64
|
+
def connect_ex(self, sock, addr):
|
|
65
|
+
assert sock is self._sock
|
|
66
|
+
|
|
67
|
+
if self._state == _States.DISCONNECTED:
|
|
68
|
+
self._state = _States.CONNECTING
|
|
69
|
+
|
|
70
|
+
if self._state == _States.CONNECTING:
|
|
71
|
+
ret = self._do_connecting(addr)
|
|
72
|
+
if ret is not None:
|
|
73
|
+
return ret
|
|
74
|
+
|
|
75
|
+
if self._state == _States.SENDING:
|
|
76
|
+
ret = self._do_sending()
|
|
77
|
+
if ret is not None:
|
|
78
|
+
return ret
|
|
79
|
+
|
|
80
|
+
if self._state == _States.READING:
|
|
81
|
+
ret = self._do_reading()
|
|
82
|
+
if ret is not None:
|
|
83
|
+
return ret
|
|
84
|
+
|
|
85
|
+
if self._state == _States.COMPLETE:
|
|
86
|
+
return 0
|
|
87
|
+
|
|
88
|
+
return errno.ECONNREFUSED
|
|
89
|
+
|
|
90
|
+
def _do_connecting(self, addr):
|
|
91
|
+
_, _, _, _, proxy_sockaddr = self._proxy_addr
|
|
92
|
+
ret = self._sock.connect_ex(proxy_sockaddr)
|
|
93
|
+
if ret and ret != errno.EISCONN:
|
|
94
|
+
return ret
|
|
95
|
+
host, port = addr[0], addr[1]
|
|
96
|
+
headers = 'CONNECT {0}:{1} HTTP/1.1\r\nHost: {0}:{1}\r\n'.format(host, port)
|
|
97
|
+
if self._proxy_url.username and self._proxy_url.password:
|
|
98
|
+
credentials = base64.b64encode(
|
|
99
|
+
'{0}:{1}'.format(self._proxy_url.username, self._proxy_url.password).encode()
|
|
100
|
+
).decode()
|
|
101
|
+
headers += 'Proxy-Authorization: Basic {}\r\n'.format(credentials)
|
|
102
|
+
self._send_buf = (headers + '\r\n').encode()
|
|
103
|
+
self._state = _States.SENDING
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
def _do_sending(self):
|
|
107
|
+
while self._send_buf:
|
|
108
|
+
try:
|
|
109
|
+
sent = self._sock.send(self._send_buf)
|
|
110
|
+
if sent == 0:
|
|
111
|
+
log.error('Proxy closed connection while sending CONNECT request')
|
|
112
|
+
return errno.ECONNREFUSED
|
|
113
|
+
self._send_buf = self._send_buf[sent:]
|
|
114
|
+
except OSError as exc:
|
|
115
|
+
if exc.errno in _WOULD_BLOCK:
|
|
116
|
+
return errno.EWOULDBLOCK
|
|
117
|
+
raise
|
|
118
|
+
self._state = _States.READING
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
def _do_reading(self):
|
|
122
|
+
while b'\r\n\r\n' not in self._recv_buf:
|
|
123
|
+
try:
|
|
124
|
+
chunk = self._sock.recv(4096)
|
|
125
|
+
if not chunk:
|
|
126
|
+
log.error('Proxy closed connection during CONNECT handshake')
|
|
127
|
+
self._sock.close()
|
|
128
|
+
return errno.ECONNREFUSED
|
|
129
|
+
self._recv_buf += chunk
|
|
130
|
+
if len(self._recv_buf) > _MAX_RESPONSE_SIZE:
|
|
131
|
+
log.error('Proxy response exceeded %d bytes without end-of-headers', _MAX_RESPONSE_SIZE)
|
|
132
|
+
self._sock.close()
|
|
133
|
+
return errno.ECONNREFUSED
|
|
134
|
+
except OSError as exc:
|
|
135
|
+
if exc.errno in _WOULD_BLOCK:
|
|
136
|
+
return errno.EWOULDBLOCK
|
|
137
|
+
raise
|
|
138
|
+
first_line = self._recv_buf.split(b'\r\n')[0]
|
|
139
|
+
if b' 200 ' in first_line or first_line.endswith(b' 200'):
|
|
140
|
+
self._state = _States.COMPLETE
|
|
141
|
+
return None
|
|
142
|
+
log.error('HTTP CONNECT to proxy failed: %r', first_line)
|
|
143
|
+
self._sock.close()
|
|
144
|
+
return errno.ECONNREFUSED
|
kafka/net/inet.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import errno
|
|
2
|
+
import logging
|
|
3
|
+
import socket
|
|
4
|
+
import time
|
|
5
|
+
from urllib.parse import urlparse
|
|
6
|
+
|
|
7
|
+
import kafka.errors as Errors
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
log = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
async def create_connection(net, host, port, socket_options=(), proxy_url=None, timeout_at=None):
|
|
14
|
+
"""Connect to host:port; raises KafkaConnectionError on failure"""
|
|
15
|
+
socket_factory = KafkaNetSocket(proxy_url)
|
|
16
|
+
addrs = socket_factory.dns_lookup(host, port)
|
|
17
|
+
exceptions = [Errors.KafkaConnectionError('DNS Resolution failure')]
|
|
18
|
+
for res in addrs:
|
|
19
|
+
try:
|
|
20
|
+
log.debug('%s: Attempting to connect to %s (options: %s)', socket_factory, res, socket_options)
|
|
21
|
+
sock = await socket_factory.connect(net, res, socket_options, timeout_at=timeout_at)
|
|
22
|
+
except (socket.error, OSError) as e:
|
|
23
|
+
exceptions.append(Errors.KafkaConnectionError('unable to connect: %s' % (e,)))
|
|
24
|
+
continue
|
|
25
|
+
except Errors.KafkaTimeoutError:
|
|
26
|
+
raise Errors.KafkaConnectionError('Connection timed out')
|
|
27
|
+
except Errors.KafkaConnectionError as e:
|
|
28
|
+
exceptions.append(e)
|
|
29
|
+
continue
|
|
30
|
+
else:
|
|
31
|
+
return sock
|
|
32
|
+
raise exceptions[-1]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class KafkaNetSocket:
|
|
36
|
+
# scheme => handling class
|
|
37
|
+
_registry = {}
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def register_class(cls, klass):
|
|
41
|
+
for scheme in klass.SCHEMES:
|
|
42
|
+
cls._registry[scheme] = klass
|
|
43
|
+
|
|
44
|
+
def __init_subclass__(cls, **kw):
|
|
45
|
+
super().__init_subclass__(**kw)
|
|
46
|
+
KafkaNetSocket.register_class(cls)
|
|
47
|
+
|
|
48
|
+
def __new__(cls, proxy_url=None):
|
|
49
|
+
if proxy_url is None:
|
|
50
|
+
return super().__new__(cls)
|
|
51
|
+
try:
|
|
52
|
+
parsed = urlparse(proxy_url)
|
|
53
|
+
except Exception:
|
|
54
|
+
raise ValueError('Unable to parse proxy_url: %s' % (proxy_url,))
|
|
55
|
+
if not parsed.scheme:
|
|
56
|
+
raise ValueError('proxy_url requires scheme:// (%s)' % (proxy_url,))
|
|
57
|
+
try:
|
|
58
|
+
klass = KafkaNetSocket._registry[parsed.scheme]
|
|
59
|
+
except KeyError:
|
|
60
|
+
raise ValueError('Unsupported proxy url scheme: %s' % (parsed.scheme))
|
|
61
|
+
return super().__new__(klass)
|
|
62
|
+
|
|
63
|
+
def __init__(self, proxy_url=None):
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
# simple sockets / no proxy
|
|
67
|
+
def dns_lookup(self, host, port, raise_error=False):
|
|
68
|
+
# XXX: all DNS functions in Python are blocking. If we really
|
|
69
|
+
# want to be non-blocking here, we need to use a 3rd-party
|
|
70
|
+
# library like python-adns, or move resolution onto its
|
|
71
|
+
# own thread. This will be subject to the default libc
|
|
72
|
+
# name resolution timeout (5s on most Linux boxes)
|
|
73
|
+
try:
|
|
74
|
+
return socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM)
|
|
75
|
+
except socket.gaierror as ex:
|
|
76
|
+
err_str = "DNS lookup failed for %s:%d, %r" % (host, port, ex)
|
|
77
|
+
if not raise_error:
|
|
78
|
+
log.warning(err_str)
|
|
79
|
+
return []
|
|
80
|
+
raise Errors.KafkaConnectionError(err_str)
|
|
81
|
+
|
|
82
|
+
def socket(self, family=socket.AF_UNSPEC, sock_type=socket.SOCK_STREAM, proto=socket.IPPROTO_TCP):
|
|
83
|
+
return socket.socket(family, sock_type, proto)
|
|
84
|
+
|
|
85
|
+
async def connect(self, net, addrinfo, socket_options=(), timeout_at=None):
|
|
86
|
+
"""Create non-blocking socket (with options) and connect to addrinfo tuple"""
|
|
87
|
+
family, sock_type, proto, _canonname, sockaddr = addrinfo
|
|
88
|
+
sock = self.socket(family, sock_type, proto)
|
|
89
|
+
sock.setblocking(False)
|
|
90
|
+
for option in socket_options:
|
|
91
|
+
sock.setsockopt(*option)
|
|
92
|
+
return await self.sock_connect(net, sock, sockaddr, timeout_at=timeout_at)
|
|
93
|
+
|
|
94
|
+
async def sock_connect(self, net, sock, sockaddr, timeout_at=None):
|
|
95
|
+
while timeout_at is None or time.monotonic() < timeout_at:
|
|
96
|
+
ret = None
|
|
97
|
+
try:
|
|
98
|
+
ret = self.connect_ex(sock, sockaddr)
|
|
99
|
+
except BlockingIOError:
|
|
100
|
+
ret = errno.EWOULDBLOCK
|
|
101
|
+
except socket.error as err:
|
|
102
|
+
ret = err.errno
|
|
103
|
+
|
|
104
|
+
# Connection succeeded
|
|
105
|
+
if not ret or ret == errno.EISCONN:
|
|
106
|
+
log.debug('Connected: %s', sock)
|
|
107
|
+
return sock
|
|
108
|
+
|
|
109
|
+
# Needs retry
|
|
110
|
+
# WSAEINVAL == 10022, but errno.WSAEINVAL is not available on non-win systems
|
|
111
|
+
elif ret in (errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK, 10022):
|
|
112
|
+
await net.wait_write(sock, timeout_at=timeout_at)
|
|
113
|
+
|
|
114
|
+
# Connection failed
|
|
115
|
+
else:
|
|
116
|
+
errstr = errno.errorcode.get(ret, 'UNKNOWN')
|
|
117
|
+
raise Errors.KafkaConnectionError('{} {}'.format(ret, errstr))
|
|
118
|
+
else:
|
|
119
|
+
raise Errors.KafkaTimeoutError('Connection timed out')
|
|
120
|
+
|
|
121
|
+
def connect_ex(self, sock, sockaddr):
|
|
122
|
+
return sock.connect_ex(sockaddr)
|