3

我有一个场景,我需要使用基本身份验证连接到安全的外部肥皂网络服务。通常这不是问题,这在我的本地开发环境中使用 basicHttpBinding。部署应用程序后,它将位于 DMZ 中,并且无法向世界发出呼叫。这在我们的内部网络上产生了对路由服务的需求,而 WCF 4.0 中的新路由功能似乎是一个不错的选择。我让代码通过路由服务作为 PoC 通过 http 调用不同的外部服务,然后决定迁移到 https。我很快发现来自初始调用应用程序的用户名凭据不会被路由服务传递,因此我实现了一个自定义 IEndpointBehavior 来将正确的凭据添加到路由服务调用到外部客户端。

客户端应用程序:

<basicHttpBinding>
    <binding name="SimpleHttp">
        <security mode="Transport">
            <transport clientCredentialType="Basic"/>
        </security>
    </binding>
</basicHttpBinding>

<endpoint address="https://mymachine/routingservice.svc"
          binding="basicHttpBinding" 
          contract="TheContract" 
          name="MyEndpoint" 
          bindingConfiguration="SimpleHttp"/>

路由服务(服务端点):

<services>
    <service behaviorConfiguration="routingConfiguration"
          name="System.ServiceModel.Routing.RoutingService">
        <endpoint address=""
              binding="basicHttpBinding"
              name="RoutingServiceEndpoint"
              contract="System.ServiceModel.Routing.IRequestReplyRouter"/>
    </service>
</services>

<basicHttpBinding>
    <binding>
        <security mode="Transport">
            <transport clientCredentialType="None"/>
        </security>
    </binding>
</basicHttpBinding>

<serviceBehaviors>
    <behavior name="routingConfiguration">
        <!-- leaving out the filter details for now, since it's match all -->
        <routing filterTableName="filterTable1" />
    </behavior>
</serviceBehaviors>

路由服务(客户端端点):

<client>
    <endpoint name="realDestination"
              address="https://externalwebservice/service/"
              binding="basicHttpBinding"
              bindingConfiguration="otherBasicHttpBinding"
              behaviorConfiguration="CredWriter"
              contract="*" />
</client>

<basicHttpBinding>
    <binding name="otherBasicHttpBinding">
        <security mode="Transport">
            <transport clientCredentialType="Basic"/>
        </security>
    </binding>
</basicHttpBinding>

<endpointBehaviors>
    <behavior name="CredWriter">
        <soapProcessing processMessages="false"/>
        <myCredentialAdder/>
    </behavior>
</endpointBehaviors>

<extensions>
    <behaviorExtensions>
        <add name="myCredentialAdder" type="Assembly Info here."/>
    </behaviorExtensions>
</extensions>

假设我没有遗漏一些明显的东西,这一切都应该正常工作,并继续以愉快的方式通过 https 将信息从客户端到路由器再到外部服务并返回。(大假设)

相反,我看到了以下异常和堆栈跟踪:

System.ServiceModel.CommunicationException: 
An error occurred while receiving the HTTP response to https://mymachine/routingservice.svc. 
This could be due to the service endpoint binding not using the HTTP protocol. 
This could also be due to an HTTP request context being aborted by the server 
(possibly due to the service shutting down). 
See server logs for more details. ---> 
System.Net.WebException: The underlying connection was closed: 
An unexpected error occurred on a receive. ---> 
System.IO.IOException: Unable to read data from the transport connection: 
An existing connection was forcibly closed by the remote host. ---> 
System.Net.Sockets.SocketException: 
An existing connection was forcibly closed by the remote host
   at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
   at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
   --- End of inner exception stack trace ---
   at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
   at System.Net.FixedSizeReader.ReadPacket(Byte[] buffer, Int32 offset, Int32 count)
   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.PooledStream.Read(Byte[] buffer, Int32 offset, Int32 size)
   at System.Net.Connection.SyncRead(HttpWebRequest request, Boolean userRetrievedStream, Boolean probeRead)
   --- End of inner exception stack trace ---
   at System.Net.HttpWebRequest.GetResponse()
   at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
   --- End of inner exception stack trace ---

Server stack trace: 
   at System.ServiceModel.Channels.HttpChannelUtilities.ProcessGetResponseWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)
   at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
   at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

Exception rethrown at [0]: 
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   //calls to my code were here.

看到这一点,我进一步研究了它,并通过 Fiddler 传输了所有内容以查看发生了什么。

我的客户端应用程序通过 https 调用我的 IIS 托管路由服务,然后等待。IIS 托管路由服务调用外部服务。这就是事情变得奇怪的地方。有一个 https 200,显示在肥皂信封中返回了一个新的“实体”,另一个 https 200 也从服务返回了另一个新的“实体”,然后我从客户端应用程序的调用几乎得到了 504 网关超时第二个请求从外部服务返回的确切时间。

这个设置给了我一个新的例外:

System.TimeoutException: The request channel timed out while waiting for a reply after 00:00:59.5720000. 
Increase the timeout value passed to the call to Request or increase the SendTimeout value on the Binding. 
The time allotted to this operation may have been a portion of a longer timeout. 
---> System.TimeoutException: The remote server returned an error: (504) Gateway Timeout. 
---> System.Net.WebException: The remote server returned an error: (504) Gateway Timeout.
   at System.Net.HttpWebRequest.GetResponse()
   at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
   --- End of inner exception stack trace ---
   at System.ServiceModel.Channels.HttpChannelUtilities.ProcessGetResponseWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)
   at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
   at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
   --- End of inner exception stack trace ---

Server stack trace: 
   at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

Exception rethrown at [0]: 
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   //calls to my code

我尝试按照异常建议延长超时时间,但这不起作用,因为它在一两秒内失败,而不是几分钟。因此,在互联网上搜索此问题的示例和其他解决方案之后,此时我还没有找到任何东西来回答为什么会失败,可能存在哪些潜在问题,或者任何试图完成相同任务的人事物。

我希望您,社区,能够找到我还没有找到的东西,已经自己解决了这个问题并愿意分享解决方案,或者可以轻松地发现我的绑定或设置的问题。

4

0 回答 0