Tomcat: using servlet and websocket (jsr356) in the same web application

I am creating a webapp sample using Guice servlets and websocket in tomcat, now when the goose filter is used, websocket stops working

Basic information:

In my web.xml, I initialized the Guiceservlet with a GuiceBasedListener

 <web-app> <listener> <listener-class>test.GuiceBasedListener</listener-class> </listener> <filter> <filter-name>guiceFilter</filter-name> <filter-class>com.google.inject.servlet.GuiceFilter</filter-class> </filter> <filter-mapping> <filter-name>guiceFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app> 

GuieBasedListener Code that binds the entire request /* to MyDispatcher

 public class GuiceBasedListener extends GuiceServletContextListener { protected Injector getInjector() { return Guice.createInjector( new ServletModule() { @Override protected void configureServlets() { bind(MyDispatcher.class).asEagerSingleton(); serve("/*").with(MyDispatcher.class);//////IMPORTANT LINE// } });}} 

MyDispatcher that just responds with a string

 public class MyDispatcher extends HttpServlet { @Inject private Injector injector; public MyDispatcher() {} public void service(ServletRequest req, ServletResponse resp) throws IOException, ServletException { resp.getOutputStream().print("SUCCESS:" + req); } } 

I also have @ServerEndPoint for Websocket

 @ServerEndpoint(value = "/websocket/chat2") public class WebSocket{ .... @OnOpen public void start(Session session) { System.out.println("Staring:"+this); } .... } 

Remarks:

  • Now, if I run the application and hit http: // app: 8080 / test , it returns SUCCESS
  • But if I try to connect to websocket using ws: // app: 8080 / websocket / chat2, it does not work.
  • Now, if I comment on serve("/*").with(MyDispatcher.class); basically, if we disable guice routing, the website will start working

  • If I disable guice-servlet, but add the servlet mapping in web.xml as below, websocket still works

    < servlet-mapping > < servlet-name > HelloWorld< / servlet-name > < url-pattern > /* < / url-pattern > < / servlet-mapping >

What am I missing or doing wrong?


EDIT:

Surveillance-Conti:

  1. What I did was define a simple filter that simply responds with FILTER .

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { response.getOutputStream().print("FILTER"); }

and changed my web.xml to

 <web-app> <filter> <filter-name>myFilter</filter-name> <filter-class>test.MyFilter</filter-class> </filter> <filter-mapping> <filter-name>myFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app> 

Now hit http://localhost:8080/app/x return FILTER as expected. But trying to connect to websocket fails, as the request shows something like this. I also noticed that as I change the MyFilter line, we return the length of the content in the responses, which means that the request reached MyFilter before tomcat processed it for websocket.

enter image description here

  1. I changed web.xml below, and guice and websocket now work fine. Therefore, I believe Guice does not honor WsFilter registered after GuiceFilter

     <filter> <filter-name>myFilter</filter-name> <filter-class>org.apache.tomcat.websocket.server.WsFilter</filter-class> </filter> <filter-mapping> <filter-name>myFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>guiceFilter</filter-name> <filter-class>com.google.inject.servlet.GuiceFilter</filter-class> </filter> <filter-mapping> <filter-name>guiceFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 

TOMCAT 8.0, Window 7, Java 1.7, Guice 4.0, Guice-servlet-4.0

+5
source share
1 answer

It also looks like a Guice issue (as mentioned in the comments). Using servlets and WebSockets in the same application should not be a problem even when displaying a servlet that spans /* .

2 relevant things about servlets and filters:

  • as already mentioned, when receiving a request to update websocket (a very special kind of HTTP request), usually Tomcat should not pass it through filters. Guice seems to be breaking this one way or another.
  • Filters are applied in the order they are matched in web.xml .

So, if WsFilter is the first, it will first intercept the request, and then check if this is a WebSocket update request .
If this is indeed a WebSocket connection, the filter will not pass it to the rest of the chain.
If this is a different type of request (GET, POST ...), it will pass it, and then Guice will do its best.

(so you found the first solution here)

If the first filter is Guice, and you use serve("/*")... then it breaks your WS.

If you comment on serve("/*")... it doesn’t matter if the Guice filter is the first or not, WsFilter may even be absent: your WS can be reached (which sets only GuiceFilter in order).

So Guice has its own “capture layer” over servlet mappings, and I think that breaks WebSockets. I don’t know if there is an error or something that can be fixed in Guice (I mean, probably, but I don’t know what exactly), but you can specify exceptions for Guice (unlike servlet mappings in web.xml )
Replace this:

 serve("/*").with(TestServlet.class); 

with this:

 serveRegex("/(?!websocket/).*").with(TestServlet.class); // Regex that accepts /.* but excludes /websocket/.* 

With this, you can save the servlet mapping to /* and remove WsFilter since it is not needed. (Tested, works for me)

So, the second solution, which has the advantage that it also allows you to specify exceptions for materials other than WebSocket.

+3
source

Source: https://habr.com/ru/post/1268277/


All Articles