none
WCF Timeout exception on streaming big files RRS feed

  • Question

  • I'm currently working on a WCF streaming service. So far everything works great for files up to 2 GB. I've set up the service as a streaming service and I am chunking the files on my own on 5 MB chunks. However, files bigger than 2 GB (somewhere there is the threshold) i always get an `InvalidOperationException`with the message

    Timeouts are not supported on this stream.


     I'm not really sure why and where this exception is thrown. It don't think this is a server side problem because every request should be the same and most of them work. But the exceptions comes from the generated proxy. So the source is 

    System.Private.ServiceModel




    Stack trace:

        at System.Runtime.AsyncResult.End[TAsyncResult](IAsyncResult result)
           at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result)
           at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
           at System.ServiceModel.Channels.ServiceChannelProxy.TaskCreator.<>c__DisplayClass0.<CreateGenericTask>b__1(IAsyncResult asyncResult)
        --- End of stack trace from previous location where exception was thrown ---
           at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
           at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
           at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
           at Company.OurApp.App.DataService.BaseFile.<DownloadItem>d__59.MoveNext()



    Here is my server implementaion:

    var response = new GetFileResponse();
                        using (var impersonation = new Impersonation(request.Domain, request.Username, request.Password))
                        {
                            using (Stream fStream = File.OpenRead(request.FullFilePath))
                            {
                                fStream.Seek(request.FilePart * request.FilePartSize, SeekOrigin.Begin);
                                BinaryReader bStream = new BinaryReader(fStream);
                                var filePart = bStream.ReadBytes(request.FilePartSize);
        
                                using (Stream mStream = new MemoryStream(filePart))
                                {
                                    response.FileByteStream = mStream;
                                    return response;
                                }
                            }
                        }
    
    The GetFileResponse looks like this:
    
        [MessageContract]
        public class GetFileResponse
        {
            [MessageBodyMember(Order = 1)]
            public Stream FileByteStream { get; set; }
        }
    

        

    This is how the client handles the download (UWP App):

    using (Stream f = await StorageFile.OpenStreamForWriteAsync())
                        {
                            //Cancelation area - after every async operation if possilble
                            for (int i = 0; i < sections; i++)
                            {
                                token.ThrowIfCancellationRequested();
                                var response = await client.GetFilePartAsync(request.ConnectionPassword, request.Domain, i, FilePartSize, FullPath, request.Password, request.Username);
                                token.ThrowIfCancellationRequested();
                                DownloadProgress = response.FileByteStream.Length;
    
                                f.Seek(0, SeekOrigin.End);
                                await f.WriteAsync(response.FileByteStream, 0, response.FileByteStream.Length);
                                await f.FlushAsync();
                            }
                        }
    

         


    And here is the service web.config:

    <system.serviceModel>
            <services>
              <service behaviorConfiguration="HttpsServiceBehaviour"
                       name="Company.OurApp.TransportService.DataService">
                <endpoint address="" binding="basicHttpBinding" bindingConfiguration="streamedBinding" contract="Company.OurAppTransportService.IDataService">
                </endpoint>
              </service>
            </services>
            <behaviors>
              <serviceBehaviors>
                <behavior name="HttpsServiceBehaviour">
                  <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
                  <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
                  <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
                  <serviceDebug includeExceptionDetailInFaults="true"/>
                </behavior>
              </serviceBehaviors>
            </behaviors>
            <bindings>
              <basicHttpBinding>
                <binding name="streamedBinding" transferMode="Streamed" closeTimeout="10:00:00">
                  <security mode="TransportCredentialOnly">
                    <transport clientCredentialType="Windows" />
                  </security>
                </binding>
              </basicHttpBinding>
            </bindings>

       



    When generating the client proxy, i set some timeouts but that didn't change anything:

     public DataServiceClient GetDataServiceClient(string endpoint = null)
                {
                    var useEndpoint = String.IsNullOrEmpty(endpoint) ? Configuration.ConfigService : endpoint;
    
                    System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding();
                    result.MaxBufferSize = int.MaxValue;
                    result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
                    result.MaxReceivedMessageSize = int.MaxValue;
                    result.AllowCookies = true;
                    result.Security.Transport.ClientCredentialType = System.ServiceModel.HttpClientCredentialType.Windows;
    
                    //TODO Try to work with timeouts for larges files?
                    result.SendTimeout = TimeSpan.FromMinutes(5);
                    result.ReceiveTimeout = TimeSpan.FromMinutes(5);
                    result.OpenTimeout = TimeSpan.MaxValue;
    
    
                    if (useEndpoint.ToLower().StartsWith("https://"))
                        result.Security.Mode = System.ServiceModel.BasicHttpSecurityMode.Transport;
                    else
                        result.Security.Mode = System.ServiceModel.BasicHttpSecurityMode.TransportCredentialOnly;
    
                    var client = new DataServiceClient(result, new System.ServiceModel.EndpointAddress(String.Concat(useEndpoint, fixedEndpointSuffix)));
                    client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
    
                    if (AppState.IsLoggedIn)
                    {
                        client.ClientCredentials.Windows.ClientCredential.UserName = $@"{AppState.Domain}\{AppState.User}";
                        client.ClientCredentials.Windows.ClientCredential.Password = AppState.Password;
                    }
    
                    return client;
                }



    Any idea where and why the exception is thrown? Server? Client? Is it coming from the stream? Help is very much appreciated.

                                                         
    Monday, June 19, 2017 7:35 AM

Answers

  • I got it solved by analyzing the exceptions with the WCF TraceViewer I also called the Service from a console application to be sure it's not a UWP problem. The problem was that I closed the streams before the response could reach the client.

    Broken implementation:

    var response = new GetFileResponse();
                using (var impersonation = new Impersonation(request.Domain, request.Username, request.Password))
                {
                    using (Stream fStream = File.OpenRead(request.FullFilePath))
                    {
                        fStream.Seek(request.FilePart * request.FilePartSize, SeekOrigin.Begin);
                        BinaryReader bStream = new BinaryReader(fStream);
                        var filePart = bStream.ReadBytes(request.FilePartSize);
    
                        using (Stream mStream = new MemoryStream(filePart))
                        {
                            response.FileByteStream = mStream;
                            return response;
                        }
                    }
                }

    This one fixed it for me:

    Stream fStream = File.OpenRead(request.FullFilePath);
    
                    long offset = request.FilePart * request.FilePartSize;
                    fStream.Seek(offset, SeekOrigin.Begin);
    
                    BinaryReader bStream = new BinaryReader(fStream);
                    var filePart = bStream.ReadBytes((int)request.FilePartSize);
    
                    Stream mStream = new MemoryStream(filePart);
    
                    response.FileByteStream = mStream;
                    return response;

    Thanks for your time Edward!!

    • Marked as answer by Dino_B Wednesday, June 28, 2017 6:06 AM
    Wednesday, June 28, 2017 6:06 AM

All replies

  • Hi Dino_B,

    Did you host WCF Service in IIS or Self-host? How long will you get this error, within 5 minutes?

    If you host in IIS, I assume you hit an IIS limitation that IIS cannot transfer more than 2 GB of data. And you will need to self-host to get around this.

    You could refer below link for more information.

    # Transferring large blobs of data with WCF

    https://blogs.msdn.microsoft.com/ryberry/2010/04/20/transferring-large-blobs-of-data-with-wcf/

    Best Regards,

    Edward


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, June 20, 2017 1:53 AM
  • Hi Dino_B,

    Did you host WCF Service in IIS or Self-host? How long will you get this error, within 5 minutes?

    If you host in IIS, I assume you hit an IIS limitation that IIS cannot transfer more than 2 GB of data. And you will need to self-host to get around this.

    You could refer below link for more information.

    # Transferring large blobs of data with WCF

    Best Regards,

    Edward

    Hey Edward,

    im hosting in IIS right. I just tracked the timeline yesterday and its sometimes after ~ 10 minutes and sometimes after ~ 15 minutes. Couldn't really say. I don't believe theres a limitation because im transfering 5 MB chunks every time. So on every request it's just a 5 MB stream which is transfered.

    Tuesday, June 20, 2017 8:15 AM
  • Hi Dino_B,

    >> I don't believe theres a limitation because im transfering 5 MB chunks every time. So on every request it's just a 5 MB stream which is transfered.

    Do you mean you have chunked 2G into many 5 MB stream, and you still got this error? Could you share us how you transfer 5MB chunks every time?

    To check whether it is related with IIS, would you mind making a test by hosting service in selfhost?

    Best Regards,

    Edward


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Wednesday, June 21, 2017 2:43 AM
  • Hey Edward,

    check out the GetFileResponse() method above (server side implementation). There you can see how i chunk the files. Yea i will try a self hosted solution for avoiding the error, thanks!

    Monday, June 26, 2017 12:31 PM
  • Hi Dino_B,

    Could you share us a simple demo which could reproduce your issue? I will try to reproduce your issue with your demo project.

    Did your issue exist when you host WCF in self host?

    If you have any updates about this issue, please feel free to let us know.

    Best Regards,

    Edward


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Wednesday, June 28, 2017 2:11 AM
  • I got it solved by analyzing the exceptions with the WCF TraceViewer I also called the Service from a console application to be sure it's not a UWP problem. The problem was that I closed the streams before the response could reach the client.

    Broken implementation:

    var response = new GetFileResponse();
                using (var impersonation = new Impersonation(request.Domain, request.Username, request.Password))
                {
                    using (Stream fStream = File.OpenRead(request.FullFilePath))
                    {
                        fStream.Seek(request.FilePart * request.FilePartSize, SeekOrigin.Begin);
                        BinaryReader bStream = new BinaryReader(fStream);
                        var filePart = bStream.ReadBytes(request.FilePartSize);
    
                        using (Stream mStream = new MemoryStream(filePart))
                        {
                            response.FileByteStream = mStream;
                            return response;
                        }
                    }
                }

    This one fixed it for me:

    Stream fStream = File.OpenRead(request.FullFilePath);
    
                    long offset = request.FilePart * request.FilePartSize;
                    fStream.Seek(offset, SeekOrigin.Begin);
    
                    BinaryReader bStream = new BinaryReader(fStream);
                    var filePart = bStream.ReadBytes((int)request.FilePartSize);
    
                    Stream mStream = new MemoryStream(filePart);
    
                    response.FileByteStream = mStream;
                    return response;

    Thanks for your time Edward!!

    • Marked as answer by Dino_B Wednesday, June 28, 2017 6:06 AM
    Wednesday, June 28, 2017 6:06 AM