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 { > >