forked from peterhinch/micropython-mqtt
-
Notifications
You must be signed in to change notification settings - Fork 4
/
mqtt_as_OOM_protection.py
55 lines (50 loc) · 2.2 KB
/
mqtt_as_OOM_protection.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# Author: Kevin Köck
# Copyright Kevin Köck 2019 Released under the MIT license
# Created on 2019-10-28
# This is basically just a paranoid change protecting the device from crashing
# with a memory allocation error if it receives a message not fitting in RAM.
# It will still receive and process the message but the arguments "topic" or "msg"
# of the callback might be None depending on which values couldn't be received
# because of the memory allocation error.
__updated__ = "2019-10-28"
__version__ = "0.1"
from .mqtt_as import MQTTClient as _MQTTClient, ticks_ms, BUSY_ERRORS, asyncio, _SOCKET_POLL_DELAY
import gc
class MQTTClient(_MQTTClient):
async def _as_read(self, n, sock=None): # OSError caught by superclass
if sock is None:
sock = self._sock
data = b''
t = ticks_ms()
r = 0
while r < n:
if self._timeout(t) or not self.isconnected():
raise OSError(-1)
nr = (n - r) if (n - r) < 200 else 200
try:
msg = sock.read(nr)
if msg is not None:
r += len(msg)
except OSError as e: # ESP32 issues weird 119 errors here
msg = None
if e.args[0] not in BUSY_ERRORS:
raise
except MemoryError as e:
# no way of knowing how many bytes were received so the rest would be
# received later and lead to buffer overflows and keepalive timeout anyway
# so best to terminate the connection
raise OSError
if msg == b'': # Connection closed by host
raise OSError(-1)
if msg is not None and data is not None: # data received
try:
data = b''.join((data, msg))
except MemoryError as e:
data = None
gc.collect()
t = ticks_ms()
self.last_rx = ticks_ms()
await asyncio.sleep_ms(0 if nr == 200 and msg is not None else _SOCKET_POLL_DELAY)
# don't wait if receiving a big message in chunks but yield
# to not block all other coroutines
return data