Friday, February 24, 2017

Jetty WebSocket proxying

Leave a Comment

Just wonder if anyone has experimented with WebSocket proxying (for transparent proxy) using embedded Jetty?

After about a day and a half playing with Jetty 9.1.2.v20140210, all I can tell is that it can't proxy WebSockets in its current form, and adding such support is non-trivial task (afaict at least).

Basically, Jetty ProxyServlet strips out the "Upgrade" and "Connection" header fields regardless of whether it's from a WebSocket handshake request. Adding these fields back is easy as shown below. But, when the proxied server returned a response with HTTP code 101 (switching protocols), no protocol upgrade is done on the proxy server. So, when the first WebSocket packet arrives, the HttpParser chokes and see that as a bad HTTP request.

If anyone already has a solution for it or is familiar with Jetty to suggest what to try, that would be very much appreciated.

Below is the code in my experiment stripping out the unimportant bits:

public class ProxyServer {     public static void main(String[] args) throws Exception     {         Server server = new Server();         ServerConnector connector = new ServerConnector(server);         connector.setPort(8888);         server.addConnector(connector);          // Setup proxy handler to handle CONNECT methods         ConnectHandler proxy = new ConnectHandler();         server.setHandler(proxy);          // Setup proxy servlet         ServletContextHandler context = new ServletContextHandler(proxy, "/", ServletContextHandler.SESSIONS);         ServletHolder proxyServlet = new ServletHolder(MyProxyServlet.class);         context.addServlet(proxyServlet, "/*");          server.start();     } }  @SuppressWarnings("serial") public class MyProxyServlet extends ProxyServlet {     @Override     protected void customizeProxyRequest(Request proxyRequest, HttpServletRequest request)     {         // Pass through the upgrade and connection header fields for websocket handshake request.          String upgradeValue = request.getHeader("Upgrade");         if (upgradeValue != null && upgradeValue.compareToIgnoreCase("websocket") == 0)         {             setHeader(proxyRequest, "Upgrade", upgradeValue);             setHeader(proxyRequest, "Connection", request.getHeader("Connection"));         }     }      @Override     protected void onResponseHeaders(HttpServletRequest request, HttpServletResponse response, Response proxyResponse)     {         super.onResponseHeaders(request, response, proxyResponse);          // Restore the upgrade and connection header fields for websocket handshake request.         HttpFields fields = proxyResponse.getHeaders();         for (HttpField field : fields)         {             if (field.getName().compareToIgnoreCase("Upgrade") == 0)             {                 String upgradeValue = field.getValue();                 if (upgradeValue != null && upgradeValue.compareToIgnoreCase("websocket") == 0)                 {                     response.setHeader(field.getName(), upgradeValue);                     for (HttpField searchField : fields)                     {                         if (searchField.getName().compareToIgnoreCase("Connection") == 0) {                             response.setHeader(searchField.getName(), searchField.getValue());                         }                     }                 }             }         }     } } 

0 Answers

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment