我正在尝试在不同的上下文中运行具有不同内容的嵌入式 Jetty websocket 服务器。
如果我将 websocket 部分放在 / 上下文中,它就可以工作,但我想在那里服务器其他内容。如果我将 websocket 部分放入任何其他上下文中,它就不起作用。
这是我的代码:
public WebsocketServer(int port, Model model, SessionManager sessionManager) throws Exception {
Server server = new Server(port);
server.setRequestLog((request, response) -> log_.info("request {}", request));
ContextHandlerCollection handlers = new ContextHandlerCollection();
ServletContextHandler sch;
sch = new ServletContextHandler();
sch.setContextPath("/model");
sch.addServlet(new ServletHolder(new ModelServlet(model)), "/");
sch.setResourceBase("cfg");
handlers.addHandler(sch);
sch = new ServletContextHandler();
sch.setContextPath("/web");
sch.addServlet(DefaultServlet.class, "/*");
sch.setResourceBase("cfg");
sch.setWelcomeFiles(new String[]{"websocket.html"});
handlers.addHandler(sch);
sch = new ServletContextHandler();
sch.setContextPath("/"); // <---- would like to change this context to /ws
Servlet websocketServlet = new JettyWebSocketServlet() {
@Override
protected void configure(JettyWebSocketServletFactory factory) {
factory.addMapping("/", (req, res) -> {
Session s = new Session(req);
sessionManager.add(s);
return s;
});
}
};
sch.addServlet(new ServletHolder(websocketServlet), "/");
JettyWebSocketServletContainerInitializer.configure(sch, null);
handlers.addHandler(sch);
sch = new ServletContextHandler();
sch.setContextPath("/*");
sch.addServlet(DefaultServlet.class, "/*");
sch.setResourceBase("cfg");
handlers.addHandler(sch);
server.setHandler(handlers);
server.start();
}
如果我将 JettyWebSocketServlet 放在 / 上下文中,它可以工作,但它永远不会到达我的 DefaultServlet。我尝试将 JettyWebSocketServlet 更改为 /ws,但随后我的网页无法连接,但我确实从 DefaultServlet 获得了结果。无论哪种情况,我的 ModelServlet 都可以在 /model 上下文中工作。
我的网页尝试连接到 ws://localhost:8080/ws
我使用的是jetty 11.0.16
Jetty 11 现已停止社区支持
参见:https://github.com/jetty/jetty.project/issues/10485
您的设置中存在一些根本性错误。
您的设置也不会通过将其拆分为不同的 ServletContext 而获得任何好处。
首先,您对
DefaultServlet
的使用,DefaultServlet
始终被命名为“default”,并位于 /
的“默认”servlet 映射上。
ServletHolder holderPwd = new ServletHolder("default", DefaultServlet.class);
holderPwd.setInitParameter("dirAllowed", "true");
scch.addServlet(holderPwd, "/"); // <-- there should always be at least one at "/"
如果您需要多个
DefaultServlet
,请阅读过去的答案:https://stackoverflow.com/a/20223103/775715
另请参阅有关此主题的最新码头示例。
接下来,
ServletContextHandler
不能有带有全局符号的上下文路径。
这很糟糕,除非您以某种方式在 URI 路径本身中发送“*”,否则不会使用。
sch = new ServletContextHandler();
sch.setContextPath("/*"); // <-- this will only map if URI path is exactly `/*`
最后,如果您希望 Websocket 处于
/
状态,则不要使用 WebSocketServlet
,因为这会覆盖 DefaultServlet
行为,这不是您想要的。
请使用
WebSocketUpgradeFilter
,因为这不会干扰其他非 Websocket 请求的 /
或 /*
请求。
// Add the websocket filter
JettyWebSocketServletContainerInitializer.configure(sch, (context, configurator) ->
{
configurator.setIdleTimeout(Duration.ofMillis(5000));
configurator.addMapping("/", (req, res) -> {
Session s = new Session(req);
sessionManager.add(s);
return s;
});
});
参见示例
最后,你的设置太复杂了。
只需 1 个
ServletContextHandler
和 url 模式的混合,您就可以完成您想做的一切。
// This is Jetty 12 code, against the ee10 environment.
ServletContextHandler sch = new ServletContextHandler();
sch.setContextPath("/");
Resource baseResource = ResourceFactory.of(sch).newClassLoaderResource("cfg");
if (!Resources.isReadableDirectory(baseResource))
throw new FileNotFoundException("Unable to find base resource");
sch.setBaseResourceBase(baseResource);
sch.setWelcomeFiles(new String[]{"websocket.html"});
sch.addServlet(new ServletHolder(new ModelServlet(model)), "/model/*");
Resource webResource = ResourceFactory.of(sch).newClassLoaderResource("web");
if (!Resources.isReadableDirectory(webResource))
throw new FileNotFoundException("Unable to find web resource");
ServletHolder holderWeb = new ServletHolder("static-web", DefaultServlet.class);
holderWeb.setInitParameter("baseResource", webResource.getURI().toASCIIString());
holderWeb.setInitParameter("dirAllowed", "true");
holderWeb.setInitParameter("pathInfoOnly", "true");
sch.addServlet(holderWeb, "/web/*");
JettyWebSocketServletContainerInitializer.configure(sch, (context, configurator) ->
{
configurator.setIdleTimeout(Duration.ofMillis(5000));
configurator.addMapping("/ws", (req, res) -> {
Session s = new Session(req);
sessionManager.add(s);
return s;
});
});
server.setHandler(sch);