Intercepting WebSocket traffic
This tutorial shows how to intercept WebSocket traffic in the browser and forward it to Java code for logging or processing.
Prerequisites
To go through this tutorial you will need:
- Git.
- Java 17 or higher.
- A valid JxBrowser license. It can be either Evaluation or Commercial. For more information on licensing please see the licensing guide.
Getting the code
To get the code please execute the following commands:
git clone https://github.com/TeamDev-IP/JxBrowser-Examples
cd JxBrowser-Examples/tutorials/intercepting-web-sockets
The approach
When a web application uses WebSockets for real-time communication, you may need to monitor or log this traffic from the Java side. Since WebSocket connections are managed entirely by JavaScript in the browser, you need a way to intercept this traffic and bridge it to Java.
The approach is straightforward:
- Override the native
WebSocketconstructor in JavaScript to wrap all WebSocket connections. - Hook into the
sendmethod andmessageevent to capture outgoing and incoming data. - Call Java functions from JavaScript to forward the intercepted data.
This technique works transparently—any code that creates a WebSocket connection will be automatically intercepted without modification.
The JavaScript interceptor
The JavaScript code intercepts WebSocket traffic by replacing the native
WebSocket constructor with a wrapper that hooks into the connection
lifecycle.
Create a file named websocket-interceptor.js:
// Save reference to the native WebSocket.
const NativeWebSocket = window.WebSocket;
// Override the global WebSocket constructor.
window.WebSocket = function(url, protocols) {
const socket = new NativeWebSocket(url, protocols);
// Intercept incoming messages.
socket.addEventListener('message', (event) => {
if (window.onWebSocketReceived) {
window.onWebSocketReceived(event.data);
}
});
// Intercept outgoing messages.
const originalSend = socket.send;
socket.send = function(data) {
if (window.onWebSocketSent) {
window.onWebSocketSent(data);
}
originalSend.call(socket, data);
};
return socket;
};
// Preserve the prototype chain.
window.WebSocket.prototype = NativeWebSocket.prototype;
The interceptor code expects two functions to be available in the window
object: onWebSocketReceived and onWebSocketSent. These will be provided by
the Java side.
The demo page
To test the interceptor, create a simple HTML page that uses WebSockets.
Create websocket-demo.html:
<!DOCTYPE html>
<html>
<head>
<title>WebSocket Interception Demo</title>
</head>
<body>
<h1>WebSocket Interception Demo</h1>
<button id="connect">Connect</button>
<button id="send" disabled>Send Message</button>
<button id="disconnect" disabled>Disconnect</button>
<div id="status">Not connected</div>
<script>
let socket = null;
document.getElementById('connect').addEventListener('click', () => {
socket = new WebSocket('wss://echo.websocket.org/');
socket.addEventListener('open', () => {
document.getElementById('status').textContent = 'Connected';
document.getElementById('connect').disabled = true;
document.getElementById('send').disabled = false;
document.getElementById('disconnect').disabled = false;
});
socket.addEventListener('close', () => {
document.getElementById('status').textContent = 'Disconnected';
document.getElementById('connect').disabled = false;
document.getElementById('send').disabled = true;
document.getElementById('disconnect').disabled = true;
});
});
document.getElementById('send').addEventListener('click', () => {
socket.send('Hello from browser at ' + new Date());
});
document.getElementById('disconnect').addEventListener('click', () => {
socket.close();
});
</script>
</body>
</html>
This page connects to a public WebSocket echo server. When you click “Send Message”, it sends a timestamped message. The server echoes it back, which triggers the incoming message handler.
Note that this HTML does not include the interceptor script. The Java application will inject it before the page loads.
The Java application
The Java application loads the HTML page, injects the JavaScript interceptor, and registers the callback functions that receive the intercepted WebSocket data.
Here’s the complete application:
import com.teamdev.jxbrowser.browser.Browser;
import com.teamdev.jxbrowser.browser.callback.InjectJsCallback;
import com.teamdev.jxbrowser.engine.Engine;
import com.teamdev.jxbrowser.engine.RenderingMode;
import com.teamdev.jxbrowser.frame.Frame;
import com.teamdev.jxbrowser.js.JsFunctionCallback;
import com.teamdev.jxbrowser.js.JsObject;
import com.teamdev.jxbrowser.view.swing.BrowserView;
import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class WebSocketInterceptorApp {
public static void main(String[] args) {
var engine = Engine.newInstance(RenderingMode.HARDWARE_ACCELERATED);
var browser = engine.newBrowser();
// Inject JavaScript and register bridge functions.
browser.set(InjectJsCallback.class, params -> {
var frame = params.frame();
injectInterceptor(frame);
registerBridgeFunctions(frame);
return InjectJsCallback.Response.proceed();
});
SwingUtilities.invokeLater(() -> {
var view = BrowserView.newInstance(browser);
var frame = new JFrame("WebSocket Interceptor");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(view, BorderLayout.CENTER);
frame.setSize(800, 600);
frame.setVisible(true);
});
// Load the demo HTML page.
var html = loadResource("websocket-demo.html");
browser.navigation().loadHtml(html);
}
private static void injectInterceptor(Frame frame) {
var script = loadResource("websocket-interceptor.js");
frame.executeJavaScript(script);
}
private static void registerBridgeFunctions(Frame frame) {
JsObject window = frame.executeJavaScript("window");
window.putProperty("onWebSocketReceived", (JsFunctionCallback) args -> {
var data = args.get(0).toString();
System.out.println("[RECEIVED] " + data);
return null;
});
window.putProperty("onWebSocketSent", (JsFunctionCallback) args -> {
var data = args.get(0).toString();
System.out.println("[SENT] " + data);
return null;
});
}
private static String loadResource(String resourceName) {
try (var stream = WebSocketInterceptorApp.class.getResourceAsStream("/" + resourceName)) {
if (stream == null) {
throw new RuntimeException("Resource not found: " + resourceName);
}
return new String(stream.readAllBytes(), StandardCharsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException("Failed to load resource: " + resourceName, e);
}
}
}
Let’s walk through the key parts:
Injecting the interceptor
The InjectJsCallback runs every time a new frame is created:
browser.set(InjectJsCallback.class, params -> {
var frame = params.frame();
injectInterceptor(frame);
registerBridgeFunctions(frame);
return InjectJsCallback.Response.proceed();
});
This callback gives you access to the frame before the page’s own JavaScript runs, which is the optimal time to inject the interceptor.
Loading and executing the interceptor script
The injectInterceptor method loads the JavaScript file from resources and
executes it:
private static void injectInterceptor(Frame frame) {
var script = loadResource("websocket-interceptor.js");
frame.executeJavaScript(script);
}
After this runs, the native WebSocket constructor is replaced, and all
WebSocket connections will be intercepted.
Registering the bridge functions
The registerBridgeFunctions method creates two Java callbacks and exposes
them to JavaScript:
private static void registerBridgeFunctions(Frame frame) {
JsObject window = frame.executeJavaScript("window");
window.putProperty("onWebSocketReceived", (JsFunctionCallback) args -> {
var data = args[0].toString();
System.out.println("[RECEIVED] " + data);
return null;
});
window.putProperty("onWebSocketSent", (JsFunctionCallback) args -> {
var data = args[0].toString();
System.out.println("[SENT] " + data);
return null;
});
}
These functions are called by the JavaScript interceptor whenever WebSocket traffic is detected. In this example, they simply print the data to the console, but you could log it to a file, store it in a database, or process it in any other way.
Running the application
When you run the application, you’ll see a browser window with three buttons.
Click “Connect” to establish a WebSocket connection. Then click “Send Message” to send data. On the Java console, you should see output like this:
[SENT] Hello from browser at 2026-01-20T13:45:23.123Z
[RECEIVED] Hello from browser at 2026-01-20T13:45:23.123Z
The echo server returns the same message you sent, so you’ll see both outgoing and incoming traffic.
Summary
In this tutorial, you learned how to:
- Override the native
WebSocketconstructor to intercept all WebSocket connections. - Hook into the
sendmethod andmessageevent to capture traffic. - Register Java functions in the JavaScript world to receive WebSocket data on Java.
- Bridge WebSocket data from the browser to Java for logging or processing.
This technique can be extended to modify WebSocket messages, block connections based on URL patterns, or add custom logging and analytics to any web application that uses WebSockets.