basic WebSocketProxy implementation

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
Lachlan Roberts 2020-11-24 13:40:48 +11:00
parent 41bf9534eb
commit d7598f3ff2
2 changed files with 322 additions and 0 deletions

View File

@ -0,0 +1,272 @@
package org.eclipse.jetty.websocket.tests.proxy;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.concurrent.Future;
import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketConnectionListener;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.WebSocketPartialListener;
import org.eclipse.jetty.websocket.api.WebSocketPingPongListener;
import org.eclipse.jetty.websocket.client.WebSocketClient;
public class WebSocketProxy
{
private final WebSocketClient client = new WebSocketClient();
private final URI serverUri = URI.create("ws://echo.websocket.org");
private final ClientToProxy clientToProxy = new ClientToProxy();
private final ProxyToServer proxyToServer = new ProxyToServer();
public WebSocketProxy()
{
LifeCycle.start(client);
}
public WebSocketConnectionListener getWebSocketConnectionListener()
{
return clientToProxy;
}
public class ClientToProxy implements WebSocketPartialListener, WebSocketPingPongListener
{
private Session session;
private FutureCallback pongWait;
public Session getSession()
{
return session;
}
public void receivedPong()
{
if (pongWait != null)
{
pongWait.succeeded();
pongWait = null;
}
}
@Override
public void onWebSocketConnect(Session session)
{
Future<Session> connect = null;
try
{
this.session = session;
connect = client.connect(proxyToServer, serverUri);
connect.get();
}
catch (Exception e)
{
if (connect != null)
connect.cancel(true);
throw new WebSocketException(e);
}
}
@Override
public void onWebSocketPartialBinary(ByteBuffer payload, boolean fin)
{
try
{
proxyToServer.getSession().getRemote().sendPartialBytes(payload, fin);
}
catch (Exception e)
{
throw new WebSocketException(e);
}
}
@Override
public void onWebSocketPartialText(String payload, boolean fin)
{
try
{
proxyToServer.getSession().getRemote().sendPartialString(payload, fin);
}
catch (Exception e)
{
throw new WebSocketException(e);
}
}
@Override
public void onWebSocketPing(ByteBuffer payload)
{
try
{
proxyToServer.getSession().getRemote().sendPing(payload);
// Block until we get pong response back from server.
// An automatic pong will occur from the implementation after we exit from here.
pongWait.get();
}
catch (Exception e)
{
throw new WebSocketException(e);
}
}
@Override
public void onWebSocketPong(ByteBuffer payload)
{
try
{
// Notify the other side we have received a Pong.
proxyToServer.receivedPong();
}
catch (Exception e)
{
throw new WebSocketException(e);
}
}
@Override
public void onWebSocketError(Throwable cause)
{
cause.printStackTrace();
try
{
// TODO: need to fail ProxyToServer as well.
if (pongWait != null)
pongWait.cancel(true);
}
catch (Exception e)
{
throw new WebSocketException(e);
}
}
@Override
public void onWebSocketClose(int statusCode, String reason)
{
try
{
Session session = proxyToServer.getSession();
if (session != null)
session.close(statusCode, reason);
}
catch (Exception e)
{
throw new WebSocketException(e);
}
}
}
public class ProxyToServer implements WebSocketPartialListener, WebSocketPingPongListener
{
private Session session;
private FutureCallback pongWait;
public Session getSession()
{
return session;
}
public void receivedPong()
{
if (pongWait != null)
{
pongWait.succeeded();
pongWait = null;
}
}
@Override
public void onWebSocketConnect(Session session)
{
this.session = session;
}
@Override
public void onWebSocketPartialBinary(ByteBuffer payload, boolean fin)
{
try
{
clientToProxy.getSession().getRemote().sendPartialBytes(payload, fin);
}
catch (Exception e)
{
throw new WebSocketException(e);
}
}
@Override
public void onWebSocketPartialText(String payload, boolean fin)
{
try
{
clientToProxy.getSession().getRemote().sendPartialString(payload, fin);
}
catch (Exception e)
{
throw new WebSocketException(e);
}
}
@Override
public void onWebSocketPing(ByteBuffer payload)
{
try
{
clientToProxy.getSession().getRemote().sendPing(payload);
// Block until we get pong response back from client.
// An automatic pong will occur from the implementation after we exit from here.
pongWait.get();
}
catch (Exception e)
{
throw new WebSocketException(e);
}
}
@Override
public void onWebSocketPong(ByteBuffer payload)
{
try
{
// Notify the other side we have received a Pong.
clientToProxy.receivedPong();
}
catch (Exception e)
{
throw new WebSocketException(e);
}
}
@Override
public void onWebSocketError(Throwable cause)
{
cause.printStackTrace();
try
{
// TODO: need to fail ProxyToServer as well.
if (pongWait != null)
pongWait.cancel(true);
}
catch (Exception e)
{
throw new WebSocketException(e);
}
}
@Override
public void onWebSocketClose(int statusCode, String reason)
{
try
{
Session session = clientToProxy.getSession();
if (session != null)
session.close(statusCode, reason);
}
catch (Exception e)
{
throw new WebSocketException(e);
}
}
}
}

View File

@ -0,0 +1,50 @@
package org.eclipse.jetty.websocket.tests.proxy;
import java.net.URI;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.websocket.server.NativeWebSocketServletContainerInitializer;
import org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class WebSocketProxyTest
{
private Server server;
private URI serverUri;
@BeforeEach
public void before() throws Exception
{
server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(8080); // TODO: remove...
server.addConnector(connector);
ServletContextHandler contextHandler = new ServletContextHandler();
WebSocketUpgradeFilter.configure(contextHandler);
NativeWebSocketServletContainerInitializer.configure(contextHandler, ((context, container) ->
{
container.addMapping("/*", (req, resp) -> new WebSocketProxy().getWebSocketConnectionListener());
}));
server.setHandler(contextHandler);
server.start();
serverUri = URI.create("ws://localhost:" + connector.getLocalPort());
}
@AfterEach
public void after() throws Exception
{
server.stop();
}
@Test
public void test() throws Exception
{
server.join();
}
}