@@ -141,5 +93,4 @@
-
-
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 7d7c4cf..ea0fd12 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -6,5 +6,4 @@
-
-
+
\ No newline at end of file
diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml
deleted file mode 100644
index 922003b..0000000
--- a/.idea/scopes/scope_settings.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index def6a6a..35eb1dd 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -1,7 +1,6 @@
-
+
-
-
+
\ No newline at end of file
diff --git a/README.md b/README.md
index c1ee7e6..bb8dc8f 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,8 @@
# eventsource-android
An Android EventSource (SSE) Library
-This is a Java implementation of the EventSource - a client for Server-Sent Events. The implementation is based on Netty
+This is a Java implementation of the EventSource - a client for Server-Sent Events. The implementation is based on Netty.
+
This project is based of off EventSource-Java:
https://github.com/aslakhellesoy/eventsource-java
@@ -21,6 +22,7 @@ Example implementation:
public void run() {
try {
eventSource = new EventSource(Uri, new SSEHandler(), extraHeaderParameters);
+ /* eventSource = new EventSource(UriProxy, UriApi, new SSEHandler(), extraHeaderParameters)*/
eventSource.connect();
} catch(URISyntaxException e) {
Log.v("Error starting eventsource", "True");
diff --git a/eventsource-android.iml b/eventsource-android.iml
index 2a02201..ab41ce8 100644
--- a/eventsource-android.iml
+++ b/eventsource-android.iml
@@ -1,15 +1,14 @@
-
+
+
-
-
-
+
@@ -17,5 +16,4 @@
-
-
+
\ No newline at end of file
diff --git a/eventsource_android/eventsource_android.iml b/eventsource_android/eventsource_android.iml
index cf8db6e..431f96a 100644
--- a/eventsource_android/eventsource_android.iml
+++ b/eventsource_android/eventsource_android.iml
@@ -1,5 +1,5 @@
-
+
@@ -9,21 +9,22 @@
+
-
-
-
+
+ generateDebugSources
+
-
+
-
+
@@ -60,22 +61,15 @@
+
-
-
-
-
-
-
-
-
@@ -85,9 +79,8 @@
-
+
-
-
+
\ No newline at end of file
diff --git a/eventsource_android/src/main/AndroidManifest.xml b/eventsource_android/src/main/AndroidManifest.xml
index 971aff4..4803636 100644
--- a/eventsource_android/src/main/AndroidManifest.xml
+++ b/eventsource_android/src/main/AndroidManifest.xml
@@ -1,8 +1,17 @@
+
+ package="tylerjroach.com.eventsource_android" >
-
+
+
+
+
+
+
+
diff --git a/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/EventSource.java b/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/EventSource.java
index da28d1b..b829d1a 100644
--- a/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/EventSource.java
+++ b/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/EventSource.java
@@ -1,13 +1,13 @@
package tylerjroach.com.eventsource_android;
+import android.util.Log;
+
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
-import org.jboss.netty.handler.codec.frame.DelimiterBasedFrameDecoder;
-import org.jboss.netty.handler.codec.frame.Delimiters;
import org.jboss.netty.handler.codec.http.HttpRequestEncoder;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.ssl.SslHandler;
@@ -17,6 +17,7 @@
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
import javax.net.ssl.SSLEngine;
@@ -30,81 +31,104 @@ public class EventSource implements EventSourceHandler {
public static final int OPEN = 1;
public static final int CLOSED = 2;
- private final ClientBootstrap bootstrap;
- private final EventSourceChannelHandler clientHandler;
- private final EventSourceHandler eventSourceHandler;
+ private static AtomicInteger count = new AtomicInteger(1);
+ private ClientBootstrap bootstrap;
+ private EventSourceChannelHandler clientHandler;
+ private EventSourceHandler eventSourceHandler;
- private URI uri;
+ private URI uri, requestUri;
private Map headers;
private int readyState;
/**
- * Creates a new EventSource client. The client will reconnect on
- * lost connections automatically, unless the connection is closed explicitly by a call to
+ * Creates a new EventSource client. The client will reconnect on
+ * lost connections automatically, unless the connection is closed explicitly by a call to
* {@link tylerjroach.com.eventsource_android.EventSource#close()}.
- *
+ *
* For sample usage, see examples at GitHub.
- *
- * @param executor the executor that will receive events
+ *
+ * @param executor the executor that will receive events
* @param reconnectionTimeMillis delay before a reconnect is made - in the event of a lost connection
- * @param pURI where to connect
- * @param eventSourceHandler receives events
- * @param headers Map of additional headers, such as passing auth tokens
+ * @param pURI where to connect
+ * @param eventSourceHandler receives events
+ * @param headers Map of additional headers, such as passing auth tokens
* @see #close()
*/
+ public EventSource(Executor executor, long reconnectionTimeMillis, final URI pURI, URI requestUri, EventSourceHandler eventSourceHandler, Map headers) {
+ this(executor, reconnectionTimeMillis, pURI, requestUri, null, eventSourceHandler, headers);
+ }
+
+
public EventSource(Executor executor, long reconnectionTimeMillis, final URI pURI, EventSourceHandler eventSourceHandler, Map headers) {
this(executor, reconnectionTimeMillis, pURI, null, eventSourceHandler, headers);
}
- public EventSource(Executor executor, long reconnectionTimeMillis, final URI pURI, SSLEngineFactory fSSLEngine, EventSourceHandler eventSourceHandler, Map headers) {
+
+ public EventSource(Executor executor, long reconnectionTimeMillis, final URI pURI, URI requestUri, SSLEngineFactory fSSLEngine, EventSourceHandler eventSourceHandler, Map headers) {
this.eventSourceHandler = eventSourceHandler;
-
bootstrap = new ClientBootstrap(
new NioClientSocketChannelFactory(
Executors.newSingleThreadExecutor(),
Executors.newSingleThreadExecutor()));
if (pURI.getScheme().equals("https") && fSSLEngine == null) {
- fSSLEngine = new SSLEngineFactory();
+ fSSLEngine = new SSLEngineFactory();
} else {
- //If we don't do this then the pipeline still attempts to use SSL
- fSSLEngine = null;
+ //If we don't do this then the pipeline still attempts to use SSL
+ fSSLEngine = null;
}
final SSLEngineFactory SSLFactory = fSSLEngine;
-
+
uri = pURI;
int port = uri.getPort();
- if (port==-1)
- {
- port = (uri.getScheme().equals("https"))?443:80;
+ // handle default port values
+ if (port == -1) {
+ port = (uri.getScheme().equals("https")) ? 443 : 80;
}
+
bootstrap.setOption("remoteAddress", new InetSocketAddress(uri.getHost(), port));
- // add this class as the event source handler so the connect() call can be intercepted
+ // add this class as the event source handler so the connect() call can be intercepted.
AsyncEventSourceHandler asyncHandler = new AsyncEventSourceHandler(executor, this);
- clientHandler = new EventSourceChannelHandler(asyncHandler, reconnectionTimeMillis, bootstrap, uri, headers);
+ clientHandler = new EventSourceChannelHandler(asyncHandler, reconnectionTimeMillis, bootstrap, uri, requestUri, headers);
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
if (SSLFactory != null) {
- SSLEngine sslEngine = SSLFactory.GetNewSSLEngine();
- sslEngine.setUseClientMode(true);
+ SSLEngine sslEngine = SSLFactory.GetNewSSLEngine();
+ sslEngine.setUseClientMode(true);
+ // add handling of https connection
pipeline.addLast("ssl", new SslHandler(sslEngine));
}
- pipeline.addLast("line", new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, Delimiters.lineDelimiter()));
+ // simply decode flow as string
pipeline.addLast("string", new StringDecoder());
-
+ // simple encode request as HTTP request
pipeline.addLast("encoder", new HttpRequestEncoder());
+ // add our own event source handler
pipeline.addLast("es-handler", clientHandler);
return pipeline;
}
});
+ connect();
}
+ public EventSource(URI proxyPrefixUri, URI requestUri, EventSourceHandler eventSourceHandler, Map headers) {
+ this(URI.create(proxyPrefixUri.toString() + requestUri.toString()), requestUri, null, eventSourceHandler, headers);
+ }
+
+ public EventSource(URI uri, URI requestUri, SSLEngineFactory sslEngineFactory, EventSourceHandler eventSourceHandler) {
+ this(Executors.newSingleThreadExecutor(), DEFAULT_RECONNECTION_TIME_MILLIS, uri, requestUri, sslEngineFactory, eventSourceHandler, null);
+ }
+
+ public EventSource(URI uri, URI requestUri, SSLEngineFactory sslEngineFactory, EventSourceHandler eventSourceHandler, Map headers) {
+ this(Executors.newSingleThreadExecutor(), DEFAULT_RECONNECTION_TIME_MILLIS, uri, requestUri, sslEngineFactory, eventSourceHandler, headers);
+ }
+
+
public EventSource(String uri, EventSourceHandler eventSourceHandler) {
this(uri, null, eventSourceHandler);
}
@@ -117,28 +141,22 @@ public EventSource(URI uri, EventSourceHandler eventSourceHandler) {
this(uri, null, eventSourceHandler);
}
- public EventSource(URI uri, EventSourceHandler eventSourceHandler, Map headers) {
- this(uri, null, eventSourceHandler, headers);
- }
-
-
public EventSource(URI uri, SSLEngineFactory sslEngineFactory, EventSourceHandler eventSourceHandler) {
- this(Executors.newSingleThreadExecutor(), DEFAULT_RECONNECTION_TIME_MILLIS, uri, sslEngineFactory, eventSourceHandler, null);
+ this(Executors.newSingleThreadExecutor(), DEFAULT_RECONNECTION_TIME_MILLIS, uri, null, sslEngineFactory, eventSourceHandler, null);
}
public EventSource(URI uri, SSLEngineFactory sslEngineFactory, EventSourceHandler eventSourceHandler, Map headers) {
- this(Executors.newSingleThreadExecutor(), DEFAULT_RECONNECTION_TIME_MILLIS, uri, sslEngineFactory, eventSourceHandler, headers);
+ this(Executors.newSingleThreadExecutor(), DEFAULT_RECONNECTION_TIME_MILLIS, uri, null, sslEngineFactory, eventSourceHandler, headers);
}
- public ChannelFuture connect() {
+
+ private ChannelFuture connect() {
readyState = CONNECTING;
-
- //To avoid perpetual "SocketUnresolvedException"
+
int port = uri.getPort();
- if (port==-1)
- {
- port = (uri.getScheme().equals("https"))?443:80;
+ if (port == -1) {
+ port = (uri.getScheme().equals("https")) ? 443 : 80;
}
bootstrap.setOption("remoteAddress", new InetSocketAddress(uri.getHost(), port));
return bootstrap.connect();
@@ -156,17 +174,8 @@ public boolean isConnected() {
public EventSource close() {
readyState = CLOSED;
clientHandler.close();
- return this;
- }
-
- /**
- * Wait until the connection is closed
- *
- * @return self
- * @throws InterruptedException if waiting was interrupted
- */
- public EventSource join() throws InterruptedException {
- clientHandler.join();
+ setEventSourceHandler(null);
+ Log.d(EventSource.class.getName(), "eventSource closed:" + count.getAndIncrement());
return this;
}
@@ -176,24 +185,51 @@ public void onConnect() throws Exception {
readyState = OPEN;
// pass event to the proper handler
- eventSourceHandler.onConnect();
+ if (eventSourceHandler != null) {
+ eventSourceHandler.onConnect();
+ }
}
@Override
public void onMessage(String event, MessageEvent message) throws Exception {
// pass event to the proper handler
- eventSourceHandler.onMessage(event, message);
+ if (eventSourceHandler != null) {
+ eventSourceHandler.onMessage(event, message);
+ }
}
@Override
public void onError(Throwable t) {
// pass event to the proper handler
- eventSourceHandler.onError(t);
+ if (eventSourceHandler != null) {
+ eventSourceHandler.onError(t);
+ }
}
-
+
@Override
public void onClosed(boolean willReconnect) {
// pass event to the proper handler
- eventSourceHandler.onClosed(willReconnect);
+ if (eventSourceHandler != null) {
+ eventSourceHandler.onClosed(willReconnect);
+ }
+ }
+
+ public EventSourceHandler getEventSourceHandler() {
+ return eventSourceHandler;
+ }
+
+ public void setEventSourceHandler(EventSourceHandler eventSourceHandler) {
+ this.eventSourceHandler = eventSourceHandler;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+
+ setEventSourceHandler(null);
+ clientHandler = null;
+ bootstrap.getFactory().releaseExternalResources();
+ bootstrap.releaseExternalResources();
+ bootstrap = null;
}
}
diff --git a/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/EventSourceHandler.java b/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/EventSourceHandler.java
index 7f95298..3af2105 100644
--- a/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/EventSourceHandler.java
+++ b/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/EventSourceHandler.java
@@ -2,7 +2,10 @@
public interface EventSourceHandler {
void onConnect() throws Exception;
+
void onMessage(String event, MessageEvent message) throws Exception;
+
void onError(Throwable t);
+
void onClosed(boolean willReconnect);
}
diff --git a/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/MessageEvent.java b/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/MessageEvent.java
index 979cf6f..083db4b 100644
--- a/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/MessageEvent.java
+++ b/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/MessageEvent.java
@@ -23,7 +23,8 @@ public boolean equals(Object o) {
MessageEvent that = (MessageEvent) o;
if (data != null ? !data.equals(that.data) : that.data != null) return false;
- if (lastEventId != null ? !lastEventId.equals(that.lastEventId) : that.lastEventId != null) return false;
+ if (lastEventId != null ? !lastEventId.equals(that.lastEventId) : that.lastEventId != null)
+ return false;
if (origin != null ? !origin.equals(that.origin) : that.origin != null) return false;
return true;
diff --git a/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/SSLEngineFactory.java b/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/SSLEngineFactory.java
index 0a64333..695a668 100644
--- a/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/SSLEngineFactory.java
+++ b/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/SSLEngineFactory.java
@@ -7,20 +7,20 @@
import javax.net.ssl.SSLEngine;
public class SSLEngineFactory {
- SSLEngine GetNewSSLEngine() {
- SSLEngine sslEngine = null;
- SSLContext sslContext;
- try {
- sslContext = SSLContext.getInstance("TLS");
- try {
- sslContext.init(null, null, null);
- sslEngine = sslContext.createSSLEngine();
- } catch (KeyManagementException e) {
- return null;
- }
- } catch (NoSuchAlgorithmException e1) {
- return null;
- }
- return sslEngine;
- }
+ SSLEngine GetNewSSLEngine() {
+ SSLEngine sslEngine = null;
+ SSLContext sslContext;
+ try {
+ sslContext = SSLContext.getInstance("TLS");
+ try {
+ sslContext.init(null, null, null);
+ sslEngine = sslContext.createSSLEngine();
+ } catch (KeyManagementException e) {
+ return null;
+ }
+ } catch (NoSuchAlgorithmException e1) {
+ return null;
+ }
+ return sslEngine;
+ }
}
diff --git a/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/impl/AsyncEventSourceHandler.java b/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/impl/AsyncEventSourceHandler.java
index 6b430bd..baef0e1 100644
--- a/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/impl/AsyncEventSourceHandler.java
+++ b/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/impl/AsyncEventSourceHandler.java
@@ -27,11 +27,10 @@ public void run() {
}
});
}
-
+
@Override
- public void onClosed(final boolean willReconnect)
- {
- executor.execute(new Runnable() {
+ public void onClosed(final boolean willReconnect) {
+ executor.execute(new Runnable() {
@Override
public void run() {
try {
diff --git a/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/impl/ConnectionHandler.java b/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/impl/ConnectionHandler.java
index e17f4b9..cb27da1 100644
--- a/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/impl/ConnectionHandler.java
+++ b/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/impl/ConnectionHandler.java
@@ -2,5 +2,6 @@
public interface ConnectionHandler {
void setReconnectionTimeMillis(long reconnectionTimeMillis);
+
void setLastEventId(String lastEventId);
}
diff --git a/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/impl/netty/EventSourceChannelHandler.java b/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/impl/netty/EventSourceChannelHandler.java
index f59d456..0a4d477 100644
--- a/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/impl/netty/EventSourceChannelHandler.java
+++ b/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/impl/netty/EventSourceChannelHandler.java
@@ -1,5 +1,7 @@
package tylerjroach.com.eventsource_android.impl.netty;
+import android.util.Log;
+
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelEvent;
@@ -34,15 +36,14 @@
public class EventSourceChannelHandler extends SimpleChannelUpstreamHandler implements ConnectionHandler {
private static final Pattern STATUS_PATTERN = Pattern.compile("HTTP/1.1 (\\d+) (.*)");
- private static final Pattern CONTENT_TYPE_PATTERN = Pattern.compile("Content-Type: text/event-stream");
+ private static final Pattern CONTENT_TYPE_PATTERN = Pattern.compile("Content-Type: text/event-stream(.*)");
private final EventSourceHandler eventSourceHandler;
private final ClientBootstrap bootstrap;
- private final URI uri;
private final Map headers;
private final EventStreamParser messageDispatcher;
-
private final Timer timer = new HashedWheelTimer();
+ private URI uri, requestUri;
private Channel channel;
private boolean reconnectOnClose = true;
private long reconnectionTimeMillis;
@@ -51,6 +52,12 @@ public class EventSourceChannelHandler extends SimpleChannelUpstreamHandler impl
private boolean headerDone;
private Integer status;
private AtomicBoolean reconnecting = new AtomicBoolean(false);
+ private StringBuffer data = new StringBuffer();
+
+ public EventSourceChannelHandler(EventSourceHandler eventSourceHandler, long reconnectionTimeMillis, ClientBootstrap bootstrap, URI uri, URI requestUri, Map headers) {
+ this(eventSourceHandler, reconnectionTimeMillis, bootstrap, uri, headers);
+ this.requestUri = requestUri;
+ }
public EventSourceChannelHandler(EventSourceHandler eventSourceHandler, long reconnectionTimeMillis, ClientBootstrap bootstrap, URI uri, Map headers) {
this.eventSourceHandler = eventSourceHandler;
@@ -61,6 +68,16 @@ public EventSourceChannelHandler(EventSourceHandler eventSourceHandler, long rec
this.messageDispatcher = new EventStreamParser(uri.toString(), eventSourceHandler, this);
}
+ private static boolean isHexNumber(String cadena) {
+ try {
+ Long.parseLong(cadena, 16);
+ return true;
+ } catch (NumberFormatException ex) {
+ // Error handling code...
+ return false;
+ }
+ }
+
@Override
public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception {
super.handleUpstream(ctx, e);
@@ -68,7 +85,15 @@ public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exc
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
- HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri.toString());
+
+ HttpRequest request;
+
+ if (requestUri != null) {
+ request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/" + requestUri.toString());
+ } else {
+ request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri.toString());
+ }
+
request.addHeader(Names.ACCEPT, "text/event-stream");
if (headers != null) {
@@ -76,9 +101,8 @@ public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) thr
request.addHeader(entry.getKey(), entry.getValue());
}
}
-
request.addHeader(Names.HOST, uri.getHost());
- request.addHeader(Names.ORIGIN, uri.getScheme()+"://" + uri.getHost());
+ request.addHeader(Names.ORIGIN, uri.getScheme() + "://" + uri.getHost());
request.addHeader(Names.CACHE_CONTROL, "no-cache");
if (lastEventId != null) {
request.addHeader("Last-Event-ID", lastEventId);
@@ -94,7 +118,7 @@ public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e)
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
- eventSourceHandler.onClosed(reconnectOnClose);
+ eventSourceHandler.onClosed(reconnectOnClose);
if (reconnectOnClose) {
reconnect();
}
@@ -102,43 +126,98 @@ public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
- String line = (String) e.getMessage();
- if (status == null) {
- Matcher statusMatcher = STATUS_PATTERN.matcher(line);
- if (statusMatcher.matches()) {
- status = Integer.parseInt(statusMatcher.group(1));
- if (status != 200) {
- eventSourceHandler.onError(new EventSourceException("Bad status from " + uri + ": " + status));
- reconnect();
+ String messageAsString = (String) e.getMessage();
+
+ String[] lines = messageAsString.split("\\n",-1);
+ //int l = 0;
+ for (String dirtyLine : lines) {
+ String line = dirtyLine.replace("\r", "");
+ //l++;
+ //Log.d(EventSourceChannelHandler.class.getName(), "line" + l + ": " + line);
+ if (!headerDone) {
+ if (status == null) {
+ // checking Status header
+ Matcher statusMatcher = STATUS_PATTERN.matcher(line);
+ if (statusMatcher.matches()) {
+ status = Integer.parseInt(statusMatcher.group(1));
+ if (status != 200) {
+ eventSourceHandler.onError(new EventSourceException("Bad status from " + uri + ": " + status));
+ reconnect();
+ break;
+ }
+ //Log.d(EventSourceChannelHandler.class.getName(), "--- HTTP CONNECTED");
+ } else {
+ eventSourceHandler.onError(new EventSourceException("Not HTTP? " + uri + ": " + line));
+ reconnect();
+ break;
+ }
+ }
+ // checking Content-Type header
+ if (CONTENT_TYPE_PATTERN.matcher(line).matches()) {
+ eventStreamOk = true;
+ //Log.d(EventSourceChannelHandler.class.getName(), "--- SSE DETECTED");
+ }
+ // ignoring other headers
+ if (line.isEmpty()) {
+ // checking end of header part
+ headerDone = true;
+ if (eventStreamOk) {
+ eventSourceHandler.onConnect();
+ } else {
+ eventSourceHandler.onError(new EventSourceException("Not event stream: " + uri + " (expected Content-Type: text/event-stream"));
+ reconnect();
+ break;
+ }
}
- return;
} else {
- eventSourceHandler.onError(new EventSourceException("Not HTTP? " + uri + ": " + line));
- reconnect();
- }
- }
- if (!headerDone) {
- if (CONTENT_TYPE_PATTERN.matcher(line).matches()) {
- eventStreamOk = true;
- }
- if (line.isEmpty()) {
- headerDone = true;
- if (eventStreamOk) {
- eventSourceHandler.onConnect();
+ // data flow: data line or data chunk
+ if (isChunkStart(line)) {
+ // ignoring chunk size in case of chunk transfer Encoding
+ //Log.d(EventSourceChannelHandler.class.getName(), "CHUNK WITH SIZE: " + line);
} else {
- eventSourceHandler.onError(new EventSourceException("Not event stream: " + uri + " (expected Content-Type: text/event-stream"));
- reconnect();
+ String[] eventLines = line.split("\\n",-1);
+
+ for (String eventLine : eventLines) {
+ if (eventLine.startsWith("event:")) {
+ // dispatching new event
+ messageDispatcher.line(eventLine);
+ //Log.d(EventSourceChannelHandler.class.getName(), "SSE EVENT: " + eventLine);
+ } else if (eventLine.startsWith("id:")) {
+ // dispatching event id
+ messageDispatcher.line(eventLine);
+ //Log.d(EventSourceChannelHandler.class.getName(), "SSE EVENT ID: " + eventLine);
+ } else if (eventLine.startsWith("data:")) {
+ // append first line to data : data may be chunked
+ data.append(eventLine);
+ } else if (eventLine.isEmpty() && data.length() != 0) {
+ // end of data : dispatch aggregated data
+ messageDispatcher.line(data.toString());
+ // prepare next event data buffer
+ //Log.d(EventSourceChannelHandler.class.getName(), "SSE EVENT DATA: " + data.toString());
+ // dispatch the end line (empty line) in order to dispatch the event
+ messageDispatcher.line(eventLine);
+ data = new StringBuffer();
+ } else {
+ // new data chunk to append
+ data.append(eventLine);
+ }
+ }
}
}
- } else {
- messageDispatcher.line(line);
}
+
}
+ private boolean isChunkStart(String line) {
+
+ return isHexNumber(line);
+ }
+
+
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
Throwable error = e.getCause();
- if(error instanceof ConnectException) {
+ if (error instanceof ConnectException) {
error = new EventSourceException("Failed to connect to " + uri, error);
}
eventSourceHandler.onError(error);
@@ -170,15 +249,20 @@ public EventSourceChannelHandler join() throws InterruptedException {
}
private void reconnect() {
- if(!reconnecting.get()) {
+ if (!reconnecting.get()) {
reconnecting.set(true);
+ data = new StringBuffer();
+ lastEventId = null;
+ status = null;
+ eventStreamOk = false;
+ headerDone = false;
timer.newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
reconnecting.set(false);
int port = uri.getPort();
- if (port==-1) {
- port = (uri.getScheme().equals("https"))?443:80;
+ if (port == -1) {
+ port = (uri.getScheme().equals("https")) ? 443 : 80;
}
bootstrap.setOption("remoteAddress", new InetSocketAddress(uri.getHost(), port));
bootstrap.connect().await();
diff --git a/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/stubs/StubHandler.java b/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/stubs/StubHandler.java
index a4ed415..41c125c 100644
--- a/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/stubs/StubHandler.java
+++ b/eventsource_android/src/main/java/tylerjroach/com/eventsource_android/stubs/StubHandler.java
@@ -17,23 +17,13 @@ public class StubHandler implements ConnectionHandler, EventSourceHandler {
private Map> messagesByEvent = new HashMap>();
private List errors = new ArrayList();
- @Override
- public void setReconnectionTimeMillis(long reconnectionTimeMillis) {
- this.reconnectionTimeMillis = reconnectionTimeMillis;
- }
-
- @Override
- public void setLastEventId(String lastEventId) {
- this.lastEventId = lastEventId;
- }
-
@Override
public void onConnect() throws Exception {
connected = true;
}
-
+
@Override
- public void onClosed(boolean willReconnect){
+ public void onClosed(boolean willReconnect) {
connected = false;
}
@@ -51,17 +41,27 @@ public Long getReconnectionTimeMillis() {
return reconnectionTimeMillis;
}
+ @Override
+ public void setReconnectionTimeMillis(long reconnectionTimeMillis) {
+ this.reconnectionTimeMillis = reconnectionTimeMillis;
+ }
+
public String getLastEventId() {
return lastEventId;
}
+ @Override
+ public void setLastEventId(String lastEventId) {
+ this.lastEventId = lastEventId;
+ }
+
public boolean isConnected() {
return connected;
}
public List getMessageEvents(String event) {
List events = messagesByEvent.get(event);
- if(events == null) {
+ if (events == null) {
events = new ArrayList();
messagesByEvent.put(event, events);
}
diff --git a/eventsource_android/src/main/res/layout/activity_main.xml b/eventsource_android/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..06f6848
--- /dev/null
+++ b/eventsource_android/src/main/res/layout/activity_main.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/eventsource_android/src/main/res/menu/menu_main.xml b/eventsource_android/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..a212461
--- /dev/null
+++ b/eventsource_android/src/main/res/menu/menu_main.xml
@@ -0,0 +1,6 @@
+
diff --git a/eventsource_android/src/main/res/values-w820dp/dimens.xml b/eventsource_android/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/eventsource_android/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/eventsource_android/src/main/res/values/dimens.xml b/eventsource_android/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/eventsource_android/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/eventsource_android/src/main/res/values/strings.xml b/eventsource_android/src/main/res/values/strings.xml
new file mode 100644
index 0000000..bddd68f
--- /dev/null
+++ b/eventsource_android/src/main/res/values/strings.xml
@@ -0,0 +1,7 @@
+
+ MainActivity
+
+ Hello world!
+ Settings
+
+