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 {

Reply via email to