Skip to content

Commit

Permalink
[#1594] Allow users to customize the Netty pipeline and thus add comp…
Browse files Browse the repository at this point in the history
…ression
  • Loading branch information
pepite committed Dec 29, 2012
1 parent 048f37b commit 2232df2
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 29 deletions.
2 changes: 1 addition & 1 deletion framework/dependencies.yml
Expand Up @@ -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.4.2.Final
- org.jboss.netty -> netty 3.6.0.Final
- org.postgresql -> postgresql 9.0
- org.slf4j -> slf4j-api 1.6.1
- org.slf4j -> slf4j-log4j12 1.6.1
Expand Down
Binary file removed framework/lib/netty-3.5.7.Final.jar
Binary file not shown.
Binary file added framework/lib/netty-3.6.0.Final.jar
Binary file not shown.
62 changes: 51 additions & 11 deletions framework/src/play/server/HttpServerPipelineFactory.java
@@ -1,31 +1,71 @@
package play.server;
package play.server;

import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
import org.jboss.netty.channel.ChannelHandler;
import play.Play;
import play.Logger;
import java.util.Map;
import java.util.HashMap;

import static org.jboss.netty.channel.Channels.pipeline;

public class HttpServerPipelineFactory implements ChannelPipelineFactory {

private String pipelineConfig = Play.configuration.getProperty("play.netty.pipeline", "play.server.FlashPolicyHandler,org.jboss.netty.handler.codec.http.HttpRequestDecoder,play.server.StreamChunkAggregator,org.jboss.netty.handler.codec.http.HttpResponseEncoder,org.jboss.netty.handler.stream.ChunkedWriteHandler,play.server.PlayHandler");

protected static Map<String, Class> classes = new HashMap<String, Class>();

public ChannelPipeline getPipeline() throws Exception {

Integer max = Integer.valueOf(Play.configuration.getProperty("play.netty.maxContentLength", "-1"));

ChannelPipeline pipeline = pipeline();
PlayHandler playHandler = new PlayHandler();

pipeline.addLast("flashPolicy", new FlashPolicyHandler());
pipeline.addLast("decoder", new HttpRequestDecoder());
pipeline.addLast("aggregator", new StreamChunkAggregator(max));
pipeline.addLast("encoder", new HttpResponseEncoder());
pipeline.addLast("chunkedWriter", playHandler.chunkedWriteHandler);
pipeline.addLast("handler", playHandler);
// 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];
try {
String name = getName(handler.trim());
ChannelHandler instance = getInstance(handler);
if (instance != null) {
pipeline.addLast(name, instance);
Server.pipelines.put(name, instance);
}
} 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);
}

return pipeline;
}

protected String getName(String name) {
if (name.lastIndexOf(".") > 0)
return name.substring(name.lastIndexOf(".") + 1);
return name;
}

protected ChannelHandler getInstance(String name) throws Exception {

Class clazz = classes.get(name);
if (clazz == null) {
clazz = Class.forName(name);
classes.put(name, clazz);
}
if (ChannelHandler.class.isAssignableFrom(clazz))
return (ChannelHandler)clazz.newInstance();
return null;
}
}

18 changes: 13 additions & 5 deletions framework/src/play/server/PlayHandler.java
Expand Up @@ -991,9 +991,7 @@ public static void setContentLength(HttpMessage message, long contentLength) {
message.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(contentLength));
}

// ~~~~~~~~~~~ Chunked response
final ChunkedWriteHandler chunkedWriteHandler = new ChunkedWriteHandler();


static class LazyChunkedInput implements org.jboss.netty.handler.stream.ChunkedInput {

private boolean closed = false;
Expand Down Expand Up @@ -1052,7 +1050,12 @@ public void writeChunk(Request playRequest, Response playResponse, ChannelHandle
copyResponse(ctx, playRequest, playResponse, nettyRequest);
}
((LazyChunkedInput) playResponse.direct).writeChunk(chunk);
chunkedWriteHandler.resumeTransfer();
if (Server.pipelines.get("ChunkedWriteHandler") != null) {
((ChunkedWriteHandler)Server.pipelines.get("ChunkedWriteHandler")).resumeTransfer();
}
if (Server.pipelines.get("SslChunkedWriteHandler") != null) {
((ChunkedWriteHandler)Server.pipelines.get("SslChunkedWriteHandler")).resumeTransfer();
}
} catch (Exception e) {
throw new UnexpectedException(e);
}
Expand All @@ -1061,7 +1064,12 @@ public void writeChunk(Request playRequest, Response playResponse, ChannelHandle
public void closeChunked(Request playRequest, Response playResponse, ChannelHandlerContext ctx, HttpRequest nettyRequest) {
try {
((LazyChunkedInput) playResponse.direct).close();
chunkedWriteHandler.resumeTransfer();
if (Server.pipelines.get("ChunkedWriteHandler") != null) {
((ChunkedWriteHandler)Server.pipelines.get("ChunkedWriteHandler")).resumeTransfer();
}
if (Server.pipelines.get("SslChunkedWriteHandler") != null) {
((ChunkedWriteHandler)Server.pipelines.get("SslChunkedWriteHandler")).resumeTransfer();
}
} catch (Exception e) {
throw new UnexpectedException(e);
}
Expand Down
4 changes: 4 additions & 0 deletions framework/src/play/server/Server.java
Expand Up @@ -10,18 +10,22 @@
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";

Expand Down
6 changes: 2 additions & 4 deletions framework/src/play/server/StreamChunkAggregator.java
Expand Up @@ -16,15 +16,13 @@ public class StreamChunkAggregator extends SimpleChannelUpstreamHandler {

private volatile HttpMessage currentMessage;
private volatile OutputStream out;
private final int maxContentLength;
private final static int maxContentLength = Integer.valueOf(Play.configuration.getProperty("play.netty.maxContentLength", "-1"));
private volatile File file;

/**
* Creates a new instance.
*/
public StreamChunkAggregator(int maxContentLength) {
this.maxContentLength = maxContentLength;
}
public StreamChunkAggregator() { }

@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
Expand Down
39 changes: 31 additions & 8 deletions framework/src/play/server/ssl/SslHttpServerPipelineFactory.java
Expand Up @@ -12,15 +12,20 @@
import play.Play;
import play.server.FlashPolicyHandler;
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;


public class SslHttpServerPipelineFactory implements ChannelPipelineFactory {
public class SslHttpServerPipelineFactory extends HttpServerPipelineFactory {

private String pipelineConfig = Play.configuration.getProperty("play.ssl.netty.pipeline", "play.server.FlashPolicyHandler,org.jboss.netty.handler.codec.http.HttpRequestDecoder,play.server.StreamChunkAggregator,org.jboss.netty.handler.codec.http.HttpResponseEncoder,org.jboss.netty.handler.stream.ChunkedWriteHandler,play.server.ssl.SslPlayHandler");

public ChannelPipeline getPipeline() throws Exception {

Integer max = Integer.valueOf(Play.configuration.getProperty("play.netty.maxContentLength", "-1"));
String mode = Play.configuration.getProperty("play.netty.clientAuth", "none");
String enabledCiphers = Play.configuration.getProperty("play.ssl.enabledCiphers", "");

Expand All @@ -42,14 +47,32 @@ public ChannelPipeline getPipeline() throws Exception {

engine.setEnableSessionCreation(true);

pipeline.addLast("flashPolicy", new FlashPolicyHandler());
pipeline.addLast("ssl", new SslHandler(engine));
pipeline.addLast("decoder", new HttpRequestDecoder());
pipeline.addLast("aggregator", new StreamChunkAggregator(max));
pipeline.addLast("encoder", new HttpResponseEncoder());
pipeline.addLast("chunkedWriter", new ChunkedWriteHandler());

// 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];
try {
String name = getName(handler.trim());
ChannelHandler instance = getInstance(handler);
if (instance != null) {
pipeline.addLast(name, instance);
Server.pipelines.put("Ssl" + name, instance);
}
} catch(Throwable e) {
Logger.error(" error adding " + handler, e);
}

pipeline.addLast("handler", new SslPlayHandler());
}

// 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);
}

return pipeline;
}
Expand Down
11 changes: 11 additions & 0 deletions resources/application-skel/conf/application.conf
Expand Up @@ -199,6 +199,17 @@ mail.smtp=mock
# Try to keep a low as possible. 1 thread will serialize all requests (very useful for debugging purpose)
# play.pool=3

# Netty pipeline configuration (advanced settings)
# You can default netty settings by overriding the following line. Each handler must be comma separated.
# The last value must be the PlayHandler class (or your own that extends PlayHandler)
# Default values are
# play.netty.pipeline = play.server.FlashPolicyHandler,org.jboss.netty.handler.codec.http.HttpRequestDecoder,play.server.StreamChunkAggregator,org.jboss.netty.handler.codec.http.HttpResponseEncoder,org.jboss.netty.handler.stream.ChunkedWriteHandler,play.server.PlayHandler
# For example, to enable Netty response compression
# play.netty.pipeline = play.server.FlashPolicyHandler,org.jboss.netty.handler.codec.http.HttpRequestDecoder,play.server.StreamChunkAggregator,org.jboss.netty.handler.codec.http.HttpResponseEncoder,org.jboss.netty.handler.codec.http.HttpContentCompressor,org.jboss.netty.handler.stream.ChunkedWriteHandler,play.server.PlayHandler
# For SSL, use the play.ssl.netty.pipeline property
# play.ssl.netty.pipeline = play.server.FlashPolicyHandler,org.jboss.netty.handler.codec.http.HttpRequestDecoder,play.server.StreamChunkAggregator,org.jboss.netty.handler.codec.http.HttpResponseEncoder,org.jboss.netty.handler.codec.http.HttpContentCompressor,org.jboss.netty.handler.stream.ChunkedWriteHandler,play.server.ssl.SslPlayHandler


# Open file from errors pages
# ~~~~~
# If your text editor supports opening files by URL, Play! will
Expand Down

0 comments on commit 2232df2

Please sign in to comment.