feat: retry on rate limited requests
authorKyle Fuller <redacted>
Fri, 23 Sep 2022 21:56:15 +0000 (22:56 +0100)
committerKyle Fuller <redacted>
Fri, 23 Sep 2022 21:56:15 +0000 (22:56 +0100)
palaver.cpp
test/test_palaver.py

index 520aaa5dec6eb2c02f0907c53860add1cf97437e..8362edac47ebbf82d770969b4eee60101612af99 100644 (file)
@@ -118,8 +118,9 @@ struct PLVHTTPRequest : PLVHTTPMessage {
 class RetryStrategy {
 public:
        bool ShouldRetryRequest(unsigned int status) {
+               bool isRateLimited = status == 429;
                bool is5xx = status >= 500 && status <= 600;
-               return is5xx;
+               return isRateLimited || is5xx;
        }
 
        unsigned int GetMaximumRetryAttempts() {
index 9c2710f4f50dfec931290d14ecc40f85a5e63379..827f0f336d1f52f618211473ca87cf7f0cf12610 100644 (file)
@@ -271,6 +271,62 @@ async def test_receiving_notification_with_push_token(znc):
     assert connected.called
 
 
+async def test_receiving_notification_with_retry_on_rate_limit(znc):
+    reader, writer = znc
+
+    async def connected(reader, writer):
+        headers, body = await read_push_request(reader)
+        assert headers['Authorization'] == 'Bearer abcdefg'
+        assert json.loads(body.decode('utf-8')) == {
+            'badge': 1,
+            'message': 'Test notification',
+            'sender': 'palaver',
+            'network': 'b758eaab1a4611a310642a6e8419fbff'
+        }
+
+        if not hasattr(connected, 'requests'):
+            connected.requests = 1
+            writer.write(b'HTTP/1.1 429 Too Many Requests\r\n')
+        else:
+            connected.requests += 1
+            writer.write(b'HTTP/1.1 204 No Content\r\n')
+
+        writer.write(b'Connection: close\r\n')
+        writer.write(b'\r\n')
+
+        await writer.drain()
+        writer.close()
+
+    server = await asyncio.start_server(connected, host='127.0.0.1', port=8121)
+    await asyncio.sleep(0.2)
+    addr = server.sockets[0].getsockname()
+    url = f'http://{addr[0]}:{addr[1]}/push'
+
+    writer.write(b'PALAVER IDENTIFY 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e b758eaab1a4611a310642a6e8419fbff\r\n')
+    await writer.drain()
+
+    line = await reader.readline()
+    assert line == b'PALAVER REQ *\r\n'
+
+    writer.write(b'PALAVER BEGIN 9167e47b01598af7423e2ecd3d0a3ec4 611d3a30a3d666fc491cdea0d2e1dd6e\r\n')
+    writer.write(f'PALAVER SET PUSH-ENDPOINT {url}\r\n'.encode('utf-8'))
+    writer.write(f'PALAVER SET PUSH-TOKEN abcdefg\r\n'.encode('utf-8'))
+    writer.write(b'PALAVER END\r\n')
+    await writer.drain()
+
+    writer.write(b'PRIVMSG *palaver :test\r\n')
+    await writer.drain()
+
+    line = await reader.readline()
+    assert line == b':*palaver!znc@znc.in PRIVMSG admin :Notification sent to 1 clients.\r\n'
+
+    await asyncio.sleep(1.2)
+    server.close()
+    await server.wait_closed()
+
+    assert connected.requests == 2
+
+
 async def test_receiving_notification_with_retry_on_server_error(znc):
     reader, writer = znc
 
git clone https://git.99rst.org/PROJECT