Additional Information about Silverlight Client Timeout Value when calling a WCF WebService

By default, a Silverlight client calling WCF uses the timeout settings documented here, https://msdn.microsoft.com/en-us/library/system.servicemodel.channels.binding.sendtimeout%28v=vs.95%29.aspx .

  • SendTimeout, the default value is 1 minute.
  • ReceiveTimeout, the default value is 10 minutes.

If you have more data to send to server or you are using Silverlight client over a slow connection, 1 minute may not be enough. MSDN documents how you can use Silverlight ServicesReferences.ClientConfig binding setting to increase the client timeout, link: https://msdn.microsoft.com/en-us/library/cc197941(v=vs.95).aspx . But it may not always work as expected and it assumes Silverlight custom timeout value is always shorter than IE browser/WinINet default timeout. If the Silverlight timeout is not shorter than IE/WinINet default timeout, changing ServicesReferences.ClientConfig setting will not work.

For example, here is my timeout setting in my ServicesReferences.ClientConfig file. Yes, I am using a very extremely large scenario to demo the story.     
<configuration>

    <system.serviceModel>

        <bindings>

            <basicHttpBinding>

                <bindingname="BasicHttpBinding_IService1"maxBufferSize="2147483647"

                     maxReceivedMessageSize="2147483647"

                      closeTimeout="00:59:00"

                     openTimeout="00:53:00"

                     receiveTimeout="03:15:00"

                     sendTimeout="03:21:00">

                    <securitymode="None" />

                </binding>

            </basicHttpBinding>

        </bindings>

        <client>

            <endpointaddress="https://hzmain/WCFtest/Service1.svc"

binding="basicHttpBinding"

bindingConfiguration="BasicHttpBinding_IService1" contract="ServiceReference1.IService1"

              name="BasicHttpBinding_IService1" />

        </client>

    </system.serviceModel>

</configuration>

 1) Let us look at WebRequest.RegisterPrefix("https://", WebRequestCreator.BrowserHttp) case, which is by default. And Silverlight uses BrowserHttp stack. 

If we look at the send event, here is timer in Silverlight:

    -> 0a7e2758 System.Threading.TimerQueueTimer

    -> 0a7e26a8 System.Net.Browser.BrowserHttpWebRequest

-> 0a7e4cec MS.Internal.InternalWebRequest

 

0:002> !do 0a7e2758

Name: System.Threading.TimerQueueTimer

MethodTable: 79509288

794f72a8 40005a8 20 System.UInt32 1 instance 12060000 m_dueTime

794f72a8 40005a9 24 System.UInt32 1 instance 4294967295 m_period

 12060000 milliseconds = 12060 seconds = 201 minutes = sendTimeout="03:21:00"

So, the timeout value did get updated in IE browser from ServicesReferences.ClientConfig, but turns out, the change is only at System.Net.Browser.BrowserHttpWebRequest level to make timer trigger abort connection. However, as I learnt, the underlying WinINet connection still continues to use the default WinINet timeout.

 

Notice the callstack to show how Silverlight npctrl.dll is eventually calling into wininet.dll for HttpSendRequestExW to send out the request.

 0:017> kL

ChildEBP RetAddr 

056eaad4 750e31de WININET!HttpSendRequestExW

056eab18 750c1734 urlmon!CINetHttp::INetAsyncSendRequest+0x3fd

056eb33c 750c1b92 urlmon!CINetHttp::INetAsyncOpenRequest+0x335

056eb35c 750c10e3 urlmon!CINet::INetAsyncConnect+0x27d

056eb378 750bd1b2 urlmon!CINet::INetAsyncOpen+0x15a

056eb38c 750bd168 urlmon!CINet::INetAsyncStart+0x1d

056eb3a4 750bd3c7 urlmon!CINet::StartCommon+0x1dd

056eb3b8 750b98da urlmon!CINet::StartEx+0x1a

056eb3ec 750b8d2b urlmon!COInetProt::StartEx+0xc1

056eb450 750ba097 urlmon!CTransaction::StartEx+0x40b

056eb4ec 750a386c urlmon!CBinding::StartBinding+0x883

056eb558 5cfb3ace urlmon!operator new+0x20

056ec664 5cfb40ca npctrl!CWindowsDownloadRequest::_Download+0x446

056ed8d0 5cfb431d npctrl!CWindowsDownloadRequest::InitiateRequest+0x5ea

056ed938 5cfb3835 npctrl!CXcpBrowserHost::ProcessAsyncDownloadRequest_BrowserImpl+0x2a7

056ed980 5cfb36ef npctrl!CXcpBrowserHost::ProcessAsyncDownloadRequest+0x117

056ed9c0 5cfb3526 npctrl!CDownloadRequest::Download+0x4f

056ed9e0 5cfb34cd npctrl!CAsyncDownloadRequestManager::DownloadRequests+0x48

056ed9f4 5cfa48a4 npctrl!CXcpDispatcher::OnWindowMessage+0x114

056eda0c 5cfa3270 npctrl!CXcpDispatcher::ProcessMessage+0xa2

 

If you continue debugging into the HttpSendRequestExW first parameter of  hRequest = 0x00cc000c handle with debugging symbol, you will see the IE/WinINet timeout, such as SendTimeOut and ReceiveTimeOut, is still using the default IE timeout value. It is not modified by your ServicesReferences.ClientConfig setting of timeout.

 

2) Now, let us look at ClientHttp case, i.e. WebRequest.RegisterPrefix("https://", WebRequestCreator.ClientHttp):

Note: For the difference between ClientHttp vs BrowserHttp handling in Silverlight, please check MSDN https://msdn.microsoft.com/en-us/library/dd920295%28v=VS.95%29.aspx .

Now it is Silverlight npctrl.dll / npctrl!CWindowsOSWebRequest class still calling WinINet. The difference is: It is calling Wininet.dll directly instead of UrlMon build-in BrowserHttp stack. So, ClientHttp implementation is still on top of wininet.dll for underlying connection instead of winsock in order to avoid ClientHttp implementing the protocol details.

Callstack:

0:044> kL

ChildEBP RetAddr 

0ea5f954 5cfb933e WININET!HttpSendRequestW

0ea5fac0 5cfb8b71 npctrl!CWindowsOSWebRequest::DoOperatingSystemWebRequest+0xbd4

0ea5faf0 5e29027b npctrl!COperatingSystemWebRequest::OperatingSystemWebRequestThreadFn+0x60

0ea5fb08 7689339a IEFRAME!_Detour_ThreadProc+0x23

0ea5fb14 77319ef2 kernel32!BaseThreadInitThunk+0xe

0ea5fb54 77319ec5 ntdll!__RtlUserThreadStart+0x70

0ea5fb6c 00000000 ntdll!_RtlUserThreadStart+0x1b

If you debug into the WININET!HttpSendRequestW handle level, you will see the WinINet is still using IE default SendTimeout value instead of your ServicesReferences.ClientConfig timeout value.

But, again, .NET level timer is 3:21 hours, which is much longer than WinINet layer can possibly even keep the connection.

0:044> !do 0acf6594

Name: System.Threading.TimerQueueTimer

MethodTable: 79509288

EEClass: 790b83e0

Size: 52(0x34) bytes

File: C:\Program Files (x86)\Microsoft Silverlight\5.1.10411.0\mscorlib.dll

Fields:

      MT Field Offset Type VT Attr Value Name

79509288 40005a5 4 ...g.TimerQueueTimer 0 instance 00000000 m_next

79509288 40005a6 8 ...g.TimerQueueTimer 0 instance 00000000 m_prev

794f71d4 40005a7 1c System.Int32 1 instance 495612727 m_startTicks

794f72a8 40005a8 20 System.UInt32 1 instance 12060000 m_dueTime

 12060000 milliseconds = 12060 seconds = 201 minutes = sendTimeout="03:21:00"

So, you can see Silverlight creates WinINet connection as the underlying connection implementation. But when you create your own wcf Silverlight client timeout values in ServicesReferences.ClientConfig, a timer is created at .NET layer, but doesn't modify the underlying WinINet timeout values, which includes both SendTimeOut and RecieveTimeOut for WinINet. It will work only if your timeout setting in ServicesReferences.ClientConfig is less than or equal to IE/WinINet timeout setting.

So, how can we also update the SendTimeOut and ReceiveTimeout at IE WinINet level for my Silverlight client?

Answer: We can only do it through IE regkey (applies to IE browser only). Here is the location and key name:

[HKCU\Software\Microsoft\windows\currentversion\internet settings\]

ReceiveTimeout

DWORD in Millisecond unit

SendTimeout

DWORD in Millisecond unit