From c20c81ab105cdf27f5a4e2604bd13085f46e21de Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Wed, 5 Apr 2023 14:53:53 +0100 Subject: [PATCH] Configurable session read timeout (#892) This set of changes provides a mechanism to runtime-configure the previously #define'd timeout for reading packets from a session. The intention here is to also extend libcurl to be able to use this interface so that when fetching from sftp servers which are very slow to return directory listings, connections do not time-out so much. * Add new field to session to hold configurable read timeout * Updated `_libssh2_packet_require()`, `_libssh2_packet_requirev()`, and `sftp_packet_requirev()` to use new field in session structure * Updated docs for API functions to set/get read timeout field in session structure * Updated `libssh2.h` to declare the get/set read timeout functions Co-authored-by: Jon Axtell Credit: Daniel Silverstone --- docs/libssh2_session_get_read_timeout.3 | 19 +++++++++++++++++++ docs/libssh2_session_set_read_timeout.3 | 20 ++++++++++++++++++++ include/libssh2.h | 4 ++++ src/libssh2_priv.h | 7 +++++-- src/packet.c | 6 +++--- src/session.c | 25 +++++++++++++++++++++++++ src/sftp.c | 2 +- 7 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 docs/libssh2_session_get_read_timeout.3 create mode 100644 docs/libssh2_session_set_read_timeout.3 diff --git a/docs/libssh2_session_get_read_timeout.3 b/docs/libssh2_session_get_read_timeout.3 new file mode 100644 index 00000000..4f47af39 --- /dev/null +++ b/docs/libssh2_session_get_read_timeout.3 @@ -0,0 +1,19 @@ +.TH libssh2_session_get_read_timeout 3 "13 Jan 2023" "libssh2 1.10.1" "libssh2 manual" +.SH NAME +libssh2_session_get_read_timeout - get the timeout for packet read functions +.SH SYNOPSIS +#include +.nf +long libssh2_session_get_read_timeout(LIBSSH2_SESSION *session); +.SH DESCRIPTION +Returns the \fBtimeout\fP (in seconds) for how long the ssh2 packet receive +function calls may wait until they consider the situation an error and +return LIBSSH2_ERROR_TIMEOUT. + +By default the timeout is 60 seconds. +.SH RETURN VALUE +The value of the timeout setting. +.SH AVAILABILITY +Added in 1.10.1 +.SH SEE ALSO +.BR libssh2_session_set_read_timeout(3) diff --git a/docs/libssh2_session_set_read_timeout.3 b/docs/libssh2_session_set_read_timeout.3 new file mode 100644 index 00000000..63acd000 --- /dev/null +++ b/docs/libssh2_session_set_read_timeout.3 @@ -0,0 +1,20 @@ +.TH libssh2_session_set_read_timeout 3 "13 Jan 2023" "libssh2 1.10.1" "libssh2 manual" +.SH NAME +libssh2_session_set_read_timeout - set timeout for packet read functions +.SH SYNOPSIS +#include +.nf +void libssh2_session_set_read_timeout(LIBSSH2_SESSION *session, long timeout); +.SH DESCRIPTION +Set the \fBtimeout\fP in seconds for how long libssh2 packet read +function calls may wait until they consider the situation an error and return +LIBSSH2_ERROR_TIMEOUT. + +By default or if you set the timeout to zero, the timeout will be set to +60 seconds. +.SH RETURN VALUE +Nothing +.SH AVAILABILITY +Added in 1.10.1 +.SH SEE ALSO +.BR libssh2_session_get_read_timeout(3) diff --git a/include/libssh2.h b/include/libssh2.h index c932d763..0c82911a 100644 --- a/include/libssh2.h +++ b/include/libssh2.h @@ -916,6 +916,10 @@ LIBSSH2_API void libssh2_session_set_timeout(LIBSSH2_SESSION* session, long timeout); LIBSSH2_API long libssh2_session_get_timeout(LIBSSH2_SESSION* session); +LIBSSH2_API void libssh2_session_set_read_timeout(LIBSSH2_SESSION* session, + long timeout); +LIBSSH2_API long libssh2_session_get_read_timeout(LIBSSH2_SESSION* session); + /* libssh2_channel_handle_extended_data is DEPRECATED, do not use! */ LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode); diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h index 0307ec09..67c4da54 100644 --- a/src/libssh2_priv.h +++ b/src/libssh2_priv.h @@ -883,6 +883,9 @@ struct _LIBSSH2_SESSION int keepalive_interval; int keepalive_want_reply; time_t keepalive_last_sent; + + /* Configurable timeout for packets. Replaces LIBSSH2_READ_TIMEOUT */ + long packet_read_timeout; }; /* session.state bits */ @@ -1089,8 +1092,8 @@ ssize_t _libssh2_recv(libssh2_socket_t socket, void *buffer, ssize_t _libssh2_send(libssh2_socket_t socket, const void *buffer, size_t length, int flags, void **abstract); -#define LIBSSH2_READ_TIMEOUT 60 /* generic timeout in seconds used when - waiting for more data to arrive */ +#define LIBSSH2_DEFAULT_READ_TIMEOUT 60 /* generic timeout in seconds used when + waiting for more data to arrive */ int _libssh2_kex_exchange(LIBSSH2_SESSION * session, int reexchange, diff --git a/src/packet.c b/src/packet.c index f507770a..fa221e83 100644 --- a/src/packet.c +++ b/src/packet.c @@ -1267,8 +1267,8 @@ _libssh2_packet_require(LIBSSH2_SESSION * session, unsigned char packet_type, } else if(ret == 0) { /* nothing available, wait until data arrives or we time out */ - long left = LIBSSH2_READ_TIMEOUT - (long)(time(NULL) - - state->start); + long left = session->packet_read_timeout - (long)(time(NULL) - + state->start); if(left <= 0) { state->start = 0; @@ -1380,7 +1380,7 @@ _libssh2_packet_requirev(LIBSSH2_SESSION *session, return ret; } if(ret <= 0) { - long left = LIBSSH2_READ_TIMEOUT - + long left = session->packet_read_timeout - (long)(time(NULL) - state->start); if(left <= 0) { diff --git a/src/session.c b/src/session.c index 65946e1e..96a2167b 100644 --- a/src/session.c +++ b/src/session.c @@ -518,6 +518,7 @@ libssh2_session_init_ex(LIBSSH2_ALLOC_FUNC((*my_alloc)), session->abstract = abstract; session->api_timeout = 0; /* timeout-free API by default */ session->api_block_mode = 1; /* blocking API by default */ + session->packet_read_timeout = LIBSSH2_DEFAULT_READ_TIMEOUT; _libssh2_debug((session, LIBSSH2_TRACE_TRANS, "New session resource allocated")); _libssh2_init_if_needed(); @@ -1469,6 +1470,30 @@ libssh2_session_get_timeout(LIBSSH2_SESSION * session) return session->api_timeout; } +/* libssh2_session_set_read_timeout + * + * Set a session's timeout (in sec) when reading packets, + * or 0 to use default of 60 seconds. + */ +LIBSSH2_API void +libssh2_session_set_read_timeout(LIBSSH2_SESSION * session, long timeout) +{ + if(timeout <= 0) { + timeout = LIBSSH2_DEFAULT_READ_TIMEOUT; + } + session->packet_read_timeout = timeout; +} + +/* libssh2_session_get_read_timeout + * + * Returns a session's timeout. Default is 60 seconds. + */ +LIBSSH2_API long +libssh2_session_get_read_timeout(LIBSSH2_SESSION * session) +{ + return session->packet_read_timeout; +} + /* * libssh2_poll_channel_read * diff --git a/src/sftp.c b/src/sftp.c index 0065f1b9..1e890b79 100644 --- a/src/sftp.c +++ b/src/sftp.c @@ -611,7 +611,7 @@ sftp_packet_requirev(LIBSSH2_SFTP *sftp, int num_valid_responses, else if(rc <= 0) { /* prevent busy-looping */ long left = - LIBSSH2_READ_TIMEOUT - + sftp->channel->session->packet_read_timeout - (long)(time(NULL) - sftp->requirev_start); if(left <= 0) {