<commit_before><?php

/*
 * This file is part of the Symfony package.
 *
 * (c) <NAME> <<EMAIL>>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\HttpClient\Response;

use Psr\Log\LoggerInterface;
use Symfony\Component\HttpClient\Chunk\FirstChunk;
use Symfony\Component\HttpClient\Exception\TransportException;
use Symfony\Component\HttpClient\Internal\CurlClientState;
use Symfony\Contracts\HttpClient\ResponseInterface;

/**
 * @author <NAME> <<EMAIL>>
 *
 * @internal
 */
final class CurlResponse implements ResponseInterface
{
    use ResponseTrait;

    private static $performing = false;
    private $multi;
    private $debugBuffer;

    /**
     * @internal
     */
    public function __construct(CurlClientState $multi, $ch, array $options = null, LoggerInterface $logger = null, string $method = 'GET', callable $resolveRedirect = null)
    {
        $this->multi = $multi;

        if (\is_resource($ch)) {
            $this->handle = $ch;
            $this->debugBuffer = fopen('php://temp', 'w+');
            curl_setopt($ch, CURLOPT_VERBOSE, true);
            curl_setopt($ch, CURLOPT_STDERR, $this->debugBuffer);
        } else {
            $this->info['url'] = $ch;
            $ch = $this->handle;
        }

        $this->id = $id = (int) $ch;
        $this->logger = $logger;
        $this->timeout = $options['timeout'] ?? null;
        $this->info['http_method'] = $method;
        $this->info['user_data'] = $options['user_data'] ?? null;
        $this->info['start_time'] = $this->info['start_time'] ?? microtime(true);
        $info = &$this->info;
        $headers = &$this->headers;

        if (!$info['response_headers']) {
            // Used to keep track of what we're waiting for
            curl_setopt($ch, CURLOPT_PRIVATE, 'headers');
        }

        if (null === $content = &$this->content) {
            $content = ($options['buffer'] ?? true) ? fopen('php://temp', 'w+') : null;
        } else {
            // Move the pushed response to the activity list
            if (ftell($content)) {
                rewind($content);
                $multi->handlesActivity[$id][] = stream_get_contents($content);
            }
            $content = ($options['buffer'] ?? true) ? $content : null;
        }

        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 (null === $options) {
            // Pushed response: buffer until requested
            curl_setopt($ch, CURLOPT_WRITEFUNCTION, static function ($ch, string $data) use (&$content): int {
                return fwrite($content, $data);
            });

            return;
        }

        if ($onProgress = $options['on_progress']) {
            $url = isset($info['url']) ? ['url' => $info['url']] : [];
            curl_setopt($ch, CURLOPT_NOPROGRESS, false);
            curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, static function ($ch, $dlSize, $dlNow) use ($onProgress, &$info, $url, $multi) {
                try {
                    $onProgress($dlNow, $dlSize, $url + curl_getinfo($ch) + $info);
                } catch (\Throwable $e) {
                    $multi->handlesActivity[(int) $ch][] = null;
                    $multi->handlesActivity[(int) $ch][] = $e;

                    return 1; // Abort the request
                }
            });
        }

        curl_setopt($ch, CURLOPT_WRITEFUNCTION, static function ($ch, string $data) use (&$content, $multi, $id): int {
            $multi->handlesActivity[$id][] = $data;

            return null !== $content ? fwrite($content, $data) : \strlen($data);
        });

        $this->initializer = static function (self $response) {
            if (null !== $response->info['error']) {
                throw new TransportException($response->info['error']);
            }

            $waitFor = curl_getinfo($ch = $response->handle, CURLINFO_PRIVATE);

            if (\in_array($waitFor, ['headers', 'destruct'], true)) {
                try {
                    if (\defined('CURLOPT_STREAM_WEIGHT')) {
                        curl_setopt($ch, CURLOPT_STREAM_WEIGHT, 32);
                    }
                    self::stream([$response])->current();
                } catch (\Throwable $e) {
                    // Persist timeouts thrown during initialization
                    $response->info['error'] = $e->getMessage();
                    $response->close();
                    throw $e;
                }
            } elseif ('content' === $waitFor && ($response->multi->handlesActivity[$response->id][0] ?? null) instanceof FirstChunk) {
                self::stream([$response])->current();
            }

            curl_setopt($ch, CURLOPT_HEADERFUNCTION, null);
            curl_setopt($ch, CURLOPT_READFUNCTION, null);
            curl_setopt($ch, CURLOPT_INFILE, null);
        };

        // Schedule the request in a non-blocking way
        $multi->openHandles[$id] = $ch;
        curl_multi_add_handle($multi->handle, $ch);
        self::perform($multi);
    }

    /**
     * {@inheritdoc}
     */
    public function getInfo(string $type = null)
    {
        if (!$info = $this->finalInfo) {
            self::perform($this->multi);

            if ('debug' === $type) {
                rewind($this->debugBuffer);

                return stream_get_contents($this->debugBuffer);
            }

            $info = array_merge($this->info, curl_getinfo($this->handle));
            $info['url'] = $this->info['url'] ?? $info['url'];
            $info['redirect_url'] = $this->info['redirect_url'] ?? null;

            // workaround curl not subtracting the time offset for pushed responses
            if (isset($this->info['url']) && $info['start_time'] / 1000 < $info['total_time']) {
                $info['total_time'] -= $info['starttransfer_time'] ?: $info['total_time'];
                $info['starttransfer_time'] = 0.0;
            }

            if (!\in_array(curl_getinfo($this->handle, CURLINFO_PRIVATE), ['headers', 'content'], true)) {
                rewind($this->debugBuffer);
                $info['debug'] = stream_get_contents($this->debugBuffer);
                curl_setopt($this->handle, CURLOPT_VERBOSE, false);
                fclose($this->debugBuffer);
                $this->debugBuffer = null;
                $this->finalInfo = $info;
            }
        }

        return null !== $type ? $info[$type] ?? null : $info;
    }

    public function __destruct()
    {
        try {
            if (null === $this->timeout) {
                return; // Unused pushed response
            }

            if ('content' === $waitFor = curl_getinfo($this->handle, CURLINFO_PRIVATE)) {
                $this->close();
            } elseif ('headers' === $waitFor) {
                curl_setopt($this->handle, CURLOPT_PRIVATE, 'destruct');
            }

            $this->doDestruct();
        } finally {
            $this->close();

            // Clear local caches when the only remaining handles are about pushed responses
            if (!$this->multi->openHandles) {<commit_msg>[HttpClient] fix closing debug stream prematurely<commit_after><?php

/*
 * This file is part of the Symfony package.
 *
 * (c) <NAME> <<EMAIL>>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\HttpClient\Response;

use Psr\Log\LoggerInterface;
use Symfony\Component\HttpClient\Chunk\FirstChunk;
use Symfony\Component\HttpClient\Exception\TransportException;
use Symfony\Component\HttpClient\Internal\CurlClientState;
use Symfony\Contracts\HttpClient\ResponseInterface;

/**
 * @author <NAME> <<EMAIL>>
 *
 * @internal
 */
final class CurlResponse implements ResponseInterface
{
    use ResponseTrait;

    private static $performing = false;
    private $multi;
    private $debugBuffer;

    /**
     * @internal
     */
    public function __construct(CurlClientState $multi, $ch, array $options = null, LoggerInterface $logger = null, string $method = 'GET', callable $resolveRedirect = null)
    {
        $this->multi = $multi;

        if (\is_resource($ch)) {
            $this->handle = $ch;
            $this->debugBuffer = fopen('php://temp', 'w+');
            curl_setopt($ch, CURLOPT_VERBOSE, true);
            curl_setopt($ch, CURLOPT_STDERR, $this->debugBuffer);
        } else {
            $this->info['url'] = $ch;
            $ch = $this->handle;
        }

        $this->id = $id = (int) $ch;
        $this->logger = $logger;
        $this->timeout = $options['timeout'] ?? null;
        $this->info['http_method'] = $method;
        $this->info['user_data'] = $options['user_data'] ?? null;
        $this->info['start_time'] = $this->info['start_time'] ?? microtime(true);
        $info = &$this->info;
        $headers = &$this->headers;

        if (!$info['response_headers']) {
            // Used to keep track of what we're waiting for
            curl_setopt($ch, CURLOPT_PRIVATE, 'headers');
        }

        if (null === $content = &$this->content) {
            $content = ($options['buffer'] ?? true) ? fopen('php://temp', 'w+') : null;
        } else {
            // Move the pushed response to the activity list
            if (ftell($content)) {
                rewind($content);
                $multi->handlesActivity[$id][] = stream_get_contents($content);
            }
            $content = ($options['buffer'] ?? true) ? $content : null;
        }

        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 (null === $options) {
            // Pushed response: buffer until requested
            curl_setopt($ch, CURLOPT_WRITEFUNCTION, static function ($ch, string $data) use (&$content): int {
                return fwrite($content, $data);
            });

            return;
        }

        if ($onProgress = $options['on_progress']) {
            $url = isset($info['url']) ? ['url' => $info['url']] : [];
            curl_setopt($ch, CURLOPT_NOPROGRESS, false);
            curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, static function ($ch, $dlSize, $dlNow) use ($onProgress, &$info, $url, $multi) {
                try {
                    $onProgress($dlNow, $dlSize, $url + curl_getinfo($ch) + $info);
                } catch (\Throwable $e) {
                    $multi->handlesActivity[(int) $ch][] = null;
                    $multi->handlesActivity[(int) $ch][] = $e;

                    return 1; // Abort the request
                }
            });
        }

        curl_setopt($ch, CURLOPT_WRITEFUNCTION, static function ($ch, string $data) use (&$content, $multi, $id): int {
            $multi->handlesActivity[$id][] = $data;

            return null !== $content ? fwrite($content, $data) : \strlen($data);
        });

        $this->initializer = static function (self $response) {
            if (null !== $response->info['error']) {
                throw new TransportException($response->info['error']);
            }

            $waitFor = curl_getinfo($ch = $response->handle, CURLINFO_PRIVATE);

            if (\in_array($waitFor, ['headers', 'destruct'], true)) {
                try {
                    if (\defined('CURLOPT_STREAM_WEIGHT')) {
                        curl_setopt($ch, CURLOPT_STREAM_WEIGHT, 32);
                    }
                    self::stream([$response])->current();
                } catch (\Throwable $e) {
                    // Persist timeouts thrown during initialization
                    $response->info['error'] = $e->getMessage();
                    $response->close();
                    throw $e;
                }
            } elseif ('content' === $waitFor && ($response->multi->handlesActivity[$response->id][0] ?? null) instanceof FirstChunk) {
                self::stream([$response])->current();
            }

            curl_setopt($ch, CURLOPT_HEADERFUNCTION, null);
            curl_setopt($ch, CURLOPT_READFUNCTION, null);
            curl_setopt($ch, CURLOPT_INFILE, null);
        };

        // Schedule the request in a non-blocking way
        $multi->openHandles[$id] = $ch;
        curl_multi_add_handle($multi->handle, $ch);
        self::perform($multi);
    }

    /**
     * {@inheritdoc}
     */
    public function getInfo(string $type = null)
    {
        if (!$info = $this->finalInfo) {
            self::perform($this->multi);

            if ('debug' === $type) {
                rewind($this->debugBuffer);

                return stream_get_contents($this->debugBuffer);
            }

            $info = array_merge($this->info, curl_getinfo($this->handle));
            $info['url'] = $this->info['url'] ?? $info['url'];
            $info['redirect_url'] = $this->info['redirect_url'] ?? null;

            // workaround curl not subtracting the time offset for pushed responses
            if (isset($this->info['url']) && $info['start_time'] / 1000 < $info['total_time']) {
                $info['total_time'] -= $info['starttransfer_time'] ?: $info['total_time'];
                $info['starttransfer_time'] = 0.0;
            }

            if (!\in_array(curl_getinfo($this->handle, CURLINFO_PRIVATE), ['headers', 'content'], true)) {
                rewind($this->debugBuffer);
                $info['debug'] = stream_get_contents($this->debugBuffer);
                curl_setopt($this->handle, CURLOPT_VERBOSE, false);
                rewind($this->debugBuffer);
                ftruncate($this->debugBuffer, 0);
                $this->finalInfo = $info;
            }
        }

        return null !== $type ? $info[$type] ?? null : $info;
    }

    public function __destruct()
    {
        try {
            if (null === $this->timeout) {
                return; // Unused pushed response
            }

            if ('content' === $waitFor = curl_getinfo($this->handle, CURLINFO_PRIVATE)) {
                $this->close();
            } elseif ('headers' === $waitFor) {
                curl_setopt($this->handle, CURLOPT_PRIVATE, 'destruct');
            }

            $this->doDestruct();
        } finally {
            $this->close();

            // Clear local caches when the only remaining handles are about pushed responses
            if (!$this->multi->openHandles) {
{'fraction_non_alphanumeric': 0.1560503127732562, 'fraction_numerical': 0.0014125243828613707, 'mean_word_length': 2.0674644109758615, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 133, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2558071', 'n_tokens_mistral': 4316, 'n_tokens_neox': 4092, 'n_words': 911}