如何在Spring MVC中代理/镜像Websocket连接URL?

时间:2018-10-18 18:49:23

标签: java spring-mvc servlets websocket spring-rest

我已经快完成How can I embed a server-side Shiny app into a JSP page without exposing the app elsewhere了,但是这个解决方案的最后一部分确实让我陷入了困境。

(基本背景:这是一台Spring MVC Java Web服务器,与Shiny应用程序位于同一台机器上。如果可能,我希望避免转移到Spring Boot。)

代码成功地将所有{Shiny“内容从localhost:3305镜像到localhost:8080/project/shiny-proxy,除了Websocket连接URL:ws://localhost:8080/project/shiny-proxy/websocket/需要映射到ws://localhost:3305/websocket/。当我在Chrome浏览器中访问http://localhost:3305/websocket/时,会返回一个硬编码的“未找到” HTML页面,因此使用http://前缀进行的请求不可能通过任何方法起作用。

请注意,我<希望> 如果可以在客户端和ws://localhost:3305/websocket/之间建立连接,则Java代码将不需要处理它们之间的任何Websocket消息。

这是到目前为止基于https://stackoverflow.com/a/23736527/7376471的控制器代码:

private static final RestTemplate restTemplate = new RestTemplate(
    /* specific HttpRequestFactory here */);

@RequestMapping(path = "/shiny-proxy/**")
public ResponseEntity<String> mirrorRest(@RequestBody(required = false) String body,
        HttpMethod method, HttpServletRequest request) throws URISyntaxException {
    String path = StringUtils.removeStart(request.getRequestURI(), "/project/shiny-proxy");
    boolean websocket = false;
    URI uri;
    if (path.endsWith("/websocket/")) {
        websocket = true;
        // restore the ws:// that the request URL started with after Spring MVC makes it http://
        uri = new URI("ws", null, "localhost", 3305, path, request.getQueryString(), null);
    } else {
        uri = new URI(request.getScheme(), null, "localhost", 3305, path, request.getQueryString(), null);
    }
    if (websocket) {
        System.out.println(request.getRequestURL().toString());
        System.out.println(request.getRequestURI());
        System.out.println(path);
        System.out.println(uri);
    }

    HttpHeaders headers = new HttpHeaders();
    if (path.endsWith(".css.map")) { // special handling for unusual content types from Shiny
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
    }
    HttpEntity<String> httpEntity = new HttpEntity<>(body, headers);
    ResponseEntity<String> ret = null;
    try {
        ret = restTemplate.exchange(uri, method, httpEntity, String.class);
    } catch (Exception e) {
        System.out.println(request.getRequestURL().toString());
        System.out.println(request.getRequestURI());
        System.out.println(path);
        System.out.println(uri);
        e.printStackTrace();
    }

    if (websocket) {
        System.out.println("ResponseEntity headers: " + ret.getHeaders());
    }
    return ret;
}

但是我找不到任何与HttpRequestFactory URL一起使用的ws://(其中许多将URI转换为不支持ws://的java.net.URLs,其他我已经测试过无法处理反射代码中的ws://),即使可以,我也不敢肯定返回ResponseEntity甚至可以用于ws连接。

所以我的问题是,有没有办法让控制器在给定ws://请求URL的情况下在客户端和localhost:3305之间正确地建立websocket连接,还是我应该放弃RestTemplate的想法并尝试像这样配置的代理改为使用Nginx?

1 个答案:

答案 0 :(得分:0)

在我的情况下,使用配置的代理解决方案非常简单: 在Include conf/extra/httpd-vhosts.conf中启用/opt/bitnami/apache2/conf/httpd.conf

并将httpd-vhosts.conf的内容设置为:

<VirtualHost *:80>
  RewriteEngine on
  RewriteRule /project/shiny-proxy/websocket/(.*) ws://localhost:3305/websocket/$1 [P,L]
</VirtualHost>

Bitnami的默认配置是如此之好,以至于不需要其他更改。