[CurlOne] Synchronous request lifecycle completion (perform & getData)

Michael via curl-and-php <[email protected]> Thu, 30 Jul 2026 01:51:36 +0500
Newsgroups gmane.comp.web.curl.php
Message-ID <CAMVeLLL3D7hwdavE_9GZ=fJ+5rXEo+JW7D94r2KbJx7yc04CBA@mail.gmail.com>
--===============3310780164404734478==
Content-Type: multipart/alternative; boundary="0000000000006a4d240657c62613"

--0000000000006a4d240657c62613
Content-Type: text/plain; charset="UTF-8"

Completion of the synchronous easy handle lifecycle requires calling
perform() method (~curl_exec) to execute the transfer before getData() (~no
analogue) can assemble and return the response body.

Analysis vs. bundled ext/curl:
The standard ext/curl extension included with PHP handles response bodies
under CURLOPT_RETURNTRANSFER by coupling directly to PHP's engine-level
smart_str API inside an internal write callback. As packet chunks arrive,
smart_str repeatedly grows a single zend_string via erealloc. Upon
completion, ownership of that allocation is handed directly to PHP userland
with zero additional copying.

CurlOne decouples from the core PHP smart_str module to remove engine
dependencies. It implements payload buffering via a singly-linked list of
16-byte aligned heap chunks (curlDataChunk_t) appended during
CURLOPT_WRITEFUNCTION. Assembly into a single contiguous php_string_t is
deferred until getData() is called, while perform() resets buffer state
ahead of subsequent transfers.

Trade-offs:

* Memory Allocation Strategy: Bundled ext/curl incurs transfer-time heap
reallocations to keep data contiguous, achieving zero-copy retrieval.
CurlOne trades transfer-time reallocation for predictable single-chunk
allocations, paying the penalty of a secondary sequential memcpy pass
across the list when constructing the final string in getData().
* libcurl Architectural Limits: Unlike its header iterator
(curl_easy_nextheader()), libcurl offers no native internal payload
accumulator. CurlOne must therefore retain a custom CURLOPT_WRITEFUNCTION
callback, incurring per-packet function call churn and an intermediate
copy. A native libcurl accumulation option would eliminate both this
callback layer and the secondary assembly pass.

Option Differences:

* CURLOPT_WRITEFUNCTION / CURLOPT_WRITEDATA: Bound internally to
CurlOne_responseSet(). Can be remapped to a userland callback with
setDataReader().
* CURLOPT_RETURNTRANSFER: Omitted entirely from the option matrix; payload
retrieval is explicitly managed via $client->getData().

--0000000000006a4d240657c62613
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr">Completion of the synchronous easy handle lifecycle requir=
es calling perform() method (~curl_exec) to execute the transfer before get=
Data() (~no analogue) can assemble and return the response body.<br><br>Ana=
lysis vs. bundled ext/curl:<br>The standard ext/curl extension included wit=
h PHP handles response bodies under CURLOPT_RETURNTRANSFER by coupling dire=
ctly to PHP&#39;s engine-level smart_str API inside an internal write callb=
ack. As packet chunks arrive, smart_str repeatedly grows a single zend_stri=
ng via erealloc. Upon completion, ownership of that allocation is handed di=
rectly to PHP userland with zero additional copying.<br><br>CurlOne decoupl=
es from the core PHP smart_str module to remove engine dependencies. It imp=
lements payload buffering via a singly-linked list of 16-byte aligned heap =
chunks (curlDataChunk_t) appended during CURLOPT_WRITEFUNCTION. Assembly in=
to a single contiguous php_string_t is deferred until getData() is called, =
while perform() resets buffer state ahead of subsequent transfers.<br><br>T=
rade-offs:<br><br>* Memory Allocation Strategy: Bundled ext/curl incurs tra=
nsfer-time heap reallocations to keep data contiguous, achieving zero-copy =
retrieval. CurlOne trades transfer-time reallocation for predictable single=
-chunk allocations, paying the penalty of a secondary sequential memcpy pas=
s across the list when constructing the final string in getData().<br>* lib=
curl Architectural Limits: Unlike its header iterator (curl_easy_nextheader=
()), libcurl offers no native internal payload accumulator. CurlOne must th=
erefore retain a custom CURLOPT_WRITEFUNCTION callback, incurring per-packe=
t function call churn and an intermediate copy. A native libcurl accumulati=
on option would eliminate both this callback layer and the secondary assemb=
ly pass.<br><br>Option Differences:<br><br>* CURLOPT_WRITEFUNCTION / CURLOP=
T_WRITEDATA: Bound internally to CurlOne_responseSet(). Can be remapped to =
a userland callback with setDataReader().<br>* CURLOPT_RETURNTRANSFER: Omit=
ted entirely from the option matrix; payload retrieval is explicitly manage=
d via $client-&gt;getData().</div>

--0000000000006a4d240657c62613--

--===============3310780164404734478==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

-- 
curl-and-php mailing list
[email protected]
https://lists.haxx.se/mailman/listinfo/curl-and-php

--===============3310780164404734478==--