JSR-356:如何在握手期间中止websocket连接?

时间:2014-02-13 19:32:56

标签: java java-ee websocket java-7 java-ee-7

我需要能够在握手期间中止websocket连接,以防HTTP请求不符合某些条件。根据我的理解,正确的地方是我自己Configurator实现的ServerEndpointConfig.Configurator.modifyHandshake()方法。我只是无法弄清楚要做什么来中止连接。有一个HandshakeResponse参数允许在响应中添加标题,但我找不到任何标题来完成这项工作。

那么如何在握手期间中止websocket连接?这甚至可能吗?

4 个答案:

答案 0 :(得分:3)

你是对的,使用'modifyHandShake()'更新响应标题,你需要删除或设置标题Sec-WebSocket-Accept的值,从the spec

检查
  

| Sec-WebSocket-Accept |标题字段表示是否      服务器愿意接受连接。如果存在,这个      标头字段必须包含发送的客户端随机数的哈希值      |仲WebSocket的密钥|以及预定义的GUID。任何其他价值      不得被解释为接受连接      服务器

    HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
  

这些字段由WebSocket客户端检查脚本页面。      如果是| Sec-WebSocket-Accept |值与预期值不匹配      值,如果缺少标头字段,或者HTTP状态代码是否为      不是101,连接将不会建立,而WebSocket框架      将不会被发送。

您的代码如下所示:

 @Override
public void modifyHandshake(ServerEndpointConfig sec,
    HandshakeRequest request, HandshakeResponse response) {
    super.modifyHandshake(sec, request, response);
    response.getHeaders().put(HandshakeResponse.SEC_WEBSOCKET_ACCEPT, new ArrayList<String>());
}

浏览器会将此解释为服务器未接受连接。例如在chrome中我收到消息

  

Websocket握手期间出错

答案 1 :(得分:2)

我知道 - 旧帖子但我没有看到任何改进。所以也许有人可以讨论我解决这个问题的方法。

我试过leos版本,运行Wildfly 8.0,Undertow 1.0

final  ArrayList<String> finalEmpty = new ArrayList<String>();
response.getHeaders().put(HandshakeResponse.SEC_WEBSOCKET_ACCEPT,finalEmpty);

这会引起一些有趣的行为:

即使您的浏览器应该关闭连接,它们也都会通过onOpen()程序。

  • Chrome:会触发onOpen(),然后触发onError()。在我的 case,我做一些关于断开连接的客户端的日志记录,所以我一直打电话 发生错误时onClose()
  • IE:会像Chrome一样行事
  • Firefox:Firefox只会运行onOpen()程序。并且不会触发onError()。因此,您的服务器甚至不知道客户端已断开连接。

不要弄乱你的标题,也不要让客户做好工作。

相反,您应该向配置添加身份验证数据。

/** some verification foo code...**/

config.getUserProperties().put("isValid",true);
然后在onOpen()

检查isValid的值。如果不是,请致电onClose(session,null);。会议将结束。

它不是最好的解决方案,但那是因为websocket认证很糟糕,而且每个浏览器有时会表现不同。请参阅:Websocket: Closing browser triggers onError() in chrome but onClose() event in Firefox

答案 2 :(得分:2)

最简单的方法是在web.xml中设置简单的web过滤器,这将在握手之前执行。

Smth喜欢:

public class MyHandshakeFilter implements Filter {

@Override
public void init(FilterConfig config) throws ServletException {
    // do nothing
}

/**
 * Passes request to chain when request port is equal to specified in the allowedPort property
 * and returns HttpServletResponse.SC_NOT_FOUND in other case.
 */
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
        ServletException {

    if (<your validation here based on http request>) {
        // request conditions are met. continue handshake...
        chain.doFilter(request, response);
    } else {
        // request conditions are NOT met. reject handshake...
        ((HttpServletResponse) response).setStatus(HttpServletResponse.SC_FORBIDDEN);
    }
}
}

并在web.xml中:

<filter>
    <filter-name>yourHandshakeFilter</filter-name>
    <filter-class>com....MyHandshakeFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>yourHandshakeFilter</filter-name>
    <url-pattern>/yourEndpointUrl</url-pattern>
</filter-mapping>

并且您的ServerEndpoint应该像:

@ServerEndpoint("/yourEndpointUrl")
public class MyServerEndpoint {
...
}

答案 3 :(得分:1)

另一种技巧:

当您从ServerEndpointConfig.Configurator#modifyHandshake抛出RuntimeException时,则表明连接未建立。

这适用于Tomcat 8.从Jetty example得到了这个想法,所以我猜它也适用于Jetty。