Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

[#1677] Fix not streaming SSE/EventSource in Chrome

[#1677] Update to Netty V3.6.6
[#1677] Add tests for EventSource
  • Loading branch information...
commit c23a6f49fa2356f1ee8a320a9322e146ecc09940 1 parent 7d9e2dc
@xael-fry authored
View
2  framework/dependencies.yml
@@ -50,7 +50,7 @@ require: &allDependencies
- org.hibernate -> jboss-transaction-api_1.1_spec 1.0.0.Final
- org.hibernate.javax.persistence -> hibernate-jpa-2.0-api 1.0.1.Final
- org.javassist -> javassist 3.15.0.GA
- - org.jboss.netty -> netty 3.6.0.Final
+ - org.jboss.netty -> netty 3.6.6.Final
- org.postgresql -> postgresql 9.0
- org.slf4j -> slf4j-api 1.6.1
- org.slf4j -> slf4j-log4j12 1.6.1
View
BIN  framework/lib/netty-3.6.0.Final.jar → framework/lib/netty-3.6.6.Final.jar
Binary file not shown
View
41 framework/src/play/server/HttpServerPipelineFactory.java
@@ -22,30 +22,41 @@
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = pipeline();
+
+ String[] handlers = pipelineConfig.split(",");
+ if(handlers.length <= 0){
+ Logger.error("You must defined at least the playHandler in \"play.netty.pipeline\"");
+ return pipeline;
+ }
+
+ // Create the play Handler (always the last one)
+ String handler = handlers[handlers.length - 1];
+ ChannelHandler instance = getInstance(handler);
+ PlayHandler playHandler = (PlayHandler) instance;
+ if (playHandler == null) {
+ Logger.error("The last handler must be the playHandler in \"play.netty.pipeline\"");
+ return pipeline;
+ }
+
// Get all the pipeline. Give the user the opportunity to add their own
- String[] handlers = pipelineConfig.split(",");
for (int i = 0; i < handlers.length - 1; i++) {
- String handler = handlers[i];
+ handler = handlers[i];
try {
String name = getName(handler.trim());
- ChannelHandler instance = getInstance(handler);
+ instance = getInstance(handler);
if (instance != null) {
- pipeline.addLast(name, instance);
- Server.pipelines.put(name, instance);
+ pipeline.addLast(name, instance);
+ playHandler.pipelines.put(name, instance);
}
- } catch(Throwable e) {
+ } catch (Throwable e) {
Logger.error(" error adding " + handler, e);
}
-
- }
-
- // The last one is always the play handler
- String handler = handlers[handlers.length - 1];
- ChannelHandler instance = getInstance(handler);
- if (instance != null) {
- pipeline.addLast("handler", instance);
- Server.pipelines.put("handler", instance);
}
+
+ if (playHandler != null) {
+ pipeline.addLast("handler", playHandler);
+ playHandler.pipelines.put("handler", playHandler);
+ }
return pipeline;
}
View
22 framework/src/play/server/PlayHandler.java
@@ -62,6 +62,11 @@
private static final String ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
private static final Charset ASCII = Charset.forName("ASCII");
private static final MessageDigest SHA_1;
+
+ /**
+ * The Pipeline is given for a PlayHandler
+ */
+ public Map<String, ChannelHandler> pipelines = new HashMap<String, ChannelHandler>();
private WebSocketServerHandshaker handshaker;
@@ -1064,11 +1069,12 @@ public void writeChunk(Request playRequest, Response playResponse, ChannelHandle
copyResponse(ctx, playRequest, playResponse, nettyRequest);
}
((LazyChunkedInput) playResponse.direct).writeChunk(chunk);
- if (Server.pipelines.get("ChunkedWriteHandler") != null) {
- ((ChunkedWriteHandler)Server.pipelines.get("ChunkedWriteHandler")).resumeTransfer();
+
+ if (this.pipelines.get("ChunkedWriteHandler") != null) {
+ ((ChunkedWriteHandler)this.pipelines.get("ChunkedWriteHandler")).resumeTransfer();
}
- if (Server.pipelines.get("SslChunkedWriteHandler") != null) {
- ((ChunkedWriteHandler)Server.pipelines.get("SslChunkedWriteHandler")).resumeTransfer();
+ if (this.pipelines.get("SslChunkedWriteHandler") != null) {
+ ((ChunkedWriteHandler)this.pipelines.get("SslChunkedWriteHandler")).resumeTransfer();
}
} catch (Exception e) {
throw new UnexpectedException(e);
@@ -1078,11 +1084,11 @@ public void writeChunk(Request playRequest, Response playResponse, ChannelHandle
public void closeChunked(Request playRequest, Response playResponse, ChannelHandlerContext ctx, HttpRequest nettyRequest) {
try {
((LazyChunkedInput) playResponse.direct).close();
- if (Server.pipelines.get("ChunkedWriteHandler") != null) {
- ((ChunkedWriteHandler)Server.pipelines.get("ChunkedWriteHandler")).resumeTransfer();
+ if (this.pipelines.get("ChunkedWriteHandler") != null) {
+ ((ChunkedWriteHandler)this.pipelines.get("ChunkedWriteHandler")).resumeTransfer();
}
- if (Server.pipelines.get("SslChunkedWriteHandler") != null) {
- ((ChunkedWriteHandler)Server.pipelines.get("SslChunkedWriteHandler")).resumeTransfer();
+ if (this.pipelines.get("SslChunkedWriteHandler") != null) {
+ ((ChunkedWriteHandler)this.pipelines.get("SslChunkedWriteHandler")).resumeTransfer();
}
} catch (Exception e) {
throw new UnexpectedException(e);
View
6 framework/src/play/server/Server.java
@@ -10,22 +10,16 @@
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelException;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
-import org.jboss.netty.channel.ChannelHandler;
-
import play.Logger;
import play.Play;
import play.Play.Mode;
import play.libs.IO;
import play.server.ssl.SslHttpServerPipelineFactory;
-import play.vfs.VirtualFile;
-import java.util.Map;
-import java.util.HashMap;
public class Server {
public static int httpPort;
public static int httpsPort;
- public static Map<String, ChannelHandler> pipelines = new HashMap<String, ChannelHandler>();
public final static String PID_FILE = "server.pid";
View
37 framework/src/play/server/ssl/SslHttpServerPipelineFactory.java
@@ -11,12 +11,14 @@
import play.Play;
import play.server.FlashPolicyHandler;
+import play.server.PlayHandler;
import play.server.StreamChunkAggregator;
import play.server.HttpServerPipelineFactory;
+
import org.jboss.netty.channel.ChannelHandler;
+
import play.Logger;
import play.server.Server;
-
import static org.jboss.netty.channel.Channels.pipeline;
@@ -51,14 +53,28 @@ public ChannelPipeline getPipeline() throws Exception {
// Get all the pipeline. Give the user the opportunity to add their own
String[] handlers = pipelineConfig.split(",");
+ if(handlers.length <= 0){
+ Logger.error("You must defined at least the SslPlayHandler in \"play.netty.pipeline\"");
+ return pipeline;
+ }
+
+ // Create the play Handler (always the last one)
+ String handler = handlers[handlers.length - 1];
+ ChannelHandler instance = getInstance(handler);
+ SslPlayHandler sslPlayHandler = (SslPlayHandler) instance;
+ if (instance == null || !(instance instanceof SslPlayHandler) || sslPlayHandler != null) {
+ Logger.error("The last handler must be the SslPlayHandler in \"play.netty.pipeline\"");
+ return pipeline;
+ }
+
for (int i = 0; i < handlers.length - 1; i++) {
- String handler = handlers[i];
+ handler = handlers[i];
try {
String name = getName(handler.trim());
- ChannelHandler instance = getInstance(handler);
+ instance = getInstance(handler);
if (instance != null) {
pipeline.addLast(name, instance);
- Server.pipelines.put("Ssl" + name, instance);
+ sslPlayHandler.pipelines.put("Ssl" + name, instance);
}
} catch(Throwable e) {
Logger.error(" error adding " + handler, e);
@@ -66,14 +82,11 @@ public ChannelPipeline getPipeline() throws Exception {
}
- // The last one is always the play handler
- String handler = handlers[handlers.length - 1];
- ChannelHandler instance = getInstance(handler);
- if (instance != null) {
- pipeline.addLast("handler", instance);
- Server.pipelines.put("SslHandler", instance);
- }
-
+ if (sslPlayHandler != null) {
+ pipeline.addLast("handler", instance);
+ sslPlayHandler.pipelines.put("SslHandler", instance);
+ }
+
return pipeline;
}
}
View
47 samples-and-tests/just-test-cases/app/controllers/EventSources.java
@@ -0,0 +1,47 @@
+package controllers;
+
+import java.io.IOException;
+
+import play.mvc.Controller;
+
+
+public class EventSources extends Controller {
+
+ public static void index() {
+ render();
+ }
+
+ public static void chunkTest() {
+ response.setContentTypeIfNotSet("text/plain");
+ response.writeChunk("Hello");
+
+ // hack to make chrome start displaying (needed in both 1.2.5 and 1.3.x)
+ for (int x = 0; x < 5000; x++)
+ response.writeChunk(" ");
+
+ int i = 0;
+ while (i++ < 10) {
+ await(1000);
+ response.writeChunk(11 - i + " Why Hello!\n");
+ }
+
+ await(1000);
+ response.writeChunk("...Blastoff?\n");
+
+ }
+
+ public static void events() throws IOException {
+ response.encoding = "UTF-8";
+ response.contentType = "text/event-stream";
+ int id = 0;
+ int i = 0;
+ while (i++ < 3) {
+ await(1000);
+ response.writeChunk("event: message\n"+ "id: " + ++id + "\n"+ "retry: 5000\n" + "data: hello " + i + "\n\n");
+ }
+
+ await(1000);
+ response.writeChunk("event: message\n" + "id: " + ++id + "\n" + "data: bye\r\n\n");
+ }
+
+}
View
77 samples-and-tests/just-test-cases/app/views/EventSources/index.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>SSE Test</title>
+ <meta charset="utf-8" />
+ <script type="text/javascript" charset="utf-8">
+ function init() {
+ if(typeof(EventSource)!=="undefined") {
+ var log = document.getElementById('log');
+ if (log) {
+ log.innerHTML = new Date() + "EventSource() testing begins..<br>";
+ }
+
+ var svrEvents = new EventSource('@{EventSources.events()}');
+ if(svrEvents){
+ log.innerHTML += new Date() + "EventSource() creating...<br>";
+ }
+
+ svrEvents.addEventListener('open', function(e) {
+ connectionOpen(true);
+ }, false);
+
+ svrEvents.addEventListener('error', function(e) {
+ if (e.readyState == EventSource.CLOSED) {
+ connectionOpen(false);
+ }
+
+ connectionOpen(false);
+ }, false);
+
+ svrEvents.addEventListener('poll', displayPoll, false); // display multi choice and send back answer
+
+
+ svrEvents.addEventListener("message", function(event) {
+ var log = document.getElementById('log');
+ if (log) {
+ log.innerHTML += new Date() + 'message: ' + event.data + "<br>";
+ }
+ }, false);
+
+ svrEvents.addEventListener("ping", function(event) {
+ var log = document.getElementById('log');
+ if (log) {
+ log.innerHTML += new Date() + 'ping at: ' + event.data + "<br>";
+ }
+ }, false);
+
+ } else {
+ var log = document.getElementById('log');
+ if (log) {
+ log.innerHTML = "EventSource() not supported<br>";
+ }
+ }
+ }
+
+ function connectionOpen(status) {
+ var log = document.getElementById('log');
+ if (log) {
+ log.innerHTML += new Date() + 'connected: ' + status + "<br>";
+ }
+ }
+
+ function displayPoll(event) {
+ var html = event.data;
+ var log = document.getElementById('log');
+ if (log) {
+ log.innerHTML += new Date() + 'poll: ' + html + "<br>";
+ }
+ }
+
+
+ </script>
+ </head>
+ <body onLoad="init()">
+ <div id="log">testing...</div>
+ </body>
+</html>
View
21 samples-and-tests/just-test-cases/test/eventSources.test.html
@@ -0,0 +1,21 @@
+#{fixture delete:'all' /}
+
+#{selenium 'Test EventSource'}
+
+ openAndWait('@{EventSources.index()}')
+
+// Not supported in html unit
+// waitForTextPresent('connected: true')
+// assertTextPresent('connected: true')
+// waitForTextPresent('message: hello 1')
+// assertTextPresent('message: hello 1')
+// waitForTextPresent('message: hello 2')
+// assertTextPresent('message: hello 2')
+// waitForTextPresent('message: hello 3')
+// assertTextPresent('message: hello 3')
+// waitForTextPresent('message: bye')
+// assertTextPresent('message: bye')
+// waitForTextPresent('connected: false')
+// assertTextPresent('connected: false')
+#{/selenium}
+
Please sign in to comment.
Something went wrong with that request. Please try again.