diff options
Diffstat (limited to 'sapi/fpm')
30 files changed, 1912 insertions, 85 deletions
diff --git a/sapi/fpm/fpm/fastcgi.c b/sapi/fpm/fpm/fastcgi.c index d77b6f8ca..d2764a59b 100644 --- a/sapi/fpm/fpm/fastcgi.c +++ b/sapi/fpm/fpm/fastcgi.c @@ -137,13 +137,14 @@ typedef union _sa_t { struct sockaddr sa; struct sockaddr_un sa_unix; struct sockaddr_in sa_inet; + struct sockaddr_in6 sa_inet6; } sa_t; static HashTable fcgi_mgmt_vars; static int is_initialized = 0; static int in_shutdown = 0; -static in_addr_t *allowed_clients = NULL; +static sa_t *allowed_clients = NULL; static sa_t client_sa; @@ -266,15 +267,23 @@ void fcgi_set_allowed_clients(char *ip) *end = 0; end++; } - allowed_clients[n] = inet_addr(cur); - if (allowed_clients[n] == INADDR_NONE) { + if (inet_pton(AF_INET, cur, &allowed_clients[n].sa_inet.sin_addr)>0) { + allowed_clients[n].sa.sa_family = AF_INET; + n++; + } else if (inet_pton(AF_INET6, cur, &allowed_clients[n].sa_inet6.sin6_addr)>0) { + allowed_clients[n].sa.sa_family = AF_INET6; + n++; + } else { zlog(ZLOG_ERROR, "Wrong IP address '%s' in listen.allowed_clients", cur); } - n++; cur = end; } - allowed_clients[n] = INADDR_NONE; + allowed_clients[n].sa.sa_family = 0; free(ip); + if (!n) { + zlog(ZLOG_ERROR, "There are no allowed addresses for this pool"); + /* don't clear allowed_clients as it will create an "open for all" security issue */ + } } } @@ -759,6 +768,43 @@ void fcgi_close(fcgi_request *req, int force, int destroy) } } +static int fcgi_is_allowed() { + int i; + + if (client_sa.sa.sa_family == AF_UNIX) { + return 1; + } + if (!allowed_clients) { + return 1; + } + if (client_sa.sa.sa_family == AF_INET) { + for (i=0 ; allowed_clients[i].sa.sa_family ; i++) { + if (allowed_clients[i].sa.sa_family == AF_INET + && !memcmp(&client_sa.sa_inet.sin_addr, &allowed_clients[i].sa_inet.sin_addr, 4)) { + return 1; + } + } + } + if (client_sa.sa.sa_family == AF_INET6) { + for (i=0 ; allowed_clients[i].sa.sa_family ; i++) { + if (allowed_clients[i].sa.sa_family == AF_INET6 + && !memcmp(&client_sa.sa_inet6.sin6_addr, &allowed_clients[i].sa_inet6.sin6_addr, 12)) { + return 1; + } +#ifdef IN6_IS_ADDR_V4MAPPED + if (allowed_clients[i].sa.sa_family == AF_INET + && IN6_IS_ADDR_V4MAPPED(&client_sa.sa_inet6.sin6_addr) + && !memcmp(((char *)&client_sa.sa_inet6.sin6_addr)+12, &allowed_clients[i].sa_inet.sin_addr, 4)) { + return 1; + } +#endif + } + } + + zlog(ZLOG_ERROR, "Connection disallowed: IP address '%s' has been dropped.", fcgi_get_last_client_ip()); + return 0; +} + int fcgi_accept_request(fcgi_request *req) { #ifdef _WIN32 @@ -809,23 +855,10 @@ int fcgi_accept_request(fcgi_request *req) FCGI_UNLOCK(req->listen_socket); client_sa = sa; - if (sa.sa.sa_family == AF_INET && req->fd >= 0 && allowed_clients) { - int n = 0; - int allowed = 0; - - while (allowed_clients[n] != INADDR_NONE) { - if (allowed_clients[n] == sa.sa_inet.sin_addr.s_addr) { - allowed = 1; - break; - } - n++; - } - if (!allowed) { - zlog(ZLOG_ERROR, "Connection disallowed: IP address '%s' has been dropped.", inet_ntoa(sa.sa_inet.sin_addr)); - closesocket(req->fd); - req->fd = -1; - continue; - } + if (req->fd >= 0 && !fcgi_is_allowed()) { + closesocket(req->fd); + req->fd = -1; + continue; } } @@ -1094,12 +1127,27 @@ void fcgi_free_mgmt_var_cb(void * ptr) pefree(*var, 1); } -char *fcgi_get_last_client_ip() /* {{{ */ +const char *fcgi_get_last_client_ip() /* {{{ */ { - if (client_sa.sa.sa_family == AF_UNIX) { - return NULL; + static char str[INET6_ADDRSTRLEN]; + + /* Ipv4 */ + if (client_sa.sa.sa_family == AF_INET) { + return inet_ntop(client_sa.sa.sa_family, &client_sa.sa_inet.sin_addr, str, INET6_ADDRSTRLEN); + } +#ifdef IN6_IS_ADDR_V4MAPPED + /* Ipv4-Mapped-Ipv6 */ + if (client_sa.sa.sa_family == AF_INET6 + && IN6_IS_ADDR_V4MAPPED(&client_sa.sa_inet6.sin6_addr)) { + return inet_ntop(AF_INET, ((char *)&client_sa.sa_inet6.sin6_addr)+12, str, INET6_ADDRSTRLEN); } - return inet_ntoa(client_sa.sa_inet.sin_addr); +#endif + /* Ipv6 */ + if (client_sa.sa.sa_family == AF_INET6) { + return inet_ntop(client_sa.sa.sa_family, &client_sa.sa_inet6.sin6_addr, str, INET6_ADDRSTRLEN); + } + /* Unix socket */ + return NULL; } /* }}} */ /* diff --git a/sapi/fpm/fpm/fastcgi.h b/sapi/fpm/fpm/fastcgi.h index 34f9eef9d..f5cfe9f66 100644 --- a/sapi/fpm/fpm/fastcgi.h +++ b/sapi/fpm/fpm/fastcgi.h @@ -133,7 +133,7 @@ int fcgi_flush(fcgi_request *req, int close); void fcgi_set_mgmt_var(const char * name, size_t name_len, const char * value, size_t value_len); void fcgi_free_mgmt_var_cb(void * ptr); -char *fcgi_get_last_client_ip(); +const char *fcgi_get_last_client_ip(); /* * Local variables: diff --git a/sapi/fpm/fpm/fpm_conf.c b/sapi/fpm/fpm/fpm_conf.c index 34e048010..1f73bc915 100644 --- a/sapi/fpm/fpm/fpm_conf.c +++ b/sapi/fpm/fpm/fpm_conf.c @@ -819,7 +819,7 @@ static int fpm_conf_process_all_pools() /* {{{ */ if (config->pm_start_servers <= 0) { config->pm_start_servers = config->pm_min_spare_servers + ((config->pm_max_spare_servers - config->pm_min_spare_servers) / 2); - zlog(ZLOG_WARNING, "[pool %s] pm.start_servers is not set. It's been set to %d.", wp->config->name, config->pm_start_servers); + zlog(ZLOG_NOTICE, "[pool %s] pm.start_servers is not set. It's been set to %d.", wp->config->name, config->pm_start_servers); } else if (config->pm_start_servers < config->pm_min_spare_servers || config->pm_start_servers > config->pm_max_spare_servers) { zlog(ZLOG_ALERT, "[pool %s] pm.start_servers(%d) must not be less than pm.min_spare_servers(%d) and not greater than pm.max_spare_servers(%d)", wp->config->name, config->pm_start_servers, config->pm_min_spare_servers, config->pm_max_spare_servers); @@ -1159,6 +1159,7 @@ static int fpm_conf_post_process(int force_daemon TSRMLS_DC) /* {{{ */ } fpm_globals.log_level = fpm_global_config.log_level; + zlog_set_level(fpm_globals.log_level); if (fpm_global_config.process_max < 0) { zlog(ZLOG_ERROR, "process_max can't be negative"); @@ -1199,15 +1200,15 @@ static int fpm_conf_post_process(int force_daemon TSRMLS_DC) /* {{{ */ return -1; } - if (0 > fpm_log_open(0)) { + if (0 > fpm_event_pre_init(fpm_global_config.events_mechanism)) { return -1; } - if (0 > fpm_event_pre_init(fpm_global_config.events_mechanism)) { + if (0 > fpm_conf_process_all_pools()) { return -1; } - if (0 > fpm_conf_process_all_pools()) { + if (0 > fpm_log_open(0)) { return -1; } @@ -1256,7 +1257,7 @@ static void fpm_conf_ini_parser_include(char *inc, void *arg TSRMLS_DC) /* {{{ * #ifdef HAVE_GLOB { g.gl_offs = 0; - if ((i = glob(inc, GLOB_ERR | GLOB_MARK | GLOB_NOSORT, NULL, &g)) != 0) { + if ((i = glob(inc, GLOB_ERR | GLOB_MARK, NULL, &g)) != 0) { #ifdef GLOB_NOMATCH if (i == GLOB_NOMATCH) { zlog(ZLOG_WARNING, "Nothing matches the include pattern '%s' from %s at line %d.", inc, filename, ini_lineno); diff --git a/sapi/fpm/fpm/fpm_env.c b/sapi/fpm/fpm/fpm_env.c index 2ff0bdc0e..3bdb34634 100644 --- a/sapi/fpm/fpm/fpm_env.c +++ b/sapi/fpm/fpm/fpm_env.c @@ -212,7 +212,7 @@ int fpm_env_init_main() /* {{{ */ #ifndef HAVE_SETPROCTITLE #ifdef __linux__ /* - * This piece of code has been inspirated from nginx and pureftpd code, whic + * This piece of code has been inspirated from nginx and pureftpd code, which * are under BSD licence. * * To change the process title in Linux we have to set argv[1] to NULL diff --git a/sapi/fpm/fpm/fpm_log.c b/sapi/fpm/fpm/fpm_log.c index 4e1a057db..b0bf32ac1 100644 --- a/sapi/fpm/fpm/fpm_log.c +++ b/sapi/fpm/fpm/fpm_log.c @@ -46,6 +46,8 @@ int fpm_log_open(int reopen) /* {{{ */ if (0 > fd) { zlog(ZLOG_SYSERROR, "failed to open access log (%s)", wp->config->access_log); return -1; + } else { + zlog(ZLOG_DEBUG, "open access log (%s)", wp->config->access_log); } if (reopen) { @@ -367,7 +369,7 @@ int fpm_log_write(char *log_format TSRMLS_DC) /* {{{ */ case 'R': /* remote IP address */ if (!test) { - char *tmp = fcgi_get_last_client_ip(); + const char *tmp = fcgi_get_last_client_ip(); len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", tmp ? tmp : "-"); } break; diff --git a/sapi/fpm/fpm/fpm_sockets.c b/sapi/fpm/fpm/fpm_sockets.c index da14d63d8..0286f0eee 100644 --- a/sapi/fpm/fpm/fpm_sockets.c +++ b/sapi/fpm/fpm/fpm_sockets.c @@ -85,13 +85,24 @@ static void *fpm_get_in_addr(struct sockaddr *sa) /* {{{ */ } /* }}} */ +static int fpm_get_in_port(struct sockaddr *sa) /* {{{ */ +{ + if (sa->sa_family == AF_INET) { + return ntohs(((struct sockaddr_in*)sa)->sin_port); + } + + return ntohs(((struct sockaddr_in6*)sa)->sin6_port); +} +/* }}} */ + static int fpm_sockets_hash_op(int sock, struct sockaddr *sa, char *key, int type, int op) /* {{{ */ { if (key == NULL) { switch (type) { case FPM_AF_INET : { - key = alloca(INET6_ADDRSTRLEN); - inet_ntop(sa->sa_family, fpm_get_in_addr(sa), key, sizeof key); + key = alloca(INET6_ADDRSTRLEN+10); + inet_ntop(sa->sa_family, fpm_get_in_addr(sa), key, INET6_ADDRSTRLEN); + sprintf(key+strlen(key), ":%d", fpm_get_in_port(sa)); break; } @@ -244,9 +255,10 @@ static int fpm_socket_af_inet_listening_socket(struct fpm_worker_pool_s *wp) /* char *dup_address = strdup(wp->config->listen_address); char *port_str = strrchr(dup_address, ':'); char *addr = NULL; + char tmpbuf[INET6_ADDRSTRLEN]; int addr_len; int port = 0; - int sock; + int sock = -1; int status; if (port_str) { /* this is host:port pair */ @@ -263,13 +275,23 @@ static int fpm_socket_af_inet_listening_socket(struct fpm_worker_pool_s *wp) /* return -1; } - // strip brackets from address for getaddrinfo - if (addr != NULL) { - addr_len = strlen(addr); - if (addr[0] == '[' && addr[addr_len - 1] == ']') { - addr[addr_len - 1] = '\0'; - addr++; - } + if (!addr) { + /* no address: default documented behavior, all IPv4 addresses */ + struct sockaddr_in sa_in; + + memset(&sa_in, 0, sizeof(sa_in)); + sa_in.sin_family = AF_INET; + sa_in.sin_port = htons(port); + sa_in.sin_addr.s_addr = htonl(INADDR_ANY); + free(dup_address); + return fpm_sockets_get_listening_socket(wp, (struct sockaddr *) &sa_in, sizeof(struct sockaddr_in)); + } + + /* strip brackets from address for getaddrinfo */ + addr_len = strlen(addr); + if (addr[0] == '[' && addr[addr_len - 1] == ']') { + addr[addr_len - 1] = '\0'; + addr++; } memset(&hints, 0, sizeof hints); @@ -281,14 +303,18 @@ static int fpm_socket_af_inet_listening_socket(struct fpm_worker_pool_s *wp) /* return -1; } - free(dup_address); - for (p = servinfo; p != NULL; p = p->ai_next) { - if ((sock = fpm_sockets_get_listening_socket(wp, p->ai_addr, p->ai_addrlen)) != -1) { - break; + inet_ntop(p->ai_family, fpm_get_in_addr(p->ai_addr), tmpbuf, INET6_ADDRSTRLEN); + if (sock < 0) { + if ((sock = fpm_sockets_get_listening_socket(wp, p->ai_addr, p->ai_addrlen)) != -1) { + zlog(ZLOG_DEBUG, "Found address for %s, socket opened on %s", dup_address, tmpbuf); + } + } else { + zlog(ZLOG_WARNING, "Found multiple addresses for %s, %s ignored", dup_address, tmpbuf); } } + free(dup_address); freeaddrinfo(servinfo); return sock; diff --git a/sapi/fpm/fpm/fpm_stdio.c b/sapi/fpm/fpm/fpm_stdio.c index fcec78435..e28c0cbe7 100644 --- a/sapi/fpm/fpm/fpm_stdio.c +++ b/sapi/fpm/fpm/fpm_stdio.c @@ -42,9 +42,28 @@ int fpm_stdio_init_main() /* {{{ */ } /* }}} */ +static inline int fpm_use_error_log() { /* {{{ */ + /* + * the error_log is NOT used when running in foreground + * and from a tty (user looking at output). + * So, error_log is used by + * - SysV init launch php-fpm as a daemon + * - Systemd launch php-fpm in foreground + */ +#if HAVE_UNISTD_H + if (fpm_global_config.daemonize || (!isatty(STDERR_FILENO) && !fpm_globals.force_stderr)) { +#else + if (fpm_global_config.daemonize) { +#endif + return 1; + } + return 0; +} + +/* }}} */ int fpm_stdio_init_final() /* {{{ */ { - if (fpm_global_config.daemonize) { + if (fpm_use_error_log()) { /* prevent duping if logging to syslog */ if (fpm_globals.error_log_fd > 0 && fpm_globals.error_log_fd != STDERR_FILENO) { @@ -67,6 +86,11 @@ int fpm_stdio_init_child(struct fpm_worker_pool_s *wp) /* {{{ */ closelog(); /* ensure to close syslog not to interrupt with PHP syslog code */ } else #endif + + /* Notice: child cannot use master error_log + * because not aware when being reopen + * else, should use if (!fpm_use_error_log()) + */ if (fpm_globals.error_log_fd > 0) { close(fpm_globals.error_log_fd); } @@ -268,11 +292,7 @@ int fpm_stdio_open_error_log(int reopen) /* {{{ */ if (!strcasecmp(fpm_global_config.error_log, "syslog")) { openlog(fpm_global_config.syslog_ident, LOG_PID | LOG_CONS, fpm_global_config.syslog_facility); fpm_globals.error_log_fd = ZLOG_SYSLOG; -#if HAVE_UNISTD_H - if (fpm_global_config.daemonize || (!isatty(STDERR_FILENO) && !fpm_globals.force_stderr)) { -#else - if (fpm_global_config.daemonize) { -#endif + if (fpm_use_error_log()) { zlog_set_fd(fpm_globals.error_log_fd); } return 0; @@ -286,7 +306,7 @@ int fpm_stdio_open_error_log(int reopen) /* {{{ */ } if (reopen) { - if (fpm_global_config.daemonize) { + if (fpm_use_error_log()) { dup2(fd, STDERR_FILENO); } @@ -295,11 +315,7 @@ int fpm_stdio_open_error_log(int reopen) /* {{{ */ fd = fpm_globals.error_log_fd; /* for FD_CLOSEXEC to work */ } else { fpm_globals.error_log_fd = fd; -#if HAVE_UNISTD_H - if (fpm_global_config.daemonize || (!isatty(STDERR_FILENO) && !fpm_globals.force_stderr)) { -#else - if (fpm_global_config.daemonize) { -#endif + if (fpm_use_error_log()) { zlog_set_fd(fpm_globals.error_log_fd); } } diff --git a/sapi/fpm/fpm/fpm_unix.c b/sapi/fpm/fpm/fpm_unix.c index 68978ee75..32448fc4d 100644 --- a/sapi/fpm/fpm/fpm_unix.c +++ b/sapi/fpm/fpm/fpm_unix.c @@ -396,7 +396,6 @@ int fpm_unix_init_main() /* {{{ */ } } - zlog_set_level(fpm_globals.log_level); return 0; } /* }}} */ diff --git a/sapi/fpm/php-fpm.8.in b/sapi/fpm/php-fpm.8.in index b02aa25ba..3a697c6a6 100644 --- a/sapi/fpm/php-fpm.8.in +++ b/sapi/fpm/php-fpm.8.in @@ -84,6 +84,13 @@ Version number Specify alternative prefix path (the default is @php_fpm_prefix@) .TP .PD 0 +.B \-\-pid \fIfile\fP +.TP +.PD 1 +.B \-g +Specify the PID file location. +.TP +.PD 0 .B \-\-fpm\-config \fIfile\fP .TP .PD 1 @@ -113,12 +120,18 @@ Force to run in background and ignore daemonize option from configuration file. Force to stay in foreground and ignore daemonize option from configuration file. .TP .PD 0 -.B \-\-zend\-extension \fIfile\fP +.B \-\-force-stderr .TP .PD 1 -.B \-z \fIfile\fP -Load Zend extension -.IR file +.B \-O +Force output to stderr in nodaemonize even if stderr is not a TTY. +.TP +.PD 0 +.B \-\-allow\-to\-run\-as\-root +.TP +.PD 1 +.B \-R +Allow pool to run as root (disabled by default) .SH FILES .TP 15 .B php-fpm.conf diff --git a/sapi/fpm/php-fpm.conf.in b/sapi/fpm/php-fpm.conf.in index 631bc46f4..833a4f4f8 100644 --- a/sapi/fpm/php-fpm.conf.in +++ b/sapi/fpm/php-fpm.conf.in @@ -131,6 +131,7 @@ ; Per pool prefix ; It only applies on the following directives: +; - 'access.log' ; - 'slowlog' ; - 'listen' (unixsocket) ; - 'chroot' @@ -150,12 +151,14 @@ group = @php_fpm_group@ ; The address on which to accept FastCGI requests. ; Valid syntaxes are: -; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific address on +; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on ; a specific port; ; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on ; a specific port; -; 'port' - to listen on a TCP socket to all addresses on a +; 'port' - to listen on a TCP socket to all IPv4 addresses on a ; specific port; +; '[::]:port' - to listen on a TCP socket to all addresses +; (IPv6 and IPv4-mapped) on a specific port; ; '/path/to/unix/socket' - to listen on a unix socket. ; Note: This value is mandatory. listen = 127.0.0.1:9000 @@ -173,7 +176,7 @@ listen = 127.0.0.1:9000 ;listen.group = @php_fpm_group@ ;listen.mode = 0660 -; List of ipv4 addresses of FastCGI clients which are allowed to connect. +; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect. ; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original ; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address ; must be separated by a comma. If this value is left blank, connections will be diff --git a/sapi/fpm/tests/002.phpt b/sapi/fpm/tests/002.phpt index 2ef6cedc3..89e431849 100644 --- a/sapi/fpm/tests/002.phpt +++ b/sapi/fpm/tests/002.phpt @@ -8,12 +8,13 @@ FPM: Startup and connect include "include.inc"; $logfile = dirname(__FILE__).'/php-fpm.log.tmp'; +$port = 9000+PHP_INT_SIZE; $cfg = <<<EOT [global] error_log = $logfile [unconfined] -listen = 127.0.0.1:9000 +listen = 127.0.0.1:$port pm = dynamic pm.max_children = 5 pm.start_servers = 2 @@ -23,10 +24,9 @@ EOT; $fpm = run_fpm($cfg, $tail); if (is_resource($fpm)) { - var_dump(fgets($tail)); - var_dump(fgets($tail)); + fpm_display_log($tail, 2); $i = 0; - while (($i++ < 30) && !($fp = @fsockopen('127.0.0.1', 9000))) { + while (($i++ < 30) && !($fp = @fsockopen('127.0.0.1', $port))) { usleep(10000); } if ($fp) { @@ -41,10 +41,8 @@ if (is_resource($fpm)) { ?> --EXPECTF-- -string(%d) "[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d -" -string(%d) "[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections -" +[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d +[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections Done --CLEAN-- <?php diff --git a/sapi/fpm/tests/003.phpt b/sapi/fpm/tests/003.phpt index 389cb2401..f928626ec 100644 --- a/sapi/fpm/tests/003.phpt +++ b/sapi/fpm/tests/003.phpt @@ -8,12 +8,13 @@ FPM: Test IPv6 support include "include.inc"; $logfile = dirname(__FILE__).'/php-fpm.log.tmp'; +$port = 9000+PHP_INT_SIZE; $cfg = <<<EOT [global] error_log = $logfile [unconfined] -listen = [::1]:9000 +listen = [::1]:$port pm = dynamic pm.max_children = 5 pm.start_servers = 2 @@ -23,10 +24,9 @@ EOT; $fpm = run_fpm($cfg, $tail); if (is_resource($fpm)) { - var_dump(fgets($tail)); - var_dump(fgets($tail)); + fpm_display_log($tail, 2); $i = 0; - while (($i++ < 30) && !($fp = fsockopen('[::1]', 9000))) { + while (($i++ < 30) && !($fp = fsockopen('[::1]', $port))) { usleep(10000); } if ($fp) { @@ -41,10 +41,8 @@ if (is_resource($fpm)) { ?> --EXPECTF-- -string(%d) "[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d -" -string(%d) "[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections -" +[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d +[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections Done --CLEAN-- <?php diff --git a/sapi/fpm/tests/004.phpt b/sapi/fpm/tests/004.phpt new file mode 100644 index 000000000..2a4d666c6 --- /dev/null +++ b/sapi/fpm/tests/004.phpt @@ -0,0 +1,59 @@ +--TEST-- +FPM: Test IPv4/IPv6 support +--SKIPIF-- +<?php include "skipif.inc"; ?> +--FILE-- +<?php + +include "include.inc"; + +$logfile = dirname(__FILE__).'/php-fpm.log.tmp'; +$port = 9000+PHP_INT_SIZE; + +$cfg = <<<EOT +[global] +error_log = $logfile +[unconfined] +listen = [::]:$port +pm = dynamic +pm.max_children = 5 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 +EOT; + +$fpm = run_fpm($cfg, $tail); +if (is_resource($fpm)) { + fpm_display_log($tail, 2); + $i = 0; + while (($i++ < 30) && !($fp = @fsockopen('127.0.0.1', $port))) { + usleep(10000); + } + if ($fp) { + echo "Done IPv4\n"; + fclose($fp); + } + while (($i++ < 30) && !($fp = @fsockopen('[::1]', $port))) { + usleep(10000); + } + if ($fp) { + echo "Done IPv6\n"; + fclose($fp); + } + proc_terminate($fpm); + stream_get_contents($tail); + fclose($tail); + proc_close($fpm); +} + +?> +--EXPECTF-- +[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d +[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections +Done IPv4 +Done IPv6 +--CLEAN-- +<?php + $logfile = dirname(__FILE__).'/php-fpm.log.tmp'; + @unlink($logfile); +?> diff --git a/sapi/fpm/tests/005.phpt b/sapi/fpm/tests/005.phpt new file mode 100644 index 000000000..c565c2a9e --- /dev/null +++ b/sapi/fpm/tests/005.phpt @@ -0,0 +1,57 @@ +--TEST-- +FPM: Test IPv4 allowed clients +--SKIPIF-- +<?php include "skipif.inc"; ?> +--FILE-- +<?php + +include "include.inc"; + +$logfile = dirname(__FILE__).'/php-fpm.log.tmp'; +$port = 9000+PHP_INT_SIZE; + +$cfg = <<<EOT +[global] +error_log = $logfile +[unconfined] +listen = [::]:$port +listen.allowed_clients = 127.0.0.1 +pm = dynamic +pm.max_children = 5 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 +EOT; + +$fpm = run_fpm($cfg, $tail); +if (is_resource($fpm)) { + fpm_display_log($tail, 2); + try { + run_request('127.0.0.1', $port); + echo "IPv4 ok\n"; + } catch (Exception $e) { + echo "IPv4 error\n"; + } + try { + run_request('[::1]', $port); + echo "IPv6 ok\n"; + } catch (Exception $e) { + echo "IPv6 error\n"; + } + proc_terminate($fpm); + stream_get_contents($tail); + fclose($tail); + proc_close($fpm); +} + +?> +--EXPECTF-- +[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d +[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections +IPv4 ok +IPv6 error +--CLEAN-- +<?php + $logfile = dirname(__FILE__).'/php-fpm.log.tmp'; + @unlink($logfile); +?> diff --git a/sapi/fpm/tests/006.phpt b/sapi/fpm/tests/006.phpt new file mode 100644 index 000000000..a12ca253d --- /dev/null +++ b/sapi/fpm/tests/006.phpt @@ -0,0 +1,57 @@ +--TEST-- +FPM: Test IPv6 allowed clients (bug #68428) +--SKIPIF-- +<?php include "skipif.inc"; ?> +--FILE-- +<?php + +include "include.inc"; + +$logfile = dirname(__FILE__).'/php-fpm.log.tmp'; +$port = 9000+PHP_INT_SIZE; + +$cfg = <<<EOT +[global] +error_log = $logfile +[unconfined] +listen = [::]:$port +listen.allowed_clients = ::1 +pm = dynamic +pm.max_children = 5 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 +EOT; + +$fpm = run_fpm($cfg, $tail); +if (is_resource($fpm)) { + fpm_display_log($tail, 2); + try { + run_request('127.0.0.1', $port); + echo "IPv4 ok\n"; + } catch (Exception $e) { + echo "IPv4 error\n"; + } + try { + run_request('[::1]', $port); + echo "IPv6 ok\n"; + } catch (Exception $e) { + echo "IPv6 error\n"; + } + proc_terminate($fpm); + stream_get_contents($tail); + fclose($tail); + proc_close($fpm); +} + +?> +--EXPECTF-- +[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d +[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections +IPv4 error +IPv6 ok +--CLEAN-- +<?php + $logfile = dirname(__FILE__).'/php-fpm.log.tmp'; + @unlink($logfile); +?> diff --git a/sapi/fpm/tests/007.phpt b/sapi/fpm/tests/007.phpt new file mode 100644 index 000000000..0d817907c --- /dev/null +++ b/sapi/fpm/tests/007.phpt @@ -0,0 +1,68 @@ +--TEST-- +FPM: Test IPv6 all addresses and access_log (bug #68421) +--SKIPIF-- +<?php include "skipif.inc"; ?> +--FILE-- +<?php + +include "include.inc"; + +$logfile = dirname(__FILE__).'/php-fpm.log.tmp'; +$accfile = dirname(__FILE__).'/php-fpm.acc.tmp'; +$port = 9000+PHP_INT_SIZE; + +$cfg = <<<EOT +[global] +error_log = $logfile +[unconfined] +listen = [::]:$port +access.log = $accfile +ping.path = /ping +ping.response = pong +pm = dynamic +pm.max_children = 5 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 +EOT; + +$fpm = run_fpm($cfg, $tail); +if (is_resource($fpm)) { + fpm_display_log($tail, 2); + try { + var_dump(strpos(run_request('127.0.0.1', $port), 'pong')); + echo "IPv4 ok\n"; + } catch (Exception $e) { + echo "IPv4 error\n"; + } + try { + var_dump(strpos(run_request('[::1]', $port), 'pong')); + echo "IPv6 ok\n"; + } catch (Exception $e) { + echo "IPv6 error\n"; + } + proc_terminate($fpm); + stream_get_contents($tail); + fclose($tail); + proc_close($fpm); + + echo file_get_contents($accfile); +} + +?> +--EXPECTF-- +[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d +[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections +int(%d) +IPv4 ok +int(%d) +IPv6 ok +127.0.0.1 %s "GET /ping" 200 +::1 %s "GET /ping" 200 +--CLEAN-- +<?php + $logfile = dirname(__FILE__).'/php-fpm.log.tmp'; + @unlink($logfile); + $accfile = dirname(__FILE__).'/php-fpm.acc.tmp'; + @unlink($accfile); +?> diff --git a/sapi/fpm/tests/008.phpt b/sapi/fpm/tests/008.phpt new file mode 100644 index 000000000..60f1ea6eb --- /dev/null +++ b/sapi/fpm/tests/008.phpt @@ -0,0 +1,84 @@ +--TEST-- +FPM: Test multi pool (dynamic + ondemand + static) (bug #68423) +--SKIPIF-- +<?php include "skipif.inc"; ?> +--FILE-- +<?php + +include "include.inc"; + +$logfile = dirname(__FILE__).'/php-fpm.log.tmp'; +$port1 = 9000+PHP_INT_SIZE; +$port2 = 9001+PHP_INT_SIZE; +$port3 = 9002+PHP_INT_SIZE; + +$cfg = <<<EOT +[global] +error_log = $logfile +[pool_dynamic] +listen = 127.0.0.1:$port1 +ping.path = /ping +ping.response = pong-dynamic +pm = dynamic +pm.max_children = 5 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 +[poold_ondemand] +listen = 127.0.0.1:$port2 +ping.path = /ping +ping.response = pong-on-demand +pm = ondemand +pm.max_children = 2 +pm.process_idle_timeout = 10 +[pool_static] +listen = 127.0.0.1:$port3 +ping.path = /ping +ping.response = pong-static +pm = static +pm.max_children = 2 +EOT; + +$fpm = run_fpm($cfg, $tail); +if (is_resource($fpm)) { + fpm_display_log($tail, 2); + try { + var_dump(strpos(run_request('127.0.0.1', $port1), 'pong-dynamic')); + echo "Dynamic ok\n"; + } catch (Exception $e) { + echo "Dynamic error\n"; + } + try { + var_dump(strpos(run_request('127.0.0.1', $port2), 'pong-on-demand')); + echo "OnDemand ok\n"; + } catch (Exception $e) { + echo "OnDemand error\n"; + } + try { + var_dump(strpos(run_request('127.0.0.1', $port3), 'pong-static')); + echo "Static ok\n"; + } catch (Exception $e) { + echo "Static error\n"; + } + + proc_terminate($fpm); + stream_get_contents($tail); + fclose($tail); + proc_close($fpm); +} + +?> +--EXPECTF-- +[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d +[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections +int(%d) +Dynamic ok +int(%d) +OnDemand ok +int(%d) +Static ok +--CLEAN-- +<?php + $logfile = dirname(__FILE__).'/php-fpm.log.tmp'; + @unlink($logfile); +?> diff --git a/sapi/fpm/tests/009.phpt b/sapi/fpm/tests/009.phpt new file mode 100644 index 000000000..34cdffcf8 --- /dev/null +++ b/sapi/fpm/tests/009.phpt @@ -0,0 +1,53 @@ +--TEST-- +FPM: Test Unix Domain Socket +--SKIPIF-- +<?php include "skipif.inc"; ?> +--FILE-- +<?php + +include "include.inc"; + +$logfile = dirname(__FILE__).'/php-fpm.log.tmp'; +$socket = dirname(__FILE__).'/php-fpm.sock'; + +$cfg = <<<EOT +[global] +error_log = $logfile +[unconfined] +listen = $socket +ping.path = /ping +ping.response = pong +pm = dynamic +pm.max_children = 5 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 +EOT; + +$fpm = run_fpm($cfg, $tail); +if (is_resource($fpm)) { + fpm_display_log($tail, 2); + try { + var_dump(strpos(run_request('unix://'.$socket, -1), 'pong')); + echo "UDS ok\n"; + } catch (Exception $e) { + echo "UDS error\n"; + } + + proc_terminate($fpm); + stream_get_contents($tail); + fclose($tail); + proc_close($fpm); +} + +?> +--EXPECTF-- +[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d +[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections +int(%d) +UDS ok +--CLEAN-- +<?php + $logfile = dirname(__FILE__).'/php-fpm.log.tmp'; + @unlink($logfile); +?> diff --git a/sapi/fpm/tests/010.phpt b/sapi/fpm/tests/010.phpt new file mode 100644 index 000000000..37c778db5 --- /dev/null +++ b/sapi/fpm/tests/010.phpt @@ -0,0 +1,84 @@ +--TEST-- +FPM: Test status page +--SKIPIF-- +<?php include "skipif.inc"; ?> +--FILE-- +<?php + +include "include.inc"; + +$logfile = dirname(__FILE__).'/php-fpm.log.tmp'; +$port = 9000+PHP_INT_SIZE; + +$cfg = <<<EOT +[global] +error_log = $logfile +[unconfined] +listen = 127.0.0.1:$port +pm.status_path = /status +pm = dynamic +pm.max_children = 5 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 +EOT; + +$fpm = run_fpm($cfg, $tail); +if (is_resource($fpm)) { + fpm_display_log($tail, 2); + try { + echo run_request('127.0.0.1', $port, '/status'); + + $html = run_request('127.0.0.1', $port, '/status', 'html'); + var_dump(strpos($html, 'text/html') && strpos($html, 'DOCTYPE') && strpos($html, 'PHP-FPM Status Page')); + + $json = run_request('127.0.0.1', $port, '/status', 'json'); + var_dump(strpos($json, 'application/json') && strpos($json, '"pool":"unconfined"')); + + $xml = run_request('127.0.0.1', $port, '/status', 'xml'); + var_dump(strpos($xml, 'text/xml') && strpos($xml, '<?xml')); + + echo "IPv4 ok\n"; + } catch (Exception $e) { + echo "IPv4 error\n"; + } + + proc_terminate($fpm); + stream_get_contents($tail); + fclose($tail); + proc_close($fpm); +} + +?> +--EXPECTF-- +[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d +[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections +X-Powered-By: PHP/%s +Expires: %s +Cache-Control: %s +Content-type: text/plain%s + +pool: unconfined +process manager: dynamic +start time: %s +start since: %d +accepted conn: 1 +listen queue: 0 +max listen queue: 0 +listen queue len: %d +idle processes: 1 +active processes: 1 +total processes: 2 +max active processes: 1 +max children reached: 0 +slow requests: 0 + +bool(true) +bool(true) +bool(true) +IPv4 ok +--CLEAN-- +<?php + $logfile = dirname(__FILE__).'/php-fpm.log.tmp'; + @unlink($logfile); +?> diff --git a/sapi/fpm/tests/011.phpt b/sapi/fpm/tests/011.phpt new file mode 100644 index 000000000..0b849f873 --- /dev/null +++ b/sapi/fpm/tests/011.phpt @@ -0,0 +1,53 @@ +--TEST-- +FPM: Test IPv4 all addresses (bug #68420) +--SKIPIF-- +<?php include "skipif.inc"; ?> +--FILE-- +<?php + +include "include.inc"; + +$logfile = dirname(__FILE__).'/php-fpm.log.tmp'; +$port = 9000+PHP_INT_SIZE; + +$cfg = <<<EOT +[global] +error_log = $logfile +[unconfined] +listen = $port +ping.path = /ping +ping.response = pong +pm = dynamic +pm.max_children = 5 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 +EOT; + +$fpm = run_fpm($cfg, $tail); +if (is_resource($fpm)) { + fpm_display_log($tail, 2); + try { + var_dump(strpos(run_request('127.0.0.1', $port), 'pong')); + echo "IPv4 ok\n"; + } catch (Exception $e) { + echo "IPv4 error\n"; + } + + proc_terminate($fpm); + stream_get_contents($tail); + fclose($tail); + proc_close($fpm); +} + +?> +--EXPECTF-- +[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d +[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections +int(%d) +IPv4 ok +--CLEAN-- +<?php + $logfile = dirname(__FILE__).'/php-fpm.log.tmp'; + @unlink($logfile); +?> diff --git a/sapi/fpm/tests/012.phpt b/sapi/fpm/tests/012.phpt new file mode 100644 index 000000000..d96c53081 --- /dev/null +++ b/sapi/fpm/tests/012.phpt @@ -0,0 +1,79 @@ +--TEST-- +FPM: Test reload configuration (bug #68442) +--SKIPIF-- +<?php include "skipif.inc"; ?> +--FILE-- +<?php + +include "include.inc"; + +$logfile = dirname(__FILE__).'/php-fpm.log.tmp'; +$pidfile = dirname(__FILE__).'/php-fpm.pid'; +$port = 9000+PHP_INT_SIZE; + +$cfg = <<<EOT +[global] +error_log = $logfile +pid = $pidfile +[unconfined] +listen = 127.0.0.1:$port +ping.path = /ping +ping.response = pong +pm = dynamic +pm.max_children = 5 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 +EOT; + +$fpm = run_fpm($cfg, $tail); +if (is_resource($fpm)) { + fpm_display_log($tail, 2); + try { + var_dump(strpos(run_request('127.0.0.1', $port), 'pong')); + echo "IPv4 ok\n"; + } catch (Exception $e) { + echo "IPv4 error\n"; + } + + $pid=file_get_contents($pidfile); + if ($pid) { + exec("kill -USR2 $pid"); + } else { + die("PID not found\n"); + } + fpm_display_log($tail, 5); + + try { + var_dump(strpos(run_request('127.0.0.1', $port), 'pong')); + echo "IPv4 ok\n"; + } catch (Exception $e) { + echo "IPv4 error\n"; + } + + proc_terminate($fpm); + stream_get_contents($tail); + fclose($tail); + proc_close($fpm); +} + +?> +--EXPECTF-- +[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d +[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections +int(%d) +IPv4 ok +[%d-%s-%d %d:%d:%d] NOTICE: Reloading in progress ... +[%d-%s-%d %d:%d:%d] NOTICE: reloading: %s +[%d-%s-%d %d:%d:%d] NOTICE: using inherited socket fd=%d, "127.0.0.1:%d" +[%d-%s-%d %d:%d:%d] NOTICE: fpm is running, pid %d +[%d-%s-%d %d:%d:%d] NOTICE: ready to handle connections +int(%d) +IPv4 ok +--CLEAN-- +<?php + $logfile = dirname(__FILE__).'/php-fpm.log.tmp'; + @unlink($logfile); + $pidfile = dirname(__FILE__).'/php-fpm.pid'; + @unlink($pidfile); +?> diff --git a/sapi/fpm/tests/013.phpt b/sapi/fpm/tests/013.phpt new file mode 100644 index 000000000..8d6a9d1d8 --- /dev/null +++ b/sapi/fpm/tests/013.phpt @@ -0,0 +1,54 @@ +--TEST-- +FPM: Test for log_level in fpm_unix_init_main #68381 +--SKIPIF-- +<?php include "skipif.inc"; ?> +--FILE-- +<?php + +include "include.inc"; + +$logfile = dirname(__FILE__).'/php-fpm.log.tmp'; +$port = 9000+PHP_INT_SIZE; + +$cfg = <<<EOT +[global] +error_log = $logfile +log_level = warning +[unconfined] +listen = 127.0.0.1:$port +user = foo +pm = dynamic +pm.max_children = 5 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 +EOT; + +$fpm = run_fpm($cfg, $tail); +if (is_resource($fpm)) { + $i = 0; + while (($i++ < 30) && !($fp = @fsockopen('127.0.0.1', $port))) { + usleep(10000); + } + if ($fp) { + echo "Started\n"; + fclose($fp); + } + proc_terminate($fpm); + if (!feof($tail)) { + echo stream_get_contents($tail); + } + fclose($tail); + proc_close($fpm); +} + +?> +Done +--EXPECTF-- +Started +Done +--CLEAN-- +<?php + $logfile = dirname(__FILE__).'/php-fpm.log.tmp'; + @unlink($logfile); +?>
\ No newline at end of file diff --git a/sapi/fpm/tests/014.phpt b/sapi/fpm/tests/014.phpt new file mode 100644 index 000000000..ee0e549cc --- /dev/null +++ b/sapi/fpm/tests/014.phpt @@ -0,0 +1,54 @@ +--TEST-- +FPM: Test for pm.start_servers default calculation message being a notice and not a warning #68458 +--SKIPIF-- +<?php include "skipif.inc"; ?> +--FILE-- +<?php + +include "include.inc"; + +$logfile = dirname(__FILE__).'/php-fpm.log.tmp'; +$port = 9000+PHP_INT_SIZE; + +$cfg = <<<EOT +[global] +error_log = $logfile +log_level = warning +[unconfined] +listen = 127.0.0.1:$port +user = foo +pm = dynamic +pm.max_children = 5 +;pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 +EOT; + +$fpm = run_fpm($cfg, $tail); +if (is_resource($fpm)) { + $i = 0; + while (($i++ < 30) && !($fp = @fsockopen('127.0.0.1', $port))) { + usleep(10000); + } + if ($fp) { + echo "Started\n"; + fclose($fp); + } + proc_terminate($fpm); + if (!feof($tail)) { + echo stream_get_contents($tail); + } + fclose($tail); + proc_close($fpm); +} + +?> +Done +--EXPECTF-- +Started +Done +--CLEAN-- +<?php + $logfile = dirname(__FILE__).'/php-fpm.log.tmp'; + @unlink($logfile); +?>
\ No newline at end of file diff --git a/sapi/fpm/tests/015.phpt b/sapi/fpm/tests/015.phpt new file mode 100644 index 000000000..fba333e25 --- /dev/null +++ b/sapi/fpm/tests/015.phpt @@ -0,0 +1,87 @@ +--TEST-- +FPM: Test various messages on start, from master and childs +--SKIPIF-- +<?php include "skipif.inc"; ?> +--FILE-- +<?php + +include "include.inc"; + +$logfile = dirname(__FILE__).'/php-fpm.log.tmp'; +$port1 = 9000+PHP_INT_SIZE; +$port2 = 9001+PHP_INT_SIZE; + +$cfg = <<<EOT +[global] +error_log = $logfile +log_level = notice +[pool1] +listen = 127.0.0.1:$port1 +listen.allowed_clients=127.0.0.1 +user = foo +pm = dynamic +pm.max_children = 5 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 +catch_workers_output = yes +[pool2] +listen = 127.0.0.1:$port2 +listen.allowed_clients=xxx +pm = dynamic +pm.max_children = 5 +pm.start_servers = 1 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 +catch_workers_output = yes +EOT; + +$fpm = run_fpm($cfg, $tail); +if (is_resource($fpm)) { + $i = 0; + while (($i++ < 30) && !($fp = @fsockopen('127.0.0.1', $port1))) { + usleep(10000); + } + if ($fp) { + echo "Started\n"; + fclose($fp); + } + for ($i=0 ; $i<10 ; $i++) { + try { + run_request('127.0.0.1', $port1); + } catch (Exception $e) { + echo "Error 1\n"; + } + } + try { + run_request('127.0.0.1', $port2); + } catch (Exception $e) { + echo "Error 2\n"; + } + proc_terminate($fpm); + if (!feof($tail)) { + echo stream_get_contents($tail); + } + fclose($tail); + proc_close($fpm); +} + +?> +Done +--EXPECTF-- +Started +Error 2 +[%s] NOTICE: [pool pool1] pm.start_servers is not set. It's been set to 2. +[%s] NOTICE: [pool pool1] 'user' directive is ignored when FPM is not running as root +[%s] NOTICE: fpm is running, pid %d +[%s] NOTICE: ready to handle connections +[%s] WARNING: [pool pool2] child %d said into stderr: "ERROR: Wrong IP address 'xxx' in listen.allowed_clients" +[%s] WARNING: [pool pool2] child %d said into stderr: "ERROR: There are no allowed addresses for this pool" +[%s] WARNING: [pool pool2] child %d said into stderr: "ERROR: Connection disallowed: IP address '127.0.0.1' has been dropped." +[%s] NOTICE: Terminating ... +[%s] NOTICE: exiting, bye-bye! +Done +--CLEAN-- +<?php + $logfile = dirname(__FILE__).'/php-fpm.log.tmp'; + @unlink($logfile); +?>
\ No newline at end of file diff --git a/sapi/fpm/tests/016.phpt b/sapi/fpm/tests/016.phpt new file mode 100644 index 000000000..1a9e8e757 --- /dev/null +++ b/sapi/fpm/tests/016.phpt @@ -0,0 +1,87 @@ +--TEST-- +FPM: Test splited configuration and load order #68391 +--SKIPIF-- +<?php include "skipif.inc"; ?> +--FILE-- +<?php + +include "include.inc"; + +$logfile = __DIR__.'/php-fpm.log.tmp'; +$logdir = __DIR__.'/conf.d'; +$port = 9000+PHP_INT_SIZE; + +// Main configuration +$cfg = <<<EOT +[global] +error_log = $logfile +log_level = notice +include = $logdir/*.conf +EOT; + +// Splited configuration +@mkdir($logdir); +$i=$port; +$names = ['cccc', 'aaaa', 'eeee', 'dddd', 'bbbb']; +foreach($names as $name) { + $poolcfg = <<<EOT +[$name] +listen = 127.0.0.1:$i +listen.allowed_clients=127.0.0.1 +user = foo +pm = ondemand +pm.max_children = 5 +EOT; + file_put_contents("$logdir/$name.conf", $poolcfg); + $i++; +} + +// Test +$fpm = run_fpm($cfg, $tail); +if (is_resource($fpm)) { + fpm_display_log($tail, count($names)+2); + $i=$port; + foreach($names as $name) { + try { + run_request('127.0.0.1', $i++); + echo "OK $name\n"; + } catch (Exception $e) { + echo "Error 1\n"; + } + } + proc_terminate($fpm); + if (!feof($tail)) { + echo stream_get_contents($tail); + } + fclose($tail); + proc_close($fpm); +} + +?> +Done +--EXPECTF-- +[%s] NOTICE: [pool aaaa] 'user' directive is ignored when FPM is not running as root +[%s] NOTICE: [pool bbbb] 'user' directive is ignored when FPM is not running as root +[%s] NOTICE: [pool cccc] 'user' directive is ignored when FPM is not running as root +[%s] NOTICE: [pool dddd] 'user' directive is ignored when FPM is not running as root +[%s] NOTICE: [pool eeee] 'user' directive is ignored when FPM is not running as root +[%s] NOTICE: fpm is running, pid %d +[%s] NOTICE: ready to handle connections +OK cccc +OK aaaa +OK eeee +OK dddd +OK bbbb +[%s] NOTICE: Terminating ... +[%s] NOTICE: exiting, bye-bye! +Done +--CLEAN-- +<?php + $logfile = __DIR__.'/php-fpm.log.tmp'; + $logdir = __DIR__.'/conf.d'; + @unlink($logfile); + foreach(glob("$logdir/*.conf") as $name) { + unlink($name); + } + @rmdir($logdir); +?>
\ No newline at end of file diff --git a/sapi/fpm/tests/017.phpt b/sapi/fpm/tests/017.phpt new file mode 100644 index 000000000..b3de089a7 --- /dev/null +++ b/sapi/fpm/tests/017.phpt @@ -0,0 +1,67 @@ +--TEST-- +FPM: Test fastcgi_finish_request function +--SKIPIF-- +<?php include "skipif.inc"; ?> +--FILE-- +<?php + +include "include.inc"; + +$logfile = __DIR__.'/php-fpm.log.tmp'; +$srcfile = __DIR__.'/php-fpm.tmp.php'; +$port = 9000+PHP_INT_SIZE; + +$cfg = <<<EOT +[global] +error_log = $logfile +[unconfined] +listen = 127.0.0.1:$port +pm = dynamic +pm.max_children = 5 +pm.start_servers = 1 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 +EOT; + +$code = <<<EOT +<?php +echo "Test Start\n"; +fastcgi_finish_request(); +echo "Test End\n"; +EOT; +file_put_contents($srcfile, $code); + +$fpm = run_fpm($cfg, $tail); +if (is_resource($fpm)) { + fpm_display_log($tail, 2); + try { + $req = run_request('127.0.0.1', $port, $srcfile); + echo strstr($req, "Test Start"); + echo "Request ok\n"; + } catch (Exception $e) { + echo "Request error\n"; + } + proc_terminate($fpm); + echo stream_get_contents($tail); + fclose($tail); + proc_close($fpm); +} + +?> +Done +--EXPECTF-- +[%s] NOTICE: fpm is running, pid %d +[%s] NOTICE: ready to handle connections +Test Start + +Request ok +[%s] NOTICE: Terminating ... +[%s] NOTICE: exiting, bye-bye! +Done +--CLEAN-- +<?php + $logfile = __DIR__.'/php-fpm.log.tmp'; + $srcfile = __DIR__.'/php-fpm.tmp.php'; + @unlink($logfile); + @unlink($srcfile); +?>
\ No newline at end of file diff --git a/sapi/fpm/tests/019.phpt b/sapi/fpm/tests/019.phpt new file mode 100644 index 000000000..cdf812624 --- /dev/null +++ b/sapi/fpm/tests/019.phpt @@ -0,0 +1,80 @@ +--TEST-- +FPM: Test global prefix +--SKIPIF-- +<?php include "skipif.inc"; ?> +--FILE-- +<?php + +include "include.inc"; + +$logfile = 'php-fpm.log.tmp'; +$accfile = 'php-fpm.acc.tmp'; +$slwfile = 'php-fpm.slw.tmp'; +$pidfile = 'php-fpm.pid.tmp'; +$port = 9000+PHP_INT_SIZE; + +$cfg = <<<EOT +[global] +error_log = $logfile +pid = $pidfile +[test] +listen = 127.0.0.1:$port +access.log = $accfile +slowlog = $slwfile; +request_slowlog_timeout = 1 +ping.path = /ping +ping.response = pong +pm = dynamic +pm.max_children = 5 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 +catch_workers_output = yes +EOT; + +$fpm = run_fpm($cfg, $tail, '--prefix '.__DIR__); +if (is_resource($fpm)) { + fpm_display_log($tail, 2); + try { + run_request('127.0.0.1', $port); + echo "Ping ok\n"; + } catch (Exception $e) { + echo "Ping error\n"; + } + printf("File %s %s\n", $logfile, (file_exists(__DIR__.'/'.$logfile) ? "exists" : "missing")); + printf("File %s %s\n", $accfile, (file_exists(__DIR__.'/'.$accfile) ? "exists" : "missing")); + printf("File %s %s\n", $slwfile, (file_exists(__DIR__.'/'.$slwfile) ? "exists" : "missing")); + printf("File %s %s\n", $pidfile, (file_exists(__DIR__.'/'.$pidfile) ? "exists" : "missing")); + + proc_terminate($fpm); + echo stream_get_contents($tail); + fclose($tail); + proc_close($fpm); + printf("File %s %s\n", $pidfile, (file_exists(__DIR__.'/'.$pidfile) ? "still exists" : "removed")); + readfile(__DIR__.'/'.$accfile); +} + +?> +--EXPECTF-- +[%s] NOTICE: fpm is running, pid %d +[%s] NOTICE: ready to handle connections +Ping ok +File php-fpm.log.tmp exists +File php-fpm.acc.tmp exists +File php-fpm.slw.tmp exists +File php-fpm.pid.tmp exists +[%s] NOTICE: Terminating ... +[%s] NOTICE: exiting, bye-bye! +File php-fpm.pid.tmp removed +127.0.0.1 - %s "GET /ping" 200 +--CLEAN-- +<?php + $logfile = __DIR__.'/php-fpm.log.tmp'; + $accfile = __DIR__.'/php-fpm.acc.tmp'; + $slwfile = __DIR__.'/php-fpm.slw.tmp'; + $pidfile = __DIR__.'/php-fpm.pid.tmp'; + @unlink($logfile); + @unlink($accfile); + @unlink($slwfile); + @unlink($pidfile); +?> diff --git a/sapi/fpm/tests/020.phpt b/sapi/fpm/tests/020.phpt new file mode 100644 index 000000000..d45eeccff --- /dev/null +++ b/sapi/fpm/tests/020.phpt @@ -0,0 +1,76 @@ +--TEST-- +FPM: Test pool prefix +--SKIPIF-- +<?php include "skipif.inc"; ?> +--FILE-- +<?php + +include "include.inc"; + +$prefix = __DIR__; +$logfile = __DIR__.'/php-fpm.log.tmp'; +$accfile = 'php-fpm.acc.tmp'; +$slwfile = 'php-fpm.slw.tmp'; +$pidfile = __DIR__.'/php-fpm.pid.tmp'; +$port = 9000+PHP_INT_SIZE; +$cfg = <<<EOT + +[global] +error_log = $logfile +pid = $pidfile +[test] +prefix = $prefix; +listen = 127.0.0.1:$port +access.log = $accfile +slowlog = $slwfile; +request_slowlog_timeout = 1 +ping.path = /ping +ping.response = pong +pm = dynamic +pm.max_children = 5 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 +catch_workers_output = yes +EOT; + +$fpm = run_fpm($cfg, $tail); +if (is_resource($fpm)) { + fpm_display_log($tail, 2); + try { + run_request('127.0.0.1', $port); + echo "Ping ok\n"; + } catch (Exception $e) { + echo "Ping error\n"; + } + printf("File %s %s\n", $accfile, (file_exists(__DIR__.'/'.$accfile) ? "exists" : "missing")); + printf("File %s %s\n", $slwfile, (file_exists(__DIR__.'/'.$slwfile) ? "exists" : "missing")); + + proc_terminate($fpm); + echo stream_get_contents($tail); + fclose($tail); + proc_close($fpm); + readfile(__DIR__.'/'.$accfile); +} + +?> +--EXPECTF-- +[%s] NOTICE: fpm is running, pid %d +[%s] NOTICE: ready to handle connections +Ping ok +File php-fpm.acc.tmp exists +File php-fpm.slw.tmp exists +[%s] NOTICE: Terminating ... +[%s] NOTICE: exiting, bye-bye! +127.0.0.1 - %s "GET /ping" 200 +--CLEAN-- +<?php + $logfile = __DIR__.'/php-fpm.log.tmp'; + $accfile = __DIR__.'/php-fpm.acc.tmp'; + $slwfile = __DIR__.'/php-fpm.slw.tmp'; + $pidfile = __DIR__.'/php-fpm.pid.tmp'; + @unlink($logfile); + @unlink($accfile); + @unlink($slwfile); + @unlink($pidfile); +?> diff --git a/sapi/fpm/tests/fcgi.inc b/sapi/fpm/tests/fcgi.inc new file mode 100644 index 000000000..b31676260 --- /dev/null +++ b/sapi/fpm/tests/fcgi.inc @@ -0,0 +1,592 @@ +<?php +/* + * This file is part of PHP-FastCGI-Client. + * + * (c) Pierrick Charron <pierrick@adoy.net> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +namespace Adoy\FastCGI; + +class TimedOutException extends \Exception {} +class ForbiddenException extends \Exception {} + +/** + * Handles communication with a FastCGI application + * + * @author Pierrick Charron <pierrick@adoy.net> + * @version 1.0 + */ +class Client +{ + const VERSION_1 = 1; + + const BEGIN_REQUEST = 1; + const ABORT_REQUEST = 2; + const END_REQUEST = 3; + const PARAMS = 4; + const STDIN = 5; + const STDOUT = 6; + const STDERR = 7; + const DATA = 8; + const GET_VALUES = 9; + const GET_VALUES_RESULT = 10; + const UNKNOWN_TYPE = 11; + const MAXTYPE = self::UNKNOWN_TYPE; + + const RESPONDER = 1; + const AUTHORIZER = 2; + const FILTER = 3; + + const REQUEST_COMPLETE = 0; + const CANT_MPX_CONN = 1; + const OVERLOADED = 2; + const UNKNOWN_ROLE = 3; + + const MAX_CONNS = 'MAX_CONNS'; + const MAX_REQS = 'MAX_REQS'; + const MPXS_CONNS = 'MPXS_CONNS'; + + const HEADER_LEN = 8; + + const REQ_STATE_WRITTEN = 1; + const REQ_STATE_OK = 2; + const REQ_STATE_ERR = 3; + const REQ_STATE_TIMED_OUT = 4; + + /** + * Socket + * @var Resource + */ + private $_sock = null; + + /** + * Host + * @var String + */ + private $_host = null; + + /** + * Port + * @var Integer + */ + private $_port = null; + + /** + * Keep Alive + * @var Boolean + */ + private $_keepAlive = false; + + /** + * Outstanding request statuses keyed by request id + * + * Each request is an array with following form: + * + * array( + * 'state' => REQ_STATE_* + * 'response' => null | string + * ) + * + * @var array + */ + private $_requests = array(); + + /** + * Use persistent sockets to connect to backend + * @var Boolean + */ + private $_persistentSocket = false; + + /** + * Connect timeout in milliseconds + * @var Integer + */ + private $_connectTimeout = 5000; + + /** + * Read/Write timeout in milliseconds + * @var Integer + */ + private $_readWriteTimeout = 5000; + + /** + * Constructor + * + * @param String $host Host of the FastCGI application + * @param Integer $port Port of the FastCGI application + */ + public function __construct($host, $port) + { + $this->_host = $host; + $this->_port = $port; + } + + /** + * Define whether or not the FastCGI application should keep the connection + * alive at the end of a request + * + * @param Boolean $b true if the connection should stay alive, false otherwise + */ + public function setKeepAlive($b) + { + $this->_keepAlive = (boolean)$b; + if (!$this->_keepAlive && $this->_sock) { + fclose($this->_sock); + } + } + + /** + * Get the keep alive status + * + * @return Boolean true if the connection should stay alive, false otherwise + */ + public function getKeepAlive() + { + return $this->_keepAlive; + } + + /** + * Define whether or not PHP should attempt to re-use sockets opened by previous + * request for efficiency + * + * @param Boolean $b true if persistent socket should be used, false otherwise + */ + public function setPersistentSocket($b) + { + $was_persistent = ($this->_sock && $this->_persistentSocket); + $this->_persistentSocket = (boolean)$b; + if (!$this->_persistentSocket && $was_persistent) { + fclose($this->_sock); + } + } + + /** + * Get the pesistent socket status + * + * @return Boolean true if the socket should be persistent, false otherwise + */ + public function getPersistentSocket() + { + return $this->_persistentSocket; + } + + + /** + * Set the connect timeout + * + * @param Integer number of milliseconds before connect will timeout + */ + public function setConnectTimeout($timeoutMs) + { + $this->_connectTimeout = $timeoutMs; + } + + /** + * Get the connect timeout + * + * @return Integer number of milliseconds before connect will timeout + */ + public function getConnectTimeout() + { + return $this->_connectTimeout; + } + + /** + * Set the read/write timeout + * + * @param Integer number of milliseconds before read or write call will timeout + */ + public function setReadWriteTimeout($timeoutMs) + { + $this->_readWriteTimeout = $timeoutMs; + $this->set_ms_timeout($this->_readWriteTimeout); + } + + /** + * Get the read timeout + * + * @return Integer number of milliseconds before read will timeout + */ + public function getReadWriteTimeout() + { + return $this->_readWriteTimeout; + } + + /** + * Helper to avoid duplicating milliseconds to secs/usecs in a few places + * + * @param Integer millisecond timeout + * @return Boolean + */ + private function set_ms_timeout($timeoutMs) { + if (!$this->_sock) { + return false; + } + return stream_set_timeout($this->_sock, floor($timeoutMs / 1000), ($timeoutMs % 1000) * 1000); + } + + + /** + * Create a connection to the FastCGI application + */ + private function connect() + { + if (!$this->_sock) { + if ($this->_persistentSocket) { + $this->_sock = pfsockopen($this->_host, $this->_port, $errno, $errstr, $this->_connectTimeout/1000); + } else { + $this->_sock = fsockopen($this->_host, $this->_port, $errno, $errstr, $this->_connectTimeout/1000); + } + + if (!$this->_sock) { + throw new \Exception('Unable to connect to FastCGI application: ' . $errstr); + } + + if (!$this->set_ms_timeout($this->_readWriteTimeout)) { + throw new \Exception('Unable to set timeout on socket'); + } + } + } + + /** + * Build a FastCGI packet + * + * @param Integer $type Type of the packet + * @param String $content Content of the packet + * @param Integer $requestId RequestId + */ + private function buildPacket($type, $content, $requestId = 1) + { + $clen = strlen($content); + return chr(self::VERSION_1) /* version */ + . chr($type) /* type */ + . chr(($requestId >> 8) & 0xFF) /* requestIdB1 */ + . chr($requestId & 0xFF) /* requestIdB0 */ + . chr(($clen >> 8 ) & 0xFF) /* contentLengthB1 */ + . chr($clen & 0xFF) /* contentLengthB0 */ + . chr(0) /* paddingLength */ + . chr(0) /* reserved */ + . $content; /* content */ + } + + /** + * Build an FastCGI Name value pair + * + * @param String $name Name + * @param String $value Value + * @return String FastCGI Name value pair + */ + private function buildNvpair($name, $value) + { + $nlen = strlen($name); + $vlen = strlen($value); + if ($nlen < 128) { + /* nameLengthB0 */ + $nvpair = chr($nlen); + } else { + /* nameLengthB3 & nameLengthB2 & nameLengthB1 & nameLengthB0 */ + $nvpair = chr(($nlen >> 24) | 0x80) . chr(($nlen >> 16) & 0xFF) . chr(($nlen >> 8) & 0xFF) . chr($nlen & 0xFF); + } + if ($vlen < 128) { + /* valueLengthB0 */ + $nvpair .= chr($vlen); + } else { + /* valueLengthB3 & valueLengthB2 & valueLengthB1 & valueLengthB0 */ + $nvpair .= chr(($vlen >> 24) | 0x80) . chr(($vlen >> 16) & 0xFF) . chr(($vlen >> 8) & 0xFF) . chr($vlen & 0xFF); + } + /* nameData & valueData */ + return $nvpair . $name . $value; + } + + /** + * Read a set of FastCGI Name value pairs + * + * @param String $data Data containing the set of FastCGI NVPair + * @return array of NVPair + */ + private function readNvpair($data, $length = null) + { + $array = array(); + + if ($length === null) { + $length = strlen($data); + } + + $p = 0; + + while ($p != $length) { + + $nlen = ord($data{$p++}); + if ($nlen >= 128) { + $nlen = ($nlen & 0x7F << 24); + $nlen |= (ord($data{$p++}) << 16); + $nlen |= (ord($data{$p++}) << 8); + $nlen |= (ord($data{$p++})); + } + $vlen = ord($data{$p++}); + if ($vlen >= 128) { + $vlen = ($nlen & 0x7F << 24); + $vlen |= (ord($data{$p++}) << 16); + $vlen |= (ord($data{$p++}) << 8); + $vlen |= (ord($data{$p++})); + } + $array[substr($data, $p, $nlen)] = substr($data, $p+$nlen, $vlen); + $p += ($nlen + $vlen); + } + + return $array; + } + + /** + * Decode a FastCGI Packet + * + * @param String $data String containing all the packet + * @return array + */ + private function decodePacketHeader($data) + { + $ret = array(); + $ret['version'] = ord($data{0}); + $ret['type'] = ord($data{1}); + $ret['requestId'] = (ord($data{2}) << 8) + ord($data{3}); + $ret['contentLength'] = (ord($data{4}) << 8) + ord($data{5}); + $ret['paddingLength'] = ord($data{6}); + $ret['reserved'] = ord($data{7}); + return $ret; + } + + /** + * Read a FastCGI Packet + * + * @return array + */ + private function readPacket() + { + if ($packet = fread($this->_sock, self::HEADER_LEN)) { + $resp = $this->decodePacketHeader($packet); + $resp['content'] = ''; + if ($resp['contentLength']) { + $len = $resp['contentLength']; + while ($len && $buf=fread($this->_sock, $len)) { + $len -= strlen($buf); + $resp['content'] .= $buf; + } + } + if ($resp['paddingLength']) { + $buf = fread($this->_sock, $resp['paddingLength']); + } + return $resp; + } else { + return false; + } + } + + /** + * Get Informations on the FastCGI application + * + * @param array $requestedInfo information to retrieve + * @return array + */ + public function getValues(array $requestedInfo) + { + $this->connect(); + + $request = ''; + foreach ($requestedInfo as $info) { + $request .= $this->buildNvpair($info, ''); + } + fwrite($this->_sock, $this->buildPacket(self::GET_VALUES, $request, 0)); + + $resp = $this->readPacket(); + if ($resp['type'] == self::GET_VALUES_RESULT) { + return $this->readNvpair($resp['content'], $resp['length']); + } else { + throw new \Exception('Unexpected response type, expecting GET_VALUES_RESULT'); + } + } + + /** + * Execute a request to the FastCGI application + * + * @param array $params Array of parameters + * @param String $stdin Content + * @return String + */ + public function request(array $params, $stdin) + { + $id = $this->async_request($params, $stdin); + return $this->wait_for_response($id); + } + + /** + * Execute a request to the FastCGI application asyncronously + * + * This sends request to application and returns the assigned ID for that request. + * + * You should keep this id for later use with wait_for_response(). Ids are chosen randomly + * rather than seqentially to guard against false-positives when using persistent sockets. + * In that case it is possible that a delayed response to a request made by a previous script + * invocation comes back on this socket and is mistaken for response to request made with same ID + * during this request. + * + * @param array $params Array of parameters + * @param String $stdin Content + * @return Integer + */ + public function async_request(array $params, $stdin) + { + $this->connect(); + + // Pick random number between 1 and max 16 bit unsigned int 65535 + $id = mt_rand(1, (1 << 16) - 1); + + // Using persistent sockets implies you want them keept alive by server! + $keepAlive = intval($this->_keepAlive || $this->_persistentSocket); + + $request = $this->buildPacket(self::BEGIN_REQUEST + ,chr(0) . chr(self::RESPONDER) . chr($keepAlive) . str_repeat(chr(0), 5) + ,$id + ); + + $paramsRequest = ''; + foreach ($params as $key => $value) { + $paramsRequest .= $this->buildNvpair($key, $value, $id); + } + if ($paramsRequest) { + $request .= $this->buildPacket(self::PARAMS, $paramsRequest, $id); + } + $request .= $this->buildPacket(self::PARAMS, '', $id); + + if ($stdin) { + $request .= $this->buildPacket(self::STDIN, $stdin, $id); + } + $request .= $this->buildPacket(self::STDIN, '', $id); + + if (fwrite($this->_sock, $request) === false || fflush($this->_sock) === false) { + + $info = stream_get_meta_data($this->_sock); + + if ($info['timed_out']) { + throw new TimedOutException('Write timed out'); + } + + // Broken pipe, tear down so future requests might succeed + fclose($this->_sock); + throw new \Exception('Failed to write request to socket'); + } + + $this->_requests[$id] = array( + 'state' => self::REQ_STATE_WRITTEN, + 'response' => null + ); + + return $id; + } + + /** + * Blocking call that waits for response to specific request + * + * @param Integer $requestId + * @param Integer $timeoutMs [optional] the number of milliseconds to wait. Defaults to the ReadWriteTimeout value set. + * @return string response body + */ + public function wait_for_response($requestId, $timeoutMs = 0) { + + if (!isset($this->_requests[$requestId])) { + throw new \Exception('Invalid request id given'); + } + + // If we already read the response during an earlier call for different id, just return it + if ($this->_requests[$requestId]['state'] == self::REQ_STATE_OK + || $this->_requests[$requestId]['state'] == self::REQ_STATE_ERR + ) { + return $this->_requests[$requestId]['response']; + } + + if ($timeoutMs > 0) { + // Reset timeout on socket for now + $this->set_ms_timeout($timeoutMs); + } else { + $timeoutMs = $this->_readWriteTimeout; + } + + // Need to manually check since we might do several reads none of which timeout themselves + // but still not get the response requested + $startTime = microtime(true); + + do { + $resp = $this->readPacket(); + + if ($resp['type'] == self::STDOUT || $resp['type'] == self::STDERR) { + if ($resp['type'] == self::STDERR) { + $this->_requests[$resp['requestId']]['state'] = self::REQ_STATE_ERR; + } + $this->_requests[$resp['requestId']]['response'] .= $resp['content']; + } + if ($resp['type'] == self::END_REQUEST) { + $this->_requests[$resp['requestId']]['state'] = self::REQ_STATE_OK; + if ($resp['requestId'] == $requestId) { + break; + } + } + if (microtime(true) - $startTime >= ($timeoutMs * 1000)) { + // Reset + $this->set_ms_timeout($this->_readWriteTimeout); + throw new \Exception('Timed out'); + } + } while ($resp); + + if (!is_array($resp)) { + $info = stream_get_meta_data($this->_sock); + + // We must reset timeout but it must be AFTER we get info + $this->set_ms_timeout($this->_readWriteTimeout); + + if ($info['timed_out']) { + throw new TimedOutException('Read timed out'); + } + + if ($info['unread_bytes'] == 0 + && $info['blocked'] + && $info['eof']) { + throw new ForbiddenException('Not in white list. Check listen.allowed_clients.'); + } + + throw new \Exception('Read failed'); + } + + // Reset timeout + $this->set_ms_timeout($this->_readWriteTimeout); + + switch (ord($resp['content']{4})) { + case self::CANT_MPX_CONN: + throw new \Exception('This app can\'t multiplex [CANT_MPX_CONN]'); + break; + case self::OVERLOADED: + throw new \Exception('New request rejected; too busy [OVERLOADED]'); + break; + case self::UNKNOWN_ROLE: + throw new \Exception('Role value not known [UNKNOWN_ROLE]'); + break; + case self::REQUEST_COMPLETE: + return $this->_requests[$requestId]['response']; + } + } +} diff --git a/sapi/fpm/tests/include.inc b/sapi/fpm/tests/include.inc index 983cbd345..b195fad50 100644 --- a/sapi/fpm/tests/include.inc +++ b/sapi/fpm/tests/include.inc @@ -76,4 +76,36 @@ function run_fpm_till($needle, $config, $max = 10) /* {{{ */ } /* }}} */ -?> +function fpm_display_log($tail, $n=1, $ignore='systemd') { + while ($n) { + $a = fgets($tail); + if (empty($ignore) || !strpos($a, $ignore)) { + echo $a; + $n--; + } + } +} + +function run_request($host, $port, $uri='/ping', $query='') { + require_once 'fcgi.inc'; + $client = new Adoy\FastCGI\Client($host, $port); + $params = array( + 'GATEWAY_INTERFACE' => 'FastCGI/1.0', + 'REQUEST_METHOD' => 'GET', + 'SCRIPT_FILENAME' => $uri, + 'SCRIPT_NAME' => $uri, + 'QUERY_STRING' => $query, + 'REQUEST_URI' => $uri . ($query ? '?'.$query : ""), + 'DOCUMENT_URI' => $uri, + 'SERVER_SOFTWARE' => 'php/fcgiclient', + 'REMOTE_ADDR' => '127.0.0.1', + 'REMOTE_PORT' => '9985', + 'SERVER_ADDR' => '127.0.0.1', + 'SERVER_PORT' => '80', + 'SERVER_NAME' => php_uname('n'), + 'SERVER_PROTOCOL' => 'HTTP/1.1', + 'CONTENT_TYPE' => '', + 'CONTENT_LENGTH' => 0 + ); + return $client->request($params, false)."\n"; +} |