package com.ankurm.ssews; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; /** * A raw-socket SSE client. * *

Deliberately not an HTTP client library: the article quotes the bytes on the wire, and any * client that parses events for you hides exactly the thing being shown. It also lets a test * abandon the socket mid-stream, which is how the disconnect case is reproduced. */ final class SseClient implements AutoCloseable { private final Socket socket; private final BufferedReader reader; private final List lines = new ArrayList<>(); SseClient(int port, String path) throws Exception { this(port, path, null); } SseClient(int port, String path, String lastEventId) throws Exception { this.socket = new Socket("127.0.0.1", port); this.socket.setSoTimeout(15_000); StringBuilder req = new StringBuilder() .append("GET ").append(path).append(" HTTP/1.1\r\n") .append("Host: 127.0.0.1:").append(port).append("\r\n") .append("Accept: text/event-stream\r\n"); if (lastEventId != null) { req.append("Last-Event-ID: ").append(lastEventId).append("\r\n"); } req.append("Connection: close\r\n\r\n"); OutputStream out = socket.getOutputStream(); out.write(req.toString().getBytes(StandardCharsets.UTF_8)); out.flush(); this.reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8)); } /** Read until the stream closes or {@code max} lines have arrived. Records everything. */ List read(int max) throws Exception { String line; while (lines.size() < max && (line = reader.readLine()) != null) { lines.add(line); } return List.copyOf(lines); } /** Read everything until the server closes the connection. */ List readToEnd() throws Exception { String line; while ((line = reader.readLine()) != null) { lines.add(line); } return List.copyOf(lines); } List lines() { return List.copyOf(lines); } @Override public void close() throws Exception { socket.close(); } }