Socket fixes and config options(#485)

This commit is contained in:
advo-kat 2023-02-10 18:59:20 +11:00 committed by GitHub
parent 64b0d47897
commit 8d9c75824c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 273 additions and 23 deletions

93
poetry.lock generated
View File

@ -976,14 +976,14 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyam
[[package]] [[package]]
name = "tenacity" name = "tenacity"
version = "8.2.0" version = "8.2.1"
description = "Retry code until it succeeds" description = "Retry code until it succeeds"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
files = [ files = [
{file = "tenacity-8.2.0-py3-none-any.whl", hash = "sha256:b723061a78ed0f4524190eae321d3d84100227d51c5677035b6615d91895e0d6"}, {file = "tenacity-8.2.1-py3-none-any.whl", hash = "sha256:dd1b769ca7002fda992322939feca5bee4fa11f39146b0af14e0b8d9f27ea854"},
{file = "tenacity-8.2.0.tar.gz", hash = "sha256:a43bcd8910406e0884ca0db3db7bed581f389c1d05165e992a1ddabfc81df05e"}, {file = "tenacity-8.2.1.tar.gz", hash = "sha256:c7bb4b86425b977726a7b49971542d4f67baf72096597d283f3ffd01f33b92df"},
] ]
[package.extras] [package.extras]
@ -1100,16 +1100,95 @@ typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
[package.extras] [package.extras]
standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"]
[[package]]
name = "websockets"
version = "10.4"
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "websockets-10.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d58804e996d7d2307173d56c297cf7bc132c52df27a3efaac5e8d43e36c21c48"},
{file = "websockets-10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc0b82d728fe21a0d03e65f81980abbbcb13b5387f733a1a870672c5be26edab"},
{file = "websockets-10.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ba089c499e1f4155d2a3c2a05d2878a3428cf321c848f2b5a45ce55f0d7d310c"},
{file = "websockets-10.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33d69ca7612f0ddff3316b0c7b33ca180d464ecac2d115805c044bf0a3b0d032"},
{file = "websockets-10.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62e627f6b6d4aed919a2052efc408da7a545c606268d5ab5bfab4432734b82b4"},
{file = "websockets-10.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ea7b82bfcae927eeffc55d2ffa31665dc7fec7b8dc654506b8e5a518eb4d50"},
{file = "websockets-10.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e0cb5cc6ece6ffa75baccfd5c02cffe776f3f5c8bf486811f9d3ea3453676ce8"},
{file = "websockets-10.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ae5e95cfb53ab1da62185e23b3130e11d64431179debac6dc3c6acf08760e9b1"},
{file = "websockets-10.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7c584f366f46ba667cfa66020344886cf47088e79c9b9d39c84ce9ea98aaa331"},
{file = "websockets-10.4-cp310-cp310-win32.whl", hash = "sha256:b029fb2032ae4724d8ae8d4f6b363f2cc39e4c7b12454df8df7f0f563ed3e61a"},
{file = "websockets-10.4-cp310-cp310-win_amd64.whl", hash = "sha256:8dc96f64ae43dde92530775e9cb169979f414dcf5cff670455d81a6823b42089"},
{file = "websockets-10.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47a2964021f2110116cc1125b3e6d87ab5ad16dea161949e7244ec583b905bb4"},
{file = "websockets-10.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e789376b52c295c4946403bd0efecf27ab98f05319df4583d3c48e43c7342c2f"},
{file = "websockets-10.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7d3f0b61c45c3fa9a349cf484962c559a8a1d80dae6977276df8fd1fa5e3cb8c"},
{file = "websockets-10.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f55b5905705725af31ccef50e55391621532cd64fbf0bc6f4bac935f0fccec46"},
{file = "websockets-10.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00c870522cdb69cd625b93f002961ffb0c095394f06ba8c48f17eef7c1541f96"},
{file = "websockets-10.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f38706e0b15d3c20ef6259fd4bc1700cd133b06c3c1bb108ffe3f8947be15fa"},
{file = "websockets-10.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f2c38d588887a609191d30e902df2a32711f708abfd85d318ca9b367258cfd0c"},
{file = "websockets-10.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:fe10ddc59b304cb19a1bdf5bd0a7719cbbc9fbdd57ac80ed436b709fcf889106"},
{file = "websockets-10.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:90fcf8929836d4a0e964d799a58823547df5a5e9afa83081761630553be731f9"},
{file = "websockets-10.4-cp311-cp311-win32.whl", hash = "sha256:b9968694c5f467bf67ef97ae7ad4d56d14be2751000c1207d31bf3bb8860bae8"},
{file = "websockets-10.4-cp311-cp311-win_amd64.whl", hash = "sha256:a7a240d7a74bf8d5cb3bfe6be7f21697a28ec4b1a437607bae08ac7acf5b4882"},
{file = "websockets-10.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:74de2b894b47f1d21cbd0b37a5e2b2392ad95d17ae983e64727e18eb281fe7cb"},
{file = "websockets-10.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3a686ecb4aa0d64ae60c9c9f1a7d5d46cab9bfb5d91a2d303d00e2cd4c4c5cc"},
{file = "websockets-10.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d15c968ea7a65211e084f523151dbf8ae44634de03c801b8bd070b74e85033"},
{file = "websockets-10.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00213676a2e46b6ebf6045bc11d0f529d9120baa6f58d122b4021ad92adabd41"},
{file = "websockets-10.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:e23173580d740bf8822fd0379e4bf30aa1d5a92a4f252d34e893070c081050df"},
{file = "websockets-10.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:dd500e0a5e11969cdd3320935ca2ff1e936f2358f9c2e61f100a1660933320ea"},
{file = "websockets-10.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4239b6027e3d66a89446908ff3027d2737afc1a375f8fd3eea630a4842ec9a0c"},
{file = "websockets-10.4-cp37-cp37m-win32.whl", hash = "sha256:8a5cc00546e0a701da4639aa0bbcb0ae2bb678c87f46da01ac2d789e1f2d2038"},
{file = "websockets-10.4-cp37-cp37m-win_amd64.whl", hash = "sha256:a9f9a735deaf9a0cadc2d8c50d1a5bcdbae8b6e539c6e08237bc4082d7c13f28"},
{file = "websockets-10.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5c1289596042fad2cdceb05e1ebf7aadf9995c928e0da2b7a4e99494953b1b94"},
{file = "websockets-10.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0cff816f51fb33c26d6e2b16b5c7d48eaa31dae5488ace6aae468b361f422b63"},
{file = "websockets-10.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dd9becd5fe29773d140d68d607d66a38f60e31b86df75332703757ee645b6faf"},
{file = "websockets-10.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45ec8e75b7dbc9539cbfafa570742fe4f676eb8b0d3694b67dabe2f2ceed8aa6"},
{file = "websockets-10.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f72e5cd0f18f262f5da20efa9e241699e0cf3a766317a17392550c9ad7b37d8"},
{file = "websockets-10.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:185929b4808b36a79c65b7865783b87b6841e852ef5407a2fb0c03381092fa3b"},
{file = "websockets-10.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7d27a7e34c313b3a7f91adcd05134315002aaf8540d7b4f90336beafaea6217c"},
{file = "websockets-10.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:884be66c76a444c59f801ac13f40c76f176f1bfa815ef5b8ed44321e74f1600b"},
{file = "websockets-10.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:931c039af54fc195fe6ad536fde4b0de04da9d5916e78e55405436348cfb0e56"},
{file = "websockets-10.4-cp38-cp38-win32.whl", hash = "sha256:db3c336f9eda2532ec0fd8ea49fef7a8df8f6c804cdf4f39e5c5c0d4a4ad9a7a"},
{file = "websockets-10.4-cp38-cp38-win_amd64.whl", hash = "sha256:48c08473563323f9c9debac781ecf66f94ad5a3680a38fe84dee5388cf5acaf6"},
{file = "websockets-10.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:40e826de3085721dabc7cf9bfd41682dadc02286d8cf149b3ad05bff89311e4f"},
{file = "websockets-10.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:56029457f219ade1f2fc12a6504ea61e14ee227a815531f9738e41203a429112"},
{file = "websockets-10.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f5fc088b7a32f244c519a048c170f14cf2251b849ef0e20cbbb0fdf0fdaf556f"},
{file = "websockets-10.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fc8709c00704194213d45e455adc106ff9e87658297f72d544220e32029cd3d"},
{file = "websockets-10.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0154f7691e4fe6c2b2bc275b5701e8b158dae92a1ab229e2b940efe11905dff4"},
{file = "websockets-10.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c6d2264f485f0b53adf22697ac11e261ce84805c232ed5dbe6b1bcb84b00ff0"},
{file = "websockets-10.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9bc42e8402dc5e9905fb8b9649f57efcb2056693b7e88faa8fb029256ba9c68c"},
{file = "websockets-10.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:edc344de4dac1d89300a053ac973299e82d3db56330f3494905643bb68801269"},
{file = "websockets-10.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:84bc2a7d075f32f6ed98652db3a680a17a4edb21ca7f80fe42e38753a58ee02b"},
{file = "websockets-10.4-cp39-cp39-win32.whl", hash = "sha256:c94ae4faf2d09f7c81847c63843f84fe47bf6253c9d60b20f25edfd30fb12588"},
{file = "websockets-10.4-cp39-cp39-win_amd64.whl", hash = "sha256:bbccd847aa0c3a69b5f691a84d2341a4f8a629c6922558f2a70611305f902d74"},
{file = "websockets-10.4-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:82ff5e1cae4e855147fd57a2863376ed7454134c2bf49ec604dfe71e446e2193"},
{file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d210abe51b5da0ffdbf7b43eed0cfdff8a55a1ab17abbec4301c9ff077dd0342"},
{file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:942de28af58f352a6f588bc72490ae0f4ccd6dfc2bd3de5945b882a078e4e179"},
{file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9b27d6c1c6cd53dc93614967e9ce00ae7f864a2d9f99fe5ed86706e1ecbf485"},
{file = "websockets-10.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3d3cac3e32b2c8414f4f87c1b2ab686fa6284a980ba283617404377cd448f631"},
{file = "websockets-10.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:da39dd03d130162deb63da51f6e66ed73032ae62e74aaccc4236e30edccddbb0"},
{file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389f8dbb5c489e305fb113ca1b6bdcdaa130923f77485db5b189de343a179393"},
{file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09a1814bb15eff7069e51fed0826df0bc0702652b5cb8f87697d469d79c23576"},
{file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff64a1d38d156d429404aaa84b27305e957fd10c30e5880d1765c9480bea490f"},
{file = "websockets-10.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b343f521b047493dc4022dd338fc6db9d9282658862756b4f6fd0e996c1380e1"},
{file = "websockets-10.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:932af322458da7e4e35df32f050389e13d3d96b09d274b22a7aa1808f292fee4"},
{file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a4162139374a49eb18ef5b2f4da1dd95c994588f5033d64e0bbfda4b6b6fcf"},
{file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c57e4c1349fbe0e446c9fa7b19ed2f8a4417233b6984277cce392819123142d3"},
{file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b627c266f295de9dea86bd1112ed3d5fafb69a348af30a2422e16590a8ecba13"},
{file = "websockets-10.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:05a7233089f8bd355e8cbe127c2e8ca0b4ea55467861906b80d2ebc7db4d6b72"},
{file = "websockets-10.4.tar.gz", hash = "sha256:eef610b23933c54d5d921c92578ae5f89813438fded840c2e9809d378dc765d3"},
]
[[package]] [[package]]
name = "zipp" name = "zipp"
version = "3.12.1" version = "3.13.0"
description = "Backport of pathlib-compatible object wrapper for zip files" description = "Backport of pathlib-compatible object wrapper for zip files"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "zipp-3.12.1-py3-none-any.whl", hash = "sha256:6c4fe274b8f85ec73c37a8e4e3fa00df9fb9335da96fb789e3b96b318e5097b3"}, {file = "zipp-3.13.0-py3-none-any.whl", hash = "sha256:e8b2a36ea17df80ffe9e2c4fda3f693c3dad6df1697d3cd3af232db680950b0b"},
{file = "zipp-3.12.1.tar.gz", hash = "sha256:a3cac813d40993596b39ea9e93a18e8a2076d5c378b8bc88ec32ab264e04ad02"}, {file = "zipp-3.13.0.tar.gz", hash = "sha256:23f70e964bc11a34cef175bc90ba2914e1e4545ea1e3e2f67c079671883f9cb6"},
] ]
[package.extras] [package.extras]
@ -1119,4 +1198,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.7" python-versions = "^3.7"
content-hash = "29af23359858d18a743ff85426e7496fb7e47bdda6d746294aea4a360df6e617" content-hash = "0a0cbe4cdf4f07a69b36c76988a3ae25f7d3856a6ab40ace5ee0682c357723d8"

View File

@ -139,10 +139,14 @@ export const updateState = async (state, setState, result, setResult, router, so
* @param setResult The function to set the result. * @param setResult The function to set the result.
* @param endpoint The endpoint to connect to. * @param endpoint The endpoint to connect to.
*/ */
export const connect = async (socket, state, setState, result, setResult, router, endpoint) => { export const connect = async (socket, state, setState, result, setResult, router, endpoint, transports) => {
// Get backend URL object from the endpoint
const endpoint_url = new URL(endpoint)
// Create the socket. // Create the socket.
socket.current = io(endpoint, { socket.current = io(endpoint, {
'path': '/event', path: endpoint_url['pathname'],
transports: transports,
autoUnref: false,
}); });
// Once the socket is open, hydrate the page. // Once the socket is open, hydrate the page.

View File

@ -9,7 +9,7 @@ from .components import *
from .components.component import custom_component as component from .components.component import custom_component as component
from .components.graphing.victory import data from .components.graphing.victory import data
from .config import Config from .config import Config
from .constants import Env from .constants import Env, Transports
from .event import EventChain, console_log, redirect, window_alert from .event import EventChain, console_log, redirect, window_alert
from .middleware import Middleware from .middleware import Middleware
from .model import Model, session from .model import Model, session

View File

@ -35,6 +35,9 @@ class App(Base):
# The Socket.IO AsyncServer. # The Socket.IO AsyncServer.
sio: Optional[AsyncServer] = None sio: Optional[AsyncServer] = None
# The socket app.
socket_app: Optional[ASGIApp] = None
# The state class to use for the app. # The state class to use for the app.
state: Type[State] = DefaultState state: Type[State] = DefaultState
@ -47,7 +50,7 @@ class App(Base):
# Middleware to add to the app. # Middleware to add to the app.
middleware: List[Middleware] = [] middleware: List[Middleware] = []
# events handlers to trigger when a page load # Event handlers to trigger when a page loads.
load_events: Dict[str, EventHandler] = {} load_events: Dict[str, EventHandler] = {}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -59,6 +62,9 @@ class App(Base):
""" """
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# Get the config
config = utils.get_config()
# Add middleware. # Add middleware.
self.middleware.append(HydrateMiddleware()) self.middleware.append(HydrateMiddleware())
@ -68,21 +74,30 @@ class App(Base):
# Set up the API. # Set up the API.
self.api = FastAPI() self.api = FastAPI()
# Set up CORS options.
cors_allowed_origins = config.cors_allowed_origins
if config.cors_allowed_origins == [constants.CORS_ALLOWED_ORIGINS]:
cors_allowed_origins = "*"
# Set up the Socket.IO AsyncServer. # Set up the Socket.IO AsyncServer.
self.sio = AsyncServer(async_mode="asgi", cors_allowed_origins="*") self.sio = AsyncServer(
async_mode="asgi",
cors_allowed_origins=cors_allowed_origins,
cors_credentials=config.cors_credentials,
max_http_buffer_size=config.polling_max_http_buffer_size,
)
# Create the socket app. Note event endpoint constant replaces the default 'socket.io' path. # Create the socket app. Note event endpoint constant replaces the default 'socket.io' path.
socket_app = ASGIApp(self.sio, socketio_path=str(constants.Endpoint.EVENT)) self.socket_app = ASGIApp(self.sio, socketio_path="")
# Create the event namespace and attach the main app. Not related to the path above. # Create the event namespace and attach the main app. Not related to any paths.
event_namespace = EventNamespace("/event") event_namespace = EventNamespace("/event", self)
event_namespace.app = self
# Register the event namespace with the socket. # Register the event namespace with the socket.
self.sio.register_namespace(event_namespace) self.sio.register_namespace(event_namespace)
# Mount the socket app with the API. # Mount the socket app with the API.
self.api.mount("/", socket_app) self.api.mount(str(constants.Endpoint.EVENT), self.socket_app)
def __repr__(self) -> str: def __repr__(self) -> str:
"""Get the string representation of the app. """Get the string representation of the app.
@ -324,12 +339,17 @@ class App(Base):
compiler.compile_components(custom_components) compiler.compile_components(custom_components)
async def process(app: App, event: Event) -> StateUpdate: async def process(
app: App, event: Event, sid: str, headers: Dict, client_ip: str
) -> StateUpdate:
"""Process an event. """Process an event.
Args: Args:
app: The app to process the event for. app: The app to process the event for.
event: The event to process. event: The event to process.
sid: The Socket.IO session id.
headers: The client headers.
client_ip: The client_ip.
Returns: Returns:
The state update after processing the event. The state update after processing the event.
@ -337,12 +357,15 @@ async def process(app: App, event: Event) -> StateUpdate:
# Get the state for the session. # Get the state for the session.
state = app.state_manager.get_state(event.token) state = app.state_manager.get_state(event.token)
# pass router_data to the state of the App # Pass router_data to the state of the App.
state.router_data = event.router_data state.router_data = event.router_data
# also pass router_data to all substates # also pass router_data to all substates
for _, substate in state.substates.items(): for _, substate in state.substates.items():
substate.router_data = event.router_data substate.router_data = event.router_data
state.router_data[constants.RouteVar.CLIENT_TOKEN] = event.token state.router_data[constants.RouteVar.CLIENT_TOKEN] = event.token
state.router_data[constants.RouteVar.SESSION_ID] = sid
state.router_data[constants.RouteVar.HEADERS] = headers
state.router_data[constants.RouteVar.CLIENT_IP] = client_ip
# Preprocess the event. # Preprocess the event.
pre = app.preprocess(state, event) pre = app.preprocess(state, event)
@ -365,9 +388,19 @@ async def process(app: App, event: Event) -> StateUpdate:
class EventNamespace(AsyncNamespace): class EventNamespace(AsyncNamespace):
"""The event namespace.""" """The event namespace."""
# The backend API object. # The application object.
app: App app: App
def __init__(self, namespace: str, app: App):
"""Initialize the event namespace.
Args:
namespace: The namespace.
app: The application object.
"""
super().__init__(namespace)
self.app = app
def on_connect(self, sid, environ): def on_connect(self, sid, environ):
"""Event for when the websocket disconnects. """Event for when the websocket disconnects.
@ -395,8 +428,21 @@ class EventNamespace(AsyncNamespace):
# Get the event. # Get the event.
event = Event.parse_raw(data) event = Event.parse_raw(data)
# Get the event environment.
assert self.app.sio is not None
environ = self.app.sio.get_environ(sid, self.namespace)
# Get the client headers.
headers = {
k.decode("utf-8"): v.decode("utf-8")
for (k, v) in environ["asgi.scope"]["headers"]
}
# Get the client IP
client_ip = environ["REMOTE_ADDR"]
# Process the event. # Process the event.
update = await process(self.app, event) update = await process(self.app, event, sid, headers, client_ip)
# Emit the event. # Emit the event.
await self.emit(str(constants.SocketEvent.EVENT), update.json(), to=sid) await self.emit(str(constants.SocketEvent.EVENT), update.json(), to=sid)

View File

@ -165,7 +165,7 @@ USE_EFFECT = join(
" return;", " return;",
" }}", " }}",
f" if (!{SOCKET}.current) {{{{", f" if (!{SOCKET}.current) {{{{",
f" connect({SOCKET}, {{state}}, {{set_state}}, {RESULT}, {SET_RESULT}, {ROUTER}, {EVENT_ENDPOINT})", f" connect({SOCKET}, {{state}}, {{set_state}}, {RESULT}, {SET_RESULT}, {ROUTER}, {EVENT_ENDPOINT}, {{transports}})",
" }}", " }}",
" const update = async () => {{", " const update = async () => {{",
f" if ({RESULT}.{STATE} != null) {{{{", f" if ({RESULT}.{STATE} != null) {{{{",

View File

@ -150,7 +150,10 @@ def compile_effects(state: Type[State]) -> str:
""" """
state_name = state.get_name() state_name = state.get_name()
set_state = templates.format_state_setter(state_name) set_state = templates.format_state_setter(state_name)
return templates.USE_EFFECT(state=state_name, set_state=set_state) transports = constants.Transports.POLLING_WEBSOCKET.get_transports()
return templates.USE_EFFECT(
state=state_name, set_state=set_state, transports=transports
)
def compile_render(component: Component) -> str: def compile_render(component: Component) -> str:

View File

@ -38,3 +38,17 @@ class Config(Base):
# Additional frontend packages to install. # Additional frontend packages to install.
frontend_packages: List[str] = [] frontend_packages: List[str] = []
# Backend transport methods.
backend_transports: Optional[
constants.Transports
] = constants.Transports.POLLING_WEBSOCKET
# List of origins that are allowed to connect to the backend API.
cors_allowed_origins: Optional[list] = [constants.CORS_ALLOWED_ORIGINS]
# Whether credentials (cookies, authentication) are allowed in requests to the backend API.
cors_credentials: Optional[bool] = True
# The maximum size of a message when using the polling backend transport.
polling_max_http_buffer_size: Optional[int] = constants.POLLING_MAX_HTTP_BUFFER_SIZE

View File

@ -206,6 +206,36 @@ class SocketEvent(Enum):
return str(self.value) return str(self.value)
class Transports(Enum):
"""Socket transports used by the pynecone backend API."""
POLLING_WEBSOCKET = "['polling', 'websocket']"
WEBSOCKET_POLLING = "['websocket', 'polling']"
WEBSOCKET_ONLY = "['websocket']"
POLLING_ONLY = "['polling']"
def __str__(self) -> str:
"""Get the string representation of the transports.
Returns:
The transports string.
"""
return str(self.value)
def get_transports(self) -> str:
"""Get the transports config for the backend.
Returns:
The transports config for the backend.
"""
# Import here to avoid circular imports.
from pynecone import utils
# Get the transports from the config.
config = utils.get_config()
return str(config.backend_transports)
class RouteArgType(SimpleNamespace): class RouteArgType(SimpleNamespace):
"""Type of dynamic route arg extracted from URI route.""" """Type of dynamic route arg extracted from URI route."""
@ -217,8 +247,11 @@ class RouteArgType(SimpleNamespace):
class RouteVar(SimpleNamespace): class RouteVar(SimpleNamespace):
"""Names of variables used in the router_data dict stored in State.""" """Names of variables used in the router_data dict stored in State."""
CLIENT_IP = "ip"
CLIENT_TOKEN = "token" CLIENT_TOKEN = "token"
HEADERS = "headers"
PATH = "pathname" PATH = "pathname"
SESSION_ID = "sid"
QUERY = "query" QUERY = "query"
@ -245,3 +278,7 @@ DESCRIPTION_404 = "The page was not found"
USE_COLOR_MODE = "useColorMode" USE_COLOR_MODE = "useColorMode"
COLOR_MODE = "colorMode" COLOR_MODE = "colorMode"
TOGGLE_COLOR_MODE = "toggleColorMode" TOGGLE_COLOR_MODE = "toggleColorMode"
# Server socket configuration variables
CORS_ALLOWED_ORIGINS = "*"
POLLING_MAX_HTTP_BUFFER_SIZE = 1000 * 1000

View File

@ -354,6 +354,30 @@ class State(Base, ABC):
""" """
return self.router_data.get(constants.RouteVar.CLIENT_TOKEN, "") return self.router_data.get(constants.RouteVar.CLIENT_TOKEN, "")
def get_sid(self) -> str:
"""Return the session ID of the client associated with this state.
Returns:
The session ID of the client.
"""
return self.router_data.get(constants.RouteVar.SESSION_ID, "")
def get_headers(self) -> Dict:
"""Return the headers of the client associated with this state.
Returns:
The headers of the client.
"""
return self.router_data.get(constants.RouteVar.HEADERS, {})
def get_client_ip(self) -> str:
"""Return the IP of the client associated with this state.
Returns:
The IP of the client.
"""
return self.router_data.get(constants.RouteVar.CLIENT_IP, "")
def get_current_page(self) -> str: def get_current_page(self) -> str:
"""Obtain the path of current page from the router data. """Obtain the path of current page from the router data.

View File

@ -36,6 +36,7 @@ redis = "^4.3.5"
httpx = "^0.23.1" httpx = "^0.23.1"
python-socketio = "^5.7.2" python-socketio = "^5.7.2"
psutil = "^5.9.4" psutil = "^5.9.4"
websockets = "^10.4"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
pytest = "^7.1.2" pytest = "^7.1.2"

View File

@ -627,6 +627,48 @@ def test_get_token(test_state):
assert test_state.get_token() == token assert test_state.get_token() == token
def test_get_sid(test_state):
"""Test getting session id.
Args:
test_state: A state.
"""
assert test_state.get_sid() == ""
sid = "9fpxSzPb9aFMb4wFAAAH"
test_state.router_data = {RouteVar.SESSION_ID: sid}
assert test_state.get_sid() == sid
def test_get_headers(test_state):
"""Test getting client headers.
Args:
test_state: A state.
"""
assert test_state.get_headers() == {}
headers = {"host": "localhost:8000", "connection": "keep-alive"}
test_state.router_data = {RouteVar.HEADERS: headers}
assert test_state.get_headers() == headers
def test_get_client_ip(test_state):
"""Test getting client IP.
Args:
test_state: A state.
"""
assert test_state.get_client_ip() == ""
client_ip = "127.0.0.1"
test_state.router_data = {RouteVar.CLIENT_IP: client_ip}
assert test_state.get_client_ip() == client_ip
def test_get_current_page(test_state): def test_get_current_page(test_state):
assert test_state.get_current_page() == "" assert test_state.get_current_page() == ""