1package dev.rafex.ether.http.jetty12.exchange;
29import java.nio.ByteBuffer;
30import java.nio.charset.StandardCharsets;
31import java.util.ArrayDeque;
32import java.util.Deque;
34import org.eclipse.jetty.server.Response;
35import org.eclipse.jetty.util.Callback;
37import dev.rafex.ether.http.core.HttpExchange.EventStream;
48final class JettyEventStream
implements EventStream {
50 private static final ByteBuffer EMPTY = ByteBuffer.allocate(0);
53 private final Callback finalCallback;
54 private final Deque<byte[]> queue =
new ArrayDeque<>();
56 private boolean writePending;
57 private boolean closing;
58 private boolean finished;
59 private Runnable onClose;
61 JettyEventStream(
final Response
response,
final Callback finalCallback) {
63 this.finalCallback = finalCallback;
67 public void send(
final String event,
final String data) {
68 final var sb =
new StringBuilder();
69 if (event !=
null && !event.isBlank()) {
70 sb.append(
"event: ").append(event).append(
'\n');
72 final String safeData = data ==
null ?
"" : data;
73 for (
final String line : safeData.split(
"\n", -1)) {
74 sb.append(
"data: ").append(line).append(
'\n');
77 enqueue(sb.toString().getBytes(StandardCharsets.UTF_8));
81 public void comment(
final String text) {
82 final String safe = (text ==
null ?
"" : text).replace(
"\n",
" ");
83 enqueue((
": " + safe +
"\n\n").getBytes(StandardCharsets.UTF_8));
87 public synchronized void onClose(
final Runnable callback) {
88 this.onClose = callback;
102 private void enqueue(
final byte[] bytes) {
103 synchronized (
this) {
104 if (closing || finished) {
107 queue.addLast(bytes);
113 private void pump() {
116 synchronized (
this) {
117 if (writePending || finished) {
120 if (!queue.isEmpty()) {
121 next = queue.pollFirst();
123 }
else if (closing) {
131 response.write(last, next ==
null ? EMPTY : ByteBuffer.wrap(next),
new Callback() {
133 public void succeeded() {
137 synchronized (JettyEventStream.this) {
138 writePending =
false;
145 public void failed(
final Throwable cause) {
151 private void complete(
final Throwable cause) {
152 final Runnable callback;
153 synchronized (
this) {
163 finalCallback.succeeded();
165 finalCallback.failed(cause);
167 if (callback !=
null) {
A long-lived, transport-agnostic handle for pushing Server-Sent Events.
void close()
Ends the stream.