[CurlOne] Architecture Review

Michael via curl-and-php <[email protected]> Sun, 2 Aug 2026 06:14:34 +0500
Newsgroups gmane.comp.web.curl.php
Message-ID <CAMVeLLK0kdfPaYS=E-sf7A73YvOzihyU2EbmvthweLizr27apw@mail.gmail.com>
--===============7071117432852861216==
Content-Type: multipart/alternative; boundary="0000000000004d952d0658062cf5"

--0000000000004d952d0658062cf5
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

The core objective of this design is to encapsulate queue management and
loop mechanics within the C layer, avoiding the need to manage loops or
handle-polling in PHP userland.

The engine centers around a single global multi-handle initialized at
extension startup. Individual transfers are managed via instances of the
internal C structure, which encapsulates the underlying libcurl easy
handle, option configurations, and completion callbacks.


--- CORE LIFECYCLE & MUTABILITY LOCKS ---

Upon CurlOne object instantiation, the extension initializes a libcurl easy
handle, sets default parameters, attaches an internal write memory
callback, and binds a direct pointer of the internal structure to the
handle using the private data option.

When execution is initiated from userland, the engine verifies that the
target handle is idle, applies an active state lock to prevent option
mutations or duplicate execution calls mid-flight, and registers the easy
handle with the global multi-handle. The engine retains internal references
to both the request object and its completion closure, ensuring active
transfers are protected from premature garbage collection while floating in
the loop.


--- GLOBAL TICKER & EVENT LOOP MECHANICS ---

Transfers are driven by a single global execution ticker function
(curl_execute) designed to be invoked periodically from userland. The
execution loop operates across three key phases:

1. CPU Yielding: When no handles are pending, the engine invokes libcurl's
multi-wait function. Compared to multi-poll which introduces extra flag
evaluation overhead, multi-wait provides a thinner abstraction.

2. Transfer Processing: Non-blocking socket I/O is driven across all active
handles using the libcurl multi-perform API.

3. Completion Queue Drainage: The engine reads completion messages, unlinks
finished handles, releases state locks, and executes registered callbacks.


To interface with userland async primitives, the execution ticker returns
explicit status codes:

* Return code -1 indicates a fatal engine or multiplexer error.

* Return code 0 signals that transfers remain active.

* Return code 1 indicates that all queued transfers have fully completed.


--- ZERO-LOOKUP EVENT ROUTING ---

A primary advantage of this architecture over the legacy ext/curl
implementation is the total elimination of event-routing lookup churn.

In legacy ext/curl, handling completed transfers requires double-lookup
overhead. Polling surfaces completion messages through multi-info-read API,
which constructs associative PHP arrays and traverses internal C-level
linked lists to expose the raw handle to userland. Userland code must then
perform a second lookup pass=E2=80=94typically across a PHP array or regist=
ry=E2=80=94to
map that handle back to its surrounding asynchronous wrapper object.

The new design bypasses both search phases. Because a direct structure
pointer is bound to the easy handle at setup using the private data option,
the execution ticker extracts the original instance pointer in O(1)
constant time upon completion. The engine immediately retrieves the stored
closure, resets state flags, and invokes the callback directly. Event
routing search passes are reduced from two down to zero.


--- FEATURE BORROWING & DATA HANDLING ---

Rather than re-implementing payload parsing or utility functions in custom
C code, the extension delegates heavy lifting directly to libcurl APIs:

- Unified Form Data via CurlOneFile: Legacy PHP ext/curl separates file
payloads into distinct CURLFile and CURLStringFile userland objects. This
extension unifies both paradigms into a single CurlOneFile helper.
Multipart forms set up through setFormData() construct native curl_mime
structure trees. Depending on whether CurlOneFile references a persistent
file path or an in-memory buffer, the C layer delegates streaming and
cleanup directly to curl_mime_filedata() or curl_mime_data(), allowing
libcurl to manage file lifetime, disk I/O, filename tagging, and MIME
headers natively.

- URL Query Assembly: Instead of constructing URL-encoded POST strings or
query parameters manually, setUrlData() offloads string encoding, delimiter
formatting, and key-value concatenation entirely to libcurl's URL API via
curl_url() and curl_url_set(). Passing CURLU_APPENDQUERY and
CURLU_URLENCODE flags offloads parameter escaping and memory allocation
directly to C-level libcurl primitives before binding the output to
CURLOPT_POSTFIELDS.

- Zero-Copy Request Payload Management: Raw string payloads set via
setRawData() increment the internal reference count of the PHP string and
pass byte pointers directly to CURLOPT_POSTFIELDS. Binary blob options
utilize CURL_BLOB_NOCOPY to anchor userland buffers directly without
duplicating heap memory.

- Header Parsing API: Response header extraction via getHeaders() bypasses
legacy manual header parsing callbacks. It utilizes libcurl's header API
(curl_easy_nextheader), allowing targeted extraction across request origins
(such as intermediate 1xx responses, CONNECT proxy headers, or trailers)
directly from C memory structures.


--- MEMORY ARCHITECTURE & RESPONSE HANDLING ---

Response payload handling is deferred to optimize throughput. Incoming data
frames are appended to a linked list of heap-allocated memory chunks
(curlDataChunk_t) during transfer. Allocation of a single contiguous PHP
string occurs only when userland explicitly requests the body via
getData(), at which point the engine flattens the chunk list into the final
string and frees the backing nodes.

Overall, this refactoring yields a significantly leaner codebase and
smaller binary footprint while dramatically reducing language-boundary
overhead during event dispatching.

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

<div dir=3D"ltr"><div>The core objective of this design is to encapsulate q=
ueue management and loop mechanics within the C layer, avoiding the need to=
 manage loops or handle-polling in PHP userland.<br><br>The engine centers =
around a single global multi-handle initialized at extension startup. Indiv=
idual transfers are managed via instances of the internal C structure, whic=
h encapsulates the underlying libcurl easy handle, option configurations, a=
nd completion callbacks.<br><br><br>--- CORE LIFECYCLE &amp; MUTABILITY LOC=
KS ---<br><br>Upon CurlOne object instantiation, the extension initializes =
a libcurl easy handle, sets default parameters, attaches an internal write =
memory callback, and binds a direct pointer of the internal structure to th=
e handle using the private data option.<br><br>When execution is initiated =
from userland, the engine verifies that the target handle is idle, applies =
an active state lock to prevent option mutations or duplicate execution cal=
ls mid-flight, and registers the easy handle with the global multi-handle. =
The engine retains internal references to both the request object and its c=
ompletion closure, ensuring active transfers are protected from premature g=
arbage collection while floating in the loop.<br><br><br>--- GLOBAL TICKER =
&amp; EVENT LOOP MECHANICS ---<br><br>Transfers are driven by a single glob=
al execution ticker function (curl_execute) designed to be invoked periodic=
ally from userland. The execution loop operates across three key phases:<br=
><br>1. CPU Yielding: When no handles are pending, the engine invokes libcu=
rl&#39;s multi-wait function. Compared to multi-poll which introduces extra=
 flag evaluation overhead, multi-wait provides a thinner abstraction.<br><b=
r>2. Transfer Processing: Non-blocking socket I/O is driven across all acti=
ve handles using the libcurl multi-perform API.<br><br>3. Completion Queue =
Drainage: The engine reads completion messages, unlinks finished handles, r=
eleases state locks, and executes registered callbacks.<br><br><br>To inter=
face with userland async primitives, the execution ticker returns explicit =
status codes:<br><br>* Return code -1 indicates a fatal engine or multiplex=
er error.<br><br>* Return code 0 signals that transfers remain active.<br><=
br>* Return code 1 indicates that all queued transfers have fully completed=
.<br><br><br>--- ZERO-LOOKUP EVENT ROUTING ---<br><br>A primary advantage o=
f this architecture over the legacy ext/curl implementation is the total el=
imination of event-routing lookup churn.<br><br>In legacy ext/curl, handlin=
g completed transfers requires double-lookup overhead. Polling surfaces com=
pletion messages through multi-info-read API, which constructs associative =
PHP arrays and traverses internal C-level linked lists to expose the raw ha=
ndle to userland. Userland code must then perform a second lookup pass=E2=
=80=94typically across a PHP array or registry=E2=80=94to map that handle b=
ack to its surrounding asynchronous wrapper object.<br><br>The new design b=
ypasses both search phases. Because a direct structure pointer is bound to =
the easy handle at setup using the private data option, the execution ticke=
r extracts the original instance pointer in O(1) constant time upon complet=
ion. The engine immediately retrieves the stored closure, resets state flag=
s, and invokes the callback directly. Event routing search passes are reduc=
ed from two down to zero.<br><br><br>--- FEATURE BORROWING &amp; DATA HANDL=
ING ---<br><br>Rather than re-implementing payload parsing or utility funct=
ions in custom C code, the extension delegates heavy lifting directly to li=
bcurl APIs:<br><br>- Unified Form Data via CurlOneFile: Legacy PHP ext/curl=
 separates file payloads into distinct CURLFile and CURLStringFile userland=
 objects. This extension unifies both paradigms into a single CurlOneFile h=
elper. Multipart forms set up through setFormData() construct native curl_m=
ime structure trees. Depending on whether CurlOneFile references a persiste=
nt file path or an in-memory buffer, the C layer delegates streaming and cl=
eanup directly to curl_mime_filedata() or curl_mime_data(), allowing libcur=
l to manage file lifetime, disk I/O, filename tagging, and MIME headers nat=
ively.<br><br>- URL Query Assembly: Instead of constructing URL-encoded POS=
T strings or query parameters manually, setUrlData() offloads string encodi=
ng, delimiter formatting, and key-value concatenation entirely to libcurl&#=
39;s URL API via curl_url() and curl_url_set(). Passing CURLU_APPENDQUERY a=
nd CURLU_URLENCODE flags offloads parameter escaping and memory allocation =
directly to C-level libcurl primitives before binding the output to CURLOPT=
_POSTFIELDS.<br><br>- Zero-Copy Request Payload Management: Raw string payl=
oads set via setRawData() increment the internal reference count of the PHP=
 string and pass byte pointers directly to CURLOPT_POSTFIELDS. Binary blob =
options utilize CURL_BLOB_NOCOPY to anchor userland buffers directly withou=
t duplicating heap memory.<br><br>- Header Parsing API: Response header ext=
raction via getHeaders() bypasses legacy manual header parsing callbacks. I=
t utilizes libcurl&#39;s header API (curl_easy_nextheader), allowing target=
ed extraction across request origins (such as intermediate 1xx responses, C=
ONNECT proxy headers, or trailers) directly from C memory structures. =C2=
=A0<br><br><br>--- MEMORY ARCHITECTURE &amp; RESPONSE HANDLING ---<br><br>R=
esponse payload handling is deferred to optimize throughput. Incoming data =
frames are appended to a linked list of heap-allocated memory chunks (curlD=
ataChunk_t) during transfer. Allocation of a single contiguous PHP string o=
ccurs only when userland explicitly requests the body via getData(), at whi=
ch point the engine flattens the chunk list into the final string and frees=
 the backing nodes.<br><br>Overall, this refactoring yields a significantly=
 leaner codebase and smaller binary footprint while dramatically reducing l=
anguage-boundary overhead during event dispatching.<br><br></div></div>

--0000000000004d952d0658062cf5--

--===============7071117432852861216==
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

--===============7071117432852861216==--