Prechádzať zdrojové kódy

Socket fixes and config options(#485)

advo-kat 2 rokov pred
rodič
commit
8d9c75824c

+ 86 - 7
poetry.lock

@@ -976,14 +976,14 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyam
 
 [[package]]
 name = "tenacity"
-version = "8.2.0"
+version = "8.2.1"
 description = "Retry code until it succeeds"
 category = "main"
 optional = false
 python-versions = ">=3.6"
 files = [
-    {file = "tenacity-8.2.0-py3-none-any.whl", hash = "sha256:b723061a78ed0f4524190eae321d3d84100227d51c5677035b6615d91895e0d6"},
-    {file = "tenacity-8.2.0.tar.gz", hash = "sha256:a43bcd8910406e0884ca0db3db7bed581f389c1d05165e992a1ddabfc81df05e"},
+    {file = "tenacity-8.2.1-py3-none-any.whl", hash = "sha256:dd1b769ca7002fda992322939feca5bee4fa11f39146b0af14e0b8d9f27ea854"},
+    {file = "tenacity-8.2.1.tar.gz", hash = "sha256:c7bb4b86425b977726a7b49971542d4f67baf72096597d283f3ffd01f33b92df"},
 ]
 
 [package.extras]
@@ -1100,16 +1100,95 @@ typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
 [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)"]
 
+[[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]]
 name = "zipp"
-version = "3.12.1"
+version = "3.13.0"
 description = "Backport of pathlib-compatible object wrapper for zip files"
 category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
-    {file = "zipp-3.12.1-py3-none-any.whl", hash = "sha256:6c4fe274b8f85ec73c37a8e4e3fa00df9fb9335da96fb789e3b96b318e5097b3"},
-    {file = "zipp-3.12.1.tar.gz", hash = "sha256:a3cac813d40993596b39ea9e93a18e8a2076d5c378b8bc88ec32ab264e04ad02"},
+    {file = "zipp-3.13.0-py3-none-any.whl", hash = "sha256:e8b2a36ea17df80ffe9e2c4fda3f693c3dad6df1697d3cd3af232db680950b0b"},
+    {file = "zipp-3.13.0.tar.gz", hash = "sha256:23f70e964bc11a34cef175bc90ba2914e1e4545ea1e3e2f67c079671883f9cb6"},
 ]
 
 [package.extras]
@@ -1119,4 +1198,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
 [metadata]
 lock-version = "2.0"
 python-versions = "^3.7"
-content-hash = "29af23359858d18a743ff85426e7496fb7e47bdda6d746294aea4a360df6e617"
+content-hash = "0a0cbe4cdf4f07a69b36c76988a3ae25f7d3856a6ab40ace5ee0682c357723d8"

+ 6 - 2
pynecone/.templates/web/utils/state.js

@@ -139,10 +139,14 @@ export const updateState = async (state, setState, result, setResult, router, so
  * @param setResult The function to set the result.
  * @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.
   socket.current = io(endpoint, {
-    'path': '/event',
+    path: endpoint_url['pathname'],
+    transports: transports,
+    autoUnref: false,
   });
 
   // Once the socket is open, hydrate the page.

+ 1 - 1
pynecone/__init__.py

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

+ 57 - 11
pynecone/app.py

@@ -35,6 +35,9 @@ class App(Base):
     # The Socket.IO AsyncServer.
     sio: Optional[AsyncServer] = None
 
+    # The socket app.
+    socket_app: Optional[ASGIApp] = None
+
     # The state class to use for the app.
     state: Type[State] = DefaultState
 
@@ -47,7 +50,7 @@ class App(Base):
     # Middleware to add to the app.
     middleware: List[Middleware] = []
 
-    # events handlers to trigger when a page load
+    # Event handlers to trigger when a page loads.
     load_events: Dict[str, EventHandler] = {}
 
     def __init__(self, *args, **kwargs):
@@ -59,6 +62,9 @@ class App(Base):
         """
         super().__init__(*args, **kwargs)
 
+        # Get the config
+        config = utils.get_config()
+
         # Add middleware.
         self.middleware.append(HydrateMiddleware())
 
@@ -68,21 +74,30 @@ class App(Base):
         # Set up the API.
         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.
-        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.
-        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.
-        event_namespace = EventNamespace("/event")
-        event_namespace.app = self
+        # Create the event namespace and attach the main app. Not related to any paths.
+        event_namespace = EventNamespace("/event", self)
 
         # Register the event namespace with the socket.
         self.sio.register_namespace(event_namespace)
 
         # 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:
         """Get the string representation of the app.
@@ -324,12 +339,17 @@ class App(Base):
         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.
 
     Args:
         app: The app to process the event for.
         event: The event to process.
+        sid: The Socket.IO session id.
+        headers: The client headers.
+        client_ip: The client_ip.
 
     Returns:
         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.
     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
     # also pass router_data to all substates
     for _, substate in state.substates.items():
         substate.router_data = event.router_data
     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.
     pre = app.preprocess(state, event)
@@ -365,9 +388,19 @@ async def process(app: App, event: Event) -> StateUpdate:
 class EventNamespace(AsyncNamespace):
     """The event namespace."""
 
-    # The backend API object.
+    # The application object.
     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):
         """Event for when the websocket disconnects.
 
@@ -395,8 +428,21 @@ class EventNamespace(AsyncNamespace):
         # Get the event.
         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.
-        update = await process(self.app, event)
+        update = await process(self.app, event, sid, headers, client_ip)
 
         # Emit the event.
         await self.emit(str(constants.SocketEvent.EVENT), update.json(), to=sid)

+ 1 - 1
pynecone/compiler/templates.py

@@ -165,7 +165,7 @@ USE_EFFECT = join(
         "    return;",
         "  }}",
         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 () => {{",
         f"    if ({RESULT}.{STATE} != null) {{{{",

+ 4 - 1
pynecone/compiler/utils.py

@@ -150,7 +150,10 @@ def compile_effects(state: Type[State]) -> str:
     """
     state_name = state.get_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:

+ 14 - 0
pynecone/config.py

@@ -38,3 +38,17 @@ class Config(Base):
 
     # Additional frontend packages to install.
     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

+ 37 - 0
pynecone/constants.py

@@ -206,6 +206,36 @@ class SocketEvent(Enum):
         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):
     """Type of dynamic route arg extracted from URI route."""
 
@@ -217,8 +247,11 @@ class RouteArgType(SimpleNamespace):
 class RouteVar(SimpleNamespace):
     """Names of variables used in the router_data dict stored in State."""
 
+    CLIENT_IP = "ip"
     CLIENT_TOKEN = "token"
+    HEADERS = "headers"
     PATH = "pathname"
+    SESSION_ID = "sid"
     QUERY = "query"
 
 
@@ -245,3 +278,7 @@ DESCRIPTION_404 = "The page was not found"
 USE_COLOR_MODE = "useColorMode"
 COLOR_MODE = "colorMode"
 TOGGLE_COLOR_MODE = "toggleColorMode"
+
+# Server socket configuration variables
+CORS_ALLOWED_ORIGINS = "*"
+POLLING_MAX_HTTP_BUFFER_SIZE = 1000 * 1000

+ 24 - 0
pynecone/state.py

@@ -354,6 +354,30 @@ class State(Base, ABC):
         """
         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:
         """Obtain the path of current page from the router data.
 

+ 1 - 0
pyproject.toml

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

+ 42 - 0
tests/test_state.py

@@ -627,6 +627,48 @@ def test_get_token(test_state):
     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):
     assert test_state.get_current_page() == ""