Exemplo n.º 1
0
        async def _func(obj: Any, request: web.Request, *a: Any, **kw: Any) -> None:
            websocket = web.WebSocketResponse()  # type: ignore

            request_ip = RequestHandler.get_request_ip(request, context)
            try:
                await websocket.prepare(request)
            except Exception:
                try:
                    await websocket.close()
                except Exception:
                    pass

                if access_log:
                    logging.getLogger('transport.http').info('[{}] {} {} "CANCELLED {}{}" {} "{}" {}'.format(
                        RequestHandler.colorize_status('websocket', 101),
                        request_ip,
                        '"{}"'.format(request._cache['auth'].login.replace('"', '')) if request._cache.get('auth') and getattr(request._cache.get('auth'), 'login', None) else '-',
                        request.path,
                        '?{}'.format(request.query_string) if request.query_string else '',
                        request._cache.get('websocket_uuid', ''),
                        request.headers.get('User-Agent', '').replace('"', ''),
                        '-'
                    ))

                return

            context['_http_open_websockets'] = context.get('_http_open_websockets', [])
            context['_http_open_websockets'].append(websocket)

            if access_log:
                logging.getLogger('transport.http').info('[{}] {} {} "OPEN {}{}" {} "{}" {}'.format(
                    RequestHandler.colorize_status('websocket', 101),
                    request_ip,
                    '"{}"'.format(request._cache['auth'].login.replace('"', '')) if request._cache.get('auth') and getattr(request._cache.get('auth'), 'login', None) else '-',
                    request.path,
                    '?{}'.format(request.query_string) if request.query_string else '',
                    request._cache.get('websocket_uuid', ''),
                    request.headers.get('User-Agent', '').replace('"', ''),
                    '-'
                ))

            result = compiled_pattern.match(request.path)
            values = inspect.getfullargspec(func)
            kwargs = {k: values.defaults[i] for i, k in enumerate(values.args[len(values.args) - len(values.defaults):])} if values.defaults else {}
            if result:
                for k, v in result.groupdict().items():
                    kwargs[k] = v

            if len(values.args) - (len(values.defaults) if values.defaults else 0) >= 3:
                # If the function takes a third required argument the value will be filled with the request object
                a = a + (request,)
            if 'request' in values.args and (len(values.args) - (len(values.defaults) if values.defaults else 0) < 3 or values.args[2] != 'request'):
                kwargs['request'] = request

            try:
                routine = func(*(obj, websocket, *a), **merge_dicts(kwargs, kw))
                callback_functions = (await routine) if isinstance(routine, Awaitable) else routine  # type: Optional[Union[Tuple, Callable]]
            except Exception as e:
                logging.getLogger('exception').exception('Uncaught exception: {}'.format(str(e)))
                try:
                    await websocket.close()
                except Exception:
                    pass

                try:
                    context['_http_open_websockets'].remove(websocket)
                except Exception:
                    pass

                if access_log:
                    logging.getLogger('transport.http').info('[{}] {} {} "{} {}{}" {} "{}" {}'.format(
                        RequestHandler.colorize_status('websocket', 500),
                        request_ip,
                        '"{}"'.format(request._cache['auth'].login.replace('"', '')) if request._cache.get('auth') and getattr(request._cache.get('auth'), 'login', None) else '-',
                        RequestHandler.colorize_status('ERROR', 500),
                        request.path,
                        '?{}'.format(request.query_string) if request.query_string else '',
                        request._cache.get('websocket_uuid', ''),
                        request.headers.get('User-Agent', '').replace('"', ''),
                        '-'
                    ))

                return

            _receive_func = None
            _close_func = None

            if callback_functions and isinstance(callback_functions, tuple):
                try:
                    _receive_func, _close_func = callback_functions
                except ValueError:
                    _receive_func, = callback_functions
            elif callback_functions:
                _receive_func = callback_functions

            try:
                async for message in websocket:
                    if message.type == WSMsgType.TEXT:
                        if _receive_func:
                            try:
                                await _receive_func(message.data)
                            except Exception as e:
                                logging.getLogger('exception').exception('Uncaught exception: {}'.format(str(e)))
                    elif message.type == WSMsgType.ERROR:
                        if not context.get('log_level') or context.get('log_level') in ['DEBUG']:
                            ws_exception = websocket.exception()
                            if isinstance(ws_exception, (EofStream, RuntimeError)):
                                pass
                            elif isinstance(ws_exception, Exception):
                                logging.getLogger('exception').exception('Uncaught exception: {}'.format(str(ws_exception)))
                            else:
                                logging.getLogger('transport.http').warning('Websocket exception: "{}"'.format(ws_exception))
                    elif message.type == WSMsgType.CLOSED:
                        break  # noqa
            except Exception as e:
                pass
            finally:
                if _close_func:
                    try:
                        await _close_func()
                    except Exception as e:
                        logging.getLogger('exception').exception('Uncaught exception: {}'.format(str(e)))
                try:
                    await websocket.close()
                except Exception:
                    pass

                try:
                    context['_http_open_websockets'].remove(websocket)
                except Exception:
                    pass
Exemplo n.º 2
0
    async def ws_handler(self, request):
        ws = web.WebSocketResponse()
        await ws.prepare(request)

        # Each connection will have its own input queue
        input_queue = queue.Queue()
        t_worker = None
        authenticated = False

        async for msg in ws:
            if authenticated:
                # Add content of every message received in input queue
                try:
                    input_queue.put(msg.data.encode())
                except UnicodeEncodeError:
                    # Should we handle Encode error?
                    # xterm.js seems to operate with the websocket in text mode,
                    pass
            else:
                try:
                    data = json.loads(msg.data)
                except json.decoder.JSONDecodeError:
                    continue

                token = data.get('token')
                if not token:
                    continue

                token = await self.middleware.call('auth.get_token', token)
                if not token:
                    ws.send_json({
                        'msg': 'failed',
                        'error': {
                            'error': errno.EACCES,
                            'reason': 'Invalid token',
                        }
                    })
                    continue

                authenticated = True
                ws.send_json({
                    'msg': 'connected',
                })
                t_worker = ShellWorkerThread(ws=ws,
                                             input_queue=input_queue,
                                             loop=asyncio.get_event_loop())
                t_worker.start()

        # If connection was not authenticated, return earlier
        if not authenticated:
            return ws

        # If connection has been closed lets make sure shell is killed
        if t_worker.shell_pid:

            try:
                kqueue = select.kqueue()
                kevent = select.kevent(t_worker.shell_pid,
                                       select.KQ_FILTER_PROC,
                                       select.KQ_EV_ADD | select.KQ_EV_ENABLE,
                                       select.KQ_NOTE_EXIT)
                kqueue.control([kevent], 0)

                os.kill(t_worker.shell_pid, signal.SIGTERM)

                # If process has not died in 2 seconds, try the big gun
                events = await self.middleware.run_in_thread(
                    kqueue.control, None, 1, 2)
                if not events:
                    os.kill(t_worker.shell_pid, signal.SIGKILL)

                    # If process has not died even with the big gun
                    # There is nothing else we can do, leave it be and
                    # release the worker thread
                    events = await self.middleware.run_in_thread(
                        kqueue.control, None, 1, 2)
                    if not events:
                        t_worker.die()
            except ProcessLookupError:
                pass

        # Wait thread join in yet another thread to avoid event loop blockage
        # There may be a simpler/better way to do this?
        await self.middleware.run_in_thread(t_worker.join)

        return ws
 def handler(request):
     ws = web.WebSocketResponse(protocols=('foo', 'bar'))
     yield from ws.prepare(request)
     yield from ws.close()
     closed.set_result(None)
     return ws
Exemplo n.º 4
0
    async def handler(request):
        ws = web.WebSocketResponse(heartbeat=0.05)
        await ws.prepare(request)

        await ws.receive()
        return ws
Exemplo n.º 5
0
 async def handler(request):
     ws = web.WebSocketResponse()
     await ws.prepare(request)
     await ws.receive()
     await ws.close()
     return ws
Exemplo n.º 6
0
    async def websocket_handler(self, request):
        """Send notifications to admin client over websocket."""

        ws = web.WebSocketResponse()
        await ws.prepare(request)
        socket_id = str(uuid.uuid4())
        queue = BasicMessageQueue()
        loop = asyncio.get_event_loop()

        if self.admin_insecure_mode:
            # open to send websocket messages without api key auth
            queue.authenticated = True
        else:
            header_admin_api_key = request.headers.get("x-api-key")
            # authenticated via http header?
            queue.authenticated = header_admin_api_key == self.admin_api_key

        try:
            self.websocket_queues[socket_id] = queue
            await queue.enqueue({
                "topic": "settings",
                "payload": {
                    "authenticated":
                    queue.authenticated,
                    "label":
                    self.context.settings.get("default_label"),
                    "endpoint":
                    self.context.settings.get("default_endpoint"),
                    "no_receive_invites":
                    self.context.settings.get("admin.no_receive_invites",
                                              False),
                    "help_link":
                    self.context.settings.get("admin.help_link"),
                },
            })

            closed = False
            receive = loop.create_task(ws.receive_json())
            send = loop.create_task(queue.dequeue(timeout=5.0))

            while not closed:
                try:
                    await asyncio.wait((receive, send),
                                       return_when=asyncio.FIRST_COMPLETED)
                    if ws.closed:
                        closed = True

                    if receive.done():
                        if not closed:
                            msg_received = None
                            msg_api_key = None
                            try:
                                # this call can re-raise exeptions from inside the task
                                msg_received = receive.result()
                                msg_api_key = msg_received.get("x-api-key")
                            except Exception:
                                LOGGER.exception(
                                    "Exception in websocket receiving task:")
                            if self.admin_api_key and self.admin_api_key == msg_api_key:
                                # authenticated via websocket message
                                queue.authenticated = True

                            receive = loop.create_task(ws.receive_json())

                    if send.done():
                        try:
                            msg = send.result()
                        except asyncio.TimeoutError:
                            msg = None

                        if msg is None:
                            # we send fake pings because the JS client
                            # can't detect real ones
                            msg = {
                                "topic": "ping",
                                "authenticated": queue.authenticated,
                            }
                        if not closed:
                            if msg:
                                await ws.send_json(msg)
                            send = loop.create_task(queue.dequeue(timeout=5.0))

                except asyncio.CancelledError:
                    closed = True

            if not receive.done():
                receive.cancel()
            if not send.done():
                send.cancel()

        finally:
            del self.websocket_queues[socket_id]

        return ws
Exemplo n.º 7
0
    async def handler(request):
        ws = web.WebSocketResponse()
        if not ws.can_prepare(request):
            raise web.HTTPUpgradeRequired()

        return web.Response()
Exemplo n.º 8
0
    async def handle(self):
        """Handle the websocket connection."""
        request = self.request
        wsock = self.wsock = web.WebSocketResponse(heartbeat=55)
        await wsock.prepare(request)
        self.debug("Connected")

        # Get a reference to current task so we can cancel our connection
        self._handle_task = asyncio.Task.current_task(loop=self.hass.loop)

        @callback
        def handle_hass_stop(event):
            """Cancel this connection."""
            self.cancel()

        unsub_stop = self.hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP,
                                                handle_hass_stop)
        self._writer_task = self.hass.async_add_job(self._writer())
        final_message = None
        msg = None
        authenticated = False

        try:
            if request[KEY_AUTHENTICATED]:
                authenticated = True

            else:
                self.debug("Request auth")
                await self.wsock.send_json(auth_required_message())
                msg = await wsock.receive_json()
                msg = AUTH_MESSAGE_SCHEMA(msg)

                if self.hass.auth.active and 'access_token' in msg:
                    self.debug("Received access_token")
                    token = self.hass.auth.async_get_access_token(
                        msg['access_token'])
                    authenticated = token is not None

                elif ((not self.hass.auth.active
                       or self.hass.auth.support_legacy)
                      and 'api_password' in msg):
                    self.debug("Received api_password")
                    authenticated = validate_password(request,
                                                      msg['api_password'])

            if not authenticated:
                self.debug("Authorization failed")
                await self.wsock.send_json(
                    auth_invalid_message('Invalid access token or password'))
                await process_wrong_login(request)
                return wsock

            self.debug("Auth OK")
            await self.wsock.send_json(auth_ok_message())

            # ---------- AUTH PHASE OVER ----------

            msg = await wsock.receive_json()
            last_id = 0
            handlers = self.hass.data[DOMAIN]

            while msg:
                self.debug("Received", msg)
                msg = MINIMAL_MESSAGE_SCHEMA(msg)
                cur_id = msg['id']

                if cur_id <= last_id:
                    self.to_write.put_nowait(
                        error_message(cur_id, ERR_ID_REUSE,
                                      'Identifier values have to increase.'))

                elif msg['type'] not in handlers:
                    self.log_error('Received invalid command: {}'.format(
                        msg['type']))
                    self.to_write.put_nowait(
                        error_message(cur_id, ERR_UNKNOWN_COMMAND,
                                      'Unknown command.'))

                else:
                    handler, schema = handlers[msg['type']]
                    handler(self.hass, self, schema(msg))

                last_id = cur_id
                msg = await wsock.receive_json()

        except vol.Invalid as err:
            error_msg = "Message incorrectly formatted: "
            if msg:
                error_msg += humanize_error(msg, err)
            else:
                error_msg += str(err)

            self.log_error(error_msg)

            if not authenticated:
                final_message = auth_invalid_message(error_msg)

            else:
                if isinstance(msg, dict):
                    iden = msg.get('id')
                else:
                    iden = None

                final_message = error_message(iden, ERR_INVALID_FORMAT,
                                              error_msg)

        except TypeError as err:
            if wsock.closed:
                self.debug("Connection closed by client")
            else:
                _LOGGER.exception("Unexpected TypeError: %s", err)

        except ValueError as err:
            msg = "Received invalid JSON"
            value = getattr(err, 'doc', None)  # Py3.5+ only
            if value:
                msg += ': {}'.format(value)
            self.log_error(msg)
            self._writer_task.cancel()

        except CANCELLATION_ERRORS:
            self.debug("Connection cancelled")

        except asyncio.QueueFull:
            self.log_error("Client exceeded max pending messages [1]:",
                           MAX_PENDING_MSG)
            self._writer_task.cancel()

        except Exception:  # pylint: disable=broad-except
            error = "Unexpected error inside websocket API. "
            if msg is not None:
                error += str(msg)
            _LOGGER.exception(error)

        finally:
            unsub_stop()

            for unsub in self.event_listeners.values():
                unsub()

            try:
                if final_message is not None:
                    self.to_write.put_nowait(final_message)
                self.to_write.put_nowait(None)
                # Make sure all error messages are written before closing
                await self._writer_task
            except asyncio.QueueFull:
                self._writer_task.cancel()

            await wsock.close()
            self.debug("Closed connection")

        return wsock
Exemplo n.º 9
0
 def __init__(self):
     super().__init__()
     self._subscriptions = []
     self.websocket = web.WebSocketResponse(heartbeat=60)
Exemplo n.º 10
0
 async def init_ws(self) -> web.WebSocketResponse:
     ws = web.WebSocketResponse()
     await ws.prepare(self.request)
     return ws
Exemplo n.º 11
0
async def job_status_ws_handler(redis_client: Redis,
                                request: web.Request) -> web.WebSocketResponse:
    ws = web.WebSocketResponse()
    await ws.prepare(request)
    logging.debug('Client connected to job status')

    jobs = {}
    job_status = {}

    async def check_status(job: Job) -> str:
        await asyncio.sleep(JOB_REFRESH_PERIOD)
        try:
            job.refresh()
        except NoSuchJobError:
            jobs[job.id] = None
        return job.id

    recieve_msg_task = asyncio.ensure_future(ws.receive())
    pending = set([recieve_msg_task])
    while not ws.closed:
        done, pending = await asyncio.wait(pending,
                                           return_when=asyncio.FIRST_COMPLETED)

        # update job list when received message
        if recieve_msg_task in done:
            msg = recieve_msg_task.result()
            if msg.type == WSMsgType.ERROR:
                logging.error(
                    f'WS connection closed with exception {ws.exception()}')

            elif msg.type == WSMsgType.TEXT:
                if msg.data == 'close':
                    await ws.close()

                else:
                    new_job_ids = json.loads(msg.data)
                    logging.debug('Watching new job ids: %s', new_job_ids)
                    jobs = dict(
                        zip(new_job_ids,
                            Job.fetch_many(new_job_ids, redis_client)))

                    pending = set(
                        asyncio.ensure_future(check_status(job))
                        for job in jobs.values() if job is not None)
                    recieve_msg_task = asyncio.ensure_future(ws.receive())
                    pending.add(recieve_msg_task)

                    job_status = {
                        job_id: job.get_status(
                            refresh=False) if job is not None else None
                        for job_id, job in jobs.items()
                    }
                    await ws.send_json(prepare_response(jobs))

        # handle job status check
        else:
            change = False
            for done_task in done:
                job_id = done_task.result()

                if jobs[job_id] is not None:
                    job = jobs[job_id]
                    new_status = job.get_status(refresh=False)
                    pending.add(check_status(jobs[job_id]))
                    if job_status[job_id] != new_status:
                        job_status[job_id] = new_status
                        change = True
                    else:
                        now = time.time()
                        if (
                                job.is_queued or job.is_started
                        ) and now - job.enqueued_at.timestamp() > TASK_LIMIT:
                            jobs[job_id].exc_info = 'task time limit exceeded'
                            change = True

                else:
                    job_status[job_id] = None
                    change = True

            if change:
                await ws.send_json(prepare_response(jobs))

    logging.debug('Client disconnected from job status')
    return ws
Exemplo n.º 12
0
async def handler(req: web.Request, service_url: str, **_kwargs):
    """ Redirects communication to jupyter notebook in the backend

        (e.g. front-end) client <---> proxy <-----> server (e.g. backend dynamic service)

    :param req: aiohttp request
    :type req: web.Request
    :param service_url: Resolved url pointing to backend jupyter service. Typically http:hostname:port/x/12345/.
    :type service_url: str
    :raises ValueError: Unexpected web-socket message
    """

    # FIXME: hash of statics somehow get do not work. then neeed to be strip away
    # Removing query ... which not sure is a good idea
    target_url = URL(service_url).origin() / req.path.lstrip("/")

    reqH = req.headers.copy()
    if (
        reqH.get("connection", "").lower() == "upgrade"
        and reqH.get("upgrade", "").lower() == "websocket"
        and req.method == "GET"
    ):
        ws_server = web.WebSocketResponse()
        available = ws_server.can_prepare(req)
        if available:
            await ws_server.prepare(req)
            logger.info("##### WS_SERVER %s", pprint.pformat(ws_server))

            try:
                req.app[APP_SOCKETS_KEY].append(ws_server)

                async with aiohttp.ClientSession(cookies=req.cookies) as session:
                    # websocket connection with backend
                    async with session.ws_connect(target_url) as ws_client:
                        logger.info("##### WS_CLIENT %s", pprint.pformat(ws_client))

                        await asyncio.wait(
                            [
                                ws_forward(ws_server, ws_client),
                                ws_forward(ws_client, ws_server),
                            ],
                            return_when=asyncio.FIRST_COMPLETED,
                        )
                        return ws_server
            finally:
                req.app[APP_SOCKETS_KEY].remove(ws_server)
    else:
        target_url = URL(service_url).origin().with_path(req.path).with_query(req.query)
        async with client_request(
            req.method,
            target_url,
            headers=reqH,
            allow_redirects=False,
            data=await req.read(),
        ) as res:
            data = await res.read()
            response = web.Response(
                headers=res.headers.copy(), status=res.status, body=data
            )
        # TODO: PC add chunks load. Mattwards takes very long to load
        # TODO: PC unique session or redo context management??
        # TODO: should be fixed in #710
        #     response = web.Response(
        #         headers=res.headers.copy(),
        #         status=res.status
        #     )
        #     await response.prepare(req)
        #     content = res.content
        #     whole = await content.read()
        #     await response.write(whole)
        logger.debug("<-- %s", target_url)
        # await response.write_eof()
        return response
Exemplo n.º 13
0
async def test_no_upgrade() -> None:
    ws = web.WebSocketResponse()
    req = make_mocked_request('GET', '/')
    with pytest.raises(web.HTTPBadRequest):
        await ws.prepare(req)
Exemplo n.º 14
0
async def test_not_get() -> None:
    ws = web.WebSocketResponse()
    req = make_mocked_request('POST', '/')
    with pytest.raises(web.HTTPMethodNotAllowed):
        await ws.prepare(req)
Exemplo n.º 15
0
    def handle(self):
        """Handle the websocket connection."""
        request = self.request
        wsock = self.wsock = web.WebSocketResponse()
        yield from wsock.prepare(request)
        self.debug("Connected")

        # Get a reference to current task so we can cancel our connection
        self._handle_task = asyncio.Task.current_task(loop=self.hass.loop)

        @callback
        def handle_hass_stop(event):
            """Cancel this connection."""
            self.cancel()

        unsub_stop = self.hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP,
                                                handle_hass_stop)
        self._writer_task = self.hass.async_add_job(self._writer())
        final_message = None
        msg = None
        authenticated = False

        try:
            if request[KEY_AUTHENTICATED]:
                authenticated = True

            else:
                yield from self.wsock.send_json(auth_required_message())
                msg = yield from wsock.receive_json()
                msg = AUTH_MESSAGE_SCHEMA(msg)

                if validate_password(request, msg['api_password']):
                    authenticated = True

                else:
                    self.debug("Invalid password")
                    yield from self.wsock.send_json(
                        auth_invalid_message('Invalid password'))

            if not authenticated:
                yield from process_wrong_login(request)
                return wsock

            yield from self.wsock.send_json(auth_ok_message())

            # ---------- AUTH PHASE OVER ----------

            msg = yield from wsock.receive_json()
            last_id = 0

            while msg:
                self.debug("Received", msg)
                msg = BASE_COMMAND_MESSAGE_SCHEMA(msg)
                cur_id = msg['id']

                if cur_id <= last_id:
                    self.to_write.put_nowait(
                        error_message(cur_id, ERR_ID_REUSE,
                                      'Identifier values have to increase.'))

                else:
                    handler_name = 'handle_{}'.format(msg['type'])
                    getattr(self, handler_name)(msg)

                last_id = cur_id
                msg = yield from wsock.receive_json()

        except vol.Invalid as err:
            error_msg = "Message incorrectly formatted: "
            if msg:
                error_msg += humanize_error(msg, err)
            else:
                error_msg += str(err)

            self.log_error(error_msg)

            if not authenticated:
                final_message = auth_invalid_message(error_msg)

            else:
                if isinstance(msg, dict):
                    iden = msg.get('id')
                else:
                    iden = None

                final_message = error_message(iden, ERR_INVALID_FORMAT,
                                              error_msg)

        except TypeError as err:
            if wsock.closed:
                self.debug("Connection closed by client")
            else:
                _LOGGER.exception("Unexpected TypeError: %s", msg)

        except ValueError as err:
            msg = "Received invalid JSON"
            value = getattr(err, 'doc', None)  # Py3.5+ only
            if value:
                msg += ': {}'.format(value)
            self.log_error(msg)
            self._writer_task.cancel()

        except asyncio.CancelledError:
            self.debug("Connection cancelled by server")

        except asyncio.QueueFull:
            self.log_error("Client exceeded max pending messages [1]:",
                           MAX_PENDING_MSG)
            self._writer_task.cancel()

        except Exception:  # pylint: disable=broad-except
            error = "Unexpected error inside websocket API. "
            if msg is not None:
                error += str(msg)
            _LOGGER.exception(error)

        finally:
            unsub_stop()

            for unsub in self.event_listeners.values():
                unsub()

            try:
                if final_message is not None:
                    self.to_write.put_nowait(final_message)
                self.to_write.put_nowait(None)
                # Make sure all error messages are written before closing
                yield from self._writer_task
            except asyncio.QueueFull:
                self._writer_task.cancel()

            yield from wsock.close()
            self.debug("Closed connection")

        return wsock
Exemplo n.º 16
0
async def ws_handler(request):
    ''' Handles websocket connection'''

    log = request.app.logger
    loop = request.app.loop

    ws = web.WebSocketResponse()

    # Display error if ws fails to start
    if not ws.can_prepare(request):
        log.error("Well, websocket failed to start..")

    # prepare websocket
    await ws.prepare(request)

    # save ws client
    request.app['client'] = ws

    log.debug('Client has connected')
    ws.send_str('Well hello there Client hero!')

    # if we still have epubs in memory let's make a dict of the epub and send it to the client
    current_epubs: dict = {
        epub_name: {
            'ready': epub.get('ready'),
            'conversions': epub.get('conversions')
        }
        for (epub_name, epub) in request.app.get('epubs').items()
    }
    if len(current_epubs) > 0:
        ws.send_json({'do': 'show_current_epubs', 'with': current_epubs})

    # and let's do the same for the conversion_unit
    current_conversion_unit = request.app.get('conversion_unit')
    if current_conversion_unit:
        ws.send_json({
            'do': 'show_current_conversion_unit',
            'with': current_conversion_unit
        })

    # go over received message
    async for msg in ws:
        log.debug(f'Client sent: {msg}'[:300])

        # convert data to json
        data: Dict[str, Union[str, Dict]] = json.loads(msg.data)

        # handle messages
        if data.get('do') == 'convert_epub':
            file = data.get('with')
            # schedule task for converting epub
            loop.create_task(convert_epub(file, app=request.app))

        elif data.get('do') == 'set_conversion_unit':
            # get conversion unit
            unit_system = data.get('with', 'metric')

            request.app['conversion_unit'] = unit_system
            log.debug(f'Conversion unit set to {unit_system}')

        elif data.get('do') == 'remove_epub':
            del request.app['epubs'][data.get('with')]

    log.debug('Client has disconnected')
    return ws
Exemplo n.º 17
0
    async def async_handle(self):
        """Handle a websocket response."""
        request = self.request
        wsock = self.wsock = web.WebSocketResponse(heartbeat=55)
        await wsock.prepare(request)
        self._logger.debug("Connected")

        # Py3.7+
        if hasattr(asyncio, 'current_task'):
            # pylint: disable=no-member
            self._handle_task = asyncio.current_task()
        else:
            self._handle_task = asyncio.Task.current_task(loop=self.hass.loop)

        @callback
        def handle_hass_stop(event):
            """Cancel this connection."""
            self._cancel()

        unsub_stop = self.hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP,
                                                handle_hass_stop)

        self._writer_task = self.hass.async_create_task(self._writer())

        auth = AuthPhase(self._logger, self.hass, self._send_message, request)
        connection = None
        disconnect_warn = None

        try:
            self._send_message(auth_required_message())

            # Auth Phase
            try:
                with async_timeout.timeout(10):
                    msg = await wsock.receive()
            except asyncio.TimeoutError:
                disconnect_warn = \
                    'Did not receive auth message within 10 seconds'
                raise Disconnect

            if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING):
                raise Disconnect

            elif msg.type != WSMsgType.TEXT:
                disconnect_warn = 'Received non-Text message.'
                raise Disconnect

            try:
                msg = msg.json()
            except ValueError:
                disconnect_warn = 'Received invalid JSON.'
                raise Disconnect

            self._logger.debug("Received %s", msg)
            connection = await auth.async_handle(msg)

            # Command phase
            while not wsock.closed:
                msg = await wsock.receive()

                if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING):
                    break

                elif msg.type != WSMsgType.TEXT:
                    disconnect_warn = 'Received non-Text message.'
                    break

                try:
                    msg = msg.json()
                except ValueError:
                    disconnect_warn = 'Received invalid JSON.'
                    break

                self._logger.debug("Received %s", msg)
                connection.async_handle(msg)

        except asyncio.CancelledError:
            self._logger.info("Connection closed by client")

        except Disconnect:
            pass

        except Exception:  # pylint: disable=broad-except
            self._logger.exception("Unexpected error inside websocket API")

        finally:
            unsub_stop()

            if connection is not None:
                connection.async_close()

            try:
                self._to_write.put_nowait(None)
                # Make sure all error messages are written before closing
                await self._writer_task
            except asyncio.QueueFull:
                self._writer_task.cancel()

            await wsock.close()

            if disconnect_warn is None:
                self._logger.debug("Disconnected")
            else:
                self._logger.warning("Disconnected: %s", disconnect_warn)

        return wsock
Exemplo n.º 18
0
 async def WebSocketCmd(self, request):
     ws = web.WebSocketResponse()
     await ws.prepare(request)
     request.app['websocketscmd'].add(ws)
     await ws.send_json(
         {"OK": ["start connection", "event on init connection"]})
     #totalFrames = cap.get(cv2.CAP_PROP_FRAME_COUNT)
     async for msg in ws:
         if msg.type == WSMsgType.TEXT:
             try:
                 print("data=", type(msg.data), msg.data)
                 msg_json = json.loads(msg.data)
                 print("json=", type(msg_json), msg_json['cmd'])
                 if msg_json['cmd'] == 'close':
                     await ws.close()
                 elif msg_json['cmd'] == 'getStreamsConfig':
                     if "manager" in self.app:
                         await self.app["manager"].getStreamsConfig(ws)
                     else:
                         await ws.send_json(
                             {"error": ['getStreamsConfig', msg.data]})
                 elif msg_json['cmd'] == 'getManagerData':
                     if "manager" in self.app:
                         data = self.app["manager"].getConfig()
                         # data = test_manager_config
                         print("getManagerData", data)
                         await ws.send_json(data)
                     else:
                         print(sys.exc_info())
                         await ws.send_json(
                             {"error": ['getManagerData', msg.data]})
                 elif msg_json['cmd'] == 'saveStream':
                     await self.saveConfig(ws, msg_json['config'])
                 elif msg_json['cmd'] == 'stopGetStream':
                     print("stopGetStream", msg_json['stream_id'])
                     self.removeLiveStream(ws, msg_json['stream_id'])
                     await ws.send_json({
                         "OK":
                         ["stopGetStream", msg_json['stream_id'], "server"]
                     })
                 elif msg_json['cmd'] == 'startGetStream':
                     print("startGetStream", msg_json['stream_id'])
                     if 'manager' in self.app:
                         if msg_json['stream_id'] in self.app[
                                 'manager'].camsList:
                             if msg_json['stream_id'] in self.live_streams:
                                 if ws not in self.live_streams[
                                         msg_json['stream_id']]:
                                     self.live_streams[
                                         msg_json['stream_id']].append[ws]
                             else:
                                 self.live_streams[
                                     msg_json['stream_id']] = [ws]
                             print("startGetStream", self.live_streams)
                             await ws.send_json({
                                 "OK": [
                                     "startGetStream",
                                     msg_json['stream_id'], "server"
                                 ]
                             })
                         else:
                             await ws.send_json({
                                 "error": [
                                     "startGetStream",
                                     msg_json['stream_id'], "server"
                                 ]
                             })
                     else:
                         await ws.send_json({
                             "error": [
                                 "startGetStream", msg_json['stream_id'],
                                 "mamanger is not running"
                             ]
                         })
                 elif msg_json['cmd'] == 'startStream':
                     print("startStream", msg_json['stream_id'])
                     if 'manager' in self.app:
                         try:
                             self.app['manager'].startStream(
                                 msg_json['stream_id'], ws)
                         except:
                             print(sys.exc_info())
                             await ws.send_json({
                                 "error": [
                                     "startStream", msg_json['stream_id'],
                                     "exception on server"
                                 ]
                             })
                     else:
                         await ws.send_json({
                             "error": [
                                 "startStream", msg_json['stream_id'],
                                 "mamanger is not running"
                             ]
                         })
                 elif msg_json['cmd'] == 'stopStream':
                     print("stopStream", msg_json['stream_id'])
                     if 'manager' in self.app:
                         try:
                             self.app['manager'].stopStream(
                                 msg_json['stream_id'])
                             del self.live_streams[msg_json['stream_id']]
                         except:
                             print(sys.exc_info())
                             await ws.send_json({
                                 "error": [
                                     "stopStream", msg_json['stream_id'],
                                     "exception on server"
                                 ]
                             })
                     else:
                         await ws.send_json({
                             "error": [
                                 "stopStream", msg_json['stream_id'],
                                 "mamanger is not running"
                             ]
                         })
                 else:
                     print("error websocket command")
                     await ws.send_json({"error": ['unknown', msg.data]})
             except:
                 print(sys.exc_info())
                 await ws.send_json(
                     {"error": ["can not parse json", msg.data]})
         elif msg.type == WSMsgType.ERROR:
             print('ws connection closed with exception %s' %
                   ws.exception())
     # print('websocket connection closed')
     #if 'manager' in self.app:
     #    try:
     #        await self.app['manager'].stopGetStream(ws, "all")
     #    except:
     #        print("server exceptption on stopGetStream on close connection")
     #        print(sys.exc_info())
     self.removeLiveStreams(ws)
     request.app['websocketscmd'].remove(ws)
     return ws
Exemplo n.º 19
0
 async def handle_websocket(self, request):
     ws = web.WebSocketResponse()
     await ws.prepare(request)
     await self.reply_to_websocket(ws)
     await ws.close()
Exemplo n.º 20
0
    async def get(self):
        """
        Initiates the websocket connection between the
        bike and the server. Requires an open ticket
        (which can be created by posting) to succeed.
        """
        socket = web.WebSocketResponse()
        await socket.prepare(self.request)
        remote = self.request.remote

        public_key = await socket.receive_bytes(timeout=0.5)
        signature = await socket.receive_bytes(timeout=0.5)

        try:
            ticket = self.open_tickets.pop_ticket(remote, public_key)
        except KeyError:
            await socket.send_str("fail:no_ticket")
            return socket

        # verify the signed challenge
        try:
            verify_key = VerifyKey(ticket.bike.public_key, encoder=RawEncoder)
            verify_key.verify(ticket.challenge, signature)
        except BadSignatureError:
            await socket.send_str("fail:invalid_sig")
            return socket

        logger.info("Bike %s connected", ticket.bike.id)
        await self.bike_connection_manager.add_connection(ticket.bike, socket)
        ticket.bike.socket = socket

        await socket.send_str("verified")
        status = await socket.receive_json()
        if "locked" in status:
            self.bike_connection_manager.update_locked(ticket.bike.id,
                                                       status["locked"])

        try:
            async for msg in socket:
                msg: WSMessage = msg
                try:
                    data = msg.json()
                except JSONDecodeError:
                    continue
                else:
                    if "method" in data:
                        valid_data = JsonRPCRequest().load(data)
                        if "id" not in valid_data and valid_data[
                                "method"] == "location_update":
                            point = Point(valid_data["params"]["long"],
                                          valid_data["params"]["lat"])
                            await self.bike_connection_manager.update_location(
                                ticket.bike.id, point)
                            self.bike_connection_manager.update_battery(
                                ticket.bike.id, valid_data["params"]["bat"])
                    else:
                        valid_data = JsonRPCResponse().load(data)
                        await self.bike_connection_manager.resolve_command(
                            ticket.bike.id, valid_data["id"],
                            valid_data["result"])
        finally:
            logger.info("Bike %s disconnected", ticket.bike.id)
            await socket.close()
            del self.bike_connection_manager._bike_connections[ticket.bike.id]
            del ticket
            del socket
Exemplo n.º 21
0
 async def handler(request):
     ws = web.WebSocketResponse(protocols=("foo", "bar"))
     await ws.prepare(request)
     await ws.close()
     closed.set_result(None)
     return ws
Exemplo n.º 22
0
async def spectrogram_handler(request):
    print('Websocket connection - starting')
    ws = web.WebSocketResponse()
    await ws.prepare(request)
    print('Websocket connection - ready')

    fs = None
    guidanceController = GuidanceController()

    async for msg in ws:

        if msg.type == WSMsgType.BINARY:
            print('Websocket connection - received binary')

            try:
                spectrogram = guidanceController.show_spectrogram(
                    bin2int16(msg.data), fs)
                print("spectrogram", np.size(spectrogram))
                await ws.send_str(
                    json.dumps({
                        'dataType': 'spectrogram',
                        'data': spectrogram.tolist()
                    }))

            except Exception as inst:
                print(type(inst))  # the exception instance
                print(inst.args)  # arguments stored in .args
                print(inst)

        if msg.type == WSMsgType.TEXT:
            print('Websocket connection - received text')
            message = json.loads(msg.data)

            if message['action'] == 'load fs':
                fs = message['fs']

                try:
                    await ws.send_str(
                        json.dumps({
                            'dataType': 'message',
                            'data': 'fs loaded'
                        }))

                except Exception as inst:
                    print(type(inst))  # the exception instance
                    print(inst.args)  # arguments stored in .args
                    print(inst)

            elif message['action'] == 'spectrogram':

                spectrogram = guidanceController.spectrogram(
                    message['data'], fs)

                plt.imshow(spectrogram)
                plt.xticks([])
                plt.yticks([])
                fig = plt.gcf()

                img = fig2img(fig)

                in_mem_file = io.BytesIO()
                img.save(in_mem_file, format="PNG")
                # reset file pointer to start
                in_mem_file.seek(0)
                img_bytes = in_mem_file.read()

                base64_encoded_result_bytes = base64.b64encode(img_bytes)
                base64_encoded_result_str = base64_encoded_result_bytes.decode(
                    'ascii')

                try:

                    await ws.send_str(
                        json.dumps({
                            'dataType': 'spectrogram_img',
                            'data': base64_encoded_result_str
                        }))

                except Exception as inst:
                    print(type(inst))  # the exception instance
                    print(inst.args)  # arguments stored in .args
                    print(inst)

                else:
                    await ws.send_str(
                        json.dumps({
                            'dataType': 'message',
                            'data': 'Audio buffer loading'
                        }))

            else:
                await ws.send_str(
                    json.dumps({
                        'dataType': 'message',
                        'data': msg.data + '/answer'
                    }))

    print('Websocket connection closed')
    return ws
Exemplo n.º 23
0
 async def handler(request):
     ws = web.WebSocketResponse()
     await ws.prepare(request)
     await ws.receive_bytes()
     await ws.send_str('test')
     await asyncio.sleep(10)
Exemplo n.º 24
0
    async def incoming_connection(self, request):
        if request.remote in self.banned_peers and time.time(
        ) < self.banned_peers[request.remote]:
            self.log.warning(
                f"Peer {request.remote} is banned, refusing connection")
            return None
        ws = web.WebSocketResponse(max_msg_size=50 * 1024 * 1024)
        await ws.prepare(request)
        close_event = asyncio.Event()
        cert_bytes = request.transport._ssl_protocol._extra[
            "ssl_object"].getpeercert(True)
        der_cert = x509.load_der_x509_certificate(cert_bytes)
        peer_id = bytes32(der_cert.fingerprint(hashes.SHA256()))
        if peer_id == self.node_id:
            return ws
        connection: Optional[WSChiaConnection] = None
        try:
            connection = WSChiaConnection(
                self._local_type,
                ws,
                self._port,
                self.log,
                False,
                False,
                request.remote,
                self.incoming_messages,
                self.connection_closed,
                peer_id,
                self._inbound_rate_limit_percent,
                self._outbound_rate_limit_percent,
                close_event,
            )
            handshake = await connection.perform_handshake(
                self._network_id,
                protocol_version,
                self._port,
                self._local_type,
            )

            assert handshake is True
            # Limit inbound connections to config's specifications.
            if not self.accept_inbound_connections(connection.connection_type):
                self.log.info(
                    f"Not accepting inbound connection: {connection.get_peer_info()}.Inbound limit reached."
                )
                await connection.close()
                close_event.set()
            else:
                await self.connection_added(connection, self.on_connect)
                if self._local_type is NodeType.INTRODUCER and connection.connection_type is NodeType.FULL_NODE:
                    self.introducer_peers.add(connection.get_peer_info())
        except ProtocolError as e:
            if connection is not None:
                await connection.close(self.invalid_protocol_ban_seconds,
                                       WSCloseCode.PROTOCOL_ERROR, e.code)
            if e.code == Err.INVALID_HANDSHAKE:
                self.log.warning(
                    "Invalid handshake with peer. Maybe the peer is running old software."
                )
                close_event.set()
            elif e.code == Err.INCOMPATIBLE_NETWORK_ID:
                self.log.warning(
                    "Incompatible network ID. Maybe the peer is on another network"
                )
                close_event.set()
            elif e.code == Err.SELF_CONNECTION:
                close_event.set()
            else:
                error_stack = traceback.format_exc()
                self.log.error(
                    f"Exception {e}, exception Stack: {error_stack}")
                close_event.set()
        except Exception as e:
            if connection is not None:
                await connection.close(
                    ws_close_code=WSCloseCode.PROTOCOL_ERROR,
                    error=Err.UNKNOWN)
            error_stack = traceback.format_exc()
            self.log.error(f"Exception {e}, exception Stack: {error_stack}")
            close_event.set()

        await close_event.wait()
        return ws
Exemplo n.º 25
0
    async def websocket(self, request: web.Request):
        """Initialize a WebSocket API connection."""
        _LOGGER.info("Home Assistant WebSocket API request initialize")

        # init server
        server = web.WebSocketResponse(heartbeat=30)
        await server.prepare(request)

        # handle authentication
        try:
            await server.send_json({
                "type": "auth_required",
                "ha_version": self.sys_homeassistant.version
            })

            # Check API access
            response = await server.receive_json()
            hassio_token = response.get("api_password") or response.get(
                "access_token")
            addon = self.sys_addons.from_token(hassio_token)

            if not addon or not addon.access_homeassistant_api:
                _LOGGER.warning("Unauthorized WebSocket access!")
                await server.send_json({
                    "type": "auth_invalid",
                    "message": "Invalid access"
                })
                return server

            _LOGGER.info("WebSocket access from %s", addon.slug)

            await server.send_json({
                "type": "auth_ok",
                "ha_version": self.sys_homeassistant.version
            })
        except (RuntimeError, ValueError) as err:
            _LOGGER.error("Can't initialize handshake: %s", err)
            return server

        # init connection to hass
        try:
            client = await self._websocket_client()
        except APIError:
            return server

        _LOGGER.info("Home Assistant WebSocket API request running")
        try:
            client_read = None
            server_read = None
            while not server.closed and not client.closed:
                if not client_read:
                    client_read = self.sys_create_task(client.receive_str())
                if not server_read:
                    server_read = self.sys_create_task(server.receive_str())

                # wait until data need to be processed
                await asyncio.wait([client_read, server_read],
                                   return_when=asyncio.FIRST_COMPLETED)

                # server
                if server_read.done() and not client.closed:
                    server_read.exception()
                    await client.send_str(server_read.result())
                    server_read = None

                # client
                if client_read.done() and not server.closed:
                    client_read.exception()
                    await server.send_str(client_read.result())
                    client_read = None

        except asyncio.CancelledError:
            pass

        except (RuntimeError, ConnectionError, TypeError) as err:
            _LOGGER.info("Home Assistant WebSocket API error: %s", err)

        finally:
            if client_read:
                client_read.cancel()
            if server_read:
                server_read.cancel()

            # close connections
            if not client.closed:
                await client.close()
            if not server.closed:
                await server.close()

        _LOGGER.info("Home Assistant WebSocket API connection is closed")
        return server
Exemplo n.º 26
0
 def handler(request):
     ws = web.WebSocketResponse()
     yield from ws.prepare(request)
     yield from ws.receive_bytes()
     ws.send_str('test')
     yield from asyncio.sleep(10, loop=loop)
Exemplo n.º 27
0
async def create(request):
    room_id = request.match_info['room_id']
    user_id = request.match_info['user_id']

    ws_current = web.WebSocketResponse()
    ws_ready = ws_current.can_prepare(request)
    if not ws_ready.ok:
        msg = "Sorry, something went wrong!"
        return web.json_response({'error': msg})

    await ws_current.prepare(request)

    query = {'_id': ObjectId(room_id), 'active': True}
    room = await Room.find_room(query=query, db=request.app['mongodb'])

    # room does not exist
    if room is None:
        msg = "Room is closed, create a new room"
        await ws_current.send_json({
            'action': 'error',
            'userId': user_id,
            'msg': room_id
        })
        await ws_current.close()
        return

    if not any(user_id == str(user['_id']) for user in room['users']):
        msg = "You have not joined the room"
        await ws_current.send_json({
            'action': 'error',
            'userId': user_id,
            'msg': msg
        })
        await ws_current.close()
        return

    users = [{
        'email': user['email'],
        'username': user['username'],
        'userId': str(user['_id'])
    } for user in room['users']]
    await ws_current.send_json({
        'action': 'connect',
        'userId': user_id,
        'users': users
    })

    current_user = next({
        'email': user['email'],
        'username': user['username'],
        'userId': str(user['_id'])
    } for user in room['users'] if user_id == str(user['_id']))

    # notify current users of new user
    for user in room['users']:
        if str(user['_id']) in request.app['websockets']:
            user_ws = request.app['websockets'][str(user['_id'])]
            await user_ws.send_json({'action': 'join', 'user': current_user})

    request.app['websockets'][user_id] = ws_current

    async for msg in ws_current:
        if msg.type == WSMsgType.text:
            if msg.data == 'close':
                await ws_current.close()
            else:
                for user in room['users']:
                    if str(user['_id']) in request.app['websockets']:
                        user_ws = request.app['websockets'][str(user['_id'])]
                        # forward message to all users except the sender
                        if user_ws is not ws_current:
                            await user_ws.send_json({
                                'action': 'sent',
                                'userId': user_id,
                                'text': msg.data
                            })
        elif msg.type == WSMsgType.ERROR:
            print('ws connection closed with exception %s' %
                  ws_current.exception())
        else:
            break

    if user_id in request.app['websockets']:
        del request.app['websockets'][user_id]

        room_filter = {'_id': ObjectId(room_id), 'active': True}
        game_filter = {'room_id': ObjectId(room_id), 'active': True}

        await room_helper.remove_user_from_room_and_game(
            room_filter, game_filter, user_id, request)

    for user in room['users']:
        if str(user['_id']) in request.app['websockets']:
            user_ws = request.app['websockets'][str(user['_id'])]
            await user_ws.send_json({
                'action': 'disconnect',
                'userId': user_id
            })

    return ws_current
Exemplo n.º 28
0
 def handler(request):
     ws = web.WebSocketResponse()
     yield from ws.prepare(request)
     yield from ws.receive()
     yield from ws.close()
     return ws
 async def no_pong_handler(self, request):
     ws = web.WebSocketResponse(autoping=False)
     self.connections.append(ws)
     await ws.prepare(request)
     await asyncio.sleep(20)
     return ws
Exemplo n.º 30
0
 async def client_shutdown(self, request):
     ws = web.WebSocketResponse()
     self.connections.append(ws)
     await ws.prepare(request)
     await asyncio.sleep(10)