[PECL-CVS] [pecl-networking-ssh2] master: Add keepalive support (#95)
[email protected] (Rasmus Lerdorf via GitHub) Sat, 4 Apr 2026 11:41:09 +0000
| Newsgroups | php.pecl.cvs |
|---|---|
| Message-ID | <[email protected]> |
Author: Rasmus Lerdorf (rlerdorf)
Committer: GitHub (web-flow)
Pusher: rlerdorf
Date: 2026-04-04T07:41:06-04:00
Commit: https://github.com/php/pecl-networking-ssh2/commit/77685aab5769cea381b71de1af0d9056c7ec6f4f
Raw diff: https://github.com/php/pecl-networking-ssh2/commit/77685aab5769cea381b71de1af0d9056c7ec6f4f.diff
Add keepalive support (#95)
* Add keepalive support
* address reviews
* Assume we have a new enough libssh2 on Windows
* address reviews
Changed paths:
A tests/ssh2_keepalive.phpt
M config.m4
M config.w32
M ssh2.c
Diff:
diff --git a/config.m4 b/config.m4
index b2746e2..500c8d6 100644
--- a/config.m4
+++ b/config.m4
@@ -53,6 +53,15 @@ if test "$PHP_SSH2" != "no"; then
-L$SSH2_DIR/lib -lm
])
+ PHP_CHECK_LIBRARY(ssh2,libssh2_keepalive_config,
+ [
+ AC_DEFINE(PHP_SSH2_KEEPALIVE, 1, [Have libssh2 with keepalive support])
+ ],[
+ AC_MSG_WARN([libssh2 keepalive support not available])
+ ],[
+ -L$SSH2_DIR/lib -lm
+ ])
+
PHP_SUBST(SSH2_SHARED_LIBADD)
PHP_NEW_EXTENSION(ssh2, ssh2.c ssh2_fopen_wrappers.c ssh2_sftp.c, $ext_shared)
diff --git a/config.w32 b/config.w32
index b715414..45a0226 100644
--- a/config.w32
+++ b/config.w32
@@ -38,6 +38,7 @@ if (PHP_SSH2 != "no") {
AC_DEFINE('HAVE_SSH2LIB', 1);
AC_DEFINE('PHP_SSH2_AGENT_AUTH', 1);
AC_DEFINE('PHP_SSH2_SESSION_TIMEOUT', 1);
+ AC_DEFINE('PHP_SSH2_KEEPALIVE', 1);
ADD_EXTENSION_DEP('ssh2', 'zlib')
ADD_EXTENSION_DEP('ssh2', 'openssl')
}
diff --git a/ssh2.c b/ssh2.c
index e2c28db..8bd51cb 100644
--- a/ssh2.c
+++ b/ssh2.c
@@ -475,6 +475,64 @@ PHP_FUNCTION(ssh2_set_timeout)
}
/* }}} */
+#ifdef PHP_SSH2_KEEPALIVE
+/* {{{ proto void ssh2_keepalive_config(resource session, bool want_reply, int interval)
+ * Set how often keepalive messages should be sent. interval is the number
+ * of seconds that can pass without any I/O; use 0 (the default) to disable
+ * keepalives. want_reply indicates whether the keepalive messages should
+ * request a response from the server.
+ */
+PHP_FUNCTION(ssh2_keepalive_config)
+{
+ LIBSSH2_SESSION *session;
+ zval *zsession;
+ zend_bool want_reply;
+ zend_long interval;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "rbl", &zsession, &want_reply, &interval) == FAILURE) {
+ return;
+ }
+
+ if (interval < 0 || interval > UINT_MAX) {
+ php_error_docref(NULL, E_WARNING, "Argument #3 ($interval) must be between 0 and %u", UINT_MAX);
+ return;
+ }
+
+ if ((session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) {
+ return;
+ }
+
+ libssh2_keepalive_config(session, want_reply, (unsigned int)interval);
+}
+/* }}} */
+
+/* {{{ proto int|false ssh2_keepalive_send(resource session)
+ * Send a keepalive message if needed. Returns the number of seconds you
+ * can sleep before you need to call this function again, or false on error.
+ */
+PHP_FUNCTION(ssh2_keepalive_send)
+{
+ LIBSSH2_SESSION *session;
+ zval *zsession;
+ int seconds_to_next;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zsession) == FAILURE) {
+ return;
+ }
+
+ if ((session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) {
+ return;
+ }
+
+ if (libssh2_keepalive_send(session, &seconds_to_next)) {
+ RETURN_FALSE;
+ }
+
+ RETURN_LONG(seconds_to_next);
+}
+/* }}} */
+#endif
+
/* {{{ proto array ssh2_methods_negotiated(resource session)
* Return list of negotiaed methods
*/
@@ -1430,6 +1488,16 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_set_timeout, 0, 0, 2)
ZEND_ARG_INFO(0, microseconds)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_keepalive_config, 0, 0, 3)
+ ZEND_ARG_INFO(0, session)
+ ZEND_ARG_INFO(0, want_reply)
+ ZEND_ARG_INFO(0, interval)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_keepalive_send, 0, 0, 1)
+ ZEND_ARG_INFO(0, session)
+ZEND_END_ARG_INFO()
+
ZEND_BEGIN_ARG_INFO(arginfo_ssh2_methods_negotiated, 1)
ZEND_ARG_INFO(0, session)
ZEND_END_ARG_INFO()
@@ -1640,6 +1708,10 @@ zend_function_entry ssh2_functions[] = {
PHP_FE(ssh2_connect, arginfo_ssh2_connect)
PHP_FE(ssh2_disconnect, arginfo_ssh2_disconnect)
PHP_FE(ssh2_set_timeout, arginfo_ssh2_set_timeout)
+#ifdef PHP_SSH2_KEEPALIVE
+ PHP_FE(ssh2_keepalive_config, arginfo_ssh2_keepalive_config)
+ PHP_FE(ssh2_keepalive_send, arginfo_ssh2_keepalive_send)
+#endif
PHP_FE(ssh2_methods_negotiated, arginfo_ssh2_methods_negotiated)
PHP_FE(ssh2_fingerprint, arginfo_ssh2_fingerprint)
diff --git a/tests/ssh2_keepalive.phpt b/tests/ssh2_keepalive.phpt
new file mode 100644
index 0000000..72596e4
--- /dev/null
+++ b/tests/ssh2_keepalive.phpt
@@ -0,0 +1,33 @@
+--TEST--
+ssh2_keepalive_config() and ssh2_keepalive_send() basic functionality
+--SKIPIF--
+<?php
+require('ssh2_skip.inc');
+if (!function_exists('ssh2_keepalive_config') || !function_exists('ssh2_keepalive_send')) {
+ die("skip keepalive support not available");
+}
+?>
+--FILE--
+<?php require('ssh2_test.inc');
+
+$ssh = ssh2_connect(TEST_SSH2_HOSTNAME, TEST_SSH2_PORT);
+
+echo "**Configure keepalive\n";
+ssh2_keepalive_config($ssh, true, 10);
+
+echo "**Send keepalive\n";
+$seconds = ssh2_keepalive_send($ssh);
+var_dump(is_int($seconds));
+var_dump($seconds > 0);
+
+echo "**Disable keepalive\n";
+ssh2_keepalive_config($ssh, false, 0);
+$seconds = ssh2_keepalive_send($ssh);
+var_dump($seconds === 0);
+--EXPECT--
+**Configure keepalive
+**Send keepalive
+bool(true)
+bool(true)
+**Disable keepalive
+bool(true)