On September 14, 2012 15:25 , PanaColina <panama.hill@xxxxxxxxx> 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} -dRewriteRule ^/?(.*)$ 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 mark@xxxxxxxxxxx
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 {
--------------------------------------------------------------------- To unsubscribe, e-mail: users-unsubscribe@xxxxxxxxxxxxxxxx For additional commands, e-mail: users-help@xxxxxxxxxxxxxxxx