Fix SFTP issue with multiple SFTP messages can arrive in one SSH packet

Fix HANDLE close event to notify about operation completion
This commit is contained in:
olegkap_cp
2010-12-10 21:34:33 +00:00
parent 2bbb42508b
commit 1ff83b9ac9
2 changed files with 68 additions and 23 deletions
@@ -7,6 +7,9 @@ using Renci.SshClient.Messages.Sftp;
namespace Renci.SshClient.Sftp
{
/// <summary>
/// Base class for all SFTP Commands
/// </summary>
internal abstract class SftpCommand
{
private SftpSession _sftpSession;
@@ -17,6 +20,8 @@ namespace Renci.SshClient.Sftp
private Exception _error;
private bool _handleCloseMessageSent;
protected SftpAsyncResult AsyncResult { get; private set; }
public int CommandTimeout { get; set; }
@@ -70,6 +75,12 @@ namespace Renci.SshClient.Sftp
protected virtual void OnStatus(StatusCodes statusCode, string errorMessage, string language)
{
if (this._handleCloseMessageSent)
{
this.OnHandleClosed();
this._handleCloseMessageSent = false;
}
}
protected virtual void OnName(IEnumerable<SftpFile> files)
@@ -107,6 +118,8 @@ namespace Renci.SshClient.Sftp
{
Handle = handle,
});
this._handleCloseMessageSent = true;
}
protected void SendReadMessage(string handle, ulong offset, uint bufferSize)
@@ -262,19 +275,19 @@ namespace Renci.SshClient.Sftp
if (this._requestId == e.Message.RequestId)
{
this.OnStatus(e.Message.StatusCode, e.Message.ErrorMessage, e.Message.Language);
}
if (e.Message.StatusCode == StatusCodes.NoSuchFile ||
e.Message.StatusCode == StatusCodes.PermissionDenied ||
e.Message.StatusCode == StatusCodes.Failure ||
e.Message.StatusCode == StatusCodes.BadMessage ||
e.Message.StatusCode == StatusCodes.NoConnection ||
e.Message.StatusCode == StatusCodes.ConnectionLost ||
e.Message.StatusCode == StatusCodes.OperationUnsupported
)
{
// Throw an exception if it was not handled by the command
throw new SshException(e.Message.ErrorMessage);
if (e.Message.StatusCode == StatusCodes.NoSuchFile ||
e.Message.StatusCode == StatusCodes.PermissionDenied ||
e.Message.StatusCode == StatusCodes.Failure ||
e.Message.StatusCode == StatusCodes.BadMessage ||
e.Message.StatusCode == StatusCodes.NoConnection ||
e.Message.StatusCode == StatusCodes.ConnectionLost ||
e.Message.StatusCode == StatusCodes.OperationUnsupported
)
{
// Throw an exception if it was not handled by the command
throw new SshException(e.Message.ErrorMessage);
}
}
}
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
@@ -89,15 +90,39 @@ namespace Renci.SshClient.Sftp
private void Channel_DataReceived(object sender, Common.ChannelDataEventArgs e)
{
var packets = new Queue<string>();
if (this._packetData == null)
{
var packetLength = (uint)(e.Data[0] << 24 | e.Data[1] << 16 | e.Data[2] << 8 | e.Data[3]);
var dataOffset = 0;
while (true)
{
// Read SFTP packet length
var packetLength = (e.Data[dataOffset + 0] << 24 | e.Data[dataOffset + 1] << 16 | e.Data[dataOffset + 2] << 8 | e.Data[dataOffset + 3]);
this._packetData = new StringBuilder((int)packetLength, (int)packetLength);
this._packetData.Append(e.Data.GetSshBytes().Skip(4).GetSshString());
// Create data holder for SFTP packet
this._packetData = new StringBuilder(packetLength, packetLength);
// Add data to the packet holder
this._packetData.Append(e.Data.GetSshBytes().Skip(dataOffset + 4).Take((int)packetLength).GetSshString());
dataOffset += (packetLength + 4);
if (dataOffset < e.Data.Length)
{
// If there is another SFTP packet in current message then queue this data and read next one
packets.Enqueue(this._packetData.ToString());
continue;
}
else
{
break;
}
}
}
else
{
// Add message data to packet data
this._packetData.Append(e.Data);
}
@@ -107,18 +132,25 @@ namespace Renci.SshClient.Sftp
return;
}
dynamic sftpMessage = SftpMessage.Load(this._packetData.ToString().GetSshBytes());
// Add last packet to the queue of packet data that need to be proccessed
packets.Enqueue(this._packetData.ToString());
this._packetData = null;
try
foreach (var packetData in packets)
{
// TODO: Check to run on different thread
this.HandleMessage(sftpMessage);
}
catch (Exception exp)
{
this.RaiseError(exp);
dynamic sftpMessage = SftpMessage.Load(packetData.GetSshBytes());
try
{
// TODO: Check to run on different thread
this.HandleMessage(sftpMessage);
}
catch (Exception exp)
{
this.RaiseError(exp);
break;
}
}
}