HTTP client and server abstractions for Selenium WebDriver communication
—
WebSocket client functionality with message types (text, binary, close) and event-driven listener interface for real-time bidirectional communication.
Main interface for WebSocket communication with message sending capabilities and resource management.
/**
* WebSocket interface for bidirectional communication
* Extends Closeable for proper resource management
*/
public interface WebSocket extends Closeable {
/**
* Logger instance for WebSocket operations
*/
Logger LOG = Logger.getLogger(WebSocket.class.getName());
/**
* Sends a WebSocket message
* @param message Message to send (TextMessage, BinaryMessage, or CloseMessage)
* @return This WebSocket instance for chaining
*/
WebSocket send(Message message);
/**
* Sends text message (convenience method)
* @param data Text data to send
* @return This WebSocket instance for chaining
*/
default WebSocket sendText(CharSequence data);
/**
* Sends binary message (convenience method)
* @param data Binary data to send
* @return This WebSocket instance for chaining
*/
default WebSocket sendBinary(byte[] data);
/**
* Closes the WebSocket connection
*/
void close();
}Usage Examples:
import org.openqa.selenium.remote.http.*;
// Open WebSocket connection via HTTP client
HttpRequest wsRequest = new HttpRequest(HttpMethod.GET, "/websocket");
wsRequest.addHeader("Upgrade", "websocket");
WebSocket.Listener listener = new WebSocket.Listener() {
@Override
public void onText(CharSequence data) {
System.out.println("Received text: " + data);
}
@Override
public void onBinary(byte[] data) {
System.out.println("Received binary: " + data.length + " bytes");
}
@Override
public void onClose(int code, String reason) {
System.out.println("Connection closed: " + code + " - " + reason);
}
@Override
public void onError(Throwable cause) {
System.err.println("WebSocket error: " + cause.getMessage());
}
};
WebSocket socket = client.openSocket(wsRequest, listener);
// Send different types of messages
socket.sendText("Hello WebSocket!");
socket.sendBinary("Binary data".getBytes());
// Send custom messages
socket.send(new TextMessage("Custom text message"));
socket.send(new BinaryMessage("Custom binary".getBytes()));
// Chaining method calls
socket.sendText("Message 1")
.sendText("Message 2")
.sendBinary("data".getBytes());
// Close connection
socket.close();Event-driven interface for handling incoming WebSocket messages and connection events.
/**
* Listener interface for WebSocket events
* Extends Consumer<Message> for functional interface compatibility
*/
public interface Listener extends Consumer<Message> {
/**
* Default message dispatcher that routes messages to specific handlers
* @param message Incoming message (BinaryMessage, TextMessage, or CloseMessage)
*/
default void accept(Message message);
/**
* Handles incoming binary messages
* Default implementation does nothing
* @param data Binary message data
*/
default void onBinary(byte[] data);
/**
* Handles connection close events
* Default implementation does nothing
* @param code Close status code
* @param reason Close reason string
*/
default void onClose(int code, String reason);
/**
* Handles incoming text messages
* Default implementation does nothing
* @param data Text message data
*/
default void onText(CharSequence data);
/**
* Handles WebSocket errors
* Default implementation logs error at WARNING level
* @param cause Error cause
*/
default void onError(Throwable cause);
}Usage Examples:
import org.openqa.selenium.remote.http.*;
// Full listener implementation
WebSocket.Listener fullListener = new WebSocket.Listener() {
@Override
public void onText(CharSequence data) {
System.out.println("Text received: " + data);
// Echo back
if (socket != null) {
socket.sendText("Echo: " + data);
}
}
@Override
public void onBinary(byte[] data) {
System.out.println("Binary received: " + data.length + " bytes");
// Process binary data
processData(data);
}
@Override
public void onClose(int code, String reason) {
System.out.println("Connection closed with code " + code + ": " + reason);
cleanup();
}
@Override
public void onError(Throwable cause) {
System.err.println("WebSocket error occurred: " + cause.getMessage());
cause.printStackTrace();
// Custom error handling
handleError(cause);
}
private void processData(byte[] data) { /* process binary data */ }
private void cleanup() { /* cleanup resources */ }
private void handleError(Throwable cause) { /* handle error */ }
};
// Minimal listener (only text messages)
WebSocket.Listener textOnlyListener = new WebSocket.Listener() {
@Override
public void onText(CharSequence data) {
System.out.println("Received: " + data);
}
};
// Lambda-based listener using Consumer interface
WebSocket.Listener lambdaListener = message -> {
if (message instanceof TextMessage) {
System.out.println("Text: " + ((TextMessage) message).text());
} else if (message instanceof BinaryMessage) {
System.out.println("Binary: " + ((BinaryMessage) message).data().length + " bytes");
}
};
// Use listeners with WebSocket
WebSocket socket1 = client.openSocket(wsRequest, fullListener);
WebSocket socket2 = client.openSocket(wsRequest, textOnlyListener);
WebSocket socket3 = client.openSocket(wsRequest, lambdaListener);Base interface for all WebSocket message types.
/**
* Marker interface for WebSocket messages
* Implemented by TextMessage, BinaryMessage, and CloseMessage
*/
public interface Message {
// Marker interface - no methods
}WebSocket text message implementation for string data.
/**
* WebSocket text message containing string data
*/
public class TextMessage implements Message {
/**
* Creates text message from character sequence
* @param text Text content for the message
*/
public TextMessage(CharSequence text);
/**
* Gets the text content
* @return Message text as String
*/
public String text();
}Usage Examples:
// Create and send text messages
TextMessage message1 = new TextMessage("Hello World");
TextMessage message2 = new TextMessage(new StringBuilder("Dynamic content"));
socket.send(message1);
socket.send(message2);
// Access text content
String content = message1.text();
System.out.println("Message content: " + content);
// Use in listener
WebSocket.Listener listener = new WebSocket.Listener() {
@Override
public void onText(CharSequence data) {
// Can also receive via accept method:
}
@Override
public void accept(Message message) {
if (message instanceof TextMessage) {
TextMessage textMsg = (TextMessage) message;
System.out.println("Received text: " + textMsg.text());
}
}
};WebSocket binary message implementation for byte data with defensive copying.
/**
* WebSocket binary message containing byte data
* Makes defensive copies to ensure data integrity
*/
public class BinaryMessage implements Message {
/**
* Creates binary message from ByteBuffer
* Makes read-only copy of the buffer
* @param data ByteBuffer containing binary data
*/
public BinaryMessage(ByteBuffer data);
/**
* Creates binary message from byte array
* Makes defensive copy of the array
* @param data Byte array containing binary data
*/
public BinaryMessage(byte[] data);
/**
* Gets the binary data
* Returns copy of internal data to prevent modification
* @return Byte array copy of message data
*/
public byte[] data();
}Usage Examples:
import java.nio.ByteBuffer;
// Create binary messages from byte array
byte[] imageData = loadImageData();
BinaryMessage imageMessage = new BinaryMessage(imageData);
socket.send(imageMessage);
// Create from ByteBuffer
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("Binary content".getBytes());
buffer.flip();
BinaryMessage bufferMessage = new BinaryMessage(buffer);
socket.send(bufferMessage);
// Access binary data (returns defensive copy)
byte[] messageData = imageMessage.data();
System.out.println("Message size: " + messageData.length + " bytes");
// Modify returned data doesn't affect original
byte[] data = imageMessage.data();
data[0] = 0; // This doesn't change the message's internal data
// Use in listener
WebSocket.Listener listener = new WebSocket.Listener() {
@Override
public void onBinary(byte[] data) {
System.out.println("Received " + data.length + " bytes");
processImageData(data);
}
@Override
public void accept(Message message) {
if (message instanceof BinaryMessage) {
BinaryMessage binMsg = (BinaryMessage) message;
byte[] data = binMsg.data();
saveBinaryData(data);
}
}
private void processImageData(byte[] data) { /* process image */ }
private void saveBinaryData(byte[] data) { /* save to file */ }
};WebSocket close message with status code and reason.
/**
* WebSocket close message with status code and optional reason
*/
public class CloseMessage implements Message {
/**
* Creates close message with status code only
* @param code Close status code
*/
public CloseMessage(int code);
/**
* Creates close message with status code and reason
* @param code Close status code
* @param reason Close reason string
*/
public CloseMessage(int code, String reason);
/**
* Gets the close status code
* @return Status code indicating close reason
*/
public int code();
/**
* Gets the close reason string
* @return Close reason or empty string if not provided
*/
public String reason();
}Usage Examples:
// Create close messages
CloseMessage normalClose = new CloseMessage(1000); // Normal closure
CloseMessage errorClose = new CloseMessage(1002, "Protocol error");
CloseMessage customClose = new CloseMessage(4000, "Custom application error");
// Send close messages
socket.send(normalClose);
// Access close information
int closeCode = errorClose.code(); // 1002
String closeReason = errorClose.reason(); // "Protocol error"
System.out.println("Closing with code " + closeCode + ": " + closeReason);
// Use in listener
WebSocket.Listener listener = new WebSocket.Listener() {
@Override
public void onClose(int code, String reason) {
System.out.println("Connection closed: " + code + " - " + reason);
// Handle different close codes
switch (code) {
case 1000:
System.out.println("Normal closure");
break;
case 1001:
System.out.println("Endpoint going away");
break;
case 1002:
System.out.println("Protocol error");
break;
default:
System.out.println("Other close reason: " + code);
}
}
@Override
public void accept(Message message) {
if (message instanceof CloseMessage) {
CloseMessage closeMsg = (CloseMessage) message;
handleClose(closeMsg.code(), closeMsg.reason());
}
}
private void handleClose(int code, String reason) {
// Custom close handling logic
}
};Complete example showing WebSocket connection lifecycle:
import org.openqa.selenium.remote.http.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class WebSocketExample {
private WebSocket socket;
private final CountDownLatch closeLatch = new CountDownLatch(1);
public void connectAndCommunicate() throws InterruptedException {
// Create HTTP client
HttpClient client = HttpClient.Factory.createDefault()
.createClient(new URL("wss://echo.websocket.org"));
// Create WebSocket request
HttpRequest wsRequest = new HttpRequest(HttpMethod.GET, "/");
// Create listener
WebSocket.Listener listener = new WebSocket.Listener() {
@Override
public void onText(CharSequence data) {
System.out.println("Echo received: " + data);
}
@Override
public void onClose(int code, String reason) {
System.out.println("Connection closed: " + code + " - " + reason);
closeLatch.countDown();
}
@Override
public void onError(Throwable cause) {
System.err.println("WebSocket error: " + cause.getMessage());
closeLatch.countDown();
}
};
// Open connection
socket = client.openSocket(wsRequest, listener);
// Send messages
socket.sendText("Hello WebSocket!");
socket.sendText("This is a test message");
// Send binary data
socket.sendBinary("Binary test data".getBytes());
// Wait a bit for responses
Thread.sleep(2000);
// Close connection gracefully
socket.send(new CloseMessage(1000, "Normal closure"));
// Wait for close confirmation
closeLatch.await(10, TimeUnit.SECONDS);
// Cleanup
client.close();
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-seleniumhq-selenium--selenium-http