FtpWebRequest 30 分钟超时
FtpWebRequest 30 minute time out
我的代码在 FTP 上下载大文件正好 30 分钟后出现超时异常。服务器是 FileZilla running on Windows. We have an SSL certificate configured with options Enable FTP over SSL/TLS support (FTPS)
and Allow explicit FTP over TLS
enabled. I have access to the server and the FileZilla 配置,但看不到任何可能导致此行为的内容。以下是 运行 在 Windows 2012 服务器计算机上的 .NET 4.6.2 上的源代码。它可以从 FTP 服务器下载文件,但如果文件下载时间超过 30 分钟,则会在恰好 30 分钟后超时并出现异常(如下所列)。
作为测试,我从同一台客户端 PC 使用 FileZilla Client、运行 从同一服务器端点同时下载多个大文件,因此每个文件的下载时间超过 30分钟完成。在这种情况下没有发生错误。
我在 Whosebug as well as Google 上搜索过,但没有找到有希望的结果。如果有人对在哪里查看(服务器端或客户端)有一些提示,我将不胜感激。
申请代码
public class FtpFileDownloader
{
// log4net
private static readonly ILog Logger = LogManager.GetLogger(typeof(FtpFileDownloader));
public void DownloadFile()
{
// setting the SecurityProtocol did not change the outcome, both were tried. Originally it was not set at all.
// ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
const int timeout = 7200000;
const string file = "some-existing-file";
try
{
var request = (FtpWebRequest) WebRequest.Create("uri-path-to-file");
request.KeepAlive = false;
request.Timeout = -1;
request.ReadWriteTimeout = timeout;
request.Credentials = new NetworkCredential("userName", "password");
request.UsePassive = true;
request.EnableSsl = true;
request.Method = WebRequestMethods.Ftp.DownloadFile;
Logger.Debug($"Downloading '{file}'");
using (var response = (FtpWebResponse) request.GetResponse())
using (var sourceStream = response.GetResponseStream())
using (var targetStream = new FileStream("some-target-on-disk", FileMode.Create, FileAccess.Write))
{
try
{
sourceStream.CopyTo(targetStream);
targetStream.Flush();
Logger.Debug($"Finished download '{file}'");
}
catch (Exception exInner)
{
Logger.Error($"Error occurred trying to download file '{file}'.", exInner);
}
}
}
catch (Exception ex)
{
Logger.Error($"Error occurred trying to dispose streams when downloading file '{file}'.", ex);
}
}
}
应用程序日志
ERROR FtpFileDownloader - Error occurred trying to download file 'some-existing-file'.
System.IO.IOException: Received an unexpected EOF or 0 bytes from the transport stream.
at System.Net.FixedSizeReader.ReadPacket(Byte[] buffer, Int32 offset, Int32 count)
at System.Net.Security._SslStream.StartFrameBody(Int32 readBytes, Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security._SslStream.StartFrameHeader(Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security._SslStream.StartReading(Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security._SslStream.ProcessRead(Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.TlsStream.Read(Byte[] buffer, Int32 offset, Int32 size)
at System.Net.FtpDataStream.Read(Byte[] buffer, Int32 offset, Int32 size)
at System.IO.Stream.InternalCopyTo(Stream destination, Int32 bufferSize)
at System.IO.Stream.CopyTo(Stream destination)
at FtpFileDownloader.DownloadFile
ERROR FtpFileDownloader - Error occurred trying to dispose streams when downloading file 'some-existing-file'.
System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a receive.
at System.Net.FtpWebRequest.SyncRequestCallback(Object obj)
at System.Net.FtpWebRequest.RequestCallback(Object obj)
at System.Net.CommandStream.Dispose(Boolean disposing)
at System.IO.Stream.Close()
at System.IO.Stream.Dispose()
at System.Net.ConnectionPool.Destroy(PooledStream pooledStream)
at System.Net.ConnectionPool.PutConnection(PooledStream pooledStream, Object owningObject, Int32 creationTimeout, Boolean canReuse)
at System.Net.FtpWebRequest.FinishRequestStage(RequestStage stage)
at System.Net.FtpWebRequest.SyncRequestCallback(Object obj)
at System.Net.FtpWebRequest.RequestCallback(Object obj)
at System.Net.CommandStream.Abort(Exception e)
at System.Net.CommandStream.CheckContinuePipeline()
at System.Net.FtpWebRequest.DataStreamClosed(CloseExState closeState)
at System.Net.FtpDataStream.System.Net.ICloseEx.CloseEx(CloseExState closeState)
at System.Net.FtpDataStream.Dispose(Boolean disposing)
at System.IO.Stream.Close()
at System.IO.Stream.Dispose()
at FtpFileDownloader.DownloadFile
FileZilla - 常规设置
Listen on these ports: 21
Max. number of users: 0 (infinite)
Number of threads: 2
Connection timeout: 120 (seconds)
No Transfer timeout: 9000 (seconds)
Log timeout: 60 (seconds)
FileZilla 服务器日志
23-2-2018 11:40:40 - (not logged in) (194.123.75.2)> Connected on port 21, sending welcome message...
23-2-2018 11:40:40 - (not logged in) (194.123.75.2)> 220 Welcome
23-2-2018 11:40:40 - (not logged in) (194.123.75.2)> AUTH TLS
23-2-2018 11:40:40 - (not logged in) (194.123.75.2)> 234 Using authentication type TLS
23-2-2018 11:40:40 - (not logged in) (194.123.75.2)> TLS connection established
23-2-2018 11:40:40 - (not logged in) (194.123.75.2)> USER my-user-account
23-2-2018 11:40:40 - (not logged in) (194.123.75.2)> 331 Password required for my-user-account
23-2-2018 11:40:40 - (not logged in) (194.123.75.2)> PASS **************
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> 230 Logged on
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> PBSZ 0
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> 200 PBSZ=0
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> PROT P
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> 200 Protection level set to P
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> OPTS utf8 on
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> 202 UTF8 mode is always enabled. No need to send this command.
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> PWD
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> 257 "/" is current directory.
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> TYPE I
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> 200 Type set to I
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> PASV
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> 227 Entering Passive Mode (IP-ADDRESS,245,222)
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> RETR path-to-file
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> 150 Opening data channel for file download from server of "/path-to-file"
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> TLS connection for data connection established
23-2-2018 12:10:41 - my-user-account (194.123.75.2)> disconnected.
请注意断开连接(最后一行)和之前的行之间有 30 分钟。如果它已成功完成传输,在 disconnected
行
之前还会有一行显示 226 Successfully transferred "/path-to-file"
进度更新:
- (2018.02.20) 我让我们的网络团队检查了防火墙规则,但他们没有找到任何感兴趣的东西。
- (2018.02.22) 我发现正在使用的 FileZilla Server 版本是
0.9.43 beta
(根据更改日志发布日期 2014-01-02) .尽管我在更改日志中没有发现任何表明此行为曾经是已修复错误的内容,但我将升级到最新版本 0.9.60.2
(发布日期 2017-02-08) 并再次执行测试。将在 24 小时内报告。
- (2018.02.23) FileZilla 已更新至最新版本。这并没有解决问题,我更新了服务器日志,但它看起来与以前的日志几乎相同,除了最后一次传输是通过 TLS 而不是 SSL 进行的。
- (2018.02.23) 我在 FileZilla 支持页面上找到了以下 link Timeouts on large files。我准备把它推回给我们托管服务提供商的网络人员再看一下。
- (2018.02.27) 原来是公司防火墙(客户端执行的网络)而不是托管防火墙(托管FileZilla的网络) 是罪魁祸首。它被配置为在
1800
秒后丢弃空闲连接。已添加规则以覆盖这两个端点之间的规则。
罪魁祸首/答案
原来是公司防火墙(客户端执行的网络)而不是托管防火墙(托管 FileZilla 的网络 ) 是罪魁祸首。它被配置为在 1800
秒后丢弃空闲连接。已添加规则以覆盖这两个端点之间的规则。
是的,我认为您的代码中没有 "mistake";只是控制连接在 30 分钟后消失,即使传输连接没有超时。也许甚至不需要更改您的 KeepAlive 和 Timeout 值,只需尝试每 20 分钟左右通过虚拟下载重用您的请求:这样您将重置控制连接计时器。
顺便说一句,我在某处读到 30 分钟是 FileZilla Server 的标准超时,它基于 6 keep-alive 配置为每 300 秒发送一次(这给了你 30 分钟的体验)。
如果您可以尝试使用另一台 FTP/FTPS 服务器,您可能会发现不同的空闲超时并且不会违反 30 分钟限制(但不同的限制)。
所以,我个人会投资制作下面的代码 async
,因此执行流程在封闭 using
之后继续,您可以进入一个循环,每 20 分钟重复一次您的请求(及其控制连接)进行虚拟下载。
当然 FileZilla Client 不需要虚拟下载,因为它在较低级别运行并且可能发送 TCP 命令以保持控制连接处于活动状态。
using (var response = (FtpWebResponse) request.GetResponse())
using (var sourceStream = response.GetResponseStream())
using (var targetStream = new FileStream("some-target-on-disk", FileMode.Create, FileAccess.Write))
{
try
{
sourceStream.CopyTo(targetStream);
targetStream.Flush();
Logger.Debug($"Finished download '{file}'");
}
catch (Exception exInner)
{
Logger.Error($"Error occurred trying to download file '{file}'.", exInner);
}
}
也许您应该尝试另一种 FTP 协议客户端的实现,它不是构建在 FtpWebRequest
之上的。
相关问题由来已久,没有明确的解决方案或答案。所以我会尝试像 FluentFTP 这样的东西,它直接使用 Winsock API。 XML 文档评论指出 DownloadFile()
应该可以很好地处理大文件下载:
/// <summary>
/// Downloads the specified file onto the local file system.
/// High-level API that takes care of various edge cases internally.
/// Supports very large files since it downloads data in chunks.
/// </summary>
更多信息请查看:
- Downloading large files(~150MB) from FTP server hangs 关于 Whosebug 的讨论
- FtpWebRequest is Broken 马特·米切尔的文章
- Winsock API Microsoft TechNet 上的页面
- Elmue Partial FTP Downloader 文章中的源代码部分
有一个类似的issue reported in FluentFTP here and stefanolazzarato posted a work-around:
int progress = -1;
try
{
FtpClient client = new FtpClient("HOST");
client.Credentials = new NetworkCredential("USER", "PASSWORD");
client.Connect();
client.UploadFile("LOCALPATH/FILENAME", "REMOTEPATH/FILENAME",
FtpExists.Overwrite,
false,
FtpVerify.None,
new Progress<FtpProgress>(p => progress = Convert.ToInt32(p.Progress))
);
}
catch (Exception ex)
{
if (progress == 100 && ex is FluentFTP.FtpException && ex.InnerException != null && ex.InnerException is TimeoutException)
{
// Upload complete
// LOG Info exception
}
else
{
// LOG Fatal exception
throw;
}
}
我的代码在 FTP 上下载大文件正好 30 分钟后出现超时异常。服务器是 FileZilla running on Windows. We have an SSL certificate configured with options Enable FTP over SSL/TLS support (FTPS)
and Allow explicit FTP over TLS
enabled. I have access to the server and the FileZilla 配置,但看不到任何可能导致此行为的内容。以下是 运行 在 Windows 2012 服务器计算机上的 .NET 4.6.2 上的源代码。它可以从 FTP 服务器下载文件,但如果文件下载时间超过 30 分钟,则会在恰好 30 分钟后超时并出现异常(如下所列)。
作为测试,我从同一台客户端 PC 使用 FileZilla Client、运行 从同一服务器端点同时下载多个大文件,因此每个文件的下载时间超过 30分钟完成。在这种情况下没有发生错误。
我在 Whosebug as well as Google 上搜索过,但没有找到有希望的结果。如果有人对在哪里查看(服务器端或客户端)有一些提示,我将不胜感激。
申请代码
public class FtpFileDownloader
{
// log4net
private static readonly ILog Logger = LogManager.GetLogger(typeof(FtpFileDownloader));
public void DownloadFile()
{
// setting the SecurityProtocol did not change the outcome, both were tried. Originally it was not set at all.
// ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
const int timeout = 7200000;
const string file = "some-existing-file";
try
{
var request = (FtpWebRequest) WebRequest.Create("uri-path-to-file");
request.KeepAlive = false;
request.Timeout = -1;
request.ReadWriteTimeout = timeout;
request.Credentials = new NetworkCredential("userName", "password");
request.UsePassive = true;
request.EnableSsl = true;
request.Method = WebRequestMethods.Ftp.DownloadFile;
Logger.Debug($"Downloading '{file}'");
using (var response = (FtpWebResponse) request.GetResponse())
using (var sourceStream = response.GetResponseStream())
using (var targetStream = new FileStream("some-target-on-disk", FileMode.Create, FileAccess.Write))
{
try
{
sourceStream.CopyTo(targetStream);
targetStream.Flush();
Logger.Debug($"Finished download '{file}'");
}
catch (Exception exInner)
{
Logger.Error($"Error occurred trying to download file '{file}'.", exInner);
}
}
}
catch (Exception ex)
{
Logger.Error($"Error occurred trying to dispose streams when downloading file '{file}'.", ex);
}
}
}
应用程序日志
ERROR FtpFileDownloader - Error occurred trying to download file 'some-existing-file'.
System.IO.IOException: Received an unexpected EOF or 0 bytes from the transport stream.
at System.Net.FixedSizeReader.ReadPacket(Byte[] buffer, Int32 offset, Int32 count)
at System.Net.Security._SslStream.StartFrameBody(Int32 readBytes, Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security._SslStream.StartFrameHeader(Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security._SslStream.StartReading(Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security._SslStream.ProcessRead(Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.TlsStream.Read(Byte[] buffer, Int32 offset, Int32 size)
at System.Net.FtpDataStream.Read(Byte[] buffer, Int32 offset, Int32 size)
at System.IO.Stream.InternalCopyTo(Stream destination, Int32 bufferSize)
at System.IO.Stream.CopyTo(Stream destination)
at FtpFileDownloader.DownloadFile
ERROR FtpFileDownloader - Error occurred trying to dispose streams when downloading file 'some-existing-file'.
System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a receive.
at System.Net.FtpWebRequest.SyncRequestCallback(Object obj)
at System.Net.FtpWebRequest.RequestCallback(Object obj)
at System.Net.CommandStream.Dispose(Boolean disposing)
at System.IO.Stream.Close()
at System.IO.Stream.Dispose()
at System.Net.ConnectionPool.Destroy(PooledStream pooledStream)
at System.Net.ConnectionPool.PutConnection(PooledStream pooledStream, Object owningObject, Int32 creationTimeout, Boolean canReuse)
at System.Net.FtpWebRequest.FinishRequestStage(RequestStage stage)
at System.Net.FtpWebRequest.SyncRequestCallback(Object obj)
at System.Net.FtpWebRequest.RequestCallback(Object obj)
at System.Net.CommandStream.Abort(Exception e)
at System.Net.CommandStream.CheckContinuePipeline()
at System.Net.FtpWebRequest.DataStreamClosed(CloseExState closeState)
at System.Net.FtpDataStream.System.Net.ICloseEx.CloseEx(CloseExState closeState)
at System.Net.FtpDataStream.Dispose(Boolean disposing)
at System.IO.Stream.Close()
at System.IO.Stream.Dispose()
at FtpFileDownloader.DownloadFile
FileZilla - 常规设置
Listen on these ports: 21
Max. number of users: 0 (infinite)
Number of threads: 2
Connection timeout: 120 (seconds)
No Transfer timeout: 9000 (seconds)
Log timeout: 60 (seconds)
FileZilla 服务器日志
23-2-2018 11:40:40 - (not logged in) (194.123.75.2)> Connected on port 21, sending welcome message...
23-2-2018 11:40:40 - (not logged in) (194.123.75.2)> 220 Welcome
23-2-2018 11:40:40 - (not logged in) (194.123.75.2)> AUTH TLS
23-2-2018 11:40:40 - (not logged in) (194.123.75.2)> 234 Using authentication type TLS
23-2-2018 11:40:40 - (not logged in) (194.123.75.2)> TLS connection established
23-2-2018 11:40:40 - (not logged in) (194.123.75.2)> USER my-user-account
23-2-2018 11:40:40 - (not logged in) (194.123.75.2)> 331 Password required for my-user-account
23-2-2018 11:40:40 - (not logged in) (194.123.75.2)> PASS **************
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> 230 Logged on
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> PBSZ 0
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> 200 PBSZ=0
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> PROT P
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> 200 Protection level set to P
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> OPTS utf8 on
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> 202 UTF8 mode is always enabled. No need to send this command.
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> PWD
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> 257 "/" is current directory.
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> TYPE I
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> 200 Type set to I
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> PASV
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> 227 Entering Passive Mode (IP-ADDRESS,245,222)
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> RETR path-to-file
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> 150 Opening data channel for file download from server of "/path-to-file"
23-2-2018 11:40:40 - my-user-account (194.123.75.2)> TLS connection for data connection established
23-2-2018 12:10:41 - my-user-account (194.123.75.2)> disconnected.
请注意断开连接(最后一行)和之前的行之间有 30 分钟。如果它已成功完成传输,在 disconnected
行
226 Successfully transferred "/path-to-file"
进度更新:
- (2018.02.20) 我让我们的网络团队检查了防火墙规则,但他们没有找到任何感兴趣的东西。
- (2018.02.22) 我发现正在使用的 FileZilla Server 版本是
0.9.43 beta
(根据更改日志发布日期 2014-01-02) .尽管我在更改日志中没有发现任何表明此行为曾经是已修复错误的内容,但我将升级到最新版本0.9.60.2
(发布日期 2017-02-08) 并再次执行测试。将在 24 小时内报告。 - (2018.02.23) FileZilla 已更新至最新版本。这并没有解决问题,我更新了服务器日志,但它看起来与以前的日志几乎相同,除了最后一次传输是通过 TLS 而不是 SSL 进行的。
- (2018.02.23) 我在 FileZilla 支持页面上找到了以下 link Timeouts on large files。我准备把它推回给我们托管服务提供商的网络人员再看一下。
- (2018.02.27) 原来是公司防火墙(客户端执行的网络)而不是托管防火墙(托管FileZilla的网络) 是罪魁祸首。它被配置为在
1800
秒后丢弃空闲连接。已添加规则以覆盖这两个端点之间的规则。
罪魁祸首/答案
原来是公司防火墙(客户端执行的网络)而不是托管防火墙(托管 FileZilla 的网络 ) 是罪魁祸首。它被配置为在 1800
秒后丢弃空闲连接。已添加规则以覆盖这两个端点之间的规则。
是的,我认为您的代码中没有 "mistake";只是控制连接在 30 分钟后消失,即使传输连接没有超时。也许甚至不需要更改您的 KeepAlive 和 Timeout 值,只需尝试每 20 分钟左右通过虚拟下载重用您的请求:这样您将重置控制连接计时器。
顺便说一句,我在某处读到 30 分钟是 FileZilla Server 的标准超时,它基于 6 keep-alive 配置为每 300 秒发送一次(这给了你 30 分钟的体验)。 如果您可以尝试使用另一台 FTP/FTPS 服务器,您可能会发现不同的空闲超时并且不会违反 30 分钟限制(但不同的限制)。
所以,我个人会投资制作下面的代码 async
,因此执行流程在封闭 using
之后继续,您可以进入一个循环,每 20 分钟重复一次您的请求(及其控制连接)进行虚拟下载。
当然 FileZilla Client 不需要虚拟下载,因为它在较低级别运行并且可能发送 TCP 命令以保持控制连接处于活动状态。
using (var response = (FtpWebResponse) request.GetResponse())
using (var sourceStream = response.GetResponseStream())
using (var targetStream = new FileStream("some-target-on-disk", FileMode.Create, FileAccess.Write))
{
try
{
sourceStream.CopyTo(targetStream);
targetStream.Flush();
Logger.Debug($"Finished download '{file}'");
}
catch (Exception exInner)
{
Logger.Error($"Error occurred trying to download file '{file}'.", exInner);
}
}
也许您应该尝试另一种 FTP 协议客户端的实现,它不是构建在 FtpWebRequest
之上的。
相关问题由来已久,没有明确的解决方案或答案。所以我会尝试像 FluentFTP 这样的东西,它直接使用 Winsock API。 XML 文档评论指出 DownloadFile()
应该可以很好地处理大文件下载:
/// <summary>
/// Downloads the specified file onto the local file system.
/// High-level API that takes care of various edge cases internally.
/// Supports very large files since it downloads data in chunks.
/// </summary>
更多信息请查看:
- Downloading large files(~150MB) from FTP server hangs 关于 Whosebug 的讨论
- FtpWebRequest is Broken 马特·米切尔的文章
- Winsock API Microsoft TechNet 上的页面
- Elmue Partial FTP Downloader 文章中的源代码部分
有一个类似的issue reported in FluentFTP here and stefanolazzarato posted a work-around:
int progress = -1; try { FtpClient client = new FtpClient("HOST"); client.Credentials = new NetworkCredential("USER", "PASSWORD"); client.Connect(); client.UploadFile("LOCALPATH/FILENAME", "REMOTEPATH/FILENAME", FtpExists.Overwrite, false, FtpVerify.None, new Progress<FtpProgress>(p => progress = Convert.ToInt32(p.Progress)) ); } catch (Exception ex) { if (progress == 100 && ex is FluentFTP.FtpException && ex.InnerException != null && ex.InnerException is TimeoutException) { // Upload complete // LOG Info exception } else { // LOG Fatal exception throw; } }