-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Gevent-based websocket support (#23)
* Added method to return current GET response * Split out function to encode JSON with current Flask encoder settings * Added basic property subscriptions via websocket * Run gevent monkey patch on server import * Added default eventlet support * Updated to default install eventlet * Moved monkey patches to server submodules * Only let eventlet websockets handle connections requesting an upgrade * Fixed websocket route URL * Added eventlet server debug and log options * Reverted to gevent default (OFM live stream breaks with eventlet) * Moved monkey patch back to server top level * Better handle requests without websocket upgrade * Better handle NULL websocket messages * Removed default websocket echo * Removed eventlet dependency
- Loading branch information
Showing
15 changed files
with
341 additions
and
139 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,11 @@ | ||
import logging | ||
|
||
EXTENSION_NAME = "flask-labthings" | ||
|
||
# Monkey patching is bad and should never be done | ||
# import eventlet | ||
# eventlet.monkey_patch() | ||
|
||
from gevent import monkey | ||
|
||
monkey.patch_all() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from .base import SocketSubscriber | ||
from .gevent import Sockets, socket_handler_loop |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
from werkzeug.routing import Map, Rule | ||
from werkzeug.exceptions import NotFound | ||
from werkzeug.http import parse_cookie | ||
from flask import request, current_app | ||
import logging | ||
from abc import ABC, abstractmethod | ||
|
||
from ..representations import encode_json | ||
|
||
|
||
class SocketSubscriber: | ||
def __init__(self, ws): | ||
self.ws = ws | ||
|
||
def property_notify(self, viewcls): | ||
if hasattr(viewcls, "get_value") and callable(viewcls.get_value): | ||
property_value = viewcls().get_value() | ||
else: | ||
property_value = None | ||
|
||
property_name = str(getattr(viewcls, "endpoint", "unknown")) | ||
|
||
response = encode_json( | ||
{"messageType": "propertyStatus", "data": {property_name: property_value},} | ||
) | ||
|
||
self.ws.send(response) | ||
|
||
|
||
class BaseSockets(ABC): | ||
def __init__(self, app=None): | ||
#: Compatibility with 'Flask' application. | ||
#: The :class:`~werkzeug.routing.Map` for this instance. You can use | ||
#: this to change the routing converters after the class was created | ||
#: but before any routes are connected. | ||
self.url_map = Map() | ||
|
||
#: Compatibility with 'Flask' application. | ||
#: All the attached blueprints in a dictionary by name. Blueprints | ||
#: can be attached multiple times so this dictionary does not tell | ||
#: you how often they got attached. | ||
self.blueprints = {} | ||
self._blueprint_order = [] | ||
|
||
if app: | ||
self.init_app(app) | ||
|
||
@abstractmethod | ||
def init_app(self, app): | ||
pass | ||
|
||
def route(self, rule, **options): | ||
def decorator(view_func): | ||
options.pop("endpoint", None) | ||
self.add_url_rule(rule, view_func, **options) | ||
return view_func | ||
|
||
return decorator | ||
|
||
def add_url_rule(self, rule, view_func, **options): | ||
self.url_map.add(Rule(rule, endpoint=view_func)) | ||
|
||
def register_blueprint(self, blueprint, **options): | ||
""" | ||
Registers a blueprint for web sockets like for 'Flask' application. | ||
Decorator :meth:`~flask.app.setupmethod` is not applied, because it | ||
requires ``debug`` and ``_got_first_request`` attributes to be defined. | ||
""" | ||
first_registration = False | ||
|
||
if blueprint.name in self.blueprints: | ||
assert self.blueprints[blueprint.name] is blueprint, ( | ||
"A blueprint's name collision occurred between %r and " | ||
'%r. Both share the same name "%s". Blueprints that ' | ||
"are created on the fly need unique names." | ||
% (blueprint, self.blueprints[blueprint.name], blueprint.name) | ||
) | ||
else: | ||
self.blueprints[blueprint.name] = blueprint | ||
self._blueprint_order.append(blueprint) | ||
first_registration = True | ||
|
||
blueprint.register(self, options, first_registration) | ||
|
||
|
||
def process_socket_message(message: str): | ||
if message: | ||
# return f"Recieved: {message}" | ||
return None | ||
else: | ||
return None |
Oops, something went wrong.