Skip to content

Commit d0bad96

Browse files
committed
Updates on metrics
1 parent 290c519 commit d0bad96

5 files changed

Lines changed: 47 additions & 71 deletions

File tree

awscrt/aws_iot_metrics.py

Lines changed: 28 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -63,32 +63,36 @@ class _MetricsFeatureId(str, Enum):
6363
# Feature Value Constants
6464

6565

66-
class _MetricsProtocolVersionValue(str, Enum):
67-
"""Protocol version values for metrics encoding.
66+
def _protocol_version_metrics_value(protocol):
67+
"""Map protocol version to its single-character metrics value.
6868
69-
Maps MQTT protocol versions to their single-character metric representations.
69+
Mapping: MQTT311->3, MQTT5->5.
7070
"""
71-
MQTT311 = "3"
72-
MQTT5 = "5"
71+
mapping = {
72+
"MQTT311": "3",
73+
"MQTT5": "5",
74+
}
75+
return mapping.get(protocol)
7376

7477

75-
class _MetricsSocketImplementationValue(str, Enum):
76-
"""Socket implementation values for metrics encoding.
78+
def _socket_implementation_metrics_value():
79+
"""Detect the socket implementation and return its single-character metrics value.
7780
78-
Maps the underlying platform socket layer to its metric representation.
79-
POSIX covers macOS and Linux; WINSOCK covers Windows.
81+
Mapping: Windows (WINSOCK)->B, all other platforms (POSIX)->A.
8082
"""
81-
POSIX = "A"
82-
WINSOCK = "B"
83+
if sys.platform == "win32":
84+
return "B"
85+
return "A"
8386

8487

85-
class _MetricsHttpProxyTypeValue(str, Enum):
86-
"""HTTP proxy type values for metrics encoding.
88+
def _http_proxy_type_metrics_value(proxy_options):
89+
"""Map proxy options to the single-character metrics value for proxy type.
8790
88-
Indicates whether the proxy connection uses plain HTTP or HTTPS (TLS).
91+
Mapping: HTTPS (has tls_connection_options)->B, HTTP->A.
8992
"""
90-
HTTP = "A"
91-
HTTPS = "B"
93+
if getattr(proxy_options, 'tls_connection_options', None) is not None:
94+
return "B"
95+
return "A"
9296

9397
# Mappings from existing enums to metrics values
9498

@@ -200,16 +204,6 @@ def _tls_cipher_preference_metrics_value(pref):
200204
return mapping.get(pref)
201205

202206

203-
def _detect_socket_implementation():
204-
"""Detect the socket implementation based on the current platform.
205-
206-
Returns _MetricsSocketImplementationValue.WINSOCK on Windows,
207-
_MetricsSocketImplementationValue.POSIX on all other platforms
208-
(macOS, Linux).
209-
"""
210-
if sys.platform == "win32":
211-
return _MetricsSocketImplementationValue.WINSOCK
212-
return _MetricsSocketImplementationValue.POSIX
213207

214208

215209
# MQTT5 encoding list
@@ -277,18 +271,15 @@ def _get_encoded_feature_list(client_options):
277271
features.append(f"{_MetricsFeatureId.INBOUND_TOPIC_ALIAS_BEHAVIOR.value}/{val}")
278272

279273
# F: protocol_version - MQTT5 always uses client options
280-
features.append(f"{_MetricsFeatureId.PROTOCOL_VERSION.value}/{_MetricsProtocolVersionValue.MQTT5.value}")
274+
features.append(f"{_MetricsFeatureId.PROTOCOL_VERSION.value}/{_protocol_version_metrics_value('MQTT5')}")
281275

282276
# G: socket_implementation - Detect based on platform
283-
features.append(f"{_MetricsFeatureId.SOCKET_IMPLEMENTATION.value}/{_detect_socket_implementation().value}")
277+
features.append(f"{_MetricsFeatureId.SOCKET_IMPLEMENTATION.value}/{_socket_implementation_metrics_value()}")
284278

285279
# H: http_proxy_type - Determine based on whether proxy uses TLS
286280
if client_options.http_proxy_options is not None:
287-
proxy_type = _MetricsHttpProxyTypeValue.HTTPS if getattr(
288-
client_options.http_proxy_options,
289-
'tls_connection_options',
290-
None) is not None else _MetricsHttpProxyTypeValue.HTTP
291-
features.append(f"{_MetricsFeatureId.HTTP_PROXY_TYPE.value}/{proxy_type.value}")
281+
val = _http_proxy_type_metrics_value(client_options.http_proxy_options)
282+
features.append(f"{_MetricsFeatureId.HTTP_PROXY_TYPE.value}/{val}")
292283

293284
# I: certificate_source - Would need to be tracked from TLS context setup. This is set at a IoT SDK level
294285

@@ -329,14 +320,13 @@ def _get_encoded_feature_list_mqtt3(proxy_options, tls_ctx=None):
329320
str: The encoded feature list string.
330321
"""
331322
features = [
332-
f"{_MetricsFeatureId.PROTOCOL_VERSION.value}/{_MetricsProtocolVersionValue.MQTT311.value}",
333-
f"{_MetricsFeatureId.SOCKET_IMPLEMENTATION.value}/{_detect_socket_implementation().value}"
323+
f"{_MetricsFeatureId.PROTOCOL_VERSION.value}/{_protocol_version_metrics_value('MQTT311')}",
324+
f"{_MetricsFeatureId.SOCKET_IMPLEMENTATION.value}/{_socket_implementation_metrics_value()}"
334325
]
335326
# H: http_proxy_type - Determine based on whether proxy uses TLS
336327
if proxy_options is not None:
337-
proxy_type = _MetricsHttpProxyTypeValue.HTTPS if getattr(
338-
proxy_options, 'tls_connection_options', None) is not None else _MetricsHttpProxyTypeValue.HTTP
339-
features.append(f"{_MetricsFeatureId.HTTP_PROXY_TYPE.value}/{proxy_type.value}")
328+
val = _http_proxy_type_metrics_value(proxy_options)
329+
features.append(f"{_MetricsFeatureId.HTTP_PROXY_TYPE.value}/{val}")
340330

341331
# J: tls_cipher_preference - security policy
342332
if tls_ctx is not None:

awscrt/mqtt5.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1821,9 +1821,7 @@ def __init__(self, client_options: ClientOptions):
18211821
socket_options = SocketOptions()
18221822

18231823
# Handle metrics configuration
1824-
enable_metrics = True
18251824
if client_options.disable_metrics:
1826-
enable_metrics = False
18271825
self._metrics = None
18281826
else:
18291827
self._metrics = _create_metrics_mqtt5(client_options)
@@ -1879,7 +1877,7 @@ def __init__(self, client_options: ClientOptions):
18791877
client_options.ack_timeout_sec,
18801878
client_options.topic_aliasing_options,
18811879
websocket_is_none,
1882-
enable_metrics,
1880+
not client_options.disable_metrics,
18831881
self._metrics,
18841882
core)
18851883

source/mqtt5_client.c

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -841,10 +841,6 @@ bool aws_py_metrics_parse(PyObject *metrics_py, struct aws_mqtt_iot_metrics *out
841841
PyObject *library_name_py = PyObject_GetAttrString(metrics_py, "library_name");
842842
out_metrics->library_name = aws_byte_cursor_from_pyunicode(library_name_py);
843843
Py_XDECREF(library_name_py);
844-
if (!out_metrics->library_name.ptr) {
845-
PyErr_SetString(PyExc_TypeError, "metrics.library_name must be str type");
846-
return false;
847-
}
848844

849845
PyObject *metadata_entries_py = PyObject_GetAttrString(metrics_py, "metadata_entries");
850846

@@ -853,11 +849,6 @@ bool aws_py_metrics_parse(PyObject *metrics_py, struct aws_mqtt_iot_metrics *out
853849
if (count > 0) {
854850
struct aws_mqtt_metadata_entry *entries =
855851
aws_mem_calloc(aws_py_get_allocator(), (size_t)count, sizeof(struct aws_mqtt_metadata_entry));
856-
if (!entries) {
857-
Py_XDECREF(metadata_entries_py);
858-
PyErr_SetAwsLastError();
859-
return false;
860-
}
861852

862853
for (Py_ssize_t i = 0; i < count; ++i) {
863854
PyObject *entry_py = PyList_GetItem(metadata_entries_py, i);
@@ -869,13 +860,6 @@ bool aws_py_metrics_parse(PyObject *metrics_py, struct aws_mqtt_iot_metrics *out
869860

870861
Py_XDECREF(key_py);
871862
Py_XDECREF(value_py);
872-
873-
if (!entries[i].key.ptr || !entries[i].value.ptr) {
874-
Py_XDECREF(metadata_entries_py);
875-
aws_mem_release(aws_py_get_allocator(), entries);
876-
PyErr_SetString(PyExc_TypeError, "metadata_entries items must have str key and value");
877-
return false;
878-
}
879863
}
880864
out_metrics->metadata_count = (size_t)count;
881865
out_metrics->metadata_entries = entries;

source/mqtt5_client.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,17 @@
1010
#include <aws/mqtt/mqtt.h>
1111

1212
/**
13-
* Parse a Python metrics object (with library_name and metadata_entries attrs)
14-
* into an aws_mqtt_iot_metrics struct. On success, returns true and populates
15-
* out_metrics. The caller must call aws_py_metrics_clean_up() when done.
13+
* Parse a Python AWSIoTMetrics object into a C aws_mqtt_iot_metrics struct.
1614
*
17-
* On failure, returns false and a Python error has been set.
15+
* WARNING: This function calls AWS_ZERO_STRUCT on out_metrics, which
16+
* unconditionally zeroes all fields. The caller must pass a pointer to an
17+
* uninitialized (or already cleaned-up) struct. If out_metrics currently owns
18+
* heap-allocated memory (e.g. a previous metadata_entries array), that memory
19+
* will be leaked because the pointer is overwritten without being freed.
20+
*
21+
* On success the caller is responsible for calling aws_py_metrics_clean_up()
22+
* to release any memory allocated here (metadata_entries).
23+
*
1824
*/
1925
bool aws_py_metrics_parse(PyObject *metrics_py, struct aws_mqtt_iot_metrics *out_metrics);
2026

test/test_aws_iot_metrics.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
AWSIoTMetrics,
99
IoTMetricsMetadata,
1010
_MetricsFeatureId,
11-
_MetricsProtocolVersionValue,
12-
_MetricsSocketImplementationValue,
13-
_MetricsHttpProxyTypeValue,
11+
_protocol_version_metrics_value,
12+
_socket_implementation_metrics_value,
13+
_http_proxy_type_metrics_value,
1414
IOT_SDK_METRICS_FEATURE_VERSION,
1515
_get_encoded_feature_list,
1616
_get_encoded_feature_list_mqtt3,
@@ -31,9 +31,7 @@
3131

3232

3333
def _expected_socket_value():
34-
if sys.platform == "win32":
35-
return _MetricsSocketImplementationValue.WINSOCK
36-
return _MetricsSocketImplementationValue.POSIX
34+
return _socket_implementation_metrics_value()
3735

3836

3937
class TestMinimalOptionsEncoding(NativeResourceTest):
@@ -45,17 +43,17 @@ def test_mqtt5_minimal(self):
4543

4644
result = _get_encoded_feature_list(options)
4745

48-
self.assertIn(f"{_MetricsFeatureId.PROTOCOL_VERSION.value}/{_MetricsProtocolVersionValue.MQTT5.value}", result)
49-
self.assertIn(f"{_MetricsFeatureId.SOCKET_IMPLEMENTATION.value}/{_expected_socket_value().value}", result)
46+
self.assertIn(f"{_MetricsFeatureId.PROTOCOL_VERSION.value}/{_protocol_version_metrics_value('MQTT5')}", result)
47+
self.assertIn(f"{_MetricsFeatureId.SOCKET_IMPLEMENTATION.value}/{_expected_socket_value()}", result)
5048
parts = result.split(",")
5149
self.assertEqual(2, len(parts))
5250

5351
def test_mqtt3_minimal(self):
5452
"""MQTT3 with no proxy and no TLS should only have protocol version and socket."""
5553
result = _get_encoded_feature_list_mqtt3(proxy_options=None, tls_ctx=None)
5654

57-
self.assertIn(f"{_MetricsFeatureId.PROTOCOL_VERSION.value}/{_MetricsProtocolVersionValue.MQTT311.value}", result)
58-
self.assertIn(f"{_MetricsFeatureId.SOCKET_IMPLEMENTATION.value}/{_expected_socket_value().value}", result)
55+
self.assertIn(f"{_MetricsFeatureId.PROTOCOL_VERSION.value}/{_protocol_version_metrics_value('MQTT311')}", result)
56+
self.assertIn(f"{_MetricsFeatureId.SOCKET_IMPLEMENTATION.value}/{_expected_socket_value()}", result)
5957
parts = result.split(",")
6058
self.assertEqual(2, len(parts))
6159

0 commit comments

Comments
 (0)