From 2dae4f05face3464abc1a553d43a3dc7efcfc5dc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Apr 2026 16:41:26 +0000 Subject: [PATCH 1/3] Initial plan From 70c9a7fa13695308b88b42d648f5945c32f5235b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Apr 2026 16:51:18 +0000 Subject: [PATCH 2/3] fix: handle set_env failures gracefully and fix session reuse after disconnect Agent-Logs-Url: https://github.com/ohto-ai/webssh_cpp/sessions/6c478c4f-3c7d-49d0-920e-e2bee2894f86 Co-authored-by: ohto-ai <46275725+ohto-ai@users.noreply.github.com> --- src/main.cpp | 11 ++++++--- src/ssh_connection/ssh_connection.cpp | 34 ++++++++++++++++++--------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 034ef97..409d85d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -263,9 +263,14 @@ int main(int argc, char *argv[]) { ctx->setStatus(HTTP_STATUS_INTERNAL_SERVER_ERROR); return ctx->send("failed to create ssh channel"); } - ssh_channel->set_env("LC_WSSH_WEBSOCKET_HOST", ctx->host()); - ssh_channel->set_env("LC_WSSH_WEBSOCKET_URL", ctx->url()); - ssh_channel->set_env("LC_WSSH_WEBSOCKET_CLIENT_IP", ctx->header("X-Real-IP", ctx->ip())); + try { + ssh_channel->set_env("LC_WSSH_WEBSOCKET_HOST", ctx->host()); + ssh_channel->set_env("LC_WSSH_WEBSOCKET_URL", ctx->url()); + ssh_channel->set_env("LC_WSSH_WEBSOCKET_CLIENT_IP", ctx->header("X-Real-IP", ctx->ip())); + } + catch (const std::exception& e) { + spdlog::warn("[{}] Failed to set env variables (server may not support channel-setenv): {}", ssh_channel->id, e.what()); + } ssh_channel->request_pty(term.empty() ? "xterm-256color" : term); ssh_channel->shell(); } diff --git a/src/ssh_connection/ssh_connection.cpp b/src/ssh_connection/ssh_connection.cpp index 28cf731..d89afdc 100644 --- a/src/ssh_connection/ssh_connection.cpp +++ b/src/ssh_connection/ssh_connection.cpp @@ -473,25 +473,37 @@ size_t ohtoai::ssh::detail::ssh_pty_connection_manager::get_session_count() cons ohtoai::ssh::detail::ssh_channel_ptr ohtoai::ssh::detail::ssh_pty_connection_manager::get_channel(const std::string &host, int port, const std::string &username, const std::string &password) { auto session_id = detail::ssh_session::generate_id(host, port, username); - auto session_iter = [this, &session_id]{ + ssh_session_ptr session; + { std::lock_guard lock(sessions_mutex); - // find first session that has less than max_channel_in_session channels - auto begin = sessions.lower_bound(session_id); - auto end = sessions.upper_bound(session_id); - return std::find_if(begin, end, [this](const auto &pair) { + // Remove stale (disconnected) sessions for this session_id so they are + // never reused after a Ctrl+D / channel-close sequence. + auto range = sessions.equal_range(session_id); + for (auto it = range.first; it != range.second; ) { + if (it->second->session == nullptr) { + it = sessions.erase(it); + } else { + ++it; + } + } + // Find the first live session with available channel capacity. + range = sessions.equal_range(session_id); + auto it = std::find_if(range.first, range.second, [this](const auto &pair) { return pair.second->channels.size() < max_channel_in_session; }); - }(); + if (it != range.second) { + session = it->second; + } + } - // if no session found, create new session - if (session_iter == sessions.end()) { - auto session = std::make_shared(); + // No usable session found – create a new one. + if (!session) { + session = std::make_shared(); session->connect(host, port); session->authenticate(username, password); std::lock_guard lock(sessions_mutex); - session_iter = sessions.emplace(session_id, session); + sessions.emplace(session_id, session); } - auto session = session_iter->second; auto channel = session->open_channel(); channels.emplace(channel->id, channel); return channel; From 06d9b4d48a8def3c08e8701ffa66a56ffbb288b0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 02:14:42 +0000 Subject: [PATCH 3/3] fix: treat max_channel_in_session == 0 as no limit in session reuse predicate Agent-Logs-Url: https://github.com/ohto-ai/webssh_cpp/sessions/0293adac-5c25-4031-9bb8-4f856c97a0ba Co-authored-by: ohto-ai <46275725+ohto-ai@users.noreply.github.com> --- src/ssh_connection/ssh_connection.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ssh_connection/ssh_connection.cpp b/src/ssh_connection/ssh_connection.cpp index d89afdc..35550f6 100644 --- a/src/ssh_connection/ssh_connection.cpp +++ b/src/ssh_connection/ssh_connection.cpp @@ -489,7 +489,8 @@ ohtoai::ssh::detail::ssh_channel_ptr ohtoai::ssh::detail::ssh_pty_connection_man // Find the first live session with available channel capacity. range = sessions.equal_range(session_id); auto it = std::find_if(range.first, range.second, [this](const auto &pair) { - return pair.second->channels.size() < max_channel_in_session; + return max_channel_in_session == 0 || + pair.second->channels.size() < max_channel_in_session; }); if (it != range.second) { session = it->second;