[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 & 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 = & 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'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 & 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'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 & 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==--