/*
* CatSaver
* Copyright (C) 2015 HiHex Ltd.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
package hihex.cs;
import com.google.common.eventbus.Subscribe;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import fi.iki.elonen.NanoHTTPD;
public class LiveEventSource extends NanoHTTPD.Response {
final LinkedBlockingQueue<LogEntry> mEntries = new LinkedBlockingQueue<>();
public LiveEventSource() {
super(Status.OK, "text/event-stream", "");
}
@Override
protected void send(final OutputStream outputStream) {
// Do not call super(). The operation of SSE here is totally different from the normal fixed-length source and
// chunked-encoding response.
try {
Events.bus.register(this);
final SimpleDateFormat dateFormat = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.ROOT);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
final Writer writer = new BufferedWriter(new OutputStreamWriter(outputStream));
writer.write("HTTP/1.1 200 OK\r\nContent-Type: text/event-stream\r\nCache-Control: no-cache\r\nDate: ");
writer.write(dateFormat.format(new Date()));
writer.write("\r\nConnection: keep-alive\r\n\r\n");
writer.flush();
while (true) {
final LogEntry entry = mEntries.poll(10, TimeUnit.SECONDS);
// We use poll() with timeout instead of take(), so that there will be a finite time before we call
// writer.flush(). The flush() call ensures that if the peer closed the connection, we will eventually
// notice we can no longer send anything and throw an IOException. This in turn interrupts the infinite
// loop and terminate the callback thread.
if (entry != null) {
writer.write("event: message\ndata: ");
entry.writeJSON(writer);
writer.write("\n\n");
} else {
writer.write("event: ping\n\n");
}
writer.flush();
}
} catch (final IOException | InterruptedException e) {
//throw new RuntimeException(e);
} finally {
Events.bus.unregister(this);
}
}
@Subscribe
public void log(final Events.LiveEntry entry) {
try {
mEntries.put(new LogEntry(entry.entry));
} catch (final InterruptedException e) {
// Ignored, the queue is unbounded.
}
}
}