7
7
from uuid import uuid4
8
8
9
9
from django .urls import reverse
10
- from reactpy import component , hooks
10
+ from reactpy import component , hooks , html
11
11
12
12
from reactpy_django .javascript_components import HttpRequest
13
13
from reactpy_django .models import SwitchSession
19
19
20
20
21
21
@component
22
- def auth_manager ():
23
- """Component that can force the client to switch HTTP sessions to match the websocket session.
22
+ def session_manager (child ):
23
+ """This component can force the client (browser) to switch HTTP sessions,
24
+ making it match the websocket session.
24
25
25
26
Used to force persistent authentication between Django's websocket and HTTP stack."""
26
27
from reactpy_django import config
27
28
28
29
switch_sessions , set_switch_sessions = hooks .use_state (False )
30
+ _ , set_rerender = hooks .use_state (uuid4 )
29
31
uuid_ref = hooks .use_ref (str (uuid4 ()))
30
32
uuid = uuid_ref .current
31
33
scope = hooks .use_connection ().scope
@@ -35,11 +37,13 @@ def setup_asgi_scope():
35
37
"""Store a trigger function in websocket scope so that ReactPy-Django's hooks can command a session synchronization."""
36
38
scope .setdefault ("reactpy" , {})
37
39
scope ["reactpy" ]["synchronize_session" ] = synchronize_session
38
- print ( "configure_asgi_scope" )
40
+ scope [ "reactpy" ][ "rerender" ] = rerender
39
41
40
42
@hooks .use_effect (dependencies = [switch_sessions ])
41
43
async def synchronize_session_timeout ():
42
- """Ensure that the ASGI scope is available to this component."""
44
+ """Ensure that the ASGI scope is available to this component.
45
+ This effect will automatically be cancelled if the session is successfully
46
+ switched (via dependencies=[switch_sessions])."""
43
47
if switch_sessions :
44
48
await asyncio .sleep (config .REACTPY_AUTH_TIMEOUT + 0.1 )
45
49
await asyncio .to_thread (
@@ -52,43 +56,44 @@ async def synchronize_session():
52
56
"""Entrypoint where the server will command the client to switch HTTP sessions
53
57
to match the websocket session. This function is stored in the websocket scope so that
54
58
ReactPy-Django's hooks can access it."""
55
- print ("sync command" )
56
59
session : SessionBase | None = scope .get ("session" )
57
60
if not session or not session .session_key :
58
- print ("sync error" )
59
61
return
60
62
61
63
# Delete any sessions currently associated with this UUID
62
64
with contextlib .suppress (SwitchSession .DoesNotExist ):
63
65
obj = await SwitchSession .objects .aget (uuid = uuid )
64
66
await obj .adelete ()
65
67
68
+ # Begin the process of synchronizing HTTP and websocket sessions
66
69
obj = await SwitchSession .objects .acreate (uuid = uuid , session_key = session .session_key )
67
70
await obj .asave ()
68
-
69
71
set_switch_sessions (True )
70
72
71
73
async def synchronize_session_callback (status_code : int , response : str ):
72
- """This callback acts as a communication bridge between the client and server, notifying the server
73
- of the client's response to the session switch command."""
74
- print ("callback" )
74
+ """This callback acts as a communication bridge, allowing the client to notify the server
75
+ of the status of session switch command."""
75
76
set_switch_sessions (False )
76
77
if status_code >= 300 or status_code < 200 :
77
78
await asyncio .to_thread (
78
79
_logger .warning ,
79
80
f"Client returned unexpected HTTP status code ({ status_code } ) while trying to sychronize sessions." ,
80
81
)
81
82
82
- # If a session cookie was generated, send it to the client
83
- print ("render" )
83
+ async def rerender ():
84
+ """Force a rerender of the entire component tree."""
85
+ set_rerender (uuid4 ())
86
+
87
+ # Switch sessions using a client side HttpRequest component, if needed
88
+ http_request = None
84
89
if switch_sessions :
85
- print ("Rendering HTTP request component with UUID " , uuid )
86
- return HttpRequest (
90
+ http_request = HttpRequest (
87
91
{
88
92
"method" : "GET" ,
89
93
"url" : reverse ("reactpy:switch_session" , args = [uuid ]),
90
94
"body" : None ,
91
95
"callback" : synchronize_session_callback ,
92
96
},
93
97
)
94
- return None
98
+
99
+ return html ._ (child , http_request )
0 commit comments