Thank you for your response. I had assumed that there was some mechanism
either within Apache HTTP Server or within PHP-FPM for handling a URI
request that corresponds to a directory. It just seemed like such a basic
requirement, to me.

I understand your point about a RewriteRule for matching directories,
although I have not been able to come up with a regex that would match any
directory at any depth, eg: /subdir, /subdir/subsubdir etc., but I am no
regex expert. I don't think a separate RewriteRule for each subdirectory
would be reasonable.

I tried your patch (thank you) on php-5.4.6 and php-5.4.7 without success.
Running the patch, there was a small offset in each case (8 lines), but
building failed:

/usr/local/src/php-5.4.6/sapi/fpm/fpm/fpm_main.c: In function
‘fpm_cgibin_saveenv’:
/usr/local/src/php-5.4.6/sapi/fpm/fpm/fpm_main.c:1388: error: ‘tsrm_ls’
undeclared (first use in this function)
/usr/local/src/php-5.4.6/sapi/fpm/fpm/fpm_main.c:1388: error: (Each
undeclared identifier is reported only once
/usr/local/src/php-5.4.6/sapi/fpm/fpm/fpm_main.c:1388: error: for each
function it appears in.)

I assumed I could simply declare tsrm_ls at the beginning of the function
(obviously, I am not a programmer). While I tried several basic ideas for
declaring it, and each one allowed php to compile without a build error,
php-fpm would not start after it was installed.

I appreciate this functionality is not necessary for every website, but I
am still surprised there is no real support for this, given that php-fpm is
not considered experimental as of httpd-2.4.

Regards,

J.L. Hill

On Sun, Sep 16, 2012 at 4:44 PM, Mark Montague <m...@catseye.org> wrote:

> On September 14, 2012 15:25 , PanaColina <panama.h...@gmail.com> wrote:
>
>> When a directory request is received, apache sends only the directory
>> name to the proxy fcgi, not the file name, even though the DirectoryIndex
>> directive is set (and working correctly in a basic config).  A critical
>> problem as http://www.example.com gives "file not found" or "no input
>> file specified" errors.
>>
>> This seems to be an issue with DirectoryIndex and apache rather than
>> something specific to the proxy module. I am guessing the problem is that
>> DirectoryIndex is bypassed when a proxy is included. I have tried putting
>> "DirectoryIndex index.php" in every possible location, testing with
>> ProxyPass, ProxyPassMatch, and ReWrite rules in various sections (Location,
>> Directory, VirtualHost) -- all tests having same problem.
>>
>> Testing with apache 2.4, I can always get apache to serve regular files
>> to the proxy, but not the specified DirectoryIndex file (index.htm or
>> index.php). For the root directory, it sends an empty string: [...]
>>
>>
>> Every test has had the same problem with DirectoryIndex.
>>
>
> DirectoryIndex won't work for you in this case.  DirectoryIndex is only
> used when Apache HTTP Server is serving files from the filesystem in
> response to requests.  If httpd sees that the URI maps onto a directory in
> the filesystem, it will look for the files specified by the DirectoryIndex
> directive and, if present, serve the first of those that it finds instead
> of automatically generating a directory index which lists everything in the
> directory.  What you are doing, though, is saying (via the Location and
> ProxyPass directives), "when a URI falls beneath /, proxy it via
> mod_proxy_fcgi through the socket /usr/local/php-fpm/var/run/**fpm-php.sock
> and let the back-end server interpret and handle the request."  In short,
> httpd is functioning correctly here, and it's the responsibility of php-fpm
> in this case to figure out what do to with a URI request that corresponds
> to a directory.
>
> But, as it turns out, php-fpm doesn't do anything special when given a URI
> that corresponds to a directory.  Specifically, it neither looks for index
> files, nor does it automatically generate a index listing of the contents
> of the directory.
>
> The solution I suggest for you is to use a URL rewriting rule to check to
> see if the URI path of the request maps onto a directory under the document
> root used by the back-end server (php-fpm).  If it does, then add
> "/index.php" to the end of the request when proxying it to the back-end
> server.  For example, replace the entire <Location> stanza you currently
> have with something like:
>
> <Directory /usr/local/apache/www>
>         Require all granted
>
>         # Proxy PHP files to php-fpm:
>         RewriteRule ^/?(.*\.php)$ fcgi://socket=%2fusr%2flocal%**
> 2fphp-fpm%2fvar%2frun%2ffpm-**php.sock/usr/local/apache/www/**$1 [P,L]
>
>         # Proxy requests for directories to php-fpm, adding "index.php" to
> the end of them:
>         RewriteCond %{REQUEST_FILENAME} -d
>         RewriteRule ^/?(.*)$ fcgi://socket=%2fusr%2flocal%**
> 2fphp-fpm%2fvar%2frun%2ffpm-**php.sock/usr/local/apache/www/**$1index.php
> [P,L]
>
> </Directory>
>
>
> I've not tried this, and it may require some tweaking before it works.
>  For example, the above should work for requests for directories where the
> URI path ends in a slash (e.g., "/test/dir/") but will likely fail when the
> URI path lacks the slash ("/test/dir").  But it has the advantage of only
> proxying requests for PHP files, letting httpd serve requests for static
> files (images, JavaScript, CSS) itself, which will be much more efficient
> than proxying the requests for static files through php-fpm.
>
> As far as my own situation goes, I've chosen a different way to get around
> php-fpm not supporting default resource files for directories.  I've
> modified the PHP source code to automatically try adding "/index.php" to
> the end of the URI path if it maps to a directory.  I've attached this
> patch in case anyone is interested, but it's not complete yet in terms of
> functionality (a lot more work remains to be done), and it's a complete and
> radical re-implementation of how PHP interprets the CGI standard which
> completely throws away what PHP currently does both with cgi.fix_pathinfo=1
> and cgi.fix_pathinfo=0 and replaces it all with what I personally believe
> PHP "should" do according to RFC 3875 with no regard for any sort of
> backward compatibility.  Feedback is welcome.
>
> --
>   Mark Montague
>   m...@catseye.org
>
>
> diff -up php-5.4.6/sapi/fpm/fpm/fastcgi.c.fpm-init-request
> php-5.4.6/sapi/fpm/fpm/fastcgi.c
> --- php-5.4.6/sapi/fpm/fpm/fastcgi.c.fpm-init-request   2012-08-15
> 04:26:05.000000000 +0000
> +++ php-5.4.6/sapi/fpm/fpm/fastcgi.c    2012-09-15 02:21:14.407835190+0000
> @@ -482,6 +482,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;
>         }
> @@ -1063,12 +1064,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.4.6/sapi/fpm/fpm/fpm_main.c.fpm-init-request
> php-5.4.6/sapi/fpm/fpm/fpm_main.c
> --- php-5.4.6/sapi/fpm/fpm/fpm_main.c.fpm-init-request  2012-09-15
> 02:21:14.396835046 +0000
> +++ php-5.4.6/sapi/fpm/fpm/fpm_main.c   2012-09-15 04:15:34.356883669 +0000
> @@ -1382,6 +1382,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;
> @@ -1837,7 +2148,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;
>
> @@ -1865,7 +2176,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 {
> @@ -1894,6 +2205,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