2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2013 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Alex Leigh <php (at) postfin (dot) com> |
16 +----------------------------------------------------------------------+
19 /* For more information on Continuity: http://www.ashpool.com/ */
22 * This code is based on the PHP5 SAPI module for NSAPI by Jayakumar
30 /* Define for CDP specific extensions */
31 #undef CONTINUITY_CDPEXT
34 #include "php_variables.h"
35 #include "ext/standard/info.h"
37 #include "php_globals.h"
40 #include "php_version.h"
42 #include "ext/standard/php_standard.h"
47 #include <continuity.h>
50 #define NSLS_D struct capi_request_context *request_context
51 #define NSLS_DC , NSLS_D
52 #define NSLS_C request_context
53 #define NSLS_CC , NSLS_C
54 #define NSG(v) (request_context->v)
57 * ZTS needs to be defined for CAPI to work
60 #error "CAPI module needs ZTS to be defined"
64 * Structure to encapsulate the CAPI request in SAPI
66 typedef struct capi_request_context
{
69 } capi_request_context
;
73 PHP_MINIT_FUNCTION(continuity
);
74 PHP_MSHUTDOWN_FUNCTION(continuity
);
75 PHP_RINIT_FUNCTION(continuity
);
76 PHP_RSHUTDOWN_FUNCTION(continuity
);
77 PHP_MINFO_FUNCTION(continuity
);
79 PHP_FUNCTION(continuity_virtual
);
80 PHP_FUNCTION(continuity_request_headers
);
81 PHP_FUNCTION(continuity_response_headers
);
83 const zend_function_entry continuity_functions
[] = {
87 zend_module_entry continuity_module_entry
= {
88 STANDARD_MODULE_HEADER
,
91 PHP_MINIT(continuity
),
92 PHP_MSHUTDOWN(continuity
),
95 PHP_MINFO(continuity
),
97 STANDARD_MODULE_PROPERTIES
100 PHP_MINIT_FUNCTION(continuity
)
105 PHP_MSHUTDOWN_FUNCTION(continuity
)
110 PHP_MINFO_FUNCTION(continuity
)
112 php_info_print_table_start();
113 php_info_print_table_row(2, "Continuity Module Revision", "$Id$");
114 php_info_print_table_row(2, "Server Version", conFget_build());
115 #ifdef CONTINUITY_CDPEXT
116 php_info_print_table_row(2,"CDP Extensions", "enabled");
118 php_info_print_table_row(2,"CDP Extensions", "disabled");
120 php_info_print_table_end();
122 /* DISPLAY_INI_ENTRIES(); */
128 * sapi_capi_ub_write: Write len bytes to the connection output.
130 static int sapi_capi_ub_write(const char *str
, unsigned int str_length TSRMLS_DC
)
133 capi_request_context
*rc
;
135 rc
= (capi_request_context
*) SG(server_context
);
136 retval
= httpFwrite(rc
->t
, (char *) str
, str_length
);
137 if (retval
== -1 || retval
== 0)
138 php_handle_aborted_connection();
143 * sapi_capi_header_handler: Add/update response headers with those provided
146 static int sapi_capi_header_handler(sapi_header_struct
* sapi_header
, sapi_headers_struct
* sapi_headers TSRMLS_DC
)
148 char *header_name
, *header_content
, *p
;
149 capi_request_context
*rc
= (capi_request_context
*) SG(server_context
);
151 lstFset_delete_key(rc
->t
->res_hdrs
, "Content-Type");
153 header_name
= sapi_header
->header
;
154 header_content
= p
= strchr(header_name
, ':');
161 } while (*header_content
== ' ');
163 lstFset_add(rc
->t
->res_hdrs
, header_name
, header_content
);
165 *p
= ':'; /* restore '*p' */
167 efree(sapi_header
->header
);
169 return 0; /* don't use the default SAPI mechanism, CAPI
170 * duplicates this functionality */
174 * sapi_capi_send_headers: Transmit the headers to the client. This has the
175 * effect of starting the response under Continuity.
177 static int sapi_capi_send_headers(sapi_headers_struct
* sapi_headers TSRMLS_DC
)
180 capi_request_context
*rc
= (capi_request_context
*) SG(server_context
);
183 * We could probably just do this in the header_handler. But, I don't know
184 * what the implication of doing it there is.
187 if (SG(sapi_headers
).send_default_content_type
) {
188 /* lstFset_delete_key(rc->t->res_hdrs, "Content-Type"); */
189 lstFset_update(rc
->t
->res_hdrs
, "Content-Type", "text/html");
191 httpFset_status(rc
->t
, SG(sapi_headers
).http_response_code
, NULL
);
192 httpFstart_response(rc
->t
);
194 return SAPI_HEADER_SENT_SUCCESSFULLY
;
198 static int sapi_capi_read_post(char *buffer
, uint count_bytes TSRMLS_DC
)
200 unsigned int max_read
, total_read
= 0;
201 capi_request_context
*rc
= (capi_request_context
*) SG(server_context
);
203 if (rc
->read_post_bytes
== -1) {
204 max_read
= MIN(count_bytes
, SG(request_info
).content_length
);
206 if (rc
->read_post_bytes
== 0)
208 max_read
= MIN(count_bytes
, (SG(request_info
).content_length
- rc
->read_post_bytes
));
211 total_read
= httpFread(rc
->t
, buffer
, max_read
);
216 rc
->read_post_bytes
= total_read
;
222 * sapi_capi_read_cookies: Return cookie information into PHP.
224 static char *sapi_capi_read_cookies(TSRMLS_D
)
227 capi_request_context
*rc
= (capi_request_context
*) SG(server_context
);
229 cookie_string
= lstFset_get(rc
->t
->req_hdrs
, "cookie");
230 return cookie_string
;
233 static void sapi_capi_register_server_variables(zval
* track_vars_array TSRMLS_DC
)
235 capi_request_context
*rc
= (capi_request_context
*) SG(server_context
);
240 /* PHP_SELF and REQUEST_URI */
241 value
= lstFset_get(rc
->t
->vars
, "uri");
243 php_register_variable("PHP_SELF", value
, track_vars_array TSRMLS_CC
);
244 php_register_variable("REQUEST_URI", value
, track_vars_array TSRMLS_CC
);
248 value
= lstFset_get(rc
->t
->vars
, "ccode");
250 php_register_variable("COUNTRY_CODE", value
, track_vars_array TSRMLS_CC
);
253 value
= lstFset_get(rc
->t
->vars
, "query");
255 php_register_variable("argv", value
, track_vars_array TSRMLS_CC
);
257 /* GATEWAY_INTERFACE */
258 php_register_variable("GATEWAY_INTERFACE", "CGI/1.1", track_vars_array TSRMLS_CC
);
260 /* SERVER_NAME and HTTP_HOST */
261 value
= lstFset_get(rc
->t
->req_hdrs
, "host");
263 php_register_variable("HTTP_HOST", value
, track_vars_array TSRMLS_CC
);
264 /* TODO: This should probably scrub the port value if one is present. */
265 php_register_variable("SERVER_NAME", value
, track_vars_array TSRMLS_CC
);
267 /* SERVER_SOFTWARE */
268 value
= lstFset_get(rc
->t
->res_hdrs
, "Server");
270 php_register_variable("SERVER_SOFTWARE", value
, track_vars_array TSRMLS_CC
);
272 /* SERVER_PROTOCOL */
273 value
= lstFset_get(rc
->t
->vars
, "protocol");
275 php_register_variable("SERVER_PROTOCOL", value
, track_vars_array TSRMLS_CC
);
278 value
= lstFset_get(rc
->t
->vars
, "method");
280 php_register_variable("REQUEST_METHOD", value
, track_vars_array TSRMLS_CC
);
283 value
= lstFset_get(rc
->t
->vars
, "query");
285 php_register_variable("QUERY_STRING", value
, track_vars_array TSRMLS_CC
);
288 value
= lstFset_get(rc
->t
->vars
, "docroot");
290 php_register_variable("DOCUMENT_ROOT", value
, track_vars_array TSRMLS_CC
);
293 value
= lstFset_get(rc
->t
->req_hdrs
, "accept");
295 php_register_variable("HTTP_ACCEPT", value
, track_vars_array TSRMLS_CC
);
297 /* HTTP_ACCEPT_CHARSET */
298 value
= lstFset_get(rc
->t
->req_hdrs
, "accept-charset");
300 php_register_variable("HTTP_ACCEPT_CHARSET", value
, track_vars_array TSRMLS_CC
);
302 /* HTTP_ACCEPT_ENCODING */
303 value
= lstFset_get(rc
->t
->req_hdrs
, "accept-encoding");
305 php_register_variable("HTTP_ACCEPT_ENCODING", value
, track_vars_array TSRMLS_CC
);
307 /* HTTP_ACCEPT_LANGUAGE */
308 value
= lstFset_get(rc
->t
->req_hdrs
, "accept-language");
310 php_register_variable("HTTP_ACCEPT_LANGUAGE", value
, track_vars_array TSRMLS_CC
);
312 /* HTTP_CONNECTION */
313 value
= lstFset_get(rc
->t
->req_hdrs
, "connection");
315 php_register_variable("HTTP_CONNECTION", value
, track_vars_array TSRMLS_CC
);
318 value
= lstFset_get(rc
->t
->req_hdrs
, "referer");
320 php_register_variable("HTTP_REFERER", value
, track_vars_array TSRMLS_CC
);
322 /* HTTP_USER_AGENT */
323 value
= lstFset_get(rc
->t
->req_hdrs
, "user-agent");
325 php_register_variable("HTTP_USER_AGENT", value
, track_vars_array TSRMLS_CC
);
328 utlFip_to_str(rc
->t
->cli_ipv4_addr
, buf
, sizeof(buf
));
329 php_register_variable("REMOTE_ADDR", buf
, track_vars_array TSRMLS_CC
);
333 /* SCRIPT_FILENAME and PATH_TRANSLATED */
334 value
= lstFset_get(rc
->t
->vars
, "path");
336 php_register_variable("SCRIPT_FILENAME", value
, track_vars_array TSRMLS_CC
);
337 php_register_variable("PATH_TRANSLATED", value
, track_vars_array TSRMLS_CC
);
346 static void capi_log_message(char *message TSRMLS_DC
)
348 capi_request_context
*rc
= (capi_request_context
*) SG(server_context
);
349 logFmsg(0, "mod/php: %s", message
);
352 static int php_capi_startup(sapi_module_struct
*sapi_module
);
354 sapi_module_struct capi_sapi_module
= {
355 "Continuity", /* name */
356 "Continuity Server Enterprise Edition", /* pretty name */
358 php_capi_startup
, /* startup */
359 php_module_shutdown_wrapper
, /* shutdown */
362 NULL
, /* deactivate */
364 sapi_capi_ub_write
, /* unbuffered write */
369 php_error
, /* error handler */
371 sapi_capi_header_handler
, /* header handler */
372 sapi_capi_send_headers
, /* send headers handler */
373 NULL
, /* send header handler */
375 sapi_capi_read_post
, /* read POST data */
376 sapi_capi_read_cookies
, /* read Cookies */
378 sapi_capi_register_server_variables
, /* register server variables */
379 capi_log_message
, /* Log message */
380 NULL
, /* Get request time */
381 NULL
, /* Child terminate */
383 NULL
, /* Block interruptions */
384 NULL
, /* Unblock interruptions */
386 STANDARD_SAPI_MODULE_PROPERTIES
389 static int php_capi_startup(sapi_module_struct
*sapi_module
) {
390 if(php_module_startup(sapi_module
,&continuity_module_entry
,1)==FAILURE
) {
398 capi_strdup(char *str
)
401 return strFcopy(str
);
405 static void capi_free(void *addr
)
411 static void capi_request_ctor(NSLS_D TSRMLS_DC
)
413 char *query_string
= lstFset_get(NSG(t
->vars
), "query");
414 char *uri
= lstFset_get(NSG(t
->vars
), "uri");
415 char *path_info
= lstFset_get(NSG(t
->vars
), "path-info");
416 char *path_translated
= lstFset_get(NSG(t
->vars
), "path");
417 char *request_method
= lstFset_get(NSG(t
->vars
), "method");
418 char *content_type
= lstFset_get(NSG(t
->req_hdrs
), "content-type");
419 char *content_length
= lstFset_get(NSG(t
->req_hdrs
), "content-length");
421 SG(request_info
).query_string
= capi_strdup(query_string
);
422 SG(request_info
).request_uri
= capi_strdup(uri
);
423 SG(request_info
).request_method
= capi_strdup(request_method
);
424 SG(request_info
).path_translated
= capi_strdup(path_translated
);
425 SG(request_info
).content_type
= capi_strdup(content_type
);
426 SG(request_info
).content_length
= (content_length
== NULL
) ? 0 : strtoul(content_length
, 0, 0);
427 SG(sapi_headers
).http_response_code
= 200;
430 static void capi_request_dtor(NSLS_D TSRMLS_DC
)
432 capi_free(SG(request_info
).query_string
);
433 capi_free(SG(request_info
).request_uri
);
434 capi_free(SG(request_info
).request_method
);
435 capi_free(SG(request_info
).path_translated
);
436 capi_free(SG(request_info
).content_type
);
439 int capi_module_main(NSLS_D TSRMLS_DC
)
441 zend_file_handle file_handle
;
443 if (php_request_startup(TSRMLS_C
) == FAILURE
) {
446 file_handle
.type
= ZEND_HANDLE_FILENAME
;
447 file_handle
.filename
= SG(request_info
).path_translated
;
448 file_handle
.free_filename
= 0;
449 file_handle
.opened_path
= NULL
;
451 php_execute_script(&file_handle TSRMLS_CC
);
452 php_request_shutdown(NULL
);
457 int phpFinit(lstTset
* opt
)
459 php_core_globals
*core_globals
;
461 tsrm_startup(128, 1, 0, NULL
);
462 core_globals
= ts_resource(core_globals_id
);
464 logFmsg(0, "mod/php: PHP Interface v3 (module)");
465 logFmsg(0, "mod/php: Copyright (c) 1999-2005 The PHP Group. All rights reserved.");
467 sapi_startup(&capi_sapi_module
);
468 capi_sapi_module
.startup(&capi_sapi_module
);
470 return STATUS_PROCEED
;
473 int phpFservice(httpTtrans
* t
, lstTset
* opts
)
476 capi_request_context
*request_context
;
480 request_context
= (capi_request_context
*) malloc(sizeof(capi_request_context
));
481 request_context
->t
= t
;
482 request_context
->read_post_bytes
= -1;
484 SG(server_context
) = request_context
;
486 capi_request_ctor(NSLS_C TSRMLS_CC
);
487 retval
= capi_module_main(NSLS_C TSRMLS_CC
);
488 capi_request_dtor(NSLS_C TSRMLS_CC
);
490 free(request_context
);
493 * This call is ostensibly provided to free the memory from PHP/TSRM when
494 * the thread terminated, but, it leaks a structure in some hash list
495 * according to the developers. Not calling this will leak the entire
496 * interpreter, around 100k, but calling it and then terminating the
497 * thread will leak the struct (around a k). The only answer with the
498 * current TSRM implementation is to reuse the threads that allocate TSRM
501 /* ts_free_thread(); */
503 if (retval
== SUCCESS
) {