Have you ever needed to troubleshoot why you are having issues with your MCU and the internet? Ever been curious how internet requests work? Well this might get you a little closer.
Introducing: SocketPoolLogger
What is SocketPoolLogger
? it's a little class, that allows you to log everything that happens with a socket.
import wifi import adafruit_connection_manager import adafruit_requests from socket_logger import SocketPoolLogger radio = wifi.radio pool = adafruit_connection_manager.get_radio_socketpool(radio) # these lines are new: pool_logger = SocketPoolLogger(pool) pool_logger.enable_log_all(True) # and here we pass in the poll_logger, not the regular pool: requests = adafruit_requests.Session(pool_logger) with requests.get("http://wifitest.adafruit.com/testwifi/index.html") as response: print(response.text)
And instead of the normal message we get from the response.text
, we get all of this:
2105.156 | 1008222480 - getaddrinfo | result: [(2, 1, 0, '', ('52.207.219.136', 80))] | args: host: wifitest.adafruit.com port: 80 family: 0 type: 1 proto: 0 flags: 0 2105.160 | 1008222480 - socket | result: <Socket> | socket hash: 1008233584 | args: family: 2 type: 1 proto: 0 fileno: None 2105.166 | 1008233584 - settimeout | result: None | args: value: 60 2105.313 | 1008233584 - connect | result: None | args: address: 52.207.219.136 port: 80 2105.317 | 1008233584 - send | result: 3 | args: bytes: b'GET' 2105.320 | 1008233584 - send | result: 2 | args: bytes: b' /' 2105.324 | 1008233584 - send | result: 19 | args: bytes: b'testwifi/index.html' 2105.327 | 1008233584 - send | result: 11 | args: bytes: b' HTTP/1.1\r\n' 2105.332 | 1008233584 - send | result: 4 | args: bytes: b'Host' 2105.334 | 1008233584 - send | result: 2 | args: bytes: b': ' 2105.337 | 1008233584 - send | result: 21 | args: bytes: b'wifitest.adafruit.com' 2105.341 | 1008233584 - send | result: 2 | args: bytes: b'\r\n' 2105.344 | 1008233584 - send | result: 10 | args: bytes: b'User-Agent' 2105.348 | 1008233584 - send | result: 2 | args: bytes: b': ' 2105.351 | 1008233584 - send | result: 22 | args: bytes: b'Adafruit CircuitPython' 2105.354 | 1008233584 - send | result: 2 | args: bytes: b'\r\n' 2105.357 | 1008233584 - send | result: 2 | args: bytes: b'\r\n' 2105.620 | 1008233584 - recv_into | result: [1, b'H'] | args: size 0 2105.624 | 1008233584 - recv_into | result: [32, b'TTP/1.1 200 OK\r\nServer: nginx/1.'] | args: size 0 2105.628 | 1008233584 - recv_into | result: [16, b'18.0 (Ubuntu)\r\nD'] | args: size 0 2105.632 | 1008233584 - recv_into | result: [31, b'ate: Mon, 27 May 2024 15:57:20 '] | args: size 0 2105.636 | 1008233584 - recv_into | result: [32, b'GMT\r\nContent-Type: text/html\r\nCo'] | args: size 0 2105.641 | 1008233584 - recv_into | result: [62, b'ntent-Length: 69\r\nLast-Modified: Thu, 09 Dec 2021 17:26:22 GMT'] | args: size 0 2105.645 | 1008233584 - recv_into | result: [20, b'\r\nConnection: keep-a'] | args: size 0 2105.649 | 1008233584 - recv_into | result: [46, b'live\r\nETag: "61b23c3e-45"\r\nAccept-Ranges: byte'] | args: size 0 2105.654 | 1008233584 - recv_into | result: [45, b's\r\n\r\nThis is a test of Adafruit WiFi!\nIf you '] | args: size 0 2105.659 | 1008233584 - recv_into | result: [29, b'can read this, its working :)'] | args: size 29 This is a test of Adafruit WiFi! If you can read this, its working :)
Okay, now just a quick break down on the logging, let's look at this line:
2105.313 | 1008233584 - connect | result: None | args: address: 52.207.219.136 port: 80
What we have is:
-
2105.313
: time.monotonic() -
1008233584
: the hash of the object, useful if you have multiple sockets doing things -
connect
: the name of the method -
result: None
: the result of the method call -
args: address: 52.207.219.136 port: 80
the args send to the method
You'll notice
import wifi import adafruit_connection_manager import adafruit_ntp from socket_logger import SocketPoolLogger radio = wifi.radio pool = adafruit_connection_manager.get_radio_socketpool(radio) # these lines are new: pool_logger = SocketPoolLogger(pool) pool_logger.enable_log_all(True) # and here we pass in the poll_logger, not the regular pool: ntp = adafruit_ntp.NTP(pool_logger) print(ntp.datetime)
And now we get all of this:
2265.725 | 1008222480 - getaddrinfo | result: [(2, 1, 0, '', ('205.233.73.201', 123))] | args: host: 0.adafruit.pool.ntp.org port: 123 family: 0 type: 0 proto: 0 flags: 0 2265.729 | 1008222480 - socket | result: <Socket> | socket hash: 1008239696 | args: family: 2 type: 2 proto: 0 fileno: None 2265.733 | 1008239696 - __enter__ | result: <SocketLogger object at 0x3c1889e0> | 2265.735 | 1008239696 - settimeout | result: None | args: value: 10 2265.739 | 1008239696 - sendto | result: 48 | args: bytes: b'#\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' address: 205.233.73.201 port: 123 2265.781 | 1008239696 - recv_into | result: [48, b'$\x03\x00\xe9\x00\x00\x00v\x00\x00\x17\x9d\xabBa~\xe9\xff(\xa8\xb6\xa8\xcf\xbb\x00\x00\x00\x00\x00\x00\x00\x00\xe9\xff,\x80Uc\xdcE\xe9\xff,\x80Uf-\x96'] | args: size 0 2265.784 | 1008239696 - __exit__ | result: None | args: exc_type: None exc_val: None exc_tb: None struct_time(tm_year=2024, tm_mon=5, tm_mday=27, tm_hour=16, tm_min=0, tm_sec=0, tm_wday=0, tm_yday=148, tm_isdst=-1)
import os import wifi import adafruit_connection_manager import adafruit_minimqtt.adafruit_minimqtt as MQTT from socket_logger import SocketPoolLogger radio = wifi.radio pool = adafruit_connection_manager.get_radio_socketpool(radio) # these lines are new: pool_logger = SocketPoolLogger(pool) pool_logger.enable_log_all(True) # and here we pass in the poll_logger, not the regular pool: client = MQTT.MQTT( broker="io.adafruit.com", username=os.getenv("AIO_USERNAME"), password=os.getenv("AIO_KEY"), socket_pool=pool_logger, ) result = client.connect() client.loop(5) client.disconnect()
Gives us:
2334.598 | 1008222480 - getaddrinfo | result: [(2, 1, 0, '', ('52.54.110.50', 1883))] | args: host: io.adafruit.com port: 1883 family: 0 type: 1 proto: 0 flags: 0 2334.602 | 1008222480 - socket | result: <Socket> | socket hash: 1008251664 | args: family: 2 type: 1 proto: 0 fileno: None 2334.607 | 1008251664 - settimeout | result: None | args: value: 1 2334.802 | 1008251664 - connect | result: None | args: address: 52.54.110.50 port: 1883 2334.808 | 1008251664 - send | result: 2 | args: bytes: b'\x10D' 2334.812 | 1008251664 - send | result: 10 | args: bytes: b'\x00\x04MQTT\x04\xc2\x00<' 2334.814 | 1008251664 - send | result: 2 | args: bytes: b'\x00\x08' 2334.818 | 1008251664 - send | result: 8 | args: bytes: b'cpy24820' 2334.821 | 1008251664 - send | result: 2 | args: bytes: b'\x00\x0c' 2334.825 | 1008251664 - send | result: 12 | args: bytes: b'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' 2334.829 | 1008251664 - send | result: 2 | args: bytes: b'\x00 ' 2334.832 | 1008251664 - send | result: 32 | args: bytes: b'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' 2335.107 | 1008251664 - recv_into | result: [1, b' '] | args: size 1 2335.112 | 1008251664 - recv_into | result: [3, b'\x02\x00\x00'] | args: size 3 2336.117 | 1008251664 - recv_into | result: [Errno 116] ETIMEDOUT | args: size 1 2337.122 | 1008251664 - recv_into | result: [Errno 116] ETIMEDOUT | args: size 1 2338.125 | 1008251664 - recv_into | result: [Errno 116] ETIMEDOUT | args: size 1 2339.128 | 1008251664 - recv_into | result: [Errno 116] ETIMEDOUT | args: size 1 2340.132 | 1008251664 - recv_into | result: [Errno 116] ETIMEDOUT | args: size 1 2340.137 | 1008251664 - send | result: 2 | args: bytes: b'\xe0\x00' 2340.142 | 1008251664 - close | result: None |
Now, be careful when sharing the output - it isn't sanitized! See all those X's? I did that by hand! This is what you would really see:
client = MQTT.MQTT( broker="io.adafruit.com", username="adafrit", password="super-secret-password", socket_pool=pool_logger, ) client.connect()
2376.983 | 1008222480 - getaddrinfo | result: [(2, 1, 0, '', ('52.54.110.50', 1883))] | args: host: io.adafruit.com port: 1883 family: 0 type: 1 proto: 0 flags: 0 2376.987 | 1008222480 - socket | result: <Socket> | socket hash: 1008251664 | args: family: 2 type: 1 proto: 0 fileno: None 2376.992 | 1008251664 - settimeout | result: None | args: value: 1 2377.099 | 1008251664 - connect | result: None | args: address: 52.54.110.50 port: 1883 2377.104 | 1008251664 - send | result: 2 | args: bytes: b'\x104' 2377.108 | 1008251664 - send | result: 10 | args: bytes: b'\x00\x04MQTT\x04\xc2\x00<' 2377.111 | 1008251664 - send | result: 2 | args: bytes: b'\x00\x08' 2377.114 | 1008251664 - send | result: 8 | args: bytes: b'cpy10984' 2377.118 | 1008251664 - send | result: 2 | args: bytes: b'\x00\x07' 2377.122 | 1008251664 - send | result: 7 | args: bytes: b'adafrit' 2377.125 | 1008251664 - send | result: 2 | args: bytes: b'\x00\x15' 2377.128 | 1008251664 - send | result: 21 | args: bytes: b'super-secret-password' 2377.407 | 1008251664 - recv_into | result: [1, b' '] | args: size 1 2377.414 | 1008251664 - recv_into | result: [3, b'\x02\x00\x05'] | args: size 3 2377.418 | 1008251664 - close | result: None | Traceback (most recent call last): File "<stdin>", line 1, in <module> File "adafruit_minimqtt/adafruit_minimqtt.py", line 429, in connect File "adafruit_minimqtt/adafruit_minimqtt.py", line 558, in _connect MMQTTException: ('Connection Refused - Unauthorized', 5)
import wifi import adafruit_connection_manager import adafruit_requests from socket_logger import SocketPoolLogger radio = wifi.radio pool = adafruit_connection_manager.get_radio_socketpool(radio) ssl_context = adafruit_connection_manager.get_radio_ssl_context(radio) # these lines are new: pool_logger = SocketPoolLogger(pool) pool_logger.enable_log_all(True) # and here we pass in the poll_logger, not the regular pool: requests = adafruit_requests.Session(pool_logger, ssl_context) with requests.get("http://www.adafruit.com/api/quotes.php") as response: print(response.text)
2402.831 | 1008222480 - getaddrinfo | result: [(2, 1, 0, '', ('104.20.39.240', 80))] | args: host: www.adafruit.com port: 80 family: 0 type: 1 proto: 0 flags: 0 2402.834 | 1008222480 - socket | result: <Socket> | socket hash: 1008273728 | args: family: 2 type: 1 proto: 0 fileno: None 2402.840 | 1008273728 - settimeout | result: None | args: value: 60 2402.865 | 1008273728 - connect | result: None | args: address: 104.20.39.240 port: 80 2402.871 | 1008273728 - send | result: 3 | args: bytes: b'GET' 2402.880 | 1008273728 - send | result: 2 | args: bytes: b' /' 2402.884 | 1008273728 - send | result: 14 | args: bytes: b'api/quotes.php' 2402.888 | 1008273728 - send | result: 11 | args: bytes: b' HTTP/1.1\r\n' 2402.892 | 1008273728 - send | result: 4 | args: bytes: b'Host' 2402.896 | 1008273728 - send | result: 2 | args: bytes: b': ' 2402.898 | 1008273728 - send | result: 16 | args: bytes: b'www.adafruit.com' 2402.903 | 1008273728 - send | result: 2 | args: bytes: b'\r\n' 2402.907 | 1008273728 - send | result: 10 | args: bytes: b'User-Agent' 2402.910 | 1008273728 - send | result: 2 | args: bytes: b': ' 2402.915 | 1008273728 - send | result: 22 | args: bytes: b'Adafruit CircuitPython' 2402.917 | 1008273728 - send | result: 2 | args: bytes: b'\r\n' 2402.921 | 1008273728 - send | result: 2 | args: bytes: b'\r\n' 2403.207 | 1008273728 - recv_into | result: [1, b'H'] | args: size 0 2403.210 | 1008273728 - recv_into | result: [32, b'TTP/1.1 301 Moved Permanently\r\nD'] | args: size 0 2403.214 | 1008273728 - recv_into | result: [31, b'ate: Mon, 27 May 2024 16:02:17 '] | args: size 0 2403.218 | 1008273728 - recv_into | result: [32, b'GMT\r\nContent-Length: 0\r\nConnecti'] | args: size 0 2403.224 | 1008273728 - recv_into | result: [56, b'on: keep-alive\r\nlocation: https://www.adafruit.com/api/q'] | args: size 0 2403.228 | 1008273728 - recv_into | result: [24, b'uotes.php\r\nCF-Cache-Stat'] | args: size 0 2403.232 | 1008273728 - recv_into | result: [51, b'us: DYNAMIC\r\nX-Content-Type-Options: nosniff\r\nServe'] | args: size 0 2403.237 | 1008273728 - recv_into | result: [47, b'r: cloudflare\r\nCF-RAY: 88a73a7b1c42c36e-SEA\r\n\r\n'] | args: size 0 2403.250 | 1008222480 - getaddrinfo | result: [(2, 1, 0, '', ('104.20.39.240', 443))] | args: host: www.adafruit.com port: 443 family: 0 type: 1 proto: 0 flags: 0 2403.254 | 1008222480 - socket | result: <Socket> | socket hash: 1008248080 | args: family: 2 type: 1 proto: 0 fileno: None 2403.270 | 1008248080 - settimeout | result: None | args: value: 60 2403.288 | 1008248080 - connect | result: None | args: address: www.adafruit.com port: 443 2403.292 | 1008248080 - send | result: 213 | args: bytes: b"\x16\x03\x03\x00\xd0\x01\x00\x00\xcc\x03\x03\x00\x00\ta\xc6\xf7\xf3\xcd\x82\xc9x\x08C\x13e\xc2\xef\xfa\\\x98xfs\x8a\x18\xed\xa9\x91\xb7\xa2gQ\x00\x00Z\xc0,\xc00\xc0\xad\xc0$\xc0(\xc0\n\xc0\x14\xc0\xaf\xc0]\xc0a\xc0I\xc0M\xc0+\xc0/\xc0\xac\xc0#\xc0'\xc0\t\xc0\x13\xc0\xae\xc0\\\xc0`\xc0H\xc0L\xc02\xc0*\xc0\x0f\xc0.\xc0&\xc0\x05\xc0_\xc0c\xc0K\xc0O\xc01\xc0)\xc0\x0e\xc0-\xc0%\xc0\x04\xc0^\xc0b\xc0J\xc0N\x00\xff\x01\x00\x00I\x00\x00\x00\x15\x00\x13\x00\x00\x10www.adafruit.com\x00\n\x00\x08\x00\x06\x00\x1d\x00\x17\x00\x18\x00\r\x00\x0e\x00\x0c\x06\x03\x06\x01\x05\x03\x05\x01\x04\x03\x04\x01\x00\x0b\x00\x02\x01\x00\x00\x16\x00\x00\x00\x17\x00\x00\x00#\x00\x00" 2403.314 | 1008248080 - recv_into | result: [5, b'\x16\x03\x03\x00C'] | args: size 0 2403.318 | 1008248080 - recv_into | result: [67, b'\x02\x00\x00?\x03\x03fT\xae\x89\x86\xdcpX\xcb%/\x13\xce\xb5\x8b<\xed[\x82Y(\xad\xe4\xb0DOWNGRD\x01\x00\xc0/\x00\x00\x17\x00\x00\x00\x00\x00\x17\x00\x00\xff\x01\x00\x01\x00\x00\x0b\x00\x02\x01\x00\x00#\x00\x00'] | args: size 0 2403.322 | 1008248080 - recv_into | result: [5, b'\x16\x03\x03\x0b\x1f'] | args: size 0 2403.327 | 1008248080 - recv_into | result: [2803, b'\x0b\x00\x0b\x1b\x00\x0b\x18\x00\x06\x810\x82\x06}0\x82\x05e\xa0\x03\x02\x01\x02\x02\x10\x01I\xed\xb5*\xea\xa2\x9c\xc1\xef=\x10\'\xea\xdc\xe60\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000`1\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x150\x13\x06\x03U\x04\n\x13\x0cDigiCert Inc1\x190\x17\x06\x03U\x04\x0b\x13\x10www.digicert.com1\x1f0\x1d\x06\x03U\x04\x03\x13\x16GeoTrust TLS RSA CA G10\x1e\x17\r230708000000Z\x17\r240807235959Z0n1\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x110\x0f\x06\x03U\x04\x08\x13\x08New York1\x110\x0f\x06\x03U\x04\x07\x13\x08New York1 0\x1e\x06\x03U\x04\n\x13\x17Adafruit Industries LLC1\x170\x15\x06\x03U\x04\x03\x0c\x0e*.adafruit.com0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\xab\x90\xda\\\xd1\xc4\xf5\xad}\xac\xb5\xe7\xa5\xed\x10\xed\xb5\x07\x1a\xa1\t\xf8\x0e=\xed\x05jh\xffGm\xa8\xf3v\xb9\x11;\xf3\xf1N\x9dN\x82\xdb\xf5\xe55\xce\xbc,@\x1a\x96\x8fU\x1b\xcd\x17\xd1;\x01\x1a\x9a\x83\xfa\xfe\xcd\xbc\x90\x17\x05:\xb7\x8bq\x1aO6\xdc\xd8\x17VX%\xaf\xdf\x8f\x7f\x8b\ro\xe9\x0fI\x173\t\xf5\x19\xf2\xb0\'\xee\xfa\x90\x8c\xc1E\xf3\xa6[\xe4\x85\xd3\x1c\x9bd.\xc7Y\x8d\xef\xd3V\x9b\xe6\x04\xb3\xa5\x98\xc9E\x9f\xa6x~\xa4\xb4\xbf\xd4X\xact8\x93\xee\x81\xe5!\xe8i\xd5n\x18\xaeo\xc4\xe7E\xcc\x8c\xcd\xb0\xfa\xd3A\xe3\xbd\xf1\x05\xf7/V1\x99\xf3\xe8Uz;vv\xf7)`iF\x0eP~0\x01\xf5\xe6\xa9\xd2s\x98)\x92b>\xce\xa5\xaf\x88(`P+\x0e\xcf1Q\xf4\xf9\xaa\x83}!\xa0wTs\x18\xb3h\xb1|\x08\xec\x80\xe8z\x08:\x8c\x7fg\x01\xc2\'w\xe6\xd7&\xe1%\x1d\x1dr\xfb\xf7c\xad\xcd\x02\x03\x01\x00\x01\xa3\x82\x03#0\x82\x03\x1f0\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14\x94O\xd4]\x8b\xe4\xa4\xe2\xa6\x80\xfe\xfd\xd8\xf9\x00\xef\xa3\xbe\x02W0\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14&\xa3<\xd3HT\xeaz\xe2\xbed\x8a\xccX\xd5\xbd\xdd\xe7\xd3Q0\'\x06\x03U\x1d\x11\x04 0\x1e\x82\x0e*.adafruit.com\x82\x0cadafruit.com0\x0e\x06\x03U\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x05\xa00\x1d\x06\x03U\x1d%\x04\x160\x14\x06\x08+\x06\x01\x05\x05\x07\x03\x01\x06\x08+\x06\x01\x05\x05\x07\x03\x020?\x06\x03U\x1d\x1f\x0480604\xa02\xa00\x86.http://cdp.geotrust.com/GeoTrustTLSRSACAG1.crl0>\x06\x03U\x1d \x0470503\x06\x06g\x81\x0c\x01\x02\x020)0\'\x06\x08+\x06\x01\x05\x05\x07\x02\x01\x16\x1bhttp://www.digicert.com/CPS0v\x06\x08+\x06\x01\x05\x05\x07\x01\x01\x04j0h0&\x06\x08+\x06\x01\x05\x05\x070\x01\x86\x1ahttp://status.geotrust.com0>\x06\x08+\x06\x01\x05\x05\x070\x02\x862http://cacerts.geotrust.com/GeoTrustTLSRSACAG1.crt0\t\x06\x03U\x1d\x13\x04\x020\x000\x82\x01\x7f\x06\n+\x06\x01\x04\x01\xd6y\x02\x04\x02\x04\x82\x01o\x04\x82\x01k\x01i\x00u\x00\xee\xcd\xd0d\xd5\xdb\x1a\xce\xc5\\\xb7\x9d\xb4\xcd\x13\xa22\x87F|\xbc\xec\xde\xc3QHYFq\x1f\xb5\x9b\x00\x00\x01\x895\xa1\xc0\x82\x00\x00\x04\x03\x00F0D\x02 j:\xe6s\xd6RP&lQ\xe8\x0c\x93\x87\xf6\x14\x89\xfb\xabC\xa9@\xfd\xbc\x00\xd2\x1f$]5\x9a\x83\x02 l|\xc8\xa8/x;\xdb\x8e\xfd\x1b\xf3\x7fT\x93\xa5n\x9b\xf8mQ\x87\x91R\t\xefhl&\xa9\x02\xf5\x00w\x00H\xb0\xe3k\xda\xa6G4\x0f\xe5j\x02\xfa\x9d0\xeb\x1cR\x01\xcbV\xdd,\x81\xd9\xbb\xbf\xab9\xd8\x84s\x00\x00\x01\x895\xa1\xc0\xa9\x00\x00\x04\x03\x00H0F\x02!\x00\xe1\x95\x8c\x1f\xa3\x04\x956}-\xcbY\xdc\xdf^|\x8c}\xc4\x80<|\x12\xbf\x8a?a\xf9"\x18@\x17\x02!\x00\xca\xbf=vZ\n]\x08\x9b\x0e\xc90\xf6\xdc,}C7*\xd4\xe4\xb5\x14Z\x88^o\xec\x9d\xfb\xbe]\x00w\x00\xda\xb6\xbfk?\xb5\xb6"\x9f\x9b\xc2\xbb\\k\xe8p\x91ql\xbbQ\x84\x854\xbd\xa4=0H\xd7\xfb\xab\x00\x00\x01\x895\xa1\xc0r\x00\x00\x04\x03\x00H0F\x02!\x00\x87\xb8.A\xd5\x9d\xa3\xde_\xbd\x90\xf6\xbb\xdf\xabyU\x8b\xc6j\xb4\x16~\xa6\xc7vC\x17$\x9f\x82\x9d\x02!\x00\xda}\xff\xa6\x11\xd9a\xa7\xb8\xd1r\xfb\xb6\x878\x8e\x0bR\x83&\x02\xf2h\x99\xb6\xca\r\x9a\xdc\xe5O(0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00g\x93\x1f\xe5\xd3\x99\xbb\x08\x89\n\xf8\xa2\xa3\xb5.XN\xfb\x1a\x19o\xf7\xb4Sa\xa1z\xb7\xc9\xf9\x84j\xec6<\xe8\xd0\x18\xc2!\xbc%O\xd6\x12m\x9b\x8d\xa5\xe18b\xdcc>>\x7f\xc09\xb98\xa9^\xbe\xb2\x11aO\x06\xb2\xa3\xf4{\xc6uu\xb6#\x83/\x19\xb6\x191\x93{\x81\x08\xc0/\x9dC\xc32u\xf5\xe2\xb9\xd2&\xbc\x12K\xb7B\x1a"N<I\xa6\x9a\xe8IS\xcaw\xec\xa6I\xc7\xb3\xf1\x89\x1c[r\xb5\xb4\x92\t\xe2?\xa6a<E\xe9,\xf3\xde\xe1\x8c\xac\xd1\xad\xab\xac\xe1r\x11\xdcn\x13\x87M\x19\xce:!Q6\x7f\xfb_\xed=\tO\xe6\x19(\xcb\xb8L\r?\xe5\x17\xe0\x9f\xc9\xa6!\xed\xb4\xbd\n\x04\n)\xce&@>\x18\x0c\x9bx+\xe3\x83\xb2I\ti\xf3X\xb9\xe0\xe1\xb9(Oz\xb2\x93\xa5\x9dB\xeb\xef\x83<\xf3f\xa1@\xb3=\x9b\xd8\xde\xa0\xf3\x0f\xa3\xbe\x86O\xf7\\R\xa5\xfcE\x96\xbe\x050hj\x97\x01\xdbF\x00\x04\x910\x82\x04\x8d0\x82\x03u\xa0\x03\x02\x01\x02\x02\x10\r\x07x*\x13?\xc6\xf9\xa5r\x96\xe11\xff\xd1y0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000a1\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x150\x13\x06\x03U\x04\n\x13\x0cDigiCert Inc1\x190\x17\x06\x03U\x04\x0b\x13\x10www.digicert.com1 0\x1e\x06\x03U\x04\x03\x13\x17DigiCert Global Root G20\x1e\x17\r171102122337Z\x17\r271102122337Z0`1\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x150\x13\x06\x03U\x04\n\x13\x0cDigiCert Inc1\x190\x17\x06\x03U\x04\x0b\x13\x10www.digicert.com1\x1f0\x1d\x06\x03U\x04\x03\x13\x16GeoTrust TLS RSA CA G10\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\xbe\x17\xe8\xec\xbe)\n\xcb\xfe\xb9-a1\xfd3$\x082.Y\xe8!\xd4\xd80\xben\x10\xc8\x84\xa0?\xba\x14\xe5\xde\xfdz\x8c\x92\x1b{\xce\x84-\xf0\xffx\xc42\xe8\xa9\xa0}_\x06\xda{\x9bKS\xa6\xc6\x1b\x02\x17!\xe1p;\xad\xfb\x83\xeb\x08T\x81\xa8\xde\x12\xb2\xd5\xc6\x88\x960\xf9\x02\xfc9\xd4\xbd\xb8"\xef\x80I\x99\xd0b\xb8a\xd0I\xde\xcb\xc2\xcb\x97\xa51\x06\x1b\xd7\xd8]\xc6\xd3T\xdeR\x016*\r\xf6\xde\xc5\xb61L\xcc\x15%j\x15o\xa9k\x04H\x0c\xde\x00A\xaa(\x80\x8b/4\xd3\x1b\xb56\xad;%\xd0\x88B@l6\x91me\xb2\x19\x86\xc0\xd2\x7f9FX\xfe0\x12`P\xdc\xee\xbbs\xe6W\x90Z\xf6\r\xca\xd7\x04KGjo4\x1a\x9d\x926\x1a.\xd9NT\xedG\xac\x0c\xbf\xf1\x80\xb2\xba\xffG{\xe99\xc4T\xc4\x94T\x99\x19\xf1W\x99\xaf\xe2\x14"[\xe8.\xbbc-\xba\xae\x81\xbd\x13\xdc\xe6\x17[\xe0\x90SI\x01\x02\x03\x01\x00\x01\xa3\x82\x01@0\x82\x01<0\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14\x94O\xd4]\x8b\xe4\xa4\xe2\xa6\x80\xfe\xfd\xd8\xf9\x00\xef\xa3\xbe\x02W0\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14N"T \x18\x95\xe6\xe3n\xe6\x0f\xfa\xfa\xb9\x12\xed\x06\x17\x8f90\x0e\x06\x03U\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x01\x860\x1d\x06\x03U\x1d%\x04\x160\x14\x06\x08+\x06\x01\x05\x05\x07\x03\x01\x06\x08+\x06\x01\x05\x05\x07\x03\x020\x12\x06\x03U\x1d\x13\x01\x01\xff\x04\x080\x06\x01\x01\xff\x02\x01\x0004\x06\x08+\x06\x01\x05\x05\x07\x01\x01\x04(0&0$\x06\x08+\x06\x01\x05\x05\x070\x01\x86\x18http://ocsp.digicert.com0B\x06\x03U\x1d\x1f\x04;0907\xa05\xa03\x861http://crl3.digicert.com/DigiCertGlobalRootG2.crl0=\x06\x03U\x1d \x0460402\x06\x04U\x1d \x000*0(\x06\x08+\x06\x01\x05\x05\x07\x02\x01\x16\x1chttps://www.digicert.com/CPS0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x82\x1c\x04:\x82\xe9\xc5\xa9\xc8e\x12\\\x08\xe3\x01\xc60\xb0\xae"\x88a{+\x07\x86\xf7\xb8\xb5D\x9c\xf5\x93\x06aF\x8a:\xa2\xb55&\xd0X\x9e<\xc1s\x8f\xbf\xb1y\xa7\x1c\x997\x9aS\xf2/\\n \x0b}\xb0\xc7\x82\xef\xeb\xeby\x12\x05\xe7\x01A\x0bC\x027\x9b\x1b\x1f\x08M7Rs)\xb8\x9f5\xe3\xf9\xa3:Yu\x01f\x90*>\x9d\xe0b)\x1e\x87\xa8\x80>,\xc4\xcc\x08\xc3.\xbfi!\x18k\x8f\x1ekC\xcd\xee\x06\x98A\x99\xb5\x82\xf2\x17\xc1\x10\xa5\xaeX\x8c\xfb\x95\xeaM\x82\xaf\xf3w_\x11iOxQ\xa6\xdf\xf7\xba\xc5?e\xbfp|\xa3\xedZ\x9a3\x9d\x9e?\xe3\x875\xa7\xda\xf3\x15\xc6X\xadi#\xdc\x0f\xbbL\xd0I\x13A\xa6?g\xe1^\x13\xf5\x0f\xf5\xd5|\x85\xbf\x87L\x82'] | args: size 0 2403.440 | 1008248080 - recv_into | result: [44, b'$a"W\xd1\xa5\x9b\xfb\x869\xad\xc3\\yfl\x071K \x91\xad\xa1\xbed\x19\x00\xee\x1e\x12x\xce\x98\xf2_\xfb0\x14i<,\xfa\x97\xc6'] | args: size 0 2403.479 | 1008248080 - recv_into | result: [5, b'\x16\x03\x03\x01,'] | args: size 0 2403.482 | 1008248080 - recv_into | result: [300, b'\x0c\x00\x01(\x03\x00\x1d 8[\x9b\xd0\xa4\x95\x1bx\xb9\x0c8FN\xf9\x1e[\xa1S\x85\xe5\xceoF}\x15\xdav\xfb9\x89(N\x04\x01\x01\x00X\xbf\xfa~y\xb5\x01A&\xc2\xa8\x9d\x13\x8a\xbf\x8b\xb9\xe3\xbf\x19\xa4\xbbV\xca\xdf\xb9!\xe9\x83\x14y\xee\x14\xdd<\xd0\\\xc4\tQ\xd6\xdb)\xaeB\x04\xe3\xa5\xd3F\x1c"\tx\x19\xd8"\xc0\xae\xdb[\x89+E\xf38\xb6\x8c\xbb\xa6\x08\xdc\xfd\r6\x00\xfbKOLh\x89\xe5\x923\x7f\xa28\x07Za\x16\x93\xedw\x9d\xcf\xc2\xbb\x9a\xee\xe7\x80&\xc3L.u/z\x0e\x9f\x17?+\xeb\xb4m\t]\xc4NrKD\x0b\x85\xc6\\\n\x94\xc5lu\xa1\xcc\xb8\x9d\xe0=\xae\xcf\x00#\xe9*z\n\xc7b\xef1(\x17@W+\x9e6\x13g\xc3\x18\xe6!\x01RDB\x8az\x19p\xb3\xfb9\xad\xbd/\x1c\xd9\xa0\xf64\xbc x\xc1T\x9a\x1a\xb6\xaa\x0c\xa0\xcd\x9b\x91T\xd6\x14\x12\x00\xd1:\xd8{n\xd4/\x9a\xbeb\xbcO\xd9\xdb>\xc0U\x9b\xfb/o\xd8-\rZB]\x94\xf0i\x96\xddqN\xcd\x19L\x96cj\x81\xfc]|\xe3\x94*?\xc0\xaf\xcc\x90\xc0'] | args: size 0 2403.504 | 1008248080 - recv_into | result: [5, b'\x16\x03\x03\x00\x04'] | args: size 0 2403.508 | 1008248080 - recv_into | result: [4, b'\x0e\x00\x00\x00'] | args: size 0 2403.691 | 1008248080 - send | result: 42 | args: bytes: b'\x16\x03\x03\x00%\x10\x00\x00! \x93\xcdc\t\x03\x06\x071\xbdM\xf1\xdf\x85\x18\x9a\xc2\xd4\x16\x92\xd0\xc3[DV\xba0\xce-7 nx' 2403.698 | 1008248080 - send | result: 6 | args: bytes: b'\x14\x03\x03\x00\x01\x01' 2403.704 | 1008248080 - send | result: 45 | args: bytes: b"\x16\x03\x03\x00(\x00\x00\x00\x00\x00\x00\x00\x00\xc3\xa4\x0czq?@:\xb1'\x9co\x10\x08\xca?y\xea\x86\x97\x074\x00\xda\x084b\x18\xb5\xe1\x82\xc1" 2403.837 | 1008248080 - recv_into | result: [5, b'\x16\x03\x03\x00\xca'] | args: size 0 2403.841 | 1008248080 - recv_into | result: [202, b'\x04\x00\x00\xc6\x00\x00\xfd\x1f\x00\xc0\\\xe2!=Y\xfd\xf3\x04\x97\x12!\x7f\x08\xbd\xa5?\xb1\x0f\x87\x8c&D!\x06a\x980\x06.\x84\xe3\x11C\xad\xd30#\xf5\xdb\x05\xdc\x17f\xfc\x90\xa6q\xeey\x7f\xb7\xd2\xe6\xd1\x04\xaf\x92D\xf7\xf9\x81[\xf0(\xd9\xa6\xc59`mT\xf5O|/\xd8\xeeB\x88\x8f}\xf5S\xb6\xb8j\xd6\xf5\x90\\\xfdT\x9a\xe3\x19\xea]\x8e\x85\xd8\xca\x88.\x97L\xfe\xc8\xb0R+\x1a3 >\xa2\x8a\xf1Y\x9c\xed\xecN\xde{r\xf2\xf5\x91X3c\x85-Rh\x87zf\x8a\x93\x916\\\xfa?\xcf\xb5\t\x8a\xbe~EO=$\x8b1BX\xe0\xca\xb0F\x00\xea\xcd\xa4\xb0\xca\x12\x95 \xe2\x1d\x93\x12~uJ\x95q\xd94|\x98\xbe\x999\x11\x9a\x0cd'] | args: size 0 2403.846 | 1008248080 - recv_into | result: [5, b'\x14\x03\x03\x00\x01'] | args: size 0 2403.850 | 1008248080 - recv_into | result: [1, b'\x01'] | args: size 0 2403.854 | 1008248080 - recv_into | result: [5, b'\x16\x03\x03\x00('] | args: size 0 2403.857 | 1008248080 - recv_into | result: [40, b'\x00\x00\x00\x00\x00\x00\x00\x00{\xd4\xc4\xeao\xa7\xc1B\xd8\xad\x96\x91UJ^\x9d\xf0\xcb]\xec\x077a\xfcj\xbe\x1f\x83\x07\xa3h\xb9'] | args: size 0 2403.863 | 1008248080 - send | result: 32 | args: bytes: b'\x17\x03\x03\x00\x1b\x00\x00\x00\x00\x00\x00\x00\x01\xfb\x11!\xc4;\x841\xef\xc5\x91\x9e\x95w\xf08\x1f\xa3\xa2g' 2403.868 | 1008248080 - send | result: 31 | args: bytes: b'\x17\x03\x03\x00\x1a\x00\x00\x00\x00\x00\x00\x00\x02F\xe8\x86\xb8?K\xbbm\xe4\xcb\x14\xc8\x10Uy\x01O\x89' 2403.874 | 1008248080 - send | result: 43 | args: bytes: b'\x17\x03\x03\x00&\x00\x00\x00\x00\x00\x00\x00\x03q\xea\xd2*D<\x86e\xa2\x1b\x95[\xf8\xad\xad\xc8j{\xf9\xc3j\xbf\xd0f\x92A\xc9\x85 w' 2403.877 | 1008248080 - send | result: 40 | args: bytes: b'\x17\x03\x03\x00#\x00\x00\x00\x00\x00\x00\x00\x04>d\x8c$\xe7C\xadhA\xc8\x87v\xe7C\x1fc\x88@\x1e\xdek\xf1\x94\x1a\xc6\xe3\xb5' 2403.882 | 1008248080 - send | result: 33 | args: bytes: b"\x17\x03\x03\x00\x1c\x00\x00\x00\x00\x00\x00\x00\x05hn\xc8\xfe\xd64\xdedr\xcc\xd4\x06\x07\xbe\x90\x9c*\xce\\'" 2403.886 | 1008248080 - send | result: 31 | args: bytes: b'\x17\x03\x03\x00\x1a\x00\x00\x00\x00\x00\x00\x00\x06\x08\xef\xc5\xa5M4\\%S41\x16\x8f\xb0@\x06L\x83' 2403.890 | 1008248080 - send | result: 45 | args: bytes: b'\x17\x03\x03\x00(\x00\x00\x00\x00\x00\x00\x00\x07 9\xda\x92\xba\xf2;\xf5*\x15?\x9d\xe8\x97\xdb\xa3\xb5m\xb2\xf7\xdf\xfdjo\xa9\xd8\xfe\x92Toa\xd5' 2403.895 | 1008248080 - send | result: 31 | args: bytes: b'\x17\x03\x03\x00\x1a\x00\x00\x00\x00\x00\x00\x00\x08\x18"c2Y\x07\xef\x9b\xc3F\xb9S\xada0\xfeF\x85' 2403.898 | 1008248080 - send | result: 39 | args: bytes: b'\x17\x03\x03\x00"\x00\x00\x00\x00\x00\x00\x00\tr\xb1\xa5\x8c\xec\x1f\x14?\xbc\xab\x8bu\xb3\x8b\xc2\x06\xb07=r\xbb}@\xc8\x96$' 2403.902 | 1008248080 - send | result: 31 | args: bytes: b'\x17\x03\x03\x00\x1a\x00\x00\x00\x00\x00\x00\x00\n/\r\xf8M\xdaS\x0c\x0e\x84\x9f\xb7#\x856%\xfd\xc1\xb1' 2403.907 | 1008248080 - send | result: 51 | args: bytes: b'\x17\x03\x03\x00.\x00\x00\x00\x00\x00\x00\x00\x0b\xf9\xae\x0f\x05\x1a\x02jr0k\x8aDS\xf7Pu3\xc9\xc4\xed\xe2\xc9K(Hh\xe9n\x06l\xd3\xb2\xaf\xac(1\n\xd4' 2403.912 | 1008248080 - send | result: 31 | args: bytes: b"\x17\x03\x03\x00\x1a\x00\x00\x00\x00\x00\x00\x00\x0c\xa4~\xf7'\xa0(\x9b\xf2 O\x12\x0c\x88\xaas\xd1^\x80" 2403.916 | 1008248080 - send | result: 31 | args: bytes: b'\x17\x03\x03\x00\x1a\x00\x00\x00\x00\x00\x00\x00\r\xca\x8d4\xc1o\xe0O/\x0c\x84\x82\xb6;\xffa\x05\xed\x8e' 2404.438 | 1008248080 - recv_into | result: [5, b'\x17\x03\x03\x04\xe9'] | args: size 0 2404.443 | 1008248080 - recv_into | result: [1257, b'\x00\x00\x00\x00\x00\x00\x00\x01\xd2K\xc9^\xd1H\xc6\x9f*\x7f,\x16\x0e\xf9\t\x0f\xc1=\x02\xaf\xbf\xe3\x0f\x90)O\xf4+DO\xb2c\x8f\x05\x88KI\xe19e\xf4\x8bx\x19\xcewP\xb3x\xe5\xe6iH\xf3\xbdP \x05\xc6\xa8\xd8,F\x99|.*\x92x\xba\xf1\xafz\x99>&\xdd\rkz\t%{\xb7\x87u\xf4\xd3\xdc\xb1L\x07-\xbe\xee\x8e^\x86\xc0\x99\xa0\xb6\x86\xc4:\xe9\x19\x91_\ny\xf8 \xb1\x12\x1a\xc7\xc6\xe8n\x97damw\xd9\xa7\xc7\x18\xd8\x10\xd5\x1f.\xc0+\x0f\x99Wx\xa4\xb5Z\xe4\xb5f\x88r\xf5e\xc0\xed"\xf1\xf3(\xed\xb6"\x0f\x1eZVb\xa5\xb0\xc6K\xe2\xa6\xcc\x84P\xdc\x04\x01\x14m1& \x9f\xaf\xf7\xe9%\xabz\xa5q\xee\x94\x93w\xb9\xdd\xceO\xfb(\xb1.8\xbd\xf6\xa90\xcb\xbcP7@C{o\xc1%\xd0\x9a.\xcfW\xfe?t\x99\xf9sM\xd0\xa7a\xfd"\xdfN\\M\xb8\xd3#\'\x87\x93\xabk\x8d\xcd\xec\xcaWj/0\x0f\xc1\x93-\x07U\xdaTD:\xf5\xd4\xe5\xa9S!\xfcJ\xb0\xb6\x1a\xc9ea"\xdb\xd9\xc6\xab-\xb9\xfb\x89\xe3{\xbd\xc0\xe7f\'R\xec\xf320a\x00\x11\xd4\xe6\x9b\xc1\x16kF\xe7\xe8\xaeOX\xabP\xab\xcf\x84Jk@\x0fzi\xa5d)\x83\xdc\xccW"\x00\xb4\x18\'\x92\\d\xfdj\x1aW\x1b\xdd\x95\xda?\\V\xb4\x9f\xa2\x11s\xdc\xe7\xbdi\xcaK^u\xe9\x94\xbfq\x8e\x91)\xdcsn.\xf2:<\x16\x05\x8f(\xfc\xc9\xda9Bq\xcc\x1c\\\r(wy\x06!\xff\xcbK\xca\x90\xaa\xa1B\xfb\xde\xa8-\x7f\x10\xaa\x91\xf0>%sO\xde\xbb~\xd8E\xea\xb8y\x91Qv\x1e\xabz\x89\xeeo\x99U\x02\xdb2\xcb\x95v\xf7$-d\xe3\xf1\x1d`\x96\x89\xb5j\x98^c\xf3\x93n9+\x15\xc4Z\xa16B\x01v\xfd\xc8qh\xca\x85\xb2\xd8,\x0e\x8aN\xb0\xc9\xff\xe9}\x83=_\xd0\x1c\xbe\xc2\x99\xeaW\xa6\xb3\xceS[e\xfdt`M\x0c\xf2\x89\xaeM?\x8bxlBJ\xa1\xb2\xcb!{\xcb(f\x15\xcf[\xfdH\x1f\xfbt\x7f\xdb\xd4\\\xd3\xdb\xaa\xe4\xa1\xcay\xc19\xad\xf9\xbd\xb9;\xe9r\n\xb3\x8cI \xb0_\x13\xe2\x1c\x16\x1c8-u\xc3W;\x1f/\xc8\xbdy\x03\x91\xcd\xce\x04\xc2\xc7Sv\xd3\x8e\xf9:V\x99$V\xc9\x9a\xf2\xd4;iZ\x8c\xb1\x87\xfe\xdfw>\x12\xab:g\xc5\xfd\xa4\xd0\xb6}\x0b\x07I\xd6\xa2\x0c\xd4fAw\x9fn\xad B\xb5\xac\x85\x9b\x87u\xcd,\xeb\xb6\x1b7*\x04G#\x93\xffw89\xb6)sb\x87g\x9e\xecQad\xd6\xc3\xbd\xef\x06\xa3]\x83HM0oYvFU\xb5$,t|\xd8\x83t\x0c\xca\xcc\x9czW\x99qCl\x18\xa1\xf9w\xb0A]2\x16\x1f\x95\xde\xda=\xb9]]m\xcct\xf0\x81\x8e-{\x08@o\xd6\x1d\xb9P\xf1g\x94lP\xcf\x80\xd5t\x15r\x9d\x9f/(\x8f\x05\xcbA~\xb5\xbd\x81S*\xec<U6B\xb1(IVV(f\x15E\xa3\xb5\xea)8\x87\xae\xc9\xaa\xff\x01\x81[\xb8\xa4\xccuk\xb0\x1e\x05\x14\xc4\x88\xabk\xd3\r\xe1\x87f\xf7\xc6\x8fA7|\xa0\xb2\xfc\x8a\x07>\xa6\x99\xed\x87W-Y\xc0\x9d\xa4\x00\x9c\xc9\x1eq\xba\xde\xaa\xce\xb6\xfdM8\x03\x83\xe2\xac\xd52\xca\x92\xa0fq7_\x9c\x04\xbcZ\xff=\x96u\xc8w\x0b\x0e\xc9a\xef\x80:\x17\xb1\x11\x0e\xb9c\xba_\x06\x85\xc6\xe1\xdf\xc8\xec*\xa6\x16]\x9c\x9cg_\xfdhu\x81\x08\xf9\x1b\xc9\xe3t\x9cD\xea\x8b\x08\xe8\xa4\xbf:|rD\x80\x8c\x00\xe4c>K\xea\x91\xa4\xce\xcd&\xa7\xf3\x16d\x11`\xa3\x8d\x18\nF\x99]\xcc\\\xb3\x94\x1a\xac\xab0Zi\xe3\xa7ra\xc0\xfc}\x8d6q\xf9\xff9)\x9c2\x12}\xeb\xa8\xc5\xeb\xc4\xd6M\xda- 9\r\x89/\xbc\xf4\x01M\x12\xd6\n<\x1b\xac`o-\x8d^\n\xde\xc5\xdb\x04$\xcfP\x7f\'-\x00\xf2\x9f\xd1e\x8d/h\n(]%\xc8\xca\xa2i\xa4}\xac\xa7?!\xb6\xdc}\xf0\xd9\xee\x12{\x90b\x8a\xa5\xe4\xaa\xb0P\x8a\xdeo\xed\xf4\xfd\xb9\x7f\r\xb47\x1eM\r\xa9\x87\x8f\xdf`\r\xe9h\xf6\x8f\x9e\x1d\xb43?r\x00\xba\xa0\xbf{3\x06\xc9\xaa\xe8P\xed \x8b\x00\xf9\xb7\xb3pbZ\xe9\x05\xe6a\xff\x03\x17\xaa(\xe38\x91\x9bL\xd2\xb6d\x8f\xfc\x7f\xe6F\xc0\r\x00@\xb3\xd7\xd2\'\xdf!\xb1\x10~\n\xf5\xecx,\x1bz\xe5\xb5!\xea\x16}ob4Y\xc0\xc4@\xc6\n\x0c\x81\x98\n4*\x12%88d\xceB\xd0\xd2\xbb.3\xaeM\xd5\xe5\xd3\xbb AW\x89\x94\x91\r*\xdc\xe2\x1an\xd6\xf9@\xb6\xc1\x85KH\xbc\x7f&v\xdcA,]CU*d\x1b\x88L\xfe\xf3B\x1b\xf8f\x07\x89jL\xe0\xc4\xd0^l\x8bt\xea\xfd\x14\xbe\x1e\xa7\x15w\xe3E%\xfa\xc5\x1ch\xb3\xab9+!>'] | args: size 0 2404.509 | 1008248080 - recv_into | result: [5, b'\x17\x03\x03\x00\x1d'] | args: size 0 2404.513 | 1008248080 - recv_into | result: [29, b'\x00\x00\x00\x00\x00\x00\x00\x02\xa25D&y\xc3\xc8\xc5\xc8F;\xa3\x0e\xbd)\x7f+\x1bS\xaf\xbf'] | args: size 0 [{"text":"Calculation and reasoning, like weaving and ploughing, are work, not for human souls, but for clever combinations of iron and wood","author":"Mary Boole"}]
import adafruit_connection_manager import adafruit_requests from socket_logger import SocketPoolLogger radio = adafruit_connection_manager.CPythonNetwork() pool = adafruit_connection_manager.get_radio_socketpool(radio) # these lines are new: pool_logger = SocketPoolLogger(pool) pool_logger.enable_log_all(True) # and here we pass in the poll_logger, not the regular pool: requests = adafruit_requests.Session(pool_logger) with requests.get("http://wifitest.adafruit.com/testwifi/index.html") as response: print(response.text)
Gives you:
755216.343 | 146920365145 - getaddrinfo | result: [(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 0, '', ('52.207.219.136', 80))] | args: host: wifitest.adafruit.com port: 80 family: 0 type: 1 proto: 0 flags: 0 755216.343 | 146920365145 - socket | result: <socket.socket fd=744, family=2, type=1, proto=0> | socket hash: 146924194892 | args: family: 2 type: 1 proto: 0 fileno: None 755216.343 | 146924194892 - settimeout | result: None | args: value: 60 755216.421 | 146924194892 - connect | result: None | args: address: 52.207.219.136 port: 80 755216.421 | 146924194892 - send | result: 3 | args: bytes: b'GET' 755216.421 | 146924194892 - send | result: 2 | args: bytes: b' /' 755216.421 | 146924194892 - send | result: 19 | args: bytes: b'testwifi/index.html' 755216.421 | 146924194892 - send | result: 11 | args: bytes: b' HTTP/1.1\r\n' 755216.421 | 146924194892 - send | result: 4 | args: bytes: b'Host' 755216.421 | 146924194892 - send | result: 2 | args: bytes: b': ' 755216.421 | 146924194892 - send | result: 21 | args: bytes: b'wifitest.adafruit.com' 755216.421 | 146924194892 - send | result: 2 | args: bytes: b'\r\n' 755216.421 | 146924194892 - send | result: 10 | args: bytes: b'User-Agent' 755216.421 | 146924194892 - send | result: 2 | args: bytes: b': ' 755216.421 | 146924194892 - send | result: 22 | args: bytes: b'Adafruit CircuitPython' 755216.421 | 146924194892 - send | result: 2 | args: bytes: b'\r\n' 755216.421 | 146924194892 - send | result: 2 | args: bytes: b'\r\n' 755216.578 | 146924194892 - recv_into | result: [32, b'TTP/1.1 200 OK\r\nServer: nginx/1.'] | args: size 0 755216.578 | 146924194892 - recv_into | result: [16, b'18.0 (Ubuntu)\r\nD'] | args: size 0 755216.578 | 146924194892 - recv_into | result: [31, b'ate: Mon, 27 May 2024 16:02:49 '] | args: size 0 755216.578 | 146924194892 - recv_into | result: [32, b'GMT\r\nContent-Type: text/html\r\nCo'] | args: size 0 755216.578 | 146924194892 - recv_into | result: [62, b'ntent-Length: 69\r\nLast-Modified: Thu, 09 Dec 2021 17:26:22 GMT'] | args: size 0 755216.578 | 146924194892 - recv_into | result: [20, b'\r\nConnection: keep-a'] | args: size 0 755216.578 | 146924194892 - recv_into | result: [46, b'live\r\nETag: "61b23c3e-45"\r\nAccept-Ranges: byte'] | args: size 0 755216.578 | 146924194892 - recv_into | result: [45, b's\r\n\r\nThis is a test of Adafruit WiFi!\nIf you '] | args: size 0 755216.593 | 146924194892 - recv_into | result: [29, b'can read this, its working :)'] | args: size 29 This is a test of Adafruit WiFi! If you can read this, its working :)
# SPDX-FileCopyrightText: Copyright (c) 2024 Justin Myers # # SPDX-License-Identifier: MIT import sys import time def _log_method(obj_hash, method, result, *args, **kwargs): print(f"{time.monotonic():<0.3f} | ", end="") print(f"{obj_hash:14} - {method:12} | ", end="") print(f"result: {str(result):12} | ", end="") result_hash = kwargs.pop("result_hash", None) if result_hash: print(f"socket hash: {result_hash} | ", end="") if args: str_args = " ".join([str(arg) for arg in args]) print(f"args: {str_args}", end="") print("") class SocketLogger: def __init__( # noqa: PLR0913 - Too many arguments self, socket, family, type, proto, *, enable_log_close=False, enable_log_connect=False, enable_log_recv_into=False, enable_log_send=False, enable_log_sendto=False, enable_log_settimeout=False, ): self._socket = socket self._family = family self._type = type self._proto = proto self._hash = hash(self._socket) # note: we can not easily copy all methods, because calling # `dir(socket)` calls the actual properties and thus can have # bad effects other_method_names = [ "accept", "bind", "listen", "recv", "recvfrom_into", "sendall", "setblocking", "setsockopt", ] if sys.implementation.name != "circuitpython": other_method_names.extend( [ "detach", "fileno", "gettimeout", "getsockopt", ] ) for other_method_name in other_method_names: other_method = getattr(self._socket, other_method_name, None) if other_method: setattr(self, other_method_name, other_method) self.enable_log(enable_log_close, "close") self.enable_log(enable_log_connect, "connect") self.enable_log(enable_log_recv_into, "recv_into") self.enable_log(enable_log_send, "send") self.enable_log(enable_log_sendto, "sendto") self.enable_log(enable_log_settimeout, "settimeout") def __del__(self): _log_method(self._hash, "__del__", None) if hasattr(self._socket, "__del__"): return self._socket.__del__() def __enter__(self): _log_method(self._hash, "__enter__", self) return self def __exit__(self, exc_type, exc_val, exc_tb): _log_method( self._hash, "__exit__", None, "exc_type:", exc_type, "exc_val:", exc_val, "exc_tb:", exc_tb, ) if hasattr(self._socket, "__exit__"): return self._socket.__exit__(exc_type, exc_val, exc_tb) @property def family(self): return getattr(self._socket, "family", self._family) @property def proto(self): return getattr(self._socket, "proto", self._proto) @property def type(self): return getattr(self._socket, "type", self._type) def _call_method(self, method_name, method, log_args, *args, **kwargs): bytes_arg = kwargs.pop("socket_logger_bytes_arg", -1) try: result = method(*args, **kwargs) if bytes_arg == -1: _log_method(self._hash, method_name, result, *log_args) else: result_bytes = bytes(args[bytes_arg][:result]) _log_method(self._hash, method_name, [result, result_bytes], *log_args) return result except Exception as exc: _log_method(self._hash, method_name, exc, *log_args) raise def _log_close(self): return self._call_method("close", self._socket.close, []) def _log_connect(self, address, *args, **kwargs): # The *args and **kwargs are for the ESP32SPI and the # _FakeSSLSocket.connect where the ESP32SPI needs a tls_mode log_args = ["address:", address[0], "port:", address[1]] return self._call_method( "connect", self._socket.connect, log_args, address, *args, **kwargs ) def _log_recv_into(self, b, size=0): log_args = ["size", size] return self._call_method( "recv_into", self._socket.recv_into, log_args, b, size, socket_logger_bytes_arg=0, ) def _log_send(self, b): # `b` could be a memoryview, force to bytes log_args = ["bytes:", bytes(b)] return self._call_method("send", self._socket.send, log_args, b) def _log_sendto(self, b, address): # `b` could be a memoryview, force to bytes log_args = ["bytes:", bytes(b), "address:", address[0], "port:", address[1]] return self._call_method("sendto", self._socket.sendto, log_args, b, address) def _log_settimeout(self, value): log_args = ["value:", value] return self._call_method("settimeout", self._socket.settimeout, log_args, value) def enable_log(self, enable, method_name): native_method = getattr(self._socket, method_name) if native_method is None: return log_method = getattr(self, f"_log_{method_name}", native_method) method = log_method if enable else native_method setattr(self, method_name, method) class SocketPoolLogger: def __init__(self, socket_pool): self._socket_pool = socket_pool self._hash = hash(self._socket_pool) self._enable_log_close = False self._enable_log_connect = False self._enable_log_recv_into = False self._enable_log_send = False self._enable_log_sendto = False self._enable_log_settimeout = False @property def SOCK_DGRAM(self): return self._socket_pool.SOCK_DGRAM @property def SOCK_STREAM(self): return self._socket_pool.SOCK_STREAM @property def AF_INET(self): return self._socket_pool.AF_INET def getaddrinfo(self, host=None, port=None, family=0, type=0, proto=0, flags=0): # noqa: PLR0913 - Too many arguments log_args = [ "host:", host, "port:", port, "family:", family, "type:", type, "proto:", proto, "flags:", flags, ] try: result = self._socket_pool.getaddrinfo( host, port, family, type, proto, flags ) _log_method(self._hash, "getaddrinfo", result, *log_args) return result except Exception as exc: _log_method(self._hash, "getaddrinfo", exc, *log_args) raise def socket(self, family=0, type=0, proto=0, fileno=None): log_args = [ "family:", family, "type:", type, "proto:", proto, "fileno:", fileno, ] try: result = self._socket_pool.socket(family=family, type=type, proto=proto) _log_method( self._hash, "socket", result, *log_args, result_hash=hash(result) ) return SocketLogger( result, family, type, proto, enable_log_close=self._enable_log_close, enable_log_connect=self._enable_log_connect, enable_log_recv_into=self._enable_log_recv_into, enable_log_send=self._enable_log_send, enable_log_sendto=self._enable_log_sendto, enable_log_settimeout=self._enable_log_settimeout, ) except Exception as exc: _log_method(self._hash, "socket", exc, *log_args) raise def enable_log_all(self, enable): self._enable_log_close = enable self._enable_log_connect = enable self._enable_log_recv_into = enable self._enable_log_send = enable self._enable_log_sendto = enable self._enable_log_settimeout = enable def enable_log_close(self, enable): self._enable_log_close = enable def enable_log_connect(self, enable): self._enable_log_connect = enable def enable_log_recv_into(self, enable): self._enable_log_recv_into = enable def enable_log_send(self, enable): self._enable_log_send = enable def enable_log_sendto(self, enable): self._enable_log_sendto = enable def enable_log_settimeout(self, enable): self._enable_log_settimeout = enable