Skip to content

Commit

Permalink
Netty: add Netty 4 as a new Netty backend and make it default
Browse files Browse the repository at this point in the history
The old Netty 3 backend was moved to play.server.netty3
The new Netty 4 backend is placed to play.server.netty4

The play.server.Server class was retained to a shortcut for Netty 4.
  • Loading branch information
xabolcs committed Nov 1, 2022
1 parent c882f36 commit bb06d20
Show file tree
Hide file tree
Showing 23 changed files with 1,713 additions and 96 deletions.
1 change: 1 addition & 0 deletions framework/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ dependencies {
implementation('org.hibernate:hibernate-ehcache:5.6.12.Final') {transitive = false}
// Upgrade to Netty4 is WIP: https://github.com/codeborne/replay/pull/25
api('io.netty:netty:3.10.6.Final')
api('io.netty:netty-all:4.1.84.Final')
api('org.slf4j:slf4j-api:2.0.3')
api('org.slf4j:slf4j-reload4j:2.0.3')
api('org.slf4j:jul-to-slf4j:2.0.3')
Expand Down
4 changes: 2 additions & 2 deletions framework/src/play/Invoker.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.jamonapi.Monitor;
import com.jamonapi.MonitorFactory;
import play.server.PlayHandler;
import play.server.NettyInvocation;
import play.utils.PThreadFactory;

import java.util.concurrent.Future;
Expand Down Expand Up @@ -32,7 +32,7 @@ public class Invoker {
* The code to run
* @return The future object, to know when the task is completed
*/
public Future<?> invoke(PlayHandler.NettyInvocation invocation) {
public Future<?> invoke(NettyInvocation invocation) {
Monitor monitor = MonitorFactory.getMonitor("Invoker queue size", "elmts.");
monitor.add(executor.getQueue().size());
invocation.onQueued();
Expand Down
6 changes: 6 additions & 0 deletions framework/src/play/server/NettyInvocation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package play.server;

import play.Invocation;

public abstract class NettyInvocation extends Invocation {
}
4 changes: 2 additions & 2 deletions framework/src/play/server/ResettableFileInputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@

import static java.util.Objects.requireNonNull;

class ResettableFileInputStream extends InputStream {
public class ResettableFileInputStream extends InputStream {
private final File file;
private InputStream in;

ResettableFileInputStream(File file) throws FileNotFoundException {
public ResettableFileInputStream(File file) throws FileNotFoundException {
this.file = requireNonNull(file);
reset();
}
Expand Down
71 changes: 3 additions & 68 deletions framework/src/play/server/Server.java
Original file line number Diff line number Diff line change
@@ -1,78 +1,13 @@
package play.server;

import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import play.Play;
import play.Play.Mode;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.concurrent.Executors;

import static java.lang.Integer.parseInt;

public class Server {
private static final Logger logger = LoggerFactory.getLogger(Server.class);

public static int httpPort;
private final Play play;

public class Server extends play.server.netty4.Server {
public Server(Play play) {
this(play, parseInt(Play.configuration.getProperty("http.port", "9000")));
super(play);
}

public Server(Play play, int port) {
this.play = play;
httpPort = port;
}

public void start() {
System.setProperty("file.encoding", "utf-8");

ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(), Executors.newCachedThreadPool())
);
InetAddress address = address();
bootstrap.setPipelineFactory(new HttpServerPipelineFactory(Play.invoker, play.getActionInvoker()));
bootstrap.bind(new InetSocketAddress(address, httpPort));
bootstrap.setOption("child.tcpNoDelay", true);

if (Play.mode == Mode.DEV) {
if (address == null) {
logger.info("Listening for HTTP on port {} (Waiting a first request to start) ...", httpPort);
} else {
logger.info("Listening for HTTP at {}:{} (Waiting a first request to start) ...", address, httpPort);
}
} else {
if (address == null) {
logger.info("Listening for HTTP on port {} ...", httpPort);
} else {
logger.info("Listening for HTTP at {}:{} ...", address, httpPort);
}
}
}

private InetAddress address() {
if (Play.configuration.getProperty("http.address") != null) {
return address(Play.configuration.getProperty("http.address"));
}

if (System.getProperties().containsKey("http.address")) {
return address(System.getProperty("http.address"));
}

return null;
}

private InetAddress address(String host) {
try {
return InetAddress.getByName(host);
}
catch (UnknownHostException e) {
throw new RuntimeException("Cannot resolve address " + host, e);
}
super(play, port);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package play.server;
package play.server.netty3;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package play.server;
package play.server.netty3;

import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package play.server;
package play.server.netty3;


import org.jboss.netty.buffer.AbstractChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBufferFactory;
import org.jboss.netty.buffer.ChannelBufferIndexFinder;
import org.jboss.netty.buffer.WrappedChannelBuffer;
import play.server.ResettableFileInputStream;

import java.io.File;
import java.io.FileNotFoundException;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package play.server;
package play.server.netty3;

import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package play.server;
package play.server.netty3;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package play.server;
package play.server.netty3;

import org.jboss.netty.channel.ChannelHandler;
import org.jboss.netty.channel.ChannelPipeline;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package play.server;
package play.server.netty3;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
Expand All @@ -17,7 +17,6 @@
import org.jboss.netty.handler.stream.ChunkedWriteHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import play.Invocation;
import play.InvocationContext;
import play.Invoker;
import play.Play;
Expand All @@ -36,6 +35,7 @@
import play.mvc.Scope.RenderArgs;
import play.mvc.results.NotFound;
import play.mvc.results.RenderStatic;
import play.server.NettyInvocation;
import play.templates.JavaExtensions;
import play.templates.TemplateLoader;
import play.utils.ErrorsCookieCrypter;
Expand Down Expand Up @@ -121,7 +121,7 @@ public void messageReceived(final ChannelHandlerContext ctx, MessageEvent messag
copyResponse(ctx, request, response, nettyRequest);
} else {
// Delegate to Play framework
invoker.invoke(new NettyInvocation(request, response, ctx, nettyRequest, messageEvent));
invoker.invoke(new Netty3Invocation(request, response, ctx, nettyRequest, messageEvent));
}

} catch (IllegalArgumentException ex) {
Expand All @@ -138,14 +138,14 @@ public void messageReceived(final ChannelHandlerContext ctx, MessageEvent messag

private static final Map<String, RenderStatic> staticPathsCache = new HashMap<>();

public class NettyInvocation extends Invocation {
private class Netty3Invocation extends NettyInvocation {
private final ChannelHandlerContext ctx;
private final Request request;
private final Response response;
private final HttpRequest nettyRequest;
private final MessageEvent event;

public NettyInvocation(Request request, Response response, ChannelHandlerContext ctx, HttpRequest nettyRequest, MessageEvent e) {
public Netty3Invocation(Request request, Response response, ChannelHandlerContext ctx, HttpRequest nettyRequest, MessageEvent e) {
this.ctx = ctx;
this.request = request;
this.response = response;
Expand Down
78 changes: 78 additions & 0 deletions framework/src/play/server/netty3/Server.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package play.server.netty3;

import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import play.Play;
import play.Play.Mode;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.concurrent.Executors;

import static java.lang.Integer.parseInt;

public class Server {
private static final Logger logger = LoggerFactory.getLogger(Server.class);

public static int httpPort;
private final Play play;

public Server(Play play) {
this(play, parseInt(Play.configuration.getProperty("http.port", "9000")));
}

public Server(Play play, int port) {
this.play = play;
httpPort = port;
}

public void start() {
System.setProperty("file.encoding", "utf-8");

ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(), Executors.newCachedThreadPool())
);
InetAddress address = address();
bootstrap.setPipelineFactory(new HttpServerPipelineFactory(Play.invoker, play.getActionInvoker()));
bootstrap.bind(new InetSocketAddress(address, httpPort));
bootstrap.setOption("child.tcpNoDelay", true);

if (Play.mode == Mode.DEV) {
if (address == null) {
logger.info("Listening for HTTP on port {} (Waiting a first request to start) ...", httpPort);
} else {
logger.info("Listening for HTTP at {}:{} (Waiting a first request to start) ...", address, httpPort);
}
} else {
if (address == null) {
logger.info("Listening for HTTP on port {} ...", httpPort);
} else {
logger.info("Listening for HTTP at {}:{} ...", address, httpPort);
}
}
}

private InetAddress address() {
if (Play.configuration.getProperty("http.address") != null) {
return address(Play.configuration.getProperty("http.address"));
}

if (System.getProperties().containsKey("http.address")) {
return address(System.getProperty("http.address"));
}

return null;
}

private InetAddress address(String host) {
try {
return InetAddress.getByName(host);
}
catch (UnknownHostException e) {
throw new RuntimeException("Cannot resolve address " + host, e);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package play.server;
package play.server.netty3;

import org.apache.commons.io.IOUtils;
import org.jboss.netty.buffer.ChannelBufferInputStream;
Expand Down
78 changes: 78 additions & 0 deletions framework/src/play/server/netty4/ByteRange.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package play.server.netty4;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import play.exceptions.UnexpectedException;

import java.io.IOException;
import java.io.RandomAccessFile;

import static java.nio.charset.StandardCharsets.UTF_8;

class ByteRange {
private static final Logger logger = LoggerFactory.getLogger(ByteRange.class);

private final String file;
private final RandomAccessFile raf;
final long start;
final long end;
private final byte[] header;
private int servedHeader;
private int servedRange;

ByteRange(String file, RandomAccessFile raf, long start, long end, long fileLength, String contentType, boolean includeHeader) {
this.file = file;
this.raf = raf;
this.start = start;
this.end = end;
if(includeHeader) {
header = ByteRangeInput.makeRangeBodyHeader(ByteRangeInput.DEFAULT_SEPARATOR, contentType, start, end, fileLength).getBytes(UTF_8);
} else {
header = new byte[0];
}
}

private long length() {
return end - start + 1;
}

long remaining() {
return end - start + 1 - servedRange;
}

long computeTotalLength() {
return length() + header.length;
}

int fill(byte[] into, int offset) {
logger.trace("fill {} at {}", file, offset);
int count = 0;
for(; offset < into.length && servedHeader < header.length; offset++, servedHeader++, count++) {
into[offset] = header[servedHeader];
}
if(offset < into.length) {
try {
raf.seek(start + servedRange);
long maxToRead = remaining() > (into.length - offset) ? (into.length - offset) : remaining();
if(maxToRead > Integer.MAX_VALUE) {
logger.debug("FileService: maxToRead >= 2^32 ! ({})", file);
maxToRead = Integer.MAX_VALUE;
}
int read = raf.read(into, offset, (int) maxToRead);
if(read < 0) {
throw new UnexpectedException("error while reading file : no more to read ! length=" + raf.length() + ", seek=" + (start + servedRange));
}
count += read;
servedRange += read;
} catch(IOException e) {
throw new UnexpectedException(e);
}
}
return count;
}

@Override
public String toString() {
return "ByteRange(" + start + "," + end + "@" + file + ")";
}
}
Loading

0 comments on commit bb06d20

Please sign in to comment.