Skip to content

Support ephemeral ports in dev server and allow cURL tests to parallelize #6132

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion ext/curl/tests/CONFLICTS

This file was deleted.

2 changes: 1 addition & 1 deletion ext/curl/tests/bug79033.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
43 changes: 34 additions & 9 deletions ext/curl/tests/server.inc
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
<?php

define ("PHP_CURL_SERVER_HOSTNAME", "localhost");
define ("PHP_CURL_SERVER_PORT", 8964);
define ("PHP_CURL_SERVER_ADDRESS", PHP_CURL_SERVER_HOSTNAME.":".PHP_CURL_SERVER_PORT);
<?php declare(strict_types=1);

function curl_cli_server_start() {
if(getenv('PHP_CURL_HTTP_REMOTE_SERVER')) {
Expand All @@ -12,21 +8,50 @@ function curl_cli_server_start() {
$php_executable = getenv('TEST_PHP_EXECUTABLE');
$doc_root = __DIR__;
$router = "responder/get.inc";
$cmd = [$php_executable, '-t', $doc_root, '-n', '-S', PHP_CURL_SERVER_ADDRESS, $router];
$cmd = [$php_executable, '-t', $doc_root, '-n', '-S', 'localhost:0', $router];
$descriptorspec = array(
0 => 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";
Expand Down Expand Up @@ -64,5 +89,5 @@ function curl_cli_server_start() {
$handle
);

return PHP_CURL_SERVER_ADDRESS;
return $bound;
}
100 changes: 65 additions & 35 deletions sapi/cli/php_cli_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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)
Expand Down