11use pyo3:: prelude:: * ;
2+ use http_handler:: { Request , Response } ;
3+ use bytes:: BytesMut ;
4+ use tokio:: sync:: oneshot;
25
36mod http;
47mod http_method;
@@ -23,45 +26,168 @@ pub use websocket::{
2326 WebSocketConnectionScope , WebSocketReceiveMessage , WebSocketSendException , WebSocketSendMessage ,
2427} ;
2528
26- fn execute_asgi_http_scope (
29+ pub async fn execute_asgi_http_scope (
2730 py_func : PyObject ,
28- scope : HttpConnectionScope ,
29- py : Python ,
30- ) -> PyResult < ( ) > {
31+ request : Request ,
32+ ) -> PyResult < Response > {
33+ // Create the ASGI scope from the HTTP request
34+ let scope = HttpConnectionScope :: from_request ( & request) ;
35+
36+ // Create channels for ASGI communication
3137 let ( rx_receiver, rx) = Receiver :: http ( ) ;
3238 let ( tx_sender, mut tx) = Sender :: http ( ) ;
3339
34- tokio:: spawn ( async move {
40+ // Channel to receive the response data
41+ let ( response_tx, response_rx) = oneshot:: channel :: < ( u16 , Vec < ( String , String ) > , Vec < u8 > ) > ( ) ;
42+
43+ // Channel to signal Python execution completion
44+ let ( python_done_tx, python_done_rx) = oneshot:: channel :: < Result < ( ) , String > > ( ) ;
45+
46+ // Task to handle messages from the ASGI app
47+ let response_task = tokio:: spawn ( async move {
48+ let mut status = 500u16 ;
49+ let mut headers = Vec :: new ( ) ;
50+ let mut body = Vec :: new ( ) ;
51+ let mut response_started = false ;
52+
3553 loop {
3654 if let Some ( msg) = tx. recv ( ) . await {
37- println ! ( "received ASGI message: {msg:#?}" )
55+ match msg {
56+ HttpSendMessage :: HttpResponseStart {
57+ status : s,
58+ headers : h,
59+ trailers : _
60+ } => {
61+ status = s;
62+ headers = h;
63+ response_started = true ;
64+ }
65+ HttpSendMessage :: HttpResponseBody {
66+ body : b,
67+ more_body
68+ } => {
69+ if response_started {
70+ body. extend_from_slice ( & b) ;
71+ if !more_body {
72+ // Response is complete
73+ let _ = response_tx. send ( ( status, headers, body) ) ;
74+ break ;
75+ }
76+ }
77+ }
78+ }
79+ } else {
80+ // Channel closed
81+ break ;
3882 }
3983 }
4084 } ) ;
4185
86+ // Send the request body to the ASGI app
87+ let request_body = request. body ( ) . clone ( ) ;
4288 tokio:: spawn ( async move {
4389 let request_message = HttpReceiveMessage :: Request {
44- body : b"Hello, world!" . to_vec ( ) ,
90+ body : request_body . to_vec ( ) ,
4591 more_body : false ,
4692 } ;
4793
48- rx. send ( request_message) . unwrap ( ) ;
49-
50- // Simulate disconnection after sending the request
51- let disconnect_message = HttpReceiveMessage :: Disconnect ;
52- rx. send ( disconnect_message) . unwrap ( ) ;
94+ if rx. send ( request_message) . is_err ( ) {
95+ eprintln ! ( "Failed to send request message" ) ;
96+ }
5397 } ) ;
5498
55- let py_func = py_func. clone_ref ( py) ;
99+ // Run the ASGI app in a Python thread
100+ tokio:: task:: spawn_blocking ( move || {
101+ Python :: with_gil ( |py| {
102+ let scope_py = scope. into_pyobject ( py) . unwrap ( ) ;
56103
57- Python :: with_gil ( |py| {
58- if let Err ( _e) = py_func. call1 (
59- py,
60- ( scope. into_pyobject ( py) . unwrap ( ) , rx_receiver, tx_sender) ,
61- ) {
62- // Log or handle error
63- }
104+ // Call the ASGI app to get a coroutine
105+ let coroutine = match py_func. call1 ( py, ( scope_py, rx_receiver, tx_sender) ) {
106+ Ok ( coro) => coro,
107+ Err ( e) => {
108+ eprintln ! ( "Failed to call ASGI app: {}" , e) ;
109+ let _ = python_done_tx. send ( Err ( format ! ( "Failed to call ASGI app: {}" , e) ) ) ;
110+ return ;
111+ }
112+ } ;
113+
114+ // Run the coroutine using asyncio
115+ let asyncio = match py. import ( "asyncio" ) {
116+ Ok ( module) => module,
117+ Err ( e) => {
118+ eprintln ! ( "Failed to import asyncio: {}" , e) ;
119+ let _ = python_done_tx. send ( Err ( format ! ( "Failed to import asyncio: {}" , e) ) ) ;
120+ return ;
121+ }
122+ } ;
123+
124+ // Create a new event loop for this request
125+ let loop_ = match asyncio. call_method0 ( "new_event_loop" ) {
126+ Ok ( loop_) => loop_,
127+ Err ( e) => {
128+ eprintln ! ( "Failed to create event loop: {}" , e) ;
129+ let _ = python_done_tx. send ( Err ( format ! ( "Failed to create event loop: {}" , e) ) ) ;
130+ return ;
131+ }
132+ } ;
133+
134+ // Set it as the current event loop
135+ if let Err ( e) = asyncio. call_method1 ( "set_event_loop" , ( & loop_, ) ) {
136+ eprintln ! ( "Failed to set event loop: {}" , e) ;
137+ let _ = python_done_tx. send ( Err ( format ! ( "Failed to set event loop: {}" , e) ) ) ;
138+ return ;
139+ }
140+
141+ // Run the coroutine
142+ let result = loop_. call_method1 ( "run_until_complete" , ( coroutine, ) ) ;
143+
144+ // Close the loop
145+ let _ = loop_. call_method0 ( "close" ) ;
146+
147+ // Send the result
148+ let _ = python_done_tx. send ( result. map ( |_| ( ) ) . map_err ( |e| {
149+ format ! ( "Failed to run ASGI coroutine: {}" , e)
150+ } ) ) ;
151+ } ) ;
64152 } ) ;
65153
66- Ok ( ( ) )
154+ // Wait for either the response or Python completion
155+ let result = tokio:: select! {
156+ response = response_rx => {
157+ response. map_err( |_| pyo3:: exceptions:: PyRuntimeError :: new_err( "Failed to receive response" ) )
158+ }
159+ python_result = python_done_rx => {
160+ match python_result {
161+ Ok ( Ok ( ( ) ) ) => {
162+ // Python completed but no response was sent
163+ Err ( pyo3:: exceptions:: PyRuntimeError :: new_err( "ASGI app completed without sending response" ) )
164+ }
165+ Ok ( Err ( e) ) => {
166+ // Python failed with error
167+ Err ( pyo3:: exceptions:: PyRuntimeError :: new_err( e) )
168+ }
169+ Err ( _) => {
170+ // Channel closed unexpectedly
171+ Err ( pyo3:: exceptions:: PyRuntimeError :: new_err( "Python execution failed unexpectedly" ) )
172+ }
173+ }
174+ }
175+ } ;
176+
177+ let ( status, headers, body) = result?;
178+
179+ // Clean up the response task
180+ response_task. abort ( ) ;
181+
182+ // Build the HTTP response
183+ let mut builder = http_handler:: response:: Builder :: new ( )
184+ . status ( status) ;
185+
186+ for ( name, value) in headers {
187+ builder = builder. header ( & name, & value) ;
188+ }
189+
190+ builder
191+ . body ( BytesMut :: from ( & body[ ..] ) )
192+ . map_err ( |e| pyo3:: exceptions:: PyRuntimeError :: new_err ( format ! ( "Failed to build response: {}" , e) ) )
67193}
0 commit comments