Does Jetty support websocket proxying?
11.0.17
Question Does Jetty support websocket proxying? I want my application to be able to proxy http requests and websocket requests via Jetty server.
Using ProxyServlet.Transparent, I am able to redirect my http requests to target server and get responses. How do we achieve the same redirection for websocket subscriptions?
The below implementation works:
Code snippet:
public class MyProxy extends ProxyServlet.Transparent
{
@Override
public void init(ServletConfig config) throws ServletException
{
myServletConfig = buildMyServletConfig(config);
super.init(myServletConfig);
}
@Override
protected String rewriteTarget(HttpServletRequest request)
{
myServletConfig.setProxyTo("http://targetServer/endpoint");
super.init(myServletConfig);
return super.rewriteTarget(request);
}
}
How do we achieve the reverse proxying for websocket subscriptions?
WebSocket proxying is possible, but not implemented -- this is one of the first times we get asked about this.
We have implemented it as a proof-of-concept (not production ready code) in a test case here: https://github.com/jetty/jetty.project/blob/jetty-10.0.18/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/proxy/WebSocketProxy.java
If you want to write your own you can start from there.
Alternatively, you may establish a commercial support from Webtide, and we will write the code for you and for the whole community -- a great way to give back.
Hi Simone, Thank you for the prompt response. As per the document, there are two ways of implementing websockets, I have tried the first way of using standard javax.websocket API. I have added a websocket server which opens a new client session to my target server and thus acting as a proxy between actual client and the target server.
Below is the pictorial representation of how I achieved websocket proxying using javax APIs:
Please confirm if it is the right way to achieve websocket proxying.
I will be trying your suggestion which uses Jetty-specific WebSocket APIs. Is there any difference in terms of performance or in any other way, between the standard javax APIs and the jetty specific APIs?
I recommend you do it with the Jetty 12 WebSocket APIs because they have better support for backpressure: https://eclipse.dev/jetty/documentation/jetty-12/programming-guide/index.html#pg-server-websocket-jetty
The problem with the Jakarta APIs is that you have to block if a remote peer is slow, and this could cause scalability issues.
With the Jetty WebSocket APIs you have full control on asynchronous writes and on the demand for more WebSocket frames.
Thank you, I will try with Jetty12 APIs.
This issue has been automatically marked as stale because it has been a full year without activity. It will be closed if no further activity occurs. Thank you for your contributions.
Hi, Thank you for the support provided.
I have a question w.r.t authentication in context of the above proxy server implementation.
The client connects to Jetty Proxy WS Server via Bearer Token or Cookie which needs to be validated at 'Target WS Server'. The ask is to pass the cookie information from 'client' to 'target' via Jetty proxy Server Implementation.
I have tried using 'ServerEndpointConfig.Configurator' on 'MyServerEndpoint' to get hold of the cookie information that the client sent, which I will retrieve using EndpointConfig object.
But how to pass these cookies to the client while establishing connection with the actual target server?
@ServerEndpoint(value = "/",subprotocols = {"graphql-transport-ws"}, configurator = ServerConfigurator.class)
public class MyServerEndpoint
{
@OnOpen
public void onOpen(Session session, EndpointConfig endpointConfig)
{
session.setMaxIdleTimeout(Integer.MAX_VALUE);
List<String> cookies = (List<String>) endpointConfig.getUserProperties().get("Cookies");
ContainerProvider.getWebSocketContainer().connectToServer(new MyClientEndpoint(), url);
}
}
@ClientEndpoint(subprotocols = {"graphql-transport-ws"})
public class MyClientEndpoint
{
@OnMessage(maxMessageSize = 1024 * 1024)
public void onMessage(String message, Session session)
{
target.sendMessage();
}
}
public class ServerConfigurator extends ServerEndpointConfig.Configurator{
@Override
public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
Map<String, List<String>> headers = request.getHeaders();
config.getUserProperties().put("Cookies",headers.get("Cookie"));
}
}
public class ClientConfigurator extends ClientEndpointConfig.Configurator {
@Override
public void beforeRequest(Map<String, List<String>> headers) {
super.beforeRequest(headers);
// headers.put(cookiesFromServer) ???
}
}
The ClientEndpointConfig.Configurator seems to be instantiated once the connectToServer call is made. So how to pass cookie information from the server to target.
@Spandana806 there is not much we can say here, you have to organize your code in a way that you can pass the cookie from the proxy server to the proxy client.
You likely already have this link, because you need to take a message arriving on the proxy server, and pass it to the proxy client.
Hello,
I am currently trying to do pretty much the same thing with Jetty 12.1, as I am testing a possible migration of an existing proxy configuration (with websocket support) from Nginx to Jetty.
From what I understood from the conversation, there is no ready-to-use mechanism to transparently forward the websocket to the proxy target, am I right?
The proper way to have Jetty performing the proxy is to have an internal WS server/client pair that intermediate the connection from the client to the proxy target?
Also, is there any plan to support websocket forwarding directly in ProxyServlet.Transparent?
The proper way to have Jetty performing the proxy is to have an internal WS server/client pair that intermediate the connection from the client to the proxy target?
Correct.
There is a test class called WebSocketProxy, but it is a test class written primarily to verify that the API was correct -- a proxy is one of the toughest use cases for APIs.
This class is definitely not production quality (it's a test class), but it can give you a hint of what's necessary.
Also, is there any plan to support websocket forwarding directly in ProxyServlet.Transparent?
No.