php-src/sapi/continuity/capi.c
2015-01-15 23:27:30 +08:00

508 lines
14 KiB
C

/*
+----------------------------------------------------------------------+
| PHP Version 7 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2015 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Alex Leigh <php (at) postfin (dot) com> |
+----------------------------------------------------------------------+
*/
/* For more information on Continuity: http://www.ashpool.com/ */
/*
* This code is based on the PHP5 SAPI module for NSAPI by Jayakumar
* Muthukumarasamy
*/
/* PHP includes */
#define CONTINUITY 1
#define CAPI_DEBUG
/* Define for CDP specific extensions */
#undef CONTINUITY_CDPEXT
#include "php.h"
#include "php_variables.h"
#include "ext/standard/info.h"
#include "php_ini.h"
#include "php_globals.h"
#include "SAPI.h"
#include "php_main.h"
#include "php_version.h"
#include "TSRM.h"
#include "ext/standard/php_standard.h"
/*
* CAPI includes
*/
#include <continuity.h>
#include <http.h>
#define NSLS_D struct capi_request_context *request_context
#define NSLS_DC , NSLS_D
#define NSLS_C request_context
#define NSLS_CC , NSLS_C
#define NSG(v) (request_context->v)
/*
* ZTS needs to be defined for CAPI to work
*/
#if !defined(ZTS)
#error "CAPI module needs ZTS to be defined"
#endif
/*
* Structure to encapsulate the CAPI request in SAPI
*/
typedef struct capi_request_context {
httpTtrans *t;
int read_post_bytes;
} capi_request_context;
/**************/
PHP_MINIT_FUNCTION(continuity);
PHP_MSHUTDOWN_FUNCTION(continuity);
PHP_RINIT_FUNCTION(continuity);
PHP_RSHUTDOWN_FUNCTION(continuity);
PHP_MINFO_FUNCTION(continuity);
PHP_FUNCTION(continuity_virtual);
PHP_FUNCTION(continuity_request_headers);
PHP_FUNCTION(continuity_response_headers);
const zend_function_entry continuity_functions[] = {
{NULL, NULL, NULL}
};
zend_module_entry continuity_module_entry = {
STANDARD_MODULE_HEADER,
"continuity",
continuity_functions,
PHP_MINIT(continuity),
PHP_MSHUTDOWN(continuity),
NULL,
NULL,
PHP_MINFO(continuity),
NO_VERSION_YET,
STANDARD_MODULE_PROPERTIES
};
PHP_MINIT_FUNCTION(continuity)
{
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(continuity)
{
return SUCCESS;
}
PHP_MINFO_FUNCTION(continuity)
{
php_info_print_table_start();
php_info_print_table_row(2, "Continuity Module Revision", "$Id$");
php_info_print_table_row(2, "Server Version", conFget_build());
#ifdef CONTINUITY_CDPEXT
php_info_print_table_row(2,"CDP Extensions", "enabled");
#else
php_info_print_table_row(2,"CDP Extensions", "disabled");
#endif
php_info_print_table_end();
/* DISPLAY_INI_ENTRIES(); */
}
/**************/
/*
* sapi_capi_ub_write: Write len bytes to the connection output.
*/
static int sapi_capi_ub_write(const char *str, unsigned int str_length)
{
int retval;
capi_request_context *rc;
rc = (capi_request_context *) SG(server_context);
retval = httpFwrite(rc->t, (char *) str, str_length);
if (retval == -1 || retval == 0)
php_handle_aborted_connection();
return retval;
}
/*
* sapi_capi_header_handler: Add/update response headers with those provided
* by the PHP engine.
*/
static int sapi_capi_header_handler(sapi_header_struct * sapi_header, sapi_headers_struct * sapi_headers)
{
char *header_name, *header_content, *p;
capi_request_context *rc = (capi_request_context *) SG(server_context);
lstFset_delete_key(rc->t->res_hdrs, "Content-Type");
header_name = sapi_header->header;
header_content = p = strchr(header_name, ':');
if (p == NULL) {
return 0;
}
*p = 0;
do {
header_content++;
} while (*header_content == ' ');
lstFset_add(rc->t->res_hdrs, header_name, header_content);
*p = ':'; /* restore '*p' */
efree(sapi_header->header);
return 0; /* don't use the default SAPI mechanism, CAPI
* duplicates this functionality */
}
/*
* sapi_capi_send_headers: Transmit the headers to the client. This has the
* effect of starting the response under Continuity.
*/
static int sapi_capi_send_headers(sapi_headers_struct * sapi_headers)
{
int retval;
capi_request_context *rc = (capi_request_context *) SG(server_context);
/*
* We could probably just do this in the header_handler. But, I don't know
* what the implication of doing it there is.
*/
if (SG(sapi_headers).send_default_content_type) {
/* lstFset_delete_key(rc->t->res_hdrs, "Content-Type"); */
lstFset_update(rc->t->res_hdrs, "Content-Type", "text/html");
}
httpFset_status(rc->t, SG(sapi_headers).http_response_code, NULL);
httpFstart_response(rc->t);
return SAPI_HEADER_SENT_SUCCESSFULLY;
}
static int sapi_capi_read_post(char *buffer, uint count_bytes)
{
unsigned int max_read, total_read = 0;
capi_request_context *rc = (capi_request_context *) SG(server_context);
if (rc->read_post_bytes == -1) {
max_read = MIN(count_bytes, SG(request_info).content_length);
} else {
if (rc->read_post_bytes == 0)
return 0;
max_read = MIN(count_bytes, (SG(request_info).content_length - rc->read_post_bytes));
}
total_read = httpFread(rc->t, buffer, max_read);
if (total_read < 0)
total_read = -1;
else
rc->read_post_bytes = total_read;
return total_read;
}
/*
* sapi_capi_read_cookies: Return cookie information into PHP.
*/
static char *sapi_capi_read_cookies(void)
{
char *cookie_string;
capi_request_context *rc = (capi_request_context *) SG(server_context);
cookie_string = lstFset_get(rc->t->req_hdrs, "cookie");
return cookie_string;
}
static void sapi_capi_register_server_variables(zval * track_vars_array)
{
capi_request_context *rc = (capi_request_context *) SG(server_context);
size_t i;
char *value;
char buf[128];
/* PHP_SELF and REQUEST_URI */
value = lstFset_get(rc->t->vars, "uri");
if (value != NULL) {
php_register_variable("PHP_SELF", value, track_vars_array);
php_register_variable("REQUEST_URI", value, track_vars_array);
}
/* COUNTRY CODE */
value = lstFset_get(rc->t->vars, "ccode");
if(value!=NULL)
php_register_variable("COUNTRY_CODE", value, track_vars_array);
/* argv */
value = lstFset_get(rc->t->vars, "query");
if (value != NULL)
php_register_variable("argv", value, track_vars_array);
/* GATEWAY_INTERFACE */
php_register_variable("GATEWAY_INTERFACE", "CGI/1.1", track_vars_array);
/* SERVER_NAME and HTTP_HOST */
value = lstFset_get(rc->t->req_hdrs, "host");
if (value != NULL) {
php_register_variable("HTTP_HOST", value, track_vars_array);
/* TODO: This should probably scrub the port value if one is present. */
php_register_variable("SERVER_NAME", value, track_vars_array);
}
/* SERVER_SOFTWARE */
value = lstFset_get(rc->t->res_hdrs, "Server");
if (value != NULL)
php_register_variable("SERVER_SOFTWARE", value, track_vars_array);
/* SERVER_PROTOCOL */
value = lstFset_get(rc->t->vars, "protocol");
if (value != NULL)
php_register_variable("SERVER_PROTOCOL", value, track_vars_array);
/* REQUEST_METHOD */
value = lstFset_get(rc->t->vars, "method");
if (value != NULL)
php_register_variable("REQUEST_METHOD", value, track_vars_array);
/* QUERY_STRING */
value = lstFset_get(rc->t->vars, "query");
if (value != NULL)
php_register_variable("QUERY_STRING", value, track_vars_array);
/* DOCUMENT_ROOT */
value = lstFset_get(rc->t->vars, "docroot");
if (value != NULL)
php_register_variable("DOCUMENT_ROOT", value, track_vars_array);
/* HTTP_ACCEPT */
value = lstFset_get(rc->t->req_hdrs, "accept");
if (value != NULL)
php_register_variable("HTTP_ACCEPT", value, track_vars_array);
/* HTTP_ACCEPT_CHARSET */
value = lstFset_get(rc->t->req_hdrs, "accept-charset");
if (value != NULL)
php_register_variable("HTTP_ACCEPT_CHARSET", value, track_vars_array);
/* HTTP_ACCEPT_ENCODING */
value = lstFset_get(rc->t->req_hdrs, "accept-encoding");
if (value != NULL)
php_register_variable("HTTP_ACCEPT_ENCODING", value, track_vars_array);
/* HTTP_ACCEPT_LANGUAGE */
value = lstFset_get(rc->t->req_hdrs, "accept-language");
if (value != NULL)
php_register_variable("HTTP_ACCEPT_LANGUAGE", value, track_vars_array);
/* HTTP_CONNECTION */
value = lstFset_get(rc->t->req_hdrs, "connection");
if (value != NULL)
php_register_variable("HTTP_CONNECTION", value, track_vars_array);
/* HTTP_REFERER */
value = lstFset_get(rc->t->req_hdrs, "referer");
if (value != NULL)
php_register_variable("HTTP_REFERER", value, track_vars_array);
/* HTTP_USER_AGENT */
value = lstFset_get(rc->t->req_hdrs, "user-agent");
if (value != NULL)
php_register_variable("HTTP_USER_AGENT", value, track_vars_array);
/* REMOTE_ADDR */
utlFip_to_str(rc->t->cli_ipv4_addr, buf, sizeof(buf));
php_register_variable("REMOTE_ADDR", buf, track_vars_array);
/* REMOTE_PORT */
/* SCRIPT_FILENAME and PATH_TRANSLATED */
value = lstFset_get(rc->t->vars, "path");
if (value != NULL) {
php_register_variable("SCRIPT_FILENAME", value, track_vars_array);
php_register_variable("PATH_TRANSLATED", value, track_vars_array);
}
/* SERVER_ADMIN */
/* Not applicable */
/* SERVER_PORT */
}
static void capi_log_message(char *message)
{
capi_request_context *rc = (capi_request_context *) SG(server_context);
logFmsg(0, "mod/php: %s", message);
}
static int php_capi_startup(sapi_module_struct *sapi_module);
sapi_module_struct capi_sapi_module = {
"Continuity", /* name */
"Continuity Server Enterprise Edition", /* pretty name */
php_capi_startup, /* startup */
php_module_shutdown_wrapper, /* shutdown */
NULL, /* activate */
NULL, /* deactivate */
sapi_capi_ub_write, /* unbuffered write */
NULL, /* flush */
NULL, /* get uid */
NULL, /* getenv */
php_error, /* error handler */
sapi_capi_header_handler, /* header handler */
sapi_capi_send_headers, /* send headers handler */
NULL, /* send header handler */
sapi_capi_read_post, /* read POST data */
sapi_capi_read_cookies, /* read Cookies */
sapi_capi_register_server_variables, /* register server variables */
capi_log_message, /* Log message */
NULL, /* Get request time */
NULL, /* Child terminate */
NULL, /* Block interruptions */
NULL, /* Unblock interruptions */
STANDARD_SAPI_MODULE_PROPERTIES
};
static int php_capi_startup(sapi_module_struct *sapi_module) {
if(php_module_startup(sapi_module,&continuity_module_entry,1)==FAILURE) {
return FAILURE;
}
return SUCCESS;
}
static char *
capi_strdup(char *str)
{
if (str != NULL)
return strFcopy(str);
return NULL;
}
static void capi_free(void *addr)
{
if (addr != NULL)
free(addr);
}
static void capi_request_ctor(NSLS_D)
{
char *query_string = lstFset_get(NSG(t->vars), "query");
char *uri = lstFset_get(NSG(t->vars), "uri");
char *path_info = lstFset_get(NSG(t->vars), "path-info");
char *path_translated = lstFset_get(NSG(t->vars), "path");
char *request_method = lstFset_get(NSG(t->vars), "method");
char *content_type = lstFset_get(NSG(t->req_hdrs), "content-type");
char *content_length = lstFset_get(NSG(t->req_hdrs), "content-length");
SG(request_info).query_string = capi_strdup(query_string);
SG(request_info).request_uri = capi_strdup(uri);
SG(request_info).request_method = capi_strdup(request_method);
SG(request_info).path_translated = capi_strdup(path_translated);
SG(request_info).content_type = capi_strdup(content_type);
SG(request_info).content_length = (content_length == NULL) ? 0 : strtoul(content_length, 0, 0);
SG(sapi_headers).http_response_code = 200;
}
static void capi_request_dtor(NSLS_D)
{
capi_free(SG(request_info).query_string);
capi_free(SG(request_info).request_uri);
capi_free(SG(request_info).request_method);
capi_free(SG(request_info).path_translated);
capi_free(SG(request_info).content_type);
}
int capi_module_main(NSLS_D)
{
zend_file_handle file_handle;
if (php_request_startup() == FAILURE) {
return FAILURE;
}
file_handle.type = ZEND_HANDLE_FILENAME;
file_handle.filename = SG(request_info).path_translated;
file_handle.free_filename = 0;
file_handle.opened_path = NULL;
php_execute_script(&file_handle);
php_request_shutdown(NULL);
return SUCCESS;
}
int phpFinit(lstTset * opt)
{
php_core_globals *core_globals;
tsrm_startup(128, 1, 0, NULL);
core_globals = ts_resource(core_globals_id);
logFmsg(0, "mod/php: PHP Interface v3 (module)");
logFmsg(0, "mod/php: Copyright (c) 1999-2015 The PHP Group. All rights reserved.");
sapi_startup(&capi_sapi_module);
capi_sapi_module.startup(&capi_sapi_module);
return STATUS_PROCEED;
}
int phpFservice(httpTtrans * t, lstTset * opts)
{
int retval;
capi_request_context *request_context;
request_context = (capi_request_context *) malloc(sizeof(capi_request_context));
request_context->t = t;
request_context->read_post_bytes = -1;
SG(server_context) = request_context;
capi_request_ctor(NSLS_C);
retval = capi_module_main(NSLS_C);
capi_request_dtor(NSLS_C);
free(request_context);
/*
* This call is ostensibly provided to free the memory from PHP/TSRM when
* the thread terminated, but, it leaks a structure in some hash list
* according to the developers. Not calling this will leak the entire
* interpreter, around 100k, but calling it and then terminating the
* thread will leak the struct (around a k). The only answer with the
* current TSRM implementation is to reuse the threads that allocate TSRM
* resources.
*/
/* ts_free_thread(); */
if (retval == SUCCESS) {
return STATUS_EXIT;
} else {
return STATUS_ERROR;
}
}