mirror of
https://github.com/doublecmd/doublecmd.git
synced 2026-06-21 09:58:13 +00:00
Merge 174fd293f9 into 54d6654ad1
This commit is contained in:
commit
507ad20e19
3 changed files with 69 additions and 24 deletions
|
|
@ -103,6 +103,7 @@ type
|
|||
FPublicKey, FPrivateKey: String;
|
||||
function Connect: Boolean; override;
|
||||
function DataSocket: Boolean; override;
|
||||
procedure ApplyKeepAlive;
|
||||
function ListMachine(Directory: String): Boolean;
|
||||
procedure DoStatus(Response: Boolean; const Value: string); override;
|
||||
procedure OnSocketStatus(Sender: TObject; Reason: THookSocketReason; const Value: String);
|
||||
|
|
@ -135,7 +136,7 @@ type
|
|||
public
|
||||
property Encoding: String write SetEncoding;
|
||||
property UseAllocate: Boolean write FUseAllocate;
|
||||
property TcpKeepAlive: Boolean write FTcpKeepAlive;
|
||||
property TcpKeepAlive: Boolean read FTcpKeepAlive write FTcpKeepAlive;
|
||||
property PublicKey: String read FPublicKey write FPublicKey;
|
||||
property PrivateKey: String read FPrivateKey write FPrivateKey;
|
||||
property ShowHidden: Boolean read FShowHidden write FShowHidden;
|
||||
|
|
@ -427,21 +428,51 @@ begin
|
|||
end;
|
||||
|
||||
function TFTPSendEx.Connect: Boolean;
|
||||
begin
|
||||
Result:= inherited Connect;
|
||||
if Result then
|
||||
begin
|
||||
LogProc(PluginNumber, MSGTYPE_CONNECT, nil);
|
||||
ApplyKeepAlive;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Enable TCP keep-alive on the control socket so that connections survive long
|
||||
idle periods: the probes keep NAT/firewall mappings open, and a peer that
|
||||
silently went away is detected (the socket errors) instead of the link
|
||||
appearing alive until the next operation blocks. Called for both the FTP
|
||||
control connection and the SSH connection (see TScpSend.Connect). }
|
||||
procedure TFTPSendEx.ApplyKeepAlive;
|
||||
{$IFDEF UNIX}
|
||||
const
|
||||
// From netinet/tcp.h (Linux). Defined locally to avoid an extra dependency.
|
||||
TCP_KEEPIDLE_ = 4; // begin probing after N seconds of idle
|
||||
TCP_KEEPINTVL_ = 5; // seconds between probes
|
||||
TCP_KEEPCNT_ = 6; // dropped after this many unanswered probes
|
||||
{$ENDIF}
|
||||
var
|
||||
Option: Cardinal = 1;
|
||||
Message: UnicodeString;
|
||||
{$IFDEF UNIX}
|
||||
KeepIdle: Integer = 30;
|
||||
KeepIntvl: Integer = 10;
|
||||
KeepCnt: Integer = 3;
|
||||
{$ENDIF}
|
||||
begin
|
||||
Result:= inherited Connect;
|
||||
if Result then LogProc(PluginNumber, MSGTYPE_CONNECT, nil);
|
||||
// Apply TcpKeepAlive option
|
||||
if FTcpKeepAlive and Result then
|
||||
if not FTcpKeepAlive then Exit;
|
||||
if SetSockOpt(FSock.Socket, SOL_SOCKET, SO_KEEPALIVE, @Option, SizeOf(Option)) <> 0 then
|
||||
begin
|
||||
if SetSockOpt(FSock.Socket, SOL_SOCKET, SO_KEEPALIVE, @Option, SizeOf(Option)) <> 0 then
|
||||
begin
|
||||
Message := UTF8ToUTF16(FSock.GetErrorDesc(synsock.WSAGetLastError));
|
||||
LogProc(PluginNumber, msgtype_importanterror, PWideChar('CSOCK ERROR ' + Message));
|
||||
end;
|
||||
Message := UTF8ToUTF16(FSock.GetErrorDesc(synsock.WSAGetLastError));
|
||||
LogProc(PluginNumber, msgtype_importanterror, PWideChar('CSOCK ERROR ' + Message));
|
||||
Exit;
|
||||
end;
|
||||
{$IFDEF UNIX}
|
||||
// Probe idle connections within ~1 minute instead of the OS default (~2 h),
|
||||
// so a dropped link is noticed on the next refresh and NAT mappings survive.
|
||||
SetSockOpt(FSock.Socket, IPPROTO_TCP, TCP_KEEPIDLE_, @KeepIdle, SizeOf(KeepIdle));
|
||||
SetSockOpt(FSock.Socket, IPPROTO_TCP, TCP_KEEPINTVL_, @KeepIntvl, SizeOf(KeepIntvl));
|
||||
SetSockOpt(FSock.Socket, IPPROTO_TCP, TCP_KEEPCNT_, @KeepCnt, SizeOf(KeepCnt));
|
||||
{$ENDIF}
|
||||
end;
|
||||
|
||||
function TFTPSendEx.DataSocket: Boolean;
|
||||
|
|
|
|||
|
|
@ -320,24 +320,28 @@ begin
|
|||
if I >= 0 then
|
||||
begin
|
||||
FtpSend:= TFTPSendEx(ActiveConnectionList.Objects[I]);
|
||||
|
||||
|
||||
if FtpSend.NetworkError then
|
||||
//Server closed the connection, or network error occurred, or whatever else.
|
||||
//Attempt to reconnect and execute login sequence
|
||||
// The link is dead: the server closed it, a network error occurred, or
|
||||
// it was dropped while the tab sat idle. Discard the stale connection
|
||||
// and open a fresh one through the normal path below. The cached
|
||||
// password is reused when available, otherwise the user is prompted
|
||||
// again. This way a simple refresh of the view fully reconnects and
|
||||
// re-lists whatever folder is currently displayed in the tab.
|
||||
begin
|
||||
LogProc(PluginNumber, msgtype_details, PWideChar('Network error detected, attempting to reconnect...'));
|
||||
I:= ConnectionList.IndexOf(ConnectionName);
|
||||
if I >= 0 then
|
||||
LogProc(PluginNumber, msgtype_details, PWideChar('Connection lost, reconnecting...'));
|
||||
// Quick connections have no stored entry, so they cannot be rebuilt.
|
||||
if ConnectionList.IndexOf(ConnectionName) < 0 then
|
||||
begin
|
||||
Connection := TConnection(ConnectionList.Objects[I]);
|
||||
if not FtpLogin(Connection, FtpSend) then
|
||||
begin
|
||||
RequestProc(PluginNumber, RT_MsgOK, nil, 'Connection lost, unable to reconnect!', nil, MAX_PATH);
|
||||
Exit;
|
||||
end;
|
||||
RequestProc(PluginNumber, RT_MsgOK, nil, 'Connection lost, unable to reconnect!', nil, MAX_PATH);
|
||||
Exit;
|
||||
end;
|
||||
ActiveConnectionList.Delete(I);
|
||||
FreeAndNil(FtpSend);
|
||||
Result:= FtpConnect(ConnectionName, FtpSend);
|
||||
Exit;
|
||||
end;
|
||||
|
||||
|
||||
Result:= True;
|
||||
end
|
||||
else
|
||||
|
|
|
|||
|
|
@ -551,6 +551,11 @@ begin
|
|||
end;
|
||||
|
||||
DoStatus(False, 'Authentication succeeded');
|
||||
|
||||
// Keep the SSH connection alive across long idle periods. TScpSend
|
||||
// overrides Connect and never runs TFTPSendEx.Connect, so without this
|
||||
// the SSH socket got no keep-alive at all (unlike plain FTP).
|
||||
ApplyKeepAlive;
|
||||
finally
|
||||
if not Result then begin
|
||||
libssh2_session_free(FSession);
|
||||
|
|
@ -637,7 +642,12 @@ end;
|
|||
|
||||
function TScpSend.NetworkError: Boolean;
|
||||
begin
|
||||
Result:= FSock.CanRead(0) and (libssh2_session_last_errno(FSession) <> 0);
|
||||
// The link is considered dead if libssh2 already recorded a fatal error, or
|
||||
// if the idle socket has unexpected pending data (a peer FIN/RST, or a
|
||||
// keep-alive probe that found the connection gone). The previous code
|
||||
// required BOTH conditions, so a connection that the server closed while the
|
||||
// tab was idle went unnoticed and never reconnected.
|
||||
Result:= (libssh2_session_last_errno(FSession) <> 0) or FSock.CanRead(0);
|
||||
end;
|
||||
|
||||
procedure TScpSend.CloneTo(AValue: TFTPSendEx);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue