Skip to content

Commit 2ae798f

Browse files
authored
Introduce better domain protocol validation (#19)
1 parent f4bf7cf commit 2ae798f

File tree

11 files changed

+107
-88
lines changed

11 files changed

+107
-88
lines changed

src/collectors/bitcoin.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
import asyncio
22
from bitcoinrpc import BitcoinRPC
3-
from helpers import strip_url, validate_protocol, generate_labels_from_metadata
3+
from helpers import check_protocol, strip_url, generate_labels_from_metadata
44
from time import perf_counter
55
from settings import logger
66

77

88
class bitcoin_collector():
99

1010
def __init__(self, rpc_metadata):
11-
validate_protocol(rpc_metadata['url'], "https")
1211
self.url = rpc_metadata['url']
13-
self.labels, self.labels_values = generate_labels_from_metadata(rpc_metadata)
12+
if check_protocol(self.url, "https"):
13+
self.labels, self.labels_values = generate_labels_from_metadata(rpc_metadata)
14+
else:
15+
logger.error("Please provide https endpoint for {}".format(strip_url(self.url)))
16+
exit(1)
1417

1518
async def _probe(self, metrics):
1619
try:

src/collectors/cardano.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
from settings import logger
22
import json
33
from collectors.ws import websocket_collector
4-
from helpers import strip_url, validate_protocol, generate_labels_from_metadata
4+
from helpers import check_protocol, strip_url, generate_labels_from_metadata
55

66

77
class cardano_collector():
88

99
def __init__(self, rpc_metadata):
10-
validate_protocol(rpc_metadata['url'], "wss")
1110
self.url = rpc_metadata['url']
12-
13-
self.labels, self.labels_values = generate_labels_from_metadata(rpc_metadata)
14-
self.ws_collector = websocket_collector(self.url)
11+
if check_protocol(self.url, "wss") or check_protocol(self.url, "ws"):
12+
self.labels, self.labels_values = generate_labels_from_metadata(rpc_metadata)
13+
self.ws_collector = websocket_collector(self.url)
14+
else:
15+
logger.error("Please provide https endpoint for {}".format(strip_url(self.url)))
16+
exit(1)
1517

1618
def _get_block_height(self):
1719
blk_height_payload = {

src/collectors/conflux.py

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,32 @@
11
from settings import cfg, logger
22
from conflux_web3 import Web3
33
import asyncio
4-
from helpers import strip_url, validate_protocol, generate_labels_from_metadata
4+
from helpers import strip_url, check_protocol, generate_labels_from_metadata
55
from collectors.ws import websocket_collector
66

77

88
class conflux_collector():
99

1010
def __init__(self, rpc_metadata):
11-
validate_protocol(rpc_metadata['url'], "wss")
1211
self.url = rpc_metadata['url']
13-
self.labels, self.labels_values = generate_labels_from_metadata(rpc_metadata)
14-
self.client = Web3(Web3.WebsocketProvider(self.url, websocket_timeout=cfg.response_timeout))
15-
self.labels, self.labels_values = generate_labels_from_metadata(rpc_metadata)
12+
if check_protocol(self.url, "wss") or check_protocol(self.url, 'ws'):
13+
self.labels, self.labels_values = generate_labels_from_metadata(rpc_metadata)
14+
self.labels, self.labels_values = generate_labels_from_metadata(rpc_metadata)
15+
self.client = Web3(Web3.WebsocketProvider(self.url, websocket_timeout=cfg.response_timeout))
16+
self.labels, self.labels_values = generate_labels_from_metadata(rpc_metadata)
1617

17-
self.ws_collector = websocket_collector(self.url,
18-
sub_payload={
19-
"method": "cfx_subscribe",
20-
"jsonrpc": "2.0",
21-
"id": 1,
22-
"params": ["newHeads"]
23-
})
24-
self.ws_collector.setDaemon(True)
25-
self.ws_collector.start()
18+
self.ws_collector = websocket_collector(self.url,
19+
sub_payload={
20+
"method": "cfx_subscribe",
21+
"jsonrpc": "2.0",
22+
"id": 1,
23+
"params": ["newHeads"]
24+
})
25+
self.ws_collector.setDaemon(True)
26+
self.ws_collector.start()
27+
else:
28+
logger.error("Please provide wss/ws endpoint for {}".format(strip_url(self.url)))
29+
exit(1)
2630

2731
def probe(self, metrics):
2832
try:
@@ -42,6 +46,8 @@ def probe(self, metrics):
4246
)
4347

4448
metrics['brpc_gas_price'].add_metric(self.labels_values, self.client.cfx.gas_price)
49+
metrics['brpc_client_version'].add_metric(self.labels_values,
50+
value={"client_version": self.client.clientVersion})
4551
else:
4652
logger.info("Client is not connected to {}".format(strip_url(self.url)))
4753
metrics['brpc_health'].add_metric(self.labels_values, False)

src/collectors/dogecoin.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from helpers import strip_url, validate_protocol, generate_labels_from_metadata
1+
from helpers import strip_url, check_protocol, generate_labels_from_metadata
22
from settings import logger
33
from time import perf_counter
44
import requests
@@ -7,9 +7,12 @@
77
class doge_collector():
88

99
def __init__(self, rpc_metadata):
10-
validate_protocol(rpc_metadata['url'], "https")
1110
self.url = rpc_metadata['url']
12-
self.labels, self.labels_values = generate_labels_from_metadata(rpc_metadata)
11+
if check_protocol(self.url, "https"):
12+
self.labels, self.labels_values = generate_labels_from_metadata(rpc_metadata)
13+
else:
14+
logger.error("Please provide https endpoint for {}".format(strip_url(self.url)))
15+
exit(1)
1316

1417
def probe(self, metrics):
1518
try:

src/collectors/evm.py

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,32 @@
11
from settings import cfg, logger
22
from web3 import Web3
33
import asyncio
4-
from helpers import strip_url, validate_protocol, generate_labels_from_metadata
4+
from helpers import strip_url, check_protocol, generate_labels_from_metadata
55
from collectors.ws import websocket_collector
66
from websockets.exceptions import WebSocketException
77

88

99
class evm_collector():
1010

1111
def __init__(self, rpc_metadata):
12-
validate_protocol(rpc_metadata['url'], "wss")
1312
self.url = rpc_metadata['url']
14-
self.client = Web3(Web3.WebsocketProvider(self.url, websocket_timeout=cfg.response_timeout))
15-
16-
self.labels, self.labels_values = generate_labels_from_metadata(rpc_metadata)
17-
18-
self.ws_collector = websocket_collector(self.url,
19-
sub_payload={
20-
"method": "eth_subscribe",
21-
"jsonrpc": "2.0",
22-
"id": rpc_metadata['chain_id'],
23-
"params": ["newHeads"]
24-
})
25-
self.net_peer_enabled = True
26-
self.ws_collector.setDaemon(True)
27-
self.ws_collector.start()
13+
if check_protocol(rpc_metadata['url'], "wss") or check_protocol(rpc_metadata['url'], 'ws'):
14+
self.client = Web3(Web3.WebsocketProvider(self.url, websocket_timeout=cfg.response_timeout))
15+
16+
self.labels, self.labels_values = generate_labels_from_metadata(rpc_metadata)
17+
18+
self.ws_collector = websocket_collector(self.url,
19+
sub_payload={
20+
"method": "eth_subscribe",
21+
"jsonrpc": "2.0",
22+
"id": rpc_metadata['chain_id'],
23+
"params": ["newHeads"]
24+
})
25+
self.ws_collector.setDaemon(True)
26+
self.ws_collector.start()
27+
else:
28+
logger.error("Please provide wss/ws endpoint for {}".format(strip_url(self.url)))
29+
exit(1)
2830

2931
def probe(self, metrics):
3032
try:
@@ -35,21 +37,13 @@ def probe(self, metrics):
3537
metrics['brpc_latency'].add_metric(self.labels_values, self.ws_collector.get_latency())
3638
metrics['brpc_block_height'].add_metric(self.labels_values, self.client.eth.block_number)
3739
metrics['brpc_total_difficulty'].add_metric(self.labels_values,
38-
self.client.eth.get_block('latest')['totalDifficulty'])
40+
self.client.eth.get_block('latest')['totalDifficulty'])
3941
metrics['brpc_difficulty'].add_metric(self.labels_values,
40-
self.client.eth.get_block('latest')['difficulty'])
41-
42-
try:
43-
if self.net_peer_enabled:
44-
metrics['brpc_net_peer_count'].add_metric(self.labels_values, self.client.net.peer_count)
45-
except ValueError:
46-
logger.error(
47-
"Net peer function is not supported for this chain, the collector will ignore this from this point on."
48-
)
49-
self.net_peer_enabled = False
50-
42+
self.client.eth.get_block('latest')['difficulty'])
5143
metrics['brpc_gas_price'].add_metric(self.labels_values, self.client.eth.gas_price)
5244
metrics['brpc_max_priority_fee'].add_metric(self.labels_values, self.client.eth.max_priority_fee)
45+
metrics['brpc_client_version'].add_metric(self.labels_values,
46+
value={"client_version": self.client.clientVersion})
5347
else:
5448
logger.info("Client is not connected to {}".format(strip_url(self.url)))
5549
metrics['brpc_health'].add_metric(self.labels_values, False)

src/collectors/filecoin.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from helpers import strip_url, validate_protocol, generate_labels_from_metadata
1+
from helpers import strip_url, check_protocol, generate_labels_from_metadata
22
from settings import logger
33
from time import perf_counter
44
import requests
@@ -7,10 +7,12 @@
77
class filecoin_collector():
88

99
def __init__(self, rpc_metadata):
10-
validate_protocol(rpc_metadata['url'], "https")
1110
self.url = rpc_metadata['url']
12-
13-
self.labels, self.labels_values = generate_labels_from_metadata(rpc_metadata)
11+
if check_protocol(self.url, "https"):
12+
self.labels, self.labels_values = generate_labels_from_metadata(rpc_metadata)
13+
else:
14+
logger.error("Please provide https endpoint for {}".format(strip_url(self.url)))
15+
exit(1)
1416

1517
def probe(self, metrics):
1618
try:

src/collectors/solana.py

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
from settings import cfg, logger
22
from solana.rpc.api import Client
3-
from helpers import strip_url, url_join, validate_protocol, generate_labels_from_metadata
3+
from helpers import strip_url, url_join, check_protocol, generate_labels_from_metadata
44
from collectors.ws import websocket_collector
55
import requests
66

77

88
class solana_collector():
99

1010
def __init__(self, rpc_metadata):
11+
self.url = rpc_metadata['url']
12+
if check_protocol(self.url, "https"):
13+
self.health_uri = url_join(self.url, "/health")
14+
self.client = Client(self.url, timeout=cfg.response_timeout)
15+
16+
self.labels, self.labels_values = generate_labels_from_metadata(rpc_metadata)
17+
else:
18+
logger.error("Please provide https endpoint for {}".format(strip_url(self.url)))
19+
exit(1)
1120

1221
try:
1322
self.subscribe_url = rpc_metadata['subscribe_url']
@@ -16,21 +25,18 @@ def __init__(self, rpc_metadata):
1625
"Please note that solana collector requires subscribe_url websocket endpoint on top of regular url. Please refer to example configuration for example"
1726
)
1827

19-
validate_protocol(rpc_metadata['subscribe_url'], 'wss')
20-
validate_protocol(rpc_metadata['url'], 'https')
21-
self.url = rpc_metadata['url']
22-
self.health_uri = url_join(self.url, "/health")
23-
self.client = Client(self.url, timeout=cfg.response_timeout)
24-
25-
self.labels, self.labels_values = generate_labels_from_metadata(rpc_metadata)
26-
self.ws_collector = websocket_collector(self.subscribe_url,
27-
sub_payload={
28-
"jsonrpc": "2.0",
29-
"id": 1,
30-
"method": "slotSubscribe"
31-
})
32-
self.ws_collector.setDaemon(True)
33-
self.ws_collector.start()
28+
if check_protocol(self.subscribe_url, "wss") or check_protocol(self.subscribe_url, 'ws'):
29+
self.ws_collector = websocket_collector(self.subscribe_url,
30+
sub_payload={
31+
"jsonrpc": "2.0",
32+
"id": 1,
33+
"method": "slotSubscribe"
34+
})
35+
self.ws_collector.setDaemon(True)
36+
self.ws_collector.start()
37+
else:
38+
logger.error("Please provide wss/ws endpoint for {}".format(strip_url(self.url)))
39+
exit(1)
3440

3541
def is_connected(self) -> bool:
3642
"""Health check."""

src/collectors/starkware.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
from settings import logger
2-
from helpers import strip_url, validate_protocol, generate_labels_from_metadata
2+
from helpers import strip_url, check_protocol, generate_labels_from_metadata
33
from time import perf_counter
44
import requests
55

66

77
class starkware_collector():
88

99
def __init__(self, rpc_metadata):
10-
validate_protocol(rpc_metadata['url'], "https")
1110
self.url = rpc_metadata['url']
12-
13-
self.labels, self.labels_values = generate_labels_from_metadata(rpc_metadata)
11+
if check_protocol(self.url, "https"):
12+
self.labels, self.labels_values = generate_labels_from_metadata(rpc_metadata)
13+
else:
14+
logger.error("Please provide https endpoint for {}".format(strip_url(self.url)))
15+
exit(1)
1416

1517
def probe(self, metrics):
1618
try:

src/exporter.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from concurrent.futures import ThreadPoolExecutor
22
from prometheus_client import REGISTRY, make_wsgi_app
3-
from prometheus_client.metrics_core import GaugeMetricFamily, CounterMetricFamily
3+
from prometheus_client.metrics_core import GaugeMetricFamily, CounterMetricFamily, InfoMetricFamily
44
from wsgiref.simple_server import make_server
55
from helpers import strip_url
66
from collectors.evm import evm_collector
@@ -65,7 +65,6 @@ def _instantiate_bitcoin(self):
6565
for item in cfg.endpoints:
6666
logger.info("Initializing bitcoin node {}".format(strip_url(item['url'])))
6767
self.collectors.append(bitcoin_collector(item))
68-
print(self.collectors[0].labels)
6968
self.labels = self.collectors[0].labels
7069

7170
def _instantiate_doge(self):
@@ -117,7 +116,11 @@ def collect(self):
117116
"brpc_net_peer_count":
118117
GaugeMetricFamily('brpc_net_peer_count',
119118
'Number of peers currently connected to the client.',
120-
labels=self.labels)
119+
labels=self.labels),
120+
"brpc_client_version":
121+
InfoMetricFamily('brpc_client_version',
122+
'Client version for the particular RPC endpoint.',
123+
labels=self.labels)
121124
}
122125

123126
def write_metrics(prom_collector, metrics):

src/helpers.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,8 @@ def strip_url(url):
88
return urllib.parse.urlparse(url).hostname
99

1010

11-
def validate_protocol(url, protocol):
12-
if urllib.parse.urlparse(url).scheme == protocol:
13-
pass
14-
else:
15-
logger.error("Non {} endpoint provided for {}".format(protocol, strip_url(url)))
16-
exit(1)
17-
18-
1911
def check_protocol(url, protocol):
12+
# Allow both ws and wss if either ws or wss is used to check protocols
2013
return urllib.parse.urlparse(url).scheme == protocol
2114

2215

0 commit comments

Comments
 (0)