@@ -21,11 +21,11 @@ use std::{collections::HashMap, sync::Mutex};
2121struct CachedResponse {
2222 status : http:: StatusCode ,
2323 headers : http:: HeaderMap ,
24- body : bytes :: Bytes ,
24+ body : Vec < u8 > ,
2525}
2626
2727pub fn get < R : Runtime > (
28- # [ allow ( unused_variables ) ] manager : Arc < AppManager < R > > ,
28+ manager : Arc < AppManager < R > > ,
2929 window_origin : & str ,
3030 web_resource_request_handler : Option < Box < WebResourceRequestHandler > > ,
3131) -> UriSchemeProtocolHandler {
@@ -39,6 +39,7 @@ pub fn get<R: Runtime>(
3939 url. pop ( ) ;
4040 }
4141
42+ #[ allow( unused_mut) ]
4243 let mut client_builder = reqwest:: ClientBuilder :: new ( ) ;
4344 if use_https {
4445 #[ cfg( feature = "rustls-tls" ) ]
@@ -47,6 +48,7 @@ pub fn get<R: Runtime>(
4748 }
4849
4950 // we can't load env vars at runtime, gotta embed them in the lib
51+ #[ allow( unused_variables) ]
5052 if let Some ( cert_pem) = option_env ! ( "TAURI_DEV_ROOT_CERTIFICATE" ) {
5153 #[ cfg( any(
5254 feature = "native-tls" ,
@@ -78,44 +80,70 @@ pub fn get<R: Runtime>(
7880 }
7981 let client = client_builder. build ( ) . unwrap ( ) ;
8082
81- let response_cache = Arc :: new ( Mutex :: new ( HashMap :: new ( ) ) ) ;
83+ let response_cache = Mutex :: new ( HashMap :: new ( ) ) ;
8284
8385 ( url, client, response_cache)
8486 } ;
8587
88+ let context = Arc :: new ( Context {
89+ manager,
90+ web_resource_request_handler,
91+ window_origin,
92+ #[ cfg( all( dev, mobile) ) ]
93+ client,
94+ #[ cfg( all( dev, mobile) ) ]
95+ url,
96+ #[ cfg( all( dev, mobile) ) ]
97+ response_cache,
98+ } ) ;
99+
86100 Box :: new ( move |_, request, responder| {
87- match get_response (
88- request,
89- & manager,
90- & window_origin,
91- web_resource_request_handler. as_deref ( ) ,
92- #[ cfg( all( dev, mobile) ) ]
93- ( & url, & client, & response_cache) ,
94- ) {
95- Ok ( response) => responder. respond ( response) ,
96- Err ( e) => responder. respond (
97- HttpResponse :: builder ( )
98- . status ( StatusCode :: INTERNAL_SERVER_ERROR )
99- . header ( CONTENT_TYPE , mime:: TEXT_PLAIN . essence_str ( ) )
100- . header ( "Access-Control-Allow-Origin" , & window_origin)
101- . body ( e. to_string ( ) . into_bytes ( ) )
102- . unwrap ( ) ,
103- ) ,
104- }
101+ let context = context. clone ( ) ;
102+ crate :: async_runtime:: spawn ( async move {
103+ match get_response ( & context, request) . await {
104+ Ok ( response) => responder. respond ( response) ,
105+ Err ( e) => responder. respond (
106+ HttpResponse :: builder ( )
107+ . status ( StatusCode :: INTERNAL_SERVER_ERROR )
108+ . header ( CONTENT_TYPE , mime:: TEXT_PLAIN . essence_str ( ) )
109+ . header ( "Access-Control-Allow-Origin" , & context. window_origin )
110+ . body ( e. to_string ( ) . into_bytes ( ) )
111+ . unwrap ( ) ,
112+ ) ,
113+ }
114+ } ) ;
105115 } )
106116}
107117
108- fn get_response < R : Runtime > (
109- #[ allow( unused_mut) ] mut request : Request < Vec < u8 > > ,
110- #[ allow( unused_variables) ] manager : & AppManager < R > ,
111- window_origin : & str ,
112- web_resource_request_handler : Option < & WebResourceRequestHandler > ,
113- #[ cfg( all( dev, mobile) ) ] ( url, client, response_cache) : (
114- & str ,
115- & reqwest:: Client ,
116- & Arc < Mutex < HashMap < String , CachedResponse > > > ,
117- ) ,
118+ struct Context < R : Runtime > {
119+ manager : Arc < AppManager < R > > ,
120+ window_origin : String ,
121+ web_resource_request_handler : Option < Box < WebResourceRequestHandler > > ,
122+
123+ #[ cfg( all( dev, mobile) ) ]
124+ url : String ,
125+ #[ cfg( all( dev, mobile) ) ]
126+ client : reqwest:: Client ,
127+ #[ cfg( all( dev, mobile) ) ]
128+ response_cache : Mutex < HashMap < String , CachedResponse > > ,
129+ }
130+
131+ async fn get_response < R : Runtime > (
132+ context : & Context < R > ,
133+ request : Request < Vec < u8 > > ,
118134) -> Result < HttpResponse < Cow < ' static , [ u8 ] > > , Box < dyn std:: error:: Error > > {
135+ let Context {
136+ manager,
137+ web_resource_request_handler,
138+ window_origin,
139+ #[ cfg( all( dev, mobile) ) ]
140+ client,
141+ #[ cfg( all( dev, mobile) ) ]
142+ url,
143+ #[ cfg( all( dev, mobile) ) ]
144+ response_cache,
145+ } = context;
146+
119147 // use the entire URI as we are going to proxy the request
120148 let path = if PROXY_DEV_SERVER {
121149 request. uri ( ) . to_string ( )
@@ -137,86 +165,105 @@ fn get_response<R: Runtime>(
137165 // where `$P` is not `localhost/*`
138166 . unwrap_or_default ( ) ;
139167
168+ #[ allow( unused_mut) ]
140169 let mut builder = HttpResponse :: builder ( )
141170 . add_configured_headers ( manager. config . app . security . headers . as_ref ( ) )
142171 . header ( "Access-Control-Allow-Origin" , window_origin) ;
143172
144173 #[ cfg( all( dev, mobile) ) ]
145- let mut response = {
146- let decoded_path = percent_encoding:: percent_decode ( path. as_bytes ( ) )
147- . decode_utf8_lossy ( )
148- . to_string ( ) ;
149- let url = format ! (
150- "{}/{}" ,
151- url. trim_end_matches( '/' ) ,
152- decoded_path. trim_start_matches( '/' )
153- ) ;
154-
155- let mut proxy_builder = client. request ( request. method ( ) . clone ( ) , & url) ;
156- for ( name, value) in request. headers ( ) {
157- proxy_builder = proxy_builder. header ( name, value) ;
158- }
159- proxy_builder = proxy_builder. body ( request. body ( ) . clone ( ) ) ;
160- match crate :: async_runtime:: safe_block_on ( proxy_builder. send ( ) ) {
161- Ok ( r) => {
162- let mut response_cache_ = response_cache. lock ( ) . unwrap ( ) ;
163- let mut response = None ;
164- if r. status ( ) == http:: StatusCode :: NOT_MODIFIED {
165- response = response_cache_. get ( & url) ;
166- }
167- let response = if let Some ( r) = response {
168- r
169- } else {
170- let status = r. status ( ) ;
171- let headers = r. headers ( ) . clone ( ) ;
172- let body = crate :: async_runtime:: safe_block_on ( r. bytes ( ) ) ?;
173- let response = CachedResponse {
174- status,
175- headers,
176- body,
177- } ;
178- response_cache_. insert ( url. clone ( ) , response) ;
179- response_cache_. get ( & url) . unwrap ( )
180- } ;
181- for ( name, value) in & response. headers {
182- builder = builder. header ( name, value) ;
183- }
184- builder
185- . status ( response. status )
186- . body ( response. body . to_vec ( ) . into ( ) ) ?
187- }
188- Err ( e) => {
189- let error_message = format ! (
190- "Failed to request {}: {}{}" ,
191- url. as_str( ) ,
192- e,
193- if let Some ( s) = e. status( ) {
194- format!( "status code: {}" , s. as_u16( ) )
195- } else if cfg!( target_os = "ios" ) {
196- ", did you grant local network permissions? That is required to reach the development server. Please grant the permission via the prompt or in `Settings > Privacy & Security > Local Network` and restart the app. See https://support.apple.com/en-us/102229 for more information." . to_string( )
197- } else {
198- "" . to_string( )
199- }
200- ) ;
201- log:: error!( "{error_message}" ) ;
202- return Err ( error_message. into ( ) ) ;
203- }
204- }
205- } ;
174+ let mut response =
175+ proxy_dev_request ( client, url, response_cache, path, builder, & request) . await ?;
206176
207177 #[ cfg( not( all( dev, mobile) ) ) ]
208178 let mut response = {
209- let use_https_scheme = request. uri ( ) . scheme ( ) == Some ( & http:: uri:: Scheme :: HTTPS ) ;
210- let asset = manager. get_asset ( path, use_https_scheme) ?;
179+ let asset = manager. get_asset (
180+ path,
181+ request. uri ( ) . scheme ( ) == Some ( & http:: uri:: Scheme :: HTTPS ) ,
182+ ) ?;
211183 builder = builder. header ( CONTENT_TYPE , & asset. mime_type ) ;
212184 if let Some ( csp) = & asset. csp_header {
213185 builder = builder. header ( "Content-Security-Policy" , csp) ;
214186 }
215187 builder. body ( asset. bytes . into ( ) ) ?
216188 } ;
217- if let Some ( handler) = & web_resource_request_handler {
189+
190+ if let Some ( handler) = web_resource_request_handler {
218191 handler ( request, & mut response) ;
219192 }
220193
221194 Ok ( response)
222195}
196+
197+ #[ cfg( all( dev, mobile) ) ]
198+ async fn proxy_dev_request (
199+ client : & reqwest:: Client ,
200+ url : & String ,
201+ response_cache : & Mutex < HashMap < String , CachedResponse > > ,
202+ path : String ,
203+ mut builder : http:: response:: Builder ,
204+ request : & Request < Vec < u8 > > ,
205+ ) -> Result < HttpResponse < Cow < ' static , [ u8 ] > > , Box < dyn std:: error:: Error > > {
206+ let decoded_path = percent_encoding:: percent_decode ( path. as_bytes ( ) )
207+ . decode_utf8_lossy ( )
208+ . to_string ( ) ;
209+ let url = format ! (
210+ "{}/{}" ,
211+ url. trim_end_matches( '/' ) ,
212+ decoded_path. trim_start_matches( '/' )
213+ ) ;
214+
215+ let mut proxy_builder = client. request ( request. method ( ) . clone ( ) , & url) ;
216+ for ( name, value) in request. headers ( ) {
217+ proxy_builder = proxy_builder. header ( name, value) ;
218+ }
219+ proxy_builder = proxy_builder. body ( request. body ( ) . clone ( ) ) ;
220+
221+ let response = proxy_builder. send ( ) . await . map_err ( |e|{
222+ let error_message = format ! (
223+ "Failed to request {url}: {e}{}" ,
224+ if let Some ( s) = e. status( ) {
225+ format!( "status code: {}" , s. as_u16( ) )
226+ } else if cfg!( target_os = "ios" ) {
227+ ", did you grant local network permissions? That is required to reach the development server. Please grant the permission via the prompt or in `Settings > Privacy & Security > Local Network` and restart the app. See https://support.apple.com/en-us/102229 for more information." . to_string( )
228+ } else {
229+ "" . to_string( )
230+ }
231+ ) ;
232+ log:: error!( "{error_message}" ) ;
233+ error_message
234+ } ) ?;
235+
236+ let status = response. status ( ) ;
237+
238+ if status == http:: StatusCode :: NOT_MODIFIED {
239+ if let Some ( response) = response_cache. lock ( ) . unwrap ( ) . get ( & url) . cloned ( ) {
240+ for ( name, value) in & response. headers {
241+ builder = builder. header ( name, value) ;
242+ }
243+
244+ return Ok ( builder. status ( response. status ) . body ( response. body . into ( ) ) ?) ;
245+ }
246+ }
247+
248+ let headers = response. headers ( ) . clone ( ) ;
249+ let body = response. bytes ( ) . await ?. to_vec ( ) ;
250+ let response = CachedResponse {
251+ status,
252+ headers,
253+ body,
254+ } ;
255+
256+ response_cache
257+ . lock ( )
258+ . unwrap ( )
259+ . insert ( url. clone ( ) , response. clone ( ) ) ;
260+
261+ for ( name, value) in & response. headers {
262+ builder = builder. header ( name, value) ;
263+ }
264+
265+ builder
266+ . status ( response. status )
267+ . body ( response. body . into ( ) )
268+ . map_err ( Into :: into)
269+ }
0 commit comments