1
1
use std:: {
2
2
env:: Args ,
3
- ffi:: c_char,
3
+ ffi:: CString ,
4
+ ops:: DerefMut ,
4
5
path:: { Path , PathBuf } ,
6
+ sync:: Arc ,
5
7
} ;
6
8
7
9
use ext_php_rs:: {
8
10
error:: Error ,
9
- ffi:: {
10
- _zend_file_handle__bindgen_ty_1, php_execute_script, sapi_get_default_content_type,
11
- zend_file_handle, zend_stream_init_filename,
12
- } ,
11
+ ffi:: { php_execute_script, php_module_shutdown, sapi_get_default_content_type} ,
13
12
zend:: { try_catch, try_catch_first, ExecutorGlobals , SapiGlobals } ,
14
13
} ;
15
14
16
15
use lang_handler:: { Handler , Request , Response } ;
17
16
18
- use crate :: {
19
- sapi:: ensure_sapi,
17
+ use super :: {
18
+ sapi:: { ensure_sapi, Sapi } ,
19
+ scopes:: { FileHandleScope , RequestScope } ,
20
20
strings:: { cstr, nullable_cstr, str_from_cstr, translate_path} ,
21
- EmbedException , RequestContext , RequestScope , Sapi ,
21
+ EmbedException , RequestContext ,
22
22
} ;
23
23
24
24
/// Embed a PHP script into a Rust application to handle HTTP requests.
25
25
#[ derive( Debug ) ]
26
26
pub struct Embed {
27
27
docroot : PathBuf ,
28
+ args : Vec < String > ,
29
+
30
+ // NOTE: This needs to hold the SAPI to keep it alive
31
+ #[ allow( dead_code) ]
32
+ sapi : Arc < Sapi > ,
28
33
}
29
34
30
35
// An embed instance may be constructed on the main thread and then shared
@@ -90,14 +95,16 @@ impl Embed {
90
95
C : AsRef < Path > ,
91
96
S : AsRef < str > + std:: fmt:: Debug ,
92
97
{
93
- ensure_sapi ( argv) ?;
94
-
95
- let docroot = docroot
96
- . as_ref ( )
98
+ let docroot_path = docroot. as_ref ( ) ;
99
+ let docroot = docroot_path
97
100
. canonicalize ( )
98
- . map_err ( |_| EmbedException :: DocRootNotFound ( docroot . as_ref ( ) . display ( ) . to_string ( ) ) ) ?;
101
+ . map_err ( |_| EmbedException :: DocRootNotFound ( docroot_path . display ( ) . to_string ( ) ) ) ?;
99
102
100
- Ok ( Embed { docroot } )
103
+ Ok ( Embed {
104
+ docroot,
105
+ args : argv. iter ( ) . map ( |v| v. as_ref ( ) . to_string ( ) ) . collect ( ) ,
106
+ sapi : ensure_sapi ( ) ?,
107
+ } )
101
108
}
102
109
103
110
/// Get the docroot used for this Embed instance
@@ -108,7 +115,6 @@ impl Embed {
108
115
/// use std::env::current_dir;
109
116
/// use php::Embed;
110
117
///
111
- ///
112
118
/// let docroot = current_dir()
113
119
/// .expect("should have current_dir");
114
120
///
@@ -152,25 +158,21 @@ impl Handler for Embed {
152
158
/// //assert_eq!(response.body(), "Hello, world!");
153
159
/// ```
154
160
fn handle ( & self , request : Request ) -> Result < Response , Self :: Error > {
155
- unsafe {
156
- ext_php_rs:: embed:: ext_php_rs_sapi_per_thread_init ( ) ;
157
- }
158
-
159
161
// Initialize the SAPI module
160
- Sapi :: startup ( ) ?;
162
+ self . sapi . startup ( ) ?;
161
163
162
164
let url = request. url ( ) ;
163
165
164
- // Get code and filename to execute
166
+ // Get original request URI and translate to final path
167
+ // TODO: Should do this with request rewriting later...
165
168
let request_uri = url. path ( ) ;
166
- let path_translated = cstr (
167
- translate_path ( & self . docroot , request_uri) ?
168
- . display ( )
169
- . to_string ( ) ,
170
- ) ?;
169
+ let translated_path = translate_path ( & self . docroot , request_uri) ?
170
+ . display ( )
171
+ . to_string ( ) ;
172
+ let path_translated = cstr ( translated_path. clone ( ) ) ?;
171
173
let request_uri = cstr ( request_uri) ?;
172
174
173
- // Extract request information
175
+ // Extract request method, query string, and headers
174
176
let request_method = cstr ( request. method ( ) ) ?;
175
177
let query_string = cstr ( url. query ( ) . unwrap_or ( "" ) ) ?;
176
178
@@ -182,27 +184,16 @@ impl Handler for Embed {
182
184
. unwrap_or ( 0 ) ;
183
185
let cookie_data = nullable_cstr ( headers. get ( "Cookie" ) ) ?;
184
186
185
- // Prepare memory stream of the code
186
- let mut file_handle = unsafe {
187
- let mut file_handle = zend_file_handle {
188
- handle : _zend_file_handle__bindgen_ty_1 {
189
- fp : std:: ptr:: null_mut ( ) ,
190
- } ,
191
- filename : std:: ptr:: null_mut ( ) ,
192
- opened_path : std:: ptr:: null_mut ( ) ,
193
- type_ : 0 , //ZEND_HANDLE_FP
194
- primary_script : false ,
195
- in_list : false ,
196
- buf : std:: ptr:: null_mut ( ) ,
197
- len : 0 ,
198
- } ;
199
-
200
- zend_stream_init_filename ( & mut file_handle, path_translated) ;
201
-
202
- // TODO: Make a scope to do zend_destroy_file_handle at the end.
187
+ // Prepare argv and argc
188
+ let argc = self . args . len ( ) as i32 ;
189
+ let mut argv_ptrs = vec ! [ ] ;
190
+ for arg in self . args . iter ( ) {
191
+ let string = CString :: new ( arg. as_bytes ( ) )
192
+ . map_err ( |_| EmbedException :: CStringEncodeFailed ( arg. to_owned ( ) ) ) ?;
193
+ argv_ptrs. push ( string. into_raw ( ) ) ;
194
+ }
203
195
204
- file_handle
205
- } ;
196
+ let script_name = translated_path. clone ( ) ;
206
197
207
198
let response = try_catch_first ( || {
208
199
RequestContext :: for_request ( request. clone ( ) ) ;
@@ -214,8 +205,8 @@ impl Handler for Embed {
214
205
215
206
// Reset state
216
207
globals. request_info . proto_num = 110 ;
217
- globals. request_info . argc = 0 ;
218
- globals. request_info . argv = std :: ptr :: null_mut ( ) ;
208
+ globals. request_info . argc = argc ;
209
+ globals. request_info . argv = argv_ptrs . as_mut_ptr ( ) ;
219
210
globals. request_info . headers_read = false ;
220
211
globals. sapi_headers . http_response_code = 200 ;
221
212
@@ -237,7 +228,8 @@ impl Handler for Embed {
237
228
238
229
// Run script in its own try/catch so bailout doesn't skip request shutdown.
239
230
try_catch ( || {
240
- if !unsafe { php_execute_script ( & mut file_handle) } {
231
+ let mut file_handle = FileHandleScope :: new ( script_name. clone ( ) ) ;
232
+ if !unsafe { php_execute_script ( file_handle. deref_mut ( ) ) } {
241
233
// return Err(EmbedException::ExecuteError);
242
234
}
243
235
@@ -275,7 +267,7 @@ impl Handler for Embed {
275
267
let mime = if mimetype. is_null ( ) {
276
268
default_mime
277
269
} else {
278
- str_from_cstr ( mimetype as * const c_char ) . unwrap_or ( default_mime)
270
+ str_from_cstr ( mimetype) . unwrap_or ( default_mime)
279
271
} ;
280
272
281
273
RequestContext :: current ( )
@@ -292,6 +284,8 @@ impl Handler for Embed {
292
284
} )
293
285
. unwrap_or ( Err ( EmbedException :: Bailout ) ) ?;
294
286
287
+ RequestContext :: reclaim ( ) ;
288
+
295
289
Ok ( response)
296
290
}
297
291
}
0 commit comments