On 2014-12-04 13:27, Eric Covener wrote:
On Thu, Dec 4, 2014 at 1:11 PM, Jim Riggs <[email protected]> wrote:
This all may certainly be true, but I just for clarity's sake (since it was my quote
that started this new mod_proxy_fcgi thread), my mod_proxy_balancer ->
mod_proxy_fcgi -> php-fpm issue is NOT an httpd issue...at least that is not how I
have treated it. It is actually a code fix I have had to make in PHP to get it to
work.
[...] It doesn't seem that usable values for these things should be so unique
to php-fpm.
My experience has been that the PHP FPM SAPI function
init_request_info() in sapi/fpm/fpm/fpm_main.c, which I think was
originally copied from the CGI SAPI, is very old code that goes to great
lengths to preserve old, not always standards-compliant behavior in
order to avoid breaking backward compatibilities. Hence, I'm not
convinced that the things Eric refers to above might not be unique to
php-fpm.
After struggling to get php-fpm working with mod_proxy_fcgi, I
eventually completely rewrote the whole init_request_info function the
way I thought it "should be" without any regards to backwards
compatibility; this solved the problems I was having.
If memory serves (it's been a few years) the main problems I was
encountering were with serving the index file for directories and
correct handling of PATH_INFO.
I've attached the patch I'm using (a completely new version of the
init_request_info function) in case anyone wants to either play with it
or compare it to the code that PHP currently uses.
--
Mark Montague
[email protected]
diff -up php-5.6.3/sapi/fpm/fpm/fastcgi.c.fpm-init-request
php-5.6.3/sapi/fpm/fpm/fastcgi.c
--- php-5.6.3/sapi/fpm/fpm/fastcgi.c.fpm-init-request 2014-11-18
20:33:20.313769152 +0000
+++ php-5.6.3/sapi/fpm/fpm/fastcgi.c 2014-11-18 20:33:38.424369147 +0000
@@ -488,6 +488,7 @@ static int fcgi_get_params(fcgi_request
ret = 0;
break;
}
+ zlog(ZLOG_DEBUG, "fcgi_get_params: %s=%s", tmp, s);
zend_hash_update(req->env, tmp, eff_name_len+1, &s,
sizeof(char*), NULL);
p += name_len + val_len;
}
@@ -1093,12 +1094,14 @@ char* fcgi_putenv(fcgi_request *req, cha
{
if (var && req) {
if (val == NULL) {
+ zlog(ZLOG_DEBUG, "fcgi_putenv: %s=", var);
zend_hash_del(req->env, var, var_len+1);
} else {
char **ret;
val = estrdup(val);
if (zend_hash_update(req->env, var, var_len+1, &val,
sizeof(char*), (void**)&ret) == SUCCESS) {
+ zlog(ZLOG_DEBUG, "fcgi_putenv: %s=%s", var,
val);
return *ret;
}
}
diff -up php-5.6.3/sapi/fpm/fpm/fpm_main.c.fpm-init-request
php-5.6.3/sapi/fpm/fpm/fpm_main.c
--- php-5.6.3/sapi/fpm/fpm/fpm_main.c.fpm-init-request 2014-11-12
13:52:21.000000000 +0000
+++ php-5.6.3/sapi/fpm/fpm/fpm_main.c 2014-11-18 20:33:38.425369123 +0000
@@ -1422,6 +1422,317 @@ static void init_request_info(TSRMLS_D)
}
/* }}} */
+static char *fpm_cgibin_saveenv(char *name, char *val)
+{
+ int name_len = strlen(name);
+ char *old_val = sapi_cgibin_getenv(name, name_len TSRMLS_CC);
+ char save_name[256];
+
+ if (val != NULL && old_val != NULL && strcmp(val, old_val) == 0) {
+ return old_val;
+ }
+
+ if (name_len < 256 - strlen("ORIG_") - 1) {
+ strcpy(save_name, "ORIG_");
+ strcat(save_name, name);
+ } else {
+ save_name[0] = '\0';
+ }
+
+ /* Save the old value only if one was not previously saved */
+ if (old_val && save_name[0] != '\0' &&
+ sapi_cgibin_getenv(save_name, strlen(save_name) TSRMLS_CC) ==
NULL) {
+ _sapi_cgibin_putenv(save_name, old_val TSRMLS_CC);
+ }
+
+ return _sapi_cgibin_putenv(name, val TSRMLS_CC);
+
+}
+
+static void init_request_info0(TSRMLS_D)
+{
+ char *document_root;
+ int document_root_len;
+ char *script_filename;
+ int script_filename_len;
+ char *script_filename_part = NULL;
+ char *s = NULL;
+ char *path = NULL;
+ char *path_info = NULL;
+ char *path_translated = NULL;
+ char *content_type;
+ char *content_length;
+ const char *auth;
+ char *ini;
+ int result;
+ struct stat st;
+ int add_index = 0;
+
+ zlog(ZLOG_DEBUG, "initializing request info:");
+
+ /* initialize the defaults */
+ SG(request_info).path_translated = NULL;
+ SG(request_info).request_method = NULL;
+ SG(request_info).proto_num = 1000;
+ SG(request_info).query_string = NULL;
+ SG(request_info).request_uri = NULL;
+ SG(request_info).content_type = NULL;
+ SG(request_info).content_length = 0;
+ SG(sapi_headers).http_response_code = 200;
+
+ /*
+ * Use our document root instead of one passed to us by our invoker.
+ * (If we're being invoked through a proxy, DOCUMENT_ROOT will be the
+ * proxy's document root, which is likely different from ours).
+ */
+ if (PG(doc_root)) {
+ document_root = fpm_cgibin_saveenv("DOCUMENT_ROOT", PG(doc_root));
+ } else {
+ document_root = sapi_cgibin_getenv("DOCUMENT_ROOT",
+ sizeof("DOCUMENT_ROOT") - 1 TSRMLS_CC);
+ }
+ /* remove any trailing slash */
+ document_root_len = (document_root != NULL ? strlen(document_root) : 0);
+ if (document_root_len > 1 &&
+ document_root[document_root_len-1] == '/') {
+ document_root[document_root_len-1] = '\0';
+ }
+ /*TRANSLATE_SLASHES(document_root);*/ /* TODO */
+
+ zlog(ZLOG_DEBUG, "finding script...");
+ script_filename = sapi_cgibin_getenv("SCRIPT_FILENAME",
+ sizeof("SCRIPT_FILENAME") - 1 TSRMLS_CC);
+
+ /* Fix proxy URLs in SCRIPT_FILENAME generated by Apache mod_proxy_fcgi:
+ * proxy:fcgi://localhost:9000/some-dir/info.php/test
+ * should be changed to:
+ * /some-dir/info.php/test
+ * See: http://bugs.php.net/bug.php?id=54152
+ * https://issues.apache.org/bugzilla/show_bug.cgi?id=50851
+ */
+#define APACHE_PROXY_FCGI_PREFIX "proxy:fcgi://"
+ if (script_filename &&
+ strncasecmp(script_filename, APACHE_PROXY_FCGI_PREFIX,
+ sizeof(APACHE_PROXY_FCGI_PREFIX) - 1) == 0) {
+ /* advance to first character of hostname */
+ char *p = script_filename + (sizeof(APACHE_PROXY_FCGI_PREFIX) - 1);
+ while (*p != '\0' && *p != '/') {
+ p++; /* move past hostname and port */
+ }
+ if (*p != '\0') {
+ /* Copy path portion in place to avoid memory leak. */
+ memmove(script_filename, p, strlen(p) + 1);
+ }
+ }
+
+ if (!script_filename || *script_filename != '/') {
+ /* This shouldn't ever happen, but just in case... */
+ zlog(ZLOG_ERROR, "got bad SCRIPT_FILENAME");
+ SG(sapi_headers).http_response_code = 404;
+ goto init_request_info_error_cleanup;
+ }
+
+ /* Remove any query string or fragment that may be present. (Apache
+ * HTTP Server 2.4.x will include a query string component in
+ * SCRIPT_FILENAME when the request is proxied via a RewriteRule with
+ * the [P] flag and a query string is present in the original request;
+ * this is apparently not a bug because there is no standard that
+ * specifies what can and cannot be in SCRIPT_FILENAME.)
+ */
+ strtok(script_filename, "?#"); /* discard query and/or fragment parts */
+
+ /* TODO: bail if ../ in script_filename takes us above document root */
+
+ /*
+ * Try to separate script_filename into the actual filename (relative to
+ * the document root directory) plus a PATH_INFO component.
+ *
+ * (If we're being invoked through a proxy server, the proxy server
+ * won't be able to set PATH_INFO correctly since it can't walk our
+ * filesystem; so we try to determine PATH_INFO ourselves, even if
+ * PATH_INFO has already been set for us).
+ *
+ */
+
+ script_filename_len = strlen(script_filename);
+ script_filename_part = estrdup(script_filename);
+ if (script_filename_part == NULL) {
+ zlog(ZLOG_ERROR, "can't allocate memory");
+ SG(sapi_headers).http_response_code = 503;
+ goto init_request_info_error_cleanup;
+ }
+
+ /* remove any trailing slash */
+ if (script_filename_part[script_filename_len - 1] == '/') {
+ script_filename_part[script_filename_len - 1] = '\0';
+ }
+
+#define INDEX_FILENAME "/index.php"
+ path = emalloc(sizeof(char) * (strlen(document_root) +
+ script_filename_len + sizeof(INDEX_FILENAME)));
+ if (path == NULL) {
+ zlog(ZLOG_ERROR, "can't allocate memory");
+ SG(sapi_headers).http_response_code = 503;
+ goto init_request_info_error_cleanup;
+ }
+
+ while (1) {
+ if (document_root_len > 1 && *document_root == '/') {
+ strcpy(path, document_root);
+ strcat(path, script_filename_part);
+ } else {
+ strcpy(path, script_filename_part);
+ if (*path != '/') {
+ path[0] = '/';
+ path[1] = '\0';
+ }
+ }
+ result = stat(path, &st);
+ zlog(ZLOG_DEBUG, "stat %s (%d, %s)", path, result, strerror(errno));
+ if (result == 0 && S_ISREG(st.st_mode)) {
+ zlog(ZLOG_DEBUG, "found it! script=%s pathinfo=%s\n", path,
+ script_filename + strlen(script_filename_part));
+ break;
+ }
+ if (result == 0 && S_ISDIR(st.st_mode)) {
+ strcat(path, INDEX_FILENAME);
+ result = stat(path, &st);
+ zlog(ZLOG_DEBUG, "..stat %s (%d, %s)", path, result,
strerror(errno));
+ if (result == 0 && S_ISREG(st.st_mode)) {
+ /* TODO: error if pathinfo is non-zero length ? */
+ zlog(ZLOG_DEBUG, "..found it! script=%s pathinfo=%s\n", path,
+ script_filename + strlen(script_filename_part));
+ add_index = 1;
+ break;
+ }
+ }
+ s = strrchr(script_filename_part, '/');
+ if (s == NULL) {
+ /* can't find the script, bail out */
+ zlog(ZLOG_DEBUG, "script not found, bailing");
+ strcpy(script_filename_part, script_filename);
+ strcpy(path, script_filename);
+ SG(sapi_headers).http_response_code = 404;
+ break;
+ }
+ *s = '\0';
+ }
+
+ zlog(ZLOG_DEBUG, "found script %s -> %s", script_filename_part, path);
+
+ if (add_index) {
+ s = emalloc(sizeof(char) * (strlen(script_filename_part) +
+ sizeof(INDEX_FILENAME)));
+ if (s == NULL) {
+ zlog(ZLOG_ERROR, "can't allocate memory");
+ SG(sapi_headers).http_response_code = 503;
+ goto init_request_info_error_cleanup;
+ }
+ strcpy(s, script_filename_part);
+ strcat(s, INDEX_FILENAME);
+ SG(request_info).request_uri = fpm_cgibin_saveenv("SCRIPT_NAME", s);
+ efree(s);
+ } else {
+ SG(request_info).request_uri = fpm_cgibin_saveenv("SCRIPT_NAME",
+ script_filename_part);
+ }
+ zlog(ZLOG_DEBUG, "set SG(request_info).request_uri=%s",
+ SG(request_info).request_uri);
+
+ path_info = estrdup(script_filename + strlen(script_filename_part));
+ if (path_info == NULL) {
+ zlog(ZLOG_ERROR, "can't allocate memory");
+ SG(sapi_headers).http_response_code = 503;
+ goto init_request_info_error_cleanup;
+ }
+
+ script_filename = path;
+ fpm_cgibin_saveenv("SCRIPT_FILENAME", script_filename);
+
+ if (is_valid_path(script_filename)) {
+ /* Bizarrely, this has nothing to do with PATH_TRANSLATED */
+ SG(request_info).path_translated = estrdup(script_filename);
+ zlog(ZLOG_DEBUG, "set SG(request_info).path_translated=%s",
+ script_filename);
+ if (SG(request_info).path_translated == NULL) {
+ zlog(ZLOG_ERROR, "can't allocate memory");
+ SG(sapi_headers).http_response_code = 503;
+ goto init_request_info_error_cleanup;
+ }
+ } else {
+ zlog(ZLOG_DEBUG, "not setting SG(request_info).path_translated");
+ }
+
+ if (*path_info != '\0') {
+ fpm_cgibin_saveenv("PATH_INFO", path_info);
+ /* PATH_TRANSLATED = DOCUMENT_ROOT + PATH_INFO */
+ path_translated = emalloc(sizeof(char) * (strlen(document_root) +
+ strlen(path_info) + 1));
+ if (path_translated == NULL) {
+ zlog(ZLOG_ERROR, "can't allocate memory");
+ SG(sapi_headers).http_response_code = 503;
+ goto init_request_info_error_cleanup;
+ }
+ strcpy(path_translated, document_root);
+ strcat(path_translated, path_info);
+ } else {
+ fpm_cgibin_saveenv("PATH_INFO", NULL);
+ fpm_cgibin_saveenv("PATH_TRANSLATED", NULL);
+ }
+
+ SG(request_info).request_method = sapi_cgibin_getenv("REQUEST_METHOD",
+ sizeof("REQUEST_METHOD") - 1 TSRMLS_CC);
+ /* FIXME - Work out proto_num here */
+ SG(request_info).query_string = sapi_cgibin_getenv("QUERY_STRING",
+ sizeof("QUERY_STRING") - 1 TSRMLS_CC);
+
+ content_type = sapi_cgibin_getenv("CONTENT_TYPE",
+ sizeof("CONTENT_TYPE") - 1 TSRMLS_CC);
+ content_length = sapi_cgibin_getenv("CONTENT_LENGTH",
+ sizeof("CONTENT_LENGTH") - 1 TSRMLS_CC);
+ SG(request_info).content_type = (content_type ? content_type : "");
+ SG(request_info).content_length = (content_length ?
+ atoi(content_length) : 0);
+
+ /* The CGI RFC allows servers to pass on unvalidated Authorization data */
+ auth = sapi_cgibin_getenv("HTTP_AUTHORIZATION",
+ sizeof("HTTP_AUTHORIZATION") - 1 TSRMLS_CC);
+ php_handle_auth_data(auth TSRMLS_CC);
+
+ /* INI stuff */
+ ini = sapi_cgibin_getenv("PHP_VALUE", sizeof("PHP_VALUE")-1 TSRMLS_CC);
+ if (ini) {
+ int mode = ZEND_INI_USER;
+ char *tmp;
+ spprintf(&tmp, 0, "%s\n", ini);
+ zend_parse_ini_string(tmp, 1, ZEND_INI_SCANNER_NORMAL,
+ (zend_ini_parser_cb_t) fastcgi_ini_parser, &mode TSRMLS_CC);
+ efree(tmp);
+ }
+
+ ini = sapi_cgibin_getenv("PHP_ADMIN_VALUE",
+ sizeof("PHP_ADMIN_VALUE")-1 TSRMLS_CC);
+ if (ini) {
+ int mode = ZEND_INI_SYSTEM;
+ char *tmp;
+ spprintf(&tmp, 0, "%s\n", ini);
+ zend_parse_ini_string(tmp, 1, ZEND_INI_SCANNER_NORMAL,
+ (zend_ini_parser_cb_t) fastcgi_ini_parser, &mode TSRMLS_CC);
+ efree(tmp);
+ }
+
+ zlog(ZLOG_DEBUG, "ok: uri=%s filename=%s", SG(request_info).request_uri,
script_filename);
+
+init_request_info_error_cleanup:
+ if (path_translated) { efree(path_translated); }
+ if (path_info) { efree(path_info); }
+ if (path) { efree(path); }
+ if (script_filename_part) { efree(script_filename_part); }
+
+ return;
+
+}
+
static void fastcgi_ini_parser(zval *arg1, zval *arg2, zval *arg3, int
callback_type, void *arg TSRMLS_DC) /* {{{ */
{
int *mode = (int *)arg;
@@ -1887,7 +2198,7 @@ consult the installation file that came
while (fcgi_accept_request(&request) >= 0) {
request_body_fd = -1;
SG(server_context) = (void *) &request;
- init_request_info(TSRMLS_C);
+ init_request_info0(TSRMLS_C);
CG(interactive) = 0;
char *primary_script = NULL;
@@ -1915,7 +2226,7 @@ consult the installation file that came
/* If path_translated is NULL, terminate here with a
404 */
if (!SG(request_info).path_translated) {
zend_try {
- zlog(ZLOG_DEBUG, "Primary script
unknown");
+ zlog(ZLOG_DEBUG, "Primary script
unknown (path_translated not set)");
SG(sapi_headers).http_response_code =
404;
PUTS("File not found.\n");
} zend_catch {
@@ -1944,6 +2255,7 @@ consult the installation file that came
PUTS("Access denied.\n");
} else {
SG(sapi_headers).http_response_code = 404;
+ zlog(ZLOG_ERROR, "returning 404
because php_fopen_primary_script() failed");
PUTS("No input file
specified.\n");
}
} zend_catch {