The proxy delivers data. Your write callback returns the wrong value. libcurl then aborts, and the surrounding code reports another “proxy download failure.”

The callback must report how many received bytes it actually handled. Returning a different count signals failure. For a deliberately blocking writable sink, a fragment can look like:

curl_setopt($handle, CURLOPT_PROXY, 'http://proxy.example:8080');
curl_setopt($handle, CURLOPT_WRITEFUNCTION,
    function ($handle, $chunk) use ($sink) {
        $written = fwrite($sink, $chunk);
        return $written === false ? 0 : $written;
    }
);

This assumes the existing application owns the handle and writable sink. A failed or short write aborts rather than silently claiming the whole chunk was saved. A nonblocking sink needs an appropriate buffering and pause/resume design; the small fragment does not provide one.

Count bytes, not displayed characters. The data can be binary or part of a multibyte text sequence. One callback is not necessarily one line, JSON document, or message from the service.

Incremental parsing needs its own state across callbacks. Conversely, accumulating every chunk until completion may be correct for a small bounded response but is not a memory policy for a large or endless stream.

Keep the transfer result and callback failure evidence. A local disk error or parser abort can occur after a valid proxy route was established. If you intend to stop after a prefix, mark that deliberate callback stop separately. Replacing the proxy address cannot repair a sink that stopped accepting data.

Ũhoro ũhĩtĩte