From 04ff7568eb7d26e889efcc39f3ef00b9b70d5ce1 Mon Sep 17 00:00:00 2001 From: checktheroads Date: Fri, 26 Jun 2020 10:10:43 -0700 Subject: [PATCH] fix issue with webhooks causing queries to fail --- hyperglass/api/routes.py | 30 +++++++++++++++++++++++------- hyperglass/external/_base.py | 32 +++++++++++++++----------------- hyperglass/external/slack.py | 2 +- 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/hyperglass/api/routes.py b/hyperglass/api/routes.py index 39a7171..3940f91 100644 --- a/hyperglass/api/routes.py +++ b/hyperglass/api/routes.py @@ -6,7 +6,7 @@ import json import time # Third Party -from fastapi import HTTPException +from fastapi import HTTPException, BackgroundTasks from starlette.requests import Request from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html @@ -25,15 +25,25 @@ from hyperglass.api.models.cert_import import EncodedRequest APP_PATH = os.environ["hyperglass_directory"] -async def query(query_data: Query, request: Request): - """Ingest request data pass it to the backend application to perform the query.""" +async def send_webhook(query_data: Query, request: Request) -> int: + """If webhooks are enabled, get request info and send a webhook. - headers = await process_headers(headers=request.headers) - - async with RIPEStat() as ripe: - network_info = await ripe.network_info(request.client.host, serialize=True) + Args: + query_data (Query): Valid query + request (Request): Starlette/FastAPI request + Returns: + int: Returns 1 regardless of result + """ if params.logging.http is not None: + headers = await process_headers(headers=request.headers) + + async with RIPEStat() as ripe: + host = headers.get( + "x-real-ip", headers.get("x-forwarded-for", request.client.host) + ) + network_info = await ripe.network_info(host, serialize=True) + async with Webhook(params.logging.http) as hook: await hook.send( query={ @@ -44,6 +54,12 @@ async def query(query_data: Query, request: Request): } ) + +async def query(query_data: Query, request: Request, background_tasks: BackgroundTasks): + """Ingest request data pass it to the backend application to perform the query.""" + + background_tasks.add_task(send_webhook, query_data, request) + # Initialize cache cache = Cache(db=params.cache.database, **REDIS_CONFIG) log.debug("Initialized cache {}", repr(cache)) diff --git a/hyperglass/external/_base.py b/hyperglass/external/_base.py index 2db2c17..f317a9a 100644 --- a/hyperglass/external/_base.py +++ b/hyperglass/external/_base.py @@ -4,7 +4,6 @@ import re import json as _json import socket -import asyncio from json import JSONDecodeError from socket import gaierror @@ -124,10 +123,23 @@ class BaseExternal: log.debug("Testing connection to {}", self.base_url) try: + # Parse out just the hostname from a URL string. + # E.g. `https://www.example.com` becomes `www.example.com` test_host = re.sub(r"http(s)?\:\/\/", "", self.base_url) - socket.socket().connect((test_host, 443)) + + # Create a generic socket object + test_socket = socket.socket() + + # Try opening a low-level socket to make sure it's even + # listening on the port prior to trying to use it. + test_socket.connect((test_host, 443)) + + # Properly shutdown & close the socket. + test_socket.shutdown(1) + test_socket.close() except gaierror as err: + # Raised if the target isn't listening on the port raise self._exception( f"{self.name} appears to be unreachable", err ) from None @@ -136,21 +148,7 @@ class BaseExternal: async def _atest(self): """Open a low-level connection to the base URL to ensure its port is open.""" - log.debug("Testing connection to {}", self.base_url) - - try: - test_host = re.sub(r"http(s)?\:\/\/", "", self.base_url) - _reader, _writer = await asyncio.open_connection(test_host, 443) - - except gaierror as err: - raise self._exception( - f"{self.name} appears to be unreachable", err - ) from None - - if _reader or _writer: - return True - else: - return False + return self._test() def _build_request(self, **kwargs): """Process requests parameters into structure usable by http library.""" diff --git a/hyperglass/external/slack.py b/hyperglass/external/slack.py index d42a4e0..0f1a204 100644 --- a/hyperglass/external/slack.py +++ b/hyperglass/external/slack.py @@ -12,7 +12,7 @@ class SlackHook(BaseExternal, name="Slack"): def __init__(self, config): """Initialize external base class with Slack connection details.""" - super().__init__(base_url="https://hooks.slack.com", config=config) + super().__init__(base_url="https://hooks.slack.com", config=config, parse=False) async def send(self, query): """Send an incoming webhook to Slack."""