33# SPDX-FileCopyrightText: 2024-2026 null8626 & Top.gg
44
55from collections .abc import Awaitable , Callable
6+ from asyncio import wait_for , TimeoutError
67from inspect import iscoroutinefunction
78from aiohttp import test_utils , web
89from typing import Any , TypeAlias
@@ -62,6 +63,8 @@ class Webhooks:
6263 :type port: :py:class:`int`
6364 :param app: The :class:`~aiohttp.web.Application` instance to use. Defaults to creating one with default configurations.
6465 :type app: :class:`~aiohttp.web.Application` | :class:`~aiohttp.test_utils.TestClient` | :py:obj:`None`
66+ :param timeout: The timeout for reading payloads in seconds. Defaults to one second.
67+ :type timeout: :py:class:`float`
6568
6669 :exception TypeError: One or more specified arguments has an invalid type.
6770 :exception ValueError: One or more specified arguments is empty.
@@ -77,6 +80,7 @@ class Webhooks:
7780 '__web_server' ,
7881 '__is_running' ,
7982 '__listeners' ,
83+ '__timeout' ,
8084 )
8185
8286 __route : str
@@ -96,6 +100,7 @@ def __init__(
96100 host : str = '0.0.0.0' ,
97101 port : int = 8080 ,
98102 app : web .Application | test_utils .TestClient | None = None ,
103+ timeout : float = 1.0 ,
99104 ):
100105 if (
101106 not isinstance (secret , str )
@@ -109,6 +114,8 @@ def __init__(
109114 raise ValueError ('The specified secret, route, and/or host must not be empty.' )
110115 elif port is not None and not isinstance (port , int ):
111116 raise TypeError ('The specified port must be an integer.' )
117+ elif timeout is not None and not isinstance (timeout , float ):
118+ raise TypeError ('The specified timeout must be a float.' )
112119
113120 self .__route = route
114121 self .__host = host
@@ -118,6 +125,7 @@ def __init__(
118125 self .__web_server = None
119126 self .__is_running = False
120127 self .__listeners = {}
128+ self .__timeout = timeout
121129
122130 def __repr__ (self ) -> str :
123131 return f'<{ __class__ .__name__ } route={ self .__route !r} host={ self .__host !r} port={ self .__port } is_running={ self .is_running } >'
@@ -200,11 +208,13 @@ async def handler(request: web.Request) -> web.Response:
200208 try :
201209 assert request .body_exists and request .has_body and request .can_read_body
202210
203- body = await request .text ()
211+ body = await wait_for ( request .text (), self . __timeout )
204212 json_body = json .loads (body )
205213
206214 payload_type = PayloadType (json_body ['type' ])
207215 payload = payload_type ._deserialize (json_body ['data' ])
216+ except TimeoutError :
217+ return web .json_response ({'error' : 'Malformed request' }, status = 400 )
208218 except web .HTTPRequestEntityTooLarge : # pragma: nocover
209219 return web .json_response ({'error' : 'Request body too large' }, status = 413 )
210220 except Exception as err : # pragma: nocover
0 commit comments