Merge branch '5.0' into 5.1

* 5.0:
  [HttpClient][CurlHttpClient] Fix http_version option usage
  [HttpClient] fix parsing response headers in CurlResponse
  [Console] Do not check for "stty" using "exec" if that function is disabled
  [Console] always use stty when possible to ask hidden questions
This commit is contained in:
Nicolas Grekas 2020-07-03 20:06:49 +02:00
commit a815bc2b41
4 changed files with 51 additions and 64 deletions

View File

@ -36,6 +36,7 @@ class QuestionHelper extends Helper
private $inputStream;
private static $shell;
private static $stty = true;
private static $stdinIsInteractive;
/**
* Asks a question to the user.
@ -418,33 +419,26 @@ class QuestionHelper extends Helper
if (self::$stty && Terminal::hasSttyAvailable()) {
$sttyMode = shell_exec('stty -g');
shell_exec('stty -echo');
$value = fgets($inputStream, 4096);
} elseif ($this->isInteractiveInput($inputStream)) {
throw new RuntimeException('Unable to hide the response.');
}
$value = fgets($inputStream, 4096);
if (self::$stty && Terminal::hasSttyAvailable()) {
shell_exec(sprintf('stty %s', $sttyMode));
if (false === $value) {
throw new MissingInputException('Aborted.');
}
if ($trimmable) {
$value = trim($value);
}
$output->writeln('');
return $value;
}
if (false !== $shell = $this->getShell()) {
$readCmd = 'csh' === $shell ? 'set mypassword = $<' : 'read -r mypassword';
$command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword' 2> /dev/null", $shell, $readCmd);
$sCommand = shell_exec($command);
$value = $trimmable ? rtrim($sCommand) : $sCommand;
$output->writeln('');
return $value;
if (false === $value) {
throw new MissingInputException('Aborted.');
}
if ($trimmable) {
$value = trim($value);
}
$output->writeln('');
throw new RuntimeException('Unable to hide the response.');
return $value;
}
/**
@ -472,56 +466,35 @@ class QuestionHelper extends Helper
throw $e;
} catch (\Exception $error) {
}
$attempts = $attempts ?? -(int) $this->askForever();
}
throw $error;
}
/**
* Returns a valid unix shell.
*
* @return string|bool The valid shell name, false in case no valid shell is found
*/
private function getShell()
private function isInteractiveInput($inputStream): bool
{
if (null !== self::$shell) {
return self::$shell;
if ('php://stdin' !== (stream_get_meta_data($inputStream)['uri'] ?? null)) {
return false;
}
self::$shell = false;
if (file_exists('/usr/bin/env')) {
// handle other OSs with bash/zsh/ksh/csh if available to hide the answer
$test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null";
foreach (['bash', 'zsh', 'ksh', 'csh'] as $sh) {
if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) {
self::$shell = $sh;
break;
}
}
}
return self::$shell;
}
private function askForever(): bool
{
$inputStream = $this->inputStream ?: fopen('php://stdin', 'r');
if ('php://stdin' !== (stream_get_meta_data($inputStream)['url'] ?? null)) {
return true;
if (null !== self::$stdinIsInteractive) {
return self::$stdinIsInteractive;
}
if (\function_exists('stream_isatty')) {
return stream_isatty($inputStream);
return self::$stdinIsInteractive = stream_isatty(fopen('php://stdin', 'r'));
}
if (\function_exists('posix_isatty')) {
return posix_isatty($inputStream);
return self::$stdinIsInteractive = posix_isatty(fopen('php://stdin', 'r'));
}
return true;
if (!\function_exists('exec')) {
return self::$stdinIsInteractive = true;
}
exec('stty 2> /dev/null', $output, $status);
return self::$stdinIsInteractive = 1 !== $status;
}
}

View File

@ -66,6 +66,11 @@ class Terminal
return self::$stty;
}
// skip check if exec function is disabled
if (!\function_exists('exec')) {
return false;
}
exec('stty 2>&1', $output, $exitcode);
return self::$stty = 0 === $exitcode;

View File

@ -138,12 +138,12 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface,
CURLOPT_CERTINFO => $options['capture_peer_cert_chain'],
];
if (\defined('CURL_VERSION_HTTP2') && (CURL_VERSION_HTTP2 & self::$curlVersion['features']) && ('https:' === $scheme || 2.0 === (float) $options['http_version'])) {
$curlopts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0;
} elseif (1.0 === (float) $options['http_version']) {
if (1.0 === (float) $options['http_version']) {
$curlopts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0;
} elseif (1.1 === (float) $options['http_version']) {
$curlopts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1;
} elseif (\defined('CURL_VERSION_HTTP2') && (CURL_VERSION_HTTP2 & self::$curlVersion['features']) && ('https:' === $scheme || 2.0 === (float) $options['http_version'])) {
$curlopts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0;
}
if (isset($options['auth_ntlm'])) {

View File

@ -45,7 +45,6 @@ final class CurlResponse implements ResponseInterface
$this->multi = $multi;
if (\is_resource($ch) || $ch instanceof \CurlHandle) {
unset($multi->handlesActivity[(int) $ch]);
$this->handle = $ch;
$this->debugBuffer = fopen('php://temp', 'w+');
if (0x074000 === $curlVersion) {
@ -76,7 +75,17 @@ final class CurlResponse implements ResponseInterface
}
curl_setopt($ch, CURLOPT_HEADERFUNCTION, static function ($ch, string $data) use (&$info, &$headers, $options, $multi, $id, &$location, $resolveRedirect, $logger): int {
return self::parseHeaderLine($ch, $data, $info, $headers, $options, $multi, $id, $location, $resolveRedirect, $logger);
if (0 !== substr_compare($data, "\r\n", -2)) {
return 0;
}
$len = 0;
foreach (explode("\r\n", substr($data, 0, -2)) as $data) {
$len += 2 + self::parseHeaderLine($ch, $data, $info, $headers, $options, $multi, $id, $location, $resolveRedirect, $logger);
}
return $len;
});
if (null === $options) {
@ -319,10 +328,10 @@ final class CurlResponse implements ResponseInterface
return \strlen($data); // Ignore HTTP trailers
}
if ("\r\n" !== $data) {
if ('' !== $data) {
try {
// Regular header line: add it to the list
self::addResponseHeaders([substr($data, 0, -2)], $info, $headers);
self::addResponseHeaders([$data], $info, $headers);
} catch (TransportException $e) {
$multi->handlesActivity[$id][] = null;
$multi->handlesActivity[$id][] = $e;
@ -332,7 +341,7 @@ final class CurlResponse implements ResponseInterface
if (0 !== strpos($data, 'HTTP/')) {
if (0 === stripos($data, 'Location:')) {
$location = trim(substr($data, 9, -2));
$location = trim(substr($data, 9));
}
return \strlen($data);