forked from platypush/platypush
[#260] Added `/ws/requests
` websocket route.
This commit is contained in:
parent
7716a416e9
commit
a069d23bb7
2 changed files with 101 additions and 22 deletions
|
@ -41,9 +41,11 @@ class HttpBackend(Backend):
|
|||
|
||||
* To execute Platypush commands via HTTP calls. In order to do so:
|
||||
|
||||
* Register a user to Platypush through the web panel (usually served on ``http://host:8008/``).
|
||||
* Register a user to Platypush through the web panel (usually
|
||||
served on ``http://host:8008/``).
|
||||
|
||||
* Generate a token for your user, either through the web panel (Settings -> Generate Token) or via API:
|
||||
* Generate a token for your user, either through the web panel
|
||||
(Settings -> Generate Token) or via API:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
|
@ -66,16 +68,35 @@ class HttpBackend(Backend):
|
|||
}
|
||||
}' http://host:8008/execute
|
||||
|
||||
* To interact with your system (and control plugins and backends) through the Platypush web panel,
|
||||
by default available on ``http://host:8008/``. Any configured plugin that has an available panel
|
||||
plugin will be automatically added to the web panel.
|
||||
* To interact with your system (and control plugins and backends)
|
||||
through the Platypush web panel, by default available on
|
||||
``http://host:8008/``. Any configured plugin that has an available
|
||||
panel plugin will be automatically added to the web panel.
|
||||
|
||||
* To create asynchronous integrations with Platypush over websockets.
|
||||
Two routes are available:
|
||||
|
||||
* ``/ws/events`` - Subscribe to this websocket to receive the
|
||||
events generated by the application.
|
||||
* ``/ws/requests`` - Subscribe to this websocket to send commands
|
||||
to Platypush and receive the response asynchronously.
|
||||
|
||||
You will have to authenticate your connection to these websockets,
|
||||
just like the ``/execute`` endpoint. In both cases, you can pass the
|
||||
token either via ``Authorization: Bearer``, via the ``token`` query
|
||||
string or body parameter, or leverage ``Authorization: Basic`` with
|
||||
username and password (not advised), or use a valid ``session_token``
|
||||
cookie from an authenticated web panel session.
|
||||
|
||||
* To display a fullscreen dashboard with custom widgets.
|
||||
|
||||
* Widgets are available as Vue.js components under ``platypush/backend/http/webapp/src/components/widgets``.
|
||||
* Widgets are available as Vue.js components under
|
||||
``platypush/backend/http/webapp/src/components/widgets``.
|
||||
|
||||
* Explore their options (some may require some plugins or backends to be configured in order to work) and
|
||||
create a new dashboard template under ``~/.config/platypush/dashboards``- e.g. ``main.xml``:
|
||||
* Explore their options (some may require some plugins or backends
|
||||
to be configured in order to work) and create a new dashboard
|
||||
template under ``~/.config/platypush/dashboards``- e.g.
|
||||
``main.xml``:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
|
@ -111,13 +132,17 @@ class HttpBackend(Backend):
|
|||
</Row>
|
||||
</Dashboard>
|
||||
|
||||
* The dashboard will be accessible under ``http://host:8008/dashboard/<name>``, where ``name=main`` if for
|
||||
example you stored your template under ``~/.config/platypush/dashboards/main.xml``.
|
||||
* The dashboard will be accessible under
|
||||
``http://host:8008/dashboard/<name>``, where ``name=main`` if for
|
||||
example you stored your template under
|
||||
``~/.config/platypush/dashboards/main.xml``.
|
||||
|
||||
* To expose custom endpoints that can be called as web hooks by other applications and run some custom logic.
|
||||
All you have to do in this case is to create a hook on a
|
||||
:class:`platypush.message.event.http.hook.WebhookEvent` with the endpoint that you want to expose and store
|
||||
it under e.g. ``~/.config/platypush/scripts/hooks.py``:
|
||||
* To expose custom endpoints that can be called as web hooks by other
|
||||
applications and run some custom logic. All you have to do in this case
|
||||
is to create a hook on a
|
||||
:class:`platypush.message.event.http.hook.WebhookEvent` with the
|
||||
endpoint that you want to expose and store it under e.g.
|
||||
``~/.config/platypush/scripts/hooks.py``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
@ -142,16 +167,21 @@ class HttpBackend(Backend):
|
|||
module can expose lists of routes to the main webapp through the
|
||||
``__routes__`` object (a list of Flask blueprints).
|
||||
|
||||
Security: Access to the endpoints requires at least one user to be registered. Access to the endpoints is regulated
|
||||
in the following ways (with the exception of event hooks, whose logic is up to the user):
|
||||
Security: Access to the endpoints requires at least one user to be
|
||||
registered. Access to the endpoints is regulated in the following ways
|
||||
(with the exception of event hooks, whose logic is up to the user):
|
||||
|
||||
* **Simple authentication** - i.e. registered username and password.
|
||||
* **JWT token** provided either over as ``Authorization: Bearer`` header or ``GET`` ``?token=<TOKEN>``
|
||||
parameter. A JWT token can be generated either through the web panel or over the ``/auth`` endpoint.
|
||||
* **Global platform token**, usually configured on the root of the ``config.yaml`` as ``token: <VALUE>``.
|
||||
It can provided either over on the ``X-Token`` header or as a ``GET`` ``?token=<TOKEN>`` parameter.
|
||||
* **Session token**, generated upon login, it can be used to authenticate requests through the ``Cookie`` header
|
||||
(cookie name: ``session_token``).
|
||||
* **JWT token** provided either over as ``Authorization: Bearer``
|
||||
header or ``GET`` ``?token=<TOKEN>`` parameter. A JWT token can be
|
||||
generated either through the web panel or over the ``/auth``
|
||||
endpoint.
|
||||
* **Global platform token**, usually configured on the root of the
|
||||
``config.yaml`` as ``token: <VALUE>``. It can provided either over on
|
||||
the ``X-Token`` header or as a ``GET`` ``?token=<TOKEN>`` parameter.
|
||||
* **Session token**, generated upon login, it can be used to
|
||||
authenticate requests through the ``Cookie`` header (cookie name:
|
||||
``session_token``).
|
||||
|
||||
"""
|
||||
|
||||
|
|
49
platypush/backend/http/app/ws/requests.py
Normal file
49
platypush/backend/http/app/ws/requests.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
from threading import Thread, current_thread
|
||||
from typing import Set
|
||||
from typing_extensions import override
|
||||
|
||||
from platypush.backend.http.app.utils import send_message
|
||||
from platypush.message.request import Request
|
||||
|
||||
from . import WSRoute, logger
|
||||
|
||||
|
||||
class WSRequestsProxy(WSRoute):
|
||||
"""
|
||||
Websocket event proxy mapped to ``/ws/requests``.
|
||||
"""
|
||||
|
||||
_max_concurrent_requests: int = 10
|
||||
""" Maximum number of concurrent requests allowed on the same connection. """
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._requests: Set[Thread] = set()
|
||||
|
||||
@classmethod
|
||||
@override
|
||||
def app_name(cls) -> str:
|
||||
return 'requests'
|
||||
|
||||
def _handle_request(self, request: Request):
|
||||
self._requests.add(current_thread())
|
||||
try:
|
||||
response = send_message(request, wait_for_response=True)
|
||||
self.send(str(response))
|
||||
finally:
|
||||
self._requests.remove(current_thread())
|
||||
|
||||
def on_message(self, message):
|
||||
if len(self._requests) > self._max_concurrent_requests:
|
||||
logger.info('Too many concurrent requests on %s', self)
|
||||
return
|
||||
|
||||
try:
|
||||
msg = Request.build(message)
|
||||
assert isinstance(msg, Request), f'Expected {Request}, got {type(msg)}'
|
||||
except Exception as e:
|
||||
logger.info('Could not build request from %s: %s', message, e)
|
||||
logger.exception(e)
|
||||
return
|
||||
|
||||
Thread(target=self._handle_request, args=(msg,)).start()
|
Loading…
Reference in a new issue