From b168fc30d391846a79a656bbe878f032a0d5df5c Mon Sep 17 00:00:00 2001 From: Sara Golemon Date: Mon, 14 Sep 2020 20:08:39 +0000 Subject: [PATCH 1/2] Support ephemeral ports in debug server --- sapi/cli/php_cli_server.c | 100 +++++++++++++++++++++++++------------- 1 file changed, 65 insertions(+), 35 deletions(-) diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c index 337886bcd344a..b296f5c1139e3 100644 --- a/sapi/cli/php_cli_server.c +++ b/sapi/cli/php_cli_server.c @@ -2353,6 +2353,65 @@ static void php_cli_server_client_dtor_wrapper(zval *zv) /* {{{ */ pefree(p, 1); } /* }}} */ +/** + * Parse the host and port portions of an address specifier in + * one of the following forms: + * - hostOrIP:port + * - [hostOrIP]:port + */ +static char *php_cli_server_parse_addr(const char *addr, int *pport) { + const char *p, *end; + long port; + + if (addr[0] == '[') { + /* Encapsulated [hostOrIP]:port or just [hostOrIP] */ + const char *start = addr + 1; + end = strchr(start, ']'); + if (!end) { + /* No ending ] delimiter to match [ */ + return NULL; + } + + p = end + 1; + if (*p != ':') { + /* Invalid char following address/missing port */ + return NULL; + } + + port = strtol(p + 1, (char**)&p, 10); + if (p && *p) { + /* Non-numeric in port */ + return NULL; + } + if (port < 0 || port > 65535) { + /* Invalid port */ + return NULL; + } + + /* Full [hostOrIP]:port provided */ + *pport = (int)port; + return pestrndup(start, end - start, 1); + } + + end = strchr(addr, ':'); + if (!end) { + /* mising port */ + return NULL; + } + + port = strtol(end + 1, (char**)&p, 10); + if (p && *p) { + /* Non-numeric port */ + return NULL; + } + if (port < 0 || port > 65535) { + /* Invalid port */ + return NULL; + } + *pport = (int)port; + return pestrndup(addr, end - addr, 1); +} + static int php_cli_server_ctor(php_cli_server *server, const char *addr, const char *document_root, const char *router) /* {{{ */ { int retval = SUCCESS; @@ -2363,40 +2422,9 @@ static int php_cli_server_ctor(php_cli_server *server, const char *addr, const c int err = 0; int port = 3000; php_socket_t server_sock = SOCK_ERR; - char *p = NULL; - if (addr[0] == '[') { - host = pestrdup(addr + 1, 1); - if (!host) { - return FAILURE; - } - p = strchr(host, ']'); - if (p) { - *p++ = '\0'; - if (*p == ':') { - port = strtol(p + 1, &p, 10); - if (port <= 0 || port > 65535) { - p = NULL; - } - } else if (*p != '\0') { - p = NULL; - } - } - } else { - host = pestrdup(addr, 1); - if (!host) { - return FAILURE; - } - p = strchr(host, ':'); - if (p) { - *p++ = '\0'; - port = strtol(p, &p, 10); - if (port <= 0 || port > 65535) { - p = NULL; - } - } - } - if (!p) { + host = php_cli_server_parse_addr(addr, &port); + if (!host) { fprintf(stderr, "Invalid address: %s\n", addr); retval = FAILURE; goto out; @@ -2720,10 +2748,12 @@ int do_cli_server(int argc, char **argv) /* {{{ */ sapi_module.phpinfo_as_text = 0; { + zend_bool ipv6 = strchr(server.host, ':'); php_cli_server_logf( PHP_CLI_SERVER_LOG_PROCESS, - "PHP %s Development Server (http://%s) started", - PHP_VERSION, server_bind_address); + "PHP %s Development Server (http://%s%s%s:%d) started", + PHP_VERSION, ipv6 ? "[" : "", server.host, + ipv6 ? "]" : "", server.port); } #if defined(SIGINT) From 9b4cac96e3762db86f3a2f402362699459e1dd5c Mon Sep 17 00:00:00 2001 From: Sara Golemon Date: Mon, 14 Sep 2020 20:45:39 +0000 Subject: [PATCH 2/2] Use ephemeral ports during curl tests with dev server --- ext/curl/tests/CONFLICTS | 1 - ext/curl/tests/bug79033.phpt | 2 +- ext/curl/tests/server.inc | 43 ++++++++++++++++++++++++++++-------- 3 files changed, 35 insertions(+), 11 deletions(-) delete mode 100644 ext/curl/tests/CONFLICTS diff --git a/ext/curl/tests/CONFLICTS b/ext/curl/tests/CONFLICTS deleted file mode 100644 index 254defddb53c5..0000000000000 --- a/ext/curl/tests/CONFLICTS +++ /dev/null @@ -1 +0,0 @@ -server diff --git a/ext/curl/tests/bug79033.phpt b/ext/curl/tests/bug79033.phpt index 454c3b2669dfe..c70611dda602f 100644 --- a/ext/curl/tests/bug79033.phpt +++ b/ext/curl/tests/bug79033.phpt @@ -21,7 +21,7 @@ var_dump(curl_getinfo($ch)["request_header"]); string(%d) "array(0) { } " -string(90) "POST /get.inc?test=post HTTP/1.1 +string(%d) "POST /get.inc?test=post HTTP/1.1 Host: localhost:%d Accept: */* Content-Length: 0 diff --git a/ext/curl/tests/server.inc b/ext/curl/tests/server.inc index 00eeb5ff76683..3051bf98a4c1d 100644 --- a/ext/curl/tests/server.inc +++ b/ext/curl/tests/server.inc @@ -1,8 +1,4 @@ - STDIN, 1 => STDOUT, - 2 => array("null"), + 2 => ['pipe', 'w'], ); $handle = proc_open($cmd, $descriptorspec, $pipes, $doc_root, null, array("suppress_errors" => true)); + // First, wait for the dev server to declare itself ready. + $bound = null; + stream_set_blocking($pipes[2], false); + for ($i = 0; $i < 60; $i++) { + usleep(50000); // 50ms per try + $status = proc_get_status($handle); + if (empty($status['running'])) { + echo "Server is not running\n"; + proc_terminate($handle); + exit(1); + } + + while (($line = fgets($pipes[2])) !== false) { + if (preg_match('@PHP \S* Development Server \(https?://(.*?:\d+)\) started@', $line, $matches)) { + $bound = $matches[1]; + // Now that we've identified the listen address, close STDERR. + // Otherwise the pipe may clog up with unread log messages. + fclose($pipes[2]); + break 2; + } + } + } + if ($bound === null) { + echo "Server did not output startup message"; + proc_terminate($handle); + exit(1); + } + + // Now wait for a connection to succeed. // note: even when server prints 'Listening on localhost:8964...Press Ctrl-C to quit.' // it might not be listening yet...need to wait until fsockopen() call returns $error = "Unable to connect to server\n"; for ($i=0; $i < 60; $i++) { usleep(50000); // 50ms per try $status = proc_get_status($handle); - $fp = @fsockopen(PHP_CURL_SERVER_HOSTNAME, PHP_CURL_SERVER_PORT); + $fp = @fsockopen("tcp://$bound"); // Failure, the server is no longer running if (!($status && $status['running'])) { $error = "Server is not running\n"; @@ -64,5 +89,5 @@ function curl_cli_server_start() { $handle ); - return PHP_CURL_SERVER_ADDRESS; + return $bound; }