0 votes
by (250 points)

Hi,
We have a server application (.NET Framework 4.7) that is exposing a SFTP file server implemented using Rebex File Server v5.7 (5.0.7999), with a virtual file system provider (class derived from ReadWriteFileSystemProvider ).
The virtual filesystem is exposing over SFTP data which is being read from Amazon S3 buckets.

My question is: is there any documentation/guidance on how to handle the errors/exceptions thrown by the custom FileSystemProvider, in order to have the Rebex SFTP component return more 'appropriate' errors to the client (with more specific messages at least).

An example: if one of the S3 buckets can't be accessed at a given moment (ex.: no permission), the AWS .NET SDK will throw an AmazonS3Exception with StatusCode=Forbidden (when called in an override method from the class derived from ReadWriteFileSystemProvider) ,
however the Rebex Sftp client component will throw just a generic SftpException with Message="Failure; Internal server error." and Status=ProtocolError (ProtocolCode=4).

Is there a way to re-throw a more specific exception on the server-side in such cases, in order to have the Sftp client recognize it and return a more specific error?

Server code:

internal class CustomS3FileSystem : ReadWriteFileSystemProvider
{
  // ...
  protected override ... SomeMethod()
  {
     // call AmazonS3Client 
     // ...      
  }
  // ...
}

// ...
_sftpFileServer = new FileServer();
_sftpFileServer.Bind(port, FileServerProtocol.Sftp);
// ...
_sftpFileServer.Users[someUser]
         .SetFileSystem(new CustomS3FileSystem (...));

Client code:

    // ...
    sFtp.GetList();
    // ...
Applies to: Rebex SFTP, File Server

1 Answer

+1 vote
by (5.3k points)
selected by
 
Best answer

//Edit: The issue mentioned in the comments has been fixed in the 6.1 version of Rebex components.

Hello,
you should use FileSystemException.
https://www.rebex.net/doc/api/Rebex.IO.FileSystem.FileSystemException.html

throw new FileSystemException(ExceptionMessage);

Below is the test which I hope speaks for itself.

FileSystemProviderThrowCustomErrorMock is a mock that inherits from ReadWriteFileSystemProvider. Every operation in mock throws FileSystemException with a custom message.

public void
             DeleteFile_When_FileSystemProvider_Throws_FileSystemException_Then_Exception_Contains_Custom_Error_Message()
             {
                    const string EXPECTED_ERROR_MESSAGE = "Delete File - error.";
                    const string TO_DELETE_FILE_NAME = "del.txt";
                    var provider = new FileSystemProviderThrowCustomErrorMock { ExceptionMessage = EXPECTED_ERROR_MESSAGE };
                    var currentExceptionMessage = runSftpOperation(provider, sftp => sftp.DeleteFile(TO_DELETE_FILE_NAME));
                    CustomStringAssert.EndsWith(EXPECTED_ERROR_MESSAGE, currentExceptionMessage);
             }
by (250 points)
I modified the code to throw FileSystemException from the overriden method in ReadWriteFileSystemProvider, but on the client, Sftp.GetList() thows the same generic exception: SftpException, with
Message="Failure; Internal server error."
ProblemType=TransferProblemType
ProtocolCode="4"
ProtcolMessage="Internal server error."
Status=ProtocolError

The code is:
--------------------------
internal class CustomS3FileSystem : ReadWriteFileSystemProvider
{
  // ...
  protected override NodeTimeInfo GetTimeInfo(NodeBase node)
  {
    string s3Path = GetS3Path(node.Path);
    s3Path = string.IsNullOrEmpty(s3Path) ? "//" : s3Path;

    try
    {
        var file = new S3FileInfo(GetS3Client(_s3BucketName), _s3BucketName, s3Path);
        return new NodeTimeInfo(file.LastWriteTime, file.LastWriteTime);
    }
    catch (AmazonS3Exception ex)
    {
        throw new FileSystemException(
            $"Error accessing the AWS S3 bucket: {_s3BucketName} object: {s3Path}: {ex.Message}", ex);
    }
  }
  // ...
}
-----------------------------

And on client:
var ftpFiles = _ftp.GetList();

--------------------

GetTimeInfo() is the first and only overridden method that is called on the server, before the exception is thrown.
by (5.3k points)
edited by
Hi,
it is really weird. _ftp is a Rebex SFTP client?

This test works.

  [Test]
        public void GetList_When_FileSystemProvider_Throws_FileSystemException_Then_Exception_Contains_Custom_Error_Message()
        {
            const string EXPECTED_ERROR_MESSAGE = "GetList - error.";
            var provider = new FileSystemProviderThrowCustomErrorMock { ExceptionMessage = EXPECTED_ERROR_MESSAGE };
            var currentExceptionMessage = runSftpOperation(provider, sftp => sftp.GetList());
            CustomStringAssert.EndsWith(EXPECTED_ERROR_MESSAGE, currentExceptionMessage);
        }

But the method in the custom FileSystemProvider that throws FileSystemException is the GetChildren method.

    protected override IEnumerable<NodeBase> GetChildren(DirectoryNode parent, NodeType nodeType)
        {
            throw new FileSystemException(ExceptionMessage);
        }
Could you please try to throw in this method?
GetTimeInfo is not called.
It is possible that your GetChildren method creates a node, then indirectly calls GetTimeInfo method, and somehow catches the FileSystemException from the GetTimeInfo method?
by (250 points)
I could throw that exception in the GetChildren() method override, however, in our case that method will not fail (for the root folder the child list is taken from other source, not directly from AWS S3). Right now, no exception is caught in GetChildren().

Only when the SFTP server is trying to retrieve the attributes for each folder under the root virtual folder, GetTimeInfo() is called, tries to go to S3, and fails (as expected).
GetTimeInfo() seems to be called directly by the Rebex FileServer component
by (5.3k points)
I can confirm that this is a bug in our virtual file system. Thanks for bringing this to our attention!
GetChildren method returns an iterator for the directory,  we call the MoveNext method on the iterator, but FileSystemException is not specially handled and a generic exception is propagated to the client.

We will fix this soon. I will keep you informed about the new release/hotfix. Thanks again.
by (250 points)
Thanks! Such a fix would be very useful, as it would allow us to display a more specific error in such cases.
by (5.3k points)
This issue is fixed in the version 6.2.
https://www.rebex.net/total-pack/history.aspx#R6.2
Thanks again for reporting!
...