====== PHP RFC: Persistent curl share handle improvement ====== * Version: 1.1 * Date: 2024-11-27 * Author: Eric Norris, erictnorris@gmail.com * Status: Under Discussion * First Published at: https://wiki.php.net/rfc/curl_share_persistence ===== Introduction ===== The vote for [[rfc:curl_share_persistence|PHP RFC: Add persistent curl share handles]] passed, but discussion raised after voting brought up the possibility of an improved implementation. After finishing up the [[https://github.com/php/php-src/pull/15603|original implementation pull request]], I had some free time to explore an alternative implementation: https://github.com/php/php-src/pull/16937. As a reminder, the previously accepted signature was: ''curl_share_init(?array $share_options, ?string $persistent_id): CurlShareHandle''. ==== Issue: CURL_LOCK_DATA_COOKIE is potentially dangerous ==== Tim Düsterhus noted concerns about allowing the ''CURL_LOCK_DATA_COOKIE'' share option: > Accidentally sharing a cookie jar for unrelated requests due to a badly chosen `$persistent_id` sounds like a vulnerability to is bound to happen to someone. This was addressed with an update to the wording of the RFC, but still remains a valid option. ==== Issue: Users must choose persistent IDs ==== Tim Düsterhus also noted concerns about chosen ''$persistent_id'' arguments: > Also badly chosen $persistent_ids might result in a large number of handles accumulating, without any kind of garbage collection. For the existing persistent handles (e.g. for database connections), the ID is chosen by PHP itself, ensuring a somewhat predictable behavior. This was not addressed in the RFC. ==== Issue: Persistent share handles are not immutable ==== The [[https://github.com/php/php-src/pull/15603|implementation pull request]] stated: > It is noteworthy that calling curl_share_setopt on the persistent handle would affect future requests using the handle; we could consider preventing this. This did not come up in any discussion on the mailing list. While it is possible to prevent this with a runtime check in ''curl_share_setopt'', and that may be within the bounds of the RFC implementation, it is not preventable via static analysis. The type signature of ''curl_share_setopt'' will not warn a user if they are passing in a persistent ''CurlShareHandle''. ===== Proposal ===== Introduce a new ''curl_share_init_persistent(array $share_options)'' function instead of adding optional parameters to the ''curl_share_init'' function. The new function will return a ''CurlSharePersistentHandle'' object that is usable with ''curl_setopt'' via the ''CURLOPT_SHARE'' option. The object will not be usable with any existing ''curl_share_*'' functions, as it should not be possible to change the options of a ''CurlSharePersistentHandle'' once created. [[https://www.php.net/manual/en/curl.constants.php#constant.curl-lock-data-cookie|CURL_LOCK_DATA_COOKIE]] will not be allowed as a share option. The documentation for this function may look like: ==== curl_share_init_persistent ==== ''curl_share_init_persistent'' — Initialize a **persistent** cURL share handle. === Description === curl_share_init_persistent(array $share_options): CurlSharePersistentHandle Initialize a **persistent** cURL share handle with the given share options. It will not be destroyed at the end of the PHP request. If a persistent share handle with the same set of ''$share_options'' is found, it will be reused. === Parameters === * ''$share_options'' — an array of [[https://www.php.net/manual/en/curl.constants.php#constant.curl-lock-data-connect|CURL_LOCK_DATA_*]] constants. **Note**: [[https://www.php.net/manual/en/curl.constants.php#constant.curl-lock-data-cookie|CURL_LOCK_DATA_COOKIE]] is not allowed and, if specified, this function will throw a ''RuntimeException''. Sharing cookies between PHP requests may lead to inadvertently mixing up sensitive cookies between users. === Return Values === Returns a persistent cURL handle on success. ===== Improvements over the original RFC ===== * ''CURL_LOCK_DATA_COOKIE'' is not allowed, which, roughly speaking, keeps curl share handles stateless. This avoids a large class of potentially hard-to-debug errors. * Users no longer have to choose a persistent ID; the function implementation will reuse a single curl share handle per unique set of ''$share_options''. This simplifies usage: * Users can start using new share options immediately without needing to remember to also change the ''$persistent_id''. * Users cannot accidentally use a ''$persistent_id'' that corresponds to a handle with ''$share_options'' different from what they would expect. * Users cannot accidentally balloon memory usage via a poorly chosen dynamic ''$persistent_id''. * Since ''CurlSharePersistentHandle'' is a unique type, it is not usable with any existing ''curl_share_*'' functions. This means that once created, a ''CurlSharePersistentHandle'' is immutable. ===== Backward Incompatible Changes ===== None. the previous RFC implementation has not landed in any released PHP version. ===== Proposed PHP Version(s) ===== Next PHP minor release. ===== RFC Impact ===== ==== To SAPIs ==== Effectively none. SAPIs will consume more memory proportional to the number of persistent curl share handles, but the number of unique possibilities for ''$share_options'' is too low to matter. ==== To Existing Extensions ==== ''ext/curl'' will have a new function. ==== To Opcache ==== None. ==== New Constants ==== None. ==== php.ini Defaults ==== None. ===== Proposed Voting Choices ===== This vote requires a ⅔ majority: * Yes * No This vote requires a simple majority: * Yes * No ===== Patches and Tests ===== * https://github.com/php/php-src/pull/16937 ===== Implementation ===== ===== References ===== * https://wiki.php.net/rfc/curl_share_persistence * https://externals.io/message/125858 ===== Rejected Features ===== * Initially, it will not be possible to pass a ''CurlSharePersistentHandle'' to ''curl_share_close''. Calling ''curl_share_close'' is currently a NOP for ''CurlShareHandle'' objects, meaning that code does not have to consider handling a closed (invalid) ''CurlShareHandle'' object. In keeping with the spirit of making invalid states unrepresentable, we should not allow users to close ''CurlSharePersistentHandle''. If a valid use-case comes up in the future, we could revisit this.