The underlying connection was closed: A connection that was expected to be kept alive was closed by the server.
We're getting this exception frequently when using .Net 4.0 web service client to communicate with an ONVIF network device.
Looking at the packet captures, this seems to be a device that is non compliant with the HTTP spec and closing a connection after sending the response, against the HTTP/1.1 default of keeping it alive.
This results in the client (WCF) trying to reuse the connection while the server has just closed it,
Until the manufacturer can fix this, is there any way I can tell the web service/SOAP client NOT to use persistent connections?
Note that modifying the header to use Connection: Close won't help unless as it's being closed anyway, but the SOAP client is expecting it to stay open.
but is there any way I can tell the web service/SOAP client NOT to use persistent connections?
Yes, you can set InstanceContextMode to PerCall, it will create new InstanceContext object is created prior to and recycled subsequent to each call.
In other words, When we configure a WCF service as per call, new service instances are created for every method call you make via a WCF proxy client.
You can use it like :
setting in ServiceBehavior over Contract Interface implementation like:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
public class CalculatorService : ICalculator
{
...
}
UPDATE --
As per your comment, it seems the solution is to explicitly set the KeepAlive property to FALSE.
It can be done in multiple ways:
Code
IIS Settings
Web.config
Code
I actually don't know how much control you have over code.But at the client end where you are consuming service, we can change this behavior like:
protected override System.Net.WebRequest GetWebRequest(Uri uri)
{
System.Net.HttpWebRequest webRequest = (System.Net.HttpWebRequest)base.GetWebRequest(uri);
webRequest.KeepAlive = false;
return webRequest;
}
Or
namespace YourNamespace
{
using System.Diagnostics;
using System.Web.Services;
using System.ComponentModel;
using System.Web.Services.Protocols;
using System;
using System.Xml.Serialization;
/// <summary>
/// This partial class makes it so all requests specify
/// "Connection: Close" instead of "Connection: KeepAlive" in the HTTP headers.
/// </summary>
public partial class YourServiceNameWse : Microsoft.Web.Services3.WebServicesClientProtocol
{
protected override System.Net.WebRequest GetWebRequest(Uri uri)
{
System.Net.HttpWebRequest webRequest = (System.Net.HttpWebRequest)base.GetWebRequest(uri);
webRequest.KeepAlive = false;
return webRequest;
}
}
}
IIS Settings
Open Internet Information Services (IIS) Manager:
If you are using Windows Server 2008 or Windows Server 2008 R2:
On the taskbar, click Start, point to Administrative Tools, and then click Internet Information Services (IIS) Manager.
If you are using Windows Vista or Windows 7:
On the taskbar, click Start, and then click Control Panel.
Double-click Administrative Tools, and then double-click Internet Information Services (IIS) Manager.
In the Connections pane, go to the site, application, or directory for which you want to enable HTTP keep-alives.
In the Home pane, double-click HTTP Response Headers.
In the HTTP Response Headers pane, click Set Common Headers... in the Actions pane.
In the Set Common HTTP Response Headers dialog box, uncheck the box to disable HTTP keep-alives, and then click OK.
Additionally you can set for particular website on IIS using CommandLine like:
appcmd.exe set config "<Your Web Site Here>" -section:system.webServer/httpProtocol /allowKeepAlive:"False"
Web.Config
<configuration>
<system.webServer>
<httpProtocol allowKeepAlive="false" />
</system.webServer>
</configuration>
I hope it can help you in some way.
The underlying HttpWebRequests are created in the HttpChannelFactory used by the generated client classes.
This is created from the HttpTransportBindingElement which exposes a KeepAliveEnabled property.
The binding element is created internally in the WSHttpBinding class, and can be changed by overriding GetTransport().
private class WSHttpBindingNoKeepAlive : WSHttpBinding {
public WSHttpBindingNoKeepAlive(SecurityMode securityMode)
: base(securityMode) {
}
protected override TransportBindingElement GetTransport() {
TransportBindingElement transport = base.GetTransport();
if (transport is HttpTransportBindingElement) {
((HttpTransportBindingElement)transport).KeepAliveEnabled = false;
}
return transport;
}
}
This overriden ...Binding class can then be used like:
WSHttpBindingNoKeepAlive clientBinding = new WSHttpBindingNoKeepAlive(SecurityMode.None);
EndpointAddress address = new EndpointAddress(this.deviceUrl);
WSNameSpace.WSClient client = new WSNameSpace.WSClient(clientBinding, address);
Related
I am using Spring Boot 2 Microservices with Spring Cloud Sleuth with the Dependency Management and Spring Cloud Version Greenwich.SR2.
My service is running in an Istio service mesh.
Sample policy of istio is set to 100 (pilot.traceSampling: 100.0).
To use distributed tracing in the mesh, the applications needs to forward HTTP headers like the X-B3-TraceId and X-B3-SpanID. This is achieved by simply adding Sleuth. All my HTTP request are are traced correctly. The sidecar proxies of Istio (Envoy) send the traces to the Jaeger backend.
Sleuth is also supposed to work with Spring WebSocket. But my incoming websocket requests do not get any trace or span id by sleuth; Logs look like [-,,,].
1. Question: Why is Sleuth not working for websocket?
My WS-Config:
#Configuration
#EnableWebSocket
public class WsConfig implements WebSocketConfigurer {
#Autowired
WebSocketHandler webSocketHandler;
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
DefaultHandshakeHandler handshakeHandler = new DefaultHandshakeHandler();
handshakeHandler.setSupportedProtocols(HANDSHAKE_PROTOCOL);
registry.addHandler(webSocketHandler, WS_HANDLER_PATH + WILDCARD)
.setAllowedOrigins("*")
.setHandshakeHandler(handshakeHandler);
}
}
My clients are able to connect to my Service via Websocket. I am implementing WebSocketHandler interface to handle WS messages.
To achieve that my WS connections are logged by Sleuth, I annotate the method that handles my connection with #NewSpan:
#Override
#NewSpan
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) {
//doWork and call other services via HTTP
}
With this, Sleuth creates trace and spanId and also propagates them to the other Services, which are called via the restTemplate in this method. But HTTP calls are not send to Jaeger. The x-B3-Sampled Header is always set to 0 by the sidcar.
2 Question: Why are those traces not send to the tracing backend?
Thank you in advance!
I used the original jetty-proxy in Jetty 9, when the embedded start proxy server, after modifying the browser's proxy port, all access can be through the proxy server, I added my own filter in proxy server, add the way is:
ServletHandler handler = new ServletHandler();
FilterHolder fh = handler.addFilterWithMapping((Class<? extends Filter>) TLFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
context.addFilter(fh, "/*", EnumSet.of(DispatcherType.REQUEST));
But my filter can only filter HTTP requests, but I can't filter HTTPS .
The development maintainers at jetty gave the results:
It is possible to deliver a CONNECT request to a Servlet service method, but it is ultimately futile to do so because it is impossible to handle a CONNECT inside a servlet. You don't really have access to the raw IO streams, only the HTTP content of the request/response. You can almost make his work, but never efficiently (no async IO etc.) So ultimately you will end up falling back to Jetty APIs anyway.
Also, why not just build on the support already provided by Jetty:
http://download.eclipse.org/jetty/stable-9/xref/org/eclipse/jetty/proxy/ConnectHandler.html http://download.eclipse.org/jetty/stable-9/xref/org/eclipse/jetty/proxy/ProxyServlet.html
SSL/NPN will be handled via our loadbalancer (Haproxy), so I don't really need Jetty to do this for us.
But all the examples I can see on the web only show how to do this with SSL/NPN, not without.
Here's what I've attempted so far:
Server server = new Server();
HTTPConfiguration httpConfig = .... // set up some additional http config here
PushStrategey push = new ReferrerPushStrategy();
List<ConnectionFactory> factories = new ArrayList<>();
factories.add(new HTTPSPDYServerConnectionFactory(SPDY.V3, httpConfig, push));
factories.add(new HTTPSPDYServerConnectionFactory(SPDY.V2, httpConfig, push));
factories.add(new HTTPConnectionFactory(httpConfig));
ServerConnector connector = new ServerConnector(server, factories.toArray(new ConnectionFactory[factories.size()]));
connector.setPort(port);
server.addConnector(connector);
connector.start();
....
Unfortunately, it seems something is wrong, when I try to access the server via clients like curl or my browser they hang indefinitely. What am I doing wrong?
Thanks
When you configure a ServerConnector to speak clear-text SPDY, your clients must also speak clear-text SPDY.
If you use clients like curl or the browser, they don't speak clear-text SPDY. The clients will send a HTTP request which is not understood (the server expects SPDY), and that's why your connection "hangs".
Only Chromium/Chrome has a mode where you can make it speak clear-text SPDY, using the --use-spdy=no-ssl parameter as described here.
Therefore, if you're using clear-text SPDY there is no point in configuring multiple ServerConnectionFactory because there is no way to select one based on the protocol being negotiated, because there is no protocol negotiation.
The protocol negotiation only happens when using SSL+NPN.
Your code is basically correct (apart the unnecessary multiple ServerConnectionFactory) if you really want to setup a clear-text SPDY ServerConnector; this is an example of how the same is setup in the Jetty SPDY test suite.
Finally, see also the reference documentation about SPDY.
I have implemented a Spring WS using XWSS for security. I have added a security configuration policy file into my application.
<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config"
dumpMessages="true">
<xwss:RequireTimestamp
id="tsp"
maxClockSkew="60"
timestampFreshnessLimit="300">
</xwss:RequireTimestamp>
<xwss:RequireUsernameToken
id="token"
passwordDigestRequired="false"
nonceRequired="false"/>
<xwss:Timestamp></xwss:Timestamp>
<xwss:UsernameToken
name="service"
password="service"
id="uToken"
digestPassword="true"
useNonce="true"/>
</xwss:SecurityConfiguration>
Now I am developing a client to access the WS. The security works fine. But I am unable to test the SUCCESS case in which the client can successfully get a response from my service. The problem is I don't know how to make my client send the usernametoken and timestamp along with the request. I am using NetBeans IDE and I am implementing a JAX-WS client to access the Spring WS using this tutorial.
Please let me know what needs to be done.
For Spring WSS there is not much difference between adding a security header to the ingoing soap messages or to the outgoing ones. The process is very similar.
In both cases, you should create a interceptor for adding the security header. It is described here. So, if you create the WS client using Spring you should not have problems, especially if you have already developed the server side, but the tutorial you referenced doesn't look like using Spring for implementing the client.
You can do this by adding the following code in you client class / class extending the webservicetgatewaysupport.
SoapHeader header = msg.getSoapHeader();
StringSource headerSource = new StringSource("<wsse:Security xmlns:wsse=\"http://docs.oasis-
open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" mustUnderstand=\"1\"> <wsse:UsernameToken>
<wsse:Username>"+userName+"</wsse:Username> <wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/
oasis-200401-wss-username-token-profile-1.0#PasswordText\">"+password+"</wsse:Password> </wsse:UsernameToken>"
+"</wsse:Security>");
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(headerSource, header.getResult());
The above has to go in the message call back handler of the marshalSendANDRecieve metho of the webserviceTemplate
Check this sample for client.
And you could use SoapUI to test your server. Import WSDL, then select any request and open "Properties" window in left-bottom corner. You would see "Username", "Password" and "WSS-Password Type" related settings.
I have an application which provides services using CXF's Servlet transport and Jetty 6.1. This application also needs to consume external services. All services support WS-Addressing specification (and WS-RM on top). To consume an external service, I run a generated service client from the application.
The problem is that when I provide a decoupled endpoint for the client (WS-RM needs this endpoint to receive incoming messages via a separate http connection), CXF runs another instance of Jetty server (in spite of the fact that Servlet transport (which provides services) and the client (which consumes some external service) share the same bus). I don't need two instances of Jetty (not saying that they can't run on the same HTTP port).
Is there a way I can provide a decoupled endpoint using an existing Jetty server and Servlet transport?
So far, I enable a decoupled endpoint like this:
Client client = ClientProxy.getClient(port);
HTTPConduit httpConduit = (HTTPConduit) client.getConduit();
httpConduit.getClient().setDecoupledEndpoint(
"http://domain.com:port/services/dec_endpoints/TestDecEndpoint");
If I provide a relative path ("/dec_endpoints/TestDecEndpoint", just like relative paths are used with provision of services via Servlet transport), HTTP conduit does not specify a full path in a SOAP message's headers so this doesn't work either (server just can't send a message to /dec_endpoints/TestDecEndpoint).
Ok, I have found a solution myself. You need to specify a relative path for decoupled endpoint and change message's addressing properties manually (after MAPAggregator interceptor, 'cause it sets up the decoupled destination) so the server can send replies to your address.
So what we have:
decoupled destination using a relative path: /dec_endpoints/SomeDestination
<ReplyTo> header with an absolute path: http://addr.com:port/servlet_path/dec_endpoints/SomeDestination
Here's an example how the path can be changed:
public class ReplyToInterceptor extends AbstractPhaseInterceptor<Message>
{
public ReplyToInterceptor() {
super(Phase.PRE_LOGICAL);
addAfter(MAPAggregator.class.getName());
}
public void handleMessage(Message message) {
AddressingProperties maps = ContextUtils.retrieveMAPs(message, false,
true);
EndpointReferenceType replyTo = maps.getReplyTo();
replyTo.getAddress().setValue(
"http://address.com:port/servlet_path/dec_endpoints/SomeDestination");
}
}