* Switch to using `zend_string*` in a lot of places that were previously
deconstructing the `zend_string` into the char and len.
* Various other minor bits of cleanup.
Rework `cluster_session_key` to return either a newly allocated
`zend_string*` when we have a prefix, or a cheap copy when we don't.
Previously we were using `int` to curry the length which was in theory
susceptible to overflow if `ZSTR_LEN(prefix) + keylen > INT_MAX)`.
Fixes#2866
* Introduce a new `RedisCmd` struct to dynamically append RESP arguments
such that we don't have to precalculate the number of arguments the
command will have up front.
Additionally the new `RedisCmd allows both a `void *` context pointer
but also can attach a `void (*ctx_dtor)(void*)` destructor so we are
still able to clean up any allocated context when commands fail.
This moves the context cleanup out of every individual reply handler
and into the generic processing wrappers.
* Create a small group of `resp_str` helper functions for lower level
concatination of RESP protocol data over the wire.
* Lots of small modernization of the codebase such as using
`zend_string*` instead of (`char *`, `size_t`) pairs.
* Greatly simplify `crosslot` handling logic
generate_lock_secret derived the secret from hostname plus pid,
roughly 22 bits of guessable entropy and also readable from Redis
under <key>_LOCK. Replace with 16 bytes from php_random_bytes_silent,
hex-encoded.
Defense in depth: an attacker with write access to the Redis
instance can already bypass the lock by DELing the key, so this is
not a primary defense; worth fixing for the case where only the
lock key itself is exposed. The hostname|pid path stays as a fallback
when php_random_bytes_silent fails, so the caller always gets a
non-NULL secret.
Add support for Redis' `DELEX` and Valkey's `DELIFEQ` when deleting the
session lock key. Local testing shows about a 10-15% improvement over
the current `EVAL[SHA]` strategy.
This commit adds a new INI settingg:
```ini
redis.session.lock_release_cmd = delex|delifeq|eval
```
By default we continue to use the `EVAL` logic and if a user specifies
another mechanism but the command doesn't exist, we warn the user and
fall back to EVAL.
This commit also refactors a few functions to avoid UB and simplify key
construction.
Instead of currying around a `php_stream_context` object, just retain
the context array provided by the user itself like we do with other
connection information like host and port. This lets users reconnect in
a loop without leaking memory.
```php
$redis = new \Redis;
while (true) {
// Previously each reconnect call would leak the
// `php_stream_context` structure.
$redis->connect('tls://127.0.0.1', 9999, 1, null, 0, 0, [
'stream' => ['verify_peer' => false, 'verify_peer_name' => false],
]);
$redis->ping();
$redis->close();
}
```
```c
int pos, i;
memcpy(&pos, key, sizeof(pos));
pos %= pool->totalWeight;
```
As unlikely as it is, this may cause UB if user's are generating their
own session IDs shorter than three bytes. This commit checks the session
id length and only copies in the bytes that we have.
Technically this could change behavior for session ID's of length 2 or
less, but this seems acceptable because currently such an ID would end
up selecing a server at random, depending on whatever bytes were past
the end of the `zend_string`.
In a future release we will fix this logic to use all of the entropy of
the session ID as well as ensure `pool->totalWeight` is > 0.
Add a specific warning when we can't parse any servers from
`php.session_save_path`.
PHP will then display the save path, but this will let users know there
was a problem parsing the path.
Fixes#1390
Signed-off-by: michael-grunder <michael.grunder@gmail.com>
The warning when a user picks an unsupported session compression
algorithm was pretty confusing.
The new message tells the user if the selection was invalid or only
invalid because the redis.so wasn't built with support.
Fixes#2570
On some glibc implementations strncmp is a macro. This commit simply creates a
`redis_strncmp` static inline wrapper function so we can `ZEND_STRL` instead of
manually counting the length or using `sizeof(s)-1` each time.
Fixes#2565
* Add compression support for PHP Sessions
Previously, compression was available for standard data but not for
session handling. This update enables the compression of PHP sessions,
allowing for more efficient Redis memory usage.
* Move session compress/uncompress logic to helper functions
* Change session_compress_data to always set the out arguments and adjust PS_READ_FUNC
Previously, the redis.session.early_refresh feature was implemented for
Redis Cluster, utilizing GETEX for the initial session read to minimize
the number of commands sent to the Redis server. However, this enhancement
was not applied to non-cluster sessions. This update addresses this
discrepancy, ensuring consistent behavior between Redis and Redis Cluster.
* fix bug: the pipeline mode socket return an unexpected result after reconnecting
* fix typos: pipeline is right
---------
Co-authored-by: marcofu <marcofu@tencent.com>
* Add ini setting redis.session.early_refresh to allow for session TTL updates on session start ( requires redis server version 6.2 or greater )
* Enable cluster session support for strict mode sessions ( via PS_VALIDATE_SID_FUNC )
* Cluster sessions used to write on every session, now we only write if the session has been modified.
* Send EXPIRE instead of SETEX if sessioh has not been changed
* If early refresh is enabled use GETEX for initial session read
* When strict sessions are enabled, check whether the session exists first, validate sid and regenerate if necessary
Add support for Redis 6 ACLs in the `Redis`, `RedisCluster`, and `RedisArray` classes.
On a related note, it adds a mechanism for users to customize how we generate persistent connection IDs such that they can be grouped in different ways depending on the specific use case required (e.g. it would allow connections to be grouped by username, or by user-defined persistent_id, or both).
Various improvements and fixes to cluster slot caching.
* Improves slot caching so any unique set of seeds all hash to the same key
* Fix a couple of memory leaks.
* Fixes a segfault when executing a multiple key command such as `MGET` or `MSET` while the cluster is resharding.