Reputation: 45
I'm running a spring 3.1.2 backend on a weblogic 12.1.3 server. In order to accept websocket connections, my configurator as follows:
public class SpringConfigurator extends Configurator {
private static final Logger LOGGER = LoggerFactory.make();
private static final Map<String, Map<Class<?>, String>> cache = new ConcurrentHashMap<String, Map<Class<?>, String>>();
private static final String MAGIC_STR = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
private static final String NO_VALUE = ObjectUtils.identityToString(new Object());
@SuppressWarnings("unchecked")
@Override
public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {
WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
if (wac == null) {
String message = "Failed to find the root WebApplicationContext. Was ContextLoaderListener not used?";
LOGGER.error(message);
throw new IllegalStateException(message);
}
String beanName = ClassUtils.getShortNameAsProperty(endpointClass);
if (wac.containsBean(beanName)) {
T endpoint = wac.getBean(beanName, endpointClass);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Using @ServerEndpoint singleton " + endpoint);
}
return endpoint;
}
Component annot = AnnotationUtils.findAnnotation(endpointClass, Component.class);
if ((annot != null) && wac.containsBean(annot.value())) {
T endpoint = wac.getBean(annot.value(), endpointClass);
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Using @ServerEndpoint singleton " + endpoint);
}
return endpoint;
}
beanName = getBeanNameByType(wac, endpointClass);
if (beanName != null) {
return (T) wac.getBean(beanName);
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Creating new @ServerEndpoint instance of type " + endpointClass);
}
return wac.getAutowireCapableBeanFactory().createBean(endpointClass);
}
// modifyHandshake() is called before getEndpointInstance()
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
super.modifyHandshake(sec, request, response);
}
private String getBeanNameByType(WebApplicationContext wac, Class<?> endpointClass) {
String wacId = wac.getId();
Map<Class<?>, String> beanNamesByType = cache.get(wacId);
if (beanNamesByType == null) {
beanNamesByType = new ConcurrentHashMap<Class<?>, String>();
cache.put(wacId, beanNamesByType);
}
if (!beanNamesByType.containsKey(endpointClass)) {
String[] names = wac.getBeanNamesForType(endpointClass);
if (names.length == 1) {
beanNamesByType.put(endpointClass, names[0]);
} else {
beanNamesByType.put(endpointClass, NO_VALUE);
if (names.length > 1) {
String message = "Found multiple @ServerEndpoint's of type " + endpointClass + ", names=" + names;
LOGGER.error(message);
throw new IllegalStateException(message);
}
}
}
String beanName = beanNamesByType.get(endpointClass);
return NO_VALUE.equals(beanName) ? null : beanName;
}
}
The problem is when I try to open websocket connection via a javascript client, it correctly generates response headers as I debugged this location:
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
super.modifyHandshake(sec, request, response);
}
But in client side it gives following error:
WebSocket connection to 'ws://localhost:7001/websocket' failed: Error during >WebSocket handshake: Invalid status line
In chrome developer tools the response seems as follows:
HTTP/0.9 200 OK
I think somehow http request does not upgrade to websocket connection.
I really appreciate any help regarding this issue.
Upvotes: 3
Views: 1788
Reputation: 1108652
I encountered exactly this issue today when testing http://showcase.omnifaces.org/push/socket on WebLogic 12.2.1.
Already at the first test attempt of the webapp, WebLogic throws the below exception when making a websocket connection:
java.lang.IllegalStateException: The async-support is disabled on this request: weblogic.servlet.internal.ServletRequest
Impl@6682044b[GET /omnifaces.push/counter?e6845a3a-26ed-4520-9824-63ffd85b24eb HTTP/1.1]
at weblogic.servlet.internal.ServletRequestImpl.startAsync(ServletRequestImpl.java:1949)
at weblogic.servlet.internal.ServletRequestImpl.startAsync(ServletRequestImpl.java:1925)
at javax.servlet.ServletRequestWrapper.startAsync(ServletRequestWrapper.java:432)
at weblogic.websocket.tyrus.TyrusServletFilter.doFilter(TyrusServletFilter.java:234)
...
It turns out that, on contrary to all other servers I tested, WebLogic's own TyrusServletFilter
, which is responsible for handling websocket handshake requests, is internally installed after the filters provided via web.xml
of the deployed webapp. The webapp shipped with a character encoding filter and a GZIP filter mapped on /*
, so they were invoked before the websocket filter. This was strange at first sight, but I thought it is what it is, so I just added <async-supported>true</async-supported>
to those webapp-provided filters so that the TyrusServletFilter
can do its job.
However, when making a websocket connection, a JavaScript error in the client side occurred when a push message was being sent, exactly the one you faced:
WebSocket connection to 'ws://localhost:7001/omnifaces.push/counter?e6845a3a-26ed-4520-9824-63ffd85b24eb' failed: Error during WebSocket handshake: Invalid status line
It turns out that WebSockets just can't deal with GZIP responses. After disabling the GZIP filter, everything continued to work flawlessly.
The root problem is however that WebLogic should have installed its TyrusServletFilter
before all webapp-provided filters. All other Java EE servers I ever have tested do this correctly. Your workaround of immediately dispatching and forwarding all websocket handshake requests to their target URL pattern, as mentioned in your comment on the question, is a good one. The alternative would be to reconfigure the web.xml
-provided filters to not match websocket handshake requests anymore, e.g. by using a more specific URL pattern, or mapping to a specific servlet instead.
Upvotes: 1