Jetty 升级 (9.2.24 -> 9.4.10) 因 websocket (UpgradeException) 而失败
Jetty upgrade (9.2.24 -> 9.4.10) fails with websocket (UpgradeException)
我正在将 jetty 从 9.2.24 升级到 9.4.10,以获得广泛使用 websockets 的应用程序。
我有一个现有的测试 (junit),它设置嵌入式码头,向其注册 rest 资源和 websocket servlet,然后测试它们是否可以访问。
当 Jetty 的版本为 9.2.24 时,测试非常好。尝试使用完全相同的代码 迁移到版本 9.4.10 失败并返回
java.io.IOException: Connect failure
at org.eclipse.jetty.websocket.jsr356.ClientContainer.connect(ClientContainer.java:232)
at org.eclipse.jetty.websocket.jsr356.ClientContainer.connectToServer(ClientContainer.java:255)
...
Caused by: org.eclipse.jetty.websocket.api.UpgradeException: 400 Bad Request
at org.eclipse.jetty.websocket.client.WebSocketUpgradeRequest.onComplete(WebSocketUpgradeRequest.java:522)
at org.eclipse.jetty.client.ResponseNotifier.notifyComplete(ResponseNotifier.java:193)
服务器端的 websocket 定义基于 JSR356 规范(即扩展 EndPoint)。用于访问 websocket 的 websocket 客户端也基于 javax.websocket(即 ContainerProvider.getWebSocketContainer().connectToServer(Endpoint instance...))——其中websocket 容器实际上是一个码头容器...
服务器设置完美。问题仅在尝试访问 websocket 时出现。我调试过,没发现客户端发起和发送websocket请求的方式有什么不同。特别是请求将 'upgrade' header 按预期设置为 'websocket'。
所以我只能假设问题出在 websocket 资源在嵌入式码头中的注册方式上。我已经调试了 working 流程(使用 9.2.24)并找到了在 jetty 中接受连接的最早位置(AbstractConnection 的选择器线程之一)。但出于某种原因,我在使用 9.4.10
时没有达到 websocket 的那个点
我已经阅读了一些资源和 SO 问题(例如 ),但找不到任何可以帮助我解决这个问题的东西。
我走投无路了。
这里是服务器注册的相关代码中的关键元素(我还有一个rest资源和websocket那个):
// web socket
ServletContextHandler wsContext = new ServletContextHandler(ServletContextHandler.SESSIONS);
wsContext.setContextPath("/ws_api");
ServerContainer container = WebSocketServerContainerInitializer.configureContext(servletContextHandler);
container.addEndpoint(new BasicServerEndpointConfig(container.getClient(), endpointClassObject, path)
// rest handler
ServletContextHandler restContext = new ServletContextHandler(ServletContextHandler.SESSIONS);
restContext.setContextPath("/rest_api");
...
ServletHolder sh = new ServletHolder(...);
restContext.addServlet(sh, "/*");
final HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[]{wsContext, restContext, new DefaultHandler()});
server.setHandler(handlers);
求助...
更新(根据 Joakim Erdfelt 请求的附加信息):
我在 class HTTPConnection class,在 BP 在 onComplete() 方法中,获取从 _channel._fields object 请求 headers 我得到:
响应 object 的状态为 200(而不是预期的 101):
我的端点 object 是一个大型继承链的一部分。它充满了我需要在上传之前删除的样板业务逻辑代码,但在根部是 javax.websocket.Endpont class,我们在这里仅实现了 onOpen(Session session, EndpointConfig config) 方法。调试时我没有得到那个方法,似乎很久以前就失败了...
您的请求 headers 看起来像这样...
Accept: application/json, application/*+json
Accept-Encoding: gzip
Cache-Control: no-cache
Connection: keep-alive
Content-Type: application/json
Host: 127.0.0.1:8080
Pragma: no-cache
Sec-WebSocket-Key: sMQPm6Cf00itLII3QBb4w==
Sec-WebSocket-Version: 13
Upgrade: websocket
User-Agent: Java/1.8.0_144
这是一个无效的 WebSocket 升级请求。
最明显的遗漏是
Connection: upgrade
但还有其他字段是兼容的 WebSocket 客户端永远不会设置的。
Content-Type: application/json
Accept: application/json, application/*+json
Accept-Encoding: gzip
我正在将 jetty 从 9.2.24 升级到 9.4.10,以获得广泛使用 websockets 的应用程序。
我有一个现有的测试 (junit),它设置嵌入式码头,向其注册 rest 资源和 websocket servlet,然后测试它们是否可以访问。
当 Jetty 的版本为 9.2.24 时,测试非常好。尝试使用完全相同的代码 迁移到版本 9.4.10 失败并返回
java.io.IOException: Connect failure
at org.eclipse.jetty.websocket.jsr356.ClientContainer.connect(ClientContainer.java:232)
at org.eclipse.jetty.websocket.jsr356.ClientContainer.connectToServer(ClientContainer.java:255)
...
Caused by: org.eclipse.jetty.websocket.api.UpgradeException: 400 Bad Request
at org.eclipse.jetty.websocket.client.WebSocketUpgradeRequest.onComplete(WebSocketUpgradeRequest.java:522)
at org.eclipse.jetty.client.ResponseNotifier.notifyComplete(ResponseNotifier.java:193)
服务器端的 websocket 定义基于 JSR356 规范(即扩展 EndPoint)。用于访问 websocket 的 websocket 客户端也基于 javax.websocket(即 ContainerProvider.getWebSocketContainer().connectToServer(Endpoint instance...))——其中websocket 容器实际上是一个码头容器...
服务器设置完美。问题仅在尝试访问 websocket 时出现。我调试过,没发现客户端发起和发送websocket请求的方式有什么不同。特别是请求将 'upgrade' header 按预期设置为 'websocket'。
所以我只能假设问题出在 websocket 资源在嵌入式码头中的注册方式上。我已经调试了 working 流程(使用 9.2.24)并找到了在 jetty 中接受连接的最早位置(AbstractConnection 的选择器线程之一)。但出于某种原因,我在使用 9.4.10
时没有达到 websocket 的那个点我已经阅读了一些资源和 SO 问题(例如
这里是服务器注册的相关代码中的关键元素(我还有一个rest资源和websocket那个):
// web socket
ServletContextHandler wsContext = new ServletContextHandler(ServletContextHandler.SESSIONS);
wsContext.setContextPath("/ws_api");
ServerContainer container = WebSocketServerContainerInitializer.configureContext(servletContextHandler);
container.addEndpoint(new BasicServerEndpointConfig(container.getClient(), endpointClassObject, path)
// rest handler
ServletContextHandler restContext = new ServletContextHandler(ServletContextHandler.SESSIONS);
restContext.setContextPath("/rest_api");
...
ServletHolder sh = new ServletHolder(...);
restContext.addServlet(sh, "/*");
final HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[]{wsContext, restContext, new DefaultHandler()});
server.setHandler(handlers);
求助...
更新(根据 Joakim Erdfelt 请求的附加信息):
我在 class HTTPConnection class,在 BP 在 onComplete() 方法中,获取从 _channel._fields object 请求 headers 我得到:
响应 object 的状态为 200(而不是预期的 101):
我的端点 object 是一个大型继承链的一部分。它充满了我需要在上传之前删除的样板业务逻辑代码,但在根部是 javax.websocket.Endpont class,我们在这里仅实现了 onOpen(Session session, EndpointConfig config) 方法。调试时我没有得到那个方法,似乎很久以前就失败了...
您的请求 headers 看起来像这样...
Accept: application/json, application/*+json
Accept-Encoding: gzip
Cache-Control: no-cache
Connection: keep-alive
Content-Type: application/json
Host: 127.0.0.1:8080
Pragma: no-cache
Sec-WebSocket-Key: sMQPm6Cf00itLII3QBb4w==
Sec-WebSocket-Version: 13
Upgrade: websocket
User-Agent: Java/1.8.0_144
这是一个无效的 WebSocket 升级请求。 最明显的遗漏是
Connection: upgrade
但还有其他字段是兼容的 WebSocket 客户端永远不会设置的。
Content-Type: application/json
Accept: application/json, application/*+json
Accept-Encoding: gzip