Selaa lähdekoodia

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

master
JustAnotherArchivist 3 vuotta sitten
vanhempi
commit
007c50fbc8
1 muutettua tiedostoa jossa 21 lisäystä ja 3 poistoa
  1. +21
    -3
      http2irc.py

+ 21
- 3
http2irc.py Näytä tiedosto

@@ -148,6 +148,8 @@ class Config(dict):
raise InvalidConfig(f'Invalid map {key!r} web path: not a string') raise InvalidConfig(f'Invalid map {key!r} web path: not a string')
if not map_['webpath'].startswith('/'): if not map_['webpath'].startswith('/'):
raise InvalidConfig(f'Invalid map {key!r} web path: does not start at the root') 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: if map_['webpath'] in seenWebPaths:
raise InvalidConfig(f'Invalid map {key!r} web path: collides with map {seenWebPaths[map_["webpath"]]!r}') raise InvalidConfig(f'Invalid map {key!r} web path: collides with map {seenWebPaths[map_["webpath"]]!r}')
seenWebPaths[map_['webpath']] = key seenWebPaths[map_['webpath']] = key
@@ -312,6 +314,7 @@ class IRCClientProtocol(asyncio.Protocol):
self.connectionClosedEvent = connectionClosedEvent self.connectionClosedEvent = connectionClosedEvent
self.loop = loop self.loop = loop
self.config = config self.config = config
self.lastRecvTime = None
self.buffer = b'' self.buffer = b''
self.connected = False self.connected = False
self.channels = channels # Currently joined/supposed-to-be-joined channels; set(str) 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): def data_received(self, data):
self.logger.debug(f'Data received: {data!r}') 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. # 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. # 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. # 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) await wait_cancel_pending({asyncio.create_task(connectionClosedEvent.wait()), asyncio.create_task(sigintEvent.wait())}, return_when = concurrent.futures.FIRST_COMPLETED)
finally: 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.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: except (ConnectionRefusedError, asyncio.TimeoutError) as e:
self.logger.error(str(e)) self.logger.error(str(e))
await wait_cancel_pending({asyncio.create_task(sigintEvent.wait())}, timeout = 5) await wait_cancel_pending({asyncio.create_task(sigintEvent.wait())}, timeout = 5)
if sigintEvent.is_set(): if sigintEvent.is_set():
break break


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



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


def __init__(self, messageQueue, config):
def __init__(self, messageQueue, ircClient, config):
self.messageQueue = messageQueue self.messageQueue = messageQueue
self.ircClient = ircClient
self.config = config 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._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 = 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.update_config(config)
self._configChanged = asyncio.Event() self._configChanged = asyncio.Event()
@@ -688,6 +702,10 @@ class WebServer:
break break
self._configChanged.clear() 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): 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}') self.logger.info(f'Received request {id(request)} from {request.remote!r} for {request.path!r} with body {(await request.read())!r}')
try: try:
@@ -761,7 +779,7 @@ async def main():
messageQueue = MessageQueue() messageQueue = MessageQueue()


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


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


Ladataan…
Peruuta
Tallenna