|
|
- # -*- coding: utf-8 -*-
- """aiohttp requester module."""
-
- import asyncio
- import logging
- import socket
-
- import aiohttp
- import aiohttp.web
- import async_timeout
-
- from async_upnp_client import UpnpRequester
- from async_upnp_client import UpnpEventHandler
-
-
- _LOGGER = logging.getLogger(__name__)
-
-
- def get_local_ip(target_host=None) -> str:
- """Try to get the local IP of this machine, used to talk to target_url."""
- target_host = target_host or '8.8.8.8'
- target_port = 80
-
- try:
- temp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- temp_sock.connect((target_host, target_port))
- return temp_sock.getsockname()[0]
- finally:
- temp_sock.close()
-
-
- class AiohttpRequester(UpnpRequester):
- """Standard AioHttpUpnpRequester, to be used with UpnpFactory."""
-
- def __init__(self, timeout=5) -> None:
- """Initializer."""
- self._timeout = timeout
-
- async def async_do_http_request(self, method, url, headers=None, body=None, body_type='text'):
- """Do a HTTP request."""
- # pylint: disable=too-many-arguments
-
- async with async_timeout.timeout(self._timeout):
- async with aiohttp.ClientSession() as session:
- async with session.request(method, url, headers=headers, data=body) as response:
- status = response.status
- headers = response.headers
-
- if body_type == 'text':
- body = await response.text()
- elif body_type == 'raw':
- body = await response.read()
- elif body_type == 'ignore':
- body = None
-
- return status, headers, body
-
-
- class AiohttpSessionRequester(UpnpRequester):
- """
- Standard AiohttpSessionRequester, to be used with UpnpFactory.
-
- With pluggable session.
- """
-
- def __init__(self, session, with_sleep=False, timeout=5) -> None:
- """Initializer."""
- self._session = session
- self._with_sleep = with_sleep
- self._timeout = timeout
-
- async def async_do_http_request(self, method, url, headers=None, body=None, body_type='text'):
- """Do a HTTP request."""
- # pylint: disable=too-many-arguments
-
- if self._with_sleep:
- await asyncio.sleep(0.01)
-
- async with async_timeout.timeout(self._timeout):
- async with self._session.request(method, url, headers=headers, data=body) as response:
- status = response.status
- headers = response.headers
-
- if body_type == 'text':
- body = await response.text()
- elif body_type == 'raw':
- body = await response.read()
- elif body_type == 'ignore':
- body = None
-
- return status, headers, body
-
-
- class AiohttpNotifyServer:
- """AIO HTTP Server to handle incoming events."""
-
- def __init__(self, requester, listen_port, listen_host=None, loop=None) -> None:
- """Initializer."""
- self._listen_port = listen_port
- self._listen_host = listen_host or get_local_ip()
- self._loop = loop or asyncio.get_event_loop()
-
- self._aiohttp_server = None
- self._server = None
-
- callback_url = "http://{}:{}/notify".format(self._listen_host, self._listen_port)
- self.event_handler = UpnpEventHandler(callback_url, requester)
-
- async def start_server(self):
- """Start the HTTP server."""
- self._aiohttp_server = aiohttp.web.Server(self._handle_request)
- try:
- self._server = await self._loop.create_server(self._aiohttp_server,
- self._listen_host,
- self._listen_port)
- except OSError as error:
- _LOGGER.error("Failed to create HTTP server at %s:%d: %s",
- self._listen_host, self._listen_port, error)
-
- async def stop_server(self):
- """Stop the HTTP server."""
- if self._aiohttp_server:
- await self._aiohttp_server.shutdown(10)
-
- if self._server:
- self._server.close()
-
- async def _handle_request(self, request) -> aiohttp.web.Response:
- """Handle incoming requests."""
- _LOGGER.debug('Received request: %s', request)
- if request.method != 'NOTIFY':
- _LOGGER.debug('Not notify')
- return aiohttp.web.Response(status=405)
-
- headers = request.headers
- body = await request.text()
-
- status = await self.event_handler.handle_notify(headers, body)
- _LOGGER.debug('NOTIFY response status: %s', status)
- return aiohttp.web.Response(status=status)
-
- @property
- def callback_url(self) -> str:
- """Return callback URL on which we are callable."""
- return self.event_handler.callback_url
|