Browse Source

Add /status endpoint for monitoring (cf. irclog commit 5b809b1b)

master
JustAnotherArchivist 3 years ago
parent
commit
007c50fbc8
1 changed files with 21 additions and 3 deletions
  1. +21
    -3
      http2irc.py

+ 21
- 3
http2irc.py View File

@@ -148,6 +148,8 @@ class Config(dict):
raise InvalidConfig(f'Invalid map {key!r} web path: not a string')
if not map_['webpath'].startswith('/'):
raise InvalidConfig(f'Invalid map {key!r} web path: does not start at the root')
if map_['webpath'] == '/status':
raise InvalidConfig(f'Invalid map {key!r} web path: cannot be "/status"')
if map_['webpath'] in seenWebPaths:
raise InvalidConfig(f'Invalid map {key!r} web path: collides with map {seenWebPaths[map_["webpath"]]!r}')
seenWebPaths[map_['webpath']] = key
@@ -312,6 +314,7 @@ class IRCClientProtocol(asyncio.Protocol):
self.connectionClosedEvent = connectionClosedEvent
self.loop = loop
self.config = config
self.lastRecvTime = None
self.buffer = b''
self.connected = False
self.channels = channels # Currently joined/supposed-to-be-joined channels; set(str)
@@ -495,6 +498,7 @@ class IRCClientProtocol(asyncio.Protocol):

def data_received(self, data):
self.logger.debug(f'Data received: {data!r}')
self.lastRecvTime = time.time()
# If there's any data left in the buffer, prepend it to the data. Split on CRLF.
# Then, process all messages except the last one (since data might not end on a CRLF) and keep the remainder in the buffer.
# If data does end with CRLF, all messages will have been processed and the buffer will be empty again.
@@ -647,24 +651,34 @@ class IRCClient:
await wait_cancel_pending({asyncio.create_task(connectionClosedEvent.wait()), asyncio.create_task(sigintEvent.wait())}, return_when = concurrent.futures.FIRST_COMPLETED)
finally:
self._transport.close() #TODO BaseTransport.close is asynchronous and then triggers the protocol's connection_lost callback; need to wait for connectionClosedEvent again perhaps to correctly handle ^C?
self._transport = None
self._protocol = None
except (ConnectionRefusedError, asyncio.TimeoutError) as e:
self.logger.error(str(e))
await wait_cancel_pending({asyncio.create_task(sigintEvent.wait())}, timeout = 5)
if sigintEvent.is_set():
break

@property
def lastRecvTime(self):
return self._protocol.lastRecvTime if self._protocol else None


class WebServer:
logger = logging.getLogger('http2irc.WebServer')

def __init__(self, messageQueue, config):
def __init__(self, messageQueue, ircClient, config):
self.messageQueue = messageQueue
self.ircClient = ircClient
self.config = config

self._paths = {} # '/path' => ('#channel', auth, module, moduleargs) where auth is either False (no authentication) or the HTTP header value for basic auth

self._app = aiohttp.web.Application()
self._app.add_routes([aiohttp.web.post('/{path:.+}', self.post)])
self._app.add_routes([
aiohttp.web.get('/status', self.get_status),
aiohttp.web.post('/{path:.+}', self.post)
])

self.update_config(config)
self._configChanged = asyncio.Event()
@@ -688,6 +702,10 @@ class WebServer:
break
self._configChanged.clear()

async def get_status(self, request):
self.logger.info(f'Received request {id(request)} from {request.remote!r} for {request.path!r}')
return (aiohttp.web.Response if (self.ircClient.lastRecvTime or 0) > time.time() - 600 else aiohttp.web.HTTPInternalServerError)()

async def post(self, request):
self.logger.info(f'Received request {id(request)} from {request.remote!r} for {request.path!r} with body {(await request.read())!r}')
try:
@@ -761,7 +779,7 @@ async def main():
messageQueue = MessageQueue()

irc = IRCClient(messageQueue, config)
webserver = WebServer(messageQueue, config)
webserver = WebServer(messageQueue, irc, config)

sigintEvent = asyncio.Event()
def sigint_callback():


Loading…
Cancel
Save