mirror of
https://github.com/clearlinux/uwsgi.git
synced 2026-08-28 17:55:39 +00:00
d4130dbc4b
preliminary api for language-independent body read another step completed body read language independent implementation ported gevent to the new read/write api ported websockets to the new read/write api removed channels subsystem removed channels subsystem ported lua to the new read/write api fixed post-buffering readline is still broken improved request body readline very difficult test for readline()/read() combo passed other improvements in postbuffering/read/readline ported --http-socket to the new api added X-Forwarded-SSL management removed old api more refactoring ported the RACK plugin to the new api ported psgi plugin to the new api defintely removed clustering simpified ifdel hell simpified ifdef hell removed useless configuration options
889 lines
25 KiB
C
889 lines
25 KiB
C
#include "php.h"
|
|
#include "SAPI.h"
|
|
#include "php_main.h"
|
|
#include "php_variables.h"
|
|
|
|
#include "ext/standard/php_smart_str.h"
|
|
#include "ext/standard/info.h"
|
|
|
|
#include "../../uwsgi.h"
|
|
|
|
extern struct uwsgi_server uwsgi;
|
|
|
|
static sapi_module_struct uwsgi_sapi_module;
|
|
|
|
struct uwsgi_php {
|
|
struct uwsgi_string_list *allowed_docroot;
|
|
struct uwsgi_string_list *allowed_ext;
|
|
struct uwsgi_string_list *allowed_scripts;
|
|
struct uwsgi_string_list *index;
|
|
struct uwsgi_string_list *set;
|
|
struct uwsgi_string_list *append_config;
|
|
struct uwsgi_regexp_list *app_bypass;
|
|
struct uwsgi_string_list *vars;
|
|
char *docroot;
|
|
char *app;
|
|
char *app_qs;
|
|
char *fallback;
|
|
size_t ini_size;
|
|
int dump_config;
|
|
char *server_software;
|
|
size_t server_software_len;
|
|
} uphp;
|
|
|
|
void uwsgi_opt_php_ini(char *opt, char *value, void *foobar) {
|
|
uwsgi_sapi_module.php_ini_path_override = uwsgi_str(value);
|
|
uwsgi_sapi_module.php_ini_ignore = 1;
|
|
}
|
|
|
|
struct uwsgi_option uwsgi_php_options[] = {
|
|
|
|
{"php-ini", required_argument, 0, "set php.ini path", uwsgi_opt_php_ini, NULL, 0},
|
|
{"php-config", required_argument, 0, "set php.ini path", uwsgi_opt_php_ini, NULL, 0},
|
|
{"php-ini-append", required_argument, 0, "set php.ini path (append mode)", uwsgi_opt_add_string_list, &uphp.append_config, 0},
|
|
{"php-config-append", required_argument, 0, "set php.ini path (append mode)", uwsgi_opt_add_string_list, &uphp.append_config, 0},
|
|
{"php-set", required_argument, 0, "set a php config directive", uwsgi_opt_add_string_list, &uphp.set, 0},
|
|
{"php-index", required_argument, 0, "list the php index files", uwsgi_opt_add_string_list, &uphp.index, 0},
|
|
{"php-docroot", required_argument, 0, "force php DOCUMENT_ROOT", uwsgi_opt_set_str, &uphp.docroot, 0},
|
|
{"php-allowed-docroot", required_argument, 0, "list the allowed document roots", uwsgi_opt_add_string_list, &uphp.allowed_docroot, 0},
|
|
{"php-allowed-ext", required_argument, 0, "list the allowed php file extensions", uwsgi_opt_add_string_list, &uphp.allowed_ext, 0},
|
|
{"php-allowed-script", required_argument, 0, "list the allowed php scripts (require absolute path)", uwsgi_opt_add_string_list, &uphp.allowed_scripts, 0},
|
|
{"php-server-software", required_argument, 0, "force php SERVER_SOFTWARE", uwsgi_opt_set_str, &uphp.server_software, 0},
|
|
{"php-app", required_argument, 0, "force the php file to run at each request", uwsgi_opt_set_str, &uphp.app, 0},
|
|
{"php-app-qs", required_argument, 0, "when in app mode force QUERY_STRING to the specified value + REQUEST_URI", uwsgi_opt_set_str, &uphp.app_qs, 0},
|
|
{"php-fallback", required_argument, 0, "run the specified php script when the request one does not exist", uwsgi_opt_set_str, &uphp.fallback, 0},
|
|
{"php-app-bypass", required_argument, 0, "if the regexp matches the uri the --php-app is bypassed", uwsgi_opt_add_regexp_list, &uphp.app_bypass, 0},
|
|
{"php-var", required_argument, 0, "add/overwrite a CGI variable at each request", uwsgi_opt_add_string_list, &uphp.vars, 0},
|
|
{"php-dump-config", no_argument, 0, "dump php config (if modified via --php-set or append options)", uwsgi_opt_true, &uphp.dump_config, 0},
|
|
{0, 0, 0, 0, 0, 0, 0},
|
|
|
|
};
|
|
|
|
|
|
static int sapi_uwsgi_ub_write(const char *str, uint str_length TSRMLS_DC)
|
|
{
|
|
struct wsgi_request *wsgi_req = (struct wsgi_request *) SG(server_context);
|
|
|
|
uwsgi_response_write_body_do(wsgi_req, (char *) str, str_length);
|
|
if (wsgi_req->write_errors > uwsgi.write_errors_tolerance) {
|
|
php_handle_aborted_connection();
|
|
return -1;
|
|
}
|
|
return str_length;
|
|
}
|
|
|
|
static int sapi_uwsgi_send_headers(sapi_headers_struct *sapi_headers)
|
|
{
|
|
sapi_header_struct *h;
|
|
zend_llist_position pos;
|
|
|
|
if (SG(request_info).no_headers == 1) {
|
|
return SAPI_HEADER_SENT_SUCCESSFULLY;
|
|
}
|
|
|
|
struct wsgi_request *wsgi_req = (struct wsgi_request *) SG(server_context);
|
|
|
|
if (!SG(sapi_headers).http_status_line) {
|
|
char status[4];
|
|
int hrc = SG(sapi_headers).http_response_code;
|
|
if (!hrc) hrc = 200;
|
|
uwsgi_num2str2n(hrc, status, 4);
|
|
uwsgi_response_prepare_headers(wsgi_req, status, 3);
|
|
}
|
|
else {
|
|
char *sl = SG(sapi_headers).http_status_line;
|
|
uwsgi_response_prepare_headers(wsgi_req, sl, strlen(sl));
|
|
}
|
|
|
|
h = zend_llist_get_first_ex(&sapi_headers->headers, &pos);
|
|
while (h) {
|
|
uwsgi_response_add_header(wsgi_req, NULL, 0, h->header, h->header_len);
|
|
h = zend_llist_get_next_ex(&sapi_headers->headers, &pos);
|
|
}
|
|
|
|
return SAPI_HEADER_SENT_SUCCESSFULLY;
|
|
}
|
|
|
|
static int sapi_uwsgi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
|
|
{
|
|
uint read_bytes = 0;
|
|
ssize_t len;
|
|
int fd = -1;
|
|
|
|
struct wsgi_request *wsgi_req = (struct wsgi_request *) SG(server_context);
|
|
|
|
count_bytes = MIN(count_bytes, wsgi_req->post_cl - SG(read_post_bytes));
|
|
|
|
while (read_bytes < count_bytes) {
|
|
ssize_t rlen = 0;
|
|
char *buf = uwsgi_request_body_read(wsgi_req, count_bytes - read_bytes, &rlen);
|
|
if (buf == uwsgi.empty) break;
|
|
if (buf) {
|
|
read_bytes += rlen;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return read_bytes;
|
|
}
|
|
|
|
|
|
static char *sapi_uwsgi_read_cookies(void)
|
|
{
|
|
uint16_t len = 0;
|
|
struct wsgi_request *wsgi_req = (struct wsgi_request *) SG(server_context);
|
|
|
|
char *cookie = uwsgi_get_var(wsgi_req, (char *)"HTTP_COOKIE", 11, &len);
|
|
if (cookie) {
|
|
return estrndup(cookie, len);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void sapi_uwsgi_register_variables(zval *track_vars_array TSRMLS_DC)
|
|
{
|
|
int i;
|
|
struct wsgi_request *wsgi_req = (struct wsgi_request *) SG(server_context);
|
|
php_import_environment_variables(track_vars_array TSRMLS_CC);
|
|
|
|
if (uphp.server_software) {
|
|
if (!uphp.server_software_len) uphp.server_software_len = strlen(uphp.server_software);
|
|
php_register_variable_safe("SERVER_SOFTWARE", uphp.server_software, uphp.server_software_len, track_vars_array TSRMLS_CC);
|
|
}
|
|
else {
|
|
php_register_variable_safe("SERVER_SOFTWARE", "uWSGI", 5, track_vars_array TSRMLS_CC);
|
|
}
|
|
|
|
for (i = 0; i < wsgi_req->var_cnt; i += 2) {
|
|
php_register_variable_safe( estrndup(wsgi_req->hvec[i].iov_base, wsgi_req->hvec[i].iov_len),
|
|
wsgi_req->hvec[i + 1].iov_base, wsgi_req->hvec[i + 1].iov_len,
|
|
track_vars_array TSRMLS_CC);
|
|
}
|
|
|
|
php_register_variable_safe("PATH_INFO", wsgi_req->path_info, wsgi_req->path_info_len, track_vars_array TSRMLS_CC);
|
|
if (wsgi_req->query_string_len > 0) {
|
|
php_register_variable_safe("QUERY_STRING", wsgi_req->query_string, wsgi_req->query_string_len, track_vars_array TSRMLS_CC);
|
|
}
|
|
|
|
php_register_variable_safe("SCRIPT_NAME", wsgi_req->script_name, wsgi_req->script_name_len, track_vars_array TSRMLS_CC);
|
|
php_register_variable_safe("SCRIPT_FILENAME", wsgi_req->file, wsgi_req->file_len, track_vars_array TSRMLS_CC);
|
|
|
|
php_register_variable_safe("DOCUMENT_ROOT", wsgi_req->document_root, wsgi_req->document_root_len, track_vars_array TSRMLS_CC);
|
|
|
|
if (wsgi_req->path_info_len) {
|
|
char *path_translated = ecalloc(1, (wsgi_req->file_len - wsgi_req->script_name_len) + wsgi_req->path_info_len + 1);
|
|
|
|
memcpy(path_translated, wsgi_req->file, (wsgi_req->file_len - wsgi_req->script_name_len));
|
|
memcpy(path_translated + (wsgi_req->file_len - wsgi_req->script_name_len), wsgi_req->path_info, wsgi_req->path_info_len);
|
|
php_register_variable_safe("PATH_TRANSLATED", path_translated, (wsgi_req->file_len - wsgi_req->script_name_len) + wsgi_req->path_info_len , track_vars_array TSRMLS_CC);
|
|
}
|
|
else {
|
|
php_register_variable_safe("PATH_TRANSLATED", "", 0, track_vars_array TSRMLS_CC);
|
|
}
|
|
|
|
php_register_variable_safe("PHP_SELF", wsgi_req->script_name, wsgi_req->script_name_len, track_vars_array TSRMLS_CC);
|
|
|
|
struct uwsgi_string_list *usl = uphp.vars;
|
|
while(usl) {
|
|
char *equal = strchr(usl->value, '=');
|
|
if (equal) {
|
|
php_register_variable_safe( estrndup(usl->value, equal-usl->value),
|
|
equal+1, strlen(equal+1), track_vars_array TSRMLS_CC);
|
|
}
|
|
usl = usl->next;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
static sapi_module_struct uwsgi_sapi_module;
|
|
|
|
|
|
|
|
|
|
void uwsgi_php_append_config(char *filename) {
|
|
size_t file_size = 0;
|
|
char *file_content = uwsgi_open_and_read(filename, &file_size, 1, NULL);
|
|
uwsgi_sapi_module.ini_entries = realloc(uwsgi_sapi_module.ini_entries, uphp.ini_size + file_size);
|
|
memcpy(uwsgi_sapi_module.ini_entries + uphp.ini_size, file_content, file_size);
|
|
uphp.ini_size += file_size-1;
|
|
free(file_content);
|
|
}
|
|
|
|
void uwsgi_php_set(char *opt) {
|
|
|
|
uwsgi_sapi_module.ini_entries = realloc(uwsgi_sapi_module.ini_entries, uphp.ini_size + strlen(opt)+2);
|
|
memcpy(uwsgi_sapi_module.ini_entries + uphp.ini_size, opt, strlen(opt));
|
|
|
|
uphp.ini_size += strlen(opt)+1;
|
|
uwsgi_sapi_module.ini_entries[uphp.ini_size-1] = '\n';
|
|
uwsgi_sapi_module.ini_entries[uphp.ini_size] = 0;
|
|
}
|
|
|
|
// future implementation...
|
|
PHP_MINIT_FUNCTION(uwsgi_php_minit) {
|
|
return SUCCESS;
|
|
}
|
|
|
|
PHP_FUNCTION(uwsgi_version) {
|
|
RETURN_STRING(UWSGI_VERSION, 1);
|
|
}
|
|
|
|
PHP_FUNCTION(uwsgi_worker_id) {
|
|
RETURN_LONG(uwsgi.mywid);
|
|
}
|
|
|
|
PHP_FUNCTION(uwsgi_masterpid) {
|
|
if (uwsgi.master_process) {
|
|
RETURN_LONG(uwsgi.workers[0].pid);
|
|
}
|
|
RETURN_LONG(0);
|
|
}
|
|
|
|
PHP_FUNCTION(uwsgi_cache_del) {
|
|
|
|
char *key = NULL;
|
|
int keylen = 0;
|
|
|
|
if (!uwsgi.caches)
|
|
RETURN_NULL();
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &keylen) == FAILURE) {
|
|
RETURN_NULL();
|
|
}
|
|
|
|
uwsgi_wlock(uwsgi.caches->lock);
|
|
if (uwsgi_cache_del(key, keylen, 0, 0)) {
|
|
uwsgi_rwunlock(uwsgi.caches->lock);
|
|
RETURN_TRUE;
|
|
}
|
|
|
|
uwsgi_rwunlock(uwsgi.caches->lock);
|
|
RETURN_NULL();
|
|
}
|
|
|
|
PHP_FUNCTION(uwsgi_cache_get) {
|
|
|
|
char *key = NULL;
|
|
int keylen = 0;
|
|
uint64_t valsize;
|
|
|
|
if (!uwsgi.caches)
|
|
RETURN_NULL();
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &keylen) == FAILURE) {
|
|
RETURN_NULL();
|
|
}
|
|
|
|
uwsgi_rlock(uwsgi.caches->lock);
|
|
char *value = uwsgi_cache_get(key, keylen, &valsize);
|
|
if (value) {
|
|
char *ret = estrndup(value, valsize);
|
|
uwsgi_rwunlock(uwsgi.caches->lock);
|
|
RETURN_STRING(ret, 0);
|
|
}
|
|
uwsgi_rwunlock(uwsgi.caches->lock);
|
|
RETURN_NULL();
|
|
}
|
|
|
|
PHP_FUNCTION(uwsgi_cache_set) {
|
|
char *key = NULL;
|
|
int keylen;
|
|
char *value = NULL;
|
|
int vallen;
|
|
uint64_t expires = 0;
|
|
|
|
if (!uwsgi.caches)
|
|
RETURN_NULL();
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &key, &keylen, &value, &vallen, &expires) == FAILURE) {
|
|
RETURN_NULL();
|
|
}
|
|
|
|
if ((uint64_t)vallen > uwsgi.caches->blocksize) {
|
|
RETURN_NULL();
|
|
}
|
|
|
|
uwsgi_wlock(uwsgi.caches->lock);
|
|
if (uwsgi_cache_set(key, keylen, value, vallen, expires, 0)) {
|
|
uwsgi_rwunlock(uwsgi.caches->lock);
|
|
RETURN_TRUE;
|
|
}
|
|
uwsgi_rwunlock(uwsgi.caches->lock);
|
|
RETURN_NULL();
|
|
|
|
}
|
|
|
|
PHP_FUNCTION(uwsgi_cache_update) {
|
|
char *key = NULL;
|
|
int keylen;
|
|
char *value = NULL;
|
|
int vallen;
|
|
uint64_t expires = 0;
|
|
|
|
if (!uwsgi.caches)
|
|
RETURN_NULL();
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", &key, &keylen, &value, &vallen, &expires) == FAILURE) {
|
|
RETURN_NULL();
|
|
}
|
|
|
|
if ((uint64_t)vallen > uwsgi.caches->blocksize) {
|
|
RETURN_NULL();
|
|
}
|
|
|
|
uwsgi_wlock(uwsgi.caches->lock);
|
|
if (uwsgi_cache_set(key, keylen, value, vallen, expires, UWSGI_CACHE_FLAG_UPDATE)) {
|
|
uwsgi_rwunlock(uwsgi.caches->lock);
|
|
RETURN_TRUE;
|
|
}
|
|
uwsgi_rwunlock(uwsgi.caches->lock);
|
|
RETURN_NULL();
|
|
|
|
}
|
|
|
|
|
|
PHP_FUNCTION(uwsgi_rpc) {
|
|
|
|
|
|
int num_args = 0;
|
|
int i;
|
|
char *node = NULL;
|
|
char *func = NULL;
|
|
zval ***varargs = NULL;
|
|
zval *z_current_obj;
|
|
char *argv[256];
|
|
uint16_t argvs[256];
|
|
uint16_t size = 0;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &varargs, &num_args) == FAILURE) {
|
|
RETURN_NULL();
|
|
}
|
|
|
|
if (num_args < 2)
|
|
goto clear;
|
|
|
|
if (num_args > 256 + 2)
|
|
goto clear;
|
|
|
|
z_current_obj = *varargs[0];
|
|
if (Z_TYPE_P(z_current_obj) != IS_STRING) {
|
|
goto clear;
|
|
}
|
|
|
|
node = Z_STRVAL_P(z_current_obj);
|
|
|
|
z_current_obj = *varargs[1];
|
|
if (Z_TYPE_P(z_current_obj) != IS_STRING) {
|
|
goto clear;
|
|
}
|
|
|
|
func = Z_STRVAL_P(z_current_obj);
|
|
|
|
for(i=0;i<(num_args-2);i++) {
|
|
z_current_obj = *varargs[i+2];
|
|
if (Z_TYPE_P(z_current_obj) != IS_STRING) {
|
|
goto clear;
|
|
}
|
|
argv[i] = Z_STRVAL_P(z_current_obj);
|
|
argvs[i] = Z_STRLEN_P(z_current_obj);
|
|
}
|
|
|
|
// response must always be freed
|
|
char *response = uwsgi_do_rpc(node, func, num_args - 2, argv, argvs, &size);
|
|
|
|
if (size > 0) {
|
|
// here we do not free varargs for performance reasons
|
|
char *ret = estrndup(response, size);
|
|
free(response);
|
|
RETURN_STRING(ret, 0);
|
|
}
|
|
free(response);
|
|
|
|
clear:
|
|
efree(varargs);
|
|
RETURN_NULL();
|
|
|
|
}
|
|
|
|
|
|
PHP_FUNCTION(uwsgi_setprocname) {
|
|
|
|
char *name;
|
|
int name_len;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
|
|
RETURN_NULL();
|
|
}
|
|
|
|
uwsgi_set_processname(estrndup(name, name_len));
|
|
|
|
RETURN_NULL();
|
|
}
|
|
|
|
PHP_FUNCTION(uwsgi_signal) {
|
|
|
|
long long_signum;
|
|
uint8_t signum = 0;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &long_signum) == FAILURE) {
|
|
RETURN_NULL();
|
|
}
|
|
|
|
signum = (uint8_t) long_signum;
|
|
uwsgi_signal_send(uwsgi.signal_socket, signum);
|
|
|
|
RETURN_NULL();
|
|
}
|
|
|
|
zend_function_entry uwsgi_php_functions[] = {
|
|
PHP_FE(uwsgi_version, NULL)
|
|
PHP_FE(uwsgi_setprocname, NULL)
|
|
PHP_FE(uwsgi_worker_id, NULL)
|
|
PHP_FE(uwsgi_masterpid, NULL)
|
|
PHP_FE(uwsgi_signal, NULL)
|
|
PHP_FE(uwsgi_rpc, NULL)
|
|
PHP_FE(uwsgi_cache_get, NULL)
|
|
PHP_FE(uwsgi_cache_set, NULL)
|
|
PHP_FE(uwsgi_cache_update, NULL)
|
|
PHP_FE(uwsgi_cache_del, NULL)
|
|
{ NULL, NULL, NULL},
|
|
};
|
|
|
|
PHP_MINFO_FUNCTION(uwsgi_php_minfo) {
|
|
php_info_print_table_start( );
|
|
php_info_print_table_row(2, "uwsgi api", "enabled");
|
|
if (uwsgi.caches) {
|
|
php_info_print_table_row(2, "uwsgi cache", "enabled");
|
|
}
|
|
else {
|
|
php_info_print_table_row(2, "uwsgi cache", "disabled");
|
|
}
|
|
php_info_print_table_end( );
|
|
}
|
|
|
|
static zend_module_entry uwsgi_module_entry = {
|
|
STANDARD_MODULE_HEADER,
|
|
"uwsgi",
|
|
uwsgi_php_functions,
|
|
PHP_MINIT(uwsgi_php_minit),
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
PHP_MINFO(uwsgi_php_minfo),
|
|
UWSGI_VERSION,
|
|
STANDARD_MODULE_PROPERTIES
|
|
};
|
|
|
|
|
|
static int php_uwsgi_startup(sapi_module_struct *sapi_module)
|
|
{
|
|
|
|
if (php_module_startup(&uwsgi_sapi_module, &uwsgi_module_entry, 1)==FAILURE) {
|
|
return FAILURE;
|
|
} else {
|
|
return SUCCESS;
|
|
}
|
|
}
|
|
|
|
static void sapi_uwsgi_log_message(char *message) {
|
|
|
|
uwsgi_log("%s\n", message);
|
|
}
|
|
|
|
static sapi_module_struct uwsgi_sapi_module = {
|
|
"uwsgi",
|
|
"uWSGI/php",
|
|
|
|
php_uwsgi_startup,
|
|
php_module_shutdown_wrapper,
|
|
|
|
NULL, /* activate */
|
|
NULL, /* deactivate */
|
|
|
|
sapi_uwsgi_ub_write,
|
|
NULL,
|
|
NULL, /* get uid */
|
|
NULL, /* getenv */
|
|
|
|
php_error,
|
|
|
|
NULL,
|
|
sapi_uwsgi_send_headers,
|
|
NULL,
|
|
sapi_uwsgi_read_post,
|
|
sapi_uwsgi_read_cookies,
|
|
|
|
sapi_uwsgi_register_variables,
|
|
sapi_uwsgi_log_message, /* Log message */
|
|
NULL, /* Get request time */
|
|
NULL, /* Child terminate */
|
|
|
|
STANDARD_SAPI_MODULE_PROPERTIES
|
|
};
|
|
|
|
int uwsgi_php_init(void) {
|
|
|
|
struct uwsgi_string_list *pset = uphp.set;
|
|
struct uwsgi_string_list *append_config = uphp.append_config;
|
|
|
|
sapi_startup(&uwsgi_sapi_module);
|
|
|
|
// applying custom options
|
|
while(append_config) {
|
|
uwsgi_php_append_config(append_config->value);
|
|
append_config = append_config->next;
|
|
}
|
|
while(pset) {
|
|
uwsgi_php_set(pset->value);
|
|
pset = pset->next;
|
|
}
|
|
|
|
if (uphp.dump_config) {
|
|
uwsgi_log("--- PHP custom config ---\n\n");
|
|
uwsgi_log("%s\n", uwsgi_sapi_module.ini_entries);
|
|
uwsgi_log("--- end of PHP custom config ---\n");
|
|
}
|
|
|
|
// fix docroot
|
|
if (uphp.docroot) {
|
|
char *orig_docroot = uphp.docroot;
|
|
uphp.docroot = uwsgi_expand_path(uphp.docroot, strlen(uphp.docroot), NULL);
|
|
if (!uphp.docroot) {
|
|
uwsgi_log("unable to set php docroot to %s\n", orig_docroot);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
uwsgi_sapi_module.startup(&uwsgi_sapi_module);
|
|
|
|
uwsgi_log("PHP %s initialized\n", PHP_VERSION);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int uwsgi_php_walk(struct wsgi_request *wsgi_req, char *full_path, char *docroot, size_t docroot_len, char **path_info) {
|
|
|
|
// and now start walking...
|
|
uint16_t i;
|
|
char *ptr = wsgi_req->path_info;
|
|
char *dst = full_path+docroot_len;
|
|
char *part = ptr;
|
|
int part_size = 0;
|
|
struct stat st;
|
|
|
|
if (wsgi_req->path_info_len == 0) return 0;
|
|
|
|
if (ptr[0] == '/') part_size++;
|
|
|
|
for(i=0;i<wsgi_req->path_info_len;i++) {
|
|
if (ptr[i] == '/') {
|
|
memcpy(dst, part, part_size-1);
|
|
*(dst+part_size-1) = 0;
|
|
|
|
if (stat(full_path, &st)) {
|
|
return -1;
|
|
}
|
|
|
|
|
|
// not a directory, stop walking
|
|
if (!S_ISDIR(st.st_mode)) {
|
|
if (i < (wsgi_req->path_info_len)-1) {
|
|
*path_info = ptr + i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
// check for buffer overflow !!!
|
|
*(dst+part_size-1) = '/';
|
|
*(dst+part_size) = 0;
|
|
|
|
dst += part_size ;
|
|
part_size = 0;
|
|
part = ptr + i + 1;
|
|
}
|
|
|
|
part_size++;
|
|
}
|
|
|
|
if (part < wsgi_req->path_info+wsgi_req->path_info_len) {
|
|
memcpy(dst, part, part_size-1);
|
|
*(dst+part_size-1) = 0;
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
int uwsgi_php_request(struct wsgi_request *wsgi_req) {
|
|
|
|
char real_filename[PATH_MAX+1];
|
|
char *path_info = NULL;
|
|
size_t real_filename_len = 0;
|
|
struct stat php_stat;
|
|
char *filename = NULL;
|
|
|
|
zend_file_handle file_handle;
|
|
|
|
SG(server_context) = (void *) wsgi_req;
|
|
|
|
if (uwsgi_parse_vars(wsgi_req)) {
|
|
return -1;
|
|
}
|
|
|
|
if (uphp.docroot) {
|
|
wsgi_req->document_root = uphp.docroot;
|
|
}
|
|
// fallback to cwd
|
|
else if (!wsgi_req->document_root_len) {
|
|
wsgi_req->document_root = uwsgi.cwd;
|
|
}
|
|
else {
|
|
// explode DOCUMENT_ROOT (both for security and sanity checks)
|
|
// this memory will be cleared on request end
|
|
char *sanitized_docroot = ecalloc(1, PATH_MAX+1);
|
|
if (!uwsgi_expand_path(wsgi_req->document_root, wsgi_req->document_root_len, sanitized_docroot)) {
|
|
efree(sanitized_docroot);
|
|
return -1;
|
|
}
|
|
wsgi_req->document_root = sanitized_docroot;
|
|
}
|
|
|
|
// fix document_root_len
|
|
wsgi_req->document_root_len = strlen(wsgi_req->document_root);
|
|
|
|
if (uphp.app) {
|
|
struct uwsgi_regexp_list *bypass = uphp.app_bypass;
|
|
while (bypass) {
|
|
if (uwsgi_regexp_match(bypass->pattern, bypass->pattern_extra, wsgi_req->uri, wsgi_req->uri_len) >= 0) {
|
|
goto oldstyle;
|
|
}
|
|
bypass = bypass->next;
|
|
}
|
|
|
|
strcpy(real_filename, uphp.app);
|
|
if (wsgi_req->path_info_len == 1 && wsgi_req->path_info[0] == '/') {
|
|
goto appready;
|
|
}
|
|
if (uphp.app_qs) {
|
|
size_t app_qs_len = strlen(uphp.app_qs);
|
|
size_t qs_len = wsgi_req->path_info_len + app_qs_len;
|
|
if (wsgi_req->query_string_len > 0) {
|
|
qs_len += 1 + wsgi_req->query_string_len;
|
|
}
|
|
char *qs = ecalloc(1, qs_len+1);
|
|
memcpy(qs, uphp.app_qs, app_qs_len);
|
|
memcpy(qs+app_qs_len, wsgi_req->path_info, wsgi_req->path_info_len);
|
|
if (wsgi_req->query_string_len > 0) {
|
|
char *ptr = qs+app_qs_len+wsgi_req->path_info_len;
|
|
*ptr = '&';
|
|
memcpy(ptr+1, wsgi_req->query_string, wsgi_req->query_string_len);
|
|
}
|
|
wsgi_req->query_string = qs;
|
|
wsgi_req->query_string_len = qs_len;
|
|
}
|
|
appready:
|
|
wsgi_req->path_info = "";
|
|
wsgi_req->path_info_len = 0;
|
|
goto secure2;
|
|
}
|
|
|
|
oldstyle:
|
|
|
|
filename = uwsgi_concat4n(wsgi_req->document_root, wsgi_req->document_root_len, "/", 1, wsgi_req->path_info, wsgi_req->path_info_len, "", 0);
|
|
|
|
if (uwsgi_php_walk(wsgi_req, filename, wsgi_req->document_root, wsgi_req->document_root_len, &path_info)) {
|
|
free(filename);
|
|
if (uphp.fallback) {
|
|
filename = uwsgi_str(uphp.fallback);
|
|
}
|
|
else {
|
|
uwsgi_404(wsgi_req);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
char *orig_path_info = wsgi_req->path_info;
|
|
uint16_t orig_path_info_len = wsgi_req->path_info_len;
|
|
|
|
if (path_info) {
|
|
wsgi_req->path_info = path_info;
|
|
wsgi_req->path_info_len = orig_path_info_len - (path_info - orig_path_info);
|
|
}
|
|
else {
|
|
wsgi_req->path_info = "";
|
|
wsgi_req->path_info_len = 0;
|
|
}
|
|
|
|
|
|
if (!realpath(filename, real_filename)) {
|
|
free(filename);
|
|
uwsgi_404(wsgi_req);
|
|
return -1;
|
|
}
|
|
|
|
free(filename);
|
|
real_filename_len = strlen(real_filename);
|
|
|
|
if (uphp.allowed_docroot) {
|
|
struct uwsgi_string_list *usl = uphp.allowed_docroot;
|
|
while(usl) {
|
|
if (!uwsgi_starts_with(real_filename, real_filename_len, usl->value, usl->len)) {
|
|
goto secure;
|
|
}
|
|
usl = usl->next;
|
|
}
|
|
uwsgi_403(wsgi_req);
|
|
uwsgi_log("PHP security error: %s is not under an allowed docroot\n", real_filename);
|
|
return -1;
|
|
}
|
|
|
|
secure:
|
|
|
|
if (stat(real_filename, &php_stat)) {
|
|
uwsgi_404(wsgi_req);
|
|
return UWSGI_OK;
|
|
}
|
|
|
|
if (S_ISDIR(php_stat.st_mode)) {
|
|
|
|
// add / to directories
|
|
if (orig_path_info_len == 0 || (orig_path_info_len > 0 && orig_path_info[orig_path_info_len-1] != '/')) {
|
|
wsgi_req->path_info = orig_path_info;
|
|
wsgi_req->path_info_len = orig_path_info_len;
|
|
uwsgi_redirect_to_slash(wsgi_req);
|
|
return UWSGI_OK;
|
|
}
|
|
struct uwsgi_string_list *upi = uphp.index;
|
|
real_filename[real_filename_len] = '/';
|
|
real_filename_len++;
|
|
int found = 0;
|
|
while(upi) {
|
|
if (real_filename_len + upi->len + 1 < PATH_MAX) {
|
|
// add + 1 to ensure null byte
|
|
memcpy(real_filename+real_filename_len, upi->value, upi->len + 1);
|
|
if (!access(real_filename, R_OK)) {
|
|
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
upi = upi->next;
|
|
}
|
|
|
|
if (!found) {
|
|
uwsgi_404(wsgi_req);
|
|
return UWSGI_OK;
|
|
}
|
|
|
|
real_filename_len = strlen(real_filename);
|
|
|
|
}
|
|
|
|
|
|
if (uphp.allowed_ext) {
|
|
struct uwsgi_string_list *usl = uphp.allowed_ext;
|
|
while(usl) {
|
|
if (real_filename_len >= usl->len) {
|
|
if (!uwsgi_strncmp(real_filename+(real_filename_len-usl->len), usl->len, usl->value, usl->len)) {
|
|
goto secure2;
|
|
}
|
|
}
|
|
usl = usl->next;
|
|
}
|
|
uwsgi_403(wsgi_req);
|
|
uwsgi_log("PHP security error: %s does not end with an allowed extension\n", real_filename);
|
|
return -1;
|
|
}
|
|
|
|
secure2:
|
|
|
|
wsgi_req->file = real_filename;
|
|
wsgi_req->file_len = strlen(wsgi_req->file);
|
|
|
|
if (uphp.allowed_scripts) {
|
|
struct uwsgi_string_list *usl = uphp.allowed_scripts;
|
|
while(usl) {
|
|
if (!uwsgi_strncmp(wsgi_req->file, wsgi_req->file_len, usl->value, usl->len)) {
|
|
goto secure3;
|
|
}
|
|
usl = usl->next;
|
|
}
|
|
uwsgi_403(wsgi_req);
|
|
uwsgi_log("PHP security error: %s is not an allowed script\n", real_filename);
|
|
return -1;
|
|
}
|
|
|
|
secure3:
|
|
|
|
if (wsgi_req->document_root[wsgi_req->document_root_len-1] == '/') {
|
|
wsgi_req->script_name = real_filename + (wsgi_req->document_root_len-1);
|
|
}
|
|
else {
|
|
wsgi_req->script_name = real_filename + wsgi_req->document_root_len;
|
|
}
|
|
|
|
wsgi_req->script_name_len = strlen(wsgi_req->script_name);
|
|
|
|
#ifdef UWSGI_DEBUG
|
|
uwsgi_log("php filename = %s script_name = %.*s (%d) document_root = %.*s (%d)\n", real_filename, wsgi_req->script_name_len, wsgi_req->script_name, wsgi_req->script_name_len,
|
|
wsgi_req->document_root_len, wsgi_req->document_root, wsgi_req->document_root_len);
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// now check for allowed paths and extensions
|
|
|
|
SG(request_info).request_uri = estrndup(wsgi_req->uri, wsgi_req->uri_len);
|
|
SG(request_info).request_method = estrndup(wsgi_req->method, wsgi_req->method_len);
|
|
SG(request_info).proto_num = 1001;
|
|
|
|
SG(request_info).query_string = estrndup(wsgi_req->query_string, wsgi_req->query_string_len);
|
|
SG(request_info).content_length = wsgi_req->post_cl;
|
|
SG(request_info).content_type = estrndup(wsgi_req->content_type, wsgi_req->content_type_len);
|
|
|
|
// reinitialize it at every request !!!
|
|
SG(sapi_headers).http_response_code = 200;
|
|
|
|
SG(request_info).path_translated = wsgi_req->file;
|
|
|
|
file_handle.type = ZEND_HANDLE_FILENAME;
|
|
file_handle.filename = real_filename;
|
|
file_handle.free_filename = 0;
|
|
file_handle.opened_path = NULL;
|
|
|
|
if (php_request_startup(TSRMLS_C) == FAILURE) {
|
|
uwsgi_500(wsgi_req);
|
|
return -1;
|
|
}
|
|
|
|
php_execute_script(&file_handle TSRMLS_CC);
|
|
|
|
php_request_shutdown(NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void uwsgi_php_after_request(struct wsgi_request *wsgi_req) {
|
|
|
|
log_request(wsgi_req);
|
|
}
|
|
|
|
|
|
SAPI_API struct uwsgi_plugin php_plugin = {
|
|
.name = "php",
|
|
.modifier1 = 14,
|
|
.init = uwsgi_php_init,
|
|
.request = uwsgi_php_request,
|
|
.after_request = uwsgi_php_after_request,
|
|
.options = uwsgi_php_options,
|
|
};
|
|
|