Skip to content

Instantly share code, notes, and snippets.

@greghelton
Created June 1, 2026 04:30
Show Gist options
  • Select an option

  • Save greghelton/cb0d3fd9b19aecb45a0ef128015e28e3 to your computer and use it in GitHub Desktop.

Select an option

Save greghelton/cb0d3fd9b19aecb45a0ef128015e28e3 to your computer and use it in GitHub Desktop.
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BrowserRelay {
// Shared queue between the /send handler and the /listen SSE stream
static final BlockingQueue<String> messages = new ArrayBlockingQueue<>(100);
public static void main(String[] args) throws IOException {
System.setOut(new PrintStream(System.out, true));
try (ServerSocket server = new ServerSocket(8080)) {
System.out.println("Relay running on http://localhost:8080");
while (true) {
Socket client = server.accept();
new Thread(() -> handleClient(client)).start();
}
}
}
static void handleClient(Socket client) {
try (client;
InputStream in = client.getInputStream();
OutputStream out = client.getOutputStream()) {
String requestLine = readLine(in);
if (requestLine == null || requestLine.isEmpty()) return;
System.out.println(requestLine);
// Drain headers, grab Origin for CORS
String origin = "http://localhost:8080";
String headerLine;
while (!(headerLine = readLine(in)).isEmpty()) {
if (headerLine.toLowerCase().startsWith("origin:"))
origin = headerLine.split(":", 2)[1].trim();
}
String url = requestLine.split(" ")[1];
if (url.startsWith("/send")) {
// POST from Firefox: extract msg param and queue it
String query = url.contains("?") ? url.split("\\?", 2)[1] : "";
String msg = parseParam(query, "msg");
messages.offer(msg.isEmpty() ? "(empty)" : msg);
System.out.println("Queued: " + msg);
String body = "OK";
writeResponse(out, "200 OK", "text/plain", origin,
body.getBytes(StandardCharsets.UTF_8), false);
} else if (url.startsWith("/listen")) {
// SSE stream held open for Safari
String headers = "HTTP/1.1 200 OK\r\n"
+ "Content-Type: text/event-stream\r\n"
+ "Cache-Control: no-cache\r\n"
+ "Access-Control-Allow-Origin: " + origin + "\r\n"
+ "Connection: keep-alive\r\n"
+ "\r\n";
out.write(headers.getBytes(StandardCharsets.US_ASCII));
out.flush();
// Block here, pushing messages as they arrive
while (true) {
String msg = messages.take(); // blocks until something is queued
String event = "data: " + msg + "\n\n"; // SSE format
out.write(event.getBytes(StandardCharsets.UTF_8));
out.flush();
System.out.println("Sent to listener: " + msg);
}
} else {
// Serve a minimal HTML page for both browsers
String body = """
<!DOCTYPE html><html><body>
<h3>Browser Relay</h3>
<script>
// Open SSE listener (run this in Safari)
function listen() {
const es = new EventSource('http://localhost:8080/listen');
es.onmessage = e => console.log('Received:', e.data);
console.log('Listening for messages...');
}
// Send a message (run this in Firefox)
function send(msg) {
fetch('http://localhost:8080/send?msg=' + encodeURIComponent(msg))
.then(() => console.log('Sent: ' + msg));
}
</script>
<p>Open the browser console and call <code>listen()</code> or <code>send('hello')</code></p>
</body></html>
""";
writeResponse(out, "200 OK", "text/html", origin,
body.getBytes(StandardCharsets.UTF_8), false);
}
} catch (IOException | InterruptedException e) {
System.out.println("Connection closed: " + e.getMessage());
}
}
static void writeResponse(OutputStream out, String status, String contentType,
String origin, byte[] body, boolean close) throws IOException {
String headers = "HTTP/1.1 " + status + "\r\n"
+ "Content-Type: " + contentType + "\r\n"
+ "Content-Length: " + body.length + "\r\n"
+ "Access-Control-Allow-Origin: " + origin + "\r\n"
+ (close ? "Connection: close\r\n" : "") + "\r\n";
out.write(headers.getBytes(StandardCharsets.US_ASCII));
out.write(body);
out.flush();
}
static String parseParam(String query, String key) throws UnsupportedEncodingException {
for (String pair : query.split("[&;]")) {
int eq = pair.indexOf('=');
if (eq == -1) continue;
String k = URLDecoder.decode(pair.substring(0, eq), StandardCharsets.UTF_8);
if (k.equals(key))
return URLDecoder.decode(pair.substring(eq + 1), StandardCharsets.UTF_8);
}
return "";
}
static String readLine(InputStream in) throws IOException {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
int b;
while ((b = in.read()) != -1) {
if (b == '\n') break;
if (b == '\r') continue;
buf.write(b);
}
return buf.toString(StandardCharsets.UTF_8);
}
}

Compile and run the Java.

Open both browsers to http://localhost:8080

Open consoles in both browsers.

In one, enter the call to listen: listen()

In the other, enter the call to send a message: send("hello from other browser")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment